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>
132 lines
3.8 KiB
Go
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)
|
|
}
|
|
}
|
|
}
|