Chrome中MediaRecorder分块录制无活动画面的WebM视频,序列播放时触发等待状态异常
Chrome中MediaRecorder分块录制无活动画面的WebM视频,序列播放时触发等待状态异常
这个问题我之前做屏幕录制工具时也碰到过,核心原因其实很明确:你每次新建MediaRecorder录制的独立WebM片段,时间戳都是从0开始计数的;再加上当画面静止时,MediaRecorder生成的视频帧间隔会大幅拉长,甚至导致片段实际时长不足2秒,这就使得MediaSource在拼接播放时,无法正确识别后续片段的时间衔接点,误以为数据缺失,从而触发waiting状态。
最简洁的修复方案:复用单个MediaRecorder
与其每次新建MediaRecorder,不如只创建一个实例,通过定时调用requestData()来导出2秒的片段。这样所有片段的时间戳是连续递增的,从录制启动时开始计算,完全避免了时间线断裂的问题,同时也能满足你每2秒生成一个独立WebM blob的需求。
修改后的完整代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Screen Record with Continuous Chunks</title> <style> .button { background: #e4e5e7; color: #030712; font-weight: bold; padding: 10px 20px; display: inline-block; cursor: pointer; } </style> </head> <body> <h2 class="button" onclick="doStart()">Start</h2> <h2 class="button" id="button_stop" onclick="doStop()">Stop</h2> <h4>Playback, once clicked on Stop</h4> <video id="video-player" autoplay controls style="max-width: 1000px"></video> <script> const eVideoPlayer = document.getElementById("video-player"); const mimeType = 'video/webm; codecs="vp8, opus"'; let videos = []; let iv; let stream1; let mediaRecorder; function doStart() { navigator.mediaDevices.getDisplayMedia({ video: true, audio: true }) .then(stream => { stream1 = stream; // 只初始化一个MediaRecorder实例 mediaRecorder = new MediaRecorder(stream, { mimeType: mimeType, videoBitsPerSecond: 60000000 }); mediaRecorder.ondataavailable = async (ev) => { console.log("data ready"); // 过滤空的blob,避免无效片段 if (ev.data.size > 0) { videos.push(ev.data); } }; mediaRecorder.start(); // 每隔2秒请求一次数据,生成一个2秒左右的片段 iv = setInterval(() => { mediaRecorder.requestData(); }, 2000); }); } async function doStop() { clearInterval(iv); // 停止前最后请求一次数据,确保收尾片段被导出 mediaRecorder.requestData(); mediaRecorder.stop(); stream1.getTracks().forEach(track => track.stop()); let sourceBuffer; const mediaSource = new MediaSource(); eVideoPlayer.onwaiting = () => console.log("waiting"); let counter = 1; mediaSource.onsourceopen = async () => { sourceBuffer = mediaSource.addSourceBuffer(mimeType); sourceBuffer.mode = "sequence"; sourceBuffer.onupdateend = async () => { if (eVideoPlayer.paused) { eVideoPlayer.play(); } if (counter < videos.length) { sourceBuffer.appendBuffer(await videos[counter++].arrayBuffer()); } }; sourceBuffer.appendBuffer(await videos[0].arrayBuffer()); }; eVideoPlayer.src = URL.createObjectURL(mediaSource); } </script> </body> </html>
关键修改点说明
- 复用MediaRecorder实例:不再每隔2秒新建录制器,所有片段共享同一个时间线,第一个片段是0-2秒,第二个是2-4秒,以此类推,完美解决时间戳衔接问题。
- 处理收尾片段:停止录制时额外调用一次
requestData(),确保最后一段不足2秒的内容也能被正常导出。 - 过滤空数据:在
ondataavailable回调中判断blob大小,避免添加无效的空片段。
如果你坚持要每次新建MediaRecorder(不推荐)
如果因为某些需求必须每次新建MediaRecorder,那你需要手动修改每个WebM片段的时间戳,让后续片段的起始时间等于前一个片段的结束时间。这涉及到解析WebM容器的内部结构,操作起来比较复杂,需要用到WebM格式的解析库,远不如上面的方案高效可靠。
内容来源于stack exchange
相关产品推荐
相关产品推荐

