diff --git a/FFMpegCore.Test/FFMpegCore.Test.csproj b/FFMpegCore.Test/FFMpegCore.Test.csproj index 8b99510..e63c0af 100644 --- a/FFMpegCore.Test/FFMpegCore.Test.csproj +++ b/FFMpegCore.Test/FFMpegCore.Test.csproj @@ -42,6 +42,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest diff --git a/FFMpegCore.Test/FFProbeTests.cs b/FFMpegCore.Test/FFProbeTests.cs index cc8f85b..9b86202 100644 --- a/FFMpegCore.Test/FFProbeTests.cs +++ b/FFMpegCore.Test/FFProbeTests.cs @@ -134,6 +134,16 @@ public void Probe_Success() Assert.AreEqual("0x31637661", info.PrimaryVideoStream.CodecTag); } + [TestMethod] + public void Probe_Rotation() + { + var info = FFProbe.Analyse(TestResources.Mp4Video); + Assert.AreEqual(0, info.PrimaryVideoStream.Rotation); + + info = FFProbe.Analyse(TestResources.Mp4VideoRotation); + Assert.AreEqual(90, info.PrimaryVideoStream.Rotation); + } + [TestMethod, Timeout(10000)] public async Task Probe_Async_Success() { diff --git a/FFMpegCore.Test/Resources/TestResources.cs b/FFMpegCore.Test/Resources/TestResources.cs index bf29960..de84080 100644 --- a/FFMpegCore.Test/Resources/TestResources.cs +++ b/FFMpegCore.Test/Resources/TestResources.cs @@ -13,6 +13,7 @@ public enum ImageType public static class TestResources { public static readonly string Mp4Video = "./Resources/input_3sec.mp4"; + public static readonly string Mp4VideoRotation = "./Resources/input_3sec_rotation_90deg.mp4"; public static readonly string WebmVideo = "./Resources/input_3sec.webm"; public static readonly string Mp4WithoutVideo = "./Resources/input_audio_only_10sec.mp4"; public static readonly string Mp4WithoutAudio = "./Resources/input_video_only_3sec.mp4"; diff --git a/FFMpegCore.Test/Resources/input_3sec_rotation_90deg.mp4 b/FFMpegCore.Test/Resources/input_3sec_rotation_90deg.mp4 new file mode 100644 index 0000000..c2a9ee2 Binary files /dev/null and b/FFMpegCore.Test/Resources/input_3sec_rotation_90deg.mp4 differ diff --git a/FFMpegCore/FFProbe/FFProbeAnalysis.cs b/FFMpegCore/FFProbe/FFProbeAnalysis.cs index ec58653..d65fc2d 100644 --- a/FFMpegCore/FFProbe/FFProbeAnalysis.cs +++ b/FFMpegCore/FFProbe/FFProbeAnalysis.cs @@ -1,4 +1,5 @@ -using System.Text.Json.Serialization; +using System.Text.Json.Nodes; +using System.Text.Json.Serialization; namespace FFMpegCore { @@ -84,6 +85,9 @@ public class FFProbeStream : ITagsContainer, IDispositionContainer [JsonPropertyName("tags")] public Dictionary Tags { get; set; } = null!; + + [JsonPropertyName("side_data_list")] + public List> SideData { get; set; } = null!; } public class Format : ITagsContainer diff --git a/FFMpegCore/FFProbe/MediaAnalysis.cs b/FFMpegCore/FFProbe/MediaAnalysis.cs index b6bc4a5..bfbc370 100644 --- a/FFMpegCore/FFProbe/MediaAnalysis.cs +++ b/FFMpegCore/FFProbe/MediaAnalysis.cs @@ -72,7 +72,7 @@ private VideoStream ParseVideoStream(FFProbeStream stream) Width = stream.Width ?? 0, Profile = stream.Profile, PixelFormat = stream.PixelFormat, - Rotation = (int)float.Parse(stream.GetRotate() ?? "0"), + Rotation = MediaAnalysisUtils.ParseRotation(stream), Language = stream.GetLanguage(), Disposition = MediaAnalysisUtils.FormatDisposition(stream.Disposition), Tags = stream.Tags.ToCaseInsensitive(), @@ -196,6 +196,20 @@ public static TimeSpan ParseDuration(FFProbeStream ffProbeStream) return ParseDuration(ffProbeStream.Duration); } + public static int ParseRotation(FFProbeStream fFProbeStream) + { + var displayMatrixSideData = fFProbeStream.SideData?.Find(item => item.TryGetValue("side_data_type", out var rawSideDataType) && rawSideDataType.ToString() == "Display Matrix"); + + if (displayMatrixSideData?.TryGetValue("rotation", out var rawRotation) ?? false) + { + return (int)float.Parse(rawRotation.ToString()); + } + else + { + return (int)float.Parse(fFProbeStream.GetRotate() ?? "0"); + } + } + public static Dictionary? FormatDisposition(Dictionary? disposition) { if (disposition == null)