datarhei-dragonfork-core/app/webrtc/ffmpeg_args_test.go
Zac Gaetano 16ae17d2a1 feat(app/webrtc): port allocator + FFmpeg arg builder
Adds Alloc(), the ephemeral loopback UDP port grabber the subsystem
uses to pick the RTP port it will hand to FFmpeg and then re-bind with
core/webrtc.NewSourceOn. Covered by a 100x rebind test.

Adds BuildArgs(), which emits the -f rtp output fragments (video on
the passed port, audio on port+1) with copy codecs by default and an
H.264 baseline / libopus re-encode leg when ForceTranscode is set.
Covered by three unit tests.
2026-04-17 09:52:09 -04:00

89 lines
2.3 KiB
Go

package webrtc
import (
"strings"
"testing"
appcfg "github.com/datarhei/core/v16/restream/app"
)
func TestBuildArgs_CopyCodecs(t *testing.T) {
cfg := appcfg.ConfigWebRTC{Enabled: true, VideoPT: 102, AudioPT: 111}
got := BuildArgs(cfg, 49200)
// Must contain -c:v copy and -c:a copy when ForceTranscode is false.
if !contains(got, "-c:v", "copy") {
t.Fatalf("expected -c:v copy, got %v", got)
}
if !contains(got, "-c:a", "copy") {
t.Fatalf("expected -c:a copy, got %v", got)
}
// Two UDP addresses, one per track, with port+1 for audio.
if !any(got, "udp://127.0.0.1:49200?") {
t.Fatalf("expected video udp on 49200, got %v", got)
}
if !any(got, "udp://127.0.0.1:49201?") {
t.Fatalf("expected audio udp on 49201, got %v", got)
}
// Payload types must be stringified.
if !contains(got, "-payload_type", "102") {
t.Fatalf("expected video PT 102, got %v", got)
}
if !contains(got, "-payload_type", "111") {
t.Fatalf("expected audio PT 111, got %v", got)
}
}
func TestBuildArgs_ForceTranscode(t *testing.T) {
cfg := appcfg.ConfigWebRTC{Enabled: true, VideoPT: 102, AudioPT: 111, ForceTranscode: true}
got := BuildArgs(cfg, 49200)
if !contains(got, "-c:v", "libx264") {
t.Fatalf("expected -c:v libx264, got %v", got)
}
if !contains(got, "-profile:v", "baseline") {
t.Fatalf("expected baseline profile, got %v", got)
}
if !contains(got, "-c:a", "libopus") {
t.Fatalf("expected -c:a libopus, got %v", got)
}
}
func TestBuildArgs_TwoTrackBoundary(t *testing.T) {
cfg := appcfg.ConfigWebRTC{Enabled: true, VideoPT: 102, AudioPT: 111}
got := BuildArgs(cfg, 49200)
// The second `-map` marks the start of the audio leg — the split
// point restream.AppendOutput callers use.
mapCount := 0
for _, a := range got {
if a == "-map" {
mapCount++
}
}
if mapCount != 2 {
t.Fatalf("expected exactly 2 -map tokens, got %d in %v", mapCount, got)
}
}
// contains reports whether the two-token sequence appears consecutively.
func contains(haystack []string, a, b string) bool {
for i := 0; i+1 < len(haystack); i++ {
if haystack[i] == a && haystack[i+1] == b {
return true
}
}
return false
}
// any reports whether any element of haystack starts with prefix.
func any(haystack []string, prefix string) bool {
for _, h := range haystack {
if strings.HasPrefix(h, prefix) {
return true
}
}
return false
}