Commit 7c3b461b by krocard Committed by Ian Baker

Make DefaultAudioSink.getFramesPerEncodedSample endianness independent

While most ExoPlayer code parsing ByteBuffers is called with buffers in big
endian, in certain situation, buffers in little endian are used too.

MediaCodec produced ByteBuffers are in little endian, while buffers
receive from the sources are in big endian (ByteBuffer's default).

As a result, some code called from AudioSink in passthrough parsed
bytebuffer in little endian. This is not correct because those
format are specified in BigEndian.

Changing the endianness of the ByteBuffer returned from MediaCodec
would impact a lot more code that can currently be tested in the
current COVID lockdown situation.

As a result, this patch instead make the parsing code independent
of the ByteBuffer.order() set. All the code that is called from
DefaultAudioSink now parses the buffer explicitly in Big Endian.

Additionally, the MPEG big endian header data of size 4 bytes was
retrieved with ByteBuffer.get, which only returns one byte.

PiperOrigin-RevId: 308116173
parent d01d0cfd
...@@ -24,6 +24,7 @@ import com.google.android.exoplayer2.drm.DrmInitData; ...@@ -24,6 +24,7 @@ import com.google.android.exoplayer2.drm.DrmInitData;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.ParsableBitArray; import com.google.android.exoplayer2.util.ParsableBitArray;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
import com.google.android.exoplayer2.util.Util;
import java.lang.annotation.Documented; import java.lang.annotation.Documented;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
...@@ -516,7 +517,7 @@ public final class Ac3Util { ...@@ -516,7 +517,7 @@ public final class Ac3Util {
int endIndex = buffer.limit() - TRUEHD_SYNCFRAME_PREFIX_LENGTH; int endIndex = buffer.limit() - TRUEHD_SYNCFRAME_PREFIX_LENGTH;
for (int i = startIndex; i <= endIndex; i++) { for (int i = startIndex; i <= endIndex; i++) {
// The syncword ends 0xBA for TrueHD or 0xBB for MLP. // The syncword ends 0xBA for TrueHD or 0xBB for MLP.
if ((buffer.getInt(i + 4) & 0xFEFFFFFF) == 0xBA6F72F8) { if ((Util.getBigEndianInt(buffer, i + 4) & 0xFFFFFFFE) == 0xF8726FBA) {
return i - startIndex; return i - startIndex;
} }
} }
......
...@@ -60,6 +60,8 @@ import java.io.IOException; ...@@ -60,6 +60,8 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
...@@ -1860,6 +1862,21 @@ public final class Util { ...@@ -1860,6 +1862,21 @@ public final class Util {
} }
/** /**
* Absolute <i>get</i> method for reading an int value in {@link ByteOrder#BIG_ENDIAN} in a {@link
* ByteBuffer}. Same as {@link ByteBuffer#getInt(int)} except the buffer's order as returned by
* {@link ByteBuffer#order()} is ignored and {@link ByteOrder#BIG_ENDIAN} is used instead.
*
* @param buffer The buffer from which to read an int in big endian.
* @param index The index from which the bytes will be read.
* @return The int value at the given index with the buffer bytes ordered most significant to
* least significant.
*/
public static int getBigEndianInt(ByteBuffer buffer, int index) {
int value = buffer.getInt(index);
return buffer.order() == ByteOrder.BIG_ENDIAN ? value : Integer.reverseBytes(value);
}
/**
* Returns the {@link C.NetworkType} of the current network connection. * Returns the {@link C.NetworkType} of the current network connection.
* *
* @param context A context to access the connectivity manager. * @param context A context to access the connectivity manager.
......
...@@ -27,6 +27,8 @@ import static com.google.common.truth.Truth.assertThat; ...@@ -27,6 +27,8 @@ import static com.google.common.truth.Truth.assertThat;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.testutil.TestUtil; import com.google.android.exoplayer2.testutil.TestUtil;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Random; import java.util.Random;
...@@ -790,6 +792,30 @@ public class UtilTest { ...@@ -790,6 +792,30 @@ public class UtilTest {
} }
@Test @Test
public void getBigEndianInt_fromBigEndian() {
byte[] bytes = {0x1F, 0x2E, 0x3D, 0x4C};
ByteBuffer byteBuffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
assertThat(Util.getBigEndianInt(byteBuffer, 0)).isEqualTo(0x1F2E3D4C);
}
@Test
public void getBigEndianInt_fromLittleEndian() {
byte[] bytes = {(byte) 0xC2, (byte) 0xD3, (byte) 0xE4, (byte) 0xF5};
ByteBuffer byteBuffer = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN);
assertThat(Util.getBigEndianInt(byteBuffer, 0)).isEqualTo(0xC2D3E4F5);
}
@Test
public void getBigEndianInt_unaligned() {
byte[] bytes = {9, 8, 7, 6, 5};
ByteBuffer byteBuffer = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN);
assertThat(Util.getBigEndianInt(byteBuffer, 1)).isEqualTo(0x08070605);
}
@Test
public void inflate_withDeflatedData_success() { public void inflate_withDeflatedData_success() {
byte[] testData = TestUtil.buildTestData(/*arbitrary test data size*/ 256 * 1024); byte[] testData = TestUtil.buildTestData(/*arbitrary test data size*/ 256 * 1024);
byte[] compressedData = new byte[testData.length * 2]; byte[] compressedData = new byte[testData.length * 2];
......
...@@ -1242,7 +1242,8 @@ public final class DefaultAudioSink implements AudioSink { ...@@ -1242,7 +1242,8 @@ public final class DefaultAudioSink implements AudioSink {
private static int getFramesPerEncodedSample(@C.Encoding int encoding, ByteBuffer buffer) { private static int getFramesPerEncodedSample(@C.Encoding int encoding, ByteBuffer buffer) {
switch (encoding) { switch (encoding) {
case C.ENCODING_MP3: case C.ENCODING_MP3:
return MpegAudioUtil.parseMpegAudioFrameSampleCount(buffer.get(buffer.position())); int headerDataInBigEndian = Util.getBigEndianInt(buffer, buffer.position());
return MpegAudioUtil.parseMpegAudioFrameSampleCount(headerDataInBigEndian);
case C.ENCODING_AAC_LC: case C.ENCODING_AAC_LC:
return AacUtil.AAC_LC_AUDIO_SAMPLE_COUNT; return AacUtil.AAC_LC_AUDIO_SAMPLE_COUNT;
case C.ENCODING_AAC_HE_V1: case C.ENCODING_AAC_HE_V1:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment