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

如何使用ffmpeg-fluent npm包拼接不同扩展名的多个视频文件

报错原因

你遇到的ffmpeg报错是因为concat滤镜强制要求所有输入视频的分辨率、帧率、像素格式、音频采样率、声道数、编码规格完全一致,你拼接的是不同扩展名的视频,上述参数大概率存在差异,导致滤镜初始化失败。

需修复的代码问题

  • 全局初始化fluent_ffmpeg实例会导致多次请求复用同一个实例,产生不可预期的错误,需要将实例创建逻辑移入/merge路由处理函数内部
  • 缺少音视频参数统一预处理步骤,不同参数的视频无法直接用concat滤镜拼接
  • 代码中output_file变量未定义,执行到end回调时会额外报错

修复后代码

const express = require("express");
const multer = require("multer");
const fluent_ffmpeg = require("fluent-ffmpeg");
const app = express();
const fs = require("fs");
const path = require("path");
const ffmpeg = require('@ffmpeg-installer/ffmpeg').path;
const ffprobe = require('@ffprobe-installer/ffprobe').path;

// 全局只设置一次路径即可
fluent_ffmpeg.setFfmpegPath(ffmpeg);
fluent_ffmpeg.setFfprobePath(ffprobe);

const PORT = process.env.PORT || 3000;
const output_file = "output.mp4";

var dir = "public";
var subDirectory = "public/uploads";

app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(express.static(path.join(__dirname,"public")));

if (!fs.existsSync(dir)) {
  fs.mkdirSync(dir);
  fs.mkdirSync(subDirectory);
}

const storage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, "public/uploads");
  },
  filename: function (req, file, cb) {
    cb(
      null,
      file.fieldname + "-" + Date.now() + path.extname(file.originalname)
    );
  }
});

var upload = multer({ storage: storage });

app.post('/merge',upload.any(),(req,res) => {
    // 每次请求创建新的ffmpeg实例
    const mergedVideo = fluent_ffmpeg();
    let file_list = [];
    let filter_chains = [];
    let concat_inputs = "";
    
    req.files.forEach((element, index) => {
        file_list.push("./"+element.path);
        // 先统一每个输入的视频参数:1920x1080分辨率,yuv420p像素格式,25fps
        filter_chains.push(`[${index}:v]scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:(ow-iw)/2:(oh-ih)/2,setsar=1,fps=25,format=yuv420p[v${index}]`);
        // 统一每个输入的音频参数:44100Hz采样率,双声道
        filter_chains.push(`[${index}:a]aformat=sample_fmts=fltp:sample_rates=44100:channel_layouts=stereo[a${index}]`);
        concat_inputs += `[v${index}][a${index}]`;
    });
   
    // 拼接concat滤镜
    filter_chains.push(`${concat_inputs} concat=n=${file_list.length}:v=1:a=1 [outv] [outa]`);
   
    const output_option = ['-map [outv]','-map [outa]', '-c:v libx264', '-c:a aac'];
   
    let chainedInputs = file_list.reduce((result, inputItem) => result.addInput(inputItem), mergedVideo);
  
    chainedInputs
    .complexFilter(filter_chains)
    .outputOption(output_option)
    .on('error', function(err) {
      console.log("Error:",err.message);
      res.status(500).send("合并失败:" + err.message);
    })
    .on('start', function() {
      console.log('Merging...');
    })
    .on('progress', function(progress) {
      console.log('合并进度:', progress.percent ? `${progress.percent.toFixed(2)}%` : '计算中');
    })
    .on('end', function() {
      console.log(output_file + ' merged.');
      res.download(path.join(__dirname, output_file), (err) => {
        if (err) console.log("下载失败:", err);
      });
    })
    .save(output_file);
});

app.get("/", (req, res) => {
  res.sendFile(__dirname + "/index.html");
});

app.listen(PORT, () => {
  console.log(`App is listening on Port ${PORT}`);
});

可选优化方案

如果拼接的视频数量多,先统一转码再拼接的效率更高,你可以先把所有输入视频转成相同参数的中间ts文件,再用ffmpeg的concat demuxer直接拼接,不需要重新编码,速度更快。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 01:45:01