Merge pull request #7828 from nyanmisaka/fix-dovi-tonemap
Fix Dolby Vision profile 5 and 8 to SDR HW tone-mapping
(cherry picked from commit 8595a979a8
)
Signed-off-by: crobibero <cody@robibe.ro>
This commit is contained in:
parent
1a9919d487
commit
c19c787273
|
@ -1711,20 +1711,30 @@ namespace Jellyfin.Api.Controllers
|
||||||
return audioTranscodeParams;
|
return audioTranscodeParams;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// flac and opus are experimental in mp4 muxer
|
||||||
|
var strictArgs = string.Empty;
|
||||||
|
|
||||||
|
if (string.Equals(state.ActualOutputAudioCodec, "flac", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| string.Equals(state.ActualOutputAudioCodec, "opus", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
strictArgs = " -strict -2";
|
||||||
|
}
|
||||||
|
|
||||||
if (EncodingHelper.IsCopyCodec(audioCodec))
|
if (EncodingHelper.IsCopyCodec(audioCodec))
|
||||||
{
|
{
|
||||||
var videoCodec = _encodingHelper.GetVideoEncoder(state, _encodingOptions);
|
var videoCodec = _encodingHelper.GetVideoEncoder(state, _encodingOptions);
|
||||||
var bitStreamArgs = EncodingHelper.GetAudioBitStreamArguments(state, state.Request.SegmentContainer, state.MediaSource.Container);
|
var bitStreamArgs = EncodingHelper.GetAudioBitStreamArguments(state, state.Request.SegmentContainer, state.MediaSource.Container);
|
||||||
|
var copyArgs = "-codec:a:0 copy" + bitStreamArgs + strictArgs;
|
||||||
|
|
||||||
if (EncodingHelper.IsCopyCodec(videoCodec) && state.EnableBreakOnNonKeyFrames(videoCodec))
|
if (EncodingHelper.IsCopyCodec(videoCodec) && state.EnableBreakOnNonKeyFrames(videoCodec))
|
||||||
{
|
{
|
||||||
return "-codec:a:0 copy -strict -2 -copypriorss:a:0 0" + bitStreamArgs;
|
return copyArgs + " -copypriorss:a:0 0";
|
||||||
}
|
}
|
||||||
|
|
||||||
return "-codec:a:0 copy -strict -2" + bitStreamArgs;
|
return copyArgs;
|
||||||
}
|
}
|
||||||
|
|
||||||
var args = "-codec:a:0 " + audioCodec;
|
var args = "-codec:a:0 " + audioCodec + strictArgs;
|
||||||
|
|
||||||
var channels = state.OutputAudioChannels;
|
var channels = state.OutputAudioChannels;
|
||||||
|
|
||||||
|
@ -1779,11 +1789,12 @@ namespace Jellyfin.Api.Controllers
|
||||||
|| string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase))
|
|| string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
if (EncodingHelper.IsCopyCodec(codec)
|
if (EncodingHelper.IsCopyCodec(codec)
|
||||||
&& (string.Equals(state.VideoStream.CodecTag, "dvh1", StringComparison.OrdinalIgnoreCase)
|
&& (string.Equals(state.VideoStream.CodecTag, "dovi", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| string.Equals(state.VideoStream.CodecTag, "dvh1", StringComparison.OrdinalIgnoreCase)
|
||||||
|| string.Equals(state.VideoStream.CodecTag, "dvhe", StringComparison.OrdinalIgnoreCase)))
|
|| string.Equals(state.VideoStream.CodecTag, "dvhe", StringComparison.OrdinalIgnoreCase)))
|
||||||
{
|
{
|
||||||
// Prefer dvh1 to dvhe
|
// Prefer dvh1 to dvhe
|
||||||
args += " -tag:v:0 dvh1";
|
args += " -tag:v:0 dvh1 -strict -2";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -13,11 +13,13 @@ using System.Threading;
|
||||||
using Jellyfin.Data.Enums;
|
using Jellyfin.Data.Enums;
|
||||||
using Jellyfin.Extensions;
|
using Jellyfin.Extensions;
|
||||||
using MediaBrowser.Common.Configuration;
|
using MediaBrowser.Common.Configuration;
|
||||||
|
using MediaBrowser.Controller.Extensions;
|
||||||
using MediaBrowser.Model.Configuration;
|
using MediaBrowser.Model.Configuration;
|
||||||
using MediaBrowser.Model.Dlna;
|
using MediaBrowser.Model.Dlna;
|
||||||
using MediaBrowser.Model.Dto;
|
using MediaBrowser.Model.Dto;
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
using MediaBrowser.Model.MediaInfo;
|
using MediaBrowser.Model.MediaInfo;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.MediaEncoding
|
namespace MediaBrowser.Controller.MediaEncoding
|
||||||
{
|
{
|
||||||
|
@ -32,6 +34,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
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 static readonly string[] _videoProfilesH264 = new[]
|
private static readonly string[] _videoProfilesH264 = new[]
|
||||||
{
|
{
|
||||||
|
@ -54,11 +57,13 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
public EncodingHelper(
|
public EncodingHelper(
|
||||||
IApplicationPaths appPaths,
|
IApplicationPaths appPaths,
|
||||||
IMediaEncoder mediaEncoder,
|
IMediaEncoder mediaEncoder,
|
||||||
ISubtitleEncoder subtitleEncoder)
|
ISubtitleEncoder subtitleEncoder,
|
||||||
|
IConfiguration config)
|
||||||
{
|
{
|
||||||
_appPaths = appPaths;
|
_appPaths = appPaths;
|
||||||
_mediaEncoder = mediaEncoder;
|
_mediaEncoder = mediaEncoder;
|
||||||
_subtitleEncoder = subtitleEncoder;
|
_subtitleEncoder = subtitleEncoder;
|
||||||
|
_config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetH264Encoder(EncodingJobInfo state, EncodingOptions encodingOptions)
|
public string GetH264Encoder(EncodingJobInfo state, EncodingOptions encodingOptions)
|
||||||
|
@ -144,15 +149,28 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
|
|
||||||
private bool IsHwTonemapAvailable(EncodingJobInfo state, EncodingOptions options)
|
private bool IsHwTonemapAvailable(EncodingJobInfo state, EncodingOptions options)
|
||||||
{
|
{
|
||||||
if (state.VideoStream == null)
|
if (state.VideoStream == null
|
||||||
|
|| !options.EnableTonemapping
|
||||||
|
|| GetVideoColorBitDepth(state) != 10)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return options.EnableTonemapping
|
if (string.Equals(state.VideoStream.CodecTag, "dovi", StringComparison.OrdinalIgnoreCase)
|
||||||
&& (string.Equals(state.VideoStream.ColorTransfer, "smpte2084", StringComparison.OrdinalIgnoreCase)
|
|| string.Equals(state.VideoStream.CodecTag, "dvh1", StringComparison.OrdinalIgnoreCase)
|
||||||
|| string.Equals(state.VideoStream.ColorTransfer, "arib-std-b67", StringComparison.OrdinalIgnoreCase))
|
|| string.Equals(state.VideoStream.CodecTag, "dvhe", StringComparison.OrdinalIgnoreCase))
|
||||||
&& GetVideoColorBitDepth(state) == 10;
|
{
|
||||||
|
// Only native SW decoder and HW accelerator can parse dovi rpu.
|
||||||
|
var vidDecoder = GetHardwareVideoDecoder(state, options) ?? string.Empty;
|
||||||
|
var isSwDecoder = string.IsNullOrEmpty(vidDecoder);
|
||||||
|
var isNvdecDecoder = vidDecoder.Contains("cuda", StringComparison.OrdinalIgnoreCase);
|
||||||
|
var isVaapiDecoder = vidDecoder.Contains("vaapi", StringComparison.OrdinalIgnoreCase);
|
||||||
|
var isD3d11vaDecoder = vidDecoder.Contains("d3d11va", StringComparison.OrdinalIgnoreCase);
|
||||||
|
return isSwDecoder || isNvdecDecoder || isVaapiDecoder || isD3d11vaDecoder;
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.Equals(state.VideoStream.ColorTransfer, "smpte2084", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| string.Equals(state.VideoStream.ColorTransfer, "arib-std-b67", StringComparison.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsVaapiVppTonemapAvailable(EncodingJobInfo state, EncodingOptions options)
|
private bool IsVaapiVppTonemapAvailable(EncodingJobInfo state, EncodingOptions options)
|
||||||
|
@ -516,8 +534,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
|
|
||||||
if (string.Equals(codec, "flac", StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(codec, "flac", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
// flac is experimental in mp4 muxer
|
return "flac";
|
||||||
return "flac -strict -2";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return codec.ToLowerInvariant();
|
return codec.ToLowerInvariant();
|
||||||
|
@ -1024,7 +1041,8 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
if (string.Equals(videoCodec, "h264_amf", StringComparison.OrdinalIgnoreCase)
|
if (string.Equals(videoCodec, "h264_amf", StringComparison.OrdinalIgnoreCase)
|
||||||
|| string.Equals(videoCodec, "hevc_amf", StringComparison.OrdinalIgnoreCase))
|
|| string.Equals(videoCodec, "hevc_amf", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return FormattableString.Invariant($" -qmin 18 -qmax 32 -b:v {bitrate} -maxrate {bitrate} -bufsize {bufsize}");
|
// Override the too high default qmin 18 in transcoding preset
|
||||||
|
return FormattableString.Invariant($" -rc cbr -qmin 0 -qmax 32 -b:v {bitrate} -maxrate {bitrate} -bufsize {bufsize}");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.Equals(videoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase)
|
if (string.Equals(videoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase)
|
||||||
|
@ -1222,10 +1240,9 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
// Example: we encoded half of desired length, then codec detected
|
// Example: we encoded half of desired length, then codec detected
|
||||||
// scene cut and inserted a keyframe; next forced keyframe would
|
// scene cut and inserted a keyframe; next forced keyframe would
|
||||||
// be created outside of segment, which breaks seeking.
|
// be created outside of segment, which breaks seeking.
|
||||||
// -sc_threshold 0 is used to prevent the hardware encoder from post processing to break the set keyframe.
|
|
||||||
gopArg = string.Format(
|
gopArg = string.Format(
|
||||||
CultureInfo.InvariantCulture,
|
CultureInfo.InvariantCulture,
|
||||||
" -g:v:0 {0} -keyint_min:v:0 {0} -sc_threshold:v:0 0",
|
" -g:v:0 {0} -keyint_min:v:0 {0}",
|
||||||
Math.Ceiling(segmentLength * framerate.Value));
|
Math.Ceiling(segmentLength * framerate.Value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1245,6 +1262,12 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
|| string.Equals(codec, "hevc_vaapi", StringComparison.OrdinalIgnoreCase))
|
|| string.Equals(codec, "hevc_vaapi", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
args += keyFrameArg;
|
args += keyFrameArg;
|
||||||
|
|
||||||
|
// prevent the libx264 from post processing to break the set keyframe.
|
||||||
|
if (string.Equals(codec, "libx264", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
args += " -sc_threshold:v:0 0";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -4865,22 +4888,21 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
public string GetInputModifier(EncodingJobInfo state, EncodingOptions encodingOptions, string segmentContainer)
|
public string GetInputModifier(EncodingJobInfo state, EncodingOptions encodingOptions, string segmentContainer)
|
||||||
{
|
{
|
||||||
var inputModifier = string.Empty;
|
var inputModifier = string.Empty;
|
||||||
var probeSizeArgument = string.Empty;
|
var analyzeDurationArgument = string.Empty;
|
||||||
|
|
||||||
string analyzeDurationArgument;
|
// Apply -analyzeduration as per the environment variable,
|
||||||
if (state.MediaSource.AnalyzeDurationMs.HasValue)
|
// otherwise ffmpeg will break on certain files due to default value is 0.
|
||||||
|
// The default value of -probesize is more than enough, so leave it as is.
|
||||||
|
var ffmpegAnalyzeDuration = _config.GetFFmpegAnalyzeDuration() ?? string.Empty;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(ffmpegAnalyzeDuration))
|
||||||
|
{
|
||||||
|
analyzeDurationArgument = "-analyzeduration " + ffmpegAnalyzeDuration;
|
||||||
|
}
|
||||||
|
else if (state.MediaSource.AnalyzeDurationMs.HasValue)
|
||||||
{
|
{
|
||||||
analyzeDurationArgument = "-analyzeduration " + (state.MediaSource.AnalyzeDurationMs.Value * 1000).ToString(CultureInfo.InvariantCulture);
|
analyzeDurationArgument = "-analyzeduration " + (state.MediaSource.AnalyzeDurationMs.Value * 1000).ToString(CultureInfo.InvariantCulture);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
analyzeDurationArgument = string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(probeSizeArgument))
|
|
||||||
{
|
|
||||||
inputModifier += " " + probeSizeArgument;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(analyzeDurationArgument))
|
if (!string.IsNullOrEmpty(analyzeDurationArgument))
|
||||||
{
|
{
|
||||||
|
|
|
@ -16,6 +16,7 @@ using MediaBrowser.Common;
|
||||||
using MediaBrowser.Common.Configuration;
|
using MediaBrowser.Common.Configuration;
|
||||||
using MediaBrowser.Common.Extensions;
|
using MediaBrowser.Common.Extensions;
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
|
using MediaBrowser.Controller.Extensions;
|
||||||
using MediaBrowser.Controller.MediaEncoding;
|
using MediaBrowser.Controller.MediaEncoding;
|
||||||
using MediaBrowser.MediaEncoding.Probing;
|
using MediaBrowser.MediaEncoding.Probing;
|
||||||
using MediaBrowser.Model.Dlna;
|
using MediaBrowser.Model.Dlna;
|
||||||
|
@ -49,6 +50,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
private readonly IServerConfigurationManager _configurationManager;
|
private readonly IServerConfigurationManager _configurationManager;
|
||||||
private readonly IFileSystem _fileSystem;
|
private readonly IFileSystem _fileSystem;
|
||||||
private readonly ILocalizationManager _localization;
|
private readonly ILocalizationManager _localization;
|
||||||
|
private readonly IConfiguration _config;
|
||||||
private readonly string _startupOptionFFmpegPath;
|
private readonly string _startupOptionFFmpegPath;
|
||||||
|
|
||||||
private readonly SemaphoreSlim _thumbnailResourcePool = new SemaphoreSlim(2, 2);
|
private readonly SemaphoreSlim _thumbnailResourcePool = new SemaphoreSlim(2, 2);
|
||||||
|
@ -85,6 +87,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
_configurationManager = configurationManager;
|
_configurationManager = configurationManager;
|
||||||
_fileSystem = fileSystem;
|
_fileSystem = fileSystem;
|
||||||
_localization = localization;
|
_localization = localization;
|
||||||
|
_config = config;
|
||||||
_startupOptionFFmpegPath = config.GetValue<string>(Controller.Extensions.ConfigurationExtensions.FfmpegPathKey) ?? string.Empty;
|
_startupOptionFFmpegPath = config.GetValue<string>(Controller.Extensions.ConfigurationExtensions.FfmpegPathKey) ?? string.Empty;
|
||||||
_jsonSerializerOptions = JsonDefaults.Options;
|
_jsonSerializerOptions = JsonDefaults.Options;
|
||||||
}
|
}
|
||||||
|
@ -371,8 +374,13 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
var inputFile = request.MediaSource.Path;
|
var inputFile = request.MediaSource.Path;
|
||||||
|
|
||||||
string analyzeDuration = string.Empty;
|
string analyzeDuration = string.Empty;
|
||||||
|
string ffmpegAnalyzeDuration = _config.GetFFmpegAnalyzeDuration() ?? string.Empty;
|
||||||
|
|
||||||
if (request.MediaSource.AnalyzeDurationMs > 0)
|
if (!string.IsNullOrEmpty(ffmpegAnalyzeDuration))
|
||||||
|
{
|
||||||
|
analyzeDuration = "-analyzeduration " + ffmpegAnalyzeDuration;
|
||||||
|
}
|
||||||
|
else if (request.MediaSource.AnalyzeDurationMs > 0)
|
||||||
{
|
{
|
||||||
analyzeDuration = "-analyzeduration " +
|
analyzeDuration = "-analyzeduration " +
|
||||||
(request.MediaSource.AnalyzeDurationMs * 1000).ToString();
|
(request.MediaSource.AnalyzeDurationMs * 1000).ToString();
|
||||||
|
@ -629,10 +637,15 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
filters.Add("thumbnail=n=" + (useLargerBatchSize ? "50" : "24"));
|
filters.Add("thumbnail=n=" + (useLargerBatchSize ? "50" : "24"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use SW tonemap on HDR video stream only when the zscale filter is available.
|
// Use SW tonemap on HDR10/HLG video stream only when the zscale filter is available.
|
||||||
var enableHdrExtraction = string.Equals(videoStream?.VideoRange, "HDR", StringComparison.OrdinalIgnoreCase) && SupportsFilter("zscale");
|
var enableHdrExtraction = false;
|
||||||
if (enableHdrExtraction)
|
|
||||||
|
if ((string.Equals(videoStream?.ColorTransfer, "smpte2084", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| string.Equals(videoStream?.ColorTransfer, "arib-std-b67", StringComparison.OrdinalIgnoreCase))
|
||||||
|
&& SupportsFilter("zscale"))
|
||||||
{
|
{
|
||||||
|
enableHdrExtraction = true;
|
||||||
|
|
||||||
filters.Add("zscale=t=linear:npl=100,format=gbrpf32le,zscale=p=bt709,tonemap=tonemap=hable:desat=0:peak=100,zscale=t=bt709:m=bt709,format=yuv420p");
|
filters.Add("zscale=t=linear:npl=100,format=gbrpf32le,zscale=p=bt709,tonemap=tonemap=hable:desat=0:peak=100,zscale=t=bt709:m=bt709,format=yuv420p");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -121,8 +121,7 @@ namespace MediaBrowser.Model.Entities
|
||||||
|
|
||||||
var codecTag = CodecTag;
|
var codecTag = CodecTag;
|
||||||
|
|
||||||
if (string.Equals(codecTag, "dva1", StringComparison.OrdinalIgnoreCase)
|
if (string.Equals(codecTag, "dovi", StringComparison.OrdinalIgnoreCase)
|
||||||
|| string.Equals(codecTag, "dvav", StringComparison.OrdinalIgnoreCase)
|
|
||||||
|| string.Equals(codecTag, "dvh1", StringComparison.OrdinalIgnoreCase)
|
|| string.Equals(codecTag, "dvh1", StringComparison.OrdinalIgnoreCase)
|
||||||
|| string.Equals(codecTag, "dvhe", StringComparison.OrdinalIgnoreCase)
|
|| string.Equals(codecTag, "dvhe", StringComparison.OrdinalIgnoreCase)
|
||||||
|| string.Equals(codecTag, "dav1", StringComparison.OrdinalIgnoreCase))
|
|| string.Equals(codecTag, "dav1", StringComparison.OrdinalIgnoreCase))
|
||||||
|
|
Loading…
Reference in New Issue
Block a user