Commit message: None
CVE: CVE-2016-0815
CWE: CWE-20
Vulnerability description: The MPEG4Source::fragmentedRead function in MPEG4Extractor.cpp in libstagefright in mediaserver in Android 4.x before 4.4.4, 5.x before 5.1.1 LMY49H, and 6.x before 2016-03-01 allows remote attackers to execute arbitrary code or cause a denial of service (memory corruption) via a crafted media file, aka internal bug 26365349.

Project: Android

static bool BetterSniffMPEG4(
 const sp<DataSource> &source, String8 *mimeType, float *confidence,
        sp<AMessage> *meta) {
 static const off64_t kMaxScanOffset = 128ll;

 off64_t offset = 0ll;
 bool foundGoodFileType = false;
 off64_t moovAtomEndOffset = -1ll;
 bool done = false;

 while (!done && offset < kMaxScanOffset) {
 uint32_t hdr[2];
 if (source->readAt(offset, hdr, 8) < 8) {
 return false;
 }

 uint64_t chunkSize = ntohl(hdr[0]);
 uint32_t chunkType = ntohl(hdr[1]);
 off64_t chunkDataOffset = offset + 8;

 if (chunkSize == 1) {
 if (source->readAt(offset + 8, &chunkSize, 8) < 8) {
 return false;
 }

            chunkSize = ntoh64(chunkSize);
            chunkDataOffset += 8;

 if (chunkSize < 16) {
 return false;
 }
 } else if (chunkSize < 8) {
 return false;
 }

 off64_t chunkDataSize = offset + chunkSize - chunkDataOffset;

 char chunkstring[5];
 MakeFourCCString(chunkType, chunkstring);
        ALOGV("saw chunk type %s, size %lld @ %lld", chunkstring, chunkSize, offset);
 switch (chunkType) {
 case FOURCC('f', 't', 'y', 'p'):
 {
 if (chunkDataSize < 8) {
 return false;
 }

 uint32_t numCompatibleBrands = (chunkDataSize - 8) / 4;
 for (size_t i = 0; i < numCompatibleBrands + 2; ++i) {
 if (i == 1) {
 continue;
 }

 uint32_t brand;
 if (source->readAt(
                                chunkDataOffset + 4 * i, &brand, 4) < 4) {
 return false;
 }

                    brand = ntohl(brand);

 if (isCompatibleBrand(brand)) {
                        foundGoodFileType = true;
 break;
 }
 }

 if (!foundGoodFileType) {
 return false;
 }

 break;
 }

 case FOURCC('m', 'o', 'o', 'v'):
 {
                moovAtomEndOffset = offset + chunkSize;

                done = true;
 break;
 }

 default:
 break;
 }

        offset += chunkSize;
 }

 if (!foundGoodFileType) {
 return false;
 }

 *mimeType = MEDIA_MIMETYPE_CONTAINER_MPEG4;
 *confidence = 0.4f;

 if (moovAtomEndOffset >= 0) {
 *meta = new AMessage;
 (*meta)->setInt64("meta-data-size", moovAtomEndOffset);

        ALOGV("found metadata size: %lld", moovAtomEndOffset);
 }

 return true;
}

Project: Android

static const char *FourCC2MIME(uint32_t fourcc) {
 switch (fourcc) {
 case FOURCC('m', 'p', '4', 'a'):
 return MEDIA_MIMETYPE_AUDIO_AAC;

 case FOURCC('s', 'a', 'm', 'r'):
 return MEDIA_MIMETYPE_AUDIO_AMR_NB;

 case FOURCC('s', 'a', 'w', 'b'):
 return MEDIA_MIMETYPE_AUDIO_AMR_WB;

 case FOURCC('m', 'p', '4', 'v'):
 return MEDIA_MIMETYPE_VIDEO_MPEG4;

 case FOURCC('s', '2', '6', '3'):
 case FOURCC('h', '2', '6', '3'):
 case FOURCC('H', '2', '6', '3'):
 return MEDIA_MIMETYPE_VIDEO_H263;

 case FOURCC('a', 'v', 'c', '1'):
 return MEDIA_MIMETYPE_VIDEO_AVC;

 default:
            CHECK(!"should not be here.");
 return NULL;
 }
}

Project: Android

MPEG4Extractor::MPEG4Extractor(const sp<DataSource> &source)
 : mSidxDuration(0),
      mMoofOffset(0),
      mDataSource(source),
      mInitCheck(NO_INIT),
      mHasVideo(false),
      mHeaderTimescale(0),
      mFirstTrack(NULL),
      mLastTrack(NULL),
      mFileMetaData(new MetaData),
      mFirstSINF(NULL),
      mIsDrm(false) {
}

Project: Android

MPEG4Source::MPEG4Source(
 const sp<MetaData> &format,
 const sp<DataSource> &dataSource,
 int32_t timeScale,
 const sp<SampleTable> &sampleTable,
 Vector<SidxEntry> &sidx,
 off64_t firstMoofOffset)
 : mFormat(format),
      mDataSource(dataSource),
      mTimescale(timeScale),
      mSampleTable(sampleTable),
      mCurrentSampleIndex(0),
      mCurrentFragmentIndex(0),
      mSegments(sidx),
      mFirstMoofOffset(firstMoofOffset),
      mCurrentMoofOffset(firstMoofOffset),
      mCurrentTime(0),
      mCurrentSampleInfoAllocSize(0),
      mCurrentSampleInfoSizes(NULL),
      mCurrentSampleInfoOffsetsAllocSize(0),
      mCurrentSampleInfoOffsets(NULL),
      mIsAVC(false),
      mNALLengthSize(0),
      mStarted(false),
      mGroup(NULL),
      mBuffer(NULL),
      mWantsNALFragments(false),
      mSrcBuffer(NULL) {

    mFormat->findInt32(kKeyCryptoMode, &mCryptoMode);
    mDefaultIVSize = 0;
    mFormat->findInt32(kKeyCryptoDefaultIVSize, &mDefaultIVSize);
 uint32_t keytype;
 const void *key;
 size_t keysize;
 if (mFormat->findData(kKeyCryptoKey, &keytype, &key, &keysize)) {
        CHECK(keysize <= 16);
        memset(mCryptoKey, 0, 16);
        memcpy(mCryptoKey, key, keysize);
 }

 const char *mime;
 bool success = mFormat->findCString(kKeyMIMEType, &mime);
    CHECK(success);

    mIsAVC = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC);

 if (mIsAVC) {
 uint32_t type;
 const void *data;
 size_t size;
        CHECK(format->findData(kKeyAVCC, &type, &data, &size));

 const uint8_t *ptr = (const uint8_t *)data;

        CHECK(size >= 7);
        CHECK_EQ((unsigned)ptr[0], 1u); // configurationVersion == 1

        mNALLengthSize = 1 + (ptr[4] & 3);
 }

    CHECK(format->findInt32(kKeyTrackID, &mTrackId));

 if (mFirstMoofOffset != 0) {
 off64_t offset = mFirstMoofOffset;
        parseChunk(&offset);
 }
}

Project: Android

size_t MPEG4Extractor::countTracks() {
 status_t err;
 if ((err = readMetaData()) != OK) {
        ALOGV("MPEG4Extractor::countTracks: no tracks");
 return 0;
 }

 size_t n = 0;
 Track *track = mFirstTrack;
 while (track) {
 ++n;
        track = track->next;
 }

    ALOGV("MPEG4Extractor::countTracks: %d tracks", n);
 return n;
}

Project: Android

sp<MediaSource> MPEG4Extractor::getTrack(size_t index) {
 status_t err;
 if ((err = readMetaData()) != OK) {
 return NULL;
 }

 Track *track = mFirstTrack;
 while (index > 0) {
 if (track == NULL) {
 return NULL;
 }

        track = track->next;
 --index;
 }

 if (track == NULL) {
 return NULL;
 }

    ALOGV("getTrack called, pssh: %d", mPssh.size());

 return new MPEG4Source(
            track->meta, mDataSource, track->timescale, track->sampleTable,
            mSidxEntries, mMoofOffset);
}

Project: Android

static void hexdump(const void *_data, size_t size) {
 const uint8_t *data = (const uint8_t *)_data;
 size_t offset = 0;
 while (offset < size) {
        printf("0x%04x  ", offset);

 size_t n = size - offset;
 if (n > 16) {
            n = 16;
 }

 for (size_t i = 0; i < 16; ++i) {
 if (i == 8) {
                printf(" ");
 }

 if (offset + i < size) {
                printf("%02x ", data[offset + i]);
 } else {
                printf("   ");
 }
 }

        printf(" ");

 for (size_t i = 0; i < n; ++i) {
 if (isprint(data[offset + i])) {
                printf("%c", data[offset + i]);
 } else {
                printf(".");
 }
 }

        printf("\n");

        offset += 16;
 }
}

Project: Android

static bool isCompatibleBrand(uint32_t fourcc) {
 static const uint32_t kCompatibleBrands[] = {
        FOURCC('i', 's', 'o', 'm'),
        FOURCC('i', 's', 'o', '2'),
        FOURCC('a', 'v', 'c', '1'),
        FOURCC('3', 'g', 'p', '4'),
        FOURCC('m', 'p', '4', '1'),
        FOURCC('m', 'p', '4', '2'),

        FOURCC('q', 't', ' ', ' '), // Apple's QuickTime
        FOURCC('M', 'S', 'N', 'V'), // Sony's PSP

        FOURCC('3', 'g', '2', 'a'), // 3GPP2
        FOURCC('3', 'g', '2', 'b'),
 };

 for (size_t i = 0;
         i < sizeof(kCompatibleBrands) / sizeof(kCompatibleBrands[0]);
 ++i) {
 if (kCompatibleBrands[i] == fourcc) {
 return true;
 }
 }

 return false;
}

Project: Android

status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
    ALOGV("entering parseChunk %lld/%d", *offset, depth);
 uint32_t hdr[2];
 if (mDataSource->readAt(*offset, hdr, 8) < 8) {
 return ERROR_IO;
 }
 uint64_t chunk_size = ntohl(hdr[0]);
 uint32_t chunk_type = ntohl(hdr[1]);
 off64_t data_offset = *offset + 8;

 if (chunk_size == 1) {
 if (mDataSource->readAt(*offset + 8, &chunk_size, 8) < 8) {
 return ERROR_IO;
 }
        chunk_size = ntoh64(chunk_size);
        data_offset += 8;

 if (chunk_size < 16) {
 return ERROR_MALFORMED;
 }
 } else if (chunk_size < 8) {
 return ERROR_MALFORMED;
 }

 char chunk[5];
 MakeFourCCString(chunk_type, chunk);
    ALOGV("chunk: %s @ %lld, %d", chunk, *offset, depth);

#if 0
 static const char kWhitespace[] = "                                        ";
 const char *indent = &kWhitespace[sizeof(kWhitespace) - 1 - 2 * depth];
    printf("%sfound chunk '%s' of size %lld\n", indent, chunk, chunk_size);

 char buffer[256];
 size_t n = chunk_size;
 if (n > sizeof(buffer)) {
        n = sizeof(buffer);
 }
 if (mDataSource->readAt(*offset, buffer, n)
 < (ssize_t)n) {
 return ERROR_IO;
 }

    hexdump(buffer, n);
#endif

 PathAdder autoAdder(&mPath, chunk_type);

 off64_t chunk_data_size = *offset + chunk_size - data_offset;

 if (chunk_type != FOURCC('c', 'p', 'r', 't')
 && chunk_type != FOURCC('c', 'o', 'v', 'r')
 && mPath.size() == 5 && underMetaDataPath(mPath)) {
 off64_t stop_offset = *offset + chunk_size;
 *offset = data_offset;
 while (*offset < stop_offset) {
 status_t err = parseChunk(offset, depth + 1);
 if (err != OK) {
 return err;
 }
 }

 if (*offset != stop_offset) {
 return ERROR_MALFORMED;
 }

 return OK;
 }

 switch(chunk_type) {
 case FOURCC('m', 'o', 'o', 'v'):
 case FOURCC('t', 'r', 'a', 'k'):
 case FOURCC('m', 'd', 'i', 'a'):
 case FOURCC('m', 'i', 'n', 'f'):
 case FOURCC('d', 'i', 'n', 'f'):
 case FOURCC('s', 't', 'b', 'l'):
 case FOURCC('m', 'v', 'e', 'x'):
 case FOURCC('m', 'o', 'o', 'f'):
 case FOURCC('t', 'r', 'a', 'f'):
 case FOURCC('m', 'f', 'r', 'a'):
 case FOURCC('u', 'd', 't', 'a'):
 case FOURCC('i', 'l', 's', 't'):
 case FOURCC('s', 'i', 'n', 'f'):
 case FOURCC('s', 'c', 'h', 'i'):
 case FOURCC('e', 'd', 't', 's'):
 {
 if (chunk_type == FOURCC('s', 't', 'b', 'l')) {
                ALOGV("sampleTable chunk is %d bytes long.", (size_t)chunk_size);

 if (mDataSource->flags()
 & (DataSource::kWantsPrefetching
 | DataSource::kIsCachingDataSource)) {
                    sp<MPEG4DataSource> cachedSource =
 new MPEG4DataSource(mDataSource);

 if (cachedSource->setCachedRange(*offset, chunk_size) == OK) {
                        mDataSource = cachedSource;
 }
 }

                mLastTrack->sampleTable = new SampleTable(mDataSource);
 }

 bool isTrack = false;
 if (chunk_type == FOURCC('t', 'r', 'a', 'k')) {
                isTrack = true;

 Track *track = new Track;
                track->next = NULL;
 if (mLastTrack) {
                    mLastTrack->next = track;
 } else {
                    mFirstTrack = track;
 }
                mLastTrack = track;

                track->meta = new MetaData;
                track->includes_expensive_metadata = false;
                track->skipTrack = false;
                track->timescale = 0;
                track->meta->setCString(kKeyMIMEType, "application/octet-stream");
 }

 off64_t stop_offset = *offset + chunk_size;
 *offset = data_offset;
 while (*offset < stop_offset) {
 status_t err = parseChunk(offset, depth + 1);
 if (err != OK) {
 return err;
 }
 }

 if (*offset != stop_offset) {
 return ERROR_MALFORMED;
 }

 if (isTrack) {
 if (mLastTrack->skipTrack) {
 Track *cur = mFirstTrack;

 if (cur == mLastTrack) {
 delete cur;
                        mFirstTrack = mLastTrack = NULL;
 } else {
 while (cur && cur->next != mLastTrack) {
                            cur = cur->next;
 }
                        cur->next = NULL;
 delete mLastTrack;
                        mLastTrack = cur;
 }

 return OK;
 }

 status_t err = verifyTrack(mLastTrack);

 if (err != OK) {
 return err;
 }
 } else if (chunk_type == FOURCC('m', 'o', 'o', 'v')) {
                mInitCheck = OK;

 if (!mIsDrm) {
 return UNKNOWN_ERROR; // Return a dummy error.
 } else {
 return OK;
 }
 }
 break;
 }

 case FOURCC('e', 'l', 's', 't'):
 {
 uint8_t version;
 if (mDataSource->readAt(data_offset, &version, 1) < 1) {
 return ERROR_IO;
 }

 uint32_t entry_count;
 if (!mDataSource->getUInt32(data_offset + 4, &entry_count)) {
 return ERROR_IO;
 }

 if (entry_count != 1) {
                ALOGW("ignoring edit list with %d entries", entry_count);
 } else if (mHeaderTimescale == 0) {
                ALOGW("ignoring edit list because timescale is 0");
 } else {
 off64_t entriesoffset = data_offset + 8;
 uint64_t segment_duration;
 int64_t media_time;

 if (version == 1) {
 if (!mDataSource->getUInt64(entriesoffset, &segment_duration) ||
 !mDataSource->getUInt64(entriesoffset + 8, (uint64_t*)&media_time)) {
 return ERROR_IO;
 }
 } else if (version == 0) {
 uint32_t sd;
 int32_t mt;
 if (!mDataSource->getUInt32(entriesoffset, &sd) ||
 !mDataSource->getUInt32(entriesoffset + 4, (uint32_t*)&mt)) {
 return ERROR_IO;
 }
                    segment_duration = sd;
                    media_time = mt;
 } else {
 return ERROR_IO;
 }

 uint64_t halfscale = mHeaderTimescale / 2;
                segment_duration = (segment_duration * 1000000 + halfscale)/ mHeaderTimescale;
                media_time = (media_time * 1000000 + halfscale) / mHeaderTimescale;

 int64_t duration;
 int32_t samplerate;
 if (mLastTrack->meta->findInt64(kKeyDuration, &duration) &&
                        mLastTrack->meta->findInt32(kKeySampleRate, &samplerate)) {

 int64_t delay = (media_time  * samplerate + 500000) / 1000000;
                    mLastTrack->meta->setInt32(kKeyEncoderDelay, delay);

 int64_t paddingus = duration - (segment_duration + media_time);
 if (paddingus < 0) {
                        paddingus = 0;
 }
 int64_t paddingsamples = (paddingus * samplerate + 500000) / 1000000;
                    mLastTrack->meta->setInt32(kKeyEncoderPadding, paddingsamples);
 }
 }
 *offset += chunk_size;
 break;
 }

 case FOURCC('f', 'r', 'm', 'a'):
 {
 uint32_t original_fourcc;
 if (mDataSource->readAt(data_offset, &original_fourcc, 4) < 4) {
 return ERROR_IO;
 }
            original_fourcc = ntohl(original_fourcc);
            ALOGV("read original format: %d", original_fourcc);
            mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(original_fourcc));
 uint32_t num_channels = 0;
 uint32_t sample_rate = 0;
 if (AdjustChannelsAndRate(original_fourcc, &num_channels, &sample_rate)) {
                mLastTrack->meta->setInt32(kKeyChannelCount, num_channels);
                mLastTrack->meta->setInt32(kKeySampleRate, sample_rate);
 }
 *offset += chunk_size;
 break;
 }

 case FOURCC('t', 'e', 'n', 'c'):
 {
 if (chunk_size < 32) {
 return ERROR_MALFORMED;
 }

 char buf[4];
            memset(buf, 0, 4);
 if (mDataSource->readAt(data_offset + 4, buf + 1, 3) < 3) {
 return ERROR_IO;
 }
 uint32_t defaultAlgorithmId = ntohl(*((int32_t*)buf));
 if (defaultAlgorithmId > 1) {
 return ERROR_MALFORMED;
 }

            memset(buf, 0, 4);
 if (mDataSource->readAt(data_offset + 7, buf + 3, 1) < 1) {
 return ERROR_IO;
 }
 uint32_t defaultIVSize = ntohl(*((int32_t*)buf));

 if ((defaultAlgorithmId == 0 && defaultIVSize != 0) ||
 (defaultAlgorithmId != 0 && defaultIVSize == 0)) {
 return ERROR_MALFORMED;
 } else if (defaultIVSize != 0 &&
                    defaultIVSize != 8 &&
                    defaultIVSize != 16) {
 return ERROR_MALFORMED;
 }

 uint8_t defaultKeyId[16];

 if (mDataSource->readAt(data_offset + 8, &defaultKeyId, 16) < 16) {
 return ERROR_IO;
 }

            mLastTrack->meta->setInt32(kKeyCryptoMode, defaultAlgorithmId);
            mLastTrack->meta->setInt32(kKeyCryptoDefaultIVSize, defaultIVSize);
            mLastTrack->meta->setData(kKeyCryptoKey, 'tenc', defaultKeyId, 16);
 *offset += chunk_size;
 break;
 }

 case FOURCC('t', 'k', 'h', 'd'):
 {
 status_t err;
 if ((err = parseTrackHeader(data_offset, chunk_data_size)) != OK) {
 return err;
 }

 *offset += chunk_size;
 break;
 }

 case FOURCC('p', 's', 's', 'h'):
 {
 PsshInfo pssh;

 if (mDataSource->readAt(data_offset + 4, &pssh.uuid, 16) < 16) {
 return ERROR_IO;
 }

 uint32_t psshdatalen = 0;
 if (mDataSource->readAt(data_offset + 20, &psshdatalen, 4) < 4) {
 return ERROR_IO;
 }
            pssh.datalen = ntohl(psshdatalen);
            ALOGV("pssh data size: %d", pssh.datalen);
 if (pssh.datalen + 20 > chunk_size) {
 return ERROR_MALFORMED;
 }

            pssh.data = new (std::nothrow) uint8_t[pssh.datalen];
 if (pssh.data == NULL) {
 return ERROR_MALFORMED;
 }
            ALOGV("allocated pssh @ %p", pssh.data);
 ssize_t requested = (ssize_t) pssh.datalen;
 if (mDataSource->readAt(data_offset + 24, pssh.data, requested) < requested) {
 return ERROR_IO;
 }
            mPssh.push_back(pssh);

 *offset += chunk_size;
 break;
 }

 case FOURCC('m', 'd', 'h', 'd'):
 {
 if (chunk_data_size < 4) {
 return ERROR_MALFORMED;
 }

 uint8_t version;
 if (mDataSource->readAt(
                        data_offset, &version, sizeof(version))
 < (ssize_t)sizeof(version)) {
 return ERROR_IO;
 }

 off64_t timescale_offset;

 if (version == 1) {
                timescale_offset = data_offset + 4 + 16;
 } else if (version == 0) {
                timescale_offset = data_offset + 4 + 8;
 } else {
 return ERROR_IO;
 }

 uint32_t timescale;
 if (mDataSource->readAt(
                        timescale_offset, &timescale, sizeof(timescale))
 < (ssize_t)sizeof(timescale)) {
 return ERROR_IO;
 }

            mLastTrack->timescale = ntohl(timescale);

 int64_t duration = 0;
 if (version == 1) {
 if (mDataSource->readAt(
                            timescale_offset + 4, &duration, sizeof(duration))
 < (ssize_t)sizeof(duration)) {
 return ERROR_IO;
 }
                duration = ntoh64(duration);
 } else {
 uint32_t duration32;
 if (mDataSource->readAt(
                            timescale_offset + 4, &duration32, sizeof(duration32))
 < (ssize_t)sizeof(duration32)) {
 return ERROR_IO;
 }
 if (duration32 != 0xffffffff) {
                    duration = ntohl(duration32);
 }
 }
            mLastTrack->meta->setInt64(
                    kKeyDuration, (duration * 1000000) / mLastTrack->timescale);

 uint8_t lang[2];
 off64_t lang_offset;
 if (version == 1) {
                lang_offset = timescale_offset + 4 + 8;
 } else if (version == 0) {
                lang_offset = timescale_offset + 4 + 4;
 } else {
 return ERROR_IO;
 }

 if (mDataSource->readAt(lang_offset, &lang, sizeof(lang))
 < (ssize_t)sizeof(lang)) {
 return ERROR_IO;
 }

 char lang_code[4];
            lang_code[0] = ((lang[0] >> 2) & 0x1f) + 0x60;
            lang_code[1] = ((lang[0] & 0x3) << 3 | (lang[1] >> 5)) + 0x60;
            lang_code[2] = (lang[1] & 0x1f) + 0x60;
            lang_code[3] = '\0';

            mLastTrack->meta->setCString(
                    kKeyMediaLanguage, lang_code);

 *offset += chunk_size;
 break;
 }

 case FOURCC('s', 't', 's', 'd'):
 {
 if (chunk_data_size < 8) {
 return ERROR_MALFORMED;
 }

 uint8_t buffer[8];
 if (chunk_data_size < (off64_t)sizeof(buffer)) {
 return ERROR_MALFORMED;
 }

 if (mDataSource->readAt(
                        data_offset, buffer, 8) < 8) {
 return ERROR_IO;
 }

 if (U32_AT(buffer) != 0) {
 return ERROR_MALFORMED;
 }

 uint32_t entry_count = U32_AT(&buffer[4]);

 if (entry_count > 1) {
 const char *mime;
                CHECK(mLastTrack->meta->findCString(kKeyMIMEType, &mime));
 if (strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP) &&
                        strcasecmp(mime, "application/octet-stream")) {
                    mLastTrack->skipTrack = true;
 *offset += chunk_size;
 break;
 }
 }
 off64_t stop_offset = *offset + chunk_size;
 *offset = data_offset + 8;
 for (uint32_t i = 0; i < entry_count; ++i) {
 status_t err = parseChunk(offset, depth + 1);
 if (err != OK) {
 return err;
 }
 }

 if (*offset != stop_offset) {
 return ERROR_MALFORMED;
 }
 break;
 }

 case FOURCC('m', 'p', '4', 'a'):
 case FOURCC('e', 'n', 'c', 'a'):
 case FOURCC('s', 'a', 'm', 'r'):
 case FOURCC('s', 'a', 'w', 'b'):
 {
 uint8_t buffer[8 + 20];
 if (chunk_data_size < (ssize_t)sizeof(buffer)) {
 return ERROR_MALFORMED;
 }

 if (mDataSource->readAt(
                        data_offset, buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) {
 return ERROR_IO;
 }

 uint16_t data_ref_index = U16_AT(&buffer[6]);
 uint32_t num_channels = U16_AT(&buffer[16]);

 uint16_t sample_size = U16_AT(&buffer[18]);
 uint32_t sample_rate = U32_AT(&buffer[24]) >> 16;

 if (chunk_type != FOURCC('e', 'n', 'c', 'a')) {
                mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(chunk_type));
 AdjustChannelsAndRate(chunk_type, &num_channels, &sample_rate);
 }
            ALOGV("*** coding='%s' %d channels, size %d, rate %d\n",
                   chunk, num_channels, sample_size, sample_rate);
            mLastTrack->meta->setInt32(kKeyChannelCount, num_channels);
            mLastTrack->meta->setInt32(kKeySampleRate, sample_rate);

 off64_t stop_offset = *offset + chunk_size;
 *offset = data_offset + sizeof(buffer);
 while (*offset < stop_offset) {
 status_t err = parseChunk(offset, depth + 1);
 if (err != OK) {
 return err;
 }
 }

 if (*offset != stop_offset) {
 return ERROR_MALFORMED;
 }
 break;
 }

 case FOURCC('m', 'p', '4', 'v'):
 case FOURCC('e', 'n', 'c', 'v'):
 case FOURCC('s', '2', '6', '3'):
 case FOURCC('H', '2', '6', '3'):
 case FOURCC('h', '2', '6', '3'):
 case FOURCC('a', 'v', 'c', '1'):
 {
            mHasVideo = true;

 uint8_t buffer[78];
 if (chunk_data_size < (ssize_t)sizeof(buffer)) {
 return ERROR_MALFORMED;
 }

 if (mDataSource->readAt(
                        data_offset, buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) {
 return ERROR_IO;
 }

 uint16_t data_ref_index = U16_AT(&buffer[6]);
 uint16_t width = U16_AT(&buffer[6 + 18]);
 uint16_t height = U16_AT(&buffer[6 + 20]);

 if (width == 0)  width  = 352;
 if (height == 0) height = 288;


 if (chunk_type != FOURCC('e', 'n', 'c', 'v')) {
                mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(chunk_type));
 }
            mLastTrack->meta->setInt32(kKeyWidth, width);
            mLastTrack->meta->setInt32(kKeyHeight, height);

 off64_t stop_offset = *offset + chunk_size;
 *offset = data_offset + sizeof(buffer);
 while (*offset < stop_offset) {
 status_t err = parseChunk(offset, depth + 1);
 if (err != OK) {
 return err;
 }
 }

 if (*offset != stop_offset) {
 return ERROR_MALFORMED;
 }
 break;
 }

 case FOURCC('s', 't', 'c', 'o'):
 case FOURCC('c', 'o', '6', '4'):
 {
 status_t err =
                mLastTrack->sampleTable->setChunkOffsetParams(
                        chunk_type, data_offset, chunk_data_size);

 if (err != OK) {
 return err;
 }

 *offset += chunk_size;
 break;
 }

 case FOURCC('s', 't', 's', 'c'):
 {
 status_t err =
                mLastTrack->sampleTable->setSampleToChunkParams(
                        data_offset, chunk_data_size);

 if (err != OK) {
 return err;
 }

 *offset += chunk_size;
 break;
 }

 case FOURCC('s', 't', 's', 'z'):
 case FOURCC('s', 't', 'z', '2'):
 {
 status_t err =
                mLastTrack->sampleTable->setSampleSizeParams(
                        chunk_type, data_offset, chunk_data_size);

 if (err != OK) {
 return err;
 }

 size_t max_size;
            err = mLastTrack->sampleTable->getMaxSampleSize(&max_size);

 if (err != OK) {
 return err;
 }

 if (max_size != 0) {
 if (max_size > SIZE_MAX - 10 * 2) {
                    ALOGE("max sample size too big: %zu", max_size);
 return ERROR_MALFORMED;
 }
                mLastTrack->meta->setInt32(kKeyMaxInputSize, max_size + 10 * 2);
 } else {
 uint32_t width, height;
 if (!mLastTrack->meta->findInt32(kKeyWidth, (int32_t*)&width) ||
 !mLastTrack->meta->findInt32(kKeyHeight,(int32_t*) &height)) {
                    ALOGE("No width or height, assuming worst case 1080p");
                    width = 1920;
                    height = 1080;
 } else {
 if (width > 32768 || height > 32768) {
                        ALOGE("can't support %u x %u video", width, height);
 return ERROR_MALFORMED;
 }
 }

 const char *mime;
                CHECK(mLastTrack->meta->findCString(kKeyMIMEType, &mime));
 if (!strcmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
                    max_size = ((width + 15) / 16) * ((height + 15) / 16) * 192;
 } else {
                    max_size = width * height * 3 / 2;
 }
                mLastTrack->meta->setInt32(kKeyMaxInputSize, max_size);
 }
 *offset += chunk_size;

 const char *mime;
            CHECK(mLastTrack->meta->findCString(kKeyMIMEType, &mime));
 if (!strncasecmp("video/", mime, 6)) {
 size_t nSamples = mLastTrack->sampleTable->countSamples();
 int64_t durationUs;
 if (mLastTrack->meta->findInt64(kKeyDuration, &durationUs)) {
 if (durationUs > 0) {
 int32_t frameRate = (nSamples * 1000000LL +
 (durationUs >> 1)) / durationUs;
                        mLastTrack->meta->setInt32(kKeyFrameRate, frameRate);
 }
 }
 }

 break;
 }

 case FOURCC('s', 't', 't', 's'):
 {
 status_t err =
                mLastTrack->sampleTable->setTimeToSampleParams(
                        data_offset, chunk_data_size);

 if (err != OK) {
 return err;
 }

 *offset += chunk_size;
 break;
 }

 case FOURCC('c', 't', 't', 's'):
 {
 status_t err =
                mLastTrack->sampleTable->setCompositionTimeToSampleParams(
                        data_offset, chunk_data_size);

 if (err != OK) {
 return err;
 }

 *offset += chunk_size;
 break;
 }

 case FOURCC('s', 't', 's', 's'):
 {
 status_t err =
                mLastTrack->sampleTable->setSyncSampleParams(
                        data_offset, chunk_data_size);

 if (err != OK) {
 return err;
 }

 *offset += chunk_size;
 break;
 }

 case FOURCC('\xA9', 'x', 'y', 'z'):
 {
 if (chunk_data_size < 8) {
 return ERROR_MALFORMED;
 }

 char buffer[18];

 off64_t location_length = chunk_data_size - 5;
 if (location_length >= (off64_t) sizeof(buffer)) {
 return ERROR_MALFORMED;
 }

 if (mDataSource->readAt(
                        data_offset + 4, buffer, location_length) < location_length) {
 return ERROR_IO;
 }

            buffer[location_length] = '\0';
            mFileMetaData->setCString(kKeyLocation, buffer);
 *offset += chunk_size;
 break;
 }

 case FOURCC('e', 's', 'd', 's'):
 {
 if (chunk_data_size < 4) {
 return ERROR_MALFORMED;
 }

 uint8_t buffer[256];
 if (chunk_data_size > (off64_t)sizeof(buffer)) {
 return ERROR_BUFFER_TOO_SMALL;
 }

 if (mDataSource->readAt(
                        data_offset, buffer, chunk_data_size) < chunk_data_size) {
 return ERROR_IO;
 }

 if (U32_AT(buffer) != 0) {
 return ERROR_MALFORMED;
 }

            mLastTrack->meta->setData(
                    kKeyESDS, kTypeESDS, &buffer[4], chunk_data_size - 4);

 if (mPath.size() >= 2
 && mPath[mPath.size() - 2] == FOURCC('m', 'p', '4', 'a')) {

 status_t err = updateAudioTrackInfoFromESDS_MPEG4Audio(
 &buffer[4], chunk_data_size - 4);

 if (err != OK) {
 return err;
 }
 }

 *offset += chunk_size;
 break;
 }

 case FOURCC('a', 'v', 'c', 'C'):
 {
            sp<ABuffer> buffer = new ABuffer(chunk_data_size);

 if (mDataSource->readAt(
                        data_offset, buffer->data(), chunk_data_size) < chunk_data_size) {
 return ERROR_IO;
 }

            mLastTrack->meta->setData(
                    kKeyAVCC, kTypeAVCC, buffer->data(), chunk_data_size);

 *offset += chunk_size;
 break;
 }

 case FOURCC('d', '2', '6', '3'):
 {
 /*
             * d263 contains a fixed 7 bytes part:
             *   vendor - 4 bytes
             *   version - 1 byte
             *   level - 1 byte
             *   profile - 1 byte
             * optionally, "d263" box itself may contain a 16-byte
             * bit rate box (bitr)
             *   average bit rate - 4 bytes
             *   max bit rate - 4 bytes
             */
 char buffer[23];
 if (chunk_data_size != 7 &&
                chunk_data_size != 23) {
                ALOGE("Incorrect D263 box size %lld", chunk_data_size);
 return ERROR_MALFORMED;
 }

 if (mDataSource->readAt(
                    data_offset, buffer, chunk_data_size) < chunk_data_size) {
 return ERROR_IO;
 }

            mLastTrack->meta->setData(kKeyD263, kTypeD263, buffer, chunk_data_size);

 *offset += chunk_size;
 break;
 }

 case FOURCC('m', 'e', 't', 'a'):
 {
 uint8_t buffer[4];
 if (chunk_data_size < (off64_t)sizeof(buffer)) {
 return ERROR_MALFORMED;
 }

 if (mDataSource->readAt(
                        data_offset, buffer, 4) < 4) {
 return ERROR_IO;
 }

 if (U32_AT(buffer) != 0) {

 *offset += chunk_size;
 return OK;
 }

 off64_t stop_offset = *offset + chunk_size;
 *offset = data_offset + sizeof(buffer);
 while (*offset < stop_offset) {
 status_t err = parseChunk(offset, depth + 1);
 if (err != OK) {
 return err;
 }
 }

 if (*offset != stop_offset) {
 return ERROR_MALFORMED;
 }
 break;
 }

 case FOURCC('m', 'e', 'a', 'n'):
 case FOURCC('n', 'a', 'm', 'e'):
 case FOURCC('d', 'a', 't', 'a'):
 {
 if (mPath.size() == 6 && underMetaDataPath(mPath)) {
 status_t err = parseMetaData(data_offset, chunk_data_size);

 if (err != OK) {
 return err;
 }
 }

 *offset += chunk_size;
 break;
 }

 case FOURCC('m', 'v', 'h', 'd'):
 {
 if (chunk_data_size < 24) {
 return ERROR_MALFORMED;
 }

 uint8_t header[24];
 if (mDataSource->readAt(
                        data_offset, header, sizeof(header))
 < (ssize_t)sizeof(header)) {
 return ERROR_IO;
 }

 uint64_t creationTime;
 if (header[0] == 1) {
                creationTime = U64_AT(&header[4]);
                mHeaderTimescale = U32_AT(&header[20]);
 } else if (header[0] != 0) {
 return ERROR_MALFORMED;
 } else {
                creationTime = U32_AT(&header[4]);
                mHeaderTimescale = U32_AT(&header[12]);
 }

 String8 s;
            convertTimeToDate(creationTime, &s);

            mFileMetaData->setCString(kKeyDate, s.string());

 *offset += chunk_size;
 break;
 }

 case FOURCC('m', 'd', 'a', 't'):
 {
            ALOGV("mdat chunk, drm: %d", mIsDrm);
 if (!mIsDrm) {
 *offset += chunk_size;
 break;
 }

 if (chunk_size < 8) {
 return ERROR_MALFORMED;
 }

 return parseDrmSINF(offset, data_offset);
 }

 case FOURCC('h', 'd', 'l', 'r'):
 {
 uint32_t buffer;
 if (mDataSource->readAt(
                        data_offset + 8, &buffer, 4) < 4) {
 return ERROR_IO;
 }

 uint32_t type = ntohl(buffer);
 if (type == FOURCC('t', 'e', 'x', 't') || type == FOURCC('s', 'b', 't', 'l')) {
 if (mLastTrack != NULL) {
                    mLastTrack->meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_TEXT_3GPP);
 }
 }

 *offset += chunk_size;
 break;
 }

 case FOURCC('t', 'x', '3', 'g'):
 {
 uint32_t type;
 const void *data;
 size_t size = 0;
 if (!mLastTrack->meta->findData(
                    kKeyTextFormatData, &type, &data, &size)) {
                size = 0;
 }

 if ((chunk_size > SIZE_MAX) || (SIZE_MAX - chunk_size <= size)) {
 return ERROR_MALFORMED;
 }
 uint8_t *buffer = new (std::nothrow) uint8_t[size + chunk_size];
 if (buffer == NULL) {
 return ERROR_MALFORMED;
 }

 if (size > 0) {
                memcpy(buffer, data, size);
 }

 if ((size_t)(mDataSource->readAt(*offset, buffer + size, chunk_size))
 < chunk_size) {
 delete[] buffer;
                buffer = NULL;

 return ERROR_IO;
 }

            mLastTrack->meta->setData(
                    kKeyTextFormatData, 0, buffer, size + chunk_size);

 delete[] buffer;

 *offset += chunk_size;
 break;
 }

 case FOURCC('c', 'o', 'v', 'r'):
 {
 if (mFileMetaData != NULL) {
                ALOGV("chunk_data_size = %lld and data_offset = %lld",
                        chunk_data_size, data_offset);

 if (chunk_data_size < 0 || static_cast<uint64_t>(chunk_data_size) >= SIZE_MAX - 1) {
 return ERROR_MALFORMED;
 }
                sp<ABuffer> buffer = new ABuffer(chunk_data_size + 1);
 if (mDataSource->readAt(
                    data_offset, buffer->data(), chunk_data_size) != (ssize_t)chunk_data_size) {
 return ERROR_IO;
 }
 const int kSkipBytesOfDataBox = 16;
 if (chunk_data_size <= kSkipBytesOfDataBox) {
 return ERROR_MALFORMED;
 }

                mFileMetaData->setData(
                    kKeyAlbumArt, MetaData::TYPE_NONE,
                    buffer->data() + kSkipBytesOfDataBox, chunk_data_size - kSkipBytesOfDataBox);
 }

 *offset += chunk_size;
 break;
 }

 case FOURCC('-', '-', '-', '-'):
 {
            mLastCommentMean.clear();
            mLastCommentName.clear();
            mLastCommentData.clear();
 *offset += chunk_size;
 break;
 }

 case FOURCC('s', 'i', 'd', 'x'):
 {
            parseSegmentIndex(data_offset, chunk_data_size);
 *offset += chunk_size;
 return UNKNOWN_ERROR; // stop parsing after sidx
 }

 default:
 {
 *offset += chunk_size;
 break;
 }
 }

 return OK;
}

Project: Android

status_t MPEG4Source::parseChunk(off64_t *offset) {
 uint32_t hdr[2];
 if (mDataSource->readAt(*offset, hdr, 8) < 8) {
 return ERROR_IO;
 }
 uint64_t chunk_size = ntohl(hdr[0]);
 uint32_t chunk_type = ntohl(hdr[1]);
 off64_t data_offset = *offset + 8;

 if (chunk_size == 1) {
 if (mDataSource->readAt(*offset + 8, &chunk_size, 8) < 8) {
 return ERROR_IO;
 }
        chunk_size = ntoh64(chunk_size);
        data_offset += 8;

 if (chunk_size < 16) {
 return ERROR_MALFORMED;
 }
 } else if (chunk_size < 8) {
 return ERROR_MALFORMED;
 }

 char chunk[5];
 MakeFourCCString(chunk_type, chunk);
    ALOGV("MPEG4Source chunk %s @ %llx", chunk, *offset);

 off64_t chunk_data_size = *offset + chunk_size - data_offset;

 switch(chunk_type) {

 case FOURCC('t', 'r', 'a', 'f'):
 case FOURCC('m', 'o', 'o', 'f'): {
 off64_t stop_offset = *offset + chunk_size;
 *offset = data_offset;
 while (*offset < stop_offset) {
 status_t err = parseChunk(offset);
 if (err != OK) {
 return err;
 }
 }
 if (chunk_type == FOURCC('m', 'o', 'o', 'f')) {
                parseChunk(offset); // doesn't actually parse it, just updates offset
                mNextMoofOffset = *offset;
 }
 break;
 }

 case FOURCC('t', 'f', 'h', 'd'): {
 status_t err;
 if ((err = parseTrackFragmentHeader(data_offset, chunk_data_size)) != OK) {
 return err;
 }
 *offset += chunk_size;
 break;
 }

 case FOURCC('t', 'r', 'u', 'n'): {
 status_t err;
 if (mLastParsedTrackId == mTrackId) {
 if ((err = parseTrackFragmentRun(data_offset, chunk_data_size)) != OK) {
 return err;
 }
 }

 *offset += chunk_size;
 break;
 }

 case FOURCC('s', 'a', 'i', 'z'): {
 status_t err;
 if ((err = parseSampleAuxiliaryInformationSizes(data_offset, chunk_data_size)) != OK) {
 return err;
 }
 *offset += chunk_size;
 break;
 }
 case FOURCC('s', 'a', 'i', 'o'): {
 status_t err;
 if ((err = parseSampleAuxiliaryInformationOffsets(data_offset, chunk_data_size)) != OK) {
 return err;
 }
 *offset += chunk_size;
 break;
 }

 case FOURCC('m', 'd', 'a', 't'): {
            ALOGV("MPEG4Source::parseChunk mdat");
 *offset += chunk_size;
 break;
 }

 default: {
 *offset += chunk_size;
 break;
 }
 }
 return OK;
}

Project: Android

status_t MPEG4Extractor::parseDrmSINF(off64_t *offset, off64_t data_offset) {
 uint8_t updateIdTag;
 if (mDataSource->readAt(data_offset, &updateIdTag, 1) < 1) {
 return ERROR_IO;
 }
    data_offset ++;

 if (0x01/*OBJECT_DESCRIPTOR_UPDATE_ID_TAG*/ != updateIdTag) {
 return ERROR_MALFORMED;
 }

 uint8_t numOfBytes;
 int32_t size = readSize(data_offset, mDataSource, &numOfBytes);
 if (size < 0) {
 return ERROR_IO;
 }
 int32_t classSize = size;
    data_offset += numOfBytes;

 while(size >= 11 ) {
 uint8_t descriptorTag;
 if (mDataSource->readAt(data_offset, &descriptorTag, 1) < 1) {
 return ERROR_IO;
 }
        data_offset ++;

 if (0x11/*OBJECT_DESCRIPTOR_ID_TAG*/ != descriptorTag) {
 return ERROR_MALFORMED;
 }

 uint8_t buffer[8];
 if (mDataSource->readAt(data_offset, buffer, 2) < 2) {
 return ERROR_IO;
 }
        data_offset += 2;

 if ((buffer[1] >> 5) & 0x0001) { //url flag is set
 return ERROR_MALFORMED;
 }

 if (mDataSource->readAt(data_offset, buffer, 8) < 8) {
 return ERROR_IO;
 }
        data_offset += 8;

 if ((0x0F/*ES_ID_REF_TAG*/ != buffer[1])
 || ( 0x0A/*IPMP_DESCRIPTOR_POINTER_ID_TAG*/ != buffer[5])) {
 return ERROR_MALFORMED;
 }

        SINF *sinf = new SINF;
        sinf->trackID = U16_AT(&buffer[3]);
        sinf->IPMPDescriptorID = buffer[7];
        sinf->next = mFirstSINF;
        mFirstSINF = sinf;

        size -= (8 + 2 + 1);
 }

 if (size != 0) {
 return ERROR_MALFORMED;
 }

 if (mDataSource->readAt(data_offset, &updateIdTag, 1) < 1) {
 return ERROR_IO;
 }
    data_offset ++;

 if(0x05/*IPMP_DESCRIPTOR_UPDATE_ID_TAG*/ != updateIdTag) {
 return ERROR_MALFORMED;
 }

    size = readSize(data_offset, mDataSource, &numOfBytes);
 if (size < 0) {
 return ERROR_IO;
 }
    classSize = size;
    data_offset += numOfBytes;

 while (size > 0) {
 uint8_t tag;
 int32_t dataLen;
 if (mDataSource->readAt(data_offset, &tag, 1) < 1) {
 return ERROR_IO;
 }
        data_offset ++;

 if (0x0B/*IPMP_DESCRIPTOR_ID_TAG*/ == tag) {
 uint8_t id;
            dataLen = readSize(data_offset, mDataSource, &numOfBytes);
 if (dataLen < 0) {
 return ERROR_IO;
 } else if (dataLen < 4) {
 return ERROR_MALFORMED;
 }
            data_offset += numOfBytes;

 if (mDataSource->readAt(data_offset, &id, 1) < 1) {
 return ERROR_IO;
 }
            data_offset ++;

            SINF *sinf = mFirstSINF;
 while (sinf && (sinf->IPMPDescriptorID != id)) {
                sinf = sinf->next;
 }
 if (sinf == NULL) {
 return ERROR_MALFORMED;
 }
            sinf->len = dataLen - 3;
            sinf->IPMPData = new (std::nothrow) char[sinf->len];
 if (sinf->IPMPData == NULL) {
 return ERROR_MALFORMED;
 }
            data_offset += 2;

 if (mDataSource->readAt(data_offset + 2, sinf->IPMPData, sinf->len) < sinf->len) {
 return ERROR_IO;
 }
            data_offset += sinf->len;

            size -= (dataLen + numOfBytes + 1);
 }
 }

 if (size != 0) {
 return ERROR_MALFORMED;
 }

 return UNKNOWN_ERROR; // Return a dummy error.
}

Project: Android

