mirror of
https://github.com/rosenbjerg/FFMpegCore.git
synced 2024-11-10 08:34:12 +01:00
parent
dbccc116fe
commit
75fa1db64f
3 changed files with 112 additions and 51 deletions
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using FFMpegCore.Test.Resources;
|
using FFMpegCore.Test.Resources;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
@ -25,6 +26,30 @@ public async Task Audio_FromStream_Duration()
|
||||||
Assert.IsTrue(fileAnalysis.Duration == streamAnalysis.Duration);
|
Assert.IsTrue(fileAnalysis.Duration == streamAnalysis.Duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void FrameAnalysis_Sync()
|
||||||
|
{
|
||||||
|
var frameAnalysis = FFProbe.GetFrames(TestResources.WebmVideo);
|
||||||
|
|
||||||
|
Assert.AreEqual(90, frameAnalysis.Frames.Count);
|
||||||
|
Assert.IsTrue(frameAnalysis.Frames.All(f => f.PixelFormat == "yuv420p"));
|
||||||
|
Assert.IsTrue(frameAnalysis.Frames.All(f => f.Height == 360));
|
||||||
|
Assert.IsTrue(frameAnalysis.Frames.All(f => f.Width == 640));
|
||||||
|
Assert.IsTrue(frameAnalysis.Frames.All(f => f.MediaType == "video"));
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task FrameAnalysis_Async()
|
||||||
|
{
|
||||||
|
var frameAnalysis = await FFProbe.GetFramesAsync(TestResources.WebmVideo);
|
||||||
|
|
||||||
|
Assert.AreEqual(90, frameAnalysis.Frames.Count);
|
||||||
|
Assert.IsTrue(frameAnalysis.Frames.All(f => f.PixelFormat == "yuv420p"));
|
||||||
|
Assert.IsTrue(frameAnalysis.Frames.All(f => f.Height == 360));
|
||||||
|
Assert.IsTrue(frameAnalysis.Frames.All(f => f.Width == 640));
|
||||||
|
Assert.IsTrue(frameAnalysis.Frames.All(f => f.MediaType == "video"));
|
||||||
|
}
|
||||||
|
|
||||||
[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)]
|
||||||
|
|
|
@ -18,7 +18,7 @@ public static IMediaAnalysis Analyse(string filePath, int outputCapacity = int.M
|
||||||
if (!File.Exists(filePath))
|
if (!File.Exists(filePath))
|
||||||
throw new FFMpegException(FFMpegExceptionType.File, $"No file found at '{filePath}'");
|
throw new FFMpegException(FFMpegExceptionType.File, $"No file found at '{filePath}'");
|
||||||
|
|
||||||
using var instance = PrepareInstance(filePath, outputCapacity, ffOptions ?? GlobalFFOptions.Current);
|
using var instance = PrepareStreamAnalysisInstance(filePath, outputCapacity, ffOptions ?? GlobalFFOptions.Current);
|
||||||
var exitCode = instance.BlockUntilFinished();
|
var exitCode = instance.BlockUntilFinished();
|
||||||
if (exitCode != 0)
|
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));
|
throw new FFMpegException(FFMpegExceptionType.Process, $"ffprobe exited with non-zero exit-code ({exitCode} - {string.Join("\n", instance.ErrorData)})", null, string.Join("\n", instance.ErrorData));
|
||||||
|
@ -30,7 +30,7 @@ public static FFProbeFrames GetFrames(string filePath, int outputCapacity = int.
|
||||||
if (!File.Exists(filePath))
|
if (!File.Exists(filePath))
|
||||||
throw new FFMpegException(FFMpegExceptionType.File, $"No file found at '{filePath}'");
|
throw new FFMpegException(FFMpegExceptionType.File, $"No file found at '{filePath}'");
|
||||||
|
|
||||||
using var instance = PrepareProbeInstance(filePath, outputCapacity, ffOptions ?? GlobalFFOptions.Current);
|
using var instance = PrepareFrameAnalysisInstance(filePath, outputCapacity, ffOptions ?? GlobalFFOptions.Current);
|
||||||
var exitCode = instance.BlockUntilFinished();
|
var exitCode = instance.BlockUntilFinished();
|
||||||
if (exitCode != 0)
|
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));
|
throw new FFMpegException(FFMpegExceptionType.Process, $"ffprobe exited with non-zero exit-code ({exitCode} - {string.Join("\n", instance.ErrorData)})", null, string.Join("\n", instance.ErrorData));
|
||||||
|
@ -39,7 +39,7 @@ public static FFProbeFrames GetFrames(string filePath, int outputCapacity = int.
|
||||||
}
|
}
|
||||||
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 = PrepareInstance(uri.AbsoluteUri, outputCapacity, ffOptions ?? GlobalFFOptions.Current);
|
using var instance = PrepareStreamAnalysisInstance(uri.AbsoluteUri, outputCapacity, ffOptions ?? GlobalFFOptions.Current);
|
||||||
var exitCode = instance.BlockUntilFinished();
|
var exitCode = instance.BlockUntilFinished();
|
||||||
if (exitCode != 0)
|
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));
|
throw new FFMpegException(FFMpegExceptionType.Process, $"ffprobe exited with non-zero exit-code ({exitCode} - {string.Join("\n", instance.ErrorData)})", null, string.Join("\n", instance.ErrorData));
|
||||||
|
@ -50,7 +50,7 @@ public static IMediaAnalysis Analyse(Stream stream, int outputCapacity = int.Max
|
||||||
{
|
{
|
||||||
var streamPipeSource = new StreamPipeSource(stream);
|
var streamPipeSource = new StreamPipeSource(stream);
|
||||||
var pipeArgument = new InputPipeArgument(streamPipeSource);
|
var pipeArgument = new InputPipeArgument(streamPipeSource);
|
||||||
using var instance = PrepareInstance(pipeArgument.PipePath, outputCapacity, ffOptions ?? GlobalFFOptions.Current);
|
using var instance = PrepareStreamAnalysisInstance(pipeArgument.PipePath, outputCapacity, ffOptions ?? GlobalFFOptions.Current);
|
||||||
pipeArgument.Pre();
|
pipeArgument.Pre();
|
||||||
|
|
||||||
var task = instance.FinishedRunning();
|
var task = instance.FinishedRunning();
|
||||||
|
@ -74,7 +74,7 @@ public static async Task<IMediaAnalysis> AnalyseAsync(string filePath, int outpu
|
||||||
if (!File.Exists(filePath))
|
if (!File.Exists(filePath))
|
||||||
throw new FFMpegException(FFMpegExceptionType.File, $"No file found at '{filePath}'");
|
throw new FFMpegException(FFMpegExceptionType.File, $"No file found at '{filePath}'");
|
||||||
|
|
||||||
using var instance = PrepareInstance(filePath, outputCapacity, ffOptions ?? GlobalFFOptions.Current);
|
using var instance = PrepareStreamAnalysisInstance(filePath, outputCapacity, ffOptions ?? GlobalFFOptions.Current);
|
||||||
await instance.FinishedRunning().ConfigureAwait(false);
|
await instance.FinishedRunning().ConfigureAwait(false);
|
||||||
return ParseOutput(instance);
|
return ParseOutput(instance);
|
||||||
}
|
}
|
||||||
|
@ -84,13 +84,13 @@ public static async Task<FFProbeFrames> GetFramesAsync(string filePath, int outp
|
||||||
if (!File.Exists(filePath))
|
if (!File.Exists(filePath))
|
||||||
throw new FFMpegException(FFMpegExceptionType.File, $"No file found at '{filePath}'");
|
throw new FFMpegException(FFMpegExceptionType.File, $"No file found at '{filePath}'");
|
||||||
|
|
||||||
using var instance = PrepareProbeInstance(filePath, outputCapacity, ffOptions ?? GlobalFFOptions.Current);
|
using var instance = PrepareFrameAnalysisInstance(filePath, outputCapacity, ffOptions ?? GlobalFFOptions.Current);
|
||||||
await instance.FinishedRunning().ConfigureAwait(false);
|
await instance.FinishedRunning().ConfigureAwait(false);
|
||||||
return ParseFramesOutput(instance);
|
return ParseFramesOutput(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 = PrepareInstance(uri.AbsoluteUri, outputCapacity, ffOptions ?? GlobalFFOptions.Current);
|
using var instance = PrepareStreamAnalysisInstance(uri.AbsoluteUri, outputCapacity, ffOptions ?? GlobalFFOptions.Current);
|
||||||
await instance.FinishedRunning().ConfigureAwait(false);
|
await instance.FinishedRunning().ConfigureAwait(false);
|
||||||
return ParseOutput(instance);
|
return ParseOutput(instance);
|
||||||
}
|
}
|
||||||
|
@ -98,7 +98,7 @@ public static async Task<IMediaAnalysis> AnalyseAsync(Stream stream, int outputC
|
||||||
{
|
{
|
||||||
var streamPipeSource = new StreamPipeSource(stream);
|
var streamPipeSource = new StreamPipeSource(stream);
|
||||||
var pipeArgument = new InputPipeArgument(streamPipeSource);
|
var pipeArgument = new InputPipeArgument(streamPipeSource);
|
||||||
using var instance = PrepareInstance(pipeArgument.PipePath, outputCapacity, ffOptions ?? GlobalFFOptions.Current);
|
using var instance = PrepareStreamAnalysisInstance(pipeArgument.PipePath, outputCapacity, ffOptions ?? GlobalFFOptions.Current);
|
||||||
pipeArgument.Pre();
|
pipeArgument.Pre();
|
||||||
|
|
||||||
var task = instance.FinishedRunning();
|
var task = instance.FinishedRunning();
|
||||||
|
@ -146,24 +146,16 @@ private static FFProbeFrames ParseFramesOutput(Instance instance)
|
||||||
return ffprobeAnalysis;
|
return ffprobeAnalysis;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Instance PrepareInstance(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);
|
||||||
|
private static Instance PrepareFrameAnalysisInstance(string filePath, int outputCapacity, FFOptions ffOptions)
|
||||||
|
=> PrepareInstance($"-loglevel error -print_format json -show_frames -v quiet -sexagesimal \"{filePath}\"", outputCapacity, ffOptions);
|
||||||
|
|
||||||
|
private static Instance PrepareInstance(string arguments, int outputCapacity, FFOptions ffOptions)
|
||||||
{
|
{
|
||||||
FFProbeHelper.RootExceptionCheck();
|
FFProbeHelper.RootExceptionCheck();
|
||||||
FFProbeHelper.VerifyFFProbeExists(ffOptions);
|
FFProbeHelper.VerifyFFProbeExists(ffOptions);
|
||||||
var arguments = $"-loglevel error -print_format json -show_format -sexagesimal -show_streams \"{filePath}\"";
|
|
||||||
var startInfo = new ProcessStartInfo(GlobalFFOptions.GetFFProbeBinaryPath(), arguments)
|
|
||||||
{
|
|
||||||
StandardOutputEncoding = ffOptions.Encoding,
|
|
||||||
StandardErrorEncoding = ffOptions.Encoding
|
|
||||||
};
|
|
||||||
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)
|
var startInfo = new ProcessStartInfo(GlobalFFOptions.GetFFProbeBinaryPath(), arguments)
|
||||||
{
|
{
|
||||||
StandardOutputEncoding = ffOptions.Encoding,
|
StandardOutputEncoding = ffOptions.Encoding,
|
||||||
|
|
|
@ -1,39 +1,83 @@
|
||||||
using System;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace FFMpegCore
|
namespace FFMpegCore
|
||||||
{
|
{
|
||||||
public class Frame
|
public class FFProbeFrameAnalysis
|
||||||
{
|
{
|
||||||
public string media_type { get; set; }
|
[JsonPropertyName("media_type")]
|
||||||
public int stream_index { get; set; }
|
public string MediaType { get; set; }
|
||||||
public int key_frame { get; set; }
|
|
||||||
public long pkt_pts { get; set; }
|
[JsonPropertyName("stream_index")]
|
||||||
public string pkt_pts_time { get; set; }
|
public int StreamIndex { get; set; }
|
||||||
public long pkt_dts { get; set; }
|
|
||||||
public string pkt_dts_time { get; set; }
|
[JsonPropertyName("key_frame")]
|
||||||
public long best_effort_timestamp { get; set; }
|
public int KeyFrame { get; set; }
|
||||||
public string best_effort_timestamp_time { get; set; }
|
|
||||||
public int pkt_duration { get; set; }
|
[JsonPropertyName("pkt_pts")]
|
||||||
public string pkt_duration_time { get; set; }
|
public long PacketPts { get; set; }
|
||||||
public long pkt_pos { get; set; }
|
|
||||||
public int pkt_size { get; set; }
|
[JsonPropertyName("pkt_pts_time")]
|
||||||
public long width { get; set; }
|
public string PacketPtsTime { get; set; }
|
||||||
public long height { get; set; }
|
|
||||||
public string pix_fmt { get; set; }
|
[JsonPropertyName("pkt_dts")]
|
||||||
public string pict_type { get; set; }
|
public long PacketDts { get; set; }
|
||||||
public long coded_picture_number { get; set; }
|
|
||||||
public long display_picture_number { get; set; }
|
[JsonPropertyName("pkt_dts_time")]
|
||||||
public int interlaced_frame { get; set; }
|
public string PacketDtsTime { get; set; }
|
||||||
public int top_field_first { get; set; }
|
|
||||||
public int repeat_pict { get; set; }
|
[JsonPropertyName("best_effort_timestamp")]
|
||||||
public string chroma_location { get; set; }
|
public long BestEffortTimestamp { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("best_effort_timestamp_time")]
|
||||||
|
public string BestEffortTimestampTime { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("pkt_duration")]
|
||||||
|
public int PacketDuration { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("pkt_duration_time")]
|
||||||
|
public string PacketDurationTime { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("pkt_pos")]
|
||||||
|
public long PacketPos { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("pkt_size")]
|
||||||
|
public int PacketSize { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("width")]
|
||||||
|
public long Width { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("height")]
|
||||||
|
public long Height { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("pix_fmt")]
|
||||||
|
public string PixelFormat { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("pict_type")]
|
||||||
|
public string PictureType { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("coded_picture_number")]
|
||||||
|
public long CodedPictureNumber { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("display_picture_number")]
|
||||||
|
public long DisplayPictureNumber { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("interlaced_frame")]
|
||||||
|
public int InterlacedFrame { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("top_field_first")]
|
||||||
|
public int TopFieldFirst { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("repeat_pict")]
|
||||||
|
public int RepeatPicture { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("chroma_location")]
|
||||||
|
public string ChromaLocation { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class FFProbeFrames
|
public class FFProbeFrames
|
||||||
{
|
{
|
||||||
public List<Frame> frames { get; set; }
|
[JsonPropertyName("frames")]
|
||||||
|
public List<FFProbeFrameAnalysis> Frames { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue