diff --git a/FFMpegCore/FFProbe/FFProbe.cs b/FFMpegCore/FFProbe/FFProbe.cs index 7d043a6..fe1e1fe 100644 --- a/FFMpegCore/FFProbe/FFProbe.cs +++ b/FFMpegCore/FFProbe/FFProbe.cs @@ -25,6 +25,18 @@ public static IMediaAnalysis Analyse(string filePath, int outputCapacity = int.M return ParseOutput(instance); } + public static FFProbeFrames GetFrames(string filePath, int outputCapacity = int.MaxValue, FFOptions? ffOptions = null) + { + if (!File.Exists(filePath)) + throw new FFMpegException(FFMpegExceptionType.File, $"No file found at '{filePath}'"); + + using var instance = PrepareProbeInstance(filePath, outputCapacity, ffOptions ?? GlobalFFOptions.Current); + var exitCode = instance.BlockUntilFinished(); + if (exitCode != 0) + throw new FFMpegException(FFMpegExceptionType.Process, $"ffprobe exited with non-zero exit-code ({exitCode} - {string.Join("\n", instance.ErrorData)})", null, string.Join("\n", instance.ErrorData)); + + return ParseFramesOutput(instance); + } public static IMediaAnalysis Analyse(Uri uri, int outputCapacity = int.MaxValue, FFOptions? ffOptions = null) { using var instance = PrepareInstance(uri.AbsoluteUri, outputCapacity, ffOptions ?? GlobalFFOptions.Current); @@ -66,6 +78,16 @@ public static async Task AnalyseAsync(string filePath, int outpu await instance.FinishedRunning().ConfigureAwait(false); return ParseOutput(instance); } + + public static async Task GetFramesAsync(string filePath, int outputCapacity = int.MaxValue, FFOptions? ffOptions = null) + { + if (!File.Exists(filePath)) + throw new FFMpegException(FFMpegExceptionType.File, $"No file found at '{filePath}'"); + + using var instance = PrepareProbeInstance(filePath, outputCapacity, ffOptions ?? GlobalFFOptions.Current); + await instance.FinishedRunning().ConfigureAwait(false); + return ParseFramesOutput(instance); + } public static async Task AnalyseAsync(Uri uri, int outputCapacity = int.MaxValue, FFOptions? ffOptions = null) { using var instance = PrepareInstance(uri.AbsoluteUri, outputCapacity, ffOptions ?? GlobalFFOptions.Current); @@ -112,6 +134,17 @@ private static IMediaAnalysis ParseOutput(Instance instance) return new MediaAnalysis(ffprobeAnalysis); } + private static FFProbeFrames ParseFramesOutput(Instance instance) + { + var json = string.Join(string.Empty, instance.OutputData); + var ffprobeAnalysis = JsonSerializer.Deserialize(json, new JsonSerializerOptions + { + PropertyNameCaseInsensitive = true, + NumberHandling = System.Text.Json.Serialization.JsonNumberHandling.AllowReadingFromString | System.Text.Json.Serialization.JsonNumberHandling.WriteAsString + }) ; + + return ffprobeAnalysis; + } private static Instance PrepareInstance(string filePath, int outputCapacity, FFOptions ffOptions) { @@ -126,5 +159,18 @@ private static Instance PrepareInstance(string filePath, int outputCapacity, FFO var instance = new Instance(startInfo) { DataBufferCapacity = outputCapacity }; return instance; } + private static Instance PrepareProbeInstance(string filePath, int outputCapacity, FFOptions ffOptions) + { + FFProbeHelper.RootExceptionCheck(); + FFProbeHelper.VerifyFFProbeExists(ffOptions); + var arguments = $"-loglevel error -print_format json -show_frames -v quiet -sexagesimal \"{filePath}\""; + var startInfo = new ProcessStartInfo(GlobalFFOptions.GetFFProbeBinaryPath(), arguments) + { + StandardOutputEncoding = ffOptions.Encoding, + StandardErrorEncoding = ffOptions.Encoding + }; + var instance = new Instance(startInfo) { DataBufferCapacity = outputCapacity }; + return instance; + } } } diff --git a/FFMpegCore/FFProbe/FrameAnalysis.cs b/FFMpegCore/FFProbe/FrameAnalysis.cs new file mode 100644 index 0000000..6ac2b19 --- /dev/null +++ b/FFMpegCore/FFProbe/FrameAnalysis.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json.Serialization; + +namespace FFMpegCore +{ + public class Frame + { + public string media_type { get; set; } + public int stream_index { get; set; } + public int key_frame { get; set; } + public long pkt_pts { get; set; } + public string pkt_pts_time { get; set; } + public long pkt_dts { get; set; } + public string pkt_dts_time { get; set; } + public long best_effort_timestamp { get; set; } + public string best_effort_timestamp_time { get; set; } + public int pkt_duration { get; set; } + public string pkt_duration_time { get; set; } + public long pkt_pos { get; set; } + public int pkt_size { get; set; } + public long width { get; set; } + public long height { get; set; } + public string pix_fmt { get; set; } + public string pict_type { get; set; } + public long coded_picture_number { get; set; } + public long display_picture_number { get; set; } + public int interlaced_frame { get; set; } + public int top_field_first { get; set; } + public int repeat_pict { get; set; } + public string chroma_location { get; set; } + } + + public class FFProbeFrames + { + public List frames { get; set; } + } +}