package video import ( "bytes" "context" "fmt" "os/exec" "strconv" "strings" ) // RemuxArgs builds the ffmpeg arguments for a lossless copy into a faststart // mp4. Streams are limited to the first video and optional first audio track; // subtitles and extra tracks are dropped so players do not stumble on them. func RemuxArgs(in, out string) []string { return []string{ "-hide_banner", "-nostdin", "-y", "-i", in, "-map", "0:v:0", "-map", "0:a:0?", "-c", "copy", "-movflags", "+faststart", out, } } // TranscodeArgs builds the ffmpeg arguments for a browser-safe h264/aac mp4. // The video is scaled to fit maxLongSide without upscaling and forced to // even dimensions; ffmpeg applies display rotation during re-encoding. func TranscodeArgs(in, out string, maxLongSide int) []string { scale := fmt.Sprintf( "scale=w='min(%d,iw)':h='min(%d,ih)':force_original_aspect_ratio=decrease:force_divisible_by=2", maxLongSide, maxLongSide) return []string{ "-hide_banner", "-nostdin", "-y", "-i", in, "-map", "0:v:0", "-map", "0:a:0?", "-c:v", "libx264", "-preset", "veryfast", "-crf", "23", "-pix_fmt", "yuv420p", "-vf", scale, "-c:a", "aac", "-b:a", "128k", "-movflags", "+faststart", out, } } // PosterArgs builds the ffmpeg arguments to extract a single poster frame at // atSecond, already scaled so downstream image pipelines decode a bounded frame. func PosterArgs(in, out string, atSecond int) []string { scale := fmt.Sprintf("scale=w='min(%d,iw)':h=-2", MaxLongSide) return []string{ "-hide_banner", "-nostdin", "-y", "-ss", strconv.Itoa(atSecond), "-i", in, "-frames:v", "1", "-vf", scale, "-f", "image2", out, } } // Run executes ffmpeg with the given arguments and returns an error carrying // the captured stderr on failure. func Run(ctx context.Context, ffmpegPath string, args ...string) error { cmd := exec.CommandContext(ctx, ffmpegPath, args...) var errb bytes.Buffer cmd.Stderr = &errb if err := cmd.Run(); err != nil { return fmt.Errorf("ffmpeg %s: %w: %s", strings.Join(args, " "), err, strings.TrimSpace(errb.String())) } return nil }