diff --git a/FFMpegCore.Test/VideoTest.cs b/FFMpegCore.Test/VideoTest.cs index e4f750d..f4553ef 100644 --- a/FFMpegCore.Test/VideoTest.cs +++ b/FFMpegCore.Test/VideoTest.cs @@ -312,6 +312,23 @@ await FFMpegArguments .ProcessAsynchronously(); }); } + [TestMethod, Timeout(10000)] + public void Video_StreamFile_OutputToMemoryStream() + { + // using var input = File.OpenRead(VideoLibrary.LocalVideo.FullName); + var output = new MemoryStream(); + + FFMpegArguments + // .FromFileInput(VideoLibrary.LocalVideo.FullName) + .FromPipeInput(new StreamPipeSource(File.OpenRead(VideoLibrary.LocalVideoWebm.FullName)), options => options.ForceFormat("webm")) + .OutputToPipe(new StreamPipeSink(output), options => options + .ForceFormat("mp4")) + .ProcessSynchronously(); + + output.Position = 0; + var result = FFProbe.Analyse(output); + Console.WriteLine(result.Duration); + } [TestMethod, Timeout(10000)] public void Video_ToMP4_Args_StreamOutputPipe_Failure() diff --git a/FFMpegCore/FFMpeg/Arguments/InputPipeArgument.cs b/FFMpegCore/FFMpeg/Arguments/InputPipeArgument.cs index adc25fb..685a019 100644 --- a/FFMpegCore/FFMpeg/Arguments/InputPipeArgument.cs +++ b/FFMpegCore/FFMpeg/Arguments/InputPipeArgument.cs @@ -17,7 +17,7 @@ public InputPipeArgument(IPipeSource writer) : base(PipeDirection.Out) Writer = writer; } - public override string Text => $"-y {Writer.GetFormat()} -i \"{PipePath}\""; + public override string Text => $"{(!string.IsNullOrEmpty(Writer.Format) ? $"-f {Writer.Format} " : string.Empty)}-i \"{PipePath}\""; protected override async Task ProcessDataAsync(CancellationToken token) { @@ -25,6 +25,7 @@ protected override async Task ProcessDataAsync(CancellationToken token) if (!Pipe.IsConnected) throw new TaskCanceledException(); await Writer.WriteAsync(Pipe, token).ConfigureAwait(false); + Pipe.Disconnect(); } } } diff --git a/FFMpegCore/FFMpeg/Arguments/OutputPipeArgument.cs b/FFMpegCore/FFMpeg/Arguments/OutputPipeArgument.cs index f089a1e..a2cf9be 100644 --- a/FFMpegCore/FFMpeg/Arguments/OutputPipeArgument.cs +++ b/FFMpegCore/FFMpeg/Arguments/OutputPipeArgument.cs @@ -22,6 +22,7 @@ protected override async Task ProcessDataAsync(CancellationToken token) if (!Pipe.IsConnected) throw new TaskCanceledException(); await Reader.ReadAsync(Pipe, token).ConfigureAwait(false); + Pipe.Disconnect(); } } } diff --git a/FFMpegCore/FFMpeg/Arguments/PipeArgument.cs b/FFMpegCore/FFMpeg/Arguments/PipeArgument.cs index 4a6113a..3225b6c 100644 --- a/FFMpegCore/FFMpeg/Arguments/PipeArgument.cs +++ b/FFMpegCore/FFMpeg/Arguments/PipeArgument.cs @@ -43,7 +43,6 @@ public async Task During(CancellationToken cancellationToken = default) catch (TaskCanceledException) { } - Pipe.Disconnect(); } protected abstract Task ProcessDataAsync(CancellationToken token); diff --git a/FFMpegCore/FFMpeg/FFMpegArgumentProcessor.cs b/FFMpegCore/FFMpeg/FFMpegArgumentProcessor.cs index 8a30dfc..424b598 100644 --- a/FFMpegCore/FFMpeg/FFMpegArgumentProcessor.cs +++ b/FFMpegCore/FFMpeg/FFMpegArgumentProcessor.cs @@ -65,8 +65,8 @@ void OnCancelEvent(object sender, EventArgs args) { errorCode = t.Result; cancellationTokenSource.Cancel(); - _ffMpegArguments.Post(); - }), _ffMpegArguments.During(cancellationTokenSource.Token)); + // _ffMpegArguments.Post(); + }), _ffMpegArguments.During(cancellationTokenSource.Token).ContinueWith(t => _ffMpegArguments.Post())); } catch (Exception e) { @@ -111,8 +111,8 @@ await Task.WhenAll(instance.FinishedRunning().ContinueWith(t => { errorCode = t.Result; cancellationTokenSource.Cancel(); - _ffMpegArguments.Post(); - }), _ffMpegArguments.During(cancellationTokenSource.Token)).ConfigureAwait(false); + // _ffMpegArguments.Post(); + }), _ffMpegArguments.During(cancellationTokenSource.Token).ContinueWith(t => _ffMpegArguments.Post())).ConfigureAwait(false); } catch (Exception e) { diff --git a/FFMpegCore/FFMpeg/Pipes/IPipeSource.cs b/FFMpegCore/FFMpeg/Pipes/IPipeSource.cs index 55fdcc3..5fde4ab 100644 --- a/FFMpegCore/FFMpeg/Pipes/IPipeSource.cs +++ b/FFMpegCore/FFMpeg/Pipes/IPipeSource.cs @@ -8,7 +8,7 @@ namespace FFMpegCore.Pipes /// public interface IPipeSource { - string GetFormat(); + string Format { get; } Task WriteAsync(System.IO.Stream outputStream, CancellationToken cancellationToken); } } diff --git a/FFMpegCore/FFMpeg/Pipes/RawVideoPipeSource.cs b/FFMpegCore/FFMpeg/Pipes/RawVideoPipeSource.cs index eef4343..de3669e 100644 --- a/FFMpegCore/FFMpeg/Pipes/RawVideoPipeSource.cs +++ b/FFMpegCore/FFMpeg/Pipes/RawVideoPipeSource.cs @@ -11,9 +11,10 @@ namespace FFMpegCore.Pipes /// public class RawVideoPipeSource : IPipeSource { - public string StreamFormat { get; private set; } = null!; public int Width { get; private set; } public int Height { get; private set; } + + public string Format { get; private set; } public int FrameRate { get; set; } = 25; private bool _formatInitialized; private readonly IEnumerator _framesEnumerator; @@ -35,14 +36,14 @@ public string GetFormat() if (!_framesEnumerator.MoveNext()) throw new InvalidOperationException("Enumerator is empty, unable to get frame"); } - StreamFormat = _framesEnumerator.Current!.Format; + Format = _framesEnumerator.Current!.Format; Width = _framesEnumerator.Current!.Width; Height = _framesEnumerator.Current!.Height; _formatInitialized = true; } - return $"-f rawvideo -r {FrameRate} -pix_fmt {StreamFormat} -s {Width}x{Height}"; + return $"-f rawvideo -r {FrameRate} -pix_fmt {Format} -s {Width}x{Height}"; } public async Task WriteAsync(System.IO.Stream outputStream, CancellationToken cancellationToken) @@ -62,10 +63,10 @@ public async Task WriteAsync(System.IO.Stream outputStream, CancellationToken ca private void CheckFrameAndThrow(IVideoFrame frame) { - if (frame.Width != Width || frame.Height != Height || frame.Format != StreamFormat) + if (frame.Width != Width || frame.Height != Height || frame.Format != Format) throw new FFMpegException(FFMpegExceptionType.Operation, "Video frame is not the same format as created raw video stream\r\n" + $"Frame format: {frame.Width}x{frame.Height} pix_fmt: {frame.Format}\r\n" + - $"Stream format: {Width}x{Height} pix_fmt: {StreamFormat}"); + $"Stream format: {Width}x{Height} pix_fmt: {Format}"); } } } diff --git a/FFMpegCore/FFMpeg/Pipes/StreamPipeSource.cs b/FFMpegCore/FFMpeg/Pipes/StreamPipeSource.cs index b364037..5d9e666 100644 --- a/FFMpegCore/FFMpeg/Pipes/StreamPipeSource.cs +++ b/FFMpegCore/FFMpeg/Pipes/StreamPipeSource.cs @@ -10,15 +10,13 @@ public class StreamPipeSource : IPipeSource { public System.IO.Stream Source { get; } public int BlockSize { get; } = 4096; - public string StreamFormat { get; } = string.Empty; + + public string Format { get; } public StreamPipeSource(System.IO.Stream source) { Source = source; } - public Task WriteAsync(System.IO.Stream outputStream, CancellationToken cancellationToken) => Source.CopyToAsync(outputStream, BlockSize, cancellationToken); - - public string GetFormat() => StreamFormat; } } diff --git a/FFMpegCore/FFMpegCore.csproj.DotSettings b/FFMpegCore/FFMpegCore.csproj.DotSettings new file mode 100644 index 0000000..69be7ec --- /dev/null +++ b/FFMpegCore/FFMpegCore.csproj.DotSettings @@ -0,0 +1,2 @@ + + True \ No newline at end of file diff --git a/FFMpegCore/FFProbe/FFProbe.cs b/FFMpegCore/FFProbe/FFProbe.cs index 5b7a1e4..5c21b9b 100644 --- a/FFMpegCore/FFProbe/FFProbe.cs +++ b/FFMpegCore/FFProbe/FFProbe.cs @@ -106,7 +106,7 @@ private static Instance PrepareInstance(string filePath, int outputCapacity) { FFProbeHelper.RootExceptionCheck(); FFProbeHelper.VerifyFFProbeExists(); - var arguments = $"-print_format json -show_format -sexagesimal -show_streams \"{filePath}\""; + var arguments = $"-loglevel error -print_format json -show_format -sexagesimal -show_streams \"{filePath}\""; var instance = new Instance(FFMpegOptions.Options.FFProbeBinary(), arguments) {DataBufferCapacity = outputCapacity}; return instance; }