From 6e10cc99607416cf7f9eceead3ace34969ab4aa6 Mon Sep 17 00:00:00 2001 From: Ynse Hoornenborg Date: Sun, 21 Jul 2024 21:43:43 +0200 Subject: [PATCH] Few Obu unit tests taken over from libgav1 --- .../Heif/Av1/OpenBitstreamUnit/ObuReader.cs | 6 +- .../Formats/Heif/Av1/ObuFrameHeaderTests.cs | 198 +++++++++++++----- .../Formats/Heif/Av1/ObuPrettyPrint.cs | 70 +++++++ 3 files changed, 220 insertions(+), 54 deletions(-) create mode 100644 tests/ImageSharp.Tests/Formats/Heif/Av1/ObuPrettyPrint.cs diff --git a/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuReader.cs b/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuReader.cs index 71ad70f595..38507b28aa 100644 --- a/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuReader.cs +++ b/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuReader.cs @@ -169,7 +169,7 @@ internal class ObuReader { header.Size++; header.TemporalId = (int)reader.ReadLiteral(3); - header.SpatialId = (int)reader.ReadLiteral(3); + header.SpatialId = (int)reader.ReadLiteral(2); if (reader.ReadLiteral(3) != 0u) { throw new ImageFormatException("Reserved bits in header extension should be unset."); @@ -708,8 +708,8 @@ internal class ObuReader tileInfo.MaxTileWidthSuperblock = Av1Constants.MaxTileWidth >> superblockSizeLog2; tileInfo.MaxTileHeightSuperblock = (Av1Constants.MaxTileArea / Av1Constants.MaxTileWidth) >> superblockSizeLog2; tileInfo.MinLog2TileColumnCount = TileLog2(tileInfo.MaxTileWidthSuperblock, superblockColumnCount); - tileInfo.MaxLog2TileColumnCount = TileLog2(1, Math.Min(superblockColumnCount, Av1Constants.MaxTileColumnCount)); - tileInfo.MaxLog2TileRowCount = TileLog2(1, Math.Min(superblockRowCount, Av1Constants.MaxTileRowCount)); + tileInfo.MaxLog2TileColumnCount = (int)Av1Math.CeilLog2((uint)Math.Min(superblockColumnCount, Av1Constants.MaxTileColumnCount)); + tileInfo.MaxLog2TileRowCount = (int)Av1Math.CeilLog2((uint)Math.Min(superblockRowCount, Av1Constants.MaxTileRowCount)); tileInfo.MinLog2TileCount = Math.Max(tileInfo.MinLog2TileColumnCount, TileLog2(maxTileAreaOfSuperBlock, superblockColumnCount * superblockRowCount)); tileInfo.HasUniformTileSpacing = reader.ReadBoolean(); if (tileInfo.HasUniformTileSpacing) diff --git a/tests/ImageSharp.Tests/Formats/Heif/Av1/ObuFrameHeaderTests.cs b/tests/ImageSharp.Tests/Formats/Heif/Av1/ObuFrameHeaderTests.cs index 51533ba6fc..2d91f6f7f2 100644 --- a/tests/ImageSharp.Tests/Formats/Heif/Av1/ObuFrameHeaderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Heif/Av1/ObuFrameHeaderTests.cs @@ -12,6 +12,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Heif.Av1; [Trait("Format", "Avif")] public class ObuFrameHeaderTests { + private static readonly byte[] DefaultSequenceHeaderBitStream = + [0x0a, 0x0b, 0x00, 0x00, 0x00, 0x04, 0x3e, 0xa7, 0xbd, 0xf7, 0xf9, 0x80, 0x40]; + [Theory] // [InlineData(TestImages.Heif.IrvineAvif, 0x0102, 0x000D)] // [InlineData(TestImages.Heif.IrvineAvif, 0x0198, 0x6BD1)] @@ -96,68 +99,161 @@ public class ObuFrameHeaderTests obuReader2.Read(ref reader2, encodedBuffer.Length, tileDecoder2); // Assert - Assert.Equal(PrettyPrintProperties(obuReader1.SequenceHeader.ColorConfig), PrettyPrintProperties(obuReader2.SequenceHeader.ColorConfig)); - Assert.Equal(PrettyPrintProperties(obuReader1.SequenceHeader), PrettyPrintProperties(obuReader2.SequenceHeader)); - Assert.Equal(PrettyPrintProperties(obuReader1.FrameHeader), PrettyPrintProperties(obuReader2.FrameHeader)); - Assert.Equal(PrettyPrintProperties(obuReader1.FrameHeader.TilesInfo), PrettyPrintProperties(obuReader2.FrameHeader.TilesInfo)); + Assert.Equal(ObuPrettyPrint.PrettyPrintProperties(obuReader1.SequenceHeader.ColorConfig), ObuPrettyPrint.PrettyPrintProperties(obuReader2.SequenceHeader.ColorConfig)); + Assert.Equal(ObuPrettyPrint.PrettyPrintProperties(obuReader1.SequenceHeader), ObuPrettyPrint.PrettyPrintProperties(obuReader2.SequenceHeader)); + Assert.Equal(ObuPrettyPrint.PrettyPrintProperties(obuReader1.FrameHeader), ObuPrettyPrint.PrettyPrintProperties(obuReader2.FrameHeader)); + Assert.Equal(ObuPrettyPrint.PrettyPrintProperties(obuReader1.FrameHeader.TilesInfo), ObuPrettyPrint.PrettyPrintProperties(obuReader2.FrameHeader.TilesInfo)); } - private static readonly char[] spaces = " ".ToCharArray(); + [Fact] + public void DefaultTemporalDelimiter() + { + // Arrange + byte[] bitStream = [0x12, 0x00]; + Av1BitStreamReader reader = new(bitStream); + ObuReader obuReader = new(); + IAv1TileDecoder tileDecoder = new Av1TileDecoderStub(); + + // Act + obuReader.Read(ref reader, bitStream.Length, tileDecoder); - private static string PrettyPrintProperties(object obj, int indent = 0) + // Assert + Assert.Null(obuReader.SequenceHeader); + Assert.Null(obuReader.FrameHeader); + } + + [Fact] + public void DefaultTemporalDelimiterWithExtension() { - StringBuilder builder = new(); - builder.Append(obj.GetType().Name); - builder.AppendLine("{"); - indent += 2; - MemberInfo[] properties = obj.GetType().FindMembers(MemberTypes.Property, BindingFlags.Instance | BindingFlags.Public, null, null); - foreach (MemberInfo member in properties) - { - builder.Append(spaces, 0, indent); - if (member is PropertyInfo property) - { - builder.Append(property.Name); - builder.Append(" = "); - object value = property.GetValue(obj) ?? "NULL"; - PrettyPrintValue(builder, value, indent); - } - } + // Bits Syntax element Value + // 1 obu_forbidden_bit 0 + // 4 obu_type 2 (OBU_TEMPORAL_DELIMITER) + // 1 obu_extension_flag 1 + // 1 obu_has_size_field 1 + // 1 obu_reserved_1bit 0 + // 3 temporal_id 6 + // 2 spatial_id 2 + // 3 extension_header_reserved_3bits 0 + // 8 obu_size 0 + + // Arrange + byte[] bitStream = [0x16, 0xd0, 0x00]; + Av1BitStreamReader reader = new(bitStream); + ObuReader obuReader = new(); + IAv1TileDecoder tileDecoder = new Av1TileDecoderStub(); - indent -= 2; - builder.Append(spaces, 0, indent); - builder.AppendLine("}"); - return builder.ToString(); + // Act + obuReader.Read(ref reader, bitStream.Length, tileDecoder); + + // Assert + Assert.Null(obuReader.SequenceHeader); + Assert.Null(obuReader.FrameHeader); } - private static void PrettyPrintValue(StringBuilder builder, object value, int indent) + [Fact] + public void DefaultHeaderWithoutSizeField() { - if (value.GetType() == typeof(string)) - { - builder.AppendLine(value.ToString()); - } - else if (value.GetType().IsArray) + // Arrange + byte[] bitStream = [0x10]; + Av1BitStreamReader reader = new(bitStream); + ObuReader obuReader = new(); + IAv1TileDecoder tileDecoder = new Av1TileDecoderStub(); + + // Act + obuReader.Read(ref reader, bitStream.Length, tileDecoder); + + // Assert + Assert.Null(obuReader.SequenceHeader); + Assert.Null(obuReader.FrameHeader); + } + + [Fact] + public void DefaultSequenceHeader() + { + // Offset Bits Syntax element Value + // 0 3 seq_profile 0 + // 3 1 still_picture 0 + // 4 1 reduced_still_picture_header 0 + // 5 1 timing_info_present_flag 0 + // 6 1 initial_display_delay_present_flag 0 + // 7 5 operating_points_cnt_minus_1 0 + // 12 12 operating_point_idc[ 0 ] 0 + // 24 5 seq_level_idx[ 0 ] 0 + // 29 4 frame_width_bits_minus_1 8 + // 33 4 frame_height_bits_minus_1 7 + // 37 9 max_frame_width_minus_1 425 + // 46 8 max_frame_height_minus_1 239 + // 54 1 frame_id_numbers_present_flag 0 + // 55 1 use_128x128_superblock 1 + // 56 1 enable_filter_intra 1 + // 57 1 enable_intra_edge_filter 1 + // 58 1 enable_interintra_compound 1 + // 59 1 enable_masked_compound 1 + // 60 1 enable_warped_motion 0 + // 61 1 enable_dual_filter 1 + // 62 1 enable_order_hint 1 + // 63 1 enable_jnt_comp 1 + // 64 1 enable_ref_frame_mvs 1 + // 65 1 seq_choose_screen_content_tools 1 + // 66 1 seq_choose_integer_mv 1 + // 67 3 order_hint_bits_minus_1 6 + // 70 1 enable_superres 0 + // 71 1 enable_cdef 1 + // 72 1 enable_restoration 1 + // ... + + // Arrange + byte[] bitStream = DefaultSequenceHeaderBitStream; + Av1BitStreamReader reader = new(bitStream); + ObuReader obuReader = new(); + IAv1TileDecoder tileDecoder = new Av1TileDecoderStub(); + ObuSequenceHeader expected = new() { - builder.AppendLine("["); - indent += 2; - builder.Append(spaces, 0, indent); - Type elementType = value.GetType().GetElementType(); - IList list = value as IList; - foreach (object item in list) + SequenceProfile = 0, + IsStillPicture = false, + IsReducedStillPictureHeader = false, + TimingInfoPresentFlag = false, + InitialDisplayDelayPresentFlag = false, + FrameWidthBits = 8 + 1, + FrameHeightBits = 7 + 1, + MaxFrameWidth = 425 + 1, + MaxFrameHeight = 239 + 1, + IsFrameIdNumbersPresent = false, + Use128x128Superblock = true, + EnableFilterIntra = true, + EnableIntraEdgeFilter = true, + EnableInterIntraCompound = true, + EnableMaskedCompound = true, + EnableWarpedMotion = false, + EnableDualFilter = true, + EnableOrderHint = true, + OperatingPoint = [new()], + + // EnableJountCompound = true, + // EnableReferenceFrameMotionVectors = true, + ForceScreenContentTools = 2, + ForceIntegerMotionVector = 2, + EnableSuperResolution = false, + EnableCdef = true, + EnableRestoration = true, + ColorConfig = new() { - PrettyPrintValue(builder, item, indent); + IsMonochrome = false, + ColorPrimaries = ObuColorPrimaries.Unspecified, + TransferCharacteristics = ObuTransferCharacteristics.Unspecified, + MatrixCoefficients = ObuMatrixCoefficients.Unspecified, + SubSamplingX = true, + SubSamplingY = true, + BitDepth = 8, } + }; - indent -= 2; - builder.Append(spaces, 0, indent); - builder.AppendLine("]"); - } - else if (value.GetType().IsClass) - { - builder.AppendLine(PrettyPrintProperties(value, indent)); - } - else - { - builder.AppendLine(value.ToString()); - } + // Act + obuReader.Read(ref reader, bitStream.Length, tileDecoder); + + // Assert + Assert.NotNull(obuReader.SequenceHeader); + Assert.Null(obuReader.FrameHeader); + Assert.Equal(ObuPrettyPrint.PrettyPrintProperties(expected), ObuPrettyPrint.PrettyPrintProperties(obuReader.SequenceHeader)); } } diff --git a/tests/ImageSharp.Tests/Formats/Heif/Av1/ObuPrettyPrint.cs b/tests/ImageSharp.Tests/Formats/Heif/Av1/ObuPrettyPrint.cs new file mode 100644 index 0000000000..7730383146 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Heif/Av1/ObuPrettyPrint.cs @@ -0,0 +1,70 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Collections; +using System.Reflection; +using System.Text; + +namespace SixLabors.ImageSharp.Tests.Formats.Heif.Av1; + +internal class ObuPrettyPrint +{ + private static readonly char[] spaces = " ".ToCharArray(); + + public static string PrettyPrintProperties(object obj, int indent = 0) + { + StringBuilder builder = new(); + builder.Append(obj.GetType().Name); + builder.AppendLine("{"); + indent += 2; + MemberInfo[] properties = obj.GetType().FindMembers(MemberTypes.Property, BindingFlags.Instance | BindingFlags.Public, null, null); + foreach (MemberInfo member in properties) + { + builder.Append(spaces, 0, indent); + if (member is PropertyInfo property) + { + builder.Append(property.Name); + builder.Append(" = "); + object value = property.GetValue(obj) ?? "NULL"; + PrettyPrintValue(builder, value, indent); + } + } + + indent -= 2; + builder.Append(spaces, 0, indent); + builder.AppendLine("}"); + return builder.ToString(); + } + + private static void PrettyPrintValue(StringBuilder builder, object value, int indent) + { + if (value.GetType() == typeof(string)) + { + builder.AppendLine(value.ToString()); + } + else if (value.GetType().IsArray) + { + builder.AppendLine("["); + indent += 2; + builder.Append(spaces, 0, indent); + Type elementType = value.GetType().GetElementType(); + IList list = value as IList; + foreach (object item in list) + { + PrettyPrintValue(builder, item, indent); + } + + indent -= 2; + builder.Append(spaces, 0, indent); + builder.AppendLine("]"); + } + else if (value.GetType().IsClass) + { + builder.AppendLine(PrettyPrintProperties(value, indent)); + } + else + { + builder.AppendLine(value.ToString()); + } + } +}