datarhei-dragonfork-core/app/webrtc/ffmpeg_args_test.go
Zac Gaetano 49677fbd3d
Some checks failed
tests / build (push) Failing after 2s
tests / build (pull_request) Failing after 1s
fix(webrtc): make WebRTC FFmpeg stream maps configurable (closes #2)
BuildArgs hardcoded -map 0✌️0 / -map 0🅰️0 for the two RTP legs.
Correct for production RTMP/SRT publishers (single combined input),
but breaks any process whose audio lives on a different input index
— multi-input lavfi test scaffolds, multi-camera pipelines, SDI +
file-audio mixes, etc.

Adds VideoMap and AudioMap fields to ConfigWebRTC (and the API DTO),
defaulting to the prior literals so existing deployments are
unaffected. BuildArgs reads them.

Tests:
- TestBuildArgs_DefaultMaps locks the empty-string default behavior
- TestBuildArgs_CustomMaps drives the multi-input override path
- TestProcessConfigWebRTCMapsRoundtrip extends the DTO roundtrip

Closes #2.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-03 12:10:51 +00:00

132 lines
3.8 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
}
// TestBuildArgs_DefaultMaps confirms 0:v:0 / 0:a:0 are emitted when
// VideoMap / AudioMap are empty (regression on the fix for issue #2 —
// the prior version had these as hardcoded literals; if VideoMap is
// ever empty unexpectedly, BuildArgs must still produce a working
// command line).
func TestBuildArgs_DefaultMaps(t *testing.T) {
cfg := appcfg.ConfigWebRTC{Enabled: true, VideoPT: 102, AudioPT: 111}
got := BuildArgs(cfg, 50000)
if !contains(got, "-map", "0:v:0") {
t.Fatalf("expected default video map 0:v:0, got %v", got)
}
if !contains(got, "-map", "0:a:0") {
t.Fatalf("expected default audio map 0:a:0, got %v", got)
}
}
// TestBuildArgs_CustomMaps drives the issue-#2 fix: when the user
// configures a multi-input pipeline (audio on input #1, etc.), the
// emitted -map values must follow the user's choice rather than the
// "0:v:0"/"0:a:0" assumption.
func TestBuildArgs_CustomMaps(t *testing.T) {
cfg := appcfg.ConfigWebRTC{
Enabled: true,
VideoPT: 102,
AudioPT: 111,
VideoMap: "0:v:1",
AudioMap: "1:a:0",
}
got := BuildArgs(cfg, 50000)
if !contains(got, "-map", "0:v:1") {
t.Fatalf("expected custom video map 0:v:1, got %v", got)
}
if !contains(got, "-map", "1:a:0") {
t.Fatalf("expected custom audio map 1:a:0, got %v", got)
}
// The default literals should NOT appear when overridden.
for _, opt := range got {
if opt == "0:v:0" || opt == "0:a:0" {
t.Errorf("expected no default maps in output, found %q in %v", opt, got)
}
}
}