如何用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协议高效拼接:
- 创建
segments.txt文件,内容为:
file 'segment1.webm' file 'segment2.webm' file 'segment3.webm'
- 执行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分段需先修复每个分段的元数据,再合并:
- 修复单个分段的
moov原子:
ffmpeg -i segment1.mp4 -c copy fix_segment1.mp4 ffmpeg -i segment2.mp4 -c copy fix_segment2.mp4
- 同样创建
segments.txt(指向修复后的文件),执行合并命令:
ffmpeg -f concat -safe 0 -i segments.txt -c copy out.mp4
3. 额外优化
- 录制结束时必须调用
mediaRecorder.stop(),触发最后一段的ondataavailable事件,避免丢帧。 - 后端接收分段时,可校验文件实际格式,避免后续合并出错。
内容的提问来源于stack exchange,提问作者Dario Cimmino
相关产品推荐
相关产品推荐

