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.
This commit is contained in:
Nodude 2026-04-22 19:44:51 +02:00
parent 2c8a0ad192
commit 900305685c
5 changed files with 240 additions and 172 deletions

View file

@ -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
}

View file

@ -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<value>([-\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
}

View file

@ -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.

View file

@ -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)...)

View file

@ -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)...)