From 5340bfe8da879ba2503cb675e6724ce33b754ae3 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 17 Sep 2015 23:08:45 -0400 Subject: [PATCH] added setting for intel qsv hardware decoding --- .../Playback/BaseStreamingService.cs | 51 +++++++++-- .../MediaEncoding/IMediaEncoder.cs | 7 ++ .../Encoder/MediaEncoder.cs | 16 ++++ .../Configuration/EncodingOptions.cs | 3 +- .../ApplicationHost.cs | 14 ++- .../FFMpeg/FFMpegDownloadInfo.cs | 20 ++--- .../FFMpeg/FFmpegValidator.cs | 90 +++++++++---------- 7 files changed, 132 insertions(+), 69 deletions(-) diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index a524490783..86f06213c4 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -290,13 +290,6 @@ namespace MediaBrowser.Api.Playback { get { - var lib = ApiEntryPoint.Instance.GetEncodingOptions().H264Encoder; - - if (!string.IsNullOrWhiteSpace(lib)) - { - return lib; - } - return "libx264"; } } @@ -809,6 +802,46 @@ namespace MediaBrowser.Api.Playback return "copy"; } + /// + /// Gets the name of the output video codec + /// + /// The state. + /// System.String. + protected string GetVideoDecoder(StreamState state) + { + if (string.Equals(ApiEntryPoint.Instance.GetEncodingOptions().HardwareVideoDecoder, "qsv", StringComparison.OrdinalIgnoreCase)) + { + if (state.VideoStream != null && !string.IsNullOrWhiteSpace(state.VideoStream.Codec)) + { + switch (state.MediaSource.VideoStream.Codec.ToLower()) + { + case "avc": + case "h264": + if (MediaEncoder.SupportsDecoder("h264_qsv")) + { + return "-c:v h264_qsv "; + } + break; + case "mpeg2video": + if (MediaEncoder.SupportsDecoder("mpeg2_qsv")) + { + return "-c:v mpeg2_qsv "; + } + break; + case "vc1": + if (MediaEncoder.SupportsDecoder("vc1_qsv")) + { + return "-c:v vc1_qsv "; + } + break; + } + } + } + + // leave blank so ffmpeg will decide + return string.Empty; + } + /// /// Gets the input argument. /// @@ -816,7 +849,7 @@ namespace MediaBrowser.Api.Playback /// System.String. protected string GetInputArgument(StreamState state) { - var arg = "-i " + GetInputPathArgument(state); + var arg = string.Format("{1}-i {0}", GetInputPathArgument(state), GetVideoDecoder(state)); if (state.SubtitleStream != null) { @@ -826,7 +859,7 @@ namespace MediaBrowser.Api.Playback } } - return arg; + return arg.Trim(); } private string GetInputPathArgument(StreamState state) diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs index 23285b612a..427af6f6d7 100644 --- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs +++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs @@ -24,6 +24,13 @@ namespace MediaBrowser.Controller.MediaEncoding /// The version. string Version { get; } + /// + /// Supportses the decoder. + /// + /// The decoder. + /// true if XXXX, false otherwise. + bool SupportsDecoder(string decoder); + /// /// Extracts the audio image. /// diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 01be4924ac..d317acc594 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -97,6 +97,22 @@ namespace MediaBrowser.MediaEncoding.Encoder FFMpegPath = ffMpegPath; } + public void SetAvailableEncoders(List list) + { + + } + + private List _decoders = new List(); + public void SetAvailableDecoders(List list) + { + _decoders = list.ToList(); + } + + public bool SupportsDecoder(string decoder) + { + return _decoders.Contains(decoder, StringComparer.OrdinalIgnoreCase); + } + /// /// Gets the encoder path. /// diff --git a/MediaBrowser.Model/Configuration/EncodingOptions.cs b/MediaBrowser.Model/Configuration/EncodingOptions.cs index 6d3894f029..77b894eb8a 100644 --- a/MediaBrowser.Model/Configuration/EncodingOptions.cs +++ b/MediaBrowser.Model/Configuration/EncodingOptions.cs @@ -6,14 +6,13 @@ namespace MediaBrowser.Model.Configuration public int EncodingThreadCount { get; set; } public string TranscodingTempPath { get; set; } public double DownMixAudioBoost { get; set; } - public string H264Encoder { get; set; } public bool EnableDebugLogging { get; set; } public bool EnableThrottling { get; set; } public int ThrottleThresholdInSeconds { get; set; } + public string HardwareVideoDecoder { get; set; } public EncodingOptions() { - H264Encoder = "libx264"; DownMixAudioBoost = 2; EnableThrottling = true; ThrottleThresholdInSeconds = 120; diff --git a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs index b55e727d65..c38c818664 100644 --- a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs +++ b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs @@ -595,9 +595,7 @@ namespace MediaBrowser.Server.Startup.Common var info = await new FFMpegDownloader(Logger, ApplicationPaths, HttpClient, ZipClient, FileSystemManager, NativeApp.Environment) .GetFFMpegInfo(NativeApp.Environment, _startupOptions, progress).ConfigureAwait(false); - new FFmpegValidator(Logger, ApplicationPaths, FileSystemManager).Validate(info); - - MediaEncoder = new MediaEncoder(LogManager.GetLogger("MediaEncoder"), + var mediaEncoder = new MediaEncoder(LogManager.GetLogger("MediaEncoder"), JsonSerializer, info.EncoderPath, info.ProbePath, @@ -611,7 +609,17 @@ namespace MediaBrowser.Server.Startup.Common SessionManager, () => SubtitleEncoder, () => MediaSourceManager); + + MediaEncoder = mediaEncoder; RegisterSingleInstance(MediaEncoder); + + Task.Run(() => + { + var result = new FFmpegValidator(Logger, ApplicationPaths, FileSystemManager).Validate(info); + + mediaEncoder.SetAvailableDecoders(result.Item1); + mediaEncoder.SetAvailableEncoders(result.Item2); + }); } /// diff --git a/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegDownloadInfo.cs b/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegDownloadInfo.cs index 2910479ef8..c4fe7e43b5 100644 --- a/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegDownloadInfo.cs +++ b/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegDownloadInfo.cs @@ -33,7 +33,7 @@ namespace MediaBrowser.Server.Startup.Common.FFMpeg case OperatingSystem.Linux: info.ArchiveType = "7z"; - info.Version = "20150717"; + info.Version = "20150917"; break; case OperatingSystem.Osx: @@ -42,7 +42,7 @@ namespace MediaBrowser.Server.Startup.Common.FFMpeg switch (environment.SystemArchitecture) { case Architecture.X86_X64: - info.Version = "20150827"; + info.Version = "20150917"; break; case Architecture.X86: info.Version = "20150110"; @@ -54,7 +54,7 @@ namespace MediaBrowser.Server.Startup.Common.FFMpeg info.FFMpegFilename = "ffmpeg.exe"; info.FFProbeFilename = "ffprobe.exe"; - info.Version = "20150717"; + info.Version = "20150916"; info.ArchiveType = "7z"; switch (environment.SystemArchitecture) @@ -83,14 +83,14 @@ namespace MediaBrowser.Server.Startup.Common.FFMpeg case Architecture.X86_X64: return new[] { - "http://ffmpeg.zeranoe.com/builds/win64/static/ffmpeg-20150717-git-8250943-win64-static.7z", - "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/windows/ffmpeg-20150901-git-b54e03c-win64-static.7z" + "http://ffmpeg.zeranoe.com/builds/win64/static/ffmpeg-20150916-git-cbbd906-win64-static.7z", + "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/windows/ffmpeg-20150916-git-cbbd906-win64-static.7z" }; case Architecture.X86: return new[] { - "http://ffmpeg.zeranoe.com/builds/win32/static/ffmpeg-20150717-git-8250943-win32-static.7z", - "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/windows/ffmpeg-20150901-git-b54e03c-win32-static.7z" + "http://ffmpeg.zeranoe.com/builds/win32/static/ffmpeg-20150916-git-cbbd906-win32-static.7z", + "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/windows/ffmpeg-20150916-git-cbbd906-win32-static.7z" }; } break; @@ -102,7 +102,7 @@ namespace MediaBrowser.Server.Startup.Common.FFMpeg case Architecture.X86_X64: return new[] { - "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/osx/ffmpeg-x64-2.7.2.7z" + "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/osx/ffmpeg-x64-2.8.0.7z" }; case Architecture.X86: return new[] @@ -119,12 +119,12 @@ namespace MediaBrowser.Server.Startup.Common.FFMpeg case Architecture.X86_X64: return new[] { - "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/linux/ffmpeg-2.7.1-64bit-static.7z" + "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/linux/ffmpeg-2.8.0-64bit-static.7z" }; case Architecture.X86: return new[] { - "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/linux/ffmpeg-2.7.1-32bit-static.7z" + "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/linux/ffmpeg-2.8.0-32bit-static.7z" }; } break; diff --git a/MediaBrowser.Server.Startup.Common/FFMpeg/FFmpegValidator.cs b/MediaBrowser.Server.Startup.Common/FFMpeg/FFmpegValidator.cs index 54cd1357a6..d3388f47e5 100644 --- a/MediaBrowser.Server.Startup.Common/FFMpeg/FFmpegValidator.cs +++ b/MediaBrowser.Server.Startup.Common/FFMpeg/FFmpegValidator.cs @@ -7,6 +7,7 @@ using System.Globalization; using System.IO; using System.Text; using MediaBrowser.Common.IO; +using System.Collections.Generic; namespace MediaBrowser.Server.Startup.Common.FFMpeg { @@ -23,71 +24,65 @@ namespace MediaBrowser.Server.Startup.Common.FFMpeg _fileSystem = fileSystem; } - public void Validate(FFMpegInfo info) + public Tuple,List> Validate(FFMpegInfo info) { _logger.Info("FFMpeg: {0}", info.EncoderPath); _logger.Info("FFProbe: {0}", info.ProbePath); - string cacheKey; + var decoders = GetDecoders(info.EncoderPath); + var encoders = GetEncoders(info.EncoderPath); + return new Tuple, List>(decoders, encoders); + } + + private List GetDecoders(string ffmpegPath) + { + string output = string.Empty; try { - cacheKey = new FileInfo(info.EncoderPath).Length.ToString(CultureInfo.InvariantCulture).GetMD5().ToString("N"); - } - catch (IOException) - { - // This could happen if ffmpeg is coming from a Path variable and we don't have the full path - cacheKey = Guid.NewGuid().ToString("N"); + output = GetFFMpegOutput(ffmpegPath, "-decoders"); } catch { - cacheKey = Guid.NewGuid().ToString("N"); } - var cachePath = Path.Combine(_appPaths.CachePath, "1" + cacheKey); + var found = new List(); + var required = new[] + { + "h264_qsv", + "mpeg2_qsv", + "vc1_qsv" + }; - ValidateCodecs(info.EncoderPath, cachePath); + foreach (var codec in required) + { + var srch = " " + codec + " "; + + if (output.IndexOf(srch, StringComparison.OrdinalIgnoreCase) == -1) + { + _logger.Warn("ffmpeg is missing decoder " + codec); + } + else + { + found.Add(codec); + } + } + + return found; } - private void ValidateCodecs(string ffmpegPath, string cachePath) + private List GetEncoders(string ffmpegPath) { string output = null; try { - output = _fileSystem.ReadAllText(cachePath, Encoding.UTF8); + output = GetFFMpegOutput(ffmpegPath, "-encoders"); } catch { - } - if (string.IsNullOrWhiteSpace(output)) - { - try - { - output = GetFFMpegOutput(ffmpegPath, "-encoders"); - } - catch - { - return; - } - - try - { - _fileSystem.CreateDirectory(Path.GetDirectoryName(cachePath)); - _fileSystem.WriteAllText(cachePath, output, Encoding.UTF8); - } - catch - { - - } - } - - ValidateCodecsFromOutput(output); - } - - private void ValidateCodecsFromOutput(string output) - { + var found = new List(); var required = new[] { "libx264", @@ -103,16 +98,21 @@ namespace MediaBrowser.Server.Startup.Common.FFMpeg "srt" }; - foreach (var encoder in required) + foreach (var codec in required) { - var srch = " " + encoder + " "; + var srch = " " + codec + " "; if (output.IndexOf(srch, StringComparison.OrdinalIgnoreCase) == -1) { - _logger.Error("ffmpeg is missing encoder " + encoder); - //throw new ArgumentException("ffmpeg is missing encoder " + encoder); + _logger.Warn("ffmpeg is missing encoder " + codec); + } + else + { + found.Add(codec); } } + + return found; } private string GetFFMpegOutput(string path, string arguments)