基于HTML5实现跨Chrome/Firefox/Edge视频录制的兼容问题求助
我之前也遇到过类似的Edge下MediaRecorder无法正常触发数据回调的问题,核心原因是不同浏览器对MediaRecorder的MIME类型支持差异较大,尤其是Edge需要明确指定兼容的编码格式,而不是依赖默认配置。下面给你几个可行的解决思路:
一、修复MediaRecorder的Edge兼容性问题
首先,你需要先检测当前浏览器支持的MediaRecorder格式,避免使用不兼容的编码。Edge对video/webm格式的支持需要明确指定codecs参数,比如vp8或vp9。
步骤1:检测支持的MIME类型
先写一个工具函数来获取浏览器支持的录制格式:
function getSupportedMimeType() { const possibleTypes = [ 'video/webm;codecs=vp9', 'video/webm;codecs=vp8', 'video/webm', 'video/mp4' // 部分新版本Edge/Chrome支持 ]; return possibleTypes.find(type => MediaRecorder.isTypeSupported(type)); }
步骤2:正确初始化MediaRecorder
用检测到的MIME类型初始化Recorder,同时注意调用start()时指定时间片(比如100ms),确保ondataavailable能稳定触发:
navigator.mediaDevices.getUserMedia({ audio: true, video: true }) .then(mediaStream => { const video = document.querySelector('video'); video.srcObject = mediaStream; // 推荐用srcObject替代createObjectURL,更现代 const supportedMimeType = getSupportedMimeType(); if (!supportedMimeType) { alert('当前浏览器不支持MediaRecorder录制'); return; } const recorder = new MediaRecorder(mediaStream, { mimeType: supportedMimeType }); const chunks = []; recorder.ondataavailable = (d) => { if (d.data.size > 0) { // 过滤空数据块 chunks.push(d.data); } }; recorder.onstop = () => { // 合并所有数据块,生成Blob const blob = new Blob(chunks, { type: supportedMimeType }); // 这里可以把Blob上传到服务器,比如用Fetch API uploadBlobToServer(blob); }; // 开始录制,每100ms触发一次ondataavailable recorder.start(100); // 示例:录制10秒后停止 setTimeout(() => recorder.stop(), 10000); }) .catch(err => console.error('获取媒体流失败:', err)); function uploadBlobToServer(blob) { const formData = new FormData(); formData.append('video', blob, 'recorded-video.webm'); fetch('/upload-video', { method: 'POST', body: formData }) .then(res => res.json()) .then(data => console.log('上传成功:', data)) .catch(err => console.error('上传失败:', err)); }
注意:Edge旧版本可能对音频编码也有要求,如果你同时录制音频,确保MIME类型包含音频编码,比如video/webm;codecs=vp8,opus,可以在possibleTypes里加上这类组合。
二、优化版Canvas帧捕获方案
如果你还是遇到Edge的兼容性问题,Canvas逐帧捕获是一个稳定的备选方案,这里给你优化后的实现,兼顾性能和兼容性:
navigator.mediaDevices.getUserMedia({ audio: true, video: true }) .then(mediaStream => { const video = document.querySelector('video'); video.srcObject = mediaStream; const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); const chunks = []; const fps = 30; // 设置录制帧率 const frameInterval = 1000 / fps; // 单独处理音频(用MediaRecorder捕获音频流) const audioStream = mediaStream.getAudioTracks()[0]; const audioRecorder = new MediaRecorder(new MediaStream([audioStream]), { mimeType: 'audio/webm' }); const audioChunks = []; audioRecorder.ondataavailable = (d) => d.data.size && audioChunks.push(d.data); audioRecorder.start(); let lastFrameTime = 0; function captureFrame(timestamp) { if (timestamp - lastFrameTime < frameInterval) { requestAnimationFrame(captureFrame); return; } lastFrameTime = timestamp; // 同步Canvas尺寸和视频尺寸 canvas.width = video.videoWidth; canvas.height = video.videoHeight; ctx.drawImage(video, 0, 0); // 将Canvas内容转为Blob(用webp格式减小体积) canvas.toBlob(blob => { if (blob) chunks.push(blob); }, 'image/webp', 0.8); requestAnimationFrame(captureFrame); } // 开始捕获帧 requestAnimationFrame(captureFrame); // 示例:10秒后停止并合并视频+音频 setTimeout(() => { audioRecorder.stop(); // 这里可以用第三方库合并视频帧和音频,然后上传 // 或者直接上传帧数据让服务器处理 console.log('捕获完成,共', chunks.length, '帧'); }, 10000); }) .catch(err => console.error('获取媒体流失败:', err));
这个方案的好处是兼容性极强,但缺点是需要处理音频和视频的同步,以及服务器端或前端的帧合并工作,如果你不需要高质量音频,也可以只捕获视频帧。
三、关于第三方库的说明
你提到的MediaStreamRecorder在Edge中失效,大概率也是因为MIME类型的问题,如果你想继续用它,同样需要手动指定兼容的编码格式,不过原生MediaRecorder已经足够覆盖主流浏览器,推荐优先用原生方案。
内容的提问来源于stack exchange,提问作者CuriousMind

