mirror of
https://github.com/rosenbjerg/FFMpegCore.git
synced 2024-11-10 08:34:12 +01:00
parent
d948867396
commit
17c9db52dd
12 changed files with 42 additions and 49 deletions
|
@ -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)
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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();
|
|
@ -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);
|
|
@ -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()
|
||||||
{
|
{
|
|
@ -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;
|
||||||
}
|
}
|
|
@ -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;
|
||||||
}
|
}
|
|
@ -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();
|
||||||
|
|
Loading…
Reference in a new issue