Chrome、Safari中从Blob获取录制音频时长异常问题求助
问题分析与解决方案
问题根源
- 编码格式不匹配:你指定Blob的MIME类型为
audio/mp3,但绝大多数浏览器的MediaRecorder并不原生支持MP3编码(Chrome默认输出WebM格式,Firefox默认输出Ogg格式)。强行指定错误的MIME类型会导致生成的Blob文件结构不合法,浏览器无法正确解析元数据,这就是Chrome返回Infinity、Safari加载失败的核心原因。 - 录制停止方式错误:你通过停止媒体流的Track来终止录制,而非调用
MediaRecorder.stop()。这种方式会导致MediaRecorder无法正确完成文件的收尾工作,生成的Blob是不完整的,缺少必要的元数据信息。 - 元数据监听逻辑不完善:仅依赖
loadedmetadata事件不足以覆盖所有场景,部分浏览器对于不完整或格式异常的音频不会触发该事件,且没有处理音频加载完成后的时长校验。
修复步骤
- 使用浏览器支持的原生编码格式:不要强行指定
audio/mp3,而是用MediaRecorder.isTypeSupported()检测并选择浏览器支持的格式,或者直接使用浏览器默认格式。 - 正确终止录制:调用
MediaRecorder.stop()来停止录制,让浏览器完成文件的收尾,生成完整的Blob。 - 完善音频时长获取逻辑:同时监听
loadedmetadata和durationchange事件,并且处理Infinity的情况,等待音频加载完成后再获取准确时长。
修改后的完整代码
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.js"></script> <button id="startAudioRecord" onClick="startAudioRecording()">Start Audio recording </button> <button id="endAudioRecorder" onClick="endAudioRecording()">End Recording</button> <div id="audioRec_div"><br>Here is the audio:<br><audio id="audioRec" ></audio></div> <script> var sc_audioStream, sc_audioRec; async function getAudioUrlDuration(src) { return new Promise((resolve, reject) => { const audio = new Audio(); audio.src = src; // 监听元数据加载和时长变化事件 const handleDuration = () => { if (audio.duration !== Infinity) { audio.removeEventListener('loadedmetadata', handleDuration); audio.removeEventListener('durationchange', handleDuration); resolve(audio.duration); } }; audio.addEventListener('loadedmetadata', handleDuration); audio.addEventListener('durationchange', handleDuration); // 监听加载错误 audio.addEventListener('error', (e) => { audio.removeEventListener('loadedmetadata', handleDuration); audio.removeEventListener('durationchange', handleDuration); alert(`加载音频元数据失败: ${e.message}`); reject(e); }); // 强制加载音频 audio.load(); }); } function getUserMedia_audio(){ navigator.mediaDevices.getUserMedia({ audio: true }) .then(stream => { sc_audioStream = stream; // 检测并使用浏览器支持的音频格式 let mimeType = 'audio/webm'; if (!MediaRecorder.isTypeSupported(mimeType)) { mimeType = 'audio/ogg'; } sc_audioRec = new MediaRecorder(stream, { mimeType }); let audioRec_arr = []; sc_audioRec.ondataavailable = e => { audioRec_arr.push(e.data); if (sc_audioRec.state === "inactive") { const blob = new Blob(audioRec_arr, { type: mimeType }); processBlob(blob); } } }) .catch((e) => { console.error(e); }); } function startAudioRecording() { sc_audioStream = null; sc_audioRec = null; getUserMedia_audio(); setTimeout(() => { if (sc_audioRec) { sc_audioRec.start(); alert('speak now to record, click end button to stop recording'); } }, 1500); } function endAudioRecording() { if (sc_audioRec && sc_audioRec.state !== 'inactive') { // 正确调用MediaRecorder.stop()终止录制 sc_audioRec.stop(); // 停止媒体流 sc_audioStream.getAudioTracks().forEach(track => { if (track.readyState === 'live') { track.stop(); } }); } } async function processBlob(blob){ const audio_recorded = $('#audioRec'); const blobUrl = URL.createObjectURL(blob); audio_recorded.attr("src", blobUrl); audio_recorded.prop("controls", true); try { const audioDuration = await getAudioUrlDuration(blobUrl); alert(`audioDuration=${audioDuration.toFixed(2)}`); } catch (e) { console.error('获取时长失败', e); } } </script>
额外说明
如果确实需要MP3格式的音频,可以在生成Blob后,使用Web Audio API或者第三方转码库将原生格式转码为MP3,但会增加代码复杂度。
内容的提问来源于stack exchange,提问作者user2818066
相关产品推荐
相关产品推荐

