是否可以异步读取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
相关产品推荐
相关产品推荐

