Browse Source

Simplify IAv1TileDecoder

pull/2633/head
Ynse Hoornenborg 2 years ago
parent
commit
e64d74d097
  1. 40
      src/ImageSharp/Formats/Heif/Av1/Av1Decoder.cs
  2. 31
      src/ImageSharp/Formats/Heif/Av1/IAv1TileDecoder.cs
  3. 2
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuFrameHeader.cs
  4. 62
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuReader.cs
  5. 2
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuTileGroupHeader.cs
  6. 16
      src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuWriter.cs
  7. 20
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileDecoder.cs
  8. 15
      tests/ImageSharp.Tests/Formats/Heif/Av1/Av1TileDecoderStub.cs
  9. 28
      tests/ImageSharp.Tests/Formats/Heif/Av1/ObuFrameHeaderTests.cs

40
src/ImageSharp/Formats/Heif/Av1/Av1Decoder.cs

@ -8,38 +8,36 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1;
internal class Av1Decoder : IAv1TileDecoder
{
private readonly Av1TileDecoder tileDecoder;
private readonly ObuReader obuReader;
private Av1TileDecoder? tileDecoder;
private Av1FrameBuffer? frameBuffer;
public Av1Decoder()
{
this.FrameInfo = new ObuFrameHeader();
this.SequenceHeader = new ObuSequenceHeader();
this.TileInfo = new ObuTileInfo();
this.SeenFrameHeader = false;
this.tileDecoder = new Av1TileDecoder(this.SequenceHeader, this.FrameInfo, this.TileInfo);
}
public bool SequenceHeaderDone { get; set; }
public bool ShowExistingFrame { get; set; }
public Av1Decoder() => this.obuReader = new();
public bool SeenFrameHeader { get; set; }
public ObuFrameHeader? FrameHeader { get; private set; }
public ObuFrameHeader FrameInfo { get; }
public ObuSequenceHeader? SequenceHeader { get; private set; }
public ObuSequenceHeader SequenceHeader { get; }
public ObuTileInfo TileInfo { get; }
public ObuTileGroupHeader? TilesHeader { get; private set; }
public void Decode(Span<byte> buffer)
{
Av1BitStreamReader reader = new(buffer);
ObuReader.Read(ref reader, buffer.Length, this, false);
this.obuReader.Read(ref reader, buffer.Length, this, false);
this.frameBuffer = this.tileDecoder?.FrameBuffer;
}
public void StartDecodeTiles()
{
this.SequenceHeader = this.obuReader.SequenceHeader;
this.FrameHeader = this.obuReader.FrameHeader;
this.TilesHeader = this.obuReader.TileGroupHeader;
this.tileDecoder = new Av1TileDecoder(this.SequenceHeader!, this.FrameHeader!, this.TilesHeader!);
}
public void DecodeTile(Span<byte> tileData, int tileNum)
=> this.tileDecoder.DecodeTile(tileData, tileNum);
=> this.tileDecoder!.DecodeTile(tileData, tileNum);
public void FinishDecodeTiles(bool doCdef, bool doLoopRestoration)
=> this.tileDecoder.FinishDecodeTiles(doCdef, doLoopRestoration);
=> this.tileDecoder!.FinishDecodeTiles(doCdef, doLoopRestoration);
}

31
src/ImageSharp/Formats/Heif/Av1/IAv1TileDecoder.cs

