Added ability to retrieve bit depth from media streams for lossless encodings (#359)

* Added ability to retrieve bit depth from media streams for lossless encodings

* Shortened sample AIFF file used in tests
This commit is contained in:
Tom Bogle 2023-02-01 02:09:16 -05:00 committed by GitHub
parent ce7ebe0cff
commit db7e1e4995
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 88 additions and 7 deletions

View file

@ -27,6 +27,12 @@
<Content Include="ffmpeg.config.json"> <Content Include="ffmpeg.config.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<None Update="Resources\24_bit_fixed.WAV">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\32_bit_float.WAV">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\input.webm"> <None Update="Resources\input.webm">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
@ -81,6 +87,15 @@
<None Update="Resources\sample.srt"> <None Update="Resources\sample.srt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
<None Update="Resources\sample3aiff.aiff">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\sampleMKV.mkv">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\sampleVOC.voc">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup> </ItemGroup>
</Project> </Project>

View file

@ -145,6 +145,9 @@ public async Task Probe_Async_Success()
{ {
var info = await FFProbe.AnalyseAsync(TestResources.Mp4Video); var info = await FFProbe.AnalyseAsync(TestResources.Mp4Video);
Assert.AreEqual(3, info.Duration.Seconds); 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)] [TestMethod, Timeout(10000)]
@ -153,6 +156,8 @@ public void Probe_Success_FromStream()
using var stream = File.OpenRead(TestResources.WebmVideo); using var stream = File.OpenRead(TestResources.WebmVideo);
var info = FFProbe.Analyse(stream); var info = FFProbe.Analyse(stream);
Assert.AreEqual(3, info.Duration.Seconds); Assert.AreEqual(3, info.Duration.Seconds);
// This video has no audio stream.
Assert.IsNull(info.PrimaryAudioStream);
} }
[TestMethod, Timeout(10000)] [TestMethod, Timeout(10000)]
@ -171,16 +176,60 @@ public async Task Probe_Success_Subtitle_Async()
Assert.AreEqual(1, info.SubtitleStreams.Count); Assert.AreEqual(1, info.SubtitleStreams.Count);
Assert.AreEqual(0, info.AudioStreams.Count); Assert.AreEqual(0, info.AudioStreams.Count);
Assert.AreEqual(0, info.VideoStreams.Count); Assert.AreEqual(0, info.VideoStreams.Count);
// BitDepth is meaningless for subtitles
Assert.IsNull(info.SubtitleStreams[0].BitDepth);
} }
[TestMethod, Timeout(10000)] [TestMethod, Timeout(10000)]
public async Task Probe_Success_Disposition_Async() public async Task Probe_Success_Disposition_Async()
{ {
var info = await FFProbe.AnalyseAsync(TestResources.Mp4Video); var info = await FFProbe.AnalyseAsync(TestResources.Mp4Video);
Assert.IsNotNull(info.PrimaryAudioStream); Assert.IsNotNull(info.PrimaryAudioStream);
Assert.IsNotNull(info.PrimaryAudioStream.Disposition); Assert.IsNotNull(info.PrimaryAudioStream.Disposition);
Assert.AreEqual(true, info.PrimaryAudioStream.Disposition["default"]); Assert.AreEqual(true, info.PrimaryAudioStream.Disposition["default"]);
Assert.AreEqual(false, info.PrimaryAudioStream.Disposition["forced"]); 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);
} }
} }
} }

Binary file not shown.

Binary file not shown.

View file

@ -21,5 +21,9 @@ public static class TestResources
public static readonly string PngImage = "./Resources/cover.png"; public static readonly string PngImage = "./Resources/cover.png";
public static readonly string ImageCollection = "./Resources/images"; public static readonly string ImageCollection = "./Resources/images";
public static readonly string SrtSubtitle = "./Resources/sample.srt"; 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";
} }
} }

Binary file not shown.

Binary file not shown.

View file

@ -26,6 +26,9 @@ public class FFProbeStream : ITagsContainer, IDispositionContainer
[JsonPropertyName("bits_per_raw_sample")] [JsonPropertyName("bits_per_raw_sample")]
public string BitsPerRawSample { get; set; } = null!; public string BitsPerRawSample { get; set; } = null!;
[JsonPropertyName("bits_per_sample")]
public int BitsPerSample { get; set; } = 0;
[JsonPropertyName("bit_rate")] [JsonPropertyName("bit_rate")]
public string BitRate { get; set; } = null!; public string BitRate { get; set; } = null!;

View file

@ -48,6 +48,13 @@ private MediaFormat ParseFormat(Format analysisFormat)
public List<SubtitleStream> SubtitleStreams { get; } public List<SubtitleStream> SubtitleStreams { get; }
public IReadOnlyList<string> ErrorData { get; } public IReadOnlyList<string> 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) private VideoStream ParseVideoStream(FFProbeStream stream)
{ {
return new VideoStream return new VideoStream
@ -72,12 +79,13 @@ private VideoStream ParseVideoStream(FFProbeStream stream)
Language = stream.GetLanguage(), Language = stream.GetLanguage(),
Disposition = MediaAnalysisUtils.FormatDisposition(stream.Disposition), Disposition = MediaAnalysisUtils.FormatDisposition(stream.Disposition),
Tags = stream.Tags.ToCaseInsensitive(), Tags = stream.Tags.ToCaseInsensitive(),
BitDepth = GetBitDepth(stream),
}; };
} }
private AudioStream ParseAudioStream(FFProbeStream stream) private AudioStream ParseAudioStream(FFProbeStream stream)
{ {
return new AudioStream return new AudioStream
{ {
Index = stream.Index, Index = stream.Index,
BitRate = !string.IsNullOrEmpty(stream.BitRate) ? MediaAnalysisUtils.ParseLongInvariant(stream.BitRate) : default, BitRate = !string.IsNullOrEmpty(stream.BitRate) ? MediaAnalysisUtils.ParseLongInvariant(stream.BitRate) : default,
@ -93,6 +101,7 @@ private AudioStream ParseAudioStream(FFProbeStream stream)
Language = stream.GetLanguage(), Language = stream.GetLanguage(),
Disposition = MediaAnalysisUtils.FormatDisposition(stream.Disposition), Disposition = MediaAnalysisUtils.FormatDisposition(stream.Disposition),
Tags = stream.Tags.ToCaseInsensitive(), Tags = stream.Tags.ToCaseInsensitive(),
BitDepth = GetBitDepth(stream),
}; };
} }

View file

@ -17,6 +17,7 @@ public abstract class MediaStream
public string? Language { get; set; } public string? Language { get; set; }
public Dictionary<string, bool>? Disposition { get; set; } public Dictionary<string, bool>? Disposition { get; set; }
public Dictionary<string, string>? Tags { get; set; } public Dictionary<string, string>? Tags { get; set; }
public int? BitDepth { get; set; }
public Codec GetCodecInfo() => FFMpeg.GetCodec(CodecName); public Codec GetCodecInfo() => FFMpeg.GetCodec(CodecName);
} }