117 lines
3.6 KiB
Go
117 lines
3.6 KiB
Go
package playback
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestMediaStatsStoreObservesVideo(t *testing.T) {
|
|
store := NewMediaStatsStore()
|
|
now := time.Date(2026, time.September, 1, 12, 0, 0, 0, time.UTC)
|
|
store.now = func() time.Time { return now }
|
|
frame := VideoFrame{
|
|
Index: 10,
|
|
Width: 1920,
|
|
Height: 1080,
|
|
Stride: 5120,
|
|
Size: 5_529_600,
|
|
Label: "Main video",
|
|
FrameRateNumerator: 30000,
|
|
FrameRateDenominator: 1001,
|
|
}
|
|
store.ObserveVideo(frame)
|
|
now = now.Add(40 * time.Millisecond)
|
|
frame.Index++
|
|
frame.Invalid = true
|
|
store.ObserveVideo(frame)
|
|
|
|
got := store.Snapshot().Video
|
|
if !got.Available || got.Label != "Main video" || got.Index != 11 {
|
|
t.Fatalf("video stats = %#v", got)
|
|
}
|
|
if got.Width != 1920 || got.Height != 1080 || got.Stride != 5120 {
|
|
t.Fatalf("video dimensions = %#v", got)
|
|
}
|
|
if got.DeclaredFPS < 29.96 || got.DeclaredFPS > 29.98 {
|
|
t.Fatalf("declared FPS = %v", got.DeclaredFPS)
|
|
}
|
|
if got.FrameDT != 40*time.Millisecond || got.Invalid != 1 {
|
|
t.Fatalf("video timing = %#v", got)
|
|
}
|
|
}
|
|
|
|
func TestMediaStatsStoreCalculatesReceivedFPS(t *testing.T) {
|
|
store := NewMediaStatsStore()
|
|
now := time.Date(2026, time.September, 1, 12, 0, 0, 0, time.UTC)
|
|
store.now = func() time.Time { return now }
|
|
store.ObserveVideo(VideoFrame{})
|
|
now = now.Add(500 * time.Millisecond)
|
|
store.ObserveVideo(VideoFrame{})
|
|
now = now.Add(500 * time.Millisecond)
|
|
store.ObserveVideo(VideoFrame{})
|
|
|
|
if got := store.Snapshot().Video.ReceivedFPS; got != 3 {
|
|
t.Fatalf("received FPS = %v, want 3", got)
|
|
}
|
|
}
|
|
|
|
func TestMediaStatsStoreObservesAudio(t *testing.T) {
|
|
store := NewMediaStatsStore()
|
|
store.ObserveAudio(AudioFrame{
|
|
Index: 100,
|
|
SampleCount: 480,
|
|
Channels: 2,
|
|
Label: "Programme audio",
|
|
SampleRateNumerator: 48_000,
|
|
SampleRateDenominator: 1,
|
|
})
|
|
|
|
got := store.Snapshot().Audio
|
|
if !got.Available || got.Label != "Programme audio" || got.Index != 100 {
|
|
t.Fatalf("audio stats = %#v", got)
|
|
}
|
|
if got.SampleRateHz != 48_000 || got.Channels != 2 || got.SampleCount != 480 {
|
|
t.Fatalf("audio format = %#v", got)
|
|
}
|
|
if got.BatchDuration != 10*time.Millisecond {
|
|
t.Fatalf("batch duration = %v, want 10ms", got.BatchDuration)
|
|
}
|
|
}
|
|
|
|
type recordingVideoStatsSink struct{ frame VideoFrame }
|
|
|
|
func (s *recordingVideoStatsSink) ConsumeVideo(_ context.Context, frame VideoFrame) error {
|
|
s.frame = frame
|
|
return nil
|
|
}
|
|
|
|
type recordingAudioStatsSink struct{ frame AudioFrame }
|
|
|
|
func (s *recordingAudioStatsSink) ConsumeAudio(_ context.Context, frame AudioFrame) error {
|
|
s.frame = frame
|
|
return nil
|
|
}
|
|
|
|
func TestStatsSinksObserveAndForwardFrames(t *testing.T) {
|
|
store := NewMediaStatsStore()
|
|
videoDownstream := &recordingVideoStatsSink{}
|
|
audioDownstream := &recordingAudioStatsSink{}
|
|
video := VideoFrame{Index: 7, Label: "video"}
|
|
audio := AudioFrame{Index: 8, Label: "audio"}
|
|
|
|
if err := (VideoStatsSink{Stats: store, Sink: videoDownstream}).ConsumeVideo(context.Background(), video); err != nil {
|
|
t.Fatalf("ConsumeVideo() error = %v", err)
|
|
}
|
|
if err := (AudioStatsSink{Stats: store, Sink: audioDownstream}).ConsumeAudio(context.Background(), audio); err != nil {
|
|
t.Fatalf("ConsumeAudio() error = %v", err)
|
|
}
|
|
if videoDownstream.frame.Index != video.Index || audioDownstream.frame.Index != audio.Index {
|
|
t.Fatalf("forwarded frames = %#v, %#v", videoDownstream.frame, audioDownstream.frame)
|
|
}
|
|
snapshot := store.Snapshot()
|
|
if snapshot.Video.Label != "video" || snapshot.Audio.Label != "audio" {
|
|
t.Fatalf("stats snapshot = %#v", snapshot)
|
|
}
|
|
}
|