Browse Source

Binary identical OBU read write round trip

pull/2633/head
Ynse Hoornenborg 2 years ago
parent
commit
138526611b
  1. 7
      src/ImageSharp/Formats/Heif/Av1/Av1BitStreamWriter.cs
  2. 58
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuReader.cs
  3. 16
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuWriter.cs
  4. 18
      tests/ImageSharp.Tests/Formats/Heif/Av1/ObuFrameHeaderTests.cs

7
src/ImageSharp/Formats/Heif/Av1/Av1BitStreamWriter.cs

@ -37,6 +37,13 @@ internal ref struct Av1BitStreamWriter
span[1] = (byte)((value >> 7) & 0xff);
return 2;
}
else if (value < 0x800000U)
{
span[0] = (byte)((value & 0x7fU) | 0x80U);
span[1] = (byte)((value >> 7) & 0xff);
span[2] = (byte)((value >> 14) & 0xff);
return 3;
}
else
{
throw new NotImplementedException("No such large values yet.");

58
src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuReader.cs

@ -247,14 +247,6 @@ internal class ObuReader
frameInfo.ModeInfoStride = Av1Math.AlignPowerOf2(sequenceHeader.MaxFrameWidth, Av1Constants.MaxSuperBlockSizeLog2) >> Av1Constants.ModeInfoSizeLog2;
}
private static bool IsValidObuType(ObuType type) => type switch
{
ObuType.SequenceHeader or ObuType.TemporalDelimiter or ObuType.FrameHeader or
ObuType.TileGroup or ObuType.Metadata or ObuType.Frame or ObuType.RedundantFrameHeader or
ObuType.TileList or ObuType.Padding => true,
_ => false,
};
/// <summary>
/// 5.5.1. General sequence header OBU syntax.
/// </summary>
@ -292,7 +284,7 @@ internal class ObuReader
sequenceHeader.TimingInfoPresentFlag = reader.ReadBoolean();
if (sequenceHeader.TimingInfoPresentFlag)
{
ReadTimingInfo(ref reader, sequenceHeader);
ReadTimingInfo(ref reader, sequenceHeader);
sequenceHeader.DecoderModelInfoPresentFlag = reader.ReadBoolean();
if (sequenceHeader.DecoderModelInfoPresentFlag)
{
@ -309,9 +301,11 @@ internal class ObuReader
sequenceHeader.OperatingPoint = new ObuOperatingPoint[operatingPointsCnt];
for (int i = 0; i < operatingPointsCnt; i++)
{
sequenceHeader.OperatingPoint[i] = new ObuOperatingPoint();
sequenceHeader.OperatingPoint[i].Idc = reader.ReadLiteral(12);
sequenceHeader.OperatingPoint[i].SequenceLevelIndex = (int)reader.ReadLiteral(5);
sequenceHeader.OperatingPoint[i] = new ObuOperatingPoint
{
Idc = reader.ReadLiteral(12),
SequenceLevelIndex = (int)reader.ReadLiteral(5)
};
if (sequenceHeader.OperatingPoint[i].SequenceLevelIndex > 7)
{
sequenceHeader.OperatingPoint[i].SequenceTier = (int)reader.ReadLiteral(1);
@ -638,36 +632,6 @@ internal class ObuReader
}
}
/// <summary>
/// 5.9.7. Frame size with refs syntax.
/// </summary>
private void ReadFrameSizeWithReferences(ref Av1BitStreamReader reader, bool frameSizeOverrideFlag)
{
ObuSequenceHeader sequenceHeader = this.SequenceHeader!;
ObuFrameHeader frameInfo = this.FrameHeader!;
bool foundReference = false;
for (int i = 0; i < Av1Constants.ReferencesPerFrame; i++)
{
foundReference = reader.ReadBoolean();
if (foundReference)
{
// Take values over from reference frame
break;
}
}
if (!foundReference)
{
this.ReadFrameSize(ref reader, frameSizeOverrideFlag);
this.ReadRenderSize(ref reader);
}
else
{
this.ReadSuperResolutionParameters(ref reader);
this.ComputeImageSize(sequenceHeader);
}
}
/// <summary>
/// 5.9.5. Frame size syntax.
/// </summary>
@ -1087,7 +1051,7 @@ internal class ObuReader
if (frameHeader.PrimaryReferenceFrame == Av1Constants.PrimaryReferenceFrameNone)
{
// InitConCoefficientCdfs();
SetupPastIndependence(frameHeader);
// SetupPastIndependence(frameHeader);
}
else
{
@ -1184,14 +1148,6 @@ internal class ObuReader
frameHeader.FilmGrainParameters = ReadFilmGrainFilterParameters(ref reader, sequenceHeader, frameHeader);
}
private static void SetupPastIndependence(ObuFrameHeader frameInfo)
{
_ = frameInfo;
// TODO: Implement.
// The current frame can be decoded without dependencies on previous coded frame.
}
private static bool IsSegmentationFeatureActive(ObuSegmentationParameters segmentationParameters, int segmentId, ObuSegmentationLevelFeature feature)
=> segmentationParameters.Enabled && segmentationParameters.IsFeatureActive(segmentId, feature);

16
src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuWriter.cs

@ -32,7 +32,7 @@ internal class ObuWriter
if (frameInfo != null && sequenceHeader != null)
{
this.WriteFrameHeader(ref writer, sequenceHeader, frameInfo, true);
this.WriteFrameHeader(ref writer, sequenceHeader, frameInfo, false);
if (frameInfo.TilesInfo != null)
{
WriteTileGroup(ref writer, frameInfo.TilesInfo, tileWriter);
@ -68,7 +68,7 @@ internal class ObuWriter
private static void WriteObuHeaderAndSize(Stream stream, ObuType type, Span<byte> payload)
{
stream.WriteByte(WriteObuHeader(type));
Span<byte> lengthBytes = stackalloc byte[2];
Span<byte> lengthBytes = stackalloc byte[3];
int lengthLength = Av1BitStreamWriter.GetLittleEndianBytes128((uint)payload.Length, lengthBytes);
stream.Write(lengthBytes, 0, lengthLength);
stream.Write(payload);
@ -416,6 +416,7 @@ internal class ObuWriter
if (frameHeader.DeltaQParameters.IsPresent)
{
writer.WriteLiteral((uint)frameHeader.DeltaQParameters.Resolution - 1, 2);
this.previousQIndex = new int[tileCount];
for (int tileIndex = 0; tileIndex < tileCount; tileIndex++)
{
this.previousQIndex[tileIndex] = frameHeader.QuantizationParameters.BaseQIndex;
@ -438,6 +439,7 @@ internal class ObuWriter
writer.WriteLiteral((uint)(1 + Av1Math.MostSignificantBit((uint)frameHeader.DeltaLoopFilterParameters.Resolution) - 1), 2);
writer.WriteBoolean(frameHeader.DeltaLoopFilterParameters.IsMulti);
int frameLoopFilterCount = sequenceHeader.ColorConfig.IsMonochrome ? Av1Constants.FrameLoopFilterCount - 2 : Av1Constants.FrameLoopFilterCount;
this.previousDeltaLoopFilter = new int[frameLoopFilterCount];
for (int loopFilterId = 0; loopFilterId < frameLoopFilterCount; loopFilterId++)
{
this.previousDeltaLoopFilter[loopFilterId] = 0;
@ -498,11 +500,14 @@ internal class ObuWriter
return headerBytes;
}
/// <summary>
/// 5.11.1. General tile group OBU syntax.
/// </summary>
private static int WriteTileGroup(ref Av1BitStreamWriter writer, ObuTileGroupHeader tileInfo, IAv1TileWriter tileWriter)
{
int tileCount = tileInfo.TileColumnCount * tileInfo.TileRowCount;
int startBitPosition = writer.BitPosition;
bool tileStartAndEndPresentFlag = tileCount != 0;
bool tileStartAndEndPresentFlag = tileCount > 1;
writer.WriteBoolean(tileStartAndEndPresentFlag);
uint tileGroupStart = 0U;
@ -623,6 +628,9 @@ internal class ObuWriter
writer.WriteBoolean(false);
}
/// <summary>
/// 5.9.11. Loop filter params syntax
/// </summary>
private static void WriteLoopFilterParameters(ref Av1BitStreamWriter writer, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameHeader)
{
if (frameHeader.CodedLossless || frameHeader.AllowIntraBlockCopy)
@ -750,7 +758,7 @@ internal class ObuWriter
private static void WriteFilmGrainFilterParameters(ref Av1BitStreamWriter writer, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameHeader)
{
ObuFilmGrainParameters grainParams = frameHeader.FilmGrainParameters;
if (!sequenceHeader.AreFilmGrainingParametersPresent && (!frameHeader.ShowFrame && !frameHeader.ShowableFrame))
if (!sequenceHeader.AreFilmGrainingParametersPresent || (!frameHeader.ShowFrame && !frameHeader.ShowableFrame))
{
return;
}

18
tests/ImageSharp.Tests/Formats/Heif/Av1/ObuFrameHeaderTests.cs

@ -12,9 +12,7 @@ public class ObuFrameHeaderTests
private static readonly byte[] DefaultSequenceHeaderBitStream =
[0x0a, 0x06, 0b001_1_1_000, 0b00_1000_01, 0b11_110101, 0b001_11101, 0b111_1_1_1_0_1, 0b1_0_0_1_1_1_10];
// TODO: Check with libgav1 test code.
private static readonly byte[] KeyFrameHeaderBitStream =
[0x32, 0x07, 0x10, 0x00];
private static readonly byte[] KeyFrameHeaderBitStream = [0x32, 0x06, 0x10, 0x00];
// Bits Syntax element Value
// 1 obu_forbidden_bit 0
@ -26,7 +24,7 @@ public class ObuFrameHeaderTests
private static readonly byte[] DefaultTemporalDelimiterBitStream = [0x12, 0x00];
[Theory]
// [InlineData(TestImages.Heif.IrvineAvif, 0x0102, 0x000d)]
// [InlineData(TestImages.Heif.IrvineAvif, 0x0198, 0x6bd1)]
[InlineData(TestImages.Heif.XnConvert, 0x010e, 0x03cc)]
[InlineData(TestImages.Heif.Orange4x4, 0x010e, 0x001d)]
@ -51,12 +49,10 @@ public class ObuFrameHeaderTests
Assert.Equal(reader.Length, blockSize);
}
/*
[Theory]
[InlineData(TestImages.Heif.Orange4x4, 0x010e, 0x001d, 0x0128)]
[InlineData(TestImages.Heif.XnConvert, 0x010e, 0x03cc, 0x0114)]
public void BinaryIdenticalRoundTripFrameHeader(string filename, int fileOffset, int blockSize, int tileOffset)
[InlineData(TestImages.Heif.Orange4x4, 0x010e, 0x001d)]
[InlineData(TestImages.Heif.XnConvert, 0x010e, 0x03cc)]
public void BinaryIdenticalRoundTripFrameHeader(string filename, int fileOffset, int blockSize)
{
// Assign
string filePath = Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, filename);
@ -74,13 +70,12 @@ public class ObuFrameHeaderTests
// Act 2
ObuWriter obuWriter = new();
obuWriter.WriteAll(encoded, obuReader.SequenceHeader, obuReader.FrameHeader, tileStub);
obuWriter.WriteAll(Configuration.Default, encoded, obuReader.SequenceHeader, obuReader.FrameHeader, tileStub);
// Assert
byte[] encodedArray = encoded.ToArray();
Assert.Equal(span, encodedArray);
}
*/
[Theory]
[InlineData(TestImages.Heif.Orange4x4, 0x010e, 0x001d)]
@ -323,5 +318,4 @@ public class ObuFrameHeaderTests
TileRowCount = 1,
}
};
}

Loading…
Cancel
Save