handle dropped packets and network delays
Add a per-user jitter buffer with restart resync. Incoming audio was rendered straight to OpenAL as packets arrived, so normal network jitter produced gaps and the renderer starved between frames. Buffer each user's decoded frames for a configurable playout delay (-jitter-buffer, default 40ms) and let the render thread pull from that buffer instead. Drop late packets, and use packet duration to set the expected frame number. A packet below the expected frame number is dropped rather than stalling delivery, and the expected frame is stepped by the packet's real duration instead of a fixed 10ms. After detecting a large backward jump in frame numbering, along with a sustained run of late packets, resync to the new frame numbering. When a sender switches audio devices, Mumble destroys and recreates their audio stream. This resets their frame number to zero without sending a terminator packet, which causes a potentially endless hang while we wait for a frame number that will not arrive for hours. Own the OpenAL context on one dedicated render thread. Contexts are current to an OS thread, so creating sources and buffers from whichever goroutine happened to be running could operate on no context at all. Every source and buffer is now created, filled, and deleted on that thread, and file playback renders through it too. Report device and capture failures with the device name. Startup errors said only that a device could not be opened, and a capture stall was reported as a microphone failure even though it is normal scheduler timing. Fatal microphone errors now exit instead of leaving a client that cannot transmit. Release queued buffers and deactivate the context on shutdown. Streams were torn down while buffers were still queued on a source, and the context was destroyed while still current. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
5ec82eb1fd
commit
6dcaa7f7a6
@@ -14,6 +14,7 @@ import (
|
||||
"os/exec"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
barnlog "git.stormux.org/storm/barnard/log"
|
||||
|
||||
@@ -114,6 +115,7 @@ func main() {
|
||||
serverSet := false
|
||||
usernameSet := false
|
||||
buffers := flag.Int("buffers", 16, "number of audio buffers to use")
|
||||
jitterBuffer := flag.Int("jitter-buffer", 40, "incoming per-user audio buffer in ms (0, 20, 40, or 60)")
|
||||
profile := flag.Bool("profile", false, "add http server to serve profiles")
|
||||
noiseSuppressionEnabled := flag.Bool("noise-suppression", false, "enable noise suppression for microphone input")
|
||||
tcpOnly := flag.Bool("tcp", false, "disable UDP, force audio through TCP tunnel")
|
||||
@@ -121,6 +123,10 @@ func main() {
|
||||
logFile := flag.String("logfile", "", "write logs to this file (logging is disabled when omitted)")
|
||||
|
||||
flag.Parse()
|
||||
selectedJitterBuffer, err := jitterBufferDuration(*jitterBuffer)
|
||||
if err != nil {
|
||||
handle_raw_error(err)
|
||||
}
|
||||
|
||||
// Set up logging
|
||||
var level barnlog.Level
|
||||
@@ -214,6 +220,7 @@ func main() {
|
||||
}
|
||||
b.Config.Buffers = *buffers
|
||||
b.Config.DisableUDP = *tcpOnly
|
||||
b.Config.IncomingAudioBuffer = selectedJitterBuffer
|
||||
|
||||
b.Hotkeys = b.UserConfig.GetHotkeys()
|
||||
b.UserConfig.SaveConfig()
|
||||
@@ -253,6 +260,18 @@ func main() {
|
||||
handle_error(&b)
|
||||
}
|
||||
|
||||
// jitterBufferDuration converts the requested incoming playout delay to a
|
||||
// supported duration. Zero starts playback without an initial safety buffer.
|
||||
func jitterBufferDuration(milliseconds int) (time.Duration, error) {
|
||||
interval := time.Duration(milliseconds) * time.Millisecond
|
||||
switch interval {
|
||||
case 0, 20 * time.Millisecond, 40 * time.Millisecond, 60 * time.Millisecond:
|
||||
return interval, nil
|
||||
default:
|
||||
return 0, fmt.Errorf("jitter buffer must be 0, 20, 40, or 60 ms, got %d", milliseconds)
|
||||
}
|
||||
}
|
||||
|
||||
func handle_raw_error(e error) {
|
||||
fmt.Fprintf(os.Stderr, "%s\n", e.Error())
|
||||
os.Exit(1)
|
||||
|
||||
Reference in New Issue
Block a user