nicknsy před 2 roky
rodič
revize
6744e712d3

+ 9 - 4
MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs

@@ -4,6 +4,7 @@
 
 using System;
 using System.Collections.Generic;
+using System.Diagnostics;
 using System.Threading;
 using System.Threading.Tasks;
 using MediaBrowser.Model.Configuration;
@@ -145,10 +146,12 @@ namespace MediaBrowser.Controller.MediaEncoding
         /// <param name="container">Video container type.</param>
         /// <param name="mediaSource">Media source information.</param>
         /// <param name="imageStream">Media stream information.</param>
-        /// <param name="interval">The interval.</param>
         /// <param name="maxWidth">The maximum width.</param>
+        /// <param name="interval">The interval.</param>
         /// <param name="allowHwAccel">Allow for hardware acceleration.</param>
-        /// <param name="allowHwEncode">Allow for hardware encoding. allowHwAccel must also be true.</param>
+        /// <param name="threads">The input/output thread count for ffmpeg.</param>
+        /// <param name="qualityScale">The qscale value for ffmpeg.</param>
+        /// <param name="priority">The process priority for the ffmpeg process.</param>
         /// <param name="encodingHelper">EncodingHelper instance.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Directory where images where extracted. A given image made before another will always be named with a lower number.</returns>
@@ -157,10 +160,12 @@ namespace MediaBrowser.Controller.MediaEncoding
             string container,
             MediaSourceInfo mediaSource,
             MediaStream imageStream,
-            TimeSpan interval,
             int maxWidth,
+            TimeSpan interval,
             bool allowHwAccel,
-            bool allowHwEncode,
+            int? threads,
+            int? qualityScale,
+            ProcessPriorityClass? priority,
             EncodingHelper encodingHelper,
             CancellationToken cancellationToken);
 

+ 26 - 11
MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs

@@ -783,14 +783,17 @@ namespace MediaBrowser.MediaEncoding.Encoder
             string container,
             MediaSourceInfo mediaSource,
             MediaStream imageStream,
-            TimeSpan interval,
             int maxWidth,
+            TimeSpan interval,
             bool allowHwAccel,
-            bool allowHwEncode,
+            int? threads,
+            int? qualityScale,
+            ProcessPriorityClass? priority,
             EncodingHelper encodingHelper,
             CancellationToken cancellationToken)
         {
             var options = allowHwAccel ? _configurationManager.GetEncodingOptions() : new EncodingOptions();
+            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.
@@ -822,7 +825,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
 
             if (!allowHwAccel)
             {
-                inputArg = "-threads " + _threads + " " + inputArg; // HW accel args set a different input thread count, only set if disabled
+                inputArg = "-threads " + threads + " " + inputArg; // HW accel args set a different input thread count, only set if disabled
             }
 
             var filterParam = encodingHelper.GetVideoProcessingFilterParam(jobState, options, jobState.OutputVideoCodec).Trim();
@@ -831,7 +834,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
                 throw new InvalidOperationException("EncodingHelper returned empty or invalid filter parameters.");
             }
 
