Merge pull request #96 from rpaschoal/mockable-media-analysis

Introduce IMediaAnalysis interface so FFProbe results are mock friendly

Former-commit-id: 7f54115596
This commit is contained in:
Malte Rosenbjerg 2020-08-17 23:37:40 +02:00 committed by GitHub
commit 3d29220528
6 changed files with 34 additions and 16 deletions

View file

@ -159,7 +159,7 @@ private void ConvertToStreamPipe(params IArgument[] inputArguments)
} }
} }
public void Convert(ContainerFormat type, Action<MediaAnalysis> validationMethod, params IArgument[] inputArguments) public void Convert(ContainerFormat type, Action<IMediaAnalysis> validationMethod, params IArgument[] inputArguments)
{ {
var output = Input.OutputLocation(type); var output = Input.OutputLocation(type);

View file

@ -21,7 +21,7 @@ public static class FFMpeg
/// <param name="captureTime">Seek position where the thumbnail should be taken.</param> /// <param name="captureTime">Seek position where the thumbnail should be taken.</param>
/// <param name="size">Thumbnail size. If width or height equal 0, the other will be computed automatically.</param> /// <param name="size">Thumbnail size. If width or height equal 0, the other will be computed automatically.</param>
/// <returns>Bitmap with the requested snapshot.</returns> /// <returns>Bitmap with the requested snapshot.</returns>
public static bool Snapshot(MediaAnalysis source, string output, Size? size = null, TimeSpan? captureTime = null) public static bool Snapshot(IMediaAnalysis source, string output, Size? size = null, TimeSpan? captureTime = null)
{ {
if (Path.GetExtension(output) != FileExtension.Png) if (Path.GetExtension(output) != FileExtension.Png)
output = Path.GetFileNameWithoutExtension(output) + FileExtension.Png; output = Path.GetFileNameWithoutExtension(output) + FileExtension.Png;
@ -40,7 +40,7 @@ public static bool Snapshot(MediaAnalysis source, string output, Size? size = nu
/// <param name="captureTime">Seek position where the thumbnail should be taken.</param> /// <param name="captureTime">Seek position where the thumbnail should be taken.</param>
/// <param name="size">Thumbnail size. If width or height equal 0, the other will be computed automatically.</param> /// <param name="size">Thumbnail size. If width or height equal 0, the other will be computed automatically.</param>
/// <returns>Bitmap with the requested snapshot.</returns> /// <returns>Bitmap with the requested snapshot.</returns>
public static Task<bool> SnapshotAsync(MediaAnalysis source, string output, Size? size = null, TimeSpan? captureTime = null) public static Task<bool> SnapshotAsync(IMediaAnalysis source, string output, Size? size = null, TimeSpan? captureTime = null)
{ {
if (Path.GetExtension(output) != FileExtension.Png) if (Path.GetExtension(output) != FileExtension.Png)
output = Path.GetFileNameWithoutExtension(output) + FileExtension.Png; output = Path.GetFileNameWithoutExtension(output) + FileExtension.Png;
@ -58,7 +58,7 @@ public static Task<bool> SnapshotAsync(MediaAnalysis source, string output, Size
/// <param name="captureTime">Seek position where the thumbnail should be taken.</param> /// <param name="captureTime">Seek position where the thumbnail should be taken.</param>
/// <param name="size">Thumbnail size. If width or height equal 0, the other will be computed automatically.</param> /// <param name="size">Thumbnail size. If width or height equal 0, the other will be computed automatically.</param>
/// <returns>Bitmap with the requested snapshot.</returns> /// <returns>Bitmap with the requested snapshot.</returns>
public static Bitmap Snapshot(MediaAnalysis source, Size? size = null, TimeSpan? captureTime = null) public static Bitmap Snapshot(IMediaAnalysis source, Size? size = null, TimeSpan? captureTime = null)
{ {
var arguments = BuildSnapshotArguments(source, size, captureTime); var arguments = BuildSnapshotArguments(source, size, captureTime);
using var ms = new MemoryStream(); using var ms = new MemoryStream();
@ -78,7 +78,7 @@ public static Bitmap Snapshot(MediaAnalysis source, Size? size = null, TimeSpan?
/// <param name="captureTime">Seek position where the thumbnail should be taken.</param> /// <param name="captureTime">Seek position where the thumbnail should be taken.</param>
/// <param name="size">Thumbnail size. If width or height equal 0, the other will be computed automatically.</param> /// <param name="size">Thumbnail size. If width or height equal 0, the other will be computed automatically.</param>
/// <returns>Bitmap with the requested snapshot.</returns> /// <returns>Bitmap with the requested snapshot.</returns>
public static async Task<Bitmap> SnapshotAsync(MediaAnalysis source, Size? size = null, TimeSpan? captureTime = null) public static async Task<Bitmap> SnapshotAsync(IMediaAnalysis source, Size? size = null, TimeSpan? captureTime = null)
{ {
var arguments = BuildSnapshotArguments(source, size, captureTime); var arguments = BuildSnapshotArguments(source, size, captureTime);
using var ms = new MemoryStream(); using var ms = new MemoryStream();
@ -92,7 +92,7 @@ await arguments
return new Bitmap(ms); return new Bitmap(ms);
} }
private static FFMpegArguments BuildSnapshotArguments(MediaAnalysis source, Size? size = null, TimeSpan? captureTime = null) private static FFMpegArguments BuildSnapshotArguments(IMediaAnalysis source, Size? size = null, TimeSpan? captureTime = null)
{ {
captureTime ??= TimeSpan.FromSeconds(source.Duration.TotalSeconds / 3); captureTime ??= TimeSpan.FromSeconds(source.Duration.TotalSeconds / 3);
size = PrepareSnapshotSize(source, size); size = PrepareSnapshotSize(source, size);
@ -104,7 +104,7 @@ private static FFMpegArguments BuildSnapshotArguments(MediaAnalysis source, Size
.Resize(size); .Resize(size);
} }
private static Size? PrepareSnapshotSize(MediaAnalysis source, Size? wantedSize) private static Size? PrepareSnapshotSize(IMediaAnalysis source, Size? wantedSize)
{ {
if (wantedSize == null || (wantedSize.Value.Height <= 0 && wantedSize.Value.Width <= 0)) if (wantedSize == null || (wantedSize.Value.Height <= 0 && wantedSize.Value.Width <= 0))
return null; return null;
@ -143,7 +143,7 @@ private static FFMpegArguments BuildSnapshotArguments(MediaAnalysis source, Size
/// <param name="multithreaded">Is encoding multithreaded.</param> /// <param name="multithreaded">Is encoding multithreaded.</param>
/// <returns>Output video information.</returns> /// <returns>Output video information.</returns>
public static bool Convert( public static bool Convert(
MediaAnalysis source, IMediaAnalysis source,
string output, string output,
ContainerFormat format, ContainerFormat format,
Speed speed = Speed.SuperFast, Speed speed = Speed.SuperFast,
@ -235,7 +235,7 @@ public static bool PosterWithAudio(string image, string audio, string output)
/// <param name="output">Output video file.</param> /// <param name="output">Output video file.</param>
/// <param name="videos">List of vides that need to be joined together.</param> /// <param name="videos">List of vides that need to be joined together.</param>
/// <returns>Output video information.</returns> /// <returns>Output video information.</returns>
public static bool Join(string output, params MediaAnalysis[] videos) public static bool Join(string output, params IMediaAnalysis[] videos)
{ {
var temporaryVideoParts = videos.Select(video => var temporaryVideoParts = videos.Select(video =>
{ {

View file

@ -11,13 +11,13 @@ namespace FFMpegCore
{ {
public static class FFProbe public static class FFProbe
{ {
public static MediaAnalysis Analyse(string filePath, int outputCapacity = int.MaxValue) public static IMediaAnalysis Analyse(string filePath, int outputCapacity = int.MaxValue)
{ {
using var instance = PrepareInstance(filePath, outputCapacity); using var instance = PrepareInstance(filePath, outputCapacity);
instance.BlockUntilFinished(); instance.BlockUntilFinished();
return ParseOutput(filePath, instance); return ParseOutput(filePath, instance);
} }
public static MediaAnalysis Analyse(System.IO.Stream stream, int outputCapacity = int.MaxValue) public static IMediaAnalysis Analyse(System.IO.Stream stream, int outputCapacity = int.MaxValue)
{ {
var streamPipeSource = new StreamPipeSource(stream); var streamPipeSource = new StreamPipeSource(stream);
var pipeArgument = new InputPipeArgument(streamPipeSource); var pipeArgument = new InputPipeArgument(streamPipeSource);
@ -40,13 +40,13 @@ public static MediaAnalysis Analyse(System.IO.Stream stream, int outputCapacity
return ParseOutput(pipeArgument.PipePath, instance); return ParseOutput(pipeArgument.PipePath, instance);
} }
public static async Task<MediaAnalysis> AnalyseAsync(string filePath, int outputCapacity = int.MaxValue) public static async Task<IMediaAnalysis> AnalyseAsync(string filePath, int outputCapacity = int.MaxValue)
{ {
using var instance = PrepareInstance(filePath, outputCapacity); using var instance = PrepareInstance(filePath, outputCapacity);
await instance.FinishedRunning(); await instance.FinishedRunning();
return ParseOutput(filePath, instance); return ParseOutput(filePath, instance);
} }
public static async Task<MediaAnalysis> AnalyseAsync(System.IO.Stream stream, int outputCapacity = int.MaxValue) public static async Task<IMediaAnalysis> AnalyseAsync(System.IO.Stream stream, int outputCapacity = int.MaxValue)
{ {
var streamPipeSource = new StreamPipeSource(stream); var streamPipeSource = new StreamPipeSource(stream);
var pipeArgument = new InputPipeArgument(streamPipeSource); var pipeArgument = new InputPipeArgument(streamPipeSource);
@ -73,7 +73,7 @@ public static async Task<MediaAnalysis> AnalyseAsync(System.IO.Stream stream, in
return ParseOutput(pipeArgument.PipePath, instance); return ParseOutput(pipeArgument.PipePath, instance);
} }
private static MediaAnalysis ParseOutput(string filePath, Instance instance) private static IMediaAnalysis ParseOutput(string filePath, Instance instance)
{ {
var json = string.Join(string.Empty, instance.OutputData); var json = string.Join(string.Empty, instance.OutputData);
var ffprobeAnalysis = JsonSerializer.Deserialize<FFProbeAnalysis>(json, new JsonSerializerOptions var ffprobeAnalysis = JsonSerializer.Deserialize<FFProbeAnalysis>(json, new JsonSerializerOptions

View file

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace FFMpegCore
{
public interface IMediaAnalysis
{
string Path { get; }
string Extension { get; }
TimeSpan Duration { get; }
MediaFormat Format { get; }
AudioStream PrimaryAudioStream { get; }
VideoStream PrimaryVideoStream { get; }
List<VideoStream> VideoStreams { get; }
List<AudioStream> AudioStreams { get; }
}
}

View file

@ -5,7 +5,7 @@
namespace FFMpegCore namespace FFMpegCore
{ {
public class MediaAnalysis internal class MediaAnalysis : IMediaAnalysis
{ {
private static readonly Regex DurationRegex = new Regex("^(\\d{1,2}:\\d{1,2}:\\d{1,2}(.\\d{1,7})?)", RegexOptions.Compiled); private static readonly Regex DurationRegex = new Regex("^(\\d{1,2}:\\d{1,2}:\\d{1,2}(.\\d{1,7})?)", RegexOptions.Compiled);
internal MediaAnalysis(string path, FFProbeAnalysis analysis) internal MediaAnalysis(string path, FFProbeAnalysis analysis)

View file

@ -12,7 +12,7 @@ public static void ConversionSizeExceptionCheck(Image image)
ConversionSizeExceptionCheck(image.Size); ConversionSizeExceptionCheck(image.Size);
} }
public static void ConversionSizeExceptionCheck(MediaAnalysis info) public static void ConversionSizeExceptionCheck(IMediaAnalysis info)
{ {
ConversionSizeExceptionCheck(new Size(info.PrimaryVideoStream.Width, info.PrimaryVideoStream.Height)); ConversionSizeExceptionCheck(new Size(info.PrimaryVideoStream.Width, info.PrimaryVideoStream.Height));
} }