2026-07-17 00:25:27 +03:00
|
|
|
package voip
|
|
|
|
|
|
|
|
|
|
import (
|
2026-07-17 10:47:52 +03:00
|
|
|
"bytes"
|
2026-07-17 00:25:27 +03:00
|
|
|
"testing"
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
lksdk "github.com/livekit/server-sdk-go/v2"
|
2026-07-17 10:10:07 +03:00
|
|
|
"github.com/pion/webrtc/v4"
|
2026-07-17 00:25:27 +03:00
|
|
|
"github.com/pion/webrtc/v4/pkg/media"
|
2026-07-17 10:10:07 +03:00
|
|
|
"github.com/purpshell/meowcaller"
|
2026-07-17 00:25:27 +03:00
|
|
|
)
|
|
|
|
|
|
2026-07-17 10:47:52 +03:00
|
|
|
func annexBNAL(nalu ...byte) []byte {
|
|
|
|
|
return append([]byte{0, 0, 0, 1}, nalu...)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestH264ParameterSetRepeaterAddsCachedHeadersToIDR(t *testing.T) {
|
|
|
|
|
repeater := h264ParameterSetRepeater{}
|
|
|
|
|
sps := annexBNAL(0x67, 0x42, 0xe0, 0x1f)
|
|
|
|
|
pps := annexBNAL(0x68, 0xce, 0x06, 0xe2)
|
|
|
|
|
repeater.Normalize(append(append([]byte{}, sps...), pps...))
|
|
|
|
|
|
|
|
|
|
idr := annexBNAL(0x65, 0x88, 0x84)
|
|
|
|
|
got, repeated := repeater.Normalize(idr)
|
|
|
|
|
want := append(append(append([]byte{}, sps...), pps...), idr...)
|
|
|
|
|
if !repeated {
|
|
|
|
|
t.Fatal("Normalize did not report repeated parameter sets")
|
|
|
|
|
}
|
|
|
|
|
if !bytes.Equal(got, want) {
|
|
|
|
|
t.Fatalf("normalized IDR = %x, want %x", got, want)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestH264ParameterSetRepeaterPreservesCompleteIDR(t *testing.T) {
|
|
|
|
|
repeater := h264ParameterSetRepeater{}
|
|
|
|
|
au := append(append(annexBNAL(0x67, 0x42, 0xe0, 0x1f), annexBNAL(0x68, 0xce, 0x06, 0xe2)...), annexBNAL(0x65, 0x88, 0x84)...)
|
|
|
|
|
|
|
|
|
|
got, repeated := repeater.Normalize(au)
|
|
|
|
|
if repeated {
|
|
|
|
|
t.Fatal("Normalize reported repeating already-present parameter sets")
|
|
|
|
|
}
|
|
|
|
|
if !bytes.Equal(got, au) {
|
|
|
|
|
t.Fatalf("complete IDR changed: got %x, want %x", got, au)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestH264ParameterSetRepeaterUsesCurrentAndCachedHeadersInDecodeOrder(t *testing.T) {
|
|
|
|
|
repeater := h264ParameterSetRepeater{}
|
|
|
|
|
oldSPS := annexBNAL(0x67, 0x42, 0xe0, 0x1f)
|
|
|
|
|
pps := annexBNAL(0x68, 0xce, 0x06, 0xe2)
|
|
|
|
|
repeater.Normalize(append(append([]byte{}, oldSPS...), pps...))
|
|
|
|
|
|
|
|
|
|
newSPS := annexBNAL(0x67, 0x42, 0xe0, 0x20)
|
|
|
|
|
idr := annexBNAL(0x65, 0x99)
|
|
|
|
|
au := append(append([]byte{}, newSPS...), idr...)
|
|
|
|
|
got, repeated := repeater.Normalize(au)
|
|
|
|
|
want := append(append(append([]byte{}, newSPS...), pps...), idr...)
|
|
|
|
|
if !repeated {
|
|
|
|
|
t.Fatal("Normalize did not report filling the missing PPS")
|
|
|
|
|
}
|
|
|
|
|
if !bytes.Equal(got, want) {
|
|
|
|
|
t.Fatalf("normalized partial IDR = %x, want %x", got, want)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-17 00:25:27 +03:00
|
|
|
type orientedSampleTrack struct {
|
|
|
|
|
orientation uint8
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (t *orientedSampleTrack) WriteSample(media.Sample, *lksdk.SampleWriteOptions) error {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (t *orientedSampleTrack) SetVideoOrientation(orientation uint8) {
|
|
|
|
|
t.orientation = orientation
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestLiveKitH264WriterSetsVideoOrientation(t *testing.T) {
|
|
|
|
|
track := &orientedSampleTrack{}
|
|
|
|
|
writer := NewLiveKitH264Writer(track, time.Second/30)
|
|
|
|
|
|
|
|
|
|
writer.SetOrientation(5)
|
|
|
|
|
|
|
|
|
|
if track.orientation != 1 {
|
|
|
|
|
t.Fatalf("track orientation = %d, want 1", track.orientation)
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-07-17 10:10:07 +03:00
|
|
|
|
|
|
|
|
func TestLiveKitParticipantRequestsRemoteVideoKeyframe(t *testing.T) {
|
|
|
|
|
const wantSSRC = webrtc.SSRC(0x12345678)
|
|
|
|
|
var gotSSRC webrtc.SSRC
|
|
|
|
|
participant := &LiveKitParticipant{}
|
|
|
|
|
|
|
|
|
|
if participant.requestRemoteVideoKeyframe() {
|
|
|
|
|
t.Fatal("requestRemoteVideoKeyframe returned true before track subscription")
|
|
|
|
|
}
|
|
|
|
|
participant.setRemoteVideoPLI(func(ssrc webrtc.SSRC) {
|
|
|
|
|
gotSSRC = ssrc
|
|
|
|
|
}, wantSSRC)
|
|
|
|
|
if gotSSRC != wantSSRC {
|
|
|
|
|
t.Fatalf("deferred PLI SSRC = %#x, want %#x", gotSSRC, wantSSRC)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gotSSRC = 0
|
|
|
|
|
if !participant.requestRemoteVideoKeyframe() {
|
|
|
|
|
t.Fatal("requestRemoteVideoKeyframe returned false with a subscribed track")
|
|
|
|
|
}
|
|
|
|
|
if gotSSRC != wantSSRC {
|
|
|
|
|
t.Fatalf("immediate PLI SSRC = %#x, want %#x", gotSSRC, wantSSRC)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-30 04:34:36 +02:00
|
|
|
func TestLiveKitParticipantKeepsScreenShareKeyframeRequestSourceSpecific(t *testing.T) {
|
|
|
|
|
const (
|
|
|
|
|
cameraSSRC = webrtc.SSRC(0x11111111)
|
|
|
|
|
screenSSRC = webrtc.SSRC(0x22222222)
|
|
|
|
|
)
|
|
|
|
|
var gotSSRC webrtc.SSRC
|
|
|
|
|
participant := &LiveKitParticipant{remoteScreenActive: true}
|
|
|
|
|
|
|
|
|
|
if participant.requestRemoteVideoKeyframe() {
|
|
|
|
|
t.Fatal("requestRemoteVideoKeyframe returned true before screen-share subscription")
|
|
|
|
|
}
|
|
|
|
|
participant.setRemoteVideoPLIForSource(liveKitVideoSourceCamera, func(ssrc webrtc.SSRC) {
|
|
|
|
|
gotSSRC = ssrc
|
|
|
|
|
}, cameraSSRC)
|
|
|
|
|
if gotSSRC != 0 {
|
|
|
|
|
t.Fatalf("camera subscription consumed pending screen-share PLI with SSRC %#x", gotSSRC)
|
|
|
|
|
}
|
|
|
|
|
participant.setRemoteVideoPLIForSource(liveKitVideoSourceScreenShare, func(ssrc webrtc.SSRC) {
|
|
|
|
|
gotSSRC = ssrc
|
|
|
|
|
}, screenSSRC)
|
|
|
|
|
if gotSSRC != screenSSRC {
|
|
|
|
|
t.Fatalf("screen-share subscription PLI SSRC = %#x, want %#x", gotSSRC, screenSSRC)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-17 10:10:07 +03:00
|
|
|
func TestManagerDefersVideoKeyframeOnlyForTrackedCall(t *testing.T) {
|
|
|
|
|
manager := &Manager{
|
|
|
|
|
calls: make(map[string]*meowcaller.Call),
|
|
|
|
|
livekit: make(map[string]*LiveKitParticipant),
|
|
|
|
|
videoKeyframePending: make(map[string]bool),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
manager.requestLiveKitVideoKeyframe("ended")
|
|
|
|
|
if manager.videoKeyframePending["ended"] {
|
|
|
|
|
t.Fatal("keyframe request was retained for an untracked call")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
manager.calls["active"] = &meowcaller.Call{}
|
|
|
|
|
manager.requestLiveKitVideoKeyframe("active")
|
|
|
|
|
if !manager.videoKeyframePending["active"] {
|
|
|
|
|
t.Fatal("keyframe request was not retained for a tracked call")
|
|
|
|
|
}
|
|
|
|
|
}
|