Former-commit-id: 39dd390e81
This commit is contained in:
Malte Rosenbjerg 2020-05-24 19:17:14 +02:00
parent d948867396
commit 17c9db52dd
12 changed files with 42 additions and 49 deletions

View file

@ -72,7 +72,7 @@ private void ConvertFromStreamPipe(ContainerFormat type, params IArgument[] inpu
var input = FFProbe.Analyse(VideoLibrary.LocalVideoWebm.FullName); var input = FFProbe.Analyse(VideoLibrary.LocalVideoWebm.FullName);
using (var inputStream = File.OpenRead(input.Path)) using (var inputStream = File.OpenRead(input.Path))
{ {
var pipeSource = new StreamPipeDataWriter(inputStream); var pipeSource = new StreamPipeSource(inputStream);
var arguments = FFMpegArguments.FromPipe(pipeSource); var arguments = FFMpegArguments.FromPipe(pipeSource);
foreach (var arg in inputArguments) foreach (var arg in inputArguments)
arguments.WithArgument(arg); arguments.WithArgument(arg);
@ -124,7 +124,7 @@ private void ConvertToStreamPipe(params IArgument[] inputArguments)
foreach (var arg in inputArguments) foreach (var arg in inputArguments)
arguments.WithArgument(arg); arguments.WithArgument(arg);
var streamPipeDataReader = new StreamPipeDataReader(ms); var streamPipeDataReader = new StreamPipeSink(ms);
var processor = arguments.OutputToPipe(streamPipeDataReader); var processor = arguments.OutputToPipe(streamPipeDataReader);
var scaling = arguments.Find<ScaleArgument>(); var scaling = arguments.Find<ScaleArgument>();
@ -220,7 +220,7 @@ public void ConvertFromPipe(ContainerFormat type, System.Drawing.Imaging.PixelFo
try try
{ {
var videoFramesSource = new RawVideoPipeDataWriter(BitmapSource.CreateBitmaps(128, fmt, 256, 256)); var videoFramesSource = new RawVideoPipeSource(BitmapSource.CreateBitmaps(128, fmt, 256, 256));
var arguments = FFMpegArguments.FromPipe(videoFramesSource); var arguments = FFMpegArguments.FromPipe(videoFramesSource);
foreach (var arg in inputArguments) foreach (var arg in inputArguments)
arguments.WithArgument(arg); arguments.WithArgument(arg);
@ -302,7 +302,7 @@ public async Task Video_ToMP4_Args_StreamOutputPipe_Async_Failure()
await Assert.ThrowsExceptionAsync<FFMpegException>(async () => await Assert.ThrowsExceptionAsync<FFMpegException>(async () =>
{ {
await using var ms = new MemoryStream(); await using var ms = new MemoryStream();
var pipeSource = new StreamPipeDataReader(ms); var pipeSource = new StreamPipeSink(ms);
await FFMpegArguments await FFMpegArguments
.FromInputFiles(VideoLibrary.LocalVideo) .FromInputFiles(VideoLibrary.LocalVideo)
.ForceFormat("mkv") .ForceFormat("mkv")
@ -322,7 +322,7 @@ public void Video_ToMP4_Args_StreamOutputPipe_Failure()
public void Video_ToMP4_Args_StreamOutputPipe_Async() public void Video_ToMP4_Args_StreamOutputPipe_Async()
{ {
using var ms = new MemoryStream(); using var ms = new MemoryStream();
var pipeSource = new StreamPipeDataReader(ms); var pipeSource = new StreamPipeSink(ms);
FFMpegArguments FFMpegArguments
.FromInputFiles(VideoLibrary.LocalVideo) .FromInputFiles(VideoLibrary.LocalVideo)
.WithVideoCodec(VideoCodec.LibX264) .WithVideoCodec(VideoCodec.LibX264)
@ -622,8 +622,8 @@ public void Video_UpdatesProgress()
public void Video_TranscodeInMemory() public void Video_TranscodeInMemory()
{ {
using var resStream = new MemoryStream(); using var resStream = new MemoryStream();
var reader = new StreamPipeDataReader(resStream); var reader = new StreamPipeSink(resStream);
var writer = new RawVideoPipeDataWriter(BitmapSource.CreateBitmaps(128, System.Drawing.Imaging.PixelFormat.Format24bppRgb, 128, 128)); var writer = new RawVideoPipeSource(BitmapSource.CreateBitmaps(128, System.Drawing.Imaging.PixelFormat.Format24bppRgb, 128, 128));
FFMpegArguments FFMpegArguments
.FromPipe(writer) .FromPipe(writer)
@ -642,8 +642,8 @@ public void Video_TranscodeInMemory()
public async Task Video_Cancel_Async() public async Task Video_Cancel_Async()
{ {
await using var resStream = new MemoryStream(); await using var resStream = new MemoryStream();
var reader = new StreamPipeDataReader(resStream); var reader = new StreamPipeSink(resStream);
var writer = new RawVideoPipeDataWriter(BitmapSource.CreateBitmaps(512, System.Drawing.Imaging.PixelFormat.Format24bppRgb, 128, 128)); var writer = new RawVideoPipeSource(BitmapSource.CreateBitmaps(512, System.Drawing.Imaging.PixelFormat.Format24bppRgb, 128, 128));
var task = FFMpegArguments var task = FFMpegArguments
.FromPipe(writer) .FromPipe(writer)

View file

@ -10,9 +10,9 @@ namespace FFMpegCore.Arguments
/// </summary> /// </summary>
public class InputPipeArgument : PipeArgument, IInputArgument public class InputPipeArgument : PipeArgument, IInputArgument
{ {
public readonly IPipeDataWriter Writer; public readonly IPipeSource Writer;
public InputPipeArgument(IPipeDataWriter writer) : base(PipeDirection.Out) public InputPipeArgument(IPipeSource writer) : base(PipeDirection.Out)
{ {
Writer = writer; Writer = writer;
} }

View file

@ -7,9 +7,9 @@ namespace FFMpegCore.Arguments
{ {
public class OutputPipeArgument : PipeArgument, IOutputArgument public class OutputPipeArgument : PipeArgument, IOutputArgument
{ {
public readonly IPipeDataReader Reader; public readonly IPipeSink Reader;
public OutputPipeArgument(IPipeDataReader reader) : base(PipeDirection.In) public OutputPipeArgument(IPipeSink reader) : base(PipeDirection.In)
{ {
Reader = reader; Reader = reader;
} }

View file

@ -59,7 +59,7 @@ public static Bitmap Snapshot(MediaAnalysis source, Size? size = null, TimeSpan?
.Resize(size) .Resize(size)
.Seek(captureTime) .Seek(captureTime)
.ForceFormat("rawvideo") .ForceFormat("rawvideo")
.OutputToPipe(new StreamPipeDataReader(ms)) .OutputToPipe(new StreamPipeSink(ms))
.ProcessSynchronously(); .ProcessSynchronously();
ms.Position = 0; ms.Position = 0;

View file

@ -12,7 +12,11 @@ namespace FFMpegCore
{ {
public class FFMpegArgumentProcessor public class FFMpegArgumentProcessor
{ {
private static readonly Regex ProgressRegex = new Regex(@"time=(\d\d:\d\d:\d\d.\d\d?)", RegexOptions.Compiled);
private readonly FFMpegArguments _ffMpegArguments; private readonly FFMpegArguments _ffMpegArguments;
private Action<double>? _onPercentageProgress;
private Action<TimeSpan>? _onTimeProgress;
private TimeSpan? _totalTimespan;
internal FFMpegArgumentProcessor(FFMpegArguments ffMpegArguments) internal FFMpegArgumentProcessor(FFMpegArguments ffMpegArguments)
{ {
@ -74,15 +78,20 @@ void OnCancelEvent(object sender, EventArgs args)
_ffMpegArguments.Post(); _ffMpegArguments.Post();
} }
return HandleCompletion(throwOnError, errorCode, instance);
}
private bool HandleCompletion(bool throwOnError, int errorCode, Instance instance)
{
if (throwOnError && errorCode != 0) if (throwOnError && errorCode != 0)
throw new FFMpegException(FFMpegExceptionType.Conversion, string.Join("\n", instance.ErrorData)); throw new FFMpegException(FFMpegExceptionType.Conversion, string.Join("\n", instance.ErrorData));
_onPercentageProgress?.Invoke(100.0); _onPercentageProgress?.Invoke(100.0);
if (_totalTimespan.HasValue) _onTimeProgress?.Invoke(_totalTimespan.Value); if (_totalTimespan.HasValue) _onTimeProgress?.Invoke(_totalTimespan.Value);
return errorCode == 0; return errorCode == 0;
} }
public async Task<bool> ProcessAsynchronously(bool throwOnError = true) public async Task<bool> ProcessAsynchronously(bool throwOnError = true)
{ {
using var instance = PrepareInstance(out var cancellationTokenSource, out var errorCode); using var instance = PrepareInstance(out var cancellationTokenSource, out var errorCode);
@ -113,13 +122,7 @@ await Task.WhenAll(instance.FinishedRunning().ContinueWith(t =>
_ffMpegArguments.Post(); _ffMpegArguments.Post();
} }
if (throwOnError && errorCode != 0) return HandleCompletion(throwOnError, errorCode, instance);
throw new FFMpegException(FFMpegExceptionType.Conversion, string.Join("\n", instance.ErrorData));
_onPercentageProgress?.Invoke(100.0);
if (_totalTimespan.HasValue) _onTimeProgress?.Invoke(_totalTimespan.Value);
return errorCode == 0;
} }
private Instance PrepareInstance(out CancellationTokenSource cancellationTokenSource, out int errorCode) private Instance PrepareInstance(out CancellationTokenSource cancellationTokenSource, out int errorCode)
@ -146,18 +149,8 @@ private static bool HandleException(bool throwOnError, Exception e, Instance ins
string.Join("\n", instance.ErrorData)); string.Join("\n", instance.ErrorData));
} }
private static readonly Regex ProgressRegex = new Regex(@"time=(\d\d:\d\d:\d\d.\d\d?)", RegexOptions.Compiled);
private Action<double>? _onPercentageProgress;
private Action<TimeSpan>? _onTimeProgress;
private TimeSpan? _totalTimespan;
private void OutputData(object sender, (DataType Type, string Data) msg) private void OutputData(object sender, (DataType Type, string Data) msg)
{ {
#if DEBUG
Trace.WriteLine(msg.Data);
#endif
var match = ProgressRegex.Match(msg.Data); var match = ProgressRegex.Match(msg.Data);
if (!match.Success) return; if (!match.Success) return;

View file

@ -32,7 +32,7 @@ private FFMpegArguments(IInputArgument inputArgument)
public static FFMpegArguments FromInputFiles(params FileInfo[] files) => new FFMpegArguments(new InputArgument(false, files)); public static FFMpegArguments FromInputFiles(params FileInfo[] files) => new FFMpegArguments(new InputArgument(false, files));
public static FFMpegArguments FromInputFiles(bool verifyExists, params FileInfo[] files) => new FFMpegArguments(new InputArgument(verifyExists, files)); public static FFMpegArguments FromInputFiles(bool verifyExists, params FileInfo[] files) => new FFMpegArguments(new InputArgument(verifyExists, files));
public static FFMpegArguments FromConcatenation(params string[] files) => new FFMpegArguments(new ConcatArgument(files)); public static FFMpegArguments FromConcatenation(params string[] files) => new FFMpegArguments(new ConcatArgument(files));
public static FFMpegArguments FromPipe(IPipeDataWriter writer) => new FFMpegArguments(new InputPipeArgument(writer)); public static FFMpegArguments FromPipe(IPipeSource writer) => new FFMpegArguments(new InputPipeArgument(writer));
public FFMpegArguments WithAudioCodec(Codec audioCodec) => WithArgument(new AudioCodecArgument(audioCodec)); public FFMpegArguments WithAudioCodec(Codec audioCodec) => WithArgument(new AudioCodecArgument(audioCodec));
@ -87,7 +87,7 @@ private FFMpegArguments(IInputArgument inputArgument)
public FFMpegArgumentProcessor OutputToFile(string file, bool overwrite = false) => ToProcessor(new OutputArgument(file, overwrite)); public FFMpegArgumentProcessor OutputToFile(string file, bool overwrite = false) => ToProcessor(new OutputArgument(file, overwrite));
public FFMpegArgumentProcessor OutputToFile(Uri uri, bool overwrite = false) => ToProcessor(new OutputArgument(uri.AbsolutePath, overwrite)); public FFMpegArgumentProcessor OutputToFile(Uri uri, bool overwrite = false) => ToProcessor(new OutputArgument(uri.AbsolutePath, overwrite));
public FFMpegArgumentProcessor OutputToPipe(IPipeDataReader reader) => ToProcessor(new OutputPipeArgument(reader)); public FFMpegArgumentProcessor OutputToPipe(IPipeSink reader) => ToProcessor(new OutputPipeArgument(reader));
public FFMpegArguments WithArgument(IArgument argument) public FFMpegArguments WithArgument(IArgument argument)
{ {

View file

@ -3,7 +3,7 @@
namespace FFMpegCore.Pipes namespace FFMpegCore.Pipes
{ {
public interface IPipeDataReader public interface IPipeSink
{ {
Task CopyAsync(System.IO.Stream inputStream, CancellationToken cancellationToken); Task CopyAsync(System.IO.Stream inputStream, CancellationToken cancellationToken);
string GetFormat(); string GetFormat();

View file

@ -6,7 +6,7 @@ namespace FFMpegCore.Pipes
/// <summary> /// <summary>
/// Interface for ffmpeg pipe source data IO /// Interface for ffmpeg pipe source data IO
/// </summary> /// </summary>
public interface IPipeDataWriter public interface IPipeSource
{ {
string GetFormat(); string GetFormat();
Task CopyAsync(System.IO.Stream outputStream, CancellationToken cancellationToken); Task CopyAsync(System.IO.Stream outputStream, CancellationToken cancellationToken);

View file

@ -7,9 +7,9 @@
namespace FFMpegCore.Pipes namespace FFMpegCore.Pipes
{ {
/// <summary> /// <summary>
/// Implementation of <see cref="IPipeDataWriter"/> for a raw video stream that is gathered from <see cref="IEnumerator{IVideoFrame}"/> /// Implementation of <see cref="IPipeSource"/> for a raw video stream that is gathered from <see cref="IEnumerator{IVideoFrame}"/>
/// </summary> /// </summary>
public class RawVideoPipeDataWriter : IPipeDataWriter public class RawVideoPipeSource : IPipeSource
{ {
public string StreamFormat { get; private set; } = null!; public string StreamFormat { get; private set; } = null!;
public int Width { get; private set; } public int Width { get; private set; }
@ -18,12 +18,12 @@ public class RawVideoPipeDataWriter : IPipeDataWriter
private bool _formatInitialized; private bool _formatInitialized;
private readonly IEnumerator<IVideoFrame> _framesEnumerator; private readonly IEnumerator<IVideoFrame> _framesEnumerator;
public RawVideoPipeDataWriter(IEnumerator<IVideoFrame> framesEnumerator) public RawVideoPipeSource(IEnumerator<IVideoFrame> framesEnumerator)
{ {
_framesEnumerator = framesEnumerator; _framesEnumerator = framesEnumerator;
} }
public RawVideoPipeDataWriter(IEnumerable<IVideoFrame> framesEnumerator) : this(framesEnumerator.GetEnumerator()) { } public RawVideoPipeSource(IEnumerable<IVideoFrame> framesEnumerator) : this(framesEnumerator.GetEnumerator()) { }
public string GetFormat() public string GetFormat()
{ {

View file

@ -3,13 +3,13 @@
namespace FFMpegCore.Pipes namespace FFMpegCore.Pipes
{ {
public class StreamPipeDataReader : IPipeDataReader public class StreamPipeSink : IPipeSink
{ {
public System.IO.Stream Destination { get; } public System.IO.Stream Destination { get; }
public int BlockSize { get; set; } = 4096; public int BlockSize { get; set; } = 4096;
public string Format { get; set; } = string.Empty; public string Format { get; set; } = string.Empty;
public StreamPipeDataReader(System.IO.Stream destination) public StreamPipeSink(System.IO.Stream destination)
{ {
Destination = destination; Destination = destination;
} }

View file

@ -4,15 +4,15 @@
namespace FFMpegCore.Pipes namespace FFMpegCore.Pipes
{ {
/// <summary> /// <summary>
/// Implementation of <see cref="IPipeDataWriter"/> used for stream redirection /// Implementation of <see cref="IPipeSource"/> used for stream redirection
/// </summary> /// </summary>
public class StreamPipeDataWriter : IPipeDataWriter public class StreamPipeSource : IPipeSource
{ {
public System.IO.Stream Source { get; } public System.IO.Stream Source { get; }
public int BlockSize { get; } = 4096; public int BlockSize { get; } = 4096;
public string StreamFormat { get; } = string.Empty; public string StreamFormat { get; } = string.Empty;
public StreamPipeDataWriter(System.IO.Stream source) public StreamPipeSource(System.IO.Stream source)
{ {
Source = source; Source = source;
} }

View file

@ -20,7 +20,7 @@ public static MediaAnalysis Analyse(string filePath, int outputCapacity = int.Ma
} }
public static MediaAnalysis Analyse(System.IO.Stream stream, int outputCapacity = int.MaxValue) public static MediaAnalysis Analyse(System.IO.Stream stream, int outputCapacity = int.MaxValue)
{ {
var streamPipeSource = new StreamPipeDataWriter(stream); var streamPipeSource = new StreamPipeSource(stream);
var pipeArgument = new InputPipeArgument(streamPipeSource); var pipeArgument = new InputPipeArgument(streamPipeSource);
using var instance = PrepareInstance(pipeArgument.PipePath, outputCapacity); using var instance = PrepareInstance(pipeArgument.PipePath, outputCapacity);
pipeArgument.Pre(); pipeArgument.Pre();
@ -49,7 +49,7 @@ public static async Task<MediaAnalysis> AnalyseAsync(string filePath, int output
} }
public static async Task<MediaAnalysis> AnalyseAsync(System.IO.Stream stream, int outputCapacity = int.MaxValue) public static async Task<MediaAnalysis> AnalyseAsync(System.IO.Stream stream, int outputCapacity = int.MaxValue)
{ {
var streamPipeSource = new StreamPipeDataWriter(stream); var streamPipeSource = new StreamPipeSource(stream);
var pipeArgument = new InputPipeArgument(streamPipeSource); var pipeArgument = new InputPipeArgument(streamPipeSource);
using var instance = PrepareInstance(pipeArgument.PipePath, outputCapacity); using var instance = PrepareInstance(pipeArgument.PipePath, outputCapacity);
pipeArgument.Pre(); pipeArgument.Pre();