From 900305685cb27fc7fccd105c5f94d9862feefab1 Mon Sep 17 00:00:00 2001 From: Nodude <75137537+NodudeWasTaken@users.noreply.github.com> Date: Wed, 22 Apr 2026 19:44:51 +0200 Subject: [PATCH] pkg/ffmpeg: refactor codec args and hardware API Move the per-codec CodecInit flag groups to VideoCodec.ExtraArgs and add ExtraArgsHQ(preset, crf) for quality-oriented encoding. Neither emits -c:v; callers set that explicitly. Rename hw* methods to HW*, change HWCanFullHWTranscode / HWMaxResFilter to take path/width/height primitives, and flip the HWCodec*Compatible helpers to take a default codec. Merge ScaleMaxLM into a single 5-arg ScaleMax. --- pkg/ffmpeg/codec.go | 147 +++++++++++++++++++++++++++++++++ pkg/ffmpeg/codec_hardware.go | 83 ++++++++++--------- pkg/ffmpeg/filter.go | 63 ++++++-------- pkg/ffmpeg/stream_segmented.go | 23 +++--- pkg/ffmpeg/stream_transcode.go | 96 +++------------------ 5 files changed, 240 insertions(+), 172 deletions(-) diff --git a/pkg/ffmpeg/codec.go b/pkg/ffmpeg/codec.go index 45fff9ffb..d7c028841 100644 --- a/pkg/ffmpeg/codec.go +++ b/pkg/ffmpeg/codec.go @@ -1,5 +1,7 @@ package ffmpeg +import "strconv" + type VideoCodec struct { Name string // The full name of the codec including profile/quality CodeName string // The core codec name without profile/quality suffix @@ -44,3 +46,148 @@ var ( AudioCodecLibOpus AudioCodec = "libopus" AudioCodecCopy AudioCodec = "copy" ) + +func (c VideoCodec) ExtraArgs() (args Args) { + switch c { + // CPU Codecs + case VideoCodecLibX264: + args = append(args, + "-pix_fmt", "yuv420p", + "-preset", "veryfast", + "-crf", "25", + "-sc_threshold", "0", + ) + case VideoCodecVP9: + args = append(args, + "-pix_fmt", "yuv420p", + "-deadline", "realtime", + "-cpu-used", "5", + "-row-mt", "1", + "-crf", "30", + "-b:v", "0", + ) + // HW Codecs + case VideoCodecN264: + args = append(args, + "-rc", "vbr", + "-cq", "15", + ) + case VideoCodecN264H: + args = append(args, + "-tune", "hq", + "-profile", "high", + "-rc", "vbr", + "-rc-lookahead", "60", + "-surfaces", "64", + "-spatial-aq", "1", + "-aq-strength", "15", + "-cq", "15", + "-coder", "cabac", + "-b_ref_mode", "middle", + ) + case VideoCodecI264, VideoCodecIVP9: + args = append(args, + "-global_quality", "20", + "-preset", "faster", + ) + case VideoCodecI264C: + args = append(args, + "-q", "20", + "-preset", "faster", + ) + case VideoCodecV264, VideoCodecVVP9: + args = append(args, + "-qp", "20", + ) + case VideoCodecA264: + args = append(args, + "-quality", "speed", + ) + case VideoCodecM264: + args = append(args, + "-realtime", "1", + ) + case VideoCodecO264: + args = append(args, + "-preset", "superfast", + "-crf", "25", + ) + } + + return args +} + +func (c VideoCodec) ExtraArgsHQ(preset string, crf int) (args Args) { + switch c { + // CPU Codecs + case VideoCodecLibX264: + args = append(args, + "-pix_fmt", "yuv420p", + "-profile:v", "high", + "-level", "4.2", + "-preset", preset, + "-crf", strconv.Itoa(crf), + "-sc_threshold", "0", + "-threads", "4", + "-sws_flags", "lanczos", + ) + case VideoCodecVP9: + args = append(args, + "-pix_fmt", "yuv420p", + "-deadline", "good", + "-cpu-used", "0", + "-row-mt", "1", + "-crf", strconv.Itoa(crf), + "-b:v", "0", + "-threads", "4", + ) + // HW Codecs - fixed HQ quality. + case VideoCodecN264: + args = append(args, + "-rc", "vbr", + "-cq", "10", + ) + case VideoCodecN264H: + args = append(args, + "-tune", "hq", + "-profile", "high", + "-rc", "vbr", + "-rc-lookahead", "60", + "-surfaces", "64", + "-spatial-aq", "1", + "-aq-strength", "15", + "-cq", "10", + "-coder", "cabac", + "-b_ref_mode", "middle", + ) + case VideoCodecI264, VideoCodecIVP9: + args = append(args, + "-global_quality", "15", + "-preset", "slow", + ) + case VideoCodecI264C: + args = append(args, + "-q", "15", + "-preset", "slow", + ) + case VideoCodecV264, VideoCodecVVP9: + args = append(args, + "-qp", "15", + ) + case VideoCodecA264: + args = append(args, + "-quality", "quality", + ) + case VideoCodecM264: + args = append(args, + "-realtime", "0", + ) + case VideoCodecO264: + args = append(args, + "-preset", "slow", + "-crf", strconv.Itoa(crf), + ) + } + + return args +} diff --git a/pkg/ffmpeg/codec_hardware.go b/pkg/ffmpeg/codec_hardware.go index 66480c5bb..d6986f8af 100644 --- a/pkg/ffmpeg/codec_hardware.go +++ b/pkg/ffmpeg/codec_hardware.go @@ -12,7 +12,6 @@ import ( "time" "github.com/stashapp/stash/pkg/logger" - "github.com/stashapp/stash/pkg/models" ) var ( @@ -80,15 +79,16 @@ func (f *FFMpeg) initHWSupport(ctx context.Context) { var args Args args = append(args, "-hide_banner") args = args.LogLevel(LogLevelWarning) - args = f.hwDeviceInit(args, codec, false) + args = f.HWDeviceInit(args, codec, false) args = args.Format("lavfi") - vFile := &models.VideoFile{Width: 1280, Height: 720} - args = args.Input(fmt.Sprintf("color=c=red:s=%dx%d", vFile.Width, vFile.Height)) + width, height := 1280, 720 + args = args.Input(fmt.Sprintf("color=c=red:s=%dx%d", width, height)) args = args.Duration(0.1) // Test scaling - videoFilter := f.hwMaxResFilter(codec, vFile, minHeight, false) - args = append(args, CodecInit(codec)...) + videoFilter := f.HWMaxResFilter(codec, width, height, minHeight, false) + args = args.VideoCodec(codec) + args = append(args, codec.ExtraArgs()...) args = args.VideoFilter(videoFilter) args = args.Format("null") @@ -144,7 +144,7 @@ func (f *FFMpeg) initHWSupport(ctx context.Context) { f.hwCodecSupport = hwCodecSupport } -func (f *FFMpeg) hwCanFullHWTranscode(ctx context.Context, codec VideoCodec, vf *models.VideoFile, reqHeight int) bool { +func (f *FFMpeg) HWCanFullHWTranscode(ctx context.Context, codec VideoCodec, path string, width int, height int, reqHeight int) bool { if codec == VideoCodecCopy { return false } @@ -153,12 +153,13 @@ func (f *FFMpeg) hwCanFullHWTranscode(ctx context.Context, codec VideoCodec, vf args = append(args, "-hide_banner") args = args.LogLevel(LogLevelWarning) args = args.XError() - args = f.hwDeviceInit(args, codec, true) - args = args.Input(vf.Path) + args = f.HWDeviceInit(args, codec, true) + args = args.Input(path) args = args.Duration(1) - videoFilter := f.hwMaxResFilter(codec, vf, reqHeight, true) - args = append(args, CodecInit(codec)...) + videoFilter := f.HWMaxResFilter(codec, width, height, reqHeight, true) + args = args.VideoCodec(codec) + args = append(args, codec.ExtraArgs()...) args = args.VideoFilter(videoFilter) args = args.Format("null") @@ -176,7 +177,7 @@ func (f *FFMpeg) hwCanFullHWTranscode(ctx context.Context, codec VideoCodec, vf errOutput = err.Error() } - logger.Debugf("[InitHWSupport] Full hardware transcode for file %s not supported. Error output:\n%s", vf.Basename, errOutput) + logger.Debugf("[InitHWSupport] Full hardware transcode for file %s not supported. Error output:\n%s", path, errOutput) return false } @@ -184,7 +185,7 @@ func (f *FFMpeg) hwCanFullHWTranscode(ctx context.Context, codec VideoCodec, vf } // Prepend input for hardware encoding only -func (f *FFMpeg) hwDeviceInit(args Args, toCodec VideoCodec, fullhw bool) Args { +func (f *FFMpeg) HWDeviceInit(args Args, toCodec VideoCodec, fullhw bool) Args { // check for custom /dev/dri device #6435 driDevice := os.Getenv("STASH_HW_DRI_DEVICE") if driDevice == "" { @@ -257,7 +258,7 @@ func (f *FFMpeg) hwDeviceInit(args Args, toCodec VideoCodec, fullhw bool) Args { } // Initialise a video filter for HW encoding -func (f *FFMpeg) hwFilterInit(toCodec VideoCodec, fullhw bool) VideoFilter { +func (f *FFMpeg) HWFilterInit(toCodec VideoCodec, fullhw bool) VideoFilter { var videoFilter VideoFilter switch toCodec { case VideoCodecV264, @@ -298,7 +299,7 @@ func (f *FFMpeg) hwFilterInit(toCodec VideoCodec, fullhw bool) VideoFilter { var scaler_re = regexp.MustCompile(`scale=(?P([-\d]+):([-\d]+))`) -func templateReplaceScale(input string, template string, match []int, vf *models.VideoFile, minusonehack bool) string { +func templateReplaceScale(input string, template string, match []int, width, height int, minusonehack bool) string { result := []byte{} if minusonehack { @@ -315,7 +316,7 @@ func templateReplaceScale(input string, template string, match []int, vf *models } // Calculate ratio - ratio := float64(vf.Width) / float64(vf.Height) + ratio := float64(width) / float64(height) if w < 0 { w = int(math.Round(float64(h) * ratio)) } else if h < 0 { @@ -342,19 +343,19 @@ func templateReplaceScale(input string, template string, match []int, vf *models } // Replace video filter scaling with hardware scaling for full hardware transcoding (also fixes the format) -func (f *FFMpeg) hwCodecFilter(args VideoFilter, codec VideoCodec, vf *models.VideoFile, fullhw bool) VideoFilter { +func (f *FFMpeg) HWCodecFilter(args VideoFilter, codec VideoCodec, width, height int, fullhw bool) VideoFilter { sargs := string(args) match := scaler_re.FindStringSubmatchIndex(sargs) if match == nil { - return f.hwApplyFullHWFilter(args, codec, fullhw) + return f.HWApplyFullHWFilter(args, codec, fullhw) } - return f.hwApplyScaleTemplate(sargs, codec, match, vf, fullhw) + return f.HWApplyScaleTemplate(sargs, codec, match, width, height, fullhw) } // Apply format switching if applicable -func (f *FFMpeg) hwApplyFullHWFilter(args VideoFilter, codec VideoCodec, fullhw bool) VideoFilter { +func (f *FFMpeg) HWApplyFullHWFilter(args VideoFilter, codec VideoCodec, fullhw bool) VideoFilter { switch codec { case VideoCodecN264, VideoCodecN264H: if fullhw && f.version.Gteq(Version{major: 5}) { // Added in FFMpeg 5 @@ -380,7 +381,7 @@ func (f *FFMpeg) hwApplyFullHWFilter(args VideoFilter, codec VideoCodec, fullhw } // Switch scaler -func (f *FFMpeg) hwApplyScaleTemplate(sargs string, codec VideoCodec, match []int, vf *models.VideoFile, fullhw bool) VideoFilter { +func (f *FFMpeg) HWApplyScaleTemplate(sargs string, codec VideoCodec, match []int, width, height int, fullhw bool) VideoFilter { var template string switch codec { @@ -418,11 +419,11 @@ func (f *FFMpeg) hwApplyScaleTemplate(sargs string, codec VideoCodec, match []in // BUG: scale_vt doesn't call ff_scale_adjust_dimensions, thus cant accept negative size values isApple := codec == VideoCodecM264 // Rockchip's scale_rkrga supports -1/-2; don't apply minus-one hack here. - return VideoFilter(templateReplaceScale(sargs, template, match, vf, isIntel || isApple)) + return VideoFilter(templateReplaceScale(sargs, template, match, width, height, isIntel || isApple)) } // Returns the max resolution for a given codec, or a default -func (f *FFMpeg) hwCodecMaxRes(codec VideoCodec) (int, int) { +func (f *FFMpeg) HWCodecMaxRes(codec VideoCodec) (int, int) { switch codec { case VideoCodecRK264: return 8192, 8192 @@ -437,18 +438,18 @@ func (f *FFMpeg) hwCodecMaxRes(codec VideoCodec) (int, int) { } // Return a maxres filter -func (f *FFMpeg) hwMaxResFilter(toCodec VideoCodec, vf *models.VideoFile, reqHeight int, fullhw bool) VideoFilter { - if vf.Width == 0 || vf.Height == 0 { +func (f *FFMpeg) HWMaxResFilter(toCodec VideoCodec, width int, height int, reqHeight int, fullhw bool) VideoFilter { + if width == 0 || height == 0 { return "" } - videoFilter := f.hwFilterInit(toCodec, fullhw) - maxWidth, maxHeight := f.hwCodecMaxRes(toCodec) - videoFilter = videoFilter.ScaleMaxLM(vf.Width, vf.Height, reqHeight, maxWidth, maxHeight) - return f.hwCodecFilter(videoFilter, toCodec, vf, fullhw) + videoFilter := f.HWFilterInit(toCodec, fullhw) + maxWidth, maxHeight := f.HWCodecMaxRes(toCodec) + videoFilter = videoFilter.ScaleMax(width, height, reqHeight, maxWidth, maxHeight) + return f.HWCodecFilter(videoFilter, toCodec, width, height, fullhw) } -// Return if a hardware accelerated for HLS is available -func (f *FFMpeg) hwCodecHLSCompatible() *VideoCodec { +// Return a hardware accelerated codec for HLS if available, otherwise the default +func (f *FFMpeg) HWCodecHLSCompatible(defaultCodec VideoCodec) VideoCodec { for _, element := range f.getHWCodecSupport() { switch element { case VideoCodecN264, @@ -459,14 +460,14 @@ func (f *FFMpeg) hwCodecHLSCompatible() *VideoCodec { VideoCodecR264, VideoCodecM264, // Note that the Apple encoder sucks at startup, thus HLS quality is crap VideoCodecRK264: - return &element + return element } } - return nil + return defaultCodec } -// Return if a hardware accelerated codec for MP4 is available -func (f *FFMpeg) hwCodecMP4Compatible() *VideoCodec { +// Return a hardware accelerated codec for MP4 if available, otherwise the default +func (f *FFMpeg) HWCodecMP4Compatible(defaultCodec VideoCodec) VideoCodec { for _, element := range f.getHWCodecSupport() { switch element { case VideoCodecN264, @@ -475,20 +476,20 @@ func (f *FFMpeg) hwCodecMP4Compatible() *VideoCodec { VideoCodecI264C, VideoCodecM264, VideoCodecRK264: - return &element + return element } } - return nil + return defaultCodec } -// Return if a hardware accelerated codec for WebM is available -func (f *FFMpeg) hwCodecWEBMCompatible() *VideoCodec { +// Return a hardware accelerated codec for WebM if available, otherwise the default +func (f *FFMpeg) HWCodecWEBMCompatible(defaultCodec VideoCodec) VideoCodec { for _, element := range f.getHWCodecSupport() { switch element { case VideoCodecIVP9, VideoCodecVVP9: - return &element + return element } } - return nil + return defaultCodec } diff --git a/pkg/ffmpeg/filter.go b/pkg/ffmpeg/filter.go index dd6ecc106..254076dca 100644 --- a/pkg/ffmpeg/filter.go +++ b/pkg/ffmpeg/filter.go @@ -36,51 +36,38 @@ func (f VideoFilter) ScaleMaxSize(maxDimensions int) VideoFilter { return f.Append(fmt.Sprintf("scale=%v:%v:force_original_aspect_ratio=decrease", maxDimensions, maxDimensions)) } -// ScaleMax returns a VideoFilter scaling to maxSize. It will scale width if it is larger than height, otherwise it will scale height. -func (f VideoFilter) ScaleMax(inputWidth, inputHeight, maxSize int) VideoFilter { - // get the smaller dimension of the input - videoSize := inputHeight - if inputWidth < videoSize { - videoSize = inputWidth +// ScaleMax scales to reqHeight (0 = source height), optionally clamped to a +// maxWidth x maxHeight rect. Aspect ratio is preserved. +func (f VideoFilter) ScaleMax(width, height, reqHeight, maxWidth, maxHeight int) VideoFilter { + // if a rect is given, clamp to whichever edge overshoots it by more + if maxWidth > 0 && maxHeight > 0 { + // projected dimensions at reqHeight (or source height if 0) + target := reqHeight + if target == 0 { + target = height + } + projectedWidth := target * width / height + + if target > maxHeight || projectedWidth > maxWidth { + // cap the edge that exceeds its limit by more + if target-maxHeight > projectedWidth-maxWidth { + return f.ScaleDimensions(-2, maxHeight) + } + return f.ScaleDimensions(maxWidth, -2) + } } - // if maxSize is larger than the video dimension, then no-op - if maxSize >= videoSize || maxSize == 0 { + // no-op if reqHeight is larger than the smaller dimension + if reqHeight == 0 || reqHeight >= min(width, height) { return f } - // we're setting either the width or height - // we'll set the smaller dimesion - if inputWidth > inputHeight { + // scale the smaller dimension to reqHeight + if width > height { // set the height - return f.ScaleDimensions(-2, maxSize) - } - - return f.ScaleDimensions(maxSize, -2) -} - -// ScaleMaxLM scales an image to fit within specified maximum dimensions while maintaining its aspect ratio. -func (f VideoFilter) ScaleMaxLM(width int, height int, reqHeight int, maxWidth int, maxHeight int) VideoFilter { - if maxWidth == 0 || maxHeight == 0 { - return f.ScaleMax(width, height, reqHeight) - } - - aspectRatio := float64(width) / float64(height) - desiredHeight := reqHeight - if desiredHeight == 0 { - desiredHeight = height - } - desiredWidth := int(float64(desiredHeight) * aspectRatio) - - if desiredHeight <= maxHeight && desiredWidth <= maxWidth { - return f.ScaleMax(width, height, reqHeight) - } - - if float64(desiredHeight-maxHeight) > float64(desiredWidth-maxWidth) { - return f.ScaleDimensions(-2, maxHeight) - } else { - return f.ScaleDimensions(maxWidth, -2) + return f.ScaleDimensions(-2, reqHeight) } + return f.ScaleDimensions(reqHeight, -2) } // Fps returns a VideoFilter setting the frames per second. diff --git a/pkg/ffmpeg/stream_segmented.go b/pkg/ffmpeg/stream_segmented.go index f35b960ab..85c95522a 100644 --- a/pkg/ffmpeg/stream_segmented.go +++ b/pkg/ffmpeg/stream_segmented.go @@ -65,7 +65,8 @@ var ( SegmentType: SegmentTypeTS, ServeManifest: serveHLSManifest, Args: func(codec VideoCodec, segment int, videoFilter VideoFilter, videoOnly bool, outputDir string) (args Args) { - args = CodecInit(codec) + args = args.VideoCodec(codec) + args = append(args, codec.ExtraArgs()...) args = append(args, "-flags", "+cgop", "-force_key_frames", fmt.Sprintf("expr:gte(t,n_forced*%d)", segmentLength), @@ -100,7 +101,8 @@ var ( SegmentType: SegmentTypeTS, ServeManifest: serveHLSManifest, Args: func(codec VideoCodec, segment int, videoFilter VideoFilter, videoOnly bool, outputDir string) (args Args) { - args = CodecInit(codec) + args = args.VideoCodec(codec) + args = append(args, codec.ExtraArgs()...) if videoOnly { args = append(args, "-an") } else { @@ -137,7 +139,8 @@ var ( init = "init" } - args = CodecInit(codec) + args = args.VideoCodec(codec) + args = append(args, codec.ExtraArgs()...) args = append(args, "-force_key_frames", fmt.Sprintf("expr:gte(t,n_forced*%d)", segmentLength), ) @@ -311,13 +314,13 @@ func HLSGetCodec(sm *StreamManager, name string) (codec VideoCodec) { switch name { case "hls": codec = VideoCodecLibX264 - if hwcodec := sm.encoder.hwCodecHLSCompatible(); hwcodec != nil && sm.config.GetTranscodeHardwareAcceleration() { - codec = *hwcodec + if sm.config.GetTranscodeHardwareAcceleration() { + codec = sm.encoder.HWCodecHLSCompatible(VideoCodecLibX264) } case "dash-v": codec = VideoCodecVP9 - if hwcodec := sm.encoder.hwCodecWEBMCompatible(); hwcodec != nil && sm.config.GetTranscodeHardwareAcceleration() { - codec = *hwcodec + if sm.config.GetTranscodeHardwareAcceleration() { + codec = sm.encoder.HWCodecWEBMCompatible(VideoCodecVP9) } case "hls-copy": codec = VideoCodecCopy @@ -335,8 +338,8 @@ func (s *runningStream) makeStreamArgs(sm *StreamManager, segment int) Args { codec := HLSGetCodec(sm, s.streamType.Name) - fullhw := sm.config.GetTranscodeHardwareAcceleration() && sm.encoder.hwCanFullHWTranscode(sm.context, codec, s.vf, s.maxTranscodeSize) - args = sm.encoder.hwDeviceInit(args, codec, fullhw) + fullhw := sm.config.GetTranscodeHardwareAcceleration() && sm.encoder.HWCanFullHWTranscode(sm.context, codec, s.vf.Path, s.vf.Width, s.vf.Height, s.maxTranscodeSize) + args = sm.encoder.HWDeviceInit(args, codec, fullhw) args = append(args, extraInputArgs...) if segment > 0 { @@ -347,7 +350,7 @@ func (s *runningStream) makeStreamArgs(sm *StreamManager, segment int) Args { videoOnly := ProbeAudioCodec(s.vf.AudioCodec) == MissingUnsupported - videoFilter := sm.encoder.hwMaxResFilter(codec, s.vf, s.maxTranscodeSize, fullhw) + videoFilter := sm.encoder.HWMaxResFilter(codec, s.vf.Width, s.vf.Height, s.maxTranscodeSize, fullhw) args = append(args, s.streamType.Args(codec, segment, videoFilter, videoOnly, s.outputDir)...) diff --git a/pkg/ffmpeg/stream_transcode.go b/pkg/ffmpeg/stream_transcode.go index bb701664f..41df28319 100644 --- a/pkg/ffmpeg/stream_transcode.go +++ b/pkg/ffmpeg/stream_transcode.go @@ -19,84 +19,12 @@ type StreamFormat struct { Args func(codec VideoCodec, videoFilter VideoFilter, videoOnly bool) Args } -func CodecInit(codec VideoCodec) (args Args) { - args = args.VideoCodec(codec) - - switch codec { - // CPU Codecs - case VideoCodecLibX264: - args = append(args, - "-pix_fmt", "yuv420p", - "-preset", "veryfast", - "-crf", "25", - "-sc_threshold", "0", - ) - case VideoCodecVP9: - args = append(args, - "-pix_fmt", "yuv420p", - "-deadline", "realtime", - "-cpu-used", "5", - "-row-mt", "1", - "-crf", "30", - "-b:v", "0", - ) - // HW Codecs - case VideoCodecN264: - args = append(args, - "-rc", "vbr", - "-cq", "15", - ) - case VideoCodecN264H: - args = append(args, - "-profile", "p7", - "-tune", "hq", - "-profile", "high", - "-rc", "vbr", - "-rc-lookahead", "60", - "-surfaces", "64", - "-spatial-aq", "1", - "-aq-strength", "15", - "-cq", "15", - "-coder", "cabac", - "-b_ref_mode", "middle", - ) - case VideoCodecI264, VideoCodecIVP9: - args = append(args, - "-global_quality", "20", - "-preset", "faster", - ) - case VideoCodecI264C: - args = append(args, - "-q", "20", - "-preset", "faster", - ) - case VideoCodecV264, VideoCodecVVP9: - args = append(args, - "-qp", "20", - ) - case VideoCodecA264: - args = append(args, - "-quality", "speed", - ) - case VideoCodecM264: - args = append(args, - "-realtime", "1", - ) - case VideoCodecO264: - args = append(args, - "-preset", "superfast", - "-crf", "25", - ) - } - - return args -} - var ( StreamTypeMP4 = StreamFormat{ MimeType: MimeMp4Video, Args: func(codec VideoCodec, videoFilter VideoFilter, videoOnly bool) (args Args) { - args = CodecInit(codec) + args = args.VideoCodec(codec) + args = append(args, codec.ExtraArgs()...) args = append(args, "-movflags", "frag_keyframe+empty_moov") args = args.VideoFilter(videoFilter) if videoOnly { @@ -111,7 +39,8 @@ var ( StreamTypeWEBM = StreamFormat{ MimeType: MimeWebmVideo, Args: func(codec VideoCodec, videoFilter VideoFilter, videoOnly bool) (args Args) { - args = CodecInit(codec) + args = args.VideoCodec(codec) + args = append(args, codec.ExtraArgs()...) args = args.VideoFilter(videoFilter) if videoOnly { args = args.SkipAudio() @@ -125,7 +54,8 @@ var ( StreamTypeMKV = StreamFormat{ MimeType: MimeMkvVideo, Args: func(codec VideoCodec, videoFilter VideoFilter, videoOnly bool) (args Args) { - args = CodecInit(codec) + args = args.VideoCodec(codec) + args = append(args, codec.ExtraArgs()...) if videoOnly { args = args.SkipAudio() } else { @@ -166,16 +96,16 @@ func (o TranscodeOptions) FileGetCodec(sm *StreamManager, maxTranscodeSize int) return VideoCodecCopy } codec = VideoCodecLibX264 - if hwcodec := sm.encoder.hwCodecMP4Compatible(); hwcodec != nil && sm.config.GetTranscodeHardwareAcceleration() { - codec = *hwcodec + if sm.config.GetTranscodeHardwareAcceleration() { + codec = sm.encoder.HWCodecMP4Compatible(VideoCodecLibX264) } case MimeWebmVideo: if !needsResize && (o.VideoFile.VideoCodec == Vp8 || o.VideoFile.VideoCodec == Vp9) { return VideoCodecCopy } codec = VideoCodecVP9 - if hwcodec := sm.encoder.hwCodecWEBMCompatible(); hwcodec != nil && sm.config.GetTranscodeHardwareAcceleration() { - codec = *hwcodec + if sm.config.GetTranscodeHardwareAcceleration() { + codec = sm.encoder.HWCodecWEBMCompatible(VideoCodecVP9) } case MimeMkvVideo: codec = VideoCodecCopy @@ -197,8 +127,8 @@ func (o TranscodeOptions) makeStreamArgs(sm *StreamManager) Args { codec := o.FileGetCodec(sm, maxTranscodeSize) - fullhw := sm.config.GetTranscodeHardwareAcceleration() && sm.encoder.hwCanFullHWTranscode(sm.context, codec, o.VideoFile, maxTranscodeSize) - args = sm.encoder.hwDeviceInit(args, codec, fullhw) + fullhw := sm.config.GetTranscodeHardwareAcceleration() && sm.encoder.HWCanFullHWTranscode(sm.context, codec, o.VideoFile.Path, o.VideoFile.Width, o.VideoFile.Height, maxTranscodeSize) + args = sm.encoder.HWDeviceInit(args, codec, fullhw) args = append(args, extraInputArgs...) if o.StartTime != 0 { @@ -209,7 +139,7 @@ func (o TranscodeOptions) makeStreamArgs(sm *StreamManager) Args { videoOnly := ProbeAudioCodec(o.VideoFile.AudioCodec) == MissingUnsupported - videoFilter := sm.encoder.hwMaxResFilter(codec, o.VideoFile, maxTranscodeSize, fullhw) + videoFilter := sm.encoder.HWMaxResFilter(codec, o.VideoFile.Width, o.VideoFile.Height, maxTranscodeSize, fullhw) args = append(args, o.StreamType.Args(codec, videoFilter, videoOnly)...)