Browse Source

Introduce ObuWriter

pull/2633/head
Ynse Hoornenborg 2 years ago
parent
commit
a1cb2ddc5e
  1. 24
      src/ImageSharp/Formats/Heif/Av1/Av1BitStreamWriter.cs
  2. 7
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuConstants.cs
  3. 6
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuConstraintDirectionalEnhancementFilterParameters.cs
  4. 4
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuLoopRestorationParameters.cs
  5. 110
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuReader.cs
  6. 7
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuRestorationType.cs
  7. 4
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuSequenceHeader.cs
  8. 583
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuWriter.cs

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

@ -63,4 +63,28 @@ internal ref struct Av1BitStreamWriter(Stream stream)
this.bitOffset = 0;
}
}
public void WriteSignedFromUnsigned(int signedValue, int n)
{
// See section 4.10.6 of the AV1-Specification
uint value = unchecked((uint)signedValue);
this.WriteLiteral(value, n + 1);
}
public void WriteLittleEndianBytes128(uint value)
{
if (value < 128)
{
this.WriteLiteral(value, 8);
}
else if (value < 0x8000U)
{
this.WriteLiteral(value >> 7, 8);
this.WriteLiteral(value & 0x80, 8);
}
else
{
throw new NotImplementedException("No such large values yet.");
}
}
}

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

@ -51,7 +51,7 @@ internal static class ObuConstants
/// </summary>
public const uint PrimaryReferenceFrameNone = 7;
public const int PimaryReferenceBits = -1;
public const int PimaryReferenceBits = 3;
/// <summary>
/// Number of segments allowed in segmentation map.
@ -79,4 +79,9 @@ internal static class ObuConstants
/// Number of segmentation features.
/// </summary>
public const int SegmentationLevelMax = 8;
/// <summary>
/// Maximum size of a loop restoration tile.
/// </summary>
public const int RestorationMaxTileSize = 256;
}

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

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

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

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

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

