Skip to content

Bug: model.generate(input=bytes) hangs for minutes on raw PCM chunks that trigger audio container false-positive #3739

Open

Description

@chongmin1

🐛 Bug Report: model.generate(input=bytes) 在处理流式原始 PCM 时长时间 hang

版本

  • FunASR: 1.4.0
  • Python: 3.10.12
  • PyTorch: 1.12.0
  • OS: Centos7.9

场景

FSMN VAD 模型做流式推理:每 200ms 收到一个 int16 PCM chunk(6400 字节,16kHz mono),调用 AutoModel.generate()。

调用方式

from funasr import AutoModel

model = AutoModel(model="iic/speech_fsmn_vad_zh-cn-16k-common-pytorch", disable_update=True)

# 每 200ms 一帧,pcm_chunk 是原始 int16 PCM bytes,6400 字节(16kHz x 0.2s x 2)
cache = {}
for pcm_chunk in pcm_chunks:
    result = model.generate(
        input=pcm_chunk,      # bytes, 原始 int16 PCM, 6400 字节
        cache=cache,
        chunk_size=200,
        disable_pbar=True,
    )

现象

服务稳定运行数周后,某次处理中 generate() 调用hang 约三分钟不返回。期间该路会话的所有后续音频帧无法处理。三分钟后抛出异常:

Failed to decode container-formatted audio bytes.
Verify that the input is a complete supported audio file and that torchaudio,
soundfile, or ffmpeg is available.

通常 generate() 对一个 200ms chunk 的推理只需 10-20ms。hang 的持续时间远超正常推理时间。

补充信息

  • FunASR 版本 1.4.0(pip show funasr 输出)
  • 输入是原始 int16 PCM bytes,不是 WAV/MP3 等容器格式文件
  • 该错误低频出现(数周一次),未找到稳定复现方式

以下是 AI(Claude)辅助分析的推测,供参考:

AI 推测的可能原因

  1. _is_audio_container 误判:load_utils.py 中 load_bytes() 对所有 bytes 输入调用 _is_audio_container() 进行容器格式检测。200ms int16 PCM chunk(6400 字节)有一定概率命中 MPEG 帧头假阳性(data[0] == 0xFF and (data[1] & 0xE0) == 0xE0),随后 _has_consecutive_mp3_frames() 在随机 PCM 数据中有非零概率通过三层帧头校验。

  2. 误判后 torchaudio 陷入长时间错误恢复:一旦判定为容器格式,load_bytes() 调用 load_audio_text_image_video(BytesIO(input), fs=16000),torchaudio 被喂入一个 6400 字节的"假 MP3 文件",内部错误恢复路径反复尝试重新同步,持续数分钟后才放弃。

(以上推测基于对 FunASR 1.4.1 funasr/utils/load_utils.py 和 funasr/auto/auto_model.py 源码的阅读,尚未通过构造特定 PCM 数据复现验证。)

Activity

  1. LauraGPT commented on Sep 30, 2026

    @LauraGPT
    Collaborator

    感谢把实测现象与 AI 推测分开说明。先提供一个适合你这条 16 kHz、单声道、signed int16 little-endian 原始 PCM 输入链路的绕过方法:把每帧显式转换为归一化 float32 波形,再交给 generate(),不要让无头 PCM 字节参与文件格式自动探测。

    保留原来的 model、每会话的 cache 和流结束处理,仅替换传入的数据:

    import numpy as np
    
    # pcm_chunk: one complete PCM chunk, 6400 bytes for your 200 ms input
    if len(pcm_chunk) % 2:
        raise ValueError("PCM16 chunk must contain complete 2-byte samples")
    speech = np.frombuffer(pcm_chunk, dtype="<i2").astype(np.float32) / 32768.0
    
    result = model.generate(
        input=speech,
        cache=cache,
        chunk_size=200,
        disable_pbar=True,
    )

    这段只解释已知格式,不做重采样。不要用于 WAV/MP3 文件字节、其他位深/字节序或多声道数据;网络分片若切断了一个采样点,应先在收包层拼完整。不要每帧重建 cache;会话结束仍按原有流式 VAD 的 is_final=True 流程收尾。

    维护侧已核对并做了无模型验证:

    • 当前 prepare_data_iterator 对直接传入的 bytes 调用 load_bytes(),而 NumPy 波形不经过这个探测入口。
    • 用一个 6400 字节的合成 PCM-compatible 序列构造了 MPEG 帧头特征,确认它能进入容器解码分支。此处把解码调用替换为立即抛出受控异常的边界探针,没有复现或声称复现真实解码器的三分钟 hang。
    • 对同一序列执行上面的转换,再经过实际的数据准备和音频加载函数,得到 3200 个 float32 样本,数值逐项保持一致,torchaudio/soundfile/ffmpeg 的解码入口均未调用;另有 int16 正负边界值校验。仓库已有音频字节测试 14 项通过。

    因此,自动探测发生歧义在原理上可以构造,但还不能据此断言你那次卡顿发生在 torchaudio;当前文件读取实现 还有 soundfile/ffmpeg 后备路径。这些验证针对当前源码,不是你的 FunASR 1.4.0 / torch 1.12.0 环境复现。

    请先用显式波形输入观察是否还会出现同类停顿。进一步定位需要完整异常链(含 __cause__)、实际导入的 funasr.__file__、torchaudio/soundfile 版本及 ffmpeg 版本;若能在受控复现中取得卡顿时的线程栈,也很有帮助。路径可打码,初步排查不需要公开业务录音。先保留 issue,区分“已验证的输入绕过路径”和“仍待定位的原始卡顿”。


    源码进展(2026-10-02):PR #3751 已合入 main。对于已知的单声道、signed int16 little-endian 原始 PCM,现在可以显式传入 input_format="pcm_s16le",绕过容器自动探测。对你这条 16 kHz 输入链路,在原来的调用中增加 input_format="pcm_s16le", fs=16000 即可;保留每会话的 cache、分块参数和结束处理。

    这是源码新增能力,尚未作为新的 PyPI 版本发布。 不要假定原有 FunASR 1.4.0 已支持这个参数;旧安装继续使用上面的 NumPy 波形绕过方法。

    已验证显式 PCM 路径不调用容器探测/解码器,并补上 VAD 非 16 kHz 输入及批量输入的采样率传递回归。两条托管 NumPy CI 均执行 93 项测试,0 失败、0 跳过;验证不涉及真实模型推理。原始三分钟卡顿仍未复现或确定根因,因此保留本 issue,等待原场景的观察结果和诊断信息。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    needs feedbackWaiting for reporter feedback or retest results

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions