Browse Source

Parse HEVC video parameter sets

pull/2633/head
James Jackson-South 1 week ago
parent
commit
aee767f782
  1. 1
      HEIF_IMPLEMENTATION_PLAN.md
  2. 28
      src/ImageSharp/Formats/Heif/Hevc/HevcBitReader.cs
  3. 36
      src/ImageSharp/Formats/Heif/Hevc/HevcCodecConfiguration.cs
  4. 115
      src/ImageSharp/Formats/Heif/Hevc/HevcProfileTierLevel.cs
  5. 251
      src/ImageSharp/Formats/Heif/Hevc/HevcVideoParameterSet.cs

1
HEIF_IMPLEMENTATION_PLAN.md

@ -113,6 +113,7 @@ This snapshot pins or classifies the available references and failures; it does
| `Av1CodecConfiguration`, `HeifItem.Av1CodecConfiguration`, `Av1HeifItemDecoder`, and AV1 grid configuration checks | AV1-ISOBMFF sections 2.3.3 and 2.3.4 codec-configuration record syntax and semantics; AVIF sections 2.1, 2.2.1, and 2.2.3 AV1 image-item, item-configuration, and HDR metadata constraints; AV1 sections 5.8.3, 5.8.4, 6.7.3, and 6.7.4 HDR metadata syntax and semantics; ISOBMFF mastering-display and content-light image properties; ITU-T H.274 section 8.9 mastering-display field semantics; MIAF section 7.3.11.4.1 grid input constraints | libavif `src/read.c` functions `avifParseCodecConfiguration`, `avifDecoderItemValidateProperties`, `avifReadCodecConfigProperty`, `avifParseContentLightLevelInformation`, and `avifSkipMasteringDisplayColourVolume` at `092276ce89098ead06db80975173191e5fee1826`; libaom `av1/decoder/obu.c` functions `read_metadata`, `read_metadata_hdr_cll`, and `read_metadata_hdr_mdcv` at `03087864cf4bea6abb0d28f95cf7843511413d8f` | Associate exactly one `av1C` property with each decoded `av01` image item, validate the fixed record and its bit depth/chroma fields against the item's AV1 sequence header and optional `pixi` channel depths, require matching configurations across grid tiles, and report the encoded image precision and monochrome shape through `HeifMetadata`. Validate low-overhead OBU framing, require exactly one sequence header in the image item, allow at most one first-position sequence header in `configOBUs`, and compare a repeated header's extension and payload exactly while ignoring only its legal size-field representation. Decode `clli` and `mdcv` as still-image item properties, validate matching HDR CLL and HDR MDCV metadata OBUs from the combined configuration/item sequence, and account for the different primary order and fixed-point precision of the ISOBMFF and AV1 MDCV representations. Expose the effective HDR values without adding sample groups, tracks, or media metadata. Related still-image HDR properties remain required. Consume but do not retain presentation-delay syntax, and introduce no sample entry, sample description, track, timing, or generic decoder-configuration model. |
| `HevcCodecConfiguration`, `HevcNalUnitArray`, and `HeifItem.HevcCodecConfiguration` | ISO/IEC 14496-15 `HEVCDecoderConfigurationRecord`, HEVC NAL-unit header syntax, and HEVC image-item configuration requirements | HM `source/Lib/TLibDecoder/NALread.cpp` and parameter-header paths at `9c1f298659ab0cee9dc13d23d0304221575410b9`; Android `libhevc` v1.6.0 decoder NAL and parameter-header paths at `c83a76b084498d55f252f48b2e3786804cdf24b7`; independently produced HEIC fixtures for `hvcC` record layout | Associate exactly one `hvcC` property with each `hvc1` image item; validate the fixed profile, level, chroma, bit-depth, reserved, length-size, array, and NAL-header fields within the property boundary; and retain only the image description plus bounded configuration NAL units required to decode that item. Consume but do not retain average-frame-rate or temporal-layer presentation fields. Do not add visual sample entries, sample descriptions, tracks, timing, access-unit timelines, or a generic decoder-configuration model. BSD and Apache-2.0 notice preservation and separate HEVC patent/release clearance remain final integration gates. |
| `HevcNalUnit`, `HevcNalUnitHeader`, `HevcRbspDecoder`, and `HevcBitReader` | HEVC sections 7.3.1.1 and 7.3.1.2 NAL-unit and header syntax, section 7.3.2.11 RBSP trailing bits, and section 9.1 Exp-Golomb parsing | HM `source/Lib/TLibDecoder/NALread.cpp` and `SyntaxElementParser.cpp` at `9c1f298659ab0cee9dc13d23d0304221575410b9`; Android `libhevc` NAL extraction and bitstream paths at `c83a76b084498d55f252f48b2e3786804cdf24b7` | Validate the fixed NAL header, remove only legal emulation-prevention bytes, and provide bounded fixed-width, flag, unsigned/signed Exp-Golomb, and RBSP-trailing-bit reads. Configuration arrays immediately use the shared parser. The primitives consume only NAL units delivered by a supported HEIF image item or its `hvcC` property and introduce no Annex B API, access-unit model, timeline, track, sample table, or generic ISO BMFF surface. |
| `HevcProfileTierLevel`, `HevcVideoParameterSet`, and `HevcCodecConfiguration.VideoParameterSets` | HEVC sections 7.3.2.1 video parameter sets, 7.3.3 profile-tier-level syntax, and E.2.2 hypothetical-reference-decoder syntax; ISO/IEC 14496-15 `HEVCDecoderConfigurationRecord` matching semantics | HM `source/Lib/TLibDecoder/TDecCAVLC.cpp` functions `parseVPS`, `parsePTL`, `parseProfileTier`, and `parseHrdParameters` at `9c1f298659ab0cee9dc13d23d0304221575410b9`; Android `libhevc` `decoder/ihevcd_parse_headers.c` profile-tier-level and VPS paths at `c83a76b084498d55f252f48b2e3786804cdf24b7` | Retain the base-layer VPS identifier, temporal-sublayer count, nesting flag, and exact general profile/tier/constraint/level fields. Validate profile, tier, compatibility, level, temporal count, and nesting against `hvcC`, while tolerating the independently observed legacy-muxer practice of zeroing some or all profile-specific constraint bits in the configuration record; SPS validation remains authoritative for chroma and bit depth. Consume but do not retain sublayer ordering, timing, and HRD syntax. Reject multilayer and layer-set state because the supported contract is one independently coded image item; add no layer-selection, access-unit, DPB, scheduling, track, or sample model. |
| `HeifContentColorVolume`, `HeifItem.ContentColorVolume`, and `HeifDecoderCore` content color-volume parsing and presentation | HEIF content color-volume item property; AVIF 1.2 content color-volume requirements; ITU-T H.274 (V4) content colour volume syntax and semantics | libavif `src/read.c` function `avifSkipContentColourVolume` at `092276ce89098ead06db80975173191e5fee1826`; official ITU-T H.274 (V4), January 2026 | Decode only the bounded per-image `cclv` property: require zero cancellation, persistence, and reserved bits; preserve optional signed G/B/R primary coordinates and normalized minimum, maximum, and average luminance values; and validate their registered ranges and ordering. Expose the effective grid-or-tile still-image value through `HeifMetadata`. Do not add SEI persistence, retained video state, tracks, samples, timing, or a generic ISO BMFF color-volume box model. |
| `HeifAmbientViewingEnvironment`, `HeifReferenceViewingEnvironment`, `HeifNominalDiffuseWhite`, and their per-item presentation metadata | ISOBMFF ambient viewing environment; ITU-T H.274 (V4) section 8.13; HEIF Amendment 1 sections 6.5.44 and 6.5.45; AVIF 1.2 image-item box requirements | libavif `src/read.c` functions `avifSkipAmbientViewingEnvironment`, `avifSkipReferenceViewingEnvironment`, and `avifSkipNominalDiffuseWhite` at `092276ce89098ead06db80975173191e5fee1826`; official ITU-T H.274 (V4), January 2026 | Decode the fixed-size `amve`, version-zero `reve`, and version-zero `ndwt` properties only when associated with the presented still-image item. Preserve physical illuminance and luminance units, distinct surround/periphery chromaticities, and the coded zero that requests the standard nominal diffuse-white default. Validate registered coordinate ranges and duplicate associations. Retain no video-SEI persistence, visual sample entry, display pipeline, track, timing, or generic viewing-environment box model. |
| `GridHeifItemDecoder` and `HeifDecoderCore` grid/thumbnail selection | ISO/IEC 23008-12 section 6.6.2.3 image-grid syntax and MIAF grid-cell constraints | libavif `src/read.c` functions `avifParseImageGridBox`, `avifDecoderDataAllocateImagePlanes`, and `avifDecoderDataCopyTileToImage` at `092276ce89098ead06db80975173191e5fee1826` | Parse version-zero 16-bit and 32-bit grid descriptors, preserve row-major `dimg` order, require the declared tile count and one coding format, validate canvas coverage and edge overlap, and crop only the rightmost column and bottom row while copying through ImageSharp pixel buffers. A primary grid whose tile codec is unavailable may use only a decodable thumbnail that explicitly references that grid. |

28
src/ImageSharp/Formats/Heif/Hevc/HevcBitReader.cs

@ -86,6 +86,34 @@ internal ref struct HevcBitReader
/// <exception cref="InvalidImageContentException">The flag extends beyond the bounded HEVC syntax.</exception>
public bool ReadFlag() => this.ReadBits(1) != 0;
/// <summary>
/// Determines whether unread syntax remains before the raw byte sequence payload trailing bits.
/// </summary>
/// <returns>
/// <see langword="true"/> when the unread bits contain syntax before the stop bit; otherwise,
/// <see langword="false"/>.
/// </returns>
public bool HasMoreRbspData()
{
int bitsRemaining = this.BitsRemaining;
if (bitsRemaining == 0)
{
return false;
}
if (bitsRemaining > 8)
{
return true;
}
int savedBitPosition = this.bitPosition;
uint remainingValue = this.ReadBits(bitsRemaining);
this.bitPosition = savedBitPosition;
// At most one partial byte can contain only rbsp_stop_one_bit followed by alignment zeros.
return remainingValue != 1U << (bitsRemaining - 1);
}
/// <summary>
/// Reads an unsigned exponential-Golomb value.
/// </summary>

36
src/ImageSharp/Formats/Heif/Hevc/HevcCodecConfiguration.cs

