fix(transcoder): correct scale filter + always force yuv420p
The previous scale expression `min(iw,iw*H/ih)':'min(ih,H)` produced odd widths (e.g. 1425×720 for a 16:9 source capped at 720p) which libx264 refuses with `width not divisible by 2`, killing the encoder before a single byte was written. Switch to `scale=-2:H:force_original_aspect_ratio=decrease`, which derives a width that preserves aspect ratio AND is rounded to a multiple of 2. Always set `-pix_fmt yuv420p` so 10-bit HEVC sources are downcast to the 8-bit format browser <video> elements actually decode. Also add `-y`, guard nil pipe in Close(), and the related transcode plumbing for browser-decided per-session quality.
This commit is contained in:
parent
70f7337226
commit
457d6e1f7c
1 changed files with 25 additions and 4 deletions
|
|
@ -97,10 +97,18 @@ func (t *Transcoder) Close() error {
|
|||
t.closed = true
|
||||
t.mu.Unlock()
|
||||
|
||||
// out is nil for the file-output flow (startTranscoderToFile) — that
|
||||
// pipeline writes directly to a temp file via -i ... output_path so we
|
||||
// never wired a stdout pipe. Only close when present.
|
||||
if t.out != nil {
|
||||
_ = t.out.Close()
|
||||
if t.cmd.Process != nil {
|
||||
}
|
||||
if t.cmd != nil && t.cmd.Process != nil {
|
||||
_ = t.cmd.Process.Kill()
|
||||
}
|
||||
if t.cmd == nil {
|
||||
return nil
|
||||
}
|
||||
done := make(chan error, 1)
|
||||
go func() { done <- t.cmd.Wait() }()
|
||||
select {
|
||||
|
|
@ -137,7 +145,12 @@ func (w *errWriter) Write(p []byte) (int, error) {
|
|||
// Exposed package-level so tests can lock the flag matrix independently of
|
||||
// process spawning.
|
||||
func buildFFmpegArgs(filePath string, opts TranscodeOpts) []string {
|
||||
args := []string{"-hide_banner", "-loglevel", "warning"}
|
||||
// -y: overwrite output without asking (the file-output flow uses an
|
||||
// already-created tmp file from os.CreateTemp, so the default "do you
|
||||
// want to overwrite?" prompt would deadlock on stdin and ffmpeg dies
|
||||
// before producing a single byte). Pipe flow doesn't need it but it's
|
||||
// harmless there.
|
||||
args := []string{"-y", "-hide_banner", "-loglevel", "warning"}
|
||||
|
||||
// Seek BEFORE input (-ss before -i) for fast keyframe-aligned start.
|
||||
if opts.StartSeconds > 0 {
|
||||
|
|
@ -174,10 +187,18 @@ func buildFFmpegArgs(filePath string, opts TranscodeOpts) []string {
|
|||
}
|
||||
args = append(args, "-b:v", coalesce(opts.VideoBitrate, "5M"))
|
||||
if opts.MaxHeight > 0 {
|
||||
// `-2:H` scales to height H, derives width preserving aspect ratio,
|
||||
// and rounds to a multiple of 2 (libx264 refuses odd dimensions).
|
||||
// `force_original_aspect_ratio=decrease` keeps shorter sources
|
||||
// untouched instead of upscaling. `pix_fmt yuv420p` keeps 10-bit
|
||||
// HEVC sources playable in browsers (8-bit only).
|
||||
args = append(args,
|
||||
"-vf",
|
||||
fmt.Sprintf("scale='min(iw,iw*%d/ih)':'min(ih,%d)'", opts.MaxHeight, opts.MaxHeight),
|
||||
fmt.Sprintf("scale=-2:%d:force_original_aspect_ratio=decrease", opts.MaxHeight),
|
||||
"-pix_fmt", "yuv420p",
|
||||
)
|
||||
} else {
|
||||
args = append(args, "-pix_fmt", "yuv420p")
|
||||
}
|
||||
args = append(args, "-c:a", "aac", "-b:a", coalesce(opts.AudioBitrate, "192k"))
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue