diff options
Diffstat (limited to 'MediaBrowser.MediaEncoding/Encoder')
| -rw-r--r-- | MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs | 32 | ||||
| -rw-r--r-- | MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs | 11 | ||||
| -rw-r--r-- | MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 91 |
3 files changed, 78 insertions, 56 deletions
diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index 5a7fabf84..0e9a5e502 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; @@ -102,6 +104,7 @@ namespace MediaBrowser.MediaEncoding.Encoder // $ ffmpeg -version | perl -ne ' print "$1=$2.$3," if /^(lib\w+)\s+(\d+)\.\s*(\d+)/' private static readonly IReadOnlyDictionary<string, Version> _ffmpegVersionMap = new Dictionary<string, Version> { + { "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) }, @@ -121,6 +124,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; @@ -207,12 +216,12 @@ namespace MediaBrowser.MediaEncoding.Encoder /// 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. /// </summary> - /// <param name="output"></param> - /// <returns></returns> + /// <param name="output">The output from "ffmpeg -version".</param> + /// <returns>The FFmpeg version.</returns> 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+\.?)+)"); + var match = Regex.Match(output, @"^ffmpeg version n?((?:[0-9]+\.?)+)"); if (match.Success) { @@ -268,11 +277,10 @@ namespace MediaBrowser.MediaEncoding.Encoder /// <summary> /// 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.". /// </summary> - /// <param name="output"></param> - /// <param name="versionString"></param> - /// <param name="versionMap"></param> - /// <returns></returns> + /// <param name="output">The 'ffmpeg -version' output.</param> + /// <returns>The library names and major.minor version numbers.</returns> private static bool TryGetFFmpegLibraryVersions(string output, out string versionString, out IReadOnlyDictionary<string, Version> versionMap) { var sb = new StringBuilder(144); @@ -281,7 +289,7 @@ namespace MediaBrowser.MediaEncoding.Encoder foreach (Match match in Regex.Matches( output, - @"((?<name>lib\w+)\s+(?<major>\d+)\.\s*(?<minor>\d+))", + @"((?<name>lib\w+)\s+(?<major>[0-9]+)\.\s*(?<minor>[0-9]+))", RegexOptions.Multiline)) { sb.Append(match.Groups["name"]) @@ -304,12 +312,6 @@ namespace MediaBrowser.MediaEncoding.Encoder return sb.Length > 0; } - private enum Codec - { - Encoder, - Decoder - } - private IEnumerable<string> GetHwaccelTypes() { string output = null; @@ -327,7 +329,7 @@ namespace MediaBrowser.MediaEncoding.Encoder return Enumerable.Empty<string>(); } - 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; diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs index d4aede572..082ae2888 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs @@ -1,4 +1,7 @@ +#pragma warning disable CS1591 + using System.Collections.Generic; +using System.Globalization; using System.Linq; using MediaBrowser.Model.MediaInfo; @@ -12,7 +15,7 @@ namespace MediaBrowser.MediaEncoding.Encoder { var url = inputFiles[0]; - return string.Format("\"{0}\"", url); + return string.Format(CultureInfo.InvariantCulture, "\"{0}\"", url); } return GetConcatInputArgument(inputFiles); @@ -31,7 +34,7 @@ namespace MediaBrowser.MediaEncoding.Encoder { var files = string.Join("|", inputFiles.Select(NormalizePath)); - return string.Format("concat:\"{0}\"", files); + return string.Format(CultureInfo.InvariantCulture, "concat:\"{0}\"", files); } // Determine the input path for video files @@ -47,13 +50,13 @@ namespace MediaBrowser.MediaEncoding.Encoder { if (path.IndexOf("://") != -1) { - return string.Format("\"{0}\"", path); + return string.Format(CultureInfo.InvariantCulture, "\"{0}\"", path); } // Quotes are valid path characters in linux and they need to be escaped here with a leading \ path = NormalizePath(path); - return string.Format("file:\"{0}\"", path); + return string.Format(CultureInfo.InvariantCulture, "file:\"{0}\"", path); } /// <summary> diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index a1a7f55c3..bdf2babfa 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -1,5 +1,8 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; +using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; @@ -19,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 { @@ -35,6 +37,11 @@ namespace MediaBrowser.MediaEncoding.Encoder /// </summary> internal const int DefaultImageExtractionTimeout = 5000; + /// <summary> + /// The us culture. + /// </summary> + private readonly CultureInfo _usCulture = new CultureInfo("en-US"); + private readonly ILogger<MediaEncoder> _logger; private readonly IServerConfigurationManager _configurationManager; private readonly IFileSystem _fileSystem; @@ -47,6 +54,10 @@ namespace MediaBrowser.MediaEncoding.Encoder private readonly object _runningProcessesLock = new object(); private readonly List<ProcessWrapper> _runningProcesses = new List<ProcessWrapper>(); + private List<string> _encoders = new List<string>(); + private List<string> _decoders = new List<string>(); + private List<string> _hwaccels = new List<string>(); + private string _ffmpegPath = string.Empty; private string _ffprobePath; @@ -77,7 +88,7 @@ namespace MediaBrowser.MediaEncoding.Encoder /// <summary> /// 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. /// </summary> public void SetFFmpegPath() { @@ -122,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. /// </summary> - /// <param name="path"></param> - /// <param name="pathType"></param> + /// <param name="path">The path.</param> + /// <param name="pathType">The path type.</param> public void UpdateEncoderPath(string path, string pathType) { string newPath; @@ -168,8 +179,8 @@ namespace MediaBrowser.MediaEncoding.Encoder /// If checks pass, global variable FFmpegPath and EncoderLocation are updated. /// </summary> /// <param name="path">FQPN to test.</param> - /// <param name="location">Location (External, Custom, System) of tool</param> - /// <returns></returns> + /// <param name="location">Location (External, Custom, System) of tool.</param> + /// <returns><c>true</c> if the version validation succeeded; otherwise, <c>false</c>.</returns> private bool ValidatePath(string path, FFmpegLocation location) { bool rc = false; @@ -218,8 +229,8 @@ namespace MediaBrowser.MediaEncoding.Encoder /// <summary> /// Search the system $PATH environment variable looking for given filename. /// </summary> - /// <param name="fileName"></param> - /// <returns></returns> + /// <param name="fileName">The filename.</param> + /// <returns>The full path to the file.</returns> private string ExistsOnSystemPath(string fileName) { var inJellyfinPath = GetEncoderPathFromDirectory(AppContext.BaseDirectory, fileName, recursive: true); @@ -243,25 +254,19 @@ namespace MediaBrowser.MediaEncoding.Encoder return null; } - private List<string> _encoders = new List<string>(); public void SetAvailableEncoders(IEnumerable<string> list) { _encoders = list.ToList(); - // _logger.Info("Supported encoders: {0}", string.Join(",", list.ToArray())); } - private List<string> _decoders = new List<string>(); public void SetAvailableDecoders(IEnumerable<string> list) { _decoders = list.ToList(); - // _logger.Info("Supported decoders: {0}", string.Join(",", list.ToArray())); } - private List<string> _hwaccels = new List<string>(); public void SetAvailableHwaccels(IEnumerable<string> list) { _hwaccels = list.ToList(); - //_logger.Info("Supported hwaccels: {0}", string.Join(",", list.ToArray())); } public bool SupportsEncoder(string encoder) @@ -329,8 +334,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); } /// <summary> @@ -339,7 +352,7 @@ namespace MediaBrowser.MediaEncoding.Encoder /// <param name="inputFiles">The input files.</param> /// <param name="protocol">The protocol.</param> /// <returns>System.String.</returns> - /// <exception cref="ArgumentException">Unrecognized InputType</exception> + /// <exception cref="ArgumentException">Unrecognized InputType.</exception> public string GetInputArgument(IReadOnlyList<string> inputFiles, MediaProtocol protocol) => EncodingUtils.GetInputArgument(inputFiles, protocol); @@ -347,7 +360,8 @@ namespace MediaBrowser.MediaEncoding.Encoder /// Gets the media info internal. /// </summary> /// <returns>Task{MediaInfoResult}.</returns> - private async Task<MediaInfo> GetMediaInfoInternal(string inputPath, + private async Task<MediaInfo> GetMediaInfoInternal( + string inputPath, string primaryPath, MediaProtocol protocol, bool extractChapters, @@ -375,7 +389,6 @@ namespace MediaBrowser.MediaEncoding.Encoder FileName = _ffprobePath, Arguments = args, - WindowStyle = ProcessWindowStyle.Hidden, ErrorDialog = false, }, @@ -436,11 +449,6 @@ namespace MediaBrowser.MediaEncoding.Encoder } } - /// <summary> - /// The us culture. - /// </summary> - protected readonly CultureInfo UsCulture = new CultureInfo("en-US"); - public Task<string> ExtractAudioImage(string path, int? imageStreamIndex, CancellationToken cancellationToken) { return ExtractImage(new[] { path }, null, null, imageStreamIndex, MediaProtocol.File, true, null, null, cancellationToken); @@ -456,8 +464,16 @@ namespace MediaBrowser.MediaEncoding.Encoder return ExtractImage(inputFiles, container, imageStream, imageStreamIndex, protocol, false, null, null, cancellationToken); } - private async Task<string> ExtractImage(string[] inputFiles, string container, MediaStream videoStream, int? imageStreamIndex, MediaProtocol protocol, bool isAudio, - Video3DFormat? threedFormat, TimeSpan? offset, CancellationToken cancellationToken) + private async Task<string> ExtractImage( + string[] inputFiles, + string container, + MediaStream videoStream, + int? imageStreamIndex, + MediaProtocol protocol, + bool isAudio, + Video3DFormat? threedFormat, + TimeSpan? offset, + CancellationToken cancellationToken) { var inputArgument = GetInputArgument(inputFiles, protocol); @@ -533,8 +549,8 @@ namespace MediaBrowser.MediaEncoding.Encoder // Use ffmpeg to sample 100 (we can drop this if required using thumbnail=50 for 50 frames) frames and pick the best thumbnail. Have a fall back just in case. var thumbnail = enableThumbnail ? ",thumbnail=24" : string.Empty; - var args = useIFrame ? string.Format("-i {0}{3} -threads 0 -v quiet -vframes 1 -vf \"{2}{4}\" -f image2 \"{1}\"", inputPath, tempExtractPath, vf, mapArg, thumbnail) : - string.Format("-i {0}{3} -threads 0 -v quiet -vframes 1 -vf \"{2}\" -f image2 \"{1}\"", inputPath, tempExtractPath, vf, mapArg); + var args = useIFrame ? string.Format(CultureInfo.InvariantCulture, "-i {0}{3} -threads 0 -v quiet -vframes 1 -vf \"{2}{4}\" -f image2 \"{1}\"", inputPath, tempExtractPath, vf, mapArg, thumbnail) : + string.Format(CultureInfo.InvariantCulture, "-i {0}{3} -threads 0 -v quiet -vframes 1 -vf \"{2}\" -f image2 \"{1}\"", inputPath, tempExtractPath, vf, mapArg); var probeSizeArgument = EncodingHelper.GetProbeSizeArgument(1); var analyzeDurationArgument = EncodingHelper.GetAnalyzeDurationArgument(1); @@ -551,7 +567,7 @@ namespace MediaBrowser.MediaEncoding.Encoder if (offset.HasValue) { - args = string.Format("-ss {0} ", GetTimeParameter(offset.Value)) + args; + args = string.Format(CultureInfo.InvariantCulture, "-ss {0} ", GetTimeParameter(offset.Value)) + args; } if (videoStream != null) @@ -622,7 +638,7 @@ namespace MediaBrowser.MediaEncoding.Encoder if (exitCode == -1 || !file.Exists || file.Length == 0) { - var msg = string.Format("ffmpeg image extraction failed for {0}", inputPath); + var msg = string.Format(CultureInfo.InvariantCulture, "ffmpeg image extraction failed for {0}", inputPath); _logger.LogError(msg); @@ -642,7 +658,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( @@ -659,19 +675,19 @@ 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); + vf += string.Format(CultureInfo.InvariantCulture, ",scale=min(iw\\,{0}):trunc(ow/dar/2)*2", maxWidthParam); } Directory.CreateDirectory(targetDirectory); var outputPath = Path.Combine(targetDirectory, filenamePrefix + "%05d.jpg"); - var args = string.Format("-i {0} -threads 0 -v quiet -vf \"{2}\" -f image2 \"{1}\"", inputArgument, outputPath, vf); + var args = string.Format(CultureInfo.InvariantCulture, "-i {0} -threads 0 -v quiet -vf \"{2}\" -f image2 \"{1}\"", inputArgument, outputPath, vf); var probeSizeArgument = EncodingHelper.GetProbeSizeArgument(1); var analyzeDurationArgument = EncodingHelper.GetAnalyzeDurationArgument(1); @@ -771,7 +787,7 @@ namespace MediaBrowser.MediaEncoding.Encoder if (exitCode == -1) { - var msg = string.Format("ffmpeg image extraction failed for {0}", inputArgument); + var msg = string.Format(CultureInfo.InvariantCulture, "ffmpeg image extraction failed for {0}", inputArgument); _logger.LogError(msg); @@ -856,6 +872,7 @@ namespace MediaBrowser.MediaEncoding.Encoder if (dispose) { StopProcesses(); + _thumbnailResourcePool.Dispose(); } } |
