Implement DVD format FFmpeg codec selection and settings

Critical fix: When a DVD format (NTSC or PAL) is selected, now properly
override the video and audio codec to use DVD-compliant standards:

Video:
- Forces MPEG-2 codec (mpeg2video)
- NTSC: 6000k bitrate, 9000k max, gop=15
- PAL: 8000k bitrate, 9500k max, gop=12

Audio:
- Forces AC-3 codec for DVD container compatibility
- 192 kbps bitrate
- 48 kHz sample rate (DVD standard)
- Stereo channels (2)

This ensures that selecting a DVD format produces DVDStyler-compatible
MPEG files without codec errors. Previously, the code was using the
default H.264 + AAC, which caused 'unsupported audio codec' errors
when trying to write to MPEG container.

Fixes the issue where DVD conversions were failing with:
  'Unsupported audio codec. Must be one of mp1, mp2, mp3, 16-bit pcm_dvd,
   pcm_s16be, ac3 or dts.'
This commit is contained in:
Stu Leak 2025-11-29 20:28:12 -05:00
parent 3e0d30d425
commit deb2a27796

46
main.go
View File

@ -890,19 +890,38 @@ func (s *appState) executeConvertJob(ctx context.Context, job *queue.Job, progre
args = append(args, "-vf", strings.Join(vf, ",")) args = append(args, "-vf", strings.Join(vf, ","))
} }
// Check if this is a DVD format (special handling required)
selectedFormat, _ := cfg["selectedFormat"].(formatOption)
isDVD := selectedFormat.Ext == ".mpg"
// Video codec // Video codec
videoCodec, _ := cfg["videoCodec"].(string) videoCodec, _ := cfg["videoCodec"].(string)
if videoCodec == "Copy" { if videoCodec == "Copy" && !isDVD {
args = append(args, "-c:v", "copy") args = append(args, "-c:v", "copy")
} else { } else {
// Determine the actual codec to use // Determine the actual codec to use
actualCodec := determineVideoCodec(convertConfig{ var actualCodec string
if isDVD {
// DVD requires MPEG-2 video
actualCodec = "mpeg2video"
} else {
actualCodec = determineVideoCodec(convertConfig{
VideoCodec: videoCodec, VideoCodec: videoCodec,
HardwareAccel: hardwareAccel, HardwareAccel: hardwareAccel,
}) })
}
args = append(args, "-c:v", actualCodec) args = append(args, "-c:v", actualCodec)
// Bitrate mode and quality // DVD-specific video settings
if isDVD {
// NTSC vs PAL settings
if strings.Contains(selectedFormat.Label, "NTSC") {
args = append(args, "-b:v", "6000k", "-maxrate", "9000k", "-bufsize", "1835k", "-g", "15")
} else if strings.Contains(selectedFormat.Label, "PAL") {
args = append(args, "-b:v", "8000k", "-maxrate", "9500k", "-bufsize", "2228k", "-g", "12")
}
} else {
// Standard bitrate mode and quality for non-DVD
bitrateMode, _ := cfg["bitrateMode"].(string) bitrateMode, _ := cfg["bitrateMode"].(string)
if bitrateMode == "CRF" || bitrateMode == "" { if bitrateMode == "CRF" || bitrateMode == "" {
crfStr, _ := cfg["crf"].(string) crfStr, _ := cfg["crf"].(string)
@ -933,15 +952,28 @@ func (s *appState) executeConvertJob(ctx context.Context, job *queue.Job, progre
args = append(args, "-pix_fmt", pixelFormat) args = append(args, "-pix_fmt", pixelFormat)
} }
} }
}
// Audio codec and settings // Audio codec and settings
audioCodec, _ := cfg["audioCodec"].(string) audioCodec, _ := cfg["audioCodec"].(string)
if audioCodec == "Copy" { if audioCodec == "Copy" && !isDVD {
args = append(args, "-c:a", "copy") args = append(args, "-c:a", "copy")
} else { } else {
actualAudioCodec := determineAudioCodec(convertConfig{AudioCodec: audioCodec}) var actualAudioCodec string
if isDVD {
// DVD requires AC-3 audio
actualAudioCodec = "ac3"
} else {
actualAudioCodec = determineAudioCodec(convertConfig{AudioCodec: audioCodec})
}
args = append(args, "-c:a", actualAudioCodec) args = append(args, "-c:a", actualAudioCodec)
// DVD-specific audio settings
if isDVD {
// DVD standard: AC-3 stereo at 48 kHz, 192 kbps
args = append(args, "-b:a", "192k", "-ar", "48000", "-ac", "2")
} else {
// Standard audio settings for non-DVD
if audioBitrate, _ := cfg["audioBitrate"].(string); audioBitrate != "" && actualAudioCodec != "flac" { if audioBitrate, _ := cfg["audioBitrate"].(string); audioBitrate != "" && actualAudioCodec != "flac" {
args = append(args, "-b:a", audioBitrate) args = append(args, "-b:a", audioBitrate)
} }
@ -957,6 +989,7 @@ func (s *appState) executeConvertJob(ctx context.Context, job *queue.Job, progre
} }
} }
} }
}
// Map cover art // Map cover art
if hasCoverArt { if hasCoverArt {
@ -965,8 +998,7 @@ func (s *appState) executeConvertJob(ctx context.Context, job *queue.Job, progre
args = append(args, "-disposition:v:1", "attached_pic") args = append(args, "-disposition:v:1", "attached_pic")
} }
// Format-specific settings // Format-specific settings (already parsed above for DVD check)
var selectedFormat formatOption
switch v := cfg["selectedFormat"].(type) { switch v := cfg["selectedFormat"].(type) {
case formatOption: case formatOption:
selectedFormat = v selectedFormat = v