diff --git a/library/core/src/androidTest/java/com/google/android/exoplayer2/util/ParsableNalUnitBitArrayTest.java b/library/core/src/androidTest/java/com/google/android/exoplayer2/util/ParsableNalUnitBitArrayTest.java index b62aff46f5..294d3d352a 100644 --- a/library/core/src/androidTest/java/com/google/android/exoplayer2/util/ParsableNalUnitBitArrayTest.java +++ b/library/core/src/androidTest/java/com/google/android/exoplayer2/util/ParsableNalUnitBitArrayTest.java @@ -95,7 +95,7 @@ public final class ParsableNalUnitBitArrayTest extends TestCase { ParsableNalUnitBitArray array = new ParsableNalUnitBitArray(createByteArray(0, 0, 3, 128, 0), 0, 5); assertFalse(array.canReadExpGolombCodedNum()); - array.skipBits(1); + array.skipBit(); assertTrue(array.canReadExpGolombCodedNum()); assertEquals(32767, array.readUnsignedExpGolombCodedInt()); assertFalse(array.canReadBits(1)); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/H264Reader.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/H264Reader.java index 8206ed7d6d..3cde946ce3 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/H264Reader.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/H264Reader.java @@ -316,7 +316,7 @@ public final class H264Reader implements ElementaryStreamReader { if (!bitArray.canReadBits(8)) { return; } - bitArray.skipBits(1); // forbidden_zero_bit + bitArray.skipBit(); // forbidden_zero_bit int nalRefIdc = bitArray.readBits(2); bitArray.skipBits(5); // nal_unit_type diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/H265Reader.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/H265Reader.java index 712ca8d69c..f6ae80ba56 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/H265Reader.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/H265Reader.java @@ -225,7 +225,7 @@ public final class H265Reader implements ElementaryStreamReader { ParsableNalUnitBitArray bitArray = new ParsableNalUnitBitArray(sps.nalData, 0, sps.nalLength); bitArray.skipBits(40 + 4); // NAL header, sps_video_parameter_set_id int maxSubLayersMinus1 = bitArray.readBits(3); - bitArray.skipBits(1); // sps_temporal_id_nesting_flag + bitArray.skipBit(); // sps_temporal_id_nesting_flag // profile_tier_level(1, sps_max_sub_layers_minus1) bitArray.skipBits(88); // if (profilePresentFlag) {...} @@ -247,7 +247,7 @@ public final class H265Reader implements ElementaryStreamReader { bitArray.readUnsignedExpGolombCodedInt(); // sps_seq_parameter_set_id int chromaFormatIdc = bitArray.readUnsignedExpGolombCodedInt(); if (chromaFormatIdc == 3) { - bitArray.skipBits(1); // separate_colour_plane_flag + bitArray.skipBit(); // separate_colour_plane_flag } int picWidthInLumaSamples = bitArray.readUnsignedExpGolombCodedInt(); int picHeightInLumaSamples = bitArray.readUnsignedExpGolombCodedInt(); @@ -288,7 +288,7 @@ public final class H265Reader implements ElementaryStreamReader { bitArray.skipBits(8); bitArray.readUnsignedExpGolombCodedInt(); // log2_min_pcm_luma_coding_block_size_minus3 bitArray.readUnsignedExpGolombCodedInt(); // log2_diff_max_min_pcm_luma_coding_block_size - bitArray.skipBits(1); // pcm_loop_filter_disabled_flag + bitArray.skipBit(); // pcm_loop_filter_disabled_flag } // Skips all short term reference picture sets. skipShortTermRefPicSets(bitArray); @@ -365,11 +365,11 @@ public final class H265Reader implements ElementaryStreamReader { interRefPicSetPredictionFlag = bitArray.readBit(); } if (interRefPicSetPredictionFlag) { - bitArray.skipBits(1); // delta_rps_sign + bitArray.skipBit(); // delta_rps_sign bitArray.readUnsignedExpGolombCodedInt(); // abs_delta_rps_minus1 for (int j = 0; j <= previousNumDeltaPocs; j++) { if (bitArray.readBit()) { // used_by_curr_pic_flag[j] - bitArray.skipBits(1); // use_delta_flag[j] + bitArray.skipBit(); // use_delta_flag[j] } } } else { @@ -378,11 +378,11 @@ public final class H265Reader implements ElementaryStreamReader { previousNumDeltaPocs = numNegativePics + numPositivePics; for (int i = 0; i < numNegativePics; i++) { bitArray.readUnsignedExpGolombCodedInt(); // delta_poc_s0_minus1[i] - bitArray.skipBits(1); // used_by_curr_pic_s0_flag[i] + bitArray.skipBit(); // used_by_curr_pic_s0_flag[i] } for (int i = 0; i < numPositivePics; i++) { bitArray.readUnsignedExpGolombCodedInt(); // delta_poc_s1_minus1[i] - bitArray.skipBits(1); // used_by_curr_pic_s1_flag[i] + bitArray.skipBit(); // used_by_curr_pic_s1_flag[i] } } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/util/NalUnitUtil.java b/library/core/src/main/java/com/google/android/exoplayer2/util/NalUnitUtil.java index ab2fec0db7..c4ed20546d 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/util/NalUnitUtil.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/util/NalUnitUtil.java @@ -265,7 +265,7 @@ public final class NalUnitUtil { } data.readUnsignedExpGolombCodedInt(); // bit_depth_luma_minus8 data.readUnsignedExpGolombCodedInt(); // bit_depth_chroma_minus8 - data.skipBits(1); // qpprime_y_zero_transform_bypass_flag + data.skipBit(); // qpprime_y_zero_transform_bypass_flag boolean seqScalingMatrixPresentFlag = data.readBit(); if (seqScalingMatrixPresentFlag) { int limit = (chromaFormatIdc != 3) ? 8 : 12; @@ -295,17 +295,17 @@ public final class NalUnitUtil { } } data.readUnsignedExpGolombCodedInt(); // max_num_ref_frames - data.skipBits(1); // gaps_in_frame_num_value_allowed_flag + data.skipBit(); // gaps_in_frame_num_value_allowed_flag int picWidthInMbs = data.readUnsignedExpGolombCodedInt() + 1; int picHeightInMapUnits = data.readUnsignedExpGolombCodedInt() + 1; boolean frameMbsOnlyFlag = data.readBit(); int frameHeightInMbs = (2 - (frameMbsOnlyFlag ? 1 : 0)) * picHeightInMapUnits; if (!frameMbsOnlyFlag) { - data.skipBits(1); // mb_adaptive_frame_field_flag + data.skipBit(); // mb_adaptive_frame_field_flag } - data.skipBits(1); // direct_8x8_inference_flag + data.skipBit(); // direct_8x8_inference_flag int frameWidth = picWidthInMbs * 16; int frameHeight = frameHeightInMbs * 16; boolean frameCroppingFlag = data.readBit(); @@ -368,7 +368,7 @@ public final class NalUnitUtil { data.skipBits(8); // nal_unit int picParameterSetId = data.readUnsignedExpGolombCodedInt(); int seqParameterSetId = data.readUnsignedExpGolombCodedInt(); - data.skipBits(1); // entropy_coding_mode_flag + data.skipBit(); // entropy_coding_mode_flag boolean bottomFieldPicOrderInFramePresentFlag = data.readBit(); return new PpsData(picParameterSetId, seqParameterSetId, bottomFieldPicOrderInFramePresentFlag); } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/util/ParsableNalUnitBitArray.java b/library/core/src/main/java/com/google/android/exoplayer2/util/ParsableNalUnitBitArray.java index 05d7a9929d..443c69909c 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/util/ParsableNalUnitBitArray.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/util/ParsableNalUnitBitArray.java @@ -54,15 +54,27 @@ public final class ParsableNalUnitBitArray { assertValidOffset(); } + /** + * Skips a single bit. + */ + public void skipBit() { + if (++bitOffset == 8) { + bitOffset = 0; + byteOffset += shouldSkipByte(byteOffset + 1) ? 2 : 1; + } + assertValidOffset(); + } + /** * Skips bits and moves current reading position forward. * - * @param n The number of bits to skip. + * @param numBits The number of bits to skip. */ - public void skipBits(int n) { + public void skipBits(int numBits) { int oldByteOffset = byteOffset; - byteOffset += (n / 8); - bitOffset += (n % 8); + int numBytes = numBits / 8; + byteOffset += numBytes; + bitOffset += numBits - (numBytes * 8); if (bitOffset > 7) { byteOffset++; bitOffset -= 8; @@ -81,13 +93,14 @@ public final class ParsableNalUnitBitArray { * Returns whether it's possible to read {@code n} bits starting from the current offset. The * offset is not modified. * - * @param n The number of bits. + * @param numBits The number of bits. * @return Whether it is possible to read {@code n} bits. */ - public boolean canReadBits(int n) { + public boolean canReadBits(int numBits) { int oldByteOffset = byteOffset; - int newByteOffset = byteOffset + (n / 8); - int newBitOffset = bitOffset + (n % 8); + int numBytes = numBits / 8; + int newByteOffset = byteOffset + numBytes; + int newBitOffset = bitOffset + numBits - (numBytes * 8); if (newBitOffset > 7) { newByteOffset++; newBitOffset -= 8; @@ -108,7 +121,9 @@ public final class ParsableNalUnitBitArray { * @return Whether the bit is set. */ public boolean readBit() { - return readBits(1) == 1; + boolean returnValue = (data[byteOffset] & (0x80 >> bitOffset)) != 0; + skipBit(); + return returnValue; } /** @@ -118,50 +133,19 @@ public final class ParsableNalUnitBitArray { * @return An integer whose bottom n bits hold the read data. */ public int readBits(int numBits) { - if (numBits == 0) { - return 0; - } - int returnValue = 0; - - // Read as many whole bytes as we can. - int wholeBytes = (numBits / 8); - for (int i = 0; i < wholeBytes; i++) { - int nextByteOffset = shouldSkipByte(byteOffset + 1) ? byteOffset + 2 : byteOffset + 1; - int byteValue; - if (bitOffset != 0) { - byteValue = ((data[byteOffset] & 0xFF) << bitOffset) - | ((data[nextByteOffset] & 0xFF) >>> (8 - bitOffset)); - } else { - byteValue = data[byteOffset]; - } - numBits -= 8; - returnValue |= (byteValue & 0xFF) << numBits; - byteOffset = nextByteOffset; + bitOffset += numBits; + while (bitOffset > 8) { + bitOffset -= 8; + returnValue |= (data[byteOffset] & 0xFF) << bitOffset; + byteOffset += shouldSkipByte(byteOffset + 1) ? 2 : 1; } - - // Read any remaining bits. - if (numBits > 0) { - int nextBit = bitOffset + numBits; - byte writeMask = (byte) (0xFF >> (8 - numBits)); - int nextByteOffset = shouldSkipByte(byteOffset + 1) ? byteOffset + 2 : byteOffset + 1; - - if (nextBit > 8) { - // Combine bits from current byte and next byte. - returnValue |= ((((data[byteOffset] & 0xFF) << (nextBit - 8) - | ((data[nextByteOffset] & 0xFF) >> (16 - nextBit))) & writeMask)); - byteOffset = nextByteOffset; - } else { - // Bits to be read only within current byte. - returnValue |= (((data[byteOffset] & 0xFF) >> (8 - nextBit)) & writeMask); - if (nextBit == 8) { - byteOffset = nextByteOffset; - } - } - - bitOffset = nextBit % 8; + returnValue |= (data[byteOffset] & 0xFF) >> 8 - bitOffset; + returnValue &= 0xFFFFFFFF >>> (32 - numBits); + if (bitOffset == 8) { + bitOffset = 0; + byteOffset += shouldSkipByte(byteOffset + 1) ? 2 : 1; } - assertValidOffset(); return returnValue; } @@ -220,7 +204,6 @@ public final class ParsableNalUnitBitArray { private void assertValidOffset() { // It is fine for position to be at the end of the array, but no further. Assertions.checkState(byteOffset >= 0 - && (bitOffset >= 0 && bitOffset < 8) && (byteOffset < byteLimit || (byteOffset == byteLimit && bitOffset == 0))); }