Fix parsing H265 short term reference picture sets
Issue: google/ExoPlayer#10316 PiperOrigin-RevId: 456084302
This commit is contained in:
parent
f5d8800d51
commit
6dc85dc241
@ -5,6 +5,8 @@
|
||||
* Extractors:
|
||||
* Add support for AVI
|
||||
([#2092](https://github.com/google/ExoPlayer/issues/2092)).
|
||||
* Fix parsing of H265 short term reference picture sets
|
||||
([#10316](https://github.com/google/ExoPlayer/issues/10316)).
|
||||
* RTSP:
|
||||
* Add RTP reader for H263
|
||||
([#63](https://github.com/androidx/media/pull/63)).
|
||||
|
@ -18,6 +18,7 @@ package androidx.media3.extractor;
|
||||
import static java.lang.Math.min;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.MimeTypes;
|
||||
import androidx.media3.common.util.Assertions;
|
||||
import androidx.media3.common.util.Log;
|
||||
@ -786,40 +787,105 @@ public final class NalUnitUtil {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -170,6 +170,32 @@ public final class NalUnitUtilTest {
|
||||
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++) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user