diff --git a/HEIF_IMPLEMENTATION_PLAN.md b/HEIF_IMPLEMENTATION_PLAN.md
index fe12ce7ee4..fe99ed305f 100644
--- a/HEIF_IMPLEMENTATION_PLAN.md
+++ b/HEIF_IMPLEMENTATION_PLAN.md
@@ -869,6 +869,7 @@ Encoder verification contract:
### 7. Write complete AVIF output
+- [~] The encoder-side AV1 codec configuration is now derived directly from the encoded sequence header and writes the fixed four-byte `av1C` record with empty `configOBUs`. The image payload retains the required sequence header, so the property introduces no sequence-header allocation, retention, or copy. Four production-header cases cover main, high, and professional profiles; 8-, 10-, and 12-bit precision; monochrome, 4:2:0, 4:2:2, and 4:4:4 sampling; exact fixed bytes; decoder reparsing; and header/property equivalence through direct net11 Release VSTest. Property-container emission and public AVIF activation remain open.
- [ ] Write the correct AVIF file type, item information, locations, references, properties, AV1 configuration, dimensions, color, alpha, metadata, and media data.
- [ ] Support single images, alpha auxiliary images, grids, multiple extents, and bounded image sequences in the final public scope.
- [ ] Preserve ICC, Exif, and XMP according to encoder options.
diff --git a/src/ImageSharp/Formats/Heif/Av1/Av1CodecConfiguration.cs b/src/ImageSharp/Formats/Heif/Av1/Av1CodecConfiguration.cs
index c1fa28592b..5e39a3f5d7 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Av1CodecConfiguration.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Av1CodecConfiguration.cs
@@ -12,6 +12,11 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1;
///
internal sealed class Av1CodecConfiguration
{
+ ///
+ /// The number of bytes in the fixed AV1 codec-configuration record.
+ ///
+ public const int FixedHeaderSize = 4;
+
///
/// The optional sequence-header payload retained from the configuration open bitstream units.
///
@@ -40,7 +45,7 @@ internal sealed class Av1CodecConfiguration
/// The general options governing metadata validation.
public Av1CodecConfiguration(Span boxBuffer, DecoderOptions options)
{
- if (boxBuffer.Length < 4)
+ if (boxBuffer.Length < FixedHeaderSize)
{
throw new InvalidImageContentException("The AV1 codec configuration is truncated.");
}
@@ -83,7 +88,7 @@ internal sealed class Av1CodecConfiguration
// The delay syntax is consumed to validate the fixed record, but it describes sample presentation and has
// no meaning for the independently presented image item supported by this bounded container implementation.
- ReadOnlySpan configObus = boxBuffer[4..];
+ ReadOnlySpan configObus = boxBuffer[FixedHeaderSize..];
int sequenceHeaderCount = ScanObus(
configObus,
true,
@@ -114,6 +119,30 @@ internal sealed class Av1CodecConfiguration
}
}
+ ///
+ /// Initializes a new instance of the class from an encoded sequence header.
+ ///
+ /// The sequence header describing the encoded image item.
+ public Av1CodecConfiguration(ObuSequenceHeader sequenceHeader)
+ {
+ ObuOperatingPoint operatingPoint = sequenceHeader.OperatingPoint[0];
+ ObuColorConfig colorConfig = sequenceHeader.ColorConfig;
+
+ this.SequenceProfile = (byte)sequenceHeader.SequenceProfile;
+ this.SequenceLevelIndex = (byte)operatingPoint.SequenceLevelIndex;
+ this.SequenceTier = operatingPoint.SequenceTier != 0;
+ this.HighBitDepth = colorConfig.BitDepth is Av1BitDepth.TenBit or Av1BitDepth.TwelveBit;
+ this.TwelveBit = colorConfig.BitDepth == Av1BitDepth.TwelveBit;
+ this.IsMonochrome = colorConfig.IsMonochrome;
+ this.ChromaSubsamplingX = colorConfig.SubSamplingX;
+ this.ChromaSubsamplingY = colorConfig.SubSamplingY;
+ this.ChromaSamplePosition = (byte)colorConfig.ChromaSamplePosition;
+ this.configSequenceHeader = [];
+ this.configSequenceHeaderExtension = -1;
+ this.configContentLightLevel = null;
+ this.configMasteringDisplayColorVolume = null;
+ }
+
///
/// Gets the sequence profile declared for the coded image.
///
@@ -164,6 +193,27 @@ internal sealed class Av1CodecConfiguration
///
public byte ChromaSamplePosition { get; }
+ ///
+ /// Writes the fixed AV1 codec-configuration record without optional configuration OBUs.
+ ///
+ /// The destination receiving the four-byte record.
+ public void WriteFixedHeader(Span destination)
+ {
+ // The image item payload already begins with its required sequence header. Keeping configOBUs empty avoids
+ // retaining or copying the same OBU into the configuration property.
+ destination[0] = 0x81;
+ destination[1] = (byte)((this.SequenceProfile << 5) | this.SequenceLevelIndex);
+ destination[2] = (byte)(
+ (this.SequenceTier ? 1 << 7 : 0)
+ | (this.HighBitDepth ? 1 << 6 : 0)
+ | (this.TwelveBit ? 1 << 5 : 0)
+ | (this.IsMonochrome ? 1 << 4 : 0)
+ | (this.ChromaSubsamplingX ? 1 << 3 : 0)
+ | (this.ChromaSubsamplingY ? 1 << 2 : 0)
+ | this.ChromaSamplePosition);
+ destination[3] = 0;
+ }
+
///
/// Validates the AV1 image item OBU layout and metadata against its item properties and configuration record.
///
diff --git a/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1EncoderFrameTests.cs b/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1EncoderFrameTests.cs
index 15d29eb4bd..b44808faef 100644
--- a/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1EncoderFrameTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1EncoderFrameTests.cs
@@ -1,6 +1,7 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
+using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Heif.Av1;
using SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
using SixLabors.ImageSharp.Formats.Heif.Av1.Pipeline;
@@ -251,6 +252,38 @@ public class Av1EncoderFrameTests
Assert.Equal(allocation.AllocationId, returned.AllocationId);
}
+ [Theory]
+ [InlineData(EightBit, Yuv400, 0x1F, 0x1C)]
+ [InlineData(TenBit, Yuv420, 0x1F, 0x4C)]
+ [InlineData(TenBit, Yuv444, 0x3F, 0x40)]
+ [InlineData(TwelveBit, Yuv422, 0x5F, 0x68)]
+ public void CodecConfigurationWritesFixedHeaderFromEncodedSequenceHeader(
+ int bitDepthValue,
+ int colorFormatValue,
+ byte expectedProfileAndLevel,
+ byte expectedColorFlags)
+ {
+ Av1BitDepth bitDepth = (Av1BitDepth)bitDepthValue;
+ Av1ColorFormat colorFormat = (Av1ColorFormat)colorFormatValue;
+ using Image source = new(8, 8);
+ using MemoryStream stream = new();
+ ObuSequenceHeader sequenceHeader = Av1FrameEncoder.Encode(
+ Configuration.Default,
+ source.Frames.RootFrame,
+ stream,
+ CreateColorConfig(bitDepth, colorFormat),
+ qIndex: 37);
+
+ Av1CodecConfiguration configuration = new(sequenceHeader);
+ byte[] fixedHeader = new byte[Av1CodecConfiguration.FixedHeaderSize];
+ configuration.WriteFixedHeader(fixedHeader);
+
+ Assert.Equal([0x81, expectedProfileAndLevel, expectedColorFlags, 0x00], fixedHeader);
+ Av1CodecConfiguration parsed = new(fixedHeader, new DecoderOptions());
+ Assert.True(configuration.HasMatchingImageConfiguration(parsed));
+ parsed.Validate(sequenceHeader);
+ }
+
[Fact]
public void ScreenContentDetectorMatchesLibaomFeatureThresholds()
{