Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions FFMpegCore.Test/FFMpegCore.Test.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@
<None Update="Resources\sample.srt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\brdisk.iso">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

<ItemGroup>
Expand Down
33 changes: 33 additions & 0 deletions FFMpegCore.Test/FFProbeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,39 @@ public void Probe_Success()
Assert.AreEqual("0x31637661", info.PrimaryVideoStream.CodecTag);
}

[TestMethod]
public void Probe_BrdiskISO()
{
var info = FFProbe.Analyse(TestResources.BrdiskISO);
Assert.AreEqual(3, info.Duration.Minutes);

Assert.AreEqual("mono", info.PrimaryAudioStream!.ChannelLayout);
Assert.AreEqual(1, info.PrimaryAudioStream.Channels);
Assert.AreEqual("ATSC A/52A (AC-3)", info.PrimaryAudioStream.CodecLongName);
Assert.AreEqual("ac3", info.PrimaryAudioStream.CodecName);
Assert.AreEqual(32000, info.PrimaryAudioStream.BitRate);
Assert.AreEqual(44100, info.PrimaryAudioStream.SampleRateHz);
Assert.AreEqual("AC-3", info.PrimaryAudioStream.CodecTagString);
Assert.AreEqual("0x332d4341", info.PrimaryAudioStream.CodecTag);

Assert.AreEqual(30000/1001m, info.PrimaryVideoStream.FrameRate);
Assert.AreEqual(30000/1001m, info.PrimaryVideoStream.AvgFrameRate);
Assert.AreEqual(20, info.PrimaryVideoStream.DisplayAspectRatio.Width);
Assert.AreEqual(11, info.PrimaryVideoStream.DisplayAspectRatio.Height);
Assert.AreEqual("yuv420p", info.PrimaryVideoStream.PixelFormat);
Assert.AreEqual("smpte170m", info.PrimaryVideoStream.ColorSpace);
Assert.AreEqual("smpte170m", info.PrimaryVideoStream.ColorPrimaries);
Assert.AreEqual("smpte170m", info.PrimaryVideoStream.ColorTransfer);
Assert.AreEqual(720, info.PrimaryVideoStream.Width);
Assert.AreEqual(480, info.PrimaryVideoStream.Height);
Assert.AreEqual("H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10", info.PrimaryVideoStream.CodecLongName);
Assert.AreEqual("h264", info.PrimaryVideoStream.CodecName);
Assert.AreEqual(8, info.PrimaryVideoStream.BitsPerRawSample);
Assert.AreEqual("High", info.PrimaryVideoStream.Profile);
Assert.AreEqual("HDMV", info.PrimaryVideoStream.CodecTagString);
Assert.AreEqual("0x564d4448", info.PrimaryVideoStream.CodecTag);
}

