From db07510017dc589da38698fd5e5f9afc55092334 Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Thu, 17 Sep 2020 19:16:23 +0800 Subject: add tonemap for AMD AMF --- .../MediaEncoding/EncodingHelper.cs | 230 ++++++++++++--------- 1 file changed, 128 insertions(+), 102 deletions(-) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 2c30ca458..790b26f69 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -452,11 +452,13 @@ namespace MediaBrowser.Controller.MediaEncoding var arg = new StringBuilder(); var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, encodingOptions) ?? string.Empty; var outputVideoCodec = GetVideoEncoder(state, encodingOptions) ?? string.Empty; + var isSwDecoder = string.IsNullOrEmpty(videoDecoder); + var isD3d11vaDecoder = videoDecoder.IndexOf("d3d11va", StringComparison.OrdinalIgnoreCase) != -1; var isVaapiDecoder = videoDecoder.IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1; var isVaapiEncoder = outputVideoCodec.IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1; var isQsvDecoder = videoDecoder.IndexOf("qsv", StringComparison.OrdinalIgnoreCase) != -1; var isQsvEncoder = outputVideoCodec.IndexOf("qsv", StringComparison.OrdinalIgnoreCase) != -1; - var isNvencHevcDecoder = videoDecoder.IndexOf("hevc_cuvid", StringComparison.OrdinalIgnoreCase) != -1; + var isNvdecHevcDecoder = videoDecoder.IndexOf("hevc_cuvid", StringComparison.OrdinalIgnoreCase) != -1; var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); var isLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux); var isMacOS = RuntimeInformation.IsOSPlatform(OSPlatform.OSX); @@ -517,11 +519,12 @@ namespace MediaBrowser.Controller.MediaEncoding } if (state.IsVideoRequest - && string.Equals(encodingOptions.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase)) + && (string.Equals(encodingOptions.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase) && isNvdecHevcDecoder || isSwDecoder) + || (string.Equals(encodingOptions.HardwareAccelerationType, "amf", StringComparison.OrdinalIgnoreCase) && isD3d11vaDecoder || isSwDecoder)) { var isColorDepth10 = IsColorDepth10(state); - if (isNvencHevcDecoder && isColorDepth10 + if (isColorDepth10 && _mediaEncoder.SupportsHwaccel("opencl") && encodingOptions.EnableTonemapping && !string.IsNullOrEmpty(state.VideoStream.VideoRange) @@ -1023,19 +1026,19 @@ namespace MediaBrowser.Controller.MediaEncoding && !string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) && !string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase) && !string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase) + && !string.Equals(videoEncoder, "h264_amf", StringComparison.OrdinalIgnoreCase) && !string.Equals(videoEncoder, "h264_v4l2m2m", StringComparison.OrdinalIgnoreCase)) { param = "-pix_fmt yuv420p " + param; } - if (string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase) + || string.Equals(videoEncoder, "h264_amf", StringComparison.OrdinalIgnoreCase)) { - var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, encodingOptions) ?? string.Empty; var videoStream = state.VideoStream; var isColorDepth10 = IsColorDepth10(state); - if (videoDecoder.IndexOf("hevc_cuvid", StringComparison.OrdinalIgnoreCase) != -1 - && isColorDepth10 + if (isColorDepth10 && _mediaEncoder.SupportsHwaccel("opencl") && encodingOptions.EnableTonemapping && !string.IsNullOrEmpty(videoStream.VideoRange) @@ -1652,47 +1655,7 @@ namespace MediaBrowser.Controller.MediaEncoding var outputSizeParam = ReadOnlySpan.Empty; var request = state.BaseRequest; - outputSizeParam = GetOutputSizeParam(state, options, outputVideoCodec).TrimEnd('"'); - - // All possible beginning of video filters - // Don't break the order - string[] beginOfOutputSizeParam = new[] - { - // for tonemap_opencl - "hwupload,tonemap_opencl", - - // hwupload=extra_hw_frames=64,vpp_qsv (for overlay_qsv on linux) - "hwupload=extra_hw_frames", - - // vpp_qsv - "vpp", - - // hwdownload,format=p010le (hardware decode + software encode for vaapi) - "hwdownload", - - // format=nv12|vaapi,hwupload,scale_vaapi - "format", - - // bwdif,scale=expr - "bwdif", - - // yadif,scale=expr - "yadif", - - // scale=expr - "scale" - }; - - var index = -1; - foreach (var param in beginOfOutputSizeParam) - { - index = outputSizeParam.IndexOf(param, StringComparison.OrdinalIgnoreCase); - if (index != -1) - { - outputSizeParam = outputSizeParam.Slice(index); - break; - } - } + outputSizeParam = GetOutputSizeParamInternal(state, options, outputVideoCodec); var videoSizeParam = string.Empty; var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, options) ?? string.Empty; @@ -2083,10 +2046,19 @@ namespace MediaBrowser.Controller.MediaEncoding return string.Format(CultureInfo.InvariantCulture, filter, widthParam, heightParam); } + public string GetOutputSizeParam( + EncodingJobInfo state, + EncodingOptions options, + string outputVideoCodec) + { + string filters = GetOutputSizeParamInternal(state, options, outputVideoCodec); + return string.IsNullOrEmpty(filters) ? string.Empty : " -vf \"" + filters + "\""; + } + /// /// If we're going to put a fixed size on the command line, this will calculate it. /// - public string GetOutputSizeParam( + public string GetOutputSizeParamInternal( EncodingJobInfo state, EncodingOptions options, string outputVideoCodec) @@ -2102,6 +2074,8 @@ namespace MediaBrowser.Controller.MediaEncoding var inputHeight = videoStream?.Height; var threeDFormat = state.MediaSource.Video3DFormat; + var isSwDecoder = string.IsNullOrEmpty(videoDecoder); + var isD3d11vaDecoder = videoDecoder.IndexOf("d3d11va", StringComparison.OrdinalIgnoreCase) != -1; var isVaapiDecoder = videoDecoder.IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1; var isVaapiH264Encoder = outputVideoCodec.IndexOf("h264_vaapi", StringComparison.OrdinalIgnoreCase) != -1; var isQsvH264Encoder = outputVideoCodec.IndexOf("h264_qsv", StringComparison.OrdinalIgnoreCase) != -1; @@ -2117,47 +2091,78 @@ namespace MediaBrowser.Controller.MediaEncoding // If double rate deinterlacing is enabled and the input framerate is 30fps or below, otherwise the output framerate will be too high for many devices var doubleRateDeinterlace = options.DeinterlaceDoubleRate && (videoStream?.RealFrameRate ?? 60) <= 30; - // Currently only with the use of NVENC decoder can we get a decent performance. - // Currently only the HEVC/H265 format is supported. - // NVIDIA Pascal and Turing or higher are recommended. - if (isNvdecHevcDecoder && isColorDepth10 - && _mediaEncoder.SupportsHwaccel("opencl") - && options.EnableTonemapping - && !string.IsNullOrEmpty(videoStream.VideoRange) - && videoStream.VideoRange.Contains("HDR", StringComparison.OrdinalIgnoreCase)) - { - var parameters = "tonemap_opencl=format=nv12:primaries=bt709:transfer=bt709:matrix=bt709:tonemap={0}:desat={1}:threshold={2}:peak={3}"; + var isScalingInAdvance = false; + var isDeinterlaceH264 = state.DeInterlace("h264", true) || state.DeInterlace("avc", true); + var isDeinterlaceHevc = state.DeInterlace("h265", true) || state.DeInterlace("hevc", true); - if (options.TonemappingParam != 0) + if ((string.Equals(options.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase) && isNvdecHevcDecoder || isSwDecoder) + || (string.Equals(options.HardwareAccelerationType, "amf", StringComparison.OrdinalIgnoreCase) && isD3d11vaDecoder || isSwDecoder)) + { + // Currently only with the use of NVENC decoder can we get a decent performance. + // Currently only the HEVC/H265 format is supported with NVDEC decoder. + // NVIDIA Pascal and Turing or higher are recommended. + // AMD Polaris and Vega or higher are recommended. + if (isColorDepth10 + && _mediaEncoder.SupportsHwaccel("opencl") + && options.EnableTonemapping + && !string.IsNullOrEmpty(videoStream.VideoRange) + && videoStream.VideoRange.Contains("HDR", StringComparison.OrdinalIgnoreCase)) { - parameters += ":param={4}"; - } + var parameters = "tonemap_opencl=format=nv12:primaries=bt709:transfer=bt709:matrix=bt709:tonemap={0}:desat={1}:threshold={2}:peak={3}"; - if (!string.Equals(options.TonemappingRange, "auto", StringComparison.OrdinalIgnoreCase)) - { - parameters += ":range={5}"; - } + if (options.TonemappingParam != 0) + { + parameters += ":param={4}"; + } - // Upload the HDR10 or HLG data to the OpenCL device, - // use tonemap_opencl filter for tone mapping, - // and then download the SDR data to memory. - filters.Add("hwupload"); - filters.Add( - string.Format( - CultureInfo.InvariantCulture, - parameters, - options.TonemappingAlgorithm, - options.TonemappingDesat, - options.TonemappingThreshold, - options.TonemappingPeak, - options.TonemappingParam, - options.TonemappingRange)); - filters.Add("hwdownload"); - - if (hasGraphicalSubs || state.DeInterlace("h265", true) || state.DeInterlace("hevc", true) - || string.Equals(outputVideoCodec, "libx264", StringComparison.OrdinalIgnoreCase)) - { - filters.Add("format=nv12"); + if (!string.Equals(options.TonemappingRange, "auto", StringComparison.OrdinalIgnoreCase)) + { + parameters += ":range={5}"; + } + + if (isSwDecoder || isD3d11vaDecoder) + { + isScalingInAdvance = true; + // Add scaling filter before tonemapping filter for performance. + filters.AddRange( + GetScalingFilters( + state, + inputWidth, + inputHeight, + threeDFormat, + videoDecoder, + outputVideoCodec, + request.Width, + request.Height, + request.MaxWidth, + request.MaxHeight)); + // Convert to hardware pixel format p010 when using SW decoder. + filters.Add("format=p010"); + } + + // Upload the HDR10 or HLG data to the OpenCL device, + // use tonemap_opencl filter for tone mapping, + // and then download the SDR data to memory. + filters.Add("hwupload"); + filters.Add( + string.Format( + CultureInfo.InvariantCulture, + parameters, + options.TonemappingAlgorithm, + options.TonemappingDesat, + options.TonemappingThreshold, + options.TonemappingPeak, + options.TonemappingParam, + options.TonemappingRange)); + filters.Add("hwdownload"); + + if (isLibX264Encoder + || hasGraphicalSubs + || (isNvdecHevcDecoder && isDeinterlaceHevc) + || (!isNvdecHevcDecoder && isDeinterlaceH264 || isDeinterlaceHevc)) + { + filters.Add("format=nv12"); + } } } @@ -2202,7 +2207,7 @@ namespace MediaBrowser.Controller.MediaEncoding } // Add hardware deinterlace filter before scaling filter - if (state.DeInterlace("h264", true) || state.DeInterlace("avc", true)) + if (isDeinterlaceH264) { if (isVaapiH264Encoder) { @@ -2215,10 +2220,7 @@ namespace MediaBrowser.Controller.MediaEncoding } // Add software deinterlace filter before scaling filter - if ((state.DeInterlace("h264", true) - || state.DeInterlace("avc", true) - || state.DeInterlace("h265", true) - || state.DeInterlace("hevc", true)) + if ((isDeinterlaceH264 || isDeinterlaceHevc) && !isVaapiH264Encoder && !isQsvH264Encoder && !isNvdecH264Decoder) @@ -2242,7 +2244,21 @@ namespace MediaBrowser.Controller.MediaEncoding } // Add scaling filter: scale_*=format=nv12 or scale_*=w=*:h=*:format=nv12 or scale=expr - filters.AddRange(GetScalingFilters(state, inputWidth, inputHeight, threeDFormat, videoDecoder, outputVideoCodec, request.Width, request.Height, request.MaxWidth, request.MaxHeight)); + if (!isScalingInAdvance) + { + filters.AddRange( + GetScalingFilters( + state, + inputWidth, + inputHeight, + threeDFormat, + videoDecoder, + outputVideoCodec, + request.Width, + request.Height, + request.MaxWidth, + request.MaxHeight)); + } // Add parameters to use VAAPI with burn-in text subtitles (GH issue #642) if (isVaapiH264Encoder) @@ -2275,7 +2291,7 @@ namespace MediaBrowser.Controller.MediaEncoding { output += string.Format( CultureInfo.InvariantCulture, - " -vf \"{0}\"", + "{0}", string.Join(",", filters)); } @@ -3069,21 +3085,31 @@ namespace MediaBrowser.Controller.MediaEncoding var isWindows8orLater = Environment.OSVersion.Version.Major > 6 || (Environment.OSVersion.Version.Major == 6 && Environment.OSVersion.Version.Minor > 1); var isDxvaSupported = _mediaEncoder.SupportsHwaccel("dxva2") || _mediaEncoder.SupportsHwaccel("d3d11va"); - if ((isDxvaSupported || IsVaapiSupported(state)) && options.HardwareDecodingCodecs.Contains(videoCodec, StringComparer.OrdinalIgnoreCase)) + if (string.Equals(options.HardwareAccelerationType, "amf", StringComparison.OrdinalIgnoreCase)) { - if (isLinux) + // Currently there is no AMF decoder on Linux, only have h264 encoder. + if (isDxvaSupported && options.HardwareDecodingCodecs.Contains(videoCodec, StringComparer.OrdinalIgnoreCase)) { - return "-hwaccel vaapi"; - } + if (isWindows && isWindows8orLater) + { + return "-hwaccel d3d11va"; + } - if (isWindows && isWindows8orLater) - { - return "-hwaccel d3d11va"; + if (isWindows && !isWindows8orLater) + { + return "-hwaccel dxva2"; + } } + } - if (isWindows && !isWindows8orLater) + if (string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase)) + { + if (IsVaapiSupported(state) && options.HardwareDecodingCodecs.Contains(videoCodec, StringComparer.OrdinalIgnoreCase)) { - return "-hwaccel dxva2"; + if (isLinux) + { + return "-hwaccel vaapi"; + } } } -- cgit v1.2.3 From 53d5f64e037c13c295d82fcb98b91ef2de8fc842 Mon Sep 17 00:00:00 2001 From: Matt Montgomery <33811686+ConfusedPolarBear@users.noreply.github.com> Date: Mon, 28 Sep 2020 15:04:31 -0500 Subject: Fix SA1513, SA1514, SA1507, and SA1508 --- Emby.Server.Implementations/Data/SqliteItemRepository.cs | 3 --- Emby.Server.Implementations/Dto/DtoService.cs | 1 - .../EntryPoints/UserDataChangeNotifier.cs | 1 - .../LiveTv/Listings/SchedulesDirect.cs | 8 -------- Emby.Server.Implementations/LiveTv/LiveTvManager.cs | 1 + Emby.Server.Implementations/Updates/InstallationManager.cs | 1 + .../Channels/InternalChannelFeatures.cs | 2 ++ MediaBrowser.Controller/Entities/Audio/Audio.cs | 1 - MediaBrowser.Controller/Entities/BaseItem.cs | 2 ++ MediaBrowser.Controller/Entities/Folder.cs | 1 - MediaBrowser.Controller/Entities/IHasMediaSources.cs | 2 -- MediaBrowser.Controller/Entities/Photo.cs | 1 - MediaBrowser.Controller/Entities/TV/Series.cs | 1 - MediaBrowser.Controller/Entities/UserViewBuilder.cs | 1 - MediaBrowser.Controller/IO/FileData.cs | 1 - MediaBrowser.Controller/Library/ILibraryManager.cs | 2 ++ MediaBrowser.Controller/Library/IMediaSourceManager.cs | 2 ++ MediaBrowser.Controller/Library/ItemResolveArgs.cs | 1 + MediaBrowser.Controller/LiveTv/ILiveTvManager.cs | 1 + MediaBrowser.Controller/LiveTv/ITunerHost.cs | 1 - MediaBrowser.Controller/LiveTv/LiveTvServiceStatusInfo.cs | 1 + MediaBrowser.Controller/LiveTv/ProgramInfo.cs | 7 +++++++ MediaBrowser.Controller/LiveTv/RecordingInfo.cs | 1 + MediaBrowser.Controller/LiveTv/TimerInfo.cs | 1 + MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 4 +--- MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs | 2 ++ MediaBrowser.Controller/Persistence/IItemRepository.cs | 1 + MediaBrowser.Controller/Resolvers/IItemResolver.cs | 1 + MediaBrowser.Controller/Session/SessionInfo.cs | 1 - MediaBrowser.Model/Dlna/StreamBuilder.cs | 14 +++++++++++++- MediaBrowser.Model/Dlna/StreamInfo.cs | 1 - MediaBrowser.Model/Dto/BaseItemDto.cs | 8 ++++++++ MediaBrowser.Model/Entities/MediaStream.cs | 2 ++ MediaBrowser.Model/Entities/MetadataProvider.cs | 4 ++++ MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs | 1 + MediaBrowser.Model/Providers/RemoteImageInfo.cs | 1 - MediaBrowser.Model/Providers/RemoteSearchResult.cs | 1 - MediaBrowser.Model/Querying/ItemFields.cs | 1 + MediaBrowser.Model/Querying/UpcomingEpisodesQuery.cs | 3 +++ MediaBrowser.Model/Session/PlaybackProgressInfo.cs | 3 +++ MediaBrowser.Model/Sync/SyncCategory.cs | 2 ++ MediaBrowser.Model/System/SystemInfo.cs | 5 ++++- MediaBrowser.Model/Tasks/IConfigurableScheduledTask.cs | 1 + .../Plugins/MusicBrainz/ArtistProvider.cs | 1 + .../Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs | 4 ++++ .../Plugins/Tmdb/Movies/GenericTmdbMovieInfo.cs | 1 - MediaBrowser.Providers/TV/DummySeasonProvider.cs | 1 - 47 files changed, 74 insertions(+), 33 deletions(-) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index ab60cee61..d09f84e17 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -2263,7 +2263,6 @@ namespace Emby.Server.Implementations.Data return query.IncludeItemTypes.Contains("Trailer", StringComparer.OrdinalIgnoreCase); } - private static readonly HashSet _artistExcludeParentTypes = new HashSet(StringComparer.OrdinalIgnoreCase) { "Series", @@ -3291,7 +3290,6 @@ namespace Emby.Server.Implementations.Data } } - var isReturningZeroItems = query.Limit.HasValue && query.Limit <= 0; var statementTexts = new List(); @@ -6006,7 +6004,6 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type } } - /// /// Gets the chapter. /// diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs index 94cfed767..677ad522a 100644 --- a/Emby.Server.Implementations/Dto/DtoService.cs +++ b/Emby.Server.Implementations/Dto/DtoService.cs @@ -468,7 +468,6 @@ namespace Emby.Server.Implementations.Dto IncludeItemTypes = new[] { typeof(MusicAlbum).Name }, Name = item.Album, Limit = 1 - }); if (parentAlbumIds.Count > 0) diff --git a/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs b/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs index 3618b88c5..1da717e75 100644 --- a/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs +++ b/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs @@ -28,7 +28,6 @@ namespace Emby.Server.Implementations.EntryPoints private readonly object _syncLock = new object(); private Timer _updateTimer; - public UserDataChangeNotifier(IUserDataManager userDataManager, ISessionManager sessionManager, IUserManager userManager) { _userDataManager = userDataManager; diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs index 655ff5853..28aabc159 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs @@ -874,7 +874,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings public List lineups { get; set; } } - public class Headends { public string headend { get; set; } @@ -886,8 +885,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings public List lineups { get; set; } } - - public class Map { public string stationID { get; set; } @@ -971,9 +968,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings public List date { get; set; } } - - - public class Rating { public string body { get; set; } @@ -1017,8 +1011,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings public string isPremiereOrFinale { get; set; } } - - public class MetadataSchedule { public string modified { get; set; } diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs index a898a564f..5bdd1c23c 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs @@ -2135,6 +2135,7 @@ namespace Emby.Server.Implementations.LiveTv } private bool _disposed = false; + /// /// Releases unmanaged and - optionally - managed resources. /// diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs index 8e24bf55c..8a6181be6 100644 --- a/Emby.Server.Implementations/Updates/InstallationManager.cs +++ b/Emby.Server.Implementations/Updates/InstallationManager.cs @@ -393,6 +393,7 @@ namespace Emby.Server.Implementations.Updates // Ignore any exceptions. } } + stream.Position = 0; _zipClient.ExtractAllFromZip(stream, targetDir, true); diff --git a/MediaBrowser.Controller/Channels/InternalChannelFeatures.cs b/MediaBrowser.Controller/Channels/InternalChannelFeatures.cs index 1074ce435..137f5d095 100644 --- a/MediaBrowser.Controller/Channels/InternalChannelFeatures.cs +++ b/MediaBrowser.Controller/Channels/InternalChannelFeatures.cs @@ -42,6 +42,7 @@ namespace MediaBrowser.Controller.Channels /// Indicates if a sort ascending/descending toggle is supported or not. /// public bool SupportsSortOrderToggle { get; set; } + /// /// Gets or sets the automatic refresh levels. /// @@ -53,6 +54,7 @@ namespace MediaBrowser.Controller.Channels /// /// The daily download limit. public int? DailyDownloadLimit { get; set; } + /// /// Gets or sets a value indicating whether [supports downloading]. /// diff --git a/MediaBrowser.Controller/Entities/Audio/Audio.cs b/MediaBrowser.Controller/Entities/Audio/Audio.cs index 2c6dea02c..8220464b3 100644 --- a/MediaBrowser.Controller/Entities/Audio/Audio.cs +++ b/MediaBrowser.Controller/Entities/Audio/Audio.cs @@ -90,7 +90,6 @@ namespace MediaBrowser.Controller.Entities.Audio var songKey = IndexNumber.HasValue ? IndexNumber.Value.ToString("0000") : string.Empty; - if (ParentIndexNumber.HasValue) { songKey = ParentIndexNumber.Value.ToString("0000") + "-" + songKey; diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 68126bd8a..2fc7d45c9 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -197,6 +197,7 @@ namespace MediaBrowser.Controller.Entities public virtual bool SupportsRemoteImageDownloading => true; private string _name; + /// /// Gets or sets the name. /// @@ -661,6 +662,7 @@ namespace MediaBrowser.Controller.Entities } private string _forcedSortName; + /// /// Gets or sets the name of the forced sort. /// diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index 11542c1ca..901ea875b 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -1386,7 +1386,6 @@ namespace MediaBrowser.Controller.Entities } } - /// /// Gets the linked children. /// diff --git a/MediaBrowser.Controller/Entities/IHasMediaSources.cs b/MediaBrowser.Controller/Entities/IHasMediaSources.cs index a7b60d168..0f612262a 100644 --- a/MediaBrowser.Controller/Entities/IHasMediaSources.cs +++ b/MediaBrowser.Controller/Entities/IHasMediaSources.cs @@ -21,7 +21,5 @@ namespace MediaBrowser.Controller.Entities List GetMediaSources(bool enablePathSubstitution); List GetMediaStreams(); - - } } diff --git a/MediaBrowser.Controller/Entities/Photo.cs b/MediaBrowser.Controller/Entities/Photo.cs index 1485d4c79..2fc66176f 100644 --- a/MediaBrowser.Controller/Entities/Photo.cs +++ b/MediaBrowser.Controller/Entities/Photo.cs @@ -16,7 +16,6 @@ namespace MediaBrowser.Controller.Entities [JsonIgnore] public override Folder LatestItemsIndexContainer => AlbumEntity; - [JsonIgnore] public PhotoAlbum AlbumEntity { diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs index 72c696c1a..75a746bfb 100644 --- a/MediaBrowser.Controller/Entities/TV/Series.cs +++ b/MediaBrowser.Controller/Entities/TV/Series.cs @@ -450,7 +450,6 @@ namespace MediaBrowser.Controller.Entities.TV }); } - protected override bool GetBlockUnratedValue(User user) { return user.GetPreference(PreferenceKind.BlockUnratedItems).Contains(UnratedItem.Series.ToString()); diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs index b384b27d1..068a76769 100644 --- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs +++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs @@ -258,7 +258,6 @@ namespace MediaBrowser.Controller.Entities IncludeItemTypes = new[] { typeof(Movie).Name }, Recursive = true, EnableTotalRecordCount = false - }).Items .SelectMany(i => i.Genres) .DistinctNames() diff --git a/MediaBrowser.Controller/IO/FileData.cs b/MediaBrowser.Controller/IO/FileData.cs index 9bc4cac39..3db60ae0b 100644 --- a/MediaBrowser.Controller/IO/FileData.cs +++ b/MediaBrowser.Controller/IO/FileData.cs @@ -111,5 +111,4 @@ namespace MediaBrowser.Controller.IO return returnResult; } } - } diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs index 804170d5c..332730bcc 100644 --- a/MediaBrowser.Controller/Library/ILibraryManager.cs +++ b/MediaBrowser.Controller/Library/ILibraryManager.cs @@ -77,6 +77,7 @@ namespace MediaBrowser.Controller.Library MusicArtist GetArtist(string name); MusicArtist GetArtist(string name, DtoOptions options); + /// /// Gets a Studio. /// @@ -234,6 +235,7 @@ namespace MediaBrowser.Controller.Library /// Occurs when [item updated]. /// event EventHandler ItemUpdated; + /// /// Occurs when [item removed]. /// diff --git a/MediaBrowser.Controller/Library/IMediaSourceManager.cs b/MediaBrowser.Controller/Library/IMediaSourceManager.cs index 9e7b1e608..22bf9488f 100644 --- a/MediaBrowser.Controller/Library/IMediaSourceManager.cs +++ b/MediaBrowser.Controller/Library/IMediaSourceManager.cs @@ -28,12 +28,14 @@ namespace MediaBrowser.Controller.Library /// The item identifier. /// IEnumerable<MediaStream>. List GetMediaStreams(Guid itemId); + /// /// Gets the media streams. /// /// The media source identifier. /// IEnumerable<MediaStream>. List GetMediaStreams(string mediaSourceId); + /// /// Gets the media streams. /// diff --git a/MediaBrowser.Controller/Library/ItemResolveArgs.cs b/MediaBrowser.Controller/Library/ItemResolveArgs.cs index 6a0dbeba2..12a311dc3 100644 --- a/MediaBrowser.Controller/Library/ItemResolveArgs.cs +++ b/MediaBrowser.Controller/Library/ItemResolveArgs.cs @@ -156,6 +156,7 @@ namespace MediaBrowser.Controller.Library } // REVIEW: @bond + /// /// Gets the physical locations. /// diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs index 55c330931..6c365caa4 100644 --- a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs +++ b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs @@ -231,6 +231,7 @@ namespace MediaBrowser.Controller.LiveTv /// Saves the tuner host. /// Task SaveTunerHost(TunerHostInfo info, bool dataSourceChanged = true); + /// /// Saves the listing provider. /// diff --git a/MediaBrowser.Controller/LiveTv/ITunerHost.cs b/MediaBrowser.Controller/LiveTv/ITunerHost.cs index ff92bf856..abca8f239 100644 --- a/MediaBrowser.Controller/LiveTv/ITunerHost.cs +++ b/MediaBrowser.Controller/LiveTv/ITunerHost.cs @@ -56,7 +56,6 @@ namespace MediaBrowser.Controller.LiveTv Task> GetChannelStreamMediaSources(string channelId, CancellationToken cancellationToken); Task> DiscoverDevices(int discoveryDurationMs, CancellationToken cancellationToken); - } public interface IConfigurableTunerHost diff --git a/MediaBrowser.Controller/LiveTv/LiveTvServiceStatusInfo.cs b/MediaBrowser.Controller/LiveTv/LiveTvServiceStatusInfo.cs index 02178297b..b62974904 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvServiceStatusInfo.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvServiceStatusInfo.cs @@ -42,6 +42,7 @@ namespace MediaBrowser.Controller.LiveTv /// /// The tuners. public List Tuners { get; set; } + /// /// Gets or sets a value indicating whether this instance is visible. /// diff --git a/MediaBrowser.Controller/LiveTv/ProgramInfo.cs b/MediaBrowser.Controller/LiveTv/ProgramInfo.cs index bdcffd5ca..f9f559ee9 100644 --- a/MediaBrowser.Controller/LiveTv/ProgramInfo.cs +++ b/MediaBrowser.Controller/LiveTv/ProgramInfo.cs @@ -35,6 +35,7 @@ namespace MediaBrowser.Controller.LiveTv /// /// The overview. public string Overview { get; set; } + /// /// Gets or sets the short overview. /// @@ -169,31 +170,37 @@ namespace MediaBrowser.Controller.LiveTv /// /// The production year. public int? ProductionYear { get; set; } + /// /// Gets or sets the home page URL. /// /// The home page URL. public string HomePageUrl { get; set; } + /// /// Gets or sets the series identifier. /// /// The series identifier. public string SeriesId { get; set; } + /// /// Gets or sets the show identifier. /// /// The show identifier. public string ShowId { get; set; } + /// /// Gets or sets the season number. /// /// The season number. public int? SeasonNumber { get; set; } + /// /// Gets or sets the episode number. /// /// The episode number. public int? EpisodeNumber { get; set; } + /// /// Gets or sets the etag. /// diff --git a/MediaBrowser.Controller/LiveTv/RecordingInfo.cs b/MediaBrowser.Controller/LiveTv/RecordingInfo.cs index 303882b7e..69190694f 100644 --- a/MediaBrowser.Controller/LiveTv/RecordingInfo.cs +++ b/MediaBrowser.Controller/LiveTv/RecordingInfo.cs @@ -187,6 +187,7 @@ namespace MediaBrowser.Controller.LiveTv /// /// null if [has image] contains no value, true if [has image]; otherwise, false. public bool? HasImage { get; set; } + /// /// Gets or sets the show identifier. /// diff --git a/MediaBrowser.Controller/LiveTv/TimerInfo.cs b/MediaBrowser.Controller/LiveTv/TimerInfo.cs index bcef4666d..aa5170617 100644 --- a/MediaBrowser.Controller/LiveTv/TimerInfo.cs +++ b/MediaBrowser.Controller/LiveTv/TimerInfo.cs @@ -113,6 +113,7 @@ namespace MediaBrowser.Controller.LiveTv // Program properties public int? SeasonNumber { get; set; } + /// /// Gets or sets the episode number. /// diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 2c30ca458..c5529ad5b 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -109,7 +109,6 @@ namespace MediaBrowser.Controller.MediaEncoding } return _mediaEncoder.SupportsHwaccel("vaapi"); - } /// @@ -508,6 +507,7 @@ namespace MediaBrowser.Controller.MediaEncoding arg.Append("-hwaccel qsv "); } } + // While using SW decoder else { @@ -1441,7 +1441,6 @@ namespace MediaBrowser.Controller.MediaEncoding var codec = outputAudioCodec ?? string.Empty; - int? transcoderChannelLimit; if (codec.IndexOf("wma", StringComparison.OrdinalIgnoreCase) != -1) { @@ -2511,7 +2510,6 @@ namespace MediaBrowser.Controller.MediaEncoding return inputModifier; } - public void AttachMediaSourceInfo( EncodingJobInfo state, MediaSourceInfo mediaSource, diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs index 68bc502a0..c7ec878d2 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs @@ -697,10 +697,12 @@ namespace MediaBrowser.Controller.MediaEncoding /// The progressive. /// Progressive, + /// /// The HLS. /// Hls, + /// /// The dash. /// diff --git a/MediaBrowser.Controller/Persistence/IItemRepository.cs b/MediaBrowser.Controller/Persistence/IItemRepository.cs index ebc37bd1f..45c6805f0 100644 --- a/MediaBrowser.Controller/Persistence/IItemRepository.cs +++ b/MediaBrowser.Controller/Persistence/IItemRepository.cs @@ -100,6 +100,7 @@ namespace MediaBrowser.Controller.Persistence /// The query. /// IEnumerable<Guid>. QueryResult GetItemIds(InternalItemsQuery query); + /// /// Gets the items. /// diff --git a/MediaBrowser.Controller/Resolvers/IItemResolver.cs b/MediaBrowser.Controller/Resolvers/IItemResolver.cs index b99c46843..eb7fb793a 100644 --- a/MediaBrowser.Controller/Resolvers/IItemResolver.cs +++ b/MediaBrowser.Controller/Resolvers/IItemResolver.cs @@ -19,6 +19,7 @@ namespace MediaBrowser.Controller.Resolvers /// The args. /// BaseItem. BaseItem ResolvePath(ItemResolveArgs args); + /// /// Gets the priority. /// diff --git a/MediaBrowser.Controller/Session/SessionInfo.cs b/MediaBrowser.Controller/Session/SessionInfo.cs index 054fd33d9..55e44c19d 100644 --- a/MediaBrowser.Controller/Session/SessionInfo.cs +++ b/MediaBrowser.Controller/Session/SessionInfo.cs @@ -22,7 +22,6 @@ namespace MediaBrowser.Controller.Session private readonly ISessionManager _sessionManager; private readonly ILogger _logger; - private readonly object _progressLock = new object(); private Timer _progressTimer; private PlaybackProgressInfo _lastProgressInfo; diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index cfe862f5a..d9e7e4fbb 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -498,7 +498,6 @@ namespace MediaBrowser.Model.Dlna } } - if (playMethods.Count > 0) { transcodeReasons.Clear(); @@ -1431,6 +1430,7 @@ namespace MediaBrowser.Model.Dlna break; } + case ProfileConditionValue.AudioChannels: { if (string.IsNullOrEmpty(qualifier)) @@ -1466,6 +1466,7 @@ namespace MediaBrowser.Model.Dlna break; } + case ProfileConditionValue.IsAvc: { if (!enableNonQualifiedConditions) @@ -1487,6 +1488,7 @@ namespace MediaBrowser.Model.Dlna break; } + case ProfileConditionValue.IsAnamorphic: { if (!enableNonQualifiedConditions) @@ -1508,6 +1510,7 @@ namespace MediaBrowser.Model.Dlna break; } + case ProfileConditionValue.IsInterlaced: { if (string.IsNullOrEmpty(qualifier)) @@ -1539,6 +1542,7 @@ namespace MediaBrowser.Model.Dlna break; } + case ProfileConditionValue.AudioProfile: case ProfileConditionValue.Has64BitOffsets: case ProfileConditionValue.PacketLength: @@ -1550,6 +1554,7 @@ namespace MediaBrowser.Model.Dlna // Not supported yet break; } + case ProfileConditionValue.RefFrames: { if (string.IsNullOrEmpty(qualifier)) @@ -1585,6 +1590,7 @@ namespace MediaBrowser.Model.Dlna break; } + case ProfileConditionValue.VideoBitDepth: { if (string.IsNullOrEmpty(qualifier)) @@ -1620,6 +1626,7 @@ namespace MediaBrowser.Model.Dlna break; } + case ProfileConditionValue.VideoProfile: { if (string.IsNullOrEmpty(qualifier)) @@ -1643,6 +1650,7 @@ namespace MediaBrowser.Model.Dlna break; } + case ProfileConditionValue.Height: { if (!enableNonQualifiedConditions) @@ -1668,6 +1676,7 @@ namespace MediaBrowser.Model.Dlna break; } + case ProfileConditionValue.VideoBitrate: { if (!enableNonQualifiedConditions) @@ -1693,6 +1702,7 @@ namespace MediaBrowser.Model.Dlna break; } + case ProfileConditionValue.VideoFramerate: { if (!enableNonQualifiedConditions) @@ -1718,6 +1728,7 @@ namespace MediaBrowser.Model.Dlna break; } + case ProfileConditionValue.VideoLevel: { if (string.IsNullOrEmpty(qualifier)) @@ -1743,6 +1754,7 @@ namespace MediaBrowser.Model.Dlna break; } + case ProfileConditionValue.Width: { if (!enableNonQualifiedConditions) diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs index 94d53ab70..9399d21f1 100644 --- a/MediaBrowser.Model/Dlna/StreamInfo.cs +++ b/MediaBrowser.Model/Dlna/StreamInfo.cs @@ -276,7 +276,6 @@ namespace MediaBrowser.Model.Dlna list.Add(new NameValuePair("SubtitleMethod", item.SubtitleStreamIndex.HasValue && item.SubtitleDeliveryMethod != SubtitleDeliveryMethod.External ? item.SubtitleDeliveryMethod.ToString() : string.Empty)); - if (!item.IsDirectStream) { if (item.RequireNonAnamorphic) diff --git a/MediaBrowser.Model/Dto/BaseItemDto.cs b/MediaBrowser.Model/Dto/BaseItemDto.cs index af3d83ade..fac754177 100644 --- a/MediaBrowser.Model/Dto/BaseItemDto.cs +++ b/MediaBrowser.Model/Dto/BaseItemDto.cs @@ -429,6 +429,7 @@ namespace MediaBrowser.Model.Dto /// /// The album id. public Guid AlbumId { get; set; } + /// /// Gets or sets the album image tag. /// @@ -599,11 +600,13 @@ namespace MediaBrowser.Model.Dto /// /// The trailer count. public int? TrailerCount { get; set; } + /// /// Gets or sets the movie count. /// /// The movie count. public int? MovieCount { get; set; } + /// /// Gets or sets the series count. /// @@ -611,16 +614,19 @@ namespace MediaBrowser.Model.Dto public int? SeriesCount { get; set; } public int? ProgramCount { get; set; } + /// /// Gets or sets the episode count. /// /// The episode count. public int? EpisodeCount { get; set; } + /// /// Gets or sets the song count. /// /// The song count. public int? SongCount { get; set; } + /// /// Gets or sets the album count. /// @@ -628,6 +634,7 @@ namespace MediaBrowser.Model.Dto public int? AlbumCount { get; set; } public int? ArtistCount { get; set; } + /// /// Gets or sets the music video count. /// @@ -768,6 +775,7 @@ namespace MediaBrowser.Model.Dto /// /// The timer identifier. public string TimerId { get; set; } + /// /// Gets or sets the current program. /// diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs index 2d37618c2..fa3c9aaa2 100644 --- a/MediaBrowser.Model/Entities/MediaStream.cs +++ b/MediaBrowser.Model/Entities/MediaStream.cs @@ -451,11 +451,13 @@ namespace MediaBrowser.Model.Entities /// /// The method. public SubtitleDeliveryMethod? DeliveryMethod { get; set; } + /// /// Gets or sets the delivery URL. /// /// The delivery URL. public string DeliveryUrl { get; set; } + /// /// Gets or sets a value indicating whether this instance is external URL. /// diff --git a/MediaBrowser.Model/Entities/MetadataProvider.cs b/MediaBrowser.Model/Entities/MetadataProvider.cs index 7fecf67b8..e9c098021 100644 --- a/MediaBrowser.Model/Entities/MetadataProvider.cs +++ b/MediaBrowser.Model/Entities/MetadataProvider.cs @@ -11,18 +11,22 @@ namespace MediaBrowser.Model.Entities /// The imdb. /// Imdb = 2, + /// /// The TMDB. /// Tmdb = 3, + /// /// The TVDB. /// Tvdb = 4, + /// /// The tvcom. /// Tvcom = 5, + /// /// Tmdb Collection Id. /// diff --git a/MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs b/MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs index ab74aff28..bcba344cc 100644 --- a/MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs +++ b/MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs @@ -84,6 +84,7 @@ namespace MediaBrowser.Model.LiveTv /// /// null if [is kids] contains no value, true if [is kids]; otherwise, false. public bool? IsKids { get; set; } + /// /// Gets or sets a value indicating whether this instance is sports. /// diff --git a/MediaBrowser.Model/Providers/RemoteImageInfo.cs b/MediaBrowser.Model/Providers/RemoteImageInfo.cs index 78ab6c706..fb25999e0 100644 --- a/MediaBrowser.Model/Providers/RemoteImageInfo.cs +++ b/MediaBrowser.Model/Providers/RemoteImageInfo.cs @@ -68,5 +68,4 @@ namespace MediaBrowser.Model.Providers /// The type of the rating. public RatingType RatingType { get; set; } } - } diff --git a/MediaBrowser.Model/Providers/RemoteSearchResult.cs b/MediaBrowser.Model/Providers/RemoteSearchResult.cs index 989741c01..a29e7ad1c 100644 --- a/MediaBrowser.Model/Providers/RemoteSearchResult.cs +++ b/MediaBrowser.Model/Providers/RemoteSearchResult.cs @@ -50,6 +50,5 @@ namespace MediaBrowser.Model.Providers public RemoteSearchResult AlbumArtist { get; set; } public RemoteSearchResult[] Artists { get; set; } - } } diff --git a/MediaBrowser.Model/Querying/ItemFields.cs b/MediaBrowser.Model/Querying/ItemFields.cs index 731d22aaf..ef4698f3f 100644 --- a/MediaBrowser.Model/Querying/ItemFields.cs +++ b/MediaBrowser.Model/Querying/ItemFields.cs @@ -168,6 +168,7 @@ namespace MediaBrowser.Model.Querying Studios, BasicSyncInfo, + /// /// The synchronize information. /// diff --git a/MediaBrowser.Model/Querying/UpcomingEpisodesQuery.cs b/MediaBrowser.Model/Querying/UpcomingEpisodesQuery.cs index 2ef6f7c60..eb6239460 100644 --- a/MediaBrowser.Model/Querying/UpcomingEpisodesQuery.cs +++ b/MediaBrowser.Model/Querying/UpcomingEpisodesQuery.cs @@ -37,16 +37,19 @@ namespace MediaBrowser.Model.Querying /// /// The fields. public ItemFields[] Fields { get; set; } + /// /// Gets or sets a value indicating whether [enable images]. /// /// null if [enable images] contains no value, true if [enable images]; otherwise, false. public bool? EnableImages { get; set; } + /// /// Gets or sets the image type limit. /// /// The image type limit. public int? ImageTypeLimit { get; set; } + /// /// Gets or sets the enable image types. /// diff --git a/MediaBrowser.Model/Session/PlaybackProgressInfo.cs b/MediaBrowser.Model/Session/PlaybackProgressInfo.cs index 21bcabf1d..73dbe6a2d 100644 --- a/MediaBrowser.Model/Session/PlaybackProgressInfo.cs +++ b/MediaBrowser.Model/Session/PlaybackProgressInfo.cs @@ -88,16 +88,19 @@ namespace MediaBrowser.Model.Session /// /// The play method. public PlayMethod PlayMethod { get; set; } + /// /// Gets or sets the live stream identifier. /// /// The live stream identifier. public string LiveStreamId { get; set; } + /// /// Gets or sets the play session identifier. /// /// The play session identifier. public string PlaySessionId { get; set; } + /// /// Gets or sets the repeat mode. /// diff --git a/MediaBrowser.Model/Sync/SyncCategory.cs b/MediaBrowser.Model/Sync/SyncCategory.cs index 80ad5f56e..1248c2f73 100644 --- a/MediaBrowser.Model/Sync/SyncCategory.cs +++ b/MediaBrowser.Model/Sync/SyncCategory.cs @@ -8,10 +8,12 @@ namespace MediaBrowser.Model.Sync /// The latest. /// Latest = 0, + /// /// The next up. /// NextUp = 1, + /// /// The resume. /// diff --git a/MediaBrowser.Model/System/SystemInfo.cs b/MediaBrowser.Model/System/SystemInfo.cs index 18ca74ee3..4b83fb7e6 100644 --- a/MediaBrowser.Model/System/SystemInfo.cs +++ b/MediaBrowser.Model/System/SystemInfo.cs @@ -14,13 +14,16 @@ namespace MediaBrowser.Model.System { /// No path to FFmpeg found. NotFound, + /// Path supplied via command line using switch --ffmpeg. SetByArgument, + /// User has supplied path via Transcoding UI page. Custom, + /// FFmpeg tool found on system $PATH. System - }; + } /// /// Class SystemInfo. diff --git a/MediaBrowser.Model/Tasks/IConfigurableScheduledTask.cs b/MediaBrowser.Model/Tasks/IConfigurableScheduledTask.cs index fbfaed22e..6212d76f7 100644 --- a/MediaBrowser.Model/Tasks/IConfigurableScheduledTask.cs +++ b/MediaBrowser.Model/Tasks/IConfigurableScheduledTask.cs @@ -9,6 +9,7 @@ namespace MediaBrowser.Model.Tasks /// /// true if this instance is hidden; otherwise, false. bool IsHidden { get; } + /// /// Gets a value indicating whether this instance is enabled. /// diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/ArtistProvider.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/ArtistProvider.cs index 781b71640..f27da7ce6 100644 --- a/MediaBrowser.Providers/Plugins/MusicBrainz/ArtistProvider.cs +++ b/MediaBrowser.Providers/Plugins/MusicBrainz/ArtistProvider.cs @@ -198,6 +198,7 @@ namespace MediaBrowser.Providers.Music result.Name = reader.ReadElementContentAsString(); break; } + case "annotation": { result.Overview = reader.ReadElementContentAsString(); diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs index 46f8988f2..abfa1c6e7 100644 --- a/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs +++ b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs @@ -444,6 +444,7 @@ namespace MediaBrowser.Providers.Music result.Title = reader.ReadElementContentAsString(); break; } + case "date": { var val = reader.ReadElementContentAsString(); @@ -454,17 +455,20 @@ namespace MediaBrowser.Providers.Music break; } + case "annotation": { result.Overview = reader.ReadElementContentAsString(); break; } + case "release-group": { result.ReleaseGroupId = reader.GetAttribute("id"); reader.Skip(); break; } + case "artist-credit": { using (var subReader = reader.ReadSubtree()) diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Movies/GenericTmdbMovieInfo.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/GenericTmdbMovieInfo.cs index 01a887eed..3c626f9eb 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/Movies/GenericTmdbMovieInfo.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/GenericTmdbMovieInfo.cs @@ -302,7 +302,6 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies { Url = string.Format(CultureInfo.InvariantCulture, "https://www.youtube.com/watch?v={0}", i.Source), Name = i.Name - }).ToArray(); } } diff --git a/MediaBrowser.Providers/TV/DummySeasonProvider.cs b/MediaBrowser.Providers/TV/DummySeasonProvider.cs index a0f7f6cfd..905cbefd3 100644 --- a/MediaBrowser.Providers/TV/DummySeasonProvider.cs +++ b/MediaBrowser.Providers/TV/DummySeasonProvider.cs @@ -217,7 +217,6 @@ namespace MediaBrowser.Providers.TV new DeleteOptions { DeleteFileLocation = true - }, false); -- cgit v1.2.3 From 2124bc2e1894e5a09e5843cfcf19ab189e70eac1 Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Sat, 3 Oct 2020 16:04:39 +0800 Subject: enhance workload when tone mapping with AMF zscale filter is required. --- .../MediaEncoding/EncodingHelper.cs | 78 +++++++++++++--------- 1 file changed, 46 insertions(+), 32 deletions(-) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 790b26f69..4a55840d5 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -859,29 +859,44 @@ namespace MediaBrowser.Controller.MediaEncoding else if (string.Equals(videoEncoder, "h264_amf", StringComparison.OrdinalIgnoreCase) || string.Equals(videoEncoder, "hevc_amf", StringComparison.OrdinalIgnoreCase)) { - switch (encodingOptions.EncoderPreset) + var videoStream = state.VideoStream; + var isColorDepth10 = IsColorDepth10(state); + + if (isColorDepth10 + && _mediaEncoder.SupportsHwaccel("opencl") + && encodingOptions.EnableTonemapping + && !string.IsNullOrEmpty(videoStream.VideoRange) + && videoStream.VideoRange.Contains("HDR", StringComparison.OrdinalIgnoreCase)) { - case "veryslow": - case "slow": - case "slower": - param += "-quality quality"; - break; + // Enhance quality and workload when tone mapping with AMF + param += "-quality quality -preanalysis true"; + } + else + { + switch (encodingOptions.EncoderPreset) + { + case "veryslow": + case "slow": + case "slower": + param += "-quality quality"; + break; - case "medium": - param += "-quality balanced"; - break; + case "medium": + param += "-quality balanced"; + break; - case "fast": - case "faster": - case "veryfast": - case "superfast": - case "ultrafast": - param += "-quality speed"; - break; + case "fast": + case "faster": + case "veryfast": + case "superfast": + case "ultrafast": + param += "-quality speed"; + break; - default: - param += "-quality speed"; - break; + default: + param += "-quality speed"; + break; + } } } else if (string.Equals(videoEncoder, "libvpx", StringComparison.OrdinalIgnoreCase)) // webm @@ -2123,19 +2138,18 @@ namespace MediaBrowser.Controller.MediaEncoding if (isSwDecoder || isD3d11vaDecoder) { isScalingInAdvance = true; - // Add scaling filter before tonemapping filter for performance. - filters.AddRange( - GetScalingFilters( - state, - inputWidth, - inputHeight, - threeDFormat, - videoDecoder, - outputVideoCodec, - request.Width, - request.Height, - request.MaxWidth, - request.MaxHeight)); + // Add zscale filter before tone mapping filter for performance. + var (width, height) = GetFixedOutputSize(inputWidth, inputHeight, request.Width, request.Height, request.MaxWidth, request.MaxHeight); + if (width.HasValue && height.HasValue) + { + filters.Add( + string.Format( + CultureInfo.InvariantCulture, + "zscale=s={0}x{1}", + width.Value, + height.Value)); + } + // Convert to hardware pixel format p010 when using SW decoder. filters.Add("format=p010"); } -- cgit v1.2.3 From 9fbf725a6de88b8381f3d1607a8c722a59b92fbd Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Sat, 3 Oct 2020 17:53:10 +0800 Subject: Enhance workload when tone mapping on some APUs --- .../MediaEncoding/EncodingHelper.cs | 56 +++++++++++----------- 1 file changed, 27 insertions(+), 29 deletions(-) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 4a55840d5..0125e909f 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -859,6 +859,31 @@ namespace MediaBrowser.Controller.MediaEncoding else if (string.Equals(videoEncoder, "h264_amf", StringComparison.OrdinalIgnoreCase) || string.Equals(videoEncoder, "hevc_amf", StringComparison.OrdinalIgnoreCase)) { + switch (encodingOptions.EncoderPreset) + { + case "veryslow": + case "slow": + case "slower": + param += "-quality quality"; + break; + + case "medium": + param += "-quality balanced"; + break; + + case "fast": + case "faster": + case "veryfast": + case "superfast": + case "ultrafast": + param += "-quality speed"; + break; + + default: + param += "-quality speed"; + break; + } + var videoStream = state.VideoStream; var isColorDepth10 = IsColorDepth10(state); @@ -868,35 +893,8 @@ namespace MediaBrowser.Controller.MediaEncoding && !string.IsNullOrEmpty(videoStream.VideoRange) && videoStream.VideoRange.Contains("HDR", StringComparison.OrdinalIgnoreCase)) { - // Enhance quality and workload when tone mapping with AMF - param += "-quality quality -preanalysis true"; - } - else - { - switch (encodingOptions.EncoderPreset) - { - case "veryslow": - case "slow": - case "slower": - param += "-quality quality"; - break; - - case "medium": - param += "-quality balanced"; - break; - - case "fast": - case "faster": - case "veryfast": - case "superfast": - case "ultrafast": - param += "-quality speed"; - break; - - default: - param += "-quality speed"; - break; - } + // Enhance workload when tone mapping with AMF on some APUs + param += " -preanalysis true"; } } else if (string.Equals(videoEncoder, "libvpx", StringComparison.OrdinalIgnoreCase)) // webm -- cgit v1.2.3 From 6f9beef7323cf133af481d003c7db7a7c6abc6dd Mon Sep 17 00:00:00 2001 From: Jan-Pieter Baert Date: Mon, 12 Oct 2020 19:22:33 +0200 Subject: Fix SA1012 and SA1013 warnings --- .../Data/SqliteItemRepository.cs | 2 +- .../LiveTv/RefreshChannelsScheduledTask.cs | 2 +- .../ScheduledTasks/Tasks/DeleteCacheFileTask.cs | 2 +- .../ScheduledTasks/Tasks/DeleteLogFileTask.cs | 2 +- .../MediaEncoding/EncodingHelper.cs | 18 +++++++++--------- .../MediaInfo/SubtitleScheduledTask.cs | 2 +- 6 files changed, 14 insertions(+), 14 deletions(-) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index d09f84e17..86a9908be 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -5357,7 +5357,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type itemCountColumns = new Dictionary() { - { "itemTypes", "(" + itemCountColumnQuery + ") as itemTypes"} + { "itemTypes", "(" + itemCountColumnQuery + ") as itemTypes" } }; } diff --git a/Emby.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs b/Emby.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs index f1b61f7c7..582b64923 100644 --- a/Emby.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs +++ b/Emby.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs @@ -43,7 +43,7 @@ namespace Emby.Server.Implementations.LiveTv return new[] { // Every so often - new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks} + new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks } }; } diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs index 5adcefc1f..692d1667d 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs @@ -71,7 +71,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks return new[] { // Every so often - new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks} + new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks } }; } diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs index 54e18eaea..184d155d4 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs @@ -65,7 +65,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks { return new[] { - new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks} + new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks } }; } diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index c5529ad5b..906afa7f6 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -70,15 +70,15 @@ namespace MediaBrowser.Controller.MediaEncoding var codecMap = new Dictionary(StringComparer.OrdinalIgnoreCase) { - {"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"}, - {"videotoolbox", hwEncoder + "_videotoolbox"} + { "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" }, + { "videotoolbox", hwEncoder + "_videotoolbox" } }; if (!string.IsNullOrEmpty(hwType) diff --git a/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs b/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs index d231bfa2f..9804ec3bb 100644 --- a/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs +++ b/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs @@ -214,7 +214,7 @@ namespace MediaBrowser.Providers.MediaInfo return new[] { // Every so often - new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks} + new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks } }; } } -- cgit v1.2.3 From ec91d3893d6f1aee7f75dabae3d2311f7592440b Mon Sep 17 00:00:00 2001 From: Jan-Pieter Baert Date: Mon, 12 Oct 2020 20:05:11 +0200 Subject: Fix SA 1116 warnings --- .../Data/BaseSqliteRepository.cs | 3 +- .../Data/SqliteItemRepository.cs | 33 ++++++++++++++-------- .../Data/SqliteUserDataRepository.cs | 9 ++++-- .../Library/Resolvers/Audio/AudioResolver.cs | 6 ++-- .../Library/Resolvers/Books/BookResolver.cs | 3 +- .../Security/AuthenticationRepository.cs | 18 ++++++++---- .../Entities/BaseItemExtensions.cs | 3 +- MediaBrowser.Controller/Entities/Folder.cs | 9 ++++-- .../Entities/UserViewBuilder.cs | 6 ++-- MediaBrowser.Controller/Library/ILibraryManager.cs | 3 +- .../MediaEncoding/EncodingHelper.cs | 3 +- .../MediaEncoding/EncodingJobInfo.cs | 6 ++-- .../MediaEncoding/IMediaEncoder.cs | 3 +- MediaBrowser.Controller/Resolvers/IItemResolver.cs | 3 +- MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs | 12 +++++--- MediaBrowser.Model/Dlna/DeviceProfile.cs | 3 +- MediaBrowser.Model/Dlna/StreamBuilder.cs | 15 ++++++---- .../Plugins/TheTvdb/TvdbClientManager.cs | 3 +- .../Plugins/TheTvdb/TvdbEpisodeProvider.cs | 3 +- 19 files changed, 96 insertions(+), 48 deletions(-) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs index 0fb050a7a..8c756a7f4 100644 --- a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs +++ b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs @@ -157,7 +157,8 @@ namespace Emby.Server.Implementations.Data protected bool TableExists(ManagedConnection connection, string name) { - return connection.RunInTransaction(db => + return connection.RunInTransaction( + db => { using (var statement = PrepareStatement(db, "select DISTINCT tbl_name from sqlite_master")) { diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 86a9908be..1c2aeda70 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -219,7 +219,8 @@ namespace Emby.Server.Implementations.Data { connection.RunQueries(queries); - connection.RunInTransaction(db => + connection.RunInTransaction( + db => { var existingColumnNames = GetColumnNames(db, "AncestorIds"); AddColumn(db, "AncestorIds", "AncestorIdText", "Text", existingColumnNames); @@ -495,7 +496,8 @@ namespace Emby.Server.Implementations.Data using (var connection = GetConnection()) { - connection.RunInTransaction(db => + connection.RunInTransaction( + db => { using (var saveImagesStatement = base.PrepareStatement(db, "Update TypedBaseItems set Images=@Images where guid=@Id")) { @@ -546,7 +548,8 @@ namespace Emby.Server.Implementations.Data using (var connection = GetConnection()) { - connection.RunInTransaction(db => + connection.RunInTransaction( + db => { SaveItemsInTranscation(db, tuples); }, TransactionMode); @@ -2032,7 +2035,8 @@ namespace Emby.Server.Implementations.Data using (var connection = GetConnection()) { - connection.RunInTransaction(db => + connection.RunInTransaction( + db => { // First delete chapters db.Execute("delete from " + ChaptersTableName + " where ItemId=@ItemId", idBlob); @@ -2921,7 +2925,8 @@ namespace Emby.Server.Implementations.Data var result = new QueryResult(); using (var connection = GetConnection(true)) { - connection.RunInTransaction(db => + connection.RunInTransaction( + db => { var statements = PrepareAll(db, statementTexts); @@ -3324,7 +3329,8 @@ namespace Emby.Server.Implementations.Data var result = new QueryResult(); using (var connection = GetConnection(true)) { - connection.RunInTransaction(db => + connection.RunInTransaction( + db => { var statements = PrepareAll(db, statementTexts); @@ -4899,7 +4905,8 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type using (var connection = GetConnection()) { - connection.RunInTransaction(db => + connection.RunInTransaction( + db => { connection.ExecuteAll(sql); }, TransactionMode); @@ -4950,7 +4957,8 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type using (var connection = GetConnection()) { - connection.RunInTransaction(db => + connection.RunInTransaction( + db => { var idBlob = id.ToByteArray(); @@ -5744,7 +5752,8 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type using (var connection = GetConnection()) { - connection.RunInTransaction(db => + connection.RunInTransaction( + db => { var itemIdBlob = itemId.ToByteArray(); @@ -5898,7 +5907,8 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type using (var connection = GetConnection()) { - connection.RunInTransaction(db => + connection.RunInTransaction( + db => { var itemIdBlob = id.ToByteArray(); @@ -6232,7 +6242,8 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type using (var connection = GetConnection()) { - connection.RunInTransaction(db => + connection.RunInTransaction( + db => { var itemIdBlob = id.ToByteArray(); diff --git a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs index 4a78aac8e..2c4e8e0fc 100644 --- a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs @@ -44,7 +44,8 @@ namespace Emby.Server.Implementations.Data var users = userDatasTableExists ? null : userManager.Users; - connection.RunInTransaction(db => + connection.RunInTransaction( + db => { db.ExecuteAll(string.Join(";", new[] { @@ -178,7 +179,8 @@ namespace Emby.Server.Implementations.Data using (var connection = GetConnection()) { - connection.RunInTransaction(db => + connection.RunInTransaction( + db => { SaveUserData(db, internalUserId, key, userData); }, TransactionMode); @@ -246,7 +248,8 @@ namespace Emby.Server.Implementations.Data using (var connection = GetConnection()) { - connection.RunInTransaction(db => + connection.RunInTransaction( + db => { foreach (var userItemData in userDataList) { diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs index 03059e6d3..70be52411 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs @@ -32,7 +32,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio /// The priority. public override ResolverPriority Priority => ResolverPriority.Fourth; - public MultiItemResolverResult ResolveMultiple(Folder parent, + public MultiItemResolverResult ResolveMultiple( + Folder parent, List files, string collectionType, IDirectoryService directoryService) @@ -50,7 +51,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio return result; } - private MultiItemResolverResult ResolveMultipleInternal(Folder parent, + private MultiItemResolverResult ResolveMultipleInternal( + Folder parent, List files, string collectionType, IDirectoryService directoryService) diff --git a/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs index 86a5d8b7d..59af7ce8a 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs @@ -50,7 +50,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.Books var fileExtension = Path.GetExtension(f.FullName) ?? string.Empty; - return _validExtensions.Contains(fileExtension, + return _validExtensions.Contains( + fileExtension, StringComparer .OrdinalIgnoreCase); }).ToList(); diff --git a/Emby.Server.Implementations/Security/AuthenticationRepository.cs b/Emby.Server.Implementations/Security/AuthenticationRepository.cs index 29393ae07..4bc12f44a 100644 --- a/Emby.Server.Implementations/Security/AuthenticationRepository.cs +++ b/Emby.Server.Implementations/Security/AuthenticationRepository.cs @@ -54,7 +54,8 @@ namespace Emby.Server.Implementations.Security { if (tableNewlyCreated && TableExists(connection, "AccessTokens")) { - connection.RunInTransaction(db => + connection.RunInTransaction( + db => { var existingColumnNames = GetColumnNames(db, "AccessTokens"); @@ -88,7 +89,8 @@ namespace Emby.Server.Implementations.Security using (var connection = GetConnection()) { - connection.RunInTransaction(db => + connection.RunInTransaction( + db => { using (var statement = db.PrepareStatement("insert into Tokens (AccessToken, DeviceId, AppName, AppVersion, DeviceName, UserId, UserName, IsActive, DateCreated, DateLastActivity) values (@AccessToken, @DeviceId, @AppName, @AppVersion, @DeviceName, @UserId, @UserName, @IsActive, @DateCreated, @DateLastActivity)")) { @@ -119,7 +121,8 @@ namespace Emby.Server.Implementations.Security using (var connection = GetConnection()) { - connection.RunInTransaction(db => + connection.RunInTransaction( + db => { using (var statement = db.PrepareStatement("Update Tokens set AccessToken=@AccessToken, DeviceId=@DeviceId, AppName=@AppName, AppVersion=@AppVersion, DeviceName=@DeviceName, UserId=@UserId, UserName=@UserName, DateCreated=@DateCreated, DateLastActivity=@DateLastActivity where Id=@Id")) { @@ -151,7 +154,8 @@ namespace Emby.Server.Implementations.Security using (var connection = GetConnection()) { - connection.RunInTransaction(db => + connection.RunInTransaction( + db => { using (var statement = db.PrepareStatement("Delete from Tokens where Id=@Id")) { @@ -346,7 +350,8 @@ namespace Emby.Server.Implementations.Security { using (var connection = GetConnection(true)) { - return connection.RunInTransaction(db => + return connection.RunInTransaction( + db => { using (var statement = base.PrepareStatement(db, "select CustomName from Devices where Id=@DeviceId")) { @@ -377,7 +382,8 @@ namespace Emby.Server.Implementations.Security using (var connection = GetConnection()) { - connection.RunInTransaction(db => + connection.RunInTransaction( + db => { using (var statement = db.PrepareStatement("replace into devices (Id, CustomName, Capabilities) VALUES (@Id, @CustomName, (Select Capabilities from Devices where Id=@Id))")) { diff --git a/MediaBrowser.Controller/Entities/BaseItemExtensions.cs b/MediaBrowser.Controller/Entities/BaseItemExtensions.cs index 8a69971d0..c65477d39 100644 --- a/MediaBrowser.Controller/Entities/BaseItemExtensions.cs +++ b/MediaBrowser.Controller/Entities/BaseItemExtensions.cs @@ -45,7 +45,8 @@ namespace MediaBrowser.Controller.Entities { if (file.StartsWith("http", System.StringComparison.OrdinalIgnoreCase)) { - item.SetImage(new ItemImageInfo + item.SetImage( + new ItemImageInfo { Path = file, Type = imageType diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index 901ea875b..35ddaffad 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -255,7 +255,8 @@ namespace MediaBrowser.Controller.Entities var id = child.Id; if (dictionary.ContainsKey(id)) { - Logger.LogError("Found folder containing items with duplicate id. Path: {path}, Child Name: {ChildName}", + Logger.LogError( + "Found folder containing items with duplicate id. Path: {path}, Child Name: {ChildName}", Path ?? Name, child.Path ?? child.Name); } @@ -984,7 +985,8 @@ namespace MediaBrowser.Controller.Entities return items; } - private static bool CollapseBoxSetItems(InternalItemsQuery query, + private static bool CollapseBoxSetItems( + InternalItemsQuery query, BaseItem queryParent, User user, IServerConfigurationManager configurationManager) @@ -1593,7 +1595,8 @@ namespace MediaBrowser.Controller.Entities /// The date played. /// if set to true [reset position]. /// Task. - public override void MarkPlayed(User user, + public override void MarkPlayed( + User user, DateTime? datePlayed, bool resetPosition) { diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs index 068a76769..7bb311900 100644 --- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs +++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs @@ -343,7 +343,8 @@ namespace MediaBrowser.Controller.Entities { var parentFolders = GetMediaFolders(parent, query.User, new[] { CollectionType.TvShows, string.Empty }); - var result = _tvSeriesManager.GetNextUp(new NextUpQuery + var result = _tvSeriesManager.GetNextUp( + new NextUpQuery { Limit = query.Limit, StartIndex = query.StartIndex, @@ -443,7 +444,8 @@ namespace MediaBrowser.Controller.Entities return Filter(item, query.User, query, BaseItem.UserDataManager, BaseItem.LibraryManager); } - public static QueryResult PostFilterAndSort(IEnumerable items, + public static QueryResult PostFilterAndSort( + IEnumerable items, BaseItem queryParent, int? totalRecordLimit, InternalItemsQuery query, diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs index 332730bcc..32703c2fd 100644 --- a/MediaBrowser.Controller/Library/ILibraryManager.cs +++ b/MediaBrowser.Controller/Library/ILibraryManager.cs @@ -566,7 +566,8 @@ namespace MediaBrowser.Controller.Library int GetCount(InternalItemsQuery query); - void AddExternalSubtitleStreams(List streams, + void AddExternalSubtitleStreams( + List streams, string videoPath, string[] files); } diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 906afa7f6..f913c32e4 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -1822,7 +1822,8 @@ namespace MediaBrowser.Controller.MediaEncoding return (Convert.ToInt32(outputWidth), Convert.ToInt32(outputHeight)); } - public List GetScalingFilters(EncodingJobInfo state, + public List GetScalingFilters( + EncodingJobInfo state, int? videoWidth, int? videoHeight, Video3DFormat? threedFormat, diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs index c7ec878d2..6cd0c70d2 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs @@ -342,7 +342,8 @@ namespace MediaBrowser.Controller.MediaEncoding { var size = new ImageDimensions(VideoStream.Width.Value, VideoStream.Height.Value); - var newSize = DrawingUtils.Resize(size, + var newSize = DrawingUtils.Resize( + size, BaseRequest.Width ?? 0, BaseRequest.Height ?? 0, BaseRequest.MaxWidth ?? 0, @@ -368,7 +369,8 @@ namespace MediaBrowser.Controller.MediaEncoding { var size = new ImageDimensions(VideoStream.Width.Value, VideoStream.Height.Value); - var newSize = DrawingUtils.Resize(size, + var newSize = DrawingUtils.Resize( + size, BaseRequest.Width ?? 0, BaseRequest.Height ?? 0, BaseRequest.MaxWidth ?? 0, diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs index 17d6dc5d2..f6bc1f4de 100644 --- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs +++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs @@ -68,7 +68,8 @@ namespace MediaBrowser.Controller.MediaEncoding /// /// Extracts the video images on interval. /// - Task ExtractVideoImagesOnInterval(string[] inputFiles, + Task ExtractVideoImagesOnInterval( + string[] inputFiles, string container, MediaStream videoStream, MediaProtocol protocol, diff --git a/MediaBrowser.Controller/Resolvers/IItemResolver.cs b/MediaBrowser.Controller/Resolvers/IItemResolver.cs index eb7fb793a..75286eadc 100644 --- a/MediaBrowser.Controller/Resolvers/IItemResolver.cs +++ b/MediaBrowser.Controller/Resolvers/IItemResolver.cs @@ -29,7 +29,8 @@ namespace MediaBrowser.Controller.Resolvers public interface IMultiItemResolver { - MultiItemResolverResult ResolveMultiple(Folder parent, + MultiItemResolverResult ResolveMultiple( + Folder parent, List files, string collectionType, IDirectoryService directoryService); diff --git a/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs b/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs index 93e60753a..8b73ecbd4 100644 --- a/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs +++ b/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs @@ -38,7 +38,8 @@ namespace MediaBrowser.Model.Dlna ";DLNA.ORG_FLAGS={0}", DlnaMaps.FlagsToString(flagValue)); - ResponseProfile mediaProfile = _profile.GetImageMediaProfile(container, + ResponseProfile mediaProfile = _profile.GetImageMediaProfile( + container, width, height); @@ -160,7 +161,8 @@ namespace MediaBrowser.Model.Dlna string dlnaflags = string.Format(CultureInfo.InvariantCulture, ";DLNA.ORG_FLAGS={0}", DlnaMaps.FlagsToString(flagValue)); - ResponseProfile mediaProfile = _profile.GetVideoMediaProfile(container, + ResponseProfile mediaProfile = _profile.GetVideoMediaProfile( + container, audioCodec, videoCodec, width, @@ -221,7 +223,8 @@ namespace MediaBrowser.Model.Dlna private static string GetImageOrgPnValue(string container, int? width, int? height) { MediaFormatProfile? format = new MediaFormatProfileResolver() - .ResolveImageFormat(container, + .ResolveImageFormat( + container, width, height); @@ -231,7 +234,8 @@ namespace MediaBrowser.Model.Dlna private static string GetAudioOrgPnValue(string container, int? audioBitrate, int? audioSampleRate, int? audioChannels) { MediaFormatProfile? format = new MediaFormatProfileResolver() - .ResolveAudioFormat(container, + .ResolveAudioFormat( + container, audioBitrate, audioSampleRate, audioChannels); diff --git a/MediaBrowser.Model/Dlna/DeviceProfile.cs b/MediaBrowser.Model/Dlna/DeviceProfile.cs index 7e921b1fd..44412f3e4 100644 --- a/MediaBrowser.Model/Dlna/DeviceProfile.cs +++ b/MediaBrowser.Model/Dlna/DeviceProfile.cs @@ -277,7 +277,8 @@ namespace MediaBrowser.Model.Dlna return null; } - public ResponseProfile GetVideoMediaProfile(string container, + public ResponseProfile GetVideoMediaProfile( + string container, string audioCodec, string videoCodec, int? width, diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index fc0aad072..4959a9b92 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -455,7 +455,8 @@ namespace MediaBrowser.Model.Dlna if (directPlayProfile == null) { - _logger.LogInformation("Profile: {0}, No audio direct play profiles found for {1} with codec {2}", + _logger.LogInformation( + "Profile: {0}, No audio direct play profiles found for {1} with codec {2}", options.Profile.Name ?? "Unknown Profile", item.Path ?? "Unknown path", audioStream.Codec ?? "Unknown codec"); @@ -679,7 +680,8 @@ namespace MediaBrowser.Model.Dlna bool isEligibleForDirectPlay = options.EnableDirectPlay && (options.ForceDirectPlay || directPlayEligibilityResult.Item1); bool isEligibleForDirectStream = options.EnableDirectStream && (options.ForceDirectStream || directStreamEligibilityResult.Item1); - _logger.LogInformation("Profile: {0}, Path: {1}, isEligibleForDirectPlay: {2}, isEligibleForDirectStream: {3}", + _logger.LogInformation( + "Profile: {0}, Path: {1}, isEligibleForDirectPlay: {2}, isEligibleForDirectStream: {3}", options.Profile.Name ?? "Unknown Profile", item.Path ?? "Unknown path", isEligibleForDirectPlay, @@ -973,7 +975,8 @@ namespace MediaBrowser.Model.Dlna if (directPlay == null) { - _logger.LogInformation("Profile: {0}, No video direct play profiles found for {1} with codec {2}", + _logger.LogInformation( + "Profile: {0}, No video direct play profiles found for {1} with codec {2}", profile.Name ?? "Unknown Profile", mediaSource.Path ?? "Unknown path", videoStream.Codec ?? "Unknown codec"); @@ -1137,7 +1140,8 @@ namespace MediaBrowser.Model.Dlna private void LogConditionFailure(DeviceProfile profile, string type, ProfileCondition condition, MediaSourceInfo mediaSource) { - _logger.LogInformation("Profile: {0}, DirectPlay=false. Reason={1}.{2} Condition: {3}. ConditionValue: {4}. IsRequired: {5}. Path: {6}", + _logger.LogInformation( + "Profile: {0}, DirectPlay=false. Reason={1}.{2} Condition: {3}. ConditionValue: {4}. IsRequired: {5}. Path: {6}", type, profile.Name ?? "Unknown Profile", condition.Property, @@ -1342,7 +1346,8 @@ namespace MediaBrowser.Model.Dlna if (itemBitrate > requestedMaxBitrate) { - _logger.LogInformation("Bitrate exceeds {PlayBackMethod} limit: media bitrate: {MediaBitrate}, max bitrate: {MaxBitrate}", + _logger.LogInformation( + "Bitrate exceeds {PlayBackMethod} limit: media bitrate: {MediaBitrate}, max bitrate: {MaxBitrate}", playMethod, itemBitrate, requestedMaxBitrate); return false; } diff --git a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbClientManager.cs b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbClientManager.cs index 5e9a4a225..ce0dab701 100644 --- a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbClientManager.cs +++ b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbClientManager.cs @@ -150,7 +150,8 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb string language, CancellationToken cancellationToken) { - searchInfo.SeriesProviderIds.TryGetValue(nameof(MetadataProvider.Tvdb), + searchInfo.SeriesProviderIds.TryGetValue( + nameof(MetadataProvider.Tvdb), out var seriesTvdbId); var episodeQuery = new EpisodeQuery(); diff --git a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeProvider.cs b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeProvider.cs index 5fa8a3e1c..fd72ea4a8 100644 --- a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeProvider.cs +++ b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeProvider.cs @@ -106,7 +106,8 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb .ConfigureAwait(false); if (string.IsNullOrEmpty(episodeTvdbId)) { - _logger.LogError("Episode {SeasonNumber}x{EpisodeNumber} not found for series {SeriesTvdbId}", + _logger.LogError( + "Episode {SeasonNumber}x{EpisodeNumber} not found for series {SeriesTvdbId}", searchInfo.ParentIndexNumber, searchInfo.IndexNumber, seriesTvdbId); return result; } -- cgit v1.2.3 From c91f42c7a098a572b47464f550e972dbed2c913f Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Wed, 28 Oct 2020 22:27:08 +0800 Subject: Default output audio bitrate to 128k --- Jellyfin.Api/Helpers/DynamicHlsHelper.cs | 2 +- .../MediaEncoding/EncodingHelper.cs | 20 ++++++++++++++++++-- 2 files changed, 19 insertions(+), 3 deletions(-) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/Jellyfin.Api/Helpers/DynamicHlsHelper.cs b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs index af0519ffa..ea012f837 100644 --- a/Jellyfin.Api/Helpers/DynamicHlsHelper.cs +++ b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs @@ -155,7 +155,7 @@ namespace Jellyfin.Api.Helpers return new FileContentResult(Array.Empty(), MimeTypes.GetMimeType("playlist.m3u8")); } - var totalBitrate = state.OutputAudioBitrate ?? 0 + state.OutputVideoBitrate ?? 0; + var totalBitrate = (state.OutputAudioBitrate ?? 0) + (state.OutputVideoBitrate ?? 0); var builder = new StringBuilder(); diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 61340cd24..996b1b5c1 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -1380,24 +1380,40 @@ namespace MediaBrowser.Controller.MediaEncoding public int? GetAudioBitrateParam(BaseEncodingJobOptions request, MediaStream audioStream) { + if (audioStream == null) + { + return null; + } + if (request.AudioBitRate.HasValue) { // Don't encode any higher than this return Math.Min(384000, request.AudioBitRate.Value); } - return null; + // Empty bitrate area is not allow on iOS + // Default audio bitrate to 128K if it is not being requested + // https://ffmpeg.org/ffmpeg-codecs.html#toc-Codec-Options + return 128000; } public int? GetAudioBitrateParam(int? audioBitRate, MediaStream audioStream) { + if (audioStream == null) + { + return null; + } + if (audioBitRate.HasValue) { // Don't encode any higher than this return Math.Min(384000, audioBitRate.Value); } - return null; + // Empty bitrate area is not allow on iOS + // Default audio bitrate to 128K if it is not being requested + // https://ffmpeg.org/ffmpeg-codecs.html#toc-Codec-Options + return 128000; } public string GetAudioFilterParam(EncodingJobInfo state, EncodingOptions encodingOptions, bool isHls) -- cgit v1.2.3 From 429e59fb818e605339eab2b368ceaaf2a1bd5c2b Mon Sep 17 00:00:00 2001 From: crobibero Date: Thu, 29 Oct 2020 13:55:57 -0600 Subject: Fix null reference --- 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 996b1b5c1..1d733479c 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -3084,7 +3084,7 @@ namespace MediaBrowser.Controller.MediaEncoding } } - var whichCodec = videoStream.Codec.ToLowerInvariant(); + var whichCodec = videoStream.Codec?.ToLowerInvariant(); switch (whichCodec) { case "avc": -- cgit v1.2.3 From d34be6faf4cf2d8d916593e28cc7d0da5ec3a40a Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Sun, 1 Nov 2020 00:33:38 +0800 Subject: fix aac directstreaming --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 5 +++-- MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs | 5 +++++ 2 files changed, 8 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 996b1b5c1..33256e4bf 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -2675,9 +2675,10 @@ namespace MediaBrowser.Controller.MediaEncoding state.MediaSource = mediaSource; var request = state.BaseRequest; - if (!string.IsNullOrWhiteSpace(request.AudioCodec)) + var supportedAudioCodecs = state.SupportedAudioCodecs; + if (request != null && supportedAudioCodecs != null && supportedAudioCodecs.Length > 0) { - var supportedAudioCodecsList = request.AudioCodec.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList(); + var supportedAudioCodecsList = supportedAudioCodecs.ToList(); ShiftAudioCodecsIfNeeded(supportedAudioCodecsList, state.AudioStream); diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs index 6cd0c70d2..6e9362cd1 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs @@ -287,6 +287,11 @@ namespace MediaBrowser.Controller.MediaEncoding return BaseRequest.AudioChannels; } + if (BaseRequest.TranscodingMaxAudioChannels.HasValue) + { + return BaseRequest.TranscodingMaxAudioChannels; + } + if (!string.IsNullOrEmpty(codec)) { var value = BaseRequest.GetOption(codec, "audiochannels"); -- cgit v1.2.3 From 85965741f57e709133fbaf5ba59ed45bbd3b5d26 Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Sun, 8 Nov 2020 01:39:32 +0800 Subject: add initial support for HEVC over FMP4-HLS --- Jellyfin.Api/Controllers/DynamicHlsController.cs | 76 +++++++- .../Controllers/UniversalAudioController.cs | 7 +- Jellyfin.Api/Helpers/DynamicHlsHelper.cs | 198 +++++++++++++++++++-- Jellyfin.Api/Helpers/HlsCodecStringHelpers.cs | 29 ++- .../MediaEncoding/EncodingHelper.cs | 187 ++++++++++++------- MediaBrowser.Model/Dlna/ResolutionNormalizer.cs | 1 - 6 files changed, 396 insertions(+), 102 deletions(-) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index e07690e11..1247ef502 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -1136,11 +1136,19 @@ namespace Jellyfin.Api.Controllers var segmentLengths = GetSegmentLengths(state); + var segmentContainer = state.Request.SegmentContainer ?? "ts"; + + // http://ffmpeg.org/ffmpeg-all.html#toc-hls-2 + var isHlsInFmp4 = string.Equals(segmentContainer, "mp4", StringComparison.OrdinalIgnoreCase); + var hlsVersion = isHlsInFmp4 ? "7" : "3"; + var builder = new StringBuilder(); builder.AppendLine("#EXTM3U") .AppendLine("#EXT-X-PLAYLIST-TYPE:VOD") - .AppendLine("#EXT-X-VERSION:3") + .Append("#EXT-X-VERSION:") + .Append(hlsVersion) + .AppendLine() .Append("#EXT-X-TARGETDURATION:") .Append(Math.Ceiling(segmentLengths.Length > 0 ? segmentLengths.Max() : state.SegmentLength)) .AppendLine() @@ -1150,6 +1158,18 @@ namespace Jellyfin.Api.Controllers var segmentExtension = GetSegmentFileExtension(streamingRequest.SegmentContainer); var queryString = Request.QueryString; + if (isHlsInFmp4) + { + builder.Append("#EXT-X-MAP:URI=\"") + .Append("hls1/") + .Append(name) + .Append("/-1") + .Append(segmentExtension) + .Append(queryString) + .Append('"') + .AppendLine(); + } + foreach (var length in segmentLengths) { builder.Append("#EXTINF:") @@ -1232,7 +1252,13 @@ namespace Jellyfin.Api.Controllers var currentTranscodingIndex = GetCurrentTranscodingIndex(playlistPath, segmentExtension); var segmentGapRequiringTranscodingChange = 24 / state.SegmentLength; - if (currentTranscodingIndex == null) + if (segmentId == -1) + { + _logger.LogDebug("Starting transcoding because fmp4 header file is being requested"); + startTranscoding = true; + segmentId = 0; + } + else if (currentTranscodingIndex == null) { _logger.LogDebug("Starting transcoding because currentTranscodingIndex=null"); startTranscoding = true; @@ -1347,13 +1373,24 @@ namespace Jellyfin.Api.Controllers var mapArgs = state.IsOutputVideo ? _encodingHelper.GetMapArgs(state) : string.Empty; - var outputTsArg = Path.Combine(Path.GetDirectoryName(outputPath), Path.GetFileNameWithoutExtension(outputPath)) + "%d" + GetSegmentFileExtension(state.Request.SegmentContainer); + var outputPrefix = Path.Combine(Path.GetDirectoryName(outputPath), Path.GetFileNameWithoutExtension(outputPath)); + var outputExtension = GetSegmentFileExtension(state.Request.SegmentContainer); + var outputTsArg = outputPrefix + "%d" + outputExtension; var segmentFormat = GetSegmentFileExtension(state.Request.SegmentContainer).TrimStart('.'); if (string.Equals(segmentFormat, "ts", StringComparison.OrdinalIgnoreCase)) { segmentFormat = "mpegts"; } + else if (string.Equals(segmentFormat, "mp4", StringComparison.OrdinalIgnoreCase)) + { + var outputFmp4HeaderArg = " -hls_fmp4_init_filename \"" + outputPrefix + "-1" + outputExtension + "\""; + segmentFormat = "fmp4" + outputFmp4HeaderArg; + } + else + { + _logger.LogError("Invalid HLS segment container: " + segmentFormat); + } var maxMuxingQueueSize = encodingOptions.MaxMuxingQueueSize > 128 ? encodingOptions.MaxMuxingQueueSize.ToString(CultureInfo.InvariantCulture) @@ -1384,7 +1421,7 @@ namespace Jellyfin.Api.Controllers { if (EncodingHelper.IsCopyCodec(audioCodec)) { - return "-acodec copy"; + return "-acodec copy -strict -2"; } var audioTranscodeParams = new List(); @@ -1416,10 +1453,10 @@ namespace Jellyfin.Api.Controllers if (EncodingHelper.IsCopyCodec(videoCodec) && state.EnableBreakOnNonKeyFrames(videoCodec)) { - return "-codec:a:0 copy -copypriorss:a:0 0"; + return "-codec:a:0 copy -strict -2 -copypriorss:a:0 0"; } - return "-codec:a:0 copy"; + return "-codec:a:0 copy -strict -2"; } var args = "-codec:a:0 " + audioCodec; @@ -1459,6 +1496,15 @@ namespace Jellyfin.Api.Controllers var args = "-codec:v:0 " + codec; + // Prefer hvc1 to hev1 + if (string.Equals(state.ActualOutputVideoCodec, "h265", StringComparison.OrdinalIgnoreCase) + || string.Equals(state.ActualOutputVideoCodec, "hevc", StringComparison.OrdinalIgnoreCase) + || string.Equals(codec, "h265", StringComparison.OrdinalIgnoreCase) + || string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase)) + { + args += " -tag:v:0 hvc1"; + } + // if (state.EnableMpegtsM2TsMode) // { // args += " -mpegts_m2ts_mode 1"; @@ -1505,18 +1551,32 @@ namespace Jellyfin.Api.Controllers args += " " + _encodingHelper.GetVideoQualityParam(state, codec, encodingOptions, "veryfast"); - // Unable to force key frames using these hw encoders, set key frames by GOP + // Unable to force key frames using these encoders, set key frames by GOP if (string.Equals(codec, "h264_qsv", StringComparison.OrdinalIgnoreCase) || string.Equals(codec, "h264_nvenc", StringComparison.OrdinalIgnoreCase) - || string.Equals(codec, "h264_amf", StringComparison.OrdinalIgnoreCase)) + || string.Equals(codec, "h264_amf", StringComparison.OrdinalIgnoreCase) + || string.Equals(codec, "hevc_qsv", StringComparison.OrdinalIgnoreCase) + || string.Equals(codec, "hevc_nvenc", StringComparison.OrdinalIgnoreCase) + || string.Equals(codec, "hevc_amf", StringComparison.OrdinalIgnoreCase)) { args += " " + gopArg; } + else if (string.Equals(codec, "libx264", StringComparison.OrdinalIgnoreCase) + || string.Equals(codec, "libx265", StringComparison.OrdinalIgnoreCase)) + { + args += " " + keyFrameArg; + } else { args += " " + keyFrameArg + gopArg; } + // Currenly b-frames in libx265 breaks the FMP4-HLS playback on iOS, disable it for now + if (string.Equals(codec, "libx265", StringComparison.OrdinalIgnoreCase)) + { + args += " -bf 0"; + } + // args += " -mixed-refs 0 -refs 3 -x264opts b_pyramid=0:weightb=0:weightp=0"; var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode; diff --git a/Jellyfin.Api/Controllers/UniversalAudioController.cs b/Jellyfin.Api/Controllers/UniversalAudioController.cs index e10f1fe91..9820c401a 100644 --- a/Jellyfin.Api/Controllers/UniversalAudioController.cs +++ b/Jellyfin.Api/Controllers/UniversalAudioController.cs @@ -191,8 +191,11 @@ namespace Jellyfin.Api.Controllers if (!isStatic && string.Equals(mediaSource.TranscodingSubProtocol, "hls", StringComparison.OrdinalIgnoreCase)) { // hls segment container can only be mpegts or fmp4 per ffmpeg documentation + // ffmpeg option -> file extension + // mpegts -> ts + // fmp4 -> mp4 // TODO: remove this when we switch back to the segment muxer - var supportedHlsContainers = new[] { "mpegts", "fmp4" }; + var supportedHlsContainers = new[] { "ts", "mp4" }; var dynamicHlsRequestDto = new HlsAudioRequestDto { @@ -201,7 +204,7 @@ namespace Jellyfin.Api.Controllers Static = isStatic, PlaySessionId = info.PlaySessionId, // fallback to mpegts if device reports some weird value unsupported by hls - SegmentContainer = Array.Exists(supportedHlsContainers, element => element == transcodingContainer) ? transcodingContainer : "mpegts", + SegmentContainer = Array.Exists(supportedHlsContainers, element => element == transcodingContainer) ? transcodingContainer : "ts", MediaSourceId = mediaSourceId, DeviceId = deviceId, AudioCodec = audioCodec, diff --git a/Jellyfin.Api/Helpers/DynamicHlsHelper.cs b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs index ea012f837..46c1ddc9f 100644 --- a/Jellyfin.Api/Helpers/DynamicHlsHelper.cs +++ b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs @@ -202,7 +202,61 @@ namespace Jellyfin.Api.Helpers AddSubtitles(state, subtitleStreams, builder, _httpContextAccessor.HttpContext.User); } - AppendPlaylist(builder, state, playlistUrl, totalBitrate, subtitleGroup); + var basicPlaylist = AppendPlaylist(builder, state, playlistUrl, totalBitrate, subtitleGroup); + + if (state.VideoStream != null && state.VideoRequest != null) + { + // Provide SDR HEVC entrance for backward compatibility + if (EncodingHelper.IsCopyCodec(state.OutputVideoCodec) + && !string.IsNullOrEmpty(state.VideoStream.VideoRange) + && string.Equals(state.VideoStream.VideoRange, "HDR", StringComparison.OrdinalIgnoreCase) + && string.Equals(state.ActualOutputVideoCodec, "hevc", StringComparison.OrdinalIgnoreCase)) + { + var requestedVideoProfiles = state.GetRequestedProfiles("hevc"); + if (requestedVideoProfiles != null && requestedVideoProfiles.Length > 0) + { + // Force HEVC Main Profile and disable video stream copy + state.OutputVideoCodec = "hevc"; + var sdrVideoUrl = ReplaceProfile(playlistUrl, "hevc", string.Join(",", requestedVideoProfiles), "main"); + sdrVideoUrl += "&AllowVideoStreamCopy=false"; + + EncodingHelper encodingHelper = new EncodingHelper(_mediaEncoder, _fileSystem, _subtitleEncoder, _configuration); + var sdrOutputVideoBitrate = encodingHelper.GetVideoBitrateParamValue(state.VideoRequest, state.VideoStream, state.OutputVideoCodec) ?? 0; + var sdrOutputAudioBitrate = encodingHelper.GetAudioBitrateParam(state.VideoRequest.AudioBitRate, state.AudioStream) ?? 0; + var sdrTotalBitrate = sdrOutputAudioBitrate + sdrOutputVideoBitrate; + + AppendPlaylist(builder, state, sdrVideoUrl, sdrTotalBitrate, subtitleGroup); + + // Restore the video codec + state.OutputVideoCodec = "copy"; + } + } + + // Provide Level 5.0 entrance for backward compatibility + // e.g. Apple A10 chips refuse the master playlist containing SDR HEVC Main Level 5.1 video, + // but in fact it is capable of playing videos up to Level 6.1. + if (EncodingHelper.IsCopyCodec(state.OutputVideoCodec) + && state.VideoStream.Level.HasValue + && state.VideoStream.Level > 150 + && !string.IsNullOrEmpty(state.VideoStream.VideoRange) + && string.Equals(state.VideoStream.VideoRange, "SDR", StringComparison.OrdinalIgnoreCase) + && string.Equals(state.ActualOutputVideoCodec, "hevc", StringComparison.OrdinalIgnoreCase)) + { + var playlistCodecsField = new StringBuilder(); + AppendPlaylistCodecsField(playlistCodecsField, state); + + // Force the video level to 5.0 + var originalLevel = state.VideoStream.Level; + state.VideoStream.Level = 150; + var newPlaylistCodecsField = new StringBuilder(); + AppendPlaylistCodecsField(newPlaylistCodecsField, state); + + // Restore the video level + state.VideoStream.Level = originalLevel; + var newPlaylist = ReplacePlaylistCodecsField(basicPlaylist, playlistCodecsField, newPlaylistCodecsField); + builder.Append(newPlaylist); + } + } if (EnableAdaptiveBitrateStreaming(state, isLiveStream, enableAdaptiveBitrateStreaming, _httpContextAccessor.HttpContext.GetNormalizedRemoteIp())) { @@ -212,40 +266,77 @@ namespace Jellyfin.Api.Helpers var variation = GetBitrateVariation(totalBitrate); var newBitrate = totalBitrate - variation; - var variantUrl = ReplaceBitrate(playlistUrl, requestedVideoBitrate, requestedVideoBitrate - variation); + var variantUrl = ReplaceVideoBitrate(playlistUrl, requestedVideoBitrate, requestedVideoBitrate - variation); AppendPlaylist(builder, state, variantUrl, newBitrate, subtitleGroup); variation *= 2; newBitrate = totalBitrate - variation; - variantUrl = ReplaceBitrate(playlistUrl, requestedVideoBitrate, requestedVideoBitrate - variation); + variantUrl = ReplaceVideoBitrate(playlistUrl, requestedVideoBitrate, requestedVideoBitrate - variation); AppendPlaylist(builder, state, variantUrl, newBitrate, subtitleGroup); } return new FileContentResult(Encoding.UTF8.GetBytes(builder.ToString()), MimeTypes.GetMimeType("playlist.m3u8")); } - private void AppendPlaylist(StringBuilder builder, StreamState state, string url, int bitrate, string? subtitleGroup) + private StringBuilder AppendPlaylist(StringBuilder builder, StreamState state, string url, int bitrate, string? subtitleGroup) { - builder.Append("#EXT-X-STREAM-INF:BANDWIDTH=") + var playlistBuilder = new StringBuilder(); + playlistBuilder.Append("#EXT-X-STREAM-INF:BANDWIDTH=") .Append(bitrate.ToString(CultureInfo.InvariantCulture)) .Append(",AVERAGE-BANDWIDTH=") .Append(bitrate.ToString(CultureInfo.InvariantCulture)); - AppendPlaylistCodecsField(builder, state); + AppendPlaylistVideoRangeField(playlistBuilder, state); + + AppendPlaylistCodecsField(playlistBuilder, state); - AppendPlaylistResolutionField(builder, state); + AppendPlaylistResolutionField(playlistBuilder, state); - AppendPlaylistFramerateField(builder, state); + AppendPlaylistFramerateField(playlistBuilder, state); if (!string.IsNullOrWhiteSpace(subtitleGroup)) { - builder.Append(",SUBTITLES=\"") + playlistBuilder.Append(",SUBTITLES=\"") .Append(subtitleGroup) .Append('"'); } - builder.Append(Environment.NewLine); - builder.AppendLine(url); + playlistBuilder.Append(Environment.NewLine); + playlistBuilder.AppendLine(url); + builder.Append(playlistBuilder); + + return playlistBuilder; + } + + /// + /// Appends a VIDEO-RANGE field containing the range of the output video stream. + /// + /// + /// StringBuilder to append the field to. + /// StreamState of the current stream. + private void AppendPlaylistVideoRangeField(StringBuilder builder, StreamState state) + { + if (state.VideoStream != null && !string.IsNullOrEmpty(state.VideoStream.VideoRange)) + { + var videoRange = state.VideoStream.VideoRange; + if (EncodingHelper.IsCopyCodec(state.OutputVideoCodec)) + { + if (string.Equals(videoRange, "SDR", StringComparison.OrdinalIgnoreCase)) + { + builder.Append(",VIDEO-RANGE=SDR"); + } + + if (string.Equals(videoRange, "HDR", StringComparison.OrdinalIgnoreCase)) + { + builder.Append(",VIDEO-RANGE=PQ"); + } + } + else + { + // Currently we only encode to SDR + builder.Append(",VIDEO-RANGE=SDR"); + } + } } /// @@ -414,25 +505,68 @@ namespace Jellyfin.Api.Helpers /// H.26X level of the output video stream. private int? GetOutputVideoCodecLevel(StreamState state) { - string? levelString; + string levelString = string.Empty; if (EncodingHelper.IsCopyCodec(state.OutputVideoCodec) + && state.VideoStream != null && state.VideoStream.Level.HasValue) { - levelString = state.VideoStream?.Level.ToString(); + levelString = state.VideoStream.Level.ToString(); } else { - levelString = state.GetRequestedLevel(state.ActualOutputVideoCodec); + if (string.Equals(state.ActualOutputVideoCodec, "h264", StringComparison.OrdinalIgnoreCase)) + { + levelString = state.GetRequestedLevel(state.ActualOutputVideoCodec) ?? "41"; + levelString = EncodingHelper.NormalizeTranscodingLevel(state, levelString); + } + + if (string.Equals(state.ActualOutputVideoCodec, "h265", StringComparison.OrdinalIgnoreCase) + || string.Equals(state.ActualOutputVideoCodec, "hevc", StringComparison.OrdinalIgnoreCase)) + { + levelString = state.GetRequestedLevel("h265") ?? state.GetRequestedLevel("hevc") ?? "120"; + levelString = EncodingHelper.NormalizeTranscodingLevel(state, levelString); + } } if (int.TryParse(levelString, NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsedLevel)) { return parsedLevel; } - return null; } + /// + /// Get the H.26X profile of the output video stream. + /// + /// StreamState of the current stream. + /// Video codec. + /// H.26X profile of the output video stream. + private string GetOutputVideoCodecProfile(StreamState state, string codec) + { + string profileString = string.Empty; + if (EncodingHelper.IsCopyCodec(state.OutputVideoCodec) + && !string.IsNullOrEmpty(state.VideoStream.Profile)) + { + profileString = state.VideoStream.Profile; + } + else if (!string.IsNullOrEmpty(codec)) + { + profileString = state.GetRequestedProfiles(codec).FirstOrDefault(); + if (string.Equals(state.ActualOutputVideoCodec, "h264", StringComparison.OrdinalIgnoreCase)) + { + profileString = profileString ?? "high"; + } + + if (string.Equals(state.ActualOutputVideoCodec, "h265", StringComparison.OrdinalIgnoreCase) + || string.Equals(state.ActualOutputVideoCodec, "hevc", StringComparison.OrdinalIgnoreCase)) + { + profileString = profileString ?? "main"; + } + } + + return profileString; + } + /// /// Gets a formatted string of the output audio codec, for use in the CODECS field. /// @@ -463,6 +597,16 @@ namespace Jellyfin.Api.Helpers return HlsCodecStringHelpers.GetEAC3String(); } + if (string.Equals(state.ActualOutputAudioCodec, "flac", StringComparison.OrdinalIgnoreCase)) + { + return HlsCodecStringHelpers.GetFLACString(); + } + + if (string.Equals(state.ActualOutputAudioCodec, "alac", StringComparison.OrdinalIgnoreCase)) + { + return HlsCodecStringHelpers.GetALACString(); + } + return string.Empty; } @@ -487,15 +631,14 @@ namespace Jellyfin.Api.Helpers if (string.Equals(codec, "h264", StringComparison.OrdinalIgnoreCase)) { - string profile = state.GetRequestedProfiles("h264").FirstOrDefault(); + string profile = GetOutputVideoCodecProfile(state, "h264"); return HlsCodecStringHelpers.GetH264String(profile, level); } if (string.Equals(codec, "h265", StringComparison.OrdinalIgnoreCase) || string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase)) { - string profile = state.GetRequestedProfiles("h265").FirstOrDefault(); - + string profile = GetOutputVideoCodecProfile(state, "hevc"); return HlsCodecStringHelpers.GetH265String(profile, level); } @@ -539,12 +682,29 @@ namespace Jellyfin.Api.Helpers return variation; } - private string ReplaceBitrate(string url, int oldValue, int newValue) + private string ReplaceVideoBitrate(string url, int oldValue, int newValue) { return url.Replace( "videobitrate=" + oldValue.ToString(CultureInfo.InvariantCulture), "videobitrate=" + newValue.ToString(CultureInfo.InvariantCulture), StringComparison.OrdinalIgnoreCase); } + + private string ReplaceProfile(string url, string codec, string oldValue, string newValue) + { + return url.Replace( + codec + "-profile=" + oldValue.ToString(), + codec + "-profile=" + newValue.ToString(), + StringComparison.OrdinalIgnoreCase); + } + + private string ReplacePlaylistCodecsField(StringBuilder playlist, StringBuilder oldValue, StringBuilder newValue) + { + var oldPlaylist = playlist.ToString(); + return oldPlaylist.Replace( + oldValue.ToString(), + newValue.ToString(), + StringComparison.OrdinalIgnoreCase); + } } } diff --git a/Jellyfin.Api/Helpers/HlsCodecStringHelpers.cs b/Jellyfin.Api/Helpers/HlsCodecStringHelpers.cs index 95f1906ef..3d7a9d9a0 100644 --- a/Jellyfin.Api/Helpers/HlsCodecStringHelpers.cs +++ b/Jellyfin.Api/Helpers/HlsCodecStringHelpers.cs @@ -85,20 +85,21 @@ namespace Jellyfin.Api.Helpers // The h265 syntax is a bit of a mystery at the time this comment was written. // This is what I've found through various sources: // FORMAT: [codecTag].[profile].[constraint?].L[level * 30].[UNKNOWN] - StringBuilder result = new StringBuilder("hev1", 16); + StringBuilder result = new StringBuilder("hvc1", 16); - if (string.Equals(profile, "main10", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(profile, "main10", StringComparison.OrdinalIgnoreCase) + || string.Equals(profile, "main 10", StringComparison.OrdinalIgnoreCase)) { - result.Append(".2.6"); + result.Append(".2.4"); } else { // Default to main if profile is invalid - result.Append(".1.6"); + result.Append(".1.4"); } result.Append(".L") - .Append(level * 3) + .Append(level) .Append(".B0"); return result.ToString(); @@ -121,5 +122,23 @@ namespace Jellyfin.Api.Helpers { return "mp4a.a6"; } + + /// + /// Gets an FLAC codec string. + /// + /// FLAC codec string. + public static string GetFLACString() + { + return "fLaC"; + } + + /// + /// Gets an ALAC codec string. + /// + /// ALAC codec string. + public static string GetALACString() + { + return "alac"; + } } } diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 5846a603a..87670e2eb 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -7,6 +7,7 @@ using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Text; +using System.Text.RegularExpressions; using System.Threading; using Jellyfin.Data.Enums; using MediaBrowser.Controller.Entities; @@ -23,7 +24,7 @@ namespace MediaBrowser.Controller.MediaEncoding { public class EncodingHelper { - private readonly CultureInfo _usCulture = new CultureInfo("en-US"); + private static readonly CultureInfo _usCulture = new CultureInfo("en-US"); private readonly IMediaEncoder _mediaEncoder; private readonly IFileSystem _fileSystem; @@ -654,16 +655,26 @@ namespace MediaBrowser.Controller.MediaEncoding return string.Empty; } - public string NormalizeTranscodingLevel(string videoCodec, string level) + public static string NormalizeTranscodingLevel(EncodingJobInfo state, string level) { - // 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) - && requestLevel > 41 - && (string.Equals(videoCodec, "h264", StringComparison.OrdinalIgnoreCase) - || string.Equals(videoCodec, "h265", StringComparison.OrdinalIgnoreCase) - || string.Equals(videoCodec, "hevc", StringComparison.OrdinalIgnoreCase))) + if (double.TryParse(level, NumberStyles.Any, _usCulture, out double requestLevel)) { - return "41"; + if (string.Equals(state.ActualOutputVideoCodec, "hevc", StringComparison.OrdinalIgnoreCase) + || string.Equals(state.ActualOutputVideoCodec, "h265", StringComparison.OrdinalIgnoreCase)) + { + if (requestLevel >= 150) + { + return "150"; + } + } + else if (string.Equals(state.ActualOutputVideoCodec, "h264", StringComparison.OrdinalIgnoreCase)) + { + // Clients may direct play higher than level 41, but there's no reason to transcode higher + if (requestLevel >= 41) + { + return "41"; + } + } } return level; @@ -809,7 +820,8 @@ namespace MediaBrowser.Controller.MediaEncoding param += " -crf " + defaultCrf; } } - else if (string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase)) // h264 (h264_qsv) + else if (string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) // h264 (h264_qsv) + || string.Equals(videoEncoder, "hevc_qsv", StringComparison.OrdinalIgnoreCase)) // hevc (hevc_qsv) { string[] valid_h264_qsv = { "veryslow", "slower", "slow", "medium", "fast", "faster", "veryfast" }; @@ -825,8 +837,9 @@ namespace MediaBrowser.Controller.MediaEncoding param += " -look_ahead 0"; } else if (string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase) // h264 (h264_nvenc) - || string.Equals(videoEncoder, "hevc_nvenc", StringComparison.OrdinalIgnoreCase)) + || string.Equals(videoEncoder, "hevc_nvenc", StringComparison.OrdinalIgnoreCase)) // hevc (hevc_nvenc) { + // following preset will be deprecated in ffmpeg 4.4, use p1~p7 instead switch (encodingOptions.EncoderPreset) { case "veryslow": @@ -856,8 +869,8 @@ namespace MediaBrowser.Controller.MediaEncoding break; } } - else if (string.Equals(videoEncoder, "h264_amf", StringComparison.OrdinalIgnoreCase) - || string.Equals(videoEncoder, "hevc_amf", StringComparison.OrdinalIgnoreCase)) + else if (string.Equals(videoEncoder, "h264_amf", StringComparison.OrdinalIgnoreCase) // h264 (h264_amf) + || string.Equals(videoEncoder, "hevc_amf", StringComparison.OrdinalIgnoreCase)) // hevc (hevc_amf) { switch (encodingOptions.EncoderPreset) { @@ -896,6 +909,11 @@ namespace MediaBrowser.Controller.MediaEncoding // Enhance workload when tone mapping with AMF on some APUs param += " -preanalysis true"; } + + if (string.Equals(videoEncoder, "hevc_amf", StringComparison.OrdinalIgnoreCase)) + { + param += " -header_insertion_mode gop -gops_per_idr 1"; + } } else if (string.Equals(videoEncoder, "libvpx", StringComparison.OrdinalIgnoreCase)) // webm { @@ -945,10 +963,24 @@ namespace MediaBrowser.Controller.MediaEncoding } var targetVideoCodec = state.ActualOutputVideoCodec; + if (string.Equals(targetVideoCodec, "h265", StringComparison.OrdinalIgnoreCase) + || string.Equals(targetVideoCodec, "hevc", StringComparison.OrdinalIgnoreCase)) + { + targetVideoCodec = "hevc"; + } var profile = state.GetRequestedProfiles(targetVideoCodec).FirstOrDefault(); + profile = Regex.Replace(profile, @"\s+", String.Empty); + + // only libx264 support encoding H264 High 10 Profile, otherwise force High Profile + if (!string.Equals(videoEncoder, "libx264", StringComparison.OrdinalIgnoreCase) + && profile != null + && profile.IndexOf("high 10", StringComparison.OrdinalIgnoreCase) != -1) + { + profile = "high"; + } - // vaapi does not support Baseline profile, force Constrained Baseline in this case, + // h264_vaapi does not support Baseline profile, force Constrained Baseline in this case, // which is compatible (and ugly) if (string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase) && profile != null @@ -957,6 +989,24 @@ namespace MediaBrowser.Controller.MediaEncoding profile = "constrained_baseline"; } + // libx264, h264_qsv and h264_nvenc does not support Constrained Baseline profile, force Baseline in this case + if ((string.Equals(videoEncoder, "libx264", StringComparison.OrdinalIgnoreCase) + || string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) + || string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase)) + && profile != null + && profile.IndexOf("baseline", StringComparison.OrdinalIgnoreCase) != -1) + { + profile = "baseline"; + } + + // Currently hevc_amf only support encoding HEVC Main Profile, otherwise force Main Profile + if (!string.Equals(videoEncoder, "hevc_amf", StringComparison.OrdinalIgnoreCase) + && profile != null + && profile.IndexOf("main 10", StringComparison.OrdinalIgnoreCase) != -1) + { + profile = "main"; + } + if (!string.IsNullOrEmpty(profile)) { if (!string.Equals(videoEncoder, "h264_omx", StringComparison.OrdinalIgnoreCase) @@ -971,55 +1021,35 @@ namespace MediaBrowser.Controller.MediaEncoding if (!string.IsNullOrEmpty(level)) { - level = NormalizeTranscodingLevel(state.OutputVideoCodec, level); + level = NormalizeTranscodingLevel(state, level); - // 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 + // libx264, QSV, AMF, VAAPI can adjust the given level to match the output if (string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) - || string.Equals(videoEncoder, "libx264", StringComparison.OrdinalIgnoreCase) - || string.Equals(videoEncoder, "libx265", StringComparison.OrdinalIgnoreCase)) + || string.Equals(videoEncoder, "libx264", StringComparison.OrdinalIgnoreCase)) { - switch (level) + param += " -level " + level; + } + else if (string.Equals(videoEncoder, "hevc_qsv", StringComparison.OrdinalIgnoreCase)) + { + // hevc_qsv use -level 51 instead of -level 153 + if (double.TryParse(level, NumberStyles.Any, _usCulture, out double hevcLevel)) { - case "30": - param += " -level 3.0"; - break; - case "31": - param += " -level 3.1"; - break; - case "32": - param += " -level 3.2"; - break; - case "40": - param += " -level 4.0"; - break; - case "41": - param += " -level 4.1"; - break; - case "42": - param += " -level 4.2"; - break; - case "50": - param += " -level 5.0"; - break; - case "51": - param += " -level 5.1"; - break; - case "52": - param += " -level 5.2"; - break; - default: - param += " -level " + level; - break; + param += " -level " + hevcLevel / 3; } } + else if (string.Equals(videoEncoder, "h264_amf", StringComparison.OrdinalIgnoreCase) + || string.Equals(videoEncoder, "hevc_amf", StringComparison.OrdinalIgnoreCase)) + { + param += " -level " + level; + } else if (string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase) || string.Equals(videoEncoder, "hevc_nvenc", StringComparison.OrdinalIgnoreCase)) { - // nvenc doesn't decode with param -level set ?! - // TODO: + // level option may cause NVENC to fail. + // NVENC cannot adjust the given level, just throw an error. } - else if (!string.Equals(videoEncoder, "h264_omx", StringComparison.OrdinalIgnoreCase)) + else if (!string.Equals(videoEncoder, "h264_omx", StringComparison.OrdinalIgnoreCase) + || !string.Equals(videoEncoder, "libx265", StringComparison.OrdinalIgnoreCase)) { param += " -level " + level; } @@ -1032,7 +1062,11 @@ namespace MediaBrowser.Controller.MediaEncoding if (string.Equals(videoEncoder, "libx265", StringComparison.OrdinalIgnoreCase)) { - // todo + // libx265 only accept level option in -x265-params + // level option may cause libx265 to fail + // libx265 cannot adjust the given level, just throw an error + // TODO: set fine tuned params + param += " -x265-params:0 no-info=1"; } if (!string.Equals(videoEncoder, "h264_omx", StringComparison.OrdinalIgnoreCase) @@ -1040,13 +1074,19 @@ namespace MediaBrowser.Controller.MediaEncoding && !string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase) && !string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase) && !string.Equals(videoEncoder, "h264_amf", StringComparison.OrdinalIgnoreCase) - && !string.Equals(videoEncoder, "h264_v4l2m2m", StringComparison.OrdinalIgnoreCase)) + && !string.Equals(videoEncoder, "h264_v4l2m2m", StringComparison.OrdinalIgnoreCase) + && !string.Equals(videoEncoder, "hevc_qsv", StringComparison.OrdinalIgnoreCase) + && !string.Equals(videoEncoder, "hevc_vaapi", StringComparison.OrdinalIgnoreCase) + && !string.Equals(videoEncoder, "hevc_nvenc", StringComparison.OrdinalIgnoreCase) + && !string.Equals(videoEncoder, "hevc_amf", StringComparison.OrdinalIgnoreCase)) { param = "-pix_fmt yuv420p " + param; } if (string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase) - || string.Equals(videoEncoder, "h264_amf", StringComparison.OrdinalIgnoreCase)) + || string.Equals(videoEncoder, "h264_amf", StringComparison.OrdinalIgnoreCase) + || string.Equals(videoEncoder, "hevc_nvenc", StringComparison.OrdinalIgnoreCase) + || string.Equals(videoEncoder, "hevc_amf", StringComparison.OrdinalIgnoreCase)) { var videoStream = state.VideoStream; var isColorDepth10 = IsColorDepth10(state); @@ -1708,7 +1748,8 @@ namespace MediaBrowser.Controller.MediaEncoding } // For QSV, feed it into hardware encoder now - if (isLinux && string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase)) + if (isLinux && (string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase) + || string.Equals(outputVideoCodec, "hevc_qsv", StringComparison.OrdinalIgnoreCase))) { videoSizeParam += ",hwupload=extra_hw_frames=64"; } @@ -1729,7 +1770,8 @@ namespace MediaBrowser.Controller.MediaEncoding : " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay\""; // When the input may or may not be hardware VAAPI decodable - if (string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase) + || string.Equals(outputVideoCodec, "hevc_vaapi", StringComparison.OrdinalIgnoreCase)) { /* [base]: HW scaling video to OutputSize @@ -1741,7 +1783,8 @@ namespace MediaBrowser.Controller.MediaEncoding // If we're hardware VAAPI decoding and software encoding, download frames from the decoder first else if (_mediaEncoder.SupportsHwaccel("vaapi") && videoDecoder.IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1 - && string.Equals(outputVideoCodec, "libx264", StringComparison.OrdinalIgnoreCase)) + && (string.Equals(outputVideoCodec, "libx264", StringComparison.OrdinalIgnoreCase) + || string.Equals(outputVideoCodec, "libx265", StringComparison.OrdinalIgnoreCase))) { /* [base]: SW scaling video to OutputSize @@ -1750,7 +1793,8 @@ namespace MediaBrowser.Controller.MediaEncoding */ retStr = " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3}[base];[base][sub]overlay\""; } - else if (string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase)) + else if (string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase) + || string.Equals(outputVideoCodec, "hevc_qsv", StringComparison.OrdinalIgnoreCase)) { /* QSV in FFMpeg can now setup hardware overlay for transcodes. @@ -1776,7 +1820,7 @@ namespace MediaBrowser.Controller.MediaEncoding videoSizeParam); } - private (int? width, int? height) GetFixedOutputSize( + public static (int? width, int? height) GetFixedOutputSize( int? videoWidth, int? videoHeight, int? requestedWidth, @@ -1836,7 +1880,9 @@ namespace MediaBrowser.Controller.MediaEncoding requestedMaxHeight); if ((string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase) - || string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase)) + || string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) + || string.Equals(videoEncoder, "hevc_vaapi", StringComparison.OrdinalIgnoreCase) + || string.Equals(videoEncoder, "hevc_qsv", StringComparison.OrdinalIgnoreCase)) && width.HasValue && height.HasValue) { @@ -1845,7 +1891,8 @@ namespace MediaBrowser.Controller.MediaEncoding // output dimensions. Output dimensions are guaranteed to be even. var outputWidth = width.Value; var outputHeight = height.Value; - var qsv_or_vaapi = string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase); + var qsv_or_vaapi = string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) + || string.Equals(videoEncoder, "hevc_qsv", StringComparison.OrdinalIgnoreCase); var isDeintEnabled = state.DeInterlace("h264", true) || state.DeInterlace("avc", true) || state.DeInterlace("h265", true) @@ -2107,10 +2154,13 @@ namespace MediaBrowser.Controller.MediaEncoding var isD3d11vaDecoder = videoDecoder.IndexOf("d3d11va", StringComparison.OrdinalIgnoreCase) != -1; var isVaapiDecoder = videoDecoder.IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1; var isVaapiH264Encoder = outputVideoCodec.IndexOf("h264_vaapi", StringComparison.OrdinalIgnoreCase) != -1; + var isVaapiHevcEncoder = outputVideoCodec.IndexOf("hevc_vaapi", StringComparison.OrdinalIgnoreCase) != -1; var isQsvH264Encoder = outputVideoCodec.IndexOf("h264_qsv", StringComparison.OrdinalIgnoreCase) != -1; + var isQsvHevcEncoder = outputVideoCodec.IndexOf("hevc_qsv", StringComparison.OrdinalIgnoreCase) != -1; var isNvdecH264Decoder = videoDecoder.IndexOf("h264_cuvid", StringComparison.OrdinalIgnoreCase) != -1; var isNvdecHevcDecoder = videoDecoder.IndexOf("hevc_cuvid", StringComparison.OrdinalIgnoreCase) != -1; var isLibX264Encoder = outputVideoCodec.IndexOf("libx264", StringComparison.OrdinalIgnoreCase) != -1; + var isLibX265Encoder = outputVideoCodec.IndexOf("libx265", StringComparison.OrdinalIgnoreCase) != -1; var isLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux); var isColorDepth10 = IsColorDepth10(state); @@ -2185,6 +2235,7 @@ namespace MediaBrowser.Controller.MediaEncoding filters.Add("hwdownload"); if (isLibX264Encoder + || isLibX265Encoder || hasGraphicalSubs || (isNvdecHevcDecoder && isDeinterlaceHevc) || (!isNvdecHevcDecoder && isDeinterlaceH264 || isDeinterlaceHevc)) @@ -2195,20 +2246,20 @@ namespace MediaBrowser.Controller.MediaEncoding } // When the input may or may not be hardware VAAPI decodable - if (isVaapiH264Encoder) + if (isVaapiH264Encoder || isVaapiHevcEncoder) { filters.Add("format=nv12|vaapi"); filters.Add("hwupload"); } // When burning in graphical subtitles using overlay_qsv, upload videostream to the same qsv context - else if (isLinux && hasGraphicalSubs && isQsvH264Encoder) + else if (isLinux && hasGraphicalSubs && (isQsvH264Encoder || isQsvHevcEncoder)) { filters.Add("hwupload=extra_hw_frames=64"); } // If we're hardware VAAPI decoding and software encoding, download frames from the decoder first - else if (IsVaapiSupported(state) && isVaapiDecoder && isLibX264Encoder) + else if (IsVaapiSupported(state) && isVaapiDecoder && (isLibX264Encoder || isLibX265Encoder)) { var codec = videoStream.Codec.ToLowerInvariant(); @@ -2250,7 +2301,9 @@ namespace MediaBrowser.Controller.MediaEncoding // Add software deinterlace filter before scaling filter if ((isDeinterlaceH264 || isDeinterlaceHevc) && !isVaapiH264Encoder + && !isVaapiHevcEncoder && !isQsvH264Encoder + && !isQsvHevcEncoder && !isNvdecH264Decoder) { if (string.Equals(options.DeinterlaceMethod, "bwdif", StringComparison.OrdinalIgnoreCase)) @@ -2289,7 +2342,7 @@ namespace MediaBrowser.Controller.MediaEncoding } // Add parameters to use VAAPI with burn-in text subtitles (GH issue #642) - if (isVaapiH264Encoder) + if (isVaapiH264Encoder || isVaapiHevcEncoder) { if (hasTextSubs) { diff --git a/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs b/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs index a4305c810..102db3b44 100644 --- a/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs +++ b/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs @@ -15,7 +15,6 @@ namespace MediaBrowser.Model.Dlna new ResolutionConfiguration(720, 950000), new ResolutionConfiguration(1280, 2500000), new ResolutionConfiguration(1920, 4000000), - new ResolutionConfiguration(2560, 8000000), new ResolutionConfiguration(3840, 35000000) }; -- cgit v1.2.3 From 5048719a64ce914f859e058c1a7b03258255836e Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Sun, 8 Nov 2020 09:01:58 +0000 Subject: minor changes per suggestions --- Jellyfin.Api/Controllers/DynamicHlsController.cs | 6 +- Jellyfin.Api/Helpers/HlsCodecStringHelpers.cs | 99 ++++++++++++++-------- .../MediaEncoding/EncodingHelper.cs | 8 +- 3 files changed, 72 insertions(+), 41 deletions(-) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index 9941c9f6e..6b1618421 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -1412,7 +1412,7 @@ namespace Jellyfin.Api.Controllers return string.Format( CultureInfo.InvariantCulture, - "{0} {1} -map_metadata -1 -map_chapters -1 -threads {2} {3} {4} {5} -copyts -avoid_negative_ts disabled -max_muxing_queue_size {6} -f hls -max_delay 5000000 -hls_time {7} -individual_header_trailer 0 -hls_segment_type {8} -start_number {9} -hls_segment_filename \"{10}\" -hls_playlist_type vod -hls_list_size 0 -y \"{11}\"", + "{0} {1} -map_metadata -1 -map_chapters -1 -threads {2} {3} {4} {5} -copyts -avoid_negative_ts 0 -max_muxing_queue_size {6} -f hls -max_delay 5000000 -hls_time {7} -individual_header_trailer 0 -hls_segment_type {8} -start_number {9} -hls_segment_filename \"{10}\" -hls_playlist_type vod -hls_list_size 0 -y \"{11}\"", inputModifier, _encodingHelper.GetInputArgument(state, encodingOptions), threads, @@ -1576,7 +1576,9 @@ namespace Jellyfin.Api.Controllers args += " " + gopArg; } else if (string.Equals(codec, "libx264", StringComparison.OrdinalIgnoreCase) - || string.Equals(codec, "libx265", StringComparison.OrdinalIgnoreCase)) + || string.Equals(codec, "libx265", StringComparison.OrdinalIgnoreCase) + || string.Equals(codec, "h264_vaapi", StringComparison.OrdinalIgnoreCase) + || string.Equals(codec, "hevc_vaapi", StringComparison.OrdinalIgnoreCase)) { args += " " + keyFrameArg; } diff --git a/Jellyfin.Api/Helpers/HlsCodecStringHelpers.cs b/Jellyfin.Api/Helpers/HlsCodecStringHelpers.cs index 3d7a9d9a0..4999fcc62 100644 --- a/Jellyfin.Api/Helpers/HlsCodecStringHelpers.cs +++ b/Jellyfin.Api/Helpers/HlsCodecStringHelpers.cs @@ -9,13 +9,38 @@ namespace Jellyfin.Api.Helpers /// public static class HlsCodecStringHelpers { + /// + /// Codec name for MP3. + /// + public const string MP3 = "mp4a.40.34"; + + /// + /// Codec name for AC-3. + /// + public const string AC3 = "mp4a.a5"; + + /// + /// Codec name for E-AC-3. + /// + public const string EAC3 = "mp4a.a6"; + + /// + /// Codec name for FLAC. + /// + public const string FLAC = "fLaC"; + + /// + /// Codec name for ALAC. + /// + public const string ALAC = "alac"; + /// /// Gets a MP3 codec string. /// /// MP3 codec string. public static string GetMP3String() { - return "mp4a.40.34"; + return MP3; } /// @@ -40,6 +65,42 @@ namespace Jellyfin.Api.Helpers return result.ToString(); } + /// + /// Gets an AC-3 codec string. + /// + /// AC-3 codec string. + public static string GetAC3String() + { + return AC3; + } + + /// + /// Gets an E-AC-3 codec string. + /// + /// E-AC-3 codec string. + public static string GetEAC3String() + { + return EAC3; + } + + /// + /// Gets an FLAC codec string. + /// + /// FLAC codec string. + public static string GetFLACString() + { + return FLAC; + } + + /// + /// Gets an ALAC codec string. + /// + /// ALAC codec string. + public static string GetALACString() + { + return ALAC; + } + /// /// Gets a H.264 codec string. /// @@ -104,41 +165,5 @@ namespace Jellyfin.Api.Helpers return result.ToString(); } - - /// - /// Gets an AC-3 codec string. - /// - /// AC-3 codec string. - public static string GetAC3String() - { - return "mp4a.a5"; - } - - /// - /// Gets an E-AC-3 codec string. - /// - /// E-AC-3 codec string. - public static string GetEAC3String() - { - return "mp4a.a6"; - } - - /// - /// Gets an FLAC codec string. - /// - /// FLAC codec string. - public static string GetFLACString() - { - return "fLaC"; - } - - /// - /// Gets an ALAC codec string. - /// - /// ALAC codec string. - public static string GetALACString() - { - return "alac"; - } } } diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 87670e2eb..6e348f1e6 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -662,6 +662,10 @@ namespace MediaBrowser.Controller.MediaEncoding if (string.Equals(state.ActualOutputVideoCodec, "hevc", StringComparison.OrdinalIgnoreCase) || string.Equals(state.ActualOutputVideoCodec, "h265", StringComparison.OrdinalIgnoreCase)) { + // Transcode to level 5.0 and lower for maximum compatibility + // level 5.0 is suitable for up to 4k 30fps hevc encoding, otherwise let the encoder to handle it + // https://en.wikipedia.org/wiki/High_Efficiency_Video_Coding_tiers_and_levels + // MaxLumaSampleRate = 3840*2160*30 = 248832000 < 267386880, if (requestLevel >= 150) { return "150"; @@ -3293,7 +3297,7 @@ namespace MediaBrowser.Controller.MediaEncoding if (state.RunTimeTicks.HasValue && state.BaseRequest.CopyTimestamps) { - args += " -copyts -avoid_negative_ts disabled -start_at_zero"; + args += " -copyts -avoid_negative_ts 0 -start_at_zero"; } if (!state.RunTimeTicks.HasValue) @@ -3341,7 +3345,7 @@ namespace MediaBrowser.Controller.MediaEncoding args += " -copyts"; } - args += " -avoid_negative_ts disabled"; + args += " -avoid_negative_ts 0"; if (!(state.SubtitleStream != null && state.SubtitleStream.IsExternal && !state.SubtitleStream.IsTextSubtitleStream)) { -- cgit v1.2.3 From 0b01acbe9172872f2685b115c219efd98619b4bc Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Wed, 11 Nov 2020 02:03:53 +0000 Subject: Apply suggestions from code review Co-authored-by: BaronGreenback --- Jellyfin.Api/Controllers/DynamicHlsController.cs | 4 +-- Jellyfin.Api/Helpers/DynamicHlsHelper.cs | 17 ++++++------ .../MediaEncoding/EncodingHelper.cs | 30 +++++++++++----------- 3 files changed, 26 insertions(+), 25 deletions(-) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index 6b1618421..4cf4c24d7 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -1565,7 +1565,7 @@ namespace Jellyfin.Api.Controllers args += " " + _encodingHelper.GetVideoQualityParam(state, codec, encodingOptions, "veryfast"); - // Unable to force key frames using these encoders, set key frames by GOP + // Unable to force key frames using these encoders, set key frames by GOP. if (string.Equals(codec, "h264_qsv", StringComparison.OrdinalIgnoreCase) || string.Equals(codec, "h264_nvenc", StringComparison.OrdinalIgnoreCase) || string.Equals(codec, "h264_amf", StringComparison.OrdinalIgnoreCase) @@ -1587,7 +1587,7 @@ namespace Jellyfin.Api.Controllers args += " " + keyFrameArg + gopArg; } - // Currenly b-frames in libx265 breaks the FMP4-HLS playback on iOS, disable it for now + // Currenly b-frames in libx265 breaks the FMP4-HLS playback on iOS, disable it for now. if (string.Equals(codec, "libx265", StringComparison.OrdinalIgnoreCase)) { args += " -bf 0"; diff --git a/Jellyfin.Api/Helpers/DynamicHlsHelper.cs b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs index 4999a582e..d6fa6e98d 100644 --- a/Jellyfin.Api/Helpers/DynamicHlsHelper.cs +++ b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs @@ -206,7 +206,7 @@ namespace Jellyfin.Api.Helpers if (state.VideoStream != null && state.VideoRequest != null) { - // Provide SDR HEVC entrance for backward compatibility + // Provide SDR HEVC entrance for backward compatibility. if (EncodingHelper.IsCopyCodec(state.OutputVideoCodec) && !string.IsNullOrEmpty(state.VideoStream.VideoRange) && string.Equals(state.VideoStream.VideoRange, "HDR", StringComparison.OrdinalIgnoreCase) @@ -215,7 +215,7 @@ namespace Jellyfin.Api.Helpers var requestedVideoProfiles = state.GetRequestedProfiles("hevc"); if (requestedVideoProfiles != null && requestedVideoProfiles.Length > 0) { - // Force HEVC Main Profile and disable video stream copy + // Force HEVC Main Profile and disable video stream copy. state.OutputVideoCodec = "hevc"; var sdrVideoUrl = ReplaceProfile(playlistUrl, "hevc", string.Join(",", requestedVideoProfiles), "main"); sdrVideoUrl += "&AllowVideoStreamCopy=false"; @@ -232,7 +232,7 @@ namespace Jellyfin.Api.Helpers } } - // Provide Level 5.0 entrance for backward compatibility + // Provide Level 5.0 entrance for backward compatibility. // e.g. Apple A10 chips refuse the master playlist containing SDR HEVC Main Level 5.1 video, // but in fact it is capable of playing videos up to Level 6.1. if (EncodingHelper.IsCopyCodec(state.OutputVideoCodec) @@ -245,13 +245,13 @@ namespace Jellyfin.Api.Helpers var playlistCodecsField = new StringBuilder(); AppendPlaylistCodecsField(playlistCodecsField, state); - // Force the video level to 5.0 + // Force the video level to 5.0. var originalLevel = state.VideoStream.Level; state.VideoStream.Level = 150; var newPlaylistCodecsField = new StringBuilder(); AppendPlaylistCodecsField(newPlaylistCodecsField, state); - // Restore the video level + // Restore the video level. state.VideoStream.Level = originalLevel; var newPlaylist = ReplacePlaylistCodecsField(basicPlaylist, playlistCodecsField, newPlaylistCodecsField); builder.Append(newPlaylist); @@ -333,7 +333,7 @@ namespace Jellyfin.Api.Helpers } else { - // Currently we only encode to SDR + // Currently we only encode to SDR. builder.Append(",VIDEO-RANGE=SDR"); } } @@ -693,9 +693,10 @@ namespace Jellyfin.Api.Helpers private string ReplaceProfile(string url, string codec, string oldValue, string newValue) { + string profileStr = codec + "-profile="; return url.Replace( - codec + "-profile=" + oldValue, - codec + "-profile=" + newValue, + profileStr + oldValue, + profileStr + newValue, StringComparison.OrdinalIgnoreCase); } diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 6e348f1e6..6255e1b61 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -662,10 +662,10 @@ namespace MediaBrowser.Controller.MediaEncoding if (string.Equals(state.ActualOutputVideoCodec, "hevc", StringComparison.OrdinalIgnoreCase) || string.Equals(state.ActualOutputVideoCodec, "h265", StringComparison.OrdinalIgnoreCase)) { - // Transcode to level 5.0 and lower for maximum compatibility - // level 5.0 is suitable for up to 4k 30fps hevc encoding, otherwise let the encoder to handle it + // Transcode to level 5.0 and lower for maximum compatibility. + // Level 5.0 is suitable for up to 4k 30fps hevc encoding, otherwise let the encoder to handle it. // https://en.wikipedia.org/wiki/High_Efficiency_Video_Coding_tiers_and_levels - // MaxLumaSampleRate = 3840*2160*30 = 248832000 < 267386880, + // MaxLumaSampleRate = 3840*2160*30 = 248832000 < 267386880. if (requestLevel >= 150) { return "150"; @@ -673,7 +673,7 @@ namespace MediaBrowser.Controller.MediaEncoding } else if (string.Equals(state.ActualOutputVideoCodec, "h264", StringComparison.OrdinalIgnoreCase)) { - // Clients may direct play higher than level 41, but there's no reason to transcode higher + // Clients may direct play higher than level 41, but there's no reason to transcode higher. if (requestLevel >= 41) { return "41"; @@ -843,7 +843,7 @@ namespace MediaBrowser.Controller.MediaEncoding else if (string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase) // h264 (h264_nvenc) || string.Equals(videoEncoder, "hevc_nvenc", StringComparison.OrdinalIgnoreCase)) // hevc (hevc_nvenc) { - // following preset will be deprecated in ffmpeg 4.4, use p1~p7 instead + // following preset will be deprecated in ffmpeg 4.4, use p1~p7 instead. switch (encodingOptions.EncoderPreset) { case "veryslow": @@ -976,7 +976,7 @@ namespace MediaBrowser.Controller.MediaEncoding var profile = state.GetRequestedProfiles(targetVideoCodec).FirstOrDefault(); profile = Regex.Replace(profile, @"\s+", String.Empty); - // only libx264 support encoding H264 High 10 Profile, otherwise force High Profile + // Only libx264 support encoding H264 High 10 Profile, otherwise force High Profile. if (!string.Equals(videoEncoder, "libx264", StringComparison.OrdinalIgnoreCase) && profile != null && profile.IndexOf("high 10", StringComparison.OrdinalIgnoreCase) != -1) @@ -985,7 +985,7 @@ namespace MediaBrowser.Controller.MediaEncoding } // h264_vaapi does not support Baseline profile, force Constrained Baseline in this case, - // which is compatible (and ugly) + // which is compatible (and ugly). if (string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase) && profile != null && profile.IndexOf("baseline", StringComparison.OrdinalIgnoreCase) != -1) @@ -993,7 +993,7 @@ namespace MediaBrowser.Controller.MediaEncoding profile = "constrained_baseline"; } - // libx264, h264_qsv and h264_nvenc does not support Constrained Baseline profile, force Baseline in this case + // libx264, h264_qsv and h264_nvenc does not support Constrained Baseline profile, force Baseline in this case. if ((string.Equals(videoEncoder, "libx264", StringComparison.OrdinalIgnoreCase) || string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) || string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase)) @@ -1003,7 +1003,7 @@ namespace MediaBrowser.Controller.MediaEncoding profile = "baseline"; } - // Currently hevc_amf only support encoding HEVC Main Profile, otherwise force Main Profile + // Currently hevc_amf only support encoding HEVC Main Profile, otherwise force Main Profile. if (!string.Equals(videoEncoder, "hevc_amf", StringComparison.OrdinalIgnoreCase) && profile != null && profile.IndexOf("main 10", StringComparison.OrdinalIgnoreCase) != -1) @@ -1027,7 +1027,7 @@ namespace MediaBrowser.Controller.MediaEncoding { level = NormalizeTranscodingLevel(state, level); - // libx264, QSV, AMF, VAAPI can adjust the given level to match the output + // libx264, QSV, AMF, VAAPI can adjust the given level to match the output. if (string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) || string.Equals(videoEncoder, "libx264", StringComparison.OrdinalIgnoreCase)) { @@ -1035,7 +1035,7 @@ namespace MediaBrowser.Controller.MediaEncoding } else if (string.Equals(videoEncoder, "hevc_qsv", StringComparison.OrdinalIgnoreCase)) { - // hevc_qsv use -level 51 instead of -level 153 + // hevc_qsv use -level 51 instead of -level 153. if (double.TryParse(level, NumberStyles.Any, _usCulture, out double hevcLevel)) { param += " -level " + hevcLevel / 3; @@ -1066,10 +1066,10 @@ namespace MediaBrowser.Controller.MediaEncoding if (string.Equals(videoEncoder, "libx265", StringComparison.OrdinalIgnoreCase)) { - // libx265 only accept level option in -x265-params - // level option may cause libx265 to fail - // libx265 cannot adjust the given level, just throw an error - // TODO: set fine tuned params + // libx265 only accept level option in -x265-params. + // level option may cause libx265 to fail. + // libx265 cannot adjust the given level, just throw an error. + // TODO: set fine tuned params. param += " -x265-params:0 no-info=1"; } -- cgit v1.2.3 From 57e5b59b93272bbbafeb1b57bdacc862c48f0996 Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Wed, 11 Nov 2020 17:08:50 +0800 Subject: adjust bitrate limit for HLS audio codecs --- Jellyfin.Api/Helpers/DynamicHlsHelper.cs | 2 +- Jellyfin.Api/Helpers/StreamingHelpers.cs | 49 ++++++--- .../MediaEncoding/EncodingHelper.cs | 50 +++++---- .../Probing/ProbeResultNormalizer.cs | 112 ++++++++++++++++++++- MediaBrowser.Model/Dlna/ResolutionNormalizer.cs | 8 +- MediaBrowser.Model/Dlna/StreamBuilder.cs | 72 +++++++++++-- MediaBrowser.Model/Dlna/StreamInfo.cs | 2 +- 7 files changed, 246 insertions(+), 49 deletions(-) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/Jellyfin.Api/Helpers/DynamicHlsHelper.cs b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs index d6fa6e98d..d2710bf40 100644 --- a/Jellyfin.Api/Helpers/DynamicHlsHelper.cs +++ b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs @@ -222,7 +222,7 @@ namespace Jellyfin.Api.Helpers EncodingHelper encodingHelper = new EncodingHelper(_mediaEncoder, _fileSystem, _subtitleEncoder, _configuration); var sdrOutputVideoBitrate = encodingHelper.GetVideoBitrateParamValue(state.VideoRequest, state.VideoStream, state.OutputVideoCodec) ?? 0; - var sdrOutputAudioBitrate = encodingHelper.GetAudioBitrateParam(state.VideoRequest.AudioBitRate, state.AudioStream) ?? 0; + var sdrOutputAudioBitrate = encodingHelper.GetAudioBitrateParam(state.VideoRequest, state.AudioStream) ?? 0; var sdrTotalBitrate = sdrOutputAudioBitrate + sdrOutputVideoBitrate; AppendPlaylist(builder, state, sdrVideoUrl, sdrTotalBitrate, subtitleGroup); diff --git a/Jellyfin.Api/Helpers/StreamingHelpers.cs b/Jellyfin.Api/Helpers/StreamingHelpers.cs index f4ec29bde..5bd347846 100644 --- a/Jellyfin.Api/Helpers/StreamingHelpers.cs +++ b/Jellyfin.Api/Helpers/StreamingHelpers.cs @@ -183,7 +183,7 @@ namespace Jellyfin.Api.Helpers state.OutputContainer = (containerInternal ?? string.Empty).TrimStart('.'); - state.OutputAudioBitrate = encodingHelper.GetAudioBitrateParam(streamingRequest.AudioBitRate, state.AudioStream); + state.OutputAudioBitrate = encodingHelper.GetAudioBitrateParam(streamingRequest.AudioBitRate, streamingRequest.AudioCodec, state.AudioStream); state.OutputAudioCodec = streamingRequest.AudioCodec; @@ -196,20 +196,41 @@ namespace Jellyfin.Api.Helpers encodingHelper.TryStreamCopy(state); - if (state.OutputVideoBitrate.HasValue && !EncodingHelper.IsCopyCodec(state.OutputVideoCodec)) + if (!EncodingHelper.IsCopyCodec(state.OutputVideoCodec) && state.OutputVideoBitrate.HasValue) { - var resolution = ResolutionNormalizer.Normalize( - state.VideoStream?.BitRate, - state.VideoStream?.Width, - state.VideoStream?.Height, - state.OutputVideoBitrate.Value, - state.VideoStream?.Codec, - state.OutputVideoCodec, - state.VideoRequest.MaxWidth, - state.VideoRequest.MaxHeight); - - state.VideoRequest.MaxWidth = resolution.MaxWidth; - state.VideoRequest.MaxHeight = resolution.MaxHeight; + var isVideoResolutionNotRequested = !state.VideoRequest.Width.HasValue + && !state.VideoRequest.Height.HasValue + && !state.VideoRequest.MaxWidth.HasValue + && !state.VideoRequest.MaxHeight.HasValue; + + if (isVideoResolutionNotRequested + && state.VideoRequest.VideoBitRate.HasValue + && state.VideoStream.BitRate.HasValue + && state.VideoRequest.VideoBitRate.Value >= state.VideoStream.BitRate.Value) + { + // Don't downscale the resolution if the width/height/MaxWidth/MaxHeight is not requested, + // and the requested video bitrate is higher than source video bitrate. + if (state.VideoStream.Width.HasValue || state.VideoStream.Height.HasValue) + { + state.VideoRequest.MaxWidth = state.VideoStream?.Width; + state.VideoRequest.MaxHeight = state.VideoStream?.Height; + } + } + else + { + var resolution = ResolutionNormalizer.Normalize( + state.VideoStream?.BitRate, + state.VideoStream?.Width, + state.VideoStream?.Height, + state.OutputVideoBitrate.Value, + state.VideoStream?.Codec, + state.OutputVideoCodec, + state.VideoRequest.MaxWidth, + state.VideoRequest.MaxHeight); + + state.VideoRequest.MaxWidth = resolution.MaxWidth; + state.VideoRequest.MaxHeight = resolution.MaxHeight; + } } } diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 6255e1b61..e8b4869ee 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -1390,7 +1390,7 @@ namespace MediaBrowser.Controller.MediaEncoding || string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase) || string.Equals(codec, "vp9", StringComparison.OrdinalIgnoreCase)) { - return .5; + return .6; } return 1; @@ -1424,36 +1424,48 @@ namespace MediaBrowser.Controller.MediaEncoding public int? GetAudioBitrateParam(BaseEncodingJobOptions request, MediaStream audioStream) { - if (audioStream == null) - { - return null; - } - - if (request.AudioBitRate.HasValue) - { - // Don't encode any higher than this - return Math.Min(384000, request.AudioBitRate.Value); - } - - // Empty bitrate area is not allow on iOS - // Default audio bitrate to 128K if it is not being requested - // https://ffmpeg.org/ffmpeg-codecs.html#toc-Codec-Options - return 128000; + return GetAudioBitrateParam(request.AudioBitRate, request.AudioCodec, audioStream); } - public int? GetAudioBitrateParam(int? audioBitRate, MediaStream audioStream) + public int? GetAudioBitrateParam(int? audioBitRate, string audioCodec, MediaStream audioStream) { if (audioStream == null) { return null; } - if (audioBitRate.HasValue) + if (audioBitRate.HasValue && string.IsNullOrEmpty(audioCodec)) { - // Don't encode any higher than this return Math.Min(384000, audioBitRate.Value); } + if (audioBitRate.HasValue && !string.IsNullOrEmpty(audioCodec)) + { + if (string.Equals(audioCodec, "aac", StringComparison.OrdinalIgnoreCase) + || string.Equals(audioCodec, "mp3", StringComparison.OrdinalIgnoreCase) + || string.Equals(audioCodec, "ac3", StringComparison.OrdinalIgnoreCase) + || string.Equals(audioCodec, "eac3", StringComparison.OrdinalIgnoreCase)) + { + if ((audioStream.Channels ?? 0) >= 6) + { + return Math.Min(640000, audioBitRate.Value); + } + + return Math.Min(384000, audioBitRate.Value); + } + + if (string.Equals(audioCodec, "flac", StringComparison.OrdinalIgnoreCase) + || string.Equals(audioCodec, "alac", StringComparison.OrdinalIgnoreCase)) + { + if ((audioStream.Channels ?? 0) >= 6) + { + return Math.Min(3584000, audioBitRate.Value); + } + + return Math.Min(1536000, audioBitRate.Value); + } + } + // Empty bitrate area is not allow on iOS // Default audio bitrate to 128K if it is not being requested // https://ffmpeg.org/ffmpeg-codecs.html#toc-Codec-Options diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index cdeefbbbd..1b0d4d1af 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs @@ -234,8 +234,8 @@ namespace MediaBrowser.MediaEncoding.Probing var channelsValue = channels.Value; - if (string.Equals(codec, "aac", StringComparison.OrdinalIgnoreCase) || - string.Equals(codec, "mp3", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(codec, "aac", StringComparison.OrdinalIgnoreCase) + || string.Equals(codec, "mp3", StringComparison.OrdinalIgnoreCase)) { if (channelsValue <= 2) { @@ -248,6 +248,34 @@ namespace MediaBrowser.MediaEncoding.Probing } } + if (string.Equals(codec, "ac3", StringComparison.OrdinalIgnoreCase) + || string.Equals(codec, "eac3", StringComparison.OrdinalIgnoreCase)) + { + if (channelsValue <= 2) + { + return 192000; + } + + if (channelsValue >= 5) + { + return 640000; + } + } + + if (string.Equals(codec, "flac", StringComparison.OrdinalIgnoreCase) + || string.Equals(codec, "alac", StringComparison.OrdinalIgnoreCase)) + { + if (channelsValue <= 2) + { + return 960000; + } + + if (channelsValue >= 5) + { + return 2880000; + } + } + return null; } @@ -774,6 +802,35 @@ namespace MediaBrowser.MediaEncoding.Probing stream.BitRate = bitrate; } + // Extract bitrate info from tag "BPS" if possible. + if (!stream.BitRate.HasValue + && (string.Equals(streamInfo.CodecType, "audio", StringComparison.OrdinalIgnoreCase) + || string.Equals(streamInfo.CodecType, "video", StringComparison.OrdinalIgnoreCase))) + { + var bps = GetBPSFromTags(streamInfo); + if (bps != null && bps > 0) + { + stream.BitRate = bps; + } + } + + // Get average bitrate info from tag "NUMBER_OF_BYTES" and "DURATION" if possible. + if (!stream.BitRate.HasValue + && (string.Equals(streamInfo.CodecType, "audio", StringComparison.OrdinalIgnoreCase) + || string.Equals(streamInfo.CodecType, "video", StringComparison.OrdinalIgnoreCase))) + { + var durationInSeconds = GetRuntimeSecondsFromTags(streamInfo); + var bytes = GetNumberOfBytesFromTags(streamInfo); + if (durationInSeconds != null && bytes != null) + { + var bps = Convert.ToInt32(bytes * 8 / durationInSeconds); + if (bps > 0) + { + stream.BitRate = bps; + } + } + } + var disposition = streamInfo.Disposition; if (disposition != null) { @@ -963,6 +1020,57 @@ namespace MediaBrowser.MediaEncoding.Probing } } + private int? GetBPSFromTags(MediaStreamInfo streamInfo) + { + if (streamInfo != null && streamInfo.Tags != null) + { + var bps = GetDictionaryValue(streamInfo.Tags, "BPS-eng") ?? GetDictionaryValue(streamInfo.Tags, "BPS"); + if (!string.IsNullOrEmpty(bps)) + { + if (int.TryParse(bps, NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsedBps)) + { + return parsedBps; + } + } + } + + return null; + } + + private double? GetRuntimeSecondsFromTags(MediaStreamInfo streamInfo) + { + if (streamInfo != null && streamInfo.Tags != null) + { + var duration = GetDictionaryValue(streamInfo.Tags, "DURATION-eng") ?? GetDictionaryValue(streamInfo.Tags, "DURATION"); + if (!string.IsNullOrEmpty(duration)) + { + if (TimeSpan.TryParse(duration, out var parsedDuration)) + { + return parsedDuration.TotalSeconds; + } + } + } + + return null; + } + + private long? GetNumberOfBytesFromTags(MediaStreamInfo streamInfo) + { + if (streamInfo != null && streamInfo.Tags != null) + { + var numberOfBytes = GetDictionaryValue(streamInfo.Tags, "NUMBER_OF_BYTES-eng") ?? GetDictionaryValue(streamInfo.Tags, "NUMBER_OF_BYTES"); + if (!string.IsNullOrEmpty(numberOfBytes)) + { + if (long.TryParse(numberOfBytes, NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsedBytes)) + { + return parsedBytes; + } + } + } + + return null; + } + private void SetSize(InternalMediaInfoResult data, MediaInfo info) { if (data.Format != null) diff --git a/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs b/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs index 102db3b44..39439f1fa 100644 --- a/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs +++ b/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs @@ -79,11 +79,11 @@ namespace MediaBrowser.Model.Dlna private static double GetVideoBitrateScaleFactor(string codec) { - if (string.Equals(codec, "h265", StringComparison.OrdinalIgnoreCase) || - string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase) || - string.Equals(codec, "vp9", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(codec, "h265", StringComparison.OrdinalIgnoreCase) + || string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase) + || string.Equals(codec, "vp9", StringComparison.OrdinalIgnoreCase)) { - return .5; + return .6; } return 1; diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index 4959a9b92..2e99fe5bf 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -872,11 +872,34 @@ namespace MediaBrowser.Model.Dlna return playlistItem; } - private static int GetDefaultAudioBitrateIfUnknown(MediaStream audioStream) + private static int GetDefaultAudioBitrate(string audioCodec, int? audioChannels) { - if ((audioStream.Channels ?? 0) >= 6) + if (!string.IsNullOrEmpty(audioCodec)) { - return 384000; + // Default to a higher bitrate for stream copy + if (string.Equals(audioCodec, "aac", StringComparison.OrdinalIgnoreCase) + || string.Equals(audioCodec, "mp3", StringComparison.OrdinalIgnoreCase) + || string.Equals(audioCodec, "ac3", StringComparison.OrdinalIgnoreCase) + || string.Equals(audioCodec, "eac3", StringComparison.OrdinalIgnoreCase)) + { + if ((audioChannels ?? 0) < 2) + { + return 128000; + } + + return (audioChannels ?? 0) >= 6 ? 640000 : 384000; + } + + if (string.Equals(audioCodec, "flac", StringComparison.OrdinalIgnoreCase) + || string.Equals(audioCodec, "alac", StringComparison.OrdinalIgnoreCase)) + { + if ((audioChannels ?? 0) < 2) + { + return 768000; + } + + return (audioChannels ?? 0) >= 6 ? 3584000 : 1536000; + } } return 192000; @@ -897,14 +920,27 @@ namespace MediaBrowser.Model.Dlna } else { - if (targetAudioChannels.HasValue && audioStream.Channels.HasValue && targetAudioChannels.Value < audioStream.Channels.Value) + if (targetAudioChannels.HasValue + && audioStream.Channels.HasValue + && audioStream.Channels.Value > targetAudioChannels.Value) { - // Reduce the bitrate if we're downmixing - defaultBitrate = targetAudioChannels.Value < 2 ? 128000 : 192000; + // Reduce the bitrate if we're downmixing. + defaultBitrate = GetDefaultAudioBitrate(targetAudioCodec, targetAudioChannels); + } + else if (targetAudioChannels.HasValue + && audioStream.Channels.HasValue + && audioStream.Channels.Value <= targetAudioChannels.Value + && !string.IsNullOrEmpty(audioStream.Codec) + && targetAudioCodecs != null + && targetAudioCodecs.Length > 0 + && !Array.Exists(targetAudioCodecs, elem => string.Equals(audioStream.Codec, elem, StringComparison.OrdinalIgnoreCase))) + { + // Shift the bitrate if we're transcoding to a different audio codec. + defaultBitrate = GetDefaultAudioBitrate(targetAudioCodec, audioStream.Channels.Value); } else { - defaultBitrate = audioStream.BitRate ?? GetDefaultAudioBitrateIfUnknown(audioStream); + defaultBitrate = audioStream.BitRate ?? GetDefaultAudioBitrate(targetAudioCodec, targetAudioChannels); } // Seeing webm encoding failures when source has 1 audio channel and 22k bitrate. @@ -938,8 +974,28 @@ namespace MediaBrowser.Model.Dlna { return 448000; } + else if (totalBitrate <= 4000000) + { + return 640000; + } + else if (totalBitrate <= 5000000) + { + return 768000; + } + else if (totalBitrate <= 10000000) + { + return 1536000; + } + else if (totalBitrate <= 15000000) + { + return 2304000; + } + else if (totalBitrate <= 20000000) + { + return 3584000; + } - return 640000; + return 7168000; } private (PlayMethod?, List) GetVideoDirectPlayProfile( diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs index 9399d21f1..7b72a1303 100644 --- a/MediaBrowser.Model/Dlna/StreamInfo.cs +++ b/MediaBrowser.Model/Dlna/StreamInfo.cs @@ -787,7 +787,7 @@ namespace MediaBrowser.Model.Dlna public int? GetTargetAudioChannels(string codec) { - var defaultValue = GlobalMaxAudioChannels; + var defaultValue = GlobalMaxAudioChannels ?? TranscodingMaxAudioChannels; var value = GetOption(codec, "audiochannels"); if (string.IsNullOrEmpty(value)) -- cgit v1.2.3 From 11c74cb65cd936f0fece57357beb7a27741edc24 Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Wed, 11 Nov 2020 19:04:58 +0800 Subject: fix for no audio stream video --- Jellyfin.Api/Controllers/DynamicHlsController.cs | 5 +++++ MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 5 +++++ MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs | 10 ++++++++++ 3 files changed, 20 insertions(+) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index 4cf4c24d7..0581928a0 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -1429,6 +1429,11 @@ namespace Jellyfin.Api.Controllers private string GetAudioArguments(StreamState state, EncodingOptions encodingOptions) { + if (state.AudioStream == null) + { + return string.Empty; + } + var audioCodec = _encodingHelper.GetAudioEncoder(state); if (!state.IsOutputVideo) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index e8b4869ee..caf3ef169 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -1518,6 +1518,11 @@ namespace MediaBrowser.Controller.MediaEncoding /// System.Nullable{System.Int32}. public int? GetNumAudioChannelsParam(EncodingJobInfo state, MediaStream audioStream, string outputAudioCodec) { + if (audioStream == null) + { + return null; + } + var request = state.BaseRequest; var inputChannels = audioStream?.Channels; diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs index 6e9362cd1..09e7889b9 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs @@ -593,6 +593,11 @@ namespace MediaBrowser.Controller.MediaEncoding { get { + if (VideoStream == null) + { + return null; + } + if (EncodingHelper.IsCopyCodec(OutputVideoCodec)) { return VideoStream?.Codec; @@ -606,6 +611,11 @@ namespace MediaBrowser.Controller.MediaEncoding { get { + if (AudioStream == null) + { + return null; + } + if (EncodingHelper.IsCopyCodec(OutputAudioCodec)) { return AudioStream?.Codec; -- cgit v1.2.3 From 5bd0c2b69d0f4fccd09866a67c741742710372dc Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Thu, 12 Nov 2020 11:02:56 +0800 Subject: add an option to disable hevc encoding --- Jellyfin.Api/Helpers/StreamingHelpers.cs | 4 +- Jellyfin.Api/Helpers/TranscodingJobHelper.cs | 3 +- .../MediaEncoding/EncodingHelper.cs | 43 +++++++++++++++++++++- .../Configuration/EncodingOptions.cs | 3 ++ 4 files changed, 50 insertions(+), 3 deletions(-) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/Jellyfin.Api/Helpers/StreamingHelpers.cs b/Jellyfin.Api/Helpers/StreamingHelpers.cs index 5bd347846..be85d5eb8 100644 --- a/Jellyfin.Api/Helpers/StreamingHelpers.cs +++ b/Jellyfin.Api/Helpers/StreamingHelpers.cs @@ -165,7 +165,9 @@ namespace Jellyfin.Api.Helpers state.DirectStreamProvider = liveStreamInfo.Item2; } - encodingHelper.AttachMediaSourceInfo(state, mediaSource, url); + var encodingOptions = serverConfigurationManager.GetEncodingOptions(); + + encodingHelper.AttachMediaSourceInfo(state, encodingOptions, mediaSource, url); string? containerInternal = Path.GetExtension(state.RequestedUrl); diff --git a/Jellyfin.Api/Helpers/TranscodingJobHelper.cs b/Jellyfin.Api/Helpers/TranscodingJobHelper.cs index 0db1fabff..f93c95fb5 100644 --- a/Jellyfin.Api/Helpers/TranscodingJobHelper.cs +++ b/Jellyfin.Api/Helpers/TranscodingJobHelper.cs @@ -770,8 +770,9 @@ namespace Jellyfin.Api.Helpers new LiveStreamRequest { OpenToken = state.MediaSource.OpenToken }, cancellationTokenSource.Token) .ConfigureAwait(false); + var encodingOptions = _serverConfigurationManager.GetEncodingOptions(); - _encodingHelper.AttachMediaSourceInfo(state, liveStreamResponse.MediaSource, state.RequestedUrl); + _encodingHelper.AttachMediaSourceInfo(state, encodingOptions, liveStreamResponse.MediaSource, state.RequestedUrl); if (state.VideoRequest != null) { diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index caf3ef169..1074f876c 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -2631,6 +2631,7 @@ namespace MediaBrowser.Controller.MediaEncoding public void AttachMediaSourceInfo( EncodingJobInfo state, + EncodingOptions encodingOptions, MediaSourceInfo mediaSource, string requestedUrl) { @@ -2761,11 +2762,23 @@ namespace MediaBrowser.Controller.MediaEncoding request.AudioCodec = state.SupportedAudioCodecs.FirstOrDefault(i => _mediaEncoder.CanEncodeToAudioCodec(i)) ?? state.SupportedAudioCodecs.FirstOrDefault(); } + + var supportedVideoCodecs = state.SupportedVideoCodecs; + if (request != null && supportedVideoCodecs != null && supportedVideoCodecs.Length > 0) + { + var supportedVideoCodecsList = supportedVideoCodecs.ToList(); + + ShiftVideoCodecsIfNeeded(supportedVideoCodecsList, encodingOptions); + + state.SupportedVideoCodecs = supportedVideoCodecsList.ToArray(); + + request.VideoCodec = state.SupportedVideoCodecs.FirstOrDefault(); + } } private void ShiftAudioCodecsIfNeeded(List audioCodecs, MediaStream audioStream) { - // Nothing to do here + // No need to shift if there is only one supported audio codec. if (audioCodecs.Count < 2) { return; @@ -2793,6 +2806,34 @@ namespace MediaBrowser.Controller.MediaEncoding } } + private void ShiftVideoCodecsIfNeeded(List videoCodecs, EncodingOptions encodingOptions) + { + // Shift hevc/h265 to the end of list if hevc encoding is not allowed. + if (encodingOptions.AllowHevcEncoding) + { + return; + } + + // No need to shift if there is only one supported video codec. + if (videoCodecs.Count < 2) + { + return; + } + + var shiftVideoCodecs = new[] { "hevc", "h265" }; + if (videoCodecs.All(i => shiftVideoCodecs.Contains(i, StringComparer.OrdinalIgnoreCase))) + { + return; + } + + while (shiftVideoCodecs.Contains(videoCodecs[0], StringComparer.OrdinalIgnoreCase)) + { + var removed = shiftVideoCodecs[0]; + videoCodecs.RemoveAt(0); + videoCodecs.Add(removed); + } + } + private void NormalizeSubtitleEmbed(EncodingJobInfo state) { if (state.SubtitleStream == null || state.SubtitleDeliveryMethod != SubtitleDeliveryMethod.Embed) diff --git a/MediaBrowser.Model/Configuration/EncodingOptions.cs b/MediaBrowser.Model/Configuration/EncodingOptions.cs index 2cd637c5b..2d4688972 100644 --- a/MediaBrowser.Model/Configuration/EncodingOptions.cs +++ b/MediaBrowser.Model/Configuration/EncodingOptions.cs @@ -63,6 +63,8 @@ namespace MediaBrowser.Model.Configuration public bool EnableHardwareEncoding { get; set; } + public bool AllowHevcEncoding { get; set; } + public bool EnableSubtitleExtraction { get; set; } public string[] HardwareDecodingCodecs { get; set; } @@ -94,6 +96,7 @@ namespace MediaBrowser.Model.Configuration EnableDecodingColorDepth10Hevc = true; EnableDecodingColorDepth10Vp9 = true; EnableHardwareEncoding = true; + AllowHevcEncoding = true; EnableSubtitleExtraction = true; HardwareDecodingCodecs = new string[] { "h264", "vc1" }; } -- cgit v1.2.3 From d91a099c9e04cb39d1b8e387dba0b44dde64342d Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Thu, 12 Nov 2020 23:10:57 +0800 Subject: allow transcoding 8ch(7.1 layout) in aac --- 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 1074f876c..1ae896df0 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -1545,6 +1545,11 @@ namespace MediaBrowser.Controller.MediaEncoding // libmp3lame currently only supports two channel output transcoderChannelLimit = 2; } + else if (codec.IndexOf("aac", StringComparison.OrdinalIgnoreCase) != -1) + { + // aac is able to handle 8ch(7.1 layout) + transcoderChannelLimit = 8; + } else { // If we don't have any media info then limit it to 6 to prevent encoding errors due to asking for too many channels -- cgit v1.2.3 From 536b0548730466d7b1e51178146c004af8801ae8 Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Sat, 14 Nov 2020 04:06:24 +0800 Subject: add experimental flag for flac --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 8 +++++++- 1 file changed, 7 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 1ae896df0..0bc5308b6 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -64,7 +64,7 @@ namespace MediaBrowser.Controller.MediaEncoding { // 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. + // Since transcoding of folder rips is experimental anyway, it's not worth adding additional variables such as this. if (state.VideoType == VideoType.VideoFile) { var hwType = encodingOptions.HardwareAccelerationType; @@ -441,6 +441,12 @@ namespace MediaBrowser.Controller.MediaEncoding return "libopus"; } + if (string.Equals(codec, "flac", StringComparison.OrdinalIgnoreCase)) + { + // flac is experimental in mp4 muxer + return "flac -strict -2"; + } + return codec.ToLowerInvariant(); } -- cgit v1.2.3 From 32bb73acbb071a8329c485738176711dac4e1bcb Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Sat, 14 Nov 2020 14:22:15 +0800 Subject: add aac_adtstoasc bitstream filter for mpegts to mp4 conversion --- Jellyfin.Api/Controllers/DynamicHlsController.cs | 29 +++++++++++++++++++--- Jellyfin.Api/Controllers/VideoHlsController.cs | 28 +++++++++++++++++++-- .../MediaEncoding/EncodingHelper.cs | 16 ++++++++++-- 3 files changed, 66 insertions(+), 7 deletions(-) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index d4ae124b9..10c2d3fec 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -1444,7 +1444,19 @@ namespace Jellyfin.Api.Controllers { if (EncodingHelper.IsCopyCodec(audioCodec)) { - return "-acodec copy -strict -2"; + var segmentFormat = HlsHelpers.GetSegmentFileExtension(state.Request.SegmentContainer).TrimStart('.'); + var bitStreamArgs = string.Empty; + + // Apply aac_adtstoasc bitstream filter when media source is in mpegts. + if (string.Equals(segmentFormat, "mp4", StringComparison.OrdinalIgnoreCase) + && (string.Equals(state.MediaSource.Container, "mpegts", StringComparison.OrdinalIgnoreCase) + || string.Equals(state.MediaSource.Container, "hls", StringComparison.OrdinalIgnoreCase))) + { + bitStreamArgs = _encodingHelper.GetBitStreamArgs(state.AudioStream); + bitStreamArgs = !string.IsNullOrEmpty(bitStreamArgs) ? " " + bitStreamArgs : string.Empty; + } + + return "-acodec copy -strict -2" + bitStreamArgs; } var audioTranscodeParams = new List(); @@ -1473,13 +1485,24 @@ namespace Jellyfin.Api.Controllers if (EncodingHelper.IsCopyCodec(audioCodec)) { var videoCodec = _encodingHelper.GetVideoEncoder(state, _encodingOptions); + var segmentFormat = HlsHelpers.GetSegmentFileExtension(state.Request.SegmentContainer).TrimStart('.'); + var bitStreamArgs = string.Empty; + + // Apply aac_adtstoasc bitstream filter when media source is in mpegts. + if (string.Equals(segmentFormat, "mp4", StringComparison.OrdinalIgnoreCase) + && (string.Equals(state.MediaSource.Container, "mpegts", StringComparison.OrdinalIgnoreCase) + || string.Equals(state.MediaSource.Container, "hls", StringComparison.OrdinalIgnoreCase))) + { + bitStreamArgs = _encodingHelper.GetBitStreamArgs(state.AudioStream); + bitStreamArgs = !string.IsNullOrEmpty(bitStreamArgs) ? " " + bitStreamArgs : string.Empty; + } if (EncodingHelper.IsCopyCodec(videoCodec) && state.EnableBreakOnNonKeyFrames(videoCodec)) { - return "-codec:a:0 copy -strict -2 -copypriorss:a:0 0"; + return "-codec:a:0 copy -strict -2 -copypriorss:a:0 0" + bitStreamArgs; } - return "-codec:a:0 copy -strict -2"; + return "-codec:a:0 copy -strict -2" + bitStreamArgs; } var args = "-codec:a:0 " + audioCodec; diff --git a/Jellyfin.Api/Controllers/VideoHlsController.cs b/Jellyfin.Api/Controllers/VideoHlsController.cs index 7b50b47b1..21c042f4b 100644 --- a/Jellyfin.Api/Controllers/VideoHlsController.cs +++ b/Jellyfin.Api/Controllers/VideoHlsController.cs @@ -439,7 +439,19 @@ namespace Jellyfin.Api.Controllers { if (EncodingHelper.IsCopyCodec(audioCodec)) { - return "-acodec copy -strict -2"; + var segmentFormat = HlsHelpers.GetSegmentFileExtension(state.Request.SegmentContainer).TrimStart('.'); + var bitStreamArgs = string.Empty; + + // Apply aac_adtstoasc bitstream filter when media source is in mpegts. + if (string.Equals(segmentFormat, "mp4", StringComparison.OrdinalIgnoreCase) + && (string.Equals(state.MediaSource.Container, "mpegts", StringComparison.OrdinalIgnoreCase) + || string.Equals(state.MediaSource.Container, "hls", StringComparison.OrdinalIgnoreCase))) + { + bitStreamArgs = _encodingHelper.GetBitStreamArgs(state.AudioStream); + bitStreamArgs = !string.IsNullOrEmpty(bitStreamArgs) ? " " + bitStreamArgs : string.Empty; + } + + return "-acodec copy -strict -2" + bitStreamArgs; } var audioTranscodeParams = new List(); @@ -467,7 +479,19 @@ namespace Jellyfin.Api.Controllers if (EncodingHelper.IsCopyCodec(audioCodec)) { - return "-codec:a:0 copy -strict -2"; + var segmentFormat = HlsHelpers.GetSegmentFileExtension(state.Request.SegmentContainer).TrimStart('.'); + var bitStreamArgs = string.Empty; + + // Apply aac_adtstoasc bitstream filter when media source is in mpegts. + if (string.Equals(segmentFormat, "mp4", StringComparison.OrdinalIgnoreCase) + && (string.Equals(state.MediaSource.Container, "mpegts", StringComparison.OrdinalIgnoreCase) + || string.Equals(state.MediaSource.Container, "hls", StringComparison.OrdinalIgnoreCase))) + { + bitStreamArgs = _encodingHelper.GetBitStreamArgs(state.AudioStream); + bitStreamArgs = !string.IsNullOrEmpty(bitStreamArgs) ? " " + bitStreamArgs : string.Empty; + } + + return "-acodec copy -strict -2" + bitStreamArgs; } var args = "-codec:a:0 " + audioCodec; diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 0bc5308b6..a8252d473 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -596,10 +596,17 @@ namespace MediaBrowser.Controller.MediaEncoding || codec.IndexOf("hevc", StringComparison.OrdinalIgnoreCase) != -1; } - // TODO This is auto inserted into the mpegts mux so it might not be needed - // https://www.ffmpeg.org/ffmpeg-bitstream-filters.html#h264_005fmp4toannexb + public bool IsAAC(MediaStream stream) + { + var codec = stream.Codec ?? string.Empty; + + return codec.IndexOf("aac", StringComparison.OrdinalIgnoreCase) != -1; + } + public string GetBitStreamArgs(MediaStream stream) { + // TODO This is auto inserted into the mpegts mux so it might not be needed + // https://www.ffmpeg.org/ffmpeg-bitstream-filters.html#h264_005fmp4toannexb if (IsH264(stream)) { return "-bsf:v h264_mp4toannexb"; @@ -608,6 +615,11 @@ namespace MediaBrowser.Controller.MediaEncoding { return "-bsf:v hevc_mp4toannexb"; } + else if (IsAAC(stream)) + { + // convert adts header(mpegts) to asc header(mp4) + return "-bsf:a aac_adtstoasc"; + } else { return null; -- cgit v1.2.3 From 06670351ae6be1d3c47a25fe14b2f253cc6ab3e7 Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Sat, 14 Nov 2020 10:19:41 +0000 Subject: Apply suggestions from code review Co-authored-by: BaronGreenback --- Jellyfin.Api/Controllers/DynamicHlsController.cs | 4 ++-- Jellyfin.Api/Controllers/VideoHlsController.cs | 2 +- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index 643c6073c..625275171 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -1453,7 +1453,7 @@ namespace Jellyfin.Api.Controllers || string.Equals(state.MediaSource.Container, "hls", StringComparison.OrdinalIgnoreCase))) { bitStreamArgs = _encodingHelper.GetBitStreamArgs(state.AudioStream); - bitStreamArgs = !string.IsNullOrEmpty(bitStreamArgs) ? " " + bitStreamArgs : string.Empty; + bitStreamArgs = string.IsNullOrEmpty(bitStreamArgs) ? string.Empty : " " + bitStreamArgs; } return "-acodec copy -strict -2" + bitStreamArgs; @@ -1494,7 +1494,7 @@ namespace Jellyfin.Api.Controllers || string.Equals(state.MediaSource.Container, "hls", StringComparison.OrdinalIgnoreCase))) { bitStreamArgs = _encodingHelper.GetBitStreamArgs(state.AudioStream); - bitStreamArgs = !string.IsNullOrEmpty(bitStreamArgs) ? " " + bitStreamArgs : string.Empty; + bitStreamArgs = string.IsNullOrEmpty(bitStreamArgs) ? string.Empty : " " + bitStreamArgs; } if (EncodingHelper.IsCopyCodec(videoCodec) && state.EnableBreakOnNonKeyFrames(videoCodec)) diff --git a/Jellyfin.Api/Controllers/VideoHlsController.cs b/Jellyfin.Api/Controllers/VideoHlsController.cs index 4ae61fbd3..93bb3a903 100644 --- a/Jellyfin.Api/Controllers/VideoHlsController.cs +++ b/Jellyfin.Api/Controllers/VideoHlsController.cs @@ -448,7 +448,7 @@ namespace Jellyfin.Api.Controllers || string.Equals(state.MediaSource.Container, "hls", StringComparison.OrdinalIgnoreCase))) { bitStreamArgs = _encodingHelper.GetBitStreamArgs(state.AudioStream); - bitStreamArgs = !string.IsNullOrEmpty(bitStreamArgs) ? " " + bitStreamArgs : string.Empty; + bitStreamArgs = string.IsNullOrEmpty(bitStreamArgs) ? string.Empty : " " + bitStreamArgs;; } return "-acodec copy -strict -2" + bitStreamArgs; diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index a8252d473..b181a6ee6 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -605,7 +605,7 @@ namespace MediaBrowser.Controller.MediaEncoding public string GetBitStreamArgs(MediaStream stream) { - // TODO This is auto inserted into the mpegts mux so it might not be needed + // TODO This is auto inserted into the mpegts mux so it might not be needed. // https://www.ffmpeg.org/ffmpeg-bitstream-filters.html#h264_005fmp4toannexb if (IsH264(stream)) { @@ -617,7 +617,7 @@ namespace MediaBrowser.Controller.MediaEncoding } else if (IsAAC(stream)) { - // convert adts header(mpegts) to asc header(mp4) + // Convert adts header(mpegts) to asc header(mp4). return "-bsf:a aac_adtstoasc"; } else -- cgit v1.2.3 From d7cdaeea7dfc7dd556765a0fb224d8428bc38c0b Mon Sep 17 00:00:00 2001 From: Fernando Fernández Date: Tue, 17 Nov 2020 14:26:05 +0100 Subject: Use all system cores when threads are set to auto or bigger than the amount of system's cores --- 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 5846a603a..3a9f57c55 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -2344,7 +2344,7 @@ namespace MediaBrowser.Controller.MediaEncoding // Automatic if (threads <= 0 || threads >= Environment.ProcessorCount) { - return 0; + return Environment.ProcessorCount; } return threads; -- cgit v1.2.3 From 38c3b6fcd37d71d09c6afe26ad26c762850f9971 Mon Sep 17 00:00:00 2001 From: Fernando Fernández Date: Wed, 18 Nov 2020 10:01:03 +0100 Subject: Fix build and thread detection logic --- Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs | 2 +- .../LiveTv/EmbyTV/EncodedRecorder.cs | 1 + Jellyfin.Api/Controllers/DynamicHlsController.cs | 2 +- Jellyfin.Api/Controllers/VideoHlsController.cs | 2 +- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 13 ++++++++----- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 16 ++++++++-------- 6 files changed, 20 insertions(+), 16 deletions(-) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index fcc2d1eeb..0dc045ee6 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -1635,7 +1635,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { if (mediaSource.RequiresLooping || !(mediaSource.Container ?? string.Empty).EndsWith("ts", StringComparison.OrdinalIgnoreCase) || (mediaSource.Protocol != MediaProtocol.File && mediaSource.Protocol != MediaProtocol.Http)) { - return new EncodedRecorder(_logger, _mediaEncoder, _config.ApplicationPaths, _jsonSerializer); + return new EncodedRecorder(_logger, _mediaEncoder, _config.ApplicationPaths, _jsonSerializer, _config); } return new DirectRecorder(_logger, _httpClientFactory, _streamHelper); diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs index 4726211d2..e6ee9819e 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs @@ -8,6 +8,7 @@ using System.IO; using System.Text; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Common.Configuration; using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Library; diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index 6e59da798..bba5e972e 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -1329,7 +1329,7 @@ namespace Jellyfin.Api.Controllers { var videoCodec = _encodingHelper.GetVideoEncoder(state, encodingOptions); - var threads = _encodingHelper.GetNumberOfThreads(state, encodingOptions, videoCodec); + var threads = EncodingHelper.GetNumberOfThreads(state, encodingOptions, videoCodec); // GetNumberOfThreads is static if (state.BaseRequest.BreakOnNonKeyFrames) { diff --git a/Jellyfin.Api/Controllers/VideoHlsController.cs b/Jellyfin.Api/Controllers/VideoHlsController.cs index 389dc8a08..065fa786e 100644 --- a/Jellyfin.Api/Controllers/VideoHlsController.cs +++ b/Jellyfin.Api/Controllers/VideoHlsController.cs @@ -359,7 +359,7 @@ namespace Jellyfin.Api.Controllers private string GetCommandLineArguments(string outputPath, StreamState state) { var videoCodec = _encodingHelper.GetVideoEncoder(state, _encodingOptions); - var threads = _encodingHelper.GetNumberOfThreads(state, _encodingOptions, videoCodec); + var threads = EncodingHelper.GetNumberOfThreads(state, _encodingOptions, videoCodec); // GetNumberOfThreads is static var inputModifier = _encodingHelper.GetInputModifier(state, _encodingOptions); var format = !string.IsNullOrWhiteSpace(state.Request.SegmentContainer) ? "." + state.Request.SegmentContainer : ".ts"; var directory = Path.GetDirectoryName(outputPath) ?? throw new ArgumentException($"Provided path ({outputPath}) is not valid.", nameof(outputPath)); diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 3a9f57c55..60c44cfcf 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -2329,20 +2329,23 @@ namespace MediaBrowser.Controller.MediaEncoding /// /// Gets the number of threads. /// - public int GetNumberOfThreads(EncodingJobInfo state, EncodingOptions encodingOptions, string outputVideoCodec) + public static int GetNumberOfThreads(EncodingJobInfo state, EncodingOptions encodingOptions, string outputVideoCodec) { - if (string.Equals(outputVideoCodec, "libvpx", StringComparison.OrdinalIgnoreCase)) + if (outputVideoCodec != null && string.Equals(outputVideoCodec, "libvpx", StringComparison.OrdinalIgnoreCase)) { // per docs: // -threads number of threads to use for encoding, can't be 0 [auto] with VP8 // (recommended value : number of real cores - 1) return Math.Max(Environment.ProcessorCount - 1, 1); } - - var threads = state.BaseRequest.CpuCoreLimit ?? encodingOptions.EncodingThreadCount; + var threads = state?.BaseRequest.CpuCoreLimit ?? encodingOptions.EncodingThreadCount; // Automatic - if (threads <= 0 || threads >= Environment.ProcessorCount) + if (threads <= 0) + { + return 0; + } + else if (threads >= Environment.ProcessorCount) { return Environment.ProcessorCount; } diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 2b4712b2d..5f60c09ae 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -524,26 +524,26 @@ namespace MediaBrowser.MediaEncoding.Encoder // apply some filters to thumbnail extracted below (below) crop any black lines that we made and get the correct ar. // This filter chain may have adverse effects on recorded tv thumbnails if ar changes during presentation ex. commercials @ diff ar - var vf = String.Empty; + var vf = string.Empty; if (threedFormat.HasValue) { switch (threedFormat.Value) { case Video3DFormat.HalfSideBySide: - vf = "crop=iw/2:ih:0:0,scale=(iw*2):ih,setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,setsar=sar=1"; + vf = "-vf crop=iw/2:ih:0:0,scale=(iw*2):ih,setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,setsar=sar=1"; // hsbs crop width in half,scale to correct size, set the display aspect,crop out any black bars we may have made. Work out the correct height based on the display aspect it will maintain the aspect where -1 in this case (3d) may not. break; case Video3DFormat.FullSideBySide: - vf = "crop=iw/2:ih:0:0,setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,setsar=sar=1"; + vf = "-vf crop=iw/2:ih:0:0,setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,setsar=sar=1"; // fsbs crop width in half,set the display aspect,crop out any black bars we may have made break; case Video3DFormat.HalfTopAndBottom: - vf = "crop=iw:ih/2:0:0,scale=(iw*2):ih),setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,setsar=sar=1"; + vf = "-vf crop=iw:ih/2:0:0,scale=(iw*2):ih),setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,setsar=sar=1"; // htab crop heigh in half,scale to correct size, set the display aspect,crop out any black bars we may have made break; case Video3DFormat.FullTopAndBottom: - vf = "crop=iw:ih/2:0:0,setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,setsar=sar=1"; + vf = "-vf crop=iw:ih/2:0:0,setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,setsar=sar=1"; // ftab crop heigt in half, set the display aspect,crop out any black bars we may have made break; default: @@ -557,8 +557,8 @@ namespace MediaBrowser.MediaEncoding.Encoder // Use ffmpeg to sample 100 (we can drop this if required using thumbnail=50 for 50 frames) frames and pick the best thumbnail. Have a fall back just in case. var thumbnail = enableThumbnail ? ",thumbnail=24" : string.Empty; - var args = useIFrame ? string.Format(CultureInfo.InvariantCulture, "-i {0}{3} -threads {5} -v quiet -vframes 1 -vf \"{2}{4}\" -f image2 \"{1}\"", inputPath, tempExtractPath, vf, mapArg, thumbnail, threads) : - string.Format(CultureInfo.InvariantCulture, "-i {0}{3} -threads {4} -v quiet -vframes 1 -vf \"{2}\" -f image2 \"{1}\"", inputPath, tempExtractPath, vf, mapArg, threads); + var args = useIFrame ? string.Format(CultureInfo.InvariantCulture, "-i {0}{3} -threads {5} -v quiet -vframes 1 {2}{4} -f image2 \"{1}\"", inputPath, tempExtractPath, vf, mapArg, thumbnail, threads) : + string.Format(CultureInfo.InvariantCulture, "-i {0}{3} -threads {4} -v quiet -vframes 1 {2} -f image2 \"{1}\"", inputPath, tempExtractPath, vf, mapArg, threads); var probeSizeArgument = EncodingHelper.GetProbeSizeArgument(1); var analyzeDurationArgument = EncodingHelper.GetAnalyzeDurationArgument(1); @@ -695,7 +695,7 @@ namespace MediaBrowser.MediaEncoding.Encoder Directory.CreateDirectory(targetDirectory); var outputPath = Path.Combine(targetDirectory, filenamePrefix + "%05d.jpg"); - var args = string.Format(CultureInfo.InvariantCulture, "-i {0} -threads {3} -v quiet -vf \"{2}\" -f image2 \"{1}\"", inputArgument, outputPath, vf, threads); + var args = string.Format(CultureInfo.InvariantCulture, "-i {0} -threads {3} -v quiet {2} -f image2 \"{1}\"", inputArgument, outputPath, vf, threads); var probeSizeArgument = EncodingHelper.GetProbeSizeArgument(1); var analyzeDurationArgument = EncodingHelper.GetAnalyzeDurationArgument(1); -- cgit v1.2.3 From 9a323f6df08927b5db06ba634f430523a236265f Mon Sep 17 00:00:00 2001 From: Greenback Date: Wed, 18 Nov 2020 13:46:14 +0000 Subject: More spelling corrections. --- Jellyfin.Data/Entities/Libraries/CollectionItem.cs | 4 ++-- Jellyfin.Data/Entities/Libraries/ItemMetadata.cs | 2 +- .../Users/DefaultAuthenticationProvider.cs | 2 +- MediaBrowser.Common/Net/INetworkManager.cs | 4 ++-- MediaBrowser.Controller/Entities/BaseItem.cs | 2 +- MediaBrowser.Controller/Entities/Folder.cs | 2 +- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 6 +++--- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 2 +- MediaBrowser.Model/Dlna/ResolutionNormalizer.cs | 2 +- MediaBrowser.Providers/Manager/ProviderUtils.cs | 4 ++-- MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs | 10 +++++----- MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs | 2 +- MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs | 10 +++++----- RSSDP/DisposableManagedObjectBase.cs | 6 +++--- RSSDP/HttpParserBase.cs | 2 +- RSSDP/ISsdpDeviceLocator.cs | 4 ++-- RSSDP/SsdpDevicePublisher.cs | 4 ++-- RSSDP/SsdpRootDevice.cs | 4 ++-- 18 files changed, 36 insertions(+), 36 deletions(-) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/Jellyfin.Data/Entities/Libraries/CollectionItem.cs b/Jellyfin.Data/Entities/Libraries/CollectionItem.cs index 4467c9bbd..f9539964d 100644 --- a/Jellyfin.Data/Entities/Libraries/CollectionItem.cs +++ b/Jellyfin.Data/Entities/Libraries/CollectionItem.cs @@ -73,7 +73,7 @@ namespace Jellyfin.Data.Entities.Libraries /// Gets or sets the next item in the collection. /// /// - /// TODO check if this properly updated dependant and has the proper principal relationship. + /// TODO check if this properly updated Dependant and has the proper principal relationship. /// public virtual CollectionItem Next { get; set; } @@ -81,7 +81,7 @@ namespace Jellyfin.Data.Entities.Libraries /// Gets or sets the previous item in the collection. /// /// - /// TODO check if this properly updated dependant and has the proper principal relationship. + /// TODO check if this properly updated Dependant and has the proper principal relationship. /// public virtual CollectionItem Previous { get; set; } diff --git a/Jellyfin.Data/Entities/Libraries/ItemMetadata.cs b/Jellyfin.Data/Entities/Libraries/ItemMetadata.cs index 1d2dc0f66..d74330c05 100644 --- a/Jellyfin.Data/Entities/Libraries/ItemMetadata.cs +++ b/Jellyfin.Data/Entities/Libraries/ItemMetadata.cs @@ -141,7 +141,7 @@ namespace Jellyfin.Data.Entities.Libraries public virtual ICollection PersonRoles { get; protected set; } /// - /// Gets or sets a collection containing the generes for this item. + /// Gets or sets a collection containing the genres for this item. /// public virtual ICollection Genres { get; protected set; } diff --git a/Jellyfin.Server.Implementations/Users/DefaultAuthenticationProvider.cs b/Jellyfin.Server.Implementations/Users/DefaultAuthenticationProvider.cs index f79e433a6..662b4bf65 100644 --- a/Jellyfin.Server.Implementations/Users/DefaultAuthenticationProvider.cs +++ b/Jellyfin.Server.Implementations/Users/DefaultAuthenticationProvider.cs @@ -53,7 +53,7 @@ namespace Jellyfin.Server.Implementations.Users bool success = false; - // As long as jellyfin supports passwordless users, we need this little block here to accommodate + // As long as jellyfin supports password-less users, we need this little block here to accommodate if (!HasPassword(resolvedUser) && string.IsNullOrEmpty(password)) { return Task.FromResult(new ProviderAuthenticationResult diff --git a/MediaBrowser.Common/Net/INetworkManager.cs b/MediaBrowser.Common/Net/INetworkManager.cs index a0330afef..12966a474 100644 --- a/MediaBrowser.Common/Net/INetworkManager.cs +++ b/MediaBrowser.Common/Net/INetworkManager.cs @@ -58,7 +58,7 @@ namespace MediaBrowser.Common.Net /// /// Investigates an caches a list of interface addresses, excluding local link and LAN excluded addresses. /// - /// The list of ipaddresses. + /// The list of ip addresses. IPAddress[] GetLocalIpAddresses(); /// @@ -73,7 +73,7 @@ namespace MediaBrowser.Common.Net /// Returns true if address is in the LAN list in the config file. /// /// The address to check. - /// If true, check against addresses in the LAN settings which have [] arroud and return true if it matches the address give in address. + /// If true, check against addresses in the LAN settings which have [] around and return true if it matches the address give in address. /// If true, returns false if address is in the 127.x.x.x or 169.128.x.x range. /// falseif the address isn't in the LAN list, true if the address has been defined as a LAN address. bool IsAddressInSubnets(IPAddress address, bool excludeInterfaces, bool excludeRFC); diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 1d44a5511..8d7773004 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -2611,7 +2611,7 @@ namespace MediaBrowser.Controller.Entities { if (!AllowsMultipleImages(type)) { - throw new ArgumentException("The change index operation is only applicable to backdrops and screenshots"); + throw new ArgumentException("The change index operation is only applicable to backdrops and screen shots"); } var info1 = GetImageInfo(type, index1); diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index a76c8a376..1e3825c6e 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -212,7 +212,7 @@ namespace MediaBrowser.Controller.Entities /// /// Loads our children. Validation will occur externally. - /// We want this sychronous. + /// We want this synchronous. /// protected virtual List LoadChildren() { diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 5846a603a..a4a5cecdc 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -63,7 +63,7 @@ namespace MediaBrowser.Controller.MediaEncoding { // 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. + // Since transcoding of folder rips is experimental anyway, it's not worth adding additional variables such as this. if (state.VideoType == VideoType.VideoFile) { var hwType = encodingOptions.HardwareAccelerationType; @@ -247,7 +247,7 @@ namespace MediaBrowser.Controller.MediaEncoding return null; } - // Seeing reported failures here, not sure yet if this is related to specfying input format + // Seeing reported failures here, not sure yet if this is related to specifying input format if (string.Equals(container, "m4v", StringComparison.OrdinalIgnoreCase)) { return null; @@ -2752,7 +2752,7 @@ namespace MediaBrowser.Controller.MediaEncoding var videoType = state.MediaSource.VideoType ?? VideoType.VideoFile; // 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. + // Since transcoding of folder rips is experimental anyway, it's not worth adding additional variables such as this. if (videoType != VideoType.VideoFile) { return null; diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 5a3a9185d..e060253ee 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -542,7 +542,7 @@ namespace MediaBrowser.MediaEncoding.Encoder break; case Video3DFormat.FullTopAndBottom: vf = "crop=iw:ih/2:0:0,setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,setsar=sar=1,scale=600:trunc(600/dar/2)*2"; - // ftab crop heigt in half, set the display aspect,crop out any black bars we may have made the scale width to 600 + // ftab crop height in half, set the display aspect,crop out any black bars we may have made the scale width to 600 break; default: break; diff --git a/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs b/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs index a4305c810..2f8614386 100644 --- a/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs +++ b/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs @@ -29,7 +29,7 @@ namespace MediaBrowser.Model.Dlna int? maxWidth, int? maxHeight) { - // If the bitrate isn't changing, then don't downlscale the resolution + // If the bitrate isn't changing, then don't downscale the resolution if (inputBitrate.HasValue && outputBitrate >= inputBitrate.Value) { if (maxWidth.HasValue || maxHeight.HasValue) diff --git a/MediaBrowser.Providers/Manager/ProviderUtils.cs b/MediaBrowser.Providers/Manager/ProviderUtils.cs index 70a5a6ac1..5621d2b86 100644 --- a/MediaBrowser.Providers/Manager/ProviderUtils.cs +++ b/MediaBrowser.Providers/Manager/ProviderUtils.cs @@ -38,7 +38,7 @@ namespace MediaBrowser.Providers.Manager { if (replaceData || string.IsNullOrEmpty(target.Name)) { - // Safeguard against incoming data having an emtpy name + // Safeguard against incoming data having an empty name if (!string.IsNullOrWhiteSpace(source.Name)) { target.Name = source.Name; @@ -48,7 +48,7 @@ namespace MediaBrowser.Providers.Manager if (replaceData || string.IsNullOrEmpty(target.OriginalTitle)) { - // Safeguard against incoming data having an emtpy name + // Safeguard against incoming data having an empty name if (!string.IsNullOrWhiteSpace(source.OriginalTitle)) { target.OriginalTitle = source.OriginalTitle; diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs index 6af52b591..332479ff8 100644 --- a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs +++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs @@ -50,7 +50,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb var result = await GetRootObject(imdbId, cancellationToken).ConfigureAwait(false); - // Only take the name and rating if the user's language is set to english, since Omdb has no localization + // Only take the name and rating if the user's language is set to English, since Omdb has no localization if (string.Equals(language, "en", StringComparison.OrdinalIgnoreCase) || _configurationManager.Configuration.EnableNewOmdbSupport) { item.Name = result.Title; @@ -151,7 +151,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb return false; } - // Only take the name and rating if the user's language is set to english, since Omdb has no localization + // Only take the name and rating if the user's language is set to English, since Omdb has no localization if (string.Equals(language, "en", StringComparison.OrdinalIgnoreCase) || _configurationManager.Configuration.EnableNewOmdbSupport) { item.Name = result.Title; @@ -385,7 +385,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb var isConfiguredForEnglish = IsConfiguredForEnglish(item) || _configurationManager.Configuration.EnableNewOmdbSupport; // Grab series genres because IMDb data is better than TVDB. Leave movies alone - // But only do it if english is the preferred language because this data will not be localized + // But only do it if English is the preferred language because this data will not be localized if (isConfiguredForEnglish && !string.IsNullOrWhiteSpace(result.Genre)) { item.Genres = Array.Empty(); @@ -401,7 +401,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb if (isConfiguredForEnglish) { - // Omdb is currently english only, so for other languages skip this and let secondary providers fill it in + // Omdb is currently English only, so for other languages skip this and let secondary providers fill it in item.Overview = result.Plot; } @@ -455,7 +455,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb { var lang = item.GetPreferredMetadataLanguage(); - // The data isn't localized and so can only be used for english users + // The data isn't localized and so can only be used for English users return string.Equals(lang, "en", StringComparison.OrdinalIgnoreCase); } diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs b/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs index b754a0795..0e8a5baab 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs @@ -98,7 +98,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb if (preferredLanguage.Length == 5) // like en-US { - // Currenty, TMDB supports 2-letter language codes only + // Currently, TMDB supports 2-letter language codes only // They are planning to change this in the future, thus we're // supplying both codes if we're having a 5-letter code. languages.Add(preferredLanguage.Substring(0, 2)); diff --git a/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs index 9cc0344c1..bce4cf009 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs @@ -134,7 +134,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers if (!string.IsNullOrWhiteSpace(val)) { - // int.TryParse is local aware, so it can be probamatic, force us culture + // int.TryParse is local aware, so it can be problematic, force us culture if (int.TryParse(val, NumberStyles.Integer, UsCulture, out var rval)) { item.AirsBeforeEpisodeNumber = rval; @@ -150,7 +150,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers if (!string.IsNullOrWhiteSpace(val)) { - // int.TryParse is local aware, so it can be probamatic, force us culture + // int.TryParse is local aware, so it can be problematic, force us culture if (int.TryParse(val, NumberStyles.Integer, UsCulture, out var rval)) { item.AirsAfterSeasonNumber = rval; @@ -166,7 +166,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers if (!string.IsNullOrWhiteSpace(val)) { - // int.TryParse is local aware, so it can be probamatic, force us culture + // int.TryParse is local aware, so it can be problematic, force us culture if (int.TryParse(val, NumberStyles.Integer, UsCulture, out var rval)) { item.AirsBeforeSeasonNumber = rval; @@ -182,7 +182,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers if (!string.IsNullOrWhiteSpace(val)) { - // int.TryParse is local aware, so it can be probamatic, force us culture + // int.TryParse is local aware, so it can be problematic, force us culture if (int.TryParse(val, NumberStyles.Integer, UsCulture, out var rval)) { item.AirsBeforeSeasonNumber = rval; @@ -198,7 +198,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers if (!string.IsNullOrWhiteSpace(val)) { - // int.TryParse is local aware, so it can be probamatic, force us culture + // int.TryParse is local aware, so it can be problematic, force us culture if (int.TryParse(val, NumberStyles.Integer, UsCulture, out var rval)) { item.AirsBeforeEpisodeNumber = rval; diff --git a/RSSDP/DisposableManagedObjectBase.cs b/RSSDP/DisposableManagedObjectBase.cs index 745ec359c..7d6a471f9 100644 --- a/RSSDP/DisposableManagedObjectBase.cs +++ b/RSSDP/DisposableManagedObjectBase.cs @@ -5,7 +5,7 @@ using System.Text; namespace Rssdp.Infrastructure { /// - /// Correclty implements the interface and pattern for an object containing only managed resources, and adds a few common niceities not on the interface such as an property. + /// Correctly implements the interface and pattern for an object containing only managed resources, and adds a few common niceties not on the interface such as an property. /// public abstract class DisposableManagedObjectBase : IDisposable { @@ -61,10 +61,10 @@ namespace Rssdp.Infrastructure /// Disposes this object instance and all internally managed resources. /// /// - /// Sets the property to true. Does not explicitly throw an exception if called multiple times, but makes no promises about behaviour of derived classes. + /// Sets the property to true. Does not explicitly throw an exception if called multiple times, but makes no promises about behavior of derived classes. /// /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly", Justification = "We do exactly as asked, but CA doesn't seem to like us also setting the IsDisposed property. Too bad, it's a good idea and shouldn't cause an exception or anything likely to interfer with the dispose process.")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly", Justification = "We do exactly as asked, but CA doesn't seem to like us also setting the IsDisposed property. Too bad, it's a good idea and shouldn't cause an exception or anything likely to interfere with the dispose process.")] public void Dispose() { IsDisposed = true; diff --git a/RSSDP/HttpParserBase.cs b/RSSDP/HttpParserBase.cs index 11202940e..c56249523 100644 --- a/RSSDP/HttpParserBase.cs +++ b/RSSDP/HttpParserBase.cs @@ -105,7 +105,7 @@ namespace Rssdp.Infrastructure var headerName = line.Substring(0, headerKeySeparatorIndex).Trim(); var headerValue = line.Substring(headerKeySeparatorIndex + 1).Trim(); - // Not sure how to determine where request headers and and content headers begin, + // Not sure how to determine where request headers and content headers begin, // at least not without a known set of headers (general headers first the content headers) // which seems like a bad way of doing it. So we'll assume if it's a known content header put it there // else use request headers. diff --git a/RSSDP/ISsdpDeviceLocator.cs b/RSSDP/ISsdpDeviceLocator.cs index 413055643..4df166cd2 100644 --- a/RSSDP/ISsdpDeviceLocator.cs +++ b/RSSDP/ISsdpDeviceLocator.cs @@ -52,7 +52,7 @@ namespace Rssdp.Infrastructure } /// - /// Aynchronously performs a search for all devices using the default search timeout, and returns an awaitable task that can be used to retrieve the results. + /// Asynchronously performs a search for all devices using the default search timeout, and returns an awaitable task that can be used to retrieve the results. /// /// A task whose result is an of instances, representing all found devices. System.Threading.Tasks.Task> SearchAsync(); @@ -83,7 +83,7 @@ namespace Rssdp.Infrastructure /// /// The amount of time to wait for network responses to the search request. Longer values will likely return more devices, but increase search time. A value between 1 and 5 is recommended by the UPnP 1.1 specification. Specify TimeSpan.Zero to return only devices already in the cache. /// - /// By design RSSDP does not support 'publishing services' as it is intended for use with non-standard UPnP devices that don't publish UPnP style services. However, it is still possible to use RSSDP to search for devices implemetning these services if you know the service type. + /// By design RSSDP does not support 'publishing services' as it is intended for use with non-standard UPnP devices that don't publish UPnP style services. However, it is still possible to use RSSDP to search for devices implementing these services if you know the service type. /// /// A task whose result is an of instances, representing all found devices. System.Threading.Tasks.Task> SearchAsync(string searchTarget, TimeSpan searchWaitTime); diff --git a/RSSDP/SsdpDevicePublisher.cs b/RSSDP/SsdpDevicePublisher.cs index 1a8577d8d..90925b9e0 100644 --- a/RSSDP/SsdpDevicePublisher.cs +++ b/RSSDP/SsdpDevicePublisher.cs @@ -102,7 +102,7 @@ namespace Rssdp.Infrastructure /// The instance to add. /// Thrown if the argument is null. /// Thrown if the contains property values that are not acceptable to the UPnP 1.0 specification. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals", MessageId = "t", Justification = "Capture task to local variable supresses compiler warning, but task is not really needed.")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals", MessageId = "t", Justification = "Capture task to local variable suppresses compiler warning, but task is not really needed.")] public void AddDevice(SsdpRootDevice device) { if (device == null) @@ -180,7 +180,7 @@ namespace Rssdp.Infrastructure /// /// /// Enabling this option will cause devices to show up in Microsoft Windows Explorer's network screens (if discovery is enabled etc.). Windows Explorer appears to search only for pnp:rootdeivce and not upnp:rootdevice. - /// If false, the system will only use upnp:rootdevice for notifiation broadcasts and and search responses, which is correct according to the UPnP/SSDP spec. + /// If false, the system will only use upnp:rootdevice for notification broadcasts and and search responses, which is correct according to the UPnP/SSDP spec. /// public bool SupportPnpRootDevice { diff --git a/RSSDP/SsdpRootDevice.cs b/RSSDP/SsdpRootDevice.cs index 8937ec331..4084b31ca 100644 --- a/RSSDP/SsdpRootDevice.cs +++ b/RSSDP/SsdpRootDevice.cs @@ -25,7 +25,7 @@ namespace Rssdp /// Specifies how long clients can cache this device's details for. Optional but defaults to which means no-caching. Recommended value is half an hour. /// /// - /// Specifiy to indicate no caching allowed. + /// Specify to indicate no caching allowed. /// Also used to specify how often to rebroadcast alive notifications. /// The UPnP/SSDP specifications indicate this should not be less than 1800 seconds (half an hour), but this is not enforced by this library. /// @@ -50,7 +50,7 @@ namespace Rssdp public IPAddress SubnetMask { get; set; } /// - /// The base URL to use for all relative url's provided in other propertise (and those of child devices). Optional. + /// The base URL to use for all relative url's provided in other properties (and those of child devices). Optional. /// /// /// Defines the base URL. Used to construct fully-qualified URLs. All relative URLs that appear elsewhere in the description are combined with this base URL. If URLBase is empty or not given, the base URL is the URL from which the device description was retrieved (which is the preferred implementation; use of URLBase is no longer recommended). Specified by UPnP vendor. Single URL. -- cgit v1.2.3 From 51dab0958d11e142f582da9c46a8679335b99dda Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Thu, 19 Nov 2020 22:46:02 +0800 Subject: changes per suggestions --- Jellyfin.Api/Controllers/DynamicHlsController.cs | 91 ++------- Jellyfin.Api/Controllers/VideoHlsController.cs | 90 ++------ Jellyfin.Api/Helpers/HlsHelpers.cs | 20 +- .../MediaEncoding/EncodingHelper.cs | 227 +++++++++++++++------ 4 files changed, 193 insertions(+), 235 deletions(-) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index 28357091b..90c2a6b58 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -43,7 +43,7 @@ namespace Jellyfin.Api.Controllers public class DynamicHlsController : BaseJellyfinApiController { private const string DefaultEncoderPreset = "veryfast"; - private readonly TranscodingJobType _transcodingJobType = TranscodingJobType.Hls; + private const TranscodingJobType _transcodingJobType = TranscodingJobType.Hls; private readonly ILibraryManager _libraryManager; private readonly IUserManager _userManager; @@ -1446,58 +1446,38 @@ namespace Jellyfin.Api.Controllers { if (EncodingHelper.IsCopyCodec(audioCodec)) { - var segmentFormat = HlsHelpers.GetSegmentFileExtension(state.Request.SegmentContainer).TrimStart('.'); - var bitStreamArgs = string.Empty; - - // Apply aac_adtstoasc bitstream filter when media source is in mpegts. - if (string.Equals(segmentFormat, "mp4", StringComparison.OrdinalIgnoreCase) - && (string.Equals(state.MediaSource.Container, "mpegts", StringComparison.OrdinalIgnoreCase) - || string.Equals(state.MediaSource.Container, "hls", StringComparison.OrdinalIgnoreCase))) - { - bitStreamArgs = _encodingHelper.GetBitStreamArgs(state.AudioStream); - bitStreamArgs = string.IsNullOrEmpty(bitStreamArgs) ? string.Empty : " " + bitStreamArgs; - } + var bitStreamArgs = EncodingHelper.GetAudioBitStreamArguments(state, state.Request.SegmentContainer, state.MediaSource.Container); return "-acodec copy -strict -2" + bitStreamArgs; } - var audioTranscodeParams = new List(); + var audioTranscodeParams = string.Empty; - audioTranscodeParams.Add("-acodec " + audioCodec); + audioTranscodeParams += "-acodec " + audioCodec; if (state.OutputAudioBitrate.HasValue) { - audioTranscodeParams.Add("-ab " + state.OutputAudioBitrate.Value.ToString(CultureInfo.InvariantCulture)); + audioTranscodeParams += " -ab " + state.OutputAudioBitrate.Value.ToString(CultureInfo.InvariantCulture); } if (state.OutputAudioChannels.HasValue) { - audioTranscodeParams.Add("-ac " + state.OutputAudioChannels.Value.ToString(CultureInfo.InvariantCulture)); + audioTranscodeParams += " -ac " + state.OutputAudioChannels.Value.ToString(CultureInfo.InvariantCulture); } if (state.OutputAudioSampleRate.HasValue) { - audioTranscodeParams.Add("-ar " + state.OutputAudioSampleRate.Value.ToString(CultureInfo.InvariantCulture)); + audioTranscodeParams += " -ar " + state.OutputAudioSampleRate.Value.ToString(CultureInfo.InvariantCulture); } - audioTranscodeParams.Add("-vn"); - return string.Join(' ', audioTranscodeParams); + audioTranscodeParams += " -vn"; + return audioTranscodeParams; } if (EncodingHelper.IsCopyCodec(audioCodec)) { var videoCodec = _encodingHelper.GetVideoEncoder(state, _encodingOptions); - var segmentFormat = HlsHelpers.GetSegmentFileExtension(state.Request.SegmentContainer).TrimStart('.'); - var bitStreamArgs = string.Empty; - - // Apply aac_adtstoasc bitstream filter when media source is in mpegts. - if (string.Equals(segmentFormat, "mp4", StringComparison.OrdinalIgnoreCase) - && (string.Equals(state.MediaSource.Container, "mpegts", StringComparison.OrdinalIgnoreCase) - || string.Equals(state.MediaSource.Container, "hls", StringComparison.OrdinalIgnoreCase))) - { - bitStreamArgs = _encodingHelper.GetBitStreamArgs(state.AudioStream); - bitStreamArgs = string.IsNullOrEmpty(bitStreamArgs) ? string.Empty : " " + bitStreamArgs; - } + var bitStreamArgs = EncodingHelper.GetAudioBitStreamArguments(state, state.Request.SegmentContainer, state.MediaSource.Container); if (EncodingHelper.IsCopyCodec(videoCodec) && state.EnableBreakOnNonKeyFrames(videoCodec)) { @@ -1528,7 +1508,7 @@ namespace Jellyfin.Api.Controllers args += " -ar " + state.OutputAudioSampleRate.Value.ToString(CultureInfo.InvariantCulture); } - args += " " + _encodingHelper.GetAudioFilterParam(state, _encodingOptions, true); + args += _encodingHelper.GetAudioFilterParam(state, _encodingOptions, true); return args; } @@ -1574,7 +1554,7 @@ namespace Jellyfin.Api.Controllers { if (state.VideoStream != null && !string.Equals(state.VideoStream.NalLengthSize, "0", StringComparison.OrdinalIgnoreCase)) { - string bitStreamArgs = _encodingHelper.GetBitStreamArgs(state.VideoStream); + string bitStreamArgs = EncodingHelper.GetBitStreamArgs(state.VideoStream); if (!string.IsNullOrEmpty(bitStreamArgs)) { args += " " + bitStreamArgs; @@ -1587,51 +1567,10 @@ namespace Jellyfin.Api.Controllers } else { - var gopArg = string.Empty; - var keyFrameArg = string.Format( - CultureInfo.InvariantCulture, - " -force_key_frames:0 \"expr:gte(t,{0}+n_forced*{1})\"", - startNumber * state.SegmentLength, - state.SegmentLength); - - var framerate = state.VideoStream?.RealFrameRate; - if (framerate.HasValue) - { - // This is to make sure keyframe interval is limited to our segment, - // as forcing keyframes is not enough. - // Example: we encoded half of desired length, then codec detected - // scene cut and inserted a keyframe; next forced keyframe would - // 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( - CultureInfo.InvariantCulture, - " -g {0} -keyint_min {0} -sc_threshold 0", - Math.Ceiling(state.SegmentLength * framerate.Value)); - } - - args += " " + _encodingHelper.GetVideoQualityParam(state, codec, _encodingOptions, DefaultEncoderPreset); + args += _encodingHelper.GetVideoQualityParam(state, codec, _encodingOptions, DefaultEncoderPreset); - // Unable to force key frames using these encoders, set key frames by GOP. - if (string.Equals(codec, "h264_qsv", StringComparison.OrdinalIgnoreCase) - || string.Equals(codec, "h264_nvenc", StringComparison.OrdinalIgnoreCase) - || string.Equals(codec, "h264_amf", StringComparison.OrdinalIgnoreCase) - || string.Equals(codec, "hevc_qsv", StringComparison.OrdinalIgnoreCase) - || string.Equals(codec, "hevc_nvenc", StringComparison.OrdinalIgnoreCase) - || string.Equals(codec, "hevc_amf", StringComparison.OrdinalIgnoreCase)) - { - args += " " + gopArg; - } - else if (string.Equals(codec, "libx264", StringComparison.OrdinalIgnoreCase) - || string.Equals(codec, "libx265", StringComparison.OrdinalIgnoreCase) - || string.Equals(codec, "h264_vaapi", StringComparison.OrdinalIgnoreCase) - || string.Equals(codec, "hevc_vaapi", StringComparison.OrdinalIgnoreCase)) - { - args += " " + keyFrameArg; - } - else - { - args += " " + keyFrameArg + gopArg; - } + // Set the key frame params for video encoding to match the hls segment time. + args += _encodingHelper.GetHlsVideoKeyFrameArguments(state, codec, state.SegmentLength, false, startNumber); // Currenly b-frames in libx265 breaks the FMP4-HLS playback on iOS, disable it for now. if (string.Equals(codec, "libx265", StringComparison.OrdinalIgnoreCase)) diff --git a/Jellyfin.Api/Controllers/VideoHlsController.cs b/Jellyfin.Api/Controllers/VideoHlsController.cs index b03402c1b..bb8026543 100644 --- a/Jellyfin.Api/Controllers/VideoHlsController.cs +++ b/Jellyfin.Api/Controllers/VideoHlsController.cs @@ -367,7 +367,7 @@ namespace Jellyfin.Api.Controllers var directory = Path.GetDirectoryName(outputPath) ?? throw new ArgumentException($"Provided path ({outputPath}) is not valid.", nameof(outputPath)); var outputFileNameWithoutExtension = Path.GetFileNameWithoutExtension(outputPath); var outputPrefix = Path.Combine(directory, outputFileNameWithoutExtension); - var outputExtension = HlsHelpers.GetSegmentFileExtension(state.Request.SegmentContainer); + var outputExtension = EncodingHelper.GetSegmentFileExtension(state.Request.SegmentContainer); var outputTsArg = outputPrefix + "%d" + outputExtension; var segmentFormat = outputExtension.TrimStart('.'); @@ -441,57 +441,37 @@ namespace Jellyfin.Api.Controllers { if (EncodingHelper.IsCopyCodec(audioCodec)) { - var segmentFormat = HlsHelpers.GetSegmentFileExtension(state.Request.SegmentContainer).TrimStart('.'); - var bitStreamArgs = string.Empty; - - // Apply aac_adtstoasc bitstream filter when media source is in mpegts. - if (string.Equals(segmentFormat, "mp4", StringComparison.OrdinalIgnoreCase) - && (string.Equals(state.MediaSource.Container, "mpegts", StringComparison.OrdinalIgnoreCase) - || string.Equals(state.MediaSource.Container, "hls", StringComparison.OrdinalIgnoreCase))) - { - bitStreamArgs = _encodingHelper.GetBitStreamArgs(state.AudioStream); - bitStreamArgs = string.IsNullOrEmpty(bitStreamArgs) ? string.Empty : " " + bitStreamArgs; - } + var bitStreamArgs = EncodingHelper.GetAudioBitStreamArguments(state, state.Request.SegmentContainer, state.MediaSource.Container); return "-acodec copy -strict -2" + bitStreamArgs; } - var audioTranscodeParams = new List(); + var audioTranscodeParams = string.Empty; - audioTranscodeParams.Add("-acodec " + audioCodec); + audioTranscodeParams += "-acodec " + audioCodec; if (state.OutputAudioBitrate.HasValue) { - audioTranscodeParams.Add("-ab " + state.OutputAudioBitrate.Value.ToString(CultureInfo.InvariantCulture)); + audioTranscodeParams += " -ab " + state.OutputAudioBitrate.Value.ToString(CultureInfo.InvariantCulture); } if (state.OutputAudioChannels.HasValue) { - audioTranscodeParams.Add("-ac " + state.OutputAudioChannels.Value.ToString(CultureInfo.InvariantCulture)); + audioTranscodeParams += " -ac " + state.OutputAudioChannels.Value.ToString(CultureInfo.InvariantCulture); } if (state.OutputAudioSampleRate.HasValue) { - audioTranscodeParams.Add("-ar " + state.OutputAudioSampleRate.Value.ToString(CultureInfo.InvariantCulture)); + audioTranscodeParams += " -ar " + state.OutputAudioSampleRate.Value.ToString(CultureInfo.InvariantCulture); } - audioTranscodeParams.Add("-vn"); - return string.Join(' ', audioTranscodeParams); + audioTranscodeParams += " -vn"; + return audioTranscodeParams; } if (EncodingHelper.IsCopyCodec(audioCodec)) { - var segmentFormat = HlsHelpers.GetSegmentFileExtension(state.Request.SegmentContainer).TrimStart('.'); - var bitStreamArgs = string.Empty; - - // Apply aac_adtstoasc bitstream filter when media source is in mpegts. - if (string.Equals(segmentFormat, "mp4", StringComparison.OrdinalIgnoreCase) - && (string.Equals(state.MediaSource.Container, "mpegts", StringComparison.OrdinalIgnoreCase) - || string.Equals(state.MediaSource.Container, "hls", StringComparison.OrdinalIgnoreCase))) - { - bitStreamArgs = _encodingHelper.GetBitStreamArgs(state.AudioStream); - bitStreamArgs = !string.IsNullOrEmpty(bitStreamArgs) ? " " + bitStreamArgs : string.Empty; - } + var bitStreamArgs = EncodingHelper.GetAudioBitStreamArguments(state, state.Request.SegmentContainer, state.MediaSource.Container); return "-acodec copy -strict -2" + bitStreamArgs; } @@ -517,7 +497,7 @@ namespace Jellyfin.Api.Controllers args += " -ar " + state.OutputAudioSampleRate.Value.ToString(CultureInfo.InvariantCulture); } - args += " " + _encodingHelper.GetAudioFilterParam(state, _encodingOptions, true); + args += _encodingHelper.GetAudioFilterParam(state, _encodingOptions, true); return args; } @@ -563,7 +543,7 @@ namespace Jellyfin.Api.Controllers // If h264_mp4toannexb is ever added, do not use it for live tv. if (state.VideoStream != null && !string.Equals(state.VideoStream.NalLengthSize, "0", StringComparison.OrdinalIgnoreCase)) { - string bitStreamArgs = _encodingHelper.GetBitStreamArgs(state.VideoStream); + string bitStreamArgs = EncodingHelper.GetBitStreamArgs(state.VideoStream); if (!string.IsNullOrEmpty(bitStreamArgs)) { args += " " + bitStreamArgs; @@ -574,50 +554,10 @@ namespace Jellyfin.Api.Controllers } else { - var gopArg = string.Empty; - var keyFrameArg = string.Format( - CultureInfo.InvariantCulture, - " -force_key_frames \"expr:gte(t,n_forced*{0})\"", - state.SegmentLength.ToString(CultureInfo.InvariantCulture)); - - var framerate = state.VideoStream?.RealFrameRate; - if (framerate.HasValue) - { - // This is to make sure keyframe interval is limited to our segment, - // as forcing keyframes is not enough. - // Example: we encoded half of desired length, then codec detected - // scene cut and inserted a keyframe; next forced keyframe would - // 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( - CultureInfo.InvariantCulture, - " -g {0} -keyint_min {0} -sc_threshold 0", - Math.Ceiling(state.SegmentLength * framerate.Value)); - } - - args += " " + _encodingHelper.GetVideoQualityParam(state, codec, _encodingOptions, DefaultEncoderPreset); + args += _encodingHelper.GetVideoQualityParam(state, codec, _encodingOptions, DefaultEncoderPreset); - // Unable to force key frames using these encoders, set key frames by GOP. - if (string.Equals(codec, "h264_qsv", StringComparison.OrdinalIgnoreCase) - || string.Equals(codec, "h264_nvenc", StringComparison.OrdinalIgnoreCase) - || string.Equals(codec, "h264_amf", StringComparison.OrdinalIgnoreCase) - || string.Equals(codec, "hevc_qsv", StringComparison.OrdinalIgnoreCase) - || string.Equals(codec, "hevc_nvenc", StringComparison.OrdinalIgnoreCase) - || string.Equals(codec, "hevc_amf", StringComparison.OrdinalIgnoreCase)) - { - args += " " + gopArg; - } - else if (string.Equals(codec, "libx264", StringComparison.OrdinalIgnoreCase) - || string.Equals(codec, "libx265", StringComparison.OrdinalIgnoreCase) - || string.Equals(codec, "h264_vaapi", StringComparison.OrdinalIgnoreCase) - || string.Equals(codec, "hevc_vaapi", StringComparison.OrdinalIgnoreCase)) - { - args += " " + keyFrameArg; - } - else - { - args += " " + keyFrameArg + gopArg; - } + // Set the key frame params for video encoding to match the hls segment time. + args += _encodingHelper.GetHlsVideoKeyFrameArguments(state, codec, state.SegmentLength, true, null); // Currenly b-frames in libx265 breaks the FMP4-HLS playback on iOS, disable it for now. if (string.Equals(codec, "libx265", StringComparison.OrdinalIgnoreCase)) diff --git a/Jellyfin.Api/Helpers/HlsHelpers.cs b/Jellyfin.Api/Helpers/HlsHelpers.cs index 1c874e4bd..18e23fb5c 100644 --- a/Jellyfin.Api/Helpers/HlsHelpers.cs +++ b/Jellyfin.Api/Helpers/HlsHelpers.cs @@ -5,6 +5,7 @@ using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using Jellyfin.Api.Models.StreamingDtos; +using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Model.IO; using Microsoft.Extensions.Logging; @@ -76,21 +77,6 @@ namespace Jellyfin.Api.Helpers } } - /// - /// Gets the extension of segment container. - /// - /// The name of the segment container. - /// The string text of extension. - public static string GetSegmentFileExtension(string? segmentContainer) - { - if (!string.IsNullOrWhiteSpace(segmentContainer)) - { - return "." + segmentContainer; - } - - return ".ts"; - } - /// /// Gets the #EXT-X-MAP string. /// @@ -103,7 +89,7 @@ namespace Jellyfin.Api.Helpers var directory = Path.GetDirectoryName(outputPath) ?? throw new ArgumentException($"Provided path ({outputPath}) is not valid.", nameof(outputPath)); var outputFileNameWithoutExtension = Path.GetFileNameWithoutExtension(outputPath); var outputPrefix = Path.Combine(directory, outputFileNameWithoutExtension); - var outputExtension = GetSegmentFileExtension(state.Request.SegmentContainer); + var outputExtension = EncodingHelper.GetSegmentFileExtension(state.Request.SegmentContainer); // on Linux/Unix // #EXT-X-MAP:URI="prefix-1.mp4" @@ -137,7 +123,7 @@ namespace Jellyfin.Api.Helpers var text = reader.ReadToEnd(); - var segmentFormat = GetSegmentFileExtension(state.Request.SegmentContainer).TrimStart('.'); + var segmentFormat = EncodingHelper.GetSegmentFileExtension(state.Request.SegmentContainer).TrimStart('.'); if (string.Equals(segmentFormat, "mp4", StringComparison.OrdinalIgnoreCase)) { var fmp4InitFileName = GetFmp4InitFileName(path, state, true); diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index b181a6ee6..a2e3318f3 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -580,7 +580,7 @@ namespace MediaBrowser.Controller.MediaEncoding /// /// The stream. /// true if the specified stream is H264; otherwise, false. - public bool IsH264(MediaStream stream) + public static bool IsH264(MediaStream stream) { var codec = stream.Codec ?? string.Empty; @@ -588,7 +588,7 @@ namespace MediaBrowser.Controller.MediaEncoding || codec.IndexOf("avc", StringComparison.OrdinalIgnoreCase) != -1; } - public bool IsH265(MediaStream stream) + public static bool IsH265(MediaStream stream) { var codec = stream.Codec ?? string.Empty; @@ -596,14 +596,14 @@ namespace MediaBrowser.Controller.MediaEncoding || codec.IndexOf("hevc", StringComparison.OrdinalIgnoreCase) != -1; } - public bool IsAAC(MediaStream stream) + public static bool IsAAC(MediaStream stream) { var codec = stream.Codec ?? string.Empty; return codec.IndexOf("aac", StringComparison.OrdinalIgnoreCase) != -1; } - public string GetBitStreamArgs(MediaStream stream) + public static string GetBitStreamArgs(MediaStream stream) { // TODO This is auto inserted into the mpegts mux so it might not be needed. // https://www.ffmpeg.org/ffmpeg-bitstream-filters.html#h264_005fmp4toannexb @@ -626,6 +626,33 @@ namespace MediaBrowser.Controller.MediaEncoding } } + public static string GetAudioBitStreamArguments(EncodingJobInfo state, string segmentContainer, string mediaSourceContainer) + { + var bitStreamArgs = string.Empty; + var segmentFormat = GetSegmentFileExtension(segmentContainer).TrimStart('.'); + + // Apply aac_adtstoasc bitstream filter when media source is in mpegts. + if (string.Equals(segmentFormat, "mp4", StringComparison.OrdinalIgnoreCase) + && (string.Equals(mediaSourceContainer, "mpegts", StringComparison.OrdinalIgnoreCase) + || string.Equals(mediaSourceContainer, "hls", StringComparison.OrdinalIgnoreCase))) + { + bitStreamArgs = GetBitStreamArgs(state.AudioStream); + bitStreamArgs = string.IsNullOrEmpty(bitStreamArgs) ? string.Empty : " " + bitStreamArgs; + } + + return bitStreamArgs; + } + + public static string GetSegmentFileExtension(string segmentContainer) + { + if (!string.IsNullOrWhiteSpace(segmentContainer)) + { + return "." + segmentContainer; + } + + return ".ts"; + } + public string GetVideoBitrateParam(EncodingJobInfo state, string videoCodec) { var bitrate = state.OutputVideoBitrate; @@ -799,6 +826,72 @@ namespace MediaBrowser.Controller.MediaEncoding return null; } + public string GetHlsVideoKeyFrameArguments( + EncodingJobInfo state, + string codec, + int segmentLength, + bool isEventPlaylist, + int? startNumber) + { + var args = string.Empty; + var gopArg = string.Empty; + var keyFrameArg = string.Empty; + if (isEventPlaylist) + { + keyFrameArg = string.Format( + CultureInfo.InvariantCulture, + " -force_key_frames:0 \"expr:gte(t,n_forced*{0})\"", + segmentLength); + } + else if (startNumber.HasValue) + { + keyFrameArg = string.Format( + CultureInfo.InvariantCulture, + " -force_key_frames:0 \"expr:gte(t,{0}+n_forced*{1})\"", + startNumber.Value * segmentLength, + segmentLength); + } + + var framerate = state.VideoStream?.RealFrameRate; + if (framerate.HasValue) + { + // This is to make sure keyframe interval is limited to our segment, + // as forcing keyframes is not enough. + // Example: we encoded half of desired length, then codec detected + // scene cut and inserted a keyframe; next forced keyframe would + // 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( + CultureInfo.InvariantCulture, + " -g:v:0 {0} -keyint_min:v:0 {0} -sc_threshold:v:0 0", + Math.Ceiling(segmentLength * framerate.Value)); + } + + // Unable to force key frames using these encoders, set key frames by GOP. + if (string.Equals(codec, "h264_qsv", StringComparison.OrdinalIgnoreCase) + || string.Equals(codec, "h264_nvenc", StringComparison.OrdinalIgnoreCase) + || string.Equals(codec, "h264_amf", StringComparison.OrdinalIgnoreCase) + || string.Equals(codec, "hevc_qsv", StringComparison.OrdinalIgnoreCase) + || string.Equals(codec, "hevc_nvenc", StringComparison.OrdinalIgnoreCase) + || string.Equals(codec, "hevc_amf", StringComparison.OrdinalIgnoreCase)) + { + args += gopArg; + } + else if (string.Equals(codec, "libx264", StringComparison.OrdinalIgnoreCase) + || string.Equals(codec, "libx265", StringComparison.OrdinalIgnoreCase) + || string.Equals(codec, "h264_vaapi", StringComparison.OrdinalIgnoreCase) + || string.Equals(codec, "hevc_vaapi", StringComparison.OrdinalIgnoreCase)) + { + args += " " + keyFrameArg; + } + else + { + args += " " + keyFrameArg + gopArg; + } + + return args; + } + /// /// Gets the video bitrate to specify on the command line. /// @@ -806,6 +899,47 @@ namespace MediaBrowser.Controller.MediaEncoding { var param = string.Empty; + if (!string.Equals(videoEncoder, "h264_omx", StringComparison.OrdinalIgnoreCase) + && !string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) + && !string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase) + && !string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase) + && !string.Equals(videoEncoder, "h264_amf", StringComparison.OrdinalIgnoreCase) + && !string.Equals(videoEncoder, "h264_v4l2m2m", StringComparison.OrdinalIgnoreCase) + && !string.Equals(videoEncoder, "hevc_qsv", StringComparison.OrdinalIgnoreCase) + && !string.Equals(videoEncoder, "hevc_vaapi", StringComparison.OrdinalIgnoreCase) + && !string.Equals(videoEncoder, "hevc_nvenc", StringComparison.OrdinalIgnoreCase) + && !string.Equals(videoEncoder, "hevc_amf", StringComparison.OrdinalIgnoreCase)) + { + param += " -pix_fmt yuv420p"; + } + + if (string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase) + || string.Equals(videoEncoder, "h264_amf", StringComparison.OrdinalIgnoreCase) + || string.Equals(videoEncoder, "hevc_nvenc", StringComparison.OrdinalIgnoreCase) + || string.Equals(videoEncoder, "hevc_amf", StringComparison.OrdinalIgnoreCase)) + { + var videoStream = state.VideoStream; + var isColorDepth10 = IsColorDepth10(state); + + if (isColorDepth10 + && _mediaEncoder.SupportsHwaccel("opencl") + && encodingOptions.EnableTonemapping + && !string.IsNullOrEmpty(videoStream.VideoRange) + && videoStream.VideoRange.Contains("HDR", StringComparison.OrdinalIgnoreCase)) + { + param += " -pix_fmt nv12"; + } + else + { + param += " -pix_fmt yuv420p"; + } + } + + if (string.Equals(videoEncoder, "h264_v4l2m2m", StringComparison.OrdinalIgnoreCase)) + { + param += " -pix_fmt nv21"; + } + var isVc1 = state.VideoStream != null && string.Equals(state.VideoStream.Codec, "vc1", StringComparison.OrdinalIgnoreCase); var isLibX265 = string.Equals(videoEncoder, "libx265", StringComparison.OrdinalIgnoreCase); @@ -814,11 +948,11 @@ namespace MediaBrowser.Controller.MediaEncoding { if (!string.IsNullOrEmpty(encodingOptions.EncoderPreset)) { - param += "-preset " + encodingOptions.EncoderPreset; + param += " -preset " + encodingOptions.EncoderPreset; } else { - param += "-preset " + defaultPreset; + param += " -preset " + defaultPreset; } int encodeCrf = encodingOptions.H264Crf; @@ -849,11 +983,11 @@ namespace MediaBrowser.Controller.MediaEncoding if (valid_h264_qsv.Contains(encodingOptions.EncoderPreset, StringComparer.OrdinalIgnoreCase)) { - param += "-preset " + encodingOptions.EncoderPreset; + param += " -preset " + encodingOptions.EncoderPreset; } else { - param += "-preset 7"; + param += " -preset 7"; } param += " -look_ahead 0"; @@ -866,16 +1000,16 @@ namespace MediaBrowser.Controller.MediaEncoding { case "veryslow": - param += "-preset slow"; // lossless is only supported on maxwell and newer(2014+) + param += " -preset slow"; // lossless is only supported on maxwell and newer(2014+) break; case "slow": case "slower": - param += "-preset slow"; + param += " -preset slow"; break; case "medium": - param += "-preset medium"; + param += " -preset medium"; break; case "fast": @@ -883,11 +1017,11 @@ namespace MediaBrowser.Controller.MediaEncoding case "veryfast": case "superfast": case "ultrafast": - param += "-preset fast"; + param += " -preset fast"; break; default: - param += "-preset default"; + param += " -preset default"; break; } } @@ -899,11 +1033,11 @@ namespace MediaBrowser.Controller.MediaEncoding case "veryslow": case "slow": case "slower": - param += "-quality quality"; + param += " -quality quality"; break; case "medium": - param += "-quality balanced"; + param += " -quality balanced"; break; case "fast": @@ -911,11 +1045,11 @@ namespace MediaBrowser.Controller.MediaEncoding case "veryfast": case "superfast": case "ultrafast": - param += "-quality speed"; + param += " -quality speed"; break; default: - param += "-quality speed"; + param += " -quality speed"; break; } @@ -957,7 +1091,7 @@ namespace MediaBrowser.Controller.MediaEncoding profileScore = Math.Min(profileScore, 2); // http://www.webmproject.org/docs/encoder-parameters/ - param += string.Format(CultureInfo.InvariantCulture, "-speed 16 -quality good -profile:v {0} -slices 8 -crf {1} -qmin {2} -qmax {3}", + param += string.Format(CultureInfo.InvariantCulture, " -speed 16 -quality good -profile:v {0} -slices 8 -crf {1} -qmin {2} -qmax {3}", profileScore.ToString(_usCulture), crf, qmin, @@ -965,15 +1099,15 @@ namespace MediaBrowser.Controller.MediaEncoding } else if (string.Equals(videoEncoder, "mpeg4", StringComparison.OrdinalIgnoreCase)) { - param += "-mbd rd -flags +mv4+aic -trellis 2 -cmp 2 -subcmp 2 -bf 2"; + param += " -mbd rd -flags +mv4+aic -trellis 2 -cmp 2 -subcmp 2 -bf 2"; } else if (string.Equals(videoEncoder, "wmv2", StringComparison.OrdinalIgnoreCase)) // asf/wmv { - param += "-qmin 2"; + param += " -qmin 2"; } else if (string.Equals(videoEncoder, "msmpeg4", StringComparison.OrdinalIgnoreCase)) { - param += "-mbd 2"; + param += " -mbd 2"; } param += GetVideoBitrateParam(state, videoEncoder); @@ -1035,7 +1169,7 @@ namespace MediaBrowser.Controller.MediaEncoding && !string.Equals(videoEncoder, "h264_v4l2m2m", StringComparison.OrdinalIgnoreCase)) { // not supported by h264_omx - param += " -profile:v " + profile; + param += " -profile:v:0 " + profile; } } @@ -1091,47 +1225,6 @@ namespace MediaBrowser.Controller.MediaEncoding param += " -x265-params:0 no-info=1"; } - if (!string.Equals(videoEncoder, "h264_omx", StringComparison.OrdinalIgnoreCase) - && !string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) - && !string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase) - && !string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase) - && !string.Equals(videoEncoder, "h264_amf", StringComparison.OrdinalIgnoreCase) - && !string.Equals(videoEncoder, "h264_v4l2m2m", StringComparison.OrdinalIgnoreCase) - && !string.Equals(videoEncoder, "hevc_qsv", StringComparison.OrdinalIgnoreCase) - && !string.Equals(videoEncoder, "hevc_vaapi", StringComparison.OrdinalIgnoreCase) - && !string.Equals(videoEncoder, "hevc_nvenc", StringComparison.OrdinalIgnoreCase) - && !string.Equals(videoEncoder, "hevc_amf", StringComparison.OrdinalIgnoreCase)) - { - param = "-pix_fmt yuv420p " + param; - } - - if (string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase) - || string.Equals(videoEncoder, "h264_amf", StringComparison.OrdinalIgnoreCase) - || string.Equals(videoEncoder, "hevc_nvenc", StringComparison.OrdinalIgnoreCase) - || string.Equals(videoEncoder, "hevc_amf", StringComparison.OrdinalIgnoreCase)) - { - var videoStream = state.VideoStream; - var isColorDepth10 = IsColorDepth10(state); - - if (isColorDepth10 - && _mediaEncoder.SupportsHwaccel("opencl") - && encodingOptions.EnableTonemapping - && !string.IsNullOrEmpty(videoStream.VideoRange) - && videoStream.VideoRange.Contains("HDR", StringComparison.OrdinalIgnoreCase)) - { - param = "-pix_fmt nv12 " + param; - } - else - { - param = "-pix_fmt yuv420p " + param; - } - } - - if (string.Equals(videoEncoder, "h264_v4l2m2m", StringComparison.OrdinalIgnoreCase)) - { - param = "-pix_fmt nv21 " + param; - } - return param; } @@ -1521,7 +1614,7 @@ namespace MediaBrowser.Controller.MediaEncoding if (filters.Count > 0) { - return "-af \"" + string.Join(",", filters) + "\""; + return " -af \"" + string.Join(",", filters) + "\""; } return string.Empty; @@ -3378,7 +3471,7 @@ namespace MediaBrowser.Controller.MediaEncoding if (state.RunTimeTicks.HasValue && state.BaseRequest.CopyTimestamps) { - args += " -copyts -avoid_negative_ts 0 -start_at_zero"; + args += " -copyts -avoid_negative_ts disabled -start_at_zero"; } if (!state.RunTimeTicks.HasValue) @@ -3426,7 +3519,7 @@ namespace MediaBrowser.Controller.MediaEncoding args += " -copyts"; } - args += " -avoid_negative_ts 0"; + args += " -avoid_negative_ts disabled"; if (!(state.SubtitleStream != null && state.SubtitleStream.IsExternal && !state.SubtitleStream.IsTextSubtitleStream)) { @@ -3490,7 +3583,7 @@ namespace MediaBrowser.Controller.MediaEncoding args += " -ar " + state.OutputAudioSampleRate.Value.ToString(_usCulture); } - args += " " + GetAudioFilterParam(state, encodingOptions, false); + args += GetAudioFilterParam(state, encodingOptions, false); return args; } -- cgit v1.2.3 From 5ff08338d5a475d2975ecc6d4fe5222456368bd2 Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Thu, 19 Nov 2020 15:02:36 +0000 Subject: Apply suggestions from code review Co-authored-by: Claus Vium --- Jellyfin.Api/Controllers/VideoHlsController.cs | 2 +- .../MediaEncoding/EncodingHelper.cs | 6 +++--- .../Probing/ProbeResultNormalizer.cs | 23 ++++++++-------------- MediaBrowser.Model/Dlna/StreamBuilder.cs | 12 +++++------ 4 files changed, 18 insertions(+), 25 deletions(-) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/Jellyfin.Api/Controllers/VideoHlsController.cs b/Jellyfin.Api/Controllers/VideoHlsController.cs index bb8026543..b151f85e2 100644 --- a/Jellyfin.Api/Controllers/VideoHlsController.cs +++ b/Jellyfin.Api/Controllers/VideoHlsController.cs @@ -578,7 +578,7 @@ namespace Jellyfin.Api.Controllers args += _encodingHelper.GetOutputSizeParam(state, _encodingOptions, codec); } - if (!(state.SubtitleStream != null && state.SubtitleStream.IsExternal && !state.SubtitleStream.IsTextSubtitleStream)) + if (state.SubtitleStream == null || !state.SubtitleStream.IsExternal || state.SubtitleStream.IsTextSubtitleStream) { args += " -start_at_zero"; } diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index a2e3318f3..6e36adc16 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -1194,7 +1194,7 @@ namespace MediaBrowser.Controller.MediaEncoding } } else if (string.Equals(videoEncoder, "h264_amf", StringComparison.OrdinalIgnoreCase) - || string.Equals(videoEncoder, "hevc_amf", StringComparison.OrdinalIgnoreCase)) + || string.Equals(videoEncoder, "hevc_amf", StringComparison.OrdinalIgnoreCase)) { param += " -level " + level; } @@ -1205,7 +1205,7 @@ namespace MediaBrowser.Controller.MediaEncoding // NVENC cannot adjust the given level, just throw an error. } else if (!string.Equals(videoEncoder, "h264_omx", StringComparison.OrdinalIgnoreCase) - || !string.Equals(videoEncoder, "libx265", StringComparison.OrdinalIgnoreCase)) + || !string.Equals(videoEncoder, "libx265", StringComparison.OrdinalIgnoreCase)) { param += " -level " + level; } @@ -1931,7 +1931,7 @@ namespace MediaBrowser.Controller.MediaEncoding retStr = " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3}[base];[base][sub]overlay\""; } else if (string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase) - || string.Equals(outputVideoCodec, "hevc_qsv", StringComparison.OrdinalIgnoreCase)) + || string.Equals(outputVideoCodec, "hevc_qsv", StringComparison.OrdinalIgnoreCase)) { /* QSV in FFMpeg can now setup hardware overlay for transcodes. diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index 387594ce6..3d3d1eb48 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs @@ -1025,12 +1025,10 @@ namespace MediaBrowser.MediaEncoding.Probing if (streamInfo != null && streamInfo.Tags != null) { var bps = GetDictionaryValue(streamInfo.Tags, "BPS-eng") ?? GetDictionaryValue(streamInfo.Tags, "BPS"); - if (!string.IsNullOrEmpty(bps)) + if (!string.IsNullOrEmpty(bps) + && int.TryParse(bps, NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsedBps)) { - if (int.TryParse(bps, NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsedBps)) - { - return parsedBps; - } + return parsedBps; } } @@ -1042,12 +1040,9 @@ namespace MediaBrowser.MediaEncoding.Probing if (streamInfo != null && streamInfo.Tags != null) { var duration = GetDictionaryValue(streamInfo.Tags, "DURATION-eng") ?? GetDictionaryValue(streamInfo.Tags, "DURATION"); - if (!string.IsNullOrEmpty(duration)) + if (!string.IsNullOrEmpty(duration) && TimeSpan.TryParse(duration, out var parsedDuration)) { - if (TimeSpan.TryParse(duration, out var parsedDuration)) - { - return parsedDuration.TotalSeconds; - } + return parsedDuration.TotalSeconds; } } @@ -1059,12 +1054,10 @@ namespace MediaBrowser.MediaEncoding.Probing if (streamInfo != null && streamInfo.Tags != null) { var numberOfBytes = GetDictionaryValue(streamInfo.Tags, "NUMBER_OF_BYTES-eng") ?? GetDictionaryValue(streamInfo.Tags, "NUMBER_OF_BYTES"); - if (!string.IsNullOrEmpty(numberOfBytes)) + if (!string.IsNullOrEmpty(numberOfBytes) + && long.TryParse(numberOfBytes, NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsedBytes)) { - if (long.TryParse(numberOfBytes, NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsedBytes)) - { - return parsedBytes; - } + return parsedBytes; } } diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index 76b137c49..2e5137005 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -928,12 +928,12 @@ namespace MediaBrowser.Model.Dlna defaultBitrate = GetDefaultAudioBitrate(targetAudioCodec, targetAudioChannels); } else if (targetAudioChannels.HasValue - && audioStream.Channels.HasValue - && audioStream.Channels.Value <= targetAudioChannels.Value - && !string.IsNullOrEmpty(audioStream.Codec) - && targetAudioCodecs != null - && targetAudioCodecs.Length > 0 - && !Array.Exists(targetAudioCodecs, elem => string.Equals(audioStream.Codec, elem, StringComparison.OrdinalIgnoreCase))) + && audioStream.Channels.HasValue + && audioStream.Channels.Value <= targetAudioChannels.Value + && !string.IsNullOrEmpty(audioStream.Codec) + && targetAudioCodecs != null + && targetAudioCodecs.Length > 0 + && !Array.Exists(targetAudioCodecs, elem => string.Equals(audioStream.Codec, elem, StringComparison.OrdinalIgnoreCase))) { // Shift the bitrate if we're transcoding to a different audio codec. defaultBitrate = GetDefaultAudioBitrate(targetAudioCodec, audioStream.Channels.Value); -- cgit v1.2.3 From 2deda0437d32016a0c73158840c9c04bc729b261 Mon Sep 17 00:00:00 2001 From: Fernando Fernández Date: Wed, 18 Nov 2020 10:54:21 +0100 Subject: Review suggestions --- Jellyfin.Api/Controllers/DynamicHlsController.cs | 2 +- Jellyfin.Api/Controllers/VideoHlsController.cs | 2 +- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 8 +++++--- 3 files changed, 7 insertions(+), 5 deletions(-) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index bba5e972e..1c2e4474c 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -1329,7 +1329,7 @@ namespace Jellyfin.Api.Controllers { var videoCodec = _encodingHelper.GetVideoEncoder(state, encodingOptions); - var threads = EncodingHelper.GetNumberOfThreads(state, encodingOptions, videoCodec); // GetNumberOfThreads is static + var threads = EncodingHelper.GetNumberOfThreads(state, encodingOptions, videoCodec); // GetNumberOfThreads is static. if (state.BaseRequest.BreakOnNonKeyFrames) { diff --git a/Jellyfin.Api/Controllers/VideoHlsController.cs b/Jellyfin.Api/Controllers/VideoHlsController.cs index 065fa786e..df9d35b13 100644 --- a/Jellyfin.Api/Controllers/VideoHlsController.cs +++ b/Jellyfin.Api/Controllers/VideoHlsController.cs @@ -359,7 +359,7 @@ namespace Jellyfin.Api.Controllers private string GetCommandLineArguments(string outputPath, StreamState state) { var videoCodec = _encodingHelper.GetVideoEncoder(state, _encodingOptions); - var threads = EncodingHelper.GetNumberOfThreads(state, _encodingOptions, videoCodec); // GetNumberOfThreads is static + var threads = EncodingHelper.GetNumberOfThreads(state, _encodingOptions, videoCodec); // GetNumberOfThreads is static. var inputModifier = _encodingHelper.GetInputModifier(state, _encodingOptions); var format = !string.IsNullOrWhiteSpace(state.Request.SegmentContainer) ? "." + state.Request.SegmentContainer : ".ts"; var directory = Path.GetDirectoryName(outputPath) ?? throw new ArgumentException($"Provided path ({outputPath}) is not valid.", nameof(outputPath)); diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 60c44cfcf..0fb8a9238 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -2329,15 +2329,17 @@ namespace MediaBrowser.Controller.MediaEncoding /// /// Gets the number of threads. /// - public static int GetNumberOfThreads(EncodingJobInfo state, EncodingOptions encodingOptions, string outputVideoCodec) +#nullable enable + public static int GetNumberOfThreads(EncodingJobInfo? state, EncodingOptions encodingOptions, string? outputVideoCodec) { - if (outputVideoCodec != null && string.Equals(outputVideoCodec, "libvpx", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(outputVideoCodec, "libvpx", StringComparison.OrdinalIgnoreCase)) { // per docs: // -threads number of threads to use for encoding, can't be 0 [auto] with VP8 // (recommended value : number of real cores - 1) return Math.Max(Environment.ProcessorCount - 1, 1); } + var threads = state?.BaseRequest.CpuCoreLimit ?? encodingOptions.EncodingThreadCount; // Automatic @@ -2352,7 +2354,7 @@ namespace MediaBrowser.Controller.MediaEncoding return threads; } - +#nullable disable public void TryStreamCopy(EncodingJobInfo state) { if (state.VideoStream != null && CanStreamCopyVideo(state, state.VideoStream)) -- cgit v1.2.3 From 44ff7a48436123e7290c576e2ca97c400e9f0ffa Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Fri, 20 Nov 2020 08:06:28 +0100 Subject: Apply suggestions from code review --- Jellyfin.Api/Controllers/VideoHlsController.cs | 2 +- .../MediaEncoding/EncodingHelper.cs | 32 +++++++++++----------- 2 files changed, 17 insertions(+), 17 deletions(-) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/Jellyfin.Api/Controllers/VideoHlsController.cs b/Jellyfin.Api/Controllers/VideoHlsController.cs index f1d6a5d45..2ac16de6b 100644 --- a/Jellyfin.Api/Controllers/VideoHlsController.cs +++ b/Jellyfin.Api/Controllers/VideoHlsController.cs @@ -394,7 +394,7 @@ namespace Jellyfin.Api.Controllers } else { - _logger.LogError("Invalid HLS segment container: " + segmentFormat); + _logger.LogError("Invalid HLS segment container: {SegmentFormat}", segmentFormat); } var maxMuxingQueueSize = _encodingOptions.MaxMuxingQueueSize > 128 diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 388c5089c..9a6f1231f 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -878,9 +878,9 @@ namespace MediaBrowser.Controller.MediaEncoding args += gopArg; } else if (string.Equals(codec, "libx264", StringComparison.OrdinalIgnoreCase) - || string.Equals(codec, "libx265", StringComparison.OrdinalIgnoreCase) - || string.Equals(codec, "h264_vaapi", StringComparison.OrdinalIgnoreCase) - || string.Equals(codec, "hevc_vaapi", StringComparison.OrdinalIgnoreCase)) + || string.Equals(codec, "libx265", StringComparison.OrdinalIgnoreCase) + || string.Equals(codec, "h264_vaapi", StringComparison.OrdinalIgnoreCase) + || string.Equals(codec, "hevc_vaapi", StringComparison.OrdinalIgnoreCase)) { args += " " + keyFrameArg; } @@ -977,7 +977,7 @@ namespace MediaBrowser.Controller.MediaEncoding } } else if (string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) // h264 (h264_qsv) - || string.Equals(videoEncoder, "hevc_qsv", StringComparison.OrdinalIgnoreCase)) // hevc (hevc_qsv) + || string.Equals(videoEncoder, "hevc_qsv", StringComparison.OrdinalIgnoreCase)) // hevc (hevc_qsv) { string[] valid_h264_qsv = { "veryslow", "slower", "slow", "medium", "fast", "faster", "veryfast" }; @@ -993,7 +993,7 @@ namespace MediaBrowser.Controller.MediaEncoding param += " -look_ahead 0"; } else if (string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase) // h264 (h264_nvenc) - || string.Equals(videoEncoder, "hevc_nvenc", StringComparison.OrdinalIgnoreCase)) // hevc (hevc_nvenc) + || string.Equals(videoEncoder, "hevc_nvenc", StringComparison.OrdinalIgnoreCase)) // hevc (hevc_nvenc) { // following preset will be deprecated in ffmpeg 4.4, use p1~p7 instead. switch (encodingOptions.EncoderPreset) @@ -1026,7 +1026,7 @@ namespace MediaBrowser.Controller.MediaEncoding } } else if (string.Equals(videoEncoder, "h264_amf", StringComparison.OrdinalIgnoreCase) // 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) { @@ -1147,10 +1147,10 @@ namespace MediaBrowser.Controller.MediaEncoding // libx264, h264_qsv and h264_nvenc does not support Constrained Baseline profile, force Baseline in this case. if ((string.Equals(videoEncoder, "libx264", StringComparison.OrdinalIgnoreCase) - || string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) - || string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase)) - && profile != null - && profile.IndexOf("baseline", StringComparison.OrdinalIgnoreCase) != -1) + || string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) + || string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase)) + && profile != null + && profile.IndexOf("baseline", StringComparison.OrdinalIgnoreCase) != -1) { profile = "baseline"; } @@ -1199,7 +1199,7 @@ namespace MediaBrowser.Controller.MediaEncoding param += " -level " + level; } else if (string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase) - || string.Equals(videoEncoder, "hevc_nvenc", StringComparison.OrdinalIgnoreCase)) + || string.Equals(videoEncoder, "hevc_nvenc", StringComparison.OrdinalIgnoreCase)) { // level option may cause NVENC to fail. // NVENC cannot adjust the given level, just throw an error. @@ -1920,8 +1920,8 @@ namespace MediaBrowser.Controller.MediaEncoding // If we're hardware VAAPI decoding and software encoding, download frames from the decoder first else if (_mediaEncoder.SupportsHwaccel("vaapi") && videoDecoder.IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1 - && (string.Equals(outputVideoCodec, "libx264", StringComparison.OrdinalIgnoreCase) - || string.Equals(outputVideoCodec, "libx265", StringComparison.OrdinalIgnoreCase))) + && (string.Equals(outputVideoCodec, "libx264", StringComparison.OrdinalIgnoreCase) + || string.Equals(outputVideoCodec, "libx265", StringComparison.OrdinalIgnoreCase))) { /* [base]: SW scaling video to OutputSize @@ -2017,9 +2017,9 @@ namespace MediaBrowser.Controller.MediaEncoding requestedMaxHeight); if ((string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase) - || string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) - || string.Equals(videoEncoder, "hevc_vaapi", StringComparison.OrdinalIgnoreCase) - || string.Equals(videoEncoder, "hevc_qsv", StringComparison.OrdinalIgnoreCase)) + || string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) + || string.Equals(videoEncoder, "hevc_vaapi", StringComparison.OrdinalIgnoreCase) + || string.Equals(videoEncoder, "hevc_qsv", StringComparison.OrdinalIgnoreCase)) && width.HasValue && height.HasValue) { -- cgit v1.2.3