harden ffmpeg pipeline failure handling and stream mapping

This commit is contained in:
2026-04-21 23:07:48 +02:00
parent beb6ce6cbb
commit d65dc182f8
6 changed files with 173 additions and 14 deletions

View File

@@ -323,17 +323,7 @@ func (d *Downloader) streamManifestWithFFmpeg(ctx context.Context, sourceURL, ou
return fmt.Errorf("ffmpeg not found for manifest stream: %w", err)
}
args := []string{
"-y",
"-protocol_whitelist", "file,http,https,tcp,tls,crypto,data",
"-i", sourceURL,
}
if includeVideo {
args = append(args, "-map", "0")
} else {
args = append(args, "-map", "0:a:0")
}
args = append(args, "-c", "copy", "-hide_banner", "-nostats", "-progress", "pipe:2", outputPath)
args := buildFFmpegStreamArgs(sourceURL, outputPath, includeVideo)
if !d.ProgressEnabled() {
cmd := exec.CommandContext(ctx, "ffmpeg", args...)
@@ -458,6 +448,24 @@ func (d *Downloader) streamManifestWithFFmpeg(ctx context.Context, sourceURL, ou
return nil
}
func buildFFmpegStreamArgs(sourceURL, outputPath string, includeVideo bool) []string {
args := []string{
"-y",
"-protocol_whitelist", "file,http,https,tcp,tls,crypto,data",
"-i", sourceURL,
}
if includeVideo {
args = append(args,
"-map", "0:v:0?",
"-map", "0:a:0?",
)
} else {
args = append(args, "-map", "0:a:0")
}
args = append(args, "-c", "copy", "-hide_banner", "-nostats", "-progress", "pipe:2", outputPath)
return args
}
type scanState struct {
totalMS int64
currentMS int64

View File

@@ -260,3 +260,38 @@ func TestParseClockDurationMSInvalid(t *testing.T) {
t.Fatalf("expected short duration to fail")
}
}
func TestBuildFFmpegStreamArgsAudioOnly(t *testing.T) {
args := buildFFmpegStreamArgs("https://example.com/master.m3u8", "/tmp/out.m4a", false)
if !containsArgPair(args, "-map", "0:a:0") {
t.Fatalf("expected audio map in args: %v", args)
}
if containsArgPair(args, "-map", "0:v:0?") {
t.Fatalf("did not expect video map in audio-only args: %v", args)
}
if containsArgPair(args, "-map", "0") {
t.Fatalf("did not expect broad map=0 in args: %v", args)
}
}
func TestBuildFFmpegStreamArgsIncludeVideo(t *testing.T) {
args := buildFFmpegStreamArgs("https://example.com/master.m3u8", "/tmp/out.mp4", true)
if !containsArgPair(args, "-map", "0:v:0?") {
t.Fatalf("expected video map in args: %v", args)
}
if !containsArgPair(args, "-map", "0:a:0?") {
t.Fatalf("expected audio map in args: %v", args)
}
if containsArgPair(args, "-map", "0") {
t.Fatalf("did not expect broad map=0 in args: %v", args)
}
}
func containsArgPair(args []string, key, value string) bool {
for i := 0; i+1 < len(args); i++ {
if args[i] == key && args[i+1] == value {
return true
}
}
return false
}