You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.07 11:19:32