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

如何将MediaRecorder音频字节经WebSocket传至Python转numpy数组

解决方案:内存中解码WebM音频字节为Numpy数组

你的核心问题是前端MediaRecorder输出的是编码后的WebM容器音频(通常为Opus编码),并非原始PCM字节流,所以直接用np.frombuffer或未配置的音频库处理必然失败。以下是无需写入硬盘的高效解决方案:

方案1:使用PyAV(基于FFmpeg的内存媒体处理)

PyAV可以直接读取内存中的WebM字节流,解码为原始PCM数据,再转换为Numpy数组。

步骤:

  1. 安装依赖:
pip install av numpy
  1. 核心代码示例(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数组。

步骤:

  1. 安装依赖:
pip install ffmpeg-python numpy
  1. 核心代码示例:
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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 04:15:37