Fix parsing of H.265 sequence parameter sets

Fix short term reference picture list parsing. Before this change, `deltaPocS0`
was derived by adding one to the value of the syntax element
`delta_poc_s0_minus1`, but (maybe surprising) the specification actually says
that `DeltaPocS0[stRpsIdx][i]` should be assigned the negation
`-(delta_poc_s0_minus1[i] + 1)` on the first iteration, then that value added
to the previous value on previous iterations. See equations (7-67) to (7-70) in
the 2021-08 version of the H.265/HEVC specification.

Also read the number of long term reference pictures once rather than on every
loop iteration (subsection 7.3.2.2.1).

PiperOrigin-RevId: 551852999
This commit is contained in:
andrewlewis 2023-07-28 16:00:13 +01:00 committed by Rohit Singh
parent 415b17c1e1
commit ddb0f86604
3 changed files with 39 additions and 7 deletions

View File

@ -92,6 +92,7 @@
* Video: * Video:
* Allow `MediaCodecVideoRenderer` to use a custom * Allow `MediaCodecVideoRenderer` to use a custom
`VideoFrameProcessor.Factory`. `VideoFrameProcessor.Factory`.
* H.265/HEVC: Fix parsing SPS short and long term reference picture info.
* Text: * Text:
* CEA-608: Change cue truncation logic to only consider visible text. * CEA-608: Change cue truncation logic to only consider visible text.
Previously indent and tab offset were included when limiting the cue Previously indent and tab offset were included when limiting the cue

View File

@ -625,8 +625,8 @@ public final class NalUnitUtil {
} }
skipShortTermReferencePictureSets(data); skipShortTermReferencePictureSets(data);
if (data.readBit()) { // long_term_ref_pics_present_flag if (data.readBit()) { // long_term_ref_pics_present_flag
// num_long_term_ref_pics_sps int numLongTermRefPicsSps = data.readUnsignedExpGolombCodedInt();
for (int i = 0; i < data.readUnsignedExpGolombCodedInt(); i++) { for (int i = 0; i < numLongTermRefPicsSps; i++) {
int ltRefPicPocLsbSpsLength = log2MaxPicOrderCntLsbMinus4 + 4; int ltRefPicPocLsbSpsLength = log2MaxPicOrderCntLsbMinus4 + 4;
// lt_ref_pic_poc_lsb_sps[i], used_by_curr_pic_lt_sps_flag[i] // lt_ref_pic_poc_lsb_sps[i], used_by_curr_pic_lt_sps_flag[i]
data.skipBits(ltRefPicPocLsbSpsLength + 1); data.skipBits(ltRefPicPocLsbSpsLength + 1);
@ -948,12 +948,14 @@ public final class NalUnitUtil {
numPositivePics = bitArray.readUnsignedExpGolombCodedInt(); numPositivePics = bitArray.readUnsignedExpGolombCodedInt();
deltaPocS0 = new int[numNegativePics]; deltaPocS0 = new int[numNegativePics];
for (int i = 0; i < numNegativePics; i++) { for (int i = 0; i < numNegativePics; i++) {
deltaPocS0[i] = bitArray.readUnsignedExpGolombCodedInt() + 1; deltaPocS0[i] =
(i > 0 ? deltaPocS0[i - 1] : 0) - (bitArray.readUnsignedExpGolombCodedInt() + 1);
bitArray.skipBit(); // used_by_curr_pic_s0_flag[i] bitArray.skipBit(); // used_by_curr_pic_s0_flag[i]
} }
deltaPocS1 = new int[numPositivePics]; deltaPocS1 = new int[numPositivePics];
for (int i = 0; i < numPositivePics; i++) { for (int i = 0; i < numPositivePics; i++) {
deltaPocS1[i] = bitArray.readUnsignedExpGolombCodedInt() + 1; deltaPocS1[i] =
(i > 0 ? deltaPocS1[i - 1] : 0) + (bitArray.readUnsignedExpGolombCodedInt() + 1);
bitArray.skipBit(); // used_by_curr_pic_s1_flag[i] bitArray.skipBit(); // used_by_curr_pic_s1_flag[i]
} }
} }

View File

@ -202,11 +202,40 @@ public final class NalUnitUtilTest {
assertThat(spsData.colorTransfer).isEqualTo(6); assertThat(spsData.colorTransfer).isEqualTo(6);
} }
/** Regression test for [Internal: b/292170736]. */
@Test
public void parseH265SpsNalUnitPayload_withShortTermRefPicSets() {
byte[] spsNalUnitPayload =
new byte[] {
1, 2, 96, 0, 0, 3, 0, 0, 3, 0, 0, 3, 0, 0, 3, 0, -106, -96, 2, 28, -128, 30, 4, -39, 111,
-110, 76, -114, -65, -7, -13, 101, 33, -51, 66, 68, 2, 65, 0, 0, 3, 0, 1, 0, 0, 3, 0, 29,
8
};
NalUnitUtil.H265SpsData spsData =
NalUnitUtil.parseH265SpsNalUnitPayload(spsNalUnitPayload, 0, spsNalUnitPayload.length);
assertThat(spsData.constraintBytes).isEqualTo(new int[] {0, 0, 0, 0, 0, 0});
assertThat(spsData.generalLevelIdc).isEqualTo(150);
assertThat(spsData.generalProfileCompatibilityFlags).isEqualTo(6);
assertThat(spsData.generalProfileIdc).isEqualTo(2);
assertThat(spsData.generalProfileSpace).isEqualTo(0);
assertThat(spsData.generalTierFlag).isFalse();
assertThat(spsData.width).isEqualTo(1080);
assertThat(spsData.height).isEqualTo(1920);
assertThat(spsData.pixelWidthHeightRatio).isEqualTo(1);
assertThat(spsData.seqParameterSetId).isEqualTo(0);
assertThat(spsData.chromaFormatIdc).isEqualTo(1);
assertThat(spsData.bitDepthLumaMinus8).isEqualTo(2);
assertThat(spsData.bitDepthChromaMinus8).isEqualTo(2);
assertThat(spsData.colorSpace).isEqualTo(6);
assertThat(spsData.colorRange).isEqualTo(2);
assertThat(spsData.colorTransfer).isEqualTo(6);
}
private static byte[] buildTestData() { private static byte[] buildTestData() {
byte[] data = new byte[20]; byte[] data = new byte[20];
for (int i = 0; i < data.length; i++) { Arrays.fill(data, (byte) 0xFF);
data[i] = (byte) 0xFF;
}
// Insert an incomplete NAL unit start code. // Insert an incomplete NAL unit start code.
data[TEST_PARTIAL_NAL_POSITION] = 0; data[TEST_PARTIAL_NAL_POSITION] = 0;
data[TEST_PARTIAL_NAL_POSITION + 1] = 0; data[TEST_PARTIAL_NAL_POSITION + 1] = 0;