使用AudioContext获取音频块采样率时解码失败的原因及解决方法
问题分析与修复方案
问题原因
MediaRecorder.start(1000)触发的ondataavailable回调返回的单个音频块不是完整的WebM文件:WebM格式需要包含文件头、编码元数据等完整结构,只有第一个chunk可能携带基础容器信息,后续的chunk只是纯音频数据片段,缺少解码必需的容器结构,导致decodeAudioData无法解析。- 每次仅用当前的
e.data创建Blob,没有累积之前的音频数据,无法形成可解码的完整媒体文件结构。
修复方案
方案一:直接从媒体轨道获取采样率(推荐,无需解码)
录制过程中音频采样率是固定的,不需要每次解码chunk,直接从MediaStream的AudioTrack里获取采样率即可,性能更高:
async function getSampleRate() { try { const constraints = { audio: { channelCount: 1 } }; const stream = await navigator.mediaDevices.getUserMedia(constraints); // 从音频轨道获取采样率 const audioTrack = stream.getAudioTracks()[0]; const sampleRate = audioTrack.getSettings().sampleRate; console.log('固定采样率:', sampleRate); mediaRecorder = new MediaRecorder(stream, { mimeType: 'audio/webm' }); mediaRecorder.start(1000); // 后续如果需要处理音频数据,依然可以监听ondataavailable,但不需要再解码拿采样率 mediaRecorder.ondataavailable = (e) => { console.log('收到音频块,采样率:', sampleRate); }; mediaRecorder.onstop = () => { console.log('Recording stopped'); }; } catch (error) { console.error('Error accessing microphone:', error); } } getSampleRate()
方案二:累积音频块后解码(如果需要验证每个阶段的采样率)
如果必须通过解码来确认采样率,需要累积所有chunk直到形成完整的可解码文件,或者至少保留文件头信息:
async function getSampleRate() { try { const constraints = { audio: { channelCount: 1 } }; const stream = await navigator.mediaDevices.getUserMedia(constraints); mediaRecorder = new MediaRecorder(stream, { mimeType: 'audio/webm' }); const audioContext = new(window.AudioContext || window.webkitAudioContext)(); let audioChunks = []; // 累积所有音频块 const handleDataAvailable = async(e) => { audioChunks.push(e.data); // 每次累积后尝试解码完整的Blob const audioBlob = new Blob(audioChunks, { type: 'audio/webm' }); const response = await fetch(URL.createObjectURL(audioBlob)); const audioArrayBuffer = await response.arrayBuffer(); try { const audioBuffer = await audioContext.decodeAudioData(audioArrayBuffer); const sampleRate = audioBuffer.sampleRate; console.log('当前采样率:', sampleRate); } catch (decodeError) { // 忽略中间chunk的解码错误,直到累积足够数据 console.log('当前块无法解码,继续累积'); } }; mediaRecorder.ondataavailable = handleDataAvailable; mediaRecorder.start(1000); mediaRecorder.onstop = () => { console.log('Recording stopped'); // 停止后可以解码完整的音频文件 const fullBlob = new Blob(audioChunks, { type: 'audio/webm' }); fetch(URL.createObjectURL(fullBlob)) .then(res => res.arrayBuffer()) .then(buf => audioContext.decodeAudioData(buf)) .then(buffer => console.log('完整音频采样率:', buffer.sampleRate)); }; } catch (error) { console.error('Error accessing microphone:', error); } } getSampleRate()
内容的提问来源于stack exchange,提问作者seriously
相关产品推荐
相关产品推荐

