Browse Source

Initial av1 bitstream parsing

pull/2633/head
Ynse Hoornenborg 2 years ago
parent
commit
5dedc94d9f
  1. 150
      src/ImageSharp/Formats/Heif/Av1/Av1BitStreamReader.cs
  2. 34
      src/ImageSharp/Formats/Heif/Av1/Av1BlockSize.cs
  3. 12
      src/ImageSharp/Formats/Heif/Av1/Av1ColorFormat.cs
  4. 8
      src/ImageSharp/Formats/Heif/Av1/Av1MainParseContext.cs
  5. 108
      src/ImageSharp/Formats/Heif/Av1/Av1Math.cs
  6. 11
      src/ImageSharp/Formats/Heif/Av1/Av1Plane.cs
  7. 22
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuChromoSamplePosition.cs
  8. 52
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuColorConfig.cs
  9. 21
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuColorPrimaries.cs
  10. 82
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuConstants.cs
  11. 13
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuConstraintDirectionalEnhancementFilterParameters.cs
  12. 8
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuFilmGrainParameters.cs
  13. 81
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuFrameHeader.cs
  14. 19
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuFrameSize.cs
  15. 12
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuFrameType.cs
  16. 21
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuHeader.cs
  17. 9
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuLoopFilterParameters.cs
  18. 9
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuLoopRestorationParameters.cs
  19. 22
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuMatrixCoefficients.cs
  20. 13
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuMetadataType.cs
  21. 17
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuOperatingPoint.cs
  22. 13
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuOrderHintInfo.cs
  23. 19
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuQuantizationParameters.cs
  24. 888
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuReader.cs
  25. 10
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuReferenceMode.cs
  26. 9
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuRestorationType.cs
  27. 16
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuSegmentationLevelFeature.cs
  28. 15
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuSegmentationParameters.cs
  29. 71
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuSequenceHeader.cs
  30. 11
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuSequenceProfile.cs
  31. 39
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuTileInfo.cs
  32. 25
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuTransferCharacteristics.cs
  33. 18
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuType.cs

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

@ -0,0 +1,150 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1;
internal ref struct Av1BitStreamReader(Span<byte> data)
{
private const int WordSize = sizeof(byte);
private const int DoubleWordSize = 2 * WordSize;
private readonly Span<byte> data = data;
private int wordPosition = 0;
private int bitOffset = 0;
public readonly int BitPosition => (this.wordPosition * WordSize) + this.bitOffset;
public readonly int Length => this.data.Length;
public void Reset()
{
this.wordPosition = 0;
this.bitOffset = 0;
}
public void Skip(int bitCount)
{
this.bitOffset += bitCount;
while (this.bitOffset >= WordSize)
{
this.bitOffset -= WordSize;
this.wordPosition++;
}
}
public uint ReadLiteral(int bitCount)
{
uint bits = (uint)(this.data[this.wordPosition] << this.bitOffset) >> (WordSize - bitCount);
this.bitOffset += bitCount;
while (this.bitOffset > WordSize)
{
uint nextWord = this.data[this.wordPosition + 1];
bits |= nextWord << (DoubleWordSize - bitCount);
}
if (this.bitOffset >= WordSize)
{
this.bitOffset -= WordSize;
}
return bits;
}
internal bool ReadBoolean()
{
bool bit = (this.data[this.wordPosition] & (1 << (WordSize - this.bitOffset))) > 0;
this.Skip(1);
return bit;
}
public ulong ReadLittleEndianBytes128(out int length)
{
// See section 4.10.5 of the AV1-Specification
DebugGuard.IsTrue((this.bitOffset & (WordSize - 1)) == 0, "Reading of Little Endian 128 value only allowed on byte alignment");
ulong value = 0;
length = 0;
for (int i = 0; i < 56; i += 7)
{
uint leb128Byte = this.ReadLiteral(8);
value |= (leb128Byte & 0x7FUL) << i;
length++;
if ((leb128Byte & 0x80U) != 0x80U)
{
break;
}
}
return value;
}
public uint ReadUnsignedVariableLength()
{
// See section 4.10.3 of the AV1-Specification
int leadingZerosCount = 0;
while (leadingZerosCount < 32 && this.ReadLiteral(1) == 0U)
{
leadingZerosCount++;
}
if (leadingZerosCount == 32)
{
return uint.MaxValue;
}
uint basis = (1U << leadingZerosCount) - 1U;
uint value = this.ReadLiteral(leadingZerosCount);
return basis + value;
}
public uint ReadNonSymmetric(uint n)
{
// See section 4.10.7 of the AV1-Specification
if (n <= 1)
{
return 0;
}
int w = (int)(Av1Math.MostSignificantBit(n) + 1);
uint m = (uint)((1 << w) - n);
uint v = this.ReadLiteral(w - 1);
if (v < m)
{
return v;
}
return (v << 1) - m + this.ReadLiteral(1);
}
public int ReadSignedFromUnsigned(int n)
{
// See section 4.10.6 of the AV1-Specification
int signedValue;
uint value = this.ReadLiteral(n);
uint signMask = 1U << (n - 1);
if ((value & signMask) == signMask)
{
// Prevent overflow by casting to long;
signedValue = (int)((long)value - (signMask << 1));
}
else
{
signedValue = (int)value;
}
return signedValue;
}
public uint ReadLittleEndian(int n)
{
// See section 4.10.4 of the AV1-Specification
DebugGuard.IsTrue((this.bitOffset & (WordSize - 1)) == 0, "Reading of Little Endian value only allowed on byte alignment");
uint t = 0;
for (int i = 0; i < 8 * n; i += 8)
{
t += this.ReadLiteral(8) << i;
}
return t;
}
}

34
src/ImageSharp/Formats/Heif/Av1/Av1BlockSize.cs

@ -0,0 +1,34 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1;
internal enum Av1BlockSize
{
Block4x4,
Block4x8,
Block8x4,
Block8x8,
Block8x16,
Block16x8,
Block16x16,
Block16x32,
Block32x16,
Block32x32,
Block32x64,
Block64x32,
Block64x64,
Block64x128,
Block128x64,
Block128x128,
Block4x16,
Block16x4,
Block8x32,
Block32x8,
Block16x64,
Block64x16,
BlockSizeSAll,
BlockSizeS = Block4x16,
BlockInvalid = 255,
BlockLargest = BlockSizeS - 1,
}

12
src/ImageSharp/Formats/Heif/Av1/Av1ColorFormat.cs

@ -0,0 +1,12 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1;
internal enum Av1ColorFormat
{
Yuv400,
Yuv420,
Yuv422,
Yuv444,
}

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

@ -0,0 +1,8 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1;
internal class Av1MainParseContext
{
}

108
src/ImageSharp/Formats/Heif/Av1/Av1Math.cs

@ -0,0 +1,108 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1;
internal static class Av1Math
{
public static uint MostSignificantBit(uint value) => value >> 31;
public static uint Log2(uint n)
{
uint result = 0U;
while ((n >>= 1) > 0)
{
result++;
}
return result;
}
public static int Log2(int n)
{
int result = 0;
while ((n >>= 1) > 0)
{
result++;
}
return result;
}
public static uint FloorLog2(uint value)
{
uint s = 0;
while (value != 0U)
{
value >>= 1;
s++;
}
return s - 1;
}
public static uint CeilLog2(uint value)
{
if (value < 2)
{
return 0;
}
uint i = 1;
uint p = 2;
while (p < value)
{
i++;
p <<= 1;
}
return i;
}
public static uint Clip1(uint value, int bitDepth) =>
Clip3(0, (1U << bitDepth) - 1, value);
public static uint Clip3(uint x, uint y, uint z)
{
if (z < x)
{
return x;
}
if (z > y)
{
return y;
}
return z;
}
public static uint Round2(uint value, int n)
{
if (n == 0)
{
return value;
}
return (uint)((value + (1 << (n - 1))) >> n);
}
public static int Round2(int value, int n)
{
if (value < 0)
{
value = -value;
}
return (int)Round2((uint)value, n);
}
internal static int AlignPowerOf2(int value, int n)
{
int mask = (1 << n) - 1;
return (value + mask) & ~mask;
}
internal static int Clamp(int value, int low, int high)
=> value < low ? low : (value > high ? high : value);
}

11
src/ImageSharp/Formats/Heif/Av1/Av1Plane.cs

@ -0,0 +1,11 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1;
internal enum Av1Plane : int
{
Y = 0,
U = 1,
V = 2,
}

22
src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuChromoSamplePosition.cs

@ -0,0 +1,22 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
internal enum ObuChromoSamplePosition : byte
{
/// <summary>
/// Unknown.
/// </summary>
Unknown = 0,
/// <summary>
/// Horizontally co-located with luma(0, 0) sample, between two vertical samples.
/// </summary>
Vertical = 1,
/// <summary>
/// Co-located with luma(0, 0) sample
/// </summary>
Colocated = 2,
}

52
src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuColorConfig.cs

@ -0,0 +1,52 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
internal class ObuColorConfig
{
internal bool IsColorDescriptionPresent { get; set; }
internal int ChannelCount { get; set; }
internal bool Monochrome { get; set; }
internal ObuColorPrimaries ColorPrimaries { get; set; }
internal ObuTransferCharacteristics TransferCharacteristics { get; set; }
internal ObuMatrixCoefficients MatrixCoefficients { get; set; }
internal bool ColorRange { get; set; }
internal bool SubSamplingX { get; set; }
internal bool SubSamplingY { get; set; }
internal bool HasSeparateUvDelta { get; set; }
internal ObuChromoSamplePosition ChromaSamplePosition { get; set; }
internal int BitDepth { get; set; }
internal bool HasSeparateUvDeltaQ { get; set; }
public Av1ColorFormat GetColorFormat()
{
Av1ColorFormat format = Av1ColorFormat.Yuv400;
if (this.SubSamplingX && this.SubSamplingY)
{
format = Av1ColorFormat.Yuv420;
}
else if (this.SubSamplingX & !this.SubSamplingY)
{
format = Av1ColorFormat.Yuv422;
}
else if (!this.SubSamplingX && !this.SubSamplingY)
{
format = Av1ColorFormat.Yuv444;
}
return format;
}
}

21
src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuColorPrimaries.cs

@ -0,0 +1,21 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
internal enum ObuColorPrimaries
{
None = 0,
Bt709 = 1,
Unspecified = 2,
Bt470M = 4,
Bt470BG = 5,
Bt601 = 6,
Smpte240 = 7,
GenericFilm = 8,
Bt2020 = 9,
Xyz = 10,
Smpte431 = 11,
Smpte432 = 12,
Ebu3213 = 22,
}

82
src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuConstants.cs

@ -0,0 +1,82 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
internal static class ObuConstants
{
public const ObuSequenceProfile MaxSequenceProfile = ObuSequenceProfile.Professional;
public const int LevelBits = -1;
/// <summary>
/// Number of fractional bits for computing position in upscaling.
/// </summary>
public const int SuperResolutionScaleBits = 14;
public const int ScaleNumerator = -1;
/// <summary>
/// Number of reference frames that can be used for inter prediction.
/// </summary>
public const int ReferencesPerFrame = 7;
/// <summary>
/// Maximum area of a tile in units of luma samples.
/// </summary>
public const int MaxTileArea = 4096 * 2304;
/// <summary>
/// Maximum width of a tile in units of luma samples.
/// </summary>
public const int MaxTileWidth = 4096;
/// <summary>
/// Maximum number of tile columns.
/// </summary>
public const int MaxTileColumnCount = 64;
/// <summary>
/// Maximum number of tile rows.
/// </summary>
public const int MaxTileRowCount = 64;
/// <summary>
/// Number of frames that can be stored for future reference.
/// </summary>
public const int ReferenceFrameCount = 8;
/// <summary>
/// Value of 'PrimaryReferenceFrame' indicating that there is no primary reference frame.
/// </summary>
public const uint PrimaryReferenceFrameNone = 7;
public const int PimaryReferenceBits = -1;
/// <summary>
/// Number of segments allowed in segmentation map.
/// </summary>
public const int MaxSegmentCount = 8;
/// <summary>
/// Smallest denominator for upscaling ratio.
/// </summary>
public const int SuperResolutionScaleDenominatorMinimum = 9;
/// <summary>
/// Base 2 logarithm of maximum size of a superblock in luma samples.
/// </summary>
public const int MaxSuperBlockSizeLog2 = 7;
/// <summary>
/// Base 2 logarithm of smallest size of a mode info block.
/// </summary>
public const int ModeInfoSizeLog2 = 2;
public const int MaxQ = 255;
/// <summary>
/// Number of segmentation features.
/// </summary>
public const int SegmentationLevelMax = 8;
}

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

@ -0,0 +1,13 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
internal class ObuConstraintDirectionalEnhancementFilterParameters
{
public bool BitCount { get; internal set; }
public int[] YStrength { get; internal set; }
public int[] UVStrength { get; internal set; }
}

8
src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuFilmGrainParameters.cs

@ -0,0 +1,8 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
internal class ObuFilmGrainParameters
{
}

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

@ -0,0 +1,81 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
internal class ObuFrameHeader
{
public bool ForceIntegerMotionVector { get; set; }
public bool AllowIntraBlockCopy { get; set; }
public bool UseReferenceFrameMotionVectors { get; set; }
public bool AllowHighPrecisionMotionVector { get; set; }
public ObuTileInfo TilesInfo { get; internal set; } = new ObuTileInfo();
public bool CodedLossless { get; internal set; }
public bool[] LosslessArray { get; internal set; } = new bool[ObuConstants.MaxSegmentCount];
public ObuQuantizationParameters QuantizationParameters { get; set; } = new ObuQuantizationParameters();
public ObuSegmentationParameters SegmentationParameters { get; set; } = new ObuSegmentationParameters();
public bool AllLossless { get; internal set; }
public bool AllowWarpedMotion { get; internal set; }
public ObuReferenceMode ReferenceMode { get; internal set; }
public ObuFilmGrainParameters FilmGrainParameters { get; internal set; } = new ObuFilmGrainParameters();
public bool ReducedTxSet { get; internal set; }
public ObuLoopFilterParameters LoopFilterParameters { get; internal set; } = new ObuLoopFilterParameters();
public ObuLoopRestorationParameters[] LoopRestorationParameters { get; internal set; } = new ObuLoopRestorationParameters[3];
public ObuConstraintDirectionalEnhancementFilterParameters ConstraintDirectionalEnhancementFilterParameters { get; internal set; } = new ObuConstraintDirectionalEnhancementFilterParameters();
public int ModeInfoStride { get; internal set; }
public bool DisableFrameEndUpdateCdf { get; internal set; }
internal ObuFrameSize FrameSize { get; set; } = new ObuFrameSize();
internal int ModeInfoColumnCount { get; set; }
internal int ModeInfoRowCount { get; set; }
internal bool ShowExistingFrame { get; set; }
internal ObuFrameType FrameType { get; set; }
internal bool[] ReferenceValid { get; set; } = new bool[ObuConstants.ReferenceFrameCount];
internal bool[] ReferenceOrderHint { get; set; } = new bool[ObuConstants.ReferenceFrameCount];
internal bool ShowFrame { get; set; }
internal bool ShowableFrame { get; set; }
internal bool ErrorResilientMode { get; set; }
internal bool AllowScreenContentTools { get; set; }
internal bool DisableCdfUpdate { get; set; }
internal bool ForeceIntegerMotionVector { get; set; }
internal uint CurrentFrameId { get; set; }
internal uint[] ReferenceFrameIndex { get; set; } = new uint[ObuConstants.ReferenceFrameCount];
internal uint OrderHint { get; set; }
internal uint PrimaryReferenceFrame { get; set; } = ObuConstants.PrimaryReferenceFrameNone;
internal uint RefreshFrameFlags { get; set; }
}

19
src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuFrameSize.cs

@ -0,0 +1,19 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
internal class ObuFrameSize
{
internal int FrameWidth { get; set; }
internal int FrameHeight { get; set; }
internal int SuperResolutionDenominator { get; set; }
internal int SuperResolutionUpscaledWidth { get; set; }
internal int RenderWidth { get; set; }
internal int RenderHeight { get; set; }
}

12
src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuFrameType.cs

@ -0,0 +1,12 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
internal enum ObuFrameType
{
KeyFrame = 0,
InterFrame = 1,
IntraOnlyFrame = 2,
SwitchFrame = 3,
}

21
src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuHeader.cs

@ -0,0 +1,21 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
internal class ObuHeader
{
public int Size { get; set; }
public ObuType Type { get; set; }
public bool HasSize { get; set; }
public bool HasExtension { get; set; }
public int TemporalId { get; set; }
public int SpatialId { get; set; }
public int PayloadSize { get; set; }
}

9
src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuLoopFilterParameters.cs

@ -0,0 +1,9 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
internal class ObuLoopFilterParameters
{
public bool[] FilterLevel { get; internal set; }
}

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

@ -0,0 +1,9 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
internal class ObuLoopRestorationParameters
{
public ObuRestorationType FrameRestorationType { get; internal set; }
}

22
src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuMatrixCoefficients.cs

@ -0,0 +1,22 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
internal enum ObuMatrixCoefficients
{
Identity = 0,
Bt407 = 1,
Unspecified = 2,
Fcc = 4,
Bt470BG = 5,
Bt601 = 6,
Smpte240 = 7,
SmpteYCgCo = 8,
Bt2020NonConstantLuminance = 9,
Bt2020ConstantLuminance = 10,
Smpte2085 = 11,
ChromaticityDerivedNonConstantLuminance = 12,
ChromaticityDerivedConstandLuminance = 13,
Bt2100ICtCp = 14,
}

13
src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuMetadataType.cs

@ -0,0 +1,13 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
internal enum ObuMetadataType
{
ItutT35,
HdrCll,
HdrMdcv,
Scalability,
Timecode
}

17
src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuOperatingPoint.cs

@ -0,0 +1,17 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
internal class ObuOperatingPoint
{
internal int OperatorIndex { get; set; }
internal int SequenceLevelIndex { get; set; }
internal int SequenceTier { get; set; }
internal bool IsDecoderModelPresent { get; set; }
internal bool IsInitialDisplayDelayPresent { get; set; }
}

13
src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuOrderHintInfo.cs

@ -0,0 +1,13 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
internal class ObuOrderHintInfo
{
internal bool EnableJointCompound { get; set; }
internal bool EnableReferenceFrameMotionVectors { get; set; }
internal int OrderHintBits { get; set; }
}

19
src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuQuantizationParameters.cs

@ -0,0 +1,19 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
internal class ObuQuantizationParameters
{
public int BaseQIndex { get; set; }
public int[] QIndex { get; set; } = new int[ObuConstants.MaxSegmentCount];
public bool IsUsingQMatrix { get; internal set; }
public int[] DeltaQDc { get; internal set; } = new int[3];
public int[] DeltaQAc { get; internal set; } = new int[3];
public int[] QMatrix { get; internal set; } = new int[3];
}

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

@ -0,0 +1,888 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
internal class ObuReader
{
private static readonly int[] WienerTapsMid = new[] { 3, -7, 15 };
private static readonly int[] SgrprojXqdMid = new[] { -32, 31 };
private static ObuHeader ReadObuHeader(Av1BitStreamReader reader)
{
ObuHeader header = new();
if (!reader.ReadBoolean())
{
throw new ImageFormatException("Forbidden bit in header should be unset.");
}
header.Type = (ObuType)reader.ReadLiteral(4);
header.HasExtension = reader.ReadBoolean();
header.HasSize = reader.ReadBoolean();
if (!reader.ReadBoolean())
{
throw new ImageFormatException("Reserved bit in header should be unset.");
}
if (header.HasExtension)
{
header.Size++;
header.TemporalId = (int)reader.ReadLiteral(3);
header.SpatialId = (int)reader.ReadLiteral(3);
if (reader.ReadLiteral(3) != 0u)
{
throw new ImageFormatException("Reserved bits in header extension should be unset.");
}
}
else
{
header.SpatialId = 0;
header.TemporalId = 0;
}
return header;
}
private static void ReadObuSize(Av1BitStreamReader reader, out int obuSize)
{
ulong rawSize = reader.ReadLittleEndianBytes128(out _);
if (rawSize > uint.MaxValue)
{
throw new ImageFormatException("OBU block too large.");
}
obuSize = (int)rawSize;
}
/// <summary>
/// Read OBU header and size.
/// </summary>
private static ObuHeader ReadObuHeaderSize(Av1BitStreamReader reader)
{
ObuHeader header = ReadObuHeader(reader);
if (header.HasSize)
{
ReadObuSize(reader, out int payloadSize);
header.PayloadSize = payloadSize;
}
return header;
}
/// <summary>
/// Check that the trailing bits start with a 1 and end with 0s.
/// </summary>
/// <remarks>Consumes a byte, if already byte aligned before the check.</remarks>
private static void ReadTrailingBits(Av1BitStreamReader reader)
{
int bitsBeforeAlignment = 8 - (reader.BitPosition & 0x7);
uint trailing = reader.ReadLiteral(bitsBeforeAlignment);
if (trailing != (1 << (bitsBeforeAlignment - 1)))
{
throw new ImageFormatException("Trailing bits not properly formatted.");
}
}
private static void AlignToByteBoundary(Av1BitStreamReader reader)
{
while ((reader.BitPosition & 0x7) > 0)
{
if (reader.ReadBoolean())
{
throw new ImageFormatException("Incorrect byte alignment padding bits.");
}
}
}
private static void ComputeImageSize(ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo)
{
frameInfo.ModeInfoColumnCount = 2 * ((frameInfo.FrameSize.FrameWidth + 7) >> 3);
frameInfo.ModeInfoRowCount = 2 * ((frameInfo.FrameSize.FrameHeight + 7) >> 3);
frameInfo.ModeInfoStride = Av1Math.AlignPowerOf2(sequenceHeader.MaxFrameWidth, ObuConstants.MaxSuperBlockSizeLog2) >> ObuConstants.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,
};
private static ObuSequenceHeader ReadSequenceHeader(Av1BitStreamReader reader)
{
ObuSequenceHeader sequenceHeader = new();
sequenceHeader.SequenceProfile = (ObuSequenceProfile)reader.ReadLiteral(3);
if (sequenceHeader.SequenceProfile > ObuConstants.MaxSequenceProfile)
{
throw new ImageFormatException("Unknown sequence profile.");
}
sequenceHeader.IsStillPicture = reader.ReadBoolean();
sequenceHeader.IsReducedStillPictureHeader = reader.ReadBoolean();
if (!sequenceHeader.IsStillPicture || !sequenceHeader.IsReducedStillPictureHeader)
{
throw new ImageFormatException("Not a picture header, is this a movie file ??");
}
sequenceHeader.TimingInfo = null;
sequenceHeader.DecoderModelInfoPresentFlag = false;
sequenceHeader.InitialDisplayDelayPresentFlag = false;
sequenceHeader.OperatingPoint[0].OperatorIndex = 0;
sequenceHeader.OperatingPoint[0].SequenceLevelIndex = (int)reader.ReadLiteral(ObuConstants.LevelBits);
if (!IsValidSequenceLevel(sequenceHeader.OperatingPoint[0].SequenceLevelIndex))
{
throw new ImageFormatException("Unknown sequnce level.");
}
sequenceHeader.OperatingPoint[0].SequenceTier = 0;
sequenceHeader.OperatingPoint[0].IsDecoderModelPresent = false;
sequenceHeader.OperatingPoint[0].IsInitialDisplayDelayPresent = false;
// Video related flags removed
// SVT-TODO: int operatingPoint = this.ChooseOperatingPoint();
// sequenceHeader.OperatingPointIndex = (int)operatingPointIndices[operatingPoint];
sequenceHeader.FrameWidthBits = (int)reader.ReadLiteral(4) + 1;
sequenceHeader.FrameHeightBits = (int)reader.ReadLiteral(4) + 1;
sequenceHeader.MaxFrameWidth = (int)reader.ReadLiteral(sequenceHeader.FrameWidthBits) + 1;
sequenceHeader.MaxFrameHeight = (int)reader.ReadLiteral(sequenceHeader.FrameHeightBits) + 1;
sequenceHeader.IsFrameIdNumbersPresent = false;
// Video related flags removed
sequenceHeader.Use128x128SuperBlock = reader.ReadBoolean();
sequenceHeader.SuperBlockSize = sequenceHeader.Use128x128SuperBlock ? Av1BlockSize.Block128x128 : Av1BlockSize.Block64x64;
sequenceHeader.ModeInfoSize = sequenceHeader.Use128x128SuperBlock ? 32 : 16;
sequenceHeader.SuperBlockSizeLog2 = sequenceHeader.Use128x128SuperBlock ? 7 : 6;
sequenceHeader.FilterIntraLevel = (int)reader.ReadLiteral(1);
sequenceHeader.EnableIntraEdgeFilter = reader.ReadBoolean();
sequenceHeader.EnableInterIntraCompound = false;
sequenceHeader.EnableMaskedCompound = false;
sequenceHeader.EnableWarpedMotion = false;
sequenceHeader.EnableDualFilter = false;
sequenceHeader.OrderHintInfo.EnableJointCompound = false;
sequenceHeader.OrderHintInfo.EnableReferenceFrameMotionVectors = false;
sequenceHeader.SequenceForceScreenContentTools = 2;
sequenceHeader.SequenceForceIntegerMotionVector = 2;
sequenceHeader.OrderHintInfo.OrderHintBits = 0;
// Video related flags removed
sequenceHeader.EnableSuperResolution = reader.ReadBoolean();
sequenceHeader.CdefLevel = (int)reader.ReadLiteral(1);
sequenceHeader.EnableRestoration = reader.ReadBoolean();
sequenceHeader.ColorConfig = ReadColorConfig(reader, sequenceHeader);
sequenceHeader.AreFilmGrainingParametersPresent = reader.ReadBoolean();
ReadTrailingBits(reader);
return sequenceHeader;
}
private static ObuColorConfig ReadColorConfig(Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader)
{
ObuColorConfig colorConfig = new();
ReadBitDepth(reader, colorConfig, sequenceHeader);
colorConfig.Monochrome = false;
if (sequenceHeader.SequenceProfile == ObuSequenceProfile.High)
{
colorConfig.Monochrome = reader.ReadBoolean();
}
colorConfig.ChannelCount = colorConfig.Monochrome ? 1 : 3;
colorConfig.IsColorDescriptionPresent = reader.ReadBoolean();
colorConfig.ColorPrimaries = ObuColorPrimaries.Unspecified;
colorConfig.TransferCharacteristics = ObuTransferCharacteristics.Unspecified;
colorConfig.MatrixCoefficients = ObuMatrixCoefficients.Unspecified;
if (colorConfig.IsColorDescriptionPresent)
{
colorConfig.ColorPrimaries = (ObuColorPrimaries)reader.ReadLiteral(8);
colorConfig.TransferCharacteristics = (ObuTransferCharacteristics)reader.ReadLiteral(8);
colorConfig.MatrixCoefficients = (ObuMatrixCoefficients)reader.ReadLiteral(8);
}
colorConfig.ColorRange = false;
colorConfig.SubSamplingX = false;
colorConfig.SubSamplingY = false;
colorConfig.ChromaSamplePosition = ObuChromoSamplePosition.Unknown;
colorConfig.HasSeparateUvDelta = false;
if (colorConfig.Monochrome)
{
colorConfig.ColorRange = reader.ReadBoolean();
colorConfig.SubSamplingX = true;
colorConfig.SubSamplingY = true;
return colorConfig;
}
else if (
colorConfig.ColorPrimaries == ObuColorPrimaries.Bt709 &&
colorConfig.TransferCharacteristics == ObuTransferCharacteristics.Srgb &&
colorConfig.MatrixCoefficients == ObuMatrixCoefficients.Identity)
{
colorConfig.ColorRange = true;
colorConfig.SubSamplingX = false;
colorConfig.SubSamplingY = false;
}
else
{
colorConfig.ColorRange = reader.ReadBoolean();
if (sequenceHeader.SequenceProfile != ObuSequenceProfile.Main)
{
if (colorConfig.BitDepth == 12)
{
colorConfig.SubSamplingX = reader.ReadBoolean();
if (colorConfig.SubSamplingX)
{
colorConfig.SubSamplingY = reader.ReadBoolean();
}
}
else
{
colorConfig.SubSamplingX = true;
colorConfig.SubSamplingY = false;
}
}
if (colorConfig.SubSamplingX && colorConfig.SubSamplingY)
{
colorConfig.ChromaSamplePosition = (ObuChromoSamplePosition)reader.ReadLiteral(2);
}
}
colorConfig.HasSeparateUvDeltaQ = reader.ReadBoolean();
return colorConfig;
}
private static void ReadBitDepth(Av1BitStreamReader reader, ObuColorConfig colorConfig, ObuSequenceHeader sequenceHeader)
{
bool hasHighBitDepth = reader.ReadBoolean();
if (sequenceHeader.SequenceProfile == ObuSequenceProfile.Professional && hasHighBitDepth)
{
colorConfig.BitDepth = reader.ReadBoolean() ? 12 : 10;
}
else if (sequenceHeader.SequenceProfile <= ObuSequenceProfile.Professional)
{
colorConfig.BitDepth = hasHighBitDepth ? 10 : 8;
}
}
private static void ReadSuperResolutionParameters(Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo)
{
bool useSuperResolution = false;
if (sequenceHeader.EnableSuperResolution)
{
useSuperResolution = reader.ReadBoolean();
}
if (useSuperResolution)
{
frameInfo.FrameSize.SuperResolutionDenominator = (int)reader.ReadLiteral(ObuConstants.SuperResolutionScaleBits) + ObuConstants.SuperResolutionScaleDenominatorMinimum;
}
else
{
frameInfo.FrameSize.SuperResolutionDenominator = ObuConstants.ScaleNumerator;
}
frameInfo.FrameSize.SuperResolutionUpscaledWidth = frameInfo.FrameSize.FrameWidth;
frameInfo.FrameSize.FrameWidth =
(frameInfo.FrameSize.SuperResolutionUpscaledWidth * ObuConstants.ScaleNumerator) +
(frameInfo.FrameSize.SuperResolutionDenominator / 2);
if (frameInfo.FrameSize.SuperResolutionDenominator != ObuConstants.ScaleNumerator)
{
int manWidth = Math.Min(16, frameInfo.FrameSize.SuperResolutionUpscaledWidth);
frameInfo.FrameSize.FrameWidth = Math.Max(manWidth, frameInfo.FrameSize.FrameWidth);
}
}
private static void ReadRenderSize(Av1BitStreamReader reader, ObuFrameHeader frameInfo)
{
bool renderSizeAndFrameSizeDifferent = reader.ReadBoolean();
if (renderSizeAndFrameSizeDifferent)
{
frameInfo.FrameSize.RenderWidth = (int)reader.ReadLiteral(16) + 1;
frameInfo.FrameSize.RenderHeight = (int)reader.ReadLiteral(16) + 1;
}
else
{
frameInfo.FrameSize.RenderWidth = frameInfo.FrameSize.SuperResolutionUpscaledWidth;
frameInfo.FrameSize.RenderHeight = frameInfo.FrameSize.FrameHeight;
}
}
private static void ReadFrameSizeWithReferences(Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, bool frameSizeOverrideFlag)
{
bool foundReference = false;
for (int i = 0; i < ObuConstants.ReferencesPerFrame; i++)
{
foundReference = reader.ReadBoolean();
if (foundReference)
{
// Take values over from reference frame
break;
}
}
if (!foundReference)
{
ReadFrameSize(reader, sequenceHeader, frameInfo, frameSizeOverrideFlag);
ReadRenderSize(reader, frameInfo);
}
else
{
ReadSuperResolutionParameters(reader, sequenceHeader, frameInfo);
ComputeImageSize(sequenceHeader, frameInfo);
}
}
private static void ReadFrameSize(Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, bool frameSizeOverrideFlag)
{
if (frameSizeOverrideFlag)
{
frameInfo.FrameSize.FrameWidth = (int)reader.ReadLiteral(sequenceHeader.FrameWidthBits) + 1;
frameInfo.FrameSize.FrameHeight = (int)reader.ReadLiteral(sequenceHeader.FrameHeightBits) + 1;
}
else
{
frameInfo.FrameSize.FrameWidth = sequenceHeader.MaxFrameWidth;
frameInfo.FrameSize.FrameHeight = sequenceHeader.MaxFrameHeight;
}
ReadSuperResolutionParameters(reader, sequenceHeader, frameInfo);
ComputeImageSize(sequenceHeader, frameInfo);
}
private static ObuTileInfo ReadTileInfo(Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo)
{
ObuTileInfo tileInfo = new();
int superBlockColumnCount;
int superBlockRowCount;
int superBlockShift;
if (sequenceHeader.Use128x128SuperBlock)
{
superBlockColumnCount = (frameInfo.ModeInfoColumnCount + 31) >> 5;
superBlockRowCount = (frameInfo.ModeInfoRowCount + 31) >> 5;
superBlockShift = 5;
}
else
{
superBlockColumnCount = (frameInfo.ModeInfoColumnCount + 15) >> 4;
superBlockRowCount = (frameInfo.ModeInfoRowCount + 15) >> 4;
superBlockShift = 4;
}
int superBlockSize = superBlockShift + 2;
int maxTileAreaOfSuperBlock = ObuConstants.MaxTileArea >> (2 * superBlockSize);
tileInfo.MaxTileWidthSuperBlock = ObuConstants.MaxTileWidth >> superBlockSize;
tileInfo.MaxTileHeightSuperBlock = (ObuConstants.MaxTileArea / ObuConstants.MaxTileWidth) >> superBlockSize;
tileInfo.MinLog2TileColumnCount = TileLog2(tileInfo.MaxTileWidthSuperBlock, superBlockColumnCount);
tileInfo.MaxLog2TileColumnCount = TileLog2(1, Math.Min(superBlockColumnCount, ObuConstants.MaxTileColumnCount));
tileInfo.MaxLog2TileRowCount = TileLog2(1, Math.Min(superBlockRowCount, ObuConstants.MaxTileRowCount));
tileInfo.MinLog2TileCount = Math.Max(tileInfo.MinLog2TileColumnCount, TileLog2(maxTileAreaOfSuperBlock, superBlockColumnCount * superBlockRowCount));
tileInfo.HasUniformTileSpacing = reader.ReadBoolean();
if (tileInfo.HasUniformTileSpacing)
{
tileInfo.TileColumnCountLog2 = tileInfo.MinLog2TileColumnCount;
while (tileInfo.TileColumnCountLog2 < tileInfo.MaxLog2TileColumnCount)
{
if (reader.ReadBoolean())
{
tileInfo.TileColumnCountLog2++;
}
else
{
break;
}
}
int tileWidthSuperBlock = (superBlockColumnCount + (1 << tileInfo.TileColumnCountLog2) - 1) >> tileInfo.TileColumnCountLog2;
if (tileWidthSuperBlock > tileInfo.MaxTileWidthSuperBlock)
{
throw new ImageFormatException("Invalid tile width specified.");
}
int i = 0;
tileInfo.TileColumnStartModeInfo = new int[superBlockColumnCount + 1];
for (int startSuperBlock = 0; startSuperBlock < superBlockColumnCount; startSuperBlock += tileWidthSuperBlock)
{
tileInfo.TileColumnStartModeInfo[i] = startSuperBlock << superBlockShift;
i++;
}
tileInfo.TileColumnStartModeInfo[i] = frameInfo.ModeInfoColumnCount;
tileInfo.TileColumnCount = i;
tileInfo.MinLog2TileRowCount = Math.Max(tileInfo.MinLog2TileCount - tileInfo.TileColumnCountLog2, 0);
tileInfo.TileRowCountLog2 = tileInfo.MinLog2TileRowCount;
while (tileInfo.TileRowCountLog2 < tileInfo.MaxLog2TileRowCount)
{
if (reader.ReadBoolean())
{
tileInfo.TileRowCountLog2++;
}
else
{
break;
}
}
int tileHeightSuperBlock = (superBlockRowCount + (1 << tileInfo.TileRowCountLog2) - 1) >> tileInfo.TileRowCountLog2;
if (tileHeightSuperBlock > tileInfo.MaxTileHeightSuperBlock)
{
throw new ImageFormatException("Invalid tile height specified.");
}
i = 0;
tileInfo.TileRowStartModeInfo = new int[superBlockRowCount + 1];
for (int startSuperBlock = 0; startSuperBlock < superBlockRowCount; startSuperBlock += tileHeightSuperBlock)
{
tileInfo.TileRowStartModeInfo[i] = startSuperBlock << superBlockShift;
i++;
}
tileInfo.TileRowStartModeInfo[i] = frameInfo.ModeInfoRowCount;
tileInfo.TileRowCount = i;
}
else
{
uint widestTileSuperBlock = 0U;
int startSuperBlock = 0;
int i = 0;
for (; startSuperBlock < superBlockColumnCount; i++)
{
tileInfo.TileColumnStartModeInfo[i] = startSuperBlock << superBlockShift;
uint maxWidth = (uint)Math.Min(superBlockColumnCount - startSuperBlock, tileInfo.MaxTileWidthSuperBlock);
uint widthInSuperBlocks = reader.ReadNonSymmetric(maxWidth) + 1;
widestTileSuperBlock = Math.Max(widthInSuperBlocks, widestTileSuperBlock);
startSuperBlock += (int)widthInSuperBlocks;
}
if (startSuperBlock != superBlockColumnCount)
{
throw new ImageFormatException("Super block tiles width does not add up to total width.");
}
tileInfo.TileColumnStartModeInfo[i] = frameInfo.ModeInfoColumnCount;
tileInfo.TileColumnCount = i;
tileInfo.TileColumnCountLog2 = TileLog2(1, tileInfo.TileColumnCount);
if (tileInfo.MinLog2TileCount > 0)
{
maxTileAreaOfSuperBlock = (superBlockRowCount * superBlockColumnCount) >> (tileInfo.MinLog2TileCount + 1);
}
else
{
maxTileAreaOfSuperBlock = superBlockRowCount * superBlockColumnCount;
}
DebugGuard.MustBeGreaterThan(widestTileSuperBlock, 0U, nameof(widestTileSuperBlock));
tileInfo.MaxTileHeightSuperBlock = Math.Max(maxTileAreaOfSuperBlock / (int)widestTileSuperBlock, 1);
startSuperBlock = 0;
for (i = 0; startSuperBlock < superBlockRowCount; i++)
{
tileInfo.TileRowStartModeInfo[i] = startSuperBlock << superBlockShift;
uint maxHeight = (uint)Math.Min(superBlockRowCount - startSuperBlock, tileInfo.MaxTileHeightSuperBlock);
uint heightInSuperBlocks = reader.ReadNonSymmetric(maxHeight) + 1;
startSuperBlock += (int)heightInSuperBlocks;
}
if (startSuperBlock != superBlockRowCount)
{
throw new ImageFormatException("Super block tiles height does not add up to total height.");
}
tileInfo.TileRowStartModeInfo[i] = frameInfo.ModeInfoRowCount;
tileInfo.TileRowCount = i;
tileInfo.TileRowCountLog2 = TileLog2(1, tileInfo.TileRowCount);
}
if (tileInfo.TileColumnCount > ObuConstants.MaxTileColumnCount || tileInfo.TileRowCount > ObuConstants.MaxTileRowCount)
{
throw new ImageFormatException("Tile width or height too big.");
}
if (tileInfo.TileColumnCountLog2 > 0 || tileInfo.TileRowCountLog2 > 0)
{
tileInfo.ContextUpdateTileId = reader.ReadLiteral(tileInfo.TileRowCountLog2 + tileInfo.TileColumnCountLog2);
tileInfo.TileSizeBytes = (int)reader.ReadLiteral(2) + 1;
}
else
{
tileInfo.ContextUpdateTileId = 0;
}
if (tileInfo.ContextUpdateTileId >= (tileInfo.TileColumnCount * tileInfo.TileRowCount))
{
throw new ImageFormatException("Context update Tile ID too large.");
}
return tileInfo;
}
private static void ReadUncompressedFrameHeader(Av1BitStreamReader reader, ObuHeader header, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, int planesCount)
{
int idLength = 0;
uint previousFrameId = 0;
bool isIntraFrame = false;
bool frameSizeOverrideFlag = false;
if (sequenceHeader.IsFrameIdNumbersPresent)
{
idLength = sequenceHeader.FrameIdLength - 1 + sequenceHeader.DeltaFrameIdLength - 2 + 3;
DebugGuard.MustBeLessThanOrEqualTo(idLength, 16, nameof(idLength));
}
if (sequenceHeader.IsReducedStillPictureHeader)
{
frameInfo.ShowExistingFrame = false;
frameInfo.FrameType = ObuFrameType.KeyFrame;
isIntraFrame = true;
frameInfo.ShowFrame = true;
frameInfo.ShowableFrame = false;
frameInfo.ErrorResilientMode = true;
}
if (frameInfo.FrameType == ObuFrameType.KeyFrame && frameInfo.ShowFrame)
{
frameInfo.ReferenceValid = new bool[ObuConstants.ReferenceFrameCount];
frameInfo.ReferenceOrderHint = new bool[ObuConstants.ReferenceFrameCount];
for (int i = 0; i < ObuConstants.ReferenceFrameCount; i++)
{
frameInfo.ReferenceValid[i] = false;
frameInfo.ReferenceOrderHint[i] = false;
}
}
frameInfo.DisableCdfUpdate = reader.ReadBoolean();
frameInfo.AllowScreenContentTools = sequenceHeader.SequenceForceScreenContentTools == 1;
if (frameInfo.AllowScreenContentTools)
{
frameInfo.AllowScreenContentTools = reader.ReadBoolean();
}
if (frameInfo.AllowScreenContentTools)
{
if (sequenceHeader.SequenceForceIntegerMotionVector == 1)
{
frameInfo.ForeceIntegerMotionVector = reader.ReadBoolean();
}
else
{
frameInfo.ForceIntegerMotionVector = sequenceHeader.SequenceForceIntegerMotionVector != 0;
}
}
else
{
frameInfo.ForeceIntegerMotionVector = false;
}
if (isIntraFrame)
{
frameInfo.ForeceIntegerMotionVector = true;
}
bool havePreviousFrameId = !(frameInfo.FrameType == ObuFrameType.KeyFrame && frameInfo.ShowFrame);
if (havePreviousFrameId)
{
previousFrameId = frameInfo.CurrentFrameId;
}
if (sequenceHeader.IsFrameIdNumbersPresent)
{
frameInfo.CurrentFrameId = reader.ReadLiteral(idLength);
if (havePreviousFrameId)
{
uint diffFrameId = (frameInfo.CurrentFrameId > previousFrameId) ?
frameInfo.CurrentFrameId - previousFrameId :
(uint)((1 << idLength) + (int)frameInfo.CurrentFrameId - previousFrameId);
if (frameInfo.CurrentFrameId == previousFrameId || diffFrameId >= 1 << (idLength - 1))
{
throw new ImageFormatException("Current frame ID cannot be same as previous Frame ID");
}
}
int diffLength = sequenceHeader.DeltaFrameIdLength;
for (int i = 0; i < ObuConstants.ReferenceFrameCount; i++)
{
if (frameInfo.CurrentFrameId > (1U << diffLength))
{
if ((frameInfo.ReferenceFrameIndex[i] > frameInfo.CurrentFrameId) ||
frameInfo.ReferenceFrameIndex[i] > (frameInfo.CurrentFrameId - (1 - diffLength)))
{
frameInfo.ReferenceValid[i] = false;
}
}
else if (frameInfo.ReferenceFrameIndex[i] > frameInfo.CurrentFrameId &&
frameInfo.ReferenceFrameIndex[i] < ((1 << idLength) + (frameInfo.CurrentFrameId - (1 << diffLength))))
{
frameInfo.ReferenceValid[i] = false;
}
}
}
else
{
frameInfo.CurrentFrameId = 0;
}
if (frameInfo.FrameType == ObuFrameType.SwitchFrame)
{
frameSizeOverrideFlag = true;
}
else if (sequenceHeader.IsReducedStillPictureHeader)
{
frameSizeOverrideFlag = false;
}
else
{
frameSizeOverrideFlag = reader.ReadBoolean();
}
frameInfo.OrderHint = reader.ReadLiteral(sequenceHeader.OrderHintInfo.OrderHintBits);
if (isIntraFrame || frameInfo.ErrorResilientMode)
{
frameInfo.PrimaryReferenceFrame = ObuConstants.PrimaryReferenceFrameNone;
}
else
{
frameInfo.PrimaryReferenceFrame = reader.ReadLiteral(ObuConstants.PimaryReferenceBits);
}
// Skipping, as no decoder info model present
frameInfo.AllowHighPrecisionMotionVector = false;
frameInfo.UseReferenceFrameMotionVectors = false;
frameInfo.AllowIntraBlockCopy = false;
if (frameInfo.FrameType == ObuFrameType.SwitchFrame || (frameInfo.FrameType == ObuFrameType.KeyFrame && frameInfo.ShowFrame))
{
frameInfo.RefreshFrameFlags = 0xffU;
}
else
{
frameInfo.RefreshFrameFlags = reader.ReadLiteral(8);
}
if (frameInfo.FrameType == ObuFrameType.IntraOnlyFrame)
{
DebugGuard.IsTrue(frameInfo.RefreshFrameFlags != 0xFFU, nameof(frameInfo.RefreshFrameFlags));
}
if (!isIntraFrame || (frameInfo.RefreshFrameFlags != 0xFFU))
{
if (frameInfo.ErrorResilientMode && sequenceHeader.OrderHintInfo != null)
{
for (int i = 0; i < ObuConstants.ReferenceFrameCount; i++)
{
int referenceOrderHint = (int)reader.ReadLiteral(sequenceHeader.OrderHintInfo.OrderHintBits);
if (referenceOrderHint != (frameInfo.ReferenceOrderHint[i] ? 1U : 0U))
{
frameInfo.ReferenceValid[i] = false;
}
}
}
}
if (isIntraFrame)
{
ReadFrameSize(reader, sequenceHeader, frameInfo, frameSizeOverrideFlag);
ReadRenderSize(reader, frameInfo);
if (frameInfo.AllowScreenContentTools && frameInfo.FrameSize.RenderWidth != 0)
{
if (frameInfo.FrameSize.FrameWidth == frameInfo.FrameSize.SuperResolutionUpscaledWidth)
{
frameInfo.AllowIntraBlockCopy = reader.ReadBoolean();
}
}
}
else
{
// Single image is always Intra.
}
SetupFrameBufferReferences(sequenceHeader, frameInfo);
CheckAddTemporalMotionVectorBuffer(sequenceHeader, frameInfo);
SetupFrameSignBias(sequenceHeader, frameInfo);
if (sequenceHeader.IsReducedStillPictureHeader || frameInfo.DisableCdfUpdate)
{
frameInfo.DisableFrameEndUpdateCdf = true;
}
if (frameInfo.PrimaryReferenceFrame == ObuConstants.PrimaryReferenceFrameNone)
{
SetupPastIndependence();
}
GenerateNextReferenceFrameMap(sequenceHeader, frameInfo);
frameInfo.TilesInfo = ReadTileInfo(reader, sequenceHeader, frameInfo);
frameInfo.QuantizationParameters = ReadQuantizationParameters(reader, sequenceHeader.ColorConfig, planesCount);
ReadSegmentationParameters(reader, sequenceHeader, frameInfo);
ReadDeltaQParameters(reader, frameInfo);
ReadDeltaLoopFilterParameters(reader, frameInfo);
SetupSegmentationDequantization();
Av1MainParseContext mainParseContext = new();
if (frameInfo.PrimaryReferenceFrame == ObuConstants.PrimaryReferenceFrameNone)
{
ResetParseContext(mainParseContext, frameInfo.QuantizationParameters.BaseQIndex);
}
int tilesCount = frameInfo.TilesInfo.TileColumnCount * frameInfo.TilesInfo.TileRowCount;
frameInfo.CodedLossless = true;
for (int segmentId = 0; segmentId < ObuConstants.MaxSegmentCount; segmentId++)
{
int qIndex = GetQIndex(frameInfo.SegmentationParameters, segmentId, frameInfo.QuantizationParameters.BaseQIndex);
frameInfo.QuantizationParameters.QIndex[segmentId] = qIndex;
frameInfo.LosslessArray[segmentId] = qIndex == 0 &&
frameInfo.QuantizationParameters.DeltaQDc[(int)Av1Plane.Y] == 0 &&
frameInfo.QuantizationParameters.DeltaQAc[(int)Av1Plane.U] == 0 &&
frameInfo.QuantizationParameters.DeltaQDc[(int)Av1Plane.U] == 0 &&
frameInfo.QuantizationParameters.DeltaQAc[(int)Av1Plane.V] == 0 &&
frameInfo.QuantizationParameters.DeltaQDc[(int)Av1Plane.V] == 0;
if (!frameInfo.LosslessArray[segmentId])
{
frameInfo.CodedLossless = false;
}
if (frameInfo.QuantizationParameters.IsUsingQMatrix)
{
if (frameInfo.LosslessArray[segmentId])
{
frameInfo.SegmentationParameters.QMLevel[0, segmentId] = 15;
frameInfo.SegmentationParameters.QMLevel[1, segmentId] = 15;
frameInfo.SegmentationParameters.QMLevel[2, segmentId] = 15;
}
else
{
frameInfo.SegmentationParameters.QMLevel[0, segmentId] = frameInfo.QuantizationParameters.QMatrix[(int)Av1Plane.Y];
frameInfo.SegmentationParameters.QMLevel[1, segmentId] = frameInfo.QuantizationParameters.QMatrix[(int)Av1Plane.U];
frameInfo.SegmentationParameters.QMLevel[2, segmentId] = frameInfo.QuantizationParameters.QMatrix[(int)Av1Plane.V];
}
}
}
frameInfo.AllLossless = frameInfo.CodedLossless && frameInfo.FrameSize.FrameWidth == frameInfo.FrameSize.SuperResolutionUpscaledWidth;
ReadLoopFilterParameters(reader, sequenceHeader, frameInfo, planesCount);
ReadConstraintDirectionalEnhancementFilterParameters(reader, sequenceHeader, frameInfo, planesCount);
ReadLoopRestorationParameter(reader, sequenceHeader, frameInfo, planesCount);
ReadTransformMode(reader, frameInfo);
frameInfo.ReferenceMode = ReadFrameReferenceMode(reader, isIntraFrame);
ReadSkipModeParameters(reader, frameInfo, isIntraFrame, sequenceHeader, frameInfo.ReferenceMode);
if (isIntraFrame || frameInfo.ErrorResilientMode || !sequenceHeader.EnableWarpedMotion)
{
frameInfo.AllowWarpedMotion = false;
}
frameInfo.ReducedTxSet = reader.ReadBoolean();
ReadGlobalMotionParameters(reader, sequenceHeader, frameInfo, isIntraFrame);
frameInfo.FilmGrainParameters = ReadFilmGrainParameters(reader, sequenceHeader, frameInfo);
}
private static bool IsSegmentationFeatureActive(ObuSegmentationParameters segmentationParameters, int segmentId, ObuSegmentationLevelFeature feature)
=> segmentationParameters.SegmentationEnabled && segmentationParameters.FeatureEnabled[segmentId, (int)feature];
private static int GetQIndex(ObuSegmentationParameters segmentationParameters, int segmentId, int baseQIndex)
{
if (IsSegmentationFeatureActive(segmentationParameters, segmentId, ObuSegmentationLevelFeature.AlternativeQuantizer))
{
int data = segmentationParameters.FeatureData[segmentId, (int)ObuSegmentationLevelFeature.AlternativeQuantizer];
int qIndex = baseQIndex + data;
return Av1Math.Clamp(qIndex, 0, ObuConstants.MaxQ);
}
else
{
return baseQIndex;
}
}
private static void ReadFrameHeader(Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, ObuHeader header, bool trailingBit)
{
int planeCount = sequenceHeader.ColorConfig.Monochrome ? 1 : 3;
int startBitPosition = reader.BitPosition;
ReadUncompressedFrameHeader(reader, header, sequenceHeader, frameInfo, planeCount);
if (trailingBit)
{
ReadTrailingBits(reader);
}
AlignToByteBoundary(reader);
int endPosition = reader.BitPosition;
int headerBytes = (endPosition - startBitPosition) / 8;
header.PayloadSize -= headerBytes;
}
private static void ReadTileGroup(Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, ObuTileInfo tileInfo, ObuHeader header, out bool isLastTileGroup)
{
int tileCount = tileInfo.TileColumnCount * tileInfo.TileRowCount;
int startBitPosition = reader.BitPosition;
bool tileStartAndEndPresentFlag = false;
if (tileCount > 1)
{
tileStartAndEndPresentFlag = reader.ReadBoolean();
}
if (header.Type == ObuType.FrameHeader)
{
DebugGuard.IsFalse(tileStartAndEndPresentFlag, nameof(tileStartAndEndPresentFlag), "Frame header should not set 'tileStartAndEndPresentFlag'.");
}
int tileGroupStart = 0;
int tileGroupEnd = tileCount - 1;
if (tileCount != 1 && tileStartAndEndPresentFlag)
{
int tileBits = Av1Math.Log2(tileInfo.TileColumnCount) + Av1Math.Log2(tileInfo.TileRowCount);
tileGroupStart = (int)reader.ReadLiteral(tileBits);
tileGroupEnd = (int)reader.ReadLiteral(tileBits);
}
isLastTileGroup = (tileGroupEnd + 1) == tileCount;
AlignToByteBoundary(reader);
int endBitPosition = reader.BitPosition;
int headerBytes = (endBitPosition - startBitPosition) / 8;
header.PayloadSize -= headerBytes;
bool noIbc = !frameInfo.AllowIntraBlockCopy;
bool doLoopFilter = noIbc && (frameInfo.LoopFilterParameters.FilterLevel[0] || frameInfo.LoopFilterParameters.FilterLevel[1]);
bool doCdef = noIbc && (!frameInfo.CodedLossless &&
(frameInfo.ConstraintDirectionalEnhancementFilterParameters.BitCount ||
frameInfo.ConstraintDirectionalEnhancementFilterParameters.YStrength[0] != 0 ||
frameInfo.ConstraintDirectionalEnhancementFilterParameters.UVStrength[0] != 0));
bool doLoopRestoration = noIbc &&
(frameInfo.LoopRestorationParameters[(int)Av1Plane.Y].FrameRestorationType != ObuRestorationType.RestoreNone ||
frameInfo.LoopRestorationParameters[(int)Av1Plane.U].FrameRestorationType != ObuRestorationType.RestoreNone ||
frameInfo.LoopRestorationParameters[(int)Av1Plane.V].FrameRestorationType != ObuRestorationType.RestoreNone);
for (int tileNum = tileGroupStart; tileNum <= tileGroupEnd; tileNum++)
{
int tileRow = tileNum / tileInfo.TileColumnCount;
int tileColumn = tileNum % tileInfo.TileColumnCount;
bool isLastTile = tileNum == tileGroupEnd;
int tileSize = header.PayloadSize;
if (!isLastTile)
{
tileSize = (int)reader.ReadLittleEndian(tileInfo.TileSizeBytes) + 1;
header.PayloadSize -= tileSize + tileInfo.TileSizeBytes;
}
// TODO: Pass more info to the decoder.
DecodeTile(sequenceHeader, frameInfo, tileInfo, tileNum);
}
if (tileGroupEnd != tileCount - 1)
{
return;
}
FinishDecodeTiles(sequenceHeader, frameInfo, doCdef, doLoopRestoration);
}
private static bool IsValidSequenceLevel(int sequenceLevelIndex)
=> sequenceLevelIndex is < 24 or 31;
private static int TileLog2(int blockSize, int target)
{
int k;
for (k = 0; (blockSize << k) < target; k++)
{
}
return k;
}
}

10
src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuReferenceMode.cs

@ -0,0 +1,10 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
internal enum ObuReferenceMode
{
ReferenceModeSelect,
SingleReference,
}

9
src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuRestorationType.cs

@ -0,0 +1,9 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
internal enum ObuRestorationType
{
RestoreNone
}

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

@ -0,0 +1,16 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
internal enum ObuSegmentationLevelFeature
{
AlternativeQuantizer,
AlternativeLoopFilterYVertical,
AlternativeLoopFilterYHorizontal,
AlternativeLoopFilterU,
AlternativeLoopFilterV,
ReferenceFrame,
Skip,
GlobalMotionVector,
}

15
src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuSegmentationParameters.cs

@ -0,0 +1,15 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
internal class ObuSegmentationParameters
{
public int[,] QMLevel { get; internal set; } = new int[3, ObuConstants.MaxSegmentCount];
public bool[,] FeatureEnabled { get; internal set; } = new bool[ObuConstants.MaxSegmentCount, ObuConstants.SegmentationLevelMax];
public bool SegmentationEnabled { get; internal set; }
public int[,] FeatureData { get; internal set; } = new int[ObuConstants.MaxSegmentCount, ObuConstants.SegmentationLevelMax];
}

71
src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuSequenceHeader.cs

@ -0,0 +1,71 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
internal class ObuSequenceHeader
{
internal bool IsStillPicture { get; set; }
internal bool IsReducedStillPictureHeader { get; set; }
internal ObuSequenceProfile SequenceProfile { get; set; }
internal ObuOperatingPoint[] OperatingPoint { get; set; } = new ObuOperatingPoint[1];
internal bool InitialDisplayDelayPresentFlag { get; set; }
internal bool DecoderModelInfoPresentFlag { get; set; }
internal object? TimingInfo { get; set; }
internal bool IsFrameIdNumbersPresent { get; set; }
internal int FrameWidthBits { get; set; }
internal int FrameHeightBits { get; set; }
internal int MaxFrameWidth { get; set; }
internal int MaxFrameHeight { get; set; }
internal bool Use128x128SuperBlock { get; set; }
internal Av1BlockSize SuperBlockSize { get; set; }
internal int ModeInfoSize { get; set; }
internal int SuperBlockSizeLog2 { get; set; }
internal int FilterIntraLevel { get; set; }
internal bool EnableIntraEdgeFilter { get; set; }
internal ObuOrderHintInfo OrderHintInfo { get; set; } = new ObuOrderHintInfo();
internal bool EnableInterIntraCompound { get; set; }
internal bool EnableMaskedCompound { get; set; }
internal bool EnableWarpedMotion { get; set; }
internal bool EnableDualFilter { get; set; }
internal int SequenceForceIntegerMotionVector { get; set; }
internal int SequenceForceScreenContentTools { get; set; }
internal bool EnableSuperResolution { get; set; }
internal int CdefLevel { get; set; }
internal bool EnableRestoration { get; set; }
internal ObuColorConfig ColorConfig { get; set; } = new ObuColorConfig();
internal bool AreFilmGrainingParametersPresent { get; set; }
internal int FrameIdLength { get; set; }
internal int DeltaFrameIdLength { get; set; }
}

