fix: reset Opus decoder on packet loss and decode errors
Track per-user audio sequence numbers to detect UDP packet loss gaps. When a sequence discontinuity is detected (loss, reorder, or burst gap), reset the Opus decoder state to prevent permanent audio corruption. Also reset the decoder when Decode() returns an error, instead of leaving the decoder in a corrupted state that produces static/popping for the remainder of the session. This fixes the 'random static/popping from user b but no other clients hear it' symptom, which occurs when one client experiences packet loss affecting only its own per-user decoder instance.
This commit is contained in:
committed by
Brandon McGinty
parent
30a02eba14
commit
dfa184211e
@@ -113,13 +113,30 @@ func (c *Client) handleUDPTunnel(buffer []byte) error {
|
||||
}
|
||||
|
||||
// Sequence
|
||||
// TODO: use in jitter buffer
|
||||
_, n = varint.Decode(buffer)
|
||||
seq, n := varint.Decode(buffer)
|
||||
if n <= 0 {
|
||||
return errInvalidProtobuf
|
||||
}
|
||||
buffer = buffer[n:]
|
||||
|
||||
// Detect sequence gaps (packet loss) and reset the decoder to prevent
|
||||
// permanent audio corruption from state desync.
|
||||
// Mumble uses a monotonically increasing sequence that wraps at MaxInt32.
|
||||
if user.audioSequenceValid {
|
||||
// Only treat as discontinuity if the gap is small enough to be loss
|
||||
// rather than a legitimate wrap-around or restart.
|
||||
gap := seq - user.audioSequence
|
||||
if gap > 1 && gap < 100 {
|
||||
decoder.Reset()
|
||||
} else if gap < 0 && gap > -100 {
|
||||
// Reordered packet — reset to be safe, since the decoder
|
||||
// state depends on correct frame ordering.
|
||||
decoder.Reset()
|
||||
}
|
||||
}
|
||||
user.audioSequence = seq
|
||||
user.audioSequenceValid = true
|
||||
|
||||
// Length
|
||||
length, n := varint.Decode(buffer)
|
||||
if n <= 0 {
|
||||
@@ -134,6 +151,8 @@ func (c *Client) handleUDPTunnel(buffer []byte) error {
|
||||
|
||||
pcm, err := decoder.Decode(buffer[:audioLength], AudioMaximumFrameSize)
|
||||
if err != nil {
|
||||
// Decode failure indicates corrupted decoder state; reset and drop.
|
||||
decoder.Reset()
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -53,6 +53,11 @@ type User struct {
|
||||
client *Client
|
||||
decoder AudioDecoder
|
||||
|
||||
// audioSequence tracks the last UDP audio packet sequence number for
|
||||
// this user, used to detect packet loss and reset the Opus decoder.
|
||||
audioSequence int64
|
||||
audioSequenceValid bool
|
||||
|
||||
AudioSource *openal.Source
|
||||
Boost uint16
|
||||
Volume float32
|
||||
|
||||
Reference in New Issue
Block a user