diff options
Diffstat (limited to 'MediaBrowser.MediaEncoding/Encoder')
| -rw-r--r-- | MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs | 7 | ||||
| -rw-r--r-- | MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs | 29 | ||||
| -rw-r--r-- | MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs | 16 | ||||
| -rw-r--r-- | MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 141 | ||||
| -rw-r--r-- | MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs | 13 |
5 files changed, 179 insertions, 27 deletions
diff --git a/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs index 2d5225344..cd1575fa5 100644 --- a/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs @@ -6,6 +6,7 @@ using MediaBrowser.Model.IO; using MediaBrowser.Model.Logging; using System; using System.Collections.Generic; +using System.Threading.Tasks; using CommonIO; namespace MediaBrowser.MediaEncoding.Encoder @@ -16,7 +17,7 @@ namespace MediaBrowser.MediaEncoding.Encoder { } - protected override string GetCommandLineArguments(EncodingJob state) + protected override Task<string> GetCommandLineArguments(EncodingJob state) { var audioTranscodeParams = new List<string>(); @@ -61,7 +62,7 @@ namespace MediaBrowser.MediaEncoding.Encoder vn = " -vn"; } - return string.Format("{0} {1}{6}{7} -threads {2}{3} {4} -id3v2_version 3 -write_id3v1 1{8} -y \"{5}\"", + var result = string.Format("{0} {1}{6}{7} -threads {2}{3} {4} -id3v2_version 3 -write_id3v1 1{8} -y \"{5}\"", inputModifier, GetInputArgument(state), threads, @@ -71,6 +72,8 @@ namespace MediaBrowser.MediaEncoding.Encoder albumCoverInput, mapArgs, metadata).Trim(); + + return Task.FromResult(result); } protected override string GetOutputFileExtension(EncodingJob state) diff --git a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs index dd227952e..22f739801 100644 --- a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs @@ -42,8 +42,8 @@ namespace MediaBrowser.MediaEncoding.Encoder IFileSystem fileSystem, IIsoManager isoManager, ILibraryManager libraryManager, - ISessionManager sessionManager, - ISubtitleEncoder subtitleEncoder, + ISessionManager sessionManager, + ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager) { MediaEncoder = mediaEncoder; @@ -71,7 +71,7 @@ namespace MediaBrowser.MediaEncoding.Encoder await AcquireResources(encodingJob, cancellationToken).ConfigureAwait(false); - var commandLineArgs = GetCommandLineArguments(encodingJob); + var commandLineArgs = await GetCommandLineArguments(encodingJob).ConfigureAwait(false); var process = new Process { @@ -131,7 +131,7 @@ namespace MediaBrowser.MediaEncoding.Encoder } cancellationToken.Register(() => Cancel(process, encodingJob)); - + // MUST read both stdout and stderr asynchronously or a deadlock may occurr process.BeginOutputReadLine(); @@ -139,7 +139,7 @@ namespace MediaBrowser.MediaEncoding.Encoder new JobLogger(Logger).StartStreamingLog(encodingJob, process.StandardError.BaseStream, encodingJob.LogFileStream); // Wait for the file to exist before proceeeding - while (!FileSystem.FileExists(encodingJob.OutputFilePath) && !encodingJob.HasExited) + while (!FileSystem.FileExists(encodingJob.OutputFilePath) && !encodingJob.HasExited) { await Task.Delay(100, cancellationToken).ConfigureAwait(false); } @@ -264,11 +264,11 @@ namespace MediaBrowser.MediaEncoding.Encoder return ConfigurationManager.GetConfiguration<EncodingOptions>("encoding"); } - protected abstract string GetCommandLineArguments(EncodingJob job); + protected abstract Task<string> GetCommandLineArguments(EncodingJob job); private string GetOutputFilePath(EncodingJob state) { - var folder = string.IsNullOrWhiteSpace(state.Options.OutputDirectory) ? + var folder = string.IsNullOrWhiteSpace(state.Options.OutputDirectory) ? ConfigurationManager.ApplicationPaths.TranscodingTempPath : state.Options.OutputDirectory; @@ -363,7 +363,7 @@ namespace MediaBrowser.MediaEncoding.Encoder { if (string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)) { - return null; + return null; } if (state.VideoStream != null && !string.IsNullOrWhiteSpace(state.VideoStream.Codec)) @@ -536,7 +536,7 @@ namespace MediaBrowser.MediaEncoding.Encoder /// <param name="state">The state.</param> /// <param name="outputVideoCodec">The output video codec.</param> /// <returns>System.String.</returns> - protected string GetGraphicalSubtitleParam(EncodingJob state, string outputVideoCodec) + protected async Task<string> GetGraphicalSubtitleParam(EncodingJob state, string outputVideoCodec) { var outputSizeParam = string.Empty; @@ -545,7 +545,8 @@ namespace MediaBrowser.MediaEncoding.Encoder // Add resolution params, if specified if (request.Width.HasValue || request.Height.HasValue || request.MaxHeight.HasValue || request.MaxWidth.HasValue) { - outputSizeParam = GetOutputSizeParam(state, outputVideoCodec).TrimEnd('"'); + outputSizeParam = await GetOutputSizeParam(state, outputVideoCodec).ConfigureAwait(false); + outputSizeParam = outputSizeParam.TrimEnd('"'); outputSizeParam = "," + outputSizeParam.Substring(outputSizeParam.IndexOf("scale", StringComparison.OrdinalIgnoreCase)); } @@ -858,7 +859,7 @@ namespace MediaBrowser.MediaEncoding.Encoder /// <param name="outputVideoCodec">The output video codec.</param> /// <param name="allowTimeStampCopy">if set to <c>true</c> [allow time stamp copy].</param> /// <returns>System.String.</returns> - protected string GetOutputSizeParam(EncodingJob state, + protected async Task<string> GetOutputSizeParam(EncodingJob state, string outputVideoCodec, bool allowTimeStampCopy = true) { @@ -935,7 +936,7 @@ namespace MediaBrowser.MediaEncoding.Encoder if (state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.Options.SubtitleMethod == SubtitleDeliveryMethod.Encode) { - var subParam = GetTextSubtitleParam(state); + var subParam = await GetTextSubtitleParam(state).ConfigureAwait(false); filters.Add(subParam); @@ -958,7 +959,7 @@ namespace MediaBrowser.MediaEncoding.Encoder /// </summary> /// <param name="state">The state.</param> /// <returns>System.String.</returns> - protected string GetTextSubtitleParam(EncodingJob state) + protected async Task<string> GetTextSubtitleParam(EncodingJob state) { var seconds = Math.Round(TimeSpan.FromTicks(state.Options.StartTimeTicks ?? 0).TotalSeconds); @@ -970,7 +971,7 @@ namespace MediaBrowser.MediaEncoding.Encoder if (!string.IsNullOrEmpty(state.SubtitleStream.Language)) { - var charenc = SubtitleEncoder.GetSubtitleFileCharacterSet(subtitlePath, state.SubtitleStream.Language, state.MediaSource.Protocol, CancellationToken.None).Result; + var charenc = await SubtitleEncoder.GetSubtitleFileCharacterSet(subtitlePath, state.SubtitleStream.Language, state.MediaSource.Protocol, CancellationToken.None).ConfigureAwait(false); if (!string.IsNullOrEmpty(charenc)) { diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs index 1544a78b6..7fe7facdf 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs @@ -99,7 +99,7 @@ namespace MediaBrowser.MediaEncoding.Encoder if (videoRequest != null) { state.OutputVideoCodec = state.Options.VideoCodec; - state.OutputVideoBitrate = GetVideoBitrateParamValue(state.Options, state.VideoStream); + state.OutputVideoBitrate = GetVideoBitrateParamValue(state.Options, state.VideoStream, state.OutputVideoCodec); if (state.OutputVideoBitrate.HasValue) { @@ -396,7 +396,7 @@ namespace MediaBrowser.MediaEncoding.Encoder return request.AudioChannels; } - private int? GetVideoBitrateParamValue(EncodingJobOptions request, MediaStream videoStream) + private int? GetVideoBitrateParamValue(EncodingJobOptions request, MediaStream videoStream, string outputVideoCodec) { var bitrate = request.VideoBitRate; @@ -421,6 +421,18 @@ namespace MediaBrowser.MediaEncoding.Encoder } } + if (bitrate.HasValue) + { + var inputVideoCodec = videoStream == null ? null : videoStream.Codec; + bitrate = ResolutionNormalizer.ScaleBitrate(bitrate.Value, inputVideoCodec, outputVideoCodec); + + // If a max bitrate was requested, don't let the scaled bitrate exceed it + if (request.VideoBitRate.HasValue) + { + bitrate = Math.Min(bitrate.Value, request.VideoBitRate.Value); + } + } + return bitrate; } diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 399fdead9..f8321f6cd 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -21,6 +21,9 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using CommonIO; +using MediaBrowser.Model.Configuration; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Extensions; namespace MediaBrowser.MediaEncoding.Encoder { @@ -64,8 +67,6 @@ namespace MediaBrowser.MediaEncoding.Encoder public string FFProbePath { get; private set; } - public string Version { get; private set; } - protected readonly IServerConfigurationManager ConfigurationManager; protected readonly IFileSystem FileSystem; protected readonly ILiveTvManager LiveTvManager; @@ -77,12 +78,12 @@ namespace MediaBrowser.MediaEncoding.Encoder protected readonly Func<IMediaSourceManager> MediaSourceManager; private readonly List<ProcessWrapper> _runningProcesses = new List<ProcessWrapper>(); + private readonly bool _hasExternalEncoder; - public MediaEncoder(ILogger logger, IJsonSerializer jsonSerializer, string ffMpegPath, string ffProbePath, string version, IServerConfigurationManager configurationManager, IFileSystem fileSystem, ILiveTvManager liveTvManager, IIsoManager isoManager, ILibraryManager libraryManager, IChannelManager channelManager, ISessionManager sessionManager, Func<ISubtitleEncoder> subtitleEncoder, Func<IMediaSourceManager> mediaSourceManager) + public MediaEncoder(ILogger logger, IJsonSerializer jsonSerializer, string ffMpegPath, string ffProbePath, bool hasExternalEncoder, IServerConfigurationManager configurationManager, IFileSystem fileSystem, ILiveTvManager liveTvManager, IIsoManager isoManager, ILibraryManager libraryManager, IChannelManager channelManager, ISessionManager sessionManager, Func<ISubtitleEncoder> subtitleEncoder, Func<IMediaSourceManager> mediaSourceManager) { _logger = logger; _jsonSerializer = jsonSerializer; - Version = version; ConfigurationManager = configurationManager; FileSystem = fileSystem; LiveTvManager = liveTvManager; @@ -94,6 +95,138 @@ namespace MediaBrowser.MediaEncoding.Encoder MediaSourceManager = mediaSourceManager; FFProbePath = ffProbePath; FFMpegPath = ffMpegPath; + + _hasExternalEncoder = hasExternalEncoder; + } + + public void Init() + { + ConfigureEncoderPaths(); + + if (_hasExternalEncoder) + { + LogPaths(); + return; + } + + // If the path was passed in, save it into config now. + var encodingOptions = GetEncodingOptions(); + var appPath = encodingOptions.EncoderAppPath; + if (!string.IsNullOrWhiteSpace(FFMpegPath) && !string.Equals(FFMpegPath, appPath, StringComparison.Ordinal)) + { + encodingOptions.EncoderAppPath = FFMpegPath; + ConfigurationManager.SaveConfiguration("encoding", encodingOptions); + } + } + + public async Task UpdateEncoderPath(string path) + { + if (string.IsNullOrWhiteSpace(path)) + { + throw new ArgumentNullException("path"); + } + + if (!File.Exists(path) && !Directory.Exists(path)) + { + throw new ResourceNotFoundException(); + } + + var newPaths = GetEncoderPaths(path); + if (string.IsNullOrWhiteSpace(newPaths.Item1)) + { + throw new ResourceNotFoundException("ffmpeg not found"); + } + if (string.IsNullOrWhiteSpace(newPaths.Item2)) + { + throw new ResourceNotFoundException("ffprobe not found"); + } + + var config = GetEncodingOptions(); + config.EncoderAppPath = path; + ConfigurationManager.SaveConfiguration("encoding", config); + + Init(); + } + + private void ConfigureEncoderPaths() + { + if (_hasExternalEncoder) + { + return; + } + + var appPath = GetEncodingOptions().EncoderAppPath; + + if (string.IsNullOrWhiteSpace(appPath)) + { + appPath = Path.Combine(ConfigurationManager.ApplicationPaths.ProgramDataPath, "ffmpeg"); + } + var newPaths = GetEncoderPaths(appPath); + + if (!string.IsNullOrWhiteSpace(newPaths.Item1) && !string.IsNullOrWhiteSpace(newPaths.Item2)) + { + FFMpegPath = newPaths.Item1; + FFProbePath = newPaths.Item2; + } + + LogPaths(); + } + + private Tuple<string, string> GetEncoderPaths(string configuredPath) + { + var appPath = configuredPath; + + if (!string.IsNullOrWhiteSpace(appPath)) + { + if (Directory.Exists(appPath)) + { + return GetPathsFromDirectory(appPath); + } + + if (File.Exists(appPath)) + { + return new Tuple<string, string>(appPath, GetProbePathFromEncoderPath(appPath)); + } + } + + return new Tuple<string, string>(null, null); + } + + private Tuple<string,string> GetPathsFromDirectory(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 = Directory.GetFiles(path); + + var ffmpegPath = files.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffmpeg", StringComparison.OrdinalIgnoreCase)); + var ffprobePath = files.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffprobe", StringComparison.OrdinalIgnoreCase)); + + if (string.IsNullOrWhiteSpace(ffmpegPath) || !File.Exists(ffmpegPath)) + { + files = Directory.GetFiles(path, "*", SearchOption.AllDirectories); + + ffmpegPath = files.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffmpeg", StringComparison.OrdinalIgnoreCase)); + ffprobePath = GetProbePathFromEncoderPath(ffmpegPath); + } + + return new Tuple<string, string>(ffmpegPath, ffprobePath); + } + + private string GetProbePathFromEncoderPath(string appPath) + { + return Directory.GetFiles(Path.GetDirectoryName(appPath)) + .FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffprobe", StringComparison.OrdinalIgnoreCase)); + } + + private void LogPaths() + { + _logger.Info("FFMpeg: {0}", FFMpegPath ?? "not found"); + _logger.Info("FFProbe: {0}", FFProbePath ?? "not found"); + } + + private EncodingOptions GetEncodingOptions() + { + return ConfigurationManager.GetConfiguration<EncodingOptions>("encoding"); } private List<string> _encoders = new List<string>(); diff --git a/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs index 3ab55168d..82a966821 100644 --- a/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs @@ -7,6 +7,7 @@ using MediaBrowser.Model.IO; using MediaBrowser.Model.Logging; using System; using System.IO; +using System.Threading.Tasks; using CommonIO; namespace MediaBrowser.MediaEncoding.Encoder @@ -17,7 +18,7 @@ namespace MediaBrowser.MediaEncoding.Encoder { } - protected override string GetCommandLineArguments(EncodingJob state) + protected override async Task<string> GetCommandLineArguments(EncodingJob state) { // Get the output codec name var videoCodec = EncodingJobFactory.GetVideoEncoder(state, GetEncodingOptions()); @@ -36,12 +37,14 @@ namespace MediaBrowser.MediaEncoding.Encoder var inputModifier = GetInputModifier(state); + var videoArguments = await GetVideoArguments(state, videoCodec).ConfigureAwait(false); + return string.Format("{0} {1}{2} {3} {4} -map_metadata -1 -threads {5} {6}{7} -y \"{8}\"", inputModifier, GetInputArgument(state), keyFrame, GetMapArgs(state), - GetVideoArguments(state, videoCodec), + videoArguments, threads, GetAudioArguments(state), format, @@ -55,7 +58,7 @@ namespace MediaBrowser.MediaEncoding.Encoder /// <param name="state">The state.</param> /// <param name="videoCodec">The video codec.</param> /// <returns>System.String.</returns> - private string GetVideoArguments(EncodingJob state, string videoCodec) + private async Task<string> GetVideoArguments(EncodingJob state, string videoCodec) { var args = "-codec:v:0 " + videoCodec; @@ -91,7 +94,7 @@ namespace MediaBrowser.MediaEncoding.Encoder // Add resolution params, if specified if (!hasGraphicalSubs) { - args += GetOutputSizeParam(state, videoCodec); + args += await GetOutputSizeParam(state, videoCodec).ConfigureAwait(false); } var qualityParam = GetVideoQualityParam(state, videoCodec); @@ -104,7 +107,7 @@ namespace MediaBrowser.MediaEncoding.Encoder // This is for internal graphical subs if (hasGraphicalSubs) { - args += GetGraphicalSubtitleParam(state, videoCodec); + args += await GetGraphicalSubtitleParam(state, videoCodec).ConfigureAwait(false); } return args; |
