Chrome中MediaRecorder调用stop触发两次dataavailable事件的处理问询
问题分析与解决方案
这是Chrome浏览器对MediaRecorder实现的已知兼容性问题,Firefox的实现更符合规范,不会出现该现象。问题根源在于使用timeslice参数时,Chrome在暂停录制后会缓存未导出的数据,调用stop()时会同时导出缓存数据和结束标记,导致两次dataavailable事件触发,且此时录制器状态已变为inactive。
1. 不移除timeslice参数的情况下规避两次触发
可以在调用pause()后主动调用requestData(),该方法会立即导出当前缓存的所有录制数据,后续调用stop()时就只会触发一次dataavailable事件(对应录制结束的最终块)。
修改后的示例代码:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Testing MediaRecorder</title> </head> <body> <script> const recordButton = document.createElement('button'); document.body.appendChild(recordButton); recordButton.innerHTML = 'start'; recordButton.onclick = async function () { const cameraStream = await navigator.mediaDevices.getUserMedia({'audio': true, 'video': true}); const mediaRecorder = new MediaRecorder(cameraStream); console.log('Starting media recording') mediaRecorder.ondataavailable = async (event) => { console.log(`Event size ${event.data.size}, recorder state: ${mediaRecorder.state}`); }; mediaRecorder.start(5000); setTimeout(() => { console.log('Pausing'); mediaRecorder.pause(); // 主动导出暂停时的缓存数据 mediaRecorder.requestData(); setTimeout(() => { console.log('Stopping'); mediaRecorder.stop(); }, 4000); }, 7000); }; </script> </body> </html>
2. 判断最终数据块的方法
如果无法规避两次触发的情况,可以通过状态标记结合stop事件来判断最终数据:
- 维护两个状态变量:
isStopping(标记即将停止录制)和isStopped(标记录制已完全停止)。 - 在
ondataavailable回调中收集所有Blob数据,直到stop事件触发后,统一处理收集到的所有数据,此时所有在停止流程中触发的dataavailable事件对应的Blob合并后即为完整的录制内容。
示例代码:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Testing MediaRecorder</title> </head> <body> <script> const recordButton = document.createElement('button'); document.body.appendChild(recordButton); recordButton.innerHTML = 'start'; recordButton.onclick = async function () { const cameraStream = await navigator.mediaDevices.getUserMedia({'audio': true, 'video': true}); const mediaRecorder = new MediaRecorder(cameraStream); const recordedBlobs = []; let isStopping = false; let isStopped = false; mediaRecorder.ondataavailable = (event) => { recordedBlobs.push(event.data); console.log(`Event size ${event.data.size}, recorder state: ${mediaRecorder.state}`); // 录制完全停止后处理最终数据 if (isStopped) { processFinalRecording(); } }; mediaRecorder.onstop = () => { isStopped = true; // 确保所有数据都被处理 processFinalRecording(); }; mediaRecorder.start(5000); setTimeout(() => { console.log('Pausing'); mediaRecorder.pause(); setTimeout(() => { console.log('Stopping'); isStopping = true; mediaRecorder.stop(); }, 4000); }, 7000); function processFinalRecording() { if (recordedBlobs.length === 0) return; // 合并所有Blob为最终录制文件 const finalBlob = new Blob(recordedBlobs, { type: mediaRecorder.mimeType }); console.log('最终录制数据已生成,大小:', finalBlob.size); // 重置状态与数据缓存 recordedBlobs.length = 0; isStopping = false; isStopped = false; } }; </script> </body> </html>
内容的提问来源于stack exchange,提问作者fblundun
相关产品推荐
相关产品推荐

