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

如何用Node.js+FFmpeg剪切视频多片段并合并为单个文件

如何用fluent-ffmpeg剪切多个视频片段并合并为单个文件

你之前尝试多次调用setStartTime和setDuration却发现只有最后一次设置生效,这是因为这些方法是针对当前输入流的,重复调用会直接覆盖之前的参数,没办法生成多个独立的片段。要实现多片段剪切合并,有两种靠谱的方案,我给你详细说说:

方案一:直接用FFmpeg的concat滤镜(无临时文件,推荐)

这种方法不需要生成中间文件,直接通过FFmpeg的滤镜把多个剪切后的流合并在一起,效率更高。核心思路是为每个片段单独添加一个视频输入,每个输入设置对应的起始时间和时长,再用concat滤镜把这些流拼接起来。

const ffmpeg = require('fluent-ffmpeg');

// 先定义好要剪切的所有片段
const videoClips = [
  { startTime: '00:00:03', duration: '02' },
  { startTime: '00:00:15', duration: '05' }
];

// 创建FFmpeg命令实例
const mergeCommand = ffmpeg();

// 为每个片段添加输入,并设置对应的时间参数
videoClips.forEach(clip => {
  mergeCommand
    .input('./videos/placeholder-video.mp4')
    .setStartTime(clip.startTime)
    .setDuration(clip.duration);
});

// 使用复杂滤镜合并所有片段
mergeCommand
  .complexFilter([
    // 处理每个流的时间戳(避免播放时卡顿),然后合并
    ...videoClips.map((_, idx) => {
      const videoPart = idx === 0 ? `[${idx}:v]` : `[${idx}:v]setpts=PTS-STARTPTS,`;
      const audioPart = idx === 0 ? `[${idx}:a]` : `[${idx}:a]asetpts=PTS-STARTPTS,`;
      return videoPart + audioPart;
    }).join(''),
    // concat参数说明:n=片段数量,v=1/a=1表示保留1条视频和1条音频流
    `concat=n=${videoClips.length}:v=1:a=1[mergedVideo][mergedAudio]`
  ])
  // 指定输出的视频和音频流
  .outputOptions('-map', '[mergedVideo]')
  .outputOptions('-map', '[mergedAudio]')
  .output('./videos/combined.mp4')
  .on('start', cmdLine => console.log('执行命令:', cmdLine))
  .on('end', () => console.log('片段合并完成!'))
  .on('error', err => console.error('处理出错:', err))
  .run();

方案二:先导出临时片段,再合并(直观易理解)

如果你觉得滤镜的写法有点复杂,可以先把每个片段单独导出成临时文件,再用mergeToFile方法把它们合并起来。这种方式逻辑更直观,适合新手调试。

const ffmpeg = require('fluent-ffmpeg');
const fs = require('fs');

// 定义片段信息,包含临时文件路径
const videoClips = [
  { startTime: '00:00:03', duration: '02', tempPath: './videos/temp_clip1.mp4' },
  { startTime: '00:00:15', duration: '05', tempPath: './videos/temp_clip2.mp4' }
];

// 第一步:批量生成所有临时片段
const generateTempClips = async () => {
  const clipPromises = videoClips.map(clip => {
    return new Promise((resolve, reject) => {
      ffmpeg('./videos/placeholder-video.mp4')
        .setStartTime(clip.startTime)
        .setDuration(clip.duration)
        .output(clip.tempPath)
        .on('end', resolve)
        .on('error', reject)
        .run();
    });
  });

  await Promise.all(clipPromises);
  console.log('所有临时片段已生成');
};

// 第二步:合并临时文件
const mergeTempClips = () => {
  ffmpeg()
    // mergeToFile(输出路径, 临时列表文件存放目录)
    .mergeToFile('./videos/combined.mp4', './videos/temp')
    // 传入所有临时文件路径
    .input(videoClips.map(c => c.tempPath).join('|'))
    .on('start', cmdLine => console.log('开始合并:', cmdLine))
    .on('end', () => {
      console.log('合并完成!');
      // 可选:合并后删除临时文件
      videoClips.forEach(clip => fs.unlinkSync(clip.tempPath));
    })
    .on('error', err => console.error('合并出错:', err))
    .run();
};

// 执行整个流程
generateTempClips()
  .then(mergeTempClips)
  .catch(err => console.error('生成片段失败:', err));

两种方案各有优劣:方案一不需要额外磁盘空间,速度更快;方案二逻辑清晰,容易排查问题,你可以根据自己的需求选择。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 04:00:59