status_t MPEG4Extractor::parseMetaData(off64_t offset, size_t size) {
 if (size < 4 || size == SIZE_MAX) {
 return ERROR_MALFORMED;
 }

 uint8_t *buffer = new (std::nothrow) uint8_t[size + 1];
 if (buffer == NULL) {
 return ERROR_MALFORMED;
 }
 if (mDataSource->readAt(
                offset, buffer, size) != (ssize_t)size) {
 delete[] buffer;
        buffer = NULL;

 return ERROR_IO;
 }

 uint32_t flags = U32_AT(buffer);

 uint32_t metadataKey = 0;
 char chunk[5];
 MakeFourCCString(mPath[4], chunk);
    ALOGV("meta: %s @ %lld", chunk, offset);
 switch (mPath[4]) {
 case FOURCC(0xa9, 'a', 'l', 'b'):
 {
            metadataKey = kKeyAlbum;
 break;
 }
 case FOURCC(0xa9, 'A', 'R', 'T'):
 {
            metadataKey = kKeyArtist;
 break;
 }
 case FOURCC('a', 'A', 'R', 'T'):
 {
            metadataKey = kKeyAlbumArtist;
 break;
 }
 case FOURCC(0xa9, 'd', 'a', 'y'):
 {
            metadataKey = kKeyYear;
 break;
 }
 case FOURCC(0xa9, 'n', 'a', 'm'):
 {
            metadataKey = kKeyTitle;
 break;
 }
 case FOURCC(0xa9, 'w', 'r', 't'):
 {
            metadataKey = kKeyWriter;
 break;
 }
 case FOURCC('c', 'o', 'v', 'r'):
 {
            metadataKey = kKeyAlbumArt;
 break;
 }
 case FOURCC('g', 'n', 'r', 'e'):
 {
            metadataKey = kKeyGenre;
 break;
 }
 case FOURCC(0xa9, 'g', 'e', 'n'):
 {
            metadataKey = kKeyGenre;
 break;
 }
 case FOURCC('c', 'p', 'i', 'l'):
 {
 if (size == 9 && flags == 21) {
 char tmp[16];
                sprintf(tmp, "%d",
 (int)buffer[size - 1]);

                mFileMetaData->setCString(kKeyCompilation, tmp);
 }
 break;
 }
 case FOURCC('t', 'r', 'k', 'n'):
 {
 if (size == 16 && flags == 0) {
 char tmp[16];
 uint16_t* pTrack = (uint16_t*)&buffer[10];
 uint16_t* pTotalTracks = (uint16_t*)&buffer[12];
                sprintf(tmp, "%d/%d", ntohs(*pTrack), ntohs(*pTotalTracks));

                mFileMetaData->setCString(kKeyCDTrackNumber, tmp);
 }
 break;
 }
 case FOURCC('d', 'i', 's', 'k'):
 {
 if ((size == 14 || size == 16) && flags == 0) {
 char tmp[16];
 uint16_t* pDisc = (uint16_t*)&buffer[10];
 uint16_t* pTotalDiscs = (uint16_t*)&buffer[12];
                sprintf(tmp, "%d/%d", ntohs(*pDisc), ntohs(*pTotalDiscs));

                mFileMetaData->setCString(kKeyDiscNumber, tmp);
 }
 break;
 }
 case FOURCC('-', '-', '-', '-'):
 {
            buffer[size] = '\0';
 switch (mPath[5]) {
 case FOURCC('m', 'e', 'a', 'n'):
                    mLastCommentMean.setTo((const char *)buffer + 4);
 break;
 case FOURCC('n', 'a', 'm', 'e'):
                    mLastCommentName.setTo((const char *)buffer + 4);
 break;
 case FOURCC('d', 'a', 't', 'a'):
 if (size < 8) {
 delete[] buffer;
                        buffer = NULL;
                        ALOGE("b/24346430");
 return ERROR_MALFORMED;
 }
                    mLastCommentData.setTo((const char *)buffer + 8);
 break;
 }

 if ((mLastCommentMean.length() != 0) &&
 (mLastCommentName.length() != 0) &&
 (mLastCommentData.length() != 0)) {

 if (mLastCommentMean == "com.apple.iTunes"
 && mLastCommentName == "iTunSMPB") {
 int32_t delay, padding;
 if (sscanf(mLastCommentData,
 " %*x %x %x %*x", &delay, &padding) == 2) {
                        mLastTrack->meta->setInt32(kKeyEncoderDelay, delay);
                        mLastTrack->meta->setInt32(kKeyEncoderPadding, padding);
 }
 }

                mLastCommentMean.clear();
                mLastCommentName.clear();
                mLastCommentData.clear();
 }
 break;
 }

 default:
 break;
 }

 if (size >= 8 && metadataKey) {
 if (metadataKey == kKeyAlbumArt) {
            mFileMetaData->setData(
                    kKeyAlbumArt, MetaData::TYPE_NONE,
                    buffer + 8, size - 8);
 } else if (metadataKey == kKeyGenre) {
 if (flags == 0) {
 int genrecode = (int)buffer[size - 1];
                genrecode--;
 if (genrecode < 0) {
                    genrecode = 255; // reserved for 'unknown genre'
 }
 char genre[10];
                sprintf(genre, "%d", genrecode);

                mFileMetaData->setCString(metadataKey, genre);
 } else if (flags == 1) {
                buffer[size] = '\0';

                mFileMetaData->setCString(
                        metadataKey, (const char *)buffer + 8);
 }
 } else {
            buffer[size] = '\0';

            mFileMetaData->setCString(
                    metadataKey, (const char *)buffer + 8);
 }
 }

 delete[] buffer;
    buffer = NULL;

 return OK;
}

Project: Android

status_t MPEG4Source::parseSampleAuxiliaryInformationOffsets(off64_t offset, off64_t size) {
    ALOGV("parseSampleAuxiliaryInformationOffsets");
 uint8_t version;
 if (mDataSource->readAt(offset, &version, sizeof(version)) != 1) {
 return ERROR_IO;
 }
    offset++;

 uint32_t flags;
 if (!mDataSource->getUInt24(offset, &flags)) {
 return ERROR_IO;
 }
    offset += 3;

 uint32_t entrycount;
 if (!mDataSource->getUInt32(offset, &entrycount)) {
 return ERROR_IO;
 }
    offset += 4;
 if (entrycount == 0) {
 return OK;
 }
 if (entrycount > UINT32_MAX / 8) {
 return ERROR_MALFORMED;
 }

 if (entrycount > mCurrentSampleInfoOffsetsAllocSize) {
 uint64_t *newPtr = (uint64_t *)realloc(mCurrentSampleInfoOffsets, entrycount * 8);
 if (newPtr == NULL) {
 return NO_MEMORY;
 }
        mCurrentSampleInfoOffsets = newPtr;
        mCurrentSampleInfoOffsetsAllocSize = entrycount;
 }
    mCurrentSampleInfoOffsetCount = entrycount;

 if (mCurrentSampleInfoOffsets == NULL) {
 return OK;
 }

 for (size_t i = 0; i < entrycount; i++) {
 if (version == 0) {
 uint32_t tmp;
 if (!mDataSource->getUInt32(offset, &tmp)) {
 return ERROR_IO;
 }
            mCurrentSampleInfoOffsets[i] = tmp;
            offset += 4;
 } else {
 uint64_t tmp;
 if (!mDataSource->getUInt64(offset, &tmp)) {
 return ERROR_IO;
 }
            mCurrentSampleInfoOffsets[i] = tmp;
            offset += 8;
 }
 }


 off64_t drmoffset = mCurrentSampleInfoOffsets[0]; // from moof

    drmoffset += mCurrentMoofOffset;
 int ivlength;
    CHECK(mFormat->findInt32(kKeyCryptoDefaultIVSize, &ivlength));

 if (ivlength != 0 && ivlength != 8 && ivlength != 16) {
        ALOGW("unsupported IV length: %d", ivlength);
 return ERROR_MALFORMED;
 }
 for (size_t i = 0; i < mCurrentSampleInfoCount; i++) {
 if (i >= mCurrentSamples.size()) {
            ALOGW("too few samples");
 break;
 }
 Sample *smpl = &mCurrentSamples.editItemAt(i);

        memset(smpl->iv, 0, 16);
 if (mDataSource->readAt(drmoffset, smpl->iv, ivlength) != ivlength) {
 return ERROR_IO;
 }

        drmoffset += ivlength;

 int32_t smplinfosize = mCurrentDefaultSampleInfoSize;
 if (smplinfosize == 0) {
            smplinfosize = mCurrentSampleInfoSizes[i];
 }
 if (smplinfosize > ivlength) {
 uint16_t numsubsamples;
 if (!mDataSource->getUInt16(drmoffset, &numsubsamples)) {
 return ERROR_IO;
 }
            drmoffset += 2;
 for (size_t j = 0; j < numsubsamples; j++) {
 uint16_t numclear;
 uint32_t numencrypted;
 if (!mDataSource->getUInt16(drmoffset, &numclear)) {
 return ERROR_IO;
 }
                drmoffset += 2;
 if (!mDataSource->getUInt32(drmoffset, &numencrypted)) {
 return ERROR_IO;
 }
                drmoffset += 4;
                smpl->clearsizes.add(numclear);
                smpl->encryptedsizes.add(numencrypted);
 }
 } else {
            smpl->clearsizes.add(0);
            smpl->encryptedsizes.add(smpl->size);
 }
 }


 return OK;
}

Project: Android

status_t MPEG4Source::parseSampleAuxiliaryInformationSizes(off64_t offset, off64_t size) {
    ALOGV("parseSampleAuxiliaryInformationSizes");
 uint8_t version;
 if (mDataSource->readAt(
            offset, &version, sizeof(version))
 < (ssize_t)sizeof(version)) {
 return ERROR_IO;
 }

 if (version != 0) {
 return ERROR_UNSUPPORTED;
 }
    offset++;

 uint32_t flags;
 if (!mDataSource->getUInt24(offset, &flags)) {
 return ERROR_IO;
 }
    offset += 3;

 if (flags & 1) {
 uint32_t tmp;
 if (!mDataSource->getUInt32(offset, &tmp)) {
 return ERROR_MALFORMED;
 }
        mCurrentAuxInfoType = tmp;
        offset += 4;
 if (!mDataSource->getUInt32(offset, &tmp)) {
 return ERROR_MALFORMED;
 }
        mCurrentAuxInfoTypeParameter = tmp;
        offset += 4;
 }

 uint8_t defsize;
 if (mDataSource->readAt(offset, &defsize, 1) != 1) {
 return ERROR_MALFORMED;
 }
    mCurrentDefaultSampleInfoSize = defsize;
    offset++;

 uint32_t smplcnt;
 if (!mDataSource->getUInt32(offset, &smplcnt)) {
 return ERROR_MALFORMED;
 }
    mCurrentSampleInfoCount = smplcnt;
    offset += 4;

 if (mCurrentDefaultSampleInfoSize != 0) {
        ALOGV("@@@@ using default sample info size of %d", mCurrentDefaultSampleInfoSize);
 return OK;
 }
 if (smplcnt > mCurrentSampleInfoAllocSize) {
        mCurrentSampleInfoSizes = (uint8_t*) realloc(mCurrentSampleInfoSizes, smplcnt);
        mCurrentSampleInfoAllocSize = smplcnt;
 }

    mDataSource->readAt(offset, mCurrentSampleInfoSizes, smplcnt);
 return OK;
}

Project: Android

status_t MPEG4Source::parseTrackFragmentRun(off64_t offset, off64_t size) {

    ALOGV("MPEG4Extractor::parseTrackFragmentRun");
 if (size < 8) {
 return -EINVAL;
 }

 enum {
        kDataOffsetPresent                  = 0x01,
        kFirstSampleFlagsPresent            = 0x04,
        kSampleDurationPresent              = 0x100,
        kSampleSizePresent                  = 0x200,
        kSampleFlagsPresent                 = 0x400,
        kSampleCompositionTimeOffsetPresent = 0x800,
 };

 uint32_t flags;
 if (!mDataSource->getUInt32(offset, &flags)) {
 return ERROR_MALFORMED;
 }
    ALOGV("fragment run flags: %08x", flags);

 if (flags & 0xff000000) {
 return -EINVAL;
 }

 if ((flags & kFirstSampleFlagsPresent) && (flags & kSampleFlagsPresent)) {
 return -EINVAL;
 }

 uint32_t sampleCount;
 if (!mDataSource->getUInt32(offset + 4, &sampleCount)) {
 return ERROR_MALFORMED;
 }
    offset += 8;
    size -= 8;

 uint64_t dataOffset = mTrackFragmentHeaderInfo.mDataOffset;

 uint32_t firstSampleFlags = 0;

 if (flags & kDataOffsetPresent) {
 if (size < 4) {
 return -EINVAL;
 }

 int32_t dataOffsetDelta;
 if (!mDataSource->getUInt32(offset, (uint32_t*)&dataOffsetDelta)) {
 return ERROR_MALFORMED;
 }

        dataOffset = mTrackFragmentHeaderInfo.mBaseDataOffset + dataOffsetDelta;

        offset += 4;
        size -= 4;
 }

 if (flags & kFirstSampleFlagsPresent) {
 if (size < 4) {
 return -EINVAL;
 }

 if (!mDataSource->getUInt32(offset, &firstSampleFlags)) {
 return ERROR_MALFORMED;
 }
        offset += 4;
        size -= 4;
 }

 uint32_t sampleDuration = 0, sampleSize = 0, sampleFlags = 0,
             sampleCtsOffset = 0;

 size_t bytesPerSample = 0;
 if (flags & kSampleDurationPresent) {
        bytesPerSample += 4;
 } else if (mTrackFragmentHeaderInfo.mFlags
 & TrackFragmentHeaderInfo::kDefaultSampleDurationPresent) {
        sampleDuration = mTrackFragmentHeaderInfo.mDefaultSampleDuration;
 } else {
        sampleDuration = mTrackFragmentHeaderInfo.mDefaultSampleDuration;
 }

 if (flags & kSampleSizePresent) {
        bytesPerSample += 4;
 } else if (mTrackFragmentHeaderInfo.mFlags
 & TrackFragmentHeaderInfo::kDefaultSampleSizePresent) {
        sampleSize = mTrackFragmentHeaderInfo.mDefaultSampleSize;
 } else {
        sampleSize = mTrackFragmentHeaderInfo.mDefaultSampleSize;
 }

 if (flags & kSampleFlagsPresent) {
        bytesPerSample += 4;
 } else if (mTrackFragmentHeaderInfo.mFlags
 & TrackFragmentHeaderInfo::kDefaultSampleFlagsPresent) {
        sampleFlags = mTrackFragmentHeaderInfo.mDefaultSampleFlags;
 } else {
        sampleFlags = mTrackFragmentHeaderInfo.mDefaultSampleFlags;
 }

 if (flags & kSampleCompositionTimeOffsetPresent) {
        bytesPerSample += 4;
 } else {
        sampleCtsOffset = 0;
 }

 if (size < sampleCount * bytesPerSample) {
 return -EINVAL;
 }

 Sample tmp;
 for (uint32_t i = 0; i < sampleCount; ++i) {
 if (flags & kSampleDurationPresent) {
 if (!mDataSource->getUInt32(offset, &sampleDuration)) {
 return ERROR_MALFORMED;
 }
            offset += 4;
 }

 if (flags & kSampleSizePresent) {
 if (!mDataSource->getUInt32(offset, &sampleSize)) {
 return ERROR_MALFORMED;
 }
            offset += 4;
 }

 if (flags & kSampleFlagsPresent) {
 if (!mDataSource->getUInt32(offset, &sampleFlags)) {
 return ERROR_MALFORMED;
 }
            offset += 4;
 }

 if (flags & kSampleCompositionTimeOffsetPresent) {
 if (!mDataSource->getUInt32(offset, &sampleCtsOffset)) {
 return ERROR_MALFORMED;
 }
            offset += 4;
 }

        ALOGV("adding sample %d at offset 0x%08llx, size %u, duration %u, "
 " flags 0x%08x", i + 1,
                dataOffset, sampleSize, sampleDuration,
 (flags & kFirstSampleFlagsPresent) && i == 0
 ? firstSampleFlags : sampleFlags);
        tmp.offset = dataOffset;
        tmp.size = sampleSize;
        tmp.duration = sampleDuration;
        mCurrentSamples.add(tmp);

        dataOffset += sampleSize;
 }

    mTrackFragmentHeaderInfo.mDataOffset = dataOffset;

 return OK;
}

Project: Android

status_t MPEG4Source::read(
 MediaBuffer **out, const ReadOptions *options) {
 Mutex::Autolock autoLock(mLock);

    CHECK(mStarted);

 if (mFirstMoofOffset > 0) {
 return fragmentedRead(out, options);
 }

 *out = NULL;

 int64_t targetSampleTimeUs = -1;

 int64_t seekTimeUs;
 ReadOptions::SeekMode mode;
 if (options && options->getSeekTo(&seekTimeUs, &mode)) {
 uint32_t findFlags = 0;
 switch (mode) {
 case ReadOptions::SEEK_PREVIOUS_SYNC:
                findFlags = SampleTable::kFlagBefore;
 break;
 case ReadOptions::SEEK_NEXT_SYNC:
                findFlags = SampleTable::kFlagAfter;
 break;
 case ReadOptions::SEEK_CLOSEST_SYNC:
 case ReadOptions::SEEK_CLOSEST:
                findFlags = SampleTable::kFlagClosest;
 break;
 default:
                CHECK(!"Should not be here.");
 break;
 }

 uint32_t sampleIndex;
 status_t err = mSampleTable->findSampleAtTime(
                seekTimeUs * mTimescale / 1000000,
 &sampleIndex, findFlags);

 if (mode == ReadOptions::SEEK_CLOSEST) {
            findFlags = SampleTable::kFlagBefore;
 }

 uint32_t syncSampleIndex;
 if (err == OK) {
            err = mSampleTable->findSyncSampleNear(
                    sampleIndex, &syncSampleIndex, findFlags);
 }

 uint32_t sampleTime;
 if (err == OK) {
            err = mSampleTable->getMetaDataForSample(
                    sampleIndex, NULL, NULL, &sampleTime);
 }

 if (err != OK) {
 if (err == ERROR_OUT_OF_RANGE) {
                err = ERROR_END_OF_STREAM;
 }
            ALOGV("end of stream");
 return err;
 }

 if (mode == ReadOptions::SEEK_CLOSEST) {
            targetSampleTimeUs = (sampleTime * 1000000ll) / mTimescale;
 }

#if 0
 uint32_t syncSampleTime;
        CHECK_EQ(OK, mSampleTable->getMetaDataForSample(
                    syncSampleIndex, NULL, NULL, &syncSampleTime));

        ALOGI("seek to time %lld us => sample at time %lld us, "
 "sync sample at time %lld us",
             seekTimeUs,
             sampleTime * 1000000ll / mTimescale,
             syncSampleTime * 1000000ll / mTimescale);
#endif

        mCurrentSampleIndex = syncSampleIndex;
 if (mBuffer != NULL) {
            mBuffer->release();
            mBuffer = NULL;
 }

 }

 off64_t offset;
 size_t size;
 uint32_t cts;
 bool isSyncSample;
 bool newBuffer = false;
 if (mBuffer == NULL) {
        newBuffer = true;

 status_t err =
            mSampleTable->getMetaDataForSample(
                    mCurrentSampleIndex, &offset, &size, &cts, &isSyncSample);

 if (err != OK) {
 return err;
 }

        err = mGroup->acquire_buffer(&mBuffer);

 if (err != OK) {
            CHECK(mBuffer == NULL);
 return err;
 }
 if (size > mBuffer->size()) {
            ALOGE("buffer too small: %zu > %zu", size, mBuffer->size());
 return ERROR_BUFFER_TOO_SMALL;
 }
 }

 if (!mIsAVC || mWantsNALFragments) {
 if (newBuffer) {
 ssize_t num_bytes_read =
                mDataSource->readAt(offset, (uint8_t *)mBuffer->data(), size);

 if (num_bytes_read < (ssize_t)size) {
                mBuffer->release();
                mBuffer = NULL;

 return ERROR_IO;
 }

            CHECK(mBuffer != NULL);
            mBuffer->set_range(0, size);
            mBuffer->meta_data()->clear();
            mBuffer->meta_data()->setInt64(
                    kKeyTime, ((int64_t)cts * 1000000) / mTimescale);

 if (targetSampleTimeUs >= 0) {
                mBuffer->meta_data()->setInt64(
                        kKeyTargetTime, targetSampleTimeUs);
 }

 if (isSyncSample) {
                mBuffer->meta_data()->setInt32(kKeyIsSyncFrame, 1);
 }

 ++mCurrentSampleIndex;
 }

 if (!mIsAVC) {
 *out = mBuffer;
            mBuffer = NULL;

 return OK;
 }


        CHECK(mBuffer->range_length() >= mNALLengthSize);

 const uint8_t *src =
 (const uint8_t *)mBuffer->data() + mBuffer->range_offset();

 size_t nal_size = parseNALSize(src);
 if (mNALLengthSize > SIZE_MAX - nal_size) {
            ALOGE("b/24441553, b/24445122");
 }
 if (mBuffer->range_length() - mNALLengthSize < nal_size) {
            ALOGE("incomplete NAL unit.");

            mBuffer->release();
            mBuffer = NULL;

 return ERROR_MALFORMED;
 }

 MediaBuffer *clone = mBuffer->clone();
        CHECK(clone != NULL);
        clone->set_range(mBuffer->range_offset() + mNALLengthSize, nal_size);

        CHECK(mBuffer != NULL);
        mBuffer->set_range(
                mBuffer->range_offset() + mNALLengthSize + nal_size,
                mBuffer->range_length() - mNALLengthSize - nal_size);

 if (mBuffer->range_length() == 0) {
            mBuffer->release();
            mBuffer = NULL;
 }

 *out = clone;

 return OK;
 } else {
 ssize_t num_bytes_read = 0;
 int32_t drm = 0;
 bool usesDRM = (mFormat->findInt32(kKeyIsDRM, &drm) && drm != 0);
 if (usesDRM) {
            num_bytes_read =
                mDataSource->readAt(offset, (uint8_t*)mBuffer->data(), size);
 } else {
            num_bytes_read = mDataSource->readAt(offset, mSrcBuffer, size);
 }

 if (num_bytes_read < (ssize_t)size) {
            mBuffer->release();
            mBuffer = NULL;

 return ERROR_IO;
 }

 if (usesDRM) {
            CHECK(mBuffer != NULL);
            mBuffer->set_range(0, size);

 } else {
 uint8_t *dstData = (uint8_t *)mBuffer->data();
 size_t srcOffset = 0;
 size_t dstOffset = 0;

 while (srcOffset < size) {
 bool isMalFormed = !isInRange(0u, size, srcOffset, mNALLengthSize);
 size_t nalLength = 0;
 if (!isMalFormed) {
                    nalLength = parseNALSize(&mSrcBuffer[srcOffset]);
                    srcOffset += mNALLengthSize;
                    isMalFormed = !isInRange(0u, size, srcOffset, nalLength);
 }

 if (isMalFormed) {
                    ALOGE("Video is malformed");
                    mBuffer->release();
                    mBuffer = NULL;
 return ERROR_MALFORMED;
 }

 if (nalLength == 0) {
 continue;
 }

                CHECK(dstOffset + 4 <= mBuffer->size());

                dstData[dstOffset++] = 0;
                dstData[dstOffset++] = 0;
                dstData[dstOffset++] = 0;
                dstData[dstOffset++] = 1;
                memcpy(&dstData[dstOffset], &mSrcBuffer[srcOffset], nalLength);
                srcOffset += nalLength;
                dstOffset += nalLength;
 }
            CHECK_EQ(srcOffset, size);
            CHECK(mBuffer != NULL);
            mBuffer->set_range(0, dstOffset);
 }

        mBuffer->meta_data()->clear();
        mBuffer->meta_data()->setInt64(
                kKeyTime, ((int64_t)cts * 1000000) / mTimescale);

 if (targetSampleTimeUs >= 0) {
            mBuffer->meta_data()->setInt64(
                    kKeyTargetTime, targetSampleTimeUs);
 }

 if (isSyncSample) {
            mBuffer->meta_data()->setInt32(kKeyIsSyncFrame, 1);
 }

 ++mCurrentSampleIndex;

 *out = mBuffer;
        mBuffer = NULL;

 return OK;
 }
}

