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

是否可以异步读取MediaStream以对接AWS Transcribe Streaming SDK?

错误根因

原生MediaStream对象未实现异步迭代器接口,因此无法直接通过for await...of遍历获取音频块,这是报错的直接原因。你可以通过MediaRecorder或Web Audio API封装实现异步迭代,适配AWS Transcribe Streaming的AudioStream要求。


方案1:基于MediaRecorder封装

适合使用编码后音频格式的场景:

navigator.mediaDevices.getUserMedia({ audio: true }).then(async stream => {
  // 封装MediaStream为异步迭代器
  const AudioStream = (async function* () {
    // 编码格式需要和后续传给Transcribe的MediaEncoding参数匹配
    const mediaRecorder = new MediaRecorder(stream, { mimeType: 'audio/webm; codecs=opus' });
    const chunkQueue = [];
    let resolveChunkPromise = null;

    mediaRecorder.ondataavailable = (e) => {
      if (e.data.size > 0) {
        if (resolveChunkPromise) {
          resolveChunkPromise(e.data);
          resolveChunkPromise = null;
        } else {
          chunkQueue.push(e.data);
        }
      }
    };

    mediaRecorder.onstop = () => {
      if (resolveChunkPromise) {
        resolveChunkPromise(null);
        resolveChunkPromise = null;
      }
    };

    // 每100ms返回一个音频块,可根据需要调整间隔
    mediaRecorder.start(100);

    try {
      while (mediaRecorder.state !== 'inactive') {
        let chunk;
        if (chunkQueue.length > 0) {
          chunk = chunkQueue.shift();
        } else {
          chunk = await new Promise(resolve => {
            resolveChunkPromise = resolve;
          });
        }
        if (!chunk) break;
        // 转成Uint8Array适配Transcribe参数要求
        const buffer = await chunk.arrayBuffer();
        yield { AudioEvent: { AudioChunk: new Uint8Array(buffer) } };
      }
    } finally {
      if (mediaRecorder.state !== 'inactive') {
        mediaRecorder.stop();
      }
      stream.getTracks().forEach(track => track.stop());
    }
  })();

  // 后续将AudioStream传入TranscribeStreamingClient的调用参数即可
});

方案2:基于Web Audio API获取原始PCM音频

适合需要使用PCM格式对接Transcribe的场景:

navigator.mediaDevices.getUserMedia({ audio: true }).then(async stream => {
  const AudioStream = (async function* () {
    // 采样率匹配Transcribe配置,常用16000/8000
    const audioContext = new AudioContext({ sampleRate: 16000 });
    const source = audioContext.createMediaStreamSource(stream);
    // 缓冲区大小、输入输出声道数,按需调整
    const processor = audioContext.createScriptProcessor(4096, 1, 1);
    const chunkQueue = [];
    let resolveChunkPromise = null;

    processor.onaudioprocess = (e) => {
      // 获取单声道PCM数据
      const inputData = e.inputBuffer.getChannelData(0);
      // 转成16位PCM,符合Transcribe对PCM格式的要求
      const int16Array = new Int16Array(inputData.length);
      for (let i = 0; i < inputData.length; i++) {
        const s = Math.max(-1, Math.min(1, inputData[i]));
        int16Array[i] = s < 0 ? s * 0x8000 : s * 0x7FFF;
      }
      const uint8Chunk = new Uint8Array(int16Array.buffer);
      if (resolveChunkPromise) {
        resolveChunkPromise(uint8Chunk);
        resolveChunkPromise = null;
      } else {
        chunkQueue.push(uint8Chunk);
      }
    };

    source.connect(processor);
    processor.connect(audioContext.destination);

    try {
      while (true) {
        let chunk;
        if (chunkQueue.length > 0) {
          chunk = chunkQueue.shift();
        } else {
          chunk = await new Promise(resolve => {
            resolveChunkPromise = resolve;
          });
        }
        yield { AudioEvent: { AudioChunk: chunk } };
      }
    } finally {
      processor.disconnect();
      source.disconnect();
      audioContext.close();
      stream.getTracks().forEach(track => track.stop());
    }
  })();
});

注意事项

  • 音频采样率、声道数、编码格式必须和你调用Transcribe时传入的配置完全一致,否则会出现识别乱码或无结果的问题
  • ScriptProcessorNode已被标记为废弃,生产环境可以替换为AudioWorklet实现,核心逻辑一致
  • 不需要继续识别时需要主动终止异步迭代,自动释放音频设备资源

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.10 04:51:04