Skip to content

Commit

Permalink
Merge branch 'main' into feature/longer-pipe-name
Browse files Browse the repository at this point in the history
  • Loading branch information
rosenbjerg authored Dec 4, 2024
2 parents c9e0cde + 6836b14 commit ee48cc1
Show file tree
Hide file tree
Showing 26 changed files with 304 additions and 37 deletions.
30 changes: 19 additions & 11 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,42 +3,48 @@ name: CI
on:
push:
branches:
- master
- main
paths:
- .github/workflows/ci.yml
- Directory.Build.props
- FFMpegCore/**
- FFMpegCore.Test/**
- FFMpegCore.Extensions.SkiaSharp/**
- FFMpegCore.Extensions.System.Drawing.Common/**
pull_request:
branches:
- main
- release
paths:
- .github/workflows/ci.yml
- FFMpegCore/**
- FFMpegCore.Test/**
- .github/workflows/ci.yml
- Directory.Build.props
- FFMpegCore/**
- FFMpegCore.Test/**
- FFMpegCore.Extensions.SkiaSharp/**
- FFMpegCore.Extensions.System.Drawing.Common/**

jobs:
ci:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [windows-latest, ubuntu-latest, macos-latest]
os: [windows-latest, ubuntu-latest, macos-13]
timeout-minutes: 7
steps:

- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: Prepare .NET
uses: actions/setup-dotnet@v3
uses: actions/setup-dotnet@v4
with:
dotnet-version: '7.0.x'
dotnet-version: '8.0.x'

- name: Lint with dotnet
run: dotnet format FFMpegCore.sln --severity warn --verify-no-changes

- name: Prepare FFMpeg
uses: FedericoCarboni/setup-ffmpeg@v2
uses: FedericoCarboni/setup-ffmpeg@v3
with:
token: ${{ secrets.GITHUB_TOKEN }}

Expand All @@ -47,7 +53,9 @@ jobs:

- if: matrix.os == 'windows-latest'
name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v3
uses: codecov/codecov-action@v4
with:
fail_ci_if_error: true
directory: FFMpegCore.Test/TestResults
fail_ci_if_error: true
token: ${{ secrets.CODECOV_TOKEN }}
os: windows
6 changes: 3 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: Prepare .NET
uses: actions/setup-dotnet@v3
uses: actions/setup-dotnet@v4
with:
dotnet-version: '7.0.x'
dotnet-version: '8.0.x'

- name: Build solution
run: dotnet pack FFMpegCore.sln -c Release
Expand Down
9 changes: 9 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,14 @@
<PackageProjectUrl>https://github.com/rosenbjerg/FFMpegCore</PackageProjectUrl>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<NeutralLanguage>en</NeutralLanguage>

<PublishRepositoryUrl>true</PublishRepositoryUrl>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
</PropertyGroup>

<PropertyGroup Condition="'$(GITHUB_ACTIONS)' == 'true'">
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
</PropertyGroup>
</Project>
2 changes: 1 addition & 1 deletion FFMpegCore.Examples/FFMpegCore.Examples.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,18 @@
<PropertyGroup>
<IsPackable>true</IsPackable>
<Description>Image extension for FFMpegCore using SkiaSharp</Description>
<PackageVersion>5.0.0</PackageVersion>
<PackageVersion>5.0.1</PackageVersion>
<PackageOutputPath>../nupkg</PackageOutputPath>
<PackageReleaseNotes>
</PackageReleaseNotes>
<PackageTags>ffmpeg ffprobe convert video audio mediafile resize analyze muxing skiasharp</PackageTags>
<Authors>Malte Rosenbjerg, Vlad Jerca, Max Bagryantsev, Dimitri Vranken</Authors>
<ProduceReferenceAssembly>true</ProduceReferenceAssembly>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="SkiaSharp" Version="2.88.6" />
<PackageReference Include="SkiaSharp.NativeAssets.Linux.NoDependencies" Version="2.88.6" />
<PackageReference Include="SkiaSharp" Version="3.116.0" />
<PackageReference Include="SkiaSharp.NativeAssets.Linux.NoDependencies" Version="3.116.0" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,17 @@
<PropertyGroup>
<IsPackable>true</IsPackable>
<Description>Image extension for FFMpegCore using System.Common.Drawing</Description>
<PackageVersion>5.0.0</PackageVersion>
<PackageVersion>5.0.1</PackageVersion>
<PackageOutputPath>../nupkg</PackageOutputPath>
<PackageReleaseNotes>
</PackageReleaseNotes>
<PackageTags>ffmpeg ffprobe convert video audio mediafile resize analyze muxing</PackageTags>
<Authors>Malte Rosenbjerg, Vlad Jerca, Max Bagryantsev</Authors>
<ProduceReferenceAssembly>true</ProduceReferenceAssembly>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="System.Drawing.Common" Version="7.0.0"/>
<PackageReference Include="System.Drawing.Common" Version="9.0.0" />
</ItemGroup>

<ItemGroup>
Expand Down
82 changes: 82 additions & 0 deletions FFMpegCore.Test/ArgumentBuilderTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ namespace FFMpegCore.Test
public class ArgumentBuilderTest
{
private readonly string[] _concatFiles = { "1.mp4", "2.mp4", "3.mp4", "4.mp4" };
private readonly string[] _multiFiles = { "1.mp3", "2.mp3", "3.mp3", "4.mp3" };

[TestMethod]
public void Builder_BuildString_IO_1()
Expand Down Expand Up @@ -611,5 +612,86 @@ public void Builder_BuildString_TeeOutput()
-i "input.mp4" -f tee "[movflags=faststart]output.mp4|[f=mpegts:select=\'0:v:0\']http://server/path"
""", str);
}
[TestMethod]
public void Builder_BuildString_MultiInput()
{
var audioStreams = string.Join("", _multiFiles.Select((item, index) => $"[{index}:0]"));
var mixFilter = $"{audioStreams}amix=inputs={_multiFiles.Length}:duration=longest:dropout_transition=1:normalize=0[final]";
var ffmpegArgs = $"-filter_complex \"{mixFilter}\" -map \"[final]\"";
var str = FFMpegArguments
.FromFileInput(_multiFiles)
.OutputToFile("output.mp3", overwrite: true, options => options
.WithCustomArgument(ffmpegArgs)
.WithAudioCodec(AudioCodec.LibMp3Lame) // Set the audio codec to MP3
.WithAudioBitrate(128) // Set the bitrate to 128kbps
.WithAudioSamplingRate(48000) // Set the sample rate to 48kHz
.WithoutMetadata() // Remove metadata
.WithCustomArgument("-ac 2 -write_xing 0 -id3v2_version 0")) // Force 2 Channels
.Arguments;
Assert.AreEqual($"-i \"1.mp3\" -i \"2.mp3\" -i \"3.mp3\" -i \"4.mp3\" -filter_complex \"[0:0][1:0][2:0][3:0]amix=inputs=4:duration=longest:dropout_transition=1:normalize=0[final]\" -map \"[final]\" -c:a libmp3lame -b:a 128k -ar 48000 -map_metadata -1 -ac 2 -write_xing 0 -id3v2_version 0 \"output.mp3\" -y", str);
}
[TestMethod]
public void Pre_VerifyExists_AllFilesExist()
{
// Arrange
var filePaths = new List<string>
{
Path.GetTempFileName(),
Path.GetTempFileName(),
Path.GetTempFileName()
};
var argument = new MultiInputArgument(true, filePaths);
try
{
// Act & Assert
argument.Pre(); // No exception should be thrown
}
finally
{
// Cleanup
foreach (var filePath in filePaths)
{
File.Delete(filePath);
}
}
}

[TestMethod]
public void Pre_VerifyExists_SomeFilesNotExist()
{
// Arrange
var filePaths = new List<string>
{
Path.GetTempFileName(),
"file2.mp4",
"file3.mp4"
};
var argument = new MultiInputArgument(true, filePaths);
try
{
// Act & Assert
Assert.ThrowsException<FileNotFoundException>(() => argument.Pre());
}
finally
{
// Cleanup
File.Delete(filePaths[0]);
}
}

[TestMethod]
public void Pre_VerifyExists_NoFilesExist()
{
// Arrange
var filePaths = new List<string>
{
"file1.mp4",
"file2.mp4",
"file3.mp4"
};
var argument = new MultiInputArgument(true, filePaths);
// Act & Assert
Assert.ThrowsException<FileNotFoundException>(() => argument.Pre());
}
}
}
22 changes: 14 additions & 8 deletions FFMpegCore.Test/FFMpegCore.Test.csproj
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<IsPackable>false</IsPackable>
<Nullable>disable</Nullable>
<LangVersion>default</LangVersion>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.0">
<PackageReference Include="coverlet.collector" Version="6.0.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="FluentAssertions" Version="6.12.0" />
<PackageReference Include="GitHubActionsTestLogger" Version="2.3.3">
<PackageReference Include="FluentAssertions" Version="7.0.0" />
<PackageReference Include="GitHubActionsTestLogger" Version="2.4.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
<PackageReference Include="MSTest.TestAdapter" Version="3.1.1" />
<PackageReference Include="MSTest.TestFramework" Version="3.1.1" />
<PackageReference Include="SkiaSharp" Version="2.88.6" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="MSTest.TestAdapter" Version="3.6.4" />
<PackageReference Include="MSTest.TestFramework" Version="3.6.4" />
<PackageReference Include="SkiaSharp" Version="3.116.0" />
</ItemGroup>

<ItemGroup>
Expand Down Expand Up @@ -51,9 +51,15 @@
<None Update="Resources\input_3sec_rotation_90deg.mp4">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\input_3sec_rotation_negative_90deg.mp4">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\input_audio_only_10sec.mp4">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\input_hdr.mov">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\input_video_only_3sec.mp4">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
Expand Down
20 changes: 20 additions & 0 deletions FFMpegCore.Test/FFProbeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ public void Probe_Success()
Assert.AreEqual(1, info.PrimaryVideoStream.SampleAspectRatio.Width);
Assert.AreEqual(1, info.PrimaryVideoStream.SampleAspectRatio.Height);
Assert.AreEqual("yuv420p", info.PrimaryVideoStream.PixelFormat);
Assert.AreEqual(31, info.PrimaryVideoStream.Level);
Assert.AreEqual(1280, info.PrimaryVideoStream.Width);
Assert.AreEqual(720, info.PrimaryVideoStream.Height);
Assert.AreEqual(25, info.PrimaryVideoStream.AvgFrameRate);
Expand All @@ -145,6 +146,13 @@ public void Probe_Rotation()
Assert.AreEqual(90, info.PrimaryVideoStream.Rotation);
}

[TestMethod]
public void Probe_Rotation_Negative_Value()
{
var info = FFProbe.Analyse(TestResources.Mp4VideoRotationNegative);
Assert.AreEqual(-90, info.PrimaryVideoStream.Rotation);
}

[TestMethod, Timeout(10000)]
public async Task Probe_Async_Success()
{
Expand Down Expand Up @@ -173,6 +181,18 @@ public async Task Probe_Success_FromStream_Async()
Assert.AreEqual(3, info.Duration.Seconds);
}

[TestMethod, Timeout(10000)]
public void Probe_HDR()
{
var info = FFProbe.Analyse(TestResources.HdrVideo);

Assert.IsNotNull(info.PrimaryVideoStream);
Assert.AreEqual("tv", info.PrimaryVideoStream.ColorRange);
Assert.AreEqual("bt2020nc", info.PrimaryVideoStream.ColorSpace);
Assert.AreEqual("arib-std-b67", info.PrimaryVideoStream.ColorTransfer);
Assert.AreEqual("bt2020", info.PrimaryVideoStream.ColorPrimaries);
}

[TestMethod, Timeout(10000)]
public async Task Probe_Success_Subtitle_Async()
{
Expand Down
2 changes: 2 additions & 0 deletions FFMpegCore.Test/Resources/TestResources.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ public static class TestResources
{
public static readonly string Mp4Video = "./Resources/input_3sec.mp4";
public static readonly string Mp4VideoRotation = "./Resources/input_3sec_rotation_90deg.mp4";
public static readonly string Mp4VideoRotationNegative = "./Resources/input_3sec_rotation_negative_90deg.mp4";
public static readonly string WebmVideo = "./Resources/input_3sec.webm";
public static readonly string HdrVideo = "./Resources/input_hdr.mov";
public static readonly string Mp4WithoutVideo = "./Resources/input_audio_only_10sec.mp4";
public static readonly string Mp4WithoutAudio = "./Resources/input_video_only_3sec.mp4";
public static readonly string RawAudio = "./Resources/audio.raw";
Expand Down
Binary file not shown.
Binary file added FFMpegCore.Test/Resources/input_hdr.mov
Binary file not shown.
15 changes: 15 additions & 0 deletions FFMpegCore.Test/VideoTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,21 @@ public void Video_Snapshot_PersistSnapshot()
Assert.AreEqual("png", analysis.PrimaryVideoStream!.CodecName);
}

[TestMethod, Timeout(BaseTimeoutMilliseconds)]
public void Video_Snapshot_Rotated_PersistSnapshot()
{
using var outputPath = new TemporaryFile("out.png");

var size = new Size(360, 0); // half the size of original video, keeping height 0 for keeping aspect ratio
FFMpeg.Snapshot(TestResources.Mp4VideoRotationNegative, outputPath, size);

var analysis = FFProbe.Analyse(outputPath);
Assert.AreEqual(size.Width, analysis.PrimaryVideoStream!.Width);
Assert.AreEqual(1280 / 2, analysis.PrimaryVideoStream!.Height);
Assert.AreEqual(0, analysis.PrimaryVideoStream!.Rotation);
Assert.AreEqual("png", analysis.PrimaryVideoStream!.CodecName);
}

[TestMethod, Timeout(BaseTimeoutMilliseconds)]
public void Video_GifSnapshot_PersistSnapshot()
{
Expand Down
22 changes: 22 additions & 0 deletions FFMpegCore/FFMpeg/Arguments/CropArgument.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System.Drawing;

namespace FFMpegCore.Arguments
{
public class CropArgument : IArgument
{
public readonly Size? Size;
public readonly int Top;
public readonly int Left;

public CropArgument(Size? size, int top, int left)
{
Size = size;
Top = top;
Left = left;
}

public CropArgument(int width, int height, int top, int left) : this(new Size(width, height), top, left) { }

public string Text => Size == null ? string.Empty : $"-vf crop={Size.Value.Width}:{Size.Value.Height}:{Left}:{Top}";
}
}
Loading

0 comments on commit ee48cc1

Please sign in to comment.