Project: Android

status_t MPEG4Extractor::readMetaData() {
 if (mInitCheck != NO_INIT) {
 return mInitCheck;
 }

 off64_t offset = 0;
 status_t err;
 while (true) {
        err = parseChunk(&offset, 0);
 if (err == OK) {
 continue;
 }

 uint32_t hdr[2];
 if (mDataSource->readAt(offset, hdr, 8) < 8) {
 break;
 }
 uint32_t chunk_type = ntohl(hdr[1]);
 if (chunk_type == FOURCC('s', 'i', 'd', 'x')) {
 continue;
 } else if (chunk_type == FOURCC('m', 'o', 'o', 'f')) {
            mMoofOffset = offset;
 }
 break;
 }

 if (mInitCheck == OK) {
 if (mHasVideo) {
            mFileMetaData->setCString(
                    kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_MPEG4);
 } else {
            mFileMetaData->setCString(kKeyMIMEType, "audio/mp4");
 }

        mInitCheck = OK;
 } else {
        mInitCheck = err;
 }

    CHECK_NE(err, (status_t)NO_INIT);

 int psshsize = 0;
 for (size_t i = 0; i < mPssh.size(); i++) {
        psshsize += 20 + mPssh[i].datalen;
 }
 if (psshsize) {
 char *buf = (char*)malloc(psshsize);
 char *ptr = buf;
 for (size_t i = 0; i < mPssh.size(); i++) {
            memcpy(ptr, mPssh[i].uuid, 20); // uuid + length
            memcpy(ptr + 20, mPssh[i].data, mPssh[i].datalen);
            ptr += (20 + mPssh[i].datalen);
 }
        mFileMetaData->setData(kKeyPssh, 'pssh', buf, psshsize);
        free(buf);
 }
 return mInitCheck;
}

Project: Android

status_t MPEG4Extractor::updateAudioTrackInfoFromESDS_MPEG4Audio(
 const void *esds_data, size_t esds_size) {
    ESDS esds(esds_data, esds_size);

 uint8_t objectTypeIndication;
 if (esds.getObjectTypeIndication(&objectTypeIndication) != OK) {
 return ERROR_MALFORMED;
 }

 if (objectTypeIndication == 0xe1) {
        mLastTrack->meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_QCELP);
 return OK;
 }

 if (objectTypeIndication  == 0x6b) {
        ALOGE("MP3 track in MP4/3GPP file is not supported");
 return ERROR_UNSUPPORTED;
 }

 const uint8_t *csd;
 size_t csd_size;
 if (esds.getCodecSpecificInfo(
 (const void **)&csd, &csd_size) != OK) {
 return ERROR_MALFORMED;
 }

#if 0
    printf("ESD of size %d\n", csd_size);
    hexdump(csd, csd_size);
#endif

 if (csd_size == 0) {

 return OK;
 }

 if (csd_size < 2) {
 return ERROR_MALFORMED;
 }

 static uint32_t kSamplingRate[] = {
 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,
 16000, 12000, 11025, 8000, 7350
 };

 ABitReader br(csd, csd_size);
 uint32_t objectType = br.getBits(5);

 if (objectType == 31) { // AAC-ELD => additional 6 bits
        objectType = 32 + br.getBits(6);
 }

    mLastTrack->meta->setInt32(kKeyAACAOT, objectType);

 uint32_t freqIndex = br.getBits(4);

 int32_t sampleRate = 0;
 int32_t numChannels = 0;
 if (freqIndex == 15) {
 if (csd_size < 5) {
 return ERROR_MALFORMED;
 }
        sampleRate = br.getBits(24);
        numChannels = br.getBits(4);
 } else {
        numChannels = br.getBits(4);

 if (freqIndex == 13 || freqIndex == 14) {
 return ERROR_MALFORMED;
 }

        sampleRate = kSamplingRate[freqIndex];
 }

 if (objectType == 5 || objectType == 29) { // SBR specific config per 14496-3 table 1.13
 uint32_t extFreqIndex = br.getBits(4);
 int32_t extSampleRate;
 if (extFreqIndex == 15) {
 if (csd_size < 8) {
 return ERROR_MALFORMED;
 }
            extSampleRate = br.getBits(24);
 } else {
 if (extFreqIndex == 13 || extFreqIndex == 14) {
 return ERROR_MALFORMED;
 }
            extSampleRate = kSamplingRate[extFreqIndex];
 }
 }

 if (numChannels == 0) {
 return ERROR_UNSUPPORTED;
 }

 int32_t prevSampleRate;
    CHECK(mLastTrack->meta->findInt32(kKeySampleRate, &prevSampleRate));

 if (prevSampleRate != sampleRate) {
        ALOGV("mpeg4 audio sample rate different from previous setting. "
 "was: %d, now: %d", prevSampleRate, sampleRate);
 }

    mLastTrack->meta->setInt32(kKeySampleRate, sampleRate);

 int32_t prevChannelCount;
    CHECK(mLastTrack->meta->findInt32(kKeyChannelCount, &prevChannelCount));

 if (prevChannelCount != numChannels) {
        ALOGV("mpeg4 audio channel count different from previous setting. "
 "was: %d, now: %d", prevChannelCount, numChannels);
 }

    mLastTrack->meta->setInt32(kKeyChannelCount, numChannels);

 return OK;
}

Project: Android

status_t MPEG4Extractor::verifyTrack(Track *track) {
 const char *mime;
    CHECK(track->meta->findCString(kKeyMIMEType, &mime));

 uint32_t type;
 const void *data;
 size_t size;
 if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
 if (!track->meta->findData(kKeyAVCC, &type, &data, &size)
 || type != kTypeAVCC) {
 return ERROR_MALFORMED;
 }
 } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4)
 || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) {
 if (!track->meta->findData(kKeyESDS, &type, &data, &size)
 || type != kTypeESDS) {
 return ERROR_MALFORMED;
 }
 }

 if (!track->sampleTable->isValid()) {
 return ERROR_MALFORMED;
 }

 return OK;
}