fix: drain media before BYE on local hangup to prevent voicemail clipping - #811
Open
rkfshakti wants to merge 2 commits into
Open
fix: drain media before BYE on local hangup to prevent voicemail clipping#811rkfshakti wants to merge 2 commits into
rkfshakti wants to merge 2 commits into
Conversation
…ping
When an agent ends a call (EndCall RPC, end_call tool, or participant
removed from the room) right after wait_for_playout() returns, the last
word of the utterance is clipped on the callee's end. The audio is
already past the agent — it is buffered in the room mixer (~100ms input
buffer), the encoder, and in-flight RTP — but the SIP bridge sends BYE
and closes media immediately, so the tail never clears the wire (issue
#4737).
Add a configurable hangup drain window (hangup_drain_time, default
500ms): on a locally-initiated clean hangup, keep feeding media for the
window before sending BYE. Remote BYE, errors, timeouts, and
pre-connect failures are not delayed.
Regression test: an EndCall RPC on an established outbound call must not
emit BYE before the drain window has elapsed. Fails on the old code
('BYE sent before the media drain window elapsed'), passes with the
fix.
…nprivileged range Devin review feedback on PR livekit#811: 1. inbound drainOnHangup: a remote CANCEL before the call is answered hit the 'cancelled' branch and slept the drain window with no media bridged. Gate the drain on the media actually being bridged (c.started broken) and drop 'cancelled' from the local-hangup set. Remote CANCEL is not a local hangup. 2. newTestMediaPort bound a random even port in 1-65535, which can land on a privileged port (<1024) and fail in CI with 'bind: permission denied' (seen on TestSetOfferReportsUnknownProvider). Constrain the test range to 10000-20000.
Author
|
Addressed the Devin review finding (cancelled inbound call delayed): |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #811 +/- ##
==========================================
+ Coverage 65.25% 67.22% +1.97%
==========================================
Files 51 42 -9
Lines 6588 8226 +1638
==========================================
+ Hits 4299 5530 +1231
- Misses 1915 2205 +290
- Partials 374 491 +117 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes livekit/livekit#4737
Problem
When an agent ends a call right after
wait_for_playout()returns — EndCall RPC,end_calltool, or removing the SIP participant — the last word of the utterance is clipped on the callee's end (voicemail scenario). The audio is already past the agent: it is buffered in the room mixer (5-frame ~100ms input buffer), the codec encoder, and in-flight RTP.wait_for_playout()only tracks the local queue, so it cannot see this tail.The SIP bridge then sends BYE and closes media immediately, so whatever is still buffered never clears the wire. A trailing pause masks the bug because it gives the tail time to flush; an abrupt end-of-speech hangup clips the last word. The reporter's workaround (2s pre-hangup sleep) confirms the mechanism.
Fix
Add a configurable drain window
hangup_drain_time(default500ms): on a locally-initiated clean hangup, keep feeding media to the peer for the window before sending BYE. The window is bounded (default 500ms) and only applies to:EndCallRPC (reasonrpc)hangup)removed)cancelled)Remote BYE (the peer hung up), errors, timeouts, media failures, and pre-connect failures are not delayed. Set
hangup_drain_time: -1to disable.Test
TestOutboundHangupDrainsMediaBeforeBYE: on an established outbound call, anEndCallRPC must not emit BYE before the drain window has elapsed. Fails on the old code (BYE sent before the media drain window elapsed), passes with the fix. All media-port and outbound tests pass; the pre-existing auth-test failures on clean main are unrelated (they fail identically without this change).