diff options
Diffstat (limited to 'MediaBrowser.MediaEncoding')
4 files changed, 144 insertions, 23 deletions
diff --git a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs index 3fd4cd731..06d20d90e 100644 --- a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs +++ b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs @@ -83,6 +83,130 @@ namespace MediaBrowser.MediaEncoding.Attachments return (mediaAttachment, attachmentStream); } + public async Task ExtractAllAttachments( + string inputFile, + MediaSourceInfo mediaSource, + string outputPath, + CancellationToken cancellationToken) + { + var semaphore = _semaphoreLocks.GetOrAdd(outputPath, key => new SemaphoreSlim(1, 1)); + + await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false); + + try + { + if (!Directory.Exists(outputPath)) + { + await ExtractAllAttachmentsInternal( + _mediaEncoder.GetInputArgument(inputFile, mediaSource), + outputPath, + cancellationToken).ConfigureAwait(false); + } + } + finally + { + semaphore.Release(); + } + } + + private async Task ExtractAllAttachmentsInternal( + string inputPath, + string outputPath, + CancellationToken cancellationToken) + { + if (string.IsNullOrEmpty(inputPath)) + { + throw new ArgumentNullException(nameof(inputPath)); + } + + if (string.IsNullOrEmpty(outputPath)) + { + throw new ArgumentNullException(nameof(outputPath)); + } + + Directory.CreateDirectory(outputPath); + + var processArgs = string.Format( + CultureInfo.InvariantCulture, + "-dump_attachment:t \"\" -i {0} -t 0 -f null null", + inputPath); + + int exitCode; + + using (var process = new Process + { + StartInfo = new ProcessStartInfo + { + Arguments = processArgs, + FileName = _mediaEncoder.EncoderPath, + UseShellExecute = false, + CreateNoWindow = true, + WindowStyle = ProcessWindowStyle.Hidden, + WorkingDirectory = outputPath, + ErrorDialog = false + }, + EnableRaisingEvents = true + }) + { + _logger.LogInformation("{File} {Arguments}", process.StartInfo.FileName, process.StartInfo.Arguments); + + process.Start(); + + var ranToCompletion = await ProcessExtensions.WaitForExitAsync(process, cancellationToken).ConfigureAwait(false); + + if (!ranToCompletion) + { + try + { + _logger.LogWarning("Killing ffmpeg attachment extraction process"); + process.Kill(); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error killing attachment extraction process"); + } + } + + exitCode = ranToCompletion ? process.ExitCode : -1; + } + + var failed = false; + + if (exitCode != 0) + { + failed = true; + + _logger.LogWarning("Deleting extracted attachments {Path} due to failure: {ExitCode}", outputPath, exitCode); + try + { + if (Directory.Exists(outputPath)) + { + Directory.Delete(outputPath); + } + } + catch (IOException ex) + { + _logger.LogError(ex, "Error deleting extracted attachments {Path}", outputPath); + } + } + else if (!Directory.Exists(outputPath)) + { + failed = true; + } + + if (failed) + { + _logger.LogError("ffmpeg attachment extraction failed for {InputPath} to {OutputPath}", inputPath, outputPath); + + throw new InvalidOperationException( + string.Format(CultureInfo.InvariantCulture, "ffmpeg attachment extraction failed for {0} to {1}", inputPath, outputPath)); + } + else + { + _logger.LogInformation("ffmpeg attachment extraction completed for {Path} to {Path}", inputPath, outputPath); + } + } + private async Task<Stream> GetAttachmentStream( MediaSourceInfo mediaSource, MediaAttachment mediaAttachment, diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index fe3069934..20d372d7a 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -44,18 +44,7 @@ namespace MediaBrowser.MediaEncoding.Encoder "mpeg4_cuvid", "vp8_cuvid", "vp9_cuvid", - "av1_cuvid", - "h264_mmal", - "mpeg2_mmal", - "mpeg4_mmal", - "vc1_mmal", - "h264_opencl", - "hevc_opencl", - "mpeg2_opencl", - "mpeg4_opencl", - "vp8_opencl", - "vp9_opencl", - "vc1_opencl" + "av1_cuvid" }; private static readonly string[] _requiredEncoders = new[] @@ -82,8 +71,6 @@ namespace MediaBrowser.MediaEncoding.Encoder "hevc_nvenc", "h264_vaapi", "hevc_vaapi", - "h264_omx", - "hevc_omx", "h264_v4l2m2m", "h264_videotoolbox", "hevc_videotoolbox" diff --git a/MediaBrowser.MediaEncoding/Probing/MediaChapter.cs b/MediaBrowser.MediaEncoding/Probing/MediaChapter.cs index a1cef7a9f..c9e948780 100644 --- a/MediaBrowser.MediaEncoding/Probing/MediaChapter.cs +++ b/MediaBrowser.MediaEncoding/Probing/MediaChapter.cs @@ -12,7 +12,7 @@ namespace MediaBrowser.MediaEncoding.Probing public class MediaChapter { [JsonPropertyName("id")] - public int Id { get; set; } + public long Id { get; set; } [JsonPropertyName("time_base")] public string TimeBase { get; set; } diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index 4c8f19604..8c0abe10d 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs @@ -613,6 +613,17 @@ namespace MediaBrowser.MediaEncoding.Probing } /// <summary> + /// Determines whether a stream code time base is double the frame rate. + /// </summary> + /// <param name="averageFrameRate">average frame rate.</param> + /// <param name="codecTimeBase">codec time base string.</param> + /// <returns>true if the codec time base is double the frame rate.</returns> + internal static bool IsCodecTimeBaseDoubleTheFrameRate(float? averageFrameRate, string codecTimeBase) + { + return MathF.Abs(((averageFrameRate ?? 0) * (GetFrameRate(codecTimeBase) ?? 0)) - 0.5f) <= float.Epsilon; + } + + /// <summary> /// Converts ffprobe stream info to our MediaStream class. /// </summary> /// <param name="isAudio">if set to <c>true</c> [is info].</param> @@ -691,9 +702,9 @@ namespace MediaBrowser.MediaEncoding.Probing if (string.IsNullOrEmpty(stream.Title)) { - // mp4 missing track title workaround: fall back to handler_name if populated + // mp4 missing track title workaround: fall back to handler_name if populated and not the default "SoundHandler" string handlerName = GetDictionaryValue(streamInfo.Tags, "handler_name"); - if (!string.IsNullOrEmpty(handlerName)) + if (!string.IsNullOrEmpty(handlerName) && !string.Equals(handlerName, "SoundHandler", StringComparison.OrdinalIgnoreCase)) { stream.Title = handlerName; } @@ -722,17 +733,16 @@ namespace MediaBrowser.MediaEncoding.Probing stream.AverageFrameRate = GetFrameRate(streamInfo.AverageFrameRate); stream.RealFrameRate = GetFrameRate(streamInfo.RFrameRate); + bool videoInterlaced = !string.IsNullOrWhiteSpace(streamInfo.FieldOrder) + && !string.Equals(streamInfo.FieldOrder, "progressive", StringComparison.OrdinalIgnoreCase); + // Some interlaced H.264 files in mp4 containers using MBAFF coding aren't flagged as being interlaced by FFprobe, // so for H.264 files we also calculate the frame rate from the codec time base and check if it is double the reported - // frame rate (both rounded to the nearest integer) to determine if the file is interlaced - int roundedTimeBaseFPS = Convert.ToInt32(1 / GetFrameRate(stream.CodecTimeBase) ?? 0); - int roundedDoubleFrameRate = Convert.ToInt32(stream.AverageFrameRate * 2 ?? 0); + // frame rate to determine if the file is interlaced - bool videoInterlaced = !string.IsNullOrWhiteSpace(streamInfo.FieldOrder) - && !string.Equals(streamInfo.FieldOrder, "progressive", StringComparison.OrdinalIgnoreCase); bool h264MbaffCoded = string.Equals(stream.Codec, "h264", StringComparison.OrdinalIgnoreCase) && string.IsNullOrWhiteSpace(streamInfo.FieldOrder) - && roundedTimeBaseFPS == roundedDoubleFrameRate; + && IsCodecTimeBaseDoubleTheFrameRate(stream.AverageFrameRate, stream.CodecTimeBase); if (videoInterlaced || h264MbaffCoded) { |
