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

如何用FFmpeg合并MediaRecorder API生成的视频分段?

问题描述

我通过MediaRecorder API从摄像头录制直播视频,将视频分割为3秒的分段,前端代码如下:

startButton.addEventListener('click', () => {
navigator.mediaDevices.getUserMedia({
    video: {
        width: 1280,
        height: 720,
        frameRate: { ideal: 30, max: 30 }
    }
})
    .then(stream => {
        video.srcObject = stream;
        mediaRecorder = new MediaRecorder(stream, { mimeType: 'video/webm' });
        mediaRecorder.ondataavailable = async (event) => {
            const blob = new Blob([event.data], { type: 'video/mp4' });
            const formData = new FormData();
            formData.append('segment', blob, `segment${segmentNumber}.mp4`);

            // 上传到Node.js服务器
            fetch('http://localhost:3000/upload', {
                method: 'POST',
                body: formData
            })
                .then((response) => response.text())
                .then((result) => {
                    console.log('Upload result:', result);
                })
                .catch((error) => {
                    console.error('Error uploading video segment:', error);
                });
            // 上传到MySQL
            fetch('upload.php', {
                method: 'POST',
                body: formData
            })
                .then(response => response.text())
                .then(result => {
                    console.log('Upload result to MYSQL:', result);
                })
                .catch(error => {
                    console.error('Error uploading video segment to MYSQL:', error);
                });
            segmentNumber++;
        };

        mediaRecorder.start(3000);
    })
    .catch(error => {
        console.error('Error accessing camera:', error);
    });
});

目前仅第一段分段可正常播放,但录制结束后用FFmpeg合并所有分段时执行命令:

ffmpeg -i segment1.mp4 -i segment2.mp4 -i segment3.mp4  out.mp4

出现如下错误:

ffmpeg version N-110223-gb18a9c2971-20230410 Copyright (c) 2000-2023 the FFmpeg developers
  built with gcc 12.2.0 (crosstool-NG 1.25.0.152_89671bf)
  configuration: --prefix=/ffbuild/prefix --pkg-config-flags=--static --pkg-config=pkg-config --cross-prefix=x86_64-w64-mingw32- --arch=x86_64 --target-os=mingw32 --enable-gpl --enable-version3 --disable-debug --disable-w32threads --enable-pthreads --enable-iconv --enable-libxml2 --enable-zlib --enable-libfreetype --enable-libfribidi --enable-gmp --enable-lzma --enable-fontconfig --enable-libvorbis --enable-opencl --disable-libpulse --enable-libvmaf --disable-libxcb --disable-xlib --enable-amf --enable-libaom --enable-libaribb24 --enable-avisynth --enable-chromaprint --enable-libdav1d --enable-libdavs2 --disable-libfdk-aac --enable-ffnvcodec --enable-cuda-llvm --enable-frei0r --enable-libgme --enable-libkvazaar --enable-libass --enable-libbluray --enable-libjxl --enable-libmp3lame --enable-libopus --enable-librist --enable-libssh --enable-libtheora --enable-libvpx --enable-libwebp --enable-lv2 --disable-libmfx --enable-libvpl --enable-openal --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenh264 --enable-libopenjpeg --enable-libopenmpt --enable-librav1e --enable-librubberband --enable-schannel --enable-sdl2 --enable-libsoxr --enable-libsrt --enable-libsvtav1 --enable-libtwolame --enable-libuavs3d --disable-libdrm --disable-vaapi --enable-libvidstab --enable-vulkan --enable-libshaderc --enable-libplacebo --enable-libx264 --enable-libx265 --enable-libxavs2 --enable-libxvid --enable-libzimg --enable-libzvbi --extra-cflags=-DLIBTWOLAME_STATIC --extra-cxxflags= --extra-ldflags=-pthread --extra-ldexeflags= --extra-libs=-lgomp --extra-version=20230410
  libavutil      58.  6.100 / 58.  6.100
  libavcodec     60.  9.100 / 60.  9.100
  libavformat    60.  4.101 / 60.  4.101
  libavdevice    60.  2.100 / 60.  2.100
  libavfilter     9.  5.100 /  9.  5.100
  libswscale      7.  2.100 /  7.  2.100
  libswresample   4. 11.100 /  4. 11.100
  libpostproc    57.  2.100 / 57.  2.100
