用 ffmpeg -copy 导出达芬奇视频工程,避免重新编码

@Ta 01-07 23:01发布,01-07 23:16修改 731点击
#!/usr/bin/env php
<?php
/**
 * 用 ffmpeg -copy 导出达芬奇视频工程,避免重新编码
 *
 * Davince Resolve 自带的导出功能会对视频进行转码,速度慢且视频质量可能会下降。
 * 如果视频工程只是简单的剪切合并,没有转场或特效,就可以用该脚本通过ffmpeg无损导出。
 * 
 * 使用方法:
 * 1. 在 Davince Resolve 选择“文件” > “导出” > “时间线”,导出为“FCP 7 XML V5文件”。
 * 2. 安装依赖包:
 *      sudo apt install ffmpeg php-cli php-xml
 * 3. 使用该脚本导出视频文件,用法:
 *      ./export.php fcp-7-xml-v5-timeline.xml output.mkv
 * 
 * 注意:除片段开始结束时间之外的所有其他设置都不会生效。
 *      该脚本不会修改视频内容,只会根据时间线XML文件对视频进行剪切拼接。
 *      如需修改视频内容(添加转场等),请用达芬奇导出。
**/

# 可执行文件的路径
define('FFMPEG', 'ffmpeg');
define('WSLPATH', '/usr/bin/wslpath');

# 临时文件夹路径
define('TMP_DIR', realpath('./tmp'));

function usage()
{
    global $argv;
    echo "Usage: $argv[0] fcp-7-xml-v5-timeline.xml output.mkv\n";
    exit;
}

if ($argc != 3) {
    usage();
}

$xml = $argv[1];
$output = $argv[2];
$pid = getmypid();

if (!is_file($xml)) {
    usage();
}

if (!is_dir(TMP_DIR)) {
    mkdir(TMP_DIR, 0777, true);
}

$obj = new SimpleXMLElement(file_get_contents($xml));

$timeBase = $obj->sequence->rate->timebase;

$i = 1;
$list = [];
$files = [];
foreach ($obj->sequence->media->video->track->clipitem as $clip) {
    $id = (string) ($clip->file['id']);
    $url = $clip->file->pathurl;

    $path = escapeshellarg(pathUrlDecode($id, $url));
    $start = escapeshellarg($clip->in / $timeBase);
    $end = escapeshellarg($clip->out / $timeBase);

    $tmpFile = TMP_DIR."/tmp-$pid-$i.mkv";
    $list[] = "file '$tmpFile'";
    $files[] = $tmpFile;

    $cmd = FFMPEG." -hide_banner -i $path -ss $start -to $end -c copy $tmpFile -y";
    echo "********************************\n$cmd\n";
    passthru($cmd);
    echo "\n";

    $i++;
}

$mergeList = TMP_DIR."/merge-$pid.list";
file_put_contents($mergeList, implode("\n", $list));

$cmd = FFMPEG." -hide_banner -f concat -safe 0 -i $mergeList -c copy ".escapeshellarg($output)." -y";
echo "********************************\n$cmd\n";
passthru($cmd);
echo "\n";

# 清理临时文件
unlink($mergeList);
foreach ($files as $file) {
    unlink($file);
}

function pathUrlDecode($id, $url)
{
    static $urlCache = [];

    if (isset($urlCache[$id])) {
        return $urlCache[$id];
    }

    $originUrl = $url;

    if (!is_file($url) && substr($url, 0, 17) == 'file://localhost/') {
        $url = substr($url, 17);
    }
    if (!is_file($url) && preg_match('#%[a-f0-9]{2}#is', $url)) {
        $url = urldecode($url);
    }

    # 转换WSL路径:C:/xxx -> /mnt/c/xxx
    if (!is_file($url) && preg_match('#^([a-z]):(.+)$#is', $url, $opt) && is_file(WSLPATH)) {
        $url = system(WSLPATH.' '.escapeshellarg($url));
    }

    if (!is_file($url)) {
        die("无法解析媒体文件URL,请手动修改XML中的文件路径。\n有问题的URL:$originUrl\n解析后:$url\n");
    }

    $urlCache[$id] = $url;
    return $url;
}
回复列表(1)
添加新回复
回复需要登录