Firefox无法将录制视频保存为MP4格式,Safari/Edge正常求助
Firefox下MediaRecorder录制视频无法保存为MP4的解决方案
问题根源
你的代码中虽然在创建Blob时指定了video/mp4类型,但Firefox的MediaRecorder并不支持直接录制H.264编码的MP4格式——它默认只能输出WebM(VP8/VP9视频+Opus音频)。你看到的“MP4文件”只是强行修改了后缀名,文件编码本质还是WebM,所以实际下载后仍是WebM格式。
而Safari和Edge支持H.264编码,因此MediaRecorder可以直接生成符合MP4标准的文件,无需转码。
解决方案
1. 动态适配浏览器支持的录制格式(无转码)
先检测浏览器支持的媒体格式,让MediaRecorder使用最优的可用格式,同时匹配正确的文件后缀:
// 优先检测支持的MP4格式,降级到WebM const getSupportedRecorderConfig = () => { const candidateFormats = [ { mimeType: 'video/mp4;codecs=avc1.42E01E,mp4a.40.2' }, // H.264 + AAC { mimeType: 'video/webm;codecs=vp9,opus' }, // VP9 + Opus { mimeType: 'video/webm;codecs=vp8,opus' } // VP8 + Opus ]; return candidateFormats.find(config => MediaRecorder.isTypeSupported(config.mimeType)) || {}; }; function startRecording() { clearRecordedData(); navigator.mediaDevices.getUserMedia({ video: true, audio: true }) .then((stream) => { const recorderOptions = getSupportedRecorderConfig(); mediaRecorder = new MediaRecorder(stream, recorderOptions); const liveCamera = document.getElementById('liveCamera'); liveCamera.srcObject = stream; liveCamera.muted = true; mediaRecorder.ondataavailable = (event) => { if (event.data.size > 0) { recordedChunks.push(event.data); } }; mediaRecorder.onstop = () => { // 使用MediaRecorder实际的MIME类型创建Blob,而非硬写video/mp4 recordedBlob = new Blob(recordedChunks, { type: mediaRecorder.mimeType }); recordedVideo.src = URL.createObjectURL(recordedBlob); enableButtons(['startRecord', 'saveRecord', 'playRecord']); disableButtons(['stopRecord']); }; mediaRecorder.start(); recordedTime = 0; updateRecordedTime(); disableButtons(['startRecord']); enableButtons(['stopRecord']); }) .catch((error) => { console.error('getUserMedia error:', error); }); } function saveRecording() { if (recordedBlob) { const url = URL.createObjectURL(recordedBlob); const a = document.createElement('a'); a.href = url; const urlParams = new URLSearchParams(window.location.search); const sourceValue = urlParams.get('source'); // 根据实际MIME类型设置文件后缀 const fileExtension = mediaRecorder.mimeType.includes('mp4') ? '.mp4' : '.webm'; a.download = sourceValue + fileExtension; document.body.appendChild(a); a.click(); document.body.removeChild(a); } }
2. 客户端转码为MP4(强制输出MP4)
如果业务必须要求所有浏览器都输出MP4格式,可以使用ffmpeg.wasm在客户端将WebM转码为MP4:
步骤1:安装依赖
npm install @ffmpeg/ffmpeg @ffmpeg/core
步骤2:修改录制停止后的逻辑
import { createFFmpeg, fetchFile } from '@ffmpeg/ffmpeg'; const ffmpeg = createFFmpeg({ log: false }); // 转码WebM到MP4的工具函数 async function transcodeWebMToMP4(webmBlob) { if (!ffmpeg.isLoaded()) { await ffmpeg.load(); } // 将Blob写入ffmpeg虚拟文件系统 ffmpeg.FS('writeFile', 'input.webm', await fetchFile(webmBlob)); // 执行转码命令:H.264视频 + AAC音频 await ffmpeg.run('-i', 'input.webm', '-c:v', 'libx264', '-preset', 'fast', '-c:a', 'aac', 'output.mp4'); // 读取转码后的文件 const mp4Data = ffmpeg.FS('readFile', 'output.mp4'); // 清理临时文件 ffmpeg.FS('unlink', 'input.webm'); ffmpeg.FS('unlink', 'output.mp4'); // 返回MP4格式的Blob return new Blob([mp4Data.buffer], { type: 'video/mp4' }); } // 修改startRecording中的onstop回调 mediaRecorder.onstop = async () => { let finalBlob = new Blob(recordedChunks, { type: mediaRecorder.mimeType }); // 如果录制的是WebM,转码为MP4 if (mediaRecorder.mimeType.includes('webm')) { try { finalBlob = await transcodeWebMToMP4(finalBlob); } catch (err) { console.error('转码失败:', err); alert('MP4转码失败,将保存原始WebM文件'); } } recordedBlob = finalBlob; recordedVideo.src = URL.createObjectURL(recordedBlob); enableButtons(['startRecord', 'saveRecord', 'playRecord']); disableButtons(['stopRecord']); }; // saveRecording函数可以保持原逻辑,直接使用.mp4后缀 function saveRecording() { if (recordedBlob) { const url = URL.createObjectURL(recordedBlob); const a = document.createElement('a'); a.href = url; const urlParams = new URLSearchParams(window.location.search); const sourceValue = urlParams.get('source'); a.download = sourceValue + '.mp4'; document.body.appendChild(a); a.click(); document.body.removeChild(a); } }
注意:客户端转码会占用大量CPU资源,大文件转码可能导致页面卡顿,建议添加加载提示,并在转码过程中禁用相关按钮。
内容的提问来源于stack exchange,提问作者Zq5437
相关产品推荐
相关产品推荐

