如何将MediaRecorder音频字节经WebSocket传至Python转numpy数组
解决方案:内存中解码WebM音频字节为Numpy数组
你的核心问题是前端MediaRecorder输出的是编码后的WebM容器音频(通常为Opus编码),并非原始PCM字节流,所以直接用np.frombuffer或未配置的音频库处理必然失败。以下是无需写入硬盘的高效解决方案:
方案1:使用PyAV(基于FFmpeg的内存媒体处理)
PyAV可以直接读取内存中的WebM字节流,解码为原始PCM数据,再转换为Numpy数组。
步骤:
- 安装依赖:
pip install av numpy
- 核心代码示例(FastAPI WebSocket处理):
import av import numpy as np from fastapi import FastAPI, WebSocket from io import BytesIO app = FastAPI() @app.websocket("/ws/audio") async def websocket_endpoint(websocket: WebSocket): await websocket.accept() audio_bytes = b"" try: while True: data = await websocket.receive_bytes() audio_bytes += data # 当收到完整音频(或累积到一定量)时解码 # 若为流式处理,可调整为按帧分段解码 if len(audio_bytes) > 0: # 用BytesIO包装内存字节 container = av.open(BytesIO(audio_bytes)) audio_frames = [] for frame in container.decode(audio=0): # 将音频帧转换为Numpy数组 # frame.format 通常为's16'(int16),可根据实际编码调整 np_frame = frame.to_ndarray() # 转换为音频处理常用的float32格式 np_frame = np_frame.astype(np.float32) / 32768.0 audio_frames.append(np_frame) # 拼接所有帧为完整音频数组 audio_np = np.concatenate(audio_frames, axis=0) print(f"解码后音频形状:{audio_np.shape}") # 此处添加你的音频处理逻辑 # ... # 重置字节缓存(流式场景可改为滑动窗口) audio_bytes = b"" except Exception as e: print(f"处理错误:{e}") finally: await websocket.close()
方案2:使用ffmpeg-python(FFmpeg命令行的Python封装)
通过内存管道将WebM字节传给FFmpeg,直接输出PCM数据,再读取为Numpy数组。
步骤:
- 安装依赖:
pip install ffmpeg-python numpy
- 核心代码示例:
import ffmpeg import numpy as np from fastapi import FastAPI, WebSocket app = FastAPI() @app.websocket("/ws/audio") async def websocket_endpoint(websocket: WebSocket): await websocket.accept() audio_bytes = b"" try: while True: data = await websocket.receive_bytes() audio_bytes += data if len(audio_bytes) > 0: # 用FFmpeg解码WebM为16位单声道PCM(参数可根据前端配置调整) out, _ = ( ffmpeg .input("pipe:", format="webm") .output("pipe:", format="s16le", acodec="pcm_s16le", ac=1, ar=16000) .run(input=audio_bytes, capture_stdout=True, capture_stderr=True) ) # 将PCM字节转换为Numpy数组 audio_np = np.frombuffer(out, dtype=np.int16).astype(np.float32) / 32768.0 print(f"解码后音频形状:{audio_np.shape}") # 此处添加你的音频处理逻辑 # ... audio_bytes = b"" except Exception as e: print(f"处理错误:{e}") finally: await websocket.close()
为什么之前的方法失败?
- 问题1:
np.frombuffer要求输入是原始PCM字节(连续的int16/float32采样数据),但WebM是封装了编码数据的容器,字节结构和PCM完全不同,因此长度不匹配报错。 - 问题2:强行将编码后的WebM字节当作int16 PCM读取,得到的是无意义的噪音——编码数据并非原始音频采样值。
- 问题3:librosa/soundfile依赖的libsndfile默认不支持WebM/Opus格式,除非编译时添加对应解码器支持,因此直接读取BytesIO会提示格式不识别。
内容的提问来源于stack exchange,提问作者Pooya Mohammadi Kazaj
相关产品推荐
相关产品推荐