11
src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuSequenceProfile.cs

@ -0,0 +1,11 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
internal enum ObuSequenceProfile : uint
{
Main = 0,
High = 1,
Professional = 2,
}

39
src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuTileInfo.cs

@ -0,0 +1,39 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
internal class ObuTileInfo
{
internal int MaxTileWidthSuperBlock { get; set; }
internal int MaxTileHeightSuperBlock { get; set; }
internal int MinLog2TileColumnCount { get; set; }
internal int MaxLog2TileColumnCount { get; set; }
internal int MaxLog2TileRowCount { get; set; }
internal int MinLog2TileCount { get; set; }
public bool HasUniformTileSpacing { get; set; }
internal int TileColumnCountLog2 { get; set; }
internal int TileColumnCount { get; set; }
internal int[] TileColumnStartModeInfo { get; set; }
internal int MinLog2TileRowCount { get; set; }
internal int TileRowCountLog2 { get; set; }
internal int[] TileRowStartModeInfo { get; set; }
internal int TileRowCount { get; set; }
internal uint ContextUpdateTileId { get; set; }
internal int TileSizeBytes { get; set; }
}

25
src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuTransferCharacteristics.cs

@ -0,0 +1,25 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
internal enum ObuTransferCharacteristics
{
Bt709 = 1,
Unspecified = 2,
Bt470M = 4,
Bt470BG = 5,
Bt601 = 6,
Smpte240 = 7,
Linear = 8,
Log100 = 9,
Log100Sqrt10 = 10,
Iec61966 = 11,
Bt1361 = 12,
Srgb = 13,
Bt202010Bit = 14,
Bt202012Bit = 15,
Smpte2084 = 16,
Smpte248 = 17,
Hlg = 18,
}

18
src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuType.cs

@ -0,0 +1,18 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
internal enum ObuType
{
None = 0,
SequenceHeader = 1,
TemporalDelimiter = 2,
FrameHeader = 3,
RedundantFrameHeader = 7,
TileGroup = 4,
Metadata = 5,
Frame = 6,
TileList = 8,
Padding = 15,
}
Loading…
Cancel
Save