diff options
Diffstat (limited to 'MediaBrowser.MediaEncoding/Encoder')
| -rw-r--r-- | MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs | 4 | ||||
| -rw-r--r-- | MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs | 8 | ||||
| -rw-r--r-- | MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs | 1 | ||||
| -rw-r--r-- | MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs | 8 | ||||
| -rw-r--r-- | MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs | 11 | ||||
| -rw-r--r-- | MediaBrowser.MediaEncoding/Encoder/JobLogger.cs | 122 | ||||
| -rw-r--r-- | MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 162 | ||||
| -rw-r--r-- | MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs | 135 |
8 files changed, 25 insertions, 426 deletions
diff --git a/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs index a4db8f3b8..05b3ca5fc 100644 --- a/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs @@ -17,7 +17,7 @@ namespace MediaBrowser.MediaEncoding.Encoder { } - protected override Task<string> GetCommandLineArguments(EncodingJob state) + protected override string GetCommandLineArguments(EncodingJob state) { var audioTranscodeParams = new List<string>(); @@ -78,7 +78,7 @@ namespace MediaBrowser.MediaEncoding.Encoder mapArgs, metadata).Trim(); - return Task.FromResult(result); + return result; } protected override string GetOutputFileExtension(EncodingJob state) diff --git a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs index b0b37f2d6..b6e695f1b 100644 --- a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs @@ -66,7 +66,7 @@ namespace MediaBrowser.MediaEncoding.Encoder IProgress<double> progress, CancellationToken cancellationToken) { - var encodingJob = await new EncodingJobFactory(Logger, LibraryManager, MediaSourceManager, ConfigurationManager) + var encodingJob = await new EncodingJobFactory(Logger, LibraryManager, MediaSourceManager, ConfigurationManager, MediaEncoder) .CreateJob(options, EncodingHelper, IsVideoEncoder, progress, cancellationToken).ConfigureAwait(false); encodingJob.OutputFilePath = GetOutputFilePath(encodingJob); @@ -76,7 +76,7 @@ namespace MediaBrowser.MediaEncoding.Encoder await AcquireResources(encodingJob, cancellationToken).ConfigureAwait(false); - var commandLineArgs = await GetCommandLineArguments(encodingJob).ConfigureAwait(false); + var commandLineArgs = GetCommandLineArguments(encodingJob); var process = ProcessFactory.Create(new ProcessOptions { @@ -242,7 +242,7 @@ namespace MediaBrowser.MediaEncoding.Encoder private void OnTranscodeBeginning(EncodingJob job) { - job.ReportTranscodingProgress(null, null, null, null); + job.ReportTranscodingProgress(null, null, null, null, null); } private void OnTranscodeFailedToStart(string path, EncodingJob job) @@ -265,7 +265,7 @@ namespace MediaBrowser.MediaEncoding.Encoder return ConfigurationManager.GetConfiguration<EncodingOptions>("encoding"); } - protected abstract Task<string> GetCommandLineArguments(EncodingJob job); + protected abstract string GetCommandLineArguments(EncodingJob job); private string GetOutputFilePath(EncodingJob state) { diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index 9c1189f6c..15221c2ac 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -89,6 +89,7 @@ namespace MediaBrowser.MediaEncoding.Encoder var found = new List<string>(); var required = new[] { + "mpeg2video", "h264_qsv", "hevc_qsv", "mpeg2_qsv", diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs index f6895696a..883709de8 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs @@ -36,7 +36,6 @@ namespace MediaBrowser.MediaEncoding.Encoder public string MimeType { get; set; } public bool EstimateContentLength { get; set; } - public bool EnableMpegtsM2TsMode { get; set; } public TranscodeSeekInfo TranscodeSeekInfo { get; set; } public long? EncodingDurationTicks { get; set; } public string LiveStreamId { get; set; } @@ -109,7 +108,6 @@ namespace MediaBrowser.MediaEncoding.Encoder } public string OutputFilePath { get; set; } - public int? OutputAudioBitrate; public string ActualOutputVideoCodec { @@ -379,7 +377,7 @@ namespace MediaBrowser.MediaEncoding.Encoder return count; } - public void ReportTranscodingProgress(TimeSpan? transcodingPosition, float? framerate, double? percentComplete, long? bytesTranscoded) + public override void ReportTranscodingProgress(TimeSpan? transcodingPosition, float? framerate, double? percentComplete, long? bytesTranscoded, int? bitRate) { var ticks = transcodingPosition.HasValue ? transcodingPosition.Value.Ticks : (long?)null; @@ -387,8 +385,8 @@ namespace MediaBrowser.MediaEncoding.Encoder if (!percentComplete.HasValue && ticks.HasValue && RunTimeTicks.HasValue) { - var pct = ticks.Value/RunTimeTicks.Value; - percentComplete = pct*100; + var pct = ticks.Value / RunTimeTicks.Value; + percentComplete = pct * 100; } if (percentComplete.HasValue) diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs index 4b336e671..3e99d68ce 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs @@ -22,15 +22,17 @@ namespace MediaBrowser.MediaEncoding.Encoder private readonly ILibraryManager _libraryManager; private readonly IMediaSourceManager _mediaSourceManager; private readonly IConfigurationManager _config; + private readonly IMediaEncoder _mediaEncoder; protected static readonly CultureInfo UsCulture = new CultureInfo("en-US"); - public EncodingJobFactory(ILogger logger, ILibraryManager libraryManager, IMediaSourceManager mediaSourceManager, IConfigurationManager config) + public EncodingJobFactory(ILogger logger, ILibraryManager libraryManager, IMediaSourceManager mediaSourceManager, IConfigurationManager config, IMediaEncoder mediaEncoder) { _logger = logger; _libraryManager = libraryManager; _mediaSourceManager = mediaSourceManager; _config = config; + _mediaEncoder = mediaEncoder; } public async Task<EncodingJob> CreateJob(EncodingJobOptions options, EncodingHelper encodingHelper, bool isVideoRequest, IProgress<double> progress, CancellationToken cancellationToken) @@ -61,6 +63,13 @@ namespace MediaBrowser.MediaEncoding.Encoder request.AudioCodec = state.SupportedAudioCodecs.FirstOrDefault(); } + if (!string.IsNullOrWhiteSpace(request.SubtitleCodec)) + { + state.SupportedSubtitleCodecs = request.SubtitleCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList(); + request.SubtitleCodec = state.SupportedSubtitleCodecs.FirstOrDefault(i => _mediaEncoder.CanEncodeToSubtitleCodec(i)) + ?? state.SupportedSubtitleCodecs.FirstOrDefault(); + } + var item = _libraryManager.GetItemById(request.ItemId); state.ItemType = item.GetType().Name; diff --git a/MediaBrowser.MediaEncoding/Encoder/JobLogger.cs b/MediaBrowser.MediaEncoding/Encoder/JobLogger.cs deleted file mode 100644 index cb6e58f17..000000000 --- a/MediaBrowser.MediaEncoding/Encoder/JobLogger.cs +++ /dev/null @@ -1,122 +0,0 @@ -using MediaBrowser.Model.Extensions; -using MediaBrowser.Model.Logging; -using System; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Text; - -namespace MediaBrowser.MediaEncoding.Encoder -{ - public class JobLogger - { - private readonly CultureInfo _usCulture = new CultureInfo("en-US"); - private readonly ILogger _logger; - - public JobLogger(ILogger logger) - { - _logger = logger; - } - - public async void StartStreamingLog(EncodingJob transcodingJob, Stream source, Stream target) - { - try - { - using (var reader = new StreamReader(source)) - { - while (!reader.EndOfStream) - { - var line = await reader.ReadLineAsync().ConfigureAwait(false); - - ParseLogLine(line, transcodingJob); - - var bytes = Encoding.UTF8.GetBytes(Environment.NewLine + line); - - await target.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false); - } - } - } - catch (Exception ex) - { - _logger.ErrorException("Error reading ffmpeg log", ex); - } - } - - private void ParseLogLine(string line, EncodingJob transcodingJob) - { - float? framerate = null; - double? percent = null; - TimeSpan? transcodingPosition = null; - long? bytesTranscoded = null; - - var parts = line.Split(' '); - - var totalMs = transcodingJob.RunTimeTicks.HasValue - ? TimeSpan.FromTicks(transcodingJob.RunTimeTicks.Value).TotalMilliseconds - : 0; - - var startMs = transcodingJob.Options.StartTimeTicks.HasValue - ? TimeSpan.FromTicks(transcodingJob.Options.StartTimeTicks.Value).TotalMilliseconds - : 0; - - for (var i = 0; i < parts.Length; i++) - { - var part = parts[i]; - - if (string.Equals(part, "fps=", StringComparison.OrdinalIgnoreCase) && - (i + 1 < parts.Length)) - { - var rate = parts[i + 1]; - float val; - - if (float.TryParse(rate, NumberStyles.Any, _usCulture, out val)) - { - framerate = val; - } - } - else if (transcodingJob.RunTimeTicks.HasValue && - part.StartsWith("time=", StringComparison.OrdinalIgnoreCase)) - { - var time = part.Split(new[] { '=' }, 2).Last(); - TimeSpan val; - - if (TimeSpan.TryParse(time, _usCulture, out val)) - { - var currentMs = startMs + val.TotalMilliseconds; - - var percentVal = currentMs / totalMs; - percent = 100 * percentVal; - - transcodingPosition = val; - } - } - else if (part.StartsWith("size=", StringComparison.OrdinalIgnoreCase)) - { - var size = part.Split(new[] { '=' }, 2).Last(); - - int? scale = null; - if (size.IndexOf("kb", StringComparison.OrdinalIgnoreCase) != -1) - { - scale = 1024; - size = size.Replace("kb", string.Empty, StringComparison.OrdinalIgnoreCase); - } - - if (scale.HasValue) - { - long val; - - if (long.TryParse(size, NumberStyles.Any, _usCulture, out val)) - { - bytesTranscoded = val * scale.Value; - } - } - } - } - - if (framerate.HasValue || percent.HasValue) - { - transcodingJob.ReportTranscodingProgress(transcodingPosition, framerate, percent, bytesTranscoded); - } - } - } -} diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 580f5c615..97f2c57eb 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -647,9 +647,9 @@ namespace MediaBrowser.MediaEncoding.Encoder var videoStream = mediaInfo.MediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video); - if (videoStream != null) + if (videoStream != null && !videoStream.IsInterlaced) { - var isInterlaced = await DetectInterlaced(mediaInfo, videoStream, inputPath, probeSizeArgument).ConfigureAwait(false); + var isInterlaced = DetectInterlaced(mediaInfo, videoStream); if (isInterlaced) { @@ -672,7 +672,7 @@ namespace MediaBrowser.MediaEncoding.Encoder } } - private async Task<bool> DetectInterlaced(MediaSourceInfo video, MediaStream videoStream, string inputPath, string probeSizeArgument) + private bool DetectInterlaced(MediaSourceInfo video, MediaStream videoStream) { var formats = (video.Container ?? string.Empty).Split(',').ToList(); var enableInterlacedDection = formats.Contains("vob", StringComparer.OrdinalIgnoreCase) || @@ -698,165 +698,9 @@ namespace MediaBrowser.MediaEncoding.Encoder } } - if (video.Protocol != MediaProtocol.File) - { - return false; - } - - var args = "{0} -i {1} -map 0:v:{2} -an -filter:v idet -frames:v 500 -an -f null /dev/null"; - - var process = _processFactory.Create(new ProcessOptions - { - CreateNoWindow = true, - UseShellExecute = false, - - // Must consume both or ffmpeg may hang due to deadlocks. See comments below. - RedirectStandardError = true, - FileName = FFMpegPath, - Arguments = string.Format(args, probeSizeArgument, inputPath, videoStream.Index.ToString(CultureInfo.InvariantCulture)).Trim(), - - IsHidden = true, - ErrorDialog = false, - EnableRaisingEvents = true - }); - - _logger.Debug("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments); - var idetFoundInterlaced = false; - - using (var processWrapper = new ProcessWrapper(process, this, _logger)) - { - try - { - StartProcess(processWrapper); - } - catch (Exception ex) - { - _logger.ErrorException("Error starting ffprobe", ex); - - throw; - } - - try - { - //process.BeginOutputReadLine(); - - using (var reader = new StreamReader(process.StandardError.BaseStream)) - { - while (!reader.EndOfStream) - { - var line = await reader.ReadLineAsync().ConfigureAwait(false); - - if (line.StartsWith("[Parsed_idet", StringComparison.OrdinalIgnoreCase)) - { - var idetResult = AnalyzeIdetResult(line); - - if (idetResult.HasValue) - { - if (!idetResult.Value) - { - return false; - } - - idetFoundInterlaced = true; - } - } - } - } - - } - catch - { - StopProcess(processWrapper, 100); - - throw; - } - } - - return idetFoundInterlaced; - } - - private bool? AnalyzeIdetResult(string line) - { - // As you can see, the filter only guessed one frame as progressive. - // Results like this are pretty typical. So if less than 30% of the detections are in the "Undetermined" category, then I only consider the video to be interlaced if at least 65% of the identified frames are in either the TFF or BFF category. - // In this case (310 + 311)/(622) = 99.8% which is well over the 65% metric. I may refine that number with more testing but I honestly do not believe I will need to. - // http://awel.domblogger.net/videoTranscode/interlace.html - var index = line.IndexOf("detection:", StringComparison.OrdinalIgnoreCase); - - if (index == -1) - { - return null; - } - - line = line.Substring(index).Trim(); - var parts = line.Split(' ').Where(i => !string.IsNullOrWhiteSpace(i)).Select(i => i.Trim()).ToList(); - - if (parts.Count < 2) - { - return null; - } - double tff = 0; - double bff = 0; - double progressive = 0; - double undetermined = 0; - double total = 0; - - for (var i = 0; i < parts.Count - 1; i++) - { - var part = parts[i]; - - if (string.Equals(part, "tff:", StringComparison.OrdinalIgnoreCase)) - { - tff = GetNextPart(parts, i); - total += tff; - } - else if (string.Equals(part, "bff:", StringComparison.OrdinalIgnoreCase)) - { - bff = GetNextPart(parts, i); - total += tff; - } - else if (string.Equals(part, "progressive:", StringComparison.OrdinalIgnoreCase)) - { - progressive = GetNextPart(parts, i); - total += progressive; - } - else if (string.Equals(part, "undetermined:", StringComparison.OrdinalIgnoreCase)) - { - undetermined = GetNextPart(parts, i); - total += undetermined; - } - } - - if (total == 0) - { - return null; - } - - if ((undetermined / total) >= .3) - { - return false; - } - - if (((tff + bff) / total) >= .4) - { - return true; - } - return false; } - private int GetNextPart(List<string> parts, int index) - { - var next = parts[index + 1]; - - int value; - if (int.TryParse(next, NumberStyles.Any, CultureInfo.InvariantCulture, out value)) - { - return value; - } - return 0; - } - /// <summary> /// The us culture /// </summary> diff --git a/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs index 52ef4d834..96c126923 100644 --- a/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs @@ -18,143 +18,12 @@ namespace MediaBrowser.MediaEncoding.Encoder { } - protected override async Task<string> GetCommandLineArguments(EncodingJob state) + protected override string GetCommandLineArguments(EncodingJob state) { // Get the output codec name var encodingOptions = GetEncodingOptions(); - var videoCodec = EncodingHelper.GetVideoEncoder(state, encodingOptions); - var format = string.Empty; - var keyFrame = string.Empty; - - if (string.Equals(Path.GetExtension(state.OutputFilePath), ".mp4", StringComparison.OrdinalIgnoreCase) && - state.Options.Context == EncodingContext.Streaming) - { - // Comparison: https://github.com/jansmolders86/mediacenterjs/blob/master/lib/transcoding/desktop.js - format = " -f mp4 -movflags frag_keyframe+empty_moov"; - } - - var threads = EncodingHelper.GetNumberOfThreads(state, encodingOptions, string.Equals(videoCodec, "libvpx", StringComparison.OrdinalIgnoreCase)); - - var inputModifier = EncodingHelper.GetInputModifier(state, encodingOptions); - - 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, - EncodingHelper.GetInputArgument(state, encodingOptions), - keyFrame, - EncodingHelper.GetMapArgs(state), - videoArguments, - threads, - GetAudioArguments(state), - format, - state.OutputFilePath - ).Trim(); - } - - /// <summary> - /// Gets video arguments to pass to ffmpeg - /// </summary> - /// <param name="state">The state.</param> - /// <param name="videoCodec">The video codec.</param> - /// <returns>System.String.</returns> - private async Task<string> GetVideoArguments(EncodingJob state, string videoCodec) - { - var args = "-codec:v:0 " + videoCodec; - - if (state.EnableMpegtsM2TsMode) - { - args += " -mpegts_m2ts_mode 1"; - } - - var isOutputMkv = string.Equals(state.Options.OutputContainer, "mkv", StringComparison.OrdinalIgnoreCase); - - if (state.RunTimeTicks.HasValue) - { - //args += " -copyts -avoid_negative_ts disabled -start_at_zero"; - } - - if (string.Equals(videoCodec, "copy", StringComparison.OrdinalIgnoreCase)) - { - if (state.VideoStream != null && EncodingHelper.IsH264(state.VideoStream) && string.Equals(state.Options.OutputContainer, "ts", StringComparison.OrdinalIgnoreCase) && !string.Equals(state.VideoStream.NalLengthSize, "0", StringComparison.OrdinalIgnoreCase)) - { - args += " -bsf:v h264_mp4toannexb"; - } - - return args; - } - - var keyFrameArg = string.Format(" -force_key_frames \"expr:gte(t,n_forced*{0})\"", - 5.ToString(UsCulture)); - - args += keyFrameArg; - - var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.Options.SubtitleMethod == SubtitleDeliveryMethod.Encode; - - // Add resolution params, if specified - if (!hasGraphicalSubs) - { - args += EncodingHelper.GetOutputSizeParam(state, videoCodec); - } - - var qualityParam = EncodingHelper.GetVideoQualityParam(state, videoCodec, GetEncodingOptions(), "superfast"); - - if (!string.IsNullOrEmpty(qualityParam)) - { - args += " " + qualityParam.Trim(); - } - - // This is for internal graphical subs - if (hasGraphicalSubs) - { - args += EncodingHelper.GetGraphicalSubtitleParam(state, videoCodec); - } - - return args; - } - - /// <summary> - /// Gets audio arguments to pass to ffmpeg - /// </summary> - /// <param name="state">The state.</param> - /// <returns>System.String.</returns> - private string GetAudioArguments(EncodingJob state) - { - // If the video doesn't have an audio stream, return a default. - if (state.AudioStream == null && state.VideoStream != null) - { - return string.Empty; - } - - // Get the output codec name - var codec = EncodingHelper.GetAudioEncoder(state); - - var args = "-codec:a:0 " + codec; - - if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase)) - { - return args; - } - - // Add the number of audio channels - var channels = state.OutputAudioChannels; - - if (channels.HasValue) - { - args += " -ac " + channels.Value; - } - - var bitrate = state.OutputAudioBitrate; - - if (bitrate.HasValue) - { - args += " -ab " + bitrate.Value.ToString(UsCulture); - } - - args += " " + EncodingHelper.GetAudioFilterParam(state, GetEncodingOptions(), false); - - return args; + return EncodingHelper.GetProgressiveVideoFullCommandLine(state, encodingOptions, state.OutputFilePath, "superfast"); } protected override string GetOutputFileExtension(EncodingJob state) |