@ -10,7 +10,7 @@ internal class ObuReader
/// <summary>
/// Decode all OBU's in a frame.
/// </summary>
public static void Read(ref Av1BitStreamReader reader, int dataSize, IAv1TileDecoder decoder, bool isAnnexB)
public static void Read(ref Av1BitStreamReader reader, int dataSize, IAv1TileDecoder decoder, bool isAnnexB = false)
{
bool frameDecodingFinished = false;
while (!frameDecodingFinished)
@ -255,7 +255,7 @@ internal class ObuReader
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.EnableFilterIntra = reader.ReadBoolean();
sequenceHeader.EnableIntraEdgeFilter = reader.ReadBoolean();
sequenceHeader.EnableInterIntraCompound = false;
sequenceHeader.EnableMaskedCompound = false;
@ -269,7 +269,7 @@ internal class ObuReader
// Video related flags removed
sequenceHeader.EnableSuperResolution = reader.ReadBoolean();
sequenceHeader.CdefLevel = (int)reader.ReadLiteral(1);
sequenceHeader.EnableCdef = reader.ReadBoolean();
sequenceHeader.EnableRestoration = reader.ReadBoolean();
sequenceHeader.ColorConfig = ReadColorConfig(ref reader, sequenceHeader);
sequenceHeader.AreFilmGrainingParametersPresent = reader.ReadBoolean();
@ -748,11 +748,7 @@ internal class ObuReader
frameSizeOverrideFlag = reader.ReadBoolean();
}
frameInfo.OrderHint = 0;
if (sequenceHeader.OrderHintInfo.OrderHintBits > 0)
{
frameInfo.OrderHint = reader.ReadLiteral(sequenceHeader.OrderHintInfo.OrderHintBits);
}
frameInfo.OrderHint = reader.ReadLiteral(sequenceHeader.OrderHintInfo.OrderHintBits);
if (isIntraFrame || frameInfo.ErrorResilientMode)
{
@ -972,11 +968,11 @@ internal class ObuReader
bool doCdef = noIbc && (!frameInfo.CodedLossless &&
(frameInfo.CdefParameters.BitCount != 0 ||
frameInfo.CdefParameters.YStrength[0] != 0 ||
frameInfo.CdefParameters.UVStrength[0] != 0));
frameInfo.CdefParameters.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);
(frameInfo.LoopRestorationParameters[(int)Av1Plane.Y].Type != ObuRestorationType.None ||
frameInfo.LoopRestorationParameters[(int)Av1Plane.U].Type != ObuRestorationType.None ||
frameInfo.LoopRestorationParameters[(int)Av1Plane.V].Type != ObuRestorationType.None);
for (int tileNum = tileGroupStart; tileNum <= tileGroupEnd; tileNum++)
{
@ -1007,7 +1003,7 @@ internal class ObuReader
int deltaQ = 0;
if (reader.ReadBoolean())
{
deltaQ = (int)reader.ReadLiteral(7);
deltaQ = reader.ReadSignedFromUnsigned(6);
}
return deltaQ;
@ -1042,7 +1038,7 @@ internal class ObuReader
if (frameInfo.DeltaLoopFilterParameters.IsPresent)
{
frameInfo.DeltaLoopFilterParameters.Resolution = (int)reader.ReadLiteral(4);
frameInfo.DeltaLoopFilterParameters.Resolution = (int)reader.ReadLiteral(2);
frameInfo.DeltaLoopFilterParameters.Multi = reader.ReadBoolean();
}
}
@ -1102,11 +1098,7 @@ internal class ObuReader
private static void ReadSegmentationParameters(ref Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, int planesCount)
{
frameInfo.SegmentationParameters.SegmentationEnabled = reader.ReadBoolean();
if (!frameInfo.SegmentationParameters.SegmentationEnabled)
{
// CopyFeatureInfo();
return;
}
Guard.IsFalse(frameInfo.SegmentationParameters.SegmentationEnabled, nameof(frameInfo.SegmentationParameters.SegmentationEnabled), "Segmentation not supported yet.");
// TODO: Parse more stuff.
}
@ -1142,6 +1134,9 @@ internal class ObuReader
}
}
/// <summary>
/// See section 5.9.20.
/// </summary>
private static void ReadLoopRestorationParameters(ref Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, int planesCount)
{
_ = planesCount;
@ -1153,37 +1148,86 @@ internal class ObuReader
return;
}
// TODO: Parse more stuff.
bool usesLoopRestoration = false;
bool usesChromaLoopRestoration = false;
for (int i = 0; i < planesCount; i++)
{
frameInfo.LoopRestorationParameters[i].Type = (ObuRestorationType)reader.ReadLiteral(2);
if (frameInfo.LoopRestorationParameters[i].Type != ObuRestorationType.None)
{
usesLoopRestoration = true;
if (i > 0)
{
usesChromaLoopRestoration = true;
}
}
}
if (usesLoopRestoration)
{
uint loopRestorationShift = reader.ReadLiteral(1);
if (sequenceHeader.Use128x128SuperBlock)
{
loopRestorationShift++;
}
else
{
if (reader.ReadBoolean())
{
loopRestorationShift += reader.ReadLiteral(1);
}
}
frameInfo.LoopRestorationParameters[0].Size = ObuConstants.RestorationMaxTileSize >> (int)(2 - loopRestorationShift);
int uvShift = 0;
if (sequenceHeader.ColorConfig.SubSamplingX && sequenceHeader.ColorConfig.SubSamplingY && usesChromaLoopRestoration)
{
uvShift = (int)reader.ReadLiteral(1);
}
frameInfo.LoopRestorationParameters[1].Size = frameInfo.LoopRestorationParameters[0].Size >> uvShift;
frameInfo.LoopRestorationParameters[2].Size = frameInfo.LoopRestorationParameters[0].Size >> uvShift;
}
}
/// <summary>
/// See section 5.9.19.
/// </summary>
private static void ReadCdefParameters(ref Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, int planesCount)
{
_ = planesCount;
ObuConstraintDirectionalEnhancementFilterParameters cdefInfo = frameInfo.CdefParameters;
if (frameInfo.CodedLossless || frameInfo.AllowIntraBlockCopy || sequenceHeader.CdefLevel == 0)
{
frameInfo.CdefParameters.BitCount = 0;
frameInfo.CdefParameters.YStrength[0] = 0;
frameInfo.CdefParameters.YStrength[4] = 0;
frameInfo.CdefParameters.UVStrength[0] = 0;
frameInfo.CdefParameters.UVStrength[4] = 0;
frameInfo.CdefParameters.Damping = 0;
cdefInfo.BitCount = 0;
cdefInfo.YStrength[0] = 0;
cdefInfo.YStrength[4] = 0;
cdefInfo.UvStrength[0] = 0;
cdefInfo.UvStrength[4] = 0;
cdefInfo.Damping = 0;
return;
}
// TODO: Parse more stuff.
cdefInfo.Damping = (int)reader.ReadLiteral(2) + 3;
cdefInfo.BitCount = (int)reader.ReadLiteral(2);
for (int i = 0; i < (1 << frameInfo.CdefParameters.BitCount); i++)
{
cdefInfo.YStrength[i] = (int)reader.ReadLiteral(6);
if (planesCount > 1)
{
cdefInfo.UvStrength[i] = (int)reader.ReadLiteral(6);
}
}
}
private static void ReadGlobalMotionParameters(ref Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, bool isIntraFrame)
{
_ = reader;
_ = sequenceHeader;
_ = frameInfo;
if (isIntraFrame)
{
return;
}
// TODO: Parse more stuff.
// Not applicable for INTRA frames.
}
private static ObuReferenceMode ReadFrameReferenceMode(ref Av1BitStreamReader reader, bool isIntraFrame)
@ -1204,7 +1248,7 @@ internal class ObuReader
}
else
{
// TODO: Parse more stuff.
// Not applicable for INTRA frames.
}
if (frameInfo.SkipModeParameters.SkipModeAllowed)

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

@ -3,7 +3,10 @@
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
internal enum ObuRestorationType
internal enum ObuRestorationType : uint
{
RestoreNone
None = 0,
Switchable = 1,
Weiner = 2,
SgrProj = 3,
}

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

@ -5,6 +5,10 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
internal class ObuSequenceHeader
{
internal bool EnableFilterIntra { get; set; }
internal bool EnableCdef { get; set; }
internal bool IsStillPicture { get; set; }
internal bool IsReducedStillPictureHeader { get; set; }

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

@ -0,0 +1,583 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
internal class ObuWriter
{
/// <summary>
/// Encode a single frame into OBU's.
/// </summary>
public static void Write(Stream stream, IAv1TileDecoder decoder)
{
MemoryStream bufferStream = new(100);
Av1BitStreamWriter writer = new(bufferStream);
WriteSequenceHeader(ref writer, decoder.SequenceHeader);
WriteObuHeaderAndSize(stream, ObuType.SequenceHeader, bufferStream.GetBuffer(), (int)bufferStream.Position);
bufferStream.Position = 0;
WriteFrameHeader(ref writer, decoder, true);
WriteObuHeaderAndSize(stream, ObuType.FrameHeader, bufferStream.GetBuffer(), (int)bufferStream.Position);
bufferStream.Position = 0;
WriteTileGroup(ref writer, decoder.TileInfo);
WriteObuHeaderAndSize(stream, ObuType.TileGroup, bufferStream.GetBuffer(), (int)bufferStream.Position);
}
private static void WriteObuHeader(ref Av1BitStreamWriter writer, ObuType type)
{
writer.WriteBoolean(false); // Forbidden bit
writer.WriteLiteral((uint)type, 4);
writer.WriteBoolean(false); // Extension
writer.WriteBoolean(true); // HasSize
writer.WriteBoolean(false); // Reserved
}
/// <summary>
/// Read OBU header and size.
/// </summary>
private static void WriteObuHeaderAndSize(Stream stream, ObuType type, Span<byte> payload, int length)
{
Av1BitStreamWriter writer = new(stream);
WriteObuHeader(ref writer, type);
_ = writer.WriteLittleEndianBytes128(length);
stream.Write(payload, 0, length);
}
/// <summary>
/// Write trsainling bits to end on a byte boundary, these trailing bits start with a 1 and end with 0s.
/// </summary>
/// <remarks>Write an additional byte, if already byte aligned before.</remarks>
private static void WriteTrailingBits(ref Av1BitStreamWriter writer)
{
int bitsBeforeAlignment = 8 - (writer.BitPosition & 0x7);
writer.WriteLiteral(0, bitsBeforeAlignment);
}
private static void AlignToByteBoundary(ref Av1BitStreamWriter writer)
{
while ((writer.BitPosition & 0x7) > 0)
{
writer.WriteBoolean(false);
}
}
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 void WriteSequenceHeader(ref Av1BitStreamWriter writer, ObuSequenceHeader sequenceHeader)
{
writer.WriteLiteral((uint)sequenceHeader.SequenceProfile, 3);
writer.WriteBoolean(true); // IsStillPicture
writer.WriteBoolean(true); // IsReducedStillPicture
writer.WriteLiteral((uint)sequenceHeader.OperatingPoint[0].SequenceLevelIndex, ObuConstants.LevelBits);
// Frame width and Height
writer.WriteLiteral((uint)sequenceHeader.FrameWidthBits - 1, 4);
writer.WriteLiteral((uint)sequenceHeader.FrameHeightBits - 1, 4);
writer.WriteLiteral((uint)sequenceHeader.MaxFrameWidth - 1, sequenceHeader.FrameWidthBits);
writer.WriteLiteral((uint)sequenceHeader.MaxFrameHeight - 1, sequenceHeader.FrameHeightBits);
// Video related flags removed
writer.WriteBoolean(sequenceHeader.Use128x128SuperBlock);
writer.WriteBoolean(sequenceHeader.EnableFilterIntra);
writer.WriteBoolean(sequenceHeader.EnableIntraEdgeFilter);
// Video related flags removed
writer.WriteBoolean(sequenceHeader.EnableSuperResolution);
writer.WriteBoolean(sequenceHeader.EnableCdef);
writer.WriteBoolean(sequenceHeader.EnableRestoration);
WriteColorConfig(ref writer, sequenceHeader);
writer.WriteBoolean(sequenceHeader.AreFilmGrainingParametersPresent);
WriteTrailingBits(ref writer);
}
private static void WriteColorConfig(ref Av1BitStreamWriter writer, ObuSequenceHeader sequenceHeader)
{
ObuColorConfig colorConfig = new();
WriteBitDepth(ref writer, colorConfig, sequenceHeader);
if (sequenceHeader.SequenceProfile != ObuSequenceProfile.High)
{
writer.WriteBoolean(colorConfig.Monochrome);
}
writer.WriteBoolean(false); // colorConfig.IsColorDescriptionPresent
if (colorConfig.Monochrome)
{
writer.WriteBoolean(colorConfig.ColorRange);
return;
}
else if (
colorConfig.ColorPrimaries == ObuColorPrimaries.Bt709 &&
colorConfig.TransferCharacteristics == ObuTransferCharacteristics.Srgb &&
colorConfig.MatrixCoefficients == ObuMatrixCoefficients.Identity)
{
colorConfig.ColorRange = true;
colorConfig.SubSamplingX = false;
colorConfig.SubSamplingY = false;
}
else
{
writer.WriteBoolean(colorConfig.ColorRange);
if (sequenceHeader.SequenceProfile == ObuSequenceProfile.Professional && colorConfig.BitDepth == 12)
{
writer.WriteBoolean(colorConfig.SubSamplingX);
if (colorConfig.SubSamplingX)
{
writer.WriteBoolean(colorConfig.SubSamplingY);
}
}
if (colorConfig.SubSamplingX && colorConfig.SubSamplingY)
{
writer.WriteLiteral((uint)colorConfig.ChromaSamplePosition, 2);
}
}
writer.WriteBoolean(colorConfig.HasSeparateUvDeltaQ);
}
private static void WriteBitDepth(ref Av1BitStreamWriter writer, ObuColorConfig colorConfig, ObuSequenceHeader sequenceHeader)
{
bool hasHighBitDepth = colorConfig.BitDepth > 8;
writer.WriteBoolean(hasHighBitDepth);
if (sequenceHeader.SequenceProfile == ObuSequenceProfile.Professional && hasHighBitDepth)
{
writer.WriteBoolean(colorConfig.BitDepth == 12);
}
}
private static void WriteSuperResolutionParameters(ref Av1BitStreamWriter writer, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo)
{
bool useSuperResolution = false;
if (sequenceHeader.EnableSuperResolution)
{
writer.WriteBoolean(useSuperResolution);
}
if (useSuperResolution)
{
writer.WriteLiteral((uint)frameInfo.FrameSize.SuperResolutionDenominator - ObuConstants.SuperResolutionScaleDenominatorMinimum, ObuConstants.SuperResolutionScaleBits);
}
}
private static void WriteRenderSize(ref Av1BitStreamWriter writer, ObuFrameHeader frameInfo)
{
bool renderSizeAndFrameSizeDifferent = false;
writer.WriteBoolean(false);
if (renderSizeAndFrameSizeDifferent)
{
writer.WriteLiteral((uint)frameInfo.FrameSize.RenderWidth - 1, 16);
writer.WriteLiteral((uint)frameInfo.FrameSize.RenderHeight - 1, 16);
}
}
private static void WriteFrameSizeWithReferences(ref Av1BitStreamWriter writer, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, bool frameSizeOverrideFlag)
{
bool foundReference = false;
for (int i = 0; i < ObuConstants.ReferencesPerFrame; i++)
{
writer.WriteBoolean(foundReference);
if (foundReference)
{
// Take values over from reference frame
break;
}
}
if (!foundReference)
{
WriteFrameSize(ref writer, sequenceHeader, frameInfo, frameSizeOverrideFlag);
WriteRenderSize(ref writer, frameInfo);
}
else
{
WriteSuperResolutionParameters(ref writer, sequenceHeader, frameInfo);
}
}
private static void WriteFrameSize(ref Av1BitStreamWriter writer, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, bool frameSizeOverrideFlag)
{
if (frameSizeOverrideFlag)
{
writer.WriteLiteral((uint)frameInfo.FrameSize.FrameWidth - 1, sequenceHeader.FrameWidthBits + 1);
writer.WriteLiteral((uint)frameInfo.FrameSize.FrameHeight - 1, sequenceHeader.FrameHeightBits + 1);
}
WriteSuperResolutionParameters(ref writer, sequenceHeader, frameInfo);
}
private static void WriteTileInfo(ref Av1BitStreamWriter writer, ObuTileInfo tileInfo)
{
Guard.IsTrue(tileInfo.HasUniformTileSpacing, nameof(tileInfo.HasUniformTileSpacing), "NON uniform_tile_spacing_flag not supported yet.");
writer.WriteBoolean(tileInfo.HasUniformTileSpacing);
if (tileInfo.HasUniformTileSpacing)
{
for (int i = 0; i < tileInfo.TileColumnCountLog2; i++)
{
writer.WriteBoolean(true);
}
if (tileInfo.TileColumnCountLog2 < tileInfo.MaxLog2TileColumnCount)
{
writer.WriteBoolean(false);
}
for (int i = 0; i < tileInfo.TileRowCountLog2; i++)
{
writer.WriteBoolean(true);
}
if (tileInfo.TileRowCountLog2 < tileInfo.MaxLog2TileRowCount)
{
writer.WriteBoolean(false);
}
}
else
{
throw new NotImplementedException("NON uniform_tile_spacing_flag not supported yet.");
}
if (tileInfo.TileColumnCountLog2 > 0 || tileInfo.TileRowCountLog2 > 0)
{
writer.WriteLiteral(tileInfo.ContextUpdateTileId, tileInfo.TileRowCountLog2 + tileInfo.TileColumnCountLog2);
writer.WriteLiteral((uint)tileInfo.TileSizeBytes - 1, 2);
}
}
private static void WriteUncompressedFrameHeader(ref Av1BitStreamWriter writer, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, int planesCount)
{
uint previousFrameId = 0;
bool isIntraFrame = true;
int idLength = sequenceHeader.FrameIdLength - 1 + sequenceHeader.DeltaFrameIdLength - 2 + 3;
writer.WriteBoolean(frameInfo.DisableCdfUpdate);
if (frameInfo.AllowScreenContentTools)
{
writer.WriteBoolean(frameInfo.AllowScreenContentTools);
}
if (frameInfo.AllowScreenContentTools)
{
if (sequenceHeader.SequenceForceIntegerMotionVector == 1)
{
writer.WriteBoolean(frameInfo.ForceIntegerMotionVector);
}
}
bool havePreviousFrameId = !(frameInfo.FrameType == ObuFrameType.KeyFrame && frameInfo.ShowFrame);
if (havePreviousFrameId)
{
previousFrameId = frameInfo.CurrentFrameId;
}
if (sequenceHeader.IsFrameIdNumbersPresent)
{
writer.WriteLiteral(frameInfo.CurrentFrameId, 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;
}
}
}
writer.WriteLiteral(frameInfo.OrderHint, sequenceHeader.OrderHintInfo.OrderHintBits);
if (!isIntraFrame && !frameInfo.ErrorResilientMode)
{
writer.WriteLiteral(frameInfo.PrimaryReferenceFrame, 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))
{
writer.WriteLiteral(frameInfo.RefreshFrameFlags, 8);
}
if (isIntraFrame)
{
WriteFrameSize(ref writer, sequenceHeader, frameInfo, false);
WriteRenderSize(ref writer, frameInfo);
if (frameInfo.AllowScreenContentTools && frameInfo.FrameSize.RenderWidth != 0)
{
if (frameInfo.FrameSize.FrameWidth == frameInfo.FrameSize.SuperResolutionUpscaledWidth)
{
writer.WriteBoolean(frameInfo.AllowIntraBlockCopy);
}
}
}
if (frameInfo.PrimaryReferenceFrame == ObuConstants.PrimaryReferenceFrameNone)
{
SetupPastIndependence(frameInfo);
}
// GenerateNextReferenceFrameMap(sequenceHeader, frameInfo);
WriteTileInfo(ref writer, frameInfo.TilesInfo);
WriteQuantizationParameters(ref writer, frameInfo.QuantizationParameters, sequenceHeader.ColorConfig, planesCount);
WriteSegmentationParameters(ref writer, sequenceHeader, frameInfo, planesCount);
WriteFrameDeltaQParameters(ref writer, frameInfo);
WriteFrameDeltaLoopFilterParameters(ref writer, frameInfo);
WriteLoopFilterParameters(ref writer, sequenceHeader, frameInfo, planesCount);
WriteCdefParameters(ref writer, sequenceHeader, frameInfo, planesCount);
WriteLoopRestorationParameters(ref writer, sequenceHeader, frameInfo, planesCount);
WriteTransformMode(ref writer, frameInfo);
// Not applicable for INTRA frames.
// WriteFrameReferenceMode(ref writer, frameInfo.ReferenceMode, isIntraFrame);
// WriteSkipModeParameters(ref writer, sequenceHeader, frameInfo, isIntraFrame, frameInfo.ReferenceMode);
writer.WriteBoolean(frameInfo.ReducedTransformSet);
// Not applicable for INTRA frames.
// WriteGlobalMotionParameters(ref writer, sequenceHeader, frameInfo, isIntraFrame);
WriteFilmGrainFilterParameters(ref writer, frameInfo.FilmGrainParameters);
}
private static void SetupPastIndependence(ObuFrameHeader frameInfo)
{
// TODO: Initialize the loop filter parameters.
}
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 int WriteFrameHeader(ref Av1BitStreamWriter writer, IAv1TileDecoder decoder, bool writeTrailingBits)
{
ObuSequenceHeader sequenceHeader = decoder.SequenceHeader;
ObuFrameHeader frameInfo = decoder.FrameInfo;
int planeCount = sequenceHeader.ColorConfig.Monochrome ? 1 : 3;
int startBitPosition = writer.BitPosition;
WriteUncompressedFrameHeader(ref writer, sequenceHeader, frameInfo, planeCount);
if (writeTrailingBits)
{
WriteTrailingBits(ref writer);
}
AlignToByteBoundary(ref writer);
int endPosition = writer.BitPosition;
int headerBytes = (endPosition - startBitPosition) / 8;
return headerBytes;
}
private static int WriteTileGroup(ref Av1BitStreamWriter writer, ObuTileInfo tileInfo)
{
int tileCount = tileInfo.TileColumnCount * tileInfo.TileRowCount;
int startBitPosition = writer.BitPosition;
bool tileStartAndEndPresentFlag = tileCount != 0;
writer.WriteBoolean(tileStartAndEndPresentFlag);
uint tileGroupStart = 0U;
uint tileGroupEnd = (uint)tileCount - 1U;
if (tileCount != 1)
{
int tileBits = Av1Math.Log2(tileInfo.TileColumnCount) + Av1Math.Log2(tileInfo.TileRowCount);
writer.WriteLiteral(tileGroupStart, tileBits);
writer.WriteLiteral(tileGroupEnd, tileBits);
}
AlignToByteBoundary(ref writer);
int endBitPosition = writer.BitPosition;
int headerBytes = (endBitPosition - startBitPosition) / 8;
return headerBytes;
}
private static int WriteDeltaQ(ref Av1BitStreamWriter writer, int deltaQ)
{
bool isCoded = deltaQ == 0;
writer.WriteBoolean(isCoded);
if (isCoded)
{
writer.WriteSignedFromUnsigned(deltaQ, 7);
}
return deltaQ;
}
private static void WriteFrameDeltaQParameters(ref Av1BitStreamWriter writer, ObuFrameHeader frameInfo)
{
if (frameInfo.QuantizationParameters.BaseQIndex > 0)
{
writer.WriteBoolean(frameInfo.DeltaQParameters.IsPresent);
}
if (frameInfo.DeltaQParameters.IsPresent)
{
writer.WriteLiteral((uint)frameInfo.DeltaQParameters.Resolution, 2);
}
}
private static void WriteFrameDeltaLoopFilterParameters(ref Av1BitStreamWriter writer, ObuFrameHeader frameInfo)
{
if (frameInfo.DeltaQParameters.IsPresent)
{
if (!frameInfo.AllowIntraBlockCopy)
{
writer.WriteBoolean(frameInfo.DeltaLoopFilterParameters.IsPresent);
}
if (frameInfo.DeltaLoopFilterParameters.IsPresent)
{
writer.WriteLiteral((uint)frameInfo.DeltaLoopFilterParameters.Resolution, 2);
writer.WriteBoolean(frameInfo.DeltaLoopFilterParameters.Multi);
}
}
}
/// <summary>
/// See section 5.9.12.
/// </summary>
private static void WriteQuantizationParameters(ref Av1BitStreamWriter writer, ObuQuantizationParameters quantParams, ObuColorConfig colorInfo, int planesCount)
{
writer.WriteLiteral((uint)quantParams.BaseQIndex, 8);
WriteDeltaQ(ref writer, quantParams.DeltaQDc[(int)Av1Plane.Y]);
if (planesCount > 1)
{
bool areUvDeltaDifferent = false;
WriteDeltaQ(ref writer, quantParams.DeltaQDc[(int)Av1Plane.U]);
WriteDeltaQ(ref writer, quantParams.DeltaQAc[(int)Av1Plane.U]);
if (areUvDeltaDifferent)
{
WriteDeltaQ(ref writer, quantParams.DeltaQDc[(int)Av1Plane.V]);
WriteDeltaQ(ref writer, quantParams.DeltaQAc[(int)Av1Plane.V]);
}
}
writer.WriteBoolean(quantParams.IsUsingQMatrix);
if (quantParams.IsUsingQMatrix)
{
writer.WriteLiteral((uint)quantParams.QMatrix[(int)Av1Plane.Y], 4);
writer.WriteLiteral((uint)quantParams.QMatrix[(int)Av1Plane.U], 4);
if (colorInfo.HasSeparateUvDeltaQ)
{
writer.WriteLiteral((uint)quantParams.QMatrix[(int)Av1Plane.V], 4);
}
}
}
private static void WriteSegmentationParameters(ref Av1BitStreamWriter writer, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, int planesCount)
{
Guard.IsFalse(frameInfo.SegmentationParameters.SegmentationEnabled, nameof(frameInfo.SegmentationParameters.SegmentationEnabled), "Segmentatino not supported yet.");
writer.WriteBoolean(false);
}
private static void WriteLoopFilterParameters(ref Av1BitStreamWriter writer, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, int planesCount)
{
_ = writer;
_ = sequenceHeader;
_ = frameInfo;
_ = planesCount;
// TODO: Parse more stuff.
}
private static void WriteTransformMode(ref Av1BitStreamWriter writer, ObuFrameHeader frameInfo)
{
if (!frameInfo.CodedLossless)
{
writer.WriteBoolean(frameInfo.TransformMode == Av1TransformMode.Select);
}
}
private static void WriteLoopRestorationParameters(ref Av1BitStreamWriter writer, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, int planesCount)
{
_ = writer;
_ = sequenceHeader;
_ = frameInfo;
_ = planesCount;
// TODO: Parse more stuff.
}
private static void WriteCdefParameters(ref Av1BitStreamWriter writer, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, int planesCount)
{
_ = writer;
_ = sequenceHeader;
_ = frameInfo;
_ = planesCount;
// TODO: Parse more stuff.
}
private static void WriteGlobalMotionParameters(ref Av1BitStreamWriter writer, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, bool isIntraFrame)
{
_ = writer;
_ = sequenceHeader;
_ = frameInfo;
// Nothing to be written for INTRA frames.
Guard.IsTrue(isIntraFrame, nameof(isIntraFrame), "Still picture contains only INTRA frames.");
}
private static void WriteFrameReferenceMode(ref Av1BitStreamWriter writer, bool isIntraFrame)
{
_ = writer;
// Nothing to be written for INTRA frames.
Guard.IsTrue(isIntraFrame, nameof(isIntraFrame), "Still picture contains only INTRA frames.");
}
private static void WriteSkipModeParameters(ref Av1BitStreamWriter writer, bool isIntraFrame)
{
_ = writer;
// Nothing to be written for INTRA frames.
Guard.IsTrue(isIntraFrame, nameof(isIntraFrame), "Still picture contains only INTRA frames.");
}
private static void WriteFilmGrainFilterParameters(ref Av1BitStreamWriter writer, ObuFilmGrainParameters filmGrainInfo)
{
_ = writer;
_ = filmGrainInfo;
// Film grain filter not supported yet
}
}
Loading…
Cancel
Save