-            return ExtractVideoImagesOnIntervalInternal(inputArg, filterParam, interval, vidEncoder, _threads, cancellationToken);
+            return ExtractVideoImagesOnIntervalInternal(inputArg, filterParam, interval, vidEncoder, threads, qualityScale, priority, cancellationToken);
         }
 
         private async Task<string> ExtractVideoImagesOnIntervalInternal(
@@ -839,7 +842,9 @@ namespace MediaBrowser.MediaEncoding.Encoder
             string filterParam,
             TimeSpan interval,
             string vidEncoder,
-            int outputThreads,
+            int? outputThreads,
+            int? qualityScale,
+            ProcessPriorityClass? priority,
             CancellationToken cancellationToken)
         {
             if (string.IsNullOrWhiteSpace(inputArg))
@@ -857,10 +862,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
             {
                 filterParam = filterParam.Insert(filterParam.IndexOf("\"", StringComparison.Ordinal) + 1, fps + ",");
             }
-            else
-            {
-                filterParam += fps + ",";
-            }
 
             var targetDirectory = Path.Combine(_configurationManager.ApplicationPaths.TempDirectory, Guid.NewGuid().ToString("N"));
             Directory.CreateDirectory(targetDirectory);
@@ -869,11 +870,12 @@ namespace MediaBrowser.MediaEncoding.Encoder
             // Final command arguments
             var args = string.Format(
                 CultureInfo.InvariantCulture,
-                "-loglevel error {0} -an -sn {1} -threads {2} -c:v {3} -f {4} \"{5}\"",
+                "-loglevel error {0} -an -sn {1} -threads {2} -c:v {3} {4}-f {5} \"{6}\"",
                 inputArg,
                 filterParam,
-                outputThreads,
+                outputThreads.GetValueOrDefault(_threads),
                 vidEncoder,
+                qualityScale.HasValue ? "-qscale:v " + qualityScale.Value.ToString(CultureInfo.InvariantCulture) + " " : string.Empty,
                 "image2",
                 outputPath);
 
@@ -904,6 +906,19 @@ namespace MediaBrowser.MediaEncoding.Encoder
                 {
                     StartProcess(processWrapper);
 
+                    // Set process priority
+                    if (priority.HasValue)
+                    {
+                        try
+                        {
+                            processWrapper.Process.PriorityClass = priority.Value;
+                        }
+                        catch (Exception ex)
+                        {
+                            _logger.LogDebug(ex, "Unable to set process priority to {Priority} for {Description}", priority.Value, processDescription);
+                        }
+                    }
+
                     // Need to give ffmpeg enough time to make all the thumbnails, which could be a while,
                     // but we still need to detect if the process hangs.
                     // Making the assumption that as long as new jpegs are showing up, everything is good.

+ 2 - 0
MediaBrowser.Model/Configuration/ServerConfiguration.cs

@@ -264,5 +264,7 @@ namespace MediaBrowser.Model.Configuration
         /// </summary>
         /// <value>The limit for parallel image encoding.</value>
         public int ParallelImageEncodingLimit { get; set; }
+
+        public TrickplayOptions TrickplayOptions { get; set; } = new TrickplayOptions();
     }
 }

+ 8 - 4
MediaBrowser.Providers/Trickplay/TrickplayImagesTask.cs

@@ -22,7 +22,6 @@ namespace MediaBrowser.Providers.Trickplay
         private readonly ILogger<TrickplayImagesTask> _logger;
         private readonly ILibraryManager _libraryManager;
         private readonly ILocalizationManager _localization;
-        private readonly IServerConfigurationManager _configurationManager;
         private readonly ITrickplayManager _trickplayManager;
 
         /// <summary>
@@ -31,19 +30,16 @@ namespace MediaBrowser.Providers.Trickplay
         /// <param name="logger">The logger.</param>
         /// <param name="libraryManager">The library manager.</param>
         /// <param name="localization">The localization manager.</param>
-        /// <param name="configurationManager">The configuration manager.</param>
         /// <param name="trickplayManager">The trickplay manager.</param>
         public TrickplayImagesTask(
             ILogger<TrickplayImagesTask> logger,
             ILibraryManager libraryManager,
             ILocalizationManager localization,
-            IServerConfigurationManager configurationManager,
             ITrickplayManager trickplayManager)
         {
             _libraryManager = libraryManager;
             _logger = logger;
             _localization = localization;
-            _configurationManager = configurationManager;
             _trickplayManager = trickplayManager;
         }
 
