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:
parent
415b17c1e1
commit
ddb0f86604
@ -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
|
||||||
|
@ -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]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user