Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
c96fdc4
Updated package description for SkiaSharp instead of Aspose.Drawing
Feb 13, 2023
f464be4
Replaced System.Drawing.Common with SkiaSharp
Feb 13, 2023
7f17d68
Updated tests for SkiaSharp
Feb 13, 2023
72c76c2
refactor: Centralized test timeout duration
Feb 13, 2023
6e38b45
chore: Increased test timeout duration
Feb 13, 2023
f95bba5
Added configuration for running tests in WSL on windows
Feb 13, 2023
bdfe87b
Enabled windows-only tests on all plattforms
Feb 13, 2023
29fc924
Added dependency SkiaSharp.NativeAssets.Linux.NoDependencies
Feb 13, 2023
fb19f45
Updated package properties for fork
Feb 13, 2023
db215ff
Revert "Updated package description for SkiaSharp instead of Aspose.D…
Feb 13, 2023
8afe1e0
Revert "Updated package properties for fork"
Feb 13, 2023
cc22d15
Moved SkiaSharp implemented to its own extension
Feb 16, 2023
315c1e1
Adjusted FFMpegCore.Examples for separate FFMpegCore.Extensions.SkiaS…
Feb 16, 2023
2458b4a
Adjusted FFMpegCore.Test for separate FFMpegCore.Extensions.SkiaSharp…
Feb 16, 2023
a66bdba
Improved SkiaSharp CreateVideoFrame performance
Feb 16, 2023
55b652a
Added TODO for additional SKColorType support
Feb 16, 2023
f3c7df1
Code formatting
Feb 16, 2023
7b32ba5
Fixed ambiguous method call
Feb 16, 2023
2bc6c64
Merge branch 'main' into feature/net7-windows-support-v5
rosenbjerg Feb 16, 2023
01a33f9
Updated package properties for SkiaSharp
Feb 20, 2023
b632582
Updated example program to avoid commented-out code
Feb 20, 2023
3ae6799
Merge branch 'feature/net7-windows-support-v5' of https://github.com/…
Feb 20, 2023
790aebb
Merge branch 'main' into feature/net7-windows-support-v5
Feb 20, 2023
9646c44
Fixed code style issues
Feb 20, 2023
317ba47
Refactored video frame generator to pure function
Feb 20, 2023
5593bc4
Improved SkiaSharp CreateVideoFrame performance
Feb 20, 2023
e3aa7a5
Removed invalid assertion
Feb 20, 2023
46a8ec7
Increased unit test timeout
Feb 20, 2023
542b514
Merge branch 'main' into feature/net7-windows-support-v5
rosenbjerg Feb 21, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions FFMpegCore.Examples/FFMpegCore.Examples.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\FFMpegCore.Extensions.SkiaSharp\FFMpegCore.Extensions.SkiaSharp.csproj" />
<ProjectReference Include="..\FFMpegCore.Extensions.System.Drawing.Common\FFMpegCore.Extensions.System.Drawing.Common.csproj"/>
<ProjectReference Include="..\FFMpegCore\FFMpegCore.csproj"/>
</ItemGroup>
Expand Down
8 changes: 7 additions & 1 deletion FFMpegCore.Examples/Program.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
using System.Drawing;
using FFMpegCore;
using FFMpegCore.Enums;
using FFMpegCore.Extensions.SkiaSharp;
using FFMpegCore.Extensions.System.Drawing.Common;
using FFMpegCore.Pipes;
using SkiaSharp;
using FFMpegImage = FFMpegCore.Extensions.System.Drawing.Common.FFMpegImage;

var inputPath = "/path/to/input";
var outputPath = "/path/to/output";
Expand Down Expand Up @@ -77,11 +80,14 @@ await FFMpegArguments
var inputImagePath = "/path/to/input/image";
{
FFMpeg.PosterWithAudio(inputPath, inputAudioPath, outputPath);
// or
// or using FFMpegCore.Extensions.System.Drawing.Common
#pragma warning disable CA1416
using var image = Image.FromFile(inputImagePath);
image.AddAudio(inputAudioPath, outputPath);
#pragma warning restore CA1416
// or using FFMpegCore.Extensions.SkiaSharp
using var skiaSharpImage = SKBitmap.Decode(inputImagePath);
skiaSharpImage.AddAudio(inputAudioPath, outputPath);
}

IVideoFrame GetNextFrame() => throw new NotImplementedException();
Expand Down
28 changes: 28 additions & 0 deletions FFMpegCore.Extensions.SkiaSharp/BitmapExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using SkiaSharp;

namespace FFMpegCore.Extensions.SkiaSharp
{
public static class BitmapExtensions
{
public static bool AddAudio(this SKBitmap poster, string audio, string output)
{
var destination = $"{Environment.TickCount}.png";
using (var fileStream = File.OpenWrite(destination))
{
poster.Encode(fileStream, SKEncodedImageFormat.Png, default); // PNG does not respect the quality parameter
}

try
{
return FFMpeg.PosterWithAudio(destination, audio, output);
}
finally
{
if (File.Exists(destination))
{
File.Delete(destination);
}
}
}
}
}
59 changes: 59 additions & 0 deletions FFMpegCore.Extensions.SkiaSharp/BitmapVideoFrameWrapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using FFMpegCore.Pipes;
using SkiaSharp;

namespace FFMpegCore.Extensions.SkiaSharp
{
public class BitmapVideoFrameWrapper : IVideoFrame, IDisposable
{
public int Width => Source.Width;

public int Height => Source.Height;

public string Format { get; private set; }

public SKBitmap Source { get; private set; }

public BitmapVideoFrameWrapper(SKBitmap bitmap)
{
Source = bitmap ?? throw new ArgumentNullException(nameof(bitmap));
Format = ConvertStreamFormat(bitmap.ColorType);
}

public void Serialize(Stream stream)
{
var data = Source.Bytes;
stream.Write(data, 0, data.Length);
}

public async Task SerializeAsync(Stream stream, CancellationToken token)
{
var data = Source.Bytes;
await stream.WriteAsync(data, 0, data.Length, token).ConfigureAwait(false);
}

public void Dispose()
{
Source.Dispose();
}

private static string ConvertStreamFormat(SKColorType fmt)
{
// TODO: Add support for additional formats
switch (fmt)
{
case SKColorType.Gray8:
return "gray8";
case SKColorType.Bgra8888:
return "bgra";
case SKColorType.Rgb888x:
return "rgb";
case SKColorType.Rgba8888:
return "rgba";
case SKColorType.Rgb565:
return "rgb565";
default:
throw new NotSupportedException($"Not supported pixel format {fmt}");
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<IsPackable>true</IsPackable>
<Description>Image extension for FFMpegCore using SkiaSharp</Description>
<PackageVersion>5.0.0</PackageVersion>
<PackageReleaseNotes>
</PackageReleaseNotes>
<PackageTags>ffmpeg ffprobe convert video audio mediafile resize analyze muxing skiasharp</PackageTags>
<Authors>Malte Rosenbjerg, Vlad Jerca, Max Bagryantsev, Dimitri Vranken</Authors>
</PropertyGroup>

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

<ItemGroup>
<ProjectReference Include="..\FFMpegCore\FFMpegCore.csproj" />
</ItemGroup>

</Project>
57 changes: 57 additions & 0 deletions FFMpegCore.Extensions.SkiaSharp/FFMpegImage.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using System.Drawing;
using FFMpegCore.Pipes;
using SkiaSharp;

namespace FFMpegCore.Extensions.SkiaSharp
{
public static class FFMpegImage
{
/// <summary>
/// Saves a 'png' thumbnail to an in-memory bitmap
/// </summary>
/// <param name="input">Source video file.</param>
/// <param name="captureTime">Seek position where the thumbnail should be taken.</param>
/// <param name="size">Thumbnail size. If width or height equal 0, the other will be computed automatically.</param>
/// <param name="streamIndex">Selected video stream index.</param>
/// <param name="inputFileIndex">Input file index</param>
/// <returns>Bitmap with the requested snapshot.</returns>
public static SKBitmap Snapshot(string input, Size? size = null, TimeSpan? captureTime = null, int? streamIndex = null, int inputFileIndex = 0)
{
var source = FFProbe.Analyse(input);
var (arguments, outputOptions) = SnapshotArgumentBuilder.BuildSnapshotArguments(input, source, size, captureTime, streamIndex, inputFileIndex);
using var ms = new MemoryStream();

arguments
.OutputToPipe(new StreamPipeSink(ms), options => outputOptions(options
.ForceFormat("rawvideo")))
.ProcessSynchronously();

ms.Position = 0;
using var bitmap = SKBitmap.Decode(ms);
return bitmap.Copy();
}
/// <summary>
/// Saves a 'png' thumbnail to an in-memory bitmap
/// </summary>
/// <param name="input">Source video file.</param>
/// <param name="captureTime">Seek position where the thumbnail should be taken.</param>
/// <param name="size">Thumbnail size. If width or height equal 0, the other will be computed automatically.</param>
/// <param name="streamIndex">Selected video stream index.</param>
/// <param name="inputFileIndex">Input file index</param>
/// <returns>Bitmap with the requested snapshot.</returns>
public static async Task<SKBitmap> SnapshotAsync(string input, Size? size = null, TimeSpan? captureTime = null, int? streamIndex = null, int inputFileIndex = 0)
{
var source = await FFProbe.AnalyseAsync(input).ConfigureAwait(false);
var (arguments, outputOptions) = SnapshotArgumentBuilder.BuildSnapshotArguments(input, source, size, captureTime, streamIndex, inputFileIndex);
using var ms = new MemoryStream();

await arguments
.OutputToPipe(new StreamPipeSink(ms), options => outputOptions(options
.ForceFormat("rawvideo")))
.ProcessAsynchronously();

ms.Position = 0;
return SKBitmap.Decode(ms);
}
}
}
1 change: 1 addition & 0 deletions FFMpegCore.Extensions.System.Drawing.Common/FFMpegImage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public static Bitmap Snapshot(string input, Size? size = null, TimeSpan? capture
using var bitmap = new Bitmap(ms);
return bitmap.Clone(new Rectangle(0, 0, bitmap.Width, bitmap.Height), bitmap.PixelFormat);
}

/// <summary>
/// Saves a 'png' thumbnail to an in-memory bitmap
/// </summary>
Expand Down
2 changes: 2 additions & 0 deletions FFMpegCore.Test/FFMpegCore.Test.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1"/>
<PackageReference Include="MSTest.TestAdapter" Version="3.0.2"/>
<PackageReference Include="MSTest.TestFramework" Version="3.0.2"/>
<PackageReference Include="SkiaSharp" Version="2.88.3" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\FFMpegCore.Extensions.SkiaSharp\FFMpegCore.Extensions.SkiaSharp.csproj" />
<ProjectReference Include="..\FFMpegCore.Extensions.System.Drawing.Common\FFMpegCore.Extensions.System.Drawing.Common.csproj"/>
<ProjectReference Include="..\FFMpegCore\FFMpegCore.csproj"/>
</ItemGroup>
Expand Down
48 changes: 39 additions & 9 deletions FFMpegCore.Test/Utilities/BitmapSources.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
using System.Drawing.Imaging;
using System.Numerics;
using System.Runtime.Versioning;
using FFMpegCore.Extensions.System.Drawing.Common;
using FFMpegCore.Pipes;
using SkiaSharp;

namespace FFMpegCore.Test.Utilities
{
[SupportedOSPlatform("windows")]
internal static class BitmapSource
{
[SupportedOSPlatform("windows")]
public static IEnumerable<IVideoFrame> CreateBitmaps(int count, PixelFormat fmt, int w, int h)
{
for (var i = 0; i < count; i++)
Expand All @@ -21,10 +21,44 @@ public static IEnumerable<IVideoFrame> CreateBitmaps(int count, PixelFormat fmt,
}
}

public static BitmapVideoFrameWrapper CreateVideoFrame(int index, PixelFormat fmt, int w, int h, float scaleNoise, float offset)
public static IEnumerable<IVideoFrame> CreateBitmaps(int count, SKColorType fmt, int w, int h)
{
for (var i = 0; i < count; i++)
{
using (var frame = CreateVideoFrame(i, fmt, w, h, 0.025f, 0.025f * w * 0.03f))
{
yield return frame;
}
}
}

[SupportedOSPlatform("windows")]
public static Extensions.System.Drawing.Common.BitmapVideoFrameWrapper CreateVideoFrame(int index, PixelFormat fmt, int w, int h, float scaleNoise, float offset)
{
var bitmap = new Bitmap(w, h, fmt);

foreach (var (x, y, red, green, blue) in GenerateVideoFramePixels(index, w, h, scaleNoise, offset))
{
var color = Color.FromArgb(red, blue, green);
bitmap.SetPixel(x, y, color);
}

return new Extensions.System.Drawing.Common.BitmapVideoFrameWrapper(bitmap);
}

public static Extensions.SkiaSharp.BitmapVideoFrameWrapper CreateVideoFrame(int index, SKColorType fmt, int w, int h, float scaleNoise, float offset)
{
var bitmap = new SKBitmap(w, h, fmt, SKAlphaType.Opaque);

bitmap.Pixels = GenerateVideoFramePixels(index, w, h, scaleNoise, offset)
.Select(args => new SKColor(args.red, args.blue, args.green))
.ToArray();

return new Extensions.SkiaSharp.BitmapVideoFrameWrapper(bitmap);
}

private static IEnumerable<(int x, int y, byte red, byte green, byte blue)> GenerateVideoFramePixels(int index, int w, int h, float scaleNoise, float offset)
{
offset = offset * index;

for (var y = 0; y < h; y++)
Expand All @@ -36,15 +70,11 @@ public static BitmapVideoFrameWrapper CreateVideoFrame(int index, PixelFormat fm
var nx = x * scaleNoise + offset;
var ny = y * scaleNoise + offset;

var value = (int)((Perlin.Noise(nx, ny) + 1.0f) / 2.0f * 255);
var value = (byte)((Perlin.Noise(nx, ny) + 1.0f) / 2.0f * 255);

var color = Color.FromArgb((int)(value * xf), (int)(value * yf), value);

bitmap.SetPixel(x, y, color);
yield return ((x, y, (byte)(value * xf), (byte)(value * yf), value));
}
}

return new BitmapVideoFrameWrapper(bitmap);
}

//
Expand Down
Loading