diff --git a/library/core/src/main/java/com/google/android/exoplayer2/util/ParsableBitArray.java b/library/core/src/main/java/com/google/android/exoplayer2/util/ParsableBitArray.java index 2d8bf95fd1..1d8c3021a5 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/util/ParsableBitArray.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/util/ParsableBitArray.java @@ -163,7 +163,7 @@ public final class ParsableBitArray { * Reads up to 32 bits. * * @param numBits The number of bits to read. - * @return An integer whose bottom n bits hold the read data. + * @return An integer whose bottom {@code numBits} bits hold the read data. */ public int readBits(int numBits) { if (numBits == 0) { @@ -185,12 +185,25 @@ public final class ParsableBitArray { return returnValue; } + /** + * Reads up to 64 bits. + * + * @param numBits The number of bits to read. + * @return A long whose bottom {@code numBits} bits hold the read data. + */ + public long readBitsToLong(int numBits) { + if (numBits <= 32) { + return Util.toUnsignedLong(readBits(numBits)); + } + return Util.toUnsignedLong(readBits(numBits - 32)) << 32 | Util.toUnsignedLong(readBits(32)); + } + /** * Reads {@code numBits} bits into {@code buffer}. * - * @param buffer The array into which the read data should be written. The trailing - * {@code numBits % 8} bits are written into the most significant bits of the last modified - * {@code buffer} byte. The remaining ones are unmodified. + * @param buffer The array into which the read data should be written. The trailing {@code numBits + * % 8} bits are written into the most significant bits of the last modified {@code buffer} + * byte. The remaining ones are unmodified. * @param offset The offset in {@code buffer} at which the read data should be written. * @param numBits The number of bits to read. */ diff --git a/library/core/src/main/java/com/google/android/exoplayer2/util/Util.java b/library/core/src/main/java/com/google/android/exoplayer2/util/Util.java index 30e11f9744..ec022eaac3 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/util/Util.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/util/Util.java @@ -146,7 +146,7 @@ public final class Util { * Converts the entirety of an {@link InputStream} to a byte array. * * @param inputStream the {@link InputStream} to be read. The input stream is not closed by this - * method. + * method. * @return a byte array containing all of the inputStream's bytes. * @throws IOException if an error occurs reading from the stream. */ @@ -366,7 +366,6 @@ public final class Util { /* length= */ second.length); return concatenation; } - /** * Creates a {@link Handler} with the specified {@link Handler.Callback} on the current {@link * Looper} thread. The method accepts partially initialized objects as callback under the @@ -1192,6 +1191,17 @@ public final class Util { return result; } + /** + * Converts an integer to a long by unsigned conversion. + * + *

This method is equivalent to {@link Integer#toUnsignedLong(int)} for API 26+. + */ + public static long toUnsignedLong(int x) { + // x is implicitly casted to a long before the bit operation is executed but this does not + // impact the method correctness. + return x & 0xFFFFFFFFL; + } + /** * Returns a byte array containing values parsed from the hex string provided. * diff --git a/library/core/src/test/java/com/google/android/exoplayer2/util/ParsableBitArrayTest.java b/library/core/src/test/java/com/google/android/exoplayer2/util/ParsableBitArrayTest.java index 677b20fdfc..f031468461 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/util/ParsableBitArrayTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/util/ParsableBitArrayTest.java @@ -107,6 +107,50 @@ public final class ParsableBitArrayTest { assertThat(result).isEqualTo(0xF0000000); } + @Test + public void testReadBitsToLong0Bits() { + byte[] testData = TestUtil.createByteArray(0x3C); + ParsableBitArray testArray = new ParsableBitArray(testData); + + long result = testArray.readBitsToLong(0); + + assertThat(result).isEqualTo(0); + } + + @Test + public void testReadBitsToLongByteAligned() { + byte[] testData = TestUtil.createByteArray(0x3C, 0xD2, 0x5F, 0x01, 0xFF, 0x14, 0x60); + ParsableBitArray testArray = new ParsableBitArray(testData); + testArray.readBits(8); + + long result = testArray.readBitsToLong(45); + + assertThat(result).isEqualTo(0xD25F01FF14L << 5 | 0x60 >> 3); + assertThat(testArray.getPosition()).isEqualTo(53); + } + + @Test + public void testReadBitsToLongNonByteAligned() { + byte[] testData = TestUtil.createByteArray(0x3C, 0xD2, 0x5F, 0x01, 0xFF, 0x14, 0x60); + ParsableBitArray testArray = new ParsableBitArray(testData); + testArray.readBits(3); + + long result = testArray.readBitsToLong(53); + + assertThat(result).isEqualTo((0x3CL & 0b11111) << 48 | 0xD25F01FF1460L); + assertThat(testArray.getPosition()).isEqualTo(56); + } + + @Test + public void testReadBitsToLongNegativeValue() { + byte[] testData = TestUtil.createByteArray(0xF0, 0, 0, 0, 0, 0, 0, 0); + ParsableBitArray testArray = new ParsableBitArray(testData); + + long result = testArray.readBitsToLong(64); + + assertThat(result).isEqualTo(0xF000000000000000L); + } + @Test public void testReadBitsToByteArray() { byte[] testData = TestUtil.createByteArray(0x3C, 0xD2, 0x5F, 0x01, 0xFF, 0x14, 0x60, 0x99); diff --git a/library/core/src/test/java/com/google/android/exoplayer2/util/UtilTest.java b/library/core/src/test/java/com/google/android/exoplayer2/util/UtilTest.java index 5a13ed0dd8..b54bb0b160 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/util/UtilTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/util/UtilTest.java @@ -217,6 +217,24 @@ public class UtilTest { assertThat(parseXsDateTime("2014-09-19T13:18:55.000-800")).isEqualTo(1411161535000L); } + @Test + public void testToUnsignedLongPositiveValue() { + int x = 0x05D67F23; + + long result = Util.toUnsignedLong(x); + + assertThat(result).isEqualTo(0x05D67F23L); + } + + @Test + public void testToUnsignedLongNegativeValue() { + int x = 0xF5D67F23; + + long result = Util.toUnsignedLong(x); + + assertThat(result).isEqualTo(0xF5D67F23L); + } + @Test public void testGetCodecsOfType() { assertThat(getCodecsOfType(null, C.TRACK_TYPE_VIDEO)).isNull();