Merge "Add EAC3 support to MediaExtractor"

gugelfrei
TreeHugger Robot 6 years ago committed by Android (Google) Code Review
commit 707225ae85

@ -650,7 +650,8 @@ static void dumpCodecProfiles(bool queryDecoders) {
MEDIA_MIMETYPE_AUDIO_MPEG, MEDIA_MIMETYPE_AUDIO_G711_MLAW,
MEDIA_MIMETYPE_AUDIO_G711_ALAW, MEDIA_MIMETYPE_AUDIO_VORBIS,
MEDIA_MIMETYPE_VIDEO_VP8, MEDIA_MIMETYPE_VIDEO_VP9,
MEDIA_MIMETYPE_VIDEO_DOLBY_VISION, MEDIA_MIMETYPE_AUDIO_AC4
MEDIA_MIMETYPE_VIDEO_DOLBY_VISION,
MEDIA_MIMETYPE_AUDIO_EAC3, MEDIA_MIMETYPE_AUDIO_AC4
};
const char *codecType = queryDecoders? "decoder" : "encoder";

@ -313,6 +313,9 @@ static const char *FourCC2MIME(uint32_t fourcc) {
case FOURCC('s', 'a', 'w', 'b'):
return MEDIA_MIMETYPE_AUDIO_AMR_WB;
case FOURCC('e', 'c', '-', '3'):
return MEDIA_MIMETYPE_AUDIO_EAC3;
case FOURCC('m', 'p', '4', 'v'):
return MEDIA_MIMETYPE_VIDEO_MPEG4;
@ -2438,13 +2441,19 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
case FOURCC('a', 'c', '-', '3'):
{
*offset += chunk_size;
return parseAC3SampleEntry(data_offset);
return parseAC3SpecificBox(data_offset);
}
case FOURCC('e', 'c', '-', '3'):
{
*offset += chunk_size;
return parseEAC3SpecificBox(data_offset);
}
case FOURCC('a', 'c', '-', '4'):
{
*offset += chunk_size;
return parseAC4SampleEntry(data_offset);
return parseAC4SpecificBox(data_offset);
}
case FOURCC('f', 't', 'y', 'p'):
@ -2518,43 +2527,43 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
return OK;
}
status_t MPEG4Extractor::parseAC4SampleEntry(off64_t offset) {
status_t MPEG4Extractor::parseChannelCountSampleRate(
off64_t *offset, uint16_t *channelCount, uint16_t *sampleRate) {
// skip 16 bytes:
// + 6-byte reserved,
// + 2-byte data reference index,
// + 8-byte reserved
offset += 16;
uint16_t channelCount;
if (!mDataSource->getUInt16(offset, &channelCount)) {
ALOGE("MPEG4Extractor: error while reading ac-4 block: cannot read channel count");
*offset += 16;
if (!mDataSource->getUInt16(*offset, channelCount)) {
ALOGE("MPEG4Extractor: error while reading sample entry box: cannot read channel count");
return ERROR_MALFORMED;
}
// skip 8 bytes:
// + 2-byte channelCount,
// + 2-byte sample size,
// + 4-byte reserved
offset += 8;
uint16_t sampleRate;
if (!mDataSource->getUInt16(offset, &sampleRate)) {
ALOGE("MPEG4Extractor: error while reading ac-4 block: cannot read sample rate");
*offset += 8;
if (!mDataSource->getUInt16(*offset, sampleRate)) {
ALOGE("MPEG4Extractor: error while reading sample entry box: cannot read sample rate");
return ERROR_MALFORMED;
}
// skip 4 bytes:
// + 2-byte sampleRate,
// + 2-byte reserved
offset += 4;
*offset += 4;
return OK;
}
status_t MPEG4Extractor::parseAC4SpecificBox(off64_t offset) {
if (mLastTrack == NULL) {
return ERROR_MALFORMED;
}
mLastTrack->meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AC4);
mLastTrack->meta.setInt32(kKeyChannelCount, channelCount);
mLastTrack->meta.setInt32(kKeySampleRate, sampleRate);
return parseAC4SpecificBox(offset);
}
status_t MPEG4Extractor::parseAC4SpecificBox(off64_t offset) {
uint16_t sampleRate, channelCount;
status_t status;
if ((status = parseChannelCountSampleRate(&offset, &channelCount, &sampleRate)) != OK) {
return status;
}
uint32_t size;
// + 4-byte size
// + 4-byte type
@ -2593,39 +2602,185 @@ status_t MPEG4Extractor::parseAC4SpecificBox(off64_t offset) {
return ERROR_MALFORMED;
}
mLastTrack->meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AC4);
mLastTrack->meta.setInt32(kKeyChannelCount, channelCount);
mLastTrack->meta.setInt32(kKeySampleRate, sampleRate);
return OK;
}
status_t MPEG4Extractor::parseAC3SampleEntry(off64_t offset) {
// skip 16 bytes:
// + 6-byte reserved,
// + 2-byte data reference index,
// + 8-byte reserved
offset += 16;
uint16_t channelCount;
if (!mDataSource->getUInt16(offset, &channelCount)) {
status_t MPEG4Extractor::parseEAC3SpecificBox(off64_t offset) {
if (mLastTrack == NULL) {
return ERROR_MALFORMED;
}
// skip 8 bytes:
// + 2-byte channelCount,
// + 2-byte sample size,
// + 4-byte reserved
offset += 8;
uint16_t sampleRate;
if (!mDataSource->getUInt16(offset, &sampleRate)) {
ALOGE("MPEG4Extractor: error while reading ac-3 block: cannot read sample rate");
uint16_t sampleRate, channels;
status_t status;
if ((status = parseChannelCountSampleRate(&offset, &channels, &sampleRate)) != OK) {
return status;
}
uint32_t size;
// + 4-byte size
// + 4-byte type
// + 3-byte payload
const uint32_t kEAC3SpecificBoxMinSize = 11;
// 13 + 3 + (8 * (2 + 5 + 5 + 3 + 1 + 3 + 4 + (14 * 9 + 1))) bits == 152 bytes theoretical max
// calculated from the required bits read below as well as the maximum number of independent
// and dependant sub streams you can have
const uint32_t kEAC3SpecificBoxMaxSize = 152;
if (!mDataSource->getUInt32(offset, &size) ||
size < kEAC3SpecificBoxMinSize ||
size > kEAC3SpecificBoxMaxSize) {
ALOGE("MPEG4Extractor: error while reading eac-3 block: cannot read specific box size");
return ERROR_MALFORMED;
}
offset += 4;
uint32_t type;
if (!mDataSource->getUInt32(offset, &type) || type != FOURCC('d', 'e', 'c', '3')) {
ALOGE("MPEG4Extractor: error while reading eac-3 specific block: header not dec3");
return ERROR_MALFORMED;
}
// skip 4 bytes:
// + 2-byte sampleRate,
// + 2-byte reserved
offset += 4;
return parseAC3SpecificBox(offset, sampleRate);
uint8_t* chunk = new (std::nothrow) uint8_t[size];
if (chunk == NULL) {
return ERROR_MALFORMED;
}
if (mDataSource->readAt(offset, chunk, size) != (ssize_t)size) {
ALOGE("MPEG4Extractor: error while reading eac-3 specific block: bitstream fields");
delete[] chunk;
return ERROR_MALFORMED;
}
ABitReader br(chunk, size);
static const unsigned channelCountTable[] = {2, 1, 2, 3, 3, 4, 4, 5};
static const unsigned sampleRateTable[] = {48000, 44100, 32000};
if (br.numBitsLeft() < 16) {
delete[] chunk;
return ERROR_MALFORMED;
}
unsigned data_rate = br.getBits(13);
ALOGV("EAC3 data rate = %d", data_rate);
unsigned num_ind_sub = br.getBits(3) + 1;
ALOGV("EAC3 independant substreams = %d", num_ind_sub);
if (br.numBitsLeft() < (num_ind_sub * 23)) {
delete[] chunk;
return ERROR_MALFORMED;
}
unsigned channelCount = 0;
for (unsigned i = 0; i < num_ind_sub; i++) {
unsigned fscod = br.getBits(2);
if (fscod == 3) {
ALOGE("Incorrect fscod (3) in EAC3 header");
delete[] chunk;
return ERROR_MALFORMED;
}
unsigned boxSampleRate = sampleRateTable[fscod];
if (boxSampleRate != sampleRate) {
ALOGE("sample rate mismatch: boxSampleRate = %d, sampleRate = %d",
boxSampleRate, sampleRate);
delete[] chunk;
return ERROR_MALFORMED;
}
unsigned bsid = br.getBits(5);
if (bsid < 8) {
ALOGW("Incorrect bsid in EAC3 header. Possibly AC-3?");
delete[] chunk;
return ERROR_MALFORMED;
}
// skip
br.skipBits(2);
unsigned bsmod = br.getBits(3);
unsigned acmod = br.getBits(3);
unsigned lfeon = br.getBits(1);
// we currently only support the first stream
if (i == 0)
channelCount = channelCountTable[acmod] + lfeon;
ALOGV("bsmod = %d, acmod = %d, lfeon = %d", bsmod, acmod, lfeon);
br.skipBits(3);
unsigned num_dep_sub = br.getBits(4);
ALOGV("EAC3 dependant substreams = %d", num_dep_sub);
if (num_dep_sub != 0) {
if (br.numBitsLeft() < 9) {
delete[] chunk;
return ERROR_MALFORMED;
}
static const char* chan_loc_tbl[] = { "Lc/Rc","Lrs/Rrs","Cs","Ts","Lsd/Rsd",
"Lw/Rw","Lvh/Rvh","Cvh","Lfe2" };
unsigned chan_loc = br.getBits(9);
unsigned mask = 1;
for (unsigned j = 0; j < 9; j++, mask <<= 1) {
if ((chan_loc & mask) != 0) {
// we currently only support the first stream
if (i == 0) {
channelCount++;
// these are 2 channels in the mask
if (j == 0 || j == 1 || j == 4 || j == 5 || j == 6) {
channelCount++;
}
}
ALOGV(" %s", chan_loc_tbl[j]);
}
}
} else {
if (br.numBitsLeft() == 0) {
delete[] chunk;
return ERROR_MALFORMED;
}
br.skipBits(1);
}
}
if (br.numBitsLeft() != 0) {
if (br.numBitsLeft() < 8) {
delete[] chunk;
return ERROR_MALFORMED;
}
unsigned mask = br.getBits(8);
for (unsigned i = 0; i < 8; i++) {
if (((0x1 << i) && mask) == 0)
continue;
if (br.numBitsLeft() < 8) {
delete[] chunk;
return ERROR_MALFORMED;
}
switch (i) {
case 0: {
unsigned complexity = br.getBits(8);
ALOGV("Found a JOC stream with complexity = %d", complexity);
}break;
default: {
br.skipBits(8);
}break;
}
}
}
mLastTrack->meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_EAC3);
mLastTrack->meta.setInt32(kKeyChannelCount, channelCount);
mLastTrack->meta.setInt32(kKeySampleRate, sampleRate);
delete[] chunk;
return OK;
}
status_t MPEG4Extractor::parseAC3SpecificBox(
off64_t offset, uint16_t sampleRate) {
status_t MPEG4Extractor::parseAC3SpecificBox(off64_t offset) {
if (mLastTrack == NULL) {
return ERROR_MALFORMED;
}
uint16_t sampleRate, channels;
status_t status;
if ((status = parseChannelCountSampleRate(&offset, &channels, &sampleRate)) != OK) {
return status;
}
uint32_t size;
// + 4-byte size
// + 4-byte type
@ -2680,9 +2835,6 @@ status_t MPEG4Extractor::parseAC3SpecificBox(
unsigned lfeon = br.getBits(1);
unsigned channelCount = channelCountTable[acmod] + lfeon;
if (mLastTrack == NULL) {
return ERROR_MALFORMED;
}
mLastTrack->meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AC3);
mLastTrack->meta.setInt32(kKeyChannelCount, channelCount);
mLastTrack->meta.setInt32(kKeySampleRate, sampleRate);

@ -139,9 +139,10 @@ private:
Track *findTrackByMimePrefix(const char *mimePrefix);
status_t parseAC3SampleEntry(off64_t offset);
status_t parseAC3SpecificBox(off64_t offset, uint16_t sampleRate);
status_t parseAC4SampleEntry(off64_t offset);
status_t parseChannelCountSampleRate(
off64_t *offset, uint16_t *channelCount, uint16_t *sampleRate);
status_t parseAC3SpecificBox(off64_t offset);
status_t parseEAC3SpecificBox(off64_t offset);
status_t parseAC4SpecificBox(off64_t offset);
MPEG4Extractor(const MPEG4Extractor &);

@ -1577,6 +1577,7 @@ static const struct mime_conv_t mimeLookup[] = {
{ MEDIA_MIMETYPE_AUDIO_VORBIS, AUDIO_FORMAT_VORBIS },
{ MEDIA_MIMETYPE_AUDIO_OPUS, AUDIO_FORMAT_OPUS},
{ MEDIA_MIMETYPE_AUDIO_AC3, AUDIO_FORMAT_AC3},
{ MEDIA_MIMETYPE_AUDIO_EAC3, AUDIO_FORMAT_E_AC3},
{ MEDIA_MIMETYPE_AUDIO_AC4, AUDIO_FORMAT_AC4},
{ MEDIA_MIMETYPE_AUDIO_FLAC, AUDIO_FORMAT_FLAC},
{ 0, AUDIO_FORMAT_INVALID }
@ -1868,4 +1869,3 @@ AString nameForFd(int fd) {
}
} // namespace android

@ -815,6 +815,10 @@ ATSParser::Stream::Stream(
mode = ElementaryStreamQueue::AC3;
break;
case STREAMTYPE_EAC3:
mode = ElementaryStreamQueue::EAC3;
break;
case STREAMTYPE_PES_PRIVATE_DATA:
if (mStreamTypeExt == EXT_DESCRIPTOR_DVB_AC4) {
mode = ElementaryStreamQueue::AC4;
@ -1026,6 +1030,7 @@ bool ATSParser::Stream::isAudio() const {
case STREAMTYPE_MPEG2_AUDIO_ADTS:
case STREAMTYPE_LPCM_AC3:
case STREAMTYPE_AC3:
case STREAMTYPE_EAC3:
case STREAMTYPE_AAC_ENCRYPTED:
case STREAMTYPE_AC3_ENCRYPTED:
return true;

@ -154,6 +154,7 @@ struct ATSParser : public RefBase {
// Stream type 0x83 is non-standard,
// it could be LPCM or TrueHD AC3
STREAMTYPE_LPCM_AC3 = 0x83,
STREAMTYPE_EAC3 = 0x87,
//Sample Encrypted types
STREAMTYPE_H264_ENCRYPTED = 0xDB,

@ -210,8 +210,81 @@ static unsigned parseAC3SyncFrame(
return payloadSize;
}
static bool IsSeeminglyValidAC3Header(const uint8_t *ptr, size_t size) {
return parseAC3SyncFrame(ptr, size, NULL) > 0;
// Parse EAC3 header assuming the current ptr is start position of syncframe,
// update metadata only applicable, and return the payload size
// ATSC A/52:2012 E2.3.1
static unsigned parseEAC3SyncFrame(
const uint8_t *ptr, size_t size, sp<MetaData> *metaData) {
static const unsigned channelCountTable[] = {2, 1, 2, 3, 3, 4, 4, 5};
static const unsigned samplingRateTable[] = {48000, 44100, 32000};
static const unsigned samplingRateTable2[] = {24000, 22050, 16000};
ABitReader bits(ptr, size);
if (bits.numBitsLeft() < 16) {
ALOGE("Not enough bits left for further parsing");
return 0;
}
if (bits.getBits(16) != 0x0B77) {
ALOGE("No valid sync word in EAC3 header");
return 0;
}
// we parse up to bsid so there needs to be at least that many bits
if (bits.numBitsLeft() < 2 + 3 + 11 + 2 + 2 + 3 + 1 + 5) {
ALOGE("Not enough bits left for further parsing");
return 0;
}
unsigned strmtyp = bits.getBits(2);
if (strmtyp == 3) {
ALOGE("Incorrect strmtyp in EAC3 header");
return 0;
}
unsigned substreamid = bits.getBits(3);
// only the first independent stream is supported
if ((strmtyp == 0 || strmtyp == 2) && substreamid != 0)
return 0;
unsigned frmsiz = bits.getBits(11);
unsigned fscod = bits.getBits(2);
unsigned samplingRate = 0;
if (fscod == 0x3) {
unsigned fscod2 = bits.getBits(2);
if (fscod2 == 3) {
ALOGW("Incorrect fscod2 in EAC3 header");
return 0;
}
samplingRate = samplingRateTable2[fscod2];
} else {
samplingRate = samplingRateTable[fscod];
unsigned numblkscod __unused = bits.getBits(2);
}
unsigned acmod = bits.getBits(3);
unsigned lfeon = bits.getBits(1);
unsigned bsid = bits.getBits(5);
if (bsid < 11 || bsid > 16) {
ALOGW("Incorrect bsid in EAC3 header. Could be AC-3 or some unknown EAC3 format");
return 0;
}
// we currently only support the first independant stream
if (metaData != NULL && (strmtyp == 0 || strmtyp == 2)) {
unsigned channelCount = channelCountTable[acmod] + lfeon;
ALOGV("EAC3 channelCount = %d", channelCount);
ALOGV("EAC3 samplingRate = %d", samplingRate);
(*metaData)->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_EAC3);
(*metaData)->setInt32(kKeyChannelCount, channelCount);
(*metaData)->setInt32(kKeySampleRate, samplingRate);
(*metaData)->setInt32(kKeyIsSyncFrame, 1);
}
unsigned payloadSize = frmsiz + 1;
payloadSize <<= 1; // convert from 16-bit words to bytes
return payloadSize;
}
// Parse AC4 header assuming the current ptr is start position of syncframe
@ -477,12 +550,19 @@ status_t ElementaryStreamQueue::appendData(
}
case AC3:
case EAC3:
{
uint8_t *ptr = (uint8_t *)data;
ssize_t startOffset = -1;
for (size_t i = 0; i < size; ++i) {
if (IsSeeminglyValidAC3Header(&ptr[i], size - i)) {
unsigned payloadSize = 0;
if (mMode == AC3) {
payloadSize = parseAC3SyncFrame(&ptr[i], size - i, NULL);
} else if (mMode == EAC3) {
payloadSize = parseEAC3SyncFrame(&ptr[i], size - i, NULL);
}
if (payloadSize > 0) {
startOffset = i;
break;
}
@ -493,7 +573,7 @@ status_t ElementaryStreamQueue::appendData(
}
if (startOffset > 0) {
ALOGI("found something resembling an AC3 syncword at "
ALOGI("found something resembling an (E)AC3 syncword at "
"offset %zd",
startOffset);
}
@ -526,8 +606,9 @@ status_t ElementaryStreamQueue::appendData(
}
if (startOffset > 0) {
ALOGI("found something resembling an AC4 syncword at offset %zd",
startOffset);
ALOGI("found something resembling an AC4 syncword at "
"offset %zd",
startOffset);
}
if (frameSize != size - startOffset) {
ALOGV("AC4 frame size is %u bytes, while the buffer size is %zd bytes.",
@ -756,7 +837,8 @@ sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnit() {
case AAC:
return dequeueAccessUnitAAC();
case AC3:
return dequeueAccessUnitAC3();
case EAC3:
return dequeueAccessUnitEAC3();
case AC4:
return dequeueAccessUnitAC4();
case MPEG_VIDEO:
@ -776,34 +858,38 @@ sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnit() {
}
}
sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitAC3() {
sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitEAC3() {
unsigned syncStartPos = 0; // in bytes
unsigned payloadSize = 0;
sp<MetaData> format = new MetaData;
ALOGV("dequeueAccessUnit_AC3[%d]: mBuffer %p(%zu)", mAUIndex, mBuffer->data(), mBuffer->size());
ALOGV("dequeueAccessUnitEAC3[%d]: mBuffer %p(%zu)", mAUIndex,
mBuffer->data(), mBuffer->size());
while (true) {
if (syncStartPos + 2 >= mBuffer->size()) {
return NULL;
}
payloadSize = parseAC3SyncFrame(
mBuffer->data() + syncStartPos,
mBuffer->size() - syncStartPos,
&format);
uint8_t *ptr = mBuffer->data() + syncStartPos;
size_t size = mBuffer->size() - syncStartPos;
if (mMode == AC3) {
payloadSize = parseAC3SyncFrame(ptr, size, &format);
} else if (mMode == EAC3) {
payloadSize = parseEAC3SyncFrame(ptr, size, &format);
}
if (payloadSize > 0) {
break;
}
ALOGV("dequeueAccessUnit_AC3[%d]: syncStartPos %u payloadSize %u",
ALOGV("dequeueAccessUnitEAC3[%d]: syncStartPos %u payloadSize %u",
mAUIndex, syncStartPos, payloadSize);
++syncStartPos;
}
if (mBuffer->size() < syncStartPos + payloadSize) {
ALOGV("Not enough buffer size for AC3");
ALOGV("Not enough buffer size for E/AC3");
return NULL;
}
@ -811,7 +897,6 @@ sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitAC3() {
mFormat = format;
}
int64_t timeUs = fetchTimestamp(syncStartPos + payloadSize);
if (timeUs < 0ll) {
ALOGE("negative timeUs");
@ -820,7 +905,12 @@ sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitAC3() {
// Not decrypting if key info not available (e.g., scanner/extractor parsing ts files)
if (mSampleDecryptor != NULL) {
mSampleDecryptor->processAC3(mBuffer->data() + syncStartPos, payloadSize);
if (mMode == AC3) {
mSampleDecryptor->processAC3(mBuffer->data() + syncStartPos, payloadSize);
} else if (mMode == EAC3) {
ALOGE("EAC3 AU is encrypted and decryption is not supported");
return NULL;
}
}
mAUIndex++;

@ -38,6 +38,7 @@ struct ElementaryStreamQueue {
H264,
AAC,
AC3,
EAC3,
AC4,
MPEG_AUDIO,
MPEG_VIDEO,
@ -116,7 +117,7 @@ private:
sp<ABuffer> dequeueAccessUnitH264();
sp<ABuffer> dequeueAccessUnitAAC();
sp<ABuffer> dequeueAccessUnitAC3();
sp<ABuffer> dequeueAccessUnitEAC3();
sp<ABuffer> dequeueAccessUnitAC4();
sp<ABuffer> dequeueAccessUnitMPEGAudio();
sp<ABuffer> dequeueAccessUnitMPEGVideo();

Loading…
Cancel
Save