@@ -77,6 +73,14 @@ namespace MediaBrowser.Providers.Trickplay
         public async Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
         {
             // TODO: libraryoptions dont run on libraries with trickplay disabled
+            /* will this still get all sub-items? should recursive be true?
+             * from chapterimagestask
+             *                 DtoOptions = new DtoOptions(false)
+                {
+                    EnableImages = false
+                },
+                SourceTypes = new SourceType[] { SourceType.Library },
+             */
             var items = _libraryManager.GetItemList(new InternalItemsQuery
             {
                 MediaTypes = new[] { MediaType.Video },

+ 34 - 15
MediaBrowser.Providers/Trickplay/TrickplayManager.cs

@@ -5,11 +5,13 @@ using System.IO;
 using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
+using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.MediaEncoding;
 using MediaBrowser.Controller.Persistence;
 using MediaBrowser.Controller.Trickplay;
+using MediaBrowser.Model.Configuration;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.IO;
 using Microsoft.Extensions.Logging;
@@ -28,6 +30,7 @@ namespace MediaBrowser.Providers.Trickplay
         private readonly IFileSystem _fileSystem;
         private readonly EncodingHelper _encodingHelper;
         private readonly ILibraryManager _libraryManager;
+        private readonly IServerConfigurationManager _config;
 
         private static readonly SemaphoreSlim _resourcePool = new(1, 1);
 
@@ -40,13 +43,15 @@ namespace MediaBrowser.Providers.Trickplay
         /// <param name="fileSystem">The file systen.</param>
         /// <param name="encodingHelper">The encoding helper.</param>
         /// <param name="libraryManager">The library manager.</param>
+        /// <param name="config">The server configuration manager.</param>
         public TrickplayManager(
             ILogger<TrickplayManager> logger,
             IItemRepository itemRepo,
             IMediaEncoder mediaEncoder,
             IFileSystem fileSystem,
             EncodingHelper encodingHelper,
-            ILibraryManager libraryManager)
+            ILibraryManager libraryManager,
+            IServerConfigurationManager config)
         {
             _logger = logger;
             _itemRepo = itemRepo;
@@ -54,6 +59,7 @@ namespace MediaBrowser.Providers.Trickplay
             _fileSystem = fileSystem;
             _encodingHelper = encodingHelper;
             _libraryManager = libraryManager;
+            _config = config;
         }
 
         /// <inheritdoc />
@@ -61,16 +67,27 @@ namespace MediaBrowser.Providers.Trickplay
         {
             _logger.LogDebug("Trickplay refresh for {ItemId} (replace existing: {Replace})", video.Id, replace);
 
-            foreach (var width in new int[] { 320 } /*todo conf*/)
+            var options = _config.Configuration.TrickplayOptions;
+            foreach (var width in options.WidthResolutions)
             {
                 cancellationToken.ThrowIfCancellationRequested();
-                await RefreshTrickplayData(video, replace, width, 10000/*todo conf*/, 10/*todo conf*/, 10/*todo conf*/, true/*todo conf*/, true/*todo conf*/, cancellationToken).ConfigureAwait(false);
+                await RefreshTrickplayDataInternal(
+                    video,
+                    replace,
+                    width,
+                    options,
+                    cancellationToken).ConfigureAwait(false);
             }
         }
 
-        private async Task RefreshTrickplayData(Video video, bool replace, int width, int interval, int tileWidth, int tileHeight, bool doHwAccel, bool doHwEncode, CancellationToken cancellationToken)
+        private async Task RefreshTrickplayDataInternal(
+            Video video,
+            bool replace,
+            int width,
+            TrickplayOptions options,
+            CancellationToken cancellationToken)
         {
-            if (!CanGenerateTrickplay(video, interval))
+            if (!CanGenerateTrickplay(video, options.Interval))
             {
                 return;
             }
@@ -108,10 +125,12 @@ namespace MediaBrowser.Providers.Trickplay
                     container,
                     mediaSource,
                     mediaStream,
-                    TimeSpan.FromMilliseconds(interval),
                     width,
-                    doHwAccel,
-                    doHwEncode,
+                    TimeSpan.FromMilliseconds(options.Interval),
+                    options.EnableHwAcceleration,
+                    options.ProcessThreads,
+                    options.Qscale,
+                    options.ProcessPriority,
                     _encodingHelper,
                     cancellationToken).ConfigureAwait(false);
 
@@ -127,7 +146,7 @@ namespace MediaBrowser.Providers.Trickplay
 
                 // Create tiles
                 var tilesTempDir = Path.Combine(imgTempDir, Guid.NewGuid().ToString("N"));
-                var tilesInfo = CreateTiles(images, width, interval, tileWidth, tileHeight, 100/* todo _config.JpegQuality*/, tilesTempDir, outputDir);
+                var tilesInfo = CreateTiles(images, width, options, tilesTempDir, outputDir);
 
                 // Save tiles info
                 try
@@ -166,7 +185,7 @@ namespace MediaBrowser.Providers.Trickplay
             }
         }
 
