From ca7d1a13000ad948eebbfdeb40542312f3e37d3e Mon Sep 17 00:00:00 2001 From: nicknsy <20588554+nicknsy@users.noreply.github.com> Date: Wed, 22 Feb 2023 00:08:35 -0800 Subject: Trickplay generation, manager, storage --- .../MediaEncoding/EncodingHelper.cs | 35 ++++++++++++++++++++++ 1 file changed, 35 insertions(+) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index b155d674d..0889a90f4 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -149,6 +149,36 @@ namespace MediaBrowser.Controller.MediaEncoding return defaultEncoder; } + private string GetMjpegEncoder(EncodingJobInfo state, EncodingOptions encodingOptions) + { + var defaultEncoder = "mjpeg"; + + if (state.VideoType == VideoType.VideoFile) + { + var hwType = encodingOptions.HardwareAccelerationType; + + var codecMap = new Dictionary(StringComparer.OrdinalIgnoreCase) + { + { "vaapi", defaultEncoder + "_vaapi" }, + { "qsv", defaultEncoder + "_qsv" } + }; + + if (!string.IsNullOrEmpty(hwType) + && encodingOptions.EnableHardwareEncoding + && codecMap.ContainsKey(hwType)) + { + var preferredEncoder = codecMap[hwType]; + + if (_mediaEncoder.SupportsEncoder(preferredEncoder)) + { + return preferredEncoder; + } + } + } + + return defaultEncoder; + } + private bool IsVaapiSupported(EncodingJobInfo state) { // vaapi will throw an error with this input @@ -277,6 +307,11 @@ namespace MediaBrowser.Controller.MediaEncoding return GetH264Encoder(state, encodingOptions); } + if (string.Equals(codec, "mjpeg", StringComparison.OrdinalIgnoreCase)) + { + return GetMjpegEncoder(state, encodingOptions); + } + if (string.Equals(codec, "vp8", StringComparison.OrdinalIgnoreCase) || string.Equals(codec, "vpx", StringComparison.OrdinalIgnoreCase)) { -- cgit v1.2.3 From dd8ef08592830236b31307e2424b491e974f024a Mon Sep 17 00:00:00 2001 From: Nick <20588554+nicknsy@users.noreply.github.com> Date: Wed, 29 Mar 2023 16:43:17 -0700 Subject: Move fps filter to GetVideoProcessingFilterParam --- .../MediaEncoding/EncodingHelper.cs | 9 +++++++++ MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 19 ++++--------------- 2 files changed, 13 insertions(+), 15 deletions(-) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 0889a90f4..bcdf2934a 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -4806,6 +4806,15 @@ namespace MediaBrowser.Controller.MediaEncoding subFilters?.RemoveAll(filter => string.IsNullOrEmpty(filter)); overlayFilters?.RemoveAll(filter => string.IsNullOrEmpty(filter)); + var framerate = GetFramerateParam(state); + if (framerate.HasValue) + { + mainFilters.Insert(0, string.Format( + CultureInfo.InvariantCulture, + "fps={0}", + framerate.Value)); + } + var mainStr = string.Empty; if (mainFilters?.Count > 0) { diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 11f42c3f9..4692bf504 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -804,7 +804,7 @@ namespace MediaBrowser.MediaEncoding.Encoder options.EnableTonemapping = false; } - var baseRequest = new BaseEncodingJobOptions { MaxWidth = maxWidth }; + var baseRequest = new BaseEncodingJobOptions { MaxWidth = maxWidth, MaxFramerate = (float)(1.0 / interval.TotalSeconds) }; var jobState = new EncodingJobInfo(TranscodingJobType.Progressive) { IsVideoRequest = true, // must be true for InputVideoHwaccelArgs to return non-empty value @@ -829,18 +829,17 @@ namespace MediaBrowser.MediaEncoding.Encoder } var filterParam = encodingHelper.GetVideoProcessingFilterParam(jobState, options, jobState.OutputVideoCodec).Trim(); - if (string.IsNullOrWhiteSpace(filterParam) || filterParam.IndexOf("\"", StringComparison.Ordinal) == -1) + if (string.IsNullOrWhiteSpace(filterParam)) { throw new InvalidOperationException("EncodingHelper returned empty or invalid filter parameters."); } - return ExtractVideoImagesOnIntervalInternal(inputArg, filterParam, interval, vidEncoder, threads, qualityScale, priority, cancellationToken); + return ExtractVideoImagesOnIntervalInternal(inputArg, filterParam, vidEncoder, threads, qualityScale, priority, cancellationToken); } private async Task ExtractVideoImagesOnIntervalInternal( string inputArg, string filterParam, - TimeSpan interval, string vidEncoder, int? outputThreads, int? qualityScale, @@ -853,16 +852,6 @@ namespace MediaBrowser.MediaEncoding.Encoder } // Output arguments - string fps = "fps=1/" + interval.TotalSeconds.ToString(CultureInfo.InvariantCulture); - if (string.IsNullOrWhiteSpace(filterParam)) - { - filterParam = "-vf \"" + fps + "\""; - } - else if (filterParam.IndexOf("\"", StringComparison.Ordinal) != -1) - { - filterParam = filterParam.Insert(filterParam.IndexOf("\"", StringComparison.Ordinal) + 1, fps + ","); - } - var targetDirectory = Path.Combine(_configurationManager.ApplicationPaths.TempDirectory, Guid.NewGuid().ToString("N")); Directory.CreateDirectory(targetDirectory); var outputPath = Path.Combine(targetDirectory, "%08d.jpg"); @@ -895,7 +884,7 @@ namespace MediaBrowser.MediaEncoding.Encoder }; var processDescription = string.Format(CultureInfo.InvariantCulture, "{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments); - _logger.LogDebug("{ProcessDescription}", processDescription); + _logger.LogInformation("Trickplay generation: {ProcessDescription}", processDescription); using (var processWrapper = new ProcessWrapper(process, this)) { -- cgit v1.2.3 From 33770322282326304b4b8073f583d6fed2354c0b Mon Sep 17 00:00:00 2001 From: Nick <20588554+nicknsy@users.noreply.github.com> Date: Mon, 1 May 2023 12:51:05 -0700 Subject: crobibero styling, format, code suggestions --- .../MediaEncoding/EncodingHelper.cs | 27 +- .../Trickplay/ITrickplayManager.cs | 77 ++- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 2 +- .../Configuration/TrickplayOptions.cs | 109 ++-- .../Configuration/TrickplayScanBehavior.cs | 25 +- MediaBrowser.Model/Entities/TrickplayTilesInfo.cs | 79 ++- .../Trickplay/TrickplayImagesTask.cs | 147 +++--- .../Trickplay/TrickplayManager.cs | 569 ++++++++++----------- .../Trickplay/TrickplayProvider.cs | 181 ++++--- 9 files changed, 602 insertions(+), 614 deletions(-) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index bcdf2934a..01b6e31e9 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -90,6 +90,13 @@ namespace MediaBrowser.Controller.MediaEncoding { "truehd", 6 }, }; + private static readonly string _defaultMjpegEncoder = "mjpeg"; + private static readonly Dictionary _mjpegCodecMap = new(StringComparer.OrdinalIgnoreCase) + { + { "vaapi", _defaultMjpegEncoder + "_vaapi" }, + { "qsv", _defaultMjpegEncoder + "_qsv" } + }; + public static readonly string[] LosslessAudioCodecs = new string[] { "alac", @@ -151,32 +158,20 @@ namespace MediaBrowser.Controller.MediaEncoding private string GetMjpegEncoder(EncodingJobInfo state, EncodingOptions encodingOptions) { - var defaultEncoder = "mjpeg"; - if (state.VideoType == VideoType.VideoFile) { var hwType = encodingOptions.HardwareAccelerationType; - var codecMap = new Dictionary(StringComparer.OrdinalIgnoreCase) - { - { "vaapi", defaultEncoder + "_vaapi" }, - { "qsv", defaultEncoder + "_qsv" } - }; - if (!string.IsNullOrEmpty(hwType) && encodingOptions.EnableHardwareEncoding - && codecMap.ContainsKey(hwType)) + && _mjpegCodecMap.TryGetValue(hwType, out var preferredEncoder) + && _mediaEncoder.SupportsEncoder(preferredEncoder)) { - var preferredEncoder = codecMap[hwType]; - - if (_mediaEncoder.SupportsEncoder(preferredEncoder)) - { - return preferredEncoder; - } + return preferredEncoder; } } - return defaultEncoder; + return _defaultMjpegEncoder; } private bool IsVaapiSupported(EncodingJobInfo state) diff --git a/MediaBrowser.Controller/Trickplay/ITrickplayManager.cs b/MediaBrowser.Controller/Trickplay/ITrickplayManager.cs index bae458f98..8e82c57d4 100644 --- a/MediaBrowser.Controller/Trickplay/ITrickplayManager.cs +++ b/MediaBrowser.Controller/Trickplay/ITrickplayManager.cs @@ -5,50 +5,49 @@ using System.Threading.Tasks; using MediaBrowser.Controller.Entities; using MediaBrowser.Model.Entities; -namespace MediaBrowser.Controller.Trickplay +namespace MediaBrowser.Controller.Trickplay; + +/// +/// Interface ITrickplayManager. +/// +public interface ITrickplayManager { /// - /// Interface ITrickplayManager. + /// Generate or replace trickplay data. /// - public interface ITrickplayManager - { - /// - /// Generate or replace trickplay data. - /// - /// The video. - /// Whether or not existing data should be replaced. - /// CancellationToken to use for operation. - /// Task. - Task RefreshTrickplayData(Video video, bool replace, CancellationToken cancellationToken); + /// The video. + /// Whether or not existing data should be replaced. + /// CancellationToken to use for operation. + /// Task. + Task RefreshTrickplayDataAsync(Video video, bool replace, CancellationToken cancellationToken); - /// - /// Get available trickplay resolutions and corresponding info. - /// - /// The item. - /// Map of width resolutions to trickplay tiles info. - Dictionary GetTilesResolutions(Guid itemId); + /// + /// Get available trickplay resolutions and corresponding info. + /// + /// The item. + /// Map of width resolutions to trickplay tiles info. + Dictionary GetTilesResolutions(Guid itemId); - /// - /// Saves trickplay tiles info. - /// - /// The item. - /// The trickplay tiles info. - void SaveTilesInfo(Guid itemId, TrickplayTilesInfo tilesInfo); + /// + /// Saves trickplay tiles info. + /// + /// The item. + /// The trickplay tiles info. + void SaveTilesInfo(Guid itemId, TrickplayTilesInfo tilesInfo); - /// - /// Gets the trickplay manifest. - /// - /// The item. - /// A map of media source id to a map of tile width to tile info. - Dictionary> GetTrickplayManifest(BaseItem item); + /// + /// Gets the trickplay manifest. + /// + /// The item. + /// A map of media source id to a map of tile width to tile info. + Dictionary> GetTrickplayManifest(BaseItem item); - /// - /// Gets the path to a trickplay tiles image. - /// - /// The item. - /// The width of a single tile. - /// The tile grid's index. - /// The absolute path. - string GetTrickplayTilePath(BaseItem item, int width, int index); - } + /// + /// Gets the path to a trickplay tiles image. + /// + /// The item. + /// The width of a single tile. + /// The tile grid's index. + /// The absolute path. + string GetTrickplayTilePath(BaseItem item, int width, int index); } diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 4692bf504..000831fe2 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -793,7 +793,7 @@ namespace MediaBrowser.MediaEncoding.Encoder CancellationToken cancellationToken) { var options = allowHwAccel ? _configurationManager.GetEncodingOptions() : new EncodingOptions(); - threads = threads ?? _threads; + threads ??= _threads; // A new EncodingOptions instance must be used as to not disable HW acceleration for all of Jellyfin. // Additionally, we must set a few fields without defaults to prevent null pointer exceptions. diff --git a/MediaBrowser.Model/Configuration/TrickplayOptions.cs b/MediaBrowser.Model/Configuration/TrickplayOptions.cs index d89e5f590..1fff1a5ed 100644 --- a/MediaBrowser.Model/Configuration/TrickplayOptions.cs +++ b/MediaBrowser.Model/Configuration/TrickplayOptions.cs @@ -1,61 +1,60 @@ using System.Collections.Generic; using System.Diagnostics; -namespace MediaBrowser.Model.Configuration +namespace MediaBrowser.Model.Configuration; + +/// +/// Class TrickplayOptions. +/// +public class TrickplayOptions { /// - /// Class TrickplayOptions. - /// - public class TrickplayOptions - { - /// - /// Gets or sets a value indicating whether or not to use HW acceleration. - /// - public bool EnableHwAcceleration { get; set; } = false; - - /// - /// Gets or sets the behavior used by trickplay provider on library scan/update. - /// - public TrickplayScanBehavior ScanBehavior { get; set; } = TrickplayScanBehavior.NonBlocking; - - /// - /// Gets or sets the process priority for the ffmpeg process. - /// - public ProcessPriorityClass ProcessPriority { get; set; } = ProcessPriorityClass.BelowNormal; - - /// - /// Gets or sets the interval, in ms, between each new trickplay image. - /// - public int Interval { get; set; } = 10000; - - /// - /// Gets or sets the target width resolutions, in px, to generates preview images for. - /// - public int[] WidthResolutions { get; set; } = new[] { 320 }; - - /// - /// Gets or sets number of tile images to allow in X dimension. - /// - public int TileWidth { get; set; } = 10; - - /// - /// Gets or sets number of tile images to allow in Y dimension. - /// - public int TileHeight { get; set; } = 10; - - /// - /// Gets or sets the ffmpeg output quality level. - /// - public int Qscale { get; set; } = 4; - - /// - /// Gets or sets the jpeg quality to use for image tiles. - /// - public int JpegQuality { get; set; } = 90; - - /// - /// Gets or sets the number of threads to be used by ffmpeg. - /// - public int ProcessThreads { get; set; } = 0; - } + /// Gets or sets a value indicating whether or not to use HW acceleration. + /// + public bool EnableHwAcceleration { get; set; } = false; + + /// + /// Gets or sets the behavior used by trickplay provider on library scan/update. + /// + public TrickplayScanBehavior ScanBehavior { get; set; } = TrickplayScanBehavior.NonBlocking; + + /// + /// Gets or sets the process priority for the ffmpeg process. + /// + public ProcessPriorityClass ProcessPriority { get; set; } = ProcessPriorityClass.BelowNormal; + + /// + /// Gets or sets the interval, in ms, between each new trickplay image. + /// + public int Interval { get; set; } = 10000; + + /// + /// Gets or sets the target width resolutions, in px, to generates preview images for. + /// + public int[] WidthResolutions { get; set; } = new[] { 320 }; + + /// + /// Gets or sets number of tile images to allow in X dimension. + /// + public int TileWidth { get; set; } = 10; + + /// + /// Gets or sets number of tile images to allow in Y dimension. + /// + public int TileHeight { get; set; } = 10; + + /// + /// Gets or sets the ffmpeg output quality level. + /// + public int Qscale { get; set; } = 4; + + /// + /// Gets or sets the jpeg quality to use for image tiles. + /// + public int JpegQuality { get; set; } = 90; + + /// + /// Gets or sets the number of threads to be used by ffmpeg. + /// + public int ProcessThreads { get; set; } = 0; } diff --git a/MediaBrowser.Model/Configuration/TrickplayScanBehavior.cs b/MediaBrowser.Model/Configuration/TrickplayScanBehavior.cs index 799794176..d0db53218 100644 --- a/MediaBrowser.Model/Configuration/TrickplayScanBehavior.cs +++ b/MediaBrowser.Model/Configuration/TrickplayScanBehavior.cs @@ -1,18 +1,17 @@ -namespace MediaBrowser.Model.Configuration +namespace MediaBrowser.Model.Configuration; + +/// +/// Enum TrickplayScanBehavior. +/// +public enum TrickplayScanBehavior { /// - /// Enum TrickplayScanBehavior. + /// Starts generation, only return once complete. /// - public enum TrickplayScanBehavior - { - /// - /// Starts generation, only return once complete. - /// - Blocking, + Blocking, - /// - /// Start generation, return immediately. - /// - NonBlocking - } + /// + /// Start generation, return immediately. + /// + NonBlocking } diff --git a/MediaBrowser.Model/Entities/TrickplayTilesInfo.cs b/MediaBrowser.Model/Entities/TrickplayTilesInfo.cs index 84b6b0322..86d37787f 100644 --- a/MediaBrowser.Model/Entities/TrickplayTilesInfo.cs +++ b/MediaBrowser.Model/Entities/TrickplayTilesInfo.cs @@ -1,50 +1,49 @@ -namespace MediaBrowser.Model.Entities +namespace MediaBrowser.Model.Entities; + +/// +/// Class TrickplayTilesInfo. +/// +public class TrickplayTilesInfo { /// - /// Class TrickplayTilesInfo. + /// Gets or sets width of an individual tile. /// - public class TrickplayTilesInfo - { - /// - /// Gets or sets width of an individual tile. - /// - /// The width. - public int Width { get; set; } + /// The width. + public int Width { get; set; } - /// - /// Gets or sets height of an individual tile. - /// - /// The height. - public int Height { get; set; } + /// + /// Gets or sets height of an individual tile. + /// + /// The height. + public int Height { get; set; } - /// - /// Gets or sets amount of tiles per row. - /// - /// The tile grid's width. - public int TileWidth { get; set; } + /// + /// Gets or sets amount of tiles per row. + /// + /// The tile grid's width. + public int TileWidth { get; set; } - /// - /// Gets or sets amount of tiles per column. - /// - /// The tile grid's height. - public int TileHeight { get; set; } + /// + /// Gets or sets amount of tiles per column. + /// + /// The tile grid's height. + public int TileHeight { get; set; } - /// - /// Gets or sets total amount of non-black tiles. - /// - /// The tile count. - public int TileCount { get; set; } + /// + /// Gets or sets total amount of non-black tiles. + /// + /// The tile count. + public int TileCount { get; set; } - /// - /// Gets or sets interval in milliseconds between each trickplay tile. - /// - /// The interval. - public int Interval { get; set; } + /// + /// Gets or sets interval in milliseconds between each trickplay tile. + /// + /// The interval. + public int Interval { get; set; } - /// - /// Gets or sets peak bandwith usage in bits per second. - /// - /// The bandwidth. - public int Bandwidth { get; set; } - } + /// + /// Gets or sets peak bandwith usage in bits per second. + /// + /// The bandwidth. + public int Bandwidth { get; set; } } diff --git a/MediaBrowser.Providers/Trickplay/TrickplayImagesTask.cs b/MediaBrowser.Providers/Trickplay/TrickplayImagesTask.cs index 8d0d9d5a3..f32557cd1 100644 --- a/MediaBrowser.Providers/Trickplay/TrickplayImagesTask.cs +++ b/MediaBrowser.Providers/Trickplay/TrickplayImagesTask.cs @@ -12,98 +12,97 @@ using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Tasks; using Microsoft.Extensions.Logging; -namespace MediaBrowser.Providers.Trickplay +namespace MediaBrowser.Providers.Trickplay; + +/// +/// Class TrickplayImagesTask. +/// +public class TrickplayImagesTask : IScheduledTask { + private readonly ILogger _logger; + private readonly ILibraryManager _libraryManager; + private readonly ILocalizationManager _localization; + private readonly ITrickplayManager _trickplayManager; + /// - /// Class TrickplayImagesTask. + /// Initializes a new instance of the class. /// - public class TrickplayImagesTask : IScheduledTask + /// The logger. + /// The library manager. + /// The localization manager. + /// The trickplay manager. + public TrickplayImagesTask( + ILogger logger, + ILibraryManager libraryManager, + ILocalizationManager localization, + ITrickplayManager trickplayManager) { - private readonly ILogger _logger; - private readonly ILibraryManager _libraryManager; - private readonly ILocalizationManager _localization; - private readonly ITrickplayManager _trickplayManager; - - /// - /// Initializes a new instance of the class. - /// - /// The logger. - /// The library manager. - /// The localization manager. - /// The trickplay manager. - public TrickplayImagesTask( - ILogger logger, - ILibraryManager libraryManager, - ILocalizationManager localization, - ITrickplayManager trickplayManager) - { - _libraryManager = libraryManager; - _logger = logger; - _localization = localization; - _trickplayManager = trickplayManager; - } + _libraryManager = libraryManager; + _logger = logger; + _localization = localization; + _trickplayManager = trickplayManager; + } - /// - public string Name => _localization.GetLocalizedString("TaskRefreshTrickplayImages"); + /// + public string Name => _localization.GetLocalizedString("TaskRefreshTrickplayImages"); - /// - public string Description => _localization.GetLocalizedString("TaskRefreshTrickplayImagesDescription"); + /// + public string Description => _localization.GetLocalizedString("TaskRefreshTrickplayImagesDescription"); - /// - public string Key => "RefreshTrickplayImages"; + /// + public string Key => "RefreshTrickplayImages"; - /// - public string Category => _localization.GetLocalizedString("TasksLibraryCategory"); + /// + public string Category => _localization.GetLocalizedString("TasksLibraryCategory"); - /// - public IEnumerable GetDefaultTriggers() + /// + public IEnumerable GetDefaultTriggers() + { + return new[] { - return new[] + new TaskTriggerInfo { - new TaskTriggerInfo - { - Type = TaskTriggerInfo.TriggerDaily, - TimeOfDayTicks = TimeSpan.FromHours(3).Ticks - } - }; - } + Type = TaskTriggerInfo.TriggerDaily, + TimeOfDayTicks = TimeSpan.FromHours(3).Ticks + } + }; + } - /// - public async Task ExecuteAsync(IProgress progress, CancellationToken cancellationToken) + /// + public async Task ExecuteAsync(IProgress progress, CancellationToken cancellationToken) + { + var items = _libraryManager.GetItemList(new InternalItemsQuery { - var items = _libraryManager.GetItemList(new InternalItemsQuery - { - MediaTypes = new[] { MediaType.Video }, - IsVirtualItem = false, - IsFolder = false, - Recursive = true - }).OfType