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

如何通过事件实现JS音频播放器分段加载音频Blob

分段音频录制与事件驱动播放实现方案

需求说明

现有JavaScript代码可每0.5秒录制音频并生成音频Blob,录制停止后仅播放第一个Blob(data[0])。需要实现:音频播放器播放完当前Blob后自动触发事件,由事件处理器依次加载播放后续Blob(data[1]、data[2]等),要求采用事件驱动的分段请求机制,而非一次性传入所有Blob。

原代码:

navigator.mediaDevices.getUserMedia({audio:true})
.then(function onSuccess(stream) {
const recorder = new MediaRecorder(stream);

const data = [];
recorder.ondataavailable = (e) => {
data.push(e.data);
};
recorder.start(500); // willfire 'dataavailable ' event every 0,5 second

recorder.onstop = (e) => {
const audio = document.createElement('audio');
audio.src = window.URL.createObjectURL(new Blob( data[0] ));
}
setTimeout(() => {
rec.stop();
}, 5000);
})
.catch(function onError(error) {
console.log(error.message);
});

核心修改点与关键对象

  • ended事件:利用<audio>元素的ended事件作为播放完成的触发信号,这是实现事件驱动播放的核心
  • 播放索引管理:维护一个currentIndex变量,记录当前播放的Blob位置,每次播放结束后递增索引
  • 资源清理:每次切换音频时调用window.URL.revokeObjectURL释放上一段Blob的URL,避免内存泄漏

修改后的完整代码

navigator.mediaDevices.getUserMedia({ audio: true })
.then(function onSuccess(stream) {
  const recorder = new MediaRecorder(stream);
  const data = [];
  let currentIndex = 0;
  let audioPlayer;

  // 收集分段录制的音频Blob
  recorder.ondataavailable = (e) => {
    data.push(e.data);
  };

  // 录制停止后初始化播放器,启动第一段音频播放
  recorder.onstop = () => {
    if (data.length === 0) return;
    
    audioPlayer = document.createElement('audio');
    // 绑定播放结束事件,触发下一段播放逻辑
    audioPlayer.addEventListener('ended', handleAudioEnded);
    // 加载并播放第一段音频
    loadAndPlaySegment(currentIndex);
    // 将播放器添加到DOM(可选,方便用户控制或调试)
    document.body.appendChild(audioPlayer);
  };

  // 播放结束事件处理器:加载下一段音频
  function handleAudioEnded() {
    currentIndex++;
    if (currentIndex < data.length) {
      loadAndPlaySegment(currentIndex);
    } else {
      // 所有音频播放完毕,清理资源
      audioPlayer.removeEventListener('ended', handleAudioEnded);
      window.URL.revokeObjectURL(audioPlayer.src);
      document.body.removeChild(audioPlayer);
    }
  }

  // 加载指定索引的Blob并启动播放
  function loadAndPlaySegment(index) {
    // 释放上一段音频的URL资源
    if (audioPlayer.src) {
      window.URL.revokeObjectURL(audioPlayer.src);
    }
    // 创建当前段的Blob URL并设置给播放器
    const blob = new Blob([data[index]], { type: 'audio/webm' });
    audioPlayer.src = window.URL.createObjectURL(blob);
    // 启动播放,捕获可能的播放错误
    audioPlayer.play().catch(err => console.error('音频播放失败:', err));
  }

  // 启动分段录制,每500ms生成一段Blob
  recorder.start(500);
  // 5秒后停止录制,并释放媒体设备
  setTimeout(() => {
    recorder.stop();
    stream.getTracks().forEach(track => track.stop());
  }, 5000);
})
.catch(function onError(error) {
  console.log(error.message);
});

代码说明

  1. 录制阶段:保留原有的ondataavailable逻辑收集分段Blob,录制停止后初始化音频播放器并启动第一段播放
  2. 事件驱动播放:通过audioPlayer.ended事件触发下一段音频的加载,完全基于事件驱动,无需轮询或一次性处理所有Blob
  3. 资源管理:每次切换音频时释放旧URL资源,播放完毕后清理播放器和事件监听,避免内存泄漏
  4. 格式兼容性:显式指定Blob的MIME类型为audio/webm(MediaRecorder默认输出格式),确保播放器能正确解析音频

内容的提问来源于stack exchange,提问作者user19309573

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 11:30:59