Skip to content

Commit

Permalink
Remove FFMpegCore dependency
Browse files Browse the repository at this point in the history
  • Loading branch information
saddam213 committed May 22, 2024
1 parent 5eb45d8 commit 8993731
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 19 deletions.
5 changes: 3 additions & 2 deletions OnnxStack.Core/Image/OnnxImage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using SixLabors.ImageSharp.Processing;
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using ImageSharp = SixLabors.ImageSharp.Image;

Expand Down Expand Up @@ -209,9 +210,9 @@ public void CopyToStream(Stream destination)
/// </summary>
/// <param name="destination">The destination.</param>
/// <returns></returns>
public Task CopyToStreamAsync(Stream destination)
public Task CopyToStreamAsync(Stream destination, CancellationToken cancellationToken)
{
return _imageData.SaveAsPngAsync(destination);
return _imageData.SaveAsPngAsync(destination, cancellationToken);
}


Expand Down
3 changes: 1 addition & 2 deletions OnnxStack.Core/OnnxStack.Core.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<Version>0.31.0</Version>
<Version>0.31.10</Version>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>disable</Nullable>
Expand Down Expand Up @@ -37,7 +37,6 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="FFMpegCore" Version="5.1.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.1" />
Expand Down
2 changes: 1 addition & 1 deletion OnnxStack.Core/Video/OnnxVideo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ public void Dispose()
public static async Task<OnnxVideo> FromFileAsync(string filename, float? frameRate = default, CancellationToken cancellationToken = default)
{
var videoBytes = await File.ReadAllBytesAsync(filename, cancellationToken);
var videoInfo = await VideoHelper.ReadVideoInfoAsync(videoBytes);
var videoInfo = await VideoHelper.ReadVideoInfoAsync(videoBytes, cancellationToken);
if (frameRate.HasValue)
videoInfo = videoInfo with { FrameRate = Math.Min(videoInfo.FrameRate, frameRate.Value) };

Expand Down
67 changes: 54 additions & 13 deletions OnnxStack.Core/Video/VideoHelper.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
using FFMpegCore;
using OnnxStack.Core.Config;
using OnnxStack.Core.Config;
using OnnxStack.Core.Image;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;

Expand Down Expand Up @@ -73,7 +73,7 @@ private static async Task WriteVideoFramesAsync(IEnumerable<OnnxImage> onnxImage
foreach (var image in onnxImages)
{
// Write each frame to the input stream of FFMPEG
await videoWriter.StandardInput.BaseStream.WriteAsync(image.GetImageBytes(), cancellationToken);
await image.CopyToStreamAsync(videoWriter.StandardInput.BaseStream, cancellationToken);
}

// Done close stream and wait for app to process
Expand Down Expand Up @@ -103,7 +103,7 @@ public static async Task WriteVideoStreamAsync(VideoInfo videoInfo, IAsyncEnumer
await foreach (var frame in videoStream)
{
// Write each frame to the input stream of FFMPEG
await frame.CopyToStreamAsync(videoWriter.StandardInput.BaseStream);
await frame.CopyToStreamAsync(videoWriter.StandardInput.BaseStream, cancellationToken);
}

// Done close stream and wait for app to process
Expand All @@ -118,12 +118,17 @@ public static async Task WriteVideoStreamAsync(VideoInfo videoInfo, IAsyncEnumer
/// </summary>
/// <param name="videoBytes">The video bytes.</param>
/// <returns></returns>
public static async Task<VideoInfo> ReadVideoInfoAsync(byte[] videoBytes)
public static async Task<VideoInfo> ReadVideoInfoAsync(byte[] videoBytes, CancellationToken cancellationToken = default)
{
using (var memoryStream = new MemoryStream(videoBytes))
string tempVideoPath = GetTempFilename();
try
{
await File.WriteAllBytesAsync(tempVideoPath, videoBytes, cancellationToken);
return await ReadVideoInfoAsync(tempVideoPath, cancellationToken);
}
finally
{
var result = await FFProbe.AnalyseAsync(memoryStream).ConfigureAwait(false);
return new VideoInfo(result.PrimaryVideoStream.Width, result.PrimaryVideoStream.Height, result.Duration, (int)result.PrimaryVideoStream.FrameRate);
DeleteTempFile(tempVideoPath);
}
}

Expand All @@ -133,10 +138,29 @@ public static async Task<VideoInfo> ReadVideoInfoAsync(byte[] videoBytes)
/// </summary>
/// <param name="filename">The filename.</param>
/// <returns></returns>
public static async Task<VideoInfo> ReadVideoInfoAsync(string filename)
public static async Task<VideoInfo> ReadVideoInfoAsync(string filename, CancellationToken cancellationToken = default)
{
var result = await FFProbe.AnalyseAsync(filename).ConfigureAwait(false);
return new VideoInfo(result.PrimaryVideoStream.Width, result.PrimaryVideoStream.Height, result.Duration, (int)result.PrimaryVideoStream.FrameRate);

using (var metadataReader = CreateMetadataReader(filename))
{
// Start FFMPEG
metadataReader.Start();

var videoInfo = default(VideoInfo);
using (StreamReader reader = metadataReader.StandardOutput)
{
string result = await reader.ReadToEndAsync();
var videoMetadata = JsonSerializer.Deserialize<VideoMetadata>(result);
var videoStream = videoMetadata.Streams.FirstOrDefault();
if (videoStream is null)
throw new Exception("Failed to parse video stream metadata");

videoInfo = new VideoInfo(videoStream.Height, videoStream.Width, videoStream.Duration, videoStream.FramesPerSecond);
}

await metadataReader.WaitForExitAsync(cancellationToken);
return videoInfo;
}
}


Expand Down Expand Up @@ -308,7 +332,7 @@ private static Process CreateReader(string inputFile, float fps)
{
var ffmpegProcess = new Process();
ffmpegProcess.StartInfo.FileName = _configuration.FFmpegPath;
ffmpegProcess.StartInfo.Arguments = $"-hide_banner -loglevel error -i \"{inputFile}\" -c:v png -r {fps} -f image2pipe -";
ffmpegProcess.StartInfo.Arguments = $"-hide_banner -loglevel error -hwaccel:v auto -i \"{inputFile}\" -c:v png -r {fps} -f image2pipe -";
ffmpegProcess.StartInfo.RedirectStandardOutput = true;
ffmpegProcess.StartInfo.UseShellExecute = false;
ffmpegProcess.StartInfo.CreateNoWindow = true;
Expand All @@ -329,14 +353,31 @@ private static Process CreateWriter(string outputFile, float fps, double aspectR
var codec = preserveTransparency ? "png" : "libx264";
var format = preserveTransparency ? "yuva420p" : "yuv420p";
ffmpegProcess.StartInfo.FileName = _configuration.FFmpegPath;
ffmpegProcess.StartInfo.Arguments = $"-hide_banner -loglevel error -framerate {fps:F4} -i - -c:v {codec} -movflags +faststart -vf format={format} -aspect {aspectRatio} {outputFile}";
ffmpegProcess.StartInfo.Arguments = $"-hide_banner -loglevel error -framerate {fps:F4} -hwaccel:v auto -i - -c:v {codec} -movflags +faststart -vf format={format} -aspect {aspectRatio} {outputFile}";
ffmpegProcess.StartInfo.RedirectStandardInput = true;
ffmpegProcess.StartInfo.UseShellExecute = false;
ffmpegProcess.StartInfo.CreateNoWindow = true;
return ffmpegProcess;
}


/// <summary>
/// Creates the metadata reader.
/// </summary>
/// <param name="inputFile">The input file.</param>
/// <returns></returns>
private static Process CreateMetadataReader(string inputFile)
{
var ffprobeProcess = new Process();
ffprobeProcess.StartInfo.FileName = _configuration.FFprobePath;
ffprobeProcess.StartInfo.Arguments = $"-v quiet -print_format json -show_format -show_streams {inputFile}";
ffprobeProcess.StartInfo.RedirectStandardOutput = true;
ffprobeProcess.StartInfo.UseShellExecute = false;
ffprobeProcess.StartInfo.CreateNoWindow = true;
return ffprobeProcess;
}


/// <summary>
/// Determines whether we are at the start of a PNG image in the specified buffer.
/// </summary>
Expand Down
75 changes: 74 additions & 1 deletion OnnxStack.Core/Video/VideoInfo.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;

namespace OnnxStack.Core.Video
{
Expand All @@ -12,6 +14,77 @@ public VideoInfo(int height, int width, TimeSpan duration, float frameRate) : th
public int Height { get; set; }
public int Width { get; set; }

public double AspectRatio => (double)Height / Width;
public double AspectRatio => (double)Width / Height;
}

public record VideoMetadata
{
[JsonPropertyName("format")]
public VideoFormat Format { get; set; }

[JsonPropertyName("streams")]
public List<VideoStream> Streams { get; set; }
}

[JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)]
public record VideoFormat
{
[JsonPropertyName("filename")]
public string FileName { get; set; }

[JsonPropertyName("nb_streams")]
public int StreamCount { get; set; }

[JsonPropertyName("format_name")]
public string FormatName { get; set; }

[JsonPropertyName("format_long_name")]
public string FormatLongName { get; set; }

[JsonPropertyName("size")]
public long Size { get; set; }

[JsonPropertyName("bit_rate")]
public long BitRate { get; set; }
}

[JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)]
public record VideoStream
{
[JsonPropertyName("codec_type")]
public string Type { get; set; }

[JsonPropertyName("codec_name")]
public string CodecName { get; set; }

[JsonPropertyName("codec_long_name")]
public string CodecLongName { get; set; }

[JsonPropertyName("pix_fmt")]
public string PixelFormat { get; set; }

[JsonPropertyName("width")]
public int Width { get; set; }

[JsonPropertyName("height")]
public int Height { get; set; }

[JsonPropertyName("nb_frames")]
public int FrameCount { get; set; }

[JsonPropertyName("duration")]
public float DurationSeconds { get; set; }

public float FramesPerSecond => GetFramesPerSecond();

public TimeSpan Duration => TimeSpan.FromSeconds(DurationSeconds);

private float GetFramesPerSecond()
{
if (FrameCount == 0 || DurationSeconds == 0)
return 0;

return FrameCount / DurationSeconds;
}
}
}

0 comments on commit 8993731

Please sign in to comment.