You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用PHP+FFmpeg为拼接的视频添加淡入淡出过渡效果?

在PHP中用FFmpeg实现带淡入淡出过渡的视频拼接

直接使用concat滤镜只能完成简单拼接,要添加视频间的淡入淡出过渡,需要通过**复杂滤镜(complex filter)**结合fade和overlay滤镜实现。以下是具体实现方案:

实现思路

  1. 预获取所有待拼接视频的时长,用于计算过渡时间点;
  2. 对相邻视频对,将前一个视频的末尾N秒添加淡出效果,后一个视频的开头N秒添加淡入效果;
  3. 通过overlay滤镜将淡入淡出的片段叠加,实现平滑过渡;
  4. 最后将所有正常片段和过渡片段拼接为完整视频。

代码实现

假设过渡时长设为1秒,可根据需求自行调整:

use FFMpeg\FFMpeg;

$ffmpeg = FFMpeg::create();
$transitionDuration = 1; // 过渡时长(单位:秒)
$files_aux = [/* 你的视频URL数组 */];
$newfilename = "/output.mp4"; // 输出文件路径

// 预加载所有视频,获取每个视频的时长
$videoDurations = [];
foreach ($files_aux as $file) {
    $video = $ffmpeg->open($file);
    $videoDurations[] = $video->getDuration();
}

// 构建复杂滤镜字符串
$filterSegments = [];
$totalVideos = count($files_aux);

foreach ($files_aux as $idx => $file) {
    $inputLabel = "v{$idx}";
    $currentDuration = $videoDurations[$idx];

    if ($totalVideos === 1) {
        // 只有单个视频,直接输出
        $filterSegments[] = "{$inputLabel},setpts=PTS-STARTPTS[v_final]";
        break;
    }

    if ($idx === 0) {
        // 第一个视频:保留主体片段 + 末尾淡出片段
        $mainEnd = $currentDuration - $transitionDuration;
        $filterSegments[] = "{$inputLabel}:trim=start=0:end={$mainEnd},setpts=PTS-STARTPTS[v0_main]";
        $filterSegments[] = "{$inputLabel}:trim=start={$mainEnd}:end={$currentDuration},setpts=PTS-STARTPTS,fade=out:st=0:d={$transitionDuration}[v0_fadeout]";
    } elseif ($idx === $totalVideos - 1) {
        // 最后一个视频:开头淡入片段 + 主体片段
        $filterSegments[] = "{$inputLabel}:trim=start=0:end={$transitionDuration},setpts=PTS-STARTPTS,fade=in:st=0:d={$transitionDuration}[v{$idx}_fadein]";
        $filterSegments[] = "{$inputLabel}:trim=start={$transitionDuration}:end={$currentDuration},setpts=PTS-STARTPTS[v{$idx}_main]";
        
        // 拼接前一个视频的淡出和当前视频的淡入,再组合所有片段
        $filterSegments[] = "[v" . ($idx-1) . "_fadeout][v{$idx}_fadein]overlay[v_trans]";
        $filterSegments[] = "[v" . ($idx-1) . "_main][v_trans][v{$idx}_main]concat=n=3:v=1:a=0[v_final]";
    } else {
        // 中间视频:开头淡入 + 主体 + 末尾淡出
        $filterSegments[] = "{$inputLabel}:trim=start=0:end={$transitionDuration},setpts=PTS-STARTPTS,fade=in:st=0:d={$transitionDuration}[v{$idx}_fadein]";
        $mainStart = $transitionDuration;
        $mainEnd = $currentDuration - $transitionDuration;
        $filterSegments[] = "{$inputLabel}:trim=start={$mainStart}:end={$mainEnd},setpts=PTS-STARTPTS[v{$idx}_main]";
        $filterSegments[] = "{$inputLabel}:trim=start={$mainEnd}:end={$currentDuration},setpts=PTS-STARTPTS,fade=out:st=0:d={$transitionDuration}[v{$idx}_fadeout]";
        
        // 拼接前一个过渡和当前淡入,再组合片段
        $filterSegments[] = "[v" . ($idx-1) . "_fadeout][v{$idx}_fadein]overlay[v{$idx}_trans]";
        $filterSegments[] = "[v" . ($idx-1) . "_combined][v{$idx}_trans][v{$idx}_main]concat=n=3:v=1:a=0[v{$idx}_combined]";
        
        // 第二个视频处理后,初始化组合片段基准
        if ($idx === 1) {
            $filterSegments[] = "[v0_main][v1_trans][v1_main]concat=n=3:v=1:a=0[v1_combined]";
        }
    }
}

// 合并所有滤镜段
$filterString = implode(';', $filterSegments);

// 构建FFmpeg命令并执行
$commandArgs = [];
// 添加所有输入文件
foreach ($files_aux as $file) {
    $commandArgs[] = "-i";
    $commandArgs[] = $file;
}
// 添加滤镜和输出参数
$commandArgs = array_merge($commandArgs, [
    '-filter_complex', $filterString,
    '-map', '[v_final]',
    '-c:v', 'libx264',
    '-crf', '23',
    '-preset', 'medium',
    __DIR__ . $newfilename
]);

$ffmpeg->getFFmpegDriver()->execute($commandArgs);

注意事项

  • 分辨率统一:所有待拼接视频的分辨率必须一致,否则overlay滤镜会导致画面错位。若分辨率不同,需在滤镜中添加scale命令统一尺寸;
  • 音频处理:上述代码仅处理视频,若需要同步处理音频的淡入淡出,可参照视频逻辑,对音频流使用afade滤镜;
  • 性能优化:复杂滤镜会增加FFmpeg计算量,可根据服务器性能调整-preset参数(如fast或ultrafast)加快处理速度;
  • 时长校验:确保每个视频时长大于过渡时长,避免出现参数错误。

内容的提问来源于stack exchange,提问作者Corboss

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 17:22:17