package playback import ( "slices" "strings" "testing" ) func TestIsPGS(t *testing.T) { cases := []struct { codec string want bool }{ {"pgs", true}, {"hdmv_pgs_subtitle", true}, {"HDMV_PGS_SUBTITLE", true}, {"dvd_subtitle", false}, {"dvb_subtitle", false}, {"subrip", false}, {"ass", false}, {"", false}, } for _, tc := range cases { if got := IsPGS(tc.codec); got != tc.want { t.Errorf("IsPGS(%q) = %v, want %v", tc.codec, got, tc.want) } } } func TestStreamExtractOutput(t *testing.T) { cases := []struct { codec string wantCodec string wantFormat string }{ {"ass", "copy", "ass"}, {"ssa", "copy", "ass"}, {"pgs", "copy", "sup"}, {"hdmv_pgs_subtitle", "copy", "sup"}, {"subrip", "webvtt", "webvtt"}, {"mov_text", "webvtt", "webvtt"}, } for _, tc := range cases { outCodec, outFormat := streamExtractOutput(tc.codec) if outCodec != tc.wantCodec || outFormat != tc.wantFormat { t.Errorf("streamExtractOutput(%q) = (%q, %q), want (%q, %q)", tc.codec, outCodec, outFormat, tc.wantCodec, tc.wantFormat) } } } func TestStreamExtractArgs_TextCodecIsWindowed(t *testing.T) { args := streamExtractArgs(StreamExtractOpts{ InputPath: "/media/movie.mkv", TrackIndex: 2, SourceCodec: "subrip", SeekSeconds: 120, DurationSeconds: 600, }) joined := strings.Join(args, " ") if !strings.Contains(joined, "-ss 120.000") { t.Fatalf("text extract should seek the input: %s", joined) } if !strings.Contains(joined, "-t 600.000") { t.Fatalf("text extract should cap the read duration: %s", joined) } if !strings.Contains(joined, "-copyts") { t.Fatalf("seeked extract must preserve source timestamps: %s", joined) } if !strings.Contains(joined, "-c:s webvtt") || !strings.Contains(joined, "-f webvtt") { t.Fatalf("text extract should transmux to WebVTT: %s", joined) } } // ASS and PGS streams are fetched once and consumed whole by their // client-side renderers, so seek/duration windowing must never apply even // when the handler passes nonzero values. func TestStreamExtractArgs_WholeTrackCodecsIgnoreWindow(t *testing.T) { for _, codec := range []string{"ass", "hdmv_pgs_subtitle"} { args := streamExtractArgs(StreamExtractOpts{ InputPath: "/media/movie.mkv", TrackIndex: 0, SourceCodec: codec, SeekSeconds: 120, DurationSeconds: 600, }) if slices.Contains(args, "-ss") { t.Errorf("%s extract must not seek the input: %v", codec, args) } if slices.Contains(args, "-t") { t.Errorf("%s extract must not cap the read duration: %v", codec, args) } if !slices.Contains(args, "copy") { t.Errorf("%s extract must copy the source stream: %v", codec, args) } } } func TestStreamExtractArgs_PGSProducesSup(t *testing.T) { args := streamExtractArgs(StreamExtractOpts{ InputPath: "/media/movie.mkv", TrackIndex: 1, SourceCodec: "pgs", }) joined := strings.Join(args, " ") if !strings.Contains(joined, "-map 0:s:1 -c:s copy -f sup pipe:1") { t.Fatalf("PGS extract should copy into a sup stream: %s", joined) } }