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

Safari搭配AirPods时MediaRecorder仅触发一次有效dataavailable事件的问题咨询

替代MediaRecorder获取getUserMedia原始音频数据的方案

针对你遇到的Safari搭配AirPods时MediaRecorder仅首次返回有效数据的兼容性问题,你可以通过Web Audio API直接获取原始音频数据,以下是两种可行方案:

方案一:使用ScriptProcessorNode(兼容性较好)

通过AudioContext创建ScriptProcessorNode,可实时获取音频流的原始PCM数据,该API虽标记为废弃,但目前仍在多数浏览器中兼容可用。

代码示例

document.querySelector("#micTest").addEventListener('click', async function(){
  if ( window.stream ) {
    this.innerText = "Test Microphone";
    window.audioContext?.close();
    window.stream.getAudioTracks().forEach(track => track.stop());
    window.stream = null;
    return;
  } else {
    this.innerText = "Stop Test";
  }

  if (!navigator.mediaDevices) throw new Error('unsupported mediaDevices');

  await navigator.mediaDevices.getUserMedia({ audio: true, video: false });
  const devices = await navigator.mediaDevices.enumerateDevices();
  const devStr = devices.map((d) => `${d.deviceId}:${d.label}`).join('\n');
  const deviceId = window.prompt(`choose device: ${devStr}`);
  if (!deviceId) return;

  const constraints = { audio: { deviceId }, video: false };
  const stream = await navigator.mediaDevices.getUserMedia(constraints);
  window.stream = stream;

  // 保留原音频播放功能
  const audio = document.createElement('audio');
  audio.controls = true;
  audio.autoplay = true;
  audio.srcObject = stream;

  // 初始化Web Audio API获取原始数据
  const audioContext = new (window.AudioContext || window.webkitAudioContext)();
  window.audioContext = audioContext;
  const source = audioContext.createMediaStreamSource(stream);
  
  // 创建处理器节点,缓冲区大小可选256/512/1024/2048/4096/8192/16384
  const processor = audioContext.createScriptProcessor(4096, 1, 1);
  
  source.connect(processor);
  processor.connect(audioContext.destination);

  // 实时获取原始PCM数据
  processor.onaudioprocess = function(e) {
    const inputData = e.inputBuffer.getChannelData(0);
    // inputData为Float32Array,取值范围[-1, 1],即原始音频采样数据
    console.log('原始音频数据长度:', inputData.length);
    // 可在此处执行数据存储、分析等操作
  };

  stream.oninactive = function() {
    console.log('Stream ended');
    audioContext.close();
  };
});

方案二:使用AudioWorklet(现代标准,推荐)

AudioWorklet是Web Audio API的现代替代方案,性能更优,适合长期维护的项目。

代码示例

  1. 创建单独的AudioWorklet处理器文件(如audio-processor.js):
class RawAudioProcessor extends AudioWorkletProcessor {
  process(inputs, outputs, parameters) {
    const inputData = inputs[0][0];
    if (inputData) {
      // 将原始数据发送到主线程
      this.port.postMessage(inputData);
    }
    return true;
  }
}

registerProcessor('raw-audio-processor', RawAudioProcessor);
  1. 主线程代码修改:
document.querySelector("#micTest").addEventListener('click', async function(){
  if ( window.stream ) {
    this.innerText = "Test Microphone";
    window.audioContext?.close();
    window.stream.getAudioTracks().forEach(track => track.stop());
    window.stream = null;
    return;
  } else {
    this.innerText = "Stop Test";
  }

  if (!navigator.mediaDevices) throw new Error('unsupported mediaDevices');

  await navigator.mediaDevices.getUserMedia({ audio: true, video: false });
  const devices = await navigator.mediaDevices.enumerateDevices();
  const devStr = devices.map((d) => `${d.deviceId}:${d.label}`).join('\n');
  const deviceId = window.prompt(`choose device: ${devStr}`);
  if (!deviceId) return;

  const constraints = { audio: { deviceId }, video: false };
  const stream = await navigator.mediaDevices.getUserMedia(constraints);
  window.stream = stream;

  // 保留原音频播放功能
  const audio = document.createElement('audio');
  audio.controls = true;
  audio.autoplay = true;
  audio.srcObject = stream;

  // 初始化AudioWorklet
  const audioContext = new (window.AudioContext || window.webkitAudioContext)();
  window.audioContext = audioContext;
  await audioContext.audioWorklet.addModule('audio-processor.js');
  
  const source = audioContext.createMediaStreamSource(stream);
  const processor = new AudioWorkletNode(audioContext, 'raw-audio-processor');
  
  source.connect(processor);
  processor.connect(audioContext.destination);

  // 接收来自Worklet的原始数据
  processor.port.onmessage = function(e) {
    const rawData = e.data; // Float32Array格式的PCM数据
    console.log('原始音频数据长度:', rawData.length);
    // 执行数据处理逻辑
  };

  stream.oninactive = function() {
    console.log('Stream ended');
    audioContext.close();
  };
});

注意事项

  • 两种方案获取的均为PCM原始采样数据(Float32Array,取值范围[-1, 1]),可根据需求自行编码为WAV等格式。
  • Safari环境下使用AudioWorklet需确保页面为HTTPS协议(本地localhost开发除外)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 19:20:20