[TestMethod]
public void Probe_Dolby_Vision()
{
Expand Down
5 changes: 4 additions & 1 deletion FFMpegCore.Test/Resources/TestResources.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace FFMpegCore.Test.Resources
using System;

namespace FFMpegCore.Test.Resources
{
public enum AudioType
{
Expand All @@ -23,5 +25,6 @@ public static class TestResources
public static readonly string SrtSubtitle = "./Resources/sample.srt";
public static readonly string Dovi = "./Resources/dovi.mp4";
public static readonly string Interlaced = "./Resources/interlaced.mkv";
public static readonly Uri BrdiskISO = new Uri("bluray:./Resources/brdisk.iso");
}
}
Binary file added FFMpegCore.Test/Resources/brdisk.iso
Binary file not shown.
124 changes: 76 additions & 48 deletions FFMpegCore/FFProbe/FFProbe.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,15 @@ public static class FFProbe
public static string GetStreamJson(string filePath, int outputCapacity = int.MaxValue,
FFOptions? ffOptions = null)
{
if (!File.Exists(filePath))
throw new FFMpegException(FFMpegExceptionType.File, $"No file found at '{filePath}'");
return GetStreamJson(FileUri(filePath), outputCapacity, ffOptions);
}
public static string GetStreamJson(Uri uri, int outputCapacity = int.MaxValue,
FFOptions? ffOptions = null)
{
if (IsLocalFile(uri) && !File.Exists(uri.LocalPath))
throw new FFMpegException(FFMpegExceptionType.File, $"No file found at '{uri.LocalPath}'");

using var instance = PrepareStreamAnalysisInstance(filePath, outputCapacity, ffOptions ?? GlobalFFOptions.Current);
using var instance = PrepareStreamAnalysisInstance(InputArgument(uri), 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));
Expand All @@ -49,10 +54,14 @@ public static IMediaAnalysis AnalyseStreamJson(string json)
}
public static IMediaAnalysis Analyse(string filePath, int outputCapacity = int.MaxValue, FFOptions? ffOptions = null)
{
if (!File.Exists(filePath))
throw new FFMpegException(FFMpegExceptionType.File, $"No file found at '{filePath}'");
return Analyse(FileUri(filePath), outputCapacity, ffOptions);
}
public static IMediaAnalysis Analyse(Uri uri, int outputCapacity = int.MaxValue, FFOptions? ffOptions = null)
{
if (IsLocalFile(uri) && !File.Exists(uri.LocalPath))
throw new FFMpegException(FFMpegExceptionType.File, $"No file found at '{uri.LocalPath}'");

using var instance = PrepareStreamAnalysisInstance(filePath, outputCapacity, ffOptions ?? GlobalFFOptions.Current);
using var instance = PrepareStreamAnalysisInstance(InputArgument(uri), 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));
Expand All @@ -62,10 +71,15 @@ public static IMediaAnalysis Analyse(string filePath, int outputCapacity = int.M
public static string GetFrameJson(string filePath, int outputCapacity = int.MaxValue,
FFOptions? ffOptions = null)
{
if (!File.Exists(filePath))
throw new FFMpegException(FFMpegExceptionType.File, $"No file found at '{filePath}'");
return GetFrameJson(FileUri(filePath), outputCapacity, ffOptions);
}
public static string GetFrameJson(Uri uri, int outputCapacity = int.MaxValue,
FFOptions? ffOptions = null)
{
if (IsLocalFile(uri) && !File.Exists(uri.LocalPath))
throw new FFMpegException(FFMpegExceptionType.File, $"No file found at '{uri.LocalPath}'");

using var instance = PrepareFrameAnalysisInstance(filePath, outputCapacity, ffOptions ?? GlobalFFOptions.Current);
using var instance = PrepareFrameAnalysisInstance(InputArgument(uri), 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));
Expand All @@ -78,10 +92,14 @@ public static FFProbeFrames AnalyseFrameJson(string json)
}
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}'");
return GetFrames(FileUri(filePath), outputCapacity, ffOptions);
}
public static FFProbeFrames GetFrames(Uri uri, int outputCapacity = int.MaxValue, FFOptions? ffOptions = null)
{
if (IsLocalFile(uri) && !File.Exists(uri.LocalPath))
throw new FFMpegException(FFMpegExceptionType.File, $"No file found at '{uri.LocalPath}'");

using var instance = PrepareFrameAnalysisInstance(filePath, outputCapacity, ffOptions ?? GlobalFFOptions.Current);
using var instance = PrepareFrameAnalysisInstance(InputArgument(uri), 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));
Expand All @@ -91,26 +109,21 @@ public static FFProbeFrames GetFrames(string filePath, int outputCapacity = int.

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}'");
return GetPackets(FileUri(filePath), outputCapacity, ffOptions);
}
public static FFProbePackets GetPackets(Uri uri, int outputCapacity = int.MaxValue, FFOptions? ffOptions = null)
{
if (IsLocalFile(uri) && !File.Exists(uri.LocalPath))
throw new FFMpegException(FFMpegExceptionType.File, $"No file found at '{uri.LocalPath}'");

using var instance = PreparePacketAnalysisInstance(filePath, outputCapacity, ffOptions ?? GlobalFFOptions.Current);
using var instance = PreparePacketAnalysisInstance(InputArgument(uri), 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)
{
using var instance = PrepareStreamAnalysisInstance(uri.AbsoluteUri, 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 ParseOutput(instance);
}
public static IMediaAnalysis Analyse(Stream stream, int outputCapacity = int.MaxValue, FFOptions? ffOptions = null)
{
var streamPipeSource = new StreamPipeSource(stream);
Expand All @@ -136,10 +149,14 @@ public static IMediaAnalysis Analyse(Stream stream, int outputCapacity = int.Max
}
public static async Task<IMediaAnalysis> AnalyseAsync(string filePath, int outputCapacity = int.MaxValue, FFOptions? ffOptions = null)
{
if (!File.Exists(filePath))
throw new FFMpegException(FFMpegExceptionType.File, $"No file found at '{filePath}'");
return await AnalyseAsync(FileUri(filePath), outputCapacity, ffOptions);
}
public static async Task<IMediaAnalysis> AnalyseAsync(Uri uri, int outputCapacity = int.MaxValue, FFOptions? ffOptions = null)
{
if (IsLocalFile(uri) && !File.Exists(uri.LocalPath))
throw new FFMpegException(FFMpegExceptionType.File, $"No file found at '{uri.LocalPath}'");

using var instance = PrepareStreamAnalysisInstance(filePath, outputCapacity, ffOptions ?? GlobalFFOptions.Current);
using var instance = PrepareStreamAnalysisInstance(InputArgument(uri), outputCapacity, ffOptions ?? GlobalFFOptions.Current);
var exitCode = await instance.FinishedRunning().ConfigureAwait(false);
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));
Expand All @@ -149,33 +166,32 @@ public static async Task<IMediaAnalysis> AnalyseAsync(string filePath, int outpu

public static async Task<FFProbeFrames> GetFramesAsync(string filePath, int outputCapacity = int.MaxValue, FFOptions? ffOptions = null)
{
if (!File.Exists(filePath))
throw new FFMpegException(FFMpegExceptionType.File, $"No file found at '{filePath}'");
return await GetFramesAsync(FileUri(filePath), outputCapacity, ffOptions);
}
public static async Task<FFProbeFrames> GetFramesAsync(Uri uri, int outputCapacity = int.MaxValue, FFOptions? ffOptions = null)
{
if (IsLocalFile(uri) && !File.Exists(uri.LocalPath))
throw new FFMpegException(FFMpegExceptionType.File, $"No file found at '{uri.LocalPath}'");

using var instance = PrepareFrameAnalysisInstance(filePath, outputCapacity, ffOptions ?? GlobalFFOptions.Current);
using var instance = PrepareFrameAnalysisInstance(InputArgument(uri), outputCapacity, ffOptions ?? GlobalFFOptions.Current);
await instance.FinishedRunning().ConfigureAwait(false);
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}'");
return await GetPacketsAsync(FileUri(filePath), outputCapacity, ffOptions);
}
public static async Task<FFProbePackets> GetPacketsAsync(Uri uri, int outputCapacity = int.MaxValue, FFOptions? ffOptions = null)
{
if (IsLocalFile(uri) && !File.Exists(uri.LocalPath))
throw new FFMpegException(FFMpegExceptionType.File, $"No file found at '{uri.LocalPath}'");

using var instance = PreparePacketAnalysisInstance(filePath, outputCapacity, ffOptions ?? GlobalFFOptions.Current);
using var instance = PreparePacketAnalysisInstance(InputArgument(uri), 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)
{
using var instance = PrepareStreamAnalysisInstance(uri.AbsoluteUri, outputCapacity, ffOptions ?? GlobalFFOptions.Current);
var exitCode = await instance.FinishedRunning().ConfigureAwait(false);
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 ParseOutput(instance);
}
public static async Task<IMediaAnalysis> AnalyseAsync(Stream stream, int outputCapacity = int.MaxValue, FFOptions? ffOptions = null)
{
var streamPipeSource = new StreamPipeSource(stream);
Expand Down Expand Up @@ -245,13 +261,25 @@ private static FFProbePackets ParsePacketsOutput(Instance instance)
return ffprobeAnalysis;
}

private static bool IsLocalFile(Uri uri)
{
return uri.IsFile || uri.Scheme == "bluray";
}
private static Uri FileUri(string filePath)
{
return new Uri(Path.GetFullPath(filePath));
}
private static string InputArgument(Uri uri)
{
return uri.IsFile ? uri.LocalPath : uri.GetComponents(UriComponents.AbsoluteUri, UriFormat.Unescaped);
}

private static Instance PrepareStreamAnalysisInstance(string filePath, int outputCapacity, FFOptions ffOptions)
=> PrepareInstance($"-loglevel error -print_format json -show_format -sexagesimal -show_streams {ffOptions.ExtraArguments} \"{filePath}\"", outputCapacity, ffOptions);
private static Instance PrepareFrameAnalysisInstance(string filePath, int outputCapacity, FFOptions ffOptions)
=> PrepareInstance($"-loglevel error -print_format json -show_frames -v quiet -sexagesimal {ffOptions.ExtraArguments} \"{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 PrepareStreamAnalysisInstance(string input, int outputCapacity, FFOptions ffOptions)
=> PrepareInstance($"-loglevel error -print_format json -show_format -sexagesimal -show_streams {ffOptions.ExtraArguments} \"{input}\"", outputCapacity, ffOptions);
private static Instance PrepareFrameAnalysisInstance(string input, int outputCapacity, FFOptions ffOptions)
=> PrepareInstance($"-loglevel error -print_format json -show_frames -v quiet -sexagesimal {ffOptions.ExtraArguments} \"{input}\"", outputCapacity, ffOptions);
private static Instance PreparePacketAnalysisInstance(string input, int outputCapacity, FFOptions ffOptions)
=> PrepareInstance($"-loglevel error -print_format json -show_packets -v quiet -sexagesimal \"{input}\"", outputCapacity, ffOptions);

private static Instance PrepareInstance(string arguments, int outputCapacity, FFOptions ffOptions)
{
Expand Down