mirror of
				https://github.com/jellyfin/jellyfin.git
				synced 2025-11-03 19:17:24 -05:00 
			
		
		
		
	Merge pull request #4610 from nyanmisaka/hdr-extract
Extract tone mapped thumbnails for HDR videos
This commit is contained in:
		
						commit
						3b4f86579b
					
				@ -35,9 +35,14 @@ namespace MediaBrowser.MediaEncoding.Encoder
 | 
				
			|||||||
    public class MediaEncoder : IMediaEncoder, IDisposable
 | 
					    public class MediaEncoder : IMediaEncoder, IDisposable
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        /// <summary>
 | 
					        /// <summary>
 | 
				
			||||||
        /// The default image extraction timeout in milliseconds.
 | 
					        /// The default SDR image extraction timeout in milliseconds.
 | 
				
			||||||
        /// </summary>
 | 
					        /// </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>
 | 
					        /// <summary>
 | 
				
			||||||
        /// The us culture.
 | 
					        /// The us culture.
 | 
				
			||||||
@ -498,9 +503,36 @@ namespace MediaBrowser.MediaEncoding.Encoder
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
 | 
					                // The failure of HDR extraction usually occurs when using custom ffmpeg that does not contain the zscale filter.
 | 
				
			||||||
                try
 | 
					                try
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    return await ExtractImageInternal(inputArgument, container, videoStream, imageStreamIndex, threedFormat, offset, true, cancellationToken).ConfigureAwait(false);
 | 
					                    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, 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)
 | 
					                catch (ArgumentException)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
@ -512,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))
 | 
					            if (string.IsNullOrEmpty(inputPath))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@ -556,6 +588,20 @@ namespace MediaBrowser.MediaEncoding.Encoder
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            var mapArg = imageStreamIndex.HasValue ? (" -map 0:v:" + imageStreamIndex.Value.ToString(CultureInfo.InvariantCulture)) : string.Empty;
 | 
					            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.
 | 
					            // 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);
 | 
					            var enableThumbnail = useIFrame && !string.Equals("wtv", container, StringComparison.OrdinalIgnoreCase);
 | 
				
			||||||
            if (enableThumbnail)
 | 
					            if (enableThumbnail)
 | 
				
			||||||
@ -638,7 +684,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
 | 
				
			|||||||
                    var timeoutMs = _configurationManager.Configuration.ImageExtractionTimeoutMs;
 | 
					                    var timeoutMs = _configurationManager.Configuration.ImageExtractionTimeoutMs;
 | 
				
			||||||
                    if (timeoutMs <= 0)
 | 
					                    if (timeoutMs <= 0)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        timeoutMs = DefaultImageExtractionTimeout;
 | 
					                        timeoutMs = enableHdrExtraction ? DefaultHdrImageExtractionTimeout : DefaultSdrImageExtractionTimeout;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    ranToCompletion = await process.WaitForExitAsync(TimeSpan.FromMilliseconds(timeoutMs)).ConfigureAwait(false);
 | 
					                    ranToCompletion = await process.WaitForExitAsync(TimeSpan.FromMilliseconds(timeoutMs)).ConfigureAwait(false);
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user