aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs')
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs129
1 files changed, 89 insertions, 40 deletions
diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
index 753da46a6..380894278 100644
--- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
@@ -18,6 +18,7 @@ using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.MediaEncoding.Probing;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dlna;
+using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO;
@@ -34,9 +35,14 @@ namespace MediaBrowser.MediaEncoding.Encoder
public class MediaEncoder : IMediaEncoder, IDisposable
{
/// <summary>
- /// The default image extraction timeout in milliseconds.
+ /// The default SDR image extraction timeout in milliseconds.
/// </summary>
- internal const int DefaultImageExtractionTimeout = 10000;
+ internal const int DefaultSdrImageExtractionTimeout = 10000;
+
+ /// <summary>
+ /// The default HDR image extraction timeout in milliseconds.
+ /// </summary>
+ internal const int DefaultHdrImageExtractionTimeout = 20000;
/// <summary>
/// The us culture.
@@ -83,8 +89,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
_jsonSerializerOptions = JsonDefaults.GetOptions();
}
- private EncodingHelper EncodingHelper => _encodingHelperFactory.Value;
-
/// <inheritdoc />
public string EncoderPath => _ffmpegPath;
@@ -320,33 +324,24 @@ namespace MediaBrowser.MediaEncoding.Encoder
public Task<MediaInfo> GetMediaInfo(MediaInfoRequest request, CancellationToken cancellationToken)
{
var extractChapters = request.MediaType == DlnaProfileType.Video && request.ExtractChapters;
+ var inputFile = request.MediaSource.Path;
- var inputFiles = MediaEncoderHelpers.GetInputArgument(_fileSystem, request.MediaSource.Path, request.MountedIso, request.PlayableStreamFileNames);
-
- var probeSize = EncodingHelper.GetProbeSizeArgument(inputFiles.Length);
- string analyzeDuration;
+ string analyzeDuration = string.Empty;
if (request.MediaSource.AnalyzeDurationMs > 0)
{
analyzeDuration = "-analyzeduration " +
(request.MediaSource.AnalyzeDurationMs * 1000).ToString();
}
- else
- {
- analyzeDuration = EncodingHelper.GetAnalyzeDurationArgument(inputFiles.Length);
- }
-
- probeSize = probeSize + " " + analyzeDuration;
- probeSize = probeSize.Trim();
var forceEnableLogging = request.MediaSource.Protocol != MediaProtocol.File;
return GetMediaInfoInternal(
- GetInputArgument(inputFiles, request.MediaSource.Protocol),
+ GetInputArgument(inputFile, request.MediaSource),
request.MediaSource.Path,
request.MediaSource.Protocol,
extractChapters,
- probeSize,
+ analyzeDuration,
request.MediaType == DlnaProfileType.Audio,
request.MediaSource.VideoType,
forceEnableLogging,
@@ -356,12 +351,20 @@ namespace MediaBrowser.MediaEncoding.Encoder
/// <summary>
/// Gets the input argument.
/// </summary>
- /// <param name="inputFiles">The input files.</param>
- /// <param name="protocol">The protocol.</param>
+ /// <param name="inputFile">The input file.</param>
+ /// <param name="mediaSource">The mediaSource.</param>
/// <returns>System.String.</returns>
/// <exception cref="ArgumentException">Unrecognized InputType.</exception>
- public string GetInputArgument(IReadOnlyList<string> inputFiles, MediaProtocol protocol)
- => EncodingUtils.GetInputArgument(inputFiles, protocol);
+ public string GetInputArgument(string inputFile, MediaSourceInfo mediaSource)
+ {
+ var prefix = "file";
+ if (mediaSource.VideoType == VideoType.BluRay || mediaSource.VideoType == VideoType.Iso)
+ {
+ prefix = "bluray";
+ }
+
+ return EncodingUtils.GetInputArgument(prefix, inputFile, mediaSource.Protocol);
+ }
/// <summary>
/// Gets the media info internal.
@@ -459,31 +462,36 @@ namespace MediaBrowser.MediaEncoding.Encoder
public Task<string> ExtractAudioImage(string path, int? imageStreamIndex, CancellationToken cancellationToken)
{
- return ExtractImage(new[] { path }, null, null, imageStreamIndex, MediaProtocol.File, true, null, null, cancellationToken);
+ var mediaSource = new MediaSourceInfo
+ {
+ Protocol = MediaProtocol.File
+ };
+
+ return ExtractImage(path, null, null, imageStreamIndex, mediaSource, true, null, null, cancellationToken);
}
- public Task<string> ExtractVideoImage(string[] inputFiles, string container, MediaProtocol protocol, MediaStream videoStream, Video3DFormat? threedFormat, TimeSpan? offset, CancellationToken cancellationToken)
+ public Task<string> ExtractVideoImage(string inputFile, string container, MediaSourceInfo mediaSource, MediaStream videoStream, Video3DFormat? threedFormat, TimeSpan? offset, CancellationToken cancellationToken)
{
- return ExtractImage(inputFiles, container, videoStream, null, protocol, false, threedFormat, offset, cancellationToken);
+ return ExtractImage(inputFile, container, videoStream, null, mediaSource, false, threedFormat, offset, cancellationToken);
}
- public Task<string> ExtractVideoImage(string[] inputFiles, string container, MediaProtocol protocol, MediaStream imageStream, int? imageStreamIndex, CancellationToken cancellationToken)
+ public Task<string> ExtractVideoImage(string inputFile, string container, MediaSourceInfo mediaSource, MediaStream imageStream, int? imageStreamIndex, CancellationToken cancellationToken)
{
- return ExtractImage(inputFiles, container, imageStream, imageStreamIndex, protocol, false, null, null, cancellationToken);
+ return ExtractImage(inputFile, container, imageStream, imageStreamIndex, mediaSource, false, null, null, cancellationToken);
}
private async Task<string> ExtractImage(
- string[] inputFiles,
+ string inputFile,
string container,
MediaStream videoStream,
int? imageStreamIndex,
- MediaProtocol protocol,
+ MediaSourceInfo mediaSource,
bool isAudio,
Video3DFormat? threedFormat,
TimeSpan? offset,
CancellationToken cancellationToken)
{
- var inputArgument = GetInputArgument(inputFiles, protocol);
+ var inputArgument = GetInputArgument(inputFile, mediaSource);
if (isAudio)
{
@@ -495,9 +503,36 @@ namespace MediaBrowser.MediaEncoding.Encoder
}
else
{
+ // The failure of HDR extraction usually occurs when using custom ffmpeg that does not contain the zscale filter.
+ try
+ {
+ return await ExtractImageInternal(inputArgument, container, videoStream, imageStreamIndex, threedFormat, offset, true, true, cancellationToken).ConfigureAwait(false);
+ }
+ catch (ArgumentException)
+ {
+ throw;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "I-frame or HDR image extraction failed, will attempt with I-frame extraction disabled. Input: {Arguments}", inputArgument);
+ }
+
try
{
- return await ExtractImageInternal(inputArgument, container, videoStream, imageStreamIndex, threedFormat, offset, true, cancellationToken).ConfigureAwait(false);
+ return await ExtractImageInternal(inputArgument, container, videoStream, imageStreamIndex, threedFormat, offset, false, true, cancellationToken).ConfigureAwait(false);
+ }
+ catch (ArgumentException)
+ {
+ throw;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "HDR image extraction failed, will fallback to SDR image extraction. Input: {Arguments}", inputArgument);
+ }
+
+ try
+ {
+ return await ExtractImageInternal(inputArgument, container, videoStream, imageStreamIndex, threedFormat, offset, true, false, cancellationToken).ConfigureAwait(false);
}
catch (ArgumentException)
{
@@ -509,10 +544,10 @@ namespace MediaBrowser.MediaEncoding.Encoder
}
}
- return await ExtractImageInternal(inputArgument, container, videoStream, imageStreamIndex, threedFormat, offset, false, cancellationToken).ConfigureAwait(false);
+ return await ExtractImageInternal(inputArgument, container, videoStream, imageStreamIndex, threedFormat, offset, false, false, cancellationToken).ConfigureAwait(false);
}
- private async Task<string> ExtractImageInternal(string inputPath, string container, MediaStream videoStream, int? imageStreamIndex, Video3DFormat? threedFormat, TimeSpan? offset, bool useIFrame, CancellationToken cancellationToken)
+ private async Task<string> ExtractImageInternal(string inputPath, string container, MediaStream videoStream, int? imageStreamIndex, Video3DFormat? threedFormat, TimeSpan? offset, bool useIFrame, bool allowTonemap, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(inputPath))
{
@@ -553,6 +588,20 @@ namespace MediaBrowser.MediaEncoding.Encoder
var mapArg = imageStreamIndex.HasValue ? (" -map 0:v:" + imageStreamIndex.Value.ToString(CultureInfo.InvariantCulture)) : string.Empty;
+ var enableHdrExtraction = allowTonemap && string.Equals(videoStream?.VideoRange, "HDR", StringComparison.OrdinalIgnoreCase);
+ if (enableHdrExtraction)
+ {
+ string tonemapFilters = "zscale=t=linear:npl=100,format=gbrpf32le,zscale=p=bt709,tonemap=tonemap=hable:desat=0:peak=100,zscale=t=bt709:m=bt709,format=yuv420p";
+ if (string.IsNullOrEmpty(vf))
+ {
+ vf = "-vf " + tonemapFilters;
+ }
+ else
+ {
+ vf += "," + tonemapFilters;
+ }
+ }
+
// 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 enableThumbnail = useIFrame && !string.Equals("wtv", container, StringComparison.OrdinalIgnoreCase);
if (enableThumbnail)
@@ -569,8 +618,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
var args = string.Format(CultureInfo.InvariantCulture, "-i {0}{3} -threads {4} -v quiet -vframes 1 {2} -f image2 \"{1}\"", inputPath, tempExtractPath, vf, mapArg, threads);
- var probeSizeArgument = EncodingHelper.GetProbeSizeArgument(1);
- var analyzeDurationArgument = EncodingHelper.GetAnalyzeDurationArgument(1);
+ var probeSizeArgument = string.Empty;
+ var analyzeDurationArgument = string.Empty;
if (!string.IsNullOrWhiteSpace(probeSizeArgument))
{
@@ -635,7 +684,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
var timeoutMs = _configurationManager.Configuration.ImageExtractionTimeoutMs;
if (timeoutMs <= 0)
{
- timeoutMs = DefaultImageExtractionTimeout;
+ timeoutMs = enableHdrExtraction ? DefaultHdrImageExtractionTimeout : DefaultSdrImageExtractionTimeout;
}
ranToCompletion = await process.WaitForExitAsync(TimeSpan.FromMilliseconds(timeoutMs)).ConfigureAwait(false);
@@ -679,10 +728,10 @@ namespace MediaBrowser.MediaEncoding.Encoder
}
public async Task ExtractVideoImagesOnInterval(
- string[] inputFiles,
+ string inputFile,
string container,
MediaStream videoStream,
- MediaProtocol protocol,
+ MediaSourceInfo mediaSource,
Video3DFormat? threedFormat,
TimeSpan interval,
string targetDirectory,
@@ -690,7 +739,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
int? maxWidth,
CancellationToken cancellationToken)
{
- var inputArgument = GetInputArgument(inputFiles, protocol);
+ var inputArgument = GetInputArgument(inputFile, mediaSource);
var vf = "fps=fps=1/" + interval.TotalSeconds.ToString(_usCulture);
@@ -706,8 +755,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
var args = string.Format(CultureInfo.InvariantCulture, "-i {0} -threads {3} -v quiet {2} -f image2 \"{1}\"", inputArgument, outputPath, vf, threads);
- var probeSizeArgument = EncodingHelper.GetProbeSizeArgument(1);
- var analyzeDurationArgument = EncodingHelper.GetAnalyzeDurationArgument(1);
+ var probeSizeArgument = string.Empty;
+ var analyzeDurationArgument = string.Empty;
if (!string.IsNullOrWhiteSpace(probeSizeArgument))
{