Add readLeb128 and readLeb128ToInt to ParsableByteArray.

Leb128 is a little-endian long of variable byte length. The format is used during the extraction of the size of the OBU configuration for the iacb configuration box.

PiperOrigin-RevId: 650295002
This commit is contained in:
ktrajkovski 2024-07-08 10:25:47 -07:00 committed by Copybara-Service
parent 007c258ceb
commit 972007abef
3 changed files with 101 additions and 11 deletions

View File

@ -19,6 +19,7 @@ import androidx.annotation.Nullable;
import com.google.common.base.Charsets;
import com.google.common.collect.ImmutableSet;
import com.google.common.primitives.Chars;
import com.google.common.primitives.Ints;
import com.google.common.primitives.UnsignedBytes;
import com.google.errorprone.annotations.CheckReturnValue;
import java.nio.ByteBuffer;
@ -597,6 +598,41 @@ public final class ParsableByteArray {
return value;
}
/**
* Reads a little endian long of variable length.
*
* @throws IllegalStateException if the byte to be read is over the limit of the parsable byte
* array
* @return long value
*/
public long readUnsignedLeb128ToLong() {
long value = 0;
// At most, 63 bits of unsigned data can be stored in a long, which corresponds to 63/7=9 bytes
// in LEB128.
for (int i = 0; i < 9; i++) {
if (this.position == limit) {
throw new IllegalStateException("Attempting to read a byte over the limit.");
}
long currentByte = this.readUnsignedByte();
value |= (currentByte & 0x7F) << (i * 7);
if ((currentByte & 0x80) == 0) {
break;
}
}
return value;
}
/**
* Reads a little endian integer of variable length.
*
* @throws IllegalArgumentException if the read value is greater than {@link Integer#MAX_VALUE} or
* less than {@link Integer#MIN_VALUE}
* @return integer value
*/
public int readUnsignedLeb128ToInt() {
return Ints.checkedCast(readUnsignedLeb128ToLong());
}
/**
* Reads a UTF byte order mark (BOM) and returns the UTF {@link Charset} it represents. Returns
* {@code null} without advancing {@link #getPosition() position} if no BOM is found.

View File

@ -18,6 +18,7 @@ package androidx.media3.common.util;
import static androidx.media3.test.utils.TestUtil.createByteArray;
import static com.google.common.truth.Truth.assertThat;
import static java.nio.charset.Charset.forName;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.fail;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@ -917,4 +918,67 @@ public final class ParsableByteArrayTest {
assertThat(parser.getPosition()).isEqualTo(22);
assertThat(parser.readLine(Charsets.UTF_16LE)).isNull();
}
@Test
public void readUnsignedLeb128ToLong() {
byte[] bytes = new byte[] {(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0x0F};
ParsableByteArray testArray = new ParsableByteArray(bytes);
long readValue = testArray.readUnsignedLeb128ToLong();
assertThat(readValue).isEqualTo(0xFFFFFFFFL);
assertThat(testArray.getPosition()).isEqualTo(5);
}
@Test
public void readMaxUnsignedLeb128ToLong() {
byte[] bytes =
new byte[] {
(byte) 0xFF,
(byte) 0xFF,
(byte) 0xFF,
(byte) 0xFF,
(byte) 0xFF,
(byte) 0xFF,
(byte) 0xFF,
(byte) 0xFF,
(byte) 0x7F
};
ParsableByteArray testArray = new ParsableByteArray(bytes);
long readValue = testArray.readUnsignedLeb128ToLong();
assertThat(readValue).isEqualTo(Long.MAX_VALUE);
assertThat(testArray.getPosition()).isEqualTo(9);
}
@Test
public void readUnsignedLeb128ToInt() {
byte[] bytes = new byte[] {(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0x0F};
ParsableByteArray testArray = new ParsableByteArray(bytes);
int readValue = testArray.readUnsignedLeb128ToInt();
assertThat(readValue).isEqualTo(0x1FFFFFF);
assertThat(testArray.getPosition()).isEqualTo(4);
}
@Test
public void readMaxUnsignedLeb128ToInt() {
byte[] bytes = new byte[] {(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0x07};
ParsableByteArray testArray = new ParsableByteArray(bytes);
int readValue = testArray.readUnsignedLeb128ToInt();
assertThat(readValue).isEqualTo(Integer.MAX_VALUE);
assertThat(testArray.getPosition()).isEqualTo(5);
}
@Test
public void readTooLongUnsignedLeb128ToInt() {
byte[] bytes = new byte[] {(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0x0F};
ParsableByteArray testArray = new ParsableByteArray(bytes);
assertThrows(IllegalArgumentException.class, testArray::readUnsignedLeb128ToInt);
}
}

View File

@ -1920,17 +1920,7 @@ import java.util.Objects;
} else if (childAtomType == Atom.TYPE_iacb) {
parent.setPosition(
childPosition + Atom.HEADER_SIZE + 1); // header and configuration version
int configObusSize = 0;
for (int i = 0; i <= 4; i++) {
int currentByte = parent.readUnsignedByte();
configObusSize |= (currentByte & 0x7F) << (i * 7);
if ((currentByte & 0x80) == 0) {
break;
}
}
if (configObusSize < 0) {
throw ParserException.createForUnsupportedContainerFeature("OBU too large.");
}
int configObusSize = parent.readUnsignedLeb128ToInt();
byte[] initializationDataBytes = new byte[configObusSize];
parent.readBytes(initializationDataBytes, /* offset= */ 0, configObusSize);
initializationData = ImmutableList.of(initializationDataBytes);