AWS Transcribe流式服务报错:TypeError: e.$unknown is undefined求助
问题描述
尝试使用AWS Transcribe流式服务,录制音频并转换为服务支持格式传入时触发以下错误:
TypeError: e.$unknown is undefined
visit models_0.js:21
fn Aws_restJson1.js:343
asyncIterator SmithyMessageEncoderStream.js:10
domande_generiche.html:107:29
原代码
import { TranscribeStreamingClient, StartStreamTranscriptionCommand } from "https://cdn.jsdelivr.net/npm/@aws-sdk/client-transcribe-streaming@latest/+esm"; let mediaRecorder; let audioChunks = []; const transcribeClient = new TranscribeStreamingClient({ region: "us-east-1", credentials: { accessKeyId: "xxxxxx", secretAccessKey: "xxxxxx" } }); document.addEventListener("click", async function(event) { if (event.target.tagName === "BUTTON" && event.target.hasAttribute("data-domanda")) { let bottone = event.target; let numeroDomanda = bottone.getAttribute("data-domanda"); console.log("Hai premuto il pulsante della domanda:", numeroDomanda); try { const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); mediaRecorder = new MediaRecorder(stream, { mimeType: "audio/webm" }); audioChunks = []; mediaRecorder.ondataavailable = event => { if (event.data.size > 0) { audioChunks.push(event.data); } }; mediaRecorder.onstop = async () => { const audioBlob = new Blob(audioChunks, { type: "audio/webm" }); const audioFile = new File([audioBlob], "recording.webm", { type: "audio/webm" }); console.log("File audio registrato", audioFile); try { //errore in questo try const audioBuffer = await audioFile.arrayBuffer(); const audioUint8 = new Uint8Array(audioBuffer); // Invia l'audio per la trascrizione streaming const audioStream = new ReadableStream({ start(controller) { controller.enqueue(audioUint8); controller.close(); } }); const command = new StartStreamTranscriptionCommand({ LanguageCode: "it-IT", MediaSampleRateHertz: 16000, // Cambiato per adattarsi a AWS MediaEncoding: "pcm", AudioStream: audioStream }); const response = await transcribeClient.send(command); let transcription = ""; if (response?.TranscriptResultStream) { for await (const event of response.TranscriptResultStream) { if (event.TranscriptEvent && event.TranscriptEvent.Transcript) { const results = event.TranscriptEvent.Transcript.Results; if (results?.length > 0 && results[0].Alternatives?.length > 0) { transcription += results[0].Alternatives[0].Transcript + " "; } } } } else { console.error("TranscriptResultStream non disponibile", response); } console.log("Trascrizione:", transcription.trim()); alert("Trascrizione: " + transcription.trim()); } catch (err) { console.error("Errore durante la trascrizione:", err); } // Reset bottone bottone.textContent = "Rispondi"; bottone.classList.remove("registrando"); }; mediaRecorder.start(); bottone.textContent = "Stop"; bottone.classList.add("registrando"); bottone.addEventListener("click", () => { if (mediaRecorder.state === "recording") { mediaRecorder.stop(); stream.getTracks().forEach(track => track.stop()); } }, { once: true }); } catch (error) { console.error("Errore durante il processo:", error); alert("Errore durante il processo."); } } });
报错原因
音频编码与格式不匹配
代码中MediaEncoding设置为pcm,但实际录制的是audio/webm容器格式(包含VP8/Opus编码),AWS Transcribe要求的PCM是原始无压缩音频数据,而非带容器的编码格式,服务无法解析该格式,触发序列化错误。音频流处理方式错误
直接将WebM文件的Uint8Array一次性塞入ReadableStream,不符合流式传输的分块要求,且格式错误导致SDK在序列化音频流时无法识别数据结构,抛出e.$unknown is undefined。采样率可能不匹配
代码指定了16000Hz采样率,但MediaRecorder默认采样率不一定为16000Hz,若实际采样率与参数不符,也会导致服务解析失败。
修复方案
1. 转换音频为PCM原始格式
使用AudioContext将录制的WebM解码后,转换为16kHz、单声道、16位有符号PCM:
// 替换mediaRecorder.onstop内的音频处理逻辑 mediaRecorder.onstop = async () => { const audioBlob = new Blob(audioChunks, { type: "audio/webm" }); const arrayBuffer = await audioBlob.arrayBuffer(); // 初始化AudioContext,指定16000Hz采样率 const audioContext = new (window.AudioContext || window.webkitAudioContext)({ sampleRate: 16000 }); const audioBuffer = await audioContext.decodeAudioData(arrayBuffer); // 转换为单声道16kHz PCM(16位有符号整数) const channelData = audioBuffer.getChannelData(0); const pcmData = new Int16Array(channelData.length); for (let i = 0; i < channelData.length; i++) { pcmData[i] = channelData[i] * 32767; // 浮点音频转16位有符号整数 } // 分块发送音频流(每100ms一块,符合AWS流式要求) const chunkSize = 16000 * 2 * 0.1; // 100ms数据量:采样率*字节数/样本*时长 let offset = 0; const audioStream = new ReadableStream({ async pull(controller) { if (offset >= pcmData.length) { controller.close(); return; } const chunk = pcmData.slice(offset, offset + chunkSize); controller.enqueue(chunk); offset += chunkSize; await new Promise(resolve => setTimeout(resolve, 100)); // 模拟流式延迟 } }); // 修正命令参数 const command = new StartStreamTranscriptionCommand({ LanguageCode: "it-IT", MediaSampleRateHertz: 16000, MediaEncoding: "pcm", AudioStream: audioStream }); // 后续发送和转录处理逻辑不变 const response = await transcribeClient.send(command); let transcription = ""; if (response?.TranscriptResultStream) { for await (const event of response.TranscriptResultStream) { if (event.TranscriptEvent && event.TranscriptEvent.Transcript) { const results = event.TranscriptEvent.Transcript.Results; if (results?.length > 0 && results[0].Alternatives?.length > 0) { transcription += results[0].Alternatives[0].Transcript + " "; } } } } else { console.error("TranscriptResultStream non disponibile", response); } console.log("Trascrizione:", transcription.trim()); alert("Trascrizione: " + transcription.trim()); };
2. 确保采样率一致
初始化AudioContext时显式指定16000Hz采样率,保证转换后的音频与传入AWS的参数匹配。
3. 分块流式发送
AWS Transcribe流式服务要求音频分块发送(每100-200ms一块),避免一次性发送全部数据,确保服务能正确解析流式数据。
内容的提问来源于stack exchange,提问作者LearningToCode