@ -1,8 +1,6 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
namespace SixLabors.ImageSharp.Formats.Heif.Av1;
/// <summary>
@ -11,34 +9,9 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1;
internal interface IAv1TileDecoder
{
/// <summary>
/// Gets or sets a value indicating whether a sequence header has been read.
/// </summary>
bool SequenceHeaderDone { get; set; }
/// <summary>
/// Gets or sets a value indicating whether to show the existing frame.
/// </summary>
bool ShowExistingFrame { get; set; }
/// <summary>
/// Gets or sets a value indicating whether a FrameHeader has just been read.
/// </summary>
bool SeenFrameHeader { get; set; }
/// <summary>
/// Gets Information about the frame.
/// </summary>
ObuFrameHeader FrameInfo { get; }
/// <summary>
/// Gets Information about the sequence of frames.
/// </summary>
ObuSequenceHeader SequenceHeader { get; }
/// <summary>
/// Gets information required to decode the tiles of a frame.
/// Start decoding all tiles of a frame.
/// </summary>
ObuTileInfo TileInfo { get; }
void StartDecodeTiles();
/// <summary>
/// Decode a single tile.

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

@ -15,7 +15,7 @@ internal class ObuFrameHeader
public bool AllowHighPrecisionMotionVector { get; set; }
public ObuTileInfo TilesInfo { get; set; } = new ObuTileInfo();
public ObuTileGroupHeader TilesInfo { get; set; } = new ObuTileGroupHeader();
public bool CodedLossless { get; set; }

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

@ -7,11 +7,18 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
internal class ObuReader
{
public ObuSequenceHeader? SequenceHeader { get; set; }
public ObuFrameHeader? FrameHeader { get; set; }
public ObuTileGroupHeader? TileGroupHeader { get; set; }
/// <summary>
/// Decode all OBU's in a frame.
/// </summary>
public static void Read(ref Av1BitStreamReader reader, int dataSize, IAv1TileDecoder decoder, bool isAnnexB = false)
public void Read(ref Av1BitStreamReader reader, int dataSize, IAv1TileDecoder decoder, bool isAnnexB = false)
{
bool seenFrameHeader = false;
bool frameDecodingFinished = false;
while (!frameDecodingFinished)
{
@ -40,34 +47,35 @@ internal class ObuReader
switch (header.Type)
{
case ObuType.SequenceHeader:
ReadSequenceHeader(ref reader, decoder.SequenceHeader);
if (decoder.SequenceHeader.ColorConfig.BitDepth == 12)
this.SequenceHeader = new();
ReadSequenceHeader(ref reader, this.SequenceHeader);
if (this.SequenceHeader.ColorConfig.BitDepth == 12)
{
// TODO: Initialize 12 bit predictors
}
decoder.SequenceHeaderDone = true;
break;
case ObuType.FrameHeader:
case ObuType.RedundantFrameHeader:
case ObuType.Frame:
if (header.Type != ObuType.Frame)
{
decoder.ShowExistingFrame = false;
// Nothing to do here.
}
else if (header.Type != ObuType.FrameHeader)
{
Guard.IsFalse(decoder.SeenFrameHeader, nameof(Av1Decoder.SeenFrameHeader), "Frame header expected");
Guard.IsFalse(seenFrameHeader, nameof(seenFrameHeader), "Frame header expected");
}
else
{
Guard.IsTrue(decoder.SeenFrameHeader, nameof(Av1Decoder.SeenFrameHeader), "Already decoded a frame header");
Guard.IsTrue(seenFrameHeader, nameof(seenFrameHeader), "Already decoded a frame header");
}
if (!decoder.SeenFrameHeader)
if (!seenFrameHeader)
{
decoder.SeenFrameHeader = true;
ReadFrameHeader(ref reader, decoder, header, header.Type != ObuType.Frame);
seenFrameHeader = true;
this.FrameHeader = new();
this.ReadFrameHeader(ref reader, header, header.Type != ObuType.Frame);
}
if (header.Type != ObuType.Frame)
@ -78,15 +86,16 @@ internal class ObuReader
goto TILE_GROUP;
case ObuType.TileGroup:
TILE_GROUP:
if (!decoder.SeenFrameHeader)
if (!seenFrameHeader)
{
throw new InvalidImageContentException("Corrupt frame");
}
ReadTileGroup(ref reader, decoder, header, out frameDecodingFinished);
this.TileGroupHeader = new();
this.ReadTileGroup(ref reader, decoder, header, out frameDecodingFinished);
if (frameDecodingFinished)
{
decoder.SeenFrameHeader = false;
seenFrameHeader = false;
}
break;
@ -465,9 +474,9 @@ internal class ObuReader
ComputeImageSize(sequenceHeader, frameInfo);
}
private static ObuTileInfo ReadTileInfo(ref Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo)
private static ObuTileGroupHeader ReadTileInfo(ref Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo)
{
ObuTileInfo tileInfo = new();
ObuTileGroupHeader tileInfo = new();
int superBlockColumnCount;
int superBlockRowCount;
int superBlockShift;
@ -633,10 +642,10 @@ internal class ObuReader
return tileInfo;
}
private static void ReadUncompressedFrameHeader(ref Av1BitStreamReader reader, IAv1TileDecoder decoder, ObuHeader header, int planesCount)
private void ReadUncompressedFrameHeader(ref Av1BitStreamReader reader, ObuHeader header, int planesCount)
{
ObuSequenceHeader sequenceHeader = decoder.SequenceHeader;
ObuFrameHeader frameInfo = decoder.FrameInfo;
ObuSequenceHeader sequenceHeader = this.SequenceHeader!;
ObuFrameHeader frameInfo = this.FrameHeader!;
int idLength = 0;
uint previousFrameId = 0;
bool isIntraFrame = false;
@ -915,13 +924,11 @@ internal class ObuReader
}
}
private static void ReadFrameHeader(ref Av1BitStreamReader reader, IAv1TileDecoder decoder, ObuHeader header, bool trailingBit)
private void ReadFrameHeader(ref Av1BitStreamReader reader, ObuHeader header, bool trailingBit)
{
ObuSequenceHeader sequenceHeader = decoder.SequenceHeader;
ObuFrameHeader frameInfo = decoder.FrameInfo;
int planeCount = sequenceHeader.ColorConfig.IsMonochrome ? 1 : 3;
int planeCount = this.SequenceHeader!.ColorConfig.IsMonochrome ? 1 : 3;
int startBitPosition = reader.BitPosition;
ReadUncompressedFrameHeader(ref reader, decoder, header, planeCount);
this.ReadUncompressedFrameHeader(ref reader, header, planeCount);
if (trailingBit)
{
ReadTrailingBits(ref reader);
@ -934,11 +941,12 @@ internal class ObuReader
header.PayloadSize -= headerBytes;
}
private static void ReadTileGroup(ref Av1BitStreamReader reader, IAv1TileDecoder decoder, ObuHeader header, out bool isLastTileGroup)
private void ReadTileGroup(ref Av1BitStreamReader reader, IAv1TileDecoder decoder, ObuHeader header, out bool isLastTileGroup)
{
ObuSequenceHeader sequenceHeader = decoder.SequenceHeader;
ObuFrameHeader frameInfo = decoder.FrameInfo;
ObuTileInfo tileInfo = decoder.TileInfo;
ObuSequenceHeader sequenceHeader = this.SequenceHeader!;
ObuFrameHeader frameInfo = this.FrameHeader!;
ObuTileGroupHeader tileInfo = this.TileGroupHeader!;
this.TileGroupHeader = tileInfo;
int tileCount = tileInfo.TileColumnCount * tileInfo.TileRowCount;
int startBitPosition = reader.BitPosition;
bool tileStartAndEndPresentFlag = false;

2
src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuTileInfo.cs → src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuTileGroupHeader.cs

@ -3,7 +3,7 @@
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
internal class ObuTileInfo
internal class ObuTileGroupHeader
{
internal int MaxTileWidthSuperBlock { get; set; }

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

@ -10,23 +10,23 @@ internal class ObuWriter
/// <summary>
/// Encode a single frame into OBU's.
/// </summary>
public static void Write(Stream stream, IAv1TileDecoder decoder)
public static void Write(Stream stream, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, ObuTileGroupHeader tileInfo)
{
MemoryStream bufferStream = new(100);
Av1BitStreamWriter writer = new(bufferStream);
WriteObuHeaderAndSize(stream, ObuType.TemporalDelimiter, [], 0);
WriteSequenceHeader(ref writer, decoder.SequenceHeader);
WriteSequenceHeader(ref writer, sequenceHeader);
writer.Flush();
WriteObuHeaderAndSize(stream, ObuType.SequenceHeader, bufferStream.GetBuffer(), (int)bufferStream.Position);
bufferStream.Position = 0;
WriteFrameHeader(ref writer, decoder, true);
WriteFrameHeader(ref writer, sequenceHeader, frameInfo, true);
writer.Flush();
WriteObuHeaderAndSize(stream, ObuType.FrameHeader, bufferStream.GetBuffer(), (int)bufferStream.Position);
bufferStream.Position = 0;
WriteTileGroup(ref writer, decoder.TileInfo);
WriteTileGroup(ref writer, tileInfo);
writer.Flush();
WriteObuHeaderAndSize(stream, ObuType.TileGroup, bufferStream.GetBuffer(), (int)bufferStream.Position);
}
@ -218,7 +218,7 @@ internal class ObuWriter
WriteSuperResolutionParameters(ref writer, sequenceHeader, frameInfo);
}
private static void WriteTileInfo(ref Av1BitStreamWriter writer, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, ObuTileInfo tileInfo)
private static void WriteTileInfo(ref Av1BitStreamWriter writer, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, ObuTileGroupHeader tileInfo)
{
int superBlockColumnCount;
int superBlockRowCount;
@ -444,10 +444,8 @@ internal class ObuWriter
}
}
private static int WriteFrameHeader(ref Av1BitStreamWriter writer, IAv1TileDecoder decoder, bool writeTrailingBits)
private static int WriteFrameHeader(ref Av1BitStreamWriter writer, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, bool writeTrailingBits)
{
ObuSequenceHeader sequenceHeader = decoder.SequenceHeader;
ObuFrameHeader frameInfo = decoder.FrameInfo;
int planeCount = sequenceHeader.ColorConfig.IsMonochrome ? 1 : 3;
int startBitPosition = writer.BitPosition;
WriteUncompressedFrameHeader(ref writer, sequenceHeader, frameInfo, planeCount);
@ -463,7 +461,7 @@ internal class ObuWriter
return headerBytes;
}
private static int WriteTileGroup(ref Av1BitStreamWriter writer, ObuTileInfo tileInfo)
private static int WriteTileGroup(ref Av1BitStreamWriter writer, ObuTileGroupHeader tileInfo)
{
int tileCount = tileInfo.TileColumnCount * tileInfo.TileRowCount;
int startBitPosition = writer.BitPosition;

20
src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileDecoder.cs

@ -29,16 +29,15 @@ internal class Av1TileDecoder : IAv1TileDecoder
private int maxLumaHeight;
private int deltaLoopFilterResolution = -1;
private int deltaQuantizerResolution = -1;
private readonly Av1FrameBuffer frameBuffer;
public Av1TileDecoder(ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, ObuTileInfo tileInfo)
public Av1TileDecoder(ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, ObuTileGroupHeader tileInfo)
{
this.FrameInfo = frameInfo;
this.SequenceHeader = sequenceHeader;
this.TileInfo = tileInfo;
// init_main_frame_ctxt
this.frameBuffer = new(this.SequenceHeader);
this.FrameBuffer = new(this.SequenceHeader);
}
public bool SequenceHeaderDone { get; set; }
@ -51,7 +50,9 @@ internal class Av1TileDecoder : IAv1TileDecoder
public ObuSequenceHeader SequenceHeader { get; }
public ObuTileInfo TileInfo { get; }
public ObuTileGroupHeader TileInfo { get; }
public Av1FrameBuffer FrameBuffer { get; }
public void DecodeTile(Span<byte> tileData, int tileNum)
{
@ -93,7 +94,7 @@ internal class Av1TileDecoder : IAv1TileDecoder
this.ClearBlockDecodedFlags(row, column, superBlock4x4Size);
Point superblockPosition = new Point(superBlockColumn, superBlockRow);
Av1SuperblockInfo superblockInfo = new(this.frameBuffer, superblockPosition);
Av1SuperblockInfo superblockInfo = new(this.FrameBuffer, superblockPosition);
// Nothing to do for CDEF
// this.ClearCdef(row, column);
@ -104,7 +105,7 @@ internal class Av1TileDecoder : IAv1TileDecoder
}
private void ClearLoopFilterDelta()
=> this.frameBuffer.ClearDeltaLoopFilter();
=> this.FrameBuffer.ClearDeltaLoopFilter();
private void ClearBlockDecodedFlags(int row, int column, int superBlock4x4Size)
{
@ -152,6 +153,11 @@ internal class Av1TileDecoder : IAv1TileDecoder
}
}
public void StartDecodeTiles()
{
// TODO: Implement
}
public void FinishDecodeTiles(bool doCdef, bool doLoopRestoration)
{
// TODO: Implement
@ -402,7 +408,7 @@ internal class Av1TileDecoder : IAv1TileDecoder
private void TransformBlock(int plane, int baseX, int baseY, Av1TransformSize transformSize, int x, int y)
{
Av1PartitionInfo partitionInfo = new(new(1, Av1BlockSize.Invalid), new(this.frameBuffer, default), false, Av1PartitionType.None);
Av1PartitionInfo partitionInfo = new(new(1, Av1BlockSize.Invalid), new(this.FrameBuffer, default), false, Av1PartitionType.None);
int startX = (baseX + 4) * x;
int startY = (baseY + 4) * y;
bool subsamplingX = this.SequenceHeader.ColorConfig.SubSamplingX;

15
tests/ImageSharp.Tests/Formats/Heif/Av1/Av1TileDecoderStub.cs

@ -2,23 +2,14 @@
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Heif.Av1;
using SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
namespace SixLabors.ImageSharp.Tests.Formats.Heif.Av1;
internal class Av1TileDecoderStub : IAv1TileDecoder
{
public bool SequenceHeaderDone { get; set; }
public bool ShowExistingFrame { get; set; }
public bool SeenFrameHeader { get; set; }
public ObuFrameHeader FrameInfo { get; } = new ObuFrameHeader();
public ObuSequenceHeader SequenceHeader { get; } = new ObuSequenceHeader();
public ObuTileInfo TileInfo { get; } = new ObuTileInfo();
public void StartDecodeTiles()
{
}
public void DecodeTile(Span<byte> tileData, int tileNum)
{

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

@ -5,7 +5,6 @@ using System.Reflection;
using System.Text;
using SixLabors.ImageSharp.Formats.Heif.Av1;
using SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
using SixLabors.ImageSharp.Formats.Heif.Av1.Symbol;
namespace SixLabors.ImageSharp.Tests.Formats.Heif.Av1;
@ -22,14 +21,17 @@ public class ObuFrameHeaderTests
string filePath = Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, filename);
byte[] content = File.ReadAllBytes(filePath);
Span<byte> span = content.AsSpan(fileOffset, blockSize);
Av1Decoder decoder = new();
Av1BitStreamReader reader = new(span);
IAv1TileDecoder decoder = new Av1TileDecoderStub();
ObuReader obuReader = new();
// Act
decoder.Decode(span);
obuReader.Read(ref reader, blockSize, decoder);
// Assert
Assert.True(decoder.SequenceHeaderDone);
Assert.False(decoder.SeenFrameHeader);
Assert.NotNull(obuReader.SequenceHeader);
Assert.NotNull(obuReader.FrameHeader);
Assert.NotNull(obuReader.TileGroupHeader);
}
/* [Theory]
@ -67,29 +69,31 @@ public class ObuFrameHeaderTests
Span<byte> span = content.AsSpan(fileOffset, blockSize);
IAv1TileDecoder tileDecoder = new Av1TileDecoderStub();
Av1BitStreamReader reader = new(span);
ObuReader obuReader1 = new();
// Act 1
ObuReader.Read(ref reader, blockSize, tileDecoder);
obuReader1.Read(ref reader, blockSize, tileDecoder);
// Assign 2
MemoryStream encoded = new();
// Act 2
ObuWriter.Write(encoded, tileDecoder);
ObuWriter.Write(encoded, obuReader1.SequenceHeader, obuReader1.FrameHeader, obuReader1.TileGroupHeader);
// Assign 2
Span<byte> encodedBuffer = encoded.ToArray();
IAv1TileDecoder tileDecoder2 = new Av1TileDecoderStub();
Av1BitStreamReader reader2 = new(span);
ObuReader obuReader2 = new();
// Act 2
ObuReader.Read(ref reader2, encodedBuffer.Length, tileDecoder2);
obuReader2.Read(ref reader2, encodedBuffer.Length, tileDecoder2);
// Assert
Assert.Equal(PrettyPrintProperties(tileDecoder.SequenceHeader.ColorConfig), PrettyPrintProperties(tileDecoder2.SequenceHeader.ColorConfig));
Assert.Equal(PrettyPrintProperties(tileDecoder.SequenceHeader), PrettyPrintProperties(tileDecoder2.SequenceHeader));
Assert.Equal(PrettyPrintProperties(tileDecoder.FrameInfo), PrettyPrintProperties(tileDecoder2.FrameInfo));
Assert.Equal(PrettyPrintProperties(tileDecoder.TileInfo), PrettyPrintProperties(tileDecoder2.TileInfo));
Assert.Equal(PrettyPrintProperties(obuReader1.SequenceHeader.ColorConfig), PrettyPrintProperties(obuReader2.SequenceHeader.ColorConfig));
Assert.Equal(PrettyPrintProperties(obuReader1.SequenceHeader), PrettyPrintProperties(obuReader2.SequenceHeader));
Assert.Equal(PrettyPrintProperties(obuReader1.FrameHeader), PrettyPrintProperties(obuReader2.FrameHeader));
Assert.Equal(PrettyPrintProperties(obuReader1.TileGroupHeader), PrettyPrintProperties(obuReader2.TileGroupHeader));
}
private static string PrettyPrintProperties(object obj)

Loading…
Cancel
Save