2
0
Эх сурвалжийг харах

added image encoder methods

Luke Pulverenti 11 жил өмнө
parent
commit
1664de62c0

+ 2 - 0
MediaBrowser.Controller/MediaEncoding/ImageEncodingOptions.cs

@@ -13,6 +13,8 @@ namespace MediaBrowser.Controller.MediaEncoding
 
         public int? MaxHeight { get; set; }
 
+        public int? Quality { get; set; }
+        
         public string Format { get; set; }
     }
 }

+ 47 - 18
MediaBrowser.MediaEncoding/Encoder/ImageEncoder.cs

@@ -1,4 +1,5 @@
-using MediaBrowser.Controller.MediaEncoding;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.MediaEncoding;
 using MediaBrowser.Model.Logging;
 using System;
 using System.Diagnostics;
@@ -13,20 +14,39 @@ namespace MediaBrowser.MediaEncoding.Encoder
     {
         private readonly string _ffmpegPath;
         private readonly ILogger _logger;
+        private readonly IFileSystem _fileSystem;
+
         private readonly CultureInfo _usCulture = new CultureInfo("en-US");
 
-        private static readonly SemaphoreSlim ResourcePool = new SemaphoreSlim(5, 5);
+        private static readonly SemaphoreSlim ResourcePool = new SemaphoreSlim(10, 10);
 
-        public ImageEncoder(string ffmpegPath, ILogger logger)
+        public ImageEncoder(string ffmpegPath, ILogger logger, IFileSystem fileSystem)
         {
             _ffmpegPath = ffmpegPath;
             _logger = logger;
+            _fileSystem = fileSystem;
         }
 
         public async Task<Stream> EncodeImage(ImageEncodingOptions options, CancellationToken cancellationToken)
         {
             ValidateInput(options);
 
+            await ResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
+
+            try
+            {
+                return await EncodeImageInternal(options, cancellationToken).ConfigureAwait(false);
+            }
+            finally
+            {
+                ResourcePool.Release();
+            }
+        }
+
+        private async Task<Stream> EncodeImageInternal(ImageEncodingOptions options, CancellationToken cancellationToken)
+        {
+            ValidateInput(options);
+
             var process = new Process
             {
                 StartInfo = new ProcessStartInfo
@@ -38,11 +58,12 @@ namespace MediaBrowser.MediaEncoding.Encoder
                     WindowStyle = ProcessWindowStyle.Hidden,
                     ErrorDialog = false,
                     RedirectStandardOutput = true,
-                    RedirectStandardError = true
+                    RedirectStandardError = true,
+                    WorkingDirectory = Path.GetDirectoryName(options.InputPath)
                 }
             };
 
-            await ResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
+            _logger.Debug("ffmpeg " + process.StartInfo.Arguments);
 
             process.Start();
 
@@ -74,8 +95,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
                 }
             }
 
-            ResourcePool.Release();
-
             var exitCode = ranToCompletion ? process.ExitCode : -1;
 
             process.Dispose();
@@ -94,16 +113,21 @@ namespace MediaBrowser.MediaEncoding.Encoder
             memoryStream.Position = 0;
             return memoryStream;
         }
-
+        
         private string GetArguments(ImageEncodingOptions options)
         {
             var vfScale = GetFilterGraph(options);
-            var outputFormat = GetOutputFormat(options);
+            var outputFormat = GetOutputFormat(options.Format);
+
+            var quality = (options.Quality ?? 100) * .3;
+            quality = 31 - quality;
+            var qualityValue = Convert.ToInt32(Math.Max(quality, 1));
 
-            return string.Format("-i file:\"{0}\" {1} -f {2}",
-                options.InputPath,
+            return string.Format("-f image2 -i file:\"{3}\" -q:v {0} {1} -f image2pipe -vcodec {2} -",
+                qualityValue.ToString(_usCulture),
                 vfScale,
-                outputFormat);
+                outputFormat,
+                Path.GetFileName(options.InputPath));
         }
 
         private string GetFilterGraph(ImageEncodingOptions options)
