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

Chrome、Safari中从Blob获取录制音频时长异常问题求助

问题分析与解决方案

问题根源

  • 编码格式不匹配:你指定Blob的MIME类型为audio/mp3,但绝大多数浏览器的MediaRecorder并不原生支持MP3编码(Chrome默认输出WebM格式,Firefox默认输出Ogg格式)。强行指定错误的MIME类型会导致生成的Blob文件结构不合法,浏览器无法正确解析元数据,这就是Chrome返回Infinity、Safari加载失败的核心原因。
  • 录制停止方式错误:你通过停止媒体流的Track来终止录制,而非调用MediaRecorder.stop()。这种方式会导致MediaRecorder无法正确完成文件的收尾工作,生成的Blob是不完整的,缺少必要的元数据信息。
  • 元数据监听逻辑不完善:仅依赖loadedmetadata事件不足以覆盖所有场景,部分浏览器对于不完整或格式异常的音频不会触发该事件,且没有处理音频加载完成后的时长校验。

修复步骤

  1. 使用浏览器支持的原生编码格式:不要强行指定audio/mp3,而是用MediaRecorder.isTypeSupported()检测并选择浏览器支持的格式,或者直接使用浏览器默认格式。
  2. 正确终止录制:调用MediaRecorder.stop()来停止录制,让浏览器完成文件的收尾,生成完整的Blob。
  3. 完善音频时长获取逻辑:同时监听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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 07:44:56