Merge pull request #351 from keg247/sample-aspect-ratio

Add SampleAspectRatio property to VideoStream

Former-commit-id: 9727ec5cfe
This commit is contained in:
Malte Rosenbjerg 2023-01-31 23:02:51 +01:00 committed by GitHub
commit b032af6405
4 changed files with 39 additions and 32 deletions

View file

@ -1,10 +1,10 @@
using System; using FFMpegCore.Test.Resources;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using FFMpegCore.Test.Resources;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace FFMpegCore.Test namespace FFMpegCore.Test
{ {
@ -24,7 +24,7 @@ public async Task Audio_FromStream_Duration()
public void FrameAnalysis_Sync() public void FrameAnalysis_Sync()
{ {
var frameAnalysis = FFProbe.GetFrames(TestResources.WebmVideo); var frameAnalysis = FFProbe.GetFrames(TestResources.WebmVideo);
Assert.AreEqual(90, frameAnalysis.Frames.Count); Assert.AreEqual(90, frameAnalysis.Frames.Count);
Assert.IsTrue(frameAnalysis.Frames.All(f => f.PixelFormat == "yuv420p")); 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.Height == 360));
@ -36,7 +36,7 @@ public void FrameAnalysis_Sync()
public async Task FrameAnalysis_Async() public async Task FrameAnalysis_Async()
{ {
var frameAnalysis = await FFProbe.GetFramesAsync(TestResources.WebmVideo); var frameAnalysis = await FFProbe.GetFramesAsync(TestResources.WebmVideo);
Assert.AreEqual(90, frameAnalysis.Frames.Count); Assert.AreEqual(90, frameAnalysis.Frames.Count);
Assert.IsTrue(frameAnalysis.Frames.All(f => f.PixelFormat == "yuv420p")); 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.Height == 360));
@ -55,12 +55,12 @@ public async Task PacketAnalysis_Async()
Assert.AreEqual(1362, packets.Last().Size); Assert.AreEqual(1362, packets.Last().Size);
} }
[TestMethod] [TestMethod]
public void PacketAnalysis_Sync() public void PacketAnalysis_Sync()
{ {
var packets = FFProbe.GetPackets(TestResources.WebmVideo).Packets; var packets = FFProbe.GetPackets(TestResources.WebmVideo).Packets;
Assert.AreEqual(96, packets.Count); Assert.AreEqual(96, packets.Count);
Assert.IsTrue(packets.All(f => f.CodecType == "video")); Assert.IsTrue(packets.All(f => f.CodecType == "video"));
Assert.AreEqual("K_", packets[0].Flags); Assert.AreEqual("K_", packets[0].Flags);
@ -74,9 +74,9 @@ public void PacketAnalysisAudioVideo_Sync()
Assert.AreEqual(216, packets.Count); Assert.AreEqual(216, packets.Count);
var actual = packets.Select(f => f.CodecType).Distinct().ToList(); var actual = packets.Select(f => f.CodecType).Distinct().ToList();
var expected = new List<string> {"audio", "video"}; var expected = new List<string> { "audio", "video" };
CollectionAssert.AreEquivalent(expected, actual); CollectionAssert.AreEquivalent(expected, actual);
Assert.IsTrue(packets.Where(t=>t.CodecType == "audio").All(f => f.Flags == "K_")); Assert.IsTrue(packets.Where(t => t.CodecType == "audio").All(f => f.Flags == "K_"));
Assert.AreEqual(75, packets.Count(t => t.CodecType == "video")); Assert.AreEqual(75, packets.Count(t => t.CodecType == "video"));
Assert.AreEqual(141, packets.Count(t => t.CodecType == "audio")); Assert.AreEqual(141, packets.Count(t => t.CodecType == "audio"));
} }
@ -105,13 +105,13 @@ public async Task Uri_Duration()
var fileAnalysis = await FFProbe.AnalyseAsync(new Uri("https://github.com/rosenbjerg/FFMpegCore/raw/master/FFMpegCore.Test/Resources/input_3sec.webm")); var fileAnalysis = await FFProbe.AnalyseAsync(new Uri("https://github.com/rosenbjerg/FFMpegCore/raw/master/FFMpegCore.Test/Resources/input_3sec.webm"));
Assert.IsNotNull(fileAnalysis); Assert.IsNotNull(fileAnalysis);
} }
[TestMethod] [TestMethod]
public void Probe_Success() public void Probe_Success()
{ {
var info = FFProbe.Analyse(TestResources.Mp4Video); var info = FFProbe.Analyse(TestResources.Mp4Video);
Assert.AreEqual(3, info.Duration.Seconds); Assert.AreEqual(3, info.Duration.Seconds);
Assert.AreEqual("5.1", info.PrimaryAudioStream!.ChannelLayout); Assert.AreEqual("5.1", info.PrimaryAudioStream!.ChannelLayout);
Assert.AreEqual(6, info.PrimaryAudioStream.Channels); Assert.AreEqual(6, info.PrimaryAudioStream.Channels);
Assert.AreEqual("AAC (Advanced Audio Coding)", info.PrimaryAudioStream.CodecLongName); Assert.AreEqual("AAC (Advanced Audio Coding)", info.PrimaryAudioStream.CodecLongName);
@ -121,10 +121,12 @@ public void Probe_Success()
Assert.AreEqual(48000, info.PrimaryAudioStream.SampleRateHz); Assert.AreEqual(48000, info.PrimaryAudioStream.SampleRateHz);
Assert.AreEqual("mp4a", info.PrimaryAudioStream.CodecTagString); Assert.AreEqual("mp4a", info.PrimaryAudioStream.CodecTagString);
Assert.AreEqual("0x6134706d", info.PrimaryAudioStream.CodecTag); Assert.AreEqual("0x6134706d", info.PrimaryAudioStream.CodecTag);
Assert.AreEqual(1471810, info.PrimaryVideoStream!.BitRate); Assert.AreEqual(1471810, info.PrimaryVideoStream!.BitRate);
Assert.AreEqual(16, info.PrimaryVideoStream.DisplayAspectRatio.Width); Assert.AreEqual(16, info.PrimaryVideoStream.DisplayAspectRatio.Width);
Assert.AreEqual(9, info.PrimaryVideoStream.DisplayAspectRatio.Height); Assert.AreEqual(9, info.PrimaryVideoStream.DisplayAspectRatio.Height);
Assert.AreEqual(1, info.PrimaryVideoStream.SampleAspectRatio.Width);
Assert.AreEqual(1, info.PrimaryVideoStream.SampleAspectRatio.Height);
Assert.AreEqual("yuv420p", info.PrimaryVideoStream.PixelFormat); Assert.AreEqual("yuv420p", info.PrimaryVideoStream.PixelFormat);
Assert.AreEqual(1280, info.PrimaryVideoStream.Width); Assert.AreEqual(1280, info.PrimaryVideoStream.Width);
Assert.AreEqual(720, info.PrimaryVideoStream.Height); Assert.AreEqual(720, info.PrimaryVideoStream.Height);
@ -137,7 +139,7 @@ public void Probe_Success()
Assert.AreEqual("avc1", info.PrimaryVideoStream.CodecTagString); Assert.AreEqual("avc1", info.PrimaryVideoStream.CodecTagString);
Assert.AreEqual("0x31637661", info.PrimaryVideoStream.CodecTag); Assert.AreEqual("0x31637661", info.PrimaryVideoStream.CodecTag);
} }
[TestMethod, Timeout(10000)] [TestMethod, Timeout(10000)]
public async Task Probe_Async_Success() public async Task Probe_Async_Success()
{ {

View file

@ -7,40 +7,40 @@ public class FFProbeAnalysis
{ {
[JsonPropertyName("streams")] [JsonPropertyName("streams")]
public List<FFProbeStream> Streams { get; set; } = null!; public List<FFProbeStream> Streams { get; set; } = null!;
[JsonPropertyName("format")] [JsonPropertyName("format")]
public Format Format { get; set; } = null!; public Format Format { get; set; } = null!;
[JsonIgnore] [JsonIgnore]
public IReadOnlyList<string> ErrorData { get; set; } = new List<string>(); public IReadOnlyList<string> ErrorData { get; set; } = new List<string>();
} }
public class FFProbeStream : ITagsContainer, IDispositionContainer public class FFProbeStream : ITagsContainer, IDispositionContainer
{ {
[JsonPropertyName("index")] [JsonPropertyName("index")]
public int Index { get; set; } public int Index { get; set; }
[JsonPropertyName("avg_frame_rate")] [JsonPropertyName("avg_frame_rate")]
public string AvgFrameRate { get; set; } = null!; public string AvgFrameRate { get; set; } = null!;
[JsonPropertyName("bits_per_raw_sample")] [JsonPropertyName("bits_per_raw_sample")]
public string BitsPerRawSample { get; set; } = null!; public string BitsPerRawSample { get; set; } = null!;
[JsonPropertyName("bit_rate")] [JsonPropertyName("bit_rate")]
public string BitRate { get; set; } = null!; public string BitRate { get; set; } = null!;
[JsonPropertyName("channels")] [JsonPropertyName("channels")]
public int? Channels { get; set; } public int? Channels { get; set; }
[JsonPropertyName("channel_layout")] [JsonPropertyName("channel_layout")]
public string ChannelLayout { get; set; } = null!; public string ChannelLayout { get; set; } = null!;
[JsonPropertyName("codec_type")] [JsonPropertyName("codec_type")]
public string CodecType { get; set; } = null!; public string CodecType { get; set; } = null!;
[JsonPropertyName("codec_name")] [JsonPropertyName("codec_name")]
public string CodecName { get; set; } = null!; public string CodecName { get; set; } = null!;
[JsonPropertyName("codec_long_name")] [JsonPropertyName("codec_long_name")]
public string CodecLongName { get; set; } = null!; public string CodecLongName { get; set; } = null!;
@ -53,6 +53,9 @@ public class FFProbeStream : ITagsContainer, IDispositionContainer
[JsonPropertyName("display_aspect_ratio")] [JsonPropertyName("display_aspect_ratio")]
public string DisplayAspectRatio { get; set; } = null!; public string DisplayAspectRatio { get; set; } = null!;
[JsonPropertyName("sample_aspect_ratio")]
public string SampleAspectRatio { get; set; } = null!;
[JsonPropertyName("duration")] [JsonPropertyName("duration")]
public string Duration { get; set; } = null!; public string Duration { get; set; } = null!;
@ -67,10 +70,10 @@ public class FFProbeStream : ITagsContainer, IDispositionContainer
[JsonPropertyName("r_frame_rate")] [JsonPropertyName("r_frame_rate")]
public string FrameRate { get; set; } = null!; public string FrameRate { get; set; } = null!;
[JsonPropertyName("pix_fmt")] [JsonPropertyName("pix_fmt")]
public string PixelFormat { get; set; } = null!; public string PixelFormat { get; set; } = null!;
[JsonPropertyName("sample_rate")] [JsonPropertyName("sample_rate")]
public string SampleRate { get; set; } = null!; public string SampleRate { get; set; } = null!;
@ -135,7 +138,7 @@ public static class TagExtensions
return tagValue; return tagValue;
return null; return null;
} }
public static string? GetLanguage(this ITagsContainer tagsContainer) => TryGetTagValue(tagsContainer, "language"); public static string? GetLanguage(this ITagsContainer tagsContainer) => TryGetTagValue(tagsContainer, "language");
public static string? GetCreationTime(this ITagsContainer tagsContainer) => TryGetTagValue(tagsContainer, "creation_time "); public static string? GetCreationTime(this ITagsContainer tagsContainer) => TryGetTagValue(tagsContainer, "creation_time ");
public static string? GetRotate(this ITagsContainer tagsContainer) => TryGetTagValue(tagsContainer, "rotate"); public static string? GetRotate(this ITagsContainer tagsContainer) => TryGetTagValue(tagsContainer, "rotate");

View file

@ -15,7 +15,7 @@ internal MediaAnalysis(FFProbeAnalysis analysis)
SubtitleStreams = analysis.Streams.Where(stream => stream.CodecType == "subtitle").Select(ParseSubtitleStream).ToList(); SubtitleStreams = analysis.Streams.Where(stream => stream.CodecType == "subtitle").Select(ParseSubtitleStream).ToList();
ErrorData = analysis.ErrorData; ErrorData = analysis.ErrorData;
} }
private MediaFormat ParseFormat(Format analysisFormat) private MediaFormat ParseFormat(Format analysisFormat)
{ {
return new MediaFormat return new MediaFormat
@ -38,7 +38,7 @@ private MediaFormat ParseFormat(Format analysisFormat)
}.Max(); }.Max();
public MediaFormat Format { get; } public MediaFormat Format { get; }
public AudioStream? PrimaryAudioStream => AudioStreams.OrderBy(stream => stream.Index).FirstOrDefault(); public AudioStream? PrimaryAudioStream => AudioStreams.OrderBy(stream => stream.Index).FirstOrDefault();
public VideoStream? PrimaryVideoStream => VideoStreams.OrderBy(stream => stream.Index).FirstOrDefault(); public VideoStream? PrimaryVideoStream => VideoStreams.OrderBy(stream => stream.Index).FirstOrDefault();
public SubtitleStream? PrimarySubtitleStream => SubtitleStreams.OrderBy(stream => stream.Index).FirstOrDefault(); public SubtitleStream? PrimarySubtitleStream => SubtitleStreams.OrderBy(stream => stream.Index).FirstOrDefault();
@ -47,7 +47,7 @@ private MediaFormat ParseFormat(Format analysisFormat)
public List<AudioStream> AudioStreams { get; } public List<AudioStream> AudioStreams { get; }
public List<SubtitleStream> SubtitleStreams { get; } public List<SubtitleStream> SubtitleStreams { get; }
public IReadOnlyList<string> ErrorData { get; } public IReadOnlyList<string> ErrorData { get; }
private VideoStream ParseVideoStream(FFProbeStream stream) private VideoStream ParseVideoStream(FFProbeStream stream)
{ {
return new VideoStream return new VideoStream
@ -61,6 +61,7 @@ private VideoStream ParseVideoStream(FFProbeStream stream)
CodecTag = stream.CodecTag, CodecTag = stream.CodecTag,
CodecTagString = stream.CodecTagString, CodecTagString = stream.CodecTagString,
DisplayAspectRatio = MediaAnalysisUtils.ParseRatioInt(stream.DisplayAspectRatio, ':'), DisplayAspectRatio = MediaAnalysisUtils.ParseRatioInt(stream.DisplayAspectRatio, ':'),
SampleAspectRatio = MediaAnalysisUtils.ParseRatioInt(stream.SampleAspectRatio, ':'),
Duration = MediaAnalysisUtils.ParseDuration(stream), Duration = MediaAnalysisUtils.ParseDuration(stream),
FrameRate = MediaAnalysisUtils.DivideRatio(MediaAnalysisUtils.ParseRatioDouble(stream.FrameRate, '/')), FrameRate = MediaAnalysisUtils.DivideRatio(MediaAnalysisUtils.ParseRatioDouble(stream.FrameRate, '/')),
Height = stream.Height ?? 0, Height = stream.Height ?? 0,
@ -141,11 +142,11 @@ public static double ParseDoubleInvariant(string line) =>
public static int ParseIntInvariant(string line) => public static int ParseIntInvariant(string line) =>
int.Parse(line, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture); int.Parse(line, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture);
public static long ParseLongInvariant(string line) => public static long ParseLongInvariant(string line) =>
long.Parse(line, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture); long.Parse(line, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture);
public static TimeSpan ParseDuration(string duration) public static TimeSpan ParseDuration(string duration)
{ {
if (!string.IsNullOrEmpty(duration)) if (!string.IsNullOrEmpty(duration))

View file

@ -7,6 +7,7 @@ public class VideoStream : MediaStream
public double AvgFrameRate { get; set; } public double AvgFrameRate { get; set; }
public int BitsPerRawSample { get; set; } public int BitsPerRawSample { get; set; }
public (int Width, int Height) DisplayAspectRatio { get; set; } public (int Width, int Height) DisplayAspectRatio { get; set; }
public (int Width, int Height) SampleAspectRatio { get; set; }
public string Profile { get; set; } = null!; public string Profile { get; set; } = null!;
public int Width { get; set; } public int Width { get; set; }
public int Height { get; set; } public int Height { get; set; }