@@ -121,7 +145,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
 
             if (options.MaxWidth.HasValue)
             {
-                widthScale = "min(iw," + options.MaxWidth.Value.ToString(_usCulture) + ")";
+                widthScale = "min(iw\\," + options.MaxWidth.Value.ToString(_usCulture) + ")";
             }
             else if (options.Width.HasValue)
             {
@@ -130,7 +154,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
 
             if (options.MaxHeight.HasValue)
             {
-                heightScale = "min(ih," + options.MaxHeight.Value.ToString(_usCulture) + ")";
+                heightScale = "min(ih\\," + options.MaxHeight.Value.ToString(_usCulture) + ")";
             }
             else if (options.Height.HasValue)
             {
@@ -139,15 +163,20 @@ namespace MediaBrowser.MediaEncoding.Encoder
 
             var scaleMethod = "lanczos";
 
-            return string.Format("-vf scale=\"{0}:{1}\" -sws_flags {2}", 
-                widthScale, 
+            return string.Format("-vf scale=\"{0}:{1}\" -sws_flags {2}",
+                widthScale,
                 heightScale,
                 scaleMethod);
         }
 
-        private string GetOutputFormat(ImageEncodingOptions options)
+        private string GetOutputFormat(string format)
         {
-            return options.Format;
+            if (string.Equals(format, "jpeg", StringComparison.OrdinalIgnoreCase) ||
+                string.Equals(format, "jpg", StringComparison.OrdinalIgnoreCase))
+            {
+                return "mjpeg";
+            }
+            return format;
         }
 
         private void ValidateInput(ImageEncodingOptions options)

+ 1 - 40
MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs

@@ -879,45 +879,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
             return memoryStream;
         }
 
-        /// <summary>
-        /// Starts the and wait for process.
-        /// </summary>
-        /// <param name="process">The process.</param>
-        /// <param name="timeout">The timeout.</param>
-        /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
-        private bool StartAndWaitForProcess(Process process, int timeout = 10000)
-        {
-            process.Start();
-
-            var ranToCompletion = process.WaitForExit(timeout);
-
-            if (!ranToCompletion)
-            {
-                try
-                {
-                    _logger.Info("Killing ffmpeg process");
-
-                    process.Kill();
-
-                    process.WaitForExit(1000);
-                }
-                catch (Win32Exception ex)
-                {
-                    _logger.ErrorException("Error killing process", ex);
-                }
-                catch (InvalidOperationException ex)
-                {
-                    _logger.ErrorException("Error killing process", ex);
-                }
-                catch (NotSupportedException ex)
-                {
-                    _logger.ErrorException("Error killing process", ex);
-                }
-            }
-
-            return ranToCompletion;
-        }
-
         /// <summary>
         /// Gets the file input argument.
         /// </summary>
@@ -950,7 +911,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
 
         public Task<Stream> EncodeImage(ImageEncodingOptions options, CancellationToken cancellationToken)
         {
-            return new ImageEncoder(FFMpegPath, _logger).EncodeImage(options, cancellationToken);
+            return new ImageEncoder(FFMpegPath, _logger, _fileSystem).EncodeImage(options, cancellationToken);
         }
 
         /// <summary>

+ 57 - 30
MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs

@@ -3,6 +3,7 @@ using MediaBrowser.Common.IO;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller.Drawing;
 using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.MediaEncoding;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Drawing;
 using MediaBrowser.Model.Entities;
@@ -52,12 +53,14 @@ namespace MediaBrowser.Server.Implementations.Drawing
         private readonly IFileSystem _fileSystem;
         private readonly IJsonSerializer _jsonSerializer;
         private readonly IServerApplicationPaths _appPaths;
+        private readonly IMediaEncoder _mediaEncoder;
 
-        public ImageProcessor(ILogger logger, IServerApplicationPaths appPaths, IFileSystem fileSystem, IJsonSerializer jsonSerializer)
+        public ImageProcessor(ILogger logger, IServerApplicationPaths appPaths, IFileSystem fileSystem, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder)
         {
             _logger = logger;
             _fileSystem = fileSystem;
             _jsonSerializer = jsonSerializer;
+            _mediaEncoder = mediaEncoder;
             _appPaths = appPaths;
 
             _saveImageSizeTimer = new Timer(SaveImageSizeCallback, null, Timeout.Infinite, Timeout.Infinite);
@@ -66,7 +69,7 @@ namespace MediaBrowser.Server.Implementations.Drawing
 
             try
             {
-                sizeDictionary = jsonSerializer.DeserializeFromFile<Dictionary<Guid, ImageSize>>(ImageSizeFile) ?? 
+                sizeDictionary = jsonSerializer.DeserializeFromFile<Dictionary<Guid, ImageSize>>(ImageSizeFile) ??
                     new Dictionary<Guid, ImageSize>();
             }
             catch (FileNotFoundException)
@@ -213,6 +216,39 @@ namespace MediaBrowser.Server.Implementations.Drawing
 
             try
             {
+                var hasPostProcessing = !string.IsNullOrEmpty(options.BackgroundColor) || options.UnplayedCount.HasValue || options.AddPlayedIndicator || options.PercentPlayed.HasValue;
+
+                if (!hasPostProcessing)
+                {
+                    using (var outputStream = await _mediaEncoder.EncodeImage(new ImageEncodingOptions
+                    {
+                        InputPath = originalImagePath,
+                        MaxHeight = options.MaxHeight,
+                        MaxWidth = options.MaxWidth,
+                        Height = options.Height,
+                        Width = options.Width,
+                        Quality = options.Quality,
+                        Format = options.OutputFormat == ImageOutputFormat.Original ? Path.GetExtension(originalImagePath).TrimStart('.') : options.OutputFormat.ToString().ToLower()
+
+                    }, CancellationToken.None).ConfigureAwait(false))
+                    {
+                        using (var outputMemoryStream = new MemoryStream())
+                        {
+                            // Save to the memory stream
+                            await outputStream.CopyToAsync(outputMemoryStream).ConfigureAwait(false);
+
+                            var bytes = outputMemoryStream.ToArray();
+
+                            await toStream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
+
+                            // kick off a task to cache the result
+                            await CacheResizedImage(cacheFilePath, bytes).ConfigureAwait(false);
+                        }
+
+                        return;
+                    }
+                }
+
                 using (var fileStream = _fileSystem.GetFileStream(originalImagePath, FileMode.Open, FileAccess.Read, FileShare.Read, true))
                 {
                     // Copy to memory stream to avoid Image locking file
@@ -241,8 +277,8 @@ namespace MediaBrowser.Server.Implementations.Drawing
                                     thumbnailGraph.SmoothingMode = SmoothingMode.HighQuality;
                                     thumbnailGraph.InterpolationMode = InterpolationMode.HighQualityBicubic;
                                     thumbnailGraph.PixelOffsetMode = PixelOffsetMode.HighQuality;
-                                    thumbnailGraph.CompositingMode = string.IsNullOrEmpty(options.BackgroundColor) && !options.UnplayedCount.HasValue && !options.AddPlayedIndicator && !options.PercentPlayed.HasValue ? 
-                                        CompositingMode.SourceCopy : 
+                                    thumbnailGraph.CompositingMode = !hasPostProcessing ?
+                                        CompositingMode.SourceCopy :
                                         CompositingMode.SourceOver;
 
                                     SetBackgroundColor(thumbnailGraph, options);
@@ -263,7 +299,7 @@ namespace MediaBrowser.Server.Implementations.Drawing
                                         await toStream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
 
                                         // kick off a task to cache the result
-                                        CacheResizedImage(cacheFilePath, bytes, semaphore);
+                                        await CacheResizedImage(cacheFilePath, bytes).ConfigureAwait(false);
                                     }
                                 }
                             }
@@ -272,11 +308,9 @@ namespace MediaBrowser.Server.Implementations.Drawing
                     }
                 }
             }
