From 29c1f54b572ceb1a09be7744f4ab2fbb5224a8a7 Mon Sep 17 00:00:00 2001 From: James Harvey <44349936+jmshrv@users.noreply.github.com> Date: Thu, 2 Feb 2023 13:54:05 +0000 Subject: Fix audio codec not being used in UniversalAudio (#9192) --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 9f7be977f..a844e6443 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -5762,6 +5762,11 @@ namespace MediaBrowser.Controller.MediaEncoding audioTranscodeParams.Add("-ac " + state.OutputAudioChannels.Value.ToString(CultureInfo.InvariantCulture)); } + if (!string.IsNullOrEmpty(state.OutputAudioCodec)) + { + audioTranscodeParams.Add("-acodec " + GetAudioEncoder(state)); + } + if (!string.Equals(state.OutputAudioCodec, "opus", StringComparison.OrdinalIgnoreCase)) { // opus only supports specific sampling rates -- cgit v1.2.3 From c70508b08984d62d3b62d233f6ce4078f2a75bb1 Mon Sep 17 00:00:00 2001 From: James Harvey <44349936+jmshrv@users.noreply.github.com> Date: Sat, 4 Feb 2023 01:56:14 +0000 Subject: Add movflags to mp4 audio encoding --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index a844e6443..d08a5116a 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -5786,6 +5786,22 @@ namespace MediaBrowser.Controller.MediaEncoding } } + // Copy the movflags from GetProgressiveVideoFullCommandLine + // See # for explanation on why this is needed + var mp4ContainerNames = new HashSet { + "mp4", + "m4a", + "m4p", + "m4b", + "m4r", + "m4v", + }; + + if (mp4ContainerNames.Contains(state.OutputContainer.ToLower())) + { + audioTranscodeParams.Add("-movflags frag_keyframe+empty_moov+delay_moov"); + } + var threads = GetNumberOfThreads(state, encodingOptions, null); var inputModifier = GetInputModifier(state, encodingOptions, null); -- cgit v1.2.3 From f2cc0e70693d134d163ab056cd412a151c1055fc Mon Sep 17 00:00:00 2001 From: James Harvey <44349936+jmshrv@users.noreply.github.com> Date: Sat, 4 Feb 2023 02:17:36 +0000 Subject: Fill in issue/PR comment --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index d08a5116a..33ecaf3bf 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -5787,7 +5787,7 @@ namespace MediaBrowser.Controller.MediaEncoding } // Copy the movflags from GetProgressiveVideoFullCommandLine - // See # for explanation on why this is needed + // See #9248 and the associated PR for why this is needed var mp4ContainerNames = new HashSet { "mp4", "m4a", -- cgit v1.2.3 From a3f41bbc0cf2b2009005ef5bb1d449002ff7017c Mon Sep 17 00:00:00 2001 From: James Harvey <44349936+jmshrv@users.noreply.github.com> Date: Sat, 4 Feb 2023 02:29:21 +0000 Subject: Remove frag_keyframe as it is not relevant for audio --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 33ecaf3bf..21cfac00a 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -5581,7 +5581,7 @@ namespace MediaBrowser.Controller.MediaEncoding && state.BaseRequest.Context == EncodingContext.Streaming) { // Comparison: https://github.com/jansmolders86/mediacenterjs/blob/master/lib/transcoding/desktop.js - format = " -f mp4 -movflags frag_keyframe+empty_moov+delay_moov"; + format = " -f mp4 -movflags empty_moov+delay_moov"; } var threads = GetNumberOfThreads(state, encodingOptions, videoCodec); -- cgit v1.2.3 From 83ae6fcdb466b2bee17af07ac415f6e5892ff3c7 Mon Sep 17 00:00:00 2001 From: James Harvey <44349936+jmshrv@users.noreply.github.com> Date: Sat, 4 Feb 2023 02:45:24 +0000 Subject: Fix Braces for multi-line statements should not share line --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 21cfac00a..c335320a7 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -5788,7 +5788,8 @@ namespace MediaBrowser.Controller.MediaEncoding // Copy the movflags from GetProgressiveVideoFullCommandLine // See #9248 and the associated PR for why this is needed - var mp4ContainerNames = new HashSet { + var mp4ContainerNames = new HashSet + { "mp4", "m4a", "m4p", -- cgit v1.2.3 From c9627f8839dbb0956df2fdb6ab8258710d837b11 Mon Sep 17 00:00:00 2001 From: James Harvey <44349936+jmshrv@users.noreply.github.com> Date: Sat, 4 Feb 2023 02:46:14 +0000 Subject: Fix Use built-in type alias --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index c335320a7..e530cba2c 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -5788,7 +5788,7 @@ namespace MediaBrowser.Controller.MediaEncoding // Copy the movflags from GetProgressiveVideoFullCommandLine // See #9248 and the associated PR for why this is needed - var mp4ContainerNames = new HashSet + var mp4ContainerNames = new HashSet { "mp4", "m4a", -- cgit v1.2.3 From 42b4ef45294833efc2cca381e555a792729889b0 Mon Sep 17 00:00:00 2001 From: James Harvey <44349936+jmshrv@users.noreply.github.com> Date: Sat, 4 Feb 2023 03:59:35 +0000 Subject: Remove frag_keyframe on audio, not video --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index e530cba2c..d6ab27b03 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -5581,7 +5581,7 @@ namespace MediaBrowser.Controller.MediaEncoding && state.BaseRequest.Context == EncodingContext.Streaming) { // Comparison: https://github.com/jansmolders86/mediacenterjs/blob/master/lib/transcoding/desktop.js - format = " -f mp4 -movflags empty_moov+delay_moov"; + format = " -f mp4 -movflags frag_keyframe+empty_moov+delay_moov"; } var threads = GetNumberOfThreads(state, encodingOptions, videoCodec); @@ -5800,7 +5800,7 @@ namespace MediaBrowser.Controller.MediaEncoding if (mp4ContainerNames.Contains(state.OutputContainer.ToLower())) { - audioTranscodeParams.Add("-movflags frag_keyframe+empty_moov+delay_moov"); + audioTranscodeParams.Add("-movflags empty_moov+delay_moov"); } var threads = GetNumberOfThreads(state, encodingOptions, null); -- cgit v1.2.3 From 4a2245fe1e97c7d450557fd1269be8584a58c478 Mon Sep 17 00:00:00 2001 From: James Harvey <44349936+jmshrv@users.noreply.github.com> Date: Sat, 4 Feb 2023 18:52:35 +0000 Subject: Move mp4 container names to class and use StringComparer.OrdinalIgnoreCase --- .../MediaEncoding/EncodingHelper.cs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index d6ab27b03..9b5edabc0 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -61,6 +61,16 @@ namespace MediaBrowser.Controller.MediaEncoding "Main10" }; + private static readonly HashSet _mp4ContainerNames = new(StringComparer.OrdinalIgnoreCase) + { + "mp4", + "m4a", + "m4p", + "m4b", + "m4r", + "m4v", + }; + public EncodingHelper( IApplicationPaths appPaths, IMediaEncoder mediaEncoder, @@ -5788,17 +5798,7 @@ namespace MediaBrowser.Controller.MediaEncoding // Copy the movflags from GetProgressiveVideoFullCommandLine // See #9248 and the associated PR for why this is needed - var mp4ContainerNames = new HashSet - { - "mp4", - "m4a", - "m4p", - "m4b", - "m4r", - "m4v", - }; - - if (mp4ContainerNames.Contains(state.OutputContainer.ToLower())) + if (_mp4ContainerNames.Contains(state.OutputContainer)) { audioTranscodeParams.Add("-movflags empty_moov+delay_moov"); } -- cgit v1.2.3 From ef4ae9a2dd9d5aff87262910e87d734ef3183125 Mon Sep 17 00:00:00 2001 From: gnattu Date: Thu, 9 Feb 2023 06:42:17 +0800 Subject: Implement hardware filters for videotoolbox, use Apple AAC encoder when available (#7807) --- .../MediaEncoding/EncodingHelper.cs | 86 ++++++++++++++++++++++ .../Encoder/EncoderValidator.cs | 6 +- 2 files changed, 91 insertions(+), 1 deletion(-) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 9b5edabc0..14547d440 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -559,6 +559,12 @@ namespace MediaBrowser.Controller.MediaEncoding if (string.Equals(codec, "aac", StringComparison.OrdinalIgnoreCase)) { + // Use Apple's aac encoder if available as it provides best audio quality + if (_mediaEncoder.SupportsEncoder("aac_at")) + { + return "aac_at"; + } + // Use libfdk_aac for better audio quality if using custom build of FFmpeg which has fdk_aac support if (_mediaEncoder.SupportsEncoder("libfdk_aac")) { @@ -2814,6 +2820,13 @@ namespace MediaBrowser.Controller.MediaEncoding { return "deinterlace_qsv=mode=2"; } + else if (hwDeintSuffix.Contains("videotoolbox", StringComparison.OrdinalIgnoreCase)) + { + return string.Format( + CultureInfo.InvariantCulture, + "yadif_videotoolbox={0}:-1:0", + doubleRateDeint ? "1" : "0"); + } return string.Empty; } @@ -4450,6 +4463,75 @@ namespace MediaBrowser.Controller.MediaEncoding return (mainFilters, subFilters, overlayFilters); } + /// + /// Gets the parameter of Apple VideoToolBox filter chain. + /// + /// Encoding state. + /// Encoding options. + /// Video encoder to use. + /// The tuple contains three lists: main, sub and overlay filters. + public (List MainFilters, List SubFilters, List OverlayFilters) GetAppleVidFilterChain( + EncodingJobInfo state, + EncodingOptions options, + string vidEncoder) + { + if (!string.Equals(options.HardwareAccelerationType, "videotoolbox", StringComparison.OrdinalIgnoreCase)) + { + return (null, null, null); + } + + var swFilterChain = GetSwVidFilterChain(state, options, vidEncoder); + + if (!options.EnableHardwareEncoding) + { + return swFilterChain; + } + + if (_mediaEncoder.EncoderVersion.CompareTo(new Version("5.0.0")) < 0) + { + // All features used here requires ffmpeg 5.0 or later, fallback to software filters if using an old ffmpeg + return swFilterChain; + } + + var doDeintH264 = state.DeInterlace("h264", true) || state.DeInterlace("avc", true); + var doDeintHevc = state.DeInterlace("h265", true) || state.DeInterlace("hevc", true); + var doDeintH2645 = doDeintH264 || doDeintHevc; + var inW = state.VideoStream?.Width; + var inH = state.VideoStream?.Height; + var reqW = state.BaseRequest.Width; + var reqH = state.BaseRequest.Height; + var reqMaxW = state.BaseRequest.MaxWidth; + var reqMaxH = state.BaseRequest.MaxHeight; + var threeDFormat = state.MediaSource.Video3DFormat; + var newfilters = new List(); + var noOverlay = swFilterChain.OverlayFilters.Count == 0; + var supportsHwDeint = _mediaEncoder.SupportsFilter("yadif_videotoolbox"); + // fallback to software filters if we are using filters not supported by hardware yet. + var useHardwareFilters = noOverlay && (!doDeintH2645 || supportsHwDeint); + + if (!useHardwareFilters) + { + return swFilterChain; + } + + // ffmpeg cannot use videotoolbox to scale + var swScaleFilter = GetSwScaleFilter(state, options, vidEncoder, inW, inH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH); + newfilters.Add(swScaleFilter); + + // hwupload on videotoolbox encoders can automatically convert AVFrame into its CVPixelBuffer equivalent + // videotoolbox will automatically convert the CVPixelBuffer to a pixel format the encoder supports, so we don't have to set a pixel format explicitly here + // This will reduce CPU usage significantly on UHD videos with 10 bit colors because we bypassed the ffmpeg pixel format conversion + newfilters.Add("hwupload"); + + if (doDeintH2645) + { + var deintFilter = GetHwDeinterlaceFilter(state, options, "videotoolbox"); + newfilters.Add(deintFilter); + } + + return (newfilters, swFilterChain.SubFilters, swFilterChain.OverlayFilters); + } + /// /// Gets the parameter of video processing filters. /// @@ -4492,6 +4574,10 @@ namespace MediaBrowser.Controller.MediaEncoding { (mainFilters, subFilters, overlayFilters) = GetAmdVidFilterChain(state, options, outputVideoCodec); } + else if (string.Equals(options.HardwareAccelerationType, "videotoolbox", StringComparison.OrdinalIgnoreCase)) + { + (mainFilters, subFilters, overlayFilters) = GetAppleVidFilterChain(state, options, outputVideoCodec); + } else { (mainFilters, subFilters, overlayFilters) = GetSwVidFilterChain(state, options, outputVideoCodec); diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index 8479b7d50..9e6134b52 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -56,6 +56,7 @@ namespace MediaBrowser.MediaEncoding.Encoder "libvpx", "libvpx-vp9", "aac", + "aac_at", "libfdk_aac", "ac3", "libmp3lame", @@ -106,7 +107,10 @@ namespace MediaBrowser.MediaEncoding.Encoder // vulkan "libplacebo", "scale_vulkan", - "overlay_vulkan" + "overlay_vulkan", + "hwupload_vaapi", + // videotoolbox + "yadif_videotoolbox" }; private static readonly IReadOnlyDictionary _filterOptionsDict = new Dictionary -- cgit v1.2.3