-        private TrickplayTilesInfo CreateTiles(List<FileSystemMetadata> images, int width, int interval, int tileWidth, int tileHeight, int quality, string workDir, string outputDir)
+        private TrickplayTilesInfo CreateTiles(List<FileSystemMetadata> images, int width, TrickplayOptions options, string workDir, string outputDir)
         {
             if (images.Count == 0)
             {
@@ -178,9 +197,9 @@ namespace MediaBrowser.Providers.Trickplay
             var tilesInfo = new TrickplayTilesInfo
             {
                 Width = width,
-                Interval = interval,
-                TileWidth = tileWidth,
-                TileHeight = tileHeight,
+                Interval = options.Interval,
+                TileWidth = options.TileWidth,
+                TileHeight = options.TileHeight,
                 TileCount = 0,
                 Bandwidth = 0
             };
@@ -244,7 +263,7 @@ namespace MediaBrowser.Providers.Trickplay
                 var tileGridPath = Path.Combine(workDir, $"{imgNo}.jpg");
                 using (var stream = File.OpenWrite(tileGridPath))
                 {
-                    tileGrid.Encode(stream, SKEncodedImageFormat.Jpeg, quality);
+                    tileGrid.Encode(stream, SKEncodedImageFormat.Jpeg, options.JpegQuality);
                 }
 
                 var bitrate = (int)Math.Ceiling((decimal)new FileInfo(tileGridPath).Length * 8 / tilesInfo.TileWidth / tilesInfo.TileHeight / (tilesInfo.Interval / 1000));
@@ -351,7 +370,7 @@ namespace MediaBrowser.Providers.Trickplay
             {
                 Directory.Move(source, destination);
             }
-            catch (System.IO.IOException)
+            catch (IOException)
             {
                 // Cross device move requires a copy
                 Directory.CreateDirectory(destination);

+ 10 - 6
MediaBrowser.Providers/Trickplay/TrickplayProvider.cs

@@ -7,6 +7,7 @@ using MediaBrowser.Controller.Entities.TV;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Controller.Trickplay;
+using MediaBrowser.Model.Configuration;
 using Microsoft.Extensions.Logging;
 
 namespace MediaBrowser.Providers.Trickplay
@@ -25,7 +26,7 @@ namespace MediaBrowser.Providers.Trickplay
         IForcedProvider
     {
         private readonly ILogger<TrickplayProvider> _logger;
-        private readonly IServerConfigurationManager _configurationManager;
+        private readonly IServerConfigurationManager _config;
         private readonly ITrickplayManager _trickplayManager;
         private readonly ILibraryManager _libraryManager;
 
@@ -33,17 +34,17 @@ namespace MediaBrowser.Providers.Trickplay
         /// Initializes a new instance of the <see cref="TrickplayProvider"/> class.
         /// </summary>
         /// <param name="logger">The logger.</param>
-        /// <param name="configurationManager">The configuration manager.</param>
+        /// <param name="config">The configuration manager.</param>
         /// <param name="trickplayManager">The trickplay manager.</param>
         /// <param name="libraryManager">The library manager.</param>
         public TrickplayProvider(
             ILogger<TrickplayProvider> logger,
-            IServerConfigurationManager configurationManager,
+            IServerConfigurationManager config,
             ITrickplayManager trickplayManager,
             ILibraryManager libraryManager)
         {
             _logger = logger;
-            _configurationManager = configurationManager;
+            _config = config;
             _trickplayManager = trickplayManager;
             _libraryManager = libraryManager;
         }
@@ -110,11 +111,14 @@ namespace MediaBrowser.Providers.Trickplay
                 return ItemUpdateType.None;
             }
 
-            // TODO: this is always blocking for metadata collection, make non-blocking option
-            if (true)
+            if (_config.Configuration.TrickplayOptions.ScanBehavior == TrickplayScanBehavior.Blocking)
             {
                 await _trickplayManager.RefreshTrickplayData(video, replace, cancellationToken).ConfigureAwait(false);
             }
+            else
+            {
+                _ = _trickplayManager.RefreshTrickplayData(video, replace, cancellationToken).ConfigureAwait(false);
+            }
 
             // The core doesn't need to trigger any save operations over this
             return ItemUpdateType.None;