src/server/internal/direct/audio_delay_test.go
5,287 bytes · 179 lines · capsule://quake0day/[email protected]
raw on github
package direct
import (
"bytes"
"encoding/binary"
"testing"
"time"
)
func testPCM(samples int, start int) []byte {
pcm := make([]byte, samples*2)
for i := 0; i < samples; i++ {
binary.LittleEndian.PutUint16(pcm[i*2:], uint16(start+i+1))
}
return pcm
}
func TestCurrentVideoBitrateKbpsUsesGCCBudget(t *testing.T) {
t.Parallel()
p := &DirectPeer{}
if got := p.currentVideoBitrateKbps(); got != defaultDirectVideoBitrateKbps {
t.Fatalf("default bitrate=%d want %d", got, defaultDirectVideoBitrateKbps)
}
p.targetBitrateBps.Store(675_000)
if got := p.currentVideoBitrateKbps(); got != minDirectVideoBitrateKbps {
t.Fatalf("low GCC bitrate=%d want %d", got, minDirectVideoBitrateKbps)
}
p.targetBitrateBps.Store(2_000_000)
if got := p.currentVideoBitrateKbps(); got != 1300 {
t.Fatalf("scaled bitrate=%d want 1300", got)
}
p.targetBitrateBps.Store(4_000_000)
if got := p.currentVideoBitrateKbps(); got != maxDirectVideoBitrateKbps {
t.Fatalf("capped bitrate=%d want %d", got, maxDirectVideoBitrateKbps)
}
}
func TestApplyAudioDelayInsertsSilenceAndPreservesLength(t *testing.T) {
t.Parallel()
p := &DirectPeer{}
const sampleRate = 1000
pcm := testPCM(200, 0)
_ = p.applyAudioDelay(1, pcm, sampleRate)
p.HandleAVSyncFeedback(1, 200, 180, "video_late_audio_leads")
out := p.applyAudioDelay(1, pcm, sampleRate)
if len(out) != len(pcm) {
t.Fatalf("len=%d want %d", len(out), len(pcm))
}
delayBytes := audioDelayPCMBytes(audioDelayStepMS, sampleRate)
if !bytes.Equal(out[:delayBytes], make([]byte, delayBytes)) {
t.Fatalf("first %d bytes should be silence", delayBytes)
}
if !bytes.Equal(out[delayBytes:], pcm[:len(pcm)-delayBytes]) {
t.Fatalf("audio content was not shifted by %d bytes", delayBytes)
}
}
func TestApplyAudioDelayCarriesDelayedTail(t *testing.T) {
t.Parallel()
p := &DirectPeer{}
const sampleRate = 1000
first := testPCM(200, 0)
second := testPCM(200, 1000)
_ = p.applyAudioDelay(1, first, sampleRate)
p.HandleAVSyncFeedback(1, 200, 180, "video_late_audio_leads")
firstOut := p.applyAudioDelay(1, first, sampleRate)
_ = firstOut
secondOut := p.applyAudioDelay(1, second, sampleRate)
delayBytes := audioDelayPCMBytes(audioDelayStepMS, sampleRate)
if !bytes.Equal(secondOut[:delayBytes], first[len(first)-delayBytes:]) {
t.Fatalf("second output does not start with delayed tail")
}
}
func TestApplyAudioDelayResetsOnEpochChange(t *testing.T) {
t.Parallel()
p := &DirectPeer{}
const sampleRate = 1000
first := testPCM(200, 0)
second := testPCM(200, 1000)
_ = p.applyAudioDelay(1, first, sampleRate)
p.HandleAVSyncFeedback(1, 200, 180, "video_late_audio_leads")
_ = p.applyAudioDelay(1, first, sampleRate)
out := p.applyAudioDelay(2, second, sampleRate)
if !bytes.Equal(out, second) {
t.Fatalf("new epoch should reset audio delay")
}
}
func TestHandleAVSyncFeedbackIgnoresStaleTurn(t *testing.T) {
t.Parallel()
p := &DirectPeer{}
p.AdvancePlaybackEpoch(2)
const sampleRate = 1000
pcm := testPCM(200, 0)
_ = p.applyAudioDelay(2, pcm, sampleRate)
p.HandleAVSyncFeedback(1, 200, 180, "video_late_audio_leads")
out := p.applyAudioDelay(2, pcm, sampleRate)
if !bytes.Equal(out, pcm) {
t.Fatalf("stale feedback should not delay current epoch audio")
}
}
func TestHandleAVSyncFeedbackUsesJitterDeltaWithoutPresentationLag(t *testing.T) {
t.Parallel()
p := &DirectPeer{}
const sampleRate = 1000
pcm := testPCM(600, 0)
_ = p.applyAudioDelay(1, pcm, sampleRate)
p.HandleAVSyncFeedback(1, 0, 500, "video_late_audio_leads")
out := p.applyAudioDelay(1, pcm, sampleRate)
delayBytes := audioDelayPCMBytes(audioDelayStepMS, sampleRate)
if !bytes.Equal(out[:delayBytes], make([]byte, delayBytes)) {
t.Fatalf("expected jitter-only feedback to insert %d bytes of silence", delayBytes)
}
}
func TestPrepareMediaPathResetKeepsUserAudioChannelOpen(t *testing.T) {
t.Parallel()
p := NewDirectPeer("session-test", nil, nil, nil, nil)
oldConnected := p.connected
p.lastVideoWriteTime = time.Now()
p.lastAudioWriteTime = time.Now()
p.targetBitrateBps.Store(1_200_000)
p.audioDelayTargetMS = 320
p.audioDelayCurrentMS = 160
p.audioDelayPCM = []byte{1, 2, 3}
p.audioDelaySampleRate = 24000
p.audioDelayEpoch = 7
p.audioDelayLastFeedback = time.Now()
p.opusEncoderSR = 24000
oldPC := p.prepareMediaPathReset()
if oldPC != nil {
t.Fatalf("old pc=%v want nil", oldPC)
}
if p.connected == oldConnected {
t.Fatalf("expected connected channel to be replaced")
}
if p.videoTrack != nil || p.audioTrack != nil {
t.Fatalf("expected media tracks to be cleared")
}
if !p.lastVideoWriteTime.IsZero() || !p.lastAudioWriteTime.IsZero() {
t.Fatalf("expected RTP write timestamps to be reset")
}
if got := p.targetBitrateBps.Load(); got != 0 {
t.Fatalf("target bitrate=%d want 0", got)
}
if p.opusEncoder != nil || p.opusEncoderSR != 0 {
t.Fatalf("expected opus encoder state to be reset")
}
if p.audioDelayTargetMS != 0 || p.audioDelayCurrentMS != 0 || len(p.audioDelayPCM) != 0 ||
p.audioDelaySampleRate != 0 || p.audioDelayEpoch != 0 || !p.audioDelayLastFeedback.IsZero() {
t.Fatalf("expected audio delay state to be reset")
}
select {
case p.userAudioCh <- []byte{42}:
default:
t.Fatalf("user audio channel should remain open and writable")
}
}