From c60e217a2f2dfffe8c10d2f810bc38f3df9cf648 Mon Sep 17 00:00:00 2001 From: Malte Rosenbjerg Date: Thu, 16 Oct 2025 15:00:43 +0200 Subject: [PATCH] Update code after update from main --- FFMpegCore.Test/VideoTest.cs | 138 +++++++++++++++++++++++---- FFMpegCore/FFMpeg/FFMpegArguments.cs | 7 +- 2 files changed, 124 insertions(+), 21 deletions(-) diff --git a/FFMpegCore.Test/VideoTest.cs b/FFMpegCore.Test/VideoTest.cs index c13ef64..0d17cb6 100644 --- a/FFMpegCore.Test/VideoTest.cs +++ b/FFMpegCore.Test/VideoTest.cs @@ -18,22 +18,26 @@ namespace FFMpegCore.Test; public class VideoTest { private const int BaseTimeoutMilliseconds = 15_000; - private string _segmentPathSource = ""; - [TestInitialize] - public void Setup() - { - _segmentPathSource = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}-"); - } - [TestCleanup] - public void Cleanup() - { - foreach (var file in Directory.EnumerateFiles(Path.GetDirectoryName(_segmentPathSource), Path.GetFileName(_segmentPathSource) + "*")) - { - File.Delete(file); - } - } + + private string _segmentPathSource = ""; + public TestContext TestContext { get; set; } + [TestInitialize] + public void Setup() + { + _segmentPathSource = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}-"); + } + + [TestCleanup] + public void Cleanup() + { + foreach (var file in Directory.EnumerateFiles(Path.GetDirectoryName(_segmentPathSource), Path.GetFileName(_segmentPathSource) + "*")) + { + File.Delete(file); + } + } + [TestMethod] [Timeout(BaseTimeoutMilliseconds, CooperativeCancellation = true)] public void Video_ToOGV() @@ -1081,11 +1085,105 @@ public class VideoTest var outputInfo = await FFProbe.AnalyseAsync(outputFile, cancellationToken: TestContext.CancellationToken); - Assert.IsNotNull(outputInfo); - Assert.AreEqual(320, outputInfo.PrimaryVideoStream!.Width); - Assert.AreEqual(240, outputInfo.PrimaryVideoStream.Height); - Assert.AreEqual("h264", outputInfo.PrimaryVideoStream.CodecName); - Assert.AreEqual("aac", outputInfo.PrimaryAudioStream!.CodecName); - } + Assert.IsNotNull(outputInfo); + Assert.AreEqual(320, outputInfo.PrimaryVideoStream!.Width); + Assert.AreEqual(240, outputInfo.PrimaryVideoStream.Height); + Assert.AreEqual("h264", outputInfo.PrimaryVideoStream.CodecName); + Assert.AreEqual("aac", outputInfo.PrimaryAudioStream!.CodecName); + } + + [TestMethod] + [Timeout(BaseTimeoutMilliseconds, CooperativeCancellation = true)] + public void Video_Segmented_File_Output() + { + using var input = File.OpenRead(TestResources.WebmVideo); + var success = FFMpegArguments + .FromPipeInput(new StreamPipeSource(input)) + .OutPutToSegmentedFiles( + new SegmentArgument($"{_segmentPathSource}%Y-%m-%d_%H-%M-%S.mkv", true, segmentOptions => segmentOptions + .Strftime(true) + .Wrap() + .Time() + .ResetTimeStamps()), + options => options + .CopyChannel() + .WithVideoCodec("h264") + .ForceFormat("matroska") + .WithConstantRateFactor(21) + .WithVideoBitrate(3000) + .WithFastStart() + .WithVideoFilters(filterOptions => filterOptions + .Scale(VideoSize.Hd) + .DrawText(DrawTextOptions.Create(@"'%{localtime}.%{eif\:1M*t-1K*trunc(t*1K)\:d\:3}'", + @"C:/Users/yan.gauthier/AppData/Local/Microsoft/Windows/Fonts/Roboto-Regular.ttf") + .WithParameter("fontcolor", "yellow") + .WithParameter("fontsize", "40") + .WithParameter("x", "(w-text_w)") + .WithParameter("y", "(h - text_h)") + .WithParameter("rate", "19") + ) + ) + ) + .CancellableThrough(TestContext.CancellationToken) + .ProcessSynchronously(false); + Assert.IsTrue(success); + } + + [TestMethod] + [Timeout(BaseTimeoutMilliseconds, CooperativeCancellation = true)] + public void Video_MultiOutput_With_Segmented_File_Output() + { + using var input = File.OpenRead(TestResources.WebmVideo); + var success = FFMpegArguments + .FromPipeInput(new StreamPipeSource(input)) + .MultiOutput(args => args + .OutputToFile($"{_segmentPathSource}2", true, options => options + .CopyChannel() + .WithVideoCodec("mjpeg") + .ForceFormat("matroska") + .WithConstantRateFactor(21) + .WithVideoBitrate(4000) + .WithFastStart() + .WithVideoFilters(filterOptions => filterOptions + .Scale(VideoSize.Hd) + .DrawText(DrawTextOptions.Create(@"'%{localtime}.%{eif\:1M*t-1K*trunc(t*1K)\:d\:3}'", + @"C:/Users/yan.gauthier/AppData/Local/Microsoft/Windows/Fonts/Roboto-Regular.ttf") + .WithParameter("fontcolor", "yellow") + .WithParameter("fontsize", "40") + .WithParameter("x", "(w-text_w)") + .WithParameter("y", "(h - text_h)") + .WithParameter("rate", "19") + ) + ) + ) + .OutPutToSegmentedFiles( + new SegmentArgument($"{_segmentPathSource}%Y-%m-%d_%H-%M-%S.mkv", true, segmentOptions => segmentOptions + .Strftime(true) + .Wrap() + .Time() + .ResetTimeStamps()), + options => options + .CopyChannel() + .WithVideoCodec("h264") + .ForceFormat("matroska") + .WithConstantRateFactor(21) + .WithVideoBitrate(3000) + .WithFastStart() + .WithVideoFilters(filterOptions => filterOptions + .Scale(VideoSize.Hd) + .DrawText(DrawTextOptions.Create(@"'%{localtime}.%{eif\:1M*t-1K*trunc(t*1K)\:d\:3}'", + @"C:/Users/yan.gauthier/AppData/Local/Microsoft/Windows/Fonts/Roboto-Regular.ttf") + .WithParameter("fontcolor", "yellow") + .WithParameter("fontsize", "40") + .WithParameter("x", "(w-text_w)") + .WithParameter("y", "(h - text_h)") + .WithParameter("rate", "19") + ) + ) + ) + ) + .CancellableThrough(TestContext.CancellationToken) + .ProcessSynchronously(false); + Assert.IsTrue(success); } } diff --git a/FFMpegCore/FFMpeg/FFMpegArguments.cs b/FFMpegCore/FFMpeg/FFMpegArguments.cs index 20df625..355d938 100644 --- a/FFMpegCore/FFMpeg/FFMpegArguments.cs +++ b/FFMpegCore/FFMpeg/FFMpegArguments.cs @@ -151,7 +151,12 @@ public sealed class FFMpegArguments : FFMpegArgumentsBase { return ToProcessor(new OutputPipeArgument(reader), addArguments); } - public FFMpegArgumentProcessor OutPutToSegmentedFiles(SegmentArgument segmentArgument, Action? addArguments = null) => ToProcessor(new OutputSegmentArgument(segmentArgument), addArguments); + + public FFMpegArgumentProcessor OutPutToSegmentedFiles(SegmentArgument segmentArgument, Action? addArguments = null) + { + return ToProcessor(new OutputSegmentArgument(segmentArgument), addArguments); + } + private FFMpegArgumentProcessor ToProcessor(IOutputArgument argument, Action? addArguments) { var args = new FFMpegArgumentOptions();