diff options
Diffstat (limited to 'MediaBrowser.MediaEncoding')
| -rw-r--r-- | MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 35 | ||||
| -rw-r--r-- | MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs | 106 |
2 files changed, 124 insertions, 17 deletions
diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 2eb647e26..237b537bc 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -827,7 +827,7 @@ namespace MediaBrowser.MediaEncoding.Encoder } /// <inheritdoc /> - public Task<string> ExtractVideoImagesOnIntervalAccelerated( + public async Task<string> ExtractVideoImagesOnIntervalAccelerated( string inputFile, string container, MediaSourceInfo mediaSource, @@ -918,18 +918,34 @@ namespace MediaBrowser.MediaEncoding.Encoder inputArg = "-hwaccel_flags +low_priority " + inputArg; } - if (enableKeyFrameOnlyExtraction) - { - inputArg = "-skip_frame nokey " + inputArg; - } - var filterParam = encodingHelper.GetVideoProcessingFilterParam(jobState, options, vidEncoder).Trim(); if (string.IsNullOrWhiteSpace(filterParam)) { throw new InvalidOperationException("EncodingHelper returned empty or invalid filter parameters."); } - return ExtractVideoImagesOnIntervalInternal(inputArg, filterParam, vidEncoder, threads, qualityScale, priority, cancellationToken); + try + { + return await ExtractVideoImagesOnIntervalInternal( + (enableKeyFrameOnlyExtraction ? "-skip_frame nokey " : string.Empty) + inputArg, + filterParam, + vidEncoder, + threads, + qualityScale, + priority, + cancellationToken).ConfigureAwait(false); + } + catch (FfmpegException ex) + { + if (!enableKeyFrameOnlyExtraction) + { + throw; + } + + _logger.LogWarning(ex, "I-frame trickplay extraction failed, will attempt standard way. Input: {InputFile}", inputFile); + } + + return await ExtractVideoImagesOnIntervalInternal(inputArg, filterParam, vidEncoder, threads, qualityScale, priority, cancellationToken).ConfigureAwait(false); } private async Task<string> ExtractVideoImagesOnIntervalInternal( @@ -1071,11 +1087,8 @@ namespace MediaBrowser.MediaEncoding.Encoder } } - var exitCode = ranToCompletion ? processWrapper.ExitCode ?? 0 : -1; - - if (exitCode == -1) + if (!ranToCompletion || processWrapper.ExitCode != 0) { - _logger.LogError("ffmpeg image extraction failed for {ProcessDescription}", processDescription); // Cleanup temp folder here, because the targetDirectory is not returned and the cleanup for failed ffmpeg process is not possible for caller. // Ideally the ffmpeg should not write any files if it fails, but it seems like it is not guaranteed. try diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs index 777e33587..359927d4d 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs @@ -171,14 +171,15 @@ namespace MediaBrowser.MediaEncoding.Subtitles { using (var stream = await GetStream(fileInfo.Path, fileInfo.Protocol, cancellationToken).ConfigureAwait(false)) { - var result = CharsetDetector.DetectFromStream(stream).Detected; + var result = await CharsetDetector.DetectFromStreamAsync(stream, cancellationToken).ConfigureAwait(false); + var detected = result.Detected; stream.Position = 0; - if (result is not null) + if (detected is not null) { - _logger.LogDebug("charset {CharSet} detected for {Path}", result.EncodingName, fileInfo.Path); + _logger.LogDebug("charset {CharSet} detected for {Path}", detected.EncodingName, fileInfo.Path); - using var reader = new StreamReader(stream, result.Encoding); + using var reader = new StreamReader(stream, detected.Encoding); var text = await reader.ReadToEndAsync(cancellationToken).ConfigureAwait(false); return new MemoryStream(Encoding.UTF8.GetBytes(text)); @@ -489,10 +490,15 @@ namespace MediaBrowser.MediaEncoding.Subtitles try { var subtitleStreams = mediaSource.MediaStreams - .Where(stream => stream is { IsExtractableSubtitleStream: true, SupportsExternalStream: true, IsExternal: false }); + .Where(stream => stream is { IsExtractableSubtitleStream: true, SupportsExternalStream: true }); foreach (var subtitleStream in subtitleStreams) { + if (subtitleStream.IsExternal && !subtitleStream.Path.EndsWith(".mks", StringComparison.OrdinalIgnoreCase)) + { + continue; + } + var outputPath = GetSubtitleCachePath(mediaSource, subtitleStream.Index, "." + GetExtractableSubtitleFileExtension(subtitleStream)); var releaser = await _semaphoreLocks.LockAsync(outputPath, cancellationToken).ConfigureAwait(false); @@ -510,6 +516,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles if (extractableStreams.Count > 0) { await ExtractAllExtractableSubtitlesInternal(mediaSource, extractableStreams, cancellationToken).ConfigureAwait(false); + await ExtractAllExtractableSubtitlesMKS(mediaSource, extractableStreams, cancellationToken).ConfigureAwait(false); } } catch (Exception ex) @@ -522,6 +529,72 @@ namespace MediaBrowser.MediaEncoding.Subtitles } } + private async Task ExtractAllExtractableSubtitlesMKS( + MediaSourceInfo mediaSource, + List<MediaStream> subtitleStreams, + CancellationToken cancellationToken) + { + var mksFiles = new List<string>(); + + foreach (var subtitleStream in subtitleStreams) + { + if (string.IsNullOrEmpty(subtitleStream.Path) || !subtitleStream.Path.EndsWith(".mks", StringComparison.OrdinalIgnoreCase)) + { + continue; + } + + if (!mksFiles.Contains(subtitleStream.Path)) + { + mksFiles.Add(subtitleStream.Path); + } + } + + if (mksFiles.Count == 0) + { + return; + } + + foreach (string mksFile in mksFiles) + { + var inputPath = _mediaEncoder.GetInputArgument(mksFile, mediaSource); + var outputPaths = new List<string>(); + var args = string.Format( + CultureInfo.InvariantCulture, + "-i {0} -copyts", + inputPath); + + foreach (var subtitleStream in subtitleStreams) + { + if (!subtitleStream.Path.Equals(mksFile, StringComparison.OrdinalIgnoreCase)) + { + continue; + } + + var outputPath = GetSubtitleCachePath(mediaSource, subtitleStream.Index, "." + GetExtractableSubtitleFileExtension(subtitleStream)); + var outputCodec = IsCodecCopyable(subtitleStream.Codec) ? "copy" : "srt"; + var streamIndex = EncodingHelper.FindIndex(mediaSource.MediaStreams, subtitleStream); + + if (streamIndex == -1) + { + _logger.LogError("Cannot find subtitle stream index for {InputPath} ({Index}), skipping this stream", inputPath, subtitleStream.Index); + continue; + } + + Directory.CreateDirectory(Path.GetDirectoryName(outputPath) ?? throw new FileNotFoundException($"Calculated path ({outputPath}) is not valid.")); + + outputPaths.Add(outputPath); + args += string.Format( + CultureInfo.InvariantCulture, + " -map 0:{0} -an -vn -c:s {1} \"{2}\"", + streamIndex, + outputCodec, + outputPath); + } + + await ExtractSubtitlesForFile(inputPath, args, outputPaths, cancellationToken).ConfigureAwait(false); + } + } + private async Task ExtractAllExtractableSubtitlesInternal( MediaSourceInfo mediaSource, List<MediaStream> subtitleStreams, @@ -536,6 +609,12 @@ namespace MediaBrowser.MediaEncoding.Subtitles foreach (var subtitleStream in subtitleStreams) { + if (!string.IsNullOrEmpty(subtitleStream.Path) && subtitleStream.Path.EndsWith(".mks", StringComparison.OrdinalIgnoreCase)) + { + _logger.LogDebug("Subtitle {Index} for file {InputPath} is part in an MKS file. Skipping", inputPath, subtitleStream.Index); + continue; + } + var outputPath = GetSubtitleCachePath(mediaSource, subtitleStream.Index, "." + GetExtractableSubtitleFileExtension(subtitleStream)); var outputCodec = IsCodecCopyable(subtitleStream.Codec) ? "copy" : "srt"; var streamIndex = EncodingHelper.FindIndex(mediaSource.MediaStreams, subtitleStream); @@ -557,6 +636,20 @@ namespace MediaBrowser.MediaEncoding.Subtitles outputPath); } + if (outputPaths.Count == 0) + { + return; + } + + await ExtractSubtitlesForFile(inputPath, args, outputPaths, cancellationToken).ConfigureAwait(false); + } + + private async Task ExtractSubtitlesForFile( + string inputPath, + string args, + List<string> outputPaths, + CancellationToken cancellationToken) + { int exitCode; using (var process = new Process @@ -846,7 +939,8 @@ namespace MediaBrowser.MediaEncoding.Subtitles using (var stream = await GetStream(path, mediaSource.Protocol, cancellationToken).ConfigureAwait(false)) { - var charset = CharsetDetector.DetectFromStream(stream).Detected?.EncodingName ?? string.Empty; + var result = await CharsetDetector.DetectFromStreamAsync(stream, cancellationToken).ConfigureAwait(false); + var charset = result.Detected?.EncodingName ?? string.Empty; // UTF16 is automatically converted to UTF8 by FFmpeg, do not specify a character encoding if ((path.EndsWith(".ass", StringComparison.Ordinal) || path.EndsWith(".ssa", StringComparison.Ordinal) || path.EndsWith(".srt", StringComparison.Ordinal)) |
