From a6bde0943e7feda25eba15e23a2307227de87d4e Mon Sep 17 00:00:00 2001 From: PloughPuff Date: Thu, 14 Feb 2019 22:01:09 +0000 Subject: Implement proper FFmpeg version checking Three routes to determine FFmpeg version: 1) Grab the 'ffmpeg version x.y' from from the -version output. This should work for all pre-built binaries. 2) Compare the library versions against known contents of FFmpeg versions. This is fallback aimed at custom builds. 3) Compare libavcodec version to determine if newer than latest known release. This suggests user is running within latest/HEAD/master build. --- .../Encoder/EncoderValidator.cs | 188 ++++++++++++++++++++- 1 file changed, 181 insertions(+), 7 deletions(-) (limited to 'MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs') diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index 262772959..275062df5 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using System.Globalization; +using System.Collections.ObjectModel; using System.Linq; using System.Text.RegularExpressions; using MediaBrowser.Model.Diagnostics; @@ -8,6 +8,71 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.MediaEncoding.Encoder { + public class FFmpegVersion + { + private int _version; + private int _multi => 100; + private const int _unknown = 0; + private const int _experimental = -1; + + public FFmpegVersion(int p1) + { + _version = p1; + } + + public FFmpegVersion(string p1) + { + var match = Regex.Match(p1, @"(?\d+)\.(?\d+)"); + + if (match.Groups["major"].Success && match.Groups["minor"].Success) + { + int major = int.Parse(match.Groups["major"].Value); + int minor = int.Parse(match.Groups["minor"].Value); + _version = (major * _multi) + minor; + } + } + + public override string ToString() + { + switch (_version) + { + case _unknown: + return "Unknown"; + case _experimental: + return "Experimental"; + default: + string major = Convert.ToString(_version / _multi); + string minor = Convert.ToString(_version % _multi); + return string.Concat(major, ".", minor); + } + } + + public bool Unknown() + { + return _version == _unknown; + } + + public int Version() + { + return _version; + } + + public bool Experimental() + { + return _version == _experimental; + } + + public bool Below(FFmpegVersion checkAgainst) + { + return (_version > 0) && (_version < checkAgainst._version); + } + + public bool Suitable(FFmpegVersion checkAgainst) + { + return (_version > 0) && (_version >= checkAgainst._version); + } + } + public class EncoderValidator { private readonly ILogger _logger; @@ -58,18 +123,127 @@ namespace MediaBrowser.MediaEncoding.Encoder return false; } - output = " " + output + " "; + // The minimum FFmpeg version required to run jellyfin successfully + FFmpegVersion required = new FFmpegVersion("4.0"); + + // Work out what the version under test is + FFmpegVersion underTest = GetFFmpegVersion(output); - for (var i = 2013; i <= 2015; i++) + if (logOutput) { - var yearString = i.ToString(CultureInfo.InvariantCulture); - if (output.IndexOf(" " + yearString + " ", StringComparison.OrdinalIgnoreCase) != -1) + if (underTest.Unknown()) + { + _logger.LogWarning("FFmpeg validation: Unknown version"); + } + else if (underTest.Below(required)) { - return false; + _logger.LogWarning("FFmpeg validation: Found version {0} which is below the minimum recommended of {1}", + underTest.ToString(), required.ToString()); } + else if (underTest.Experimental()) + { + _logger.LogWarning("FFmpeg validation: Unknown version: {0}?", underTest.ToString()); + } + else + { + _logger.LogInformation("FFmpeg validation: Detected version {0}", underTest.ToString()); + } + } + + return underTest.Suitable(required); + } + + /// + /// Using the output from "ffmpeg -version" work out the FFmpeg version. + /// For pre-built binaries the first line should contain a string like "ffmpeg version x.y", which is easy + /// to parse. If this is not available, then we try to match known library versions to FFmpeg versions. + /// If that fails then we use one of the main libraries to determine if it's new/older than the latest + /// we have stored. + /// + /// + /// + static private FFmpegVersion GetFFmpegVersion(string output) + { + // For pre-built binaries the FFmpeg version should be mentioned at the very start of the output + var match = Regex.Match(output, @"ffmpeg version (\d+\.\d+)"); + + if (match.Success) + { + return new FFmpegVersion(match.Groups[1].Value); + } + else + { + // Try and use the individual library versions to determine a FFmpeg version + // This lookup table is to be maintained with the following command line: + // $ ./ffmpeg.exe -version | perl -ne ' print "$1=$2.$3," if /^(lib\w+)\s+(\d+)\.\s*(\d+)/' + ReadOnlyDictionary lut = new ReadOnlyDictionary + (new Dictionary + { + { new FFmpegVersion("4.1"), "libavutil=56.22,libavcodec=58.35,libavformat=58.20,libavdevice=58.5,libavfilter=7.40,libswscale=5.3,libswresample=3.3,libpostproc=55.3," }, + { new FFmpegVersion("4.0"), "libavutil=56.14,libavcodec=58.18,libavformat=58.12,libavdevice=58.3,libavfilter=7.16,libswscale=5.1,libswresample=3.1,libpostproc=55.1," }, + { new FFmpegVersion("3.4"), "libavutil=55.78,libavcodec=57.107,libavformat=57.83,libavdevice=57.10,libavfilter=6.107,libswscale=4.8,libswresample=2.9,libpostproc=54.7," }, + { new FFmpegVersion("3.3"), "libavutil=55.58,libavcodec=57.89,libavformat=57.71,libavdevice=57.6,libavfilter=6.82,libswscale=4.6,libswresample=2.7,libpostproc=54.5," }, + { new FFmpegVersion("3.2"), "libavutil=55.34,libavcodec=57.64,libavformat=57.56,libavdevice=57.1,libavfilter=6.65,libswscale=4.2,libswresample=2.3,libpostproc=54.1," }, + { new FFmpegVersion("2.8"), "libavutil=54.31,libavcodec=56.60,libavformat=56.40,libavdevice=56.4,libavfilter=5.40,libswscale=3.1,libswresample=1.2,libpostproc=53.3," } + }); + + // Create a reduced version string and lookup key from dictionary + var reducedVersion = GetVersionString(output); + var found = lut.FirstOrDefault(x => x.Value == reducedVersion).Key; + + if (found != null) + { + return found; + } + else + { + // Unknown version. Test the main libavcoder version in the candidate with the + // latest from the dictionary. If candidate is greater than dictionary chances are + // the user if running HEAD/master ffmpeg build (which is probably ok) + var firstElement = lut.FirstOrDefault(); + + var reqVer = Regex.Match(firstElement.Value, @"libavcodec=(\d+\.\d+)"); + var gotVer = Regex.Match(reducedVersion, @"libavcodec=(\d+\.\d+)"); + + if (reqVer.Success && gotVer.Success) + { + var req = new FFmpegVersion(reqVer.Groups[1].Value); + var got = new FFmpegVersion(gotVer.Groups[1].Value); + + // The library versions are not comparable with the FFmpeg version so must check + // candidate (got) against value from dictionary (req). Return Experimental if suitable + if( got.Suitable(req) ) + { + return new FFmpegVersion(-1); + + } + } + } + } + + // Default to return Unknown + return new FFmpegVersion(0); + } + + /// + /// Grabs the library names and major.minor version numbers from the 'ffmpeg -version' output + /// and condenses them on to one line. Output format is "name1=major.minor,name2=major.minor,etc." + /// + /// + /// + static private string GetVersionString(string output) + { + string pattern = @"((?lib\w+)\s+(?\d+)\.\s*(?\d+))"; + RegexOptions options = RegexOptions.Multiline; + + string rc = null; + + foreach (Match m in Regex.Matches(output, pattern, options)) + { + rc += string.Concat(m.Groups["name"], '=', m.Groups["major"], '.', m.Groups["minor"], ','); } - return true; + return rc; } private static readonly string[] requiredDecoders = new[] -- cgit v1.2.3 From d8d237f6f295990c54bb2b0e36e0be04bacbdc3b Mon Sep 17 00:00:00 2001 From: PloughPuff Date: Fri, 15 Feb 2019 23:51:22 +0000 Subject: Review comments Addressed review comments from JustAMan. Removed code to determine experimental version. Store major and minor as two ints. Allow control of a min and max recommended version. --- .../Encoder/EncoderValidator.cs | 110 ++++++++------------- 1 file changed, 42 insertions(+), 68 deletions(-) (limited to 'MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs') diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index 275062df5..ad777a9aa 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -10,15 +10,10 @@ namespace MediaBrowser.MediaEncoding.Encoder { public class FFmpegVersion { - private int _version; - private int _multi => 100; - private const int _unknown = 0; - private const int _experimental = -1; + private readonly int _major; + private readonly int _minor; - public FFmpegVersion(int p1) - { - _version = p1; - } + private const int _unknown = 0; public FFmpegVersion(string p1) { @@ -26,50 +21,50 @@ namespace MediaBrowser.MediaEncoding.Encoder if (match.Groups["major"].Success && match.Groups["minor"].Success) { - int major = int.Parse(match.Groups["major"].Value); - int minor = int.Parse(match.Groups["minor"].Value); - _version = (major * _multi) + minor; + _major = int.Parse(match.Groups["major"].Value); + _minor = int.Parse(match.Groups["minor"].Value); + } + else + { + _major = _unknown; + _minor = _unknown; } } public override string ToString() { - switch (_version) + switch (_major) { case _unknown: return "Unknown"; - case _experimental: - return "Experimental"; default: - string major = Convert.ToString(_version / _multi); - string minor = Convert.ToString(_version % _multi); - return string.Concat(major, ".", minor); + return string.Format("{0}.{1}",_major,_minor); } } public bool Unknown() { - return _version == _unknown; + return _major == _unknown; } - public int Version() + public bool Below(FFmpegVersion checkAgainst) { - return _version; + return ToScalar() < checkAgainst.ToScalar(); } - public bool Experimental() + public bool Above(FFmpegVersion checkAgainst) { - return _version == _experimental; + return ToScalar() > checkAgainst.ToScalar(); } - public bool Below(FFmpegVersion checkAgainst) + public bool Same(FFmpegVersion checkAgainst) { - return (_version > 0) && (_version < checkAgainst._version); + return ToScalar() == checkAgainst.ToScalar(); } - public bool Suitable(FFmpegVersion checkAgainst) + private int ToScalar() { - return (_version > 0) && (_version >= checkAgainst._version); + return (_major * 1000) + _minor; } } @@ -123,34 +118,43 @@ namespace MediaBrowser.MediaEncoding.Encoder return false; } - // The minimum FFmpeg version required to run jellyfin successfully - FFmpegVersion required = new FFmpegVersion("4.0"); + // The min and max FFmpeg versions required to run jellyfin successfully + FFmpegVersion minRequired = new FFmpegVersion("4.0"); + FFmpegVersion maxRequired = new FFmpegVersion("4.0"); // Work out what the version under test is FFmpegVersion underTest = GetFFmpegVersion(output); if (logOutput) { + _logger.LogInformation("FFmpeg validation: Found ffmpeg version {0}", underTest.ToString()); + if (underTest.Unknown()) { - _logger.LogWarning("FFmpeg validation: Unknown version"); + if (minRequired.Same(maxRequired)) + { + _logger.LogWarning("FFmpeg validation: We recommend ffmpeg version {0}", minRequired.ToString()); + } + else + { + _logger.LogWarning("FFmpeg validation: We recommend a minimum of {0} and maximum of {1}", minRequired.ToString(), maxRequired.ToString()); + } } - else if (underTest.Below(required)) + else if (underTest.Below(minRequired)) { - _logger.LogWarning("FFmpeg validation: Found version {0} which is below the minimum recommended of {1}", - underTest.ToString(), required.ToString()); + _logger.LogWarning("FFmpeg validation: The minimum recommended ffmpeg version is {0}", minRequired.ToString()); } - else if (underTest.Experimental()) + else if (underTest.Above(maxRequired)) { - _logger.LogWarning("FFmpeg validation: Unknown version: {0}?", underTest.ToString()); + _logger.LogWarning("FFmpeg validation: The maximum recommended ffmpeg version is {0}", maxRequired.ToString()); } else { - _logger.LogInformation("FFmpeg validation: Detected version {0}", underTest.ToString()); + // Version is ok so no warning required } } - return underTest.Suitable(required); + return !underTest.Below(minRequired) && !underTest.Above(maxRequired); } /// @@ -176,7 +180,7 @@ namespace MediaBrowser.MediaEncoding.Encoder // Try and use the individual library versions to determine a FFmpeg version // This lookup table is to be maintained with the following command line: // $ ./ffmpeg.exe -version | perl -ne ' print "$1=$2.$3," if /^(lib\w+)\s+(\d+)\.\s*(\d+)/' - ReadOnlyDictionary lut = new ReadOnlyDictionary + var lut = new ReadOnlyDictionary (new Dictionary { { new FFmpegVersion("4.1"), "libavutil=56.22,libavcodec=58.35,libavformat=58.20,libavdevice=58.5,libavfilter=7.40,libswscale=5.3,libswresample=3.3,libpostproc=55.3," }, @@ -191,38 +195,8 @@ namespace MediaBrowser.MediaEncoding.Encoder var reducedVersion = GetVersionString(output); var found = lut.FirstOrDefault(x => x.Value == reducedVersion).Key; - if (found != null) - { - return found; - } - else - { - // Unknown version. Test the main libavcoder version in the candidate with the - // latest from the dictionary. If candidate is greater than dictionary chances are - // the user if running HEAD/master ffmpeg build (which is probably ok) - var firstElement = lut.FirstOrDefault(); - - var reqVer = Regex.Match(firstElement.Value, @"libavcodec=(\d+\.\d+)"); - var gotVer = Regex.Match(reducedVersion, @"libavcodec=(\d+\.\d+)"); - - if (reqVer.Success && gotVer.Success) - { - var req = new FFmpegVersion(reqVer.Groups[1].Value); - var got = new FFmpegVersion(gotVer.Groups[1].Value); - - // The library versions are not comparable with the FFmpeg version so must check - // candidate (got) against value from dictionary (req). Return Experimental if suitable - if( got.Suitable(req) ) - { - return new FFmpegVersion(-1); - - } - } - } + return found ?? new FFmpegVersion("Unknown"); } - - // Default to return Unknown - return new FFmpegVersion(0); } /// -- cgit v1.2.3 From 69ea15f73a053e2526457b0eec757fb6f1ca1cd7 Mon Sep 17 00:00:00 2001 From: PloughPuff Date: Sat, 16 Feb 2019 00:47:38 +0000 Subject: Use string interpolation Two further review comments from JustAMan. --- MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs') diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index ad777a9aa..be33ea58e 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -38,7 +38,7 @@ namespace MediaBrowser.MediaEncoding.Encoder case _unknown: return "Unknown"; default: - return string.Format("{0}.{1}",_major,_minor); + return $"{_major}.{_minor}"; } } @@ -150,7 +150,7 @@ namespace MediaBrowser.MediaEncoding.Encoder } else { - // Version is ok so no warning required + _logger.LogInformation("FFmpeg validation: Found suitable ffmpeg version"); } } -- cgit v1.2.3 From 7668ecf9c95fe7deb8211704c24266d5b17f6c8c Mon Sep 17 00:00:00 2001 From: PloughPuff Date: Wed, 27 Feb 2019 18:20:48 +0000 Subject: Use Version Class to ease comparisons --- .../Encoder/EncoderValidator.cs | 105 +++++---------------- 1 file changed, 23 insertions(+), 82 deletions(-) (limited to 'MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs') diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index be33ea58e..5ebd189ac 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -8,66 +8,6 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.MediaEncoding.Encoder { - public class FFmpegVersion - { - private readonly int _major; - private readonly int _minor; - - private const int _unknown = 0; - - public FFmpegVersion(string p1) - { - var match = Regex.Match(p1, @"(?\d+)\.(?\d+)"); - - if (match.Groups["major"].Success && match.Groups["minor"].Success) - { - _major = int.Parse(match.Groups["major"].Value); - _minor = int.Parse(match.Groups["minor"].Value); - } - else - { - _major = _unknown; - _minor = _unknown; - } - } - - public override string ToString() - { - switch (_major) - { - case _unknown: - return "Unknown"; - default: - return $"{_major}.{_minor}"; - } - } - - public bool Unknown() - { - return _major == _unknown; - } - - public bool Below(FFmpegVersion checkAgainst) - { - return ToScalar() < checkAgainst.ToScalar(); - } - - public bool Above(FFmpegVersion checkAgainst) - { - return ToScalar() > checkAgainst.ToScalar(); - } - - public bool Same(FFmpegVersion checkAgainst) - { - return ToScalar() == checkAgainst.ToScalar(); - } - - private int ToScalar() - { - return (_major * 1000) + _minor; - } - } - public class EncoderValidator { private readonly ILogger _logger; @@ -119,19 +59,19 @@ namespace MediaBrowser.MediaEncoding.Encoder } // The min and max FFmpeg versions required to run jellyfin successfully - FFmpegVersion minRequired = new FFmpegVersion("4.0"); - FFmpegVersion maxRequired = new FFmpegVersion("4.0"); + var minRequired = new Version(4, 0); + var maxRequired = new Version(4, 0); // Work out what the version under test is - FFmpegVersion underTest = GetFFmpegVersion(output); + var underTest = GetFFmpegVersion(output); if (logOutput) { - _logger.LogInformation("FFmpeg validation: Found ffmpeg version {0}", underTest.ToString()); + _logger.LogInformation("FFmpeg validation: Found ffmpeg version {0}", underTest != null ? underTest.ToString() : "unknown"); - if (underTest.Unknown()) + if (underTest == null) // Version is unknown { - if (minRequired.Same(maxRequired)) + if (minRequired.Equals(maxRequired)) { _logger.LogWarning("FFmpeg validation: We recommend ffmpeg version {0}", minRequired.ToString()); } @@ -140,21 +80,22 @@ namespace MediaBrowser.MediaEncoding.Encoder _logger.LogWarning("FFmpeg validation: We recommend a minimum of {0} and maximum of {1}", minRequired.ToString(), maxRequired.ToString()); } } - else if (underTest.Below(minRequired)) + else if (underTest.CompareTo(minRequired) < 0) // Version is below what we recommend { _logger.LogWarning("FFmpeg validation: The minimum recommended ffmpeg version is {0}", minRequired.ToString()); } - else if (underTest.Above(maxRequired)) + else if (underTest.CompareTo(maxRequired) > 0) // Version is above what we recommend { _logger.LogWarning("FFmpeg validation: The maximum recommended ffmpeg version is {0}", maxRequired.ToString()); } - else + else // Version is ok { _logger.LogInformation("FFmpeg validation: Found suitable ffmpeg version"); } } - return !underTest.Below(minRequired) && !underTest.Above(maxRequired); + // underTest shall be null if versions is unknown + return (underTest == null) ? false : !(underTest.CompareTo(minRequired) < 0) && !(underTest.CompareTo(maxRequired) > 0); } /// @@ -166,36 +107,36 @@ namespace MediaBrowser.MediaEncoding.Encoder /// /// /// - static private FFmpegVersion GetFFmpegVersion(string output) + static private Version GetFFmpegVersion(string output) { // For pre-built binaries the FFmpeg version should be mentioned at the very start of the output var match = Regex.Match(output, @"ffmpeg version (\d+\.\d+)"); if (match.Success) { - return new FFmpegVersion(match.Groups[1].Value); + return new Version(match.Groups[1].Value); } else { // Try and use the individual library versions to determine a FFmpeg version // This lookup table is to be maintained with the following command line: // $ ./ffmpeg.exe -version | perl -ne ' print "$1=$2.$3," if /^(lib\w+)\s+(\d+)\.\s*(\d+)/' - var lut = new ReadOnlyDictionary - (new Dictionary + var lut = new ReadOnlyDictionary + (new Dictionary { - { new FFmpegVersion("4.1"), "libavutil=56.22,libavcodec=58.35,libavformat=58.20,libavdevice=58.5,libavfilter=7.40,libswscale=5.3,libswresample=3.3,libpostproc=55.3," }, - { new FFmpegVersion("4.0"), "libavutil=56.14,libavcodec=58.18,libavformat=58.12,libavdevice=58.3,libavfilter=7.16,libswscale=5.1,libswresample=3.1,libpostproc=55.1," }, - { new FFmpegVersion("3.4"), "libavutil=55.78,libavcodec=57.107,libavformat=57.83,libavdevice=57.10,libavfilter=6.107,libswscale=4.8,libswresample=2.9,libpostproc=54.7," }, - { new FFmpegVersion("3.3"), "libavutil=55.58,libavcodec=57.89,libavformat=57.71,libavdevice=57.6,libavfilter=6.82,libswscale=4.6,libswresample=2.7,libpostproc=54.5," }, - { new FFmpegVersion("3.2"), "libavutil=55.34,libavcodec=57.64,libavformat=57.56,libavdevice=57.1,libavfilter=6.65,libswscale=4.2,libswresample=2.3,libpostproc=54.1," }, - { new FFmpegVersion("2.8"), "libavutil=54.31,libavcodec=56.60,libavformat=56.40,libavdevice=56.4,libavfilter=5.40,libswscale=3.1,libswresample=1.2,libpostproc=53.3," } + { new Version("4.1"), "libavutil=56.22,libavcodec=58.35,libavformat=58.20,libavdevice=58.5,libavfilter=7.40,libswscale=5.3,libswresample=3.3,libpostproc=55.3," }, + { new Version("4.0"), "libavutil=56.14,libavcodec=58.18,libavformat=58.12,libavdevice=58.3,libavfilter=7.16,libswscale=5.1,libswresample=3.1,libpostproc=55.1," }, + { new Version("3.4"), "libavutil=55.78,libavcodec=57.107,libavformat=57.83,libavdevice=57.10,libavfilter=6.107,libswscale=4.8,libswresample=2.9,libpostproc=54.7," }, + { new Version("3.3"), "libavutil=55.58,libavcodec=57.89,libavformat=57.71,libavdevice=57.6,libavfilter=6.82,libswscale=4.6,libswresample=2.7,libpostproc=54.5," }, + { new Version("3.2"), "libavutil=55.34,libavcodec=57.64,libavformat=57.56,libavdevice=57.1,libavfilter=6.65,libswscale=4.2,libswresample=2.3,libpostproc=54.1," }, + { new Version("2.8"), "libavutil=54.31,libavcodec=56.60,libavformat=56.40,libavdevice=56.4,libavfilter=5.40,libswscale=3.1,libswresample=1.2,libpostproc=53.3," } }); // Create a reduced version string and lookup key from dictionary var reducedVersion = GetVersionString(output); - var found = lut.FirstOrDefault(x => x.Value == reducedVersion).Key; - return found ?? new FFmpegVersion("Unknown"); + // Try to lookup the string and return Key, otherwise if not found returns null + return lut.FirstOrDefault(x => x.Value == reducedVersion).Key; } } -- cgit v1.2.3 From f1086a72bf1ebf486464c877efb66bb4f87771fe Mon Sep 17 00:00:00 2001 From: Vasily Date: Thu, 28 Feb 2019 15:58:41 +0000 Subject: Improve logic when determining return value Co-Authored-By: ploughpuff <33969763+ploughpuff@users.noreply.github.com> --- MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs') diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index 5ebd189ac..f725d2c01 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -95,7 +95,7 @@ namespace MediaBrowser.MediaEncoding.Encoder } // underTest shall be null if versions is unknown - return (underTest == null) ? false : !(underTest.CompareTo(minRequired) < 0) && !(underTest.CompareTo(maxRequired) > 0); + return (underTest == null) ? false : (underTest.CompareTo(minRequired) >= 0 && underTest.CompareTo(maxRequired) <= 0); } /// -- cgit v1.2.3 From 20775116f76b12bf77672fa37c4ea5f82b69f157 Mon Sep 17 00:00:00 2001 From: PloughPuff Date: Fri, 8 Feb 2019 13:35:26 +0000 Subject: Reworked FFmpeg path discovery and always display to user 1) Reworked FFmpeg and FFprobe path discovery (CLI switch, Custom xml, system $PATH, UI update trigger). Removed FFMpeg folder from Emby.Server.Implementations. All path discovery now in MediaEncoder. 2) Always display FFmpeg path to user in Transcode page. 3) Allow user to remove a Custome FFmpeg path and return to using system $PATH (or --ffmpeg if available). 4) Remove unused code associated with 'prebuilt' FFmpeg. 5) Much improved logging during path discovery. --- Emby.Server.Implementations/ApplicationHost.cs | 72 +--- Emby.Server.Implementations/FFMpeg/FFMpegInfo.cs | 24 -- .../FFMpeg/FFMpegInstallInfo.cs | 17 - Emby.Server.Implementations/FFMpeg/FFMpegLoader.cs | 132 ------- .../Encoder/EncoderValidator.cs | 2 +- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 433 ++++++++++----------- .../Configuration/EncodingOptions.cs | 3 +- 7 files changed, 212 insertions(+), 471 deletions(-) delete mode 100644 Emby.Server.Implementations/FFMpeg/FFMpegInfo.cs delete mode 100644 Emby.Server.Implementations/FFMpeg/FFMpegInstallInfo.cs delete mode 100644 Emby.Server.Implementations/FFMpeg/FFMpegLoader.cs (limited to 'MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 94d2cd5da..2c0d0e746 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -28,7 +28,6 @@ using Emby.Server.Implementations.Data; using Emby.Server.Implementations.Devices; using Emby.Server.Implementations.Diagnostics; using Emby.Server.Implementations.Dto; -using Emby.Server.Implementations.FFMpeg; using Emby.Server.Implementations.HttpServer; using Emby.Server.Implementations.HttpServer.Security; using Emby.Server.Implementations.IO; @@ -792,7 +791,8 @@ namespace Emby.Server.Implementations ChapterManager = new ChapterManager(LibraryManager, LoggerFactory, ServerConfigurationManager, ItemRepository); serviceCollection.AddSingleton(ChapterManager); - RegisterMediaEncoder(serviceCollection); + MediaEncoder = new MediaBrowser.MediaEncoding.Encoder.MediaEncoder(LoggerFactory, JsonSerializer, StartupOptions.FFmpegPath, StartupOptions.FFprobePath, ServerConfigurationManager, FileSystemManager, () => SubtitleEncoder, () => MediaSourceManager, ProcessFactory, 5000); + serviceCollection.AddSingleton(MediaEncoder); EncodingManager = new MediaEncoder.EncodingManager(FileSystemManager, LoggerFactory, MediaEncoder, ChapterManager, LibraryManager); serviceCollection.AddSingleton(EncodingManager); @@ -908,83 +908,25 @@ namespace Emby.Server.Implementations return new ImageProcessor(LoggerFactory, ServerConfigurationManager.ApplicationPaths, FileSystemManager, ImageEncoder, () => LibraryManager, () => MediaEncoder); } - protected virtual FFMpegInstallInfo GetFfmpegInstallInfo() - { - var info = new FFMpegInstallInfo(); - - // Windows builds: http://ffmpeg.zeranoe.com/builds/ - // Linux builds: http://johnvansickle.com/ffmpeg/ - // OS X builds: http://ffmpegmac.net/ - // OS X x64: http://www.evermeet.cx/ffmpeg/ - - if (EnvironmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Linux) - { - info.FFMpegFilename = "ffmpeg"; - info.FFProbeFilename = "ffprobe"; - info.ArchiveType = "7z"; - info.Version = "20170308"; - } - else if (EnvironmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows) - { - info.FFMpegFilename = "ffmpeg.exe"; - info.FFProbeFilename = "ffprobe.exe"; - info.Version = "20170308"; - info.ArchiveType = "7z"; - } - else if (EnvironmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.OSX) - { - info.FFMpegFilename = "ffmpeg"; - info.FFProbeFilename = "ffprobe"; - info.ArchiveType = "7z"; - info.Version = "20170308"; - } - - return info; - } - - protected virtual FFMpegInfo GetFFMpegInfo() - { - return new FFMpegLoader(ApplicationPaths, FileSystemManager, GetFfmpegInstallInfo()) - .GetFFMpegInfo(StartupOptions); - } - /// /// Registers the media encoder. /// /// Task. - private void RegisterMediaEncoder(IServiceCollection serviceCollection) + private void RegisterMediaEncoder(IAssemblyInfo assemblyInfo) { - string encoderPath = null; - string probePath = null; - - var info = GetFFMpegInfo(); - - encoderPath = info.EncoderPath; - probePath = info.ProbePath; - var hasExternalEncoder = string.Equals(info.Version, "external", StringComparison.OrdinalIgnoreCase); - - var mediaEncoder = new MediaBrowser.MediaEncoding.Encoder.MediaEncoder( + MediaEncoder = new MediaBrowser.MediaEncoding.Encoder.MediaEncoder( LoggerFactory, JsonSerializer, - encoderPath, - probePath, - hasExternalEncoder, + StartupOptions.FFmpegPath, + StartupOptions.FFprobePath, ServerConfigurationManager, FileSystemManager, - LiveTvManager, - IsoManager, - LibraryManager, - ChannelManager, - SessionManager, () => SubtitleEncoder, () => MediaSourceManager, - HttpClient, - ZipClient, ProcessFactory, 5000); - MediaEncoder = mediaEncoder; - serviceCollection.AddSingleton(MediaEncoder); + RegisterSingleInstance(MediaEncoder); } /// diff --git a/Emby.Server.Implementations/FFMpeg/FFMpegInfo.cs b/Emby.Server.Implementations/FFMpeg/FFMpegInfo.cs deleted file mode 100644 index 60cd7b3d7..000000000 --- a/Emby.Server.Implementations/FFMpeg/FFMpegInfo.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace Emby.Server.Implementations.FFMpeg -{ - /// - /// Class FFMpegInfo - /// - public class FFMpegInfo - { - /// - /// Gets or sets the path. - /// - /// The path. - public string EncoderPath { get; set; } - /// - /// Gets or sets the probe path. - /// - /// The probe path. - public string ProbePath { get; set; } - /// - /// Gets or sets the version. - /// - /// The version. - public string Version { get; set; } - } -} diff --git a/Emby.Server.Implementations/FFMpeg/FFMpegInstallInfo.cs b/Emby.Server.Implementations/FFMpeg/FFMpegInstallInfo.cs deleted file mode 100644 index fa9cb5e01..000000000 --- a/Emby.Server.Implementations/FFMpeg/FFMpegInstallInfo.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Emby.Server.Implementations.FFMpeg -{ - public class FFMpegInstallInfo - { - public string Version { get; set; } - public string FFMpegFilename { get; set; } - public string FFProbeFilename { get; set; } - public string ArchiveType { get; set; } - - public FFMpegInstallInfo() - { - Version = "Path"; - FFMpegFilename = "ffmpeg"; - FFProbeFilename = "ffprobe"; - } - } -} diff --git a/Emby.Server.Implementations/FFMpeg/FFMpegLoader.cs b/Emby.Server.Implementations/FFMpeg/FFMpegLoader.cs deleted file mode 100644 index bbf51dd24..000000000 --- a/Emby.Server.Implementations/FFMpeg/FFMpegLoader.cs +++ /dev/null @@ -1,132 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using MediaBrowser.Common.Configuration; -using MediaBrowser.Model.IO; - -namespace Emby.Server.Implementations.FFMpeg -{ - public class FFMpegLoader - { - private readonly IApplicationPaths _appPaths; - private readonly IFileSystem _fileSystem; - private readonly FFMpegInstallInfo _ffmpegInstallInfo; - - public FFMpegLoader(IApplicationPaths appPaths, IFileSystem fileSystem, FFMpegInstallInfo ffmpegInstallInfo) - { - _appPaths = appPaths; - _fileSystem = fileSystem; - _ffmpegInstallInfo = ffmpegInstallInfo; - } - - public FFMpegInfo GetFFMpegInfo(IStartupOptions options) - { - var customffMpegPath = options.FFmpegPath; - var customffProbePath = options.FFprobePath; - - if (!string.IsNullOrWhiteSpace(customffMpegPath) && !string.IsNullOrWhiteSpace(customffProbePath)) - { - return new FFMpegInfo - { - ProbePath = customffProbePath, - EncoderPath = customffMpegPath, - Version = "external" - }; - } - - var downloadInfo = _ffmpegInstallInfo; - - var prebuiltFolder = _appPaths.ProgramSystemPath; - var prebuiltffmpeg = Path.Combine(prebuiltFolder, downloadInfo.FFMpegFilename); - var prebuiltffprobe = Path.Combine(prebuiltFolder, downloadInfo.FFProbeFilename); - if (File.Exists(prebuiltffmpeg) && File.Exists(prebuiltffprobe)) - { - return new FFMpegInfo - { - ProbePath = prebuiltffprobe, - EncoderPath = prebuiltffmpeg, - Version = "external" - }; - } - - var version = downloadInfo.Version; - - if (string.Equals(version, "0", StringComparison.OrdinalIgnoreCase)) - { - return new FFMpegInfo(); - } - - var rootEncoderPath = Path.Combine(_appPaths.ProgramDataPath, "ffmpeg"); - var versionedDirectoryPath = Path.Combine(rootEncoderPath, version); - - var info = new FFMpegInfo - { - ProbePath = Path.Combine(versionedDirectoryPath, downloadInfo.FFProbeFilename), - EncoderPath = Path.Combine(versionedDirectoryPath, downloadInfo.FFMpegFilename), - Version = version - }; - - Directory.CreateDirectory(versionedDirectoryPath); - - var excludeFromDeletions = new List { versionedDirectoryPath }; - - if (!File.Exists(info.ProbePath) || !File.Exists(info.EncoderPath)) - { - // ffmpeg not present. See if there's an older version we can start with - var existingVersion = GetExistingVersion(info, rootEncoderPath); - - // No older version. Need to download and block until complete - if (existingVersion == null) - { - return new FFMpegInfo(); - } - else - { - info = existingVersion; - versionedDirectoryPath = Path.GetDirectoryName(info.EncoderPath); - excludeFromDeletions.Add(versionedDirectoryPath); - } - } - - // Allow just one of these to be overridden, if desired. - if (!string.IsNullOrWhiteSpace(customffMpegPath)) - { - info.EncoderPath = customffMpegPath; - } - if (!string.IsNullOrWhiteSpace(customffProbePath)) - { - info.ProbePath = customffProbePath; - } - - return info; - } - - private FFMpegInfo GetExistingVersion(FFMpegInfo info, string rootEncoderPath) - { - var encoderFilename = Path.GetFileName(info.EncoderPath); - var probeFilename = Path.GetFileName(info.ProbePath); - - foreach (var directory in _fileSystem.GetDirectoryPaths(rootEncoderPath)) - { - var allFiles = _fileSystem.GetFilePaths(directory, true).ToList(); - - var encoder = allFiles.FirstOrDefault(i => string.Equals(Path.GetFileName(i), encoderFilename, StringComparison.OrdinalIgnoreCase)); - var probe = allFiles.FirstOrDefault(i => string.Equals(Path.GetFileName(i), probeFilename, StringComparison.OrdinalIgnoreCase)); - - if (!string.IsNullOrWhiteSpace(encoder) && - !string.IsNullOrWhiteSpace(probe)) - { - return new FFMpegInfo - { - EncoderPath = encoder, - ProbePath = probe, - Version = Path.GetFileName(Path.GetDirectoryName(probe)) - }; - } - } - - return null; - } - } -} diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index f725d2c01..1eeea87a0 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -19,7 +19,7 @@ namespace MediaBrowser.MediaEncoding.Encoder _processFactory = processFactory; } - public (IEnumerable decoders, IEnumerable encoders) Validate(string encoderPath) + public (IEnumerable decoders, IEnumerable encoders) GetAvailableCoders(string encoderPath) { _logger.LogInformation("Validating media encoder at {EncoderPath}", encoderPath); diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 7f29c06b4..36d72cad9 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -7,13 +7,9 @@ using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.MediaEncoding; -using MediaBrowser.Controller.Session; using MediaBrowser.MediaEncoding.Probing; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Diagnostics; @@ -32,323 +28,288 @@ namespace MediaBrowser.MediaEncoding.Encoder public class MediaEncoder : IMediaEncoder, IDisposable { /// - /// The _logger - /// - private readonly ILogger _logger; - - /// - /// Gets the json serializer. + /// Gets the encoder path. /// - /// The json serializer. - private readonly IJsonSerializer _jsonSerializer; + /// The encoder path. + public string EncoderPath => FFmpegPath; /// - /// The _thumbnail resource pool + /// External: path supplied via command line + /// Custom: coming from UI or config/encoding.xml file + /// System: FFmpeg found in system $PATH + /// null: No FFmpeg found /// - private readonly SemaphoreSlim _thumbnailResourcePool = new SemaphoreSlim(1, 1); - - public string FFMpegPath { get; private set; } - - public string FFProbePath { get; private set; } + public string EncoderLocationType { get; private set; } + private readonly ILogger _logger; + private readonly IJsonSerializer _jsonSerializer; + private string FFmpegPath { get; set; } + private string FFprobePath { get; set; } protected readonly IServerConfigurationManager ConfigurationManager; protected readonly IFileSystem FileSystem; - protected readonly ILiveTvManager LiveTvManager; - protected readonly IIsoManager IsoManager; - protected readonly ILibraryManager LibraryManager; - protected readonly IChannelManager ChannelManager; - protected readonly ISessionManager SessionManager; protected readonly Func SubtitleEncoder; protected readonly Func MediaSourceManager; - private readonly IHttpClient _httpClient; - private readonly IZipClient _zipClient; private readonly IProcessFactory _processFactory; + private readonly int DefaultImageExtractionTimeoutMs; + private readonly string StartupOptionFFmpegPath; + private readonly string StartupOptionFFprobePath; + private readonly SemaphoreSlim _thumbnailResourcePool = new SemaphoreSlim(1, 1); private readonly List _runningProcesses = new List(); - private readonly bool _hasExternalEncoder; - private readonly string _originalFFMpegPath; - private readonly string _originalFFProbePath; - private readonly int DefaultImageExtractionTimeoutMs; public MediaEncoder( ILoggerFactory loggerFactory, IJsonSerializer jsonSerializer, - string ffMpegPath, - string ffProbePath, - bool hasExternalEncoder, + string startupOptionsFFmpegPath, + string startupOptionsFFprobePath, IServerConfigurationManager configurationManager, IFileSystem fileSystem, - ILiveTvManager liveTvManager, - IIsoManager isoManager, - ILibraryManager libraryManager, - IChannelManager channelManager, - ISessionManager sessionManager, Func subtitleEncoder, Func mediaSourceManager, - IHttpClient httpClient, - IZipClient zipClient, IProcessFactory processFactory, int defaultImageExtractionTimeoutMs) { _logger = loggerFactory.CreateLogger(nameof(MediaEncoder)); _jsonSerializer = jsonSerializer; + StartupOptionFFmpegPath = startupOptionsFFmpegPath; + StartupOptionFFprobePath = startupOptionsFFprobePath; ConfigurationManager = configurationManager; FileSystem = fileSystem; - LiveTvManager = liveTvManager; - IsoManager = isoManager; - LibraryManager = libraryManager; - ChannelManager = channelManager; - SessionManager = sessionManager; SubtitleEncoder = subtitleEncoder; - MediaSourceManager = mediaSourceManager; - _httpClient = httpClient; - _zipClient = zipClient; _processFactory = processFactory; DefaultImageExtractionTimeoutMs = defaultImageExtractionTimeoutMs; - FFProbePath = ffProbePath; - FFMpegPath = ffMpegPath; - _originalFFProbePath = ffProbePath; - _originalFFMpegPath = ffMpegPath; - _hasExternalEncoder = hasExternalEncoder; } - public string EncoderLocationType + /// + /// Run at startup or if the user removes a Custom path from transcode page. + /// Sets global variables FFmpegPath and EncoderLocationType. + /// If startup options --ffprobe is given then FFprobePath is set too. + /// + public void Init() { - get + // 1) If given, use the --ffmpeg CLI switch + if (ValidatePathFFmpeg("From CLI Switch", StartupOptionFFmpegPath)) { - if (_hasExternalEncoder) - { - return "External"; - } - - if (string.IsNullOrWhiteSpace(FFMpegPath)) - { - return null; - } - - if (IsSystemInstalledPath(FFMpegPath)) - { - return "System"; - } - - return "Custom"; + _logger.LogInformation("FFmpeg: Using path from command line switch --ffmpeg"); + EncoderLocationType = "External"; } - } - private bool IsSystemInstalledPath(string path) - { - if (path.IndexOf("/", StringComparison.Ordinal) == -1 && path.IndexOf("\\", StringComparison.Ordinal) == -1) + // 2) Try Custom path stroed in config/encoding xml file under tag + else if (ValidatePathFFmpeg("From Config File", ConfigurationManager.GetConfiguration("encoding").EncoderAppPathCustom)) { - return true; + _logger.LogInformation("FFmpeg: Using path from config/encoding.xml file"); + EncoderLocationType = "Custom"; } - return false; - } - - public void Init() - { - InitPaths(); - - if (!string.IsNullOrWhiteSpace(FFMpegPath)) + // 3) Search system $PATH environment variable for valid FFmpeg + else if (ValidatePathFFmpeg("From $PATH", ExistsOnSystemPath("ffmpeg"))) { - var result = new EncoderValidator(_logger, _processFactory).Validate(FFMpegPath); - - SetAvailableDecoders(result.decoders); - SetAvailableEncoders(result.encoders); + _logger.LogInformation("FFmpeg: Using system $PATH for FFmpeg"); + EncoderLocationType = "System"; + } + else + { + _logger.LogError("FFmpeg: No suitable executable found"); + FFmpegPath = null; + EncoderLocationType = null; } - } - - private void InitPaths() - { - ConfigureEncoderPaths(); - if (_hasExternalEncoder) + // If given, use the --ffprobe CLI switch + if (ValidatePathFFprobe("CLI Switch", StartupOptionFFprobePath)) { - LogPaths(); - return; + _logger.LogInformation("FFprobe: Using path from command line switch --ffprobe"); + } + else + { + // FFprobe path from command line is no good, so set to null and let ReInit() try + // and set using the FFmpeg path. + FFprobePath = null; } - // If the path was passed in, save it into config now. - var encodingOptions = GetEncodingOptions(); - var appPath = encodingOptions.EncoderAppPath; + ReInit(); + } - var valueToSave = FFMpegPath; + /// + /// Writes the currently used FFmpeg to config/encoding.xml file. + /// Sets the FFprobe path if not currently set. + /// Interrogates the FFmpeg tool to identify what encoders/decodres are available. + /// + private void ReInit() + { + // Write the FFmpeg path to the config/encoding.xml file so it appears in UI + var config = ConfigurationManager.GetConfiguration("encoding"); + config.EncoderAppPath = FFmpegPath ?? string.Empty; + ConfigurationManager.SaveConfiguration("encoding", config); - if (!string.IsNullOrWhiteSpace(valueToSave)) + // Only if mpeg path is set, try and set path to probe + if (FFmpegPath != null) { - // if using system variable, don't save this. - if (IsSystemInstalledPath(valueToSave) || _hasExternalEncoder) + // Probe would be null here if no valid --ffprobe path was given + // at startup, or we're performing ReInit following mpeg path update from UI + if (FFprobePath == null) { - valueToSave = null; + // Use the mpeg path to create a probe path + if (ValidatePathFFprobe("Copied from FFmpeg:", GetProbePathFromEncoderPath(FFmpegPath))) + { + _logger.LogInformation("FFprobe: Using FFprobe in same folders as FFmpeg"); + } + else + { + _logger.LogError("FFprobe: No suitable executable found"); + } } - } - if (!string.Equals(valueToSave, appPath, StringComparison.Ordinal)) - { - encodingOptions.EncoderAppPath = valueToSave; - ConfigurationManager.SaveConfiguration("encoding", encodingOptions); + // Interrogate to understand what coders it supports + var result = new EncoderValidator(_logger, _processFactory).GetAvailableCoders(FFmpegPath); + + SetAvailableDecoders(result.decoders); + SetAvailableEncoders(result.encoders); } + + // Stamp FFmpeg paths to the log file + LogPaths(); } + /// + /// Triggered from the Settings > Trascoding UI page when users sumits Custom FFmpeg path to use. + /// + /// + /// public void UpdateEncoderPath(string path, string pathType) { - if (_hasExternalEncoder) - { - return; - } - _logger.LogInformation("Attempting to update encoder path to {0}. pathType: {1}", path ?? string.Empty, pathType ?? string.Empty); - Tuple newPaths; - - if (string.Equals(pathType, "system", StringComparison.OrdinalIgnoreCase)) + if (!string.Equals(pathType, "custom", StringComparison.OrdinalIgnoreCase)) { - path = "ffmpeg"; - - newPaths = TestForInstalledVersions(); + throw new ArgumentException("Unexpected pathType value"); } - else if (string.Equals(pathType, "custom", StringComparison.OrdinalIgnoreCase)) + else { if (string.IsNullOrWhiteSpace(path)) { - throw new ArgumentNullException(nameof(path)); - } + // User had cleared the cutom path in UI. Clear the Custom config + // setting and peform full Init to relook any CLI switches and system $PATH + var config = ConfigurationManager.GetConfiguration("encoding"); + config.EncoderAppPathCustom = string.Empty; + ConfigurationManager.SaveConfiguration("encoding", config); - if (!File.Exists(path) && !Directory.Exists(path)) + Init(); + } + else if (!File.Exists(path) && !Directory.Exists(path)) { + // Given path is neither file or folder throw new ResourceNotFoundException(); } - newPaths = GetEncoderPaths(path); - } - else - { - throw new ArgumentException("Unexpected pathType value"); - } - - if (string.IsNullOrWhiteSpace(newPaths.Item1)) - { - throw new ResourceNotFoundException("ffmpeg not found"); - } - if (string.IsNullOrWhiteSpace(newPaths.Item2)) - { - throw new ResourceNotFoundException("ffprobe not found"); - } - - path = newPaths.Item1; + else + { + // Supplied path could be either file path or folder path. + // Resolve down to file path and validate + path = GetEncoderPath(path); - if (!ValidateVersion(path, true)) - { - throw new ResourceNotFoundException("ffmpeg version 3.0 or greater is required."); - } + if (path == null) + { + throw new ResourceNotFoundException("FFmpeg not found"); + } + else if (!ValidatePathFFmpeg("New From UI", path)) + { + throw new ResourceNotFoundException("Failed validation checks. Version 4.0 or greater is required"); + } + else + { + EncoderLocationType = "Custom"; - var config = GetEncodingOptions(); - config.EncoderAppPath = path; - ConfigurationManager.SaveConfiguration("encoding", config); + // Write the validated mpeg path to the xml as + // This ensures its not lost on new startup + var config = ConfigurationManager.GetConfiguration("encoding"); + config.EncoderAppPathCustom = FFmpegPath; + ConfigurationManager.SaveConfiguration("encoding", config); - Init(); - } + FFprobePath = null; // Clear probe path so it gets relooked in ReInit() - private bool ValidateVersion(string path, bool logOutput) - { - return new EncoderValidator(_logger, _processFactory).ValidateVersion(path, logOutput); + ReInit(); + } + } + } } - private void ConfigureEncoderPaths() + private bool ValidatePath(string type, string path) { - if (_hasExternalEncoder) + if (!string.IsNullOrEmpty(path)) { - return; - } - - var appPath = GetEncodingOptions().EncoderAppPath; + if (File.Exists(path)) + { + var valid = new EncoderValidator(_logger, _processFactory).ValidateVersion(path, true); - if (string.IsNullOrWhiteSpace(appPath)) - { - appPath = Path.Combine(ConfigurationManager.ApplicationPaths.ProgramDataPath, "ffmpeg"); + if (valid == true) + { + return true; + } + else + { + _logger.LogError("{0}: Failed validation checks. Version 4.0 or greater is required: {1}", type, path); + } + } + else + { + _logger.LogError("{0}: File not found: {1}", type, path); + } } - var newPaths = GetEncoderPaths(appPath); - if (string.IsNullOrWhiteSpace(newPaths.Item1) || string.IsNullOrWhiteSpace(newPaths.Item2) || IsSystemInstalledPath(appPath)) - { - newPaths = TestForInstalledVersions(); - } + return false; + } - if (!string.IsNullOrWhiteSpace(newPaths.Item1) && !string.IsNullOrWhiteSpace(newPaths.Item2)) + private bool ValidatePathFFmpeg(string comment, string path) + { + if (ValidatePath("FFmpeg: " + comment, path) == true) { - FFMpegPath = newPaths.Item1; - FFProbePath = newPaths.Item2; + FFmpegPath = path; + return true; } - LogPaths(); + return false; } - private Tuple GetEncoderPaths(string configuredPath) + private bool ValidatePathFFprobe(string comment, string path) { - var appPath = configuredPath; - - if (!string.IsNullOrWhiteSpace(appPath)) + if (ValidatePath("FFprobe: " + comment, path) == true) { - if (Directory.Exists(appPath)) - { - return GetPathsFromDirectory(appPath); - } - - if (File.Exists(appPath)) - { - return new Tuple(appPath, GetProbePathFromEncoderPath(appPath)); - } + FFprobePath = path; + return true; } - return new Tuple(null, null); + return false; } - private Tuple TestForInstalledVersions() + private string GetEncoderPath(string path) { - string encoderPath = null; - string probePath = null; - - if (_hasExternalEncoder && ValidateVersion(_originalFFMpegPath, true)) + if (Directory.Exists(path)) { - encoderPath = _originalFFMpegPath; - probePath = _originalFFProbePath; + return GetEncoderPathFromDirectory(path); } - if (string.IsNullOrWhiteSpace(encoderPath)) + if (File.Exists(path)) { - if (ValidateVersion("ffmpeg", true) && ValidateVersion("ffprobe", false)) - { - encoderPath = "ffmpeg"; - probePath = "ffprobe"; - } + return path; } - return new Tuple(encoderPath, probePath); + return null; } - private Tuple GetPathsFromDirectory(string path) + private string GetEncoderPathFromDirectory(string path) { - // Since we can't predict the file extension, first try directly within the folder - // If that doesn't pan out, then do a recursive search - var files = FileSystem.GetFilePaths(path); - - var excludeExtensions = new[] { ".c" }; - - var ffmpegPath = files.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffmpeg", StringComparison.OrdinalIgnoreCase) && !excludeExtensions.Contains(Path.GetExtension(i) ?? string.Empty)); - var ffprobePath = files.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffprobe", StringComparison.OrdinalIgnoreCase) && !excludeExtensions.Contains(Path.GetExtension(i) ?? string.Empty)); - - if (string.IsNullOrWhiteSpace(ffmpegPath) || !File.Exists(ffmpegPath)) + try { - files = FileSystem.GetFilePaths(path, true); + var files = FileSystem.GetFilePaths(path); - ffmpegPath = files.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffmpeg", StringComparison.OrdinalIgnoreCase) && !excludeExtensions.Contains(Path.GetExtension(i) ?? string.Empty)); + var excludeExtensions = new[] { ".c" }; - if (!string.IsNullOrWhiteSpace(ffmpegPath)) - { - ffprobePath = GetProbePathFromEncoderPath(ffmpegPath); - } + return files.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffmpeg", StringComparison.OrdinalIgnoreCase) && !excludeExtensions.Contains(Path.GetExtension(i) ?? string.Empty)); + } + catch (Exception) + { + // Trap all exceptions, like DirNotExists, and return null + return null; } - - return new Tuple(ffmpegPath, ffprobePath); } private string GetProbePathFromEncoderPath(string appPath) @@ -357,15 +318,31 @@ namespace MediaBrowser.MediaEncoding.Encoder .FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffprobe", StringComparison.OrdinalIgnoreCase)); } - private void LogPaths() + /// + /// Search the system $PATH environment variable looking for given filename. + /// + /// + /// + private string ExistsOnSystemPath(string fileName) { - _logger.LogInformation("FFMpeg: {0}", FFMpegPath ?? "not found"); - _logger.LogInformation("FFProbe: {0}", FFProbePath ?? "not found"); + var values = Environment.GetEnvironmentVariable("PATH"); + + foreach (var path in values.Split(Path.PathSeparator)) + { + var candidatePath = GetEncoderPathFromDirectory(path); + + if (ValidatePath("Found on PATH", candidatePath)) + { + return candidatePath; + } + } + return null; } - private EncodingOptions GetEncodingOptions() + private void LogPaths() { - return ConfigurationManager.GetConfiguration("encoding"); + _logger.LogInformation("FFMpeg: {0}", FFmpegPath ?? "not found"); + _logger.LogInformation("FFProbe: {0}", FFprobePath ?? "not found"); } private List _encoders = new List(); @@ -412,12 +389,6 @@ namespace MediaBrowser.MediaEncoding.Encoder return true; } - /// - /// Gets the encoder path. - /// - /// The encoder path. - public string EncoderPath => FFMpegPath; - /// /// Gets the media info. /// @@ -489,7 +460,7 @@ namespace MediaBrowser.MediaEncoding.Encoder // Must consume both or ffmpeg may hang due to deadlocks. See comments below. RedirectStandardOutput = true, - FileName = FFProbePath, + FileName = FFprobePath, Arguments = string.Format(args, probeSizeArgument, inputPath).Trim(), IsHidden = true, @@ -691,7 +662,7 @@ namespace MediaBrowser.MediaEncoding.Encoder { CreateNoWindow = true, UseShellExecute = false, - FileName = FFMpegPath, + FileName = FFmpegPath, Arguments = args, IsHidden = true, ErrorDialog = false, @@ -814,7 +785,7 @@ namespace MediaBrowser.MediaEncoding.Encoder { CreateNoWindow = true, UseShellExecute = false, - FileName = FFMpegPath, + FileName = FFmpegPath, Arguments = args, IsHidden = true, ErrorDialog = false, diff --git a/MediaBrowser.Model/Configuration/EncodingOptions.cs b/MediaBrowser.Model/Configuration/EncodingOptions.cs index 8584bd3dd..ff697437a 100644 --- a/MediaBrowser.Model/Configuration/EncodingOptions.cs +++ b/MediaBrowser.Model/Configuration/EncodingOptions.cs @@ -8,7 +8,8 @@ namespace MediaBrowser.Model.Configuration public bool EnableThrottling { get; set; } public int ThrottleDelaySeconds { get; set; } public string HardwareAccelerationType { get; set; } - public string EncoderAppPath { get; set; } + public string EncoderAppPathCustom { get; set; } // FFmpeg path as set by the user via the UI + public string EncoderAppPath { get; set; } // The current FFmpeg path being used by the system public string VaapiDevice { get; set; } public int H264Crf { get; set; } public string H264Preset { get; set; } -- cgit v1.2.3 From ed69e690b89e6a3e6e22bbf448af08a25c38e71b Mon Sep 17 00:00:00 2001 From: PloughPuff Date: Tue, 12 Feb 2019 22:05:42 +0000 Subject: Review comments Address review comments from JustAMan, Bond-009 and cvium. --- Emby.Server.Implementations/ApplicationHost.cs | 35 +-- .../MediaEncoding/IMediaEncoder.cs | 3 +- .../Encoder/EncoderValidator.cs | 8 + MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 247 ++++++++++----------- MediaBrowser.Model/System/SystemInfo.cs | 17 +- 5 files changed, 150 insertions(+), 160 deletions(-) (limited to 'MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 2c0d0e746..dd29f2ade 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -791,7 +791,17 @@ namespace Emby.Server.Implementations ChapterManager = new ChapterManager(LibraryManager, LoggerFactory, ServerConfigurationManager, ItemRepository); serviceCollection.AddSingleton(ChapterManager); - MediaEncoder = new MediaBrowser.MediaEncoding.Encoder.MediaEncoder(LoggerFactory, JsonSerializer, StartupOptions.FFmpegPath, StartupOptions.FFprobePath, ServerConfigurationManager, FileSystemManager, () => SubtitleEncoder, () => MediaSourceManager, ProcessFactory, 5000); + MediaEncoder = new MediaBrowser.MediaEncoding.Encoder.MediaEncoder( + LoggerFactory, + JsonSerializer, + StartupOptions.FFmpegPath, + StartupOptions.FFprobePath, + ServerConfigurationManager, + FileSystemManager, + () => SubtitleEncoder, + () => MediaSourceManager, + ProcessFactory, + 5000); serviceCollection.AddSingleton(MediaEncoder); EncodingManager = new MediaEncoder.EncodingManager(FileSystemManager, LoggerFactory, MediaEncoder, ChapterManager, LibraryManager); @@ -908,27 +918,6 @@ namespace Emby.Server.Implementations return new ImageProcessor(LoggerFactory, ServerConfigurationManager.ApplicationPaths, FileSystemManager, ImageEncoder, () => LibraryManager, () => MediaEncoder); } - /// - /// Registers the media encoder. - /// - /// Task. - private void RegisterMediaEncoder(IAssemblyInfo assemblyInfo) - { - MediaEncoder = new MediaBrowser.MediaEncoding.Encoder.MediaEncoder( - LoggerFactory, - JsonSerializer, - StartupOptions.FFmpegPath, - StartupOptions.FFprobePath, - ServerConfigurationManager, - FileSystemManager, - () => SubtitleEncoder, - () => MediaSourceManager, - ProcessFactory, - 5000); - - RegisterSingleInstance(MediaEncoder); - } - /// /// Gets the user repository. /// @@ -1404,7 +1393,7 @@ namespace Emby.Server.Implementations ServerName = FriendlyName, LocalAddress = localAddress, SupportsLibraryMonitor = true, - EncoderLocationType = MediaEncoder.EncoderLocationType, + EncoderLocation = MediaEncoder.EncoderLocation, SystemArchitecture = EnvironmentInfo.SystemArchitecture, SystemUpdateLevel = SystemUpdateLevel, PackageName = StartupOptions.PackageName diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs index 057e43910..8852dac05 100644 --- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs +++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs @@ -6,6 +6,7 @@ using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using MediaBrowser.Model.MediaInfo; +using MediaBrowser.Model.System; namespace MediaBrowser.Controller.MediaEncoding { @@ -14,7 +15,7 @@ namespace MediaBrowser.Controller.MediaEncoding /// public interface IMediaEncoder : ITranscoderSupport { - string EncoderLocationType { get; } + FFmpegLocation EncoderLocation { get; } /// /// Gets the encoder path. diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index 1eeea87a0..3eed891cb 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -48,6 +48,10 @@ namespace MediaBrowser.MediaEncoding.Encoder if (string.IsNullOrWhiteSpace(output)) { + if (logOutput) + { + _logger.LogError("FFmpeg validation: The process returned no result"); + } return false; } @@ -55,6 +59,10 @@ namespace MediaBrowser.MediaEncoding.Encoder if (output.IndexOf("Libav developers", StringComparison.OrdinalIgnoreCase) != -1) { + if (logOutput) + { + _logger.LogError("FFmpeg validation: avconv instead of ffmpeg is not supported"); + } return false; } diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 36d72cad9..9aad67ec7 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; +using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Configuration; @@ -18,6 +19,7 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.Serialization; +using MediaBrowser.Model.System; using Microsoft.Extensions.Logging; namespace MediaBrowser.MediaEncoding.Encoder @@ -34,17 +36,16 @@ namespace MediaBrowser.MediaEncoding.Encoder public string EncoderPath => FFmpegPath; /// - /// External: path supplied via command line - /// Custom: coming from UI or config/encoding.xml file - /// System: FFmpeg found in system $PATH - /// null: No FFmpeg found + /// The location of the discovered FFmpeg tool. /// - public string EncoderLocationType { get; private set; } + public FFmpegLocation EncoderLocation { get; private set; } + + private FFmpegLocation ProbeLocation; private readonly ILogger _logger; private readonly IJsonSerializer _jsonSerializer; - private string FFmpegPath { get; set; } - private string FFprobePath { get; set; } + private string FFmpegPath; + private string FFprobePath; protected readonly IServerConfigurationManager ConfigurationManager; protected readonly IFileSystem FileSystem; protected readonly Func SubtitleEncoder; @@ -54,6 +55,11 @@ namespace MediaBrowser.MediaEncoding.Encoder private readonly string StartupOptionFFmpegPath; private readonly string StartupOptionFFprobePath; + /// + /// Enum to identify the two types of FF utilities of interest. + /// + private enum FFtype { Mpeg, Probe }; + private readonly SemaphoreSlim _thumbnailResourcePool = new SemaphoreSlim(1, 1); private readonly List _runningProcesses = new List(); @@ -82,48 +88,24 @@ namespace MediaBrowser.MediaEncoding.Encoder /// /// Run at startup or if the user removes a Custom path from transcode page. - /// Sets global variables FFmpegPath and EncoderLocationType. - /// If startup options --ffprobe is given then FFprobePath is set too. + /// Sets global variables FFmpegPath. + /// Precedence is: Config > CLI > $PATH /// public void Init() { - // 1) If given, use the --ffmpeg CLI switch - if (ValidatePathFFmpeg("From CLI Switch", StartupOptionFFmpegPath)) - { - _logger.LogInformation("FFmpeg: Using path from command line switch --ffmpeg"); - EncoderLocationType = "External"; - } - - // 2) Try Custom path stroed in config/encoding xml file under tag - else if (ValidatePathFFmpeg("From Config File", ConfigurationManager.GetConfiguration("encoding").EncoderAppPathCustom)) - { - _logger.LogInformation("FFmpeg: Using path from config/encoding.xml file"); - EncoderLocationType = "Custom"; - } - - // 3) Search system $PATH environment variable for valid FFmpeg - else if (ValidatePathFFmpeg("From $PATH", ExistsOnSystemPath("ffmpeg"))) + // 1) Custom path stored in config/encoding xml file under tag takes precedence + if (!ValidatePath(FFtype.Mpeg, ConfigurationManager.GetConfiguration("encoding").EncoderAppPathCustom, FFmpegLocation.Custom)) { - _logger.LogInformation("FFmpeg: Using system $PATH for FFmpeg"); - EncoderLocationType = "System"; - } - else - { - _logger.LogError("FFmpeg: No suitable executable found"); - FFmpegPath = null; - EncoderLocationType = null; - } - - // If given, use the --ffprobe CLI switch - if (ValidatePathFFprobe("CLI Switch", StartupOptionFFprobePath)) - { - _logger.LogInformation("FFprobe: Using path from command line switch --ffprobe"); - } - else - { - // FFprobe path from command line is no good, so set to null and let ReInit() try - // and set using the FFmpeg path. - FFprobePath = null; + // 2) Check if the --ffmpeg CLI switch has been given + if (!ValidatePath(FFtype.Mpeg, StartupOptionFFmpegPath, FFmpegLocation.SetByArgument)) + { + // 3) Search system $PATH environment variable for valid FFmpeg + if (!ValidatePath(FFtype.Mpeg, ExistsOnSystemPath("ffmpeg"), FFmpegLocation.System)) + { + EncoderLocation = FFmpegLocation.NotFound; + FFmpegPath = null; + } + } } ReInit(); @@ -136,27 +118,27 @@ namespace MediaBrowser.MediaEncoding.Encoder /// private void ReInit() { - // Write the FFmpeg path to the config/encoding.xml file so it appears in UI + // Write the FFmpeg path to the config/encoding.xml file as so it appears in UI var config = ConfigurationManager.GetConfiguration("encoding"); config.EncoderAppPath = FFmpegPath ?? string.Empty; ConfigurationManager.SaveConfiguration("encoding", config); + // Clear probe settings in case probe validation fails + ProbeLocation = FFmpegLocation.NotFound; + FFprobePath = null; + // Only if mpeg path is set, try and set path to probe if (FFmpegPath != null) { - // Probe would be null here if no valid --ffprobe path was given - // at startup, or we're performing ReInit following mpeg path update from UI - if (FFprobePath == null) + if (EncoderLocation == FFmpegLocation.Custom || StartupOptionFFprobePath == null) { - // Use the mpeg path to create a probe path - if (ValidatePathFFprobe("Copied from FFmpeg:", GetProbePathFromEncoderPath(FFmpegPath))) - { - _logger.LogInformation("FFprobe: Using FFprobe in same folders as FFmpeg"); - } - else - { - _logger.LogError("FFprobe: No suitable executable found"); - } + // If mpeg was read from config, or CLI switch not given, try and set probe from mpeg path + ValidatePath(FFtype.Probe, GetProbePathFromEncoderPath(FFmpegPath), EncoderLocation); + } + else + { + // Else try and set probe path from CLI switch + ValidatePath(FFtype.Probe, StartupOptionFFmpegPath, FFmpegLocation.SetByArgument); } // Interrogate to understand what coders it supports @@ -183,108 +165,95 @@ namespace MediaBrowser.MediaEncoding.Encoder { throw new ArgumentException("Unexpected pathType value"); } - else + + if (string.IsNullOrWhiteSpace(path)) { - if (string.IsNullOrWhiteSpace(path)) - { - // User had cleared the cutom path in UI. Clear the Custom config - // setting and peform full Init to relook any CLI switches and system $PATH - var config = ConfigurationManager.GetConfiguration("encoding"); - config.EncoderAppPathCustom = string.Empty; - ConfigurationManager.SaveConfiguration("encoding", config); + // User had cleared the custom path in UI. Clear the Custom config + // setting and perform full Init to reinspect any CLI switches and system $PATH + var config = ConfigurationManager.GetConfiguration("encoding"); + config.EncoderAppPathCustom = string.Empty; + ConfigurationManager.SaveConfiguration("encoding", config); - Init(); - } - else if (!File.Exists(path) && !Directory.Exists(path)) + Init(); + } + else if (!File.Exists(path) && !Directory.Exists(path)) + { + // Given path is neither file or folder + throw new ResourceNotFoundException(); + } + else + { + // Supplied path could be either file path or folder path. + // Resolve down to file path and validate + if (!ValidatePath(FFtype.Mpeg, GetEncoderPath(path), FFmpegLocation.Custom)) { - // Given path is neither file or folder - throw new ResourceNotFoundException(); + throw new ResourceNotFoundException("Failed validation checks."); } else { - // Supplied path could be either file path or folder path. - // Resolve down to file path and validate - path = GetEncoderPath(path); - - if (path == null) - { - throw new ResourceNotFoundException("FFmpeg not found"); - } - else if (!ValidatePathFFmpeg("New From UI", path)) - { - throw new ResourceNotFoundException("Failed validation checks. Version 4.0 or greater is required"); - } - else - { - EncoderLocationType = "Custom"; - - // Write the validated mpeg path to the xml as - // This ensures its not lost on new startup - var config = ConfigurationManager.GetConfiguration("encoding"); - config.EncoderAppPathCustom = FFmpegPath; - ConfigurationManager.SaveConfiguration("encoding", config); - - FFprobePath = null; // Clear probe path so it gets relooked in ReInit() + // Write the validated mpeg path to the xml as + // This ensures its not lost on new startup + var config = ConfigurationManager.GetConfiguration("encoding"); + config.EncoderAppPathCustom = FFmpegPath; + ConfigurationManager.SaveConfiguration("encoding", config); - ReInit(); - } + ReInit(); } } } - private bool ValidatePath(string type, string path) + /// + /// Validates the supplied FQPN to ensure it is a FFxxx utility. + /// If checks pass, global variable FFmpegPath (or FFprobePath) and + /// EncoderLocation (or ProbeLocation) are updated. + /// + /// Either mpeg or probe + /// FQPN to test + /// Location (External, Custom, System) of tool + /// + private bool ValidatePath(FFtype type, string path, FFmpegLocation location) { + bool rc = false; + if (!string.IsNullOrEmpty(path)) { if (File.Exists(path)) { - var valid = new EncoderValidator(_logger, _processFactory).ValidateVersion(path, true); + rc = new EncoderValidator(_logger, _processFactory).ValidateVersion(path, false); - if (valid == true) + // Only update the global variables if the checks passed + if (rc) { - return true; + if (type == FFtype.Mpeg) + { + FFmpegPath = path; + EncoderLocation = location; + } + else + { + FFprobePath = path; + ProbeLocation = location; + } } else { - _logger.LogError("{0}: Failed validation checks. Version 4.0 or greater is required: {1}", type, path); + _logger.LogError("{0}: {1}: Failed version check: {2}", type.ToString(), location.ToString(), path); } } else { - _logger.LogError("{0}: File not found: {1}", type, path); + _logger.LogError("{0}: {1}: File not found: {2}", type.ToString(), location.ToString(), path); } } - return false; - } - - private bool ValidatePathFFmpeg(string comment, string path) - { - if (ValidatePath("FFmpeg: " + comment, path) == true) - { - FFmpegPath = path; - return true; - } - - return false; - } - - private bool ValidatePathFFprobe(string comment, string path) - { - if (ValidatePath("FFprobe: " + comment, path) == true) - { - FFprobePath = path; - return true; - } - - return false; + return rc; } private string GetEncoderPath(string path) { if (Directory.Exists(path)) { - return GetEncoderPathFromDirectory(path); + return GetEncoderPathFromDirectory(path, "ffmpeg"); } if (File.Exists(path)) @@ -295,7 +264,7 @@ namespace MediaBrowser.MediaEncoding.Encoder return null; } - private string GetEncoderPathFromDirectory(string path) + private string GetEncoderPathFromDirectory(string path, string filename) { try { @@ -303,7 +272,8 @@ namespace MediaBrowser.MediaEncoding.Encoder var excludeExtensions = new[] { ".c" }; - return files.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffmpeg", StringComparison.OrdinalIgnoreCase) && !excludeExtensions.Contains(Path.GetExtension(i) ?? string.Empty)); + return files.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), filename, StringComparison.OrdinalIgnoreCase) + && !excludeExtensions.Contains(Path.GetExtension(i) ?? string.Empty)); } catch (Exception) { @@ -314,8 +284,15 @@ namespace MediaBrowser.MediaEncoding.Encoder private string GetProbePathFromEncoderPath(string appPath) { - return FileSystem.GetFilePaths(Path.GetDirectoryName(appPath)) - .FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffprobe", StringComparison.OrdinalIgnoreCase)); + if (!string.IsNullOrEmpty(appPath)) + { + string pattern = @"[^\/\\]+?(\.[^\/\\\n.]+)?$"; + string substitution = @"ffprobe$1"; + + return Regex.Replace(appPath, pattern, substitution); + } + + return null; } /// @@ -323,15 +300,15 @@ namespace MediaBrowser.MediaEncoding.Encoder /// /// /// - private string ExistsOnSystemPath(string fileName) + private string ExistsOnSystemPath(string filename) { var values = Environment.GetEnvironmentVariable("PATH"); foreach (var path in values.Split(Path.PathSeparator)) { - var candidatePath = GetEncoderPathFromDirectory(path); + var candidatePath = GetEncoderPathFromDirectory(path, filename); - if (ValidatePath("Found on PATH", candidatePath)) + if (!string.IsNullOrEmpty(candidatePath)) { return candidatePath; } @@ -341,8 +318,8 @@ namespace MediaBrowser.MediaEncoding.Encoder private void LogPaths() { - _logger.LogInformation("FFMpeg: {0}", FFmpegPath ?? "not found"); - _logger.LogInformation("FFProbe: {0}", FFprobePath ?? "not found"); + _logger.LogInformation("FFmpeg: {0}: {1}", EncoderLocation.ToString(), FFmpegPath ?? string.Empty); + _logger.LogInformation("FFprobe: {0}: {1}", ProbeLocation.ToString(), FFprobePath ?? string.Empty); } private List _encoders = new List(); diff --git a/MediaBrowser.Model/System/SystemInfo.cs b/MediaBrowser.Model/System/SystemInfo.cs index 581a1069c..6482f2c84 100644 --- a/MediaBrowser.Model/System/SystemInfo.cs +++ b/MediaBrowser.Model/System/SystemInfo.cs @@ -4,6 +4,21 @@ using MediaBrowser.Model.Updates; namespace MediaBrowser.Model.System { + /// + /// Enum describing the location of the FFmpeg tool. + /// + public enum FFmpegLocation + { + /// No path to FFmpeg found. + NotFound, + /// Path supplied via command line using switch --ffmpeg. + SetByArgument, + /// User has supplied path via Transcoding UI page. + Custom, + /// FFmpeg tool found on system $PATH. + System + }; + /// /// Class SystemInfo /// @@ -122,7 +137,7 @@ namespace MediaBrowser.Model.System /// true if this instance has update available; otherwise, false. public bool HasUpdateAvailable { get; set; } - public string EncoderLocationType { get; set; } + public FFmpegLocation EncoderLocation { get; set; } public Architecture SystemArchitecture { get; set; } -- cgit v1.2.3 From 5d85076ad5152b60ca2101177330b24944a65411 Mon Sep 17 00:00:00 2001 From: JMCC Date: Sat, 11 May 2019 17:17:32 +0200 Subject: Enable Exynos V4L2-m2m HW encoder --- MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs | 1 + 1 file changed, 1 insertion(+) (limited to 'MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs') diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index 3eed891cb..b00350875 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -207,6 +207,7 @@ namespace MediaBrowser.MediaEncoding.Encoder "hevc_omx", "h264_vaapi", "hevc_vaapi", + "h264_v4l2m2m", "ac3" }; -- cgit v1.2.3 From 1b01a6ece1c60ed5b767f825265de17f78a2770a Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 28 Sep 2019 01:29:54 +0200 Subject: Add tests for EncoderValidator * Add support for ffmpeg 4.2 * Parse the complete ffmpeg version instead of only the first 2 digits * Make max and min version optional * Remove max limitation (for now) * Style improvements --- .../Encoder/EncoderValidator.cs | 299 ++++++++++----------- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 21 +- .../Properties/AssemblyInfo.cs | 2 + MediaBrowser.sln | 7 + .../Jellyfin.Common.Tests.csproj | 4 +- .../EncoderValidatorTests.cs | 39 +++ .../EncoderValidatorTestsData.cs | 31 +++ .../Jellyfin.MediaEncoding.Tests.csproj | 19 ++ 8 files changed, 246 insertions(+), 176 deletions(-) create mode 100644 tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs create mode 100644 tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs create mode 100644 tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj (limited to 'MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs') diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index b00350875..da0b7693e 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -1,111 +1,154 @@ using System; using System.Collections.Generic; -using System.Collections.ObjectModel; +using System.Diagnostics; using System.Linq; +using System.Text; using System.Text.RegularExpressions; -using MediaBrowser.Model.Diagnostics; using Microsoft.Extensions.Logging; namespace MediaBrowser.MediaEncoding.Encoder { public class EncoderValidator { - private readonly ILogger _logger; - private readonly IProcessFactory _processFactory; + private const string DefaultEncoderPath = "ffmpeg"; - public EncoderValidator(ILogger logger, IProcessFactory processFactory) + private static readonly string[] requiredDecoders = new[] { - _logger = logger; - _processFactory = processFactory; - } + "mpeg2video", + "h264_qsv", + "hevc_qsv", + "mpeg2_qsv", + "vc1_qsv", + "h264_cuvid", + "hevc_cuvid", + "dts", + "ac3", + "aac", + "mp3", + "h264", + "hevc" + }; - public (IEnumerable decoders, IEnumerable encoders) GetAvailableCoders(string encoderPath) + private static readonly string[] requiredEncoders = new[] + { + "libx264", + "libx265", + "mpeg4", + "msmpeg4", + "libvpx", + "libvpx-vp9", + "aac", + "libmp3lame", + "libopus", + "libvorbis", + "srt", + "h264_nvenc", + "hevc_nvenc", + "h264_qsv", + "hevc_qsv", + "h264_omx", + "hevc_omx", + "h264_vaapi", + "hevc_vaapi", + "h264_v4l2m2m", + "ac3" + }; + + // Try and use the individual library versions to determine a FFmpeg version + // This lookup table is to be maintained with the following command line: + // $ ffmpeg -version | perl -ne ' print "$1=$2.$3," if /^(lib\w+)\s+(\d+)\.\s*(\d+)/' + private static readonly IReadOnlyDictionary _ffmpegVersionMap = new Dictionary { - _logger.LogInformation("Validating media encoder at {EncoderPath}", encoderPath); + { "libavutil=56.31,libavcodec=58.54,libavformat=58.29,libavdevice=58.8,libavfilter=7.57,libswscale=5.5,libswresample=3.5,libpostproc=55.5,", new Version(4, 2) }, + { "libavutil=56.22,libavcodec=58.35,libavformat=58.20,libavdevice=58.5,libavfilter=7.40,libswscale=5.3,libswresample=3.3,libpostproc=55.3,", new Version(4, 1) }, + { "libavutil=56.14,libavcodec=58.18,libavformat=58.12,libavdevice=58.3,libavfilter=7.16,libswscale=5.1,libswresample=3.1,libpostproc=55.1,", new Version(4, 0) }, + { "libavutil=55.78,libavcodec=57.107,libavformat=57.83,libavdevice=57.10,libavfilter=6.107,libswscale=4.8,libswresample=2.9,libpostproc=54.7,", new Version(3, 4) }, + { "libavutil=55.58,libavcodec=57.89,libavformat=57.71,libavdevice=57.6,libavfilter=6.82,libswscale=4.6,libswresample=2.7,libpostproc=54.5,", new Version(3, 3) }, + { "libavutil=55.34,libavcodec=57.64,libavformat=57.56,libavdevice=57.1,libavfilter=6.65,libswscale=4.2,libswresample=2.3,libpostproc=54.1,", new Version(3, 2) }, + { "libavutil=54.31,libavcodec=56.60,libavformat=56.40,libavdevice=56.4,libavfilter=5.40,libswscale=3.1,libswresample=1.2,libpostproc=53.3,", new Version(2, 8) } + }; - var decoders = GetCodecs(encoderPath, Codec.Decoder); - var encoders = GetCodecs(encoderPath, Codec.Encoder); + private readonly ILogger _logger; - _logger.LogInformation("Encoder validation complete"); + private readonly string _encoderPath; - return (decoders, encoders); + public EncoderValidator(ILogger logger, string encoderPath = DefaultEncoderPath) + { + _logger = logger; + _encoderPath = encoderPath; } - public bool ValidateVersion(string encoderAppPath, bool logOutput) + public static Version MinVersion { get; } = new Version(4, 0); + + public static Version MaxVersion { get; } = null; + + public bool ValidateVersion() { string output = null; try { - output = GetProcessOutput(encoderAppPath, "-version"); + output = GetProcessOutput(_encoderPath, "-version"); } catch (Exception ex) { - if (logOutput) - { - _logger.LogError(ex, "Error validating encoder"); - } + _logger.LogError(ex, "Error validating encoder"); } if (string.IsNullOrWhiteSpace(output)) { - if (logOutput) - { - _logger.LogError("FFmpeg validation: The process returned no result"); - } + _logger.LogError("FFmpeg validation: The process returned no result"); return false; } _logger.LogDebug("ffmpeg output: {Output}", output); - if (output.IndexOf("Libav developers", StringComparison.OrdinalIgnoreCase) != -1) + return ValidateVersionInternal(output); + } + + internal bool ValidateVersionInternal(string versionOutput) + { + if (versionOutput.IndexOf("Libav developers", StringComparison.OrdinalIgnoreCase) != -1) { - if (logOutput) - { - _logger.LogError("FFmpeg validation: avconv instead of ffmpeg is not supported"); - } + _logger.LogError("FFmpeg validation: avconv instead of ffmpeg is not supported"); return false; } - // The min and max FFmpeg versions required to run jellyfin successfully - var minRequired = new Version(4, 0); - var maxRequired = new Version(4, 0); - // Work out what the version under test is - var underTest = GetFFmpegVersion(output); + var version = GetFFmpegVersion(versionOutput); - if (logOutput) - { - _logger.LogInformation("FFmpeg validation: Found ffmpeg version {0}", underTest != null ? underTest.ToString() : "unknown"); + _logger.LogInformation("Found ffmpeg version {0}", version != null ? version.ToString() : "unknown"); - if (underTest == null) // Version is unknown - { - if (minRequired.Equals(maxRequired)) - { - _logger.LogWarning("FFmpeg validation: We recommend ffmpeg version {0}", minRequired.ToString()); - } - else - { - _logger.LogWarning("FFmpeg validation: We recommend a minimum of {0} and maximum of {1}", minRequired.ToString(), maxRequired.ToString()); - } - } - else if (underTest.CompareTo(minRequired) < 0) // Version is below what we recommend - { - _logger.LogWarning("FFmpeg validation: The minimum recommended ffmpeg version is {0}", minRequired.ToString()); - } - else if (underTest.CompareTo(maxRequired) > 0) // Version is above what we recommend + if (version == null && MinVersion != null && MaxVersion != null) // Version is unknown + { + if (MinVersion == MaxVersion) { - _logger.LogWarning("FFmpeg validation: The maximum recommended ffmpeg version is {0}", maxRequired.ToString()); + _logger.LogWarning("FFmpeg validation: We recommend ffmpeg version {0}", MinVersion); } - else // Version is ok + else { - _logger.LogInformation("FFmpeg validation: Found suitable ffmpeg version"); + _logger.LogWarning("FFmpeg validation: We recommend a minimum of {0} and maximum of {1}", MinVersion, MaxVersion); } + + return false; + } + else if (MinVersion != null && version < MinVersion) // Version is below what we recommend + { + _logger.LogWarning("FFmpeg validation: The minimum recommended ffmpeg version is {0}", MinVersion); + return false; + } + else if (MaxVersion != null && version > MaxVersion) // Version is above what we recommend + { + _logger.LogWarning("FFmpeg validation: The maximum recommended ffmpeg version is {0}", MaxVersion); + return false; } - // underTest shall be null if versions is unknown - return (underTest == null) ? false : (underTest.CompareTo(minRequired) >= 0 && underTest.CompareTo(maxRequired) <= 0); + return true; } + public IEnumerable GetDecoders() => GetCodecs(Codec.Decoder); + + public IEnumerable GetEncoders() => GetCodecs(Codec.Encoder); + /// /// Using the output from "ffmpeg -version" work out the FFmpeg version. /// For pre-built binaries the first line should contain a string like "ffmpeg version x.y", which is easy @@ -115,10 +158,10 @@ namespace MediaBrowser.MediaEncoding.Encoder /// /// /// - static private Version GetFFmpegVersion(string output) + internal static Version GetFFmpegVersion(string output) { // For pre-built binaries the FFmpeg version should be mentioned at the very start of the output - var match = Regex.Match(output, @"ffmpeg version (\d+\.\d+)"); + var match = Regex.Match(output, @"ffmpeg version ((?:\d+\.?)+)"); if (match.Success) { @@ -126,25 +169,11 @@ namespace MediaBrowser.MediaEncoding.Encoder } else { - // Try and use the individual library versions to determine a FFmpeg version - // This lookup table is to be maintained with the following command line: - // $ ./ffmpeg.exe -version | perl -ne ' print "$1=$2.$3," if /^(lib\w+)\s+(\d+)\.\s*(\d+)/' - var lut = new ReadOnlyDictionary - (new Dictionary - { - { new Version("4.1"), "libavutil=56.22,libavcodec=58.35,libavformat=58.20,libavdevice=58.5,libavfilter=7.40,libswscale=5.3,libswresample=3.3,libpostproc=55.3," }, - { new Version("4.0"), "libavutil=56.14,libavcodec=58.18,libavformat=58.12,libavdevice=58.3,libavfilter=7.16,libswscale=5.1,libswresample=3.1,libpostproc=55.1," }, - { new Version("3.4"), "libavutil=55.78,libavcodec=57.107,libavformat=57.83,libavdevice=57.10,libavfilter=6.107,libswscale=4.8,libswresample=2.9,libpostproc=54.7," }, - { new Version("3.3"), "libavutil=55.58,libavcodec=57.89,libavformat=57.71,libavdevice=57.6,libavfilter=6.82,libswscale=4.6,libswresample=2.7,libpostproc=54.5," }, - { new Version("3.2"), "libavutil=55.34,libavcodec=57.64,libavformat=57.56,libavdevice=57.1,libavfilter=6.65,libswscale=4.2,libswresample=2.3,libpostproc=54.1," }, - { new Version("2.8"), "libavutil=54.31,libavcodec=56.60,libavformat=56.40,libavdevice=56.4,libavfilter=5.40,libswscale=3.1,libswresample=1.2,libpostproc=53.3," } - }); - // Create a reduced version string and lookup key from dictionary - var reducedVersion = GetVersionString(output); + var reducedVersion = GetLibrariesVersionString(output); // Try to lookup the string and return Key, otherwise if not found returns null - return lut.FirstOrDefault(x => x.Value == reducedVersion).Key; + return _ffmpegVersionMap.TryGetValue(reducedVersion, out Version version) ? version : null; } } @@ -154,76 +183,38 @@ namespace MediaBrowser.MediaEncoding.Encoder /// /// /// - static private string GetVersionString(string output) + private static string GetLibrariesVersionString(string output) { - string pattern = @"((?lib\w+)\s+(?\d+)\.\s*(?\d+))"; - RegexOptions options = RegexOptions.Multiline; - - string rc = null; - - foreach (Match m in Regex.Matches(output, pattern, options)) + var rc = new StringBuilder(144); + foreach (Match m in Regex.Matches( + output, + @"((?lib\w+)\s+(?\d+)\.\s*(?\d+))", + RegexOptions.Multiline)) { - rc += string.Concat(m.Groups["name"], '=', m.Groups["major"], '.', m.Groups["minor"], ','); + rc.Append(m.Groups["name"]) + .Append('=') + .Append(m.Groups["major"]) + .Append('.') + .Append(m.Groups["minor"]) + .Append(','); } - return rc; + return rc.Length == 0 ? null : rc.ToString(); } - private static readonly string[] requiredDecoders = new[] - { - "mpeg2video", - "h264_qsv", - "hevc_qsv", - "mpeg2_qsv", - "vc1_qsv", - "h264_cuvid", - "hevc_cuvid", - "dts", - "ac3", - "aac", - "mp3", - "h264", - "hevc" - }; - - private static readonly string[] requiredEncoders = new[] - { - "libx264", - "libx265", - "mpeg4", - "msmpeg4", - "libvpx", - "libvpx-vp9", - "aac", - "libmp3lame", - "libopus", - "libvorbis", - "srt", - "h264_nvenc", - "hevc_nvenc", - "h264_qsv", - "hevc_qsv", - "h264_omx", - "hevc_omx", - "h264_vaapi", - "hevc_vaapi", - "h264_v4l2m2m", - "ac3" - }; - private enum Codec { Encoder, Decoder } - private IEnumerable GetCodecs(string encoderAppPath, Codec codec) + private IEnumerable GetCodecs(Codec codec) { string codecstr = codec == Codec.Encoder ? "encoders" : "decoders"; string output = null; try { - output = GetProcessOutput(encoderAppPath, "-" + codecstr); + output = GetProcessOutput(_encoderPath, "-" + codecstr); } catch (Exception ex) { @@ -250,45 +241,25 @@ namespace MediaBrowser.MediaEncoding.Encoder private string GetProcessOutput(string path, string arguments) { - IProcess process = _processFactory.Create(new ProcessOptions + using (var process = new Process() { - CreateNoWindow = true, - UseShellExecute = false, - FileName = path, - Arguments = arguments, - IsHidden = true, - ErrorDialog = false, - RedirectStandardOutput = true, - // ffmpeg uses stderr to log info, don't show this - RedirectStandardError = true - }); - - _logger.LogDebug("Running {Path} {Arguments}", path, arguments); - - using (process) + StartInfo = new ProcessStartInfo(path, arguments) + { + CreateNoWindow = true, + UseShellExecute = false, + WindowStyle = ProcessWindowStyle.Hidden, + ErrorDialog = false, + RedirectStandardOutput = true, + // ffmpeg uses stderr to log info, don't show this + RedirectStandardError = true + } + }) { + _logger.LogDebug("Running {Path} {Arguments}", path, arguments); + process.Start(); - try - { - return process.StandardOutput.ReadToEnd(); - } - catch - { - _logger.LogWarning("Killing process {Path} {Arguments}", path, arguments); - - // Hate having to do this - try - { - process.Kill(); - } - catch (Exception ex) - { - _logger.LogError(ex, "Error killing process"); - } - - throw; - } + return process.StandardOutput.ReadToEnd(); } } } diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 75bb960c3..04ff66991 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -114,13 +114,13 @@ namespace MediaBrowser.MediaEncoding.Encoder FFprobePath = Regex.Replace(FFmpegPath, @"[^\/\\]+?(\.[^\/\\\n.]+)?$", @"ffprobe$1"); // Interrogate to understand what coders are supported - var result = new EncoderValidator(_logger, _processFactory).GetAvailableCoders(FFmpegPath); + var validator = new EncoderValidator(_logger, FFmpegPath); - SetAvailableDecoders(result.decoders); - SetAvailableEncoders(result.encoders); + SetAvailableDecoders(validator.GetDecoders()); + SetAvailableEncoders(validator.GetEncoders()); } - _logger.LogInformation("FFmpeg: {0}: {1}", EncoderLocation.ToString(), FFmpegPath ?? string.Empty); + _logger.LogInformation("FFmpeg: {0}: {1}", EncoderLocation, FFmpegPath ?? string.Empty); } /// @@ -183,11 +183,11 @@ namespace MediaBrowser.MediaEncoding.Encoder { if (File.Exists(path)) { - rc = new EncoderValidator(_logger, _processFactory).ValidateVersion(path, true); + rc = new EncoderValidator(_logger, path).ValidateVersion(); if (!rc) { - _logger.LogWarning("FFmpeg: {0}: Failed version check: {1}", location.ToString(), path); + _logger.LogWarning("FFmpeg: {0}: Failed version check: {1}", location, path); } // ToDo - Enable the ffmpeg validator. At the moment any version can be used. @@ -198,7 +198,7 @@ namespace MediaBrowser.MediaEncoding.Encoder } else { - _logger.LogWarning("FFmpeg: {0}: File not found: {1}", location.ToString(), path); + _logger.LogWarning("FFmpeg: {0}: File not found: {1}", location, path); } } @@ -228,9 +228,9 @@ namespace MediaBrowser.MediaEncoding.Encoder /// /// /// - private string ExistsOnSystemPath(string filename) + private string ExistsOnSystemPath(string fileName) { - string inJellyfinPath = GetEncoderPathFromDirectory(System.AppContext.BaseDirectory, filename); + string inJellyfinPath = GetEncoderPathFromDirectory(System.AppContext.BaseDirectory, fileName); if (!string.IsNullOrEmpty(inJellyfinPath)) { return inJellyfinPath; @@ -239,13 +239,14 @@ namespace MediaBrowser.MediaEncoding.Encoder foreach (var path in values.Split(Path.PathSeparator)) { - var candidatePath = GetEncoderPathFromDirectory(path, filename); + var candidatePath = GetEncoderPathFromDirectory(path, fileName); if (!string.IsNullOrEmpty(candidatePath)) { return candidatePath; } } + return null; } diff --git a/MediaBrowser.MediaEncoding/Properties/AssemblyInfo.cs b/MediaBrowser.MediaEncoding/Properties/AssemblyInfo.cs index a9491374b..7b74cfc89 100644 --- a/MediaBrowser.MediaEncoding/Properties/AssemblyInfo.cs +++ b/MediaBrowser.MediaEncoding/Properties/AssemblyInfo.cs @@ -1,5 +1,6 @@ using System.Reflection; using System.Resources; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following @@ -14,6 +15,7 @@ using System.Runtime.InteropServices; [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: NeutralResourcesLanguage("en")] +[assembly: InternalsVisibleTo("Jellyfin.MediaEncoding.Tests")] // 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 diff --git a/MediaBrowser.sln b/MediaBrowser.sln index d23ca1cdb..dd4e9f8a6 100644 --- a/MediaBrowser.sln +++ b/MediaBrowser.sln @@ -57,6 +57,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{FBBB5129 EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Common.Tests", "tests\Jellyfin.Common.Tests\Jellyfin.Common.Tests.csproj", "{DF194677-DFD3-42AF-9F75-D44D5A416478}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.MediaEncoding.Tests", "tests\Jellyfin.MediaEncoding.Tests\Jellyfin.MediaEncoding.Tests.csproj", "{28464062-0939-4AA7-9F7B-24DDDA61A7C0}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -159,6 +161,10 @@ Global {DF194677-DFD3-42AF-9F75-D44D5A416478}.Debug|Any CPU.Build.0 = Debug|Any CPU {DF194677-DFD3-42AF-9F75-D44D5A416478}.Release|Any CPU.ActiveCfg = Release|Any CPU {DF194677-DFD3-42AF-9F75-D44D5A416478}.Release|Any CPU.Build.0 = Release|Any CPU + {28464062-0939-4AA7-9F7B-24DDDA61A7C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {28464062-0939-4AA7-9F7B-24DDDA61A7C0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {28464062-0939-4AA7-9F7B-24DDDA61A7C0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {28464062-0939-4AA7-9F7B-24DDDA61A7C0}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -186,5 +192,6 @@ Global EndGlobalSection GlobalSection(NestedProjects) = preSolution {DF194677-DFD3-42AF-9F75-D44D5A416478} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} + {28464062-0939-4AA7-9F7B-24DDDA61A7C0} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} EndGlobalSection EndGlobal diff --git a/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj b/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj index 449aaa1a5..242d99381 100644 --- a/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj +++ b/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj @@ -2,14 +2,14 @@ netcoreapp2.2 - false - + + diff --git a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs new file mode 100644 index 000000000..9fa1e4f7e --- /dev/null +++ b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using MediaBrowser.MediaEncoding.Encoder; +using Microsoft.Extensions.Logging.Abstractions; +using Xunit; + +namespace Jellyfin.MediaEncoding.Tests +{ + public class EncoderValidatorTests + { + private class GetFFmpegVersionTestData : IEnumerable + { + public IEnumerator GetEnumerator() + { + yield return new object[] { EncoderValidatorTestsData.FFmpegV42Output, new Version(4, 2) }; + yield return new object[] { EncoderValidatorTestsData.FFmpegV404Output, new Version(4, 0, 4) }; + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + + [Theory] + [ClassData(typeof(GetFFmpegVersionTestData))] + public void GetFFmpegVersionTest(string versionOutput, Version version) + { + Assert.Equal(version, EncoderValidator.GetFFmpegVersion(versionOutput)); + } + + [Theory] + [InlineData(EncoderValidatorTestsData.FFmpegV42Output, true)] + [InlineData(EncoderValidatorTestsData.FFmpegV404Output, true)] + public void ValidateVersionInternalTest(string versionOutput, bool valid) + { + var val = new EncoderValidator(new NullLogger()); + Assert.Equal(valid, val.ValidateVersionInternal(versionOutput)); + } + } +} diff --git a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs new file mode 100644 index 000000000..d2a2e4358 --- /dev/null +++ b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs @@ -0,0 +1,31 @@ +namespace Jellyfin.MediaEncoding.Tests +{ + internal static class EncoderValidatorTestsData + { + public const string FFmpegV42Output = @"ffmpeg version n4.2 Copyright (c) 2000-2019 the FFmpeg developers +built with gcc 9.1.0 (GCC) +configuration: --prefix=/usr --disable-debug --disable-static --disable-stripping --enable-fontconfig --enable-gmp --enable-gnutls --enable-gpl --enable-ladspa --enable-libaom --enable-libass --enable-libbluray --enable-libdav1d --enable-libdrm --enable-libfreetype --enable-libfribidi --enable-libgsm --enable-libiec61883 --enable-libjack --enable-libmodplug --enable-libmp3lame --enable-libopencore_amrnb --enable-libopencore_amrwb --enable-libopenjpeg --enable-libopus --enable-libpulse --enable-libsoxr --enable-libspeex --enable-libssh --enable-libtheora --enable-libv4l2 --enable-libvidstab --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxcb --enable-libxml2 --enable-libxvid --enable-nvdec --enable-nvenc --enable-omx --enable-shared --enable-version3 +libavutil 56. 31.100 / 56. 31.100 +libavcodec 58. 54.100 / 58. 54.100 +libavformat 58. 29.100 / 58. 29.100 +libavdevice 58. 8.100 / 58. 8.100 +libavfilter 7. 57.100 / 7. 57.100 +libswscale 5. 5.100 / 5. 5.100 +libswresample 3. 5.100 / 3. 5.100 +libpostproc 55. 5.100 / 55. 5.100 +"; + + public const string FFmpegV404Output = @"ffmpeg version 4.0.4 Copyright (c) 2000-2019 the FFmpeg developers +built with gcc 8 (Debian 8.3.0-6) +configuration: --toolchain=hardened --prefix=/usr --target-os=linux --enable-cross-compile --extra-cflags=--static --enable-gpl --enable-static --disable-doc --disable-ffplay --disable-shared --disable-libxcb --disable-sdl2 --disable-xlib --enable-libfontconfig --enable-fontconfig --enable-gmp --enable-gnutls --enable-libass --enable-libbluray --enable-libdrm --enable-libfreetype --enable-libfribidi --enable-libmp3lame --enable-libopus --enable-libtheora --enable-libvorbis --enable-libwebp --enable-libx264 --enable-libx265 --enable-libzvbi --enable-omx --enable-omx-rpi --enable-version3 --enable-vaapi --enable-vdpau --arch=amd64 --enable-nvenc --enable-nvdec +libavutil 56. 14.100 / 56. 14.100 +libavcodec 58. 18.100 / 58. 18.100 +libavformat 58. 12.100 / 58. 12.100 +libavdevice 58. 3.100 / 58. 3.100 +libavfilter 7. 16.100 / 7. 16.100 +libswscale 5. 1.100 / 5. 1.100 +libswresample 3. 1.100 / 3. 1.100 +libpostproc 55. 1.100 / 55. 1.100 +"; + } +} diff --git a/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj b/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj new file mode 100644 index 000000000..beb03d563 --- /dev/null +++ b/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj @@ -0,0 +1,19 @@ + + + + netcoreapp2.2 + false + + + + + + + + + + + + + + -- cgit v1.2.3 From 3e1aab6b29621db741ecfb469189322c907b673f Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sun, 29 Sep 2019 13:41:24 +0200 Subject: Fix ffmpeg version check for unknown versions --- .../Encoder/EncoderValidator.cs | 19 +++++++------ .../EncoderValidatorTests.cs | 4 +++ .../EncoderValidatorTestsData.cs | 32 ++++++++++++++++++---- 3 files changed, 41 insertions(+), 14 deletions(-) (limited to 'MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs') diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index da0b7693e..3620abfee 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -118,15 +118,18 @@ namespace MediaBrowser.MediaEncoding.Encoder _logger.LogInformation("Found ffmpeg version {0}", version != null ? version.ToString() : "unknown"); - if (version == null && MinVersion != null && MaxVersion != null) // Version is unknown + if (version == null) { - if (MinVersion == MaxVersion) + if (MinVersion != null && MaxVersion != null) // Version is unknown { - _logger.LogWarning("FFmpeg validation: We recommend ffmpeg version {0}", MinVersion); - } - else - { - _logger.LogWarning("FFmpeg validation: We recommend a minimum of {0} and maximum of {1}", MinVersion, MaxVersion); + if (MinVersion == MaxVersion) + { + _logger.LogWarning("FFmpeg validation: We recommend ffmpeg version {0}", MinVersion); + } + else + { + _logger.LogWarning("FFmpeg validation: We recommend a minimum of {0} and maximum of {1}", MinVersion, MaxVersion); + } } return false; @@ -161,7 +164,7 @@ namespace MediaBrowser.MediaEncoding.Encoder internal static Version GetFFmpegVersion(string output) { // For pre-built binaries the FFmpeg version should be mentioned at the very start of the output - var match = Regex.Match(output, @"ffmpeg version ((?:\d+\.?)+)"); + var match = Regex.Match(output, @"^ffmpeg version n?((?:\d+\.?)+)"); if (match.Success) { diff --git a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs index 5a759bcb2..a7848316e 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs @@ -13,9 +13,11 @@ namespace Jellyfin.MediaEncoding.Tests { public IEnumerator GetEnumerator() { + yield return new object[] { EncoderValidatorTestsData.FFmpegV421Output, new Version(4, 2, 1) }; yield return new object[] { EncoderValidatorTestsData.FFmpegV42Output, new Version(4, 2) }; yield return new object[] { EncoderValidatorTestsData.FFmpegV414Output, new Version(4, 1, 4) }; yield return new object[] { EncoderValidatorTestsData.FFmpegV404Output, new Version(4, 0, 4) }; + yield return new object[] { EncoderValidatorTestsData.FFmpegGitUnknownOutput, null }; } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); @@ -29,9 +31,11 @@ namespace Jellyfin.MediaEncoding.Tests } [Theory] + [InlineData(EncoderValidatorTestsData.FFmpegV421Output, true)] [InlineData(EncoderValidatorTestsData.FFmpegV42Output, true)] [InlineData(EncoderValidatorTestsData.FFmpegV414Output, true)] [InlineData(EncoderValidatorTestsData.FFmpegV404Output, true)] + [InlineData(EncoderValidatorTestsData.FFmpegGitUnknownOutput, false)] public void ValidateVersionInternalTest(string versionOutput, bool valid) { var val = new EncoderValidator(new NullLogger()); diff --git a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs index 1d444e2b3..12fde0770 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs @@ -2,6 +2,18 @@ namespace Jellyfin.MediaEncoding.Tests { internal static class EncoderValidatorTestsData { + public const string FFmpegV421Output = @"ffmpeg version 4.2.1 Copyright (c) 2000-2019 the FFmpeg developers +built with gcc 9.1.1 (GCC) 20190807 +configuration: --enable-gpl --enable-version3 --enable-sdl2 --enable-fontconfig --enable-gnutls --enable-iconv --enable-libass --enable-libdav1d --enable-libbluray --enable-libfreetype --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libopus --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libtheora --enable-libtwolame --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libzimg --enable-lzma --enable-zlib --enable-gmp --enable-libvidstab --enable-libvorbis --enable-libvo-amrwbenc --enable-libmysofa --enable-libspeex --enable-libxvid --enable-libaom --enable-libmfx --enable-amf --enable-ffnvcodec --enable-cuvid --enable-d3d11va --enable-nvenc --enable-nvdec --enable-dxva2 --enable-avisynth --enable-libopenmpt +libavutil 56. 31.100 / 56. 31.100 +libavcodec 58. 54.100 / 58. 54.100 +libavformat 58. 29.100 / 58. 29.100 +libavdevice 58. 8.100 / 58. 8.100 +libavfilter 7. 57.100 / 7. 57.100 +libswscale 5. 5.100 / 5. 5.100 +libswresample 3. 5.100 / 3. 5.100 +libpostproc 55. 5.100 / 55. 5.100"; + public const string FFmpegV42Output = @"ffmpeg version n4.2 Copyright (c) 2000-2019 the FFmpeg developers built with gcc 9.1.0 (GCC) configuration: --prefix=/usr --disable-debug --disable-static --disable-stripping --enable-fontconfig --enable-gmp --enable-gnutls --enable-gpl --enable-ladspa --enable-libaom --enable-libass --enable-libbluray --enable-libdav1d --enable-libdrm --enable-libfreetype --enable-libfribidi --enable-libgsm --enable-libiec61883 --enable-libjack --enable-libmodplug --enable-libmp3lame --enable-libopencore_amrnb --enable-libopencore_amrwb --enable-libopenjpeg --enable-libopus --enable-libpulse --enable-libsoxr --enable-libspeex --enable-libssh --enable-libtheora --enable-libv4l2 --enable-libvidstab --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxcb --enable-libxml2 --enable-libxvid --enable-nvdec --enable-nvenc --enable-omx --enable-shared --enable-version3 @@ -12,8 +24,7 @@ libavdevice 58. 8.100 / 58. 8.100 libavfilter 7. 57.100 / 7. 57.100 libswscale 5. 5.100 / 5. 5.100 libswresample 3. 5.100 / 3. 5.100 -libpostproc 55. 5.100 / 55. 5.100 -"; +libpostproc 55. 5.100 / 55. 5.100"; public const string FFmpegV414Output = @"ffmpeg version 4.1.4-1~deb10u1 Copyright (c) 2000-2019 the FFmpeg developers built with gcc 8 (Raspbian 8.3.0-6+rpi1) @@ -26,8 +37,7 @@ libavfilter 7. 40.101 / 7. 40.101 libavresample 4. 0. 0 / 4. 0. 0 libswscale 5. 3.100 / 5. 3.100 libswresample 3. 3.100 / 3. 3.100 -libpostproc 55. 3.100 / 55. 3.100 -"; +libpostproc 55. 3.100 / 55. 3.100"; public const string FFmpegV404Output = @"ffmpeg version 4.0.4 Copyright (c) 2000-2019 the FFmpeg developers built with gcc 8 (Debian 8.3.0-6) @@ -39,8 +49,18 @@ libavdevice 58. 3.100 / 58. 3.100 libavfilter 7. 16.100 / 7. 16.100 libswscale 5. 1.100 / 5. 1.100 libswresample 3. 1.100 / 3. 1.100 -libpostproc 55. 1.100 / 55. 1.100 -"; +libpostproc 55. 1.100 / 55. 1.100"; + public const string FFmpegGitUnknownOutput = @"ffmpeg version N-94303-g7cb4f8c962 Copyright (c) 2000-2019 the FFmpeg developers +built with gcc 9.1.1 (GCC) 20190716 +configuration: --enable-gpl --enable-version3 --enable-sdl2 --enable-fontconfig --enable-gnutls --enable-iconv --enable-libass --enable-libdav1d --enable-libbluray --enable-libfreetype --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libopus --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libtheora --enable-libtwolame --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libzimg --enable-lzma --enable-zlib --enable-gmp --enable-libvidstab --enable-libvorbis --enable-libvo-amrwbenc --enable-libmysofa --enable-libspeex --enable-libxvid --enable-libaom --enable-libmfx --enable-amf --enable-ffnvcodec --enable-cuvid --enable-d3d11va --enable-nvenc --enable-nvdec --enable-dxva2 --enable-avisynth --enable-libopenmpt +libavutil 56. 30.100 / 56. 30.100 +libavcodec 58. 53.101 / 58. 53.101 +libavformat 58. 28.102 / 58. 28.102 +libavdevice 58. 7.100 / 58. 7.100 +libavfilter 7. 56.101 / 7. 56.101 +libswscale 5. 4.101 / 5. 4.101 +libswresample 3. 4.100 / 3. 4.100 +libpostproc 55. 4.100 / 55. 4.100"; } } -- cgit v1.2.3 From 94ef239de0087ed4df490673b5b395d8b0111e8f Mon Sep 17 00:00:00 2001 From: ferferga Date: Mon, 25 Nov 2019 23:09:23 +0100 Subject: Add full Raspberry Pi hardware decoding support --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 16 ++++++++++++++-- MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs | 4 ++++ 2 files changed, 18 insertions(+), 2 deletions(-) (limited to 'MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs') diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 349e371a7..0664bdd98 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -2529,13 +2529,25 @@ namespace MediaBrowser.Controller.MediaEncoding case "h264": if (_mediaEncoder.SupportsDecoder("h264_mmal") && encodingOptions.HardwareDecodingCodecs.Contains("h264", StringComparer.OrdinalIgnoreCase)) { - return "-c:v h264_mmal"; + return "-c:v h264_mmal "; } break; case "mpeg2video": if (_mediaEncoder.SupportsDecoder("mpeg2_mmal") && encodingOptions.HardwareDecodingCodecs.Contains("mpeg2video", StringComparer.OrdinalIgnoreCase)) { - return "-c:v mpeg2_mmal"; + return "-c:v mpeg2_mmal "; + } + break; + case "mpeg4": + if (_mediaEncoder.SupportsDecoder("mpeg4_mmal") && encodingOptions.HardwareDecodingCodecs.Contains("mpeg4", StringComparer.OrdinalIgnoreCase)) + { + return "-c:v mpeg4_mmal "; + } + break; + case "vc1": + if (_mediaEncoder.SupportsDecoder("vc1_mmal") && encodingOptions.HardwareDecodingCodecs.Contains("vc1", StringComparer.OrdinalIgnoreCase)) + { + return "-c:v vc1_mmal "; } break; } diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index 3620abfee..1feca0ec9 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -18,7 +18,10 @@ namespace MediaBrowser.MediaEncoding.Encoder "h264_qsv", "hevc_qsv", "mpeg2_qsv", + "mpeg2_mmal", + "mpeg4_mmal", "vc1_qsv", + "vc1_mmal", "h264_cuvid", "hevc_cuvid", "dts", @@ -26,6 +29,7 @@ namespace MediaBrowser.MediaEncoding.Encoder "aac", "mp3", "h264", + "h264_mmal", "hevc" }; -- cgit v1.2.3 From e95239e28132b4d6486ee9272d20ec5ada46b66c Mon Sep 17 00:00:00 2001 From: Nyanmisaka <799610810@qq.com> Date: Sat, 11 Jan 2020 01:36:25 +0800 Subject: add support for AMD h264_amf & hevc_amf --- MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs') diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index 1feca0ec9..993cd3f74 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -55,7 +55,9 @@ namespace MediaBrowser.MediaEncoding.Encoder "h264_vaapi", "hevc_vaapi", "h264_v4l2m2m", - "ac3" + "ac3", + "h264_amf", + "hevc_amf" }; // Try and use the individual library versions to determine a FFmpeg version -- cgit v1.2.3 From 2c10891b66bf5963d2bfca34b704607a3bc0de85 Mon Sep 17 00:00:00 2001 From: Nyanmisaka <799610810@qq.com> Date: Wed, 15 Jan 2020 18:45:28 +0800 Subject: turn on indentation. --- MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs') diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index 993cd3f74..f5decdc0d 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -56,8 +56,8 @@ namespace MediaBrowser.MediaEncoding.Encoder "hevc_vaapi", "h264_v4l2m2m", "ac3", - "h264_amf", - "hevc_amf" + "h264_amf", + "hevc_amf" }; // Try and use the individual library versions to determine a FFmpeg version -- cgit v1.2.3 From db2366066029fa8e261d308d9db845070c0b9ca4 Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Mon, 30 Mar 2020 15:53:49 +0800 Subject: prefer to use libfdk_aac for better audio quality --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 5 +++++ MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs | 1 + 2 files changed, 6 insertions(+) (limited to 'MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs') diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 342c76414..2037a27fa 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -424,6 +424,11 @@ namespace MediaBrowser.Controller.MediaEncoding if (string.Equals(codec, "aac", StringComparison.OrdinalIgnoreCase)) { + // Prefer to use libfdk_aac for better audio quality while using the custom build FFmpeg + if (_mediaEncoder.SupportsEncoder("libfdk_aac")) + { + return "libfdk_aac"; + } return "aac -strict experimental"; } diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index f5decdc0d..6e036d24c 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -42,6 +42,7 @@ namespace MediaBrowser.MediaEncoding.Encoder "libvpx", "libvpx-vp9", "aac", + "libfdk_aac", "libmp3lame", "libopus", "libvorbis", -- cgit v1.2.3 From b4b93995f7f05971bd8532cadc9b80db07de9ab3 Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Thu, 9 Apr 2020 00:15:01 +0800 Subject: add more separate hw decoding toggles --- .../MediaEncoding/EncodingHelper.cs | 267 ++++++++++++++++++--- .../MediaEncoding/IMediaEncoder.cs | 15 +- .../Encoder/EncoderValidator.cs | 77 ++++-- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 13 + .../Subtitles/SubtitleEncoder.cs | 2 +- .../Configuration/EncodingOptions.cs | 2 + 6 files changed, 330 insertions(+), 46 deletions(-) (limited to 'MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs') diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 61a330675..807ba4e93 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -103,6 +103,11 @@ namespace MediaBrowser.Controller.MediaEncoding return false; } + if (!_mediaEncoder.SupportsHwaccel("vaapi")) + { + return false; + } + return true; } @@ -444,18 +449,30 @@ namespace MediaBrowser.Controller.MediaEncoding public string GetInputArgument(EncodingJobInfo state, EncodingOptions encodingOptions) { var arg = new StringBuilder(); + var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, encodingOptions); + var outputVideoCodec = GetVideoEncoder(state, encodingOptions); - if (state.IsVideoRequest - && string.Equals(encodingOptions.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase)) + if (state.IsVideoRequest && string.Equals(encodingOptions.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase)) { - arg.Append("-hwaccel vaapi -hwaccel_output_format vaapi") - .Append(" -vaapi_device ") - .Append(encodingOptions.VaapiDevice) - .Append(' '); + // While using VAAPI decoder + if ((videoDecoder ?? string.Empty).IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1) + { + arg.Append("-hwaccel_output_format vaapi") + .Append(" -vaapi_device ") + .Append(encodingOptions.VaapiDevice) + .Append(" "); + } + // While using SW decoder and VAAPI encoder + else if ((videoDecoder ?? string.Empty).IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) == -1 + && (outputVideoCodec ?? string.Empty).IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1) + { + arg.Append("-vaapi_device ") + .Append(encodingOptions.VaapiDevice) + .Append(" "); + } } - if (state.IsVideoRequest - && string.Equals(encodingOptions.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase)) + if (state.IsVideoRequest && string.Equals(encodingOptions.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase)) { var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, encodingOptions); var outputVideoCodec = GetVideoEncoder(state, encodingOptions); @@ -1655,7 +1672,7 @@ namespace MediaBrowser.Controller.MediaEncoding For software decoding and hardware encoding option, frames must be hwuploaded into hardware with fixed frame size. */ - if (!string.IsNullOrEmpty(videoDecoder) && videoDecoder.Contains("qsv", StringComparison.OrdinalIgnoreCase)) + if ((videoDecoder ?? string.Empty).IndexOf("qsv", StringComparison.OrdinalIgnoreCase) != -1) { retStr = " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay_qsv=x=(W-w)/2:y=(H-h)/2{3}\""; } @@ -2511,16 +2528,15 @@ namespace MediaBrowser.Controller.MediaEncoding /// protected string GetHardwareAcceleratedVideoDecoder(EncodingJobInfo state, EncodingOptions encodingOptions) { + var videoType = state.MediaSource.VideoType ?? VideoType.VideoFile; + var videoStream = state.VideoStream; + var IsColorDepth10 = (videoStream.Profile ?? string.Empty).IndexOf("10", StringComparison.OrdinalIgnoreCase) != -1; + if (string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)) { return null; } - return GetHardwareAcceleratedVideoDecoder(state.MediaSource.VideoType ?? VideoType.VideoFile, state.VideoStream, encodingOptions); - } - - public string GetHardwareAcceleratedVideoDecoder(VideoType videoType, MediaStream videoStream, EncodingOptions encodingOptions) - { // Only use alternative encoders for video files. // When using concat with folder rips, if the mfx session fails to initialize, ffmpeg will be stuck retrying and will not exit gracefully // Since transcoding of folder rips is expiremental anyway, it's not worth adding additional variables such as this. @@ -2533,6 +2549,14 @@ namespace MediaBrowser.Controller.MediaEncoding && !string.IsNullOrEmpty(videoStream.Codec) && !string.IsNullOrEmpty(encodingOptions.HardwareAccelerationType)) { + // Only hevc and vp9 formats have 10-bit hardware decoder support now. + if (IsColorDepth10 && !(string.Equals(videoStream.Codec, "hevc", StringComparison.OrdinalIgnoreCase) + || string.Equals(videoStream.Codec, "h265", StringComparison.OrdinalIgnoreCase) + || string.Equals(videoStream.Codec, "vp9", StringComparison.OrdinalIgnoreCase))) + { + return null; + } + if (string.Equals(encodingOptions.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase)) { switch (videoStream.Codec.ToLowerInvariant()) @@ -2554,8 +2578,17 @@ namespace MediaBrowser.Controller.MediaEncoding case "h265": if (_mediaEncoder.SupportsDecoder("hevc_qsv") && encodingOptions.HardwareDecodingCodecs.Contains("hevc", StringComparer.OrdinalIgnoreCase)) { - //return "-c:v hevc_qsv -load_plugin hevc_hw "; - return "-c:v hevc_qsv"; + if (IsColorDepth10) + { + if (encodingOptions.EnableDecodingColorDepth10) + { + return "-c:v hevc_qsv "; + } + + return null; + } + + return "-c:v hevc_qsv "; } break; case "mpeg2video": @@ -2570,6 +2603,28 @@ namespace MediaBrowser.Controller.MediaEncoding return "-c:v vc1_qsv"; } break; + case "vp8": + if (_mediaEncoder.SupportsDecoder("vp8_qsv") && encodingOptions.HardwareDecodingCodecs.Contains("vp8", StringComparer.OrdinalIgnoreCase)) + { + return "-c:v vp8_qsv "; + } + break; + case "vp9": + if (_mediaEncoder.SupportsDecoder("vp9_qsv") && encodingOptions.HardwareDecodingCodecs.Contains("vp9", StringComparer.OrdinalIgnoreCase)) + { + if (IsColorDepth10) + { + if (encodingOptions.EnableDecodingColorDepth10) + { + return "-c:v vp9_qsv "; + } + + return null; + } + + return "-c:v vp9_qsv "; + } + break; } } @@ -2594,7 +2649,17 @@ namespace MediaBrowser.Controller.MediaEncoding case "h265": if (_mediaEncoder.SupportsDecoder("hevc_cuvid") && encodingOptions.HardwareDecodingCodecs.Contains("hevc", StringComparer.OrdinalIgnoreCase)) { - return "-c:v hevc_cuvid"; + if (IsColorDepth10) + { + if (encodingOptions.EnableDecodingColorDepth10) + { + return "-c:v hevc_cuvid "; + } + + return null; + } + + return "-c:v hevc_cuvid "; } break; case "mpeg2video": @@ -2615,6 +2680,28 @@ namespace MediaBrowser.Controller.MediaEncoding return "-c:v mpeg4_cuvid"; } break; + case "vp8": + if (_mediaEncoder.SupportsDecoder("vp8_cuvid") && encodingOptions.HardwareDecodingCodecs.Contains("vp8", StringComparer.OrdinalIgnoreCase)) + { + return "-c:v vp8_cuvid "; + } + break; + case "vp9": + if (_mediaEncoder.SupportsDecoder("vp9_cuvid") && encodingOptions.HardwareDecodingCodecs.Contains("vp9", StringComparer.OrdinalIgnoreCase)) + { + if (IsColorDepth10) + { + if (encodingOptions.EnableDecodingColorDepth10) + { + return "-c:v vp9_cuvid "; + } + + return null; + } + + return "-c:v vp9_cuvid "; + } + break; } } @@ -2633,7 +2720,17 @@ namespace MediaBrowser.Controller.MediaEncoding case "h265": if (_mediaEncoder.SupportsDecoder("hevc_mediacodec") && encodingOptions.HardwareDecodingCodecs.Contains("hevc", StringComparer.OrdinalIgnoreCase)) { - return "-c:v hevc_mediacodec"; + if (IsColorDepth10) + { + if (encodingOptions.EnableDecodingColorDepth10) + { + return "-c:v hevc_mediacodec "; + } + + return null; + } + + return "-c:v hevc_mediacodec "; } break; case "mpeg2video": @@ -2657,7 +2754,17 @@ namespace MediaBrowser.Controller.MediaEncoding case "vp9": if (_mediaEncoder.SupportsDecoder("vp9_mediacodec") && encodingOptions.HardwareDecodingCodecs.Contains("vp9", StringComparer.OrdinalIgnoreCase)) { - return "-c:v vp9_mediacodec"; + if (IsColorDepth10) + { + if (encodingOptions.EnableDecodingColorDepth10) + { + return "-c:v vp9_mediacodec "; + } + + return null; + } + + return "-c:v vp9_mediacodec "; } break; } @@ -2697,27 +2804,133 @@ namespace MediaBrowser.Controller.MediaEncoding else if (string.Equals(encodingOptions.HardwareAccelerationType, "amf", StringComparison.OrdinalIgnoreCase)) { - if (Environment.OSVersion.Platform == PlatformID.Win32NT) + switch (videoStream.Codec.ToLowerInvariant()) { - if (Environment.OSVersion.Version.Major > 6 || (Environment.OSVersion.Version.Major == 6 && Environment.OSVersion.Version.Minor > 1)) - return "-hwaccel d3d11va"; - else - return "-hwaccel dxva2"; + case "avc": + case "h264": + return GetHwaccelType(state, encodingOptions, "h264"); + case "hevc": + case "h265": + if (IsColorDepth10) + { + if (encodingOptions.EnableDecodingColorDepth10) + { + return GetHwaccelType(state, encodingOptions, "hevc"); + } + + return null; + } + + return GetHwaccelType(state, encodingOptions, "hevc"); + case "mpeg2video": + return GetHwaccelType(state, encodingOptions, "mpeg2video"); + case "vc1": + return GetHwaccelType(state, encodingOptions, "vc1"); + case "mpeg4": + return GetHwaccelType(state, encodingOptions, "mpeg4"); + case "vp9": + if (IsColorDepth10) + { + if (encodingOptions.EnableDecodingColorDepth10) + { + return GetHwaccelType(state, encodingOptions, "vp9"); + } + + return null; + } + + return GetHwaccelType(state, encodingOptions, "vp9"); } - else + } + + else if (string.Equals(encodingOptions.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase)) + { + switch (videoStream.Codec.ToLowerInvariant()) { - return "-hwaccel vaapi"; + case "avc": + case "h264": + return GetHwaccelType(state, encodingOptions, "h264"); + case "hevc": + case "h265": + if (IsColorDepth10) + { + if (encodingOptions.EnableDecodingColorDepth10) + { + return GetHwaccelType(state, encodingOptions, "hevc"); + } + + return null; + } + + return GetHwaccelType(state, encodingOptions, "hevc"); + case "mpeg2video": + return GetHwaccelType(state, encodingOptions, "mpeg2video"); + case "vc1": + return GetHwaccelType(state, encodingOptions, "vc1"); + case "vp8": + return GetHwaccelType(state, encodingOptions, "vp8"); + case "vp9": + if (IsColorDepth10) + { + if (encodingOptions.EnableDecodingColorDepth10) + { + return GetHwaccelType(state, encodingOptions, "vp9"); + } + + return null; + } + + return GetHwaccelType(state, encodingOptions, "vp9"); } } } + var whichCodec = videoStream.Codec.ToLowerInvariant(); + switch (whichCodec) + { + case "avc": + whichCodec = "h264"; + break; + case "h265": + whichCodec = "hevc"; + break; + } + // Avoid a second attempt if no hardware acceleration is being used - encodingOptions.HardwareDecodingCodecs = Array.Empty(); + encodingOptions.HardwareDecodingCodecs = encodingOptions.HardwareDecodingCodecs.Where(val => val != whichCodec).ToArray(); // leave blank so ffmpeg will decide return null; } + /// + /// Gets a hwaccel type to use as a hardware decoder(dxva/vaapi) depending on the system + /// + public string GetHwaccelType(EncodingJobInfo state, EncodingOptions options, string videoCodec) + { + var IsWindows = Environment.OSVersion.Platform == PlatformID.Win32NT; + var IsNewWindows = Environment.OSVersion.Version.Major > 6 || (Environment.OSVersion.Version.Major == 6 && Environment.OSVersion.Version.Minor > 1); + var IsDxvaSupported = _mediaEncoder.SupportsHwaccel("dxva2") || _mediaEncoder.SupportsHwaccel("d3d11va"); + + if ((IsDxvaSupported || IsVaapiSupported(state)) && options.HardwareDecodingCodecs.Contains(videoCodec, StringComparer.OrdinalIgnoreCase)) + { + if (!IsWindows) + { + return "-hwaccel vaapi "; + } + else if (IsWindows && IsNewWindows) + { + return "-hwaccel d3d11va "; + } + else if (IsWindows && !IsNewWindows) + { + return "-hwaccel dxva2 "; + } + } + + return null; + } + public string GetSubtitleEmbedArguments(EncodingJobInfo state) { if (state.SubtitleStream == null || state.SubtitleDeliveryMethod != SubtitleDeliveryMethod.Embed) diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs index 37f0b11a7..6fa3bed48 100644 --- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs +++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs @@ -26,6 +26,13 @@ namespace MediaBrowser.Controller.MediaEncoding /// The encoder path. string EncoderPath { get; } + /// + /// Supportses the encoder. + /// + /// The encoder. + /// true if XXXX, false otherwise. + bool SupportsEncoder(string encoder); + /// /// Supportses the decoder. /// @@ -33,6 +40,13 @@ namespace MediaBrowser.Controller.MediaEncoding /// true if XXXX, false otherwise. bool SupportsDecoder(string decoder); + /// + /// Supportses the hwaccel. + /// + /// The hwaccel. + /// true if XXXX, false otherwise. + bool SupportsHwaccel(string hwaccel); + /// /// Extracts the audio image. /// @@ -98,7 +112,6 @@ namespace MediaBrowser.Controller.MediaEncoding void SetFFmpegPath(); void UpdateEncoderPath(string path, string pathType); - bool SupportsEncoder(string encoder); IEnumerable GetPrimaryPlaylistVobFiles(string path, IIsoMount isoMount, uint? titleNumber); } diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index 6e036d24c..e329f605d 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -14,23 +14,38 @@ namespace MediaBrowser.MediaEncoding.Encoder private static readonly string[] requiredDecoders = new[] { + "h264", + "hevc", "mpeg2video", + "mpeg4", + "msmpeg4", + "dts", + "ac3", + "aac", + "mp3", "h264_qsv", "hevc_qsv", "mpeg2_qsv", - "mpeg2_mmal", - "mpeg4_mmal", "vc1_qsv", - "vc1_mmal", + "vp8_qsv", + "vp9_qsv", "h264_cuvid", "hevc_cuvid", - "dts", - "ac3", - "aac", - "mp3", - "h264", + "mpeg2_cuvid", + "vc1_cuvid", + "mpeg4_cuvid", + "vp8_cuvid", + "vp9_cuvid", "h264_mmal", - "hevc" + "mpeg2_mmal", + "mpeg4_mmal", + "vc1_mmal", + "h264_mediacodec", + "hevc_mediacodec", + "mpeg2_mediacodec", + "mpeg4_mediacodec", + "vp8_mediacodec", + "vp9_mediacodec" }; private static readonly string[] requiredEncoders = new[] @@ -43,22 +58,22 @@ namespace MediaBrowser.MediaEncoding.Encoder "libvpx-vp9", "aac", "libfdk_aac", + "ac3", "libmp3lame", "libopus", "libvorbis", "srt", - "h264_nvenc", - "hevc_nvenc", + "h264_amf", + "hevc_amf", "h264_qsv", "hevc_qsv", - "h264_omx", - "hevc_omx", + "h264_nvenc", + "hevc_nvenc", "h264_vaapi", "hevc_vaapi", - "h264_v4l2m2m", - "ac3", - "h264_amf", - "hevc_amf" + "h264_omx", + "hevc_omx", + "h264_v4l2m2m" }; // Try and use the individual library versions to determine a FFmpeg version @@ -159,6 +174,8 @@ namespace MediaBrowser.MediaEncoding.Encoder public IEnumerable GetEncoders() => GetCodecs(Codec.Encoder); + public IEnumerable GetHwaccels() => GetHwaccelTypes(); + /// /// Using the output from "ffmpeg -version" work out the FFmpeg version. /// For pre-built binaries the first line should contain a string like "ffmpeg version x.y", which is easy @@ -218,6 +235,32 @@ namespace MediaBrowser.MediaEncoding.Encoder Decoder } + private IEnumerable GetHwaccelTypes() + { + string output = null; + try + { + output = GetProcessOutput(_encoderPath, "-hwaccels"); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error detecting available hwaccel types"); + } + + if (string.IsNullOrWhiteSpace(output)) + { + return Enumerable.Empty(); + } + + var found = output.Split(new char[] {'\r','\n'},StringSplitOptions.RemoveEmptyEntries).Distinct().ToList(); + + found.RemoveAt(0); + + _logger.LogInformation("Available hwaccel types: {Types}", found); + + return found; + } + private IEnumerable GetCodecs(Codec codec) { string codecstr = codec == Codec.Encoder ? "encoders" : "decoders"; diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 1377502dd..4cc971809 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -111,6 +111,7 @@ namespace MediaBrowser.MediaEncoding.Encoder SetAvailableDecoders(validator.GetDecoders()); SetAvailableEncoders(validator.GetEncoders()); + SetAvailableHwaccels(validator.GetHwaccels()); } _logger.LogInformation("FFmpeg: {EncoderLocation}: {FfmpegPath}", EncoderLocation, _ffmpegPath ?? string.Empty); @@ -257,6 +258,13 @@ namespace MediaBrowser.MediaEncoding.Encoder //_logger.Info("Supported decoders: {0}", string.Join(",", list.ToArray())); } + private List _hwaccels = new List(); + public void SetAvailableHwaccels(IEnumerable list) + { + _hwaccels = list.ToList(); + //_logger.Info("Supported hwaccels: {0}", string.Join(",", list.ToArray())); + } + public bool SupportsEncoder(string encoder) { return _encoders.Contains(encoder, StringComparer.OrdinalIgnoreCase); @@ -267,6 +275,11 @@ namespace MediaBrowser.MediaEncoding.Encoder return _decoders.Contains(decoder, StringComparer.OrdinalIgnoreCase); } + public bool SupportsHwaccel(string hwaccel) + { + return _hwaccels.Contains(hwaccel, StringComparer.OrdinalIgnoreCase); + } + public bool CanEncodeToAudioCodec(string codec) { if (string.Equals(codec, "opus", StringComparison.OrdinalIgnoreCase)) diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs index ba171295e..7d85f6ef7 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs @@ -737,7 +737,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles var charset = CharsetDetector.DetectFromStream(stream).Detected?.EncodingName; // UTF16 is automatically converted to UTF8 by FFmpeg, do not specify a character encoding - if ((path.EndsWith(".ass") || path.EndsWith(".ssa")) + if ((path.EndsWith(".ass") || path.EndsWith(".ssa") || path.EndsWith(".srt")) && (string.Equals(charset, "utf-16le", StringComparison.OrdinalIgnoreCase) || string.Equals(charset, "utf-16be", StringComparison.OrdinalIgnoreCase))) { diff --git a/MediaBrowser.Model/Configuration/EncodingOptions.cs b/MediaBrowser.Model/Configuration/EncodingOptions.cs index 648568fd7..dfc0deea9 100644 --- a/MediaBrowser.Model/Configuration/EncodingOptions.cs +++ b/MediaBrowser.Model/Configuration/EncodingOptions.cs @@ -25,6 +25,7 @@ namespace MediaBrowser.Model.Configuration public int H265Crf { get; set; } public string EncoderPreset { get; set; } public string DeinterlaceMethod { get; set; } + public bool EnableDecodingColorDepth10 { get; set; } public bool EnableHardwareEncoding { get; set; } public bool EnableSubtitleExtraction { get; set; } @@ -41,6 +42,7 @@ namespace MediaBrowser.Model.Configuration H264Crf = 23; H265Crf = 28; DeinterlaceMethod = "yadif"; + EnableDecodingColorDepth10 = true; EnableHardwareEncoding = true; EnableSubtitleExtraction = true; HardwareDecodingCodecs = new string[] { "h264", "vc1" }; -- cgit v1.2.3 From 1ff95289ef88d058e09b8189b579a35351b26f82 Mon Sep 17 00:00:00 2001 From: artiume Date: Mon, 25 May 2020 17:58:29 -0400 Subject: Update EncoderValidator.cs --- MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs') diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index e329f605d..be77a855d 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -46,6 +46,12 @@ namespace MediaBrowser.MediaEncoding.Encoder "mpeg4_mediacodec", "vp8_mediacodec", "vp9_mediacodec" + "h264_videotoolbox", + "hevc_videotoolbox", + "mpeg2_videotoolbox", + "mpeg4_videotoolbox", + "vp8_videotoolbox", + "vp9_videotoolbox" }; private static readonly string[] requiredEncoders = new[] @@ -74,6 +80,8 @@ namespace MediaBrowser.MediaEncoding.Encoder "h264_omx", "hevc_omx", "h264_v4l2m2m" + "h264_videotoolbox" + "hevc_videotoolbox" }; // Try and use the individual library versions to determine a FFmpeg version @@ -252,7 +260,7 @@ namespace MediaBrowser.MediaEncoding.Encoder return Enumerable.Empty(); } - var found = output.Split(new char[] {'\r','\n'},StringSplitOptions.RemoveEmptyEntries).Distinct().ToList(); + var found = output.Split(new char[] {'\r','\n'}, StringSplitOptions.RemoveEmptyEntries).Distinct().ToList(); found.RemoveAt(0); -- cgit v1.2.3 From 3c8237975924c326cfe460872654730f30e999db Mon Sep 17 00:00:00 2001 From: artiume Date: Mon, 25 May 2020 18:14:22 -0400 Subject: Update EncoderValidator.cs --- MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs') diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index be77a855d..89f33b048 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -51,7 +51,8 @@ namespace MediaBrowser.MediaEncoding.Encoder "mpeg2_videotoolbox", "mpeg4_videotoolbox", "vp8_videotoolbox", - "vp9_videotoolbox" + "vp9_videotoolbox", + "vc1_videotoolbox" }; private static readonly string[] requiredEncoders = new[] -- cgit v1.2.3 From 628734931ceb87f265320cf83b3fd29710a03c2b Mon Sep 17 00:00:00 2001 From: Vasily Date: Tue, 26 May 2020 18:41:38 +0300 Subject: Fix missing commas and merge defects --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 3 --- MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs | 6 +++--- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 2 +- 3 files changed, 4 insertions(+), 7 deletions(-) (limited to 'MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs') diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 50acc35f2..94fa5c6c0 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -475,9 +475,6 @@ namespace MediaBrowser.Controller.MediaEncoding if (state.IsVideoRequest && string.Equals(encodingOptions.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase)) { - var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, encodingOptions); - var outputVideoCodec = GetVideoEncoder(state, encodingOptions); - var hasTextSubs = state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode; if (!hasTextSubs) diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index 89f33b048..f155379a0 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -45,7 +45,7 @@ namespace MediaBrowser.MediaEncoding.Encoder "mpeg2_mediacodec", "mpeg4_mediacodec", "vp8_mediacodec", - "vp9_mediacodec" + "vp9_mediacodec", "h264_videotoolbox", "hevc_videotoolbox", "mpeg2_videotoolbox", @@ -80,8 +80,8 @@ namespace MediaBrowser.MediaEncoding.Encoder "hevc_vaapi", "h264_omx", "hevc_omx", - "h264_v4l2m2m" - "h264_videotoolbox" + "h264_v4l2m2m", + "h264_videotoolbox", "hevc_videotoolbox" }; diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index c51b8c110..4cc971809 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -258,7 +258,7 @@ namespace MediaBrowser.MediaEncoding.Encoder //_logger.Info("Supported decoders: {0}", string.Join(",", list.ToArray())); } - private List _hwaccels = Array.Empty(); + private List _hwaccels = new List(); public void SetAvailableHwaccels(IEnumerable list) { _hwaccels = list.ToList(); -- cgit v1.2.3 From aa17a53e83a61c23b7cd33d0294f3ccfe1000daf Mon Sep 17 00:00:00 2001 From: Vasily Date: Tue, 26 May 2020 19:05:44 +0300 Subject: Skip only line saying "Hardware acceleration methods:" instead of some random one --- MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs') diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index f155379a0..8910249af 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -261,10 +261,7 @@ namespace MediaBrowser.MediaEncoding.Encoder return Enumerable.Empty(); } - var found = output.Split(new char[] {'\r','\n'}, StringSplitOptions.RemoveEmptyEntries).Distinct().ToList(); - - found.RemoveAt(0); - + var found = output.Split(new char[] {'\r','\n'}, StringSplitOptions.RemoveEmptyEntries).Skip(1).Distinct().ToList(); _logger.LogInformation("Available hwaccel types: {Types}", found); return found; -- cgit v1.2.3 From e103d087d316bc19bf6227b21e8d9dfd091fd6a7 Mon Sep 17 00:00:00 2001 From: Max Git Date: Mon, 1 Jun 2020 07:10:15 +0200 Subject: Try harder at detecting FFmpeg version and enable the validation --- .../Encoder/EncoderValidator.cs | 120 ++++++++++++++++----- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 3 - .../EncoderValidatorTests.cs | 7 +- 3 files changed, 97 insertions(+), 33 deletions(-) (limited to 'MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs') diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index 6e036d24c..d13d6045e 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Globalization; using System.Linq; using System.Text; using System.Text.RegularExpressions; @@ -12,7 +13,7 @@ namespace MediaBrowser.MediaEncoding.Encoder { private const string DefaultEncoderPath = "ffmpeg"; - private static readonly string[] requiredDecoders = new[] + private static readonly string[] _requiredDecoders = new[] { "mpeg2video", "h264_qsv", @@ -33,7 +34,7 @@ namespace MediaBrowser.MediaEncoding.Encoder "hevc" }; - private static readonly string[] requiredEncoders = new[] + private static readonly string[] _requiredEncoders = new[] { "libx264", "libx265", @@ -61,7 +62,19 @@ namespace MediaBrowser.MediaEncoding.Encoder "hevc_amf" }; - // Try and use the individual library versions to determine a FFmpeg version + // These are the library versions that corresponds to our minimum ffmpeg version 4.x according to the version table below + private static readonly IReadOnlyDictionary _ffmpegMinimumLibraryVersions = new Dictionary + { + {"libavutil", 56.14}, + {"libavcodec", 58.18 }, + {"libavformat", 58.12 }, + {"libavdevice", 58.3 }, + {"libavfilter", 7.16 }, + {"libswscale", 5.1 }, + {"libswresample", 3.1}, + {"libpostproc", 55.1 } + }; + // This lookup table is to be maintained with the following command line: // $ ffmpeg -version | perl -ne ' print "$1=$2.$3," if /^(lib\w+)\s+(\d+)\.\s*(\d+)/' private static readonly IReadOnlyDictionary _ffmpegVersionMap = new Dictionary @@ -123,32 +136,36 @@ namespace MediaBrowser.MediaEncoding.Encoder // Work out what the version under test is var version = GetFFmpegVersion(versionOutput); - _logger.LogInformation("Found ffmpeg version {0}", version != null ? version.ToString() : "unknown"); + _logger.LogInformation("Found ffmpeg version {Version}", version != null ? version.ToString() : "unknown"); if (version == null) { - if (MinVersion != null && MaxVersion != null) // Version is unknown + if (MaxVersion != null) // Version is unknown { if (MinVersion == MaxVersion) { - _logger.LogWarning("FFmpeg validation: We recommend ffmpeg version {0}", MinVersion); + _logger.LogWarning("FFmpeg validation: We recommend version {MinVersion}", MinVersion); } else { - _logger.LogWarning("FFmpeg validation: We recommend a minimum of {0} and maximum of {1}", MinVersion, MaxVersion); + _logger.LogWarning("FFmpeg validation: We recommend a minimum of {MinVersion} and maximum of {MaxVersion}", MinVersion, MaxVersion); } } + else + { + _logger.LogWarning("FFmpeg validation: We recommend minimum version {MinVersion}", MinVersion); + } return false; } - else if (MinVersion != null && version < MinVersion) // Version is below what we recommend + else if (version < MinVersion) // Version is below what we recommend { - _logger.LogWarning("FFmpeg validation: The minimum recommended ffmpeg version is {0}", MinVersion); + _logger.LogWarning("FFmpeg validation: The minimum recommended version is {MinVersion}", MinVersion); return false; } else if (MaxVersion != null && version > MaxVersion) // Version is above what we recommend { - _logger.LogWarning("FFmpeg validation: The maximum recommended ffmpeg version is {0}", MaxVersion); + _logger.LogWarning("FFmpeg validation: The maximum recommended version is {MaxVersion}", MaxVersion); return false; } @@ -162,13 +179,12 @@ namespace MediaBrowser.MediaEncoding.Encoder /// /// Using the output from "ffmpeg -version" work out the FFmpeg version. /// For pre-built binaries the first line should contain a string like "ffmpeg version x.y", which is easy - /// to parse. If this is not available, then we try to match known library versions to FFmpeg versions. - /// If that fails then we use one of the main libraries to determine if it's new/older than the latest - /// we have stored. + /// to parse. If this is not available, then we try to match known library versions to FFmpeg versions. + /// If that fails then we test the libraries to determine if they're newer than our minimum versions. /// /// /// - internal static Version GetFFmpegVersion(string output) + internal Version GetFFmpegVersion(string output) { // For pre-built binaries the FFmpeg version should be mentioned at the very start of the output var match = Regex.Match(output, @"^ffmpeg version n?((?:\d+\.?)+)"); @@ -179,37 +195,87 @@ namespace MediaBrowser.MediaEncoding.Encoder } else { - // Create a reduced version string and lookup key from dictionary - var reducedVersion = GetLibrariesVersionString(output); + if (!TryGetFFmpegLibraryVersions(output, out string versionString, out IReadOnlyDictionary versionMap)) + { + _logger.LogError("No ffmpeg library versions found"); + + return null; + } + + // First try to lookup the full version string + if (_ffmpegVersionMap.TryGetValue(versionString, out Version version)) + { + return version; + } + + // Then try to test for minimum library versions + return TestMinimumFFmpegLibraryVersions(versionMap); + } + } + + private Version TestMinimumFFmpegLibraryVersions(IReadOnlyDictionary versionMap) + { + var allVersionsValidated = true; - // Try to lookup the string and return Key, otherwise if not found returns null - return _ffmpegVersionMap.TryGetValue(reducedVersion, out Version version) ? version : null; + foreach (var minimumVersion in _ffmpegMinimumLibraryVersions) + { + if (versionMap.TryGetValue(minimumVersion.Key, out var foundVersion)) + { + if (foundVersion >= minimumVersion.Value) + { + _logger.LogInformation("Found {Library} version {FoundVersion} ({MinimumVersion})", minimumVersion.Key, foundVersion, minimumVersion.Value); + } + else + { + _logger.LogWarning("Found {Library} version {FoundVersion} lower than recommended version {MinimumVersion}", minimumVersion.Key, foundVersion, minimumVersion.Value); + allVersionsValidated = false; + } + } + else + { + _logger.LogError("{Library} version not found", minimumVersion.Key); + allVersionsValidated = false; + } } + + return allVersionsValidated ? MinVersion : null; } /// /// Grabs the library names and major.minor version numbers from the 'ffmpeg -version' output - /// and condenses them on to one line. Output format is "name1=major.minor,name2=major.minor,etc." /// /// + /// + /// /// - private static string GetLibrariesVersionString(string output) + private static bool TryGetFFmpegLibraryVersions(string output, out string versionString, out IReadOnlyDictionary versionMap) { - var rc = new StringBuilder(144); - foreach (Match m in Regex.Matches( + var sb = new StringBuilder(144); + + var map = new Dictionary(); + + foreach (Match match in Regex.Matches( output, @"((?lib\w+)\s+(?\d+)\.\s*(?\d+))", RegexOptions.Multiline)) { - rc.Append(m.Groups["name"]) + sb.Append(match.Groups["name"]) .Append('=') - .Append(m.Groups["major"]) + .Append(match.Groups["major"]) .Append('.') - .Append(m.Groups["minor"]) + .Append(match.Groups["minor"]) .Append(','); + + var str = $"{match.Groups["major"]}.{match.Groups["minor"]}"; + var versionNumber = double.Parse(str, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture); + + map.Add(match.Groups["name"].Value, versionNumber); } - return rc.Length == 0 ? null : rc.ToString(); + versionString = sb.ToString(); + versionMap = map as IReadOnlyDictionary; + + return sb.Length > 0; } private enum Codec @@ -236,7 +302,7 @@ namespace MediaBrowser.MediaEncoding.Encoder return Enumerable.Empty(); } - var required = codec == Codec.Encoder ? requiredEncoders : requiredDecoders; + var required = codec == Codec.Encoder ? _requiredEncoders : _requiredDecoders; var found = Regex .Matches(output, @"^\s\S{6}\s(?[\w|-]+)\s+.+$", RegexOptions.Multiline) diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 1377502dd..2abd31a50 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -183,9 +183,6 @@ namespace MediaBrowser.MediaEncoding.Encoder _logger.LogWarning("FFmpeg: {Location}: Failed version check: {Path}", location, path); } - // ToDo - Enable the ffmpeg validator. At the moment any version can be used. - rc = true; - _ffmpegPath = path; EncoderLocation = location; } diff --git a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs index e0f1f236c..ae389efcd 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs @@ -17,7 +17,7 @@ namespace Jellyfin.MediaEncoding.Tests yield return new object?[] { EncoderValidatorTestsData.FFmpegV42Output, new Version(4, 2) }; yield return new object?[] { EncoderValidatorTestsData.FFmpegV414Output, new Version(4, 1, 4) }; yield return new object?[] { EncoderValidatorTestsData.FFmpegV404Output, new Version(4, 0, 4) }; - yield return new object?[] { EncoderValidatorTestsData.FFmpegGitUnknownOutput, null }; + yield return new object?[] { EncoderValidatorTestsData.FFmpegGitUnknownOutput, new Version(4, 0) }; } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); @@ -27,7 +27,8 @@ namespace Jellyfin.MediaEncoding.Tests [ClassData(typeof(GetFFmpegVersionTestData))] public void GetFFmpegVersionTest(string versionOutput, Version? version) { - Assert.Equal(version, EncoderValidator.GetFFmpegVersion(versionOutput)); + var val = new EncoderValidator(new NullLogger()); + Assert.Equal(version, val.GetFFmpegVersion(versionOutput)); } [Theory] @@ -35,7 +36,7 @@ namespace Jellyfin.MediaEncoding.Tests [InlineData(EncoderValidatorTestsData.FFmpegV42Output, true)] [InlineData(EncoderValidatorTestsData.FFmpegV414Output, true)] [InlineData(EncoderValidatorTestsData.FFmpegV404Output, true)] - [InlineData(EncoderValidatorTestsData.FFmpegGitUnknownOutput, false)] + [InlineData(EncoderValidatorTestsData.FFmpegGitUnknownOutput, true)] public void ValidateVersionInternalTest(string versionOutput, bool valid) { var val = new EncoderValidator(new NullLogger()); -- cgit v1.2.3 From 480fd0a66a7d8e47542e89b4751b9cf59b5faa9b Mon Sep 17 00:00:00 2001 From: rotvel Date: Mon, 1 Jun 2020 16:00:58 +0200 Subject: Update MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs Co-authored-by: Cody Robibero --- MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs') diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index d13d6045e..801479eed 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -65,14 +65,14 @@ namespace MediaBrowser.MediaEncoding.Encoder // These are the library versions that corresponds to our minimum ffmpeg version 4.x according to the version table below private static readonly IReadOnlyDictionary _ffmpegMinimumLibraryVersions = new Dictionary { - {"libavutil", 56.14}, - {"libavcodec", 58.18 }, - {"libavformat", 58.12 }, - {"libavdevice", 58.3 }, - {"libavfilter", 7.16 }, - {"libswscale", 5.1 }, - {"libswresample", 3.1}, - {"libpostproc", 55.1 } + { "libavutil", 56.14 }, + { "libavcodec", 58.18 }, + { "libavformat", 58.12 }, + { "libavdevice", 58.3 }, + { "libavfilter", 7.16 }, + { "libswscale", 5.1 }, + { "libswresample", 3.1 }, + { "libpostproc", 55.1 } }; // This lookup table is to be maintained with the following command line: -- cgit v1.2.3 From 0476acf5d557e7b95de2292d70d41b74deeef9ed Mon Sep 17 00:00:00 2001 From: artiume Date: Fri, 29 May 2020 10:20:47 -0400 Subject: Update EncoderValidator.cs --- MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs') diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index 8910249af..4250edfb7 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -46,13 +46,13 @@ namespace MediaBrowser.MediaEncoding.Encoder "mpeg4_mediacodec", "vp8_mediacodec", "vp9_mediacodec", - "h264_videotoolbox", - "hevc_videotoolbox", - "mpeg2_videotoolbox", - "mpeg4_videotoolbox", - "vp8_videotoolbox", - "vp9_videotoolbox", - "vc1_videotoolbox" + "h264_opencl", + "hevc_opencl", + "mpeg2_opencl", + "mpeg4_opencl", + "vp8_opencl", + "vp9_opencl", + "vc1_opencl" }; private static readonly string[] requiredEncoders = new[] -- cgit v1.2.3 From 11f3a0dc58386c815b34e0579cb37c3557e366f4 Mon Sep 17 00:00:00 2001 From: Max Git Date: Mon, 15 Jun 2020 15:10:59 +0200 Subject: Use Version instead of double. Use correct version number for libavdevice. --- .../Encoder/EncoderValidator.cs | 33 +++++++++++----------- 1 file changed, 17 insertions(+), 16 deletions(-) (limited to 'MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs') diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index 801479eed..28471b956 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -63,16 +63,16 @@ namespace MediaBrowser.MediaEncoding.Encoder }; // These are the library versions that corresponds to our minimum ffmpeg version 4.x according to the version table below - private static readonly IReadOnlyDictionary _ffmpegMinimumLibraryVersions = new Dictionary + private static readonly IReadOnlyDictionary _ffmpegMinimumLibraryVersions = new Dictionary { - { "libavutil", 56.14 }, - { "libavcodec", 58.18 }, - { "libavformat", 58.12 }, - { "libavdevice", 58.3 }, - { "libavfilter", 7.16 }, - { "libswscale", 5.1 }, - { "libswresample", 3.1 }, - { "libpostproc", 55.1 } + { "libavutil", new Version(56, 14) }, + { "libavcodec", new Version(58, 18) }, + { "libavformat", new Version(58, 12) }, + { "libavdevice", new Version(58, 3) }, + { "libavfilter", new Version(7, 16) }, + { "libswscale", new Version(5, 1) }, + { "libswresample", new Version(3, 1) }, + { "libpostproc", new Version(55, 1) } }; // This lookup table is to be maintained with the following command line: @@ -195,7 +195,7 @@ namespace MediaBrowser.MediaEncoding.Encoder } else { - if (!TryGetFFmpegLibraryVersions(output, out string versionString, out IReadOnlyDictionary versionMap)) + if (!TryGetFFmpegLibraryVersions(output, out string versionString, out IReadOnlyDictionary versionMap)) { _logger.LogError("No ffmpeg library versions found"); @@ -213,7 +213,7 @@ namespace MediaBrowser.MediaEncoding.Encoder } } - private Version TestMinimumFFmpegLibraryVersions(IReadOnlyDictionary versionMap) + private Version TestMinimumFFmpegLibraryVersions(IReadOnlyDictionary versionMap) { var allVersionsValidated = true; @@ -248,11 +248,11 @@ namespace MediaBrowser.MediaEncoding.Encoder /// /// /// - private static bool TryGetFFmpegLibraryVersions(string output, out string versionString, out IReadOnlyDictionary versionMap) + private static bool TryGetFFmpegLibraryVersions(string output, out string versionString, out IReadOnlyDictionary versionMap) { var sb = new StringBuilder(144); - var map = new Dictionary(); + var map = new Dictionary(); foreach (Match match in Regex.Matches( output, @@ -267,13 +267,14 @@ namespace MediaBrowser.MediaEncoding.Encoder .Append(','); var str = $"{match.Groups["major"]}.{match.Groups["minor"]}"; - var versionNumber = double.Parse(str, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture); - map.Add(match.Groups["name"].Value, versionNumber); + var version = Version.Parse(str); + + map.Add(match.Groups["name"].Value, version); } versionString = sb.ToString(); - versionMap = map as IReadOnlyDictionary; + versionMap = map as IReadOnlyDictionary; return sb.Length > 0; } -- cgit v1.2.3 From ef3200e178e41cede865e0876a6d56171b1f4cce Mon Sep 17 00:00:00 2001 From: Max Git Date: Mon, 15 Jun 2020 19:50:09 +0200 Subject: Remove redundant cast --- MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs') diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index 28471b956..a055e57ec 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -274,7 +274,7 @@ namespace MediaBrowser.MediaEncoding.Encoder } versionString = sb.ToString(); - versionMap = map as IReadOnlyDictionary; + versionMap = map; return sb.Length > 0; } -- cgit v1.2.3 From e6c197b96991a619d58e816ee56c23644c4a1de1 Mon Sep 17 00:00:00 2001 From: Max Git Date: Tue, 16 Jun 2020 01:09:41 +0200 Subject: Cleanup --- MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs | 1 - 1 file changed, 1 deletion(-) (limited to 'MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs') diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index a055e57ec..be4625505 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.Globalization; using System.Linq; using System.Text; using System.Text.RegularExpressions; -- cgit v1.2.3 From f5c53528618f2cd644f0d787c1e367f9886ff79a Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Sat, 4 Jul 2020 19:14:49 +0800 Subject: add FFmpeg 4.3 detection and tests --- MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs | 1 + tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs | 2 ++ .../EncoderValidatorTestsData.cs | 12 ++++++++++++ 3 files changed, 15 insertions(+) (limited to 'MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs') diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index 4250edfb7..0fd0239b4 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -90,6 +90,7 @@ namespace MediaBrowser.MediaEncoding.Encoder // $ ffmpeg -version | perl -ne ' print "$1=$2.$3," if /^(lib\w+)\s+(\d+)\.\s*(\d+)/' private static readonly IReadOnlyDictionary _ffmpegVersionMap = new Dictionary { + { "libavutil=56.51,libavcodec=58.91,libavformat=58.45,libavdevice=58.10,libavfilter=7.85,libswscale=5.7,libswresample=3.7,libpostproc=55.7,", new Version(4, 3) }, { "libavutil=56.31,libavcodec=58.54,libavformat=58.29,libavdevice=58.8,libavfilter=7.57,libswscale=5.5,libswresample=3.5,libpostproc=55.5,", new Version(4, 2) }, { "libavutil=56.22,libavcodec=58.35,libavformat=58.20,libavdevice=58.5,libavfilter=7.40,libswscale=5.3,libswresample=3.3,libpostproc=55.3,", new Version(4, 1) }, { "libavutil=56.14,libavcodec=58.18,libavformat=58.12,libavdevice=58.3,libavfilter=7.16,libswscale=5.1,libswresample=3.1,libpostproc=55.1,", new Version(4, 0) }, diff --git a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs index af29fec87..9eb601edf 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs @@ -17,6 +17,7 @@ namespace Jellyfin.MediaEncoding.Tests } [Theory] + [InlineData(EncoderValidatorTestsData.FFmpegV43Output, true)] [InlineData(EncoderValidatorTestsData.FFmpegV421Output, true)] [InlineData(EncoderValidatorTestsData.FFmpegV42Output, true)] [InlineData(EncoderValidatorTestsData.FFmpegV414Output, true)] @@ -32,6 +33,7 @@ namespace Jellyfin.MediaEncoding.Tests { public IEnumerator GetEnumerator() { + yield return new object?[] { EncoderValidatorTestsData.FFmpegV43Output, new Version(4, 3) }; yield return new object?[] { EncoderValidatorTestsData.FFmpegV421Output, new Version(4, 2, 1) }; yield return new object?[] { EncoderValidatorTestsData.FFmpegV42Output, new Version(4, 2) }; yield return new object?[] { EncoderValidatorTestsData.FFmpegV414Output, new Version(4, 1, 4) }; diff --git a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs index c46c9578b..f5ff3d723 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs @@ -2,6 +2,18 @@ namespace Jellyfin.MediaEncoding.Tests { internal static class EncoderValidatorTestsData { + public const string FFmpegV43Output = @"ffmpeg version 4.3 Copyright (c) 2000-2020 the FFmpeg developers +built with gcc 7 (Ubuntu 7.5.0-3ubuntu1~18.04) +configuration: --prefix=/usr/lib/jellyfin-ffmpeg --target-os=linux --disable-doc --disable-ffplay --disable-shared --disable-libxcb --disable-vdpau --disable-sdl2 --disable-xlib --enable-gpl --enable-version3 --enable-static --enable-libfontconfig --enable-fontconfig --enable-gmp --enable-gnutls --enable-libass --enable-libbluray --enable-libdrm --enable-libfreetype --enable-libfribidi --enable-libmp3lame --enable-libopus --enable-libtheora --enable-libvorbis --enable-libwebp --enable-libx264 --enable-libx265 --enable-libzvbi --arch=amd64 --enable-amf --enable-nvenc --enable-nvdec --enable-vaapi --enable-opencl +libavutil 56. 51.100 / 56. 51.100 +libavcodec 58. 91.100 / 58. 91.100 +libavformat 58. 45.100 / 58. 45.100 +libavdevice 58. 10.100 / 58. 10.100 +libavfilter 7. 85.100 / 7. 85.100 +libswscale 5. 7.100 / 5. 7.100 +libswresample 3. 7.100 / 3. 7.100 +libpostproc 55. 7.100 / 55. 7.100"; + public const string FFmpegV421Output = @"ffmpeg version 4.2.1 Copyright (c) 2000-2019 the FFmpeg developers built with gcc 9.1.1 (GCC) 20190807 configuration: --enable-gpl --enable-version3 --enable-sdl2 --enable-fontconfig --enable-gnutls --enable-iconv --enable-libass --enable-libdav1d --enable-libbluray --enable-libfreetype --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libopus --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libtheora --enable-libtwolame --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libzimg --enable-lzma --enable-zlib --enable-gmp --enable-libvidstab --enable-libvorbis --enable-libvo-amrwbenc --enable-libmysofa --enable-libspeex --enable-libxvid --enable-libaom --enable-libmfx --enable-amf --enable-ffnvcodec --enable-cuvid --enable-d3d11va --enable-nvenc --enable-nvdec --enable-dxva2 --enable-avisynth --enable-libopenmpt -- cgit v1.2.3 From e98351b9128670abf12976af735cfb40d40be36d Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Mon, 20 Jul 2020 14:14:15 +0200 Subject: Replace \d with [0-9] in ffmpeg detection and scan code --- Emby.Naming/Common/NamingOptions.cs | 60 +++++++++++----------- .../Encoder/EncoderValidator.cs | 4 +- 2 files changed, 32 insertions(+), 32 deletions(-) (limited to 'MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs') diff --git a/Emby.Naming/Common/NamingOptions.cs b/Emby.Naming/Common/NamingOptions.cs index 1b343790e..d1e17f416 100644 --- a/Emby.Naming/Common/NamingOptions.cs +++ b/Emby.Naming/Common/NamingOptions.cs @@ -136,8 +136,8 @@ namespace Emby.Naming.Common CleanDateTimes = new[] { - @"(.+[^_\,\.\(\)\[\]\-])[_\.\(\)\[\]\-](19\d{2}|20\d{2})([ _\,\.\(\)\[\]\-][^0-9]|).*(19\d{2}|20\d{2})*", - @"(.+[^_\,\.\(\)\[\]\-])[ _\.\(\)\[\]\-]+(19\d{2}|20\d{2})([ _\,\.\(\)\[\]\-][^0-9]|).*(19\d{2}|20\d{2})*" + @"(.+[^_\,\.\(\)\[\]\-])[_\.\(\)\[\]\-](19[0-9]{2}|20[0-9]{2})([ _\,\.\(\)\[\]\-][^0-9]|).*(19[0-9]{2}|20[0-9]{2})*", + @"(.+[^_\,\.\(\)\[\]\-])[ _\.\(\)\[\]\-]+(19[0-9]{2}|20[0-9]{2})([ _\,\.\(\)\[\]\-][^0-9]|).*(19[0-9]{2}|20[0-9]{2})*" }; CleanStrings = new[] @@ -277,7 +277,7 @@ namespace Emby.Naming.Common // This isn't a Kodi naming rule, but the expression below causes false positives, // so we make sure this one gets tested first. // "Foo Bar 889" - new EpisodeExpression(@".*[\\\/](?![Ee]pisode)(?[\w\s]+?)\s(?\d{1,3})(-(?\d{2,3}))*[^\\\/x]*$") + new EpisodeExpression(@".*[\\\/](?![Ee]pisode)(?[\w\s]+?)\s(?[0-9]{1,3})(-(?[0-9]{2,3}))*[^\\\/x]*$") { IsNamed = true }, @@ -300,32 +300,32 @@ namespace Emby.Naming.Common // *** End Kodi Standard Naming // [bar] Foo - 1 [baz] - new EpisodeExpression(@".*?(\[.*?\])+.*?(?[\w\s]+?)[-\s_]+(?\d+).*$") + new EpisodeExpression(@".*?(\[.*?\])+.*?(?[\w\s]+?)[-\s_]+(?[0-9]+).*$") { IsNamed = true }, - new EpisodeExpression(@".*(\\|\/)[sS]?(?\d+)[xX](?\d+)[^\\\/]*$") + new EpisodeExpression(@".*(\\|\/)[sS]?(?[0-9]+)[xX](?[0-9]+)[^\\\/]*$") { IsNamed = true }, - new EpisodeExpression(@".*(\\|\/)[sS](?\d+)[x,X]?[eE](?\d+)[^\\\/]*$") + new EpisodeExpression(@".*(\\|\/)[sS](?[0-9]+)[x,X]?[eE](?[0-9]+)[^\\\/]*$") { IsNamed = true }, - new EpisodeExpression(@".*(\\|\/)(?((?![sS]?\d{1,4}[xX]\d{1,3})[^\\\/])*)?([sS]?(?\d{1,4})[xX](?\d+))[^\\\/]*$") + new EpisodeExpression(@".*(\\|\/)(?((?![sS]?[0-9]{1,4}[xX][0-9]{1,3})[^\\\/])*)?([sS]?(?[0-9]{1,4})[xX](?[0-9]+))[^\\\/]*$") { IsNamed = true }, - new EpisodeExpression(@".*(\\|\/)(?[^\\\/]*)[sS](?\d{1,4})[xX\.]?[eE](?\d+)[^\\\/]*$") + new EpisodeExpression(@".*(\\|\/)(?[^\\\/]*)[sS](?[0-9]{1,4})[xX\.]?[eE](?[0-9]+)[^\\\/]*$") { IsNamed = true }, // "01.avi" - new EpisodeExpression(@".*[\\\/](?\d+)(-(?\d+))*\.\w+$") + new EpisodeExpression(@".*[\\\/](?[0-9]+)(-(?[0-9]+))*\.\w+$") { IsOptimistic = true, IsNamed = true @@ -335,34 +335,34 @@ namespace Emby.Naming.Common new EpisodeExpression(@"([0-9]+)-([0-9]+)"), // "01 - blah.avi", "01-blah.avi" - new EpisodeExpression(@".*(\\|\/)(?\d{1,3})(-(?\d{2,3}))*\s?-\s?[^\\\/]*$") + new EpisodeExpression(@".*(\\|\/)(?[0-9]{1,3})(-(?[0-9]{2,3}))*\s?-\s?[^\\\/]*$") { IsOptimistic = true, IsNamed = true }, // "01.blah.avi" - new EpisodeExpression(@".*(\\|\/)(?\d{1,3})(-(?\d{2,3}))*\.[^\\\/]+$") + new EpisodeExpression(@".*(\\|\/)(?[0-9]{1,3})(-(?[0-9]{2,3}))*\.[^\\\/]+$") { IsOptimistic = true, IsNamed = true }, // "blah - 01.avi", "blah 2 - 01.avi", "blah - 01 blah.avi", "blah 2 - 01 blah", "blah - 01 - blah.avi", "blah 2 - 01 - blah" - new EpisodeExpression(@".*[\\\/][^\\\/]* - (?\d{1,3})(-(?\d{2,3}))*[^\\\/]*$") + new EpisodeExpression(@".*[\\\/][^\\\/]* - (?[0-9]{1,3})(-(?[0-9]{2,3}))*[^\\\/]*$") { IsOptimistic = true, IsNamed = true }, // "01 episode title.avi" - new EpisodeExpression(@"[Ss]eason[\._ ](?[0-9]+)[\\\/](?\d{1,3})([^\\\/]*)$") + new EpisodeExpression(@"[Ss]eason[\._ ](?[0-9]+)[\\\/](?[0-9]{1,3})([^\\\/]*)$") { IsOptimistic = true, IsNamed = true }, // "Episode 16", "Episode 16 - Title" - new EpisodeExpression(@".*[\\\/][^\\\/]* (?\d{1,3})(-(?\d{2,3}))*[^\\\/]*$") + new EpisodeExpression(@".*[\\\/][^\\\/]* (?[0-9]{1,3})(-(?[0-9]{2,3}))*[^\\\/]*$") { IsOptimistic = true, IsNamed = true @@ -625,17 +625,17 @@ namespace Emby.Naming.Common AudioBookPartsExpressions = new[] { // Detect specified chapters, like CH 01 - @"ch(?:apter)?[\s_-]?(?\d+)", + @"ch(?:apter)?[\s_-]?(?[0-9]+)", // Detect specified parts, like Part 02 - @"p(?:ar)?t[\s_-]?(?\d+)", + @"p(?:ar)?t[\s_-]?(?[0-9]+)", // Chapter is often beginning of filename - @"^(?\d+)", + "^(?[0-9]+)", // Part if often ending of filename - @"(?\d+)$", + "(?[0-9]+)$", // Sometimes named as 0001_005 (chapter_part) - @"(?\d+)_(?\d+)", + "(?[0-9]+)_(?[0-9]+)", // Some audiobooks are ripped from cd's, and will be named by disk number. - @"dis(?:c|k)[\s_-]?(?\d+)" + @"dis(?:c|k)[\s_-]?(?[0-9]+)" }; var extensions = VideoFileExtensions.ToList(); @@ -675,16 +675,16 @@ namespace Emby.Naming.Common MultipleEpisodeExpressions = new string[] { - @".*(\\|\/)[sS]?(?\d{1,4})[xX](?\d{1,3})((-| - )\d{1,4}[eExX](?\d{1,3}))+[^\\\/]*$", - @".*(\\|\/)[sS]?(?\d{1,4})[xX](?\d{1,3})((-| - )\d{1,4}[xX][eE](?\d{1,3}))+[^\\\/]*$", - @".*(\\|\/)[sS]?(?\d{1,4})[xX](?\d{1,3})((-| - )?[xXeE](?\d{1,3}))+[^\\\/]*$", - @".*(\\|\/)[sS]?(?\d{1,4})[xX](?\d{1,3})(-[xE]?[eE]?(?\d{1,3}))+[^\\\/]*$", - @".*(\\|\/)(?((?![sS]?\d{1,4}[xX]\d{1,3})[^\\\/])*)?([sS]?(?\d{1,4})[xX](?\d{1,3}))((-| - )\d{1,4}[xXeE](?\d{1,3}))+[^\\\/]*$", - @".*(\\|\/)(?((?![sS]?\d{1,4}[xX]\d{1,3})[^\\\/])*)?([sS]?(?\d{1,4})[xX](?\d{1,3}))((-| - )\d{1,4}[xX][eE](?\d{1,3}))+[^\\\/]*$", - @".*(\\|\/)(?((?![sS]?\d{1,4}[xX]\d{1,3})[^\\\/])*)?([sS]?(?\d{1,4})[xX](?\d{1,3}))((-| - )?[xXeE](?\d{1,3}))+[^\\\/]*$", - @".*(\\|\/)(?((?![sS]?\d{1,4}[xX]\d{1,3})[^\\\/])*)?([sS]?(?\d{1,4})[xX](?\d{1,3}))(-[xX]?[eE]?(?\d{1,3}))+[^\\\/]*$", - @".*(\\|\/)(?[^\\\/]*)[sS](?\d{1,4})[xX\.]?[eE](?\d{1,3})((-| - )?[xXeE](?\d{1,3}))+[^\\\/]*$", - @".*(\\|\/)(?[^\\\/]*)[sS](?\d{1,4})[xX\.]?[eE](?\d{1,3})(-[xX]?[eE]?(?\d{1,3}))+[^\\\/]*$" + @".*(\\|\/)[sS]?(?[0-9]{1,4})[xX](?[0-9]{1,3})((-| - )[0-9]{1,4}[eExX](?[0-9]{1,3}))+[^\\\/]*$", + @".*(\\|\/)[sS]?(?[0-9]{1,4})[xX](?[0-9]{1,3})((-| - )[0-9]{1,4}[xX][eE](?[0-9]{1,3}))+[^\\\/]*$", + @".*(\\|\/)[sS]?(?[0-9]{1,4})[xX](?[0-9]{1,3})((-| - )?[xXeE](?[0-9]{1,3}))+[^\\\/]*$", + @".*(\\|\/)[sS]?(?[0-9]{1,4})[xX](?[0-9]{1,3})(-[xE]?[eE]?(?[0-9]{1,3}))+[^\\\/]*$", + @".*(\\|\/)(?((?![sS]?[0-9]{1,4}[xX][0-9]{1,3})[^\\\/])*)?([sS]?(?[0-9]{1,4})[xX](?[0-9]{1,3}))((-| - )[0-9]{1,4}[xXeE](?[0-9]{1,3}))+[^\\\/]*$", + @".*(\\|\/)(?((?![sS]?[0-9]{1,4}[xX][0-9]{1,3})[^\\\/])*)?([sS]?(?[0-9]{1,4})[xX](?[0-9]{1,3}))((-| - )[0-9]{1,4}[xX][eE](?[0-9]{1,3}))+[^\\\/]*$", + @".*(\\|\/)(?((?![sS]?[0-9]{1,4}[xX][0-9]{1,3})[^\\\/])*)?([sS]?(?[0-9]{1,4})[xX](?[0-9]{1,3}))((-| - )?[xXeE](?[0-9]{1,3}))+[^\\\/]*$", + @".*(\\|\/)(?((?![sS]?[0-9]{1,4}[xX][0-9]{1,3})[^\\\/])*)?([sS]?(?[0-9]{1,4})[xX](?[0-9]{1,3}))(-[xX]?[eE]?(?[0-9]{1,3}))+[^\\\/]*$", + @".*(\\|\/)(?[^\\\/]*)[sS](?[0-9]{1,4})[xX\.]?[eE](?[0-9]{1,3})((-| - )?[xXeE](?[0-9]{1,3}))+[^\\\/]*$", + @".*(\\|\/)(?[^\\\/]*)[sS](?[0-9]{1,4})[xX\.]?[eE](?[0-9]{1,3})(-[xX]?[eE]?(?[0-9]{1,3}))+[^\\\/]*$" }.Select(i => new EpisodeExpression(i) { IsNamed = true diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index 0fd0239b4..5c43fdcfa 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -198,7 +198,7 @@ namespace MediaBrowser.MediaEncoding.Encoder internal static Version GetFFmpegVersion(string output) { // For pre-built binaries the FFmpeg version should be mentioned at the very start of the output - var match = Regex.Match(output, @"^ffmpeg version n?((?:\d+\.?)+)"); + var match = Regex.Match(output, @"^ffmpeg version n?((?:[0-9]+\.?)+)"); if (match.Success) { @@ -225,7 +225,7 @@ namespace MediaBrowser.MediaEncoding.Encoder var rc = new StringBuilder(144); foreach (Match m in Regex.Matches( output, - @"((?lib\w+)\s+(?\d+)\.\s*(?\d+))", + @"((?lib\w+)\s+(?[0-9]+)\.\s*(?[0-9]+))", RegexOptions.Multiline)) { rc.Append(m.Groups["name"]) -- cgit v1.2.3 From 18efa25a6fdcfab2326cb35bb5781138a83e9d56 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Tue, 4 Aug 2020 16:20:52 +0200 Subject: Enable TreatWarningsAsErrors for MediaBrowser.MediaEncoding --- MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs | 2 ++ MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs | 4 ++++ .../Configuration/EncodingConfigurationFactory.cs | 2 ++ MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs | 2 ++ MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs | 2 ++ MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 2 ++ MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj | 1 + MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs | 3 +++ MediaBrowser.MediaEncoding/Probing/MediaChapter.cs | 2 ++ MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs | 4 ++++ MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs | 2 ++ MediaBrowser.MediaEncoding/Subtitles/AssParser.cs | 3 +++ MediaBrowser.MediaEncoding/Subtitles/ISubtitleParser.cs | 2 ++ MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs | 6 +++--- 14 files changed, 34 insertions(+), 3 deletions(-) (limited to 'MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs') diff --git a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs index f02999370..a8ebe6bc5 100644 --- a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs +++ b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Concurrent; using System.Diagnostics; diff --git a/MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs b/MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs index ccfae2fa5..9108d9649 100644 --- a/MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs +++ b/MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs @@ -15,6 +15,10 @@ namespace MediaBrowser.MediaEncoding.BdInfo { private readonly IFileSystem _fileSystem; + /// + /// Initializes a new instance of the class. + /// + /// The filesystem. public BdInfoExaminer(IFileSystem fileSystem) { _fileSystem = fileSystem; diff --git a/MediaBrowser.MediaEncoding/Configuration/EncodingConfigurationFactory.cs b/MediaBrowser.MediaEncoding/Configuration/EncodingConfigurationFactory.cs index 75534b5bd..fea7ee6fe 100644 --- a/MediaBrowser.MediaEncoding/Configuration/EncodingConfigurationFactory.cs +++ b/MediaBrowser.MediaEncoding/Configuration/EncodingConfigurationFactory.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Globalization; diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index 5c43fdcfa..1ac56f845 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Diagnostics; diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs index d4aede572..7c2d9f1fd 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System.Collections.Generic; using System.Linq; using MediaBrowser.Model.MediaInfo; diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 62fdbc618..0f0ae877f 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Globalization; diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj index aeb4dbe73..dab5f866c 100644 --- a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj +++ b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj @@ -9,6 +9,7 @@ netstandard2.1 false true + true diff --git a/MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs b/MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs index 3aa296f7f..b2d4db894 100644 --- a/MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs +++ b/MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs @@ -3,6 +3,9 @@ using System.Collections.Generic; namespace MediaBrowser.MediaEncoding.Probing { + /// + /// Class containing helper methods for working with FFprobe output. + /// public static class FFProbeHelpers { /// diff --git a/MediaBrowser.MediaEncoding/Probing/MediaChapter.cs b/MediaBrowser.MediaEncoding/Probing/MediaChapter.cs index 6a45ccf49..de062d06b 100644 --- a/MediaBrowser.MediaEncoding/Probing/MediaChapter.cs +++ b/MediaBrowser.MediaEncoding/Probing/MediaChapter.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System.Collections.Generic; using System.Text.Json.Serialization; diff --git a/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs b/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs index a2ea0766a..93ef6f93e 100644 --- a/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs +++ b/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs @@ -269,6 +269,10 @@ namespace MediaBrowser.MediaEncoding.Probing [JsonPropertyName("loro_surmixlev")] public string LoroSurmixlev { get; set; } + /// + /// Gets or sets the field_order. + /// + /// The loro_surmixlev. [JsonPropertyName("field_order")] public string FieldOrder { get; set; } diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index 0b447e3e6..8aaaf4a09 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Globalization; diff --git a/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs b/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs index 43a45291c..e6e21756a 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Globalization; @@ -13,6 +15,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles { private readonly CultureInfo _usCulture = new CultureInfo("en-US"); + /// public SubtitleTrackInfo Parse(Stream stream, CancellationToken cancellationToken) { var trackInfo = new SubtitleTrackInfo(); diff --git a/MediaBrowser.MediaEncoding/Subtitles/ISubtitleParser.cs b/MediaBrowser.MediaEncoding/Subtitles/ISubtitleParser.cs index f0d107196..c0023ebf2 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/ISubtitleParser.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/ISubtitleParser.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System.IO; using System.Threading; using MediaBrowser.Model.MediaInfo; diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs index 2afa89cda..7c0697279 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs @@ -380,6 +380,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles /// Converts the text subtitle to SRT. /// /// The input path. + /// The language. /// The input protocol. /// The output path. /// The cancellation token. @@ -407,14 +408,13 @@ namespace MediaBrowser.MediaEncoding.Subtitles /// Converts the text subtitle to SRT internal. /// /// The input path. + /// The language. /// The input protocol. /// The output path. /// The cancellation token. /// Task. /// - /// inputPath - /// or - /// outputPath + /// The inputPath or outputPath is null /// private async Task ConvertTextSubtitleToSrtInternal(string inputPath, string language, MediaProtocol inputProtocol, string outputPath, CancellationToken cancellationToken) { -- cgit v1.2.3 From 53f99d5d4bdf3f2f5b65d53f9d84f1ea220e58e7 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Tue, 4 Aug 2020 17:08:09 +0200 Subject: Add some analyzers to MediaBrowser.MediaEncoding --- .../BdInfo/BdInfoDirectoryInfo.cs | 19 ++++-- .../BdInfo/BdInfoFileInfo.cs | 19 +++--- .../Configuration/EncodingConfigurationFactory.cs | 32 ---------- .../Configuration/EncodingConfigurationStore.cs | 38 +++++++++++ .../Encoder/EncoderValidator.cs | 30 ++++----- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 74 +++++++++++++--------- .../MediaBrowser.MediaEncoding.csproj | 12 ++++ .../Probing/ProbeResultNormalizer.cs | 65 +++++++++---------- MediaBrowser.MediaEncoding/Subtitles/AssParser.cs | 18 +++--- MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs | 14 ++-- .../Subtitles/SubtitleEncoder.cs | 58 ++++++++--------- 11 files changed, 210 insertions(+), 169 deletions(-) create mode 100644 MediaBrowser.MediaEncoding/Configuration/EncodingConfigurationStore.cs (limited to 'MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs') diff --git a/MediaBrowser.MediaEncoding/BdInfo/BdInfoDirectoryInfo.cs b/MediaBrowser.MediaEncoding/BdInfo/BdInfoDirectoryInfo.cs index e040286ab..4a54b677d 100644 --- a/MediaBrowser.MediaEncoding/BdInfo/BdInfoDirectoryInfo.cs +++ b/MediaBrowser.MediaEncoding/BdInfo/BdInfoDirectoryInfo.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Linq; using BDInfo.IO; @@ -5,7 +7,7 @@ using MediaBrowser.Model.IO; namespace MediaBrowser.MediaEncoding.BdInfo { - class BdInfoDirectoryInfo : IDirectoryInfo + public class BdInfoDirectoryInfo : IDirectoryInfo { private readonly IFileSystem _fileSystem = null; @@ -43,25 +45,32 @@ namespace MediaBrowser.MediaEncoding.BdInfo public IDirectoryInfo[] GetDirectories() { - return Array.ConvertAll(_fileSystem.GetDirectories(_impl.FullName).ToArray(), + return Array.ConvertAll( + _fileSystem.GetDirectories(_impl.FullName).ToArray(), x => new BdInfoDirectoryInfo(_fileSystem, x)); } public IFileInfo[] GetFiles() { - return Array.ConvertAll(_fileSystem.GetFiles(_impl.FullName).ToArray(), + return Array.ConvertAll( + _fileSystem.GetFiles(_impl.FullName).ToArray(), x => new BdInfoFileInfo(x)); } public IFileInfo[] GetFiles(string searchPattern) { - return Array.ConvertAll(_fileSystem.GetFiles(_impl.FullName, new[] { searchPattern }, false, false).ToArray(), + return Array.ConvertAll( + _fileSystem.GetFiles(_impl.FullName, new[] { searchPattern }, false, false).ToArray(), x => new BdInfoFileInfo(x)); } public IFileInfo[] GetFiles(string searchPattern, System.IO.SearchOption searchOption) { - return Array.ConvertAll(_fileSystem.GetFiles(_impl.FullName, new[] { searchPattern }, false, + return Array.ConvertAll( + _fileSystem.GetFiles( + _impl.FullName, + new[] { searchPattern }, + false, searchOption.HasFlag(System.IO.SearchOption.AllDirectories)).ToArray(), x => new BdInfoFileInfo(x)); } diff --git a/MediaBrowser.MediaEncoding/BdInfo/BdInfoFileInfo.cs b/MediaBrowser.MediaEncoding/BdInfo/BdInfoFileInfo.cs index a6ff4f767..0a8af8e9c 100644 --- a/MediaBrowser.MediaEncoding/BdInfo/BdInfoFileInfo.cs +++ b/MediaBrowser.MediaEncoding/BdInfo/BdInfoFileInfo.cs @@ -1,11 +1,18 @@ +#pragma warning disable CS1591 + using System.IO; using MediaBrowser.Model.IO; namespace MediaBrowser.MediaEncoding.BdInfo { - class BdInfoFileInfo : BDInfo.IO.IFileInfo + public class BdInfoFileInfo : BDInfo.IO.IFileInfo { - FileSystemMetadata _impl = null; + private FileSystemMetadata _impl = null; + + public BdInfoFileInfo(FileSystemMetadata impl) + { + _impl = impl; + } public string Name => _impl.Name; @@ -17,14 +24,10 @@ namespace MediaBrowser.MediaEncoding.BdInfo public bool IsDir => _impl.IsDirectory; - public BdInfoFileInfo(FileSystemMetadata impl) - { - _impl = impl; - } - public System.IO.Stream OpenRead() { - return new FileStream(FullName, + return new FileStream( + FullName, FileMode.Open, FileAccess.Read, FileShare.Read); diff --git a/MediaBrowser.MediaEncoding/Configuration/EncodingConfigurationFactory.cs b/MediaBrowser.MediaEncoding/Configuration/EncodingConfigurationFactory.cs index fea7ee6fe..f81a337db 100644 --- a/MediaBrowser.MediaEncoding/Configuration/EncodingConfigurationFactory.cs +++ b/MediaBrowser.MediaEncoding/Configuration/EncodingConfigurationFactory.cs @@ -1,11 +1,7 @@ #pragma warning disable CS1591 -using System; using System.Collections.Generic; -using System.Globalization; -using System.IO; using MediaBrowser.Common.Configuration; -using MediaBrowser.Model.Configuration; namespace MediaBrowser.MediaEncoding.Configuration { @@ -19,32 +15,4 @@ namespace MediaBrowser.MediaEncoding.Configuration }; } } - - public class EncodingConfigurationStore : ConfigurationStore, IValidatingConfiguration - { - public EncodingConfigurationStore() - { - ConfigurationType = typeof(EncodingOptions); - Key = "encoding"; - } - - public void Validate(object oldConfig, object newConfig) - { - var newPath = ((EncodingOptions)newConfig).TranscodingTempPath; - - if (!string.IsNullOrWhiteSpace(newPath) - && !string.Equals(((EncodingOptions)oldConfig).TranscodingTempPath, newPath, StringComparison.Ordinal)) - { - // Validate - if (!Directory.Exists(newPath)) - { - throw new DirectoryNotFoundException( - string.Format( - CultureInfo.InvariantCulture, - "{0} does not exist.", - newPath)); - } - } - } - } } diff --git a/MediaBrowser.MediaEncoding/Configuration/EncodingConfigurationStore.cs b/MediaBrowser.MediaEncoding/Configuration/EncodingConfigurationStore.cs new file mode 100644 index 000000000..2f158157e --- /dev/null +++ b/MediaBrowser.MediaEncoding/Configuration/EncodingConfigurationStore.cs @@ -0,0 +1,38 @@ +#pragma warning disable CS1591 + +using System; +using System.Globalization; +using System.IO; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Model.Configuration; + +namespace MediaBrowser.MediaEncoding.Configuration +{ + public class EncodingConfigurationStore : ConfigurationStore, IValidatingConfiguration + { + public EncodingConfigurationStore() + { + ConfigurationType = typeof(EncodingOptions); + Key = "encoding"; + } + + public void Validate(object oldConfig, object newConfig) + { + var newPath = ((EncodingOptions)newConfig).TranscodingTempPath; + + if (!string.IsNullOrWhiteSpace(newPath) + && !string.Equals(((EncodingOptions)oldConfig).TranscodingTempPath, newPath, StringComparison.Ordinal)) + { + // Validate + if (!Directory.Exists(newPath)) + { + throw new DirectoryNotFoundException( + string.Format( + CultureInfo.InvariantCulture, + "{0} does not exist.", + newPath)); + } + } + } + } +} diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index 1ac56f845..75123a843 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -14,7 +14,7 @@ namespace MediaBrowser.MediaEncoding.Encoder { private const string DefaultEncoderPath = "ffmpeg"; - private static readonly string[] requiredDecoders = new[] + private static readonly string[] _requiredDecoders = new[] { "h264", "hevc", @@ -57,7 +57,7 @@ namespace MediaBrowser.MediaEncoding.Encoder "vc1_opencl" }; - private static readonly string[] requiredEncoders = new[] + private static readonly string[] _requiredEncoders = new[] { "libx264", "libx265", @@ -112,6 +112,12 @@ namespace MediaBrowser.MediaEncoding.Encoder _encoderPath = encoderPath; } + private enum Codec + { + Encoder, + Decoder + } + public static Version MinVersion { get; } = new Version(4, 0); public static Version MaxVersion { get; } = null; @@ -195,8 +201,8 @@ namespace MediaBrowser.MediaEncoding.Encoder /// If that fails then we use one of the main libraries to determine if it's new/older than the latest /// we have stored. /// - /// - /// + /// The output from "ffmpeg -version". + /// The FFmpeg version. internal static Version GetFFmpegVersion(string output) { // For pre-built binaries the FFmpeg version should be mentioned at the very start of the output @@ -218,10 +224,10 @@ namespace MediaBrowser.MediaEncoding.Encoder /// /// Grabs the library names and major.minor version numbers from the 'ffmpeg -version' output - /// and condenses them on to one line. Output format is "name1=major.minor,name2=major.minor,etc." + /// and condenses them on to one line. Output format is "name1=major.minor,name2=major.minor,etc.". /// - /// - /// + /// The 'ffmpeg -version' output. + /// The library names and major.minor version numbers. private static string GetLibrariesVersionString(string output) { var rc = new StringBuilder(144); @@ -241,12 +247,6 @@ namespace MediaBrowser.MediaEncoding.Encoder return rc.Length == 0 ? null : rc.ToString(); } - private enum Codec - { - Encoder, - Decoder - } - private IEnumerable GetHwaccelTypes() { string output = null; @@ -264,7 +264,7 @@ namespace MediaBrowser.MediaEncoding.Encoder return Enumerable.Empty(); } - var found = output.Split(new char[] {'\r','\n'}, StringSplitOptions.RemoveEmptyEntries).Skip(1).Distinct().ToList(); + var found = output.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries).Skip(1).Distinct().ToList(); _logger.LogInformation("Available hwaccel types: {Types}", found); return found; @@ -288,7 +288,7 @@ namespace MediaBrowser.MediaEncoding.Encoder return Enumerable.Empty(); } - var required = codec == Codec.Encoder ? requiredEncoders : requiredDecoders; + var required = codec == Codec.Encoder ? _requiredEncoders : _requiredDecoders; var found = Regex .Matches(output, @"^\s\S{6}\s(?[\w|-]+)\s+.+$", RegexOptions.Multiline) diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 0f0ae877f..62e6e8e3c 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; @@ -21,9 +22,8 @@ using MediaBrowser.Model.Globalization; using MediaBrowser.Model.IO; using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.System; -using Microsoft.Extensions.Logging; -using System.Diagnostics; using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; namespace MediaBrowser.MediaEncoding.Encoder { @@ -37,6 +37,11 @@ namespace MediaBrowser.MediaEncoding.Encoder /// internal const int DefaultImageExtractionTimeout = 5000; + /// + /// The us culture. + /// + private readonly CultureInfo _usCulture = new CultureInfo("en-US"); + private readonly ILogger _logger; private readonly IServerConfigurationManager _configurationManager; private readonly IFileSystem _fileSystem; @@ -49,6 +54,10 @@ namespace MediaBrowser.MediaEncoding.Encoder private readonly object _runningProcessesLock = new object(); private readonly List _runningProcesses = new List(); + private List _encoders = new List(); + private List _decoders = new List(); + private List _hwaccels = new List(); + private string _ffmpegPath = string.Empty; private string _ffprobePath; @@ -79,7 +88,7 @@ namespace MediaBrowser.MediaEncoding.Encoder /// /// Run at startup or if the user removes a Custom path from transcode page. /// Sets global variables FFmpegPath. - /// Precedence is: Config > CLI > $PATH + /// Precedence is: Config > CLI > $PATH. /// public void SetFFmpegPath() { @@ -124,8 +133,8 @@ namespace MediaBrowser.MediaEncoding.Encoder /// Triggered from the Settings > Transcoding UI page when users submits Custom FFmpeg path to use. /// Only write the new path to xml if it exists. Do not perform validation checks on ffmpeg here. /// - /// - /// + /// The path. + /// The path type. public void UpdateEncoderPath(string path, string pathType) { string newPath; @@ -170,8 +179,8 @@ namespace MediaBrowser.MediaEncoding.Encoder /// If checks pass, global variable FFmpegPath and EncoderLocation are updated. /// /// FQPN to test. - /// Location (External, Custom, System) of tool - /// + /// Location (External, Custom, System) of tool. + /// true if the version validation succeeded; otherwise, false. private bool ValidatePath(string path, FFmpegLocation location) { bool rc = false; @@ -223,8 +232,8 @@ namespace MediaBrowser.MediaEncoding.Encoder /// /// Search the system $PATH environment variable looking for given filename. /// - /// - /// + /// The filename. + /// The full path to the file. private string ExistsOnSystemPath(string fileName) { var inJellyfinPath = GetEncoderPathFromDirectory(AppContext.BaseDirectory, fileName, recursive: true); @@ -248,25 +257,19 @@ namespace MediaBrowser.MediaEncoding.Encoder return null; } - private List _encoders = new List(); public void SetAvailableEncoders(IEnumerable list) { _encoders = list.ToList(); - // _logger.Info("Supported encoders: {0}", string.Join(",", list.ToArray())); } - private List _decoders = new List(); public void SetAvailableDecoders(IEnumerable list) { _decoders = list.ToList(); - // _logger.Info("Supported decoders: {0}", string.Join(",", list.ToArray())); } - private List _hwaccels = new List(); public void SetAvailableHwaccels(IEnumerable list) { _hwaccels = list.ToList(); - //_logger.Info("Supported hwaccels: {0}", string.Join(",", list.ToArray())); } public bool SupportsEncoder(string encoder) @@ -334,8 +337,16 @@ namespace MediaBrowser.MediaEncoding.Encoder var forceEnableLogging = request.MediaSource.Protocol != MediaProtocol.File; - return GetMediaInfoInternal(GetInputArgument(inputFiles, request.MediaSource.Protocol), request.MediaSource.Path, request.MediaSource.Protocol, extractChapters, - probeSize, request.MediaType == DlnaProfileType.Audio, request.MediaSource.VideoType, forceEnableLogging, cancellationToken); + return GetMediaInfoInternal( + GetInputArgument(inputFiles, request.MediaSource.Protocol), + request.MediaSource.Path, + request.MediaSource.Protocol, + extractChapters, + probeSize, + request.MediaType == DlnaProfileType.Audio, + request.MediaSource.VideoType, + forceEnableLogging, + cancellationToken); } /// @@ -344,7 +355,7 @@ namespace MediaBrowser.MediaEncoding.Encoder /// The input files. /// The protocol. /// System.String. - /// Unrecognized InputType + /// Unrecognized InputType. public string GetInputArgument(IReadOnlyList inputFiles, MediaProtocol protocol) => EncodingUtils.GetInputArgument(inputFiles, protocol); @@ -352,7 +363,8 @@ namespace MediaBrowser.MediaEncoding.Encoder /// Gets the media info internal. /// /// Task{MediaInfoResult}. - private async Task GetMediaInfoInternal(string inputPath, + private async Task GetMediaInfoInternal( + string inputPath, string primaryPath, MediaProtocol protocol, bool extractChapters, @@ -380,7 +392,6 @@ namespace MediaBrowser.MediaEncoding.Encoder FileName = _ffprobePath, Arguments = args, - WindowStyle = ProcessWindowStyle.Hidden, ErrorDialog = false, }, @@ -441,11 +452,6 @@ namespace MediaBrowser.MediaEncoding.Encoder } } - /// - /// The us culture. - /// - protected readonly CultureInfo UsCulture = new CultureInfo("en-US"); - public Task ExtractAudioImage(string path, int? imageStreamIndex, CancellationToken cancellationToken) { return ExtractImage(new[] { path }, null, null, imageStreamIndex, MediaProtocol.File, true, null, null, cancellationToken); @@ -461,8 +467,16 @@ namespace MediaBrowser.MediaEncoding.Encoder return ExtractImage(inputFiles, container, imageStream, imageStreamIndex, protocol, false, null, null, cancellationToken); } - private async Task ExtractImage(string[] inputFiles, string container, MediaStream videoStream, int? imageStreamIndex, MediaProtocol protocol, bool isAudio, - Video3DFormat? threedFormat, TimeSpan? offset, CancellationToken cancellationToken) + private async Task ExtractImage( + string[] inputFiles, + string container, + MediaStream videoStream, + int? imageStreamIndex, + MediaProtocol protocol, + bool isAudio, + Video3DFormat? threedFormat, + TimeSpan? offset, + CancellationToken cancellationToken) { var inputArgument = GetInputArgument(inputFiles, protocol); @@ -647,7 +661,7 @@ namespace MediaBrowser.MediaEncoding.Encoder public string GetTimeParameter(TimeSpan time) { - return time.ToString(@"hh\:mm\:ss\.fff", UsCulture); + return time.ToString(@"hh\:mm\:ss\.fff", _usCulture); } public async Task ExtractVideoImagesOnInterval( @@ -664,11 +678,11 @@ namespace MediaBrowser.MediaEncoding.Encoder { var inputArgument = GetInputArgument(inputFiles, protocol); - var vf = "fps=fps=1/" + interval.TotalSeconds.ToString(UsCulture); + var vf = "fps=fps=1/" + interval.TotalSeconds.ToString(_usCulture); if (maxWidth.HasValue) { - var maxWidthParam = maxWidth.Value.ToString(UsCulture); + var maxWidthParam = maxWidth.Value.ToString(_usCulture); vf += string.Format(",scale=min(iw\\,{0}):trunc(ow/dar/2)*2", maxWidthParam); } diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj index dab5f866c..017f917e2 100644 --- a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj +++ b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj @@ -28,4 +28,16 @@ + + ../jellyfin.ruleset + + + + + + + + + + diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index 8aaaf4a09..19e3bd8e6 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs @@ -18,10 +18,19 @@ namespace MediaBrowser.MediaEncoding.Probing { public class ProbeResultNormalizer { + // When extracting subtitles, the maximum length to consider (to avoid invalid filenames) + private const int MaxSubtitleDescriptionExtractionLength = 100; + + private const string ArtistReplaceValue = " | "; + + private readonly char[] _nameDelimiters = { '/', '|', ';', '\\' }; + private readonly CultureInfo _usCulture = new CultureInfo("en-US"); private readonly ILogger _logger; private readonly ILocalizationManager _localization; + private List _splitWhiteList = null; + public ProbeResultNormalizer(ILogger logger, ILocalizationManager localization) { _logger = logger; @@ -370,7 +379,6 @@ namespace MediaBrowser.MediaEncoding.Probing private List ReadValueArray(XmlReader reader) { - var pairs = new List(); reader.MoveToContent(); @@ -951,50 +959,46 @@ namespace MediaBrowser.MediaEncoding.Probing private void SetAudioInfoFromTags(MediaInfo audio, Dictionary tags) { + var peoples = new List(); var composer = FFProbeHelpers.GetDictionaryValue(tags, "composer"); if (!string.IsNullOrWhiteSpace(composer)) { - var peoples = new List(); foreach (var person in Split(composer, false)) { peoples.Add(new BaseItemPerson { Name = person, Type = PersonType.Composer }); } - - audio.People = peoples.ToArray(); } - // var conductor = FFProbeHelpers.GetDictionaryValue(tags, "conductor"); - // if (!string.IsNullOrWhiteSpace(conductor)) - //{ - // foreach (var person in Split(conductor, false)) - // { - // audio.People.Add(new BaseItemPerson { Name = person, Type = PersonType.Conductor }); - // } - //} + var conductor = FFProbeHelpers.GetDictionaryValue(tags, "conductor"); + if (!string.IsNullOrWhiteSpace(conductor)) + { + foreach (var person in Split(conductor, false)) + { + peoples.Add(new BaseItemPerson { Name = person, Type = PersonType.Conductor }); + } + } - // var lyricist = FFProbeHelpers.GetDictionaryValue(tags, "lyricist"); - // if (!string.IsNullOrWhiteSpace(lyricist)) - //{ - // foreach (var person in Split(lyricist, false)) - // { - // audio.People.Add(new BaseItemPerson { Name = person, Type = PersonType.Lyricist }); - // } - //} + var lyricist = FFProbeHelpers.GetDictionaryValue(tags, "lyricist"); + if (!string.IsNullOrWhiteSpace(lyricist)) + { + foreach (var person in Split(lyricist, false)) + { + peoples.Add(new BaseItemPerson { Name = person, Type = PersonType.Lyricist }); + } + } // Check for writer some music is tagged that way as alternative to composer/lyricist var writer = FFProbeHelpers.GetDictionaryValue(tags, "writer"); if (!string.IsNullOrWhiteSpace(writer)) { - var peoples = new List(); foreach (var person in Split(writer, false)) { peoples.Add(new BaseItemPerson { Name = person, Type = PersonType.Writer }); } - - audio.People = peoples.ToArray(); } + audio.People = peoples.ToArray(); audio.Album = FFProbeHelpers.GetDictionaryValue(tags, "album"); var artists = FFProbeHelpers.GetDictionaryValue(tags, "artists"); @@ -1119,8 +1123,6 @@ namespace MediaBrowser.MediaEncoding.Probing .FirstOrDefault(i => !string.IsNullOrWhiteSpace(i)); } - private readonly char[] _nameDelimiters = { '/', '|', ';', '\\' }; - /// /// Splits the specified val. /// @@ -1140,8 +1142,6 @@ namespace MediaBrowser.MediaEncoding.Probing .Select(i => i.Trim()); } - private const string ArtistReplaceValue = " | "; - private IEnumerable SplitArtists(string val, char[] delimiters, bool splitFeaturing) { if (splitFeaturing) @@ -1171,9 +1171,6 @@ namespace MediaBrowser.MediaEncoding.Probing return artistsFound; } - - private List _splitWhiteList = null; - private IEnumerable GetSplitWhitelist() { if (_splitWhiteList == null) @@ -1250,7 +1247,7 @@ namespace MediaBrowser.MediaEncoding.Probing } /// - /// Gets the disc number, which is sometimes can be in the form of '1', or '1/3' + /// Gets the disc number, which is sometimes can be in the form of '1', or '1/3'. /// /// The tags. /// Name of the tag. @@ -1296,8 +1293,6 @@ namespace MediaBrowser.MediaEncoding.Probing return info; } - private const int MaxSubtitleDescriptionExtractionLength = 100; // When extracting subtitles, the maximum length to consider (to avoid invalid filenames) - private void FetchWtvInfo(MediaInfo video, InternalMediaInfoResult data) { if (data.Format == null || data.Format.Tags == null) @@ -1382,8 +1377,8 @@ namespace MediaBrowser.MediaEncoding.Probing if (subtitle.Contains('/', StringComparison.Ordinal)) // It contains a episode number and season number { string[] numbers = subtitle.Split(' '); - video.IndexNumber = int.Parse(numbers[0].Replace(".", "").Split('/')[0]); - int totalEpisodesInSeason = int.Parse(numbers[0].Replace(".", "").Split('/')[1]); + video.IndexNumber = int.Parse(numbers[0].Replace(".", string.Empty, StringComparison.Ordinal).Split('/')[0]); + int totalEpisodesInSeason = int.Parse(numbers[0].Replace(".", string.Empty, StringComparison.Ordinal).Split('/')[1]); description = string.Join(" ", numbers, 1, numbers.Length - 1).Trim(); // Skip the first, concatenate the rest, clean up spaces and save it } diff --git a/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs b/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs index e6e21756a..308b62886 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs @@ -25,7 +25,8 @@ namespace MediaBrowser.MediaEncoding.Subtitles { string line; while (reader.ReadLine() != "[Events]") - { } + { + } var headers = ParseFieldHeaders(reader.ReadLine()); @@ -75,17 +76,14 @@ namespace MediaBrowser.MediaEncoding.Subtitles { var fields = line.Substring(8).Split(',').Select(x => x.Trim()).ToList(); - var result = new Dictionary { - {"Start", fields.IndexOf("Start")}, - {"End", fields.IndexOf("End")}, - {"Text", fields.IndexOf("Text")} - }; - return result; + return new Dictionary + { + { "Start", fields.IndexOf("Start") }, + { "End", fields.IndexOf("End") }, + { "Text", fields.IndexOf("Text") } + }; } - /// - /// Credit: https://github.com/SubtitleEdit/subtitleedit/blob/master/src/Logic/SubtitleFormats/AdvancedSubStationAlpha.cs - /// private void RemoteNativeFormatting(SubtitleTrackEvent p) { int indexOfBegin = p.Text.IndexOf('{'); diff --git a/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs b/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs index bd330f568..6b7a81e6e 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs @@ -8,7 +8,7 @@ using MediaBrowser.Model.MediaInfo; namespace MediaBrowser.MediaEncoding.Subtitles { /// - /// Credit to https://github.com/SubtitleEdit/subtitleedit/blob/a299dc4407a31796364cc6ad83f0d3786194ba22/src/Logic/SubtitleFormats/SubStationAlpha.cs + /// Credit. /// public class SsaParser : ISubtitleParser { @@ -179,10 +179,12 @@ namespace MediaBrowser.MediaEncoding.Subtitles { // h:mm:ss.cc string[] timeCode = time.Split(':', '.'); - return new TimeSpan(0, int.Parse(timeCode[0]), - int.Parse(timeCode[1]), - int.Parse(timeCode[2]), - int.Parse(timeCode[3]) * 10).Ticks; + return new TimeSpan( + 0, + int.Parse(timeCode[0]), + int.Parse(timeCode[1]), + int.Parse(timeCode[2]), + int.Parse(timeCode[3]) * 10).Ticks; } private static string GetFormattedText(string text) @@ -282,6 +284,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles { text = text.Insert(start, ""); } + int indexOfEndTag = text.IndexOf("{\\c}", start); if (indexOfEndTag > 0) { @@ -320,6 +323,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles { text = text.Insert(start, ""); } + text += ""; } } diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs index 7c0697279..374e35b96 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs @@ -34,6 +34,12 @@ namespace MediaBrowser.MediaEncoding.Subtitles private readonly IHttpClient _httpClient; private readonly IMediaSourceManager _mediaSourceManager; + /// + /// The _semaphoreLocks. + /// + private readonly ConcurrentDictionary _semaphoreLocks = + new ConcurrentDictionary(); + public SubtitleEncoder( ILibraryManager libraryManager, ILogger logger, @@ -269,25 +275,6 @@ namespace MediaBrowser.MediaEncoding.Subtitles return new SubtitleInfo(subtitleStream.Path, protocol, currentFormat, true); } - private struct SubtitleInfo - { - public SubtitleInfo(string path, MediaProtocol protocol, string format, bool isExternal) - { - Path = path; - Protocol = protocol; - Format = format; - IsExternal = isExternal; - } - - public string Path { get; set; } - - public MediaProtocol Protocol { get; set; } - - public string Format { get; set; } - - public bool IsExternal { get; set; } - } - private ISubtitleParser GetReader(string format, bool throwIfMissing) { if (string.IsNullOrEmpty(format)) @@ -360,12 +347,6 @@ namespace MediaBrowser.MediaEncoding.Subtitles throw new ArgumentException("Unsupported format: " + format); } - /// - /// The _semaphoreLocks. - /// - private readonly ConcurrentDictionary _semaphoreLocks = - new ConcurrentDictionary(); - /// /// Gets the lock. /// @@ -414,7 +395,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles /// The cancellation token. /// Task. /// - /// The inputPath or outputPath is null + /// The inputPath or outputPath is null. /// private async Task ConvertTextSubtitleToSrtInternal(string inputPath, string language, MediaProtocol inputProtocol, string outputPath, CancellationToken cancellationToken) { @@ -438,7 +419,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles (encodingParam.Equals("UTF-16BE", StringComparison.OrdinalIgnoreCase) || encodingParam.Equals("UTF-16LE", StringComparison.OrdinalIgnoreCase))) { - encodingParam = ""; + encodingParam = string.Empty; } else if (!string.IsNullOrEmpty(encodingParam)) { @@ -540,7 +521,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles /// The output path. /// The cancellation token. /// Task. - /// Must use inputPath list overload + /// Must use inputPath list overload. private async Task ExtractTextSubtitle( string[] inputFiles, MediaProtocol protocol, @@ -759,7 +740,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles && (string.Equals(charset, "utf-16le", StringComparison.OrdinalIgnoreCase) || string.Equals(charset, "utf-16be", StringComparison.OrdinalIgnoreCase))) { - charset = ""; + charset = string.Empty; } _logger.LogDebug("charset {0} detected for {Path}", charset ?? "null", path); @@ -790,5 +771,24 @@ namespace MediaBrowser.MediaEncoding.Subtitles throw new ArgumentOutOfRangeException(nameof(protocol)); } } + + private struct SubtitleInfo + { + public SubtitleInfo(string path, MediaProtocol protocol, string format, bool isExternal) + { + Path = path; + Protocol = protocol; + Format = format; + IsExternal = isExternal; + } + + public string Path { get; set; } + + public MediaProtocol Protocol { get; set; } + + public string Format { get; set; } + + public bool IsExternal { get; set; } + } } } -- cgit v1.2.3 From cb5cb075a9803e296f6de594307994b88cb156d0 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Thu, 20 Aug 2020 17:16:09 +0200 Subject: Simplify FFmpeg detection code --- .../Encoder/EncoderValidator.cs | 58 +++------------------- .../EncoderValidatorTests.cs | 2 + .../EncoderValidatorTestsData.cs | 12 +++++ 3 files changed, 21 insertions(+), 51 deletions(-) (limited to 'MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs') diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index 0e9a5e502..1c389c237 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -100,20 +100,6 @@ namespace MediaBrowser.MediaEncoding.Encoder { "libpostproc", new Version(55, 1) } }; - // This lookup table is to be maintained with the following command line: - // $ ffmpeg -version | perl -ne ' print "$1=$2.$3," if /^(lib\w+)\s+(\d+)\.\s*(\d+)/' - private static readonly IReadOnlyDictionary _ffmpegVersionMap = new Dictionary - { - { "libavutil=56.51,libavcodec=58.91,libavformat=58.45,libavdevice=58.10,libavfilter=7.85,libswscale=5.7,libswresample=3.7,libpostproc=55.7,", new Version(4, 3) }, - { "libavutil=56.31,libavcodec=58.54,libavformat=58.29,libavdevice=58.8,libavfilter=7.57,libswscale=5.5,libswresample=3.5,libpostproc=55.5,", new Version(4, 2) }, - { "libavutil=56.22,libavcodec=58.35,libavformat=58.20,libavdevice=58.5,libavfilter=7.40,libswscale=5.3,libswresample=3.3,libpostproc=55.3,", new Version(4, 1) }, - { "libavutil=56.14,libavcodec=58.18,libavformat=58.12,libavdevice=58.3,libavfilter=7.16,libswscale=5.1,libswresample=3.1,libpostproc=55.1,", new Version(4, 0) }, - { "libavutil=55.78,libavcodec=57.107,libavformat=57.83,libavdevice=57.10,libavfilter=6.107,libswscale=4.8,libswresample=2.9,libpostproc=54.7,", new Version(3, 4) }, - { "libavutil=55.58,libavcodec=57.89,libavformat=57.71,libavdevice=57.6,libavfilter=6.82,libswscale=4.6,libswresample=2.7,libpostproc=54.5,", new Version(3, 3) }, - { "libavutil=55.34,libavcodec=57.64,libavformat=57.56,libavdevice=57.1,libavfilter=6.65,libswscale=4.2,libswresample=2.3,libpostproc=54.1,", new Version(3, 2) }, - { "libavutil=54.31,libavcodec=56.60,libavformat=56.40,libavdevice=56.4,libavfilter=5.40,libswscale=3.1,libswresample=1.2,libpostproc=53.3,", new Version(2, 8) } - }; - private readonly ILogger _logger; private readonly string _encoderPath; @@ -130,6 +116,7 @@ namespace MediaBrowser.MediaEncoding.Encoder Decoder } + // When changing this, also change the minimum library versions in _ffmpegMinimumLibraryVersions public static Version MinVersion { get; } = new Version(4, 0); public static Version MaxVersion { get; } = null; @@ -227,28 +214,9 @@ namespace MediaBrowser.MediaEncoding.Encoder { return new Version(match.Groups[1].Value); } - else - { - if (!TryGetFFmpegLibraryVersions(output, out string versionString, out IReadOnlyDictionary versionMap)) - { - _logger.LogError("No ffmpeg library versions found"); - return null; - } - - // First try to lookup the full version string - if (_ffmpegVersionMap.TryGetValue(versionString, out Version version)) - { - return version; - } + var versionMap = GetFFmpegLibraryVersions(output); - // Then try to test for minimum library versions - return TestMinimumFFmpegLibraryVersions(versionMap); - } - } - - private Version TestMinimumFFmpegLibraryVersions(IReadOnlyDictionary versionMap) - { var allVersionsValidated = true; foreach (var minimumVersion in _ffmpegMinimumLibraryVersions) @@ -281,10 +249,8 @@ namespace MediaBrowser.MediaEncoding.Encoder /// /// The 'ffmpeg -version' output. /// The library names and major.minor version numbers. - private static bool TryGetFFmpegLibraryVersions(string output, out string versionString, out IReadOnlyDictionary versionMap) + private static IReadOnlyDictionary GetFFmpegLibraryVersions(string output) { - var sb = new StringBuilder(144); - var map = new Dictionary(); foreach (Match match in Regex.Matches( @@ -292,24 +258,14 @@ namespace MediaBrowser.MediaEncoding.Encoder @"((?lib\w+)\s+(?[0-9]+)\.\s*(?[0-9]+))", RegexOptions.Multiline)) { - sb.Append(match.Groups["name"]) - .Append('=') - .Append(match.Groups["major"]) - .Append('.') - .Append(match.Groups["minor"]) - .Append(','); - - var str = $"{match.Groups["major"]}.{match.Groups["minor"]}"; - - var version = Version.Parse(str); + var version = new Version( + int.Parse(match.Groups["major"].Value), + int.Parse(match.Groups["minor"].Value)); map.Add(match.Groups["name"].Value, version); } - versionString = sb.ToString(); - versionMap = map; - - return sb.Length > 0; + return map; } private IEnumerable GetHwaccelTypes() diff --git a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs index c6b9cf67c..26db2dab0 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs @@ -18,6 +18,7 @@ namespace Jellyfin.MediaEncoding.Tests } [Theory] + [InlineData(EncoderValidatorTestsData.FFmpegV431Output, true)] [InlineData(EncoderValidatorTestsData.FFmpegV43Output, true)] [InlineData(EncoderValidatorTestsData.FFmpegV421Output, true)] [InlineData(EncoderValidatorTestsData.FFmpegV42Output, true)] @@ -34,6 +35,7 @@ namespace Jellyfin.MediaEncoding.Tests { public IEnumerator GetEnumerator() { + yield return new object?[] { EncoderValidatorTestsData.FFmpegV431Output, new Version(4, 3, 1) }; yield return new object?[] { EncoderValidatorTestsData.FFmpegV43Output, new Version(4, 3) }; yield return new object?[] { EncoderValidatorTestsData.FFmpegV421Output, new Version(4, 2, 1) }; yield return new object?[] { EncoderValidatorTestsData.FFmpegV42Output, new Version(4, 2) }; diff --git a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs index f5ff3d723..f781471cc 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs @@ -2,6 +2,18 @@ namespace Jellyfin.MediaEncoding.Tests { internal static class EncoderValidatorTestsData { + public const string FFmpegV431Output = @"ffmpeg version n4.3.1 Copyright (c) 2000-2020 the FFmpeg developers +built with gcc 10.1.0 (GCC) +configuration: --prefix=/usr --disable-debug --disable-static --disable-stripping --enable-avisynth --enable-fontconfig --enable-gmp --enable-gnutls --enable-gpl --enable-ladspa --enable-libaom --enable-libass --enable-libbluray --enable-libdav1d --enable-libdrm --enable-libfreetype --enable-libfribidi --enable-libgsm --enable-libiec61883 --enable-libjack --enable-libmfx --enable-libmodplug --enable-libmp3lame --enable-libopencore_amrnb --enable-libopencore_amrwb --enable-libopenjpeg --enable-libopus --enable-libpulse --enable-librav1e --enable-libsoxr --enable-libspeex --enable-libsrt --enable-libssh --enable-libtheora --enable-libv4l2 --enable-libvidstab --enable-libvmaf --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxcb --enable-libxml2 --enable-libxvid --enable-nvdec --enable-nvenc --enable-omx --enable-shared --enable-version3 +libavutil 56. 51.100 / 56. 51.100 +libavcodec 58. 91.100 / 58. 91.100 +libavformat 58. 45.100 / 58. 45.100 +libavdevice 58. 10.100 / 58. 10.100 +libavfilter 7. 85.100 / 7. 85.100 +libswscale 5. 7.100 / 5. 7.100 +libswresample 3. 7.100 / 3. 7.100 +libpostproc 55. 7.100 / 55. 7.100"; + public const string FFmpegV43Output = @"ffmpeg version 4.3 Copyright (c) 2000-2020 the FFmpeg developers built with gcc 7 (Ubuntu 7.5.0-3ubuntu1~18.04) configuration: --prefix=/usr/lib/jellyfin-ffmpeg --target-os=linux --disable-doc --disable-ffplay --disable-shared --disable-libxcb --disable-vdpau --disable-sdl2 --disable-xlib --enable-gpl --enable-version3 --enable-static --enable-libfontconfig --enable-fontconfig --enable-gmp --enable-gnutls --enable-libass --enable-libbluray --enable-libdrm --enable-libfreetype --enable-libfribidi --enable-libmp3lame --enable-libopus --enable-libtheora --enable-libvorbis --enable-libwebp --enable-libx264 --enable-libx265 --enable-libzvbi --arch=amd64 --enable-amf --enable-nvenc --enable-nvdec --enable-vaapi --enable-opencl -- cgit v1.2.3 From 301e029d42356d156a04d6269806d74916f80cc8 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Thu, 20 Aug 2020 17:45:40 +0200 Subject: Add unsupported ffmpeg version to tests --- MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs | 1 - .../Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs | 6 ++++-- .../EncoderValidatorTestsData.cs | 14 +++++++++++++- 3 files changed, 17 insertions(+), 4 deletions(-) (limited to 'MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs') diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index 1c389c237..3c19109a7 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using System.Text; using System.Text.RegularExpressions; using Microsoft.Extensions.Logging; diff --git a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs index 26db2dab0..39fd8afda 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs @@ -24,7 +24,8 @@ namespace Jellyfin.MediaEncoding.Tests [InlineData(EncoderValidatorTestsData.FFmpegV42Output, true)] [InlineData(EncoderValidatorTestsData.FFmpegV414Output, true)] [InlineData(EncoderValidatorTestsData.FFmpegV404Output, true)] - [InlineData(EncoderValidatorTestsData.FFmpegGitUnknownOutput, true)] + [InlineData(EncoderValidatorTestsData.FFmpegGitUnknownOutput2, true)] + [InlineData(EncoderValidatorTestsData.FFmpegGitUnknownOutput, false)] public void ValidateVersionInternalTest(string versionOutput, bool valid) { var val = new EncoderValidator(new NullLogger()); @@ -41,7 +42,8 @@ namespace Jellyfin.MediaEncoding.Tests yield return new object?[] { EncoderValidatorTestsData.FFmpegV42Output, new Version(4, 2) }; yield return new object?[] { EncoderValidatorTestsData.FFmpegV414Output, new Version(4, 1, 4) }; yield return new object?[] { EncoderValidatorTestsData.FFmpegV404Output, new Version(4, 0, 4) }; - yield return new object?[] { EncoderValidatorTestsData.FFmpegGitUnknownOutput, new Version(4, 0) }; + yield return new object?[] { EncoderValidatorTestsData.FFmpegGitUnknownOutput2, new Version(4, 0) }; + yield return new object?[] { EncoderValidatorTestsData.FFmpegGitUnknownOutput, null }; } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); diff --git a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs index f781471cc..9f5bef9a8 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs @@ -75,7 +75,7 @@ libswscale 5. 1.100 / 5. 1.100 libswresample 3. 1.100 / 3. 1.100 libpostproc 55. 1.100 / 55. 1.100"; - public const string FFmpegGitUnknownOutput = @"ffmpeg version N-94303-g7cb4f8c962 Copyright (c) 2000-2019 the FFmpeg developers + public const string FFmpegGitUnknownOutput2 = @"ffmpeg version N-94303-g7cb4f8c962 Copyright (c) 2000-2019 the FFmpeg developers built with gcc 9.1.1 (GCC) 20190716 configuration: --enable-gpl --enable-version3 --enable-sdl2 --enable-fontconfig --enable-gnutls --enable-iconv --enable-libass --enable-libdav1d --enable-libbluray --enable-libfreetype --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libopus --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libtheora --enable-libtwolame --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libzimg --enable-lzma --enable-zlib --enable-gmp --enable-libvidstab --enable-libvorbis --enable-libvo-amrwbenc --enable-libmysofa --enable-libspeex --enable-libxvid --enable-libaom --enable-libmfx --enable-amf --enable-ffnvcodec --enable-cuvid --enable-d3d11va --enable-nvenc --enable-nvdec --enable-dxva2 --enable-avisynth --enable-libopenmpt libavutil 56. 30.100 / 56. 30.100 @@ -86,5 +86,17 @@ libavfilter 7. 56.101 / 7. 56.101 libswscale 5. 4.101 / 5. 4.101 libswresample 3. 4.100 / 3. 4.100 libpostproc 55. 4.100 / 55. 4.100"; + + public const string FFmpegGitUnknownOutput = @"ffmpeg version N-45325-gb173e0353-static https://johnvansickle.com/ffmpeg/ Copyright (c) 2000-2018 the FFmpeg developers +built with gcc 6.3.0 (Debian 6.3.0-18+deb9u1) 20170516 +configuration: --enable-gpl --enable-version3 --enable-static --disable-debug --disable-ffplay --disable-indev=sndio --disable-outdev=sndio --cc=gcc-6 --enable-fontconfig --enable-frei0r --enable-gnutls --enable-gray --enable-libfribidi --enable-libass --enable-libfreetype --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-librubberband --enable-libsoxr --enable-libspeex --enable-libvorbis --enable-libopus --enable-libtheora --enable-libvidstab --enable-libvo-amrwbenc --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxvid --enable-libzimg +libavutil 56. 9.100 / 56. 9.100 +libavcodec 58. 14.100 / 58. 14.100 +libavformat 58. 10.100 / 58. 10.100 +libavdevice 58. 2.100 / 58. 2.100 +libavfilter 7. 13.100 / 7. 13.100 +libswscale 5. 0.102 / 5. 0.102 +libswresample 3. 0.101 / 3. 0.101 +libpostproc 55. 0.100 / 55. 0.100"; } } -- cgit v1.2.3 From 119f64f5e7b09aeb4ff8f59237093906c1e08f5f Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Fri, 21 Aug 2020 22:01:19 +0200 Subject: Make some methods async --- .../Channels/ChannelManager.cs | 21 ++++++--- .../Collections/CollectionManager.cs | 51 +++++++++------------- .../Library/LibraryManager.cs | 39 +++++++---------- .../LiveTv/LiveTvManager.cs | 16 ++++--- .../Playlists/PlaylistManager.cs | 16 +++---- Jellyfin.Api/Controllers/CollectionController.cs | 15 ++++--- Jellyfin.Api/Controllers/ImageController.cs | 14 +++--- Jellyfin.Api/Controllers/ItemUpdateController.cs | 7 +-- Jellyfin.Api/Controllers/PlaylistsController.cs | 12 ++--- Jellyfin.Api/Controllers/RemoteImageController.cs | 2 +- Jellyfin.Api/Controllers/VideosController.cs | 14 +++--- .../Collections/ICollectionManager.cs | 12 ++--- MediaBrowser.Controller/Entities/BaseItem.cs | 20 ++++----- MediaBrowser.Controller/Entities/Folder.cs | 4 +- MediaBrowser.Controller/Entities/Video.cs | 7 +-- MediaBrowser.Controller/Library/ILibraryManager.cs | 41 +++++++++++++---- .../Playlists/IPlaylistManager.cs | 6 +-- .../Encoder/EncoderValidator.cs | 5 ++- MediaBrowser.Providers/Manager/MetadataService.cs | 4 +- MediaBrowser.Providers/TV/DummySeasonProvider.cs | 4 +- 20 files changed, 165 insertions(+), 145 deletions(-) (limited to 'MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs') diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs index d8ab1f1a1..26fc1bee4 100644 --- a/Emby.Server.Implementations/Channels/ChannelManager.cs +++ b/Emby.Server.Implementations/Channels/ChannelManager.cs @@ -746,12 +746,21 @@ namespace Emby.Server.Implementations.Channels // null if came from cache if (itemsResult != null) { - var internalItems = itemsResult.Items - .Select(i => GetChannelItemEntity(i, channelProvider, channel.Id, parentItem, cancellationToken)) - .ToArray(); + var items = itemsResult.Items; + var itemsLen = items.Count; + var internalItems = new Guid[itemsLen]; + for (int i = 0; i < itemsLen; i++) + { + internalItems[i] = (await GetChannelItemEntityAsync( + items[i], + channelProvider, + channel.Id, + parentItem, + cancellationToken).ConfigureAwait(false)).Id; + } var existingIds = _libraryManager.GetItemIds(query); - var deadIds = existingIds.Except(internalItems.Select(i => i.Id)) + var deadIds = existingIds.Except(internalItems) .ToArray(); foreach (var deadId in deadIds) @@ -963,7 +972,7 @@ namespace Emby.Server.Implementations.Channels return item; } - private BaseItem GetChannelItemEntity(ChannelItemInfo info, IChannel channelProvider, Guid internalChannelId, BaseItem parentFolder, CancellationToken cancellationToken) + private async Task GetChannelItemEntityAsync(ChannelItemInfo info, IChannel channelProvider, Guid internalChannelId, BaseItem parentFolder, CancellationToken cancellationToken) { var parentFolderId = parentFolder.Id; @@ -1165,7 +1174,7 @@ namespace Emby.Server.Implementations.Channels } else if (forceUpdate) { - item.UpdateToRepository(ItemUpdateType.None, cancellationToken); + await item.UpdateToRepositoryAsync(ItemUpdateType.None, cancellationToken).ConfigureAwait(false); } if ((isNew || forceUpdate) && info.Type == ChannelItemType.Media) diff --git a/Emby.Server.Implementations/Collections/CollectionManager.cs b/Emby.Server.Implementations/Collections/CollectionManager.cs index ac2edc1e2..3011a37e3 100644 --- a/Emby.Server.Implementations/Collections/CollectionManager.cs +++ b/Emby.Server.Implementations/Collections/CollectionManager.cs @@ -132,7 +132,7 @@ namespace Emby.Server.Implementations.Collections } /// - public BoxSet CreateCollection(CollectionCreationOptions options) + public async Task CreateCollectionAsync(CollectionCreationOptions options) { var name = options.Name; @@ -141,7 +141,7 @@ namespace Emby.Server.Implementations.Collections // This could cause it to get re-resolved as a plain folder var folderName = _fileSystem.GetValidFilename(name) + " [boxset]"; - var parentFolder = GetCollectionsFolder(true).GetAwaiter().GetResult(); + var parentFolder = await GetCollectionsFolder(true).ConfigureAwait(false); if (parentFolder == null) { @@ -169,12 +169,16 @@ namespace Emby.Server.Implementations.Collections if (options.ItemIdList.Length > 0) { - AddToCollection(collection.Id, options.ItemIdList, false, new MetadataRefreshOptions(new DirectoryService(_fileSystem)) - { - // The initial adding of items is going to create a local metadata file - // This will cause internet metadata to be skipped as a result - MetadataRefreshMode = MetadataRefreshMode.FullRefresh - }); + await AddToCollectionAsync( + collection.Id, + options.ItemIdList.Select(x => new Guid(x)), + false, + new MetadataRefreshOptions(new DirectoryService(_fileSystem)) + { + // The initial adding of items is going to create a local metadata file + // This will cause internet metadata to be skipped as a result + MetadataRefreshMode = MetadataRefreshMode.FullRefresh + }).ConfigureAwait(false); } else { @@ -197,18 +201,10 @@ namespace Emby.Server.Implementations.Collections } /// - public void AddToCollection(Guid collectionId, IEnumerable ids) - { - AddToCollection(collectionId, ids, true, new MetadataRefreshOptions(new DirectoryService(_fileSystem))); - } + public Task AddToCollectionAsync(Guid collectionId, IEnumerable ids) + => AddToCollectionAsync(collectionId, ids, true, new MetadataRefreshOptions(new DirectoryService(_fileSystem))); - /// - public void AddToCollection(Guid collectionId, IEnumerable ids) - { - AddToCollection(collectionId, ids.Select(i => i.ToString("N", CultureInfo.InvariantCulture)), true, new MetadataRefreshOptions(new DirectoryService(_fileSystem))); - } - - private void AddToCollection(Guid collectionId, IEnumerable ids, bool fireEvent, MetadataRefreshOptions refreshOptions) + private async Task AddToCollectionAsync(Guid collectionId, IEnumerable ids, bool fireEvent, MetadataRefreshOptions refreshOptions) { var collection = _libraryManager.GetItemById(collectionId) as BoxSet; if (collection == null) @@ -224,15 +220,14 @@ namespace Emby.Server.Implementations.Collections foreach (var id in ids) { - var guidId = new Guid(id); - var item = _libraryManager.GetItemById(guidId); + var item = _libraryManager.GetItemById(id); if (item == null) { throw new ArgumentException("No item exists with the supplied Id"); } - if (!currentLinkedChildrenIds.Contains(guidId)) + if (!currentLinkedChildrenIds.Contains(id)) { itemList.Add(item); @@ -249,7 +244,7 @@ namespace Emby.Server.Implementations.Collections collection.UpdateRatingToItems(linkedChildrenList); - collection.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None); + await collection.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); refreshOptions.ForceSave = true; _providerManager.QueueRefresh(collection.Id, refreshOptions, RefreshPriority.High); @@ -266,13 +261,7 @@ namespace Emby.Server.Implementations.Collections } /// - public void RemoveFromCollection(Guid collectionId, IEnumerable itemIds) - { - RemoveFromCollection(collectionId, itemIds.Select(i => new Guid(i))); - } - - /// - public void RemoveFromCollection(Guid collectionId, IEnumerable itemIds) + public async Task RemoveFromCollectionAsync(Guid collectionId, IEnumerable itemIds) { var collection = _libraryManager.GetItemById(collectionId) as BoxSet; @@ -309,7 +298,7 @@ namespace Emby.Server.Implementations.Collections collection.LinkedChildren = collection.LinkedChildren.Except(list).ToArray(); } - collection.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None); + await collection.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); _providerManager.QueueRefresh( collection.Id, new MetadataRefreshOptions(new DirectoryService(_fileSystem)) diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 7ed8f0bbf..375f09f5b 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -771,7 +771,7 @@ namespace Emby.Server.Implementations.Library if (folder.ParentId != rootFolder.Id) { folder.ParentId = rootFolder.Id; - folder.UpdateToRepository(ItemUpdateType.MetadataImport, CancellationToken.None); + folder.UpdateToRepositoryAsync(ItemUpdateType.MetadataImport, CancellationToken.None).GetAwaiter().GetResult(); } rootFolder.AddVirtualChild(folder); @@ -1868,7 +1868,8 @@ namespace Emby.Server.Implementations.Library return image.Path != null && !image.IsLocalFile; } - public void UpdateImages(BaseItem item, bool forceUpdate = false) + /// + public async Task UpdateImagesAsync(BaseItem item, bool forceUpdate = false) { if (item == null) { @@ -1891,7 +1892,7 @@ namespace Emby.Server.Implementations.Library try { var index = item.GetImageIndex(img); - image = ConvertImageToLocal(item, img, index).ConfigureAwait(false).GetAwaiter().GetResult(); + image = await ConvertImageToLocal(item, img, index).ConfigureAwait(false); } catch (ArgumentException) { @@ -1913,7 +1914,7 @@ namespace Emby.Server.Implementations.Library } catch (Exception ex) { - _logger.LogError(ex, "Cannnot get image dimensions for {0}", image.Path); + _logger.LogError(ex, "Cannot get image dimensions for {0}", image.Path); image.Width = 0; image.Height = 0; continue; @@ -1943,10 +1944,8 @@ namespace Emby.Server.Implementations.Library RegisterItem(item); } - /// - /// Updates the item. - /// - public void UpdateItems(IReadOnlyList items, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken) + /// + public async Task UpdateItemsAsync(IReadOnlyList items, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken) { foreach (var item in items) { @@ -1957,7 +1956,7 @@ namespace Emby.Server.Implementations.Library item.DateLastSaved = DateTime.UtcNow; - UpdateImages(item, updateReason >= ItemUpdateType.ImageUpdate); + await UpdateImagesAsync(item, updateReason >= ItemUpdateType.ImageUpdate).ConfigureAwait(false); } _itemRepository.SaveItems(items, cancellationToken); @@ -1991,17 +1990,9 @@ namespace Emby.Server.Implementations.Library } } - /// - /// Updates the item. - /// - /// The item. - /// The parent item. - /// The update reason. - /// The cancellation token. - public void UpdateItem(BaseItem item, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken) - { - UpdateItems(new[] { item }, parent, updateReason, cancellationToken); - } + /// + public Task UpdateItemAsync(BaseItem item, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken) + => UpdateItemsAsync(new[] { item }, parent, updateReason, cancellationToken); /// /// Reports the item removed. @@ -2233,7 +2224,7 @@ namespace Emby.Server.Implementations.Library if (refresh) { - item.UpdateToRepository(ItemUpdateType.MetadataImport, CancellationToken.None); + item.UpdateToRepositoryAsync(ItemUpdateType.MetadataImport, CancellationToken.None).GetAwaiter().GetResult(); ProviderManager.QueueRefresh(item.Id, new MetadataRefreshOptions(new DirectoryService(_fileSystem)), RefreshPriority.Normal); } @@ -2420,7 +2411,7 @@ namespace Emby.Server.Implementations.Library if (!string.Equals(viewType, item.ViewType, StringComparison.OrdinalIgnoreCase)) { item.ViewType = viewType; - item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None); + item.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).GetAwaiter().GetResult(); } var refresh = isNew || DateTime.UtcNow - item.DateLastRefreshed >= _viewRefreshInterval; @@ -2902,7 +2893,7 @@ namespace Emby.Server.Implementations.Library await ProviderManager.SaveImage(item, url, image.Type, imageIndex, CancellationToken.None).ConfigureAwait(false); - item.UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None); + await item.UpdateToRepositoryAsync(ItemUpdateType.ImageUpdate, CancellationToken.None).ConfigureAwait(false); return item.GetImageInfo(image.Type, imageIndex); } @@ -2920,7 +2911,7 @@ namespace Emby.Server.Implementations.Library // Remove this image to prevent it from retrying over and over item.RemoveImage(image); - item.UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None); + await item.UpdateToRepositoryAsync(ItemUpdateType.ImageUpdate, CancellationToken.None).ConfigureAwait(false); throw new InvalidOperationException(); } diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs index 90cbd85a5..5ed6baeb9 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs @@ -422,7 +422,7 @@ namespace Emby.Server.Implementations.LiveTv } } - private LiveTvChannel GetChannel(ChannelInfo channelInfo, string serviceName, BaseItem parentFolder, CancellationToken cancellationToken) + private async Task GetChannelAsync(ChannelInfo channelInfo, string serviceName, BaseItem parentFolder, CancellationToken cancellationToken) { var parentFolderId = parentFolder.Id; var isNew = false; @@ -512,7 +512,7 @@ namespace Emby.Server.Implementations.LiveTv } else if (forceUpdate) { - _libraryManager.UpdateItem(item, parentFolder, ItemUpdateType.MetadataImport, cancellationToken); + await _libraryManager.UpdateItemAsync(item, parentFolder, ItemUpdateType.MetadataImport, cancellationToken).ConfigureAwait(false); } return item; @@ -1129,7 +1129,7 @@ namespace Emby.Server.Implementations.LiveTv try { - var item = GetChannel(channelInfo.Item2, channelInfo.Item1, parentFolder, cancellationToken); + var item = await GetChannelAsync(channelInfo.Item2, channelInfo.Item1, parentFolder, cancellationToken).ConfigureAwait(false); list.Add(item); } @@ -1146,7 +1146,7 @@ namespace Emby.Server.Implementations.LiveTv double percent = numComplete; percent /= allChannelsList.Count; - progress.Report(5 * percent + 10); + progress.Report((5 * percent) + 10); } progress.Report(15); @@ -1221,7 +1221,11 @@ namespace Emby.Server.Implementations.LiveTv if (updatedPrograms.Count > 0) { - _libraryManager.UpdateItems(updatedPrograms, currentChannel, ItemUpdateType.MetadataImport, cancellationToken); + await _libraryManager.UpdateItemsAsync( + updatedPrograms, + currentChannel, + ItemUpdateType.MetadataImport, + cancellationToken).ConfigureAwait(false); } currentChannel.IsMovie = isMovie; @@ -1234,7 +1238,7 @@ namespace Emby.Server.Implementations.LiveTv currentChannel.AddTag("Kids"); } - currentChannel.UpdateToRepository(ItemUpdateType.MetadataImport, cancellationToken); + await currentChannel.UpdateToRepositoryAsync(ItemUpdateType.MetadataImport, cancellationToken).ConfigureAwait(false); await currentChannel.RefreshMetadata( new MetadataRefreshOptions(new DirectoryService(_fileSystem)) { diff --git a/Emby.Server.Implementations/Playlists/PlaylistManager.cs b/Emby.Server.Implementations/Playlists/PlaylistManager.cs index 38ceadedb..d35223b0a 100644 --- a/Emby.Server.Implementations/Playlists/PlaylistManager.cs +++ b/Emby.Server.Implementations/Playlists/PlaylistManager.cs @@ -184,17 +184,17 @@ namespace Emby.Server.Implementations.Playlists return Playlist.GetPlaylistItems(playlistMediaType, items, user, options); } - public void AddToPlaylist(string playlistId, ICollection itemIds, Guid userId) + public Task AddToPlaylistAsync(string playlistId, ICollection itemIds, Guid userId) { var user = userId.Equals(Guid.Empty) ? null : _userManager.GetUserById(userId); - AddToPlaylistInternal(playlistId, itemIds, user, new DtoOptions(false) + return AddToPlaylistInternal(playlistId, itemIds, user, new DtoOptions(false) { EnableImages = true }); } - private void AddToPlaylistInternal(string playlistId, ICollection newItemIds, User user, DtoOptions options) + private async Task AddToPlaylistInternal(string playlistId, ICollection newItemIds, User user, DtoOptions options) { // Retrieve the existing playlist var playlist = _libraryManager.GetItemById(playlistId) as Playlist @@ -238,7 +238,7 @@ namespace Emby.Server.Implementations.Playlists // Update the playlist in the repository playlist.LinkedChildren = newLinkedChildren; - playlist.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None); + await playlist.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); // Update the playlist on disk if (playlist.IsFile) @@ -256,7 +256,7 @@ namespace Emby.Server.Implementations.Playlists RefreshPriority.High); } - public void RemoveFromPlaylist(string playlistId, IEnumerable entryIds) + public async Task RemoveFromPlaylistAsync(string playlistId, IEnumerable entryIds) { if (!(_libraryManager.GetItemById(playlistId) is Playlist playlist)) { @@ -273,7 +273,7 @@ namespace Emby.Server.Implementations.Playlists .Select(i => i.Item1) .ToArray(); - playlist.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None); + await playlist.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); if (playlist.IsFile) { @@ -289,7 +289,7 @@ namespace Emby.Server.Implementations.Playlists RefreshPriority.High); } - public void MoveItem(string playlistId, string entryId, int newIndex) + public async Task MoveItemAsync(string playlistId, string entryId, int newIndex) { if (!(_libraryManager.GetItemById(playlistId) is Playlist playlist)) { @@ -322,7 +322,7 @@ namespace Emby.Server.Implementations.Playlists playlist.LinkedChildren = newList.ToArray(); - playlist.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None); + await playlist.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); if (playlist.IsFile) { diff --git a/Jellyfin.Api/Controllers/CollectionController.cs b/Jellyfin.Api/Controllers/CollectionController.cs index 53821a188..c5910d6e8 100644 --- a/Jellyfin.Api/Controllers/CollectionController.cs +++ b/Jellyfin.Api/Controllers/CollectionController.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel.DataAnnotations; +using System.Threading.Tasks; using Jellyfin.Api.Constants; using Jellyfin.Api.Extensions; using Jellyfin.Api.Helpers; @@ -51,7 +52,7 @@ namespace Jellyfin.Api.Controllers /// A with information about the new collection. [HttpPost] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult CreateCollection( + public async Task> CreateCollection( [FromQuery] string? name, [FromQuery] string? ids, [FromQuery] Guid? parentId, @@ -59,14 +60,14 @@ namespace Jellyfin.Api.Controllers { var userId = _authContext.GetAuthorizationInfo(Request).UserId; - var item = _collectionManager.CreateCollection(new CollectionCreationOptions + var item = await _collectionManager.CreateCollectionAsync(new CollectionCreationOptions { IsLocked = isLocked, Name = name, ParentId = parentId, ItemIdList = RequestHelpers.Split(ids, ',', true), UserIds = new[] { userId } - }); + }).ConfigureAwait(false); var dtoOptions = new DtoOptions().AddClientFields(Request); @@ -87,9 +88,9 @@ namespace Jellyfin.Api.Controllers /// A indicating success. [HttpPost("{collectionId}/Items")] [ProducesResponseType(StatusCodes.Status204NoContent)] - public ActionResult AddToCollection([FromRoute] Guid collectionId, [FromQuery, Required] string? itemIds) + public async Task AddToCollection([FromRoute] Guid collectionId, [FromQuery, Required] string? itemIds) { - _collectionManager.AddToCollection(collectionId, RequestHelpers.Split(itemIds, ',', true)); + await _collectionManager.AddToCollectionAsync(collectionId, RequestHelpers.GetGuids(itemIds)).ConfigureAwait(true); return NoContent(); } @@ -102,9 +103,9 @@ namespace Jellyfin.Api.Controllers /// A indicating success. [HttpDelete("{collectionId}/Items")] [ProducesResponseType(StatusCodes.Status204NoContent)] - public ActionResult RemoveFromCollection([FromRoute] Guid collectionId, [FromQuery, Required] string? itemIds) + public async Task RemoveFromCollection([FromRoute] Guid collectionId, [FromQuery, Required] string? itemIds) { - _collectionManager.RemoveFromCollection(collectionId, RequestHelpers.Split(itemIds, ',', true)); + await _collectionManager.RemoveFromCollectionAsync(collectionId, RequestHelpers.GetGuids(itemIds)).ConfigureAwait(false); return NoContent(); } } diff --git a/Jellyfin.Api/Controllers/ImageController.cs b/Jellyfin.Api/Controllers/ImageController.cs index 75734f0af..ca9c2fa46 100644 --- a/Jellyfin.Api/Controllers/ImageController.cs +++ b/Jellyfin.Api/Controllers/ImageController.cs @@ -174,7 +174,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.RequiresElevation)] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult DeleteItemImage( + public async Task DeleteItemImage( [FromRoute] Guid itemId, [FromRoute] ImageType imageType, [FromRoute] int? imageIndex = null) @@ -185,7 +185,7 @@ namespace Jellyfin.Api.Controllers return NotFound(); } - item.DeleteImage(imageType, imageIndex ?? 0); + await item.DeleteImageAsync(imageType, imageIndex ?? 0).ConfigureAwait(false); return NoContent(); } @@ -218,7 +218,7 @@ namespace Jellyfin.Api.Controllers // Handle image/png; charset=utf-8 var mimeType = Request.ContentType.Split(';').FirstOrDefault(); await _providerManager.SaveImage(item, Request.Body, mimeType, imageType, null, CancellationToken.None).ConfigureAwait(false); - item.UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None); + await item.UpdateToRepositoryAsync(ItemUpdateType.ImageUpdate, CancellationToken.None).ConfigureAwait(false); return NoContent(); } @@ -237,7 +237,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.RequiresElevation)] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult UpdateItemImageIndex( + public async Task UpdateItemImageIndex( [FromRoute] Guid itemId, [FromRoute] ImageType imageType, [FromRoute] int imageIndex, @@ -249,7 +249,7 @@ namespace Jellyfin.Api.Controllers return NotFound(); } - item.SwapImages(imageType, imageIndex, newIndex); + await item.SwapImagesAsync(imageType, imageIndex, newIndex).ConfigureAwait(false); return NoContent(); } @@ -264,7 +264,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult> GetItemImageInfos([FromRoute] Guid itemId) + public async Task>> GetItemImageInfos([FromRoute] Guid itemId) { var item = _libraryManager.GetItemById(itemId); if (item == null) @@ -281,7 +281,7 @@ namespace Jellyfin.Api.Controllers return list; } - _libraryManager.UpdateImages(item); // this makes sure dimensions and hashes are correct + await _libraryManager.UpdateImagesAsync(item).ConfigureAwait(false); // this makes sure dimensions and hashes are correct foreach (var image in itemImages) { diff --git a/Jellyfin.Api/Controllers/ItemUpdateController.cs b/Jellyfin.Api/Controllers/ItemUpdateController.cs index 4b40c6ada..ec52f4996 100644 --- a/Jellyfin.Api/Controllers/ItemUpdateController.cs +++ b/Jellyfin.Api/Controllers/ItemUpdateController.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Threading; +using System.Threading.Tasks; using Jellyfin.Api.Constants; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; @@ -67,7 +68,7 @@ namespace Jellyfin.Api.Controllers [HttpPost("Items/{itemId}")] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult UpdateItem([FromRoute] Guid itemId, [FromBody, Required] BaseItemDto request) + public async Task UpdateItem([FromRoute] Guid itemId, [FromBody, Required] BaseItemDto request) { var item = _libraryManager.GetItemById(itemId); if (item == null) @@ -101,7 +102,7 @@ namespace Jellyfin.Api.Controllers item.OnMetadataChanged(); - item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None); + await item.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); if (isLockedChanged && item.IsFolder) { @@ -110,7 +111,7 @@ namespace Jellyfin.Api.Controllers foreach (var child in folder.GetRecursiveChildren()) { child.IsLocked = newLockData; - child.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None); + await child.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); } } diff --git a/Jellyfin.Api/Controllers/PlaylistsController.cs b/Jellyfin.Api/Controllers/PlaylistsController.cs index 12c87d7c3..d69228c33 100644 --- a/Jellyfin.Api/Controllers/PlaylistsController.cs +++ b/Jellyfin.Api/Controllers/PlaylistsController.cs @@ -83,12 +83,12 @@ namespace Jellyfin.Api.Controllers /// An on success. [HttpPost("{playlistId}/Items")] [ProducesResponseType(StatusCodes.Status204NoContent)] - public ActionResult AddToPlaylist( + public async Task AddToPlaylist( [FromRoute] string? playlistId, [FromQuery] string? ids, [FromQuery] Guid? userId) { - _playlistManager.AddToPlaylist(playlistId, RequestHelpers.GetGuids(ids), userId ?? Guid.Empty); + await _playlistManager.AddToPlaylistAsync(playlistId, RequestHelpers.GetGuids(ids), userId ?? Guid.Empty).ConfigureAwait(false); return NoContent(); } @@ -102,12 +102,12 @@ namespace Jellyfin.Api.Controllers /// An on success. [HttpPost("{playlistId}/Items/{itemId}/Move/{newIndex}")] [ProducesResponseType(StatusCodes.Status204NoContent)] - public ActionResult MoveItem( + public async Task MoveItem( [FromRoute] string? playlistId, [FromRoute] string? itemId, [FromRoute] int newIndex) { - _playlistManager.MoveItem(playlistId, itemId, newIndex); + await _playlistManager.MoveItemAsync(playlistId, itemId, newIndex).ConfigureAwait(false); return NoContent(); } @@ -120,9 +120,9 @@ namespace Jellyfin.Api.Controllers /// An on success. [HttpDelete("{playlistId}/Items")] [ProducesResponseType(StatusCodes.Status204NoContent)] - public ActionResult RemoveFromPlaylist([FromRoute] string? playlistId, [FromQuery] string? entryIds) + public async Task RemoveFromPlaylist([FromRoute] string? playlistId, [FromQuery] string? entryIds) { - _playlistManager.RemoveFromPlaylist(playlistId, RequestHelpers.Split(entryIds, ',', true)); + await _playlistManager.RemoveFromPlaylistAsync(playlistId, RequestHelpers.Split(entryIds, ',', true)).ConfigureAwait(false); return NoContent(); } diff --git a/Jellyfin.Api/Controllers/RemoteImageController.cs b/Jellyfin.Api/Controllers/RemoteImageController.cs index a203c50b9..30a4f73fc 100644 --- a/Jellyfin.Api/Controllers/RemoteImageController.cs +++ b/Jellyfin.Api/Controllers/RemoteImageController.cs @@ -221,7 +221,7 @@ namespace Jellyfin.Api.Controllers await _providerManager.SaveImage(item, imageUrl, type, null, CancellationToken.None) .ConfigureAwait(false); - item.UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None); + await item.UpdateToRepositoryAsync(ItemUpdateType.ImageUpdate, CancellationToken.None).ConfigureAwait(false); return NoContent(); } diff --git a/Jellyfin.Api/Controllers/VideosController.cs b/Jellyfin.Api/Controllers/VideosController.cs index 14d3f2460..f42810c94 100644 --- a/Jellyfin.Api/Controllers/VideosController.cs +++ b/Jellyfin.Api/Controllers/VideosController.cs @@ -161,7 +161,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.RequiresElevation)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult DeleteAlternateSources([FromRoute] Guid itemId) + public async Task DeleteAlternateSources([FromRoute] Guid itemId) { var video = (Video)_libraryManager.GetItemById(itemId); @@ -180,12 +180,12 @@ namespace Jellyfin.Api.Controllers link.SetPrimaryVersionId(null); link.LinkedAlternateVersions = Array.Empty(); - link.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None); + await link.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); } video.LinkedAlternateVersions = Array.Empty(); video.SetPrimaryVersionId(null); - video.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None); + await video.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); return NoContent(); } @@ -201,7 +201,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.RequiresElevation)] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status400BadRequest)] - public ActionResult MergeVersions([FromQuery, Required] string? itemIds) + public async Task MergeVersions([FromQuery, Required] string? itemIds) { var items = RequestHelpers.Split(itemIds, ',', true) .Select(i => _libraryManager.GetItemById(i)) @@ -239,7 +239,7 @@ namespace Jellyfin.Api.Controllers { item.SetPrimaryVersionId(primaryVersion.Id.ToString("N", CultureInfo.InvariantCulture)); - item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None); + await item.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); list.Add(new LinkedChild { @@ -258,12 +258,12 @@ namespace Jellyfin.Api.Controllers if (item.LinkedAlternateVersions.Length > 0) { item.LinkedAlternateVersions = Array.Empty(); - item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None); + await item.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); } } primaryVersion.LinkedAlternateVersions = list.ToArray(); - primaryVersion.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None); + await primaryVersion.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); return NoContent(); } diff --git a/MediaBrowser.Controller/Collections/ICollectionManager.cs b/MediaBrowser.Controller/Collections/ICollectionManager.cs index 701423c0f..3861ae634 100644 --- a/MediaBrowser.Controller/Collections/ICollectionManager.cs +++ b/MediaBrowser.Controller/Collections/ICollectionManager.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Threading.Tasks; using Jellyfin.Data.Entities; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; @@ -27,24 +28,23 @@ namespace MediaBrowser.Controller.Collections /// Creates the collection. /// /// The options. - BoxSet CreateCollection(CollectionCreationOptions options); + Task CreateCollectionAsync(CollectionCreationOptions options); /// /// Adds to collection. /// /// The collection identifier. /// The item ids. - void AddToCollection(Guid collectionId, IEnumerable itemIds); + /// representing the asynchronous operation. + Task AddToCollectionAsync(Guid collectionId, IEnumerable itemIds); /// /// Removes from collection. /// /// The collection identifier. /// The item ids. - void RemoveFromCollection(Guid collectionId, IEnumerable itemIds); - - void AddToCollection(Guid collectionId, IEnumerable itemIds); - void RemoveFromCollection(Guid collectionId, IEnumerable itemIds); + /// A representing the asynchronous operation. + Task RemoveFromCollectionAsync(Guid collectionId, IEnumerable itemIds); /// /// Collapses the items within box sets. diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index f34309c40..9e595ddc3 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -1390,7 +1390,7 @@ namespace MediaBrowser.Controller.Entities new List(); var ownedItemsChanged = await RefreshedOwnedItems(options, files, cancellationToken).ConfigureAwait(false); - LibraryManager.UpdateImages(this); // ensure all image properties in DB are fresh + await LibraryManager.UpdateImagesAsync(this).ConfigureAwait(false); // ensure all image properties in DB are fresh if (ownedItemsChanged) { @@ -2279,7 +2279,7 @@ namespace MediaBrowser.Controller.Entities /// /// The type. /// The index. - public void DeleteImage(ImageType type, int index) + public async Task DeleteImageAsync(ImageType type, int index) { var info = GetImageInfo(type, index); @@ -2297,7 +2297,7 @@ namespace MediaBrowser.Controller.Entities FileSystem.DeleteFile(info.Path); } - UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None); + await UpdateToRepositoryAsync(ItemUpdateType.ImageUpdate, CancellationToken.None).ConfigureAwait(false); } public void RemoveImage(ItemImageInfo image) @@ -2310,10 +2310,8 @@ namespace MediaBrowser.Controller.Entities ImageInfos = ImageInfos.Except(deletedImages).ToArray(); } - public virtual void UpdateToRepository(ItemUpdateType updateReason, CancellationToken cancellationToken) - { - LibraryManager.UpdateItem(this, GetParent(), updateReason, cancellationToken); - } + public virtual Task UpdateToRepositoryAsync(ItemUpdateType updateReason, CancellationToken cancellationToken) + => LibraryManager.UpdateItemAsync(this, GetParent(), updateReason, cancellationToken); /// /// Validates that images within the item are still on the filesystem. @@ -2558,7 +2556,7 @@ namespace MediaBrowser.Controller.Entities return type == ImageType.Backdrop || type == ImageType.Screenshot || type == ImageType.Chapter; } - public void SwapImages(ImageType type, int index1, int index2) + public Task SwapImagesAsync(ImageType type, int index1, int index2) { if (!AllowsMultipleImages(type)) { @@ -2571,13 +2569,13 @@ namespace MediaBrowser.Controller.Entities if (info1 == null || info2 == null) { // Nothing to do - return; + return Task.CompletedTask; } if (!info1.IsLocalFile || !info2.IsLocalFile) { // TODO: Not supported yet - return; + return Task.CompletedTask; } var path1 = info1.Path; @@ -2594,7 +2592,7 @@ namespace MediaBrowser.Controller.Entities info2.Width = 0; info2.Height = 0; - UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None); + return UpdateToRepositoryAsync(ItemUpdateType.ImageUpdate, CancellationToken.None); } public virtual bool IsPlayed(User user) diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index 6441340f9..11542c1ca 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -350,12 +350,12 @@ namespace MediaBrowser.Controller.Entities if (currentChild.UpdateFromResolvedItem(child) > ItemUpdateType.None) { - currentChild.UpdateToRepository(ItemUpdateType.MetadataImport, cancellationToken); + await currentChild.UpdateToRepositoryAsync(ItemUpdateType.MetadataImport, cancellationToken).ConfigureAwait(false); } else { // metadata is up-to-date; make sure DB has correct images dimensions and hash - LibraryManager.UpdateImages(currentChild); + await LibraryManager.UpdateImagesAsync(currentChild).ConfigureAwait(false); } continue; diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs index b7d7e8e1a..eeff78e10 100644 --- a/MediaBrowser.Controller/Entities/Video.cs +++ b/MediaBrowser.Controller/Entities/Video.cs @@ -495,9 +495,10 @@ namespace MediaBrowser.Controller.Entities } } - public override void UpdateToRepository(ItemUpdateType updateReason, CancellationToken cancellationToken) + /// + public override async Task UpdateToRepositoryAsync(ItemUpdateType updateReason, CancellationToken cancellationToken) { - base.UpdateToRepository(updateReason, cancellationToken); + await base.UpdateToRepositoryAsync(updateReason, cancellationToken).ConfigureAwait(false); var localAlternates = GetLocalAlternateVersionIds() .Select(i => LibraryManager.GetItemById(i)) @@ -514,7 +515,7 @@ namespace MediaBrowser.Controller.Entities item.Genres = Genres; item.ProviderIds = ProviderIds; - item.UpdateToRepository(ItemUpdateType.MetadataDownload, cancellationToken); + await item.UpdateToRepositoryAsync(ItemUpdateType.MetadataDownload, cancellationToken).ConfigureAwait(false); } } diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs index 9abcf2b62..d53b1fc8d 100644 --- a/MediaBrowser.Controller/Library/ILibraryManager.cs +++ b/MediaBrowser.Controller/Library/ILibraryManager.cs @@ -72,6 +72,7 @@ namespace MediaBrowser.Controller.Library /// The name. /// Task{Artist}. MusicArtist GetArtist(string name); + MusicArtist GetArtist(string name, DtoOptions options); /// /// Gets a Studio. @@ -124,7 +125,7 @@ namespace MediaBrowser.Controller.Library /// void QueueLibraryScan(); - void UpdateImages(BaseItem item, bool forceUpdate = false); + Task UpdateImagesAsync(BaseItem item, bool forceUpdate = false); /// /// Gets the default view. @@ -179,6 +180,7 @@ namespace MediaBrowser.Controller.Library /// The sort order. /// IEnumerable{BaseItem}. IEnumerable Sort(IEnumerable items, User user, IEnumerable sortBy, SortOrder sortOrder); + IEnumerable Sort(IEnumerable items, User user, IEnumerable> orderBy); /// @@ -200,9 +202,16 @@ namespace MediaBrowser.Controller.Library /// /// Updates the item. /// - void UpdateItems(IReadOnlyList items, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken); + Task UpdateItemsAsync(IReadOnlyList items, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken); - void UpdateItem(BaseItem item, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken); + /// + /// Updates the item. + /// + /// The item. + /// The parent item. + /// The update reason. + /// The cancellation token. + Task UpdateItemAsync(BaseItem item, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken); /// /// Retrieves the item. @@ -317,7 +326,8 @@ namespace MediaBrowser.Controller.Library /// The name. /// Type of the view. /// Name of the sort. - UserView GetNamedView(string name, + UserView GetNamedView( + string name, string viewType, string sortName); @@ -329,7 +339,8 @@ namespace MediaBrowser.Controller.Library /// Type of the view. /// Name of the sort. /// The unique identifier. - UserView GetNamedView(string name, + UserView GetNamedView( + string name, Guid parentId, string viewType, string sortName, @@ -341,7 +352,8 @@ namespace MediaBrowser.Controller.Library /// The parent. /// Type of the view. /// Name of the sort. - UserView GetShadowView(BaseItem parent, + UserView GetShadowView( + BaseItem parent, string viewType, string sortName); @@ -393,7 +405,9 @@ namespace MediaBrowser.Controller.Library /// The file system children. /// The directory service. /// IEnumerable<Trailer>. - IEnumerable