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) } } }