diff options
| author | Frank <fhriley@gmail.com> | 2019-06-01 15:03:48 -0700 |
|---|---|---|
| committer | Frank <fhriley@gmail.com> | 2019-06-01 15:46:41 -0700 |
| commit | 3ba709fcc32d7255a2cb2466dde8c2479130a2bc (patch) | |
| tree | 9f9969e525296fbaf9afe0c4a2ec7d5267619836 /MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | |
| parent | b1f764984f7098ee1164efee77f1bcb3de9fd08a (diff) | |
Fix #1432. Add support for encoding with libx265 and hevc_nvenc.
Diffstat (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs')
| -rw-r--r-- | MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 139 |
1 files changed, 98 insertions, 41 deletions
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index e378c2b89..f4370f0a5 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -34,8 +34,16 @@ namespace MediaBrowser.Controller.MediaEncoding public string GetH264Encoder(EncodingJobInfo state, EncodingOptions encodingOptions) { - var defaultEncoder = "libx264"; + return GetH264OrH265Encoder("libx264", "h264", state, encodingOptions); + } + public string GetH265Encoder(EncodingJobInfo state, EncodingOptions encodingOptions) + { + return GetH264OrH265Encoder("libx265", "hevc", state, encodingOptions); + } + + private string GetH264OrH265Encoder(string defaultEncoder, string hwEncoder, EncodingJobInfo state, EncodingOptions encodingOptions) + { // Only use alternative encoders for video files. // When using concat with folder rips, if the mfx session fails to initialize, ffmpeg will be stuck retrying and will not exit gracefully // Since transcoding of folder rips is expiremental anyway, it's not worth adding additional variables such as this. @@ -45,14 +53,14 @@ namespace MediaBrowser.Controller.MediaEncoding var codecMap = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) { - {"qsv", "h264_qsv"}, - {"h264_qsv", "h264_qsv"}, - {"nvenc", "h264_nvenc"}, - {"amf", "h264_amf"}, - {"omx", "h264_omx"}, - {"h264_v4l2m2m", "h264_v4l2m2m"}, - {"mediacodec", "h264_mediacodec"}, - {"vaapi", "h264_vaapi"} + {"qsv", hwEncoder + "_qsv"}, + {hwEncoder + "_qsv", hwEncoder + "_qsv"}, + {"nvenc", hwEncoder + "_nvenc"}, + {"amf", hwEncoder + "_amf"}, + {"omx", hwEncoder + "_omx"}, + {hwEncoder + "_v4l2m2m", hwEncoder + "_v4l2m2m"}, + {"mediacodec", hwEncoder + "_mediacodec"}, + {"vaapi", hwEncoder + "_vaapi"} }; if (!string.IsNullOrEmpty(hwType) @@ -119,6 +127,11 @@ namespace MediaBrowser.Controller.MediaEncoding if (!string.IsNullOrEmpty(codec)) { + if (string.Equals(codec, "h265", StringComparison.OrdinalIgnoreCase) || + string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase)) + { + return GetH265Encoder(state, encodingOptions); + } if (string.Equals(codec, "h264", StringComparison.OrdinalIgnoreCase)) { return GetH264Encoder(state, encodingOptions); @@ -476,6 +489,30 @@ namespace MediaBrowser.Controller.MediaEncoding codec.IndexOf("avc", StringComparison.OrdinalIgnoreCase) != -1; } + public bool IsH265(MediaStream stream) + { + var codec = stream.Codec ?? string.Empty; + + return codec.IndexOf("265", StringComparison.OrdinalIgnoreCase) != -1 || + codec.IndexOf("hevc", StringComparison.OrdinalIgnoreCase) != -1; + } + + public string GetBitStreamArgs(MediaStream stream) + { + if (IsH264(stream)) + { + return "-bsf:v h264_mp4toannexb"; + } + else if (IsH265(stream)) + { + return "-bsf:v hevc_mp4toannexb"; + } + else + { + return null; + } + } + public string GetVideoBitrateParam(EncodingJobInfo state, string videoCodec) { var bitrate = state.OutputVideoBitrate; @@ -494,7 +531,8 @@ namespace MediaBrowser.Controller.MediaEncoding return string.Format(" -b:v {0}", bitrate.Value.ToString(_usCulture)); } - if (string.Equals(videoCodec, "libx264", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(videoCodec, "libx264", StringComparison.OrdinalIgnoreCase) || + string.Equals(videoCodec, "libx265", StringComparison.OrdinalIgnoreCase)) { // h264 return string.Format(" -maxrate {0} -bufsize {1}", @@ -515,8 +553,10 @@ namespace MediaBrowser.Controller.MediaEncoding { // Clients may direct play higher than level 41, but there's no reason to transcode higher if (double.TryParse(level, NumberStyles.Any, _usCulture, out double requestLevel) - && string.Equals(videoCodec, "h264", StringComparison.OrdinalIgnoreCase) - && requestLevel > 41) + && requestLevel > 41 + && (string.Equals(videoCodec, "h264", StringComparison.OrdinalIgnoreCase) + || string.Equals(videoCodec, "h265", StringComparison.OrdinalIgnoreCase) + || string.Equals(videoCodec, "hevc", StringComparison.OrdinalIgnoreCase))) { return "41"; } @@ -616,49 +656,53 @@ namespace MediaBrowser.Controller.MediaEncoding /// <summary> /// Gets the video bitrate to specify on the command line /// </summary> - public string GetVideoQualityParam(EncodingJobInfo state, string videoEncoder, EncodingOptions encodingOptions, string defaultH264Preset) + public string GetVideoQualityParam(EncodingJobInfo state, string videoEncoder, EncodingOptions encodingOptions, string defaultPreset) { var param = string.Empty; var isVc1 = state.VideoStream != null && string.Equals(state.VideoStream.Codec, "vc1", StringComparison.OrdinalIgnoreCase); + var isLibX265 = string.Equals(videoEncoder, "libx265", StringComparison.OrdinalIgnoreCase); - if (string.Equals(videoEncoder, "libx264", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(videoEncoder, "libx264", StringComparison.OrdinalIgnoreCase) || isLibX265) { - if (!string.IsNullOrEmpty(encodingOptions.H264Preset)) + if (!string.IsNullOrEmpty(encodingOptions.EncoderPreset)) { - param += "-preset " + encodingOptions.H264Preset; + param += "-preset " + encodingOptions.EncoderPreset; } else { - param += "-preset " + defaultH264Preset; + param += "-preset " + defaultPreset; } - if (encodingOptions.H264Crf >= 0 && encodingOptions.H264Crf <= 51) + int encodeCrf = encodingOptions.H264Crf; + if (isLibX265) { - param += " -crf " + encodingOptions.H264Crf.ToString(CultureInfo.InvariantCulture); + encodeCrf = encodingOptions.H265Crf; + } + if (encodeCrf >= 0 && encodeCrf <= 51) + { + param += " -crf " + encodeCrf.ToString(CultureInfo.InvariantCulture); } else { - param += " -crf 23"; + string defaultCrf = "23"; + if (isLibX265) + { + defaultCrf = "28"; + } + param += " -crf " + defaultCrf; } } - else if (string.Equals(videoEncoder, "libx265", StringComparison.OrdinalIgnoreCase)) - { - param += "-preset fast"; - - param += " -crf 28"; - } - // h264 (h264_qsv) else if (string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase)) { string[] valid_h264_qsv = { "veryslow", "slower", "slow", "medium", "fast", "faster", "veryfast" }; - if (valid_h264_qsv.Contains(encodingOptions.H264Preset, StringComparer.OrdinalIgnoreCase)) + if (valid_h264_qsv.Contains(encodingOptions.EncoderPreset, StringComparer.OrdinalIgnoreCase)) { - param += "-preset " + encodingOptions.H264Preset; + param += "-preset " + encodingOptions.EncoderPreset; } else { @@ -670,9 +714,10 @@ namespace MediaBrowser.Controller.MediaEncoding } // h264 (h264_nvenc) - else if (string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase)) + else if (string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase) || + string.Equals(videoEncoder, "hevc_nvenc", StringComparison.OrdinalIgnoreCase)) { - switch (encodingOptions.H264Preset) + switch (encodingOptions.EncoderPreset) { case "veryslow": @@ -786,7 +831,8 @@ namespace MediaBrowser.Controller.MediaEncoding // h264_qsv and h264_nvenc expect levels to be expressed as a decimal. libx264 supports decimal and non-decimal format // also needed for libx264 due to https://trac.ffmpeg.org/ticket/3307 if (string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) || - string.Equals(videoEncoder, "libx264", StringComparison.OrdinalIgnoreCase)) + string.Equals(videoEncoder, "libx264", StringComparison.OrdinalIgnoreCase) || + string.Equals(videoEncoder, "libx265", StringComparison.OrdinalIgnoreCase)) { switch (level) { @@ -823,9 +869,10 @@ namespace MediaBrowser.Controller.MediaEncoding } } // nvenc doesn't decode with param -level set ?! - else if (string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase)) + else if (string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase) || + string.Equals(videoEncoder, "hevc_nvenc", StringComparison.OrdinalIgnoreCase)) { - //param += ""; + // todo param += ""; } else if (!string.Equals(videoEncoder, "h264_omx", StringComparison.OrdinalIgnoreCase)) { @@ -838,6 +885,11 @@ namespace MediaBrowser.Controller.MediaEncoding param += " -x264opts:0 subme=0:me_range=4:rc_lookahead=10:me=dia:no_chroma_me:8x8dct=0:partitions=none"; } + if (string.Equals(videoEncoder, "libx265", StringComparison.OrdinalIgnoreCase)) + { + // todo + } + if (!string.Equals(videoEncoder, "h264_omx", StringComparison.OrdinalIgnoreCase) && !string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) && !string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase) && @@ -1715,7 +1767,8 @@ namespace MediaBrowser.Controller.MediaEncoding var videoStream = state.VideoStream; - if (state.DeInterlace("h264", true) && !string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase)) + if ((state.DeInterlace("h264", true) || state.DeInterlace("h265", true) || state.DeInterlace("hevc", true)) && + !string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase)) { var inputFramerate = videoStream == null ? null : videoStream.RealFrameRate; @@ -2376,7 +2429,7 @@ namespace MediaBrowser.Controller.MediaEncoding return args; } - public string GetProgressiveVideoFullCommandLine(EncodingJobInfo state, EncodingOptions encodingOptions, string outputPath, string defaultH264Preset) + public string GetProgressiveVideoFullCommandLine(EncodingJobInfo state, EncodingOptions encodingOptions, string outputPath, string defaultPreset) { // Get the output codec name var videoCodec = GetVideoEncoder(state, encodingOptions); @@ -2400,7 +2453,7 @@ namespace MediaBrowser.Controller.MediaEncoding GetInputArgument(state, encodingOptions), keyFrame, GetMapArgs(state), - GetProgressiveVideoArguments(state, encodingOptions, videoCodec, defaultH264Preset), + GetProgressiveVideoArguments(state, encodingOptions, videoCodec, defaultPreset), threads, GetProgressiveVideoAudioArguments(state, encodingOptions), GetSubtitleEmbedArguments(state), @@ -2425,7 +2478,7 @@ namespace MediaBrowser.Controller.MediaEncoding return string.Empty; } - public string GetProgressiveVideoArguments(EncodingJobInfo state, EncodingOptions encodingOptions, string videoCodec, string defaultH264Preset) + public string GetProgressiveVideoArguments(EncodingJobInfo state, EncodingOptions encodingOptions, string videoCodec, string defaultPreset) { var args = "-codec:v:0 " + videoCodec; @@ -2436,11 +2489,15 @@ namespace MediaBrowser.Controller.MediaEncoding if (string.Equals(videoCodec, "copy", StringComparison.OrdinalIgnoreCase)) { - if (state.VideoStream != null && IsH264(state.VideoStream) && + if (state.VideoStream != null && string.Equals(state.OutputContainer, "ts", StringComparison.OrdinalIgnoreCase) && !string.Equals(state.VideoStream.NalLengthSize, "0", StringComparison.OrdinalIgnoreCase)) { - args += " -bsf:v h264_mp4toannexb"; + string bitStreamArgs = GetBitStreamArgs(state.VideoStream); + if (!string.IsNullOrEmpty(bitStreamArgs)) + { + args += " " + bitStreamArgs; + } } if (state.RunTimeTicks.HasValue && state.BaseRequest.CopyTimestamps) @@ -2486,7 +2543,7 @@ namespace MediaBrowser.Controller.MediaEncoding args += GetGraphicalSubtitleParam(state, encodingOptions, videoCodec); } - var qualityParam = GetVideoQualityParam(state, videoCodec, encodingOptions, defaultH264Preset); + var qualityParam = GetVideoQualityParam(state, videoCodec, encodingOptions, defaultPreset); if (!string.IsNullOrEmpty(qualityParam)) { |
