WebSocket单音频块发送时Python服务端无效输入报错问题
解决WebSocket发送单块音频Blob后服务端无效输入错误问题
问题背景
通过WebSocket每4秒从客户端发送音频Blob:
- 原实现:客户端将每次音频数据追加到Blob后发送,虽能正常运行,但Blob体积逐渐增大导致实时性滞后
- 修改后问题:改为仅发送当前最新音频块,服务端接收第一个Blob正常,第二个开始出现无效输入错误
问题原因
MediaRecorder的dataavailable事件返回的event.data是不带WAV文件头的原始音频片段。WAV格式需要包含采样率、位深、声道数等元数据的文件头才能被正常解析:
- 第一次发送时,服务端可能兼容解析(或偶然包含完整头),但后续片段缺少文件头,导致音频解析器无法识别有效输入。
解决方案
方案1:客户端为每个音频片段生成完整WAV文件头
修改客户端代码,不再累积Blob,而是为当前音频片段添加标准WAV文件头后发送:
recorder.mediaRecorder.addEventListener( "dataavailable", async (event) => { // 处理当前音频片段,生成带WAV头的Blob const audioBuffer = await event.data.arrayBuffer(); const wavBlob = createWavBlob(audioBuffer); if (recorder.socket?.readyState === 1) { recorder.socket.send(wavBlob); } } ); recorder.mediaRecorder.start(DELAY_TIME_MILLISECONDS); // 辅助函数:为原始PCM音频添加WAV文件头 function createWavBlob(audioBuffer) { // 根据实际录制参数调整以下值 const sampleRate = 44100; const numChannels = 1; const bitDepth = 16; const buffer = new ArrayBuffer(44 + audioBuffer.byteLength); const header = new DataView(buffer); // 写入WAV文件头 header.setUint8(0, 0x52); // R header.setUint8(1, 0x49); // I header.setUint8(2, 0x46); // F header.setUint8(3, 0x46); // F header.setUint32(4, 44 + audioBuffer.byteLength, true); // 文件总长度 header.setUint8(8, 0x57); // W header.setUint8(9, 0x41); // A header.setUint8(10, 0x56); // V header.setUint8(11, 0x45); // E header.setUint8(12, 0x66); // f header.setUint8(13, 0x6d); // m header.setUint8(14, 0x74); // t header.setUint8(15, 0x20); // 空格 header.setUint32(16, 16, true); // 子块长度 header.setUint16(20, 1, true); // PCM音频格式 header.setUint16(22, numChannels, true); // 声道数 header.setUint32(24, sampleRate, true); // 采样率 header.setUint32(28, sampleRate * numChannels * bitDepth / 8, true); // 字节率 header.setUint16(32, numChannels * bitDepth / 8, true); // 块对齐 header.setUint16(34, bitDepth, true); // 位深 header.setUint8(36, 0x64); // d header.setUint8(37, 0x61); // a header.setUint8(38, 0x74); // t header.setUint8(39, 0x61); // a header.setUint32(40, audioBuffer.byteLength, true); // 数据长度 // 复制原始音频数据到文件头之后 const dataView = new DataView(buffer, 44); const sourceView = new DataView(audioBuffer); for (let i = 0; i < audioBuffer.byteLength; i++) { dataView.setUint8(i, sourceView.getUint8(i)); } return new Blob([buffer], { type: "audio/wav; codecs=MS_PCM" }); }
方案2:服务端直接解析原始PCM数据
如果客户端发送的是原始PCM数据(不带文件头),修改服务端代码跳过WAV头解析,直接处理原始音频字节:
import numpy as np while True: bytes_data = await websocket.receive_bytes() print("============received audio bytes") if bytes_data: # 假设客户端发送的是16位单声道、44100Hz的PCM数据 # 直接将字节转为numpy数组 audio_array = np.frombuffer(bytes_data, dtype=np.int16) # 转为浮点型(范围-1到1),适配后续处理 audio_array = audio_array.astype(np.float32) / 32768.0 # 后续处理逻辑...
注意事项
- 客户端录制的音频参数(采样率、声道数、位深)必须与服务端解析时的参数完全一致,否则会出现音频失真或解析错误
- 使用MediaRecorder时,需确保指定的mimeType为PCM编码的WAV格式,比如
audio/wav; codecs=MS_PCM,避免浏览器输出其他压缩格式
内容的提问来源于stack exchange,提问作者GeM
相关产品推荐
相关产品推荐

