Skip to content

Commit

Permalink
Fix parsing H265 short term reference picture sets
Browse files Browse the repository at this point in the history
Issue: #10316
PiperOrigin-RevId: 456084302
(cherry picked from commit d86bc10)
  • Loading branch information
ojw28 authored and rohitjoins committed Jun 20, 2022
1 parent 5051b47 commit 3727385
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 16 deletions.
Expand Up @@ -18,6 +18,7 @@
import static java.lang.Math.min;

import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
import java.nio.ByteBuffer;
import java.util.Arrays;

Expand Down Expand Up @@ -781,40 +782,105 @@ private static void skipH265ScalingList(ParsableNalUnitBitArray bitArray) {
}
}

/**
* Skips any short term reference picture sets contained in a SPS.
*
* <p>Note: The st_ref_pic_set parsing in this method is simplified for the case where they're
* contained in a SPS, and would need generalizing for use elsewhere.
*/
private static void skipShortTermReferencePictureSets(ParsableNalUnitBitArray bitArray) {
int numShortTermRefPicSets = bitArray.readUnsignedExpGolombCodedInt();
boolean interRefPicSetPredictionFlag = false;
int numNegativePics;
int numPositivePics;
// As this method applies in a SPS, the only element of NumDeltaPocs accessed is the previous
// one, so we just keep track of that rather than storing the whole array.
// RefRpsIdx = stRpsIdx - (delta_idx_minus1 + 1) and delta_idx_minus1 is always zero in SPS.
int previousNumDeltaPocs = 0;
// As this method applies in a SPS, each short term reference picture set only accesses data
// from the previous one. This is because RefRpsIdx = stRpsIdx - (delta_idx_minus1 + 1), and
// delta_idx_minus1 is always zero in a SPS. Hence we just keep track of variables from the
// previous one as we iterate.
int previousNumNegativePics = C.INDEX_UNSET;
int previousNumPositivePics = C.INDEX_UNSET;
int[] previousDeltaPocS0 = new int[0];
int[] previousDeltaPocS1 = new int[0];
for (int stRpsIdx = 0; stRpsIdx < numShortTermRefPicSets; stRpsIdx++) {
if (stRpsIdx != 0) {
interRefPicSetPredictionFlag = bitArray.readBit();
}
int numNegativePics;
int numPositivePics;
int[] deltaPocS0;
int[] deltaPocS1;

boolean interRefPicSetPredictionFlag = stRpsIdx != 0 && bitArray.readBit();
if (interRefPicSetPredictionFlag) {
bitArray.skipBit(); // delta_rps_sign
bitArray.readUnsignedExpGolombCodedInt(); // abs_delta_rps_minus1
int previousNumDeltaPocs = previousNumNegativePics + previousNumPositivePics;

int deltaRpsSign = bitArray.readBit() ? 1 : 0;
int absDeltaRps = bitArray.readUnsignedExpGolombCodedInt() + 1;
int deltaRps = (1 - 2 * deltaRpsSign) * absDeltaRps;

boolean[] useDeltaFlags = new boolean[previousNumDeltaPocs + 1];
for (int j = 0; j <= previousNumDeltaPocs; j++) {
if (!bitArray.readBit()) { // used_by_curr_pic_flag[j]
bitArray.skipBit(); // use_delta_flag[j]
useDeltaFlags[j] = bitArray.readBit();
} else {
// When use_delta_flag[j] is not present, its value is 1.
useDeltaFlags[j] = true;
}
}

// Derive numNegativePics, numPositivePics, deltaPocS0 and deltaPocS1 as per Rec. ITU-T
// H.265 v6 (06/2019) Section 7.4.8
int i = 0;
deltaPocS0 = new int[previousNumDeltaPocs + 1];
deltaPocS1 = new int[previousNumDeltaPocs + 1];
for (int j = previousNumPositivePics - 1; j >= 0; j--) {
int dPoc = previousDeltaPocS1[j] + deltaRps;
if (dPoc < 0 && useDeltaFlags[previousNumNegativePics + j]) {
deltaPocS0[i++] = dPoc;
}
}
if (deltaRps < 0 && useDeltaFlags[previousNumDeltaPocs]) {
deltaPocS0[i++] = deltaRps;
}
for (int j = 0; j < previousNumNegativePics; j++) {
int dPoc = previousDeltaPocS0[j] + deltaRps;
if (dPoc < 0 && useDeltaFlags[j]) {
deltaPocS0[i++] = dPoc;
}
}
numNegativePics = i;
deltaPocS0 = Arrays.copyOf(deltaPocS0, numNegativePics);

i = 0;
for (int j = previousNumNegativePics - 1; j >= 0; j--) {
int dPoc = previousDeltaPocS0[j] + deltaRps;
if (dPoc > 0 && useDeltaFlags[j]) {
deltaPocS1[i++] = dPoc;
}
}
if (deltaRps > 0 && useDeltaFlags[previousNumDeltaPocs]) {
deltaPocS1[i++] = deltaRps;
}
for (int j = 0; j < previousNumPositivePics; j++) {
int dPoc = previousDeltaPocS1[j] + deltaRps;
if (dPoc > 0 && useDeltaFlags[previousNumNegativePics + j]) {
deltaPocS1[i++] = dPoc;
}
}
numPositivePics = i;
deltaPocS1 = Arrays.copyOf(deltaPocS1, numPositivePics);
} else {
numNegativePics = bitArray.readUnsignedExpGolombCodedInt();
numPositivePics = bitArray.readUnsignedExpGolombCodedInt();
previousNumDeltaPocs = numNegativePics + numPositivePics;
deltaPocS0 = new int[numNegativePics];
for (int i = 0; i < numNegativePics; i++) {
bitArray.readUnsignedExpGolombCodedInt(); // delta_poc_s0_minus1[i]
deltaPocS0[i] = bitArray.readUnsignedExpGolombCodedInt() + 1;
bitArray.skipBit(); // used_by_curr_pic_s0_flag[i]
}
deltaPocS1 = new int[numPositivePics];
for (int i = 0; i < numPositivePics; i++) {
bitArray.readUnsignedExpGolombCodedInt(); // delta_poc_s1_minus1[i]
deltaPocS1[i] = bitArray.readUnsignedExpGolombCodedInt() + 1;
bitArray.skipBit(); // used_by_curr_pic_s1_flag[i]
}
}
previousNumNegativePics = numNegativePics;
previousNumPositivePics = numPositivePics;
previousDeltaPocS0 = deltaPocS0;
previousDeltaPocS1 = deltaPocS1;
}
}

Expand Down
Expand Up @@ -168,6 +168,32 @@ public void discardToSps() {
assertDiscardToSpsMatchesExpected("FF00000001660000000167FF", "0000000167FF");
}

/** Regression test for https://github.com/google/ExoPlayer/issues/10316. */
@Test
public void parseH265SpsNalUnitPayload_exoghi_10316() {
byte[] spsNalUnitPayload =
new byte[] {
1, 2, 32, 0, 0, 3, 0, -112, 0, 0, 3, 0, 0, 3, 0, -106, -96, 1, -32, 32, 2, 28, 77, -98,
87, -110, 66, -111, -123, 22, 74, -86, -53, -101, -98, -68, -28, 9, 119, -21, -103, 120,
-16, 22, -95, 34, 1, 54, -62, 0, 0, 7, -46, 0, 0, -69, -127, -12, 85, -17, 126, 0, -29,
-128, 28, 120, 1, -57, 0, 56, -15
};

NalUnitUtil.H265SpsData spsData =
NalUnitUtil.parseH265SpsNalUnitPayload(spsNalUnitPayload, 0, spsNalUnitPayload.length);

assertThat(spsData.constraintBytes).isEqualTo(new int[] {144, 0, 0, 0, 0, 0});
assertThat(spsData.generalLevelIdc).isEqualTo(150);
assertThat(spsData.generalProfileCompatibilityFlags).isEqualTo(4);
assertThat(spsData.generalProfileIdc).isEqualTo(2);
assertThat(spsData.generalProfileSpace).isEqualTo(0);
assertThat(spsData.generalTierFlag).isFalse();
assertThat(spsData.height).isEqualTo(2160);
assertThat(spsData.pixelWidthHeightRatio).isEqualTo(1);
assertThat(spsData.seqParameterSetId).isEqualTo(0);
assertThat(spsData.width).isEqualTo(3840);
}

private static byte[] buildTestData() {
byte[] data = new byte[20];
for (int i = 0; i < data.length; i++) {
Expand Down

0 comments on commit 3727385

Please sign in to comment.