@ -70,6 +70,8 @@ internal sealed class HevcCodecConfiguration
// to reach the image item's NAL length width without retaining playback state in the still-image model.
offset += 2;
byte temporalAndLengthFields = data[offset++];
int temporalLayerCount = (temporalAndLengthFields >> 3) & 7;
bool temporalIdNested = (temporalAndLengthFields & 4) != 0;
this.NalUnitLengthSize = (temporalAndLengthFields & 3) + 1;
int arrayCount = data[offset++];
@ -133,6 +135,35 @@ internal sealed class HevcCodecConfiguration
{
throw new InvalidImageContentException("The HEVC codec configuration contains unexpected trailing data.");
}
List<HevcVideoParameterSet> videoParameterSets = new();
foreach (HevcNalUnitArray nalUnitArray in this.nalUnitArrays)
{
const byte videoParameterSetNalUnitType = 32;
if (nalUnitArray.NalUnitType != videoParameterSetNalUnitType)
{
continue;
}
foreach (HevcNalUnit nalUnit in nalUnitArray.NalUnits)
{
HevcVideoParameterSet videoParameterSet = new(nalUnit);
// Legacy HEIC muxers commonly preserve only the original four source/packing constraint bits in
// hvcC and zero later profile-specific constraint bits. SPS validation provides the authoritative
// chroma and bit-depth checks, so do not reject otherwise matching Range Extensions images here.
if (!videoParameterSet.ProfileTierLevel.Matches(this)
|| (temporalLayerCount != 0 && videoParameterSet.MaxSubLayers != temporalLayerCount)
|| (temporalLayerCount != 0 && videoParameterSet.TemporalIdNestingFlag != temporalIdNested))
{
throw new InvalidImageContentException("The HEVC video parameter set does not match its codec configuration.");
}
videoParameterSets.Add(videoParameterSet);
}
}
this.VideoParameterSets = videoParameterSets;
}
/// <summary>
@ -201,6 +232,11 @@ internal sealed class HevcCodecConfiguration
/// </summary>
public IReadOnlyList<HevcNalUnitArray> NalUnitArrays => this.nalUnitArrays;
/// <summary>
/// Gets the validated video parameter sets carried by the codec-configuration property.
/// </summary>
public IReadOnlyList<HevcVideoParameterSet> VideoParameterSets { get; }
/// <summary>
/// Validates the associated pixel-information property against the coded luma and chroma sample precisions.
/// </summary>

115
src/ImageSharp/Formats/Heif/Hevc/HevcProfileTierLevel.cs

@ -0,0 +1,115 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Hevc;
/// <summary>
/// Contains the general HEVC profile, tier, constraint, and level description declared by a parameter set.
/// </summary>
internal sealed class HevcProfileTierLevel
{
/// <summary>
/// Initializes a new instance of the <see cref="HevcProfileTierLevel"/> class.
/// </summary>
/// <param name="reader">The parameter-set raw byte sequence payload reader.</param>
/// <param name="maxSubLayersMinusOne">The highest declared temporal sublayer index.</param>
/// <exception cref="InvalidImageContentException">
/// The profile-tier-level syntax is truncated or contains nonzero reserved bits.
/// </exception>
public HevcProfileTierLevel(ref HevcBitReader reader, int maxSubLayersMinusOne)
{
DebugGuard.MustBeBetweenOrEqualTo(maxSubLayersMinusOne, 0, 6, nameof(maxSubLayersMinusOne));
this.ProfileSpace = (byte)reader.ReadBits(2);
this.TierFlag = reader.ReadFlag();
this.ProfileIdc = (byte)reader.ReadBits(5);
this.ProfileCompatibilityFlags = reader.ReadBits(32);
// The configuration record carries these 48 bits verbatim. Preserve their exact ordering so the
// parameter set can be checked without reinterpreting profile-specific constraint layouts.
this.ConstraintIndicatorFlags = ((ulong)reader.ReadBits(16) << 32) | reader.ReadBits(32);
this.LevelIdc = (byte)reader.ReadBits(8);
Span<bool> subLayerProfilePresent = stackalloc bool[6];
Span<bool> subLayerLevelPresent = stackalloc bool[6];
for (int subLayer = 0; subLayer < maxSubLayersMinusOne; subLayer++)
{
subLayerProfilePresent[subLayer] = reader.ReadFlag();
subLayerLevelPresent[subLayer] = reader.ReadFlag();
}
if (maxSubLayersMinusOne > 0)
{
for (int subLayer = maxSubLayersMinusOne; subLayer < 8; subLayer++)
{
if (reader.ReadBits(2) != 0)
{
throw new InvalidImageContentException("The HEVC profile-tier-level syntax has nonzero reserved bits.");
}
}
}
for (int subLayer = 0; subLayer < maxSubLayersMinusOne; subLayer++)
{
if (subLayerProfilePresent[subLayer])
{
// A sublayer profile repeats the fixed 88-bit profile and constraint structure. It is consumed
// for alignment but not retained because one still-image item has no temporal playback model.
reader.ReadBits(2);
reader.ReadFlag();
reader.ReadBits(5);
reader.ReadBits(32);
reader.ReadBits(16);
reader.ReadBits(32);
}
if (subLayerLevelPresent[subLayer])
{
reader.ReadBits(8);
}
}
}
/// <summary>
/// Gets the namespace of the declared profile identifier.
/// </summary>
public byte ProfileSpace { get; }
/// <summary>
/// Gets a value indicating whether the high tier is declared.
/// </summary>
public bool TierFlag { get; }
/// <summary>
/// Gets the five-bit profile identifier.
/// </summary>
public byte ProfileIdc { get; }
/// <summary>
/// Gets the profile-compatibility flags.
/// </summary>
public uint ProfileCompatibilityFlags { get; }
/// <summary>
/// Gets the 48-bit profile-constraint flags.
/// </summary>
public ulong ConstraintIndicatorFlags { get; }
/// <summary>
/// Gets the eight-bit level identifier.
/// </summary>
public byte LevelIdc { get; }
/// <summary>
/// Determines whether this parameter-set description is compatible with an image item's codec-configuration
/// property.
/// </summary>
/// <param name="configuration">The associated HEVC codec configuration.</param>
/// <returns><see langword="true"/> when the general profile, tier, compatibility, and level fields match.</returns>
public bool Matches(HevcCodecConfiguration configuration)
=> this.ProfileSpace == configuration.GeneralProfileSpace
&& this.TierFlag == configuration.GeneralTierFlag
&& this.ProfileIdc == configuration.GeneralProfileIdc
&& this.ProfileCompatibilityFlags == configuration.GeneralProfileCompatibilityFlags
&& this.LevelIdc == configuration.GeneralLevelIdc;
}

251
src/ImageSharp/Formats/Heif/Hevc/HevcVideoParameterSet.cs

