mirror of
https://github.com/stashapp/stash.git
synced 2025-12-16 20:07:05 +03:00
Full hardware transcoding (#4765)
This commit is contained in:
@@ -8,6 +8,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/stashapp/stash/pkg/logger"
|
"github.com/stashapp/stash/pkg/logger"
|
||||||
|
"github.com/stashapp/stash/pkg/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -24,6 +25,8 @@ var (
|
|||||||
VideoCodecVVPX VideoCodec = "vp8_vaapi"
|
VideoCodecVVPX VideoCodec = "vp8_vaapi"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const minHeight int = 256
|
||||||
|
|
||||||
// Tests all (given) hardware codec's
|
// Tests all (given) hardware codec's
|
||||||
func (f *FFMpeg) InitHWSupport(ctx context.Context) {
|
func (f *FFMpeg) InitHWSupport(ctx context.Context) {
|
||||||
var hwCodecSupport []VideoCodec
|
var hwCodecSupport []VideoCodec
|
||||||
@@ -39,15 +42,13 @@ func (f *FFMpeg) InitHWSupport(ctx context.Context) {
|
|||||||
var args Args
|
var args Args
|
||||||
args = append(args, "-hide_banner")
|
args = append(args, "-hide_banner")
|
||||||
args = args.LogLevel(LogLevelWarning)
|
args = args.LogLevel(LogLevelWarning)
|
||||||
args = f.hwDeviceInit(args, codec)
|
args = f.hwDeviceInit(args, codec, false)
|
||||||
args = args.Format("lavfi")
|
args = args.Format("lavfi")
|
||||||
args = args.Input("color=c=red")
|
args = args.Input(fmt.Sprintf("color=c=red:s=%dx%d", 1280, 720))
|
||||||
args = args.Duration(0.1)
|
args = args.Duration(0.1)
|
||||||
|
|
||||||
videoFilter := f.hwFilterInit(codec)
|
|
||||||
// Test scaling
|
// Test scaling
|
||||||
videoFilter = videoFilter.ScaleDimensions(-2, 160)
|
videoFilter := f.hwMaxResFilter(codec, 1280, 720, minHeight, false)
|
||||||
videoFilter = f.hwCodecFilter(videoFilter, codec)
|
|
||||||
args = append(args, CodecInit(codec)...)
|
args = append(args, CodecInit(codec)...)
|
||||||
args = args.VideoFilter(videoFilter)
|
args = args.VideoFilter(videoFilter)
|
||||||
|
|
||||||
@@ -59,12 +60,7 @@ func (f *FFMpeg) InitHWSupport(ctx context.Context) {
|
|||||||
var stderr bytes.Buffer
|
var stderr bytes.Buffer
|
||||||
cmd.Stderr = &stderr
|
cmd.Stderr = &stderr
|
||||||
|
|
||||||
if err := cmd.Start(); err != nil {
|
if err := cmd.Run(); err != nil {
|
||||||
logger.Debugf("[InitHWSupport] error starting command: %v", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := cmd.Wait(); err != nil {
|
|
||||||
errOutput := stderr.String()
|
errOutput := stderr.String()
|
||||||
|
|
||||||
if len(errOutput) == 0 {
|
if len(errOutput) == 0 {
|
||||||
@@ -77,7 +73,7 @@ func (f *FFMpeg) InitHWSupport(ctx context.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
outstr := "[InitHWSupport] Supported HW codecs:\n"
|
outstr := fmt.Sprintf("[InitHWSupport] Supported HW codecs [%d]:\n", len(hwCodecSupport))
|
||||||
for _, codec := range hwCodecSupport {
|
for _, codec := range hwCodecSupport {
|
||||||
outstr += fmt.Sprintf("\t%s\n", codec)
|
outstr += fmt.Sprintf("\t%s\n", codec)
|
||||||
}
|
}
|
||||||
@@ -86,66 +82,153 @@ func (f *FFMpeg) InitHWSupport(ctx context.Context) {
|
|||||||
f.hwCodecSupport = hwCodecSupport
|
f.hwCodecSupport = hwCodecSupport
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *FFMpeg) hwCanFullHWTranscode(ctx context.Context, vf *models.VideoFile, codec VideoCodec) bool {
|
||||||
|
var args Args
|
||||||
|
args = append(args, "-hide_banner")
|
||||||
|
args = args.LogLevel(LogLevelWarning)
|
||||||
|
args = args.XError()
|
||||||
|
args = f.hwDeviceInit(args, codec, true)
|
||||||
|
args = args.Input(vf.Path)
|
||||||
|
args = args.Duration(0.1)
|
||||||
|
|
||||||
|
videoFilter := f.hwMaxResFilter(codec, vf.Width, vf.Height, minHeight, true)
|
||||||
|
args = append(args, CodecInit(codec)...)
|
||||||
|
args = args.VideoFilter(videoFilter)
|
||||||
|
|
||||||
|
args = args.Format("null")
|
||||||
|
args = args.Output("-")
|
||||||
|
|
||||||
|
cmd := f.Command(ctx, args)
|
||||||
|
|
||||||
|
var stderr bytes.Buffer
|
||||||
|
cmd.Stderr = &stderr
|
||||||
|
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
errOutput := stderr.String()
|
||||||
|
|
||||||
|
if len(errOutput) == 0 {
|
||||||
|
errOutput = err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Debugf("[InitHWSupport] Full hardware transcode for file %s not supported. Error output:\n%s", vf.Basename, errOutput)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// Prepend input for hardware encoding only
|
// Prepend input for hardware encoding only
|
||||||
func (f *FFMpeg) hwDeviceInit(args Args, codec VideoCodec) Args {
|
func (f *FFMpeg) hwDeviceInit(args Args, toCodec VideoCodec, fullhw bool) Args {
|
||||||
switch codec {
|
switch toCodec {
|
||||||
case VideoCodecN264:
|
case VideoCodecN264:
|
||||||
args = append(args, "-hwaccel_device")
|
args = append(args, "-hwaccel_device")
|
||||||
args = append(args, "0")
|
args = append(args, "0")
|
||||||
|
if fullhw {
|
||||||
|
args = append(args, "-hwaccel")
|
||||||
|
args = append(args, "cuda")
|
||||||
|
args = append(args, "-hwaccel_output_format")
|
||||||
|
args = append(args, "cuda")
|
||||||
|
args = append(args, "-extra_hw_frames")
|
||||||
|
args = append(args, "5")
|
||||||
|
}
|
||||||
case VideoCodecV264,
|
case VideoCodecV264,
|
||||||
VideoCodecVVP9:
|
VideoCodecVVP9:
|
||||||
args = append(args, "-vaapi_device")
|
args = append(args, "-vaapi_device")
|
||||||
args = append(args, "/dev/dri/renderD128")
|
args = append(args, "/dev/dri/renderD128")
|
||||||
|
if fullhw {
|
||||||
|
args = append(args, "-hwaccel")
|
||||||
|
args = append(args, "vaapi")
|
||||||
|
args = append(args, "-hwaccel_output_format")
|
||||||
|
args = append(args, "vaapi")
|
||||||
|
}
|
||||||
case VideoCodecI264,
|
case VideoCodecI264,
|
||||||
VideoCodecIVP9:
|
VideoCodecIVP9:
|
||||||
args = append(args, "-init_hw_device")
|
if fullhw {
|
||||||
args = append(args, "qsv=hw")
|
args = append(args, "-hwaccel")
|
||||||
args = append(args, "-filter_hw_device")
|
args = append(args, "qsv")
|
||||||
args = append(args, "hw")
|
args = append(args, "-hwaccel_output_format")
|
||||||
|
args = append(args, "qsv")
|
||||||
|
} else {
|
||||||
|
args = append(args, "-init_hw_device")
|
||||||
|
args = append(args, "qsv=hw")
|
||||||
|
args = append(args, "-filter_hw_device")
|
||||||
|
args = append(args, "hw")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return args
|
return args
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialise a video filter for HW encoding
|
// Initialise a video filter for HW encoding
|
||||||
func (f *FFMpeg) hwFilterInit(codec VideoCodec) VideoFilter {
|
func (f *FFMpeg) hwFilterInit(toCodec VideoCodec, fullhw bool) VideoFilter {
|
||||||
var videoFilter VideoFilter
|
var videoFilter VideoFilter
|
||||||
switch codec {
|
switch toCodec {
|
||||||
case VideoCodecV264,
|
case VideoCodecV264,
|
||||||
VideoCodecVVP9:
|
VideoCodecVVP9:
|
||||||
videoFilter = videoFilter.Append("format=nv12")
|
if !fullhw {
|
||||||
videoFilter = videoFilter.Append("hwupload")
|
videoFilter = videoFilter.Append("format=nv12")
|
||||||
|
videoFilter = videoFilter.Append("hwupload")
|
||||||
|
}
|
||||||
case VideoCodecN264:
|
case VideoCodecN264:
|
||||||
videoFilter = videoFilter.Append("format=nv12")
|
if !fullhw {
|
||||||
videoFilter = videoFilter.Append("hwupload_cuda")
|
videoFilter = videoFilter.Append("format=nv12")
|
||||||
|
videoFilter = videoFilter.Append("hwupload_cuda")
|
||||||
|
}
|
||||||
case VideoCodecI264,
|
case VideoCodecI264,
|
||||||
VideoCodecIVP9:
|
VideoCodecIVP9:
|
||||||
videoFilter = videoFilter.Append("hwupload=extra_hw_frames=64")
|
if !fullhw {
|
||||||
videoFilter = videoFilter.Append("format=qsv")
|
videoFilter = videoFilter.Append("hwupload=extra_hw_frames=64")
|
||||||
|
videoFilter = videoFilter.Append("format=qsv")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return videoFilter
|
return videoFilter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var scaler_re = regexp.MustCompile(`scale=(?P<value>[-\d]+:[-\d]+)`)
|
||||||
|
|
||||||
|
func templateReplaceScale(input string, template string, match []int, minusonehack bool) string {
|
||||||
|
result := []byte{}
|
||||||
|
|
||||||
|
res := string(scaler_re.ExpandString(result, template, input, match))
|
||||||
|
|
||||||
|
// BUG: [scale_qsv]: Size values less than -1 are not acceptable.
|
||||||
|
// Fix: Replace all instances of -2 with -1 in a scale operation
|
||||||
|
if minusonehack {
|
||||||
|
res = strings.ReplaceAll(res, "-2", "-1")
|
||||||
|
}
|
||||||
|
|
||||||
|
matchStart := match[0]
|
||||||
|
matchEnd := match[1]
|
||||||
|
|
||||||
|
return input[0:matchStart] + res + input[matchEnd:]
|
||||||
|
}
|
||||||
|
|
||||||
// Replace video filter scaling with hardware scaling for full hardware transcoding
|
// Replace video filter scaling with hardware scaling for full hardware transcoding
|
||||||
func (f *FFMpeg) hwCodecFilter(args VideoFilter, codec VideoCodec) VideoFilter {
|
func (f *FFMpeg) hwCodecFilter(args VideoFilter, codec VideoCodec, fullhw bool) VideoFilter {
|
||||||
sargs := string(args)
|
sargs := string(args)
|
||||||
|
|
||||||
if strings.Contains(sargs, "scale=") {
|
match := scaler_re.FindStringSubmatchIndex(sargs)
|
||||||
switch codec {
|
if match == nil {
|
||||||
case VideoCodecN264:
|
return args
|
||||||
args = VideoFilter(strings.Replace(sargs, "scale=", "scale_cuda=", 1))
|
}
|
||||||
case VideoCodecV264,
|
|
||||||
VideoCodecVVP9:
|
switch codec {
|
||||||
args = VideoFilter(strings.Replace(sargs, "scale=", "scale_vaapi=", 1))
|
case VideoCodecN264:
|
||||||
case VideoCodecI264,
|
template := "scale_cuda=$value"
|
||||||
VideoCodecIVP9:
|
// In 10bit inputs you might get an error like "10 bit encode not supported"
|
||||||
// BUG: [scale_qsv]: Size values less than -1 are not acceptable.
|
if fullhw && f.version.major >= 5 {
|
||||||
// Fix: Replace all instances of -2 with -1 in a scale operation
|
template += ":format=nv12"
|
||||||
re := regexp.MustCompile(`(scale=)([\d:]*)(-2)(.*)`)
|
|
||||||
sargs = re.ReplaceAllString(sargs, "scale=$2-1$4")
|
|
||||||
args = VideoFilter(strings.Replace(sargs, "scale=", "scale_qsv=", 1))
|
|
||||||
}
|
}
|
||||||
|
args = VideoFilter(templateReplaceScale(sargs, template, match, false))
|
||||||
|
case VideoCodecV264,
|
||||||
|
VideoCodecVVP9:
|
||||||
|
template := "scale_vaapi=$value"
|
||||||
|
args = VideoFilter(templateReplaceScale(sargs, template, match, false))
|
||||||
|
case VideoCodecI264,
|
||||||
|
VideoCodecIVP9:
|
||||||
|
template := "scale_qsv=$value"
|
||||||
|
args = VideoFilter(templateReplaceScale(sargs, template, match, true))
|
||||||
}
|
}
|
||||||
|
|
||||||
return args
|
return args
|
||||||
@@ -153,7 +236,9 @@ func (f *FFMpeg) hwCodecFilter(args VideoFilter, codec VideoCodec) VideoFilter {
|
|||||||
|
|
||||||
// Returns the max resolution for a given codec, or a default
|
// Returns the max resolution for a given codec, or a default
|
||||||
func (f *FFMpeg) hwCodecMaxRes(codec VideoCodec, dW int, dH int) (int, int) {
|
func (f *FFMpeg) hwCodecMaxRes(codec VideoCodec, dW int, dH int) (int, int) {
|
||||||
if codec == VideoCodecN264 {
|
switch codec {
|
||||||
|
case VideoCodecN264,
|
||||||
|
VideoCodecI264:
|
||||||
return 4096, 4096
|
return 4096, 4096
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,11 +246,14 @@ func (f *FFMpeg) hwCodecMaxRes(codec VideoCodec, dW int, dH int) (int, int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Return a maxres filter
|
// Return a maxres filter
|
||||||
func (f *FFMpeg) hwMaxResFilter(codec VideoCodec, width int, height int, max int) VideoFilter {
|
func (f *FFMpeg) hwMaxResFilter(toCodec VideoCodec, width int, height int, reqHeight int, fullhw bool) VideoFilter {
|
||||||
videoFilter := f.hwFilterInit(codec)
|
if width == 0 || height == 0 {
|
||||||
maxWidth, maxHeight := f.hwCodecMaxRes(codec, width, height)
|
return ""
|
||||||
videoFilter = videoFilter.ScaleMaxLM(width, height, max, maxWidth, maxHeight)
|
}
|
||||||
return f.hwCodecFilter(videoFilter, codec)
|
videoFilter := f.hwFilterInit(toCodec, fullhw)
|
||||||
|
maxWidth, maxHeight := f.hwCodecMaxRes(toCodec, width, height)
|
||||||
|
videoFilter = videoFilter.ScaleMaxLM(width, height, reqHeight, maxWidth, maxHeight)
|
||||||
|
return f.hwCodecFilter(videoFilter, toCodec, fullhw)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return if a hardware accelerated for HLS is available
|
// Return if a hardware accelerated for HLS is available
|
||||||
|
|||||||
@@ -2,10 +2,13 @@
|
|||||||
package ffmpeg
|
package ffmpeg
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
stashExec "github.com/stashapp/stash/pkg/exec"
|
stashExec "github.com/stashapp/stash/pkg/exec"
|
||||||
@@ -93,9 +96,54 @@ func ResolveFFMpeg(path string, fallbackPath string) string {
|
|||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *FFMpeg) getVersion() error {
|
||||||
|
var args Args
|
||||||
|
args = append(args, "-version")
|
||||||
|
cmd := f.Command(context.Background(), args)
|
||||||
|
|
||||||
|
var stdout bytes.Buffer
|
||||||
|
cmd.Stdout = &stdout
|
||||||
|
|
||||||
|
var err error
|
||||||
|
if err = cmd.Run(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
version_re := regexp.MustCompile(`ffmpeg version ((\d+)\.(\d+)\.(\d+))`)
|
||||||
|
stdoutStr := stdout.String()
|
||||||
|
match := version_re.FindStringSubmatchIndex(stdoutStr)
|
||||||
|
if match == nil {
|
||||||
|
return errors.New("version string malformed")
|
||||||
|
}
|
||||||
|
|
||||||
|
majorS := stdoutStr[match[4]:match[5]]
|
||||||
|
minorS := stdoutStr[match[6]:match[7]]
|
||||||
|
patchS := stdoutStr[match[8]:match[9]]
|
||||||
|
if i, err := strconv.Atoi(majorS); err == nil {
|
||||||
|
f.version.major = i
|
||||||
|
}
|
||||||
|
if i, err := strconv.Atoi(minorS); err == nil {
|
||||||
|
f.version.minor = i
|
||||||
|
}
|
||||||
|
if i, err := strconv.Atoi(patchS); err == nil {
|
||||||
|
f.version.patch = i
|
||||||
|
}
|
||||||
|
logger.Debugf("FFMpeg version %d.%d.%d detected", f.version.major, f.version.minor, f.version.patch)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FFMpeg version params
|
||||||
|
type FFMpegVersion struct {
|
||||||
|
major int
|
||||||
|
minor int
|
||||||
|
patch int
|
||||||
|
}
|
||||||
|
|
||||||
// FFMpeg provides an interface to ffmpeg.
|
// FFMpeg provides an interface to ffmpeg.
|
||||||
type FFMpeg struct {
|
type FFMpeg struct {
|
||||||
ffmpeg string
|
ffmpeg string
|
||||||
|
version FFMpegVersion
|
||||||
hwCodecSupport []VideoCodec
|
hwCodecSupport []VideoCodec
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,6 +152,9 @@ func NewEncoder(ffmpegPath string) *FFMpeg {
|
|||||||
ret := &FFMpeg{
|
ret := &FFMpeg{
|
||||||
ffmpeg: ffmpegPath,
|
ffmpeg: ffmpegPath,
|
||||||
}
|
}
|
||||||
|
if err := ret.getVersion(); err != nil {
|
||||||
|
logger.Warnf("FFMpeg version not detected %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ var (
|
|||||||
"-f", "hls",
|
"-f", "hls",
|
||||||
"-start_number", fmt.Sprint(segment),
|
"-start_number", fmt.Sprint(segment),
|
||||||
"-hls_time", fmt.Sprint(segmentLength),
|
"-hls_time", fmt.Sprint(segmentLength),
|
||||||
|
"-hls_flags", "split_by_time",
|
||||||
"-hls_segment_type", "mpegts",
|
"-hls_segment_type", "mpegts",
|
||||||
"-hls_playlist_type", "vod",
|
"-hls_playlist_type", "vod",
|
||||||
"-hls_segment_filename", filepath.Join(outputDir, ".%d.ts"),
|
"-hls_segment_filename", filepath.Join(outputDir, ".%d.ts"),
|
||||||
@@ -110,6 +111,7 @@ var (
|
|||||||
"-f", "hls",
|
"-f", "hls",
|
||||||
"-start_number", fmt.Sprint(segment),
|
"-start_number", fmt.Sprint(segment),
|
||||||
"-hls_time", fmt.Sprint(segmentLength),
|
"-hls_time", fmt.Sprint(segmentLength),
|
||||||
|
"-hls_flags", "split_by_time",
|
||||||
"-hls_segment_type", "mpegts",
|
"-hls_segment_type", "mpegts",
|
||||||
"-hls_playlist_type", "vod",
|
"-hls_playlist_type", "vod",
|
||||||
"-hls_segment_filename", filepath.Join(outputDir, ".%d.ts"),
|
"-hls_segment_filename", filepath.Join(outputDir, ".%d.ts"),
|
||||||
@@ -328,7 +330,8 @@ func (s *runningStream) makeStreamArgs(sm *StreamManager, segment int) Args {
|
|||||||
|
|
||||||
codec := HLSGetCodec(sm, s.streamType.Name)
|
codec := HLSGetCodec(sm, s.streamType.Name)
|
||||||
|
|
||||||
args = sm.encoder.hwDeviceInit(args, codec)
|
fullhw := sm.config.GetTranscodeHardwareAcceleration() && sm.encoder.hwCanFullHWTranscode(sm.context, s.vf, codec)
|
||||||
|
args = sm.encoder.hwDeviceInit(args, codec, fullhw)
|
||||||
args = append(args, extraInputArgs...)
|
args = append(args, extraInputArgs...)
|
||||||
|
|
||||||
if segment > 0 {
|
if segment > 0 {
|
||||||
@@ -339,7 +342,7 @@ func (s *runningStream) makeStreamArgs(sm *StreamManager, segment int) Args {
|
|||||||
|
|
||||||
videoOnly := ProbeAudioCodec(s.vf.AudioCodec) == MissingUnsupported
|
videoOnly := ProbeAudioCodec(s.vf.AudioCodec) == MissingUnsupported
|
||||||
|
|
||||||
videoFilter := sm.encoder.hwMaxResFilter(codec, s.vf.Width, s.vf.Height, s.maxTranscodeSize)
|
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)...)
|
args = append(args, s.streamType.Args(codec, segment, videoFilter, videoOnly, s.outputDir)...)
|
||||||
|
|
||||||
|
|||||||
@@ -186,7 +186,8 @@ func (o TranscodeOptions) makeStreamArgs(sm *StreamManager) Args {
|
|||||||
|
|
||||||
codec := o.FileGetCodec(sm, maxTranscodeSize)
|
codec := o.FileGetCodec(sm, maxTranscodeSize)
|
||||||
|
|
||||||
args = sm.encoder.hwDeviceInit(args, codec)
|
fullhw := sm.config.GetTranscodeHardwareAcceleration() && sm.encoder.hwCanFullHWTranscode(sm.context, o.VideoFile, codec)
|
||||||
|
args = sm.encoder.hwDeviceInit(args, codec, fullhw)
|
||||||
args = append(args, extraInputArgs...)
|
args = append(args, extraInputArgs...)
|
||||||
|
|
||||||
if o.StartTime != 0 {
|
if o.StartTime != 0 {
|
||||||
@@ -197,7 +198,7 @@ func (o TranscodeOptions) makeStreamArgs(sm *StreamManager) Args {
|
|||||||
|
|
||||||
videoOnly := ProbeAudioCodec(o.VideoFile.AudioCodec) == MissingUnsupported
|
videoOnly := ProbeAudioCodec(o.VideoFile.AudioCodec) == MissingUnsupported
|
||||||
|
|
||||||
videoFilter := sm.encoder.hwMaxResFilter(codec, o.VideoFile.Width, o.VideoFile.Height, maxTranscodeSize)
|
videoFilter := sm.encoder.hwMaxResFilter(codec, o.VideoFile.Width, o.VideoFile.Height, maxTranscodeSize, fullhw)
|
||||||
|
|
||||||
args = append(args, o.StreamType.Args(codec, videoFilter, videoOnly)...)
|
args = append(args, o.StreamType.Args(codec, videoFilter, videoOnly)...)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user