mirror of
https://github.com/rosenbjerg/FFMpegCore.git
synced 2024-11-10 00:24:14 +01:00
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
Former-commit-id: db7e1e4995
This commit is contained in:
parent
910ec6c759
commit
7dfefbdfdb
10 changed files with 88 additions and 7 deletions
|
@ -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>
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
BIN
FFMpegCore.Test/Resources/24_bit_fixed.WAV
Normal file
BIN
FFMpegCore.Test/Resources/24_bit_fixed.WAV
Normal file
Binary file not shown.
BIN
FFMpegCore.Test/Resources/32_bit_float.WAV
Normal file
BIN
FFMpegCore.Test/Resources/32_bit_float.WAV
Normal file
Binary file not shown.
|
@ -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";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
BIN
FFMpegCore.Test/Resources/sample3aiff.aiff
Normal file
BIN
FFMpegCore.Test/Resources/sample3aiff.aiff
Normal file
Binary file not shown.
BIN
FFMpegCore.Test/Resources/sampleMKV.mkv
Normal file
BIN
FFMpegCore.Test/Resources/sampleMKV.mkv
Normal file
Binary file not shown.
|
@ -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!;
|
||||||
|
|
||||||
|
|
|
@ -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),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue