log the incoming audio path at packet level

Record decoder creation, sequence numbers, frame lengths, and decode
results for each tunneled audio packet, and note when a slow listener
has a packet dropped.
Diagnosing a codec or ordering problem previously meant adding print
statements and rebuilding. These sit at info and debug, so they cost
nothing unless -logfile is given.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Brandon McGinty
2026-08-20 14:42:52 -04:00
co-authored by Claude Opus 5
parent 872149c977
commit d02177af71
2 changed files with 19 additions and 5 deletions
+17 -2
View File
@@ -118,28 +118,31 @@ func (c *Client) handleUDPTunnel(buffer []byte) error {
buffer = buffer[1:]
session, n := varint.Decode(buffer)
if n <= 0 {
log.Warn("handleUDPTunnel: session varint decode failed")
return errInvalidProtobuf
}
buffer = buffer[n:]
user := c.Users[uint32(session)]
if user == nil {
log.Warn("handleUDPTunnel: unknown user session=%d", session)
return errInvalidProtobuf
}
decoder := user.decoder
if decoder == nil {
// TODO: decoder pool
// TODO: de-reference after stream is done
codec := c.audioCodec
if codec == nil {
log.Warn("handleUDPTunnel: no audio codec available")
return errNoCodec
}
decoder = codec.NewDecoder()
user.decoder = decoder
log.Info("handleUDPTunnel: created new decoder for %s", user.Name)
}
// Sequence
seq, n := varint.Decode(buffer)
if n <= 0 {
log.Warn("handleUDPTunnel: seq varint decode failed")
return errInvalidProtobuf
}
buffer = buffer[n:]
@@ -170,13 +173,20 @@ func (c *Client) handleUDPTunnel(buffer []byte) error {
// Length
length, n := varint.Decode(buffer)
if n <= 0 {
log.Warn("handleUDPTunnel: length varint decode failed")
return errInvalidProtobuf
}
buffer = buffer[n:]
// Opus audio packets set the 13th bit in the size field as the terminator.
audioLength := int(length) &^ 0x2000
isFinal := (length & 0x2000) != 0
log.Info("handleUDPTunnel: %s session=%d seq=%d audio_len=%d final=%v buf_remain=%d",
user.Name, session, seq, audioLength, isFinal, len(buffer))
if audioLength > len(buffer) {
log.Warn("handleUDPTunnel: audio length %d > remaining buffer %d",
audioLength, len(buffer))
return errInvalidProtobuf
}
@@ -189,6 +199,9 @@ func (c *Client) handleUDPTunnel(buffer []byte) error {
return err
}
log.Info("handleUDPTunnel: Opus decode OK for %s seq=%d pcm_samples=%d",
user.Name, seq, len(pcm))
event := AudioPacket{
Client: c,
Sender: user,
@@ -271,6 +284,7 @@ func (c *Client) dispatchAudio(user *User, packet *AudioPacket) {
for _, delivery := range deliveries {
if delivery.new {
log.Debug("new audio stream from %s (session=%d)", user.Name, user.Session)
delivery.listener.OnAudioStream(&AudioStreamEvent{Client: c, User: user, C: delivery.ch})
}
// User removal can run on a different protocol goroutine. Keep the
@@ -283,6 +297,7 @@ func (c *Client) dispatchAudio(user *User, packet *AudioPacket) {
case delivery.ch <- packet:
default:
// Never allow a slow listener to block protocol processing.
log.Debug("dropping buffered audio for slow listener (session=%d)", user.Session)
}
}
listeners.mu.Unlock()
+2 -3
View File
@@ -204,8 +204,8 @@ func main() {
}
b.Config.Buffers = *buffers
b.Config.AudioInterval = selectedAudioInterval
b.Config.DisableUDP = *tcpOnly
b.Config.IncomingAudioBuffer = selectedJitterBuffer
b.Config.DisableUDP = *tcpOnly
b.Hotkeys = b.UserConfig.GetHotkeys()
if err := b.UserConfig.SaveConfig(); err != nil {
@@ -275,8 +275,7 @@ func jitterBufferDuration(milliseconds int) (time.Duration, error) {
}
}
// serverAddress appends Mumble's default port unless the address already has
// one. A bracketed or bare IPv6 literal is not a host:port pair.
// serverAddress adds Mumble's default port without corrupting an IPv6 literal.
func serverAddress(address string) string {
if _, port, err := net.SplitHostPort(address); err == nil && port != "" {
return address