Commit 84d19c46 by aquilescanta Committed by Oliver Woodman

Add support for AAC-LATM in transport streams

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=163337073
parent 5278a637
...@@ -16,7 +16,6 @@ ...@@ -16,7 +16,6 @@
package com.google.android.exoplayer2.util; package com.google.android.exoplayer2.util;
import android.test.MoreAsserts; import android.test.MoreAsserts;
import junit.framework.TestCase; import junit.framework.TestCase;
/** /**
...@@ -27,8 +26,14 @@ public final class ParsableBitArrayTest extends TestCase { ...@@ -27,8 +26,14 @@ public final class ParsableBitArrayTest extends TestCase {
private static final byte[] TEST_DATA = new byte[] {0x3C, (byte) 0xD2, (byte) 0x5F, (byte) 0x01, private static final byte[] TEST_DATA = new byte[] {0x3C, (byte) 0xD2, (byte) 0x5F, (byte) 0x01,
(byte) 0xFF, (byte) 0x14, (byte) 0x60, (byte) 0x99}; (byte) 0xFF, (byte) 0x14, (byte) 0x60, (byte) 0x99};
private ParsableBitArray testArray;
@Override
public void setUp() {
testArray = new ParsableBitArray(TEST_DATA);
}
public void testReadAllBytes() { public void testReadAllBytes() {
ParsableBitArray testArray = new ParsableBitArray(TEST_DATA);
byte[] bytesRead = new byte[TEST_DATA.length]; byte[] bytesRead = new byte[TEST_DATA.length];
testArray.readBytes(bytesRead, 0, TEST_DATA.length); testArray.readBytes(bytesRead, 0, TEST_DATA.length);
MoreAsserts.assertEquals(TEST_DATA, bytesRead); MoreAsserts.assertEquals(TEST_DATA, bytesRead);
...@@ -37,13 +42,12 @@ public final class ParsableBitArrayTest extends TestCase { ...@@ -37,13 +42,12 @@ public final class ParsableBitArrayTest extends TestCase {
} }
public void testReadBit() { public void testReadBit() {
ParsableBitArray testArray = new ParsableBitArray(TEST_DATA); assertReadBitsToEnd(0);
assertReadBitsToEnd(0, testArray);
} }
public void testReadBits() { public void testReadBits() {
ParsableBitArray testArray = new ParsableBitArray(TEST_DATA);
assertEquals(getTestDataBits(0, 5), testArray.readBits(5)); assertEquals(getTestDataBits(0, 5), testArray.readBits(5));
assertEquals(getTestDataBits(5, 0), testArray.readBits(0));
assertEquals(getTestDataBits(5, 3), testArray.readBits(3)); assertEquals(getTestDataBits(5, 3), testArray.readBits(3));
assertEquals(getTestDataBits(8, 16), testArray.readBits(16)); assertEquals(getTestDataBits(8, 16), testArray.readBits(16));
assertEquals(getTestDataBits(24, 3), testArray.readBits(3)); assertEquals(getTestDataBits(24, 3), testArray.readBits(3));
...@@ -52,67 +56,97 @@ public final class ParsableBitArrayTest extends TestCase { ...@@ -52,67 +56,97 @@ public final class ParsableBitArrayTest extends TestCase {
assertEquals(getTestDataBits(50, 14), testArray.readBits(14)); assertEquals(getTestDataBits(50, 14), testArray.readBits(14));
} }
public void testReadBitsToByteArray() {
byte[] result = new byte[TEST_DATA.length];
// Test read within byte boundaries.
testArray.readBits(result, 0, 6);
assertEquals(TEST_DATA[0] & 0xFC, result[0]);
// Test read across byte boundaries.
testArray.readBits(result, 0, 8);
assertEquals(((TEST_DATA[0] & 0x03) << 6) | ((TEST_DATA[1] & 0xFC) >> 2), result[0]);
// Test reading across multiple bytes.
testArray.readBits(result, 1, 50);
for (int i = 1; i < 7; i++) {
assertEquals((byte) (((TEST_DATA[i] & 0x03) << 6) | ((TEST_DATA[i + 1] & 0xFC) >> 2)),
result[i]);
}
assertEquals((byte) (TEST_DATA[7] & 0x03) << 6, result[7]);
assertEquals(0, testArray.bitsLeft());
// Test read last buffer byte across input data bytes.
testArray.setPosition(31);
result[3] = 0;
testArray.readBits(result, 3, 3);
assertEquals((byte) 0xE0, result[3]);
// Test read bits in the middle of a input data byte.
result[0] = 0;
assertEquals(34, testArray.getPosition());
testArray.readBits(result, 0, 3);
assertEquals((byte) 0xE0, result[0]);
// Test read 0 bits.
testArray.setPosition(32);
result[1] = 0;
testArray.readBits(result, 1, 0);
assertEquals(0, result[1]);
// Test least significant bits are unmodified.
result[1] = (byte) 0xFF;
testArray.setPosition(16);
testArray.readBits(result, 0, 9);
assertEquals(0x5F, result[0]);
assertEquals(0x7F, result[1]);
}
public void testRead32BitsByteAligned() { public void testRead32BitsByteAligned() {
ParsableBitArray testArray = new ParsableBitArray(TEST_DATA);
assertEquals(getTestDataBits(0, 32), testArray.readBits(32)); assertEquals(getTestDataBits(0, 32), testArray.readBits(32));
assertEquals(getTestDataBits(32, 32), testArray.readBits(32)); assertEquals(getTestDataBits(32, 32), testArray.readBits(32));
} }
public void testRead32BitsNonByteAligned() { public void testRead32BitsNonByteAligned() {
ParsableBitArray testArray = new ParsableBitArray(TEST_DATA);
assertEquals(getTestDataBits(0, 5), testArray.readBits(5)); assertEquals(getTestDataBits(0, 5), testArray.readBits(5));
assertEquals(getTestDataBits(5, 32), testArray.readBits(32)); assertEquals(getTestDataBits(5, 32), testArray.readBits(32));
} }
public void testSkipBytes() { public void testSkipBytes() {
ParsableBitArray testArray = new ParsableBitArray(TEST_DATA);
testArray.skipBytes(2); testArray.skipBytes(2);
assertReadBitsToEnd(16, testArray); assertReadBitsToEnd(16);
} }
public void testSkipBitsByteAligned() { public void testSkipBitsByteAligned() {
ParsableBitArray testArray = new ParsableBitArray(TEST_DATA);
testArray.skipBits(16); testArray.skipBits(16);
assertReadBitsToEnd(16, testArray); assertReadBitsToEnd(16);
} }
public void testSkipBitsNonByteAligned() { public void testSkipBitsNonByteAligned() {
ParsableBitArray testArray = new ParsableBitArray(TEST_DATA);
testArray.skipBits(5); testArray.skipBits(5);
assertReadBitsToEnd(5, testArray); assertReadBitsToEnd(5);
} }
public void testSetPositionByteAligned() { public void testSetPositionByteAligned() {
ParsableBitArray testArray = new ParsableBitArray(TEST_DATA);
testArray.setPosition(16); testArray.setPosition(16);
assertReadBitsToEnd(16, testArray); assertReadBitsToEnd(16);
} }
public void testSetPositionNonByteAligned() { public void testSetPositionNonByteAligned() {
ParsableBitArray testArray = new ParsableBitArray(TEST_DATA);
testArray.setPosition(5); testArray.setPosition(5);
assertReadBitsToEnd(5, testArray); assertReadBitsToEnd(5);
} }
public void testByteAlignFromNonByteAligned() { public void testByteAlignFromNonByteAligned() {
ParsableBitArray testArray = new ParsableBitArray(TEST_DATA);
testArray.setPosition(11); testArray.setPosition(11);
testArray.byteAlign(); testArray.byteAlign();
assertEquals(2, testArray.getBytePosition()); assertEquals(2, testArray.getBytePosition());
assertEquals(16, testArray.getPosition()); assertEquals(16, testArray.getPosition());
assertReadBitsToEnd(16, testArray); assertReadBitsToEnd(16);
} }
public void testByteAlignFromByteAligned() { public void testByteAlignFromByteAligned() {
ParsableBitArray testArray = new ParsableBitArray(TEST_DATA);
testArray.setPosition(16); testArray.setPosition(16);
testArray.byteAlign(); // Should be a no-op. testArray.byteAlign(); // Should be a no-op.
assertEquals(2, testArray.getBytePosition()); assertEquals(2, testArray.getBytePosition());
assertEquals(16, testArray.getPosition()); assertEquals(16, testArray.getPosition());
assertReadBitsToEnd(16, testArray); assertReadBitsToEnd(16);
} }
private static void assertReadBitsToEnd(int expectedStartPosition, ParsableBitArray testArray) { private void assertReadBitsToEnd(int expectedStartPosition) {
int position = testArray.getPosition(); int position = testArray.getPosition();
assertEquals(expectedStartPosition, position); assertEquals(expectedStartPosition, position);
for (int i = position; i < TEST_DATA.length * 8; i++) { for (int i = position; i < TEST_DATA.length * 8; i++) {
......
...@@ -94,9 +94,12 @@ public final class DefaultTsPayloadReaderFactory implements TsPayloadReader.Fact ...@@ -94,9 +94,12 @@ public final class DefaultTsPayloadReaderFactory implements TsPayloadReader.Fact
case TsExtractor.TS_STREAM_TYPE_MPA: case TsExtractor.TS_STREAM_TYPE_MPA:
case TsExtractor.TS_STREAM_TYPE_MPA_LSF: case TsExtractor.TS_STREAM_TYPE_MPA_LSF:
return new PesReader(new MpegAudioReader(esInfo.language)); return new PesReader(new MpegAudioReader(esInfo.language));
case TsExtractor.TS_STREAM_TYPE_AAC: case TsExtractor.TS_STREAM_TYPE_AAC_ADTS:
return isSet(FLAG_IGNORE_AAC_STREAM) return isSet(FLAG_IGNORE_AAC_STREAM)
? null : new PesReader(new AdtsReader(false, esInfo.language)); ? null : new PesReader(new AdtsReader(false, esInfo.language));
case TsExtractor.TS_STREAM_TYPE_AAC_LATM:
return isSet(FLAG_IGNORE_AAC_STREAM)
? null : new PesReader(new LatmReader(esInfo.language));
case TsExtractor.TS_STREAM_TYPE_AC3: case TsExtractor.TS_STREAM_TYPE_AC3:
case TsExtractor.TS_STREAM_TYPE_E_AC3: case TsExtractor.TS_STREAM_TYPE_E_AC3:
return new PesReader(new Ac3Reader(esInfo.language)); return new PesReader(new Ac3Reader(esInfo.language));
......
...@@ -84,7 +84,8 @@ public final class TsExtractor implements Extractor { ...@@ -84,7 +84,8 @@ public final class TsExtractor implements Extractor {
public static final int TS_STREAM_TYPE_MPA = 0x03; public static final int TS_STREAM_TYPE_MPA = 0x03;
public static final int TS_STREAM_TYPE_MPA_LSF = 0x04; public static final int TS_STREAM_TYPE_MPA_LSF = 0x04;
public static final int TS_STREAM_TYPE_AAC = 0x0F; public static final int TS_STREAM_TYPE_AAC_ADTS = 0x0F;
public static final int TS_STREAM_TYPE_AAC_LATM = 0x11;
public static final int TS_STREAM_TYPE_AC3 = 0x81; public static final int TS_STREAM_TYPE_AC3 = 0x81;
public static final int TS_STREAM_TYPE_DTS = 0x8A; public static final int TS_STREAM_TYPE_DTS = 0x8A;
public static final int TS_STREAM_TYPE_HDMV_DTS = 0x82; public static final int TS_STREAM_TYPE_HDMV_DTS = 0x82;
......
...@@ -83,11 +83,21 @@ public final class CodecSpecificDataUtil { ...@@ -83,11 +83,21 @@ public final class CodecSpecificDataUtil {
/** /**
* Parses an AudioSpecificConfig, as defined in ISO 14496-3 1.6.2.1 * Parses an AudioSpecificConfig, as defined in ISO 14496-3 1.6.2.1
* *
* @param audioSpecificConfig The AudioSpecificConfig to parse. * @param audioSpecificConfig A byte array containing the AudioSpecificConfig to parse.
* @return A pair consisting of the sample rate in Hz and the channel count. * @return A pair consisting of the sample rate in Hz and the channel count.
*/ */
public static Pair<Integer, Integer> parseAacAudioSpecificConfig(byte[] audioSpecificConfig) { public static Pair<Integer, Integer> parseAacAudioSpecificConfig(byte[] audioSpecificConfig) {
ParsableBitArray bitArray = new ParsableBitArray(audioSpecificConfig); return parseAacAudioSpecificConfig(new ParsableBitArray(audioSpecificConfig));
}
/**
* Parses an AudioSpecificConfig, as defined in ISO 14496-3 1.6.2.1
*
* @param bitArray A {@link ParsableBitArray} containing the AudioSpecificConfig to parse. The
* position is advanced to the end of the AudioSpecificConfig.
* @return A pair consisting of the sample rate in Hz and the channel count.
*/
public static Pair<Integer, Integer> parseAacAudioSpecificConfig(ParsableBitArray bitArray) {
int audioObjectType = getAacAudioObjectType(bitArray); int audioObjectType = getAacAudioObjectType(bitArray);
int sampleRate = getAacSamplingFrequency(bitArray); int sampleRate = getAacSamplingFrequency(bitArray);
int channelConfiguration = bitArray.readBits(4); int channelConfiguration = bitArray.readBits(4);
...@@ -104,6 +114,39 @@ public final class CodecSpecificDataUtil { ...@@ -104,6 +114,39 @@ public final class CodecSpecificDataUtil {
channelConfiguration = bitArray.readBits(4); channelConfiguration = bitArray.readBits(4);
} }
} }
switch (audioObjectType) {
case 1:
case 2:
case 3:
case 4:
case 6:
case 7:
case 17:
case 19:
case 20:
case 21:
case 22:
case 23:
parseGaSpecificConfig(bitArray, audioObjectType, channelConfiguration);
break;
default:
throw new UnsupportedOperationException();
}
switch (audioObjectType) {
case 17:
case 19:
case 20:
case 21:
case 22:
case 23:
int epConfig = bitArray.readBits(2);
if (epConfig == 2 || epConfig == 3) {
throw new UnsupportedOperationException();
}
break;
}
// For supported containers, bits_to_decode() is always 0.
int channelCount = AUDIO_SPECIFIC_CONFIG_CHANNEL_COUNT_TABLE[channelConfiguration]; int channelCount = AUDIO_SPECIFIC_CONFIG_CHANNEL_COUNT_TABLE[channelConfiguration];
Assertions.checkArgument(channelCount != AUDIO_SPECIFIC_CONFIG_CHANNEL_CONFIGURATION_INVALID); Assertions.checkArgument(channelCount != AUDIO_SPECIFIC_CONFIG_CHANNEL_CONFIGURATION_INVALID);
return Pair.create(sampleRate, channelCount); return Pair.create(sampleRate, channelCount);
...@@ -269,4 +312,32 @@ public final class CodecSpecificDataUtil { ...@@ -269,4 +312,32 @@ public final class CodecSpecificDataUtil {
return samplingFrequency; return samplingFrequency;
} }
private static void parseGaSpecificConfig(ParsableBitArray bitArray, int audioObjectType,
int channelConfiguration) {
bitArray.skipBits(1); // frameLengthFlag.
boolean dependsOnCoreDecoder = bitArray.readBit();
if (dependsOnCoreDecoder) {
bitArray.skipBits(14); // coreCoderDelay.
}
boolean extensionFlag = bitArray.readBit();
if (channelConfiguration == 0) {
throw new UnsupportedOperationException(); // TODO: Implement programConfigElement();
}
if (audioObjectType == 6 || audioObjectType == 20) {
bitArray.skipBits(3); // layerNr.
}
if (extensionFlag) {
if (audioObjectType == 22) {
bitArray.skipBits(16); // numOfSubFrame (5), layer_length(11).
}
if (audioObjectType == 17 || audioObjectType == 19 || audioObjectType == 20
|| audioObjectType == 23) {
// aacSectionDataResilienceFlag, aacScalefactorDataResilienceFlag,
// aacSpectralDataResilienceFlag.
bitArray.skipBits(3);
}
bitArray.skipBits(1); // extensionFlag3.
}
}
} }
...@@ -155,6 +155,9 @@ public final class ParsableBitArray { ...@@ -155,6 +155,9 @@ public final class ParsableBitArray {
* @return An integer whose bottom n bits hold the read data. * @return An integer whose bottom n bits hold the read data.
*/ */
public int readBits(int numBits) { public int readBits(int numBits) {
if (numBits == 0) {
return 0;
}
int returnValue = 0; int returnValue = 0;
bitOffset += numBits; bitOffset += numBits;
while (bitOffset > 8) { while (bitOffset > 8) {
...@@ -172,6 +175,40 @@ public final class ParsableBitArray { ...@@ -172,6 +175,40 @@ public final class ParsableBitArray {
} }
/** /**
* 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 offset The offset in {@code buffer} at which the read data should be written.
* @param numBits The number of bits to read.
*/
public void readBits(byte[] buffer, int offset, int numBits) {
// Whole bytes.
int to = offset + (numBits >> 3) /* numBits / 8 */;
for (int i = offset; i < to; i++) {
buffer[i] = (byte) (data[byteOffset++] << bitOffset);
buffer[i] |= (data[byteOffset] & 0xFF) >> (8 - bitOffset);
}
// Trailing bits.
int bitsLeft = numBits & 7 /* numBits % 8 */;
buffer[to] &= 0xFF >> bitsLeft; // Set to 0 the bits that are going to be overwritten.
if (bitOffset + bitsLeft > 8) {
// We read the rest of data[byteOffset] and increase byteOffset.
buffer[to] |= (byte) ((data[byteOffset++] & 0xFF) << bitOffset);
bitOffset -= 8;
}
bitOffset += bitsLeft;
int lastDataByteTrailingBits = (data[byteOffset] & 0xFF) >> (8 - bitOffset);
buffer[to] |= (byte) (lastDataByteTrailingBits << (8 - bitsLeft));
if (bitOffset == 8) {
bitOffset = 0;
byteOffset++;
}
assertValidOffset();
}
/**
* Aligns the position to the next byte boundary. Does nothing if the position is already aligned. * Aligns the position to the next byte boundary. Does nothing if the position is already aligned.
*/ */
public void byteAlign() { public void byteAlign() {
......
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