Browse Source

Obu header decoding

pull/2633/head
Ynse Hoornenborg 2 years ago
parent
commit
47dc7746fc
  1. 8
      src/ImageSharp/Formats/Heif/Av1/Av1BitStreamReader.cs
  2. 28
      src/ImageSharp/Formats/Heif/Av1/Av1DecoderHandle.cs
  3. 4
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuConstraintDirectionalEnhancementFilterParameters.cs
  4. 2
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuFrameHeader.cs
  5. 2
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuLoopRestorationParameters.cs
  6. 321
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuReader.cs
  7. 32
      tests/ImageSharp.Tests/Formats/Heif/Av1/ObuFrameHeaderTests.cs

8
src/ImageSharp/Formats/Heif/Av1/Av1BitStreamReader.cs

@ -1,8 +1,6 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System;
using System.Buffers.Binary;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.Formats.Heif.Av1; namespace SixLabors.ImageSharp.Formats.Heif.Av1;
@ -50,7 +48,7 @@ internal ref struct Av1BitStreamReader
public uint ReadLiteral(int bitCount) public uint ReadLiteral(int bitCount)
{ {
DebugGuard.MustBeBetweenOrEqualTo(bitCount, 1, 32, nameof(bitCount)); DebugGuard.MustBeBetweenOrEqualTo(bitCount, 0, 32, nameof(bitCount));
uint bits = (this.currentWord << this.bitOffset) >> (WordSize - bitCount); uint bits = (this.currentWord << this.bitOffset) >> (WordSize - bitCount);
this.bitOffset += bitCount; this.bitOffset += bitCount;
@ -77,7 +75,7 @@ internal ref struct Av1BitStreamReader
public ulong ReadLittleEndianBytes128(out int length) public ulong ReadLittleEndianBytes128(out int length)
{ {
// See section 4.10.5 of the AV1-Specification // See section 4.10.5 of the AV1-Specification
DebugGuard.IsTrue((this.bitOffset & 0xF7) == 0, "Reading of Little Endian 128 value only allowed on byte alignment"); DebugGuard.IsTrue((this.bitOffset & 0x07) == 0, $"Reading of Little Endian 128 value only allowed on byte alignment (offset {this.BitPosition}).");
ulong value = 0; ulong value = 0;
length = 0; length = 0;
@ -122,7 +120,7 @@ internal ref struct Av1BitStreamReader
return 0; return 0;
} }
int w = (int)(Av1Math.MostSignificantBit(n) + 1); int w = (int)(Av1Math.FloorLog2(n) + 1);
uint m = (uint)((1 << w) - n); uint m = (uint)((1 << w) - n);
uint v = this.ReadLiteral(w - 1); uint v = this.ReadLiteral(w - 1);
if (v < m) if (v < m)

28
src/ImageSharp/Formats/Heif/Av1/Av1DecoderHandle.cs

@ -0,0 +1,28 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
namespace SixLabors.ImageSharp.Formats.Heif.Av1;
internal class Av1DecoderHandle
{
public Av1DecoderHandle()
{
this.FrameInfo = new ObuFrameHeader();
this.SequenceHeader = new ObuSequenceHeader();
this.TileInfo = new ObuTileInfo();
}
public bool SequenceHeaderDone { get; set; }
public bool ShowExistingFrame { get; set; }
public bool SeenFrameHeader { get; set; }
public ObuFrameHeader FrameInfo { get; }
public ObuSequenceHeader SequenceHeader { get; }
public ObuTileInfo TileInfo { get; }
}

4
src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuConstraintDirectionalEnhancementFilterParameters.cs

@ -7,9 +7,9 @@ internal class ObuConstraintDirectionalEnhancementFilterParameters
{ {
public int BitCount { get; internal set; } public int BitCount { get; internal set; }
public int[] YStrength { get; internal set; } = new int[4]; public int[] YStrength { get; internal set; } = new int[5];
public int[] UVStrength { get; internal set; } = new int[4]; public int[] UVStrength { get; internal set; } = new int[5];
public int Damping { get; internal set; } public int Damping { get; internal set; }
} }

2
src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuFrameHeader.cs

@ -77,8 +77,6 @@ internal class ObuFrameHeader
internal bool DisableCdfUpdate { get; set; } internal bool DisableCdfUpdate { get; set; }
internal bool ForeceIntegerMotionVector { get; set; }
internal uint CurrentFrameId { get; set; } internal uint CurrentFrameId { get; set; }
internal uint[] ReferenceFrameIndex { get; set; } = new uint[ObuConstants.ReferenceFrameCount]; internal uint[] ReferenceFrameIndex { get; set; } = new uint[ObuConstants.ReferenceFrameCount];

2
src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuLoopRestorationParameters.cs

@ -5,5 +5,5 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
internal class ObuLoopRestorationParameters internal class ObuLoopRestorationParameters
{ {
public ObuRestorationType FrameRestorationType { get; internal set; } public ObuRestorationType FrameRestorationType { get; internal set; } = ObuRestorationType.RestoreNone;
} }

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

@ -7,21 +7,117 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
internal class ObuReader internal class ObuReader
{ {
private static readonly int[] WienerTapsMid = new[] { 3, -7, 15 }; /// <summary>
private static readonly int[] SgrprojXqdMid = new[] { -32, 31 }; /// Decode all OBU's in a frame.
/// </summary>
public static void Read(ref Av1BitStreamReader reader, int dataSize, Av1DecoderHandle decoderHandle, bool isAnnexB)
{
bool frameDecodingFinished = false;
while (!frameDecodingFinished)
{
int lengthSize = 0;
int payloadSize = 0;
if (isAnnexB)
{
ReadObuSize(ref reader, out payloadSize, out lengthSize);
}
ObuHeader header = ReadObuHeaderSize(ref reader, out lengthSize);
if (isAnnexB)
{
header.PayloadSize -= header.Size;
dataSize -= lengthSize;
lengthSize = 0;
}
payloadSize = header.PayloadSize;
dataSize -= header.Size + lengthSize;
if (isAnnexB && dataSize < payloadSize)
{
throw new InvalidImageContentException("Corrupt frame");
}
switch (header.Type)
{
case ObuType.SequenceHeader:
ReadSequenceHeader(ref reader, decoderHandle.SequenceHeader);
if (decoderHandle.SequenceHeader.ColorConfig.BitDepth == 12)
{
// TODO: Initialize 12 bit predictors
}
decoderHandle.SequenceHeaderDone = true;
break;
case ObuType.FrameHeader:
case ObuType.RedundantFrameHeader:
case ObuType.Frame:
if (header.Type != ObuType.Frame)
{
decoderHandle.ShowExistingFrame = false;
}
else if (header.Type != ObuType.FrameHeader)
{
Guard.IsFalse(decoderHandle.SeenFrameHeader, nameof(Av1DecoderHandle.SeenFrameHeader), "Frame header expected");
}
else
{
Guard.IsTrue(decoderHandle.SeenFrameHeader, nameof(Av1DecoderHandle.SeenFrameHeader), "Already decoded a frame header");
}
if (!decoderHandle.SeenFrameHeader)
{
decoderHandle.SeenFrameHeader = true;
ReadFrameHeader(ref reader, decoderHandle.SequenceHeader, decoderHandle.FrameInfo, header, header.Type != ObuType.Frame);
}
if (header.Type != ObuType.Frame)
{
break; // For OBU_TILE_GROUP comes under OBU_FRAME
}
goto TILE_GROUP;
case ObuType.TileGroup:
TILE_GROUP:
if (!decoderHandle.SeenFrameHeader)
{
throw new InvalidImageContentException("Corrupt frame");
}
ReadTileGroup(ref reader, decoderHandle.SequenceHeader, decoderHandle.FrameInfo, decoderHandle.TileInfo, header, out frameDecodingFinished);
if (frameDecodingFinished)
{
decoderHandle.SeenFrameHeader = false;
}
private static ObuHeader ReadObuHeader(Av1BitStreamReader reader) break;
case ObuType.TemporalDelimiter:
default:
// Ignore unknown OBU types.
// throw new InvalidImageContentException($"Unknown OBU header found: {header.Type.ToString()}");
break;
}
dataSize -= payloadSize;
if (dataSize <= 0)
{
frameDecodingFinished = true;
}
}
}
private static ObuHeader ReadObuHeader(ref Av1BitStreamReader reader)
{ {
ObuHeader header = new(); ObuHeader header = new();
if (!reader.ReadBoolean()) if (reader.ReadBoolean())
{ {
throw new ImageFormatException("Forbidden bit in header should be unset."); throw new ImageFormatException("Forbidden bit in header should be unset.");
} }
header.Size = 1;
header.Type = (ObuType)reader.ReadLiteral(4); header.Type = (ObuType)reader.ReadLiteral(4);
header.HasExtension = reader.ReadBoolean(); header.HasExtension = reader.ReadBoolean();
header.HasSize = reader.ReadBoolean(); header.HasSize = reader.ReadBoolean();
if (!reader.ReadBoolean()) if (reader.ReadBoolean())
{ {
throw new ImageFormatException("Reserved bit in header should be unset."); throw new ImageFormatException("Reserved bit in header should be unset.");
} }
@ -45,9 +141,9 @@ internal class ObuReader
return header; return header;
} }
private static void ReadObuSize(Av1BitStreamReader reader, out int obuSize) private static void ReadObuSize(ref Av1BitStreamReader reader, out int obuSize, out int lengthSize)
{ {
ulong rawSize = reader.ReadLittleEndianBytes128(out _); ulong rawSize = reader.ReadLittleEndianBytes128(out lengthSize);
if (rawSize > uint.MaxValue) if (rawSize > uint.MaxValue)
{ {
throw new ImageFormatException("OBU block too large."); throw new ImageFormatException("OBU block too large.");
@ -59,12 +155,13 @@ internal class ObuReader
/// <summary> /// <summary>
/// Read OBU header and size. /// Read OBU header and size.
/// </summary> /// </summary>
private static ObuHeader ReadObuHeaderSize(Av1BitStreamReader reader) private static ObuHeader ReadObuHeaderSize(ref Av1BitStreamReader reader, out int lengthSize)
{ {
ObuHeader header = ReadObuHeader(reader); ObuHeader header = ReadObuHeader(ref reader);
lengthSize = 0;
if (header.HasSize) if (header.HasSize)
{ {
ReadObuSize(reader, out int payloadSize); ReadObuSize(ref reader, out int payloadSize, out lengthSize);
header.PayloadSize = payloadSize; header.PayloadSize = payloadSize;
} }
@ -75,17 +172,17 @@ internal class ObuReader
/// Check that the trailing bits start with a 1 and end with 0s. /// Check that the trailing bits start with a 1 and end with 0s.
/// </summary> /// </summary>
/// <remarks>Consumes a byte, if already byte aligned before the check.</remarks> /// <remarks>Consumes a byte, if already byte aligned before the check.</remarks>
private static void ReadTrailingBits(Av1BitStreamReader reader) private static void ReadTrailingBits(ref Av1BitStreamReader reader)
{ {
int bitsBeforeAlignment = 8 - (reader.BitPosition & 0x7); int bitsBeforeAlignment = 8 - (reader.BitPosition & 0x7);
uint trailing = reader.ReadLiteral(bitsBeforeAlignment); uint trailing = reader.ReadLiteral(bitsBeforeAlignment);
if (trailing != (1 << (bitsBeforeAlignment - 1))) if (trailing != (1U << (bitsBeforeAlignment - 1)))
{ {
throw new ImageFormatException("Trailing bits not properly formatted."); throw new ImageFormatException("Trailing bits not properly formatted.");
} }
} }
private static void AlignToByteBoundary(Av1BitStreamReader reader) private static void AlignToByteBoundary(ref Av1BitStreamReader reader)
{ {
while ((reader.BitPosition & 0x7) > 0) while ((reader.BitPosition & 0x7) > 0)
{ {
@ -111,9 +208,8 @@ internal class ObuReader
_ => false, _ => false,
}; };
private static ObuSequenceHeader ReadSequenceHeader(Av1BitStreamReader reader) private static void ReadSequenceHeader(ref Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader)
{ {
ObuSequenceHeader sequenceHeader = new();
sequenceHeader.SequenceProfile = (ObuSequenceProfile)reader.ReadLiteral(3); sequenceHeader.SequenceProfile = (ObuSequenceProfile)reader.ReadLiteral(3);
if (sequenceHeader.SequenceProfile > ObuConstants.MaxSequenceProfile) if (sequenceHeader.SequenceProfile > ObuConstants.MaxSequenceProfile)
{ {
@ -130,16 +226,19 @@ internal class ObuReader
sequenceHeader.TimingInfo = null; sequenceHeader.TimingInfo = null;
sequenceHeader.DecoderModelInfoPresentFlag = false; sequenceHeader.DecoderModelInfoPresentFlag = false;
sequenceHeader.InitialDisplayDelayPresentFlag = false; sequenceHeader.InitialDisplayDelayPresentFlag = false;
sequenceHeader.OperatingPoint[0].OperatorIndex = 0; sequenceHeader.OperatingPoint = new ObuOperatingPoint[1];
sequenceHeader.OperatingPoint[0].SequenceLevelIndex = (int)reader.ReadLiteral(ObuConstants.LevelBits); ObuOperatingPoint operatingPoint = new();
sequenceHeader.OperatingPoint[0] = operatingPoint;
operatingPoint.OperatorIndex = 0;
operatingPoint.SequenceLevelIndex = (int)reader.ReadLiteral(ObuConstants.LevelBits);
if (!IsValidSequenceLevel(sequenceHeader.OperatingPoint[0].SequenceLevelIndex)) if (!IsValidSequenceLevel(sequenceHeader.OperatingPoint[0].SequenceLevelIndex))
{ {
throw new ImageFormatException("Unknown sequnce level."); throw new ImageFormatException("Invalid sequence level.");
} }
sequenceHeader.OperatingPoint[0].SequenceTier = 0; operatingPoint.SequenceTier = 0;
sequenceHeader.OperatingPoint[0].IsDecoderModelPresent = false; operatingPoint.IsDecoderModelPresent = false;
sequenceHeader.OperatingPoint[0].IsInitialDisplayDelayPresent = false; operatingPoint.IsInitialDisplayDelayPresent = false;
// Video related flags removed // Video related flags removed
@ -172,18 +271,17 @@ internal class ObuReader
sequenceHeader.EnableSuperResolution = reader.ReadBoolean(); sequenceHeader.EnableSuperResolution = reader.ReadBoolean();
sequenceHeader.CdefLevel = (int)reader.ReadLiteral(1); sequenceHeader.CdefLevel = (int)reader.ReadLiteral(1);
sequenceHeader.EnableRestoration = reader.ReadBoolean(); sequenceHeader.EnableRestoration = reader.ReadBoolean();
sequenceHeader.ColorConfig = ReadColorConfig(reader, sequenceHeader); sequenceHeader.ColorConfig = ReadColorConfig(ref reader, sequenceHeader);
sequenceHeader.AreFilmGrainingParametersPresent = reader.ReadBoolean(); sequenceHeader.AreFilmGrainingParametersPresent = reader.ReadBoolean();
ReadTrailingBits(reader); ReadTrailingBits(ref reader);
return sequenceHeader;
} }
private static ObuColorConfig ReadColorConfig(Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader) private static ObuColorConfig ReadColorConfig(ref Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader)
{ {
ObuColorConfig colorConfig = new(); ObuColorConfig colorConfig = new();
ReadBitDepth(reader, colorConfig, sequenceHeader); ReadBitDepth(ref reader, colorConfig, sequenceHeader);
colorConfig.Monochrome = false; colorConfig.Monochrome = false;
if (sequenceHeader.SequenceProfile == ObuSequenceProfile.High) if (sequenceHeader.SequenceProfile != ObuSequenceProfile.High)
{ {
colorConfig.Monochrome = reader.ReadBoolean(); colorConfig.Monochrome = reader.ReadBoolean();
} }
@ -224,21 +322,33 @@ internal class ObuReader
else else
{ {
colorConfig.ColorRange = reader.ReadBoolean(); colorConfig.ColorRange = reader.ReadBoolean();
if (sequenceHeader.SequenceProfile != ObuSequenceProfile.Main) switch (sequenceHeader.SequenceProfile)
{ {
if (colorConfig.BitDepth == 12) case ObuSequenceProfile.Main:
{
colorConfig.SubSamplingX = reader.ReadBoolean();
if (colorConfig.SubSamplingX)
{
colorConfig.SubSamplingY = reader.ReadBoolean();
}
}
else
{
colorConfig.SubSamplingX = true; colorConfig.SubSamplingX = true;
colorConfig.SubSamplingY = true;
break;
case ObuSequenceProfile.High:
colorConfig.SubSamplingX = false;
colorConfig.SubSamplingY = false; colorConfig.SubSamplingY = false;
} break;
case ObuSequenceProfile.Professional:
default:
if (colorConfig.BitDepth == 12)
{
colorConfig.SubSamplingX = reader.ReadBoolean();
if (colorConfig.SubSamplingX)
{
colorConfig.SubSamplingY = reader.ReadBoolean();
}
}
else
{
colorConfig.SubSamplingX = true;
colorConfig.SubSamplingY = false;
}
break;
} }
if (colorConfig.SubSamplingX && colorConfig.SubSamplingY) if (colorConfig.SubSamplingX && colorConfig.SubSamplingY)
@ -251,7 +361,7 @@ internal class ObuReader
return colorConfig; return colorConfig;
} }
private static void ReadBitDepth(Av1BitStreamReader reader, ObuColorConfig colorConfig, ObuSequenceHeader sequenceHeader) private static void ReadBitDepth(ref Av1BitStreamReader reader, ObuColorConfig colorConfig, ObuSequenceHeader sequenceHeader)
{ {
bool hasHighBitDepth = reader.ReadBoolean(); bool hasHighBitDepth = reader.ReadBoolean();
if (sequenceHeader.SequenceProfile == ObuSequenceProfile.Professional && hasHighBitDepth) if (sequenceHeader.SequenceProfile == ObuSequenceProfile.Professional && hasHighBitDepth)
@ -264,7 +374,7 @@ internal class ObuReader
} }
} }
private static void ReadSuperResolutionParameters(Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo) private static void ReadSuperResolutionParameters(ref Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo)
{ {
bool useSuperResolution = false; bool useSuperResolution = false;
if (sequenceHeader.EnableSuperResolution) if (sequenceHeader.EnableSuperResolution)
@ -283,8 +393,9 @@ internal class ObuReader
frameInfo.FrameSize.SuperResolutionUpscaledWidth = frameInfo.FrameSize.FrameWidth; frameInfo.FrameSize.SuperResolutionUpscaledWidth = frameInfo.FrameSize.FrameWidth;
frameInfo.FrameSize.FrameWidth = frameInfo.FrameSize.FrameWidth =
(frameInfo.FrameSize.SuperResolutionUpscaledWidth * ObuConstants.ScaleNumerator) + ((frameInfo.FrameSize.SuperResolutionUpscaledWidth * ObuConstants.ScaleNumerator) +
(frameInfo.FrameSize.SuperResolutionDenominator / 2); (frameInfo.FrameSize.SuperResolutionDenominator / 2)) /
frameInfo.FrameSize.SuperResolutionDenominator;
if (frameInfo.FrameSize.SuperResolutionDenominator != ObuConstants.ScaleNumerator) if (frameInfo.FrameSize.SuperResolutionDenominator != ObuConstants.ScaleNumerator)
{ {
@ -293,7 +404,7 @@ internal class ObuReader
} }
} }
private static void ReadRenderSize(Av1BitStreamReader reader, ObuFrameHeader frameInfo) private static void ReadRenderSize(ref Av1BitStreamReader reader, ObuFrameHeader frameInfo)
{ {
bool renderSizeAndFrameSizeDifferent = reader.ReadBoolean(); bool renderSizeAndFrameSizeDifferent = reader.ReadBoolean();
if (renderSizeAndFrameSizeDifferent) if (renderSizeAndFrameSizeDifferent)
@ -308,7 +419,7 @@ internal class ObuReader
} }
} }
private static void ReadFrameSizeWithReferences(Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, bool frameSizeOverrideFlag) private static void ReadFrameSizeWithReferences(ref Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, bool frameSizeOverrideFlag)
{ {
bool foundReference = false; bool foundReference = false;
for (int i = 0; i < ObuConstants.ReferencesPerFrame; i++) for (int i = 0; i < ObuConstants.ReferencesPerFrame; i++)
@ -323,17 +434,17 @@ internal class ObuReader
if (!foundReference) if (!foundReference)
{ {
ReadFrameSize(reader, sequenceHeader, frameInfo, frameSizeOverrideFlag); ReadFrameSize(ref reader, sequenceHeader, frameInfo, frameSizeOverrideFlag);
ReadRenderSize(reader, frameInfo); ReadRenderSize(ref reader, frameInfo);
} }
else else
{ {
ReadSuperResolutionParameters(reader, sequenceHeader, frameInfo); ReadSuperResolutionParameters(ref reader, sequenceHeader, frameInfo);
ComputeImageSize(sequenceHeader, frameInfo); ComputeImageSize(sequenceHeader, frameInfo);
} }
} }
private static void ReadFrameSize(Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, bool frameSizeOverrideFlag) private static void ReadFrameSize(ref Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, bool frameSizeOverrideFlag)
{ {
if (frameSizeOverrideFlag) if (frameSizeOverrideFlag)
{ {
@ -346,11 +457,11 @@ internal class ObuReader
frameInfo.FrameSize.FrameHeight = sequenceHeader.MaxFrameHeight; frameInfo.FrameSize.FrameHeight = sequenceHeader.MaxFrameHeight;
} }
ReadSuperResolutionParameters(reader, sequenceHeader, frameInfo); ReadSuperResolutionParameters(ref reader, sequenceHeader, frameInfo);
ComputeImageSize(sequenceHeader, frameInfo); ComputeImageSize(sequenceHeader, frameInfo);
} }
private static ObuTileInfo ReadTileInfo(Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo) private static ObuTileInfo ReadTileInfo(ref Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo)
{ {
ObuTileInfo tileInfo = new(); ObuTileInfo tileInfo = new();
int superBlockColumnCount; int superBlockColumnCount;
@ -518,7 +629,7 @@ internal class ObuReader
return tileInfo; return tileInfo;
} }
private static void ReadUncompressedFrameHeader(Av1BitStreamReader reader, ObuHeader header, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, int planesCount) private static void ReadUncompressedFrameHeader(ref Av1BitStreamReader reader, ObuHeader header, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, int planesCount)
{ {
int idLength = 0; int idLength = 0;
uint previousFrameId = 0; uint previousFrameId = 0;
@ -562,7 +673,7 @@ internal class ObuReader
{ {
if (sequenceHeader.SequenceForceIntegerMotionVector == 1) if (sequenceHeader.SequenceForceIntegerMotionVector == 1)
{ {
frameInfo.ForeceIntegerMotionVector = reader.ReadBoolean(); frameInfo.ForceIntegerMotionVector = reader.ReadBoolean();
} }
else else
{ {
@ -571,12 +682,12 @@ internal class ObuReader
} }
else else
{ {
frameInfo.ForeceIntegerMotionVector = false; frameInfo.ForceIntegerMotionVector = false;
} }
if (isIntraFrame) if (isIntraFrame)
{ {
frameInfo.ForeceIntegerMotionVector = true; frameInfo.ForceIntegerMotionVector = true;
} }
bool havePreviousFrameId = !(frameInfo.FrameType == ObuFrameType.KeyFrame && frameInfo.ShowFrame); bool havePreviousFrameId = !(frameInfo.FrameType == ObuFrameType.KeyFrame && frameInfo.ShowFrame);
@ -635,7 +746,12 @@ internal class ObuReader
frameSizeOverrideFlag = reader.ReadBoolean(); frameSizeOverrideFlag = reader.ReadBoolean();
} }
frameInfo.OrderHint = reader.ReadLiteral(sequenceHeader.OrderHintInfo.OrderHintBits); frameInfo.OrderHint = 0;
if (sequenceHeader.OrderHintInfo.OrderHintBits > 0)
{
frameInfo.OrderHint = reader.ReadLiteral(sequenceHeader.OrderHintInfo.OrderHintBits);
}
if (isIntraFrame || frameInfo.ErrorResilientMode) if (isIntraFrame || frameInfo.ErrorResilientMode)
{ {
frameInfo.PrimaryReferenceFrame = ObuConstants.PrimaryReferenceFrameNone; frameInfo.PrimaryReferenceFrame = ObuConstants.PrimaryReferenceFrameNone;
@ -651,7 +767,7 @@ internal class ObuReader
frameInfo.AllowIntraBlockCopy = false; frameInfo.AllowIntraBlockCopy = false;
if (frameInfo.FrameType == ObuFrameType.SwitchFrame || (frameInfo.FrameType == ObuFrameType.KeyFrame && frameInfo.ShowFrame)) if (frameInfo.FrameType == ObuFrameType.SwitchFrame || (frameInfo.FrameType == ObuFrameType.KeyFrame && frameInfo.ShowFrame))
{ {
frameInfo.RefreshFrameFlags = 0xffU; frameInfo.RefreshFrameFlags = 0xFFU;
} }
else else
{ {
@ -680,8 +796,8 @@ internal class ObuReader
if (isIntraFrame) if (isIntraFrame)
{ {
ReadFrameSize(reader, sequenceHeader, frameInfo, frameSizeOverrideFlag); ReadFrameSize(ref reader, sequenceHeader, frameInfo, frameSizeOverrideFlag);
ReadRenderSize(reader, frameInfo); ReadRenderSize(ref reader, frameInfo);
if (frameInfo.AllowScreenContentTools && frameInfo.FrameSize.RenderWidth != 0) if (frameInfo.AllowScreenContentTools && frameInfo.FrameSize.RenderWidth != 0)
{ {
if (frameInfo.FrameSize.FrameWidth == frameInfo.FrameSize.SuperResolutionUpscaledWidth) if (frameInfo.FrameSize.FrameWidth == frameInfo.FrameSize.SuperResolutionUpscaledWidth)
@ -706,15 +822,15 @@ internal class ObuReader
if (frameInfo.PrimaryReferenceFrame == ObuConstants.PrimaryReferenceFrameNone) if (frameInfo.PrimaryReferenceFrame == ObuConstants.PrimaryReferenceFrameNone)
{ {
// SetupPastIndependence(); SetupPastIndependence(frameInfo);
} }
// GenerateNextReferenceFrameMap(sequenceHeader, frameInfo); // GenerateNextReferenceFrameMap(sequenceHeader, frameInfo);
frameInfo.TilesInfo = ReadTileInfo(reader, sequenceHeader, frameInfo); frameInfo.TilesInfo = ReadTileInfo(ref reader, sequenceHeader, frameInfo);
ReadQuantizationParameters(reader, frameInfo.QuantizationParameters, sequenceHeader.ColorConfig, planesCount); ReadQuantizationParameters(ref reader, frameInfo.QuantizationParameters, sequenceHeader.ColorConfig, planesCount);
ReadSegmentationParameters(reader, sequenceHeader, frameInfo, planesCount); ReadSegmentationParameters(ref reader, sequenceHeader, frameInfo, planesCount);
ReadFrameDeltaQParameters(reader, frameInfo); ReadFrameDeltaQParameters(ref reader, frameInfo);
ReadFrameDeltaLoopFilterParameters(reader, frameInfo); ReadFrameDeltaLoopFilterParameters(ref reader, frameInfo);
// SetupSegmentationDequantization(); // SetupSegmentationDequantization();
Av1MainParseContext mainParseContext = new(); Av1MainParseContext mainParseContext = new();
@ -758,21 +874,26 @@ internal class ObuReader
} }
frameInfo.AllLossless = frameInfo.CodedLossless && frameInfo.FrameSize.FrameWidth == frameInfo.FrameSize.SuperResolutionUpscaledWidth; frameInfo.AllLossless = frameInfo.CodedLossless && frameInfo.FrameSize.FrameWidth == frameInfo.FrameSize.SuperResolutionUpscaledWidth;
ReadLoopFilterParameters(reader, sequenceHeader, frameInfo, planesCount); ReadLoopFilterParameters(ref reader, sequenceHeader, frameInfo, planesCount);
ReadCdefParameters(reader, sequenceHeader, frameInfo, planesCount); ReadCdefParameters(ref reader, sequenceHeader, frameInfo, planesCount);
ReadLoopRestorationParameters(reader, sequenceHeader, frameInfo, planesCount); ReadLoopRestorationParameters(ref reader, sequenceHeader, frameInfo, planesCount);
ReadTransformMode(reader, frameInfo); ReadTransformMode(ref reader, frameInfo);
frameInfo.ReferenceMode = ReadFrameReferenceMode(reader, isIntraFrame); frameInfo.ReferenceMode = ReadFrameReferenceMode(ref reader, isIntraFrame);
ReadSkipModeParameters(reader, sequenceHeader, frameInfo, isIntraFrame, frameInfo.ReferenceMode); ReadSkipModeParameters(ref reader, sequenceHeader, frameInfo, isIntraFrame, frameInfo.ReferenceMode);
if (isIntraFrame || frameInfo.ErrorResilientMode || !sequenceHeader.EnableWarpedMotion) if (isIntraFrame || frameInfo.ErrorResilientMode || !sequenceHeader.EnableWarpedMotion)
{ {
frameInfo.AllowWarpedMotion = false; frameInfo.AllowWarpedMotion = false;
} }
frameInfo.ReducedTransformSet = reader.ReadBoolean(); frameInfo.ReducedTransformSet = reader.ReadBoolean();
ReadGlobalMotionParameters(reader, sequenceHeader, frameInfo, isIntraFrame); ReadGlobalMotionParameters(ref reader, sequenceHeader, frameInfo, isIntraFrame);
frameInfo.FilmGrainParameters = ReadFilmGrainFilterParameters(reader, sequenceHeader, frameInfo); frameInfo.FilmGrainParameters = ReadFilmGrainFilterParameters(ref reader, sequenceHeader, frameInfo);
}
private static void SetupPastIndependence(ObuFrameHeader frameInfo)
{
// TODO: Initialize the loop filter parameters.
} }
private static bool IsSegmentationFeatureActive(ObuSegmentationParameters segmentationParameters, int segmentId, ObuSegmentationLevelFeature feature) private static bool IsSegmentationFeatureActive(ObuSegmentationParameters segmentationParameters, int segmentId, ObuSegmentationLevelFeature feature)
@ -792,24 +913,24 @@ internal class ObuReader
} }
} }
private static void ReadFrameHeader(Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, ObuHeader header, bool trailingBit) private static void ReadFrameHeader(ref Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, ObuHeader header, bool trailingBit)
{ {
int planeCount = sequenceHeader.ColorConfig.Monochrome ? 1 : 3; int planeCount = sequenceHeader.ColorConfig.Monochrome ? 1 : 3;
int startBitPosition = reader.BitPosition; int startBitPosition = reader.BitPosition;
ReadUncompressedFrameHeader(reader, header, sequenceHeader, frameInfo, planeCount); ReadUncompressedFrameHeader(ref reader, header, sequenceHeader, frameInfo, planeCount);
if (trailingBit) if (trailingBit)
{ {
ReadTrailingBits(reader); ReadTrailingBits(ref reader);
} }
AlignToByteBoundary(reader); AlignToByteBoundary(ref reader);
int endPosition = reader.BitPosition; int endPosition = reader.BitPosition;
int headerBytes = (endPosition - startBitPosition) / 8; int headerBytes = (endPosition - startBitPosition) / 8;
header.PayloadSize -= headerBytes; header.PayloadSize -= headerBytes;
} }
private static void ReadTileGroup(Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, ObuTileInfo tileInfo, ObuHeader header, out bool isLastTileGroup) private static void ReadTileGroup(ref Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, ObuTileInfo tileInfo, ObuHeader header, out bool isLastTileGroup)
{ {
int tileCount = tileInfo.TileColumnCount * tileInfo.TileRowCount; int tileCount = tileInfo.TileColumnCount * tileInfo.TileRowCount;
int startBitPosition = reader.BitPosition; int startBitPosition = reader.BitPosition;
@ -834,7 +955,7 @@ internal class ObuReader
} }
isLastTileGroup = (tileGroupEnd + 1) == tileCount; isLastTileGroup = (tileGroupEnd + 1) == tileCount;
AlignToByteBoundary(reader); AlignToByteBoundary(ref reader);
int endBitPosition = reader.BitPosition; int endBitPosition = reader.BitPosition;
int headerBytes = (endBitPosition - startBitPosition) / 8; int headerBytes = (endBitPosition - startBitPosition) / 8;
header.PayloadSize -= headerBytes; header.PayloadSize -= headerBytes;
@ -874,7 +995,7 @@ internal class ObuReader
// FinishDecodeTiles(sequenceHeader, frameInfo, doCdef, doLoopRestoration); // FinishDecodeTiles(sequenceHeader, frameInfo, doCdef, doLoopRestoration);
} }
private static int ReadDeltaQ(Av1BitStreamReader reader) private static int ReadDeltaQ(ref Av1BitStreamReader reader)
{ {
int deltaQ = 0; int deltaQ = 0;
if (reader.ReadBoolean()) if (reader.ReadBoolean())
@ -885,7 +1006,7 @@ internal class ObuReader
return deltaQ; return deltaQ;
} }
private static void ReadFrameDeltaQParameters(Av1BitStreamReader reader, ObuFrameHeader frameInfo) private static void ReadFrameDeltaQParameters(ref Av1BitStreamReader reader, ObuFrameHeader frameInfo)
{ {
frameInfo.DeltaQParameters.Resolution = 0; frameInfo.DeltaQParameters.Resolution = 0;
frameInfo.DeltaQParameters.IsPresent = false; frameInfo.DeltaQParameters.IsPresent = false;
@ -900,7 +1021,7 @@ internal class ObuReader
} }
} }
private static void ReadFrameDeltaLoopFilterParameters(Av1BitStreamReader reader, ObuFrameHeader frameInfo) private static void ReadFrameDeltaLoopFilterParameters(ref Av1BitStreamReader reader, ObuFrameHeader frameInfo)
{ {
frameInfo.DeltaLoopFilterParameters.IsPresent = false; frameInfo.DeltaLoopFilterParameters.IsPresent = false;
frameInfo.DeltaLoopFilterParameters.Resolution = 0; frameInfo.DeltaLoopFilterParameters.Resolution = 0;
@ -920,20 +1041,20 @@ internal class ObuReader
} }
} }
private static void ReadQuantizationParameters(Av1BitStreamReader reader, ObuQuantizationParameters quantParams, ObuColorConfig colorInfo, int planesCount) private static void ReadQuantizationParameters(ref Av1BitStreamReader reader, ObuQuantizationParameters quantParams, ObuColorConfig colorInfo, int planesCount)
{ {
quantParams.BaseQIndex = (int)reader.ReadLiteral(8); quantParams.BaseQIndex = (int)reader.ReadLiteral(8);
quantParams.DeltaQDc[(int)Av1Plane.Y] = ReadDeltaQ(reader); quantParams.DeltaQDc[(int)Av1Plane.Y] = ReadDeltaQ(ref reader);
quantParams.DeltaQAc[(int)Av1Plane.Y] = 0; quantParams.DeltaQAc[(int)Av1Plane.Y] = 0;
if (planesCount > 1) if (planesCount > 1)
{ {
bool areUvDeltaDifferent = false; bool areUvDeltaDifferent = false;
quantParams.DeltaQDc[(int)Av1Plane.U] = ReadDeltaQ(reader); quantParams.DeltaQDc[(int)Av1Plane.U] = ReadDeltaQ(ref reader);
quantParams.DeltaQAc[(int)Av1Plane.U] = ReadDeltaQ(reader); quantParams.DeltaQAc[(int)Av1Plane.U] = ReadDeltaQ(ref reader);
if (areUvDeltaDifferent) if (areUvDeltaDifferent)
{ {
quantParams.DeltaQDc[(int)Av1Plane.V] = ReadDeltaQ(reader); quantParams.DeltaQDc[(int)Av1Plane.V] = ReadDeltaQ(ref reader);
quantParams.DeltaQAc[(int)Av1Plane.V] = ReadDeltaQ(reader); quantParams.DeltaQAc[(int)Av1Plane.V] = ReadDeltaQ(ref reader);
} }
else else
{ {
@ -971,7 +1092,7 @@ internal class ObuReader
} }
} }
private static void ReadSegmentationParameters(Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, int planesCount) private static void ReadSegmentationParameters(ref Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, int planesCount)
{ {
frameInfo.SegmentationParameters.SegmentationEnabled = reader.ReadBoolean(); frameInfo.SegmentationParameters.SegmentationEnabled = reader.ReadBoolean();
if (!frameInfo.SegmentationParameters.SegmentationEnabled) if (!frameInfo.SegmentationParameters.SegmentationEnabled)
@ -983,7 +1104,7 @@ internal class ObuReader
// TODO: Parse more stuff. // TODO: Parse more stuff.
} }
private static void ReadLoopFilterParameters(Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, int planesCount) private static void ReadLoopFilterParameters(ref Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, int planesCount)
{ {
if (frameInfo.CodedLossless || frameInfo.AllowIntraBlockCopy) if (frameInfo.CodedLossless || frameInfo.AllowIntraBlockCopy)
{ {
@ -995,7 +1116,7 @@ internal class ObuReader
// TODO: Parse more stuff. // TODO: Parse more stuff.
} }
private static void ReadTransformMode(Av1BitStreamReader reader, ObuFrameHeader frameInfo) private static void ReadTransformMode(ref Av1BitStreamReader reader, ObuFrameHeader frameInfo)
{ {
if (frameInfo.CodedLossless) if (frameInfo.CodedLossless)
{ {
@ -1014,21 +1135,21 @@ internal class ObuReader
} }
} }
private static void ReadLoopRestorationParameters(Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, int planesCount) private static void ReadLoopRestorationParameters(ref Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, int planesCount)
{ {
_ = planesCount; _ = planesCount;
if (frameInfo.CodedLossless || frameInfo.AllowIntraBlockCopy || !sequenceHeader.EnableRestoration) if (frameInfo.CodedLossless || frameInfo.AllowIntraBlockCopy || !sequenceHeader.EnableRestoration)
{ {
frameInfo.LoopRestorationParameters[0].FrameRestorationType = ObuRestorationType.RestoreNone; frameInfo.LoopRestorationParameters[0] = new ObuLoopRestorationParameters();
frameInfo.LoopRestorationParameters[1].FrameRestorationType = ObuRestorationType.RestoreNone; frameInfo.LoopRestorationParameters[1] = new ObuLoopRestorationParameters();
frameInfo.LoopRestorationParameters[2].FrameRestorationType = ObuRestorationType.RestoreNone; frameInfo.LoopRestorationParameters[2] = new ObuLoopRestorationParameters();
return; return;
} }
// TODO: Parse more stuff. // TODO: Parse more stuff.
} }
private static void ReadCdefParameters(Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, int planesCount) private static void ReadCdefParameters(ref Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, int planesCount)
{ {
_ = planesCount; _ = planesCount;
if (frameInfo.CodedLossless || frameInfo.AllowIntraBlockCopy || sequenceHeader.CdefLevel == 0) if (frameInfo.CodedLossless || frameInfo.AllowIntraBlockCopy || sequenceHeader.CdefLevel == 0)
@ -1045,7 +1166,7 @@ internal class ObuReader
// TODO: Parse more stuff. // TODO: Parse more stuff.
} }
private static void ReadGlobalMotionParameters(Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, bool isIntraFrame) private static void ReadGlobalMotionParameters(ref Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, bool isIntraFrame)
{ {
_ = reader; _ = reader;
_ = sequenceHeader; _ = sequenceHeader;
@ -1058,7 +1179,7 @@ internal class ObuReader
// TODO: Parse more stuff. // TODO: Parse more stuff.
} }
private static ObuReferenceMode ReadFrameReferenceMode(Av1BitStreamReader reader, bool isIntraFrame) private static ObuReferenceMode ReadFrameReferenceMode(ref Av1BitStreamReader reader, bool isIntraFrame)
{ {
if (isIntraFrame) if (isIntraFrame)
{ {
@ -1068,7 +1189,7 @@ internal class ObuReader
return (ObuReferenceMode)reader.ReadLiteral(1); return (ObuReferenceMode)reader.ReadLiteral(1);
} }
private static void ReadSkipModeParameters(Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, bool isIntraFrame, ObuReferenceMode referenceSelect) private static void ReadSkipModeParameters(ref Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, bool isIntraFrame, ObuReferenceMode referenceSelect)
{ {
if (isIntraFrame || referenceSelect == ObuReferenceMode.ReferenceModeSelect || !sequenceHeader.OrderHintInfo.EnableOrderHint) if (isIntraFrame || referenceSelect == ObuReferenceMode.ReferenceModeSelect || !sequenceHeader.OrderHintInfo.EnableOrderHint)
{ {
@ -1089,7 +1210,7 @@ internal class ObuReader
} }
} }
private static ObuFilmGrainParameters ReadFilmGrainFilterParameters(Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo) private static ObuFilmGrainParameters ReadFilmGrainFilterParameters(ref Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo)
{ {
ObuFilmGrainParameters grainParams = new(); ObuFilmGrainParameters grainParams = new();
if (!sequenceHeader.AreFilmGrainingParametersPresent || (!frameInfo.ShowFrame && !frameInfo.ShowableFrame)) if (!sequenceHeader.AreFilmGrainingParametersPresent || (!frameInfo.ShowFrame && !frameInfo.ShowableFrame))

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

@ -0,0 +1,32 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Heif.Av1;
using SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
namespace SixLabors.ImageSharp.Tests.Formats.Heif.Av1;
[Trait("Format", "Avif")]
public class ObuFrameHeaderTests
{
[Theory]
// [InlineData(TestImages.Heif.IrvineAvif, 0x0102, 0x000D, false)]
// [InlineData(TestImages.Heif.IrvineAvif, 0x0198, 0x6BD1, false)]
[InlineData(TestImages.Heif.XnConvert, 0x010E, 0x0017, false)]
public void ReadFrameHeader(string filename, int fileOffset, int blockSize, bool isAnnexB)
{
// Assign
string filePath = Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, filename);
byte[] content = File.ReadAllBytes(filePath);
Av1BitStreamReader reader = new(content.AsSpan(fileOffset));
Av1DecoderHandle decoder = new();
// Act
ObuReader.Read(ref reader, blockSize, decoder, isAnnexB);
// Assert
Assert.True(decoder.SequenceHeaderDone);
Assert.False(decoder.SeenFrameHeader);
}
}
Loading…
Cancel
Save