mirror of
https://github.com/jellyfin/jellyfin.git
synced 2025-06-01 12:44:25 -04:00
remove standard output redirect on image extractions
This commit is contained in:
parent
580a68d19c
commit
00634b62c5
@ -35,7 +35,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||||||
/// <param name="imageStreamIndex">Index of the image stream.</param>
|
/// <param name="imageStreamIndex">Index of the image stream.</param>
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <returns>Task{Stream}.</returns>
|
/// <returns>Task{Stream}.</returns>
|
||||||
Task<Stream> ExtractAudioImage(string path, int? imageStreamIndex, CancellationToken cancellationToken);
|
Task<string> ExtractAudioImage(string path, int? imageStreamIndex, CancellationToken cancellationToken);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Extracts the video image.
|
/// Extracts the video image.
|
||||||
@ -46,9 +46,9 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||||||
/// <param name="offset">The offset.</param>
|
/// <param name="offset">The offset.</param>
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <returns>Task{Stream}.</returns>
|
/// <returns>Task{Stream}.</returns>
|
||||||
Task<Stream> ExtractVideoImage(string[] inputFiles, MediaProtocol protocol, Video3DFormat? threedFormat, TimeSpan? offset, CancellationToken cancellationToken);
|
Task<string> ExtractVideoImage(string[] inputFiles, MediaProtocol protocol, Video3DFormat? threedFormat, TimeSpan? offset, CancellationToken cancellationToken);
|
||||||
|
|
||||||
Task<Stream> ExtractVideoImage(string[] inputFiles, MediaProtocol protocol, int? imageStreamIndex, CancellationToken cancellationToken);
|
Task<string> ExtractVideoImage(string[] inputFiles, MediaProtocol protocol, int? imageStreamIndex, CancellationToken cancellationToken);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Extracts the video images on interval.
|
/// Extracts the video images on interval.
|
||||||
|
@ -117,8 +117,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|||||||
Arguments = arguments,
|
Arguments = arguments,
|
||||||
WindowStyle = ProcessWindowStyle.Hidden,
|
WindowStyle = ProcessWindowStyle.Hidden,
|
||||||
ErrorDialog = false,
|
ErrorDialog = false,
|
||||||
RedirectStandardOutput = true,
|
RedirectStandardOutput = true
|
||||||
//RedirectStandardError = true
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -128,8 +127,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
//process.BeginErrorReadLine();
|
|
||||||
|
|
||||||
return process.StandardOutput.ReadToEnd();
|
return process.StandardOutput.ReadToEnd();
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
|
@ -767,22 +767,22 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected readonly CultureInfo UsCulture = new CultureInfo("en-US");
|
protected readonly CultureInfo UsCulture = new CultureInfo("en-US");
|
||||||
|
|
||||||
public Task<Stream> ExtractAudioImage(string path, int? imageStreamIndex, CancellationToken cancellationToken)
|
public Task<string> ExtractAudioImage(string path, int? imageStreamIndex, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
return ExtractImage(new[] { path }, imageStreamIndex, MediaProtocol.File, true, null, null, cancellationToken);
|
return ExtractImage(new[] { path }, imageStreamIndex, MediaProtocol.File, true, null, null, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<Stream> ExtractVideoImage(string[] inputFiles, MediaProtocol protocol, Video3DFormat? threedFormat, TimeSpan? offset, CancellationToken cancellationToken)
|
public Task<string> ExtractVideoImage(string[] inputFiles, MediaProtocol protocol, Video3DFormat? threedFormat, TimeSpan? offset, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
return ExtractImage(inputFiles, null, protocol, false, threedFormat, offset, cancellationToken);
|
return ExtractImage(inputFiles, null, protocol, false, threedFormat, offset, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<Stream> ExtractVideoImage(string[] inputFiles, MediaProtocol protocol, int? imageStreamIndex, CancellationToken cancellationToken)
|
public Task<string> ExtractVideoImage(string[] inputFiles, MediaProtocol protocol, int? imageStreamIndex, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
return ExtractImage(inputFiles, imageStreamIndex, protocol, false, null, null, cancellationToken);
|
return ExtractImage(inputFiles, imageStreamIndex, protocol, false, null, null, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<Stream> ExtractImage(string[] inputFiles, int? imageStreamIndex, MediaProtocol protocol, bool isAudio,
|
private async Task<string> ExtractImage(string[] inputFiles, int? imageStreamIndex, MediaProtocol protocol, bool isAudio,
|
||||||
Video3DFormat? threedFormat, TimeSpan? offset, CancellationToken cancellationToken)
|
Video3DFormat? threedFormat, TimeSpan? offset, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var resourcePool = isAudio ? _audioImageResourcePool : _videoImageResourcePool;
|
var resourcePool = isAudio ? _audioImageResourcePool : _videoImageResourcePool;
|
||||||
@ -816,13 +816,16 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|||||||
return await ExtractImageInternal(inputArgument, imageStreamIndex, protocol, threedFormat, offset, false, resourcePool, cancellationToken).ConfigureAwait(false);
|
return await ExtractImageInternal(inputArgument, imageStreamIndex, protocol, threedFormat, offset, false, resourcePool, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<Stream> ExtractImageInternal(string inputPath, int? imageStreamIndex, MediaProtocol protocol, Video3DFormat? threedFormat, TimeSpan? offset, bool useIFrame, SemaphoreSlim resourcePool, CancellationToken cancellationToken)
|
private async Task<string> ExtractImageInternal(string inputPath, int? imageStreamIndex, MediaProtocol protocol, Video3DFormat? threedFormat, TimeSpan? offset, bool useIFrame, SemaphoreSlim resourcePool, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(inputPath))
|
if (string.IsNullOrEmpty(inputPath))
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException("inputPath");
|
throw new ArgumentNullException("inputPath");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var tempExtractPath = Path.Combine(ConfigurationManager.ApplicationPaths.TempDirectory, Guid.NewGuid() + ".jpg");
|
||||||
|
Directory.CreateDirectory(Path.GetDirectoryName(tempExtractPath));
|
||||||
|
|
||||||
// apply some filters to thumbnail extracted below (below) crop any black lines that we made and get the correct ar then scale to width 600.
|
// apply some filters to thumbnail extracted below (below) crop any black lines that we made and get the correct ar then scale to width 600.
|
||||||
// This filter chain may have adverse effects on recorded tv thumbnails if ar changes during presentation ex. commercials @ diff ar
|
// This filter chain may have adverse effects on recorded tv thumbnails if ar changes during presentation ex. commercials @ diff ar
|
||||||
var vf = "scale=600:trunc(600/dar/2)*2";
|
var vf = "scale=600:trunc(600/dar/2)*2";
|
||||||
@ -855,8 +858,8 @@ 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;
|
||||||
|
|
||||||
// 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 args = useIFrame ? string.Format("-i {0}{3} -threads 1 -v quiet -vframes 1 -vf \"{2},thumbnail=30\" -f image2 \"{1}\"", inputPath, "-", vf, mapArg) :
|
var args = useIFrame ? string.Format("-i {0}{3} -threads 1 -v quiet -vframes 1 -vf \"{2},thumbnail=30\" -f image2 \"{1}\"", inputPath, tempExtractPath, vf, mapArg) :
|
||||||
string.Format("-i {0}{3} -threads 1 -v quiet -vframes 1 -vf \"{2}\" -f image2 \"{1}\"", inputPath, "-", vf, mapArg);
|
string.Format("-i {0}{3} -threads 1 -v quiet -vframes 1 -vf \"{2}\" -f image2 \"{1}\"", inputPath, tempExtractPath, vf, mapArg);
|
||||||
|
|
||||||
var probeSize = GetProbeSizeArgument(new[] { inputPath }, protocol);
|
var probeSize = GetProbeSizeArgument(new[] { inputPath }, protocol);
|
||||||
|
|
||||||
@ -880,8 +883,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|||||||
Arguments = args,
|
Arguments = args,
|
||||||
WindowStyle = ProcessWindowStyle.Hidden,
|
WindowStyle = ProcessWindowStyle.Hidden,
|
||||||
ErrorDialog = false,
|
ErrorDialog = false,
|
||||||
RedirectStandardOutput = true,
|
|
||||||
RedirectStandardError = true,
|
|
||||||
RedirectStandardInput = true
|
RedirectStandardInput = true
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -894,20 +895,10 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|||||||
|
|
||||||
bool ranToCompletion;
|
bool ranToCompletion;
|
||||||
|
|
||||||
var memoryStream = new MemoryStream();
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
StartProcess(processWrapper);
|
StartProcess(processWrapper);
|
||||||
|
|
||||||
#pragma warning disable 4014
|
|
||||||
// Important - don't await the log task or we won't be able to kill ffmpeg when the user stops playback
|
|
||||||
process.StandardOutput.BaseStream.CopyToAsync(memoryStream);
|
|
||||||
#pragma warning restore 4014
|
|
||||||
|
|
||||||
// MUST read both stdout and stderr asynchronously or a deadlock may occurr
|
|
||||||
process.BeginErrorReadLine();
|
|
||||||
|
|
||||||
ranToCompletion = process.WaitForExit(10000);
|
ranToCompletion = process.WaitForExit(10000);
|
||||||
|
|
||||||
if (!ranToCompletion)
|
if (!ranToCompletion)
|
||||||
@ -922,11 +913,10 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|||||||
}
|
}
|
||||||
|
|
||||||
var exitCode = ranToCompletion ? processWrapper.ExitCode ?? 0 : -1;
|
var exitCode = ranToCompletion ? processWrapper.ExitCode ?? 0 : -1;
|
||||||
|
var file = new FileInfo(tempExtractPath);
|
||||||
|
|
||||||
if (exitCode == -1 || memoryStream.Length == 0)
|
if (exitCode == -1 || !file.Exists || file.Length == 0)
|
||||||
{
|
{
|
||||||
memoryStream.Dispose();
|
|
||||||
|
|
||||||
var msg = string.Format("ffmpeg image extraction failed for {0}", inputPath);
|
var msg = string.Format("ffmpeg image extraction failed for {0}", inputPath);
|
||||||
|
|
||||||
_logger.Error(msg);
|
_logger.Error(msg);
|
||||||
@ -934,8 +924,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|||||||
throw new ApplicationException(msg);
|
throw new ApplicationException(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
memoryStream.Position = 0;
|
return tempExtractPath;
|
||||||
return memoryStream;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,12 +83,17 @@ namespace MediaBrowser.Providers.MediaInfo
|
|||||||
|
|
||||||
var imageStreamIndex = imageStream == null ? (int?)null : imageStream.Index;
|
var imageStreamIndex = imageStream == null ? (int?)null : imageStream.Index;
|
||||||
|
|
||||||
using (var stream = await _mediaEncoder.ExtractAudioImage(item.Path, imageStreamIndex, cancellationToken).ConfigureAwait(false))
|
var tempFile = await _mediaEncoder.ExtractAudioImage(item.Path, imageStreamIndex, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
|
File.Copy(tempFile, path, true);
|
||||||
|
|
||||||
|
try
|
||||||
{
|
{
|
||||||
using (var fileStream = _fileSystem.GetFileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, true))
|
File.Delete(tempFile);
|
||||||
{
|
|
||||||
await stream.CopyToAsync(fileStream).ConfigureAwait(false);
|
|
||||||
}
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -116,7 +116,7 @@ namespace MediaBrowser.Providers.MediaInfo
|
|||||||
imageStreams.FirstOrDefault(i => (i.Comment ?? string.Empty).IndexOf("cover", StringComparison.OrdinalIgnoreCase) != -1) ??
|
imageStreams.FirstOrDefault(i => (i.Comment ?? string.Empty).IndexOf("cover", StringComparison.OrdinalIgnoreCase) != -1) ??
|
||||||
imageStreams.FirstOrDefault();
|
imageStreams.FirstOrDefault();
|
||||||
|
|
||||||
Stream stream;
|
string extractedImagePath;
|
||||||
|
|
||||||
if (imageStream != null)
|
if (imageStream != null)
|
||||||
{
|
{
|
||||||
@ -135,7 +135,7 @@ namespace MediaBrowser.Providers.MediaInfo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
stream = await _mediaEncoder.ExtractVideoImage(inputPath, protocol, videoIndex, cancellationToken).ConfigureAwait(false);
|
extractedImagePath = await _mediaEncoder.ExtractVideoImage(inputPath, protocol, videoIndex, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -146,14 +146,15 @@ namespace MediaBrowser.Providers.MediaInfo
|
|||||||
? TimeSpan.FromTicks(Convert.ToInt64(item.RunTimeTicks.Value * .1))
|
? TimeSpan.FromTicks(Convert.ToInt64(item.RunTimeTicks.Value * .1))
|
||||||
: TimeSpan.FromSeconds(10);
|
: TimeSpan.FromSeconds(10);
|
||||||
|
|
||||||
stream = await _mediaEncoder.ExtractVideoImage(inputPath, protocol, item.Video3DFormat, imageOffset, cancellationToken).ConfigureAwait(false);
|
extractedImagePath = await _mediaEncoder.ExtractVideoImage(inputPath, protocol, item.Video3DFormat, imageOffset, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new DynamicImageResponse
|
return new DynamicImageResponse
|
||||||
{
|
{
|
||||||
Format = ImageFormat.Jpg,
|
Format = ImageFormat.Jpg,
|
||||||
HasImage = true,
|
HasImage = true,
|
||||||
Stream = stream
|
Path = extractedImagePath,
|
||||||
|
Protocol = MediaProtocol.File
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
@ -139,12 +139,16 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder
|
|||||||
{
|
{
|
||||||
_fileSystem.CreateDirectory(Path.GetDirectoryName(path));
|
_fileSystem.CreateDirectory(Path.GetDirectoryName(path));
|
||||||
|
|
||||||
using (var stream = await _encoder.ExtractVideoImage(inputPath, protocol, video.Video3DFormat, time, cancellationToken).ConfigureAwait(false))
|
var tempFile = await _encoder.ExtractVideoImage(inputPath, protocol, video.Video3DFormat, time, cancellationToken).ConfigureAwait(false);
|
||||||
|
File.Copy(tempFile, path, true);
|
||||||
|
|
||||||
|
try
|
||||||
{
|
{
|
||||||
using (var fileStream = _fileSystem.GetFileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, true))
|
File.Delete(tempFile);
|
||||||
{
|
|
||||||
await stream.CopyToAsync(fileStream).ConfigureAwait(false);
|
|
||||||
}
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
chapter.ImagePath = path;
|
chapter.ImagePath = path;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user