Skip to content

fix: bound streaming VAD frame buffers#3236

Open
LauraGPT wants to merge 2 commits into
mainfrom
codex/fix-realtime-vad-buffer-growth-20260715
Open

fix: bound streaming VAD frame buffers#3236
LauraGPT wants to merge 2 commits into
mainfrom
codex/fix-realtime-vad-buffer-growth-20260715

Conversation

@LauraGPT

Copy link
Copy Markdown
Collaborator

Summary

Fixes #3101.

Long-running streaming VAD appended every waveform frame exposed by
WavFrontendOnline, including LFR context, while score/decibel cleanup used
only emitted VAD frames. A typical 60 ms call exposed 10 waveform frames but
emitted 6 score frames, so four stale frames accumulated per call and each
torch.cat became progressively more expensive.

This change:

  • lets streaming VAD opt in to exact score-aligned waveform spans from the
    frontend, without adding copy/concat work to other frontend consumers;
  • rejects ambiguous or incomplete waveform spans instead of fabricating audio;
  • appends only newly emitted frame-shift samples after the first chunk; and
  • compacts consumed waveform, decibel, and score prefixes even when no endpoint
    is emitted, while preserving absolute frame indexes.

Type of change

  • Bug fix
  • Documentation
  • Example or demo
  • Runtime or deployment
  • Benchmark or evaluation
  • Model/training change

Validation

  • python -m compileall funasr examples tests
  • Docs or links checked (no docs or links changed)
  • Runtime/deployment command tested

Focused tests:

pytest tests/test_fsmn_vad_streaming_buffers.py tests/test_fsmn_vad_dynamic_silence.py
16 passed

pytest tests/test_realtime_ws_service.py
13 passed

The new regression suite was run red before each fix and covers normal chunks,
160-sample and empty frontend final flushes, an initial zero-score chunk,
lfr_m=7/lfr_n=6, endpoint reset followed by more audio, no-endpoint history
compaction, opt-in frontend overhead, positional compatibility, and atomic
failure for misaligned waveform spans.

Real-model parity on H100:

  • 70 s streaming input plus the 10 ms final flush produced the exact base
    segments and SHA-256 ccdcfd29a79c551620f56b5fe8e839bdc89b5d71c201fef5aa64794f9a798097.
  • Offline AutoModel.generate() produced the exact base seven segments and
    SHA-256 1ee13953dbd29cbf8326283542b90a98daccba8180ac487d06c2e377304d1bde.
  • 18 real-frontend continuity cases passed across lfr_n=1/2/6, short initial
    chunks, and final chunk sizes from 0 to 960 samples.

Long-run H100 measurements:

Scenario Before After
Repeated speech + silence feed median 4.105 ms at 0.5 min to 24.381 ms at 20 min; 17,642,240 retained samples and 80,264 decibels 4.427 ms at 20 min, 4.390 ms at 60 min, and 4.344 ms at 120 min; retained history stayed between 240 and 6,800 samples and between 0 and 41 scores/decibels
Continuous silence, no endpoint 4.570 ms at 1 min to 16.527 ms at 10 min; 9,599,600 retained samples and 59,996 scores 4.804 ms at 1 min to 4.490 ms at 10 min; 6,800 retained samples and 41 scores/decibels throughout

The final mixed-audio two-hour soak completed 72,029 feed calls and 394
segments. Frontend tensor state remained at 5,441 elements, maximum RSS stayed
at 988,888 KiB, and GPU allocation stayed flat.

User impact

Realtime Fun-ASR-Nano WebSocket sessions no longer slow down as VAD context or
already-consumed silence accumulates. This directly targets the increasing RTF
and eventual disconnect reported after roughly one hour with speaker tracking
enabled.

Notes for reviewers

  • No WebSocket protocol or model output semantics changed.
  • The legacy waveform fallback is intentionally strict: custom frontends must
    provide an exact waveform span, otherwise the model raises before mutating
    history.
  • DynamicStreamingVAD.finalize() continues to use its existing 10 ms final
    flush; empty-input public AutoModel.generate() behavior is unchanged.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces support for exact score-aligned waveform spans in the streaming VAD model and the online wav frontend. Key changes include adding a DropCachedFrames method to manage memory, aligning VAD score frames with waveform samples in ComputeDecibel, and adding comprehensive unit tests to verify streaming buffer behavior and waveform alignment. The reviewer suggests using .get("waveforms") instead of direct dictionary access to avoid a potential KeyError when custom frontends are used.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread funasr/models/fsmn_vad_streaming/model.py Outdated
@LauraGPT LauraGPT requested a review from yuekaizhang July 15, 2026 12:13
@LauraGPT

Copy link
Copy Markdown
Collaborator Author

@yuekaizhang, this is ready for maintainer review when available. It addresses the remaining long-session degradation reported in #3101 after the WebSocket/speaker-state fix: the shared streaming VAD was retaining LFR context and consumed history, causing per-feed cost to grow over time.

Current head 30c2fa9e is mergeable, the only review thread is resolved, and validation is green:

  • 30 focused regression tests;
  • real 70-second streaming output parity;
  • two-hour H100 mixed speech/silence soak with bounded VAD state and flat feed latency;
  • compileall and git diff --check.

No further contributor-side blocker is currently visible.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

即時轉錄一段時間後會自行斷線

1 participant