Commit message: None
CVE: CVE-2015-3864
CWE: CWE-189
Vulnerability description: Integer underflow in the MPEG4Extractor::parseChunk function in MPEG4Extractor.cpp in libstagefright in mediaserver in Android before 5.1.1 LMY48M allows remote attackers to execute arbitrary code via crafted MPEG-4 data, aka internal bug 23034759.  NOTE: this vulnerability exists because of an incomplete fix for CVE-2015-3824.

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 %" PRIu64 " @ %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 bool LegacySniffMPEG4(
 const sp<DataSource> &source, String8 *mimeType, float *confidence) {
 uint8_t header[8];

 ssize_t n = source->readAt(4, header, sizeof(header));
 if (n < (ssize_t)sizeof(header)) {
 return false;
 }

 if (!memcmp(header, "ftyp3gp", 7) || !memcmp(header, "ftypmp42", 8)
 || !memcmp(header, "ftyp3gr6", 8) || !memcmp(header, "ftyp3gs6", 8)
 || !memcmp(header, "ftyp3ge6", 8) || !memcmp(header, "ftyp3gg6", 8)
 || !memcmp(header, "ftypisom", 8) || !memcmp(header, "ftypM4V ", 8)
 || !memcmp(header, "ftypM4A ", 8) || !memcmp(header, "ftypf4v ", 8)
 || !memcmp(header, "ftypkddi", 8) || !memcmp(header, "ftypM4VP", 8)) {
 *mimeType = MEDIA_MIMETYPE_CONTAINER_MPEG4;
 *confidence = 0.4;

 return true;
 }

 return false;
}

Project: Android

MPEG4Extractor::MPEG4Extractor(const sp<DataSource> &source)
 : 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<MPEG4Extractor> &owner,
 const sp<MetaData> &format,
 const sp<DataSource> &dataSource,
 int32_t timeScale,
 const sp<SampleTable> &sampleTable,
 Vector<SidxEntry> &sidx,
 const Trex *trex,
 off64_t firstMoofOffset)
 : mOwner(owner),
      mFormat(format),
      mDataSource(dataSource),
      mTimescale(timeScale),
      mSampleTable(sampleTable),
      mCurrentSampleIndex(0),
      mCurrentFragmentIndex(0),
      mSegments(sidx),
      mTrex(trex),
      mFirstMoofOffset(firstMoofOffset),
      mCurrentMoofOffset(firstMoofOffset),
      mCurrentTime(0),
      mCurrentSampleInfoAllocSize(0),
      mCurrentSampleInfoSizes(NULL),
      mCurrentSampleInfoOffsetsAllocSize(0),
      mCurrentSampleInfoOffsets(NULL),
      mIsAVC(false),
      mIsHEVC(false),
      mNALLengthSize(0),
      mStarted(false),
      mGroup(NULL),
      mBuffer(NULL),
      mWantsNALFragments(false),
      mSrcBuffer(NULL) {

    memset(&mTrackFragmentHeaderInfo, 0, sizeof(mTrackFragmentHeaderInfo));

    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);
    mIsHEVC = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC);

 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);
 } else if (mIsHEVC) {
 uint32_t type;
 const void *data;
 size_t size;
        CHECK(format->findData(kKeyHVCC, &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[14 + 7] & 3);
 }

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

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

Project: Android

static void MakeFourCCString(uint32_t x, char *s) {
    s[0] = x >> 24;
    s[1] = (x >> 16) & 0xff;
    s[2] = (x >> 8) & 0xff;
    s[3] = x & 0xff;
    s[4] = '\0';
}

Project: Android

 PathAdder(Vector<uint32_t> *path, uint32_t chunkType)
 : mPath(path) {
        mPath->push(chunkType);
 }

Project: Android

bool SniffMPEG4(
 const sp<DataSource> &source, String8 *mimeType, float *confidence,
        sp<AMessage> *meta) {
 if (BetterSniffMPEG4(source, mimeType, confidence, meta)) {
 return true;
 }

 if (LegacySniffMPEG4(source, mimeType, confidence)) {
        ALOGW("Identified supported mpeg4 through LegacySniffMPEG4.");
 return true;
 }

 return false;
}

Project: Android

static void convertTimeToDate(int64_t time_1904, String8 *s) {
 time_t time_1970 = time_1904 - (((66 * 365 + 17) * 24) * 3600);

 char tmp[32];
    strftime(tmp, sizeof(tmp), "%Y%m%dT%H%M%S.000Z", gmtime(&time_1970));

    s->setTo(tmp);
}

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: %zu tracks", n);
 return n;
}

Project: Android

MPEG4Extractor::Track *MPEG4Extractor::findTrackByMimePrefix(
 const char *mimePrefix) {
 for (Track *track = mFirstTrack; track != NULL; track = track->next) {
 const char *mime;
 if (track->meta != NULL
 && track->meta->findCString(kKeyMIMEType, &mime)
 && !strncasecmp(mime, mimePrefix, strlen(mimePrefix))) {
 return track;
 }
 }

 return NULL;
}

Project: Android

uint32_t MPEG4DataSource::flags() {
 return mSource->flags();
}

Project: Android

uint32_t MPEG4Extractor::flags() const {
 return CAN_PAUSE |
 ((mMoofOffset == 0 || mSidxEntries.size() != 0) ?
 (CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_SEEK) : 0);
}

Project: Android

status_t MPEG4Source::fragmentedRead(
 MediaBuffer **out, const ReadOptions *options) {

    ALOGV("MPEG4Source::fragmentedRead");

    CHECK(mStarted);

 *out = NULL;

 int64_t targetSampleTimeUs = -1;

 int64_t seekTimeUs;
 ReadOptions::SeekMode mode;
 if (options && options->getSeekTo(&seekTimeUs, &mode)) {

 int numSidxEntries = mSegments.size();
 if (numSidxEntries != 0) {
 int64_t totalTime = 0;
 off64_t totalOffset = mFirstMoofOffset;
 for (int i = 0; i < numSidxEntries; i++) {
 const SidxEntry *se = &mSegments[i];
 if (totalTime + se->mDurationUs > seekTimeUs) {
 if ((mode == ReadOptions::SEEK_NEXT_SYNC && seekTimeUs > totalTime) ||
 (mode == ReadOptions::SEEK_CLOSEST_SYNC &&
 (seekTimeUs - totalTime) > (totalTime + se->mDurationUs - seekTimeUs))) {
                        totalTime += se->mDurationUs;
                        totalOffset += se->mSize;
 }
 break;
 }
                totalTime += se->mDurationUs;
                totalOffset += se->mSize;
 }
            mCurrentMoofOffset = totalOffset;
            mCurrentSamples.clear();
            mCurrentSampleIndex = 0;
            parseChunk(&totalOffset);
            mCurrentTime = totalTime * mTimescale / 1000000ll;
 } else {
            mCurrentMoofOffset = mFirstMoofOffset;
            mCurrentSamples.clear();
            mCurrentSampleIndex = 0;
 off64_t tmp = mCurrentMoofOffset;
            parseChunk(&tmp);
            mCurrentTime = 0;
 }

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

 }

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

 if (mCurrentSampleIndex >= mCurrentSamples.size()) {
 if (mNextMoofOffset <= mCurrentMoofOffset) {
 return ERROR_END_OF_STREAM;
 }
 off64_t nextMoof = mNextMoofOffset;
            mCurrentMoofOffset = nextMoof;
            mCurrentSamples.clear();
            mCurrentSampleIndex = 0;
            parseChunk(&nextMoof);
 if (mCurrentSampleIndex >= mCurrentSamples.size()) {
 return ERROR_END_OF_STREAM;
 }
 }

 const Sample *smpl = &mCurrentSamples[mCurrentSampleIndex];
        offset = smpl->offset;
        size = smpl->size;
        cts = mCurrentTime + smpl->compositionOffset;
        mCurrentTime += smpl->duration;
        isSyncSample = (mCurrentSampleIndex == 0); // XXX

 status_t err = mGroup->acquire_buffer(&mBuffer);

 if (err != OK) {
            CHECK(mBuffer == NULL);
            ALOGV("acquire_buffer returned %d", err);
 return err;
 }
 }

 const Sample *smpl = &mCurrentSamples[mCurrentSampleIndex];
 const sp<MetaData> bufmeta = mBuffer->meta_data();
    bufmeta->clear();
 if (smpl->encryptedsizes.size()) {
        bufmeta->setData(kKeyPlainSizes, 0,
                smpl->clearsizes.array(), smpl->clearsizes.size() * 4);
        bufmeta->setData(kKeyEncryptedSizes, 0,
                smpl->encryptedsizes.array(), smpl->encryptedsizes.size() * 4);
        bufmeta->setData(kKeyCryptoIV, 0, smpl->iv, 16); // use 16 or the actual size?
        bufmeta->setInt32(kKeyCryptoDefaultIVSize, mDefaultIVSize);
        bufmeta->setInt32(kKeyCryptoMode, mCryptoMode);
        bufmeta->setData(kKeyCryptoKey, 0, mCryptoKey, 16);
 }

 if ((!mIsAVC && !mIsHEVC)|| 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;

                ALOGV("i/o error");
 return ERROR_IO;
 }

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

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

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

 ++mCurrentSampleIndex;
 }

 if (!mIsAVC && !mIsHEVC) {
 *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 (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 {
        ALOGV("whole NAL");
 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;

            ALOGV("i/o error");
 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((size_t)0u, size, srcOffset, mNALLengthSize);
 size_t nalLength = 0;
 if (!isMalFormed) {
                    nalLength = parseNALSize(&mSrcBuffer[srcOffset]);
                    srcOffset += mNALLengthSize;
                    isMalFormed = !isInRange((size_t)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()->setInt64(
                kKeyTime, ((int64_t)cts * 1000000) / mTimescale);
        mBuffer->meta_data()->setInt64(
                kKeyDuration, ((int64_t)smpl->duration * 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

sp<MetaData> MPEG4Source::getFormat() {
 Mutex::Autolock autoLock(mLock);

 return mFormat;
}

Project: Android

status_t MPEG4DataSource::getSize(off64_t *size) {
 return mSource->getSize(size);
}

Project: Android

sp<MetaData> MPEG4Extractor::getTrackMetaData(
 size_t index, uint32_t flags) {
 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;
 }

 if ((flags & kIncludeExtensiveMetaData)
 && !track->includes_expensive_metadata) {
        track->includes_expensive_metadata = true;

 const char *mime;
        CHECK(track->meta->findCString(kKeyMIMEType, &mime));
 if (!strncasecmp("video/", mime, 6)) {
 if (mMoofOffset > 0) {
 int64_t duration;
 if (track->meta->findInt64(kKeyDuration, &duration)) {
                    track->meta->setInt64(
                            kKeyThumbnailTime, duration / 4);
 }
 } else {
 uint32_t sampleIndex;
 uint32_t sampleTime;
 if (track->sampleTable->findThumbnailSample(&sampleIndex) == OK
 && track->sampleTable->getMetaDataForSample(
                            sampleIndex, NULL /* offset */, NULL /* size */,
 &sampleTime) == OK) {
                    track->meta->setInt64(
                            kKeyThumbnailTime,
 ((int64_t)sampleTime * 1000000) / track->timescale);
 }
 }
 }
 }

 return track->meta;
}

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%04zx  ", 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

status_t MPEG4DataSource::initCheck() const {
 return mSource->initCheck();
}

Project: Android

status_t MPEG4Extractor::parse3GPPMetaData(off64_t offset, size_t size, int depth) {
 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 metadataKey = 0;
 switch (mPath[depth]) {
 case FOURCC('t', 'i', 't', 'l'):
 {
            metadataKey = kKeyTitle;
 break;
 }
 case FOURCC('p', 'e', 'r', 'f'):
 {
            metadataKey = kKeyArtist;
 break;
 }
 case FOURCC('a', 'u', 't', 'h'):
 {
            metadataKey = kKeyWriter;
 break;
 }
 case FOURCC('g', 'n', 'r', 'e'):
 {
            metadataKey = kKeyGenre;
 break;
 }
 case FOURCC('a', 'l', 'b', 'm'):
 {
 if (buffer[size - 1] != '\0') {
 char tmp[4];
              sprintf(tmp, "%u", buffer[size - 1]);

              mFileMetaData->setCString(kKeyCDTrackNumber, tmp);
 }

            metadataKey = kKeyAlbum;
 break;
 }
 case FOURCC('y', 'r', 'r', 'c'):
 {
 char tmp[5];
 uint16_t year = U16_AT(&buffer[4]);

 if (year < 10000) {
                sprintf(tmp, "%u", year);

                mFileMetaData->setCString(kKeyYear, tmp);
 }
 break;
 }

 default:
 break;
 }

 if (metadataKey > 0) {
 bool isUTF8 = true; // Common case
 char16_t *framedata = NULL;
 int len16 = 0; // Number of UTF-16 characters

 if (size < 6) {
 return ERROR_MALFORMED;
 }

 if (size - 6 >= 4) {
            len16 = ((size - 6) / 2) - 1; // don't include 0x0000 terminator
            framedata = (char16_t *)(buffer + 6);
 if (0xfffe == *framedata) {
 for (int i = 0; i < len16; i++) {
                    framedata[i] = bswap_16(framedata[i]);
 }
 }

 if (0xfeff == *framedata) {
                framedata++;
                len16--;
                isUTF8 = false;
 }
 }

 if (isUTF8) {
            buffer[size] = 0;
            mFileMetaData->setCString(metadataKey, (const char *)buffer + 6);
 } else {
 String8 tmpUTF8str(framedata, len16);
            mFileMetaData->setCString(metadataKey, tmpUTF8str.string());
 }
 }

 delete[] buffer;
    buffer = NULL;

 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')) {

 while (true) {
 if (mDataSource->readAt(*offset, hdr, 8) < 8) {
 return ERROR_END_OF_STREAM;
 }
                    chunk_size = ntohl(hdr[0]);
                    chunk_type = ntohl(hdr[1]);
 if (chunk_type == FOURCC('m', 'o', 'o', 'f')) {
                        mNextMoofOffset = *offset;
 break;
 }
 *offset += chunk_size;
 }
 }
 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, 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::parseITunesMetaData(off64_t offset, size_t size) {
 if (size < 4) {
 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'):
                    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 && !mFileMetaData->hasData(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

size_t MPEG4Source::parseNALSize(const uint8_t *data) const {
 switch (mNALLengthSize) {
 case 1:
 return *data;
 case 2:
 return U16_AT(data);
 case 3:
 return ((size_t)data[0] << 16) | U16_AT(&data[1]);
 case 4:
 return U32_AT(data);
 }

    CHECK(!"Should not be here.");

 return 0;
}

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 > mCurrentSampleInfoOffsetsAllocSize) {
        mCurrentSampleInfoOffsets = (uint64_t*) realloc(mCurrentSampleInfoOffsets, entrycount * 8);
        mCurrentSampleInfoOffsetsAllocSize = entrycount;
 }
    mCurrentSampleInfoOffsetCount = entrycount;

 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));

 for (size_t i = 0; i < mCurrentSampleInfoCount; i++) {
 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 MPEG4Extractor::parseSegmentIndex(off64_t offset, size_t size) {
  ALOGV("MPEG4Extractor::parseSegmentIndex");

 if (size < 12) {
 return -EINVAL;
 }

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

 uint32_t version = flags >> 24;
    flags &= 0xffffff;

    ALOGV("sidx version %d", version);

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

 uint32_t timeScale;
 if (!mDataSource->getUInt32(offset + 8, &timeScale)) {
 return ERROR_MALFORMED;
 }
    ALOGV("sidx refid/timescale: %d/%d", referenceId, timeScale);

 uint64_t earliestPresentationTime;
 uint64_t firstOffset;

    offset += 12;
    size -= 12;

 if (version == 0) {
 if (size < 8) {
 return -EINVAL;
 }
 uint32_t tmp;
 if (!mDataSource->getUInt32(offset, &tmp)) {
 return ERROR_MALFORMED;
 }
        earliestPresentationTime = tmp;
 if (!mDataSource->getUInt32(offset + 4, &tmp)) {
 return ERROR_MALFORMED;
 }
        firstOffset = tmp;
        offset += 8;
        size -= 8;
 } else {
 if (size < 16) {
 return -EINVAL;
 }
 if (!mDataSource->getUInt64(offset, &earliestPresentationTime)) {
 return ERROR_MALFORMED;
 }
 if (!mDataSource->getUInt64(offset + 8, &firstOffset)) {
 return ERROR_MALFORMED;
 }
        offset += 16;
        size -= 16;
 }
    ALOGV("sidx pres/off: %" PRIu64 "/%" PRIu64, earliestPresentationTime, firstOffset);

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

 uint16_t referenceCount;
 if (!mDataSource->getUInt16(offset + 2, &referenceCount)) {
 return ERROR_MALFORMED;
 }
    offset += 4;
    size -= 4;
    ALOGV("refcount: %d", referenceCount);

 if (size < referenceCount * 12) {
 return -EINVAL;
 }

 uint64_t total_duration = 0;
 for (unsigned int i = 0; i < referenceCount; i++) {
 uint32_t d1, d2, d3;

 if (!mDataSource->getUInt32(offset, &d1) || // size
 !mDataSource->getUInt32(offset + 4, &d2) || // duration
 !mDataSource->getUInt32(offset + 8, &d3)) { // flags
 return ERROR_MALFORMED;
 }

 if (d1 & 0x80000000) {
            ALOGW("sub-sidx boxes not supported yet");
 }
 bool sap = d3 & 0x80000000;
 uint32_t saptype = (d3 >> 28) & 7;
 if (!sap || (saptype != 1 && saptype != 2)) {
            ALOGW("not a stream access point, or unsupported type: %08x", d3);
 }
        total_duration += d2;
        offset += 12;
        ALOGV(" item %d, %08x %08x %08x", i, d1, d2, d3);
 SidxEntry se;
        se.mSize = d1 & 0x7fffffff;
        se.mDurationUs = 1000000LL * d2 / timeScale;
        mSidxEntries.add(se);
 }

 uint64_t sidxDuration = total_duration * 1000000 / timeScale;

 int64_t metaDuration;
 if (!mLastTrack->meta->findInt64(kKeyDuration, &metaDuration) || metaDuration == 0) {
        mLastTrack->meta->setInt64(kKeyDuration, sidxDuration);
 }
 return OK;
}

Project: Android

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

 if (size < 8) {
 return -EINVAL;
 }

 uint32_t flags;
 if (!mDataSource->getUInt32(offset, &flags)) { // actually version + flags
 return ERROR_MALFORMED;
 }

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

 if (!mDataSource->getUInt32(offset + 4, (uint32_t*)&mLastParsedTrackId)) {
 return ERROR_MALFORMED;
 }

 if (mLastParsedTrackId != mTrackId) {
 return OK;
 }

    mTrackFragmentHeaderInfo.mFlags = flags;
    mTrackFragmentHeaderInfo.mTrackID = mLastParsedTrackId;
    offset += 8;
    size -= 8;

    ALOGV("fragment header: %08x %08x", flags, mTrackFragmentHeaderInfo.mTrackID);

 if (flags & TrackFragmentHeaderInfo::kBaseDataOffsetPresent) {
 if (size < 8) {
 return -EINVAL;
 }

 if (!mDataSource->getUInt64(offset, &mTrackFragmentHeaderInfo.mBaseDataOffset)) {
 return ERROR_MALFORMED;
 }
        offset += 8;
        size -= 8;
 }

 if (flags & TrackFragmentHeaderInfo::kSampleDescriptionIndexPresent) {
 if (size < 4) {
 return -EINVAL;
 }

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

 if (flags & TrackFragmentHeaderInfo::kDefaultSampleDurationPresent) {
 if (size < 4) {
 return -EINVAL;
 }

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

 if (flags & TrackFragmentHeaderInfo::kDefaultSampleSizePresent) {
 if (size < 4) {
 return -EINVAL;
 }

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

 if (flags & TrackFragmentHeaderInfo::kDefaultSampleFlagsPresent) {
 if (size < 4) {
 return -EINVAL;
 }

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

 if (!(flags & TrackFragmentHeaderInfo::kBaseDataOffsetPresent)) {
        mTrackFragmentHeaderInfo.mBaseDataOffset = mCurrentMoofOffset;
 }

    mTrackFragmentHeaderInfo.mDataOffset = 0;
 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 if (mTrex) {
        sampleDuration = mTrex->default_sample_duration;
 }

 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 < (off64_t)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%08" PRIx64 ", 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;
        tmp.compositionOffset = sampleCtsOffset;
        mCurrentSamples.add(tmp);

        dataOffset += sampleSize;
 }

    mTrackFragmentHeaderInfo.mDataOffset = dataOffset;

 return OK;
}

Project: Android

status_t MPEG4Extractor::parseTrackHeader(
 off64_t data_offset, off64_t data_size) {
 if (data_size < 4) {
 return ERROR_MALFORMED;
 }

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

 size_t dynSize = (version == 1) ? 36 : 24;

 uint8_t buffer[36 + 60];

 if (data_size != (off64_t)dynSize + 60) {
 return ERROR_MALFORMED;
 }

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

 uint64_t ctime, mtime, duration;
 int32_t id;

 if (version == 1) {
        ctime = U64_AT(&buffer[4]);
        mtime = U64_AT(&buffer[12]);
        id = U32_AT(&buffer[20]);
        duration = U64_AT(&buffer[28]);
 } else if (version == 0) {
        ctime = U32_AT(&buffer[4]);
        mtime = U32_AT(&buffer[8]);
        id = U32_AT(&buffer[12]);
        duration = U32_AT(&buffer[20]);
 } else {
 return ERROR_UNSUPPORTED;
 }

    mLastTrack->meta->setInt32(kKeyTrackID, id);

 size_t matrixOffset = dynSize + 16;
 int32_t a00 = U32_AT(&buffer[matrixOffset]);
 int32_t a01 = U32_AT(&buffer[matrixOffset + 4]);
 int32_t dx = U32_AT(&buffer[matrixOffset + 8]);
 int32_t a10 = U32_AT(&buffer[matrixOffset + 12]);
 int32_t a11 = U32_AT(&buffer[matrixOffset + 16]);
 int32_t dy = U32_AT(&buffer[matrixOffset + 20]);

#if 0
    ALOGI("x' = %.2f * x + %.2f * y + %.2f",
         a00 / 65536.0f, a01 / 65536.0f, dx / 65536.0f);
    ALOGI("y' = %.2f * x + %.2f * y + %.2f",
         a10 / 65536.0f, a11 / 65536.0f, dy / 65536.0f);
#endif

 uint32_t rotationDegrees;

 static const int32_t kFixedOne = 0x10000;
 if (a00 == kFixedOne && a01 == 0 && a10 == 0 && a11 == kFixedOne) {
        rotationDegrees = 0;
 } else if (a00 == 0 && a01 == kFixedOne && a10 == -kFixedOne && a11 == 0) {
        rotationDegrees = 90;
 } else if (a00 == 0 && a01 == -kFixedOne && a10 == kFixedOne && a11 == 0) {
        rotationDegrees = 270;
 } else if (a00 == -kFixedOne && a01 == 0 && a10 == 0 && a11 == -kFixedOne) {
        rotationDegrees = 180;
 } else {
        ALOGW("We only support 0,90,180,270 degree rotation matrices");
        rotationDegrees = 0;
 }

 if (rotationDegrees != 0) {
        mLastTrack->meta->setInt32(kKeyRotation, rotationDegrees);
 }

 uint32_t width = U32_AT(&buffer[dynSize + 52]);
 uint32_t height = U32_AT(&buffer[dynSize + 56]);
    mLastTrack->meta->setInt32(kKeyDisplayWidth, width >> 16);
    mLastTrack->meta->setInt32(kKeyDisplayHeight, height >> 16);

 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, 1000000, mTimescale,
 &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, stts;
 bool isSyncSample;
 bool newBuffer = false;
 if (mBuffer == NULL) {
        newBuffer = true;

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

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

        err = mGroup->acquire_buffer(&mBuffer);

 if (err != OK) {
            CHECK(mBuffer == NULL);
 return err;
 }
 }

 if ((!mIsAVC && !mIsHEVC) || 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);
            mBuffer->meta_data()->setInt64(
                    kKeyDuration, ((int64_t)stts * 1000000) / mTimescale);

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

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

 ++mCurrentSampleIndex;
 }

 if (!mIsAVC && !mIsHEVC) {
 *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 (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((size_t)0u, size, srcOffset, mNALLengthSize);
 size_t nalLength = 0;
 if (!isMalFormed) {
                    nalLength = parseNALSize(&mSrcBuffer[srcOffset]);
                    srcOffset += mNALLengthSize;
                    isMalFormed = !isInRange((size_t)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);
        mBuffer->meta_data()->setInt64(
                kKeyDuration, ((int64_t)stts * 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

ssize_t MPEG4DataSource::readAt(off64_t offset, void *data, size_t size) {
 Mutex::Autolock autoLock(mLock);

 if (isInRange(mCachedOffset, mCachedSize, offset, size)) {
        memcpy(data, &mCache[offset - mCachedOffset], size);
 return size;
 }

 return mSource->readAt(offset, data, size);
}

Project: Android

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

 off64_t offset = 0;
 status_t err;
 while (true) {
 off64_t orig_offset = offset;
        err = parseChunk(&offset, 0);

 if (err != OK && err != UNKNOWN_ERROR) {
 break;
 } else if (offset <= orig_offset) {
            ALOGE("did not advance: 0x%lld->0x%lld", orig_offset, offset);
            err = ERROR_MALFORMED;
 break;
 } else 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('m', 'o', 'o', 'f')) {
            mMoofOffset = offset;
 } else if (chunk_type != FOURCC('m', 'd', 'a', 't')) {
 continue;
 }
 break;
 }

 if (mInitCheck == OK) {
 if (mHasVideo) {
            mFileMetaData->setCString(
                    kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_MPEG4);
 } else {
            mFileMetaData->setCString(kKeyMIMEType, "audio/mp4");
 }
 } 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

static int32_t readSize(off64_t offset,
 const sp<DataSource> DataSource, uint8_t *numOfBytes) {
 uint32_t size = 0;
 uint8_t data;
 bool moreData = true;
 *numOfBytes = 0;

 while (moreData) {
 if (DataSource->readAt(offset, &data, 1) < 1) {
 return -1;
 }
        offset ++;
        moreData = (data >= 128) ? true : false;
        size = (size << 7) | (data & 0x7f); // Take last 7 bits
 (*numOfBytes) ++;
 }

 return size;
}

Project: Android

status_t MPEG4DataSource::setCachedRange(off64_t offset, size_t size) {
 Mutex::Autolock autoLock(mLock);

    clearCache();

    mCache = (uint8_t *)malloc(size);

 if (mCache == NULL) {
 return -ENOMEM;
 }

    mCachedOffset = offset;
    mCachedSize = size;

 ssize_t err = mSource->readAt(mCachedOffset, mCache, mCachedSize);

 if (err < (ssize_t)size) {
        clearCache();

 return ERROR_IO;
 }

 return OK;
}

Project: Android

status_t MPEG4Source::start(MetaData *params) {
 Mutex::Autolock autoLock(mLock);

    CHECK(!mStarted);

 int32_t val;
 if (params && params->findInt32(kKeyWantsNALFragments, &val)
 && val != 0) {
        mWantsNALFragments = true;
 } else {
        mWantsNALFragments = false;
 }

    mGroup = new MediaBufferGroup;

 int32_t max_size;
    CHECK(mFormat->findInt32(kKeyMaxInputSize, &max_size));

    mGroup->add_buffer(new MediaBuffer(max_size));

    mSrcBuffer = new (std::nothrow) uint8_t[max_size];
 if (mSrcBuffer == NULL) {
 return ERROR_MALFORMED;
 }

    mStarted = true;

 return OK;
}

Project: Android

status_t MPEG4Source::stop() {
 Mutex::Autolock autoLock(mLock);

    CHECK(mStarted);

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

 delete[] mSrcBuffer;
    mSrcBuffer = NULL;

 delete mGroup;
    mGroup = NULL;

    mStarted = false;
    mCurrentSampleIndex = 0;

 return OK;
}

Project: Android

static bool underMetaDataPath(const Vector<uint32_t> &path) {
 return path.size() >= 5
 && path[0] == FOURCC('m', 'o', 'o', 'v')
 && path[1] == FOURCC('u', 'd', 't', 'a')
 && path[2] == FOURCC('m', 'e', 't', 'a')
 && path[3] == FOURCC('i', 'l', 's', 't');
}

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_HEVC)) {
 if (!track->meta->findData(kKeyHVCC, &type, &data, &size)
 || type != kTypeHVCC) {
 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 == NULL || !track->sampleTable->isValid()) {
        ALOGE("stbl atom missing/invalid.");
 return ERROR_MALFORMED;
 }

 return OK;
}

Project: Android

MPEG4DataSource::~MPEG4DataSource() {
    clearCache();
}

Project: Android

MPEG4Extractor::~MPEG4Extractor() {
 Track *track = mFirstTrack;
 while (track) {
 Track *next = track->next;

 delete track;
        track = next;
 }
    mFirstTrack = mLastTrack = NULL;

    SINF *sinf = mFirstSINF;
 while (sinf) {
        SINF *next = sinf->next;
 delete[] sinf->IPMPData;
 delete sinf;
        sinf = next;
 }
    mFirstSINF = NULL;

 for (size_t i = 0; i < mPssh.size(); i++) {
 delete [] mPssh[i].data;
 }
}

Project: Android

MPEG4Source::~MPEG4Source() {
 if (mStarted) {
        stop();
 }
    free(mCurrentSampleInfoSizes);
    free(mCurrentSampleInfoOffsets);
}

Project: Android

 ~PathAdder() {
        mPath->pop();
 }