mirror of
https://github.com/rosenbjerg/FFMpegCore.git
synced 2024-11-10 08:34:12 +01:00
Init
This commit is contained in:
parent
af67cc2fbb
commit
4f28983972
21 changed files with 432 additions and 324 deletions
|
@ -8,7 +8,7 @@ namespace FFMpegCore.Test
|
|||
[TestClass]
|
||||
public class ArgumentBuilderTest
|
||||
{
|
||||
private readonly string[] _concatFiles = { "1.mp4", "2.mp4", "3.mp4", "4.mp4"};
|
||||
private readonly string[] _concatFiles = { "1.mp4", "2.mp4", "3.mp4", "4.mp4" };
|
||||
|
||||
|
||||
[TestMethod]
|
||||
|
@ -21,28 +21,35 @@ public void Builder_BuildString_IO_1()
|
|||
[TestMethod]
|
||||
public void Builder_BuildString_Scale()
|
||||
{
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", true, opt => opt.Scale(VideoSize.Hd)).Arguments;
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4")
|
||||
.OutputToFile("output.mp4", true, opt => opt
|
||||
.WithVideoFilters(filterOptions => filterOptions
|
||||
.Scale(VideoSize.Hd)))
|
||||
.Arguments;
|
||||
Assert.AreEqual("-i \"input.mp4\" -vf scale=-1:720 \"output.mp4\" -y", str);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Builder_BuildString_AudioCodec()
|
||||
{
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", true, opt => opt.WithAudioCodec(AudioCodec.Aac)).Arguments;
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4")
|
||||
.OutputToFile("output.mp4", true, opt => opt.WithAudioCodec(AudioCodec.Aac)).Arguments;
|
||||
Assert.AreEqual("-i \"input.mp4\" -c:a aac \"output.mp4\" -y", str);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Builder_BuildString_AudioBitrate()
|
||||
{
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", true, opt => opt.WithAudioBitrate(AudioQuality.Normal)).Arguments;
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4")
|
||||
.OutputToFile("output.mp4", true, opt => opt.WithAudioBitrate(AudioQuality.Normal)).Arguments;
|
||||
Assert.AreEqual("-i \"input.mp4\" -b:a 128k \"output.mp4\" -y", str);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Builder_BuildString_Quiet()
|
||||
{
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4").WithGlobalOptions(opt => opt.WithVerbosityLevel()).OutputToFile("output.mp4", false).Arguments;
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4").WithGlobalOptions(opt => opt.WithVerbosityLevel())
|
||||
.OutputToFile("output.mp4", false).Arguments;
|
||||
Assert.AreEqual("-hide_banner -loglevel error -i \"input.mp4\" \"output.mp4\"", str);
|
||||
}
|
||||
|
||||
|
@ -50,27 +57,32 @@ public void Builder_BuildString_Quiet()
|
|||
[TestMethod]
|
||||
public void Builder_BuildString_AudioCodec_Fluent()
|
||||
{
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.WithAudioCodec(AudioCodec.Aac).WithAudioBitrate(128)).Arguments;
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false,
|
||||
opt => opt.WithAudioCodec(AudioCodec.Aac).WithAudioBitrate(128)).Arguments;
|
||||
Assert.AreEqual("-i \"input.mp4\" -c:a aac -b:a 128k \"output.mp4\"", str);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Builder_BuildString_BitStream()
|
||||
{
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.WithBitStreamFilter(Channel.Audio, Filter.H264_Mp4ToAnnexB)).Arguments;
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false,
|
||||
opt => opt.WithBitStreamFilter(Channel.Audio, Filter.H264_Mp4ToAnnexB)).Arguments;
|
||||
Assert.AreEqual("-i \"input.mp4\" -bsf:a h264_mp4toannexb \"output.mp4\"", str);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Builder_BuildString_HardwareAcceleration_Auto()
|
||||
{
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.WithHardwareAcceleration()).Arguments;
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4")
|
||||
.OutputToFile("output.mp4", false, opt => opt.WithHardwareAcceleration()).Arguments;
|
||||
Assert.AreEqual("-i \"input.mp4\" -hwaccel \"output.mp4\"", str);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Builder_BuildString_HardwareAcceleration_Specific()
|
||||
{
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.WithHardwareAcceleration(HardwareAccelerationDevice.CUVID)).Arguments;
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false,
|
||||
opt => opt.WithHardwareAcceleration(HardwareAccelerationDevice.CUVID)).Arguments;
|
||||
Assert.AreEqual("-i \"input.mp4\" -hwaccel cuvid \"output.mp4\"", str);
|
||||
}
|
||||
|
||||
|
@ -84,140 +96,175 @@ public void Builder_BuildString_Concat()
|
|||
[TestMethod]
|
||||
public void Builder_BuildString_Copy_Audio()
|
||||
{
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.CopyChannel(Channel.Audio)).Arguments;
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4")
|
||||
.OutputToFile("output.mp4", false, opt => opt.CopyChannel(Channel.Audio)).Arguments;
|
||||
Assert.AreEqual("-i \"input.mp4\" -c:a copy \"output.mp4\"", str);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Builder_BuildString_Copy_Video()
|
||||
{
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.CopyChannel(Channel.Video)).Arguments;
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4")
|
||||
.OutputToFile("output.mp4", false, opt => opt.CopyChannel(Channel.Video)).Arguments;
|
||||
Assert.AreEqual("-i \"input.mp4\" -c:v copy \"output.mp4\"", str);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Builder_BuildString_Copy_Both()
|
||||
{
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.CopyChannel()).Arguments;
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4")
|
||||
.OutputToFile("output.mp4", false, opt => opt.CopyChannel()).Arguments;
|
||||
Assert.AreEqual("-i \"input.mp4\" -c copy \"output.mp4\"", str);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Builder_BuildString_DisableChannel_Audio()
|
||||
{
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.DisableChannel(Channel.Audio)).Arguments;
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4")
|
||||
.OutputToFile("output.mp4", false, opt => opt.DisableChannel(Channel.Audio)).Arguments;
|
||||
Assert.AreEqual("-i \"input.mp4\" -an \"output.mp4\"", str);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Builder_BuildString_DisableChannel_Video()
|
||||
{
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.DisableChannel(Channel.Video)).Arguments;
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4")
|
||||
.OutputToFile("output.mp4", false, opt => opt.DisableChannel(Channel.Video)).Arguments;
|
||||
Assert.AreEqual("-i \"input.mp4\" -vn \"output.mp4\"", str);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Builder_BuildString_AudioSamplingRate_Default()
|
||||
{
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.WithAudioSamplingRate()).Arguments;
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4")
|
||||
.OutputToFile("output.mp4", false, opt => opt.WithAudioSamplingRate()).Arguments;
|
||||
Assert.AreEqual("-i \"input.mp4\" -ar 48000 \"output.mp4\"", str);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Builder_BuildString_AudioSamplingRate()
|
||||
{
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.WithAudioSamplingRate(44000)).Arguments;
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4")
|
||||
.OutputToFile("output.mp4", false, opt => opt.WithAudioSamplingRate(44000)).Arguments;
|
||||
Assert.AreEqual("-i \"input.mp4\" -ar 44000 \"output.mp4\"", str);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Builder_BuildString_VariableBitrate()
|
||||
{
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.WithVariableBitrate(5)).Arguments;
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4")
|
||||
.OutputToFile("output.mp4", false, opt => opt.WithVariableBitrate(5)).Arguments;
|
||||
Assert.AreEqual("-i \"input.mp4\" -vbr 5 \"output.mp4\"", str);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Builder_BuildString_Faststart()
|
||||
{
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.WithFastStart()).Arguments;
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4")
|
||||
.OutputToFile("output.mp4", false, opt => opt.WithFastStart()).Arguments;
|
||||
Assert.AreEqual("-i \"input.mp4\" -movflags faststart \"output.mp4\"", str);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Builder_BuildString_Overwrite()
|
||||
{
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.OverwriteExisting()).Arguments;
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4")
|
||||
.OutputToFile("output.mp4", false, opt => opt.OverwriteExisting()).Arguments;
|
||||
Assert.AreEqual("-i \"input.mp4\" -y \"output.mp4\"", str);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Builder_BuildString_RemoveMetadata()
|
||||
{
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.WithoutMetadata()).Arguments;
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4")
|
||||
.OutputToFile("output.mp4", false, opt => opt.WithoutMetadata()).Arguments;
|
||||
Assert.AreEqual("-i \"input.mp4\" -map_metadata -1 \"output.mp4\"", str);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Builder_BuildString_Transpose()
|
||||
{
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.Transpose(Transposition.CounterClockwise90)).Arguments;
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4")
|
||||
.OutputToFile("output.mp4", false, opt => opt
|
||||
.WithVideoFilters(filterOptions => filterOptions
|
||||
.Transpose(Transposition.CounterClockwise90)))
|
||||
.Arguments;
|
||||
Assert.AreEqual("-i \"input.mp4\" -vf \"transpose=2\" \"output.mp4\"", str);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Builder_BuildString_TransposeScale()
|
||||
{
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4")
|
||||
.OutputToFile("output.mp4", false, opt => opt
|
||||
.WithVideoFilters(filterOptions => filterOptions
|
||||
.Transpose(Transposition.CounterClockwise90)
|
||||
.Scale(200, 300)))
|
||||
.Arguments;
|
||||
Assert.AreEqual("-i \"input.mp4\" -vf \"transpose=2, scale=200:300\" \"output.mp4\"", str);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Builder_BuildString_ForceFormat()
|
||||
{
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4", false, opt => opt.ForceFormat(VideoType.Mp4)).OutputToFile("output.mp4", false, opt => opt.ForceFormat(VideoType.Mp4)).Arguments;
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4", false, opt => opt.ForceFormat(VideoType.Mp4))
|
||||
.OutputToFile("output.mp4", false, opt => opt.ForceFormat(VideoType.Mp4)).Arguments;
|
||||
Assert.AreEqual("-f mp4 -i \"input.mp4\" -f mp4 \"output.mp4\"", str);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Builder_BuildString_FrameOutputCount()
|
||||
{
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.WithFrameOutputCount(50)).Arguments;
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4")
|
||||
.OutputToFile("output.mp4", false, opt => opt.WithFrameOutputCount(50)).Arguments;
|
||||
Assert.AreEqual("-i \"input.mp4\" -vframes 50 \"output.mp4\"", str);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Builder_BuildString_FrameRate()
|
||||
{
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.WithFramerate(50)).Arguments;
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4")
|
||||
.OutputToFile("output.mp4", false, opt => opt.WithFramerate(50)).Arguments;
|
||||
Assert.AreEqual("-i \"input.mp4\" -r 50 \"output.mp4\"", str);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Builder_BuildString_Loop()
|
||||
{
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.Loop(50)).Arguments;
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.Loop(50))
|
||||
.Arguments;
|
||||
Assert.AreEqual("-i \"input.mp4\" -loop 50 \"output.mp4\"", str);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Builder_BuildString_Seek()
|
||||
{
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4", false, opt => opt.Seek(TimeSpan.FromSeconds(10))).OutputToFile("output.mp4", false, opt => opt.Seek(TimeSpan.FromSeconds(10))).Arguments;
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4", false, opt => opt.Seek(TimeSpan.FromSeconds(10)))
|
||||
.OutputToFile("output.mp4", false, opt => opt.Seek(TimeSpan.FromSeconds(10))).Arguments;
|
||||
Assert.AreEqual("-ss 00:00:10 -i \"input.mp4\" -ss 00:00:10 \"output.mp4\"", str);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Builder_BuildString_Shortest()
|
||||
{
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.UsingShortest()).Arguments;
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4")
|
||||
.OutputToFile("output.mp4", false, opt => opt.UsingShortest()).Arguments;
|
||||
Assert.AreEqual("-i \"input.mp4\" -shortest \"output.mp4\"", str);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Builder_BuildString_Size()
|
||||
{
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.Resize(1920, 1080)).Arguments;
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4")
|
||||
.OutputToFile("output.mp4", false, opt => opt.Resize(1920, 1080)).Arguments;
|
||||
Assert.AreEqual("-i \"input.mp4\" -s 1920x1080 \"output.mp4\"", str);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Builder_BuildString_Speed()
|
||||
{
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.WithSpeedPreset(Speed.Fast)).Arguments;
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4")
|
||||
.OutputToFile("output.mp4", false, opt => opt.WithSpeedPreset(Speed.Fast)).Arguments;
|
||||
Assert.AreEqual("-i \"input.mp4\" -preset fast \"output.mp4\"", str);
|
||||
}
|
||||
|
||||
|
@ -227,6 +274,7 @@ public void Builder_BuildString_DrawtextFilter()
|
|||
var str = FFMpegArguments
|
||||
.FromFileInput("input.mp4")
|
||||
.OutputToFile("output.mp4", false, opt => opt
|
||||
.WithVideoFilters(filterOptions => filterOptions
|
||||
.DrawText(DrawTextOptions
|
||||
.Create("Stack Overflow", "/path/to/font.ttf")
|
||||
.WithParameter("fontcolor", "white")
|
||||
|
@ -235,10 +283,12 @@ public void Builder_BuildString_DrawtextFilter()
|
|||
.WithParameter("boxcolor", "black@0.5")
|
||||
.WithParameter("boxborderw", "5")
|
||||
.WithParameter("x", "(w-text_w)/2")
|
||||
.WithParameter("y", "(h-text_h)/2")))
|
||||
.WithParameter("y", "(h-text_h)/2"))))
|
||||
.Arguments;
|
||||
|
||||
Assert.AreEqual("-i \"input.mp4\" -vf drawtext=\"text='Stack Overflow':fontfile=/path/to/font.ttf:fontcolor=white:fontsize=24:box=1:boxcolor=black@0.5:boxborderw=5:x=(w-text_w)/2:y=(h-text_h)/2\" \"output.mp4\"", str);
|
||||
Assert.AreEqual(
|
||||
"-i \"input.mp4\" -vf drawtext=\"text='Stack Overflow':fontfile=/path/to/font.ttf:fontcolor=white:fontsize=24:box=1:boxcolor=black@0.5:boxborderw=5:x=(w-text_w)/2:y=(h-text_h)/2\" \"output.mp4\"",
|
||||
str);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
|
@ -247,45 +297,53 @@ public void Builder_BuildString_DrawtextFilter_Alt()
|
|||
var str = FFMpegArguments
|
||||
.FromFileInput("input.mp4")
|
||||
.OutputToFile("output.mp4", false, opt => opt
|
||||
.WithVideoFilters(filterOptions => filterOptions
|
||||
.DrawText(DrawTextOptions
|
||||
.Create("Stack Overflow", "/path/to/font.ttf", ("fontcolor", "white"), ("fontsize", "24"))))
|
||||
.Create("Stack Overflow", "/path/to/font.ttf", ("fontcolor", "white"), ("fontsize", "24")))))
|
||||
.Arguments;
|
||||
|
||||
Assert.AreEqual("-i \"input.mp4\" -vf drawtext=\"text='Stack Overflow':fontfile=/path/to/font.ttf:fontcolor=white:fontsize=24\" \"output.mp4\"", str);
|
||||
Assert.AreEqual(
|
||||
"-i \"input.mp4\" -vf drawtext=\"text='Stack Overflow':fontfile=/path/to/font.ttf:fontcolor=white:fontsize=24\" \"output.mp4\"",
|
||||
str);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Builder_BuildString_StartNumber()
|
||||
{
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.WithStartNumber(50)).Arguments;
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4")
|
||||
.OutputToFile("output.mp4", false, opt => opt.WithStartNumber(50)).Arguments;
|
||||
Assert.AreEqual("-i \"input.mp4\" -start_number 50 \"output.mp4\"", str);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Builder_BuildString_Threads_1()
|
||||
{
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.UsingThreads(50)).Arguments;
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4")
|
||||
.OutputToFile("output.mp4", false, opt => opt.UsingThreads(50)).Arguments;
|
||||
Assert.AreEqual("-i \"input.mp4\" -threads 50 \"output.mp4\"", str);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Builder_BuildString_Threads_2()
|
||||
{
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.UsingMultithreading(true)).Arguments;
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4")
|
||||
.OutputToFile("output.mp4", false, opt => opt.UsingMultithreading(true)).Arguments;
|
||||
Assert.AreEqual($"-i \"input.mp4\" -threads {Environment.ProcessorCount} \"output.mp4\"", str);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Builder_BuildString_Codec()
|
||||
{
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.WithVideoCodec(VideoCodec.LibX264)).Arguments;
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4")
|
||||
.OutputToFile("output.mp4", false, opt => opt.WithVideoCodec(VideoCodec.LibX264)).Arguments;
|
||||
Assert.AreEqual("-i \"input.mp4\" -c:v libx264 \"output.mp4\"", str);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Builder_BuildString_Codec_Override()
|
||||
{
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", true, opt => opt.WithVideoCodec(VideoCodec.LibX264).ForcePixelFormat("yuv420p")).Arguments;
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", true,
|
||||
opt => opt.WithVideoCodec(VideoCodec.LibX264).ForcePixelFormat("yuv420p")).Arguments;
|
||||
Assert.AreEqual("-i \"input.mp4\" -c:v libx264 -pix_fmt yuv420p \"output.mp4\" -y", str);
|
||||
}
|
||||
|
||||
|
@ -293,17 +351,20 @@ public void Builder_BuildString_Codec_Override()
|
|||
[TestMethod]
|
||||
public void Builder_BuildString_Duration()
|
||||
{
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.WithDuration(TimeSpan.FromSeconds(20))).Arguments;
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4")
|
||||
.OutputToFile("output.mp4", false, opt => opt.WithDuration(TimeSpan.FromSeconds(20))).Arguments;
|
||||
Assert.AreEqual("-i \"input.mp4\" -t 00:00:20 \"output.mp4\"", str);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Builder_BuildString_Raw()
|
||||
{
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4", false, opt => opt.WithCustomArgument(null!)).OutputToFile("output.mp4", false, opt => opt.WithCustomArgument(null!)).Arguments;
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4", false, opt => opt.WithCustomArgument(null!))
|
||||
.OutputToFile("output.mp4", false, opt => opt.WithCustomArgument(null!)).Arguments;
|
||||
Assert.AreEqual(" -i \"input.mp4\" \"output.mp4\"", str);
|
||||
|
||||
str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.WithCustomArgument("-acodec copy")).Arguments;
|
||||
str = FFMpegArguments.FromFileInput("input.mp4")
|
||||
.OutputToFile("output.mp4", false, opt => opt.WithCustomArgument("-acodec copy")).Arguments;
|
||||
Assert.AreEqual("-i \"input.mp4\" -acodec copy \"output.mp4\"", str);
|
||||
}
|
||||
|
||||
|
@ -311,7 +372,8 @@ public void Builder_BuildString_Raw()
|
|||
[TestMethod]
|
||||
public void Builder_BuildString_ForcePixelFormat()
|
||||
{
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.ForcePixelFormat("yuv444p")).Arguments;
|
||||
var str = FFMpegArguments.FromFileInput("input.mp4")
|
||||
.OutputToFile("output.mp4", false, opt => opt.ForcePixelFormat("yuv444p")).Arguments;
|
||||
Assert.AreEqual("-i \"input.mp4\" -pix_fmt yuv444p \"output.mp4\"", str);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System.IO;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using FFMpegCore.Test.Resources;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
@ -24,15 +25,20 @@ public async Task Audio_FromStream_Duration()
|
|||
Assert.IsTrue(fileAnalysis.Duration == streamAnalysis.Duration);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Uri_Duration()
|
||||
{
|
||||
var fileAnalysis = await FFProbe.AnalyseAsync(new Uri("https://github.com/rosenbjerg/FFMpegCore/raw/master/FFMpegCore.Test/Resources/input_3sec.webm"));
|
||||
Assert.IsNotNull(fileAnalysis);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Probe_Success()
|
||||
{
|
||||
var info = FFProbe.Analyse(TestResources.Mp4Video);
|
||||
Assert.AreEqual(3, info.Duration.Seconds);
|
||||
Assert.AreEqual(".mp4", info.Extension);
|
||||
Assert.AreEqual(TestResources.Mp4Video, info.Path);
|
||||
|
||||
Assert.AreEqual("5.1", info.PrimaryAudioStream.ChannelLayout);
|
||||
Assert.AreEqual("5.1", info.PrimaryAudioStream!.ChannelLayout);
|
||||
Assert.AreEqual(6, info.PrimaryAudioStream.Channels);
|
||||
Assert.AreEqual("AAC (Advanced Audio Coding)", info.PrimaryAudioStream.CodecLongName);
|
||||
Assert.AreEqual("aac", info.PrimaryAudioStream.CodecName);
|
||||
|
@ -40,7 +46,7 @@ public void Probe_Success()
|
|||
Assert.AreEqual(377351, info.PrimaryAudioStream.BitRate);
|
||||
Assert.AreEqual(48000, info.PrimaryAudioStream.SampleRateHz);
|
||||
|
||||
Assert.AreEqual(1471810, info.PrimaryVideoStream.BitRate);
|
||||
Assert.AreEqual(1471810, info.PrimaryVideoStream!.BitRate);
|
||||
Assert.AreEqual(16, info.PrimaryVideoStream.DisplayAspectRatio.Width);
|
||||
Assert.AreEqual(9, info.PrimaryVideoStream.DisplayAspectRatio.Height);
|
||||
Assert.AreEqual("yuv420p", info.PrimaryVideoStream.PixelFormat);
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace FFMpegCore.Test
|
||||
{
|
||||
static class TasksExtensions
|
||||
{
|
||||
public static T WaitForResult<T>(this Task<T> task) =>
|
||||
task.ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
}
|
||||
}
|
|
@ -57,95 +57,6 @@ public bool Convert(ContainerFormat type, bool multithreaded = false, VideoSize
|
|||
);
|
||||
}
|
||||
|
||||
private void ConvertFromStreamPipe(ContainerFormat type, params IArgument[] arguments)
|
||||
{
|
||||
using var outputFile = new TemporaryFile($"out{type.Extension}");
|
||||
|
||||
var input = FFProbe.Analyse(TestResources.WebmVideo);
|
||||
using var inputStream = File.OpenRead(input.Path);
|
||||
var processor = FFMpegArguments
|
||||
.FromPipeInput(new StreamPipeSource(inputStream))
|
||||
.OutputToFile(outputFile, false, opt =>
|
||||
{
|
||||
foreach (var arg in arguments)
|
||||
opt.WithArgument(arg);
|
||||
});
|
||||
|
||||
var scaling = arguments.OfType<ScaleArgument>().FirstOrDefault();
|
||||
|
||||
var success = processor.ProcessSynchronously();
|
||||
|
||||
var outputVideo = FFProbe.Analyse(outputFile);
|
||||
|
||||
Assert.IsTrue(success);
|
||||
Assert.IsTrue(File.Exists(outputFile));
|
||||
Assert.IsTrue(Math.Abs((outputVideo.Duration - input.Duration).TotalMilliseconds) < 1000.0 / input.PrimaryVideoStream.FrameRate);
|
||||
|
||||
if (scaling?.Size == null)
|
||||
{
|
||||
Assert.AreEqual(outputVideo.PrimaryVideoStream.Width, input.PrimaryVideoStream.Width);
|
||||
Assert.AreEqual(outputVideo.PrimaryVideoStream.Height, input.PrimaryVideoStream.Height);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (scaling.Size.Value.Width != -1)
|
||||
{
|
||||
Assert.AreEqual(outputVideo.PrimaryVideoStream.Width, scaling.Size.Value.Width);
|
||||
}
|
||||
|
||||
if (scaling.Size.Value.Height != -1)
|
||||
{
|
||||
Assert.AreEqual(outputVideo.PrimaryVideoStream.Height, scaling.Size.Value.Height);
|
||||
}
|
||||
|
||||
Assert.AreNotEqual(outputVideo.PrimaryVideoStream.Width, input.PrimaryVideoStream.Width);
|
||||
Assert.AreNotEqual(outputVideo.PrimaryVideoStream.Height, input.PrimaryVideoStream.Height);
|
||||
}
|
||||
}
|
||||
|
||||
private void ConvertToStreamPipe(params IArgument[] arguments)
|
||||
{
|
||||
using var ms = new MemoryStream();
|
||||
var processor = FFMpegArguments
|
||||
.FromFileInput(TestResources.Mp4Video)
|
||||
.OutputToPipe(new StreamPipeSink(ms), opt =>
|
||||
{
|
||||
foreach (var arg in arguments)
|
||||
opt.WithArgument(arg);
|
||||
});
|
||||
|
||||
var scaling = arguments.OfType<ScaleArgument>().FirstOrDefault();
|
||||
|
||||
processor.ProcessSynchronously();
|
||||
|
||||
ms.Position = 0;
|
||||
var outputVideo = FFProbe.Analyse(ms);
|
||||
|
||||
var input = FFProbe.Analyse(TestResources.Mp4Video);
|
||||
// Assert.IsTrue(Math.Abs((outputVideo.Duration - input.Duration).TotalMilliseconds) < 1000.0 / input.PrimaryVideoStream.FrameRate);
|
||||
|
||||
if (scaling?.Size == null)
|
||||
{
|
||||
Assert.AreEqual(outputVideo.PrimaryVideoStream.Width, input.PrimaryVideoStream.Width);
|
||||
Assert.AreEqual(outputVideo.PrimaryVideoStream.Height, input.PrimaryVideoStream.Height);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (scaling.Size.Value.Width != -1)
|
||||
{
|
||||
Assert.AreEqual(outputVideo.PrimaryVideoStream.Width, scaling.Size.Value.Width);
|
||||
}
|
||||
|
||||
if (scaling.Size.Value.Height != -1)
|
||||
{
|
||||
Assert.AreEqual(outputVideo.PrimaryVideoStream.Height, scaling.Size.Value.Height);
|
||||
}
|
||||
|
||||
Assert.AreNotEqual(outputVideo.PrimaryVideoStream.Width, input.PrimaryVideoStream.Width);
|
||||
Assert.AreNotEqual(outputVideo.PrimaryVideoStream.Height, input.PrimaryVideoStream.Height);
|
||||
}
|
||||
}
|
||||
|
||||
public void Convert(ContainerFormat type, Action<IMediaAnalysis> validationMethod, params IArgument[] arguments)
|
||||
{
|
||||
using var outputFile = new TemporaryFile($"out{type.Extension}");
|
||||
|
@ -195,45 +106,6 @@ public void Convert(ContainerFormat type, params IArgument[] inputArguments)
|
|||
Convert(type, null, inputArguments);
|
||||
}
|
||||
|
||||
public void ConvertFromPipe(ContainerFormat type, System.Drawing.Imaging.PixelFormat fmt, params IArgument[] arguments)
|
||||
{
|
||||
using var outputFile = new TemporaryFile($"out{type.Extension}");
|
||||
|
||||
var videoFramesSource = new RawVideoPipeSource(BitmapSource.CreateBitmaps(128, fmt, 256, 256));
|
||||
var processor = FFMpegArguments.FromPipeInput(videoFramesSource).OutputToFile(outputFile, false, opt =>
|
||||
{
|
||||
foreach (var arg in arguments)
|
||||
opt.WithArgument(arg);
|
||||
});
|
||||
var scaling = arguments.OfType<ScaleArgument>().FirstOrDefault();
|
||||
processor.ProcessSynchronously();
|
||||
|
||||
var outputVideo = FFProbe.Analyse(outputFile);
|
||||
|
||||
Assert.IsTrue(File.Exists(outputFile));
|
||||
|
||||
if (scaling?.Size == null)
|
||||
{
|
||||
Assert.AreEqual(outputVideo.PrimaryVideoStream.Width, videoFramesSource.Width);
|
||||
Assert.AreEqual(outputVideo.PrimaryVideoStream.Height, videoFramesSource.Height);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (scaling.Size.Value.Width != -1)
|
||||
{
|
||||
Assert.AreEqual(outputVideo.PrimaryVideoStream.Width, scaling.Size.Value.Width);
|
||||
}
|
||||
|
||||
if (scaling.Size.Value.Height != -1)
|
||||
{
|
||||
Assert.AreEqual(outputVideo.PrimaryVideoStream.Height, scaling.Size.Value.Height);
|
||||
}
|
||||
|
||||
Assert.AreNotEqual(outputVideo.PrimaryVideoStream.Width, videoFramesSource.Width);
|
||||
Assert.AreNotEqual(outputVideo.PrimaryVideoStream.Height, videoFramesSource.Height);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod, Timeout(10000)]
|
||||
public void Video_ToMP4()
|
||||
{
|
||||
|
@ -258,13 +130,31 @@ public void Video_ToMP4_Args()
|
|||
[DataRow(System.Drawing.Imaging.PixelFormat.Format32bppArgb)]
|
||||
public void Video_ToMP4_Args_Pipe(System.Drawing.Imaging.PixelFormat pixelFormat)
|
||||
{
|
||||
ConvertFromPipe(VideoType.Mp4, pixelFormat, new VideoCodecArgument(VideoCodec.LibX264));
|
||||
using var outputFile = new TemporaryFile($"out{VideoType.Mp4.Extension}");
|
||||
|
||||
var videoFramesSource = new RawVideoPipeSource(BitmapSource.CreateBitmaps(128, pixelFormat, 256, 256));
|
||||
var success = FFMpegArguments
|
||||
.FromPipeInput(videoFramesSource)
|
||||
.OutputToFile(outputFile, false, opt => opt
|
||||
.WithVideoCodec(VideoCodec.LibX264))
|
||||
.ProcessSynchronously();
|
||||
Assert.IsTrue(success);
|
||||
}
|
||||
|
||||
[TestMethod, Timeout(10000)]
|
||||
public void Video_ToMP4_Args_StreamPipe()
|
||||
{
|
||||
ConvertFromStreamPipe(VideoType.Mp4, new VideoCodecArgument(VideoCodec.LibX264));
|
||||
using var input = File.OpenRead(TestResources.WebmVideo);
|
||||
using var output = new TemporaryFile($"out{VideoType.Mp4.Extension}");
|
||||
|
||||
var success = FFMpegArguments
|
||||
.FromPipeInput(new StreamPipeSource(input))
|
||||
.OutputToFile(output, false, opt => opt
|
||||
.WithVideoCodec(VideoCodec.LibX264))
|
||||
.ProcessSynchronously();
|
||||
Assert.IsTrue(success);
|
||||
|
||||
var outputVideo = FFProbe.Analyse(output);
|
||||
}
|
||||
|
||||
[TestMethod, Timeout(10000)]
|
||||
|
@ -286,8 +176,9 @@ public void Video_StreamFile_OutputToMemoryStream()
|
|||
var output = new MemoryStream();
|
||||
|
||||
FFMpegArguments
|
||||
.FromPipeInput(new StreamPipeSource(File.OpenRead(TestResources.WebmVideo)), options => options.ForceFormat("webm"))
|
||||
.OutputToPipe(new StreamPipeSink(output), options => options
|
||||
.FromPipeInput(new StreamPipeSource(File.OpenRead(TestResources.WebmVideo)), opt => opt
|
||||
.ForceFormat("webm"))
|
||||
.OutputToPipe(new StreamPipeSink(output), opt => opt
|
||||
.ForceFormat("mpegts"))
|
||||
.ProcessSynchronously();
|
||||
|
||||
|
@ -299,22 +190,31 @@ public void Video_StreamFile_OutputToMemoryStream()
|
|||
[TestMethod, Timeout(10000)]
|
||||
public void Video_ToMP4_Args_StreamOutputPipe_Failure()
|
||||
{
|
||||
Assert.ThrowsException<FFMpegException>(() => ConvertToStreamPipe(new ForceFormatArgument("mkv")));
|
||||
Assert.ThrowsException<FFMpegException>(() =>
|
||||
{
|
||||
using var ms = new MemoryStream();
|
||||
var processor = FFMpegArguments
|
||||
.FromFileInput(TestResources.Mp4Video)
|
||||
.OutputToPipe(new StreamPipeSink(ms), opt => opt
|
||||
.ForceFormat("mkv"))
|
||||
.ProcessSynchronously();
|
||||
ms.Position = 0;
|
||||
var outputVideo = FFProbe.Analyse(ms);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
[TestMethod, Timeout(10000)]
|
||||
public void Video_ToMP4_Args_StreamOutputPipe_Async()
|
||||
public async Task Video_ToMP4_Args_StreamOutputPipe_Async()
|
||||
{
|
||||
using var ms = new MemoryStream();
|
||||
await using var ms = new MemoryStream();
|
||||
var pipeSource = new StreamPipeSink(ms);
|
||||
FFMpegArguments
|
||||
await FFMpegArguments
|
||||
.FromFileInput(TestResources.Mp4Video)
|
||||
.OutputToPipe(pipeSource, opt => opt
|
||||
.WithVideoCodec(VideoCodec.LibX264)
|
||||
.ForceFormat("matroska"))
|
||||
.ProcessAsynchronously()
|
||||
.WaitForResult();
|
||||
.ProcessAsynchronously();
|
||||
}
|
||||
|
||||
[TestMethod, Timeout(10000)]
|
||||
|
@ -334,7 +234,19 @@ await FFMpegArguments.FromFileInput(TestResources.Mp4Video)
|
|||
[TestMethod, Timeout(10000)]
|
||||
public void Video_ToMP4_Args_StreamOutputPipe()
|
||||
{
|
||||
ConvertToStreamPipe(new VideoCodecArgument(VideoCodec.LibX264), new ForceFormatArgument("matroska"));
|
||||
using var input = new MemoryStream();
|
||||
var success = FFMpegArguments
|
||||
.FromFileInput(TestResources.Mp4Video)
|
||||
.OutputToPipe(new StreamPipeSink(input), opt => opt
|
||||
.WithVideoCodec(VideoCodec.LibVpx)
|
||||
.ForceFormat("matroska"))
|
||||
.ProcessSynchronously();
|
||||
Assert.IsTrue(success);
|
||||
|
||||
input.Position = 0;
|
||||
var inputAnalysis = FFProbe.Analyse(TestResources.Mp4Video);
|
||||
var outputAnalysis = FFProbe.Analyse(input);
|
||||
Assert.AreEqual(inputAnalysis.Duration.TotalSeconds, outputAnalysis.Duration.TotalSeconds, 0.3);
|
||||
}
|
||||
|
||||
[TestMethod, Timeout(10000)]
|
||||
|
@ -355,42 +267,73 @@ public void Video_ToTS_Args()
|
|||
[DataTestMethod, Timeout(10000)]
|
||||
[DataRow(System.Drawing.Imaging.PixelFormat.Format24bppRgb)]
|
||||
[DataRow(System.Drawing.Imaging.PixelFormat.Format32bppArgb)]
|
||||
public void Video_ToTS_Args_Pipe(System.Drawing.Imaging.PixelFormat pixelFormat)
|
||||
public async Task Video_ToTS_Args_Pipe(System.Drawing.Imaging.PixelFormat pixelFormat)
|
||||
{
|
||||
ConvertFromPipe(VideoType.Ts, pixelFormat, new ForceFormatArgument(VideoType.Ts));
|
||||
using var output = new TemporaryFile($"out{VideoType.Ts.Extension}");
|
||||
var input = new RawVideoPipeSource(BitmapSource.CreateBitmaps(128, pixelFormat, 256, 256));
|
||||
|
||||
var success = await FFMpegArguments
|
||||
.FromPipeInput(input)
|
||||
.OutputToFile(output, false, opt => opt
|
||||
.ForceFormat(VideoType.Ts))
|
||||
.ProcessAsynchronously();
|
||||
Assert.IsTrue(success);
|
||||
|
||||
var analysis = await FFProbe.AnalyseAsync(output);
|
||||
Assert.AreEqual(VideoType.Ts.Name, analysis.Format.FormatName);
|
||||
}
|
||||
|
||||
[TestMethod, Timeout(10000)]
|
||||
public void Video_ToOGV_Resize()
|
||||
public async Task Video_ToOGV_Resize()
|
||||
{
|
||||
Convert(VideoType.Ogv, true, VideoSize.Ed);
|
||||
}
|
||||
|
||||
[TestMethod, Timeout(10000)]
|
||||
public void Video_ToOGV_Resize_Args()
|
||||
{
|
||||
Convert(VideoType.Ogv, new ScaleArgument(VideoSize.Ed), new VideoCodecArgument(VideoCodec.LibTheora));
|
||||
using var outputFile = new TemporaryFile($"out{VideoType.Ogv.Extension}");
|
||||
var success = await FFMpegArguments
|
||||
.FromFileInput(TestResources.Mp4Video)
|
||||
.OutputToFile(outputFile, false, opt => opt
|
||||
.Resize(VideoSize.Ed)
|
||||
.WithVideoCodec(VideoCodec.LibTheora))
|
||||
.ProcessAsynchronously();
|
||||
Assert.IsTrue(success);
|
||||
}
|
||||
|
||||
[DataTestMethod, Timeout(10000)]
|
||||
[DataRow(System.Drawing.Imaging.PixelFormat.Format24bppRgb)]
|
||||
[DataRow(System.Drawing.Imaging.PixelFormat.Format32bppArgb)]
|
||||
// [DataRow(PixelFormat.Format48bppRgb)]
|
||||
public void Video_ToOGV_Resize_Args_Pipe(System.Drawing.Imaging.PixelFormat pixelFormat)
|
||||
public void RawVideoPipeSource_Ogv_Scale(System.Drawing.Imaging.PixelFormat pixelFormat)
|
||||
{
|
||||
ConvertFromPipe(VideoType.Ogv, pixelFormat, new ScaleArgument(VideoSize.Ed), new VideoCodecArgument(VideoCodec.LibTheora));
|
||||
using var outputFile = new TemporaryFile($"out{VideoType.Ogv.Extension}");
|
||||
var videoFramesSource = new RawVideoPipeSource(BitmapSource.CreateBitmaps(128, pixelFormat, 256, 256));
|
||||
|
||||
FFMpegArguments
|
||||
.FromPipeInput(videoFramesSource)
|
||||
.OutputToFile(outputFile, false, opt => opt
|
||||
.WithVideoFilters(filterOptions => filterOptions
|
||||
.Scale(VideoSize.Ed))
|
||||
.WithVideoCodec(VideoCodec.LibTheora))
|
||||
.ProcessSynchronously();
|
||||
|
||||
var analysis = FFProbe.Analyse(outputFile);
|
||||
Assert.Equals((int)VideoSize.Ed, analysis!.PrimaryVideoStream.Width);
|
||||
}
|
||||
|
||||
[TestMethod, Timeout(10000)]
|
||||
public void Video_ToMP4_Resize()
|
||||
public void Scale_Mp4_Multithreaded()
|
||||
{
|
||||
Convert(VideoType.Mp4, true, VideoSize.Ed);
|
||||
}
|
||||
using var outputFile = new TemporaryFile($"out{VideoType.Mp4.Extension}");
|
||||
|
||||
[TestMethod, Timeout(10000)]
|
||||
public void Video_ToMP4_Resize_Args()
|
||||
{
|
||||
Convert(VideoType.Mp4, new ScaleArgument(VideoSize.Ld), new VideoCodecArgument(VideoCodec.LibX264));
|
||||
var success = FFMpegArguments
|
||||
.FromFileInput(TestResources.Mp4Video)
|
||||
.OutputToFile(outputFile, false, opt => opt
|
||||
.UsingMultithreading(true)
|
||||
.WithVideoFilters(filterOptions => filterOptions
|
||||
.Scale(VideoSize.Ld))
|
||||
.WithVideoCodec(VideoCodec.LibX264))
|
||||
.ProcessSynchronously();
|
||||
Assert.IsTrue(success);
|
||||
|
||||
var analysis = FFProbe.Analyse(outputFile);
|
||||
Assert.AreEqual((int)VideoSize.Ld, analysis!.PrimaryVideoStream.Width);
|
||||
}
|
||||
|
||||
[DataTestMethod, Timeout(10000)]
|
||||
|
@ -399,7 +342,19 @@ public void Video_ToMP4_Resize_Args()
|
|||
// [DataRow(PixelFormat.Format48bppRgb)]
|
||||
public void Video_ToMP4_Resize_Args_Pipe(System.Drawing.Imaging.PixelFormat pixelFormat)
|
||||
{
|
||||
ConvertFromPipe(VideoType.Mp4, pixelFormat, new ScaleArgument(VideoSize.Ld), new VideoCodecArgument(VideoCodec.LibX264));
|
||||
using var outputFile = new TemporaryFile($"out{VideoType.Mp4.Extension}");
|
||||
var videoFramesSource = new RawVideoPipeSource(BitmapSource.CreateBitmaps(128, pixelFormat, 256, 256));
|
||||
|
||||
var success = FFMpegArguments
|
||||
.FromPipeInput(videoFramesSource)
|
||||
.OutputToFile(outputFile, false, opt => opt
|
||||
.Resize(VideoSize.Ld)
|
||||
.WithVideoCodec(VideoCodec.LibX264))
|
||||
.ProcessSynchronously();
|
||||
Assert.IsTrue(success);
|
||||
|
||||
var analysis = FFProbe.Analyse(outputFile);
|
||||
Assert.AreEqual((int)VideoSize.Ld, analysis!.PrimaryVideoStream.Width);
|
||||
}
|
||||
|
||||
[TestMethod, Timeout(10000)]
|
||||
|
|
|
@ -6,7 +6,7 @@ namespace FFMpegCore.Arguments
|
|||
/// <summary>
|
||||
/// Drawtext video filter argument
|
||||
/// </summary>
|
||||
public class DrawTextArgument : IArgument
|
||||
public class DrawTextArgument : IVideoFilterArgument
|
||||
{
|
||||
public readonly DrawTextOptions Options;
|
||||
|
||||
|
@ -15,7 +15,8 @@ public DrawTextArgument(DrawTextOptions options)
|
|||
Options = options;
|
||||
}
|
||||
|
||||
public string Text => $"-vf drawtext=\"{Options.TextInternal}\"";
|
||||
public string Key { get; } = "drawtext";
|
||||
public string Value => Options.TextInternal;
|
||||
}
|
||||
|
||||
public class DrawTextOptions
|
||||
|
|
|
@ -6,7 +6,7 @@ namespace FFMpegCore.Arguments
|
|||
/// <summary>
|
||||
/// Represents scale parameter
|
||||
/// </summary>
|
||||
public class ScaleArgument : IArgument
|
||||
public class ScaleArgument : IVideoFilterArgument
|
||||
{
|
||||
public readonly Size? Size;
|
||||
public ScaleArgument(Size? size)
|
||||
|
@ -18,9 +18,10 @@ public ScaleArgument(int width, int height) : this(new Size(width, height)) { }
|
|||
|
||||
public ScaleArgument(VideoSize videosize)
|
||||
{
|
||||
Size = videosize == VideoSize.Original ? new Size(-1, -1) : new Size(-1, (int)videosize);
|
||||
Size = videosize == VideoSize.Original ? null : (Size?)new Size(-1, (int)videosize);
|
||||
}
|
||||
|
||||
public virtual string Text => Size.HasValue ? $"-vf scale={Size.Value.Width}:{Size.Value.Height}" : string.Empty;
|
||||
public string Key { get; } = "scale";
|
||||
public string Value => Size == null ? string.Empty : $"{Size.Value.Width}:{Size.Value.Height}";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,14 +6,21 @@ namespace FFMpegCore.Arguments
|
|||
/// <summary>
|
||||
/// Represents size parameter
|
||||
/// </summary>
|
||||
public class SizeArgument : ScaleArgument
|
||||
public class SizeArgument : IArgument
|
||||
{
|
||||
public SizeArgument(Size? value) : base(value) { }
|
||||
public readonly Size? Size;
|
||||
public SizeArgument(Size? size)
|
||||
{
|
||||
Size = size;
|
||||
}
|
||||
|
||||
public SizeArgument(VideoSize videosize) : base(videosize) { }
|
||||
public SizeArgument(int width, int height) : this(new Size(width, height)) { }
|
||||
|
||||
public SizeArgument(int width, int height) : base(width, height) { }
|
||||
public SizeArgument(VideoSize videosize)
|
||||
{
|
||||
Size = videosize == VideoSize.Original ? new Size(-1, -1) : new Size(-1, (int)videosize);
|
||||
}
|
||||
|
||||
public override string Text => Size.HasValue ? $"-s {Size.Value.Width}x{Size.Value.Height}" : string.Empty;
|
||||
public string Text => Size == null ? string.Empty : $"-s {Size.Value.Width}x{Size.Value.Height}";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ namespace FFMpegCore.Arguments
|
|||
/// 2 = 90CounterClockwise
|
||||
/// 3 = 90Clockwise and Vertical Flip
|
||||
/// </summary>
|
||||
public class TransposeArgument : IArgument
|
||||
public class TransposeArgument : IVideoFilterArgument
|
||||
{
|
||||
public readonly Transposition Transposition;
|
||||
public TransposeArgument(Transposition transposition)
|
||||
|
@ -17,6 +17,7 @@ public TransposeArgument(Transposition transposition)
|
|||
Transposition = transposition;
|
||||
}
|
||||
|
||||
public string Text => $"-vf \"transpose={(int)Transposition}\"";
|
||||
public string Key { get; } = "transpose";
|
||||
public string Value => ((int)Transposition).ToString();
|
||||
}
|
||||
}
|
50
FFMpegCore/FFMpeg/Arguments/VideoFiltersArgument.cs
Normal file
50
FFMpegCore/FFMpeg/Arguments/VideoFiltersArgument.cs
Normal file
|
@ -0,0 +1,50 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using FFMpegCore.Enums;
|
||||
using FFMpegCore.Exceptions;
|
||||
|
||||
namespace FFMpegCore.Arguments
|
||||
{
|
||||
public class VideoFiltersArgument : IArgument
|
||||
{
|
||||
public readonly VideoFilterOptions Options;
|
||||
|
||||
public VideoFiltersArgument(VideoFilterOptions options)
|
||||
{
|
||||
Options = options;
|
||||
}
|
||||
public string Text { get; set; }
|
||||
|
||||
public string GetText()
|
||||
{
|
||||
if (!Options.Arguments.Any())
|
||||
throw new FFMpegArgumentException("No video-filter arguments provided");
|
||||
|
||||
return $"-vf \"{string.Join(", ", Options.Arguments.Where(arg => !string.IsNullOrEmpty(arg.Value)).Select(arg => $"{arg.Key}={arg.Value.Replace(",", "\\,")}"))}\"";
|
||||
}
|
||||
}
|
||||
|
||||
public interface IVideoFilterArgument
|
||||
{
|
||||
public string Key { get; }
|
||||
public string Value { get; }
|
||||
}
|
||||
|
||||
public class VideoFilterOptions
|
||||
{
|
||||
public List<IVideoFilterArgument> Arguments { get; } = new List<IVideoFilterArgument>();
|
||||
|
||||
public VideoFilterOptions Scale(VideoSize videoSize) => WithArgument(new ScaleArgument(videoSize));
|
||||
public VideoFilterOptions Scale(int width, int height) => WithArgument(new ScaleArgument(width, height));
|
||||
public VideoFilterOptions Scale(Size size) => WithArgument(new ScaleArgument(size));
|
||||
public VideoFilterOptions Transpose(Transposition transposition) => WithArgument(new TransposeArgument(transposition));
|
||||
public VideoFilterOptions DrawText(DrawTextOptions drawTextOptions) => WithArgument(new DrawTextArgument(drawTextOptions));
|
||||
|
||||
private VideoFilterOptions WithArgument(IVideoFilterArgument argument)
|
||||
{
|
||||
Arguments.Add(argument);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -11,18 +11,52 @@ public enum FFMpegExceptionType
|
|||
Process
|
||||
}
|
||||
|
||||
public abstract class FFException : Exception
|
||||
{
|
||||
protected FFException(string message) : base(message) { }
|
||||
protected FFException(string message, Exception innerException) : base(message, innerException) { }
|
||||
}
|
||||
public abstract class FFProcessException : FFException
|
||||
{
|
||||
protected FFProcessException(string process, int exitCode, string errorOutput)
|
||||
: base($"{process} exited with non-zero exit-code {exitCode}\n{errorOutput}")
|
||||
{
|
||||
ExitCode = exitCode;
|
||||
ErrorOutput = errorOutput;
|
||||
}
|
||||
|
||||
public int ExitCode { get; }
|
||||
public string ErrorOutput { get; }
|
||||
}
|
||||
public class FFMpegProcessException : FFProcessException
|
||||
{
|
||||
public FFMpegProcessException(int exitCode, string errorOutput)
|
||||
: base("ffmpeg", exitCode, errorOutput) { }
|
||||
}
|
||||
public class FFProbeProcessException : FFProcessException
|
||||
{
|
||||
public FFProbeProcessException(int exitCode, string errorOutput)
|
||||
: base("ffprobe", exitCode, errorOutput) { }
|
||||
}
|
||||
|
||||
public class FFMpegException : Exception
|
||||
{
|
||||
public FFMpegException(FFMpegExceptionType type, string? message = null, Exception? innerException = null, string ffmpegErrorOutput = "", string ffmpegOutput = "")
|
||||
public FFMpegException(FFMpegExceptionType type, string? message = null, Exception? innerException = null, string ffMpegErrorOutput = "")
|
||||
: base(message, innerException)
|
||||
{
|
||||
FfmpegOutput = ffmpegOutput;
|
||||
FfmpegErrorOutput = ffmpegErrorOutput;
|
||||
FFMpegErrorOutput = ffMpegErrorOutput;
|
||||
Type = type;
|
||||
}
|
||||
|
||||
public FFMpegExceptionType Type { get; }
|
||||
public string FfmpegOutput { get; }
|
||||
public string FfmpegErrorOutput { get; }
|
||||
public string FFMpegErrorOutput { get; }
|
||||
}
|
||||
|
||||
public class FFMpegArgumentException : Exception
|
||||
{
|
||||
public FFMpegArgumentException(string? message = null, Exception? innerException = null)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,6 +8,7 @@
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using FFMpegCore.Arguments;
|
||||
|
||||
namespace FFMpegCore
|
||||
{
|
||||
|
@ -171,7 +172,8 @@ public static bool Convert(
|
|||
.UsingMultithreading(multithreaded)
|
||||
.WithVideoCodec(VideoCodec.LibX264)
|
||||
.WithVideoBitrate(2400)
|
||||
.Scale(outputSize)
|
||||
.WithVideoFilters(filterOptions => filterOptions
|
||||
.Scale(outputSize))
|
||||
.WithSpeedPreset(speed)
|
||||
.WithAudioCodec(AudioCodec.Aac)
|
||||
.WithAudioBitrate(audioQuality))
|
||||
|
@ -182,7 +184,8 @@ public static bool Convert(
|
|||
.UsingMultithreading(multithreaded)
|
||||
.WithVideoCodec(VideoCodec.LibTheora)
|
||||
.WithVideoBitrate(2400)
|
||||
.Scale(outputSize)
|
||||
.WithVideoFilters(filterOptions => filterOptions
|
||||
.Scale(outputSize))
|
||||
.WithSpeedPreset(speed)
|
||||
.WithAudioCodec(AudioCodec.LibVorbis)
|
||||
.WithAudioBitrate(audioQuality))
|
||||
|
@ -200,7 +203,8 @@ public static bool Convert(
|
|||
.UsingMultithreading(multithreaded)
|
||||
.WithVideoCodec(VideoCodec.LibVpx)
|
||||
.WithVideoBitrate(2400)
|
||||
.Scale(outputSize)
|
||||
.WithVideoFilters(filterOptions => filterOptions
|
||||
.Scale(outputSize))
|
||||
.WithSpeedPreset(speed)
|
||||
.WithAudioCodec(AudioCodec.LibVorbis)
|
||||
.WithAudioBitrate(audioQuality))
|
||||
|
@ -398,7 +402,7 @@ internal static IReadOnlyList<PixelFormat> GetPixelFormatsInternal()
|
|||
FFMpegHelper.RootExceptionCheck();
|
||||
|
||||
var list = new List<PixelFormat>();
|
||||
using var instance = new Instances.Instance(FFMpegOptions.Options.FFmpegBinary(), "-pix_fmts");
|
||||
using var instance = new Instances.Instance(FFMpegOptions.Options.FFMpegBinary(), "-pix_fmts");
|
||||
instance.DataReceived += (e, args) =>
|
||||
{
|
||||
if (PixelFormat.TryParse(args.Data, out var format))
|
||||
|
@ -443,7 +447,7 @@ private static void ParsePartOfCodecs(Dictionary<string, Codec> codecs, string a
|
|||
{
|
||||
FFMpegHelper.RootExceptionCheck();
|
||||
|
||||
using var instance = new Instances.Instance(FFMpegOptions.Options.FFmpegBinary(), arguments);
|
||||
using var instance = new Instances.Instance(FFMpegOptions.Options.FFMpegBinary(), arguments);
|
||||
instance.DataReceived += (e, args) =>
|
||||
{
|
||||
var codec = parser(args.Data);
|
||||
|
@ -527,7 +531,7 @@ internal static IReadOnlyList<ContainerFormat> GetContainersFormatsInternal()
|
|||
FFMpegHelper.RootExceptionCheck();
|
||||
|
||||
var list = new List<ContainerFormat>();
|
||||
using var instance = new Instances.Instance(FFMpegOptions.Options.FFmpegBinary(), "-formats");
|
||||
using var instance = new Instances.Instance(FFMpegOptions.Options.FFMpegBinary(), "-formats");
|
||||
instance.DataReceived += (e, args) =>
|
||||
{
|
||||
if (ContainerFormat.TryParse(args.Data, out var fmt))
|
||||
|
|
|
@ -20,9 +20,7 @@ internal FFMpegArgumentOptions() { }
|
|||
public FFMpegArgumentOptions Resize(int width, int height) => WithArgument(new SizeArgument(width, height));
|
||||
public FFMpegArgumentOptions Resize(Size? size) => WithArgument(new SizeArgument(size));
|
||||
|
||||
public FFMpegArgumentOptions Scale(VideoSize videoSize) => WithArgument(new ScaleArgument(videoSize));
|
||||
public FFMpegArgumentOptions Scale(int width, int height) => WithArgument(new ScaleArgument(width, height));
|
||||
public FFMpegArgumentOptions Scale(Size size) => WithArgument(new ScaleArgument(size));
|
||||
|
||||
|
||||
public FFMpegArgumentOptions WithBitStreamFilter(Channel channel, Filter filter) => WithArgument(new BitStreamFilterArgument(channel, filter));
|
||||
public FFMpegArgumentOptions WithConstantRateFactor(int crf) => WithArgument(new ConstantRateFactorArgument(crf));
|
||||
|
@ -40,6 +38,13 @@ internal FFMpegArgumentOptions() { }
|
|||
public FFMpegArgumentOptions WithVideoCodec(Codec videoCodec) => WithArgument(new VideoCodecArgument(videoCodec));
|
||||
public FFMpegArgumentOptions WithVideoCodec(string videoCodec) => WithArgument(new VideoCodecArgument(videoCodec));
|
||||
public FFMpegArgumentOptions WithVideoBitrate(int bitrate) => WithArgument(new VideoBitrateArgument(bitrate));
|
||||
public FFMpegArgumentOptions WithVideoFilters(Action<VideoFilterOptions> videoFilterOptions)
|
||||
{
|
||||
var videoFilterOptionsObj = new VideoFilterOptions();
|
||||
videoFilterOptions(videoFilterOptionsObj);
|
||||
return WithArgument(new VideoFiltersArgument(videoFilterOptionsObj));
|
||||
}
|
||||
|
||||
public FFMpegArgumentOptions WithFramerate(double framerate) => WithArgument(new FrameRateArgument(framerate));
|
||||
public FFMpegArgumentOptions WithoutMetadata() => WithArgument(new RemoveMetadataArgument());
|
||||
public FFMpegArgumentOptions WithSpeedPreset(Speed speed) => WithArgument(new SpeedPresetArgument(speed));
|
||||
|
@ -47,7 +52,6 @@ internal FFMpegArgumentOptions() { }
|
|||
public FFMpegArgumentOptions WithCustomArgument(string argument) => WithArgument(new CustomArgument(argument));
|
||||
|
||||
public FFMpegArgumentOptions Seek(TimeSpan? seekTo) => WithArgument(new SeekArgument(seekTo));
|
||||
public FFMpegArgumentOptions Transpose(Transposition transposition) => WithArgument(new TransposeArgument(transposition));
|
||||
public FFMpegArgumentOptions Loop(int times) => WithArgument(new LoopArgument(times));
|
||||
public FFMpegArgumentOptions OverwriteExisting() => WithArgument(new OverwriteArgument());
|
||||
|
||||
|
@ -56,8 +60,6 @@ internal FFMpegArgumentOptions() { }
|
|||
public FFMpegArgumentOptions ForcePixelFormat(string pixelFormat) => WithArgument(new ForcePixelFormat(pixelFormat));
|
||||
public FFMpegArgumentOptions ForcePixelFormat(PixelFormat pixelFormat) => WithArgument(new ForcePixelFormat(pixelFormat));
|
||||
|
||||
public FFMpegArgumentOptions DrawText(DrawTextOptions drawTextOptions) => WithArgument(new DrawTextArgument(drawTextOptions));
|
||||
|
||||
public FFMpegArgumentOptions WithArgument(IArgument argument)
|
||||
{
|
||||
Arguments.Add(argument);
|
||||
|
|
|
@ -83,18 +83,18 @@ void OnCancelEvent(object sender, EventArgs args)
|
|||
CancelEvent -= OnCancelEvent;
|
||||
}
|
||||
|
||||
return HandleCompletion(throwOnError, errorCode, instance.ErrorData, instance.OutputData);
|
||||
return HandleCompletion(throwOnError, errorCode, instance.ErrorData);
|
||||
}
|
||||
|
||||
private bool HandleCompletion(bool throwOnError, int errorCode, IReadOnlyList<string> errorData, IReadOnlyList<string> outputData)
|
||||
private bool HandleCompletion(bool throwOnError, int exitCode, IReadOnlyList<string> errorData)
|
||||
{
|
||||
if (throwOnError && errorCode != 0)
|
||||
throw new FFMpegException(FFMpegExceptionType.Conversion, "FFMpeg exited with non-zero exitcode.", null, string.Join("\n", errorData), string.Join("\n", outputData));
|
||||
if (throwOnError && exitCode != 0)
|
||||
throw new FFMpegProcessException(exitCode, string.Join("\n", errorData));
|
||||
|
||||
_onPercentageProgress?.Invoke(100.0);
|
||||
if (_totalTimespan.HasValue) _onTimeProgress?.Invoke(_totalTimespan.Value);
|
||||
|
||||
return errorCode == 0;
|
||||
return exitCode == 0;
|
||||
}
|
||||
|
||||
public async Task<bool> ProcessAsynchronously(bool throwOnError = true)
|
||||
|
@ -122,14 +122,14 @@ await Task.WhenAll(instance.FinishedRunning().ContinueWith(t =>
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (!HandleException(throwOnError, e, instance.ErrorData, instance.OutputData)) return false;
|
||||
if (!HandleException(throwOnError, e, instance.ErrorData)) return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
CancelEvent -= OnCancelEvent;
|
||||
}
|
||||
|
||||
return HandleCompletion(throwOnError, errorCode, instance.ErrorData, instance.OutputData);
|
||||
return HandleCompletion(throwOnError, errorCode, instance.ErrorData);
|
||||
}
|
||||
|
||||
private Instance PrepareInstance(out CancellationTokenSource cancellationTokenSource)
|
||||
|
@ -138,7 +138,7 @@ private Instance PrepareInstance(out CancellationTokenSource cancellationTokenSo
|
|||
FFMpegHelper.VerifyFFMpegExists();
|
||||
var startInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = FFMpegOptions.Options.FFmpegBinary(),
|
||||
FileName = FFMpegOptions.Options.FFMpegBinary(),
|
||||
Arguments = _ffMpegArguments.Text,
|
||||
StandardOutputEncoding = FFMpegOptions.Options.Encoding,
|
||||
StandardErrorEncoding = FFMpegOptions.Options.Encoding,
|
||||
|
@ -153,12 +153,13 @@ private Instance PrepareInstance(out CancellationTokenSource cancellationTokenSo
|
|||
}
|
||||
|
||||
|
||||
private static bool HandleException(bool throwOnError, Exception e, IReadOnlyList<string> errorData, IReadOnlyList<string> outputData)
|
||||
private static bool HandleException(bool throwOnError, Exception e, IReadOnlyList<string> errorData)
|
||||
{
|
||||
if (!throwOnError)
|
||||
return false;
|
||||
|
||||
throw new FFMpegException(FFMpegExceptionType.Process, "Exception thrown during processing", e, string.Join("\n", errorData), string.Join("\n", outputData));
|
||||
throw new FFMpegProcessException(exitCode, string.Join("\n", errorData));
|
||||
throw new FFMpegException(FFMpegExceptionType.Process, "Exception thrown during processing", e, string.Join("\n", errorData));
|
||||
}
|
||||
|
||||
private void OutputData(object sender, (DataType Type, string Data) msg)
|
||||
|
|
|
@ -21,7 +21,6 @@ private FFMpegArguments() { }
|
|||
public static FFMpegArguments FromDemuxConcatInput(IEnumerable<string> filePaths, Action<FFMpegArgumentOptions>? addArguments = null) => new FFMpegArguments().WithInput(new DemuxConcatArgument(filePaths), addArguments);
|
||||
public static FFMpegArguments FromFileInput(string filePath, bool verifyExists = true, Action<FFMpegArgumentOptions>? addArguments = null) => new FFMpegArguments().WithInput(new InputArgument(verifyExists, filePath), addArguments);
|
||||
public static FFMpegArguments FromFileInput(FileInfo fileInfo, Action<FFMpegArgumentOptions>? addArguments = null) => new FFMpegArguments().WithInput(new InputArgument(fileInfo.FullName, false), addArguments);
|
||||
public static FFMpegArguments FromFileInput(IMediaAnalysis mediaAnalysis, Action<FFMpegArgumentOptions>? addArguments = null) => new FFMpegArguments().WithInput(new InputArgument(mediaAnalysis.Path, false), addArguments);
|
||||
public static FFMpegArguments FromUrlInput(Uri uri, Action<FFMpegArgumentOptions>? addArguments = null) => new FFMpegArguments().WithInput(new InputArgument(uri.AbsoluteUri, false), addArguments);
|
||||
public static FFMpegArguments FromPipeInput(IPipeSource sourcePipe, Action<FFMpegArgumentOptions>? addArguments = null) => new FFMpegArguments().WithInput(new InputPipeArgument(sourcePipe), addArguments);
|
||||
|
||||
|
@ -36,7 +35,6 @@ public FFMpegArguments WithGlobalOptions(Action<FFMpegGlobalOptions> configureOp
|
|||
public FFMpegArguments AddDemuxConcatInput(IEnumerable<string> filePaths, Action<FFMpegArgumentOptions>? addArguments = null) => WithInput(new DemuxConcatArgument(filePaths), addArguments);
|
||||
public FFMpegArguments AddFileInput(string filePath, bool verifyExists = true, Action<FFMpegArgumentOptions>? addArguments = null) => WithInput(new InputArgument(verifyExists, filePath), addArguments);
|
||||
public FFMpegArguments AddFileInput(FileInfo fileInfo, Action<FFMpegArgumentOptions>? addArguments = null) => WithInput(new InputArgument(fileInfo.FullName, false), addArguments);
|
||||
public FFMpegArguments AddFileInput(IMediaAnalysis mediaAnalysis, Action<FFMpegArgumentOptions>? addArguments = null) => WithInput(new InputArgument(mediaAnalysis.Path, false), addArguments);
|
||||
public FFMpegArguments AddUrlInput(Uri uri, Action<FFMpegArgumentOptions>? addArguments = null) => WithInput(new InputArgument(uri.AbsoluteUri, false), addArguments);
|
||||
public FFMpegArguments AddPipeInput(IPipeSource sourcePipe, Action<FFMpegArgumentOptions>? addArguments = null) => WithInput(new InputPipeArgument(sourcePipe), addArguments);
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ static FFMpegOptions()
|
|||
public bool UseCache { get; set; } = true;
|
||||
public Encoding Encoding { get; set; } = Encoding.Default;
|
||||
|
||||
public string FFmpegBinary() => FFBinary("FFMpeg");
|
||||
public string FFMpegBinary() => FFBinary("FFMpeg");
|
||||
|
||||
public string FFProbeBinary() => FFBinary("FFProbe");
|
||||
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=ffmpeg/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=ffmpeg/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=ffprobe/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
|
@ -12,22 +12,28 @@ namespace FFMpegCore
|
|||
{
|
||||
public static class FFProbe
|
||||
{
|
||||
public static IMediaAnalysis? Analyse(string filePath, int outputCapacity = int.MaxValue)
|
||||
public static IMediaAnalysis Analyse(string filePath, int outputCapacity = int.MaxValue)
|
||||
{
|
||||
if (!File.Exists(filePath))
|
||||
throw new FFMpegException(FFMpegExceptionType.File, $"No file found at '{filePath}'");
|
||||
|
||||
using var instance = PrepareInstance(filePath, outputCapacity);
|
||||
instance.BlockUntilFinished();
|
||||
return ParseOutput(filePath, instance);
|
||||
var exitCode = instance.BlockUntilFinished();
|
||||
if (exitCode != 0)
|
||||
throw new FFProbeProcessException(exitCode, string.Join("\n", instance.ErrorData));
|
||||
|
||||
return ParseOutput(instance);
|
||||
}
|
||||
public static IMediaAnalysis? Analyse(Uri uri, int outputCapacity = int.MaxValue)
|
||||
public static IMediaAnalysis Analyse(Uri uri, int outputCapacity = int.MaxValue)
|
||||
{
|
||||
using var instance = PrepareInstance(uri.AbsoluteUri, outputCapacity);
|
||||
instance.BlockUntilFinished();
|
||||
return ParseOutput(uri.AbsoluteUri, instance);
|
||||
var exitCode = instance.BlockUntilFinished();
|
||||
if (exitCode != 0)
|
||||
throw new FFProbeProcessException(exitCode, string.Join("\n", instance.ErrorData));
|
||||
|
||||
return ParseOutput(instance);
|
||||
}
|
||||
public static IMediaAnalysis? Analyse(Stream stream, int outputCapacity = int.MaxValue)
|
||||
public static IMediaAnalysis Analyse(Stream stream, int outputCapacity = int.MaxValue)
|
||||
{
|
||||
var streamPipeSource = new StreamPipeSource(stream);
|
||||
var pipeArgument = new InputPipeArgument(streamPipeSource);
|
||||
|
@ -46,26 +52,26 @@ public static class FFProbe
|
|||
}
|
||||
var exitCode = task.ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
if (exitCode != 0)
|
||||
throw new FFMpegException(FFMpegExceptionType.Process, $"FFProbe process returned exit status {exitCode}", null, string.Join("\n", instance.ErrorData), string.Join("\n", instance.OutputData));
|
||||
throw new FFProbeProcessException(exitCode, string.Join("\n", instance.ErrorData));
|
||||
|
||||
return ParseOutput(pipeArgument.PipePath, instance);
|
||||
return ParseOutput(instance);
|
||||
}
|
||||
public static async Task<IMediaAnalysis?> AnalyseAsync(string filePath, int outputCapacity = int.MaxValue)
|
||||
public static async Task<IMediaAnalysis> AnalyseAsync(string filePath, int outputCapacity = int.MaxValue)
|
||||
{
|
||||
if (!File.Exists(filePath))
|
||||
throw new FFMpegException(FFMpegExceptionType.File, $"No file found at '{filePath}'");
|
||||
|
||||
using var instance = PrepareInstance(filePath, outputCapacity);
|
||||
await instance.FinishedRunning();
|
||||
return ParseOutput(filePath, instance);
|
||||
return ParseOutput(instance);
|
||||
}
|
||||
public static async Task<IMediaAnalysis?> AnalyseAsync(Uri uri, int outputCapacity = int.MaxValue)
|
||||
public static async Task<IMediaAnalysis> AnalyseAsync(Uri uri, int outputCapacity = int.MaxValue)
|
||||
{
|
||||
using var instance = PrepareInstance(uri.AbsoluteUri, outputCapacity);
|
||||
await instance.FinishedRunning();
|
||||
return ParseOutput(uri.AbsoluteUri, instance);
|
||||
return ParseOutput(instance);
|
||||
}
|
||||
public static async Task<IMediaAnalysis?> AnalyseAsync(Stream stream, int outputCapacity = int.MaxValue)
|
||||
public static async Task<IMediaAnalysis> AnalyseAsync(Stream stream, int outputCapacity = int.MaxValue)
|
||||
{
|
||||
var streamPipeSource = new StreamPipeSource(stream);
|
||||
var pipeArgument = new InputPipeArgument(streamPipeSource);
|
||||
|
@ -86,21 +92,24 @@ public static class FFProbe
|
|||
}
|
||||
var exitCode = await task;
|
||||
if (exitCode != 0)
|
||||
throw new FFMpegException(FFMpegExceptionType.Process, $"FFProbe process returned exit status {exitCode}", null, string.Join("\n", instance.ErrorData), string.Join("\n", instance.OutputData));
|
||||
throw new FFMpegException(FFMpegExceptionType.Process, $"FFProbe process returned exit status {exitCode}", null, string.Join("\n", instance.ErrorData));
|
||||
|
||||
pipeArgument.Post();
|
||||
return ParseOutput(pipeArgument.PipePath, instance);
|
||||
return ParseOutput(instance);
|
||||
}
|
||||
|
||||
private static IMediaAnalysis? ParseOutput(string filePath, Instance instance)
|
||||
private static IMediaAnalysis ParseOutput(Instance instance)
|
||||
{
|
||||
var json = string.Join(string.Empty, instance.OutputData);
|
||||
var ffprobeAnalysis = JsonSerializer.Deserialize<FFProbeAnalysis>(json, new JsonSerializerOptions
|
||||
{
|
||||
PropertyNameCaseInsensitive = true
|
||||
})!;
|
||||
if (ffprobeAnalysis?.Format == null) return null;
|
||||
return new MediaAnalysis(filePath, ffprobeAnalysis);
|
||||
});
|
||||
|
||||
if (ffprobeAnalysis?.Format == null)
|
||||
throw new Exception();
|
||||
|
||||
return new MediaAnalysis(ffprobeAnalysis);
|
||||
}
|
||||
|
||||
private static Instance PrepareInstance(string filePath, int outputCapacity)
|
||||
|
|
|
@ -5,12 +5,10 @@ namespace FFMpegCore
|
|||
{
|
||||
public interface IMediaAnalysis
|
||||
{
|
||||
string Path { get; }
|
||||
string Extension { get; }
|
||||
TimeSpan Duration { get; }
|
||||
MediaFormat Format { get; }
|
||||
AudioStream PrimaryAudioStream { get; }
|
||||
VideoStream PrimaryVideoStream { get; }
|
||||
AudioStream? PrimaryAudioStream { get; }
|
||||
VideoStream? PrimaryVideoStream { get; }
|
||||
List<VideoStream> VideoStreams { get; }
|
||||
List<AudioStream> AudioStreams { get; }
|
||||
}
|
||||
|
|
|
@ -9,14 +9,11 @@ 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);
|
||||
|
||||
internal MediaAnalysis(string path, FFProbeAnalysis analysis)
|
||||
internal MediaAnalysis(FFProbeAnalysis analysis)
|
||||
{
|
||||
Format = ParseFormat(analysis.Format);
|
||||
VideoStreams = analysis.Streams.Where(stream => stream.CodecType == "video").Select(ParseVideoStream).ToList();
|
||||
AudioStreams = analysis.Streams.Where(stream => stream.CodecType == "audio").Select(ParseAudioStream).ToList();
|
||||
PrimaryVideoStream = VideoStreams.OrderBy(stream => stream.Index).FirstOrDefault();
|
||||
PrimaryAudioStream = AudioStreams.OrderBy(stream => stream.Index).FirstOrDefault();
|
||||
Path = path;
|
||||
}
|
||||
|
||||
private MediaFormat ParseFormat(Format analysisFormat)
|
||||
|
@ -33,9 +30,6 @@ private MediaFormat ParseFormat(Format analysisFormat)
|
|||
};
|
||||
}
|
||||
|
||||
public string Path { get; }
|
||||
public string Extension => System.IO.Path.GetExtension(Path);
|
||||
|
||||
public TimeSpan Duration => new[]
|
||||
{
|
||||
Format.Duration,
|
||||
|
@ -44,9 +38,9 @@ private MediaFormat ParseFormat(Format analysisFormat)
|
|||
}.Max();
|
||||
|
||||
public MediaFormat Format { get; }
|
||||
public AudioStream PrimaryAudioStream { get; }
|
||||
public AudioStream? PrimaryAudioStream => AudioStreams.OrderBy(stream => stream.Index).FirstOrDefault();
|
||||
|
||||
public VideoStream PrimaryVideoStream { get; }
|
||||
public VideoStream? PrimaryVideoStream => VideoStreams.OrderBy(stream => stream.Index).FirstOrDefault();
|
||||
|
||||
public List<VideoStream> VideoStreams { get; }
|
||||
public List<AudioStream> AudioStreams { get; }
|
||||
|
|
|
@ -11,22 +11,16 @@ public static class FFMpegHelper
|
|||
private static bool _ffmpegVerified;
|
||||
|
||||
public static void ConversionSizeExceptionCheck(Image image)
|
||||
{
|
||||
ConversionSizeExceptionCheck(image.Size);
|
||||
}
|
||||
=> ConversionSizeExceptionCheck(image.Size.Width, image.Size.Height);
|
||||
|
||||
public static void ConversionSizeExceptionCheck(IMediaAnalysis info)
|
||||
{
|
||||
ConversionSizeExceptionCheck(new Size(info.PrimaryVideoStream.Width, info.PrimaryVideoStream.Height));
|
||||
}
|
||||
=> ConversionSizeExceptionCheck(info.PrimaryVideoStream!.Width, info.PrimaryVideoStream.Height);
|
||||
|
||||
private static void ConversionSizeExceptionCheck(Size size)
|
||||
{
|
||||
if (size.Height % 2 != 0 || size.Width % 2 != 0 )
|
||||
private static void ConversionSizeExceptionCheck(int width, int height)
|
||||
{
|
||||
if (height % 2 != 0 || width % 2 != 0 )
|
||||
throw new ArgumentException("FFMpeg yuv420p encoding requires the width and height to be a multiple of 2!");
|
||||
}
|
||||
}
|
||||
|
||||
public static void ExtensionExceptionCheck(string filename, string extension)
|
||||
{
|
||||
|
@ -45,7 +39,7 @@ public static void RootExceptionCheck()
|
|||
public static void VerifyFFMpegExists()
|
||||
{
|
||||
if (_ffmpegVerified) return;
|
||||
var (exitCode, _) = Instance.Finish(FFMpegOptions.Options.FFmpegBinary(), "-version");
|
||||
var (exitCode, _) = Instance.Finish(FFMpegOptions.Options.FFMpegBinary(), "-version");
|
||||
_ffmpegVerified = exitCode == 0;
|
||||
if (!_ffmpegVerified) throw new FFMpegException(FFMpegExceptionType.Operation, "ffmpeg was not found on your system");
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue