diff options
Diffstat (limited to 'MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs')
| -rw-r--r-- | MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 120 |
1 files changed, 53 insertions, 67 deletions
diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index ad84ffee8..5c3345008 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -24,6 +24,7 @@ using CommonIO; using MediaBrowser.Model.Configuration; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.IO; using MediaBrowser.Common.Net; namespace MediaBrowser.MediaEncoding.Encoder @@ -79,11 +80,14 @@ namespace MediaBrowser.MediaEncoding.Encoder protected readonly Func<IMediaSourceManager> MediaSourceManager; private readonly IHttpClient _httpClient; private readonly IZipClient _zipClient; + private readonly IMemoryStreamProvider _memoryStreamProvider; private readonly List<ProcessWrapper> _runningProcesses = new List<ProcessWrapper>(); private readonly bool _hasExternalEncoder; + private string _originalFFMpegPath; + private string _originalFFProbePath; - public MediaEncoder(ILogger logger, IJsonSerializer jsonSerializer, string ffMpegPath, string ffProbePath, bool hasExternalEncoder, IServerConfigurationManager configurationManager, IFileSystem fileSystem, ILiveTvManager liveTvManager, IIsoManager isoManager, ILibraryManager libraryManager, IChannelManager channelManager, ISessionManager sessionManager, Func<ISubtitleEncoder> subtitleEncoder, Func<IMediaSourceManager> mediaSourceManager, IHttpClient httpClient, IZipClient zipClient) + public MediaEncoder(ILogger logger, IJsonSerializer jsonSerializer, string ffMpegPath, string ffProbePath, bool hasExternalEncoder, IServerConfigurationManager configurationManager, IFileSystem fileSystem, ILiveTvManager liveTvManager, IIsoManager isoManager, ILibraryManager libraryManager, IChannelManager channelManager, ISessionManager sessionManager, Func<ISubtitleEncoder> subtitleEncoder, Func<IMediaSourceManager> mediaSourceManager, IHttpClient httpClient, IZipClient zipClient, IMemoryStreamProvider memoryStreamProvider) { _logger = logger; _jsonSerializer = jsonSerializer; @@ -98,8 +102,11 @@ namespace MediaBrowser.MediaEncoding.Encoder MediaSourceManager = mediaSourceManager; _httpClient = httpClient; _zipClient = zipClient; + _memoryStreamProvider = memoryStreamProvider; FFProbePath = ffProbePath; FFMpegPath = ffMpegPath; + _originalFFProbePath = ffProbePath; + _originalFFMpegPath = ffMpegPath; _hasExternalEncoder = hasExternalEncoder; } @@ -108,11 +115,6 @@ namespace MediaBrowser.MediaEncoding.Encoder { get { - if (_hasExternalEncoder) - { - return "External"; - } - if (string.IsNullOrWhiteSpace(FFMpegPath)) { return null; @@ -177,12 +179,6 @@ namespace MediaBrowser.MediaEncoding.Encoder { ConfigureEncoderPaths(); - if (_hasExternalEncoder) - { - LogPaths(); - return; - } - // If the path was passed in, save it into config now. var encodingOptions = GetEncodingOptions(); var appPath = encodingOptions.EncoderAppPath; @@ -207,11 +203,6 @@ namespace MediaBrowser.MediaEncoding.Encoder public async Task UpdateEncoderPath(string path, string pathType) { - if (_hasExternalEncoder) - { - return; - } - Tuple<string, string> newPaths; if (string.Equals(pathType, "system", StringComparison.OrdinalIgnoreCase)) @@ -247,6 +238,13 @@ namespace MediaBrowser.MediaEncoding.Encoder throw new ResourceNotFoundException("ffprobe not found"); } + path = newPaths.Item1; + + if (!ValidateVersion(path)) + { + throw new ResourceNotFoundException("ffmpeg version 3.0 or greater is required."); + } + var config = GetEncodingOptions(); config.EncoderAppPath = path; ConfigurationManager.SaveConfiguration("encoding", config); @@ -254,13 +252,13 @@ namespace MediaBrowser.MediaEncoding.Encoder Init(); } - private void ConfigureEncoderPaths() + private bool ValidateVersion(string path) { - if (_hasExternalEncoder) - { - return; - } + return new EncoderValidator(_logger).ValidateVersion(path); + } + private void ConfigureEncoderPaths() + { var appPath = GetEncodingOptions().EncoderAppPath; if (string.IsNullOrWhiteSpace(appPath)) @@ -308,45 +306,22 @@ namespace MediaBrowser.MediaEncoding.Encoder string encoderPath = null; string probePath = null; - if (TestSystemInstalled("ffmpeg")) + if (_hasExternalEncoder && ValidateVersion(_originalFFMpegPath)) { - encoderPath = "ffmpeg"; + encoderPath = _originalFFMpegPath; + probePath = _originalFFProbePath; } - if (TestSystemInstalled("ffprobe")) - { - probePath = "ffprobe"; - } - - return new Tuple<string, string>(encoderPath, probePath); - } - private bool TestSystemInstalled(string app) - { - try + if (string.IsNullOrWhiteSpace(encoderPath)) { - var startInfo = new ProcessStartInfo + if (ValidateVersion("ffmpeg") && ValidateVersion("ffprobe")) { - FileName = app, - Arguments = "-v", - UseShellExecute = false, - CreateNoWindow = true, - WindowStyle = ProcessWindowStyle.Hidden, - ErrorDialog = false - }; - - using (var process = Process.Start(startInfo)) - { - process.WaitForExit(); + encoderPath = "ffmpeg"; + probePath = "ffprobe"; } - - _logger.Debug("System app installed: " + app); - return true; - } - catch - { - _logger.Debug("System app not installed: " + app); - return false; } + + return new Tuple<string, string>(encoderPath, probePath); } private Tuple<string, string> GetPathsFromDirectory(string path) @@ -451,8 +426,10 @@ namespace MediaBrowser.MediaEncoding.Encoder var inputFiles = MediaEncoderHelpers.GetInputArgument(FileSystem, request.InputPath, request.Protocol, request.MountedIso, request.PlayableStreamFileNames); + var probeSizeArgument = GetProbeSizeArgument(inputFiles, request.Protocol); + return GetMediaInfoInternal(GetInputArgument(inputFiles, request.Protocol), request.InputPath, request.Protocol, extractChapters, - GetProbeSizeArgument(inputFiles, request.Protocol), request.MediaType == DlnaProfileType.Audio, request.VideoType, cancellationToken); + probeSizeArgument, request.MediaType == DlnaProfileType.Audio, request.VideoType, cancellationToken); } /// <summary> @@ -572,7 +549,7 @@ namespace MediaBrowser.MediaEncoding.Encoder } } - var mediaInfo = new ProbeResultNormalizer(_logger, FileSystem).GetMediaInfo(result, videoType, isAudio, primaryPath, protocol); + var mediaInfo = new ProbeResultNormalizer(_logger, FileSystem, _memoryStreamProvider).GetMediaInfo(result, videoType, isAudio, primaryPath, protocol); var videoStream = mediaInfo.MediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video); @@ -799,20 +776,20 @@ namespace MediaBrowser.MediaEncoding.Encoder public Task<string> ExtractAudioImage(string path, int? imageStreamIndex, CancellationToken cancellationToken) { - return ExtractImage(new[] { path }, imageStreamIndex, MediaProtocol.File, true, null, null, cancellationToken); + return ExtractImage(new[] { path }, null, imageStreamIndex, MediaProtocol.File, true, null, null, cancellationToken); } - public Task<string> ExtractVideoImage(string[] inputFiles, MediaProtocol protocol, Video3DFormat? threedFormat, TimeSpan? offset, CancellationToken cancellationToken) + public Task<string> ExtractVideoImage(string[] inputFiles, string container, MediaProtocol protocol, Video3DFormat? threedFormat, TimeSpan? offset, CancellationToken cancellationToken) { - return ExtractImage(inputFiles, null, protocol, false, threedFormat, offset, cancellationToken); + return ExtractImage(inputFiles, container, null, protocol, false, threedFormat, offset, cancellationToken); } - public Task<string> ExtractVideoImage(string[] inputFiles, MediaProtocol protocol, int? imageStreamIndex, CancellationToken cancellationToken) + public Task<string> ExtractVideoImage(string[] inputFiles, string container, MediaProtocol protocol, int? imageStreamIndex, CancellationToken cancellationToken) { - return ExtractImage(inputFiles, imageStreamIndex, protocol, false, null, null, cancellationToken); + return ExtractImage(inputFiles, container, imageStreamIndex, protocol, false, null, null, cancellationToken); } - private async Task<string> ExtractImage(string[] inputFiles, int? imageStreamIndex, MediaProtocol protocol, bool isAudio, + private async Task<string> ExtractImage(string[] inputFiles, string container, int? imageStreamIndex, MediaProtocol protocol, bool isAudio, Video3DFormat? threedFormat, TimeSpan? offset, CancellationToken cancellationToken) { var resourcePool = isAudio ? _audioImageResourcePool : _videoImageResourcePool; @@ -831,7 +808,7 @@ namespace MediaBrowser.MediaEncoding.Encoder { try { - return await ExtractImageInternal(inputArgument, imageStreamIndex, protocol, threedFormat, offset, true, resourcePool, cancellationToken).ConfigureAwait(false); + return await ExtractImageInternal(inputArgument, container, imageStreamIndex, protocol, threedFormat, offset, true, resourcePool, cancellationToken).ConfigureAwait(false); } catch (ArgumentException) { @@ -843,10 +820,10 @@ namespace MediaBrowser.MediaEncoding.Encoder } } - return await ExtractImageInternal(inputArgument, imageStreamIndex, protocol, threedFormat, offset, false, resourcePool, cancellationToken).ConfigureAwait(false); + return await ExtractImageInternal(inputArgument, container, imageStreamIndex, protocol, threedFormat, offset, false, resourcePool, cancellationToken).ConfigureAwait(false); } - private async Task<string> ExtractImageInternal(string inputPath, int? imageStreamIndex, MediaProtocol protocol, Video3DFormat? threedFormat, TimeSpan? offset, bool useIFrame, SemaphoreSlim resourcePool, CancellationToken cancellationToken) + private async Task<string> ExtractImageInternal(string inputPath, string container, int? imageStreamIndex, MediaProtocol protocol, Video3DFormat? threedFormat, TimeSpan? offset, bool useIFrame, SemaphoreSlim resourcePool, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(inputPath)) { @@ -887,8 +864,11 @@ namespace MediaBrowser.MediaEncoding.Encoder var mapArg = imageStreamIndex.HasValue ? (" -map 0:v:" + imageStreamIndex.Value.ToString(CultureInfo.InvariantCulture)) : string.Empty; + var enableThumbnail = !new List<string> { "wtv" }.Contains(container ?? string.Empty, StringComparer.OrdinalIgnoreCase); + var thumbnail = enableThumbnail ? ",thumbnail=30" : string.Empty; + // 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}{3} -threads 0 -v quiet -vframes 1 -vf \"{2},thumbnail=30\" -f image2 \"{1}\"", inputPath, tempExtractPath, vf, mapArg) : + var args = useIFrame ? string.Format("-i {0}{3} -threads 0 -v quiet -vframes 1 -vf \"{2}{4}\" -f image2 \"{1}\"", inputPath, tempExtractPath, vf, mapArg, thumbnail) : string.Format("-i {0}{3} -threads 0 -v quiet -vframes 1 -vf \"{2}\" -f image2 \"{1}\"", inputPath, tempExtractPath, vf, mapArg); var probeSize = GetProbeSizeArgument(new[] { inputPath }, protocol); @@ -928,7 +908,13 @@ namespace MediaBrowser.MediaEncoding.Encoder { StartProcess(processWrapper); - ranToCompletion = process.WaitForExit(10000); + var timeoutMs = ConfigurationManager.Configuration.ImageExtractionTimeoutMs; + if (timeoutMs <= 0) + { + timeoutMs = Environment.Is64BitOperatingSystem ? (Environment.ProcessorCount > 2 ? 14000 : 20000) : 40000; + } + + ranToCompletion = process.WaitForExit(timeoutMs); if (!ranToCompletion) { |
