mirror of
https://github.com/rosenbjerg/FFMpegCore.git
synced 2024-11-14 02:04:14 +01:00
Merge branch 'master' of https://github.com/rosenbjerg/FFMpegCore
Former-commit-id: c6bdf55a77
This commit is contained in:
commit
0126cd1e77
44 changed files with 644 additions and 564 deletions
17
.github/workflows/ci.yml
vendored
17
.github/workflows/ci.yml
vendored
|
@ -1,9 +1,18 @@
|
||||||
name: CI
|
name: CI
|
||||||
on: [push, pull_request]
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches-ignore:
|
||||||
|
- release
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
- release
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
ci:
|
ci:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 10
|
timeout-minutes: 7
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
- name: Prepare FFMpeg
|
- name: Prepare FFMpeg
|
||||||
|
@ -12,7 +21,5 @@ jobs:
|
||||||
uses: actions/setup-dotnet@v1
|
uses: actions/setup-dotnet@v1
|
||||||
with:
|
with:
|
||||||
dotnet-version: 3.1.101
|
dotnet-version: 3.1.101
|
||||||
- name: Build with dotnet
|
|
||||||
run: dotnet build
|
|
||||||
- name: Test with dotnet
|
- name: Test with dotnet
|
||||||
run: dotnet test
|
run: dotnet test --logger GitHubActions
|
||||||
|
|
10
.github/workflows/release.yml
vendored
10
.github/workflows/release.yml
vendored
|
@ -8,16 +8,12 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
- name: Prepare FFMpeg
|
|
||||||
run: sudo apt-get update && sudo apt-get install -y ffmpeg
|
|
||||||
- name: Setup .NET Core
|
- name: Setup .NET Core
|
||||||
uses: actions/setup-dotnet@v1
|
uses: actions/setup-dotnet@v1
|
||||||
with:
|
with:
|
||||||
dotnet-version: 3.1
|
dotnet-version: 3.1
|
||||||
- name: Build solution -c Release
|
- name: Build solution
|
||||||
run: dotnet build
|
run: dotnet build --output build
|
||||||
- name: Run unit tests
|
|
||||||
run: dotnet test
|
|
||||||
- name: Publish NuGet package
|
- name: Publish NuGet package
|
||||||
run: NUPKG=`find . -type f -name FFMpegCore*.nupkg` && dotnet nuget push $NUPKG -k ${{ secrets.NUGET_TOKEN }} -s https://api.nuget.org/v3/index.json
|
run: dotnet nuget push "build/*.nupkg" --skip-duplicate --source nuget.org --api-key ${{ secrets.NUGET_TOKEN }}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
using System;
|
using System;
|
||||||
using FFMpegCore.Arguments;
|
using FFMpegCore.Arguments;
|
||||||
using FFMpegCore.Enums;
|
using FFMpegCore.Enums;
|
||||||
using FFMpegCore.Exceptions;
|
|
||||||
|
|
||||||
namespace FFMpegCore.Test
|
namespace FFMpegCore.Test
|
||||||
{
|
{
|
||||||
|
@ -15,217 +14,217 @@ public class ArgumentBuilderTest : BaseTest
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void Builder_BuildString_IO_1()
|
public void Builder_BuildString_IO_1()
|
||||||
{
|
{
|
||||||
var str = FFMpegArguments.FromInputFiles(true, "input.mp4").OutputToFile("output.mp4").Arguments;
|
var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4").Arguments;
|
||||||
Assert.AreEqual("-i \"input.mp4\" \"output.mp4\" -y", str);
|
Assert.AreEqual("-i \"input.mp4\" \"output.mp4\" -y", str);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void Builder_BuildString_Scale()
|
public void Builder_BuildString_Scale()
|
||||||
{
|
{
|
||||||
var str = FFMpegArguments.FromInputFiles(true, "input.mp4").Scale(VideoSize.Hd).OutputToFile("output.mp4").Arguments;
|
var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", true, opt => opt.Scale(VideoSize.Hd)).Arguments;
|
||||||
Assert.AreEqual("-i \"input.mp4\" -vf scale=-1:720 \"output.mp4\" -y", str);
|
Assert.AreEqual("-i \"input.mp4\" -vf scale=-1:720 \"output.mp4\" -y", str);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void Builder_BuildString_AudioCodec()
|
public void Builder_BuildString_AudioCodec()
|
||||||
{
|
{
|
||||||
var str = FFMpegArguments.FromInputFiles(true, "input.mp4").WithAudioCodec(AudioCodec.Aac).OutputToFile("output.mp4").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);
|
Assert.AreEqual("-i \"input.mp4\" -c:a aac \"output.mp4\" -y", str);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void Builder_BuildString_AudioBitrate()
|
public void Builder_BuildString_AudioBitrate()
|
||||||
{
|
{
|
||||||
var str = FFMpegArguments.FromInputFiles(true, "input.mp4").WithAudioBitrate(AudioQuality.Normal).OutputToFile("output.mp4").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);
|
Assert.AreEqual("-i \"input.mp4\" -b:a 128k \"output.mp4\" -y", str);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void Builder_BuildString_Quiet()
|
public void Builder_BuildString_Quiet()
|
||||||
{
|
{
|
||||||
var str = FFMpegArguments.FromInputFiles(true, "input.mp4").WithVerbosityLevel().OutputToFile("output.mp4", false).Arguments;
|
var str = FFMpegArguments.FromFileInput("input.mp4").WithGlobalOptions(opt => opt.WithVerbosityLevel()).OutputToFile("output.mp4", false).Arguments;
|
||||||
Assert.AreEqual("-i \"input.mp4\" -hide_banner -loglevel error \"output.mp4\"", str);
|
Assert.AreEqual("-hide_banner -loglevel error -i \"input.mp4\" \"output.mp4\"", str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void Builder_BuildString_AudioCodec_Fluent()
|
public void Builder_BuildString_AudioCodec_Fluent()
|
||||||
{
|
{
|
||||||
var str = FFMpegArguments.FromInputFiles(true, "input.mp4").WithAudioCodec(AudioCodec.Aac).WithAudioBitrate(128).OutputToFile("output.mp4", false).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);
|
Assert.AreEqual("-i \"input.mp4\" -c:a aac -b:a 128k \"output.mp4\"", str);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void Builder_BuildString_BitStream()
|
public void Builder_BuildString_BitStream()
|
||||||
{
|
{
|
||||||
var str = FFMpegArguments.FromInputFiles(true, "input.mp4").WithBitStreamFilter(Channel.Audio, Filter.H264_Mp4ToAnnexB).OutputToFile("output.mp4", false).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);
|
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;
|
||||||
|
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;
|
||||||
|
Assert.AreEqual("-i \"input.mp4\" -hwaccel cuvid \"output.mp4\"", str);
|
||||||
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void Builder_BuildString_Concat()
|
public void Builder_BuildString_Concat()
|
||||||
{
|
{
|
||||||
var str = FFMpegArguments.FromConcatenation(_concatFiles).OutputToFile("output.mp4", false).Arguments;
|
var str = FFMpegArguments.FromConcatInput(_concatFiles).OutputToFile("output.mp4", false).Arguments;
|
||||||
Assert.AreEqual("-i \"concat:1.mp4|2.mp4|3.mp4|4.mp4\" \"output.mp4\"", str);
|
Assert.AreEqual("-i \"concat:1.mp4|2.mp4|3.mp4|4.mp4\" \"output.mp4\"", str);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void Builder_BuildString_Copy_Audio()
|
public void Builder_BuildString_Copy_Audio()
|
||||||
{
|
{
|
||||||
var str = FFMpegArguments.FromInputFiles(true, "input.mp4").CopyChannel(Channel.Audio).OutputToFile("output.mp4", false).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);
|
Assert.AreEqual("-i \"input.mp4\" -c:a copy \"output.mp4\"", str);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void Builder_BuildString_Copy_Video()
|
public void Builder_BuildString_Copy_Video()
|
||||||
{
|
{
|
||||||
var str = FFMpegArguments.FromInputFiles(true, "input.mp4").CopyChannel(Channel.Video).OutputToFile("output.mp4", false).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);
|
Assert.AreEqual("-i \"input.mp4\" -c:v copy \"output.mp4\"", str);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void Builder_BuildString_Copy_Both()
|
public void Builder_BuildString_Copy_Both()
|
||||||
{
|
{
|
||||||
var str = FFMpegArguments.FromInputFiles(true, "input.mp4").CopyChannel().OutputToFile("output.mp4", false).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);
|
Assert.AreEqual("-i \"input.mp4\" -c copy \"output.mp4\"", str);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void Builder_BuildString_DisableChannel_Audio()
|
public void Builder_BuildString_DisableChannel_Audio()
|
||||||
{
|
{
|
||||||
var str = FFMpegArguments.FromInputFiles(true, "input.mp4").DisableChannel(Channel.Audio).OutputToFile("output.mp4", false).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);
|
Assert.AreEqual("-i \"input.mp4\" -an \"output.mp4\"", str);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void Builder_BuildString_DisableChannel_Video()
|
public void Builder_BuildString_DisableChannel_Video()
|
||||||
{
|
{
|
||||||
var str = FFMpegArguments.FromInputFiles(true, "input.mp4").DisableChannel(Channel.Video).OutputToFile("output.mp4", false).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);
|
Assert.AreEqual("-i \"input.mp4\" -vn \"output.mp4\"", str);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public void Builder_BuildString_DisableChannel_Both()
|
|
||||||
{
|
|
||||||
Assert.ThrowsException<FFMpegException>(() => FFMpegArguments.FromInputFiles(true, "input.mp4").DisableChannel(Channel.Both));
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void Builder_BuildString_AudioSamplingRate_Default()
|
public void Builder_BuildString_AudioSamplingRate_Default()
|
||||||
{
|
{
|
||||||
var str = FFMpegArguments.FromInputFiles(true, "input.mp4").WithAudioSamplingRate().OutputToFile("output.mp4", false).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);
|
Assert.AreEqual("-i \"input.mp4\" -ar 48000 \"output.mp4\"", str);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void Builder_BuildString_AudioSamplingRate()
|
public void Builder_BuildString_AudioSamplingRate()
|
||||||
{
|
{
|
||||||
var str = FFMpegArguments.FromInputFiles(true, "input.mp4").WithAudioSamplingRate(44000).OutputToFile("output.mp4", false).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);
|
Assert.AreEqual("-i \"input.mp4\" -ar 44000 \"output.mp4\"", str);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void Builder_BuildString_VariableBitrate()
|
public void Builder_BuildString_VariableBitrate()
|
||||||
{
|
{
|
||||||
var str = FFMpegArguments.FromInputFiles(true, "input.mp4").WithVariableBitrate(5).OutputToFile("output.mp4", false).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);
|
Assert.AreEqual("-i \"input.mp4\" -vbr 5 \"output.mp4\"", str);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void Builder_BuildString_Faststart()
|
public void Builder_BuildString_Faststart()
|
||||||
{
|
{
|
||||||
var str = FFMpegArguments.FromInputFiles(true, "input.mp4").WithFastStart().OutputToFile("output.mp4", false).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);
|
Assert.AreEqual("-i \"input.mp4\" -movflags faststart \"output.mp4\"", str);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void Builder_BuildString_Overwrite()
|
public void Builder_BuildString_Overwrite()
|
||||||
{
|
{
|
||||||
var str = FFMpegArguments.FromInputFiles(true, "input.mp4").OverwriteExisting().OutputToFile("output.mp4", false).Arguments;
|
var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.OverwriteExisting()).Arguments;
|
||||||
Assert.AreEqual("-i \"input.mp4\" -y \"output.mp4\"", str);
|
Assert.AreEqual("-i \"input.mp4\" -y \"output.mp4\"", str);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void Builder_BuildString_RemoveMetadata()
|
public void Builder_BuildString_RemoveMetadata()
|
||||||
{
|
{
|
||||||
var str = FFMpegArguments.FromInputFiles(true, "input.mp4").WithoutMetadata().OutputToFile("output.mp4", false).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);
|
Assert.AreEqual("-i \"input.mp4\" -map_metadata -1 \"output.mp4\"", str);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void Builder_BuildString_Transpose()
|
public void Builder_BuildString_Transpose()
|
||||||
{
|
{
|
||||||
var str = FFMpegArguments.FromInputFiles(true, "input.mp4").Transpose(Transposition.CounterClockwise90).OutputToFile("output.mp4", false).Arguments;
|
var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.Transpose(Transposition.CounterClockwise90)).Arguments;
|
||||||
Assert.AreEqual("-i \"input.mp4\" -vf \"transpose=2\" \"output.mp4\"", str);
|
Assert.AreEqual("-i \"input.mp4\" -vf \"transpose=2\" \"output.mp4\"", str);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public void Builder_BuildString_CpuSpeed()
|
|
||||||
{
|
|
||||||
var str = FFMpegArguments.FromInputFiles(true, "input.mp4").WithCpuSpeed(10).OutputToFile("output.mp4", false).Arguments;
|
|
||||||
Assert.AreEqual("-i \"input.mp4\" -quality good -cpu-used 10 -deadline realtime \"output.mp4\"", str);
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void Builder_BuildString_ForceFormat()
|
public void Builder_BuildString_ForceFormat()
|
||||||
{
|
{
|
||||||
var str = FFMpegArguments.FromInputFiles(true, "input.mp4").ForceFormat(VideoType.Mp4).OutputToFile("output.mp4", false).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("-i \"input.mp4\" -f mp4 \"output.mp4\"", str);
|
Assert.AreEqual("-f mp4 -i \"input.mp4\" -f mp4 \"output.mp4\"", str);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void Builder_BuildString_FrameOutputCount()
|
public void Builder_BuildString_FrameOutputCount()
|
||||||
{
|
{
|
||||||
var str = FFMpegArguments.FromInputFiles(true, "input.mp4").WithFrameOutputCount(50).OutputToFile("output.mp4", false).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);
|
Assert.AreEqual("-i \"input.mp4\" -vframes 50 \"output.mp4\"", str);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void Builder_BuildString_VideoStreamNumber()
|
public void Builder_BuildString_VideoStreamNumber()
|
||||||
{
|
{
|
||||||
var str = FFMpegArguments.FromInputFiles(true, "input.mp4").WithVideoStream(1).OutputToFile("output.mp4", false).Arguments;
|
var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.SelectStream(1)).Arguments;
|
||||||
Assert.AreEqual("-i \"input.mp4\" -map 0:1 \"output.mp4\"", str);
|
Assert.AreEqual("-i \"input.mp4\" -map 0:1 \"output.mp4\"", str);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void Builder_BuildString_FrameRate()
|
public void Builder_BuildString_FrameRate()
|
||||||
{
|
{
|
||||||
var str = FFMpegArguments.FromInputFiles(true, "input.mp4").WithFramerate(50).OutputToFile("output.mp4", false).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);
|
Assert.AreEqual("-i \"input.mp4\" -r 50 \"output.mp4\"", str);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void Builder_BuildString_Loop()
|
public void Builder_BuildString_Loop()
|
||||||
{
|
{
|
||||||
var str = FFMpegArguments.FromInputFiles(true, "input.mp4").Loop(50).OutputToFile("output.mp4", false).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);
|
Assert.AreEqual("-i \"input.mp4\" -loop 50 \"output.mp4\"", str);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void Builder_BuildString_Seek()
|
public void Builder_BuildString_Seek()
|
||||||
{
|
{
|
||||||
var str = FFMpegArguments.FromInputFiles(true, "input.mp4").Seek(TimeSpan.FromSeconds(10)).OutputToFile("output.mp4", false).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("-i \"input.mp4\" -ss 00:00:10 \"output.mp4\"", str);
|
Assert.AreEqual("-ss 00:00:10 -i \"input.mp4\" -ss 00:00:10 \"output.mp4\"", str);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void Builder_BuildString_Shortest()
|
public void Builder_BuildString_Shortest()
|
||||||
{
|
{
|
||||||
var str = FFMpegArguments.FromInputFiles(true, "input.mp4").UsingShortest().OutputToFile("output.mp4", false).Arguments;
|
var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.UsingShortest()).Arguments;
|
||||||
Assert.AreEqual("-i \"input.mp4\" -shortest \"output.mp4\"", str);
|
Assert.AreEqual("-i \"input.mp4\" -shortest \"output.mp4\"", str);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void Builder_BuildString_Size()
|
public void Builder_BuildString_Size()
|
||||||
{
|
{
|
||||||
var str = FFMpegArguments.FromInputFiles(true, "input.mp4").Resize(1920, 1080).OutputToFile("output.mp4", false).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);
|
Assert.AreEqual("-i \"input.mp4\" -s 1920x1080 \"output.mp4\"", str);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void Builder_BuildString_Speed()
|
public void Builder_BuildString_Speed()
|
||||||
{
|
{
|
||||||
var str = FFMpegArguments.FromInputFiles(true, "input.mp4").WithSpeedPreset(Speed.Fast).OutputToFile("output.mp4", false).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);
|
Assert.AreEqual("-i \"input.mp4\" -preset fast \"output.mp4\"", str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -233,17 +232,18 @@ public void Builder_BuildString_Speed()
|
||||||
public void Builder_BuildString_DrawtextFilter()
|
public void Builder_BuildString_DrawtextFilter()
|
||||||
{
|
{
|
||||||
var str = FFMpegArguments
|
var str = FFMpegArguments
|
||||||
.FromInputFiles(true, "input.mp4")
|
.FromFileInput("input.mp4")
|
||||||
.DrawText(DrawTextOptions
|
.OutputToFile("output.mp4", false, opt => opt
|
||||||
.Create("Stack Overflow", "/path/to/font.ttf")
|
.DrawText(DrawTextOptions
|
||||||
.WithParameter("fontcolor", "white")
|
.Create("Stack Overflow", "/path/to/font.ttf")
|
||||||
.WithParameter("fontsize", "24")
|
.WithParameter("fontcolor", "white")
|
||||||
.WithParameter("box", "1")
|
.WithParameter("fontsize", "24")
|
||||||
.WithParameter("boxcolor", "black@0.5")
|
.WithParameter("box", "1")
|
||||||
.WithParameter("boxborderw", "5")
|
.WithParameter("boxcolor", "black@0.5")
|
||||||
.WithParameter("x", "(w-text_w)/2")
|
.WithParameter("boxborderw", "5")
|
||||||
.WithParameter("y", "(h-text_h)/2"))
|
.WithParameter("x", "(w-text_w)/2")
|
||||||
.OutputToFile("output.mp4", false).Arguments;
|
.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);
|
||||||
}
|
}
|
||||||
|
@ -252,10 +252,11 @@ public void Builder_BuildString_DrawtextFilter()
|
||||||
public void Builder_BuildString_DrawtextFilter_Alt()
|
public void Builder_BuildString_DrawtextFilter_Alt()
|
||||||
{
|
{
|
||||||
var str = FFMpegArguments
|
var str = FFMpegArguments
|
||||||
.FromInputFiles(true, "input.mp4")
|
.FromFileInput("input.mp4")
|
||||||
.DrawText(DrawTextOptions
|
.OutputToFile("output.mp4", false, opt => opt
|
||||||
.Create("Stack Overflow", "/path/to/font.ttf", ("fontcolor", "white"), ("fontsize", "24")))
|
.DrawText(DrawTextOptions
|
||||||
.OutputToFile("output.mp4", false).Arguments;
|
.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);
|
||||||
}
|
}
|
||||||
|
@ -263,35 +264,35 @@ public void Builder_BuildString_DrawtextFilter_Alt()
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void Builder_BuildString_StartNumber()
|
public void Builder_BuildString_StartNumber()
|
||||||
{
|
{
|
||||||
var str = FFMpegArguments.FromInputFiles(true, "input.mp4").WithStartNumber(50).OutputToFile("output.mp4", false).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);
|
Assert.AreEqual("-i \"input.mp4\" -start_number 50 \"output.mp4\"", str);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void Builder_BuildString_Threads_1()
|
public void Builder_BuildString_Threads_1()
|
||||||
{
|
{
|
||||||
var str = FFMpegArguments.FromInputFiles(true, "input.mp4").UsingThreads(50).OutputToFile("output.mp4", false).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);
|
Assert.AreEqual("-i \"input.mp4\" -threads 50 \"output.mp4\"", str);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void Builder_BuildString_Threads_2()
|
public void Builder_BuildString_Threads_2()
|
||||||
{
|
{
|
||||||
var str = FFMpegArguments.FromInputFiles(true, "input.mp4").UsingMultithreading(true).OutputToFile("output.mp4", false).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);
|
Assert.AreEqual($"-i \"input.mp4\" -threads {Environment.ProcessorCount} \"output.mp4\"", str);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void Builder_BuildString_Codec()
|
public void Builder_BuildString_Codec()
|
||||||
{
|
{
|
||||||
var str = FFMpegArguments.FromInputFiles(true, "input.mp4").WithVideoCodec(VideoCodec.LibX264).OutputToFile("output.mp4", false).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);
|
Assert.AreEqual("-i \"input.mp4\" -c:v libx264 \"output.mp4\"", str);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void Builder_BuildString_Codec_Override()
|
public void Builder_BuildString_Codec_Override()
|
||||||
{
|
{
|
||||||
var str = FFMpegArguments.FromInputFiles(true, "input.mp4").WithVideoCodec(VideoCodec.LibX264).ForcePixelFormat("yuv420p").OutputToFile("output.mp4").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);
|
Assert.AreEqual("-i \"input.mp4\" -c:v libx264 -pix_fmt yuv420p \"output.mp4\" -y", str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -299,17 +300,17 @@ public void Builder_BuildString_Codec_Override()
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void Builder_BuildString_Duration()
|
public void Builder_BuildString_Duration()
|
||||||
{
|
{
|
||||||
var str = FFMpegArguments.FromInputFiles(true, "input.mp4").WithDuration(TimeSpan.FromSeconds(20)).OutputToFile("output.mp4", false).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);
|
Assert.AreEqual("-i \"input.mp4\" -t 00:00:20 \"output.mp4\"", str);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void Builder_BuildString_Raw()
|
public void Builder_BuildString_Raw()
|
||||||
{
|
{
|
||||||
var str = FFMpegArguments.FromInputFiles(true, "input.mp4").WithCustomArgument(null).OutputToFile("output.mp4", false).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);
|
Assert.AreEqual(" -i \"input.mp4\" \"output.mp4\"", str);
|
||||||
|
|
||||||
str = FFMpegArguments.FromInputFiles(true, "input.mp4").WithCustomArgument("-acodec copy").OutputToFile("output.mp4", false).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);
|
Assert.AreEqual("-i \"input.mp4\" -acodec copy \"output.mp4\"", str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -317,7 +318,7 @@ public void Builder_BuildString_Raw()
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void Builder_BuildString_ForcePixelFormat()
|
public void Builder_BuildString_ForcePixelFormat()
|
||||||
{
|
{
|
||||||
var str = FFMpegArguments.FromInputFiles(true, "input.mp4").ForcePixelFormat("yuv444p").OutputToFile("output.mp4", false).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);
|
Assert.AreEqual("-i \"input.mp4\" -pix_fmt yuv444p \"output.mp4\"", str);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,18 @@
|
||||||
<None Update="Resources\input.webm">
|
<None Update="Resources\input.webm">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
|
<None Update="Resources\input_3sec.mp4">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="Resources\input_3sec.webm">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="Resources\input_audio_only_10sec.mp4">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="Resources\input_video_only_3sec.mp4">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -24,6 +36,7 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="GitHubActionsTestLogger" Version="1.1.1" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.0" />
|
||||||
<PackageReference Include="MSTest.TestAdapter" Version="2.1.2" />
|
<PackageReference Include="MSTest.TestAdapter" Version="2.1.2" />
|
||||||
<PackageReference Include="MSTest.TestFramework" Version="2.1.2" />
|
<PackageReference Include="MSTest.TestFramework" Version="2.1.2" />
|
||||||
|
|
|
@ -18,7 +18,7 @@ public void Probe_TooLongOutput()
|
||||||
public void Probe_Success()
|
public void Probe_Success()
|
||||||
{
|
{
|
||||||
var info = FFProbe.Analyse(VideoLibrary.LocalVideo.FullName);
|
var info = FFProbe.Analyse(VideoLibrary.LocalVideo.FullName);
|
||||||
Assert.AreEqual(13, info.Duration.Seconds);
|
Assert.AreEqual(3, info.Duration.Seconds);
|
||||||
Assert.AreEqual(".mp4", info.Extension);
|
Assert.AreEqual(".mp4", info.Extension);
|
||||||
Assert.AreEqual(VideoLibrary.LocalVideo.FullName, info.Path);
|
Assert.AreEqual(VideoLibrary.LocalVideo.FullName, info.Path);
|
||||||
|
|
||||||
|
@ -27,10 +27,10 @@ public void Probe_Success()
|
||||||
Assert.AreEqual("AAC (Advanced Audio Coding)", info.PrimaryAudioStream.CodecLongName);
|
Assert.AreEqual("AAC (Advanced Audio Coding)", info.PrimaryAudioStream.CodecLongName);
|
||||||
Assert.AreEqual("aac", info.PrimaryAudioStream.CodecName);
|
Assert.AreEqual("aac", info.PrimaryAudioStream.CodecName);
|
||||||
Assert.AreEqual("LC", info.PrimaryAudioStream.Profile);
|
Assert.AreEqual("LC", info.PrimaryAudioStream.Profile);
|
||||||
Assert.AreEqual(381988, info.PrimaryAudioStream.BitRate);
|
Assert.AreEqual(377351, info.PrimaryAudioStream.BitRate);
|
||||||
Assert.AreEqual(48000, info.PrimaryAudioStream.SampleRateHz);
|
Assert.AreEqual(48000, info.PrimaryAudioStream.SampleRateHz);
|
||||||
|
|
||||||
Assert.AreEqual(862991, info.PrimaryVideoStream.BitRate);
|
Assert.AreEqual(1471810, info.PrimaryVideoStream.BitRate);
|
||||||
Assert.AreEqual(16, info.PrimaryVideoStream.DisplayAspectRatio.Width);
|
Assert.AreEqual(16, info.PrimaryVideoStream.DisplayAspectRatio.Width);
|
||||||
Assert.AreEqual(9, info.PrimaryVideoStream.DisplayAspectRatio.Height);
|
Assert.AreEqual(9, info.PrimaryVideoStream.DisplayAspectRatio.Height);
|
||||||
Assert.AreEqual("yuv420p", info.PrimaryVideoStream.PixelFormat);
|
Assert.AreEqual("yuv420p", info.PrimaryVideoStream.PixelFormat);
|
||||||
|
@ -44,11 +44,11 @@ public void Probe_Success()
|
||||||
Assert.AreEqual("Main", info.PrimaryVideoStream.Profile);
|
Assert.AreEqual("Main", info.PrimaryVideoStream.Profile);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod, Timeout(10000)]
|
||||||
public async Task Probe_Async_Success()
|
public async Task Probe_Async_Success()
|
||||||
{
|
{
|
||||||
var info = await FFProbe.AnalyseAsync(VideoLibrary.LocalVideo.FullName);
|
var info = await FFProbe.AnalyseAsync(VideoLibrary.LocalVideo.FullName);
|
||||||
Assert.AreEqual(13, info.Duration.Seconds);
|
Assert.AreEqual(3, info.Duration.Seconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod, Timeout(10000)]
|
[TestMethod, Timeout(10000)]
|
||||||
|
@ -56,15 +56,15 @@ public void Probe_Success_FromStream()
|
||||||
{
|
{
|
||||||
using var stream = File.OpenRead(VideoLibrary.LocalVideoWebm.FullName);
|
using var stream = File.OpenRead(VideoLibrary.LocalVideoWebm.FullName);
|
||||||
var info = FFProbe.Analyse(stream);
|
var info = FFProbe.Analyse(stream);
|
||||||
Assert.AreEqual(10, info.Duration.Seconds);
|
Assert.AreEqual(3, info.Duration.Seconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod, Timeout(10000)]
|
||||||
public async Task Probe_Success_FromStream_Async()
|
public async Task Probe_Success_FromStream_Async()
|
||||||
{
|
{
|
||||||
await using var stream = File.OpenRead(VideoLibrary.LocalVideoWebm.FullName);
|
await using var stream = File.OpenRead(VideoLibrary.LocalVideoWebm.FullName);
|
||||||
var info = await FFProbe.AnalyseAsync(stream);
|
var info = await FFProbe.AnalyseAsync(stream);
|
||||||
Assert.AreEqual(10, info.Duration.Seconds);
|
Assert.AreEqual(3, info.Duration.Seconds);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
using System.IO;
|
using System;
|
||||||
|
using System.IO;
|
||||||
using FFMpegCore.Enums;
|
using FFMpegCore.Enums;
|
||||||
|
|
||||||
namespace FFMpegCore.Test.Resources
|
namespace FFMpegCore.Test.Resources
|
||||||
|
@ -15,10 +16,10 @@ public enum ImageType
|
||||||
|
|
||||||
public static class VideoLibrary
|
public static class VideoLibrary
|
||||||
{
|
{
|
||||||
public static readonly FileInfo LocalVideo = new FileInfo($".{Path.DirectorySeparatorChar}Resources{Path.DirectorySeparatorChar}input.mp4");
|
public static readonly FileInfo LocalVideo = new FileInfo($".{Path.DirectorySeparatorChar}Resources{Path.DirectorySeparatorChar}input_3sec.mp4");
|
||||||
public static readonly FileInfo LocalVideoWebm = new FileInfo($".{Path.DirectorySeparatorChar}Resources{Path.DirectorySeparatorChar}input.webm");
|
public static readonly FileInfo LocalVideoWebm = new FileInfo($".{Path.DirectorySeparatorChar}Resources{Path.DirectorySeparatorChar}input_3sec.webm");
|
||||||
public static readonly FileInfo LocalVideoAudioOnly = new FileInfo($".{Path.DirectorySeparatorChar}Resources{Path.DirectorySeparatorChar}audio_only.mp4");
|
public static readonly FileInfo LocalVideoAudioOnly = new FileInfo($".{Path.DirectorySeparatorChar}Resources{Path.DirectorySeparatorChar}input_audio_only_10sec.mp4");
|
||||||
public static readonly FileInfo LocalVideoNoAudio = new FileInfo($".{Path.DirectorySeparatorChar}Resources{Path.DirectorySeparatorChar}mute.mp4");
|
public static readonly FileInfo LocalVideoNoAudio = new FileInfo($".{Path.DirectorySeparatorChar}Resources{Path.DirectorySeparatorChar}input_video_only_3sec.mp4");
|
||||||
public static readonly FileInfo LocalAudio = new FileInfo($".{Path.DirectorySeparatorChar}Resources{Path.DirectorySeparatorChar}audio.mp3");
|
public static readonly FileInfo LocalAudio = new FileInfo($".{Path.DirectorySeparatorChar}Resources{Path.DirectorySeparatorChar}audio.mp3");
|
||||||
public static readonly FileInfo LocalCover = new FileInfo($".{Path.DirectorySeparatorChar}Resources{Path.DirectorySeparatorChar}cover.png");
|
public static readonly FileInfo LocalCover = new FileInfo($".{Path.DirectorySeparatorChar}Resources{Path.DirectorySeparatorChar}cover.png");
|
||||||
public static readonly FileInfo ImageDirectory = new FileInfo($".{Path.DirectorySeparatorChar}Resources{Path.DirectorySeparatorChar}images");
|
public static readonly FileInfo ImageDirectory = new FileInfo($".{Path.DirectorySeparatorChar}Resources{Path.DirectorySeparatorChar}images");
|
||||||
|
@ -44,7 +45,7 @@ public static string OutputLocation(this FileInfo file, string type, string keyw
|
||||||
string originalLocation = file.Directory.FullName,
|
string originalLocation = file.Directory.FullName,
|
||||||
outputFile = file.Name.Replace(file.Extension, keyword + "." + type.ToLowerInvariant());
|
outputFile = file.Name.Replace(file.Extension, keyword + "." + type.ToLowerInvariant());
|
||||||
|
|
||||||
return $"{originalLocation}{Path.DirectorySeparatorChar}{outputFile}";
|
return $"{originalLocation}{Path.DirectorySeparatorChar}{Guid.NewGuid()}_{outputFile}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
FFMpegCore.Test/Resources/input_3sec.mp4
Normal file
BIN
FFMpegCore.Test/Resources/input_3sec.mp4
Normal file
Binary file not shown.
BIN
FFMpegCore.Test/Resources/input_3sec.webm
Normal file
BIN
FFMpegCore.Test/Resources/input_3sec.webm
Normal file
Binary file not shown.
BIN
FFMpegCore.Test/Resources/input_audio_only_10sec.mp4
Normal file
BIN
FFMpegCore.Test/Resources/input_audio_only_10sec.mp4
Normal file
Binary file not shown.
BIN
FFMpegCore.Test/Resources/input_video_only_3sec.mp4
Normal file
BIN
FFMpegCore.Test/Resources/input_video_only_3sec.mp4
Normal file
Binary file not shown.
Binary file not shown.
|
@ -63,51 +63,51 @@ public bool Convert(ContainerFormat type, bool multithreaded = false, VideoSize
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ConvertFromStreamPipe(ContainerFormat type, params IArgument[] inputArguments)
|
private void ConvertFromStreamPipe(ContainerFormat type, params IArgument[] arguments)
|
||||||
{
|
{
|
||||||
var output = Input.OutputLocation(type);
|
var output = Input.OutputLocation(type);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
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 processor = FFMpegArguments
|
||||||
|
.FromPipeInput(new StreamPipeSource(inputStream))
|
||||||
|
.OutputToFile(output, false, opt =>
|
||||||
|
{
|
||||||
|
foreach (var arg in arguments)
|
||||||
|
opt.WithArgument(arg);
|
||||||
|
});
|
||||||
|
|
||||||
|
var scaling = arguments.OfType<ScaleArgument>().FirstOrDefault();
|
||||||
|
|
||||||
|
var success = processor.ProcessSynchronously();
|
||||||
|
|
||||||
|
var outputVideo = FFProbe.Analyse(output);
|
||||||
|
|
||||||
|
Assert.IsTrue(success);
|
||||||
|
Assert.IsTrue(File.Exists(output));
|
||||||
|
Assert.IsTrue(Math.Abs((outputVideo.Duration - input.Duration).TotalMilliseconds) < 1000.0 / input.PrimaryVideoStream.FrameRate);
|
||||||
|
|
||||||
|
if (scaling?.Size == null)
|
||||||
{
|
{
|
||||||
var pipeSource = new StreamPipeSource(inputStream);
|
Assert.AreEqual(outputVideo.PrimaryVideoStream.Width, input.PrimaryVideoStream.Width);
|
||||||
var arguments = FFMpegArguments.FromPipe(pipeSource);
|
Assert.AreEqual(outputVideo.PrimaryVideoStream.Height, input.PrimaryVideoStream.Height);
|
||||||
foreach (var arg in inputArguments)
|
}
|
||||||
arguments.WithArgument(arg);
|
else
|
||||||
var processor = arguments.OutputToFile(output);
|
{
|
||||||
|
if (scaling.Size.Value.Width != -1)
|
||||||
var scaling = arguments.Find<ScaleArgument>();
|
|
||||||
|
|
||||||
var success = processor.ProcessSynchronously();
|
|
||||||
|
|
||||||
var outputVideo = FFProbe.Analyse(output);
|
|
||||||
|
|
||||||
Assert.IsTrue(success);
|
|
||||||
Assert.IsTrue(File.Exists(output));
|
|
||||||
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.Width, scaling.Size.Value.Width);
|
||||||
Assert.AreEqual(outputVideo.PrimaryVideoStream.Height, input.PrimaryVideoStream.Height);
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if (scaling.Size.Value.Height != -1)
|
||||||
{
|
{
|
||||||
if (scaling.Size.Value.Width != -1)
|
Assert.AreEqual(outputVideo.PrimaryVideoStream.Height, scaling.Size.Value.Height);
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Assert.AreNotEqual(outputVideo.PrimaryVideoStream.Width, input.PrimaryVideoStream.Width);
|
||||||
|
Assert.AreNotEqual(outputVideo.PrimaryVideoStream.Height, input.PrimaryVideoStream.Height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
@ -117,17 +117,18 @@ private void ConvertFromStreamPipe(ContainerFormat type, params IArgument[] inpu
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ConvertToStreamPipe(params IArgument[] inputArguments)
|
private void ConvertToStreamPipe(params IArgument[] arguments)
|
||||||
{
|
{
|
||||||
using var ms = new MemoryStream();
|
using var ms = new MemoryStream();
|
||||||
var arguments = FFMpegArguments.FromInputFiles(VideoLibrary.LocalVideo);
|
var processor = FFMpegArguments
|
||||||
foreach (var arg in inputArguments)
|
.FromFileInput(VideoLibrary.LocalVideo)
|
||||||
arguments.WithArgument(arg);
|
.OutputToPipe(new StreamPipeSink(ms), opt =>
|
||||||
|
{
|
||||||
|
foreach (var arg in arguments)
|
||||||
|
opt.WithArgument(arg);
|
||||||
|
});
|
||||||
|
|
||||||
var streamPipeDataReader = new StreamPipeSink(ms);
|
var scaling = arguments.OfType<ScaleArgument>().FirstOrDefault();
|
||||||
var processor = arguments.OutputToPipe(streamPipeDataReader);
|
|
||||||
|
|
||||||
var scaling = arguments.Find<ScaleArgument>();
|
|
||||||
|
|
||||||
processor.ProcessSynchronously();
|
processor.ProcessSynchronously();
|
||||||
|
|
||||||
|
@ -159,7 +160,7 @@ private void ConvertToStreamPipe(params IArgument[] inputArguments)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Convert(ContainerFormat type, Action<IMediaAnalysis> validationMethod, params IArgument[] inputArguments)
|
public void Convert(ContainerFormat type, Action<IMediaAnalysis> validationMethod, params IArgument[] arguments)
|
||||||
{
|
{
|
||||||
var output = Input.OutputLocation(type);
|
var output = Input.OutputLocation(type);
|
||||||
|
|
||||||
|
@ -167,13 +168,15 @@ public void Convert(ContainerFormat type, Action<IMediaAnalysis> validationMetho
|
||||||
{
|
{
|
||||||
var input = FFProbe.Analyse(Input.FullName);
|
var input = FFProbe.Analyse(Input.FullName);
|
||||||
|
|
||||||
var arguments = FFMpegArguments.FromInputFiles(VideoLibrary.LocalVideo.FullName);
|
var processor = FFMpegArguments
|
||||||
foreach (var arg in inputArguments)
|
.FromFileInput(VideoLibrary.LocalVideo)
|
||||||
arguments.WithArgument(arg);
|
.OutputToFile(output, false, opt =>
|
||||||
|
{
|
||||||
|
foreach (var arg in arguments)
|
||||||
|
opt.WithArgument(arg);
|
||||||
|
});
|
||||||
|
|
||||||
var processor = arguments.OutputToFile(output);
|
var scaling = arguments.OfType<ScaleArgument>().FirstOrDefault();
|
||||||
|
|
||||||
var scaling = arguments.Find<ScaleArgument>();
|
|
||||||
processor.ProcessSynchronously();
|
processor.ProcessSynchronously();
|
||||||
|
|
||||||
var outputVideo = FFProbe.Analyse(output);
|
var outputVideo = FFProbe.Analyse(output);
|
||||||
|
@ -214,19 +217,19 @@ public void Convert(ContainerFormat type, params IArgument[] inputArguments)
|
||||||
Convert(type, null, inputArguments);
|
Convert(type, null, inputArguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ConvertFromPipe(ContainerFormat type, System.Drawing.Imaging.PixelFormat fmt, params IArgument[] inputArguments)
|
public void ConvertFromPipe(ContainerFormat type, System.Drawing.Imaging.PixelFormat fmt, params IArgument[] arguments)
|
||||||
{
|
{
|
||||||
var output = Input.OutputLocation(type);
|
var output = Input.OutputLocation(type);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var videoFramesSource = new RawVideoPipeSource(BitmapSource.CreateBitmaps(128, fmt, 256, 256));
|
var videoFramesSource = new RawVideoPipeSource(BitmapSource.CreateBitmaps(128, fmt, 256, 256));
|
||||||
var arguments = FFMpegArguments.FromPipe(videoFramesSource);
|
var processor = FFMpegArguments.FromPipeInput(videoFramesSource).OutputToFile(output, false, opt =>
|
||||||
foreach (var arg in inputArguments)
|
{
|
||||||
arguments.WithArgument(arg);
|
foreach (var arg in arguments)
|
||||||
var processor = arguments.OutputToFile(output);
|
opt.WithArgument(arg);
|
||||||
|
});
|
||||||
var scaling = arguments.Find<ScaleArgument>();
|
var scaling = arguments.OfType<ScaleArgument>().FirstOrDefault();
|
||||||
processor.ProcessSynchronously();
|
processor.ProcessSynchronously();
|
||||||
|
|
||||||
var outputVideo = FFProbe.Analyse(output);
|
var outputVideo = FFProbe.Analyse(output);
|
||||||
|
@ -262,26 +265,26 @@ public void ConvertFromPipe(ContainerFormat type, System.Drawing.Imaging.PixelFo
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod, Timeout(10000)]
|
||||||
public void Video_ToMP4()
|
public void Video_ToMP4()
|
||||||
{
|
{
|
||||||
Convert(VideoType.Mp4);
|
Convert(VideoType.Mp4);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod, Timeout(10000)]
|
||||||
public void Video_ToMP4_YUV444p()
|
public void Video_ToMP4_YUV444p()
|
||||||
{
|
{
|
||||||
Convert(VideoType.Mp4, (a) => Assert.IsTrue(a.VideoStreams.First().PixelFormat == "yuv444p"),
|
Convert(VideoType.Mp4, (a) => Assert.IsTrue(a.VideoStreams.First().PixelFormat == "yuv444p"),
|
||||||
new ForcePixelFormat("yuv444p"));
|
new ForcePixelFormat("yuv444p"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod, Timeout(10000)]
|
||||||
public void Video_ToMP4_Args()
|
public void Video_ToMP4_Args()
|
||||||
{
|
{
|
||||||
Convert(VideoType.Mp4, new VideoCodecArgument(VideoCodec.LibX264));
|
Convert(VideoType.Mp4, new VideoCodecArgument(VideoCodec.LibX264));
|
||||||
}
|
}
|
||||||
|
|
||||||
[DataTestMethod]
|
[DataTestMethod, Timeout(10000)]
|
||||||
[DataRow(System.Drawing.Imaging.PixelFormat.Format24bppRgb)]
|
[DataRow(System.Drawing.Imaging.PixelFormat.Format24bppRgb)]
|
||||||
[DataRow(System.Drawing.Imaging.PixelFormat.Format32bppArgb)]
|
[DataRow(System.Drawing.Imaging.PixelFormat.Format32bppArgb)]
|
||||||
// [DataRow(PixelFormat.Format48bppRgb)]
|
// [DataRow(PixelFormat.Format48bppRgb)]
|
||||||
|
@ -290,13 +293,13 @@ public void Video_ToMP4_Args_Pipe(System.Drawing.Imaging.PixelFormat pixelFormat
|
||||||
ConvertFromPipe(VideoType.Mp4, pixelFormat, new VideoCodecArgument(VideoCodec.LibX264));
|
ConvertFromPipe(VideoType.Mp4, pixelFormat, new VideoCodecArgument(VideoCodec.LibX264));
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod, Timeout(10000)]
|
||||||
public void Video_ToMP4_Args_StreamPipe()
|
public void Video_ToMP4_Args_StreamPipe()
|
||||||
{
|
{
|
||||||
ConvertFromStreamPipe(VideoType.Mp4, new VideoCodecArgument(VideoCodec.LibX264));
|
ConvertFromStreamPipe(VideoType.Mp4, new VideoCodecArgument(VideoCodec.LibX264));
|
||||||
}
|
}
|
||||||
|
|
||||||
// [TestMethod, Timeout(10000)]
|
[TestMethod, Timeout(10000)]
|
||||||
public async Task Video_ToMP4_Args_StreamOutputPipe_Async_Failure()
|
public async Task Video_ToMP4_Args_StreamOutputPipe_Async_Failure()
|
||||||
{
|
{
|
||||||
await Assert.ThrowsExceptionAsync<FFMpegException>(async () =>
|
await Assert.ThrowsExceptionAsync<FFMpegException>(async () =>
|
||||||
|
@ -304,61 +307,60 @@ await Assert.ThrowsExceptionAsync<FFMpegException>(async () =>
|
||||||
await using var ms = new MemoryStream();
|
await using var ms = new MemoryStream();
|
||||||
var pipeSource = new StreamPipeSink(ms);
|
var pipeSource = new StreamPipeSink(ms);
|
||||||
await FFMpegArguments
|
await FFMpegArguments
|
||||||
.FromInputFiles(VideoLibrary.LocalVideo)
|
.FromFileInput(VideoLibrary.LocalVideo)
|
||||||
.ForceFormat("mkv")
|
.OutputToPipe(pipeSource, opt => opt.ForceFormat("mkv"))
|
||||||
.OutputToPipe(pipeSource)
|
|
||||||
.ProcessAsynchronously();
|
.ProcessAsynchronously();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// [TestMethod, Timeout(10000)]
|
[TestMethod, Timeout(10000)]
|
||||||
public void Video_ToMP4_Args_StreamOutputPipe_Failure()
|
public void Video_ToMP4_Args_StreamOutputPipe_Failure()
|
||||||
{
|
{
|
||||||
Assert.ThrowsException<FFMpegException>(() => ConvertToStreamPipe(new ForceFormatArgument("mkv")));
|
Assert.ThrowsException<FFMpegException>(() => ConvertToStreamPipe(new ForceFormatArgument("mkv")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod, Timeout(10000)]
|
||||||
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 StreamPipeSink(ms);
|
var pipeSource = new StreamPipeSink(ms);
|
||||||
FFMpegArguments
|
FFMpegArguments
|
||||||
.FromInputFiles(VideoLibrary.LocalVideo)
|
.FromFileInput(VideoLibrary.LocalVideo)
|
||||||
.WithVideoCodec(VideoCodec.LibX264)
|
.OutputToPipe(pipeSource, opt => opt
|
||||||
.ForceFormat("matroska")
|
.WithVideoCodec(VideoCodec.LibX264)
|
||||||
.OutputToPipe(pipeSource)
|
.ForceFormat("matroska"))
|
||||||
.ProcessAsynchronously()
|
.ProcessAsynchronously()
|
||||||
.WaitForResult();
|
.WaitForResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod, Timeout(10000)]
|
||||||
public async Task TestDuplicateRun()
|
public async Task TestDuplicateRun()
|
||||||
{
|
{
|
||||||
FFMpegArguments.FromInputFiles(VideoLibrary.LocalVideo)
|
FFMpegArguments.FromFileInput(VideoLibrary.LocalVideo)
|
||||||
.OutputToFile("temporary.mp4", true)
|
.OutputToFile("temporary.mp4")
|
||||||
.ProcessSynchronously();
|
.ProcessSynchronously();
|
||||||
|
|
||||||
await FFMpegArguments.FromInputFiles(VideoLibrary.LocalVideo)
|
await FFMpegArguments.FromFileInput(VideoLibrary.LocalVideo)
|
||||||
.OutputToFile("temporary.mp4", true)
|
.OutputToFile("temporary.mp4")
|
||||||
.ProcessAsynchronously();
|
.ProcessAsynchronously();
|
||||||
|
|
||||||
File.Delete("temporary.mp4");
|
File.Delete("temporary.mp4");
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod, Timeout(10000)]
|
||||||
public void Video_ToMP4_Args_StreamOutputPipe()
|
public void Video_ToMP4_Args_StreamOutputPipe()
|
||||||
{
|
{
|
||||||
ConvertToStreamPipe(new VideoCodecArgument(VideoCodec.LibX264), new ForceFormatArgument("matroska"));
|
ConvertToStreamPipe(new VideoCodecArgument(VideoCodec.LibX264), new ForceFormatArgument("matroska"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod, Timeout(10000)]
|
||||||
public void Video_ToTS()
|
public void Video_ToTS()
|
||||||
{
|
{
|
||||||
Convert(VideoType.Ts);
|
Convert(VideoType.Ts);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod, Timeout(10000)]
|
||||||
public void Video_ToTS_Args()
|
public void Video_ToTS_Args()
|
||||||
{
|
{
|
||||||
Convert(VideoType.Ts,
|
Convert(VideoType.Ts,
|
||||||
|
@ -367,7 +369,7 @@ public void Video_ToTS_Args()
|
||||||
new ForceFormatArgument(VideoType.MpegTs));
|
new ForceFormatArgument(VideoType.MpegTs));
|
||||||
}
|
}
|
||||||
|
|
||||||
[DataTestMethod]
|
[DataTestMethod, Timeout(10000)]
|
||||||
[DataRow(System.Drawing.Imaging.PixelFormat.Format24bppRgb)]
|
[DataRow(System.Drawing.Imaging.PixelFormat.Format24bppRgb)]
|
||||||
[DataRow(System.Drawing.Imaging.PixelFormat.Format32bppArgb)]
|
[DataRow(System.Drawing.Imaging.PixelFormat.Format32bppArgb)]
|
||||||
// [DataRow(PixelFormat.Format48bppRgb)]
|
// [DataRow(PixelFormat.Format48bppRgb)]
|
||||||
|
@ -376,19 +378,19 @@ public void Video_ToTS_Args_Pipe(System.Drawing.Imaging.PixelFormat pixelFormat)
|
||||||
ConvertFromPipe(VideoType.Ts, pixelFormat, new ForceFormatArgument(VideoType.Ts));
|
ConvertFromPipe(VideoType.Ts, pixelFormat, new ForceFormatArgument(VideoType.Ts));
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod, Timeout(10000)]
|
||||||
public void Video_ToOGV_Resize()
|
public void Video_ToOGV_Resize()
|
||||||
{
|
{
|
||||||
Convert(VideoType.Ogv, true, VideoSize.Ed);
|
Convert(VideoType.Ogv, true, VideoSize.Ed);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod, Timeout(10000)]
|
||||||
public void Video_ToOGV_Resize_Args()
|
public void Video_ToOGV_Resize_Args()
|
||||||
{
|
{
|
||||||
Convert(VideoType.Ogv, new ScaleArgument(VideoSize.Ed), new VideoCodecArgument(VideoCodec.LibTheora));
|
Convert(VideoType.Ogv, new ScaleArgument(VideoSize.Ed), new VideoCodecArgument(VideoCodec.LibTheora));
|
||||||
}
|
}
|
||||||
|
|
||||||
[DataTestMethod]
|
[DataTestMethod, Timeout(10000)]
|
||||||
[DataRow(System.Drawing.Imaging.PixelFormat.Format24bppRgb)]
|
[DataRow(System.Drawing.Imaging.PixelFormat.Format24bppRgb)]
|
||||||
[DataRow(System.Drawing.Imaging.PixelFormat.Format32bppArgb)]
|
[DataRow(System.Drawing.Imaging.PixelFormat.Format32bppArgb)]
|
||||||
// [DataRow(PixelFormat.Format48bppRgb)]
|
// [DataRow(PixelFormat.Format48bppRgb)]
|
||||||
|
@ -397,19 +399,19 @@ public void Video_ToOGV_Resize_Args_Pipe(System.Drawing.Imaging.PixelFormat pixe
|
||||||
ConvertFromPipe(VideoType.Ogv, pixelFormat, new ScaleArgument(VideoSize.Ed), new VideoCodecArgument(VideoCodec.LibTheora));
|
ConvertFromPipe(VideoType.Ogv, pixelFormat, new ScaleArgument(VideoSize.Ed), new VideoCodecArgument(VideoCodec.LibTheora));
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod, Timeout(10000)]
|
||||||
public void Video_ToMP4_Resize()
|
public void Video_ToMP4_Resize()
|
||||||
{
|
{
|
||||||
Convert(VideoType.Mp4, true, VideoSize.Ed);
|
Convert(VideoType.Mp4, true, VideoSize.Ed);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod, Timeout(10000)]
|
||||||
public void Video_ToMP4_Resize_Args()
|
public void Video_ToMP4_Resize_Args()
|
||||||
{
|
{
|
||||||
Convert(VideoType.Mp4, new ScaleArgument(VideoSize.Ld), new VideoCodecArgument(VideoCodec.LibX264));
|
Convert(VideoType.Mp4, new ScaleArgument(VideoSize.Ld), new VideoCodecArgument(VideoCodec.LibX264));
|
||||||
}
|
}
|
||||||
|
|
||||||
[DataTestMethod]
|
[DataTestMethod, Timeout(10000)]
|
||||||
[DataRow(System.Drawing.Imaging.PixelFormat.Format24bppRgb)]
|
[DataRow(System.Drawing.Imaging.PixelFormat.Format24bppRgb)]
|
||||||
[DataRow(System.Drawing.Imaging.PixelFormat.Format32bppArgb)]
|
[DataRow(System.Drawing.Imaging.PixelFormat.Format32bppArgb)]
|
||||||
// [DataRow(PixelFormat.Format48bppRgb)]
|
// [DataRow(PixelFormat.Format48bppRgb)]
|
||||||
|
@ -418,31 +420,31 @@ public void Video_ToMP4_Resize_Args_Pipe(System.Drawing.Imaging.PixelFormat pixe
|
||||||
ConvertFromPipe(VideoType.Mp4, pixelFormat, new ScaleArgument(VideoSize.Ld), new VideoCodecArgument(VideoCodec.LibX264));
|
ConvertFromPipe(VideoType.Mp4, pixelFormat, new ScaleArgument(VideoSize.Ld), new VideoCodecArgument(VideoCodec.LibX264));
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod, Timeout(10000)]
|
||||||
public void Video_ToOGV()
|
public void Video_ToOGV()
|
||||||
{
|
{
|
||||||
Convert(VideoType.Ogv);
|
Convert(VideoType.Ogv);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod, Timeout(10000)]
|
||||||
public void Video_ToMP4_MultiThread()
|
public void Video_ToMP4_MultiThread()
|
||||||
{
|
{
|
||||||
Convert(VideoType.Mp4, true);
|
Convert(VideoType.Mp4, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod, Timeout(10000)]
|
||||||
public void Video_ToTS_MultiThread()
|
public void Video_ToTS_MultiThread()
|
||||||
{
|
{
|
||||||
Convert(VideoType.Ts, true);
|
Convert(VideoType.Ts, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod, Timeout(10000)]
|
||||||
public void Video_ToOGV_MultiThread()
|
public void Video_ToOGV_MultiThread()
|
||||||
{
|
{
|
||||||
Convert(VideoType.Ogv, true);
|
Convert(VideoType.Ogv, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod, Timeout(10000)]
|
||||||
public void Video_Snapshot_InMemory()
|
public void Video_Snapshot_InMemory()
|
||||||
{
|
{
|
||||||
var output = Input.OutputLocation(ImageType.Png);
|
var output = Input.OutputLocation(ImageType.Png);
|
||||||
|
@ -463,7 +465,7 @@ public void Video_Snapshot_InMemory()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod, Timeout(10000)]
|
||||||
public void Video_Snapshot_PersistSnapshot()
|
public void Video_Snapshot_PersistSnapshot()
|
||||||
{
|
{
|
||||||
var output = Input.OutputLocation(ImageType.Png);
|
var output = Input.OutputLocation(ImageType.Png);
|
||||||
|
@ -486,7 +488,7 @@ public void Video_Snapshot_PersistSnapshot()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod, Timeout(10000)]
|
||||||
public void Video_Join()
|
public void Video_Join()
|
||||||
{
|
{
|
||||||
var output = Input.OutputLocation(VideoType.Mp4);
|
var output = Input.OutputLocation(VideoType.Mp4);
|
||||||
|
@ -520,7 +522,7 @@ public void Video_Join()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod, Timeout(10000)]
|
||||||
public void Video_Join_Image_Sequence()
|
public void Video_Join_Image_Sequence()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
@ -558,17 +560,17 @@ public void Video_Join_Image_Sequence()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod, Timeout(10000)]
|
||||||
public void Video_With_Only_Audio_Should_Extract_Metadata()
|
public void Video_With_Only_Audio_Should_Extract_Metadata()
|
||||||
{
|
{
|
||||||
var video = FFProbe.Analyse(VideoLibrary.LocalVideoAudioOnly.FullName);
|
var video = FFProbe.Analyse(VideoLibrary.LocalVideoAudioOnly.FullName);
|
||||||
Assert.AreEqual(null, video.PrimaryVideoStream);
|
Assert.AreEqual(null, video.PrimaryVideoStream);
|
||||||
Assert.AreEqual("aac", video.PrimaryAudioStream.CodecName);
|
Assert.AreEqual("aac", video.PrimaryAudioStream.CodecName);
|
||||||
Assert.AreEqual(79.5, video.Duration.TotalSeconds, 0.5);
|
Assert.AreEqual(10, video.Duration.TotalSeconds, 0.5);
|
||||||
// Assert.AreEqual(1.25, video.Size);
|
// Assert.AreEqual(1.25, video.Size);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod, Timeout(10000)]
|
||||||
public void Video_Duration()
|
public void Video_Duration()
|
||||||
{
|
{
|
||||||
var video = FFProbe.Analyse(VideoLibrary.LocalVideo.FullName);
|
var video = FFProbe.Analyse(VideoLibrary.LocalVideo.FullName);
|
||||||
|
@ -577,9 +579,8 @@ public void Video_Duration()
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
FFMpegArguments
|
FFMpegArguments
|
||||||
.FromInputFiles(VideoLibrary.LocalVideo)
|
.FromFileInput(VideoLibrary.LocalVideo)
|
||||||
.WithDuration(TimeSpan.FromSeconds(video.Duration.TotalSeconds - 5))
|
.OutputToFile(output, false, opt => opt.WithDuration(TimeSpan.FromSeconds(video.Duration.TotalSeconds - 2)))
|
||||||
.OutputToFile(output)
|
|
||||||
.ProcessSynchronously();
|
.ProcessSynchronously();
|
||||||
|
|
||||||
Assert.IsTrue(File.Exists(output));
|
Assert.IsTrue(File.Exists(output));
|
||||||
|
@ -588,7 +589,7 @@ public void Video_Duration()
|
||||||
Assert.AreEqual(video.Duration.Days, outputVideo.Duration.Days);
|
Assert.AreEqual(video.Duration.Days, outputVideo.Duration.Days);
|
||||||
Assert.AreEqual(video.Duration.Hours, outputVideo.Duration.Hours);
|
Assert.AreEqual(video.Duration.Hours, outputVideo.Duration.Hours);
|
||||||
Assert.AreEqual(video.Duration.Minutes, outputVideo.Duration.Minutes);
|
Assert.AreEqual(video.Duration.Minutes, outputVideo.Duration.Minutes);
|
||||||
Assert.AreEqual(video.Duration.Seconds - 5, outputVideo.Duration.Seconds);
|
Assert.AreEqual(video.Duration.Seconds - 2, outputVideo.Duration.Seconds);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
@ -597,7 +598,7 @@ public void Video_Duration()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod, Timeout(10000)]
|
||||||
public void Video_UpdatesProgress()
|
public void Video_UpdatesProgress()
|
||||||
{
|
{
|
||||||
var output = Input.OutputLocation(VideoType.Mp4);
|
var output = Input.OutputLocation(VideoType.Mp4);
|
||||||
|
@ -613,9 +614,9 @@ public void Video_UpdatesProgress()
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var success = FFMpegArguments
|
var success = FFMpegArguments
|
||||||
.FromInputFiles(VideoLibrary.LocalVideo)
|
.FromFileInput(VideoLibrary.LocalVideo)
|
||||||
.WithDuration(TimeSpan.FromSeconds(8))
|
.OutputToFile(output, false, opt => opt
|
||||||
.OutputToFile(output)
|
.WithDuration(TimeSpan.FromSeconds(2)))
|
||||||
.NotifyOnProgress(OnPercentageProgess, analysis.Duration)
|
.NotifyOnProgress(OnPercentageProgess, analysis.Duration)
|
||||||
.NotifyOnProgress(OnTimeProgess)
|
.NotifyOnProgress(OnTimeProgess)
|
||||||
.ProcessSynchronously();
|
.ProcessSynchronously();
|
||||||
|
@ -632,7 +633,7 @@ public void Video_UpdatesProgress()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod, Timeout(10000)]
|
||||||
public void Video_TranscodeInMemory()
|
public void Video_TranscodeInMemory()
|
||||||
{
|
{
|
||||||
using var resStream = new MemoryStream();
|
using var resStream = new MemoryStream();
|
||||||
|
@ -640,10 +641,10 @@ public void Video_TranscodeInMemory()
|
||||||
var writer = new RawVideoPipeSource(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)
|
.FromPipeInput(writer)
|
||||||
.WithVideoCodec("vp9")
|
.OutputToPipe(reader, opt => opt
|
||||||
.ForceFormat("webm")
|
.WithVideoCodec("vp9")
|
||||||
.OutputToPipe(reader)
|
.ForceFormat("webm"))
|
||||||
.ProcessSynchronously();
|
.ProcessSynchronously();
|
||||||
|
|
||||||
resStream.Position = 0;
|
resStream.Position = 0;
|
||||||
|
@ -652,26 +653,36 @@ public void Video_TranscodeInMemory()
|
||||||
Assert.AreEqual(vi.PrimaryVideoStream.Height, 128);
|
Assert.AreEqual(vi.PrimaryVideoStream.Height, 128);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod, Timeout(10000)]
|
||||||
public async Task Video_Cancel_Async()
|
public async Task Video_Cancel_Async()
|
||||||
{
|
{
|
||||||
await using var resStream = new MemoryStream();
|
var output = Input.OutputLocation(VideoType.Mp4);
|
||||||
var reader = new StreamPipeSink(resStream);
|
|
||||||
var writer = new RawVideoPipeSource(BitmapSource.CreateBitmaps(512, System.Drawing.Imaging.PixelFormat.Format24bppRgb, 128, 128));
|
|
||||||
|
|
||||||
var task = FFMpegArguments
|
var task = FFMpegArguments
|
||||||
.FromPipe(writer)
|
.FromFileInput(VideoLibrary.LocalVideo)
|
||||||
.WithVideoCodec("vp9")
|
.OutputToFile(output, false, opt => opt
|
||||||
.ForceFormat("webm")
|
.Resize(new Size(1000, 1000))
|
||||||
.OutputToPipe(reader)
|
.WithAudioCodec(AudioCodec.Aac)
|
||||||
|
.WithVideoCodec(VideoCodec.LibX264)
|
||||||
|
.WithConstantRateFactor(14)
|
||||||
|
.WithSpeedPreset(Speed.VerySlow)
|
||||||
|
.Loop(3))
|
||||||
.CancellableThrough(out var cancel)
|
.CancellableThrough(out var cancel)
|
||||||
.ProcessAsynchronously(false);
|
.ProcessAsynchronously(false);
|
||||||
|
|
||||||
await Task.Delay(300);
|
try
|
||||||
cancel();
|
{
|
||||||
|
await Task.Delay(300);
|
||||||
|
cancel();
|
||||||
|
|
||||||
var result = await task;
|
var result = await task;
|
||||||
Assert.IsFalse(result);
|
Assert.IsFalse(result);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (File.Exists(output))
|
||||||
|
File.Delete(output);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ public ConcatArgument(IEnumerable<string> values)
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Pre() { }
|
public void Pre() { }
|
||||||
public Task During(CancellationToken? cancellationToken = null) => Task.CompletedTask;
|
public Task During(CancellationToken cancellationToken = default) => Task.CompletedTask;
|
||||||
public void Post() { }
|
public void Post() { }
|
||||||
|
|
||||||
public string Text => $"-i \"concat:{string.Join(@"|", Values)}\"";
|
public string Text => $"-i \"concat:{string.Join(@"|", Values)}\"";
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
namespace FFMpegCore.Arguments
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Represents cpu speed parameter
|
|
||||||
/// </summary>
|
|
||||||
public class CpuSpeedArgument : IArgument
|
|
||||||
{
|
|
||||||
public readonly int CpuSpeed;
|
|
||||||
public CpuSpeedArgument(int cpuSpeed)
|
|
||||||
{
|
|
||||||
CpuSpeed = cpuSpeed;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Text => $"-quality good -cpu-used {CpuSpeed} -deadline realtime";
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -21,7 +21,7 @@ public DemuxConcatArgument(IEnumerable<string> values)
|
||||||
private readonly string _tempFileName = Path.Combine(FFMpegOptions.Options.TempDirectory, Guid.NewGuid() + ".txt");
|
private readonly string _tempFileName = Path.Combine(FFMpegOptions.Options.TempDirectory, Guid.NewGuid() + ".txt");
|
||||||
|
|
||||||
public void Pre() => File.WriteAllLines(_tempFileName, Values);
|
public void Pre() => File.WriteAllLines(_tempFileName, Values);
|
||||||
public Task During(CancellationToken? cancellationToken = null) => Task.CompletedTask;
|
public Task During(CancellationToken cancellationToken = default) => Task.CompletedTask;
|
||||||
public void Post() => File.Delete(_tempFileName);
|
public void Post() => File.Delete(_tempFileName);
|
||||||
|
|
||||||
public string Text => $"-f concat -safe 0 -i \"{_tempFileName}\"";
|
public string Text => $"-f concat -safe 0 -i \"{_tempFileName}\"";
|
||||||
|
|
|
@ -4,7 +4,7 @@ namespace FFMpegCore.Arguments
|
||||||
{
|
{
|
||||||
public class ForcePixelFormat : IArgument
|
public class ForcePixelFormat : IArgument
|
||||||
{
|
{
|
||||||
public string PixelFormat { get; private set; }
|
public string PixelFormat { get; }
|
||||||
public string Text => $"-pix_fmt {PixelFormat}";
|
public string Text => $"-pix_fmt {PixelFormat}";
|
||||||
|
|
||||||
public ForcePixelFormat(string format)
|
public ForcePixelFormat(string format)
|
||||||
|
|
|
@ -12,6 +12,6 @@ public FrameRateArgument(double framerate)
|
||||||
Framerate = framerate;
|
Framerate = framerate;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Text => $"-r {Framerate}";
|
public string Text => $"-r {Framerate.ToString(System.Globalization.CultureInfo.InvariantCulture)}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
18
FFMpegCore/FFMpeg/Arguments/HardwareAccelerationArgument.cs
Normal file
18
FFMpegCore/FFMpeg/Arguments/HardwareAccelerationArgument.cs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
using FFMpegCore.Enums;
|
||||||
|
|
||||||
|
namespace FFMpegCore.Arguments
|
||||||
|
{
|
||||||
|
public class HardwareAccelerationArgument : IArgument
|
||||||
|
{
|
||||||
|
public HardwareAccelerationDevice HardwareAccelerationDevice { get; }
|
||||||
|
|
||||||
|
public HardwareAccelerationArgument(HardwareAccelerationDevice hardwareAccelerationDevice)
|
||||||
|
{
|
||||||
|
HardwareAccelerationDevice = hardwareAccelerationDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Text => HardwareAccelerationDevice != HardwareAccelerationDevice.Auto
|
||||||
|
? $"-hwaccel {HardwareAccelerationDevice.ToString().ToLower()}"
|
||||||
|
: "-hwaccel";
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,7 +6,7 @@ namespace FFMpegCore.Arguments
|
||||||
public interface IInputOutputArgument : IArgument
|
public interface IInputOutputArgument : IArgument
|
||||||
{
|
{
|
||||||
void Pre();
|
void Pre();
|
||||||
Task During(CancellationToken? cancellationToken = null);
|
Task During(CancellationToken cancellationToken = default);
|
||||||
void Post();
|
void Post();
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,6 +1,4 @@
|
||||||
using System;
|
using System.IO;
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
@ -12,33 +10,25 @@ namespace FFMpegCore.Arguments
|
||||||
public class InputArgument : IInputArgument
|
public class InputArgument : IInputArgument
|
||||||
{
|
{
|
||||||
public readonly bool VerifyExists;
|
public readonly bool VerifyExists;
|
||||||
public readonly string[] FilePaths;
|
public readonly string FilePath;
|
||||||
|
|
||||||
public InputArgument(bool verifyExists, params string[] filePaths)
|
public InputArgument(bool verifyExists, string filePaths)
|
||||||
{
|
{
|
||||||
VerifyExists = verifyExists;
|
VerifyExists = verifyExists;
|
||||||
FilePaths = filePaths;
|
FilePath = filePaths;
|
||||||
}
|
}
|
||||||
|
|
||||||
public InputArgument(params string[] filePaths) : this(true, filePaths) { }
|
public InputArgument(string path, bool verifyExists) : this(verifyExists, path) { }
|
||||||
public InputArgument(params FileInfo[] fileInfos) : this(false, fileInfos) { }
|
|
||||||
public InputArgument(params Uri[] uris) : this(false, uris) { }
|
|
||||||
public InputArgument(bool verifyExists, params FileInfo[] fileInfos) : this(verifyExists, fileInfos.Select(v => v.FullName).ToArray()) { }
|
|
||||||
public InputArgument(bool verifyExists, params Uri[] uris) : this(verifyExists, uris.Select(v => v.AbsoluteUri).ToArray()) { }
|
|
||||||
|
|
||||||
public void Pre()
|
public void Pre()
|
||||||
{
|
{
|
||||||
if (!VerifyExists) return;
|
if (VerifyExists && !File.Exists(FilePath))
|
||||||
foreach (var filePath in FilePaths)
|
throw new FileNotFoundException("Input file not found", FilePath);
|
||||||
{
|
|
||||||
if (!File.Exists(filePath))
|
|
||||||
throw new FileNotFoundException("Input file not found", filePath);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task During(CancellationToken? cancellationToken = null) => Task.CompletedTask;
|
public Task During(CancellationToken cancellationToken = default) => Task.CompletedTask;
|
||||||
public void Post() { }
|
public void Post() { }
|
||||||
|
|
||||||
public string Text => string.Join(" ", FilePaths.Select(v => $"-i \"{v}\""));
|
public string Text => $"-i \"{FilePath}\"";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ public InputPipeArgument(IPipeSource writer) : base(PipeDirection.Out)
|
||||||
|
|
||||||
public override string Text => $"-y {Writer.GetFormat()} -i \"{PipePath}\"";
|
public override string Text => $"-y {Writer.GetFormat()} -i \"{PipePath}\"";
|
||||||
|
|
||||||
public override async Task ProcessDataAsync(CancellationToken token)
|
protected override async Task ProcessDataAsync(CancellationToken token)
|
||||||
{
|
{
|
||||||
await Pipe.WaitForConnectionAsync(token).ConfigureAwait(false);
|
await Pipe.WaitForConnectionAsync(token).ConfigureAwait(false);
|
||||||
if (!Pipe.IsConnected)
|
if (!Pipe.IsConnected)
|
||||||
|
|
|
@ -25,11 +25,9 @@ public void Pre()
|
||||||
if (!Overwrite && File.Exists(Path))
|
if (!Overwrite && File.Exists(Path))
|
||||||
throw new FFMpegException(FFMpegExceptionType.File, "Output file already exists and overwrite is disabled");
|
throw new FFMpegException(FFMpegExceptionType.File, "Output file already exists and overwrite is disabled");
|
||||||
}
|
}
|
||||||
public Task During(CancellationToken? cancellationToken = null) => Task.CompletedTask;
|
public Task During(CancellationToken cancellationToken = default) => Task.CompletedTask;
|
||||||
public void Post()
|
public void Post()
|
||||||
{
|
{
|
||||||
if (!File.Exists(Path))
|
|
||||||
throw new FFMpegException(FFMpegExceptionType.File, "Output file was not created");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public OutputArgument(FileInfo value) : this(value.FullName) { }
|
public OutputArgument(FileInfo value) : this(value.FullName) { }
|
||||||
|
|
|
@ -16,7 +16,7 @@ public OutputPipeArgument(IPipeSink reader) : base(PipeDirection.In)
|
||||||
|
|
||||||
public override string Text => $"\"{PipePath}\" -y";
|
public override string Text => $"\"{PipePath}\" -y";
|
||||||
|
|
||||||
public override async Task ProcessDataAsync(CancellationToken token)
|
protected override async Task ProcessDataAsync(CancellationToken token)
|
||||||
{
|
{
|
||||||
await Pipe.WaitForConnectionAsync(token).ConfigureAwait(false);
|
await Pipe.WaitForConnectionAsync(token).ConfigureAwait(false);
|
||||||
if (!Pipe.IsConnected)
|
if (!Pipe.IsConnected)
|
||||||
|
|
|
@ -34,19 +34,19 @@ public void Post()
|
||||||
Pipe = null!;
|
Pipe = null!;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task During(CancellationToken? cancellationToken = null)
|
public async Task During(CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await ProcessDataAsync(cancellationToken ?? CancellationToken.None).ConfigureAwait(false);
|
await ProcessDataAsync(cancellationToken);
|
||||||
}
|
}
|
||||||
catch (TaskCanceledException)
|
catch (TaskCanceledException)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
Post();
|
Pipe.Disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract Task ProcessDataAsync(CancellationToken token);
|
protected abstract Task ProcessDataAsync(CancellationToken token);
|
||||||
public abstract string Text { get; }
|
public abstract string Text { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,31 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace FFMpegCore.Arguments
|
|
||||||
{
|
|
||||||
public class SeekedFileInputArgument : IInputArgument
|
|
||||||
{
|
|
||||||
public readonly (string FilePath, TimeSpan StartTime)[] SeekedFiles;
|
|
||||||
|
|
||||||
public SeekedFileInputArgument((string file, TimeSpan startTime)[] seekedFiles)
|
|
||||||
{
|
|
||||||
SeekedFiles = seekedFiles;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Pre()
|
|
||||||
{
|
|
||||||
foreach (var (seekedFile, _) in SeekedFiles)
|
|
||||||
{
|
|
||||||
if (!File.Exists(seekedFile))
|
|
||||||
throw new FileNotFoundException("Input file not found", seekedFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public Task During(CancellationToken? cancellationToken = null) => Task.CompletedTask;
|
|
||||||
public void Post() { }
|
|
||||||
|
|
||||||
public string Text => string.Join(" ", SeekedFiles.Select(seekedFile => $"-ss {seekedFile.StartTime} -i \"{seekedFile.FilePath}\""));
|
|
||||||
}
|
|
||||||
}
|
|
14
FFMpegCore/FFMpeg/Enums/HardwareAccelerationDevice.cs
Normal file
14
FFMpegCore/FFMpeg/Enums/HardwareAccelerationDevice.cs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
namespace FFMpegCore.Enums
|
||||||
|
{
|
||||||
|
public enum HardwareAccelerationDevice
|
||||||
|
{
|
||||||
|
Auto,
|
||||||
|
D3D11VA,
|
||||||
|
DXVA2,
|
||||||
|
QSV,
|
||||||
|
CUVID,
|
||||||
|
VDPAU,
|
||||||
|
VAAPI,
|
||||||
|
LibMFX
|
||||||
|
}
|
||||||
|
}
|
|
@ -27,10 +27,10 @@ public static bool Snapshot(IMediaAnalysis source, string output, Size? size = n
|
||||||
if (Path.GetExtension(output) != FileExtension.Png)
|
if (Path.GetExtension(output) != FileExtension.Png)
|
||||||
output = Path.GetFileNameWithoutExtension(output) + FileExtension.Png;
|
output = Path.GetFileNameWithoutExtension(output) + FileExtension.Png;
|
||||||
|
|
||||||
var arguments = BuildSnapshotArguments(source, size, captureTime, videoStreamNumber);
|
var (arguments, outputOptions) = BuildSnapshotArguments(source, size, captureTime, videoStreamNumber);
|
||||||
|
|
||||||
return arguments
|
return arguments
|
||||||
.OutputToFile(output)
|
.OutputToFile(output, true, outputOptions)
|
||||||
.ProcessSynchronously();
|
.ProcessSynchronously();
|
||||||
}
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -47,10 +47,10 @@ public static Task<bool> SnapshotAsync(IMediaAnalysis source, string output, Siz
|
||||||
if (Path.GetExtension(output) != FileExtension.Png)
|
if (Path.GetExtension(output) != FileExtension.Png)
|
||||||
output = Path.GetFileNameWithoutExtension(output) + FileExtension.Png;
|
output = Path.GetFileNameWithoutExtension(output) + FileExtension.Png;
|
||||||
|
|
||||||
var arguments = BuildSnapshotArguments(source, size, captureTime, videoStreamNumber);
|
var (arguments, outputOptions) = BuildSnapshotArguments(source, size, captureTime, videoStreamNumber);
|
||||||
|
|
||||||
return arguments
|
return arguments
|
||||||
.OutputToFile(output)
|
.OutputToFile(output, true, outputOptions)
|
||||||
.ProcessAsynchronously();
|
.ProcessAsynchronously();
|
||||||
}
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -63,12 +63,12 @@ public static Task<bool> SnapshotAsync(IMediaAnalysis source, string output, Siz
|
||||||
/// <returns>Bitmap with the requested snapshot.</returns>
|
/// <returns>Bitmap with the requested snapshot.</returns>
|
||||||
public static Bitmap Snapshot(IMediaAnalysis source, Size? size = null, TimeSpan? captureTime = null, int videoStreamNumber = 0)
|
public static Bitmap Snapshot(IMediaAnalysis source, Size? size = null, TimeSpan? captureTime = null, int videoStreamNumber = 0)
|
||||||
{
|
{
|
||||||
var arguments = BuildSnapshotArguments(source, size, captureTime, videoStreamNumber);
|
var (arguments, outputOptions) = BuildSnapshotArguments(source, size, captureTime, videoStreamNumber);
|
||||||
using var ms = new MemoryStream();
|
using var ms = new MemoryStream();
|
||||||
|
|
||||||
arguments
|
arguments
|
||||||
.ForceFormat("rawvideo")
|
.OutputToPipe(new StreamPipeSink(ms), options => outputOptions(options
|
||||||
.OutputToPipe(new StreamPipeSink(ms))
|
.ForceFormat("rawvideo")))
|
||||||
.ProcessSynchronously();
|
.ProcessSynchronously();
|
||||||
|
|
||||||
ms.Position = 0;
|
ms.Position = 0;
|
||||||
|
@ -84,29 +84,31 @@ public static Bitmap Snapshot(IMediaAnalysis source, Size? size = null, TimeSpan
|
||||||
/// <returns>Bitmap with the requested snapshot.</returns>
|
/// <returns>Bitmap with the requested snapshot.</returns>
|
||||||
public static async Task<Bitmap> SnapshotAsync(IMediaAnalysis source, Size? size = null, TimeSpan? captureTime = null, int videoStreamNumber = 0)
|
public static async Task<Bitmap> SnapshotAsync(IMediaAnalysis source, Size? size = null, TimeSpan? captureTime = null, int videoStreamNumber = 0)
|
||||||
{
|
{
|
||||||
var arguments = BuildSnapshotArguments(source, size, captureTime, videoStreamNumber);
|
var (arguments, outputOptions) = BuildSnapshotArguments(source, size, captureTime, videoStreamNumber);
|
||||||
using var ms = new MemoryStream();
|
using var ms = new MemoryStream();
|
||||||
|
|
||||||
await arguments
|
await arguments
|
||||||
.ForceFormat("rawvideo")
|
.OutputToPipe(new StreamPipeSink(ms), options => outputOptions(options
|
||||||
.OutputToPipe(new StreamPipeSink(ms))
|
.ForceFormat("rawvideo")))
|
||||||
.ProcessAsynchronously();
|
.ProcessAsynchronously();
|
||||||
|
|
||||||
ms.Position = 0;
|
ms.Position = 0;
|
||||||
return new Bitmap(ms);
|
return new Bitmap(ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static FFMpegArguments BuildSnapshotArguments(IMediaAnalysis source, Size? size = null, TimeSpan? captureTime = null, int videoStreamNumber = 0)
|
private static (FFMpegArguments, Action<FFMpegArgumentOptions> outputOptions) BuildSnapshotArguments(IMediaAnalysis source, Size? size = null, TimeSpan? captureTime = null, int videoStreamNumber = 0)
|
||||||
{
|
{
|
||||||
captureTime ??= TimeSpan.FromSeconds(source.Duration.TotalSeconds / 3);
|
captureTime ??= TimeSpan.FromSeconds(source.Duration.TotalSeconds / 3);
|
||||||
size = PrepareSnapshotSize(source, size);
|
size = PrepareSnapshotSize(source, size);
|
||||||
|
|
||||||
return FFMpegArguments
|
return (FFMpegArguments
|
||||||
.FromSeekedFiles((source.Path, captureTime ?? TimeSpan.Zero))
|
.FromFileInput(source, options => options
|
||||||
.WithVideoStream(videoStreamNumber)
|
.Seek(captureTime)),
|
||||||
.WithVideoCodec(VideoCodec.Png)
|
options => options
|
||||||
.WithFrameOutputCount(1)
|
.SelectStream(videoStreamNumber)
|
||||||
.Resize(size);
|
.WithVideoCodec(VideoCodec.Png)
|
||||||
|
.WithFrameOutputCount(1)
|
||||||
|
.Resize(size));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Size? PrepareSnapshotSize(IMediaAnalysis source, Size? wantedSize)
|
private static Size? PrepareSnapshotSize(IMediaAnalysis source, Size? wantedSize)
|
||||||
|
@ -168,44 +170,44 @@ public static bool Convert(
|
||||||
return format.Name switch
|
return format.Name switch
|
||||||
{
|
{
|
||||||
"mp4" => FFMpegArguments
|
"mp4" => FFMpegArguments
|
||||||
.FromInputFiles(true, source.Path)
|
.FromFileInput(source)
|
||||||
.UsingMultithreading(multithreaded)
|
.OutputToFile(output, true, options => options
|
||||||
.WithVideoCodec(VideoCodec.LibX264)
|
.UsingMultithreading(multithreaded)
|
||||||
.WithVideoBitrate(2400)
|
.WithVideoCodec(VideoCodec.LibX264)
|
||||||
.Scale(outputSize)
|
.WithVideoBitrate(2400)
|
||||||
.WithSpeedPreset(speed)
|
.Scale(outputSize)
|
||||||
.WithAudioCodec(AudioCodec.Aac)
|
.WithSpeedPreset(speed)
|
||||||
.WithAudioBitrate(audioQuality)
|
.WithAudioCodec(AudioCodec.Aac)
|
||||||
.OutputToFile(output)
|
.WithAudioBitrate(audioQuality))
|
||||||
.ProcessSynchronously(),
|
.ProcessSynchronously(),
|
||||||
"ogv" => FFMpegArguments
|
"ogv" => FFMpegArguments
|
||||||
.FromInputFiles(true, source.Path)
|
.FromFileInput(source)
|
||||||
.UsingMultithreading(multithreaded)
|
.OutputToFile(output, true, options => options
|
||||||
.WithVideoCodec(VideoCodec.LibTheora)
|
.UsingMultithreading(multithreaded)
|
||||||
.WithVideoBitrate(2400)
|
.WithVideoCodec(VideoCodec.LibTheora)
|
||||||
.Scale(outputSize)
|
.WithVideoBitrate(2400)
|
||||||
.WithSpeedPreset(speed)
|
.Scale(outputSize)
|
||||||
.WithAudioCodec(AudioCodec.LibVorbis)
|
.WithSpeedPreset(speed)
|
||||||
.WithAudioBitrate(audioQuality)
|
.WithAudioCodec(AudioCodec.LibVorbis)
|
||||||
.OutputToFile(output)
|
.WithAudioBitrate(audioQuality))
|
||||||
.ProcessSynchronously(),
|
.ProcessSynchronously(),
|
||||||
"mpegts" => FFMpegArguments
|
"mpegts" => FFMpegArguments
|
||||||
.FromInputFiles(true, source.Path)
|
.FromFileInput(source)
|
||||||
.CopyChannel()
|
.OutputToFile(output, true, options => options
|
||||||
.WithBitStreamFilter(Channel.Video, Filter.H264_Mp4ToAnnexB)
|
.CopyChannel()
|
||||||
.ForceFormat(VideoType.Ts)
|
.WithBitStreamFilter(Channel.Video, Filter.H264_Mp4ToAnnexB)
|
||||||
.OutputToFile(output)
|
.ForceFormat(VideoType.Ts))
|
||||||
.ProcessSynchronously(),
|
.ProcessSynchronously(),
|
||||||
"webm" => FFMpegArguments
|
"webm" => FFMpegArguments
|
||||||
.FromInputFiles(true, source.Path)
|
.FromFileInput(source)
|
||||||
.UsingMultithreading(multithreaded)
|
.OutputToFile(output, true, options => options
|
||||||
.WithVideoCodec(VideoCodec.LibVpx)
|
.UsingMultithreading(multithreaded)
|
||||||
.WithVideoBitrate(2400)
|
.WithVideoCodec(VideoCodec.LibVpx)
|
||||||
.Scale(outputSize)
|
.WithVideoBitrate(2400)
|
||||||
.WithSpeedPreset(speed)
|
.Scale(outputSize)
|
||||||
.WithAudioCodec(AudioCodec.LibVorbis)
|
.WithSpeedPreset(speed)
|
||||||
.WithAudioBitrate(audioQuality)
|
.WithAudioCodec(AudioCodec.LibVorbis)
|
||||||
.OutputToFile(output)
|
.WithAudioBitrate(audioQuality))
|
||||||
.ProcessSynchronously(),
|
.ProcessSynchronously(),
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(format))
|
_ => throw new ArgumentOutOfRangeException(nameof(format))
|
||||||
};
|
};
|
||||||
|
@ -224,13 +226,14 @@ public static bool PosterWithAudio(string image, string audio, string output)
|
||||||
FFMpegHelper.ConversionSizeExceptionCheck(Image.FromFile(image));
|
FFMpegHelper.ConversionSizeExceptionCheck(Image.FromFile(image));
|
||||||
|
|
||||||
return FFMpegArguments
|
return FFMpegArguments
|
||||||
.FromInputFiles(true, image, audio)
|
.FromFileInput(image)
|
||||||
.Loop(1)
|
.AddFileInput(audio)
|
||||||
.WithVideoCodec(VideoCodec.LibX264)
|
.OutputToFile(output, true, options => options
|
||||||
.WithConstantRateFactor(21)
|
.Loop(1)
|
||||||
.WithAudioBitrate(AudioQuality.Normal)
|
.WithVideoCodec(VideoCodec.LibX264)
|
||||||
.UsingShortest()
|
.WithConstantRateFactor(21)
|
||||||
.OutputToFile(output)
|
.WithAudioBitrate(AudioQuality.Normal)
|
||||||
|
.UsingShortest())
|
||||||
.ProcessSynchronously();
|
.ProcessSynchronously();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -254,10 +257,10 @@ public static bool Join(string output, params IMediaAnalysis[] videos)
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return FFMpegArguments
|
return FFMpegArguments
|
||||||
.FromConcatenation(temporaryVideoParts)
|
.FromConcatInput(temporaryVideoParts)
|
||||||
.CopyChannel()
|
.OutputToFile(output, true, options => options
|
||||||
.WithBitStreamFilter(Channel.Audio, Filter.Aac_AdtstoAsc)
|
.CopyChannel()
|
||||||
.OutputToFile(output)
|
.WithBitStreamFilter(Channel.Audio, Filter.Aac_AdtstoAsc))
|
||||||
.ProcessSynchronously();
|
.ProcessSynchronously();
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
@ -299,10 +302,10 @@ public static bool JoinImageSequence(string output, double frameRate = 30, param
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return FFMpegArguments
|
return FFMpegArguments
|
||||||
.FromInputFiles(false, Path.Combine(tempFolderName, "%09d.png"))
|
.FromFileInput(Path.Combine(tempFolderName, "%09d.png"), false)
|
||||||
.Resize(firstImage.Width, firstImage.Height)
|
.OutputToFile(output, true, options => options
|
||||||
.WithFramerate(frameRate)
|
.Resize(firstImage.Width, firstImage.Height)
|
||||||
.OutputToFile(output)
|
.WithFramerate(frameRate))
|
||||||
.ProcessSynchronously();
|
.ProcessSynchronously();
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
@ -326,7 +329,7 @@ public static bool SaveM3U8Stream(Uri uri, string output)
|
||||||
throw new ArgumentException($"Uri: {uri.AbsoluteUri}, does not point to a valid http(s) stream.");
|
throw new ArgumentException($"Uri: {uri.AbsoluteUri}, does not point to a valid http(s) stream.");
|
||||||
|
|
||||||
return FFMpegArguments
|
return FFMpegArguments
|
||||||
.FromInputFiles(false, uri)
|
.FromUrlInput(uri)
|
||||||
.OutputToFile(output)
|
.OutputToFile(output)
|
||||||
.ProcessSynchronously();
|
.ProcessSynchronously();
|
||||||
}
|
}
|
||||||
|
@ -344,10 +347,10 @@ public static bool Mute(string input, string output)
|
||||||
FFMpegHelper.ExtensionExceptionCheck(output, source.Extension);
|
FFMpegHelper.ExtensionExceptionCheck(output, source.Extension);
|
||||||
|
|
||||||
return FFMpegArguments
|
return FFMpegArguments
|
||||||
.FromInputFiles(true, source.Path)
|
.FromFileInput(source)
|
||||||
.CopyChannel(Channel.Video)
|
.OutputToFile(output, true, options => options
|
||||||
.DisableChannel(Channel.Audio)
|
.CopyChannel(Channel.Video)
|
||||||
.OutputToFile(output)
|
.DisableChannel(Channel.Audio))
|
||||||
.ProcessSynchronously();
|
.ProcessSynchronously();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -362,9 +365,9 @@ public static bool ExtractAudio(string input, string output)
|
||||||
FFMpegHelper.ExtensionExceptionCheck(output, FileExtension.Mp3);
|
FFMpegHelper.ExtensionExceptionCheck(output, FileExtension.Mp3);
|
||||||
|
|
||||||
return FFMpegArguments
|
return FFMpegArguments
|
||||||
.FromInputFiles(true, input)
|
.FromFileInput(input)
|
||||||
.DisableChannel(Channel.Video)
|
.OutputToFile(output, true, options => options
|
||||||
.OutputToFile(output)
|
.DisableChannel(Channel.Video))
|
||||||
.ProcessSynchronously();
|
.ProcessSynchronously();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -383,26 +386,27 @@ public static bool ReplaceAudio(string input, string inputAudio, string output,
|
||||||
FFMpegHelper.ExtensionExceptionCheck(output, source.Extension);
|
FFMpegHelper.ExtensionExceptionCheck(output, source.Extension);
|
||||||
|
|
||||||
return FFMpegArguments
|
return FFMpegArguments
|
||||||
.FromInputFiles(true, source.Path, inputAudio)
|
.FromFileInput(source)
|
||||||
.CopyChannel()
|
.AddFileInput(inputAudio)
|
||||||
.WithAudioCodec(AudioCodec.Aac)
|
.OutputToFile(output, true, options => options
|
||||||
.WithAudioBitrate(AudioQuality.Good)
|
.CopyChannel()
|
||||||
.UsingShortest(stopAtShortest)
|
.WithAudioCodec(AudioCodec.Aac)
|
||||||
.OutputToFile(output)
|
.WithAudioBitrate(AudioQuality.Good)
|
||||||
|
.UsingShortest(stopAtShortest))
|
||||||
.ProcessSynchronously();
|
.ProcessSynchronously();
|
||||||
}
|
}
|
||||||
|
|
||||||
#region PixelFormats
|
#region PixelFormats
|
||||||
internal static IReadOnlyList<Enums.PixelFormat> GetPixelFormatsInternal()
|
internal static IReadOnlyList<PixelFormat> GetPixelFormatsInternal()
|
||||||
{
|
{
|
||||||
FFMpegHelper.RootExceptionCheck(FFMpegOptions.Options.RootDirectory);
|
FFMpegHelper.RootExceptionCheck();
|
||||||
|
|
||||||
var list = new List<Enums.PixelFormat>();
|
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) =>
|
instance.DataReceived += (e, args) =>
|
||||||
{
|
{
|
||||||
if (Enums.PixelFormat.TryParse(args.Data, out var fmt))
|
if (PixelFormat.TryParse(args.Data, out var format))
|
||||||
list.Add(fmt);
|
list.Add(format);
|
||||||
};
|
};
|
||||||
|
|
||||||
var exitCode = instance.BlockUntilFinished();
|
var exitCode = instance.BlockUntilFinished();
|
||||||
|
@ -411,14 +415,14 @@ public static bool ReplaceAudio(string input, string inputAudio, string output,
|
||||||
return list.AsReadOnly();
|
return list.AsReadOnly();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IReadOnlyList<Enums.PixelFormat> GetPixelFormats()
|
public static IReadOnlyList<PixelFormat> GetPixelFormats()
|
||||||
{
|
{
|
||||||
if (!FFMpegOptions.Options.UseCache)
|
if (!FFMpegOptions.Options.UseCache)
|
||||||
return GetPixelFormatsInternal();
|
return GetPixelFormatsInternal();
|
||||||
return FFMpegCache.PixelFormats.Values.ToList().AsReadOnly();
|
return FFMpegCache.PixelFormats.Values.ToList().AsReadOnly();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool TryGetPixelFormat(string name, out Enums.PixelFormat fmt)
|
public static bool TryGetPixelFormat(string name, out PixelFormat fmt)
|
||||||
{
|
{
|
||||||
if (!FFMpegOptions.Options.UseCache)
|
if (!FFMpegOptions.Options.UseCache)
|
||||||
{
|
{
|
||||||
|
@ -429,7 +433,7 @@ public static bool TryGetPixelFormat(string name, out Enums.PixelFormat fmt)
|
||||||
return FFMpegCache.PixelFormats.TryGetValue(name, out fmt);
|
return FFMpegCache.PixelFormats.TryGetValue(name, out fmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Enums.PixelFormat GetPixelFormat(string name)
|
public static PixelFormat GetPixelFormat(string name)
|
||||||
{
|
{
|
||||||
if (TryGetPixelFormat(name, out var fmt))
|
if (TryGetPixelFormat(name, out var fmt))
|
||||||
return fmt;
|
return fmt;
|
||||||
|
@ -438,9 +442,10 @@ public static Enums.PixelFormat GetPixelFormat(string name)
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Codecs
|
#region Codecs
|
||||||
internal static void ParsePartOfCodecs(Dictionary<string, Codec> codecs, string arguments, Func<string, Codec?> parser)
|
|
||||||
|
private static void ParsePartOfCodecs(Dictionary<string, Codec> codecs, string arguments, Func<string, Codec?> parser)
|
||||||
{
|
{
|
||||||
FFMpegHelper.RootExceptionCheck(FFMpegOptions.Options.RootDirectory);
|
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) =>
|
instance.DataReceived += (e, args) =>
|
||||||
|
@ -523,7 +528,7 @@ public static Codec GetCodec(string name)
|
||||||
#region ContainerFormats
|
#region ContainerFormats
|
||||||
internal static IReadOnlyList<ContainerFormat> GetContainersFormatsInternal()
|
internal static IReadOnlyList<ContainerFormat> GetContainersFormatsInternal()
|
||||||
{
|
{
|
||||||
FFMpegHelper.RootExceptionCheck(FFMpegOptions.Options.RootDirectory);
|
FFMpegHelper.RootExceptionCheck();
|
||||||
|
|
||||||
var list = new List<ContainerFormat>();
|
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");
|
||||||
|
|
68
FFMpegCore/FFMpeg/FFMpegArgumentOptions.cs
Normal file
68
FFMpegCore/FFMpeg/FFMpegArgumentOptions.cs
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
using System;
|
||||||
|
using System.Drawing;
|
||||||
|
using FFMpegCore.Arguments;
|
||||||
|
using FFMpegCore.Enums;
|
||||||
|
|
||||||
|
namespace FFMpegCore
|
||||||
|
{
|
||||||
|
public class FFMpegArgumentOptions : FFMpegOptionsBase
|
||||||
|
{
|
||||||
|
internal FFMpegArgumentOptions() { }
|
||||||
|
|
||||||
|
public FFMpegArgumentOptions WithAudioCodec(Codec audioCodec) => WithArgument(new AudioCodecArgument(audioCodec));
|
||||||
|
public FFMpegArgumentOptions WithAudioCodec(string audioCodec) => WithArgument(new AudioCodecArgument(audioCodec));
|
||||||
|
public FFMpegArgumentOptions WithAudioBitrate(AudioQuality audioQuality) => WithArgument(new AudioBitrateArgument(audioQuality));
|
||||||
|
public FFMpegArgumentOptions WithAudioBitrate(int bitrate) => WithArgument(new AudioBitrateArgument(bitrate));
|
||||||
|
public FFMpegArgumentOptions WithAudioSamplingRate(int samplingRate = 48000) => WithArgument(new AudioSamplingRateArgument(samplingRate));
|
||||||
|
public FFMpegArgumentOptions WithVariableBitrate(int vbr) => WithArgument(new VariableBitRateArgument(vbr));
|
||||||
|
|
||||||
|
public FFMpegArgumentOptions Resize(VideoSize videoSize) => WithArgument(new SizeArgument(videoSize));
|
||||||
|
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));
|
||||||
|
public FFMpegArgumentOptions CopyChannel(Channel channel = Channel.Both) => WithArgument(new CopyArgument(channel));
|
||||||
|
public FFMpegArgumentOptions DisableChannel(Channel channel) => WithArgument(new DisableChannelArgument(channel));
|
||||||
|
public FFMpegArgumentOptions WithDuration(TimeSpan? duration) => WithArgument(new DurationArgument(duration));
|
||||||
|
public FFMpegArgumentOptions WithFastStart() => WithArgument(new FaststartArgument());
|
||||||
|
public FFMpegArgumentOptions WithFrameOutputCount(int frames) => WithArgument(new FrameOutputCountArgument(frames));
|
||||||
|
public FFMpegArgumentOptions WithHardwareAcceleration(HardwareAccelerationDevice hardwareAccelerationDevice = HardwareAccelerationDevice.Auto) => WithArgument(new HardwareAccelerationArgument(hardwareAccelerationDevice));
|
||||||
|
|
||||||
|
public FFMpegArgumentOptions UsingShortest(bool shortest = true) => WithArgument(new ShortestArgument(shortest));
|
||||||
|
public FFMpegArgumentOptions UsingMultithreading(bool multithread) => WithArgument(new ThreadsArgument(multithread));
|
||||||
|
public FFMpegArgumentOptions UsingThreads(int threads) => WithArgument(new ThreadsArgument(threads));
|
||||||
|
|
||||||
|
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 WithFramerate(double framerate) => WithArgument(new FrameRateArgument(framerate));
|
||||||
|
public FFMpegArgumentOptions WithoutMetadata() => WithArgument(new RemoveMetadataArgument());
|
||||||
|
public FFMpegArgumentOptions WithSpeedPreset(Speed speed) => WithArgument(new SpeedPresetArgument(speed));
|
||||||
|
public FFMpegArgumentOptions WithStartNumber(int startNumber) => WithArgument(new StartNumberArgument(startNumber));
|
||||||
|
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());
|
||||||
|
public FFMpegArgumentOptions SelectStream(int index) => WithArgument(new MapStreamArgument(index));
|
||||||
|
|
||||||
|
public FFMpegArgumentOptions ForceFormat(ContainerFormat format) => WithArgument(new ForceFormatArgument(format));
|
||||||
|
public FFMpegArgumentOptions ForceFormat(string format) => WithArgument(new ForceFormatArgument(format));
|
||||||
|
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);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -51,20 +51,22 @@ public bool ProcessSynchronously(bool throwOnError = true)
|
||||||
|
|
||||||
void OnCancelEvent(object sender, EventArgs args)
|
void OnCancelEvent(object sender, EventArgs args)
|
||||||
{
|
{
|
||||||
instance?.SendInput("q");
|
instance.SendInput("q");
|
||||||
cancellationTokenSource.Cancel();
|
cancellationTokenSource.Cancel();
|
||||||
|
instance.Started = false;
|
||||||
}
|
}
|
||||||
CancelEvent += OnCancelEvent;
|
CancelEvent += OnCancelEvent;
|
||||||
|
instance.Exited += delegate { cancellationTokenSource.Cancel(); };
|
||||||
|
|
||||||
_ffMpegArguments.Pre();
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
_ffMpegArguments.Pre();
|
||||||
Task.WaitAll(instance.FinishedRunning().ContinueWith(t =>
|
Task.WaitAll(instance.FinishedRunning().ContinueWith(t =>
|
||||||
{
|
{
|
||||||
errorCode = t.Result;
|
errorCode = t.Result;
|
||||||
cancellationTokenSource.Cancel();
|
cancellationTokenSource.Cancel();
|
||||||
|
_ffMpegArguments.Post();
|
||||||
}), _ffMpegArguments.During(cancellationTokenSource.Token));
|
}), _ffMpegArguments.During(cancellationTokenSource.Token));
|
||||||
_ffMpegArguments.Post();
|
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
@ -98,16 +100,18 @@ void OnCancelEvent(object sender, EventArgs args)
|
||||||
{
|
{
|
||||||
instance?.SendInput("q");
|
instance?.SendInput("q");
|
||||||
cancellationTokenSource.Cancel();
|
cancellationTokenSource.Cancel();
|
||||||
|
instance.Started = false;
|
||||||
}
|
}
|
||||||
CancelEvent += OnCancelEvent;
|
CancelEvent += OnCancelEvent;
|
||||||
|
|
||||||
_ffMpegArguments.Pre();
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
_ffMpegArguments.Pre();
|
||||||
await Task.WhenAll(instance.FinishedRunning().ContinueWith(t =>
|
await Task.WhenAll(instance.FinishedRunning().ContinueWith(t =>
|
||||||
{
|
{
|
||||||
errorCode = t.Result;
|
errorCode = t.Result;
|
||||||
cancellationTokenSource.Cancel();
|
cancellationTokenSource.Cancel();
|
||||||
|
_ffMpegArguments.Post();
|
||||||
}), _ffMpegArguments.During(cancellationTokenSource.Token)).ConfigureAwait(false);
|
}), _ffMpegArguments.During(cancellationTokenSource.Token)).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
@ -117,7 +121,6 @@ await Task.WhenAll(instance.FinishedRunning().ContinueWith(t =>
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
CancelEvent -= OnCancelEvent;
|
CancelEvent -= OnCancelEvent;
|
||||||
_ffMpegArguments.Post();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return HandleCompletion(throwOnError, errorCode, instance.ErrorData);
|
return HandleCompletion(throwOnError, errorCode, instance.ErrorData);
|
||||||
|
@ -125,9 +128,9 @@ await Task.WhenAll(instance.FinishedRunning().ContinueWith(t =>
|
||||||
|
|
||||||
private Instance PrepareInstance(out CancellationTokenSource cancellationTokenSource)
|
private Instance PrepareInstance(out CancellationTokenSource cancellationTokenSource)
|
||||||
{
|
{
|
||||||
FFMpegHelper.RootExceptionCheck(FFMpegOptions.Options.RootDirectory);
|
FFMpegHelper.RootExceptionCheck();
|
||||||
|
FFMpegHelper.VerifyFFMpegExists();
|
||||||
var instance = new Instance(FFMpegOptions.Options.FFmpegBinary(), _ffMpegArguments.Text);
|
var instance = new Instance(FFMpegOptions.Options.FFmpegBinary(), _ffMpegArguments.Text);
|
||||||
instance.DataReceived += OutputData;
|
|
||||||
cancellationTokenSource = new CancellationTokenSource();
|
cancellationTokenSource = new CancellationTokenSource();
|
||||||
|
|
||||||
if (_onTimeProgress != null || (_onPercentageProgress != null && _totalTimespan != null))
|
if (_onTimeProgress != null || (_onPercentageProgress != null && _totalTimespan != null))
|
||||||
|
@ -136,6 +139,7 @@ private Instance PrepareInstance(out CancellationTokenSource cancellationTokenSo
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static bool HandleException(bool throwOnError, Exception e, IReadOnlyList<string> errorData)
|
private static bool HandleException(bool throwOnError, Exception e, IReadOnlyList<string> errorData)
|
||||||
{
|
{
|
||||||
if (!throwOnError)
|
if (!throwOnError)
|
||||||
|
|
|
@ -1,127 +1,81 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Drawing;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using FFMpegCore.Arguments;
|
using FFMpegCore.Arguments;
|
||||||
using FFMpegCore.Enums;
|
|
||||||
using FFMpegCore.Pipes;
|
using FFMpegCore.Pipes;
|
||||||
|
|
||||||
namespace FFMpegCore
|
namespace FFMpegCore
|
||||||
{
|
{
|
||||||
public class FFMpegArguments
|
public sealed class FFMpegArguments : FFMpegOptionsBase
|
||||||
{
|
{
|
||||||
private readonly IInputArgument _inputArgument;
|
private readonly FFMpegGlobalOptions _globalOptions = new FFMpegGlobalOptions();
|
||||||
private IOutputArgument _outputArgument = null!;
|
|
||||||
private readonly List<IArgument> _arguments;
|
|
||||||
|
|
||||||
private FFMpegArguments(IInputArgument inputArgument)
|
private FFMpegArguments() { }
|
||||||
|
|
||||||
|
public string Text => string.Join(" ", _globalOptions.Arguments.Concat(Arguments).Select(arg => arg.Text));
|
||||||
|
|
||||||
|
public static FFMpegArguments FromConcatInput(IEnumerable<string> filePaths, Action<FFMpegArgumentOptions>? addArguments = null) => new FFMpegArguments().WithInput(new ConcatArgument(filePaths), addArguments);
|
||||||
|
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);
|
||||||
|
|
||||||
|
|
||||||
|
public FFMpegArguments WithGlobalOptions(Action<FFMpegGlobalOptions> configureOptions)
|
||||||
{
|
{
|
||||||
_inputArgument = inputArgument;
|
configureOptions(_globalOptions);
|
||||||
_arguments = new List<IArgument> { inputArgument };
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Text => string.Join(" ", _arguments.Select(arg => arg.Text));
|
|
||||||
|
|
||||||
public static FFMpegArguments FromSeekedFiles(params (string file, TimeSpan startTime)[] seekedFiles) => new FFMpegArguments(new SeekedFileInputArgument(seekedFiles));
|
|
||||||
public static FFMpegArguments FromInputFiles(params string[] files) => new FFMpegArguments(new InputArgument(true, files));
|
|
||||||
public static FFMpegArguments FromInputFiles(bool verifyExists, params string[] files) => new FFMpegArguments(new InputArgument(verifyExists, files));
|
|
||||||
public static FFMpegArguments FromInputFiles(params Uri[] uris) => new FFMpegArguments(new InputArgument(false, uris));
|
|
||||||
public static FFMpegArguments FromInputFiles(bool verifyExists, params Uri[] uris) => new FFMpegArguments(new InputArgument(verifyExists, uris));
|
|
||||||
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 FromConcatenation(params string[] files) => new FFMpegArguments(new ConcatArgument(files));
|
|
||||||
public static FFMpegArguments FromDemuxConcatenation(params string[] files) => new FFMpegArguments(new DemuxConcatArgument(files));
|
|
||||||
public static FFMpegArguments FromPipe(IPipeSource writer) => new FFMpegArguments(new InputPipeArgument(writer));
|
|
||||||
|
|
||||||
|
|
||||||
public FFMpegArguments WithAudioCodec(Codec audioCodec) => WithArgument(new AudioCodecArgument(audioCodec));
|
|
||||||
public FFMpegArguments WithAudioCodec(string audioCodec) => WithArgument(new AudioCodecArgument(audioCodec));
|
|
||||||
public FFMpegArguments WithAudioBitrate(AudioQuality audioQuality) => WithArgument(new AudioBitrateArgument(audioQuality));
|
|
||||||
public FFMpegArguments WithAudioBitrate(int bitrate) => WithArgument(new AudioBitrateArgument(bitrate));
|
|
||||||
public FFMpegArguments WithAudioSamplingRate(int samplingRate = 48000) => WithArgument(new AudioSamplingRateArgument(samplingRate));
|
|
||||||
public FFMpegArguments WithVariableBitrate(int vbr) => WithArgument(new VariableBitRateArgument(vbr));
|
|
||||||
|
|
||||||
public FFMpegArguments Resize(VideoSize videoSize) => WithArgument(new SizeArgument(videoSize));
|
|
||||||
public FFMpegArguments Resize(int width, int height) => WithArgument(new SizeArgument(width, height));
|
|
||||||
public FFMpegArguments Resize(Size? size) => WithArgument(new SizeArgument(size));
|
|
||||||
|
|
||||||
public FFMpegArguments Scale(VideoSize videoSize) => WithArgument(new ScaleArgument(videoSize));
|
|
||||||
public FFMpegArguments Scale(int width, int height) => WithArgument(new ScaleArgument(width, height));
|
|
||||||
public FFMpegArguments Scale(Size size) => WithArgument(new ScaleArgument(size));
|
|
||||||
|
|
||||||
public FFMpegArguments WithBitStreamFilter(Channel channel, Filter filter) => WithArgument(new BitStreamFilterArgument(channel, filter));
|
|
||||||
public FFMpegArguments WithConstantRateFactor(int crf) => WithArgument(new ConstantRateFactorArgument(crf));
|
|
||||||
public FFMpegArguments CopyChannel(Channel channel = Channel.Both) => WithArgument(new CopyArgument(channel));
|
|
||||||
public FFMpegArguments DisableChannel(Channel channel) => WithArgument(new DisableChannelArgument(channel));
|
|
||||||
public FFMpegArguments WithDuration(TimeSpan? duration) => WithArgument(new DurationArgument(duration));
|
|
||||||
public FFMpegArguments WithFastStart() => WithArgument(new FaststartArgument());
|
|
||||||
public FFMpegArguments WithFrameOutputCount(int frames) => WithArgument(new FrameOutputCountArgument(frames));
|
|
||||||
public FFMpegArguments WithVideoStream(int videoStreamNumber) => WithArgument(new MapStreamArgument(videoStreamNumber));
|
|
||||||
|
|
||||||
public FFMpegArguments UsingShortest(bool shortest = true) => WithArgument(new ShortestArgument(shortest));
|
|
||||||
public FFMpegArguments UsingMultithreading(bool multithread) => WithArgument(new ThreadsArgument(multithread));
|
|
||||||
public FFMpegArguments UsingThreads(int threads) => WithArgument(new ThreadsArgument(threads));
|
|
||||||
|
|
||||||
public FFMpegArguments WithVideoCodec(Codec videoCodec) => WithArgument(new VideoCodecArgument(videoCodec));
|
|
||||||
public FFMpegArguments WithVideoCodec(string videoCodec) => WithArgument(new VideoCodecArgument(videoCodec));
|
|
||||||
public FFMpegArguments WithVideoBitrate(int bitrate) => WithArgument(new VideoBitrateArgument(bitrate));
|
|
||||||
public FFMpegArguments WithFramerate(double framerate) => WithArgument(new FrameRateArgument(framerate));
|
|
||||||
public FFMpegArguments WithoutMetadata() => WithArgument(new RemoveMetadataArgument());
|
|
||||||
public FFMpegArguments WithSpeedPreset(Speed speed) => WithArgument(new SpeedPresetArgument(speed));
|
|
||||||
public FFMpegArguments WithStartNumber(int startNumber) => WithArgument(new StartNumberArgument(startNumber));
|
|
||||||
public FFMpegArguments WithCpuSpeed(int cpuSpeed) => WithArgument(new CpuSpeedArgument(cpuSpeed));
|
|
||||||
public FFMpegArguments WithCustomArgument(string argument) => WithArgument(new CustomArgument(argument));
|
|
||||||
|
|
||||||
public FFMpegArguments Seek(TimeSpan? seekTo) => WithArgument(new SeekArgument(seekTo));
|
|
||||||
public FFMpegArguments Transpose(Transposition transposition) => WithArgument(new TransposeArgument(transposition));
|
|
||||||
public FFMpegArguments Loop(int times) => WithArgument(new LoopArgument(times));
|
|
||||||
public FFMpegArguments OverwriteExisting() => WithArgument(new OverwriteArgument());
|
|
||||||
public FFMpegArguments WithVerbosityLevel(VerbosityLevel verbosityLevel = VerbosityLevel.Error) => WithArgument(new VerbosityLevelArgument(verbosityLevel));
|
|
||||||
|
|
||||||
public FFMpegArguments ForceFormat(ContainerFormat format) => WithArgument(new ForceFormatArgument(format));
|
|
||||||
public FFMpegArguments ForceFormat(string format) => WithArgument(new ForceFormatArgument(format));
|
|
||||||
public FFMpegArguments ForcePixelFormat(string pixelFormat) => WithArgument(new ForcePixelFormat(pixelFormat));
|
|
||||||
public FFMpegArguments ForcePixelFormat(PixelFormat pixelFormat) => WithArgument(new ForcePixelFormat(pixelFormat));
|
|
||||||
|
|
||||||
public FFMpegArguments DrawText(DrawTextOptions drawTextOptions) => WithArgument(new DrawTextArgument(drawTextOptions));
|
|
||||||
|
|
||||||
public FFMpegArgumentProcessor OutputToFile(string file, bool overwrite = true) => ToProcessor(new OutputArgument(file, overwrite));
|
|
||||||
public FFMpegArgumentProcessor OutputToFile(Uri uri, bool overwrite = true) => ToProcessor(new OutputArgument(uri.AbsolutePath, overwrite));
|
|
||||||
public FFMpegArgumentProcessor OutputToPipe(IPipeSink reader) => ToProcessor(new OutputPipeArgument(reader));
|
|
||||||
|
|
||||||
public FFMpegArguments WithArgument(IArgument argument)
|
|
||||||
{
|
|
||||||
_arguments.Add(argument);
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
private FFMpegArgumentProcessor ToProcessor(IOutputArgument argument)
|
|
||||||
|
public FFMpegArguments AddConcatInput(IEnumerable<string> filePaths, Action<FFMpegArgumentOptions>? addArguments = null) => WithInput(new ConcatArgument(filePaths), addArguments);
|
||||||
|
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);
|
||||||
|
|
||||||
|
private FFMpegArguments WithInput(IInputArgument inputArgument, Action<FFMpegArgumentOptions>? addArguments)
|
||||||
{
|
{
|
||||||
_arguments.Add(argument);
|
var arguments = new FFMpegArgumentOptions();
|
||||||
_outputArgument = argument;
|
addArguments?.Invoke(arguments);
|
||||||
|
Arguments.AddRange(arguments.Arguments);
|
||||||
|
Arguments.Add(inputArgument);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FFMpegArgumentProcessor OutputToFile(string file, bool overwrite = true, Action<FFMpegArgumentOptions>? addArguments = null) => ToProcessor(new OutputArgument(file, overwrite), addArguments);
|
||||||
|
public FFMpegArgumentProcessor OutputToFile(Uri uri, bool overwrite = true, Action<FFMpegArgumentOptions>? addArguments = null) => ToProcessor(new OutputArgument(uri.AbsolutePath, overwrite), addArguments);
|
||||||
|
public FFMpegArgumentProcessor OutputToPipe(IPipeSink reader, Action<FFMpegArgumentOptions>? addArguments = null) => ToProcessor(new OutputPipeArgument(reader), addArguments);
|
||||||
|
|
||||||
|
private FFMpegArgumentProcessor ToProcessor(IOutputArgument argument, Action<FFMpegArgumentOptions>? addArguments)
|
||||||
|
{
|
||||||
|
var args = new FFMpegArgumentOptions();
|
||||||
|
addArguments?.Invoke(args);
|
||||||
|
Arguments.AddRange(args.Arguments);
|
||||||
|
Arguments.Add(argument);
|
||||||
return new FFMpegArgumentProcessor(this);
|
return new FFMpegArgumentProcessor(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void Pre()
|
internal void Pre()
|
||||||
{
|
{
|
||||||
_inputArgument.Pre();
|
foreach (var argument in Arguments.OfType<IInputOutputArgument>())
|
||||||
_outputArgument.Pre();
|
argument.Pre();
|
||||||
}
|
}
|
||||||
internal async Task During(CancellationToken? cancellationToken = null)
|
internal async Task During(CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
await Task.WhenAll(_inputArgument.During(cancellationToken), _outputArgument.During(cancellationToken)).ConfigureAwait(false);
|
var inputOutputArguments = Arguments.OfType<IInputOutputArgument>();
|
||||||
|
await Task.WhenAll(inputOutputArguments.Select(io => io.During(cancellationToken))).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
internal void Post()
|
internal void Post()
|
||||||
{
|
{
|
||||||
_inputArgument.Post();
|
foreach (var argument in Arguments.OfType<IInputOutputArgument>())
|
||||||
_outputArgument.Post();
|
argument.Post();
|
||||||
}
|
|
||||||
|
|
||||||
public TArgument Find<TArgument>() where TArgument : class, IArgument
|
|
||||||
{
|
|
||||||
return _arguments.OfType<TArgument>().FirstOrDefault();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
18
FFMpegCore/FFMpeg/FFMpegGlobalOptions.cs
Normal file
18
FFMpegCore/FFMpeg/FFMpegGlobalOptions.cs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
using FFMpegCore.Arguments;
|
||||||
|
|
||||||
|
namespace FFMpegCore
|
||||||
|
{
|
||||||
|
public sealed class FFMpegGlobalOptions : FFMpegOptionsBase
|
||||||
|
{
|
||||||
|
internal FFMpegGlobalOptions() { }
|
||||||
|
|
||||||
|
public FFMpegGlobalOptions WithVerbosityLevel(VerbosityLevel verbosityLevel = VerbosityLevel.Error) => WithOption(new VerbosityLevelArgument(verbosityLevel));
|
||||||
|
|
||||||
|
private FFMpegGlobalOptions WithOption(IArgument argument)
|
||||||
|
{
|
||||||
|
Arguments.Add(argument);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
10
FFMpegCore/FFMpeg/FFMpegOptionsBase.cs
Normal file
10
FFMpegCore/FFMpeg/FFMpegOptionsBase.cs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using FFMpegCore.Arguments;
|
||||||
|
|
||||||
|
namespace FFMpegCore
|
||||||
|
{
|
||||||
|
public abstract class FFMpegOptionsBase
|
||||||
|
{
|
||||||
|
internal readonly List<IArgument> Arguments = new List<IArgument>();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,21 +1,18 @@
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace FFMpegCore.Pipes
|
namespace FFMpegCore.Pipes
|
||||||
{
|
{
|
||||||
static class PipeHelpers
|
static class PipeHelpers
|
||||||
{
|
{
|
||||||
static readonly string PipePrefix = Path.Combine(Path.GetTempPath(), "CoreFxPipe_");
|
public static string GetUnqiuePipeName() => $"FFMpegCore_{Guid.NewGuid()}";
|
||||||
|
|
||||||
public static string GetUnqiuePipeName() => "FFMpegCore_" + Guid.NewGuid();
|
|
||||||
|
|
||||||
public static string GetPipePath(string pipeName)
|
public static string GetPipePath(string pipeName)
|
||||||
{
|
{
|
||||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
return $@"\\.\pipe\{pipeName}";
|
return $@"\\.\pipe\{pipeName}";
|
||||||
else
|
else
|
||||||
return $"unix:{PipePrefix}{pipeName}"; // dotnet uses unix sockets on unix, for more see https://github.com/dotnet/runtime/issues/24390
|
return $"unix:/tmp/CoreFxPipe_{pipeName}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,15 +5,15 @@
|
||||||
<RepositoryUrl>https://github.com/rosenbjerg/FFMpegCore</RepositoryUrl>
|
<RepositoryUrl>https://github.com/rosenbjerg/FFMpegCore</RepositoryUrl>
|
||||||
<PackageProjectUrl>https://github.com/rosenbjerg/FFMpegCore</PackageProjectUrl>
|
<PackageProjectUrl>https://github.com/rosenbjerg/FFMpegCore</PackageProjectUrl>
|
||||||
<Copyright></Copyright>
|
<Copyright></Copyright>
|
||||||
<Description>A great way to use FFMpeg encoding when writing video applications, client-side and server-side. It has wrapper methods that allow conversion to all web formats: MP4, OGV, TS and methods of capturing screens from the videos.</Description>
|
<Description>A .NET Standard FFMpeg/FFProbe wrapper for easily integrating media analysis and conversion into your C# applications</Description>
|
||||||
<Version>1.0.12</Version>
|
<Version>3.0.0.0</Version>
|
||||||
<AssemblyVersion>1.1.0.0</AssemblyVersion>
|
<AssemblyVersion>3.0.0.0</AssemblyVersion>
|
||||||
<FileVersion>1.1.0.0</FileVersion>
|
<FileVersion>3.0.0.0</FileVersion>
|
||||||
<PackageReleaseNotes>- Make Tags a dictionary for flexibility
|
<PackageReleaseNotes>- Fix hanging pipes on unix sockets
|
||||||
- Handle rotated video frames in snapshot</PackageReleaseNotes>
|
- Internal API cleanup</PackageReleaseNotes>
|
||||||
<LangVersion>8</LangVersion>
|
<LangVersion>8</LangVersion>
|
||||||
<PackageVersion>2.2.6</PackageVersion>
|
<PackageVersion>3.1.0</PackageVersion>
|
||||||
<Authors>Vlad Jerca, Malte Rosenbjerg</Authors>
|
<Authors>Malte Rosenbjerg, Vlad Jerca</Authors>
|
||||||
<PackageTags>ffmpeg ffprobe convert video audio mediafile resize analyze muxing</PackageTags>
|
<PackageTags>ffmpeg ffprobe convert video audio mediafile resize analyze muxing</PackageTags>
|
||||||
<RepositoryType>GitHub</RepositoryType>
|
<RepositoryType>GitHub</RepositoryType>
|
||||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
<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>
|
|
||||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=ffprobe/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
|
|
@ -1,18 +0,0 @@
|
||||||
<?xml version="1.0"?>
|
|
||||||
<package >
|
|
||||||
<metadata>
|
|
||||||
<id>$id$</id>
|
|
||||||
<version>$version$</version>
|
|
||||||
<title>$title$</title>
|
|
||||||
<authors>Vlad Jerca</authors>
|
|
||||||
<owners>Vlad Jerca</owners>
|
|
||||||
<projectUrl>https://github.com/rosenbjerg/FFMpegCore</projectUrl>
|
|
||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
|
||||||
<description>$description$</description>
|
|
||||||
<releaseNotes>
|
|
||||||
More information available @ https://github.com/rosenbjerg/FFMpegCore
|
|
||||||
</releaseNotes>
|
|
||||||
<copyright>Copyright 2020</copyright>
|
|
||||||
<tags>ffmpeg ffprobe video audio media conversion analysis ffmpegcore mp4 ogv</tags>
|
|
||||||
</metadata>
|
|
||||||
</package>
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System.IO;
|
using System;
|
||||||
|
using System.IO;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using FFMpegCore.Arguments;
|
using FFMpegCore.Arguments;
|
||||||
|
@ -13,11 +14,20 @@ 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);
|
using var instance = PrepareInstance(filePath, outputCapacity);
|
||||||
instance.BlockUntilFinished();
|
instance.BlockUntilFinished();
|
||||||
return ParseOutput(filePath, instance);
|
return ParseOutput(filePath, instance);
|
||||||
}
|
}
|
||||||
public static IMediaAnalysis Analyse(System.IO.Stream stream, 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);
|
||||||
|
}
|
||||||
|
public static IMediaAnalysis Analyse(Stream stream, int outputCapacity = int.MaxValue)
|
||||||
{
|
{
|
||||||
var streamPipeSource = new StreamPipeSource(stream);
|
var streamPipeSource = new StreamPipeSource(stream);
|
||||||
var pipeArgument = new InputPipeArgument(streamPipeSource);
|
var pipeArgument = new InputPipeArgument(streamPipeSource);
|
||||||
|
@ -42,11 +52,20 @@ public static IMediaAnalysis Analyse(System.IO.Stream stream, int outputCapacity
|
||||||
}
|
}
|
||||||
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);
|
using var instance = PrepareInstance(filePath, outputCapacity);
|
||||||
await instance.FinishedRunning();
|
await instance.FinishedRunning();
|
||||||
return ParseOutput(filePath, instance);
|
return ParseOutput(filePath, instance);
|
||||||
}
|
}
|
||||||
public static async Task<IMediaAnalysis> AnalyseAsync(System.IO.Stream stream, 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);
|
||||||
|
}
|
||||||
|
public static async Task<IMediaAnalysis> AnalyseAsync(Stream stream, int outputCapacity = int.MaxValue)
|
||||||
{
|
{
|
||||||
var streamPipeSource = new StreamPipeSource(stream);
|
var streamPipeSource = new StreamPipeSource(stream);
|
||||||
var pipeArgument = new InputPipeArgument(streamPipeSource);
|
var pipeArgument = new InputPipeArgument(streamPipeSource);
|
||||||
|
@ -85,10 +104,10 @@ private static IMediaAnalysis ParseOutput(string filePath, Instance instance)
|
||||||
|
|
||||||
private static Instance PrepareInstance(string filePath, int outputCapacity)
|
private static Instance PrepareInstance(string filePath, int outputCapacity)
|
||||||
{
|
{
|
||||||
FFProbeHelper.RootExceptionCheck(FFMpegOptions.Options.RootDirectory);
|
FFProbeHelper.RootExceptionCheck();
|
||||||
var ffprobe = FFMpegOptions.Options.FFProbeBinary();
|
FFProbeHelper.VerifyFFProbeExists();
|
||||||
var arguments = $"-print_format json -show_format -sexagesimal -show_streams \"{filePath}\"";
|
var arguments = $"-print_format json -show_format -sexagesimal -show_streams \"{filePath}\"";
|
||||||
var instance = new Instance(ffprobe, arguments) {DataBufferCapacity = outputCapacity};
|
var instance = new Instance(FFMpegOptions.Options.FFProbeBinary(), arguments) {DataBufferCapacity = outputCapacity};
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace FFMpegCore
|
namespace FFMpegCore
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,11 +2,14 @@
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using FFMpegCore.Exceptions;
|
using FFMpegCore.Exceptions;
|
||||||
|
using Instances;
|
||||||
|
|
||||||
namespace FFMpegCore.Helpers
|
namespace FFMpegCore.Helpers
|
||||||
{
|
{
|
||||||
public static class FFMpegHelper
|
public static class FFMpegHelper
|
||||||
{
|
{
|
||||||
|
private static bool _ffmpegVerified;
|
||||||
|
|
||||||
public static void ConversionSizeExceptionCheck(Image image)
|
public static void ConversionSizeExceptionCheck(Image image)
|
||||||
{
|
{
|
||||||
ConversionSizeExceptionCheck(image.Size);
|
ConversionSizeExceptionCheck(image.Size);
|
||||||
|
@ -32,11 +35,19 @@ public static void ExtensionExceptionCheck(string filename, string extension)
|
||||||
$"Invalid output file. File extension should be '{extension}' required.");
|
$"Invalid output file. File extension should be '{extension}' required.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void RootExceptionCheck(string root)
|
public static void RootExceptionCheck()
|
||||||
{
|
{
|
||||||
if (root == null)
|
if (FFMpegOptions.Options.RootDirectory == null)
|
||||||
throw new FFMpegException(FFMpegExceptionType.Dependency,
|
throw new FFMpegException(FFMpegExceptionType.Dependency,
|
||||||
"FFMpeg root is not configured in app config. Missing key 'ffmpegRoot'.");
|
"FFMpeg root is not configured in app config. Missing key 'ffmpegRoot'.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void VerifyFFMpegExists()
|
||||||
|
{
|
||||||
|
if (_ffmpegVerified) return;
|
||||||
|
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");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
using FFMpegCore.Exceptions;
|
using FFMpegCore.Exceptions;
|
||||||
|
using Instances;
|
||||||
|
|
||||||
namespace FFMpegCore.Helpers
|
namespace FFMpegCore.Helpers
|
||||||
{
|
{
|
||||||
public class FFProbeHelper
|
public class FFProbeHelper
|
||||||
{
|
{
|
||||||
|
private static bool _ffprobeVerified;
|
||||||
|
|
||||||
public static int Gcd(int first, int second)
|
public static int Gcd(int first, int second)
|
||||||
{
|
{
|
||||||
while (first != 0 && second != 0)
|
while (first != 0 && second != 0)
|
||||||
|
@ -15,12 +18,20 @@ public static int Gcd(int first, int second)
|
||||||
return first == 0 ? second : first;
|
return first == 0 ? second : first;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void RootExceptionCheck(string root)
|
public static void RootExceptionCheck()
|
||||||
{
|
{
|
||||||
if (root == null)
|
if (FFMpegOptions.Options.RootDirectory == null)
|
||||||
throw new FFMpegException(FFMpegExceptionType.Dependency,
|
throw new FFMpegException(FFMpegExceptionType.Dependency,
|
||||||
"FFProbe root is not configured in app config. Missing key 'ffmpegRoot'.");
|
"FFProbe root is not configured in app config. Missing key 'ffmpegRoot'.");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void VerifyFFProbeExists()
|
||||||
|
{
|
||||||
|
if (_ffprobeVerified) return;
|
||||||
|
var (exitCode, _) = Instance.Finish(FFMpegOptions.Options.FFProbeBinary(), "-version");
|
||||||
|
_ffprobeVerified = exitCode == 0;
|
||||||
|
if (!_ffprobeVerified) throw new FFMpegException(FFMpegExceptionType.Operation, "ffprobe was not found on your system");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
41
README.md
41
README.md
|
@ -1,6 +1,9 @@
|
||||||
# FFMpegCore
|
# FFMpegCore
|
||||||
[![NuGet Badge](https://buildstats.info/nuget/FFMpegCore)](https://www.nuget.org/packages/FFMpegCore/)
|
|
||||||
[![CI](https://github.com/rosenbjerg/FFMpegCore/workflows/CI/badge.svg)](https://github.com/rosenbjerg/FFMpegCore/actions?query=workflow%3ACI)
|
[![CI](https://github.com/rosenbjerg/FFMpegCore/workflows/CI/badge.svg)](https://github.com/rosenbjerg/FFMpegCore/actions?query=workflow%3ACI)
|
||||||
|
[![NuGet Badge](https://buildstats.info/nuget/FFMpegCore)](https://www.nuget.org/packages/FFMpegCore/)
|
||||||
|
[![GitHub issues](https://img.shields.io/github/issues/rosenbjerg/FFMpegCore)](https://github.com/rosenbjerg/FFMpegCore/issues)
|
||||||
|
[![GitHub stars](https://img.shields.io/github/stars/rosenbjerg/FFMpegCore)](https://github.com/rosenbjerg/FFMpegCore/stargazers)
|
||||||
|
[![GitHub](https://img.shields.io/github/license/rosenbjerg/FFMpegCore)](https://github.com/rosenbjerg/FFMpegCore/blob/master/LICENSE)
|
||||||
|
|
||||||
# Setup
|
# Setup
|
||||||
|
|
||||||
|
@ -10,7 +13,7 @@
|
||||||
Install-Package FFMpegCore
|
Install-Package FFMpegCore
|
||||||
```
|
```
|
||||||
|
|
||||||
A great way to use FFMpeg encoding when writing video applications, client-side and server-side. It has wrapper methods that allow conversion to popular web formats, such as Mp4, WebM, Ogv, TS, and methods for capturing screenshots from videos, among other.
|
A .NET Standard FFMpeg/FFProbe wrapper for easily integrating media analysis and conversion into your C# applications. Support both synchronous and asynchronous use
|
||||||
|
|
||||||
# API
|
# API
|
||||||
|
|
||||||
|
@ -34,20 +37,20 @@ Easily build your FFMpeg arguments using the fluent argument builder:
|
||||||
Convert input file to h264/aac scaled to 720p w/ faststart, for web playback
|
Convert input file to h264/aac scaled to 720p w/ faststart, for web playback
|
||||||
```csharp
|
```csharp
|
||||||
FFMpegArguments
|
FFMpegArguments
|
||||||
.FromInputFiles(inputFilePath)
|
.FromFileInput(inputPath)
|
||||||
.WithVideoCodec(VideoCodec.LibX264)
|
.OutputToFile(outputPath, false, options => options
|
||||||
.WithConstantRateFactor(21)
|
.WithVideoCodec(VideoCodec.LibX264)
|
||||||
.WithAudioCodec(AudioCodec.Aac)
|
.WithConstantRateFactor(21)
|
||||||
.WithVariableBitrate(4)
|
.WithAudioCodec(AudioCodec.Aac)
|
||||||
.WithFastStart()
|
.WithVariableBitrate(4)
|
||||||
.Scale(VideoSize.Hd)
|
.WithFastStart()
|
||||||
.OutputToFile(output)
|
.Scale(VideoSize.Hd))
|
||||||
.ProcessSynchronously(),
|
.ProcessSynchronously();
|
||||||
```
|
```
|
||||||
|
|
||||||
Easily capture screens from your videos:
|
Easily capture screens from your videos:
|
||||||
```csharp
|
```csharp
|
||||||
var mediaFileAnalysis = FFProbe.Analyse(inputFilePath);
|
var mediaFileAnalysis = FFProbe.Analyse(inputPath);
|
||||||
|
|
||||||
// process the snapshot in-memory and use the Bitmap directly
|
// process the snapshot in-memory and use the Bitmap directly
|
||||||
var bitmap = FFMpeg.Snapshot(mediaFileAnalysis, new Size(200, 400), TimeSpan.FromMinutes(1));
|
var bitmap = FFMpeg.Snapshot(mediaFileAnalysis, new Size(200, 400), TimeSpan.FromMinutes(1));
|
||||||
|
@ -59,10 +62,10 @@ FFMpeg.Snapshot(mediaFileAnalysis, outputPath, new Size(200, 400), TimeSpan.From
|
||||||
Convert to and/or from streams
|
Convert to and/or from streams
|
||||||
```csharp
|
```csharp
|
||||||
await FFMpegArguments
|
await FFMpegArguments
|
||||||
.FromPipe(new StreamPipeDataWriter(inputStream))
|
.FromPipeInput(new StreamPipeSource(inputStream))
|
||||||
.WithVideoCodec("vp9")
|
.OutputToPipe(new StreamPipeSink(outputStream), options => options
|
||||||
.ForceFormat("webm")
|
.WithVideoCodec("vp9")
|
||||||
.OutputToPipe(new StreamPipeDataReader(outputStream))
|
.ForceFormat("webm"))
|
||||||
.ProcessAsynchronously();
|
.ProcessAsynchronously();
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -133,9 +136,8 @@ var videoFramesSource = new RawVideoPipeSource(CreateFrames(64)) //pass IEnumera
|
||||||
FrameRate = 30 //set source frame rate
|
FrameRate = 30 //set source frame rate
|
||||||
};
|
};
|
||||||
FFMpegArguments
|
FFMpegArguments
|
||||||
.FromPipe(videoFramesSource)
|
.FromPipeInput(videoFramesSource, <input_stream_options>)
|
||||||
// ... other encoding arguments
|
.OutputToFile("temporary.mp4", false, <output_options>)
|
||||||
.OutputToFile("temporary.mp4")
|
|
||||||
.ProcessSynchronously();
|
.ProcessSynchronously();
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -210,6 +212,7 @@ The root and temp directory for the ffmpeg binaries can be configured via the `f
|
||||||
<a href="https://github.com/devlev"><img src="https://avatars3.githubusercontent.com/u/2109995?v=4" title="devlev" width="80" height="80"></a>
|
<a href="https://github.com/devlev"><img src="https://avatars3.githubusercontent.com/u/2109995?v=4" title="devlev" width="80" height="80"></a>
|
||||||
<a href="https://github.com/tugrulelmas"><img src="https://avatars3.githubusercontent.com/u/3829187?v=4" title="tugrulelmas" width="80" height="80"></a>
|
<a href="https://github.com/tugrulelmas"><img src="https://avatars3.githubusercontent.com/u/3829187?v=4" title="tugrulelmas" width="80" height="80"></a>
|
||||||
<a href="https://github.com/rosenbjerg"><img src="https://avatars3.githubusercontent.com/u/11181960?v=4" title="rosenbjerg" width="80" height="80"></a>
|
<a href="https://github.com/rosenbjerg"><img src="https://avatars3.githubusercontent.com/u/11181960?v=4" title="rosenbjerg" width="80" height="80"></a>
|
||||||
|
<a href="https://github.com/WeihanLi"><img src="https://avatars3.githubusercontent.com/u/7604648?v=4" title="weihanli" width="80" height="80"></a>
|
||||||
|
|
||||||
### License
|
### License
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue