parse ffprobes -show_packets output

This commit is contained in:
Alex Zhukov 2021-11-08 06:28:16 -08:00
parent 0f82509c41
commit 239e2aef42
3 changed files with 124 additions and 0 deletions

View file

@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -50,6 +51,43 @@ public async Task FrameAnalysis_Async()
Assert.IsTrue(frameAnalysis.Frames.All(f => f.MediaType == "video")); Assert.IsTrue(frameAnalysis.Frames.All(f => f.MediaType == "video"));
} }
[TestMethod]
public async Task PacketAnalysis_Async()
{
var packetAnalysis = await FFProbe.GetPacketsAsync(TestResources.WebmVideo);
var packets = packetAnalysis.Packets;
Assert.AreEqual(96, packets.Count);
Assert.IsTrue(packets.All(f => f.CodecType == "video"));
Assert.AreEqual("K_", packets[0].Flags);
Assert.AreEqual(1362, packets.Last().Size);
}
[TestMethod]
public void PacketAnalysis_Sync()
{
var packets = FFProbe.GetPackets(TestResources.WebmVideo).Packets;
Assert.AreEqual(96, packets.Count);
Assert.IsTrue(packets.All(f => f.CodecType == "video"));
Assert.AreEqual("K_", packets[0].Flags);
Assert.AreEqual(1362, packets.Last().Size);
}
[TestMethod]
public void PacketAnalysisAudioVideo_Sync()
{
var packets = FFProbe.GetPackets(TestResources.Mp4Video).Packets;
Assert.AreEqual(216, packets.Count);
var actual = packets.Select(f => f.CodecType).Distinct().ToList();
var expected = new List<string> {"audio", "video"};
CollectionAssert.AreEquivalent(expected, actual);
Assert.IsTrue(packets.Where(t=>t.CodecType == "audio").All(f => f.Flags == "K_"));
Assert.AreEqual(75, packets.Count(t => t.CodecType == "video"));
Assert.AreEqual(141, packets.Count(t => t.CodecType == "audio"));
}
[DataTestMethod] [DataTestMethod]
[DataRow("0:00:03.008000", 0, 0, 0, 3, 8)] [DataRow("0:00:03.008000", 0, 0, 0, 3, 8)]
[DataRow("05:12:59.177", 0, 5, 12, 59, 177)] [DataRow("05:12:59.177", 0, 5, 12, 59, 177)]

View file

@ -37,6 +37,20 @@ public static FFProbeFrames GetFrames(string filePath, int outputCapacity = int.
return ParseFramesOutput(instance); return ParseFramesOutput(instance);
} }
public static FFProbePackets GetPackets(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 = PreparePacketAnalysisInstance(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 ParsePacketsOutput(instance);
}
public static IMediaAnalysis Analyse(Uri uri, int outputCapacity = int.MaxValue, FFOptions? ffOptions = null) public static IMediaAnalysis Analyse(Uri uri, int outputCapacity = int.MaxValue, FFOptions? ffOptions = null)
{ {
using var instance = PrepareStreamAnalysisInstance(uri.AbsoluteUri, outputCapacity, ffOptions ?? GlobalFFOptions.Current); using var instance = PrepareStreamAnalysisInstance(uri.AbsoluteUri, outputCapacity, ffOptions ?? GlobalFFOptions.Current);
@ -91,6 +105,17 @@ public static async Task<FFProbeFrames> GetFramesAsync(string filePath, int outp
await instance.FinishedRunning().ConfigureAwait(false); await instance.FinishedRunning().ConfigureAwait(false);
return ParseFramesOutput(instance); return ParseFramesOutput(instance);
} }
public static async Task<FFProbePackets> GetPacketsAsync(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 = PreparePacketAnalysisInstance(filePath, outputCapacity, ffOptions ?? GlobalFFOptions.Current);
await instance.FinishedRunning().ConfigureAwait(false);
return ParsePacketsOutput(instance);
}
public static async Task<IMediaAnalysis> AnalyseAsync(Uri uri, int outputCapacity = int.MaxValue, FFOptions? ffOptions = null) public static async Task<IMediaAnalysis> AnalyseAsync(Uri uri, int outputCapacity = int.MaxValue, FFOptions? ffOptions = null)
{ {
using var instance = PrepareStreamAnalysisInstance(uri.AbsoluteUri, outputCapacity, ffOptions ?? GlobalFFOptions.Current); using var instance = PrepareStreamAnalysisInstance(uri.AbsoluteUri, outputCapacity, ffOptions ?? GlobalFFOptions.Current);
@ -152,11 +177,25 @@ private static FFProbeFrames ParseFramesOutput(Instance instance)
return ffprobeAnalysis; return ffprobeAnalysis;
} }
private static FFProbePackets ParsePacketsOutput(Instance instance)
{
var json = string.Join(string.Empty, instance.OutputData);
var ffprobeAnalysis = JsonSerializer.Deserialize<FFProbePackets>(json, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true,
NumberHandling = System.Text.Json.Serialization.JsonNumberHandling.AllowReadingFromString | System.Text.Json.Serialization.JsonNumberHandling.WriteAsString
}) ;
return ffprobeAnalysis;
}
private static Instance PrepareStreamAnalysisInstance(string filePath, int outputCapacity, FFOptions ffOptions) private static Instance PrepareStreamAnalysisInstance(string filePath, int outputCapacity, FFOptions ffOptions)
=> PrepareInstance($"-loglevel error -print_format json -show_format -sexagesimal -show_streams \"{filePath}\"", outputCapacity, ffOptions); => PrepareInstance($"-loglevel error -print_format json -show_format -sexagesimal -show_streams \"{filePath}\"", outputCapacity, ffOptions);
private static Instance PrepareFrameAnalysisInstance(string filePath, int outputCapacity, FFOptions ffOptions) private static Instance PrepareFrameAnalysisInstance(string filePath, int outputCapacity, FFOptions ffOptions)
=> PrepareInstance($"-loglevel error -print_format json -show_frames -v quiet -sexagesimal \"{filePath}\"", outputCapacity, ffOptions); => PrepareInstance($"-loglevel error -print_format json -show_frames -v quiet -sexagesimal \"{filePath}\"", outputCapacity, ffOptions);
private static Instance PreparePacketAnalysisInstance(string filePath, int outputCapacity, FFOptions ffOptions)
=> PrepareInstance($"-loglevel error -print_format json -show_packets -v quiet -sexagesimal \"{filePath}\"", outputCapacity, ffOptions);
private static Instance PrepareInstance(string arguments, int outputCapacity, FFOptions ffOptions) private static Instance PrepareInstance(string arguments, int outputCapacity, FFOptions ffOptions)
{ {

View file

@ -0,0 +1,47 @@
using System.Collections.Generic;
using System.Text.Json.Serialization;
namespace FFMpegCore
{
public class FFProbePacketAnalysis
{
[JsonPropertyName("codec_type")]
public string CodecType { get; set; }
[JsonPropertyName("stream_index")]
public int StreamIndex { get; set; }
[JsonPropertyName("pts")]
public long Pts { get; set; }
[JsonPropertyName("pts_time")]
public string PtsTime { get; set; }
[JsonPropertyName("dts")]
public long Dts { get; set; }
[JsonPropertyName("dts_time")]
public string DtsTime { get; set; }
[JsonPropertyName("duration")]
public int Duration { get; set; }
[JsonPropertyName("duration_time")]
public string DurationTime { get; set; }
[JsonPropertyName("size")]
public int Size { get; set; }
[JsonPropertyName("pos")]
public long Pos { get; set; }
[JsonPropertyName("flags")]
public string Flags { get; set; }
}
public class FFProbePackets
{
[JsonPropertyName("packets")]
public List<FFProbePacketAnalysis> Packets { get; set; }
}
}