Skip to content

Commit

Permalink
remove standard output redirect on image extractions
Browse files Browse the repository at this point in the history
  • Loading branch information
LukePulverenti committed Jul 1, 2016
1 parent cfe2ba7 commit e442c54
Show file tree
Hide file tree
Showing 6 changed files with 41 additions and 45 deletions.
6 changes: 3 additions & 3 deletions MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
Expand Up @@ -35,7 +35,7 @@ public interface IMediaEncoder : ITranscoderSupport
/// <param name="imageStreamIndex">Index of the image stream.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{Stream}.</returns>
Task<Stream> ExtractAudioImage(string path, int? imageStreamIndex, CancellationToken cancellationToken);
Task<string> ExtractAudioImage(string path, int? imageStreamIndex, CancellationToken cancellationToken);

/// <summary>
/// Extracts the video image.
Expand All @@ -46,9 +46,9 @@ public interface IMediaEncoder : ITranscoderSupport
/// <param name="offset">The offset.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <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>
/// Extracts the video images on interval.
Expand Down
5 changes: 1 addition & 4 deletions MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
Expand Up @@ -117,8 +117,7 @@ private string GetProcessOutput(string path, string arguments)
Arguments = arguments,
WindowStyle = ProcessWindowStyle.Hidden,
ErrorDialog = false,
RedirectStandardOutput = true,
//RedirectStandardError = true
RedirectStandardOutput = true
}
};

Expand All @@ -128,8 +127,6 @@ private string GetProcessOutput(string path, string arguments)

try
{
//process.BeginErrorReadLine();

return process.StandardOutput.ReadToEnd();
}
catch
Expand Down
37 changes: 13 additions & 24 deletions MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
Expand Up @@ -767,22 +767,22 @@ private int GetNextPart(List<string> parts, int index)
/// </summary>
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);
}

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);
}

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);
}

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)
{
var resourcePool = isAudio ? _audioImageResourcePool : _videoImageResourcePool;
Expand Down Expand Up @@ -816,13 +816,16 @@ public Task<Stream> ExtractVideoImage(string[] inputFiles, MediaProtocol protoco
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))
{
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.
// 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";
Expand Down Expand Up @@ -855,8 +858,8 @@ private async Task<Stream> ExtractImageInternal(string inputPath, int? imageStre
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.
var args = useIFrame ? string.Format("-i {0}{3} -threads 1 -v quiet -vframes 1 -vf \"{2},thumbnail=30\" -f image2 \"{1}\"", inputPath, "-", vf, mapArg) :
string.Format("-i {0}{3} -threads 1 -v quiet -vframes 1 -vf \"{2}\" -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, tempExtractPath, vf, mapArg);

var probeSize = GetProbeSizeArgument(new[] { inputPath }, protocol);

Expand All @@ -880,8 +883,6 @@ private async Task<Stream> ExtractImageInternal(string inputPath, int? imageStre
Arguments = args,
WindowStyle = ProcessWindowStyle.Hidden,
ErrorDialog = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
RedirectStandardInput = true
}
};
Expand All @@ -894,20 +895,10 @@ private async Task<Stream> ExtractImageInternal(string inputPath, int? imageStre

bool ranToCompletion;

var memoryStream = new MemoryStream();

try
{
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);

if (!ranToCompletion)
Expand All @@ -922,20 +913,18 @@ private async Task<Stream> ExtractImageInternal(string inputPath, int? imageStre
}

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);

_logger.Error(msg);

throw new ApplicationException(msg);
}

memoryStream.Position = 0;
return memoryStream;
return tempExtractPath;
}
}

Expand Down
15 changes: 10 additions & 5 deletions MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs
Expand Up @@ -83,12 +83,17 @@ public async Task<DynamicImageResponse> GetImage(Audio item, List<MediaStream> i

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))
{
await stream.CopyToAsync(fileStream).ConfigureAwait(false);
}
File.Delete(tempFile);
}
catch
{

}
}
}
Expand Down
9 changes: 5 additions & 4 deletions MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs
Expand Up @@ -116,7 +116,7 @@ public async Task<DynamicImageResponse> GetVideoImage(Video item, CancellationTo
imageStreams.FirstOrDefault(i => (i.Comment ?? string.Empty).IndexOf("cover", StringComparison.OrdinalIgnoreCase) != -1) ??
imageStreams.FirstOrDefault();

Stream stream;
string extractedImagePath;

if (imageStream != null)
{
Expand All @@ -135,7 +135,7 @@ public async Task<DynamicImageResponse> GetVideoImage(Video item, CancellationTo
}
}

stream = await _mediaEncoder.ExtractVideoImage(inputPath, protocol, videoIndex, cancellationToken).ConfigureAwait(false);
extractedImagePath = await _mediaEncoder.ExtractVideoImage(inputPath, protocol, videoIndex, cancellationToken).ConfigureAwait(false);
}
else
{
Expand All @@ -146,14 +146,15 @@ public async Task<DynamicImageResponse> GetVideoImage(Video item, CancellationTo
? TimeSpan.FromTicks(Convert.ToInt64(item.RunTimeTicks.Value * .1))
: 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
{
Format = ImageFormat.Jpg,
HasImage = true,
Stream = stream
Path = extractedImagePath,
Protocol = MediaProtocol.File
};
}
finally
Expand Down
Expand Up @@ -139,12 +139,16 @@ public async Task<bool> RefreshChapterImages(ChapterImageRefreshOptions options,
{
_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
{
File.Delete(tempFile);
}
catch
{
using (var fileStream = _fileSystem.GetFileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, true))
{
await stream.CopyToAsync(fileStream).ConfigureAwait(false);
}

}

chapter.ImagePath = path;
Expand Down

0 comments on commit e442c54

Please sign in to comment.