如何用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
相关产品推荐
相关产品推荐