-            catch
+            finally
             {
                 semaphore.Release();
-
-                throw;
             }
         }
 
@@ -285,33 +319,26 @@ namespace MediaBrowser.Server.Implementations.Drawing
         /// </summary>
         /// <param name="cacheFilePath">The cache file path.</param>
         /// <param name="bytes">The bytes.</param>
-        /// <param name="semaphore">The semaphore.</param>
-        private void CacheResizedImage(string cacheFilePath, byte[] bytes, SemaphoreSlim semaphore)
+        /// <returns>Task.</returns>
+        private async Task CacheResizedImage(string cacheFilePath, byte[] bytes)
         {
-            Task.Run(async () =>
+            try
             {
-                try
-                {
-                    var parentPath = Path.GetDirectoryName(cacheFilePath);
+                var parentPath = Path.GetDirectoryName(cacheFilePath);
 
-                    Directory.CreateDirectory(parentPath);
+                Directory.CreateDirectory(parentPath);
 
-                    // Save to the cache location
-                    using (var cacheFileStream = _fileSystem.GetFileStream(cacheFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, true))
-                    {
-                        // Save to the filestream
-                        await cacheFileStream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
-                    }
-                }
-                catch (Exception ex)
-                {
-                    _logger.ErrorException("Error writing to image cache file {0}", ex, cacheFilePath);
-                }
-                finally
+                // Save to the cache location
+                using (var cacheFileStream = _fileSystem.GetFileStream(cacheFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, true))
                 {
-                    semaphore.Release();
+                    // Save to the filestream
+                    await cacheFileStream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
                 }
-            });
+            }
+            catch (Exception ex)
+            {
+                _logger.ErrorException("Error writing to image cache file {0}", ex, cacheFilePath);
+            }
         }
 
         /// <summary>
