From 39ea2adbc56e164c0716e076632239186b5c3a44 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 27 Mar 2014 15:30:21 -0400 Subject: [PATCH] create separate media encoding project --- .../Playback/BaseStreamingService.cs | 38 +++---- .../Playback/Hls/BaseHlsService.cs | 2 +- .../BaseProgressiveStreamingService.cs | 5 + .../MediaEncoding/IMediaEncoder.cs | 13 ++- MediaBrowser.Dlna/DlnaManager.cs | 2 +- MediaBrowser.Dlna/PlayTo/Device.cs | 2 +- MediaBrowser.Dlna/Profiles/DefaultProfile.cs | 18 ---- MediaBrowser.Dlna/Profiles/Xbox360Profile.cs | 14 --- MediaBrowser.Dlna/Profiles/Xml/Default.xml | 7 -- MediaBrowser.Dlna/Profiles/Xml/Denon AVR.xml | 7 -- .../Profiles/Xml/Linksys DMA2100.xml | 7 -- MediaBrowser.Dlna/Profiles/Xml/Xbox 360.xml | 2 - MediaBrowser.Dlna/Profiles/Xml/Xbox One.xml | 7 -- MediaBrowser.Dlna/Profiles/Xml/foobar2000.xml | 7 -- .../BdInfo/BdInfoExaminer.cs | 2 +- .../Encoder}/MediaEncoder.cs | 101 +++++++++++------- .../MediaBrowser.MediaEncoding.csproj | 80 ++++++++++++++ .../Properties/AssemblyInfo.cs | 36 +++++++ MediaBrowser.MediaEncoding/packages.config | 4 + .../MediaInfo/AudioImageProvider.cs | 2 +- .../MediaInfo/FFProbeVideoInfo.cs | 2 - .../MediaInfo/VideoImageProvider.cs | 2 +- .../IO/LibraryMonitor.cs | 58 +++++----- ...MediaBrowser.Server.Implementations.csproj | 10 -- .../MediaEncoder/EncodingManager.cs | 2 +- .../SqliteMediaStreamsRepository.cs | 2 + .../packages.config | 1 - .../ApplicationHost.cs | 8 +- .../MediaBrowser.ServerApplication.csproj | 4 + MediaBrowser.sln | 16 +++ 30 files changed, 277 insertions(+), 184 deletions(-) rename {MediaBrowser.Server.Implementations => MediaBrowser.MediaEncoding}/BdInfo/BdInfoExaminer.cs (99%) rename {MediaBrowser.Server.Implementations/MediaEncoder => MediaBrowser.MediaEncoding/Encoder}/MediaEncoder.cs (91%) create mode 100644 MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj create mode 100644 MediaBrowser.MediaEncoding/Properties/AssemblyInfo.cs create mode 100644 MediaBrowser.MediaEncoding/packages.config diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 2002e594c7..ceb96d226c 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -316,6 +316,7 @@ namespace MediaBrowser.Api.Playback /// /// The state. /// The video codec. + /// if set to true [is HLS]. /// System.String. protected string GetVideoQualityParam(StreamState state, string videoCodec, bool isHls) { @@ -340,20 +341,17 @@ namespace MediaBrowser.Api.Playback break; } - if (!isHls) + switch (qualitySetting) { - switch (qualitySetting) - { - case EncodingQuality.HighSpeed: - param += " -crf 23"; - break; - case EncodingQuality.HighQuality: - param += " -crf 20"; - break; - case EncodingQuality.MaxQuality: - param += " -crf 18"; - break; - } + case EncodingQuality.HighSpeed: + param += " -crf 23"; + break; + case EncodingQuality.HighQuality: + param += " -crf 20"; + break; + case EncodingQuality.MaxQuality: + param += " -crf 18"; + break; } } @@ -1032,11 +1030,6 @@ namespace MediaBrowser.Api.Playback { var hasFixedResolution = state.VideoRequest.HasFixedResolution; - if (isHls) - { - return string.Format(" -b:v {0} -maxrate ({0}*.80) -bufsize {0}", bitrate.Value.ToString(UsCulture)); - } - if (string.Equals(videoCodec, "libvpx", StringComparison.OrdinalIgnoreCase)) { if (hasFixedResolution) @@ -1047,7 +1040,6 @@ namespace MediaBrowser.Api.Playback // With vpx when crf is used, b:v becomes a max rate // https://trac.ffmpeg.org/wiki/vpxEncodingGuide return string.Format(" -b:v {0}", bitrate.Value.ToString(UsCulture)); - //return string.Format(" -minrate:v ({0}*.95) -maxrate:v ({0}*1.05) -bufsize:v {0} -b:v {0}", bitrate.Value.ToString(UsCulture)); } if (string.Equals(videoCodec, "msmpeg4", StringComparison.OrdinalIgnoreCase)) @@ -1055,13 +1047,17 @@ namespace MediaBrowser.Api.Playback return string.Format(" -b:v {0}", bitrate.Value.ToString(UsCulture)); } - // H264 if (hasFixedResolution) { + if (isHls) + { + return string.Format(" -b:v {0} -maxrate ({0}*.80) -bufsize {0}", bitrate.Value.ToString(UsCulture)); + } + return string.Format(" -b:v {0}", bitrate.Value.ToString(UsCulture)); } - + return string.Format(" -maxrate {0} -bufsize {1}", bitrate.Value.ToString(UsCulture), (bitrate.Value * 2).ToString(UsCulture)); diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs index 198376d6a1..96b36ac7fc 100644 --- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs @@ -200,7 +200,7 @@ namespace MediaBrowser.Api.Playback.Hls builder.AppendLine("#EXTM3U"); // Pad a little to satisfy the apple hls validator - var paddedBitrate = Convert.ToInt32(bitrate * 1.05); + var paddedBitrate = Convert.ToInt32(bitrate * 1.15); // Main stream builder.AppendLine("#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=" + paddedBitrate.ToString(UsCulture)); diff --git a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs index 78b3f29481..b4fe9a094f 100644 --- a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs +++ b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs @@ -266,6 +266,11 @@ namespace MediaBrowser.Api.Playback.Progressive return result; } + /// + /// Gets the length of the estimated content. + /// + /// The state. + /// System.Nullable{System.Int64}. private long? GetEstimatedContentLength(StreamState state) { var totalBitrate = 0; diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs index 119688fa70..657e52e5a8 100644 --- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs +++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs @@ -24,16 +24,23 @@ namespace MediaBrowser.Controller.MediaEncoding string Version { get; } /// - /// Extracts the image. + /// Extracts the audio image. + /// + /// The path. + /// The cancellation token. + /// Task{Stream}. + Task ExtractAudioImage(string path, CancellationToken cancellationToken); + + /// + /// Extracts the video image. /// /// The input files. /// The type. - /// if set to true [is audio]. /// The threed format. /// The offset. /// The cancellation token. /// Task{Stream}. - Task ExtractImage(string[] inputFiles, InputType type, bool isAudio, Video3DFormat? threedFormat, TimeSpan? offset, CancellationToken cancellationToken); + Task ExtractVideoImage(string[] inputFiles, InputType type, Video3DFormat? threedFormat, TimeSpan? offset, CancellationToken cancellationToken); /// /// Extracts the text subtitle. diff --git a/MediaBrowser.Dlna/DlnaManager.cs b/MediaBrowser.Dlna/DlnaManager.cs index edccc71c99..ec9ecb9ef2 100644 --- a/MediaBrowser.Dlna/DlnaManager.cs +++ b/MediaBrowser.Dlna/DlnaManager.cs @@ -197,7 +197,7 @@ namespace MediaBrowser.Dlna throw new ArgumentNullException("headers"); } - return GetProfiles().FirstOrDefault(i => IsMatch(headers, i.Identification)); + return GetProfiles().FirstOrDefault(i => i.Identification != null && IsMatch(headers, i.Identification)); } private bool IsMatch(IDictionary headers, DeviceIdentification profileInfo) diff --git a/MediaBrowser.Dlna/PlayTo/Device.cs b/MediaBrowser.Dlna/PlayTo/Device.cs index fa0bfbca8e..c0f88f285e 100644 --- a/MediaBrowser.Dlna/PlayTo/Device.cs +++ b/MediaBrowser.Dlna/PlayTo/Device.cs @@ -631,7 +631,7 @@ namespace MediaBrowser.Dlna.PlayTo RendererCommands = TransportCommands.Create(document); } - internal TransportCommands AvCommands + private TransportCommands AvCommands { get; set; diff --git a/MediaBrowser.Dlna/Profiles/DefaultProfile.cs b/MediaBrowser.Dlna/Profiles/DefaultProfile.cs index 0efe187554..6b5513e289 100644 --- a/MediaBrowser.Dlna/Profiles/DefaultProfile.cs +++ b/MediaBrowser.Dlna/Profiles/DefaultProfile.cs @@ -57,24 +57,6 @@ namespace MediaBrowser.Dlna.Profiles Type = DlnaProfileType.Video } }; - - CodecProfiles = new[] - { - new CodecProfile - { - Type = CodecType.VideoCodec, - Conditions = new [] - { - new ProfileCondition - { - Condition = ProfileConditionType.LessThanEqual, - Property = ProfileConditionValue.VideoLevel, - Value = "3", - IsRequired = false - } - } - } - }; } } } diff --git a/MediaBrowser.Dlna/Profiles/Xbox360Profile.cs b/MediaBrowser.Dlna/Profiles/Xbox360Profile.cs index 640317fbcf..ed9edeb27a 100644 --- a/MediaBrowser.Dlna/Profiles/Xbox360Profile.cs +++ b/MediaBrowser.Dlna/Profiles/Xbox360Profile.cs @@ -229,13 +229,6 @@ namespace MediaBrowser.Dlna.Profiles Property = ProfileConditionValue.VideoBitrate, Value = "10240000", IsRequired = false - }, - new ProfileCondition - { - Condition = ProfileConditionType.LessThanEqual, - Property = ProfileConditionValue.VideoLevel, - Value = "3", - IsRequired = false } } }, @@ -271,13 +264,6 @@ namespace MediaBrowser.Dlna.Profiles Property = ProfileConditionValue.VideoBitrate, Value = "15360000", IsRequired = false - }, - new ProfileCondition - { - Condition = ProfileConditionType.LessThanEqual, - Property = ProfileConditionValue.VideoLevel, - Value = "3", - IsRequired = false } } }, diff --git a/MediaBrowser.Dlna/Profiles/Xml/Default.xml b/MediaBrowser.Dlna/Profiles/Xml/Default.xml index 52b92c6f35..60f22a64a9 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Default.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Default.xml @@ -30,12 +30,5 @@ - - - - - - - \ No newline at end of file diff --git a/MediaBrowser.Dlna/Profiles/Xml/Denon AVR.xml b/MediaBrowser.Dlna/Profiles/Xml/Denon AVR.xml index 3332e4d450..28bb3289e2 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Denon AVR.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Denon AVR.xml @@ -34,12 +34,5 @@ - - - - - - - \ No newline at end of file diff --git a/MediaBrowser.Dlna/Profiles/Xml/Linksys DMA2100.xml b/MediaBrowser.Dlna/Profiles/Xml/Linksys DMA2100.xml index b19cb0f7b4..477fee2dde 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Linksys DMA2100.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Linksys DMA2100.xml @@ -34,12 +34,5 @@ - - - - - - - \ No newline at end of file diff --git a/MediaBrowser.Dlna/Profiles/Xml/Xbox 360.xml b/MediaBrowser.Dlna/Profiles/Xml/Xbox 360.xml index e7839ca777..ea25391976 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Xbox 360.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Xbox 360.xml @@ -73,7 +73,6 @@ - @@ -82,7 +81,6 @@ - diff --git a/MediaBrowser.Dlna/Profiles/Xml/Xbox One.xml b/MediaBrowser.Dlna/Profiles/Xml/Xbox One.xml index 5243218fb6..ef4fec99ac 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Xbox One.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Xbox One.xml @@ -32,13 +32,6 @@ - - - - - - - diff --git a/MediaBrowser.Dlna/Profiles/Xml/foobar2000.xml b/MediaBrowser.Dlna/Profiles/Xml/foobar2000.xml index 126d7fe73f..3e6623ac48 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/foobar2000.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/foobar2000.xml @@ -36,12 +36,5 @@ - - - - - - - \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/BdInfo/BdInfoExaminer.cs b/MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs similarity index 99% rename from MediaBrowser.Server.Implementations/BdInfo/BdInfoExaminer.cs rename to MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs index 18f1b92fe7..b15b8d15dd 100644 --- a/MediaBrowser.Server.Implementations/BdInfo/BdInfoExaminer.cs +++ b/MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs @@ -5,7 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; -namespace MediaBrowser.Server.Implementations.BdInfo +namespace MediaBrowser.MediaEncoding.BdInfo { /// /// Class BdInfoExaminer diff --git a/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs similarity index 91% rename from MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs rename to MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index c646d80bce..7cabf23c48 100644 --- a/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -6,17 +6,15 @@ using MediaBrowser.Model.Logging; using MediaBrowser.Model.Serialization; using System; using System.Collections.Concurrent; -using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Globalization; using System.IO; -using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; -namespace MediaBrowser.Server.Implementations.MediaEncoder +namespace MediaBrowser.MediaEncoding.Encoder { /// /// Class MediaEncoder @@ -53,6 +51,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder /// The FF probe resource pool /// private readonly SemaphoreSlim _ffProbeResourcePool = new SemaphoreSlim(2, 2); + private readonly IFileSystem _fileSystem; public string FFMpegPath { get; private set; } @@ -62,7 +61,8 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder public string Version { get; private set; } public MediaEncoder(ILogger logger, IApplicationPaths appPaths, - IJsonSerializer jsonSerializer, string ffMpegPath, string ffProbePath, string version, IFileSystem fileSystem) + IJsonSerializer jsonSerializer, string ffMpegPath, string ffProbePath, string version, + IFileSystem fileSystem) { _logger = logger; _appPaths = appPaths; @@ -85,7 +85,8 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder /// /// The _semaphoreLocks /// - private readonly ConcurrentDictionary _semaphoreLocks = new ConcurrentDictionary(); + private readonly ConcurrentDictionary _semaphoreLocks = + new ConcurrentDictionary(); /// /// Gets the lock. @@ -106,10 +107,10 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder /// The cancellation token. /// Task. public Task GetMediaInfo(string[] inputFiles, InputType type, bool isAudio, - CancellationToken cancellationToken) + CancellationToken cancellationToken) { return GetMediaInfoInternal(GetInputArgument(inputFiles, type), !isAudio, - GetProbeSizeArgument(type), cancellationToken); + GetProbeSizeArgument(type), cancellationToken); } /// @@ -172,8 +173,8 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder /// Task{MediaInfoResult}. /// private async Task GetMediaInfoInternal(string inputPath, bool extractChapters, - string probeSizeArgument, - CancellationToken cancellationToken) + string probeSizeArgument, + CancellationToken cancellationToken) { var args = extractChapters ? "{0} -i {1} -threads 0 -v info -print_format json -show_streams -show_chapters -show_format" @@ -191,7 +192,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder RedirectStandardError = true, FileName = FFProbePath, Arguments = string.Format(args, - probeSizeArgument, inputPath).Trim(), + probeSizeArgument, inputPath).Trim(), WindowStyle = ProcessWindowStyle.Hidden, ErrorDialog = false @@ -225,7 +226,8 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder { process.BeginErrorReadLine(); - result = _jsonSerializer.DeserializeFromStream(process.StandardOutput.BaseStream); + result = + _jsonSerializer.DeserializeFromStream(process.StandardOutput.BaseStream); } catch { @@ -295,7 +297,8 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder /// The language. /// The cancellation token. /// Task. - public async Task ConvertTextSubtitleToAss(string inputPath, string outputPath, string language, CancellationToken cancellationToken) + public async Task ConvertTextSubtitleToAss(string inputPath, string outputPath, string language, + CancellationToken cancellationToken) { var semaphore = GetLock(outputPath); @@ -340,33 +343,35 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder } - var encodingParam = string.IsNullOrEmpty(language) ? string.Empty : - GetSubtitleLanguageEncodingParam(language) + " "; + var encodingParam = string.IsNullOrEmpty(language) + ? string.Empty + : GetSubtitleLanguageEncodingParam(language) + " "; var process = new Process + { + StartInfo = new ProcessStartInfo { - StartInfo = new ProcessStartInfo - { - RedirectStandardOutput = false, - RedirectStandardError = true, - - CreateNoWindow = true, - UseShellExecute = false, - FileName = FFMpegPath, - Arguments = - string.Format("{0} -i \"{1}\" -c:s ass \"{2}\"", encodingParam, inputPath, outputPath), - - WindowStyle = ProcessWindowStyle.Hidden, - ErrorDialog = false - } - }; + RedirectStandardOutput = false, + RedirectStandardError = true, + + CreateNoWindow = true, + UseShellExecute = false, + FileName = FFMpegPath, + Arguments = + string.Format("{0} -i \"{1}\" -c:s ass \"{2}\"", encodingParam, inputPath, outputPath), + + WindowStyle = ProcessWindowStyle.Hidden, + ErrorDialog = false + } + }; _logger.Debug("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments); var logFilePath = Path.Combine(_appPaths.LogDirectoryPath, "ffmpeg-sub-convert-" + Guid.NewGuid() + ".txt"); Directory.CreateDirectory(Path.GetDirectoryName(logFilePath)); - var logFileStream = _fileSystem.GetFileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, true); + var logFileStream = _fileSystem.GetFileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, + true); try { @@ -525,7 +530,8 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder /// The cancellation token. /// Task. /// Must use inputPath list overload - public async Task ExtractTextSubtitle(string[] inputFiles, InputType type, int subtitleStreamIndex, bool copySubtitleStream, string outputPath, CancellationToken cancellationToken) + public async Task ExtractTextSubtitle(string[] inputFiles, InputType type, int subtitleStreamIndex, + bool copySubtitleStream, string outputPath, CancellationToken cancellationToken) { var semaphore = GetLock(outputPath); @@ -535,7 +541,9 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder { if (!File.Exists(outputPath)) { - await ExtractTextSubtitleInternal(GetInputArgument(inputFiles, type), subtitleStreamIndex, copySubtitleStream, outputPath, cancellationToken).ConfigureAwait(false); + await + ExtractTextSubtitleInternal(GetInputArgument(inputFiles, type), subtitleStreamIndex, + copySubtitleStream, outputPath, cancellationToken).ConfigureAwait(false); } } finally @@ -559,7 +567,8 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder /// or /// cancellationToken /// - private async Task ExtractTextSubtitleInternal(string inputPath, int subtitleStreamIndex, bool copySubtitleStream, string outputPath, CancellationToken cancellationToken) + private async Task ExtractTextSubtitleInternal(string inputPath, int subtitleStreamIndex, + bool copySubtitleStream, string outputPath, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(inputPath)) { @@ -571,11 +580,13 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder throw new ArgumentNullException("outputPath"); } - string processArgs = string.Format("-i {0} -map 0:{1} -an -vn -c:s ass \"{2}\"", inputPath, subtitleStreamIndex, outputPath); + string processArgs = string.Format("-i {0} -map 0:{1} -an -vn -c:s ass \"{2}\"", inputPath, + subtitleStreamIndex, outputPath); if (copySubtitleStream) { - processArgs = string.Format("-i {0} -map 0:{1} -an -vn -c:s copy \"{2}\"", inputPath, subtitleStreamIndex, outputPath); + processArgs = string.Format("-i {0} -map 0:{1} -an -vn -c:s copy \"{2}\"", inputPath, + subtitleStreamIndex, outputPath); } var process = new Process @@ -600,7 +611,8 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder var logFilePath = Path.Combine(_appPaths.LogDirectoryPath, "ffmpeg-sub-extract-" + Guid.NewGuid() + ".txt"); Directory.CreateDirectory(Path.GetDirectoryName(logFilePath)); - var logFileStream = _fileSystem.GetFileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, true); + var logFileStream = _fileSystem.GetFileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, + true); try { @@ -715,7 +727,18 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder } } - public async Task ExtractImage(string[] inputFiles, InputType type, bool isAudio, + public Task ExtractAudioImage(string path, CancellationToken cancellationToken) + { + return ExtractImage(new[] { path }, InputType.File, true, null, null, cancellationToken); + } + + public Task ExtractVideoImage(string[] inputFiles, InputType type, Video3DFormat? threedFormat, + TimeSpan? offset, CancellationToken cancellationToken) + { + return ExtractImage(inputFiles, type, false, threedFormat, offset, cancellationToken); + } + + private async Task ExtractImage(string[] inputFiles, InputType type, bool isAudio, Video3DFormat? threedFormat, TimeSpan? offset, CancellationToken cancellationToken) { var resourcePool = isAudio ? _audioImageResourcePool : _videoImageResourcePool; @@ -773,7 +796,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder } // 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 args = useIFrame ? string.Format("-i {0} -threads 0 -v quiet -vframes 1 -vf \"{2},thumbnail\" -f image2 \"{1}\"", inputPath, "-", vf) : + var args = useIFrame ? string.Format("-i {0} -threads 0 -v quiet -vframes 1 -vf \"{2},thumbnail=80\" -f image2 \"{1}\"", inputPath, "-", vf) : string.Format("-i {0} -threads 0 -v quiet -vframes 1 -vf \"{2}\" -f image2 \"{1}\"", inputPath, "-", vf); var probeSize = GetProbeSizeArgument(type); @@ -834,7 +857,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder _logger.ErrorException("Error killing process", ex); } } - + resourcePool.Release(); var exitCode = ranToCompletion ? process.ExitCode : -1; diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj new file mode 100644 index 0000000000..d92522bf01 --- /dev/null +++ b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj @@ -0,0 +1,80 @@ + + + + + Debug + AnyCPU + {0BD82FA6-EB8A-4452-8AF5-74F9C3849451} + Library + Properties + MediaBrowser.MediaEncoding + MediaBrowser.MediaEncoding + v4.5 + 512 + ..\ + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\MediaBrowser.BdInfo.1.0.0.10\lib\net35\BDInfo.dll + + + ..\packages\MediaBrowser.BdInfo.1.0.0.10\lib\net35\DvdLib.dll + + + + + + + + + + + + + + + + + {9142eefa-7570-41e1-bfcc-468bb571af2f} + MediaBrowser.Common + + + {17e1f4e6-8abd-4fe5-9ecf-43d4b6087ba2} + MediaBrowser.Controller + + + {7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b} + MediaBrowser.Model + + + + + + + + + \ No newline at end of file diff --git a/MediaBrowser.MediaEncoding/Properties/AssemblyInfo.cs b/MediaBrowser.MediaEncoding/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..6616e46acc --- /dev/null +++ b/MediaBrowser.MediaEncoding/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("MediaBrowser.MediaEncoding")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("MediaBrowser.MediaEncoding")] +[assembly: AssemblyCopyright("Copyright © 2014")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("05f49ab9-2a90-4332-9d41-7817a9cccd90")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/MediaBrowser.MediaEncoding/packages.config b/MediaBrowser.MediaEncoding/packages.config new file mode 100644 index 0000000000..6e52b72b8f --- /dev/null +++ b/MediaBrowser.MediaEncoding/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs index b3c3f278eb..b2ca97f55c 100644 --- a/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs @@ -94,7 +94,7 @@ namespace MediaBrowser.Providers.MediaInfo { Directory.CreateDirectory(Path.GetDirectoryName(path)); - using (var stream = await _mediaEncoder.ExtractImage(new[] { item.Path }, InputType.File, true, null, null, cancellationToken).ConfigureAwait(false)) + using (var stream = await _mediaEncoder.ExtractAudioImage(item.Path, cancellationToken).ConfigureAwait(false)) { using (var fileStream = _fileSystem.GetFileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, true)) { diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs index 58fe7f66d4..cb326c5ad0 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs @@ -113,8 +113,6 @@ namespace MediaBrowser.Providers.MediaInfo { cancellationToken.ThrowIfCancellationRequested(); - cancellationToken.ThrowIfCancellationRequested(); - var idString = item.Id.ToString("N"); var cachePath = Path.Combine(_appPaths.CachePath, "ffprobe-video", diff --git a/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs b/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs index 3547584979..70daa3f519 100644 --- a/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs @@ -93,7 +93,7 @@ namespace MediaBrowser.Providers.MediaInfo var inputPath = MediaEncoderHelpers.GetInputArgument(item.Path, item.LocationType == LocationType.Remote, item.VideoType, item.IsoType, isoMount, item.PlayableStreamFileNames, out type); - var stream = await _mediaEncoder.ExtractImage(inputPath, type, false, item.Video3DFormat, imageOffset, cancellationToken).ConfigureAwait(false); + var stream = await _mediaEncoder.ExtractVideoImage(inputPath, type, item.Video3DFormat, imageOffset, cancellationToken).ConfigureAwait(false); return new DynamicImageResponse { diff --git a/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs b/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs index 0a0b3f4bcd..9279fd8d73 100644 --- a/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs +++ b/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs @@ -249,17 +249,24 @@ namespace MediaBrowser.Server.Implementations.IO // Creating a FileSystemWatcher over the LAN can take hundreds of milliseconds, so wrap it in a Task to do them all in parallel Task.Run(() => { - var newWatcher = new FileSystemWatcher(path, "*") { IncludeSubdirectories = true, InternalBufferSize = 32767 }; + try + { + var newWatcher = new FileSystemWatcher(path, "*") + { + IncludeSubdirectories = true, + InternalBufferSize = 32767 + }; - newWatcher.Created += watcher_Changed; - newWatcher.Deleted += watcher_Changed; - newWatcher.Renamed += watcher_Changed; - newWatcher.Changed += watcher_Changed; + newWatcher.NotifyFilter = NotifyFilters.CreationTime | NotifyFilters.DirectoryName | + NotifyFilters.FileName | NotifyFilters.LastWrite | NotifyFilters.Size; - newWatcher.Error += watcher_Error; + newWatcher.Created += watcher_Changed; + newWatcher.Deleted += watcher_Changed; + newWatcher.Renamed += watcher_Changed; + newWatcher.Changed += watcher_Changed; + + newWatcher.Error += watcher_Error; - try - { if (_fileSystemWatchers.TryAdd(path, newWatcher)) { newWatcher.EnableRaisingEvents = true; @@ -272,11 +279,7 @@ namespace MediaBrowser.Server.Implementations.IO } } - catch (IOException ex) - { - Logger.ErrorException("Error watching path: {0}", ex, path); - } - catch (PlatformNotSupportedException ex) + catch (Exception ex) { Logger.ErrorException("Error watching path: {0}", ex, path); } @@ -346,7 +349,9 @@ namespace MediaBrowser.Server.Implementations.IO { try { - OnWatcherChanged(e); + Logger.Debug("Watcher sees change of type " + e.ChangeType + " to " + e.FullPath); + + ReportFileSystemChanged(e.FullPath); } catch (Exception ex) { @@ -354,13 +359,6 @@ namespace MediaBrowser.Server.Implementations.IO } } - private void OnWatcherChanged(FileSystemEventArgs e) - { - Logger.Debug("Watcher sees change of type " + e.ChangeType + " to " + e.FullPath); - - ReportFileSystemChanged(e.FullPath); - } - public void ReportFileSystemChanged(string path) { if (string.IsNullOrEmpty(path)) @@ -370,12 +368,9 @@ namespace MediaBrowser.Server.Implementations.IO var filename = Path.GetFileName(path); - // Ignore certain files - if (!string.IsNullOrEmpty(filename) && _alwaysIgnoreFiles.Contains(filename, StringComparer.OrdinalIgnoreCase)) - { - return; - } + var monitorPath = !(!string.IsNullOrEmpty(filename) && _alwaysIgnoreFiles.Contains(filename, StringComparer.OrdinalIgnoreCase)); + // Ignore certain files var tempIgnorePaths = _tempIgnoredPaths.Keys.ToList(); // If the parent of an ignored path has a change event, ignore that too @@ -416,12 +411,15 @@ namespace MediaBrowser.Server.Implementations.IO })) { - return; + monitorPath = false; } - // Avoid implicitly captured closure - var affectedPath = path; - _affectedPaths.AddOrUpdate(path, path, (key, oldValue) => affectedPath); + if (monitorPath) + { + // Avoid implicitly captured closure + var affectedPath = path; + _affectedPaths.AddOrUpdate(path, path, (key, oldValue) => affectedPath); + } lock (_timerLock) { diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index 73a12caf2c..ea7ef2ed65 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -48,14 +48,6 @@ ..\packages\Alchemy.2.2.1\lib\net40\Alchemy.dll - - False - ..\packages\MediaBrowser.BdInfo.1.0.0.10\lib\net35\BDInfo.dll - - - False - ..\packages\MediaBrowser.BdInfo.1.0.0.10\lib\net35\DvdLib.dll - False ..\packages\Mono.Nat.1.2.3\lib\Net40\Mono.Nat.dll @@ -105,7 +97,6 @@ Properties\SharedVersion.cs - @@ -191,7 +182,6 @@ - diff --git a/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs b/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs index f74865d2c7..7237ffee5c 100644 --- a/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs +++ b/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs @@ -178,7 +178,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder { Directory.CreateDirectory(Path.GetDirectoryName(path)); - using (var stream = await _encoder.ExtractImage(inputPath, type, false, video.Video3DFormat, time, cancellationToken).ConfigureAwait(false)) + using (var stream = await _encoder.ExtractVideoImage(inputPath, type, video.Video3DFormat, time, cancellationToken).ConfigureAwait(false)) { using (var fileStream = _fileSystem.GetFileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, true)) { diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteMediaStreamsRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteMediaStreamsRepository.cs index f4e7fd0a6b..fde1e7f216 100644 --- a/MediaBrowser.Server.Implementations/Persistence/SqliteMediaStreamsRepository.cs +++ b/MediaBrowser.Server.Implementations/Persistence/SqliteMediaStreamsRepository.cs @@ -37,6 +37,8 @@ namespace MediaBrowser.Server.Implementations.Persistence var createTableCommand = "create table if not exists mediastreams "; + // Add PixelFormat column + createTableCommand += "(ItemId GUID, StreamIndex INT, StreamType TEXT, Codec TEXT, Language TEXT, ChannelLayout TEXT, Profile TEXT, AspectRatio TEXT, Path TEXT, IsInterlaced BIT, BitRate INT NULL, Channels INT NULL, SampleRate INT NULL, IsDefault BIT, IsForced BIT, IsExternal BIT, Height INT NULL, Width INT NULL, AverageFrameRate FLOAT NULL, RealFrameRate FLOAT NULL, Level FLOAT NULL, PRIMARY KEY (ItemId, StreamIndex))"; string[] queries = { diff --git a/MediaBrowser.Server.Implementations/packages.config b/MediaBrowser.Server.Implementations/packages.config index f04536190b..d82e880c98 100644 --- a/MediaBrowser.Server.Implementations/packages.config +++ b/MediaBrowser.Server.Implementations/packages.config @@ -1,7 +1,6 @@  - diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs index fe7b17d1dd..dff6242d1a 100644 --- a/MediaBrowser.ServerApplication/ApplicationHost.cs +++ b/MediaBrowser.ServerApplication/ApplicationHost.cs @@ -33,13 +33,14 @@ using MediaBrowser.Controller.Sorting; using MediaBrowser.Controller.Themes; using MediaBrowser.Dlna; using MediaBrowser.Dlna.PlayTo; +using MediaBrowser.MediaEncoding.BdInfo; +using MediaBrowser.MediaEncoding.Encoder; using MediaBrowser.Model.Logging; using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.System; using MediaBrowser.Model.Updates; using MediaBrowser.Providers.Manager; using MediaBrowser.Server.Implementations; -using MediaBrowser.Server.Implementations.BdInfo; using MediaBrowser.Server.Implementations.Channels; using MediaBrowser.Server.Implementations.Collections; using MediaBrowser.Server.Implementations.Configuration; @@ -815,7 +816,10 @@ namespace MediaBrowser.ServerApplication // Server implementations list.Add(typeof(ServerApplicationPaths).Assembly); - // Dlna implementations + // MediaEncoding + list.Add(typeof(MediaEncoder).Assembly); + + // Dlna list.Add(typeof(PlayToServerEntryPoint).Assembly); list.AddRange(Assemblies.GetAssembliesWithParts()); diff --git a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj index 227d0dd1d3..d999841ca7 100644 --- a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj +++ b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj @@ -195,6 +195,10 @@ {734098eb-6dc1-4dd0-a1ca-3140dcd2737c} MediaBrowser.Dlna + + {0bd82fa6-eb8a-4452-8af5-74f9c3849451} + MediaBrowser.MediaEncoding + {7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b} MediaBrowser.Model diff --git a/MediaBrowser.sln b/MediaBrowser.sln index 7ac1580659..7dc06fb0ce 100644 --- a/MediaBrowser.sln +++ b/MediaBrowser.sln @@ -41,6 +41,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.ServerApplicat EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Dlna", "MediaBrowser.Dlna\MediaBrowser.Dlna.csproj", "{734098EB-6DC1-4DD0-A1CA-3140DCD2737C}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.MediaEncoding", "MediaBrowser.MediaEncoding\MediaBrowser.MediaEncoding.csproj", "{0BD82FA6-EB8A-4452-8AF5-74F9C3849451}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -247,6 +249,20 @@ Global {734098EB-6DC1-4DD0-A1CA-3140DCD2737C}.Release|Win32.ActiveCfg = Release|Any CPU {734098EB-6DC1-4DD0-A1CA-3140DCD2737C}.Release|x64.ActiveCfg = Release|Any CPU {734098EB-6DC1-4DD0-A1CA-3140DCD2737C}.Release|x86.ActiveCfg = Release|Any CPU + {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Debug|Win32.ActiveCfg = Debug|Any CPU + {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Debug|x64.ActiveCfg = Debug|Any CPU + {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Debug|x86.ActiveCfg = Debug|Any CPU + {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release|Any CPU.Build.0 = Release|Any CPU + {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release|Win32.ActiveCfg = Release|Any CPU + {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release|x64.ActiveCfg = Release|Any CPU + {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release|x86.ActiveCfg = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE