Commit message: None
CVE: CVE-2017-0820
Vulnerability description: A vulnerability in the Android media framework (n/a). Product: Android. Versions: 7.0, 7.1.1, 7.1.2, 8.0. Android ID: A-62187433.

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 = chunkSize - (chunkDataOffset - offset);
 if (chunkDataSize < 0) {
            ALOGE("b/23540914");
 return ERROR_MALFORMED;
 }

 char chunkstring[5];
 MakeFourCCString(chunkType, chunkstring);
        ALOGV("saw chunk type %s, size %" PRIu64 " @ %lld", chunkstring, chunkSize, (long long)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", (long long)moovAtomEndOffset);
 }

 return true;
}

Project: Android

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

Project: Android

static bool convertTimeToDate(int64_t time_1904, String8 *s) {
 static const int64_t delta = (((66 * 365 + 17) * 24) * 3600);
 if (time_1904 < INT64_MIN + delta) {
 return false;
 }
 time_t time_1970 = time_1904 - delta;

 char tmp[32];
 struct tm* tm = gmtime(&time_1970);
 if (tm != NULL &&
            strftime(tmp, sizeof(tmp), "%Y%m%dT%H%M%S.000Z", tm) > 0) {
        s->setTo(tmp);
 return true;
 }
 return false;
}

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;
            mNextMoofOffset = -1;
            mCurrentSamples.clear();
            mCurrentSampleIndex = 0;
 status_t err = parseChunk(&totalOffset);
 if (err != OK) {
 return err;
 }
            mCurrentTime = totalTime * mTimescale / 1000000ll;
 } else {
            mCurrentMoofOffset = mFirstMoofOffset;
            mNextMoofOffset = -1;
            mCurrentSamples.clear();
            mCurrentSampleIndex = 0;
 off64_t tmp = mCurrentMoofOffset;
 status_t err = parseChunk(&tmp);
 if (err != OK) {
 return err;
 }
            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;
 status_t err = parseChunk(&nextMoof);
 if (err != OK) {
 return err;
 }
 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;
 }
 if (size > mBuffer->size()) {
            ALOGE("buffer too small: %zu > %zu", size, mBuffer->size());
 return ERROR_BUFFER_TOO_SMALL;
 }
 }

 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) {
 if (!isInRange((size_t)0u, mBuffer->size(), size)) {
                mBuffer->release();
                mBuffer = NULL;

                ALOGE("fragmentedRead ERROR_MALFORMED size %zu", size);
 return ERROR_MALFORMED;
 }

 ssize_t num_bytes_read =
                mDataSource->readAt(offset, (uint8_t *)mBuffer->data(), size);

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

                ALOGE("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 (mIsAVC) {
 uint32_t layerId = FindAVCLayerId(
 (const uint8_t *)mBuffer->data(), mBuffer->range_length());
                mBuffer->meta_data()->setInt32(kKeyTemporalLayerId, layerId);
 }

 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 (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 {
        ALOGV("whole NAL");
 ssize_t num_bytes_read = 0;
 int32_t drm = 0;
 bool usesDRM = (mFormat->findInt32(kKeyIsDRM, &drm) && drm != 0);
 void *data = NULL;
 bool isMalFormed = false;
 if (usesDRM) {
 if (mBuffer == NULL || !isInRange((size_t)0u, mBuffer->size(), size)) {
                isMalFormed = true;
 } else {
                data = mBuffer->data();
 }
 } else {
 int32_t max_size;
 if (mFormat == NULL
 || !mFormat->findInt32(kKeyMaxInputSize, &max_size)
 || !isInRange((size_t)0u, (size_t)max_size, size)) {
                isMalFormed = true;
 } else {
                data = mSrcBuffer;
 }
 }

 if (isMalFormed || data == NULL) {
            ALOGE("isMalFormed size %zu", size);
 if (mBuffer != NULL) {
                mBuffer->release();
                mBuffer = NULL;
 }
 return ERROR_MALFORMED;
 }
        num_bytes_read = mDataSource->readAt(offset, data, size);

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

            ALOGE("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) {
                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)
 || !isInRange((size_t)0u, mBuffer->size(), dstOffset, (size_t)4u)
 || !isInRange((size_t)0u, mBuffer->size(), dstOffset + 4, nalLength);
 }

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

 if (nalLength == 0) {
 continue;
 }

 if (dstOffset > SIZE_MAX - 4 ||
                        dstOffset + 4 > SIZE_MAX - nalLength ||
                        dstOffset + 4 + nalLength > mBuffer->size()) {
                    ALOGE("b/26365349 : %zu %zu", dstOffset, mBuffer->size());
                    android_errorWriteLog(0x534e4554, "26365349");
                    mBuffer->release();
                    mBuffer = NULL;
 return ERROR_MALFORMED;
 }

                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<IMediaSource> 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;
 }


 Trex *trex = NULL;
 int32_t trackId;
 if (track->meta->findInt32(kKeyTrackID, &trackId)) {
 for (size_t i = 0; i < mTrex.size(); i++) {
 Trex *t = &mTrex.editItemAt(i);
 if (t->track_ID == (uint32_t) trackId) {
                trex = t;
 break;
 }
 }
 } else {
        ALOGE("b/21657957");
 return NULL;
 }

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

 const char *mime;
 if (!track->meta->findCString(kKeyMIMEType, &mime)) {
 return NULL;
 }

 if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
 uint32_t type;
 const void *data;
 size_t size;
 if (!track->meta->findData(kKeyAVCC, &type, &data, &size)) {
 return NULL;
 }

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

 if (size < 7 || ptr[0] != 1) { // configurationVersion == 1
 return NULL;
 }
 } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC)) {
 uint32_t type;
 const void *data;
 size_t size;
 if (!track->meta->findData(kKeyHVCC, &type, &data, &size)) {
 return NULL;
 }

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

 if (size < 22 || ptr[0] != 1) { // configurationVersion == 1
 return NULL;
 }
 }

    sp<MPEG4Source> source = new MPEG4Source(this,
            track->meta, mDataSource, track->timescale, track->sampleTable,
            mSidxEntries, trex, mMoofOffset);
 if (source->init() != OK) {
 return NULL;
 }
 return source;
}

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 (!strcmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG2)) {
 off64_t offset;
 size_t size;
 if (track->sampleTable->getMetaDataForSample(
 0 /* sampleIndex */, &offset, &size, NULL /* sampleTime */) == OK) {
 if (size > kMaxTrackHeaderSize) {
                        size = kMaxTrackHeaderSize;
 }
 uint8_t header[kMaxTrackHeaderSize];
 if (mDataSource->readAt(offset, &header, size) == (ssize_t)size) {
                        track->meta->setData(kKeyStreamHeader, 'mdat', header, size);
 }
 }
 }

 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->timescale != 0 &&
                        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

status_t MPEG4Source::init() {
 if (mFirstMoofOffset != 0) {
 off64_t offset = mFirstMoofOffset;
 return parseChunk(&offset);
 }
 return OK;
}

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'):
 {
 if (size < 6) {
 delete[] buffer;
                buffer = NULL;
                ALOGE("b/62133227");
                android_errorWriteLog(0x534e4554, "62133227");
 return ERROR_MALFORMED;
 }
 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) {
 delete[] buffer;
            buffer = NULL;
 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 MPEG4Extractor::parseAC3SampleEntry(off64_t offset) {
    offset += 16;
 uint16_t channelCount;
 if (!mDataSource->getUInt16(offset, &channelCount)) {
 return ERROR_MALFORMED;
 }
    offset += 8;
 uint16_t sampleRate;
 if (!mDataSource->getUInt16(offset, &sampleRate)) {
        ALOGE("MPEG4Extractor: error while reading ac-3 block: cannot read sample rate");
 return ERROR_MALFORMED;
 }

    offset += 4;
 return parseAC3SpecificBox(offset, sampleRate);
}

Project: Android

status_t MPEG4Extractor::parseAC3SpecificBox(
 off64_t offset, uint16_t sampleRate) {
 uint32_t size;
 const uint32_t kAC3SpecificBoxSize = 11;
 if (!mDataSource->getUInt32(offset, &size) || size < kAC3SpecificBoxSize) {
        ALOGE("MPEG4Extractor: error while reading ac-3 block: cannot read specific box size");
 return ERROR_MALFORMED;
 }

    offset += 4;
 uint32_t type;
 if (!mDataSource->getUInt32(offset, &type) || type != FOURCC('d', 'a', 'c', '3')) {
        ALOGE("MPEG4Extractor: error while reading ac-3 specific block: header not dac3");
 return ERROR_MALFORMED;
 }

    offset += 4;
 const uint32_t kAC3SpecificBoxPayloadSize = 3;
 uint8_t chunk[kAC3SpecificBoxPayloadSize];
 if (mDataSource->readAt(offset, chunk, sizeof(chunk)) != sizeof(chunk)) {
        ALOGE("MPEG4Extractor: error while reading ac-3 specific block: bitstream fields");
 return ERROR_MALFORMED;
 }

 ABitReader br(chunk, sizeof(chunk));
 static const unsigned channelCountTable[] = {2, 1, 2, 3, 3, 4, 4, 5};
 static const unsigned sampleRateTable[] = {48000, 44100, 32000};

 unsigned fscod = br.getBits(2);
 if (fscod == 3) {
        ALOGE("Incorrect fscod (3) in AC3 header");
 return ERROR_MALFORMED;
 }
 unsigned boxSampleRate = sampleRateTable[fscod];
 if (boxSampleRate != sampleRate) {
        ALOGE("sample rate mismatch: boxSampleRate = %d, sampleRate = %d",
            boxSampleRate, sampleRate);
 return ERROR_MALFORMED;
 }

 unsigned bsid = br.getBits(5);
 if (bsid > 8) {
        ALOGW("Incorrect bsid in AC3 header. Possibly E-AC-3?");
 return ERROR_MALFORMED;
 }

 unsigned bsmod __unused = br.getBits(3);

 unsigned acmod = br.getBits(3);
 unsigned lfeon = br.getBits(1);
 unsigned channelCount = channelCountTable[acmod] + lfeon;

 if (mLastTrack == NULL) {
 return ERROR_MALFORMED;
 }
    mLastTrack->meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AC3);
    mLastTrack->meta->setInt32(kKeyChannelCount, channelCount);
    mLastTrack->meta->setInt32(kKeySampleRate, sampleRate);
 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, (long long)*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) {
 break;
 }
                    chunk_size = ntohl(hdr[0]);
                    chunk_type = ntohl(hdr[1]);
 if (chunk_size == 1) {
 if (mDataSource->readAt(*offset + 8, &chunk_size, 8) < 8) {
 return ERROR_IO;
 }
                        chunk_size = ntoh64(chunk_size);
 if (chunk_size < 16) {
 return ERROR_MALFORMED;
 }
 } else if (chunk_size == 0) {
 } else if (chunk_size < 8) {
 return ERROR_MALFORMED;
 }

 if (chunk_type == FOURCC('m', 'o', 'o', 'f')) {
                        mNextMoofOffset = *offset;
 break;
 } else if (chunk_size == 0) {
 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::parseColorInfo(off64_t offset, size_t size) {
 if (size < 4 || size == SIZE_MAX || mLastTrack == NULL) {
 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;
 }

 int32_t type = U32_AT(&buffer[0]);
 if ((type == FOURCC('n', 'c', 'l', 'x') && size >= 11)
 || (type == FOURCC('n', 'c', 'l', 'c') && size >= 10)) {
 int32_t primaries = U16_AT(&buffer[4]);
 int32_t transfer = U16_AT(&buffer[6]);
 int32_t coeffs = U16_AT(&buffer[8]);
 bool fullRange = (type == FOURCC('n', 'c', 'l', 'x')) && (buffer[10] & 128);

 ColorAspects aspects;
 ColorUtils::convertIsoColorAspectsToCodecAspects(
                primaries, transfer, coeffs, fullRange, aspects);

 if (!mLastTrack->meta->hasData(kKeyColorPrimaries)) {
            mLastTrack->meta->setInt32(kKeyColorPrimaries, aspects.mPrimaries);
            mLastTrack->meta->setInt32(kKeyTransferFunction, aspects.mTransfer);
            mLastTrack->meta->setInt32(kKeyColorMatrix, aspects.mMatrixCoeffs);
            mLastTrack->meta->setInt32(kKeyColorRange, aspects.mRange);
 }
 }

 delete[] buffer;
    buffer = NULL;

 return OK;
}

Project: Android

status_t MPEG4Extractor::parseITunesMetaData(off64_t offset, size_t size) {
 if (size == 0) {
 return OK;
 }

 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, (long long)offset);
 switch ((int32_t)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) {
 if (mLastTrack == NULL) {
 delete[] buffer;
 return ERROR_MALFORMED;
 }

                        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

status_t MPEG4Extractor::parseQTMetaVal(
 int32_t keyId, off64_t offset, size_t size) {
 ssize_t index = mMetaKeyMap.indexOfKey(keyId);
 if (index < 0) {
 return ERROR_MALFORMED;
 }

 if (size <= 16) {
 return ERROR_MALFORMED;
 }
 uint32_t dataSize;
 if (!mDataSource->getUInt32(offset, &dataSize)
 || dataSize > size || dataSize <= 16) {
 return ERROR_MALFORMED;
 }
 uint32_t atomFourCC;
 if (!mDataSource->getUInt32(offset + 4, &atomFourCC)
 || atomFourCC != FOURCC('d', 'a', 't', 'a')) {
 return ERROR_MALFORMED;
 }
 uint32_t dataType;
 if (!mDataSource->getUInt32(offset + 8, &dataType)
 || ((dataType & 0xff000000) != 0)) {
 return ERROR_MALFORMED;
 }

    dataSize -= 16;
    offset += 16;

 if (dataType == 23 && dataSize >= 4) {
 uint32_t val;
 if (!mDataSource->getUInt32(offset, &val)) {
 return ERROR_MALFORMED;
 }
 if (!strcasecmp(mMetaKeyMap[index].c_str(), "com.android.capture.fps")) {
            mFileMetaData->setFloat(kKeyCaptureFramerate, *(float *)&val);
 }
 } else if (dataType == 67 && dataSize >= 4) {
 uint32_t val;
 if (!mDataSource->getUInt32(offset, &val)) {
 return ERROR_MALFORMED;
 }
 if (!strcasecmp(mMetaKeyMap[index].c_str(), "com.android.video.temporal_layers_count")) {
            mFileMetaData->setInt32(kKeyTemporalLayerCount, val);
 }
 } else {
        ALOGV("ignoring key: type %d, size %d", dataType, dataSize);
 }

 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) {
 uint8_t * newPtr = (uint8_t*) realloc(mCurrentSampleInfoSizes, smplcnt);
 if (newPtr == NULL) {
            ALOGE("failed to realloc %u -> %u", mCurrentSampleInfoAllocSize, smplcnt);
 return NO_MEMORY;
 }
        mCurrentSampleInfoSizes = newPtr;
        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;
 }
 uint8_t version = flags >> 24;
    flags &= 0xffffff;
    ALOGV("fragment run version: 0x%02x, flags: 0x%06x", version, flags);

 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

void MPEG4Extractor::populateMetrics() {
    ALOGV("MPEG4Extractor::populateMetrics");
}

Project: Android

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

    CHECK(mStarted);

 if (options != nullptr && options->getNonBlocking() && !mGroup->has_buffers()) {
 *out = nullptr;
 return WOULD_BLOCK;
 }

 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 = 0;
 size_t size = 0;
 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 (size > mBuffer->size()) {
            ALOGE("buffer too small: %zu > %zu", size, mBuffer->size());
 return ERROR_BUFFER_TOO_SMALL;
 }
 }

 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 (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((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;
 }

 if (dstOffset > SIZE_MAX - 4 ||
                        dstOffset + 4 > SIZE_MAX - nalLength ||
                        dstOffset + 4 + nalLength > mBuffer->size()) {
                    ALOGE("b/27208621 : %zu %zu", dstOffset, mBuffer->size());
                    android_errorWriteLog(0x534e4554, "27208621");
                    mBuffer->release();
                    mBuffer = NULL;
 return ERROR_MALFORMED;
 }

                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 (mIsAVC) {
 uint32_t layerId = FindAVCLayerId(
 (const uint8_t *)mBuffer->data(), mBuffer->range_length());
            mBuffer->meta_data()->setInt32(kKeyTemporalLayerId, layerId);
 }

 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;
 bool sawMoovOrSidx = false;

 while (!(sawMoovOrSidx && (mMdatFound || mMoofFound))) {
 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: %lld->%lld", (long long)orig_offset, (long long)offset);
            err = ERROR_MALFORMED;
 break;
 } else if (err == UNKNOWN_ERROR) {
            sawMoovOrSidx = true;
 }
 }

 if (mInitCheck == OK) {
 if (findTrackByMimePrefix("video/") != NULL) {
            mFileMetaData->setCString(
                    kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_MPEG4);
 } else if (findTrackByMimePrefix("audio/") != NULL) {
            mFileMetaData->setCString(kKeyMIMEType, "audio/mp4");
 } else {
            mFileMetaData->setCString(kKeyMIMEType, "application/octet-stream");
 }
 } else {
        mInitCheck = err;
 }

    CHECK_NE(err, (status_t)NO_INIT);

 uint64_t psshsize = 0;
 for (size_t i = 0; i < mPssh.size(); i++) {
        psshsize += 20 + mPssh[i].datalen;
 }
 if (psshsize > 0 && psshsize <= UINT32_MAX) {
 char *buf = (char*)malloc(psshsize);
 if (!buf) {
            ALOGE("b/28471206");
 return NO_MEMORY;
 }
 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 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;
 }

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

 const size_t kMaxBufferSize = 64 * 1024 * 1024;
 if (max_size > kMaxBufferSize) {
        ALOGE("bogus max input size: %zu > %zu", max_size, kMaxBufferSize);
 return ERROR_MALFORMED;
 }
 if (max_size == 0) {
        ALOGE("zero max input size");
 return ERROR_MALFORMED;
 }

 const size_t kMaxBuffers = 8;
 const size_t buffers = min(kMaxBufferSize / max_size, kMaxBuffers);
    mGroup = new MediaBufferGroup(buffers, max_size);
    mSrcBuffer = new (std::nothrow) uint8_t[max_size];
 if (mSrcBuffer == NULL) {
 delete mGroup;
        mGroup = NULL;
 return ERROR_MALFORMED;
 }

    mStarted = true;

 return OK;
}

Project: Android

 virtual bool supportNonblockingRead() { return true; }