Keep OpenAL jitter playback continuous
This commit is contained in:
committed by
Brandon McGinty
parent
5b0879ddaa
commit
99f1f4b9ab
@@ -51,9 +51,17 @@ type Recorder interface {
|
||||
const recorderOutgoingSource uint32 = ^uint32(0)
|
||||
|
||||
const (
|
||||
maxBufferSize = 11520 // Max frame size (2880) * bytes per stereo sample (4)
|
||||
maxBufferSize = 11520 // Max frame size (2880) * bytes per stereo sample (4)
|
||||
jitterMinPackets = 3
|
||||
jitterMaxPackets = 10
|
||||
)
|
||||
|
||||
// jitterPlaybackReady holds the initial playout delay only once. Requiring
|
||||
// the minimum on every packet drains and refills the renderer in bursts.
|
||||
func jitterPlaybackReady(started bool, buffered int) bool {
|
||||
return started || buffered >= jitterMinPackets
|
||||
}
|
||||
|
||||
var (
|
||||
ErrState = errors.New("gumbleopenal: invalid state")
|
||||
ErrMic = errors.New("gumbleopenal: microphone disconnected or misconfigured")
|
||||
@@ -451,15 +459,10 @@ func (s *Stream) OnAudioStream(e *gumble.AudioStreamEvent) {
|
||||
}
|
||||
|
||||
// Jitter buffer: collects incoming packets, reorders by
|
||||
// sequence number, and releases them in order after a small
|
||||
// initial delay (3-5 packets / 30-50ms).
|
||||
const (
|
||||
jitterMinPackets = 3 // minimum packets before playout starts
|
||||
jitterMaxPackets = 10 // maximum buffered packets before dropping oldest
|
||||
)
|
||||
// sequence number, and releases them after a small initial delay.
|
||||
var jitterBuf []*gumble.AudioPacket
|
||||
var jitterNextSeq int64
|
||||
var jitterInit bool
|
||||
var jitterInit, jitterStarted bool
|
||||
|
||||
// insertSorted inserts a packet into the jitter buffer sorted
|
||||
// by sequence number.
|
||||
@@ -509,10 +512,13 @@ func (s *Stream) OnAudioStream(e *gumble.AudioStreamEvent) {
|
||||
jitterInit = true
|
||||
}
|
||||
|
||||
// Only start playing when we have enough buffered packets
|
||||
if len(jitterBuf) < jitterMinPackets {
|
||||
// Hold only the initial packets. Once playback starts, drain every
|
||||
// ready packet so the renderer is fed continuously rather than in
|
||||
// bursts of jitterMinPackets packets.
|
||||
if !jitterPlaybackReady(jitterStarted, len(jitterBuf)) {
|
||||
continue
|
||||
}
|
||||
jitterStarted = true
|
||||
|
||||
// Drain all packets that are ready (in sequence order)
|
||||
for {
|
||||
|
||||
@@ -48,6 +48,18 @@ func TestStopSourceWaitsForWorker(t *testing.T) {
|
||||
<-returned
|
||||
}
|
||||
|
||||
func TestJitterPlaybackDelayAppliesOnlyAtStartup(t *testing.T) {
|
||||
if jitterPlaybackReady(false, jitterMinPackets-1) {
|
||||
t.Fatal("jitter playback started before initial buffer filled")
|
||||
}
|
||||
if !jitterPlaybackReady(false, jitterMinPackets) {
|
||||
t.Fatal("jitter playback did not start after initial buffer filled")
|
||||
}
|
||||
if !jitterPlaybackReady(true, 1) {
|
||||
t.Fatal("jitter playback paused while refilling after startup")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRenderRejectsWorkAfterShutdown(t *testing.T) {
|
||||
s := &Stream{renderClosed: true}
|
||||
called := false
|
||||
|
||||
Reference in New Issue
Block a user