JS调用MediaRecorder录制audio/webm音频下载得到空Blob问题
问题根因
你遇到的两个异常本质是同一个问题导致的:
MediaRecorder.stop()是异步API,调用该方法后不会同步把录制完成的音频分片全部写入recordedBlobs数组。所有录制数据都是通过ondataavailable事件回调异步推送的,你在Stop()函数里刚调用完mediaRecorder.stop()就立刻执行downloadVideo(),此时数据回调还没触发,recordedBlobs本来就是空数组,所以生成的Blob是空的、打印length结果为0。- 控制台里点开
recordedBlobs日志能看到有内容,是浏览器控制台的懒解析机制导致的:打印引用类型对象时,控制台不会立刻固化对象的快照,等你手动点开日志查看时,异步的ondataavailable回调已经执行完成,把数据push进了数组,所以你能看到数组里的内容,但这并不代表执行console.log的瞬间数组里有数据。
修复方案
不要在调用stop()后立刻执行下载,改为监听MediaRecorder的stop事件,等所有数据分片都接收完成、录制状态完全停止后再触发下载逻辑。同时补全初始化状态判断,避免用户在麦克风权限申请完成前点击按钮报错。
修正后的完整代码如下:
<script> let mediaRecorder; let recordedBlobs; // 标记录音器初始化状态,避免未初始化就点击按钮 let isRecorderReady = false; if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) { console.log("getUserMedia supported."); navigator.mediaDevices .getUserMedia({audio: true, video: false}) .then(function (stream) { mediaRecorder = new MediaRecorder(stream); // 统一绑定事件回调 mediaRecorder.ondataavailable = handleDataAvailable; mediaRecorder.onstop = () => { // 录制完全停止、所有数据接收完成后再触发下载 downloadVideo(); } isRecorderReady = true; }) .catch(function (err) { console.log("The following getUserMedia error occurred: " + err); }); } else { console.log("getUserMedia not supported on your browser!"); } function handleDataAvailable(event) { console.log('handleDataAvailable', event); if (event.data && event.data.size > 0) { recordedBlobs.push(event.data); } } function Play() { if (!isRecorderReady) { console.log("录音器还未初始化完成,请稍后再试"); return; } console.log("play"); recordedBlobs = []; mediaRecorder.start(); console.log(mediaRecorder.state); } function Stop() { if (!isRecorderReady || mediaRecorder.state !== "recording") { return; } console.log("stop"); mediaRecorder.stop(); } function downloadVideo() { console.log('recorded blob length:', recordedBlobs.length); var blob = new Blob(recordedBlobs, {type: "audio/webm; codecs=opus"}); console.log('new blob size:', blob.size); const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.style.display = 'none'; a.href = url; a.download = 'test.webm'; document.body.appendChild(a); a.click(); setTimeout(() => { document.body.removeChild(a); window.URL.revokeObjectURL(url); }, 100); } </script>
补充说明
如果需要录制时按时间片生成切片,可以在调用mediaRecorder.start()时传入时间片参数,比如mediaRecorder.start(1000)代表每1秒触发一次dataavailable事件推送1秒长度的音频分片,不影响最终下载逻辑。
内容的提问来源于stack exchange,提问作者Marlee
相关产品推荐
相关产品推荐

