mirror of https://github.com/SixLabors/ImageSharp
33 changed files with 1858 additions and 0 deletions
@ -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; |
||||
|
} |
||||
|
} |
||||
@ -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, |
||||
|
} |
||||
@ -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, |
||||
|
} |
||||
@ -0,0 +1,8 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Heif.Av1; |
||||
|
|
||||
|
internal class Av1MainParseContext |
||||
|
{ |
||||
|
} |
||||
@ -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); |
||||
|
} |
||||
@ -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, |
||||
|
} |
||||
@ -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, |
||||
|
} |
||||
@ -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; |
||||
|
} |
||||
|
} |
||||
@ -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, |
||||
|
} |
||||
@ -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; |
||||
|
} |
||||
@ -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; } |
||||
|
} |
||||
@ -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 |
||||
|
{ |
||||
|
} |
||||
@ -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; } |
||||
|
} |
||||
@ -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; } |
||||
|
} |
||||
@ -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, |
||||
|
} |
||||
@ -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; } |
||||
|
} |
||||
@ -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; } |
||||
|
} |
||||
@ -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; } |
||||
|
} |
||||
@ -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, |
||||
|
} |
||||
@ -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 |
||||
|
} |
||||
@ -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; } |
||||
|
} |
||||
@ -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; } |
||||
|
} |
||||
@ -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]; |
||||
|
} |
||||
@ -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; |
||||
|
} |
||||
|
} |
||||
@ -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, |
||||
|
} |
||||
@ -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 |
||||
|
} |
||||
@ -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, |
||||
|
} |
||||
@ -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]; |
||||
|
} |
||||
@ -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; } |
||||
|
} |
||||
@ -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, |
||||
|
} |
||||
@ -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; } |
||||
|
} |
||||
@ -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, |
||||
|
} |
||||
@ -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…
Reference in new issue