diff --git a/FFMpegCore.Test/FFMpegCore.Test.csproj b/FFMpegCore.Test/FFMpegCore.Test.csproj index 5a0c11b..8b99510 100644 --- a/FFMpegCore.Test/FFMpegCore.Test.csproj +++ b/FFMpegCore.Test/FFMpegCore.Test.csproj @@ -27,6 +27,12 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest + PreserveNewest @@ -81,6 +87,15 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + diff --git a/FFMpegCore.Test/FFProbeTests.cs b/FFMpegCore.Test/FFProbeTests.cs index 87e883c..01fb217 100644 --- a/FFMpegCore.Test/FFProbeTests.cs +++ b/FFMpegCore.Test/FFProbeTests.cs @@ -145,6 +145,9 @@ public async Task Probe_Async_Success() { var info = await FFProbe.AnalyseAsync(TestResources.Mp4Video); Assert.AreEqual(3, info.Duration.Seconds); + Assert.AreEqual(8, info.PrimaryVideoStream.BitDepth); + // This video's audio stream is AAC, which is lossy, so bit depth is meaningless. + Assert.IsNull(info.PrimaryAudioStream.BitDepth); } [TestMethod, Timeout(10000)] @@ -153,6 +156,8 @@ public void Probe_Success_FromStream() using var stream = File.OpenRead(TestResources.WebmVideo); var info = FFProbe.Analyse(stream); Assert.AreEqual(3, info.Duration.Seconds); + // This video has no audio stream. + Assert.IsNull(info.PrimaryAudioStream); } [TestMethod, Timeout(10000)] @@ -171,16 +176,60 @@ public async Task Probe_Success_Subtitle_Async() Assert.AreEqual(1, info.SubtitleStreams.Count); Assert.AreEqual(0, info.AudioStreams.Count); Assert.AreEqual(0, info.VideoStreams.Count); + // BitDepth is meaningless for subtitles + Assert.IsNull(info.SubtitleStreams[0].BitDepth); } [TestMethod, Timeout(10000)] public async Task Probe_Success_Disposition_Async() { - var info = await FFProbe.AnalyseAsync(TestResources.Mp4Video); - Assert.IsNotNull(info.PrimaryAudioStream); - Assert.IsNotNull(info.PrimaryAudioStream.Disposition); - Assert.AreEqual(true, info.PrimaryAudioStream.Disposition["default"]); - Assert.AreEqual(false, info.PrimaryAudioStream.Disposition["forced"]); + var info = await FFProbe.AnalyseAsync(TestResources.Mp4Video); + Assert.IsNotNull(info.PrimaryAudioStream); + Assert.IsNotNull(info.PrimaryAudioStream.Disposition); + Assert.AreEqual(true, info.PrimaryAudioStream.Disposition["default"]); + Assert.AreEqual(false, info.PrimaryAudioStream.Disposition["forced"]); + } + + [TestMethod, Timeout(10000)] + public async Task Probe_Success_Mp3AudioBitDepthNull_Async() + { + var info = await FFProbe.AnalyseAsync(TestResources.Mp3Audio); + Assert.IsNotNull(info.PrimaryAudioStream); + // mp3 is lossy, so bit depth is meaningless. + Assert.IsNull(info.PrimaryAudioStream.BitDepth); + } + + [TestMethod, Timeout(10000)] + public async Task Probe_Success_VocAudioBitDepth_Async() + { + var info = await FFProbe.AnalyseAsync(TestResources.AiffAudio); + Assert.IsNotNull(info.PrimaryAudioStream); + Assert.AreEqual(16, info.PrimaryAudioStream.BitDepth); + } + + [TestMethod, Timeout(10000)] + public async Task Probe_Success_MkvVideoBitDepth_Async() + { + var info = await FFProbe.AnalyseAsync(TestResources.MkvVideo); + Assert.IsNotNull(info.PrimaryAudioStream); + Assert.AreEqual(8, info.PrimaryVideoStream.BitDepth); + Assert.IsNull(info.PrimaryAudioStream.BitDepth); + } + + [TestMethod, Timeout(10000)] + public async Task Probe_Success_24BitWavBitDepth_Async() + { + var info = await FFProbe.AnalyseAsync(TestResources.Wav24Bit); + Assert.IsNotNull(info.PrimaryAudioStream); + Assert.AreEqual(24, info.PrimaryAudioStream.BitDepth); + } + + [TestMethod, Timeout(10000)] + public async Task Probe_Success_32BitWavBitDepth_Async() + { + var info = await FFProbe.AnalyseAsync(TestResources.Wav32Bit); + Assert.IsNotNull(info.PrimaryAudioStream); + Assert.AreEqual(32, info.PrimaryAudioStream.BitDepth); } } } \ No newline at end of file diff --git a/FFMpegCore.Test/Resources/24_bit_fixed.WAV b/FFMpegCore.Test/Resources/24_bit_fixed.WAV new file mode 100644 index 0000000..acd1265 Binary files /dev/null and b/FFMpegCore.Test/Resources/24_bit_fixed.WAV differ diff --git a/FFMpegCore.Test/Resources/32_bit_float.WAV b/FFMpegCore.Test/Resources/32_bit_float.WAV new file mode 100644 index 0000000..46bfa29 Binary files /dev/null and b/FFMpegCore.Test/Resources/32_bit_float.WAV differ diff --git a/FFMpegCore.Test/Resources/TestResources.cs b/FFMpegCore.Test/Resources/TestResources.cs index 14f8abe..bf29960 100644 --- a/FFMpegCore.Test/Resources/TestResources.cs +++ b/FFMpegCore.Test/Resources/TestResources.cs @@ -21,5 +21,9 @@ public static class TestResources public static readonly string PngImage = "./Resources/cover.png"; public static readonly string ImageCollection = "./Resources/images"; public static readonly string SrtSubtitle = "./Resources/sample.srt"; + public static readonly string AiffAudio = "./Resources/sample3aiff.aiff"; + public static readonly string MkvVideo = "./Resources/sampleMKV.mkv"; + public static readonly string Wav24Bit = "./Resources/24_bit_fixed.WAV"; + public static readonly string Wav32Bit = "./Resources/32_bit_float.WAV"; } } diff --git a/FFMpegCore.Test/Resources/sample3aiff.aiff b/FFMpegCore.Test/Resources/sample3aiff.aiff new file mode 100644 index 0000000..b9973ae Binary files /dev/null and b/FFMpegCore.Test/Resources/sample3aiff.aiff differ diff --git a/FFMpegCore.Test/Resources/sampleMKV.mkv b/FFMpegCore.Test/Resources/sampleMKV.mkv new file mode 100644 index 0000000..5b162de Binary files /dev/null and b/FFMpegCore.Test/Resources/sampleMKV.mkv differ diff --git a/FFMpegCore/FFProbe/FFProbeAnalysis.cs b/FFMpegCore/FFProbe/FFProbeAnalysis.cs index eb65bb2..596c55f 100644 --- a/FFMpegCore/FFProbe/FFProbeAnalysis.cs +++ b/FFMpegCore/FFProbe/FFProbeAnalysis.cs @@ -25,7 +25,10 @@ public class FFProbeStream : ITagsContainer, IDispositionContainer [JsonPropertyName("bits_per_raw_sample")] public string BitsPerRawSample { get; set; } = null!; - + + [JsonPropertyName("bits_per_sample")] + public int BitsPerSample { get; set; } = 0; + [JsonPropertyName("bit_rate")] public string BitRate { get; set; } = null!; diff --git a/FFMpegCore/FFProbe/MediaAnalysis.cs b/FFMpegCore/FFProbe/MediaAnalysis.cs index bb68c22..4f29447 100644 --- a/FFMpegCore/FFProbe/MediaAnalysis.cs +++ b/FFMpegCore/FFProbe/MediaAnalysis.cs @@ -47,6 +47,13 @@ private MediaFormat ParseFormat(Format analysisFormat) public List AudioStreams { get; } public List SubtitleStreams { get; } public IReadOnlyList ErrorData { get; } + + private int? GetBitDepth(FFProbeStream stream) + { + var bitDepth = int.TryParse(stream.BitsPerRawSample, out var bprs) ? bprs : + stream.BitsPerSample; + return bitDepth == 0 ? null : (int?)bitDepth; + } private VideoStream ParseVideoStream(FFProbeStream stream) { @@ -72,12 +79,13 @@ private VideoStream ParseVideoStream(FFProbeStream stream) Language = stream.GetLanguage(), Disposition = MediaAnalysisUtils.FormatDisposition(stream.Disposition), Tags = stream.Tags.ToCaseInsensitive(), + BitDepth = GetBitDepth(stream), }; } private AudioStream ParseAudioStream(FFProbeStream stream) { - return new AudioStream + return new AudioStream { Index = stream.Index, BitRate = !string.IsNullOrEmpty(stream.BitRate) ? MediaAnalysisUtils.ParseLongInvariant(stream.BitRate) : default, @@ -93,6 +101,7 @@ private AudioStream ParseAudioStream(FFProbeStream stream) Language = stream.GetLanguage(), Disposition = MediaAnalysisUtils.FormatDisposition(stream.Disposition), Tags = stream.Tags.ToCaseInsensitive(), + BitDepth = GetBitDepth(stream), }; } diff --git a/FFMpegCore/FFProbe/MediaStream.cs b/FFMpegCore/FFProbe/MediaStream.cs index ffab04b..36ecf56 100644 --- a/FFMpegCore/FFProbe/MediaStream.cs +++ b/FFMpegCore/FFProbe/MediaStream.cs @@ -17,6 +17,7 @@ public abstract class MediaStream public string? Language { get; set; } public Dictionary? Disposition { get; set; } public Dictionary? Tags { get; set; } + public int? BitDepth { get; set; } public Codec GetCodecInfo() => FFMpeg.GetCodec(CodecName); }