@@ -519,7 +546,7 @@ namespace MediaBrowser.Server.Implementations.Drawing
             {
                 filename += "iv=" + IndicatorVersion;
             }
-            
+
             if (!string.IsNullOrEmpty(backgroundColor))
             {
                 filename += "b=" + backgroundColor;

+ 7 - 7
MediaBrowser.ServerApplication/ApplicationHost.cs

@@ -473,7 +473,13 @@ namespace MediaBrowser.ServerApplication
             LocalizationManager = new LocalizationManager(ServerConfigurationManager, FileSystemManager);
             RegisterSingleInstance(LocalizationManager);
 
-            ImageProcessor = new ImageProcessor(LogManager.GetLogger("ImageProcessor"), ServerConfigurationManager.ApplicationPaths, FileSystemManager, JsonSerializer);
+            var innerProgress = new ActionableProgress<double>();
+            innerProgress.RegisterAction(p => progress.Report((.75 * p) + 15));
+
+            await RegisterMediaEncoder(innerProgress).ConfigureAwait(false);
+            progress.Report(90);
+
+            ImageProcessor = new ImageProcessor(LogManager.GetLogger("ImageProcessor"), ServerConfigurationManager.ApplicationPaths, FileSystemManager, JsonSerializer, MediaEncoder);
             RegisterSingleInstance(ImageProcessor);
 
             DtoService = new DtoService(Logger, LibraryManager, UserManager, UserDataManager, ItemRepository, ImageProcessor, ServerConfigurationManager, FileSystemManager, ProviderManager);
@@ -487,12 +493,6 @@ namespace MediaBrowser.ServerApplication
 
             progress.Report(15);
 
-            var innerProgress = new ActionableProgress<double>();
-            innerProgress.RegisterAction(p => progress.Report((.75 * p) + 15));
-
-            await RegisterMediaEncoder(innerProgress).ConfigureAwait(false);
-            progress.Report(90);
-
             EncodingManager = new EncodingManager(ServerConfigurationManager, FileSystemManager, Logger, ItemRepository,
                 MediaEncoder);
             RegisterSingleInstance(EncodingManager);

+ 3 - 0
MediaBrowser.sln

@@ -267,4 +267,7 @@ Global
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
 	EndGlobalSection
+	GlobalSection(Performance) = preSolution
+		HasPerformanceSessions = true
+	EndGlobalSection
 EndGlobal