Input #0, matroska,webm, from 'segment1.mp4':
  Metadata:
    encoder         : Chrome
  Duration: N/A, start: 0.000000, bitrate: N/A
  Stream #0:0(eng): Video: h264 (Constrained Baseline), yuv420p(progressive), 1280x720 [SAR 1:1 DAR 16:9], 30.30 fps, 30 tbr, 1k tbn (default)
[mov,mp4,m4a,3gp,3g2,mj2 @ 000001d93cf25fc0] Format mov,mp4,m4a,3gp,3g2,mj2 detected only with low score of 1, misdetection possible!
[mov,mp4,m4a,3gp,3g2,mj2 @ 000001d93cf25fc0] moov atom not found
segment2.mp4: Invalid data found when processing input
解决思路与方案

核心问题分析

报错根源是容器格式不匹配:

  • MediaRecorder初始化指定的是video/webm格式,但前端将Blob强制标记为video/mp4并命名为.mp4后缀,导致文件实际是WebM内容却被伪装成MP4。
  • 第一段能播放是因为浏览器兼容解析,但后续分段缺少MP4必需的moov原子(索引元数据),FFmpeg无法识别这种伪MP4文件。

分步解决

1. 前端修正:统一容器格式

方案A:保持WebM格式(兼容性更好)

修改前端代码,让Blob类型、文件名后缀与MediaRecorder输出格式一致:

mediaRecorder = new MediaRecorder(stream, { mimeType: 'video/webm' });
mediaRecorder.ondataavailable = async (event) => {
    // 用WebM格式打包Blob,文件名后缀改为webm
    const blob = new Blob([event.data], { type: 'video/webm' });
    const formData = new FormData();
    formData.append('segment', blob, `segment${segmentNumber}.webm`);
    // 后续上传逻辑不变
};

方案B:直接录制MP4(需浏览器支持)

先检测浏览器是否支持MP4编码的MediaRecorder,再初始化:

let recorderMimeType = 'video/webm';
if (MediaRecorder.isTypeSupported('video/mp4')) {
    recorderMimeType = 'video/mp4';
}
mediaRecorder = new MediaRecorder(stream, { mimeType: recorderMimeType });

// Blob类型和文件名对应匹配
const blob = new Blob([event.data], { type: recorderMimeType });
formData.append('segment', blob, `segment${segmentNumber}.${recorderMimeType.split('/')[1]}`);

2. FFmpeg合并修正

针对WebM分段合并

WebM是流媒体格式,可直接用concat协议高效拼接:

  1. 创建segments.txt文件,内容为:
file 'segment1.webm'
file 'segment2.webm'
file 'segment3.webm'
  1. 执行FFmpeg命令:
# 直接拼接,无转码
ffmpeg -f concat -safe 0 -i segments.txt -c copy out.webm

# 若需要转成MP4
ffmpeg -f concat -safe 0 -i segments.txt -c:v libx264 -c:a aac out.mp4

针对MP4分段合并

MP4分段需先修复每个分段的元数据,再合并:

  1. 修复单个分段的moov原子:
ffmpeg -i segment1.mp4 -c copy fix_segment1.mp4
ffmpeg -i segment2.mp4 -c copy fix_segment2.mp4
  1. 同样创建segments.txt(指向修复后的文件),执行合并命令:
ffmpeg -f concat -safe 0 -i segments.txt -c copy out.mp4

3. 额外优化

  • 录制结束时必须调用mediaRecorder.stop(),触发最后一段的ondataavailable事件,避免丢帧。
  • 后端接收分段时,可校验文件实际格式,避免后续合并出错。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 03:17:11