@ -0,0 +1,251 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Hevc;
/// <summary>
/// Contains the bounded HEVC video-parameter-set fields required to validate and decode one still-image item.
/// </summary>
internal sealed class HevcVideoParameterSet
{
/// <summary>
/// Initializes a new instance of the <see cref="HevcVideoParameterSet"/> class.
/// </summary>
/// <param name="nalUnit">The decoded video-parameter-set NAL unit.</param>
/// <exception cref="InvalidImageContentException">
/// The NAL unit is not a supported, conforming base-layer video parameter set.
/// </exception>
public HevcVideoParameterSet(HevcNalUnit nalUnit)
{
const byte videoParameterSetNalUnitType = 32;
if (nalUnit.Header.NalUnitType != videoParameterSetNalUnitType
|| nalUnit.Header.LayerId != 0
|| nalUnit.Header.TemporalId != 0)
{
throw new InvalidImageContentException("The HEVC video parameter set has an invalid NAL-unit header.");
}
HevcBitReader reader = new(nalUnit.Rbsp.Span);
this.Id = (byte)reader.ReadBits(4);
bool baseLayerInternal = reader.ReadFlag();
bool baseLayerAvailable = reader.ReadFlag();
if (!baseLayerInternal || !baseLayerAvailable)
{
throw new InvalidImageContentException("The HEVC video parameter set does not make its base layer available.");
}
int maxLayersMinusOne = (int)reader.ReadBits(6);
if (maxLayersMinusOne != 0)
{
// HEIF auxiliary images are separate image items. Importing an HEVC multilayer selection model would
// exceed the one-presented-image contract and is not part of the exposed still-picture profiles.
throw new InvalidImageContentException("Layered HEVC video parameter sets are not supported for still-image items.");
}
int maxSubLayersMinusOne = (int)reader.ReadBits(3);
if (maxSubLayersMinusOne > 6)
{
throw new InvalidImageContentException("The HEVC video parameter set declares too many temporal sublayers.");
}
this.MaxSubLayers = maxSubLayersMinusOne + 1;
this.TemporalIdNestingFlag = reader.ReadFlag();
if (maxSubLayersMinusOne == 0 && !this.TemporalIdNestingFlag)
{
throw new InvalidImageContentException("The HEVC video parameter set has invalid temporal nesting.");
}
if (reader.ReadBits(16) != ushort.MaxValue)
{
throw new InvalidImageContentException("The HEVC video parameter set has invalid reserved bits.");
}
this.ProfileTierLevel = new HevcProfileTierLevel(ref reader, maxSubLayersMinusOne);
bool subLayerOrderingInfoPresent = reader.ReadFlag();
int firstOrderingSubLayer = subLayerOrderingInfoPresent ? 0 : maxSubLayersMinusOne;
for (int subLayer = firstOrderingSubLayer; subLayer <= maxSubLayersMinusOne; subLayer++)
{
uint maxDecodedPictureBufferingMinusOne = reader.ReadUnsignedExpGolomb();
uint maxNumReorderPictures = reader.ReadUnsignedExpGolomb();
reader.ReadUnsignedExpGolomb();
if (maxNumReorderPictures > maxDecodedPictureBufferingMinusOne)
{
throw new InvalidImageContentException("The HEVC video parameter set has invalid sublayer ordering limits.");
}
}
uint maxLayerId = reader.ReadBits(6);
uint numLayerSetsMinusOne = reader.ReadUnsignedExpGolomb();
if (maxLayerId != 0 || numLayerSetsMinusOne != 0)
{
throw new InvalidImageContentException("HEVC layer sets are not supported for still-image items.");
}
bool timingInfoPresent = reader.ReadFlag();
if (timingInfoPresent)
{
// Timing and hypothetical-reference-decoder values are required for bit alignment but do not describe
// the pixels of the one image item, so they are deliberately consumed without retained playback state.
reader.ReadBits(32);
reader.ReadBits(32);
if (reader.ReadFlag())
{
reader.ReadUnsignedExpGolomb();
}
uint hrdParameterCount = reader.ReadUnsignedExpGolomb();
if (hrdParameterCount > 1024)
{
throw new InvalidImageContentException("The HEVC video parameter set declares too many HRD parameter sets.");
}
bool nalHrdParametersPresent = false;
bool vclHrdParametersPresent = false;
bool subPictureHrdParametersPresent = false;
for (uint hrdIndex = 0; hrdIndex < hrdParameterCount; hrdIndex++)
{
uint layerSetIndex = reader.ReadUnsignedExpGolomb();
if (layerSetIndex != 0)
{
throw new InvalidImageContentException("The HEVC HRD parameters reference an unsupported layer set.");
}
bool commonInformationPresent = hrdIndex == 0 || reader.ReadFlag();
SkipHrdParameters(
ref reader,
commonInformationPresent,
maxSubLayersMinusOne,
ref nalHrdParametersPresent,
ref vclHrdParametersPresent,
ref subPictureHrdParametersPresent);
}
}
if (reader.ReadFlag())
{
while (reader.HasMoreRbspData())
{
reader.ReadFlag();
}
}
reader.ReadRbspTrailingBits();
}
/// <summary>
/// Gets the four-bit video-parameter-set identifier.
/// </summary>
public byte Id { get; }
/// <summary>
/// Gets the declared number of temporal sublayers.
/// </summary>
public int MaxSubLayers { get; }
/// <summary>
/// Gets a value indicating whether temporal identifiers are nested.
/// </summary>
public bool TemporalIdNestingFlag { get; }
/// <summary>
/// Gets the general profile, tier, constraint, and level description.
/// </summary>
public HevcProfileTierLevel ProfileTierLevel { get; }
/// <summary>
/// Consumes hypothetical-reference-decoder syntax without adding playback state to the still-image model.
/// </summary>
/// <param name="reader">The video-parameter-set raw byte sequence payload reader.</param>
/// <param name="commonInformationPresent">Whether common HRD flags are coded for this parameter set.</param>
/// <param name="maxSubLayersMinusOne">The highest declared temporal sublayer index.</param>
/// <param name="nalHrdParametersPresent">The effective NAL HRD presence flag.</param>
/// <param name="vclHrdParametersPresent">The effective VCL HRD presence flag.</param>
/// <param name="subPictureHrdParametersPresent">The effective sub-picture HRD presence flag.</param>
/// <exception cref="InvalidImageContentException">The HRD syntax is truncated or exceeds its registered bounds.</exception>
private static void SkipHrdParameters(
ref HevcBitReader reader,
bool commonInformationPresent,
int maxSubLayersMinusOne,
ref bool nalHrdParametersPresent,
ref bool vclHrdParametersPresent,
ref bool subPictureHrdParametersPresent)
{
if (commonInformationPresent)
{
nalHrdParametersPresent = reader.ReadFlag();
vclHrdParametersPresent = reader.ReadFlag();
subPictureHrdParametersPresent = false;
if (nalHrdParametersPresent || vclHrdParametersPresent)
{
subPictureHrdParametersPresent = reader.ReadFlag();
if (subPictureHrdParametersPresent)
{
reader.ReadBits(8);
reader.ReadBits(5);
reader.ReadFlag();
reader.ReadBits(5);
}
reader.ReadBits(4);
reader.ReadBits(4);
if (subPictureHrdParametersPresent)
{
reader.ReadBits(4);
}
reader.ReadBits(5);
reader.ReadBits(5);
reader.ReadBits(5);
}
}
for (int subLayer = 0; subLayer <= maxSubLayersMinusOne; subLayer++)
{
bool fixedPictureRateGeneral = reader.ReadFlag();
bool fixedPictureRateWithinCvs = fixedPictureRateGeneral || reader.ReadFlag();
bool lowDelayHrd = false;
if (fixedPictureRateWithinCvs)
{
reader.ReadUnsignedExpGolomb();
}
else
{
lowDelayHrd = reader.ReadFlag();
}
uint cpbCountMinusOne = 0;
if (!lowDelayHrd)
{
cpbCountMinusOne = reader.ReadUnsignedExpGolomb();
if (cpbCountMinusOne > 31)
{
throw new InvalidImageContentException("The HEVC HRD syntax declares too many coded-picture buffers.");
}
}
for (int hrdKind = 0; hrdKind < 2; hrdKind++)
{
bool parametersPresent = hrdKind == 0 ? nalHrdParametersPresent : vclHrdParametersPresent;
if (!parametersPresent)
{
continue;
}
for (uint cpbIndex = 0; cpbIndex <= cpbCountMinusOne; cpbIndex++)
{
reader.ReadUnsignedExpGolomb();
reader.ReadUnsignedExpGolomb();
if (subPictureHrdParametersPresent)
{
reader.ReadUnsignedExpGolomb();
reader.ReadUnsignedExpGolomb();
}
reader.ReadFlag();
}
}
}
}
}
Loading…
Cancel
Save