EncodingHelper.cs (jellyfin-10.8.9) | : | EncodingHelper.cs (jellyfin-10.8.10) | ||
---|---|---|---|---|
skipping to change at line 44 | skipping to change at line 44 | |||
private readonly IApplicationPaths _appPaths; | private readonly IApplicationPaths _appPaths; | |||
private readonly IMediaEncoder _mediaEncoder; | private readonly IMediaEncoder _mediaEncoder; | |||
private readonly ISubtitleEncoder _subtitleEncoder; | private readonly ISubtitleEncoder _subtitleEncoder; | |||
private readonly IConfiguration _config; | private readonly IConfiguration _config; | |||
// i915 hang was fixed by linux 6.2 (3f882f2) | // i915 hang was fixed by linux 6.2 (3f882f2) | |||
private readonly Version _minKerneli915Hang = new Version(5, 18); | private readonly Version _minKerneli915Hang = new Version(5, 18); | |||
private readonly Version _maxKerneli915Hang = new Version(6, 1, 3); | private readonly Version _maxKerneli915Hang = new Version(6, 1, 3); | |||
private readonly Version _minFixedKernel60i915Hang = new Version(6, 0, 1 8); | private readonly Version _minFixedKernel60i915Hang = new Version(6, 0, 1 8); | |||
private readonly Version _minFFmpegImplictHwaccel = new Version(6, 0); | ||||
private readonly Version _minFFmpegHwaUnsafeOutput = new Version(6, 0); | ||||
private readonly Version _minFFmpegOclCuTonemapMode = new Version(5, 1, | ||||
3); | ||||
private static readonly string[] _videoProfilesH264 = new[] | private static readonly string[] _videoProfilesH264 = new[] | |||
{ | { | |||
"ConstrainedBaseline", | "ConstrainedBaseline", | |||
"Baseline", | "Baseline", | |||
"Extended", | "Extended", | |||
"Main", | "Main", | |||
"High", | "High", | |||
"ProgressiveHigh", | "ProgressiveHigh", | |||
"ConstrainedHigh", | "ConstrainedHigh", | |||
"High10" | "High10" | |||
}; | }; | |||
private static readonly string[] _videoProfilesH265 = new[] | private static readonly string[] _videoProfilesH265 = new[] | |||
{ | { | |||
"Main", | "Main", | |||
"Main10" | "Main10" | |||
}; | }; | |||
public static readonly string[] LosslessAudioCodecs = new string[] | ||||
{ | ||||
"alac", | ||||
"ape", | ||||
"flac", | ||||
"mlp", | ||||
"truehd", | ||||
"wavpack" | ||||
}; | ||||
public EncodingHelper( | public EncodingHelper( | |||
IApplicationPaths appPaths, | IApplicationPaths appPaths, | |||
IMediaEncoder mediaEncoder, | IMediaEncoder mediaEncoder, | |||
ISubtitleEncoder subtitleEncoder, | ISubtitleEncoder subtitleEncoder, | |||
IConfiguration config) | IConfiguration config) | |||
{ | { | |||
_appPaths = appPaths; | _appPaths = appPaths; | |||
_mediaEncoder = mediaEncoder; | _mediaEncoder = mediaEncoder; | |||
_subtitleEncoder = subtitleEncoder; | _subtitleEncoder = subtitleEncoder; | |||
_config = config; | _config = config; | |||
skipping to change at line 548 | skipping to change at line 562 | |||
if (string.Equals(codec, "opus", StringComparison.OrdinalIgnoreCase) ) | if (string.Equals(codec, "opus", StringComparison.OrdinalIgnoreCase) ) | |||
{ | { | |||
return "libopus"; | return "libopus"; | |||
} | } | |||
if (string.Equals(codec, "flac", StringComparison.OrdinalIgnoreCase) ) | if (string.Equals(codec, "flac", StringComparison.OrdinalIgnoreCase) ) | |||
{ | { | |||
return "flac"; | return "flac"; | |||
} | } | |||
if (string.Equals(codec, "dts", StringComparison.OrdinalIgnoreCase)) | ||||
{ | ||||
return "dca"; | ||||
} | ||||
return codec.ToLowerInvariant(); | return codec.ToLowerInvariant(); | |||
} | } | |||
private string GetVideoToolboxDeviceArgs(string alias) | private string GetVideoToolboxDeviceArgs(string alias) | |||
{ | { | |||
alias ??= VideotoolboxAlias; | alias ??= VideotoolboxAlias; | |||
// device selection in vt is not supported. | // device selection in vt is not supported. | |||
return " -init_hw_device videotoolbox=" + alias; | return " -init_hw_device videotoolbox=" + alias; | |||
} | } | |||
skipping to change at line 650 | skipping to change at line 669 | |||
private string GetFilterHwDeviceArgs(string alias) | private string GetFilterHwDeviceArgs(string alias) | |||
{ | { | |||
return string.IsNullOrEmpty(alias) | return string.IsNullOrEmpty(alias) | |||
? string.Empty | ? string.Empty | |||
: " -filter_hw_device " + alias; | : " -filter_hw_device " + alias; | |||
} | } | |||
public string GetGraphicalSubCanvasSize(EncodingJobInfo state) | public string GetGraphicalSubCanvasSize(EncodingJobInfo state) | |||
{ | { | |||
// DVBSUB and DVDSUB use the fixed canvas size 720x576 | ||||
if (state.SubtitleStream != null | if (state.SubtitleStream != null | |||
&& state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode | && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode | |||
&& !state.SubtitleStream.IsTextSubtitleStream) | && !state.SubtitleStream.IsTextSubtitleStream | |||
&& !string.Equals(state.SubtitleStream.Codec, "DVBSUB", StringCo | ||||
mparison.OrdinalIgnoreCase) | ||||
&& !string.Equals(state.SubtitleStream.Codec, "DVDSUB", StringCo | ||||
mparison.OrdinalIgnoreCase)) | ||||
{ | { | |||
var inW = state.VideoStream?.Width; | var inW = state.VideoStream?.Width; | |||
var inH = state.VideoStream?.Height; | var inH = state.VideoStream?.Height; | |||
var reqW = state.BaseRequest.Width; | var reqW = state.BaseRequest.Width; | |||
var reqH = state.BaseRequest.Height; | var reqH = state.BaseRequest.Height; | |||
var reqMaxW = state.BaseRequest.MaxWidth; | var reqMaxW = state.BaseRequest.MaxWidth; | |||
var reqMaxH = state.BaseRequest.MaxHeight; | var reqMaxH = state.BaseRequest.MaxHeight; | |||
// setup a relative small canvas_size for overlay_qsv/vaapi to r educe transfer overhead | // setup a relative small canvas_size for overlay_qsv/vaapi to r educe transfer overhead | |||
var (overlayW, overlayH) = GetFixedOutputSize(inW, inH, reqW, re qH, reqMaxW, 1080); | var (overlayW, overlayH) = GetFixedOutputSize(inW, inH, reqW, re qH, reqMaxW, 1080); | |||
skipping to change at line 1444 | skipping to change at line 1466 | |||
} | } | |||
else if (string.Equals(videoEncoder, "h264_nvenc", StringComparison. OrdinalIgnoreCase) // h264 (h264_nvenc) | else if (string.Equals(videoEncoder, "h264_nvenc", StringComparison. OrdinalIgnoreCase) // h264 (h264_nvenc) | |||
|| string.Equals(videoEncoder, "hevc_nvenc", StringComparis on.OrdinalIgnoreCase)) // hevc (hevc_nvenc) | || string.Equals(videoEncoder, "hevc_nvenc", StringComparis on.OrdinalIgnoreCase)) // hevc (hevc_nvenc) | |||
{ | { | |||
switch (encodingOptions.EncoderPreset) | switch (encodingOptions.EncoderPreset) | |||
{ | { | |||
case "veryslow": | case "veryslow": | |||
param += " -preset p7"; | param += " -preset p7"; | |||
break; | break; | |||
case "slow": | case "slower": | |||
param += " -preset p6"; | param += " -preset p6"; | |||
break; | break; | |||
case "slower": | case "slow": | |||
param += " -preset p5"; | param += " -preset p5"; | |||
break; | break; | |||
case "medium": | case "medium": | |||
param += " -preset p4"; | param += " -preset p4"; | |||
break; | break; | |||
case "fast": | case "fast": | |||
param += " -preset p3"; | param += " -preset p3"; | |||
break; | break; | |||
skipping to change at line 1481 | skipping to change at line 1503 | |||
param += " -preset p1"; | param += " -preset p1"; | |||
break; | break; | |||
} | } | |||
} | } | |||
else if (string.Equals(videoEncoder, "h264_amf", StringComparison.Or dinalIgnoreCase) // h264 (h264_amf) | else if (string.Equals(videoEncoder, "h264_amf", StringComparison.Or dinalIgnoreCase) // h264 (h264_amf) | |||
|| string.Equals(videoEncoder, "hevc_amf", StringComparison .OrdinalIgnoreCase)) // hevc (hevc_amf) | || string.Equals(videoEncoder, "hevc_amf", StringComparison .OrdinalIgnoreCase)) // hevc (hevc_amf) | |||
{ | { | |||
switch (encodingOptions.EncoderPreset) | switch (encodingOptions.EncoderPreset) | |||
{ | { | |||
case "veryslow": | case "veryslow": | |||
case "slow": | ||||
case "slower": | case "slower": | |||
case "slow": | ||||
param += " -quality quality"; | param += " -quality quality"; | |||
break; | break; | |||
case "medium": | case "medium": | |||
param += " -quality balanced"; | param += " -quality balanced"; | |||
break; | break; | |||
case "fast": | case "fast": | |||
case "faster": | case "faster": | |||
case "veryfast": | case "veryfast": | |||
skipping to change at line 1955 | skipping to change at line 1977 | |||
{ | { | |||
return false; | return false; | |||
} | } | |||
if (audioStream.SampleRate.Value > request.AudioSampleRate.Value ) | if (audioStream.SampleRate.Value > request.AudioSampleRate.Value ) | |||
{ | { | |||
return false; | return false; | |||
} | } | |||
} | } | |||
// Video bitrate must fall within requested value | // Audio bitrate must fall within requested value | |||
if (request.AudioBitRate.HasValue | if (request.AudioBitRate.HasValue | |||
&& audioStream.BitDepth.HasValue | && audioStream.BitRate.HasValue | |||
&& audioStream.BitRate.Value > request.AudioBitRate.Value) | && audioStream.BitRate.Value > request.AudioBitRate.Value) | |||
{ | { | |||
return false; | return false; | |||
} | } | |||
return request.EnableAutoStreamCopy; | return request.EnableAutoStreamCopy; | |||
} | } | |||
public int GetVideoBitrateParamValue(BaseEncodingJobOptions request, Med iaStream videoStream, string outputVideoCodec) | public int GetVideoBitrateParamValue(BaseEncodingJobOptions request, Med iaStream videoStream, string outputVideoCodec) | |||
{ | { | |||
skipping to change at line 2021 | skipping to change at line 2043 | |||
sourceBitrate *= 2; | sourceBitrate *= 2; | |||
} | } | |||
var bitrate = Math.Min(sourceBitrate, requestedBitrate); | var bitrate = Math.Min(sourceBitrate, requestedBitrate); | |||
return bitrate; | return bitrate; | |||
} | } | |||
private static double GetVideoBitrateScaleFactor(string codec) | private static double GetVideoBitrateScaleFactor(string codec) | |||
{ | { | |||
// hevc & vp9 - 40% more efficient than h.264 | ||||
if (string.Equals(codec, "h265", StringComparison.OrdinalIgnoreCase) | if (string.Equals(codec, "h265", StringComparison.OrdinalIgnoreCase) | |||
|| string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCa se) | || string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCa se) | |||
|| string.Equals(codec, "vp9", StringComparison.OrdinalIgnoreCas | || string.Equals(codec, "vp9", StringComparison.OrdinalIgnoreCas | |||
e) | e)) | |||
|| string.Equals(codec, "av1", StringComparison.OrdinalIgnoreCas | ||||
e)) | ||||
{ | { | |||
return .6; | return .6; | |||
} | } | |||
// av1 - 50% more efficient than h.264 | ||||
if (string.Equals(codec, "av1", StringComparison.OrdinalIgnoreCase)) | ||||
{ | ||||
return .5; | ||||
} | ||||
return 1; | return 1; | |||
} | } | |||
private static int ScaleBitrate(int bitrate, string inputVideoCodec, str ing outputVideoCodec) | private static int ScaleBitrate(int bitrate, string inputVideoCodec, str ing outputVideoCodec) | |||
{ | { | |||
var inputScaleFactor = GetVideoBitrateScaleFactor(inputVideoCodec); | var inputScaleFactor = GetVideoBitrateScaleFactor(inputVideoCodec); | |||
var outputScaleFactor = GetVideoBitrateScaleFactor(outputVideoCodec) ; | var outputScaleFactor = GetVideoBitrateScaleFactor(outputVideoCodec) ; | |||
var scaleFactor = outputScaleFactor / inputScaleFactor; | ||||
// Don't scale the real bitrate lower than the requested bitrate | ||||
var scaleFactor = Math.Min(outputScaleFactor / inputScaleFactor, 1); | ||||
if (bitrate <= 500000) | if (bitrate <= 500000) | |||
{ | { | |||
scaleFactor = Math.Max(scaleFactor, 4); | scaleFactor = Math.Max(scaleFactor, 4); | |||
} | } | |||
else if (bitrate <= 1000000) | else if (bitrate <= 1000000) | |||
{ | { | |||
scaleFactor = Math.Max(scaleFactor, 3); | scaleFactor = Math.Max(scaleFactor, 3); | |||
} | } | |||
else if (bitrate <= 2000000) | else if (bitrate <= 2000000) | |||
skipping to change at line 2058 | skipping to change at line 2088 | |||
scaleFactor = Math.Max(scaleFactor, 2.5); | scaleFactor = Math.Max(scaleFactor, 2.5); | |||
} | } | |||
else if (bitrate <= 3000000) | else if (bitrate <= 3000000) | |||
{ | { | |||
scaleFactor = Math.Max(scaleFactor, 2); | scaleFactor = Math.Max(scaleFactor, 2); | |||
} | } | |||
return Convert.ToInt32(scaleFactor * bitrate); | return Convert.ToInt32(scaleFactor * bitrate); | |||
} | } | |||
public int? GetAudioBitrateParam(BaseEncodingJobOptions request, MediaSt ream audioStream) | public int? GetAudioBitrateParam(BaseEncodingJobOptions request, MediaSt ream audioStream, int? outputAudioChannels) | |||
{ | { | |||
return GetAudioBitrateParam(request.AudioBitRate, request.AudioCodec , audioStream); | return GetAudioBitrateParam(request.AudioBitRate, request.AudioCodec , audioStream, outputAudioChannels); | |||
} | } | |||
public int? GetAudioBitrateParam(int? audioBitRate, string audioCodec, M ediaStream audioStream) | public int? GetAudioBitrateParam(int? audioBitRate, string audioCodec, M ediaStream audioStream, int? outputAudioChannels) | |||
{ | { | |||
if (audioStream == null) | if (audioStream == null) | |||
{ | { | |||
return null; | return null; | |||
} | } | |||
if (audioBitRate.HasValue && string.IsNullOrEmpty(audioCodec)) | var inputChannels = audioStream.Channels ?? 0; | |||
{ | var outputChannels = outputAudioChannels ?? 0; | |||
return Math.Min(384000, audioBitRate.Value); | var bitrate = audioBitRate ?? int.MaxValue; | |||
if (string.IsNullOrEmpty(audioCodec) | ||||
|| string.Equals(audioCodec, "aac", StringComparison.OrdinalIgno | ||||
reCase) | ||||
|| string.Equals(audioCodec, "mp3", StringComparison.OrdinalIgno | ||||
reCase) | ||||
|| string.Equals(audioCodec, "opus", StringComparison.OrdinalIgn | ||||
oreCase) | ||||
|| string.Equals(audioCodec, "vorbis", StringComparison.OrdinalI | ||||
gnoreCase) | ||||
|| string.Equals(audioCodec, "ac3", StringComparison.OrdinalIgno | ||||
reCase) | ||||
|| string.Equals(audioCodec, "eac3", StringComparison.OrdinalIgn | ||||
oreCase)) | ||||
{ | ||||
return (inputChannels, outputChannels) switch | ||||
{ | ||||
(>= 6, >= 6 or 0) => Math.Min(640000, bitrate), | ||||
(> 0, > 0) => Math.Min(outputChannels * 128000, bitrate), | ||||
(> 0, _) => Math.Min(inputChannels * 128000, bitrate), | ||||
(_, _) => Math.Min(384000, bitrate) | ||||
}; | ||||
} | } | |||
if (audioBitRate.HasValue && !string.IsNullOrEmpty(audioCodec)) | if (string.Equals(audioCodec, "dts", StringComparison.OrdinalIgnoreC | |||
ase) | ||||
|| string.Equals(audioCodec, "dca", StringComparison.OrdinalIgno | ||||
reCase)) | ||||
{ | { | |||
if (string.Equals(audioCodec, "aac", StringComparison.OrdinalIgn | return (inputChannels, outputChannels) switch | |||
oreCase) | ||||
|| string.Equals(audioCodec, "mp3", StringComparison.Ordinal | ||||
IgnoreCase) | ||||
|| string.Equals(audioCodec, "opus", StringComparison.Ordina | ||||
lIgnoreCase) | ||||
|| string.Equals(audioCodec, "vorbis", StringComparison.Ordi | ||||
nalIgnoreCase) | ||||
|| string.Equals(audioCodec, "ac3", StringComparison.Ordinal | ||||
IgnoreCase) | ||||
|| string.Equals(audioCodec, "eac3", StringComparison.Ordina | ||||
lIgnoreCase)) | ||||
{ | ||||
if ((audioStream.Channels ?? 0) >= 6) | ||||
{ | ||||
return Math.Min(640000, audioBitRate.Value); | ||||
} | ||||
return Math.Min(384000, audioBitRate.Value); | ||||
} | ||||
if (string.Equals(audioCodec, "flac", StringComparison.OrdinalIg | ||||
noreCase) | ||||
|| string.Equals(audioCodec, "alac", StringComparison.Ordina | ||||
lIgnoreCase)) | ||||
{ | { | |||
if ((audioStream.Channels ?? 0) >= 6) | (>= 6, >= 6 or 0) => Math.Min(768000, bitrate), | |||
{ | (> 0, > 0) => Math.Min(outputChannels * 136000, bitrate), | |||
return Math.Min(3584000, audioBitRate.Value); | (> 0, _) => Math.Min(inputChannels * 136000, bitrate), | |||
} | (_, _) => Math.Min(672000, bitrate) | |||
}; | ||||
return Math.Min(1536000, audioBitRate.Value); | ||||
} | ||||
} | } | |||
// Empty bitrate area is not allow on iOS | // Empty bitrate area is not allow on iOS | |||
// Default audio bitrate to 128K if it is not being requested | // Default audio bitrate to 128K per channel if we don't have codec specific defaults | |||
// https://ffmpeg.org/ffmpeg-codecs.html#toc-Codec-Options | // https://ffmpeg.org/ffmpeg-codecs.html#toc-Codec-Options | |||
return 128000; | return 128000 * (outputAudioChannels ?? audioStream.Channels ?? 2); | |||
} | } | |||
public string GetAudioFilterParam(EncodingJobInfo state, EncodingOptions encodingOptions) | public string GetAudioFilterParam(EncodingJobInfo state, EncodingOptions encodingOptions) | |||
{ | { | |||
var channels = state.OutputAudioChannels; | var channels = state.OutputAudioChannels; | |||
var filters = new List<string>(); | var filters = new List<string>(); | |||
// Boost volume to 200% when downsampling from 6ch to 2ch | // Boost volume to 200% when downsampling from 6ch to 2ch | |||
if (channels.HasValue | if (channels.HasValue | |||
skipping to change at line 2401 | skipping to change at line 2430 | |||
args += string.Format( | args += string.Format( | |||
CultureInfo.InvariantCulture, | CultureInfo.InvariantCulture, | |||
" -map 1:{0} -sn", | " -map 1:{0} -sn", | |||
externalSubtitleStreamIndex); | externalSubtitleStreamIndex); | |||
} | } | |||
return args; | return args; | |||
} | } | |||
/// <summary> | /// <summary> | |||
/// Gets the negative map args by filters. | ||||
/// </summary> | ||||
/// <param name="state">The state.</param> | ||||
/// <param name="videoProcessFilters">The videoProcessFilters.</param> | ||||
/// <returns>System.String.</returns> | ||||
public string GetNegativeMapArgsByFilters(EncodingJobInfo state, string | ||||
videoProcessFilters) | ||||
{ | ||||
string args = string.Empty; | ||||
// http://ffmpeg.org/ffmpeg-all.html#toc-Complex-filtergraphs-1 | ||||
if (state.VideoStream != null && videoProcessFilters.Contains("-filt | ||||
er_complex", StringComparison.Ordinal)) | ||||
{ | ||||
int videoStreamIndex = FindIndex(state.MediaSource.MediaStreams, | ||||
state.VideoStream); | ||||
args += string.Format( | ||||
CultureInfo.InvariantCulture, | ||||
"-map -0:{0} ", | ||||
videoStreamIndex); | ||||
} | ||||
return args; | ||||
} | ||||
/// <summary> | ||||
/// Determines which stream will be used for playback. | /// Determines which stream will be used for playback. | |||
/// </summary> | /// </summary> | |||
/// <param name="allStream">All stream.</param> | /// <param name="allStream">All stream.</param> | |||
/// <param name="desiredIndex">Index of the desired.</param> | /// <param name="desiredIndex">Index of the desired.</param> | |||
/// <param name="type">The type.</param> | /// <param name="type">The type.</param> | |||
/// <param name="returnFirstIfNoIndex">if set to <c>true</c> [return fir st if no index].</param> | /// <param name="returnFirstIfNoIndex">if set to <c>true</c> [return fir st if no index].</param> | |||
/// <returns>MediaStream.</returns> | /// <returns>MediaStream.</returns> | |||
public MediaStream GetMediaStream(IEnumerable<MediaStream> allStream, in t? desiredIndex, MediaStreamType type, bool returnFirstIfNoIndex = true) | public MediaStream GetMediaStream(IEnumerable<MediaStream> allStream, in t? desiredIndex, MediaStreamType type, bool returnFirstIfNoIndex = true) | |||
{ | { | |||
var streams = allStream.Where(s => s.Type == type).OrderBy(i => i.In dex).ToList(); | var streams = allStream.Where(s => s.Type == type).OrderBy(i => i.In dex).ToList(); | |||
skipping to change at line 2770 | skipping to change at line 2823 | |||
doubleRateDeint ? "field" : "frame"); | doubleRateDeint ? "field" : "frame"); | |||
} | } | |||
else if (hwDeintSuffix.Contains("qsv", StringComparison.OrdinalIgnor eCase)) | else if (hwDeintSuffix.Contains("qsv", StringComparison.OrdinalIgnor eCase)) | |||
{ | { | |||
return "deinterlace_qsv=mode=2"; | return "deinterlace_qsv=mode=2"; | |||
} | } | |||
return string.Empty; | return string.Empty; | |||
} | } | |||
public static string GetHwTonemapFilter(EncodingOptions options, string hwTonemapSuffix, string videoFormat) | public string GetHwTonemapFilter(EncodingOptions options, string hwTonem apSuffix, string videoFormat) | |||
{ | { | |||
if (string.IsNullOrEmpty(hwTonemapSuffix)) | if (string.IsNullOrEmpty(hwTonemapSuffix)) | |||
{ | { | |||
return string.Empty; | return string.Empty; | |||
} | } | |||
var args = "tonemap_{0}=format={1}:p=bt709:t=bt709:m=bt709"; | var args = "tonemap_{0}=format={1}:p=bt709:t=bt709:m=bt709"; | |||
if (hwTonemapSuffix.Contains("vaapi", StringComparison.OrdinalIgnore Case)) | if (hwTonemapSuffix.Contains("vaapi", StringComparison.OrdinalIgnore Case)) | |||
{ | { | |||
args += ",procamp_vaapi=b={2}:c={3}:extra_hw_frames=16"; | args = "procamp_vaapi=b={2}:c={3}," + args + ":extra_hw_frames=3 | |||
2"; | ||||
return string.Format( | return string.Format( | |||
CultureInfo.InvariantCulture, | CultureInfo.InvariantCulture, | |||
args, | args, | |||
hwTonemapSuffix, | hwTonemapSuffix, | |||
videoFormat ?? "nv12", | videoFormat ?? "nv12", | |||
options.VppTonemappingBrightness, | options.VppTonemappingBrightness, | |||
options.VppTonemappingContrast); | options.VppTonemappingContrast); | |||
} | } | |||
else | else | |||
{ | { | |||
args += ":tonemap={2}:peak={3}:desat={4}"; | args += ":tonemap={2}:peak={3}:desat={4}"; | |||
if (string.Equals(options.TonemappingMode, "max", StringComparis | ||||
on.OrdinalIgnoreCase) | ||||
|| string.Equals(options.TonemappingMode, "rgb", StringCompa | ||||
rison.OrdinalIgnoreCase)) | ||||
{ | ||||
if (_mediaEncoder.EncoderVersion >= _minFFmpegOclCuTonemapMo | ||||
de) | ||||
{ | ||||
args += ":tonemap_mode={5}"; | ||||
} | ||||
} | ||||
if (options.TonemappingParam != 0) | if (options.TonemappingParam != 0) | |||
{ | { | |||
args += ":param={5}"; | args += ":param={6}"; | |||
} | } | |||
if (!string.Equals(options.TonemappingRange, "auto", StringCompa | if (string.Equals(options.TonemappingRange, "tv", StringComparis | |||
rison.OrdinalIgnoreCase)) | on.OrdinalIgnoreCase) | |||
|| string.Equals(options.TonemappingRange, "pc", StringCompa | ||||
rison.OrdinalIgnoreCase)) | ||||
{ | { | |||
args += ":range={6}"; | args += ":range={7}"; | |||
} | } | |||
} | } | |||
return string.Format( | return string.Format( | |||
CultureInfo.InvariantCulture, | CultureInfo.InvariantCulture, | |||
args, | args, | |||
hwTonemapSuffix, | hwTonemapSuffix, | |||
videoFormat ?? "nv12", | videoFormat ?? "nv12", | |||
options.TonemappingAlgorithm, | options.TonemappingAlgorithm, | |||
options.TonemappingPeak, | options.TonemappingPeak, | |||
options.TonemappingDesat, | options.TonemappingDesat, | |||
options.TonemappingMode, | ||||
options.TonemappingParam, | options.TonemappingParam, | |||
options.TonemappingRange); | options.TonemappingRange); | |||
} | } | |||
/// <summary> | /// <summary> | |||
/// Gets the parameter of software filter chain. | /// Gets the parameter of software filter chain. | |||
/// </summary> | /// </summary> | |||
/// <param name="state">Encoding state.</param> | /// <param name="state">Encoding state.</param> | |||
/// <param name="options">Encoding options.</param> | /// <param name="options">Encoding options.</param> | |||
/// <param name="vidEncoder">Video encoder to use.</param> | /// <param name="vidEncoder">Video encoder to use.</param> | |||
skipping to change at line 3217 | skipping to change at line 3282 | |||
} | } | |||
var memoryOutput = false; | var memoryOutput = false; | |||
var isUploadForOclTonemap = isSwDecoder && doOclTonemap; | var isUploadForOclTonemap = isSwDecoder && doOclTonemap; | |||
if (isD3d11vaDecoder && isSwEncoder) | if (isD3d11vaDecoder && isSwEncoder) | |||
{ | { | |||
memoryOutput = true; | memoryOutput = true; | |||
// OUTPUT nv12 surface(memory) | // OUTPUT nv12 surface(memory) | |||
// prefer hwmap to hwdownload on opencl. | // prefer hwmap to hwdownload on opencl. | |||
var hwTransferFilter = hasGraphicalSubs ? "hwdownload" : "hwmap" ; | var hwTransferFilter = hasGraphicalSubs ? "hwdownload" : "hwmap= mode=read"; | |||
mainFilters.Add(hwTransferFilter); | mainFilters.Add(hwTransferFilter); | |||
mainFilters.Add("format=nv12"); | mainFilters.Add("format=nv12"); | |||
} | } | |||
// OUTPUT yuv420p surface | // OUTPUT yuv420p surface | |||
if (isSwDecoder && isAmfEncoder && !isUploadForOclTonemap) | if (isSwDecoder && isAmfEncoder && !isUploadForOclTonemap) | |||
{ | { | |||
memoryOutput = true; | memoryOutput = true; | |||
} | } | |||
skipping to change at line 3460 | skipping to change at line 3525 | |||
var memoryOutput = false; | var memoryOutput = false; | |||
var isUploadForOclTonemap = isSwDecoder && doOclTonemap; | var isUploadForOclTonemap = isSwDecoder && doOclTonemap; | |||
var isHwmapUsable = isSwEncoder && doOclTonemap; | var isHwmapUsable = isSwEncoder && doOclTonemap; | |||
if ((isHwDecoder && isSwEncoder) || isUploadForOclTonemap) | if ((isHwDecoder && isSwEncoder) || isUploadForOclTonemap) | |||
{ | { | |||
memoryOutput = true; | memoryOutput = true; | |||
// OUTPUT nv12 surface(memory) | // OUTPUT nv12 surface(memory) | |||
// prefer hwmap to hwdownload on opencl. | // prefer hwmap to hwdownload on opencl. | |||
// qsv hwmap is not fully implemented for the time being. | // qsv hwmap is not fully implemented for the time being. | |||
mainFilters.Add(isHwmapUsable ? "hwmap" : "hwdownload"); | mainFilters.Add(isHwmapUsable ? "hwmap=mode=read" : "hwdownload" ); | |||
mainFilters.Add("format=nv12"); | mainFilters.Add("format=nv12"); | |||
} | } | |||
// OUTPUT nv12 surface(memory) | // OUTPUT nv12 surface(memory) | |||
if (isSwDecoder && isQsvEncoder) | if (isSwDecoder && isQsvEncoder) | |||
{ | { | |||
memoryOutput = true; | memoryOutput = true; | |||
} | } | |||
if (memoryOutput) | if (memoryOutput) | |||
skipping to change at line 3618 | skipping to change at line 3683 | |||
// INPUT vaapi/qsv surface(vram) | // INPUT vaapi/qsv surface(vram) | |||
// hw deint | // hw deint | |||
if (doDeintH2645) | if (doDeintH2645) | |||
{ | { | |||
var deintFilter = GetHwDeinterlaceFilter(state, options, isV aapiDecoder ? "vaapi" : "qsv"); | var deintFilter = GetHwDeinterlaceFilter(state, options, isV aapiDecoder ? "vaapi" : "qsv"); | |||
mainFilters.Add(deintFilter); | mainFilters.Add(deintFilter); | |||
} | } | |||
var outFormat = doTonemap ? string.Empty : "nv12"; | var outFormat = doTonemap ? string.Empty : "nv12"; | |||
var hwScaleFilter = GetHwScaleFilter(isVaapiDecoder ? "vaapi" : "qsv", outFormat, inW, inH, reqW, reqH, reqMaxW, reqMaxH); | var hwScaleFilter = GetHwScaleFilter(isVaapiDecoder ? "vaapi" : "qsv", outFormat, inW, inH, reqW, reqH, reqMaxW, reqMaxH); | |||
// allocate extra pool sizes for vaapi vpp | ||||
if (!string.IsNullOrEmpty(hwScaleFilter) && isVaapiDecoder) | ||||
{ | ||||
hwScaleFilter += ":extra_hw_frames=24"; | ||||
} | ||||
// hw scale | // hw scale | |||
mainFilters.Add(hwScaleFilter); | mainFilters.Add(hwScaleFilter); | |||
} | } | |||
// vaapi vpp tonemap | // vaapi vpp tonemap | |||
if (doVaVppTonemap && isHwDecoder) | if (doVaVppTonemap && isHwDecoder) | |||
{ | { | |||
if (isQsvDecoder) | if (isQsvDecoder) | |||
{ | { | |||
// map from qsv to vaapi. | // map from qsv to vaapi. | |||
skipping to change at line 3664 | skipping to change at line 3736 | |||
var memoryOutput = false; | var memoryOutput = false; | |||
var isUploadForOclTonemap = isSwDecoder && doOclTonemap; | var isUploadForOclTonemap = isSwDecoder && doOclTonemap; | |||
var isHwmapUsable = isSwEncoder && (doOclTonemap || isVaapiDecoder); | var isHwmapUsable = isSwEncoder && (doOclTonemap || isVaapiDecoder); | |||
if ((isHwDecoder && isSwEncoder) || isUploadForOclTonemap) | if ((isHwDecoder && isSwEncoder) || isUploadForOclTonemap) | |||
{ | { | |||
memoryOutput = true; | memoryOutput = true; | |||
// OUTPUT nv12 surface(memory) | // OUTPUT nv12 surface(memory) | |||
// prefer hwmap to hwdownload on opencl/vaapi. | // prefer hwmap to hwdownload on opencl/vaapi. | |||
// qsv hwmap is not fully implemented for the time being. | // qsv hwmap is not fully implemented for the time being. | |||
mainFilters.Add(isHwmapUsable ? "hwmap" : "hwdownload"); | mainFilters.Add(isHwmapUsable ? "hwmap=mode=read" : "hwdownload" ); | |||
mainFilters.Add("format=nv12"); | mainFilters.Add("format=nv12"); | |||
} | } | |||
// OUTPUT nv12 surface(memory) | // OUTPUT nv12 surface(memory) | |||
if (isSwDecoder && isQsvEncoder) | if (isSwDecoder && isQsvEncoder) | |||
{ | { | |||
memoryOutput = true; | memoryOutput = true; | |||
} | } | |||
if (memoryOutput) | if (memoryOutput) | |||
skipping to change at line 3881 | skipping to change at line 3953 | |||
// INPUT vaapi surface(vram) | // INPUT vaapi surface(vram) | |||
// hw deint | // hw deint | |||
if (doDeintH2645) | if (doDeintH2645) | |||
{ | { | |||
var deintFilter = GetHwDeinterlaceFilter(state, options, "va api"); | var deintFilter = GetHwDeinterlaceFilter(state, options, "va api"); | |||
mainFilters.Add(deintFilter); | mainFilters.Add(deintFilter); | |||
} | } | |||
var outFormat = doTonemap ? string.Empty : "nv12"; | var outFormat = doTonemap ? string.Empty : "nv12"; | |||
var hwScaleFilter = GetHwScaleFilter("vaapi", outFormat, inW, in H, reqW, reqH, reqMaxW, reqMaxH); | var hwScaleFilter = GetHwScaleFilter("vaapi", outFormat, inW, in H, reqW, reqH, reqMaxW, reqMaxH); | |||
// allocate extra pool sizes for vaapi vpp | ||||
if (!string.IsNullOrEmpty(hwScaleFilter)) | ||||
{ | ||||
hwScaleFilter += ":extra_hw_frames=24"; | ||||
} | ||||
// hw scale | // hw scale | |||
mainFilters.Add(hwScaleFilter); | mainFilters.Add(hwScaleFilter); | |||
} | } | |||
// vaapi vpp tonemap | // vaapi vpp tonemap | |||
if (doVaVppTonemap && isVaapiDecoder) | if (doVaVppTonemap && isVaapiDecoder) | |||
{ | { | |||
var tonemapFilter = GetHwTonemapFilter(options, "vaapi", "nv12") ; | var tonemapFilter = GetHwTonemapFilter(options, "vaapi", "nv12") ; | |||
mainFilters.Add(tonemapFilter); | mainFilters.Add(tonemapFilter); | |||
} | } | |||
skipping to change at line 3922 | skipping to change at line 4001 | |||
var memoryOutput = false; | var memoryOutput = false; | |||
var isUploadForOclTonemap = isSwDecoder && doOclTonemap; | var isUploadForOclTonemap = isSwDecoder && doOclTonemap; | |||
var isHwmapNotUsable = isUploadForOclTonemap && isVaapiEncoder; | var isHwmapNotUsable = isUploadForOclTonemap && isVaapiEncoder; | |||
if ((isVaapiDecoder && isSwEncoder) || isUploadForOclTonemap) | if ((isVaapiDecoder && isSwEncoder) || isUploadForOclTonemap) | |||
{ | { | |||
memoryOutput = true; | memoryOutput = true; | |||
// OUTPUT nv12 surface(memory) | // OUTPUT nv12 surface(memory) | |||
// prefer hwmap to hwdownload on opencl/vaapi. | // prefer hwmap to hwdownload on opencl/vaapi. | |||
mainFilters.Add(isHwmapNotUsable ? "hwdownload" : "hwmap"); | mainFilters.Add(isHwmapNotUsable ? "hwdownload" : "hwmap=mode=re ad"); | |||
mainFilters.Add("format=nv12"); | mainFilters.Add("format=nv12"); | |||
} | } | |||
// OUTPUT nv12 surface(memory) | // OUTPUT nv12 surface(memory) | |||
if (isSwDecoder && isVaapiEncoder) | if (isSwDecoder && isVaapiEncoder) | |||
{ | { | |||
memoryOutput = true; | memoryOutput = true; | |||
} | } | |||
if (memoryOutput) | if (memoryOutput) | |||
skipping to change at line 4075 | skipping to change at line 4154 | |||
// INPUT vaapi surface(vram) | // INPUT vaapi surface(vram) | |||
// hw deint | // hw deint | |||
if (doDeintH2645) | if (doDeintH2645) | |||
{ | { | |||
var deintFilter = GetHwDeinterlaceFilter(state, options, "va api"); | var deintFilter = GetHwDeinterlaceFilter(state, options, "va api"); | |||
mainFilters.Add(deintFilter); | mainFilters.Add(deintFilter); | |||
} | } | |||
outFormat = doOclTonemap ? string.Empty : "nv12"; | outFormat = doOclTonemap ? string.Empty : "nv12"; | |||
var hwScaleFilter = GetHwScaleFilter("vaapi", outFormat, inW, in H, reqW, reqH, reqMaxW, reqMaxH); | var hwScaleFilter = GetHwScaleFilter("vaapi", outFormat, inW, in H, reqW, reqH, reqMaxW, reqMaxH); | |||
// allocate extra pool sizes for vaapi vpp | ||||
if (!string.IsNullOrEmpty(hwScaleFilter)) | ||||
{ | ||||
hwScaleFilter += ":extra_hw_frames=24"; | ||||
} | ||||
// hw scale | // hw scale | |||
mainFilters.Add(hwScaleFilter); | mainFilters.Add(hwScaleFilter); | |||
} | } | |||
if (doOclTonemap && isVaapiDecoder) | if (doOclTonemap && isVaapiDecoder) | |||
{ | { | |||
if (isi965Driver) | if (isi965Driver) | |||
{ | { | |||
// map from vaapi to opencl via vaapi-opencl interop(Intel o nly). | // map from vaapi to opencl via vaapi-opencl interop(Intel o nly). | |||
mainFilters.Add("hwmap=derive_device=opencl"); | mainFilters.Add("hwmap=derive_device=opencl"); | |||
skipping to change at line 4379 | skipping to change at line 4465 | |||
protected string GetHardwareVideoDecoder(EncodingJobInfo state, Encoding Options options) | protected string GetHardwareVideoDecoder(EncodingJobInfo state, Encoding Options options) | |||
{ | { | |||
var videoStream = state.VideoStream; | var videoStream = state.VideoStream; | |||
var mediaSource = state.MediaSource; | var mediaSource = state.MediaSource; | |||
if (videoStream == null || mediaSource == null) | if (videoStream == null || mediaSource == null) | |||
{ | { | |||
return null; | return null; | |||
} | } | |||
// HWA decoders can handle both video files and video folders. | // HWA decoders can handle both video files and video folders. | |||
var videoType = mediaSource.VideoType; | var videoType = state.VideoType; | |||
if (videoType != VideoType.VideoFile | if (videoType != VideoType.VideoFile | |||
&& videoType != VideoType.Iso | && videoType != VideoType.Iso | |||
&& videoType != VideoType.Dvd | && videoType != VideoType.Dvd | |||
&& videoType != VideoType.BluRay) | && videoType != VideoType.BluRay) | |||
{ | { | |||
return null; | return null; | |||
} | } | |||
if (IsCopyCodec(state.OutputVideoCodec)) | if (IsCopyCodec(state.OutputVideoCodec)) | |||
{ | { | |||
skipping to change at line 4520 | skipping to change at line 4606 | |||
var isWindows = OperatingSystem.IsWindows(); | var isWindows = OperatingSystem.IsWindows(); | |||
var isLinux = OperatingSystem.IsLinux(); | var isLinux = OperatingSystem.IsLinux(); | |||
var isMacOS = OperatingSystem.IsMacOS(); | var isMacOS = OperatingSystem.IsMacOS(); | |||
var isD3d11Supported = isWindows && _mediaEncoder.SupportsHwaccel("d 3d11va"); | var isD3d11Supported = isWindows && _mediaEncoder.SupportsHwaccel("d 3d11va"); | |||
var isVaapiSupported = isLinux && IsVaapiSupported(state); | var isVaapiSupported = isLinux && IsVaapiSupported(state); | |||
var isCudaSupported = (isLinux || isWindows) && IsCudaFullSupported( ); | var isCudaSupported = (isLinux || isWindows) && IsCudaFullSupported( ); | |||
var isQsvSupported = (isLinux || isWindows) && _mediaEncoder.Support sHwaccel("qsv"); | var isQsvSupported = (isLinux || isWindows) && _mediaEncoder.Support sHwaccel("qsv"); | |||
var isVideotoolboxSupported = isMacOS && _mediaEncoder.SupportsHwacc el("videotoolbox"); | var isVideotoolboxSupported = isMacOS && _mediaEncoder.SupportsHwacc el("videotoolbox"); | |||
var isCodecAvailable = options.HardwareDecodingCodecs.Contains(video Codec, StringComparison.OrdinalIgnoreCase); | var isCodecAvailable = options.HardwareDecodingCodecs.Contains(video Codec, StringComparison.OrdinalIgnoreCase); | |||
var ffmpegVersion = _mediaEncoder.EncoderVersion; | ||||
// Set the av1 codec explicitly to trigger hw accelerator, otherwise libdav1d will be used. | // Set the av1 codec explicitly to trigger hw accelerator, otherwise libdav1d will be used. | |||
var isAv1 = string.Equals(videoCodec, "av1", StringComparison.Ordina | var isAv1 = ffmpegVersion < _minFFmpegImplictHwaccel | |||
lIgnoreCase); | && string.Equals(videoCodec, "av1", StringComparison.OrdinalIgno | |||
reCase); | ||||
// Allow profile mismatch if decoding H.264 baseline with d3d11va an | ||||
d vaapi hwaccels. | ||||
var profileMismatch = string.Equals(videoCodec, "h264", StringCompar | ||||
ison.OrdinalIgnoreCase) | ||||
&& string.Equals(state.VideoStream?.Profile, "baseline", StringC | ||||
omparison.OrdinalIgnoreCase); | ||||
// Disable the extra internal copy in nvdec. We already handle it in | ||||
filter chain. | ||||
var nvdecNoInternalCopy = ffmpegVersion >= _minFFmpegHwaUnsafeOutput | ||||
; | ||||
if (bitDepth == 10 && isCodecAvailable) | if (bitDepth == 10 && isCodecAvailable) | |||
{ | { | |||
if (string.Equals(videoCodec, "hevc", StringComparison.OrdinalIg noreCase) | if (string.Equals(videoCodec, "hevc", StringComparison.OrdinalIg noreCase) | |||
&& options.HardwareDecodingCodecs.Contains("hevc", StringCom parison.OrdinalIgnoreCase) | && options.HardwareDecodingCodecs.Contains("hevc", StringCom parison.OrdinalIgnoreCase) | |||
&& !options.EnableDecodingColorDepth10Hevc) | && !options.EnableDecodingColorDepth10Hevc) | |||
{ | { | |||
return null; | return null; | |||
} | } | |||
skipping to change at line 4547 | skipping to change at line 4643 | |||
} | } | |||
} | } | |||
// Intel qsv/d3d11va/vaapi | // Intel qsv/d3d11va/vaapi | |||
if (string.Equals(options.HardwareAccelerationType, "qsv", StringCom parison.OrdinalIgnoreCase)) | if (string.Equals(options.HardwareAccelerationType, "qsv", StringCom parison.OrdinalIgnoreCase)) | |||
{ | { | |||
if (options.PreferSystemNativeHwDecoder) | if (options.PreferSystemNativeHwDecoder) | |||
{ | { | |||
if (isVaapiSupported && isCodecAvailable) | if (isVaapiSupported && isCodecAvailable) | |||
{ | { | |||
return " -hwaccel vaapi" + (outputHwSurface ? " -hwaccel | return " -hwaccel vaapi" + (outputHwSurface ? " -hwaccel | |||
_output_format vaapi" : string.Empty) + (isAv1 ? " -c:v av1" : string.Empty); | _output_format vaapi" : string.Empty) | |||
+ (profileMismatch ? " -hwaccel_flags +allow_profile | ||||
_mismatch" : string.Empty) + (isAv1 ? " -c:v av1" : string.Empty); | ||||
} | } | |||
if (isD3d11Supported && isCodecAvailable) | if (isD3d11Supported && isCodecAvailable) | |||
{ | { | |||
// set -threads 3 to intel d3d11va decoder explicitly. L ower threads may result in dead lock. | // set -threads 3 to intel d3d11va decoder explicitly. L ower threads may result in dead lock. | |||
// on newer devices such as Xe, the larger the init_pool _size, the longer the initialization time for opencl to derive from d3d11. | // on newer devices such as Xe, the larger the init_pool _size, the longer the initialization time for opencl to derive from d3d11. | |||
return " -hwaccel d3d11va" + (outputHwSurface ? " -hwacc | return " -hwaccel d3d11va" + (outputHwSurface ? " -hwacc | |||
el_output_format d3d11" : string.Empty) + " -threads 3" + (isAv1 ? " -c:v av1" : | el_output_format d3d11" : string.Empty) | |||
string.Empty); | + (profileMismatch ? " -hwaccel_flags +allow_profile | |||
_mismatch" : string.Empty) + " -threads 3" + (isAv1 ? " -c:v av1" : string.Empty | ||||
); | ||||
} | } | |||
} | } | |||
else | else | |||
{ | { | |||
if (isQsvSupported && isCodecAvailable) | if (isQsvSupported && isCodecAvailable) | |||
{ | { | |||
return " -hwaccel qsv" + (outputHwSurface ? " -hwaccel_o utput_format qsv" : string.Empty); | return " -hwaccel qsv" + (outputHwSurface ? " -hwaccel_o utput_format qsv" : string.Empty); | |||
} | } | |||
} | } | |||
} | } | |||
// Nvidia cuda | // Nvidia cuda | |||
if (string.Equals(options.HardwareAccelerationType, "nvenc", StringC omparison.OrdinalIgnoreCase)) | if (string.Equals(options.HardwareAccelerationType, "nvenc", StringC omparison.OrdinalIgnoreCase)) | |||
{ | { | |||
if (isCudaSupported && isCodecAvailable) | if (isCudaSupported && isCodecAvailable) | |||
{ | { | |||
if (options.EnableEnhancedNvdecDecoder) | if (options.EnableEnhancedNvdecDecoder) | |||
{ | { | |||
// set -threads 1 to nvdec decoder explicitly since it d oesn't implement threading support. | // set -threads 1 to nvdec decoder explicitly since it d oesn't implement threading support. | |||
return " -hwaccel cuda" + (outputHwSurface ? " -hwaccel_ | return " -hwaccel cuda" + (outputHwSurface ? " -hwaccel_ | |||
output_format cuda" : string.Empty) + " -threads 1" + (isAv1 ? " -c:v av1" : str | output_format cuda" : string.Empty) | |||
ing.Empty); | + (nvdecNoInternalCopy ? " -hwaccel_flags +unsafe_ou | |||
tput" : string.Empty) + " -threads 1" + (isAv1 ? " -c:v av1" : string.Empty); | ||||
} | } | |||
else | else | |||
{ | { | |||
// cuvid decoder doesn't have threading issue. | // cuvid decoder doesn't have threading issue. | |||
return " -hwaccel cuda" + (outputHwSurface ? " -hwaccel_ output_format cuda" : string.Empty); | return " -hwaccel cuda" + (outputHwSurface ? " -hwaccel_ output_format cuda" : string.Empty); | |||
} | } | |||
} | } | |||
} | } | |||
// Amd d3d11va | // Amd d3d11va | |||
if (string.Equals(options.HardwareAccelerationType, "amf", StringCom parison.OrdinalIgnoreCase)) | if (string.Equals(options.HardwareAccelerationType, "amf", StringCom parison.OrdinalIgnoreCase)) | |||
{ | { | |||
if (isD3d11Supported && isCodecAvailable) | if (isD3d11Supported && isCodecAvailable) | |||
{ | { | |||
return " -hwaccel d3d11va" + (outputHwSurface ? " -hwaccel_o | return " -hwaccel d3d11va" + (outputHwSurface ? " -hwaccel_o | |||
utput_format d3d11" : string.Empty) + (isAv1 ? " -c:v av1" : string.Empty); | utput_format d3d11" : string.Empty) | |||
+ (profileMismatch ? " -hwaccel_flags +allow_profile_mis | ||||
match" : string.Empty) + (isAv1 ? " -c:v av1" : string.Empty); | ||||
} | } | |||
} | } | |||
// Vaapi | // Vaapi | |||
if (string.Equals(options.HardwareAccelerationType, "vaapi", StringC omparison.OrdinalIgnoreCase) | if (string.Equals(options.HardwareAccelerationType, "vaapi", StringC omparison.OrdinalIgnoreCase) | |||
&& isVaapiSupported | && isVaapiSupported | |||
&& isCodecAvailable) | && isCodecAvailable) | |||
{ | { | |||
return " -hwaccel vaapi" + (outputHwSurface ? " -hwaccel_output_ | return " -hwaccel vaapi" + (outputHwSurface ? " -hwaccel_output_ | |||
format vaapi" : string.Empty) + (isAv1 ? " -c:v av1" : string.Empty); | format vaapi" : string.Empty) | |||
+ (profileMismatch ? " -hwaccel_flags +allow_profile_mismatc | ||||
h" : string.Empty) + (isAv1 ? " -c:v av1" : string.Empty); | ||||
} | } | |||
// Apple videotoolbox | ||||
if (string.Equals(options.HardwareAccelerationType, "videotoolbox", StringComparison.OrdinalIgnoreCase) | if (string.Equals(options.HardwareAccelerationType, "videotoolbox", StringComparison.OrdinalIgnoreCase) | |||
&& isVideotoolboxSupported | && isVideotoolboxSupported | |||
&& isCodecAvailable) | && isCodecAvailable) | |||
{ | { | |||
return " -hwaccel videotoolbox" + (outputHwSurface ? " -hwaccel_ output_format videotoolbox_vld" : string.Empty); | return " -hwaccel videotoolbox" + (outputHwSurface ? " -hwaccel_ output_format videotoolbox_vld" : string.Empty); | |||
} | } | |||
return null; | return null; | |||
} | } | |||
skipping to change at line 5216 | skipping to change at line 5318 | |||
} | } | |||
private void ShiftAudioCodecsIfNeeded(List<string> audioCodecs, MediaStr eam audioStream) | private void ShiftAudioCodecsIfNeeded(List<string> audioCodecs, MediaStr eam audioStream) | |||
{ | { | |||
// No need to shift if there is only one supported audio codec. | // No need to shift if there is only one supported audio codec. | |||
if (audioCodecs.Count < 2) | if (audioCodecs.Count < 2) | |||
{ | { | |||
return; | return; | |||
} | } | |||
var inputChannels = audioStream == null ? 6 : audioStream.Channels ? | var inputChannels = audioStream is null ? 6 : audioStream.Channels ? | |||
? 6; | ? 6; | |||
var shiftAudioCodecs = new List<string>(); | ||||
if (inputChannels >= 6) | if (inputChannels >= 6) | |||
{ | { | |||
return; | // DTS and TrueHD are not supported by HLS | |||
// Keep them in the supported codecs list, but shift them to the | ||||
end of the list so that if transcoding happens, another codec is used | ||||
shiftAudioCodecs.Add("dca"); | ||||
shiftAudioCodecs.Add("truehd"); | ||||
} | ||||
else | ||||
{ | ||||
// Transcoding to 2ch ac3 or eac3 almost always causes a playbac | ||||
k failure | ||||
// Keep them in the supported codecs list, but shift them to the | ||||
end of the list so that if transcoding happens, another codec is used | ||||
shiftAudioCodecs.Add("ac3"); | ||||
shiftAudioCodecs.Add("eac3"); | ||||
} | } | |||
// Transcoding to 2ch ac3 almost always causes a playback failure | ||||
// Keep it in the supported codecs list, but shift it to the end of | ||||
the list so that if transcoding happens, another codec is used | ||||
var shiftAudioCodecs = new[] { "ac3", "eac3" }; | ||||
if (audioCodecs.All(i => shiftAudioCodecs.Contains(i, StringComparis on.OrdinalIgnoreCase))) | if (audioCodecs.All(i => shiftAudioCodecs.Contains(i, StringComparis on.OrdinalIgnoreCase))) | |||
{ | { | |||
return; | return; | |||
} | } | |||
while (shiftAudioCodecs.Contains(audioCodecs[0], StringComparison.Or dinalIgnoreCase)) | while (shiftAudioCodecs.Contains(audioCodecs[0], StringComparison.Or dinalIgnoreCase)) | |||
{ | { | |||
var removed = shiftAudioCodecs[0]; | var removed = shiftAudioCodecs[0]; | |||
audioCodecs.RemoveAt(0); | audioCodecs.RemoveAt(0); | |||
audioCodecs.Add(removed); | audioCodecs.Add(removed); | |||
skipping to change at line 5401 | skipping to change at line 5511 | |||
args += keyFrameArg; | args += keyFrameArg; | |||
var hasGraphicalSubs = state.SubtitleStream != null && !state.Su btitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDel iveryMethod.Encode; | var hasGraphicalSubs = state.SubtitleStream != null && !state.Su btitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDel iveryMethod.Encode; | |||
var hasCopyTs = false; | var hasCopyTs = false; | |||
// video processing filters. | // video processing filters. | |||
var videoProcessParam = GetVideoProcessingFilterParam(state, enc odingOptions, videoCodec); | var videoProcessParam = GetVideoProcessingFilterParam(state, enc odingOptions, videoCodec); | |||
args += videoProcessParam; | var negativeMapArgs = GetNegativeMapArgsByFilters(state, videoPr | |||
ocessParam); | ||||
args = negativeMapArgs + args + videoProcessParam; | ||||
hasCopyTs = videoProcessParam.Contains("copyts", StringCompariso n.OrdinalIgnoreCase); | hasCopyTs = videoProcessParam.Contains("copyts", StringCompariso n.OrdinalIgnoreCase); | |||
if (state.RunTimeTicks.HasValue && state.BaseRequest.CopyTimesta mps) | if (state.RunTimeTicks.HasValue && state.BaseRequest.CopyTimesta mps) | |||
{ | { | |||
if (!hasCopyTs) | if (!hasCopyTs) | |||
{ | { | |||
args += " -copyts"; | args += " -copyts"; | |||
} | } | |||
skipping to change at line 5466 | skipping to change at line 5578 | |||
// Add the number of audio channels | // Add the number of audio channels | |||
var channels = state.OutputAudioChannels; | var channels = state.OutputAudioChannels; | |||
if (channels.HasValue) | if (channels.HasValue) | |||
{ | { | |||
args += " -ac " + channels.Value; | args += " -ac " + channels.Value; | |||
} | } | |||
var bitrate = state.OutputAudioBitrate; | var bitrate = state.OutputAudioBitrate; | |||
if (bitrate.HasValue) | if (bitrate.HasValue && !LosslessAudioCodecs.Contains(codec, StringC omparison.OrdinalIgnoreCase)) | |||
{ | { | |||
args += " -ab " + bitrate.Value.ToString(CultureInfo.InvariantCu lture); | args += " -ab " + bitrate.Value.ToString(CultureInfo.InvariantCu lture); | |||
} | } | |||
if (state.OutputAudioSampleRate.HasValue) | if (state.OutputAudioSampleRate.HasValue) | |||
{ | { | |||
args += " -ar " + state.OutputAudioSampleRate.Value.ToString(Cul tureInfo.InvariantCulture); | args += " -ar " + state.OutputAudioSampleRate.Value.ToString(Cul tureInfo.InvariantCulture); | |||
} | } | |||
args += GetAudioFilterParam(state, encodingOptions); | args += GetAudioFilterParam(state, encodingOptions); | |||
return args; | return args; | |||
} | } | |||
public string GetProgressiveAudioFullCommandLine(EncodingJobInfo state, EncodingOptions encodingOptions, string outputPath) | public string GetProgressiveAudioFullCommandLine(EncodingJobInfo state, EncodingOptions encodingOptions, string outputPath) | |||
{ | { | |||
var audioTranscodeParams = new List<string>(); | var audioTranscodeParams = new List<string>(); | |||
var bitrate = state.OutputAudioBitrate; | var bitrate = state.OutputAudioBitrate; | |||
var channels = state.OutputAudioChannels; | ||||
var outputCodec = state.OutputAudioCodec; | ||||
if (bitrate.HasValue) | if (bitrate.HasValue && !LosslessAudioCodecs.Contains(outputCodec, S tringComparison.OrdinalIgnoreCase)) | |||
{ | { | |||
audioTranscodeParams.Add("-ab " + bitrate.Value.ToString(Culture Info.InvariantCulture)); | audioTranscodeParams.Add("-ab " + bitrate.Value.ToString(Culture Info.InvariantCulture)); | |||
} | } | |||
if (state.OutputAudioChannels.HasValue) | if (state.OutputAudioChannels.HasValue) | |||
{ | { | |||
audioTranscodeParams.Add("-ac " + state.OutputAudioChannels.Valu e.ToString(CultureInfo.InvariantCulture)); | audioTranscodeParams.Add("-ac " + state.OutputAudioChannels.Valu e.ToString(CultureInfo.InvariantCulture)); | |||
} | } | |||
if (!string.Equals(state.OutputAudioCodec, "opus", StringComparison. | if (!string.IsNullOrEmpty(outputCodec)) | |||
OrdinalIgnoreCase)) | { | |||
audioTranscodeParams.Add("-acodec " + GetAudioEncoder(state)); | ||||
} | ||||
if (!string.Equals(outputCodec, "opus", StringComparison.OrdinalIgno | ||||
reCase)) | ||||
{ | { | |||
// opus only supports specific sampling rates | // opus only supports specific sampling rates | |||
var sampleRate = state.OutputAudioSampleRate; | var sampleRate = state.OutputAudioSampleRate; | |||
if (sampleRate.HasValue) | if (sampleRate.HasValue) | |||
{ | { | |||
var sampleRateValue = sampleRate.Value switch | var sampleRateValue = sampleRate.Value switch | |||
{ | { | |||
<= 8000 => 8000, | <= 8000 => 8000, | |||
<= 12000 => 12000, | <= 12000 => 12000, | |||
<= 16000 => 16000, | <= 16000 => 16000, | |||
End of changes. 56 change blocks. | ||||
89 lines changed or deleted | 230 lines changed or added |