| /* |
| * Copyright (C) 2012 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| //#define LOG_NDEBUG 0 |
| #define LOG_TAG "Parser" |
| #include <utils/Log.h> |
| |
| #include "Parser.h" |
| #include "TrackFragment.h" |
| |
| #include "ESDS.h" |
| |
| #include <media/stagefright/foundation/ABuffer.h> |
| #include <media/stagefright/foundation/ADebug.h> |
| #include <media/stagefright/foundation/AMessage.h> |
| #include <media/stagefright/foundation/hexdump.h> |
| #include <media/stagefright/MediaDefs.h> |
| #include <media/stagefright/MediaErrors.h> |
| #include <media/stagefright/Utils.h> |
| |
| #include "../NuPlayerStreamListener.h" |
| |
| namespace android { |
| |
| static const char *Fourcc2String(uint32_t fourcc) { |
| static char buffer[5]; |
| buffer[4] = '\0'; |
| buffer[0] = fourcc >> 24; |
| buffer[1] = (fourcc >> 16) & 0xff; |
| buffer[2] = (fourcc >> 8) & 0xff; |
| buffer[3] = fourcc & 0xff; |
| |
| return buffer; |
| } |
| |
| static const char *IndentString(size_t n) { |
| static const char kSpace[] = " "; |
| return kSpace + sizeof(kSpace) - 2 * n - 1; |
| } |
| |
| // static |
| const Parser::DispatchEntry Parser::kDispatchTable[] = { |
| { FOURCC('m', 'o', 'o', 'v'), 0, NULL }, |
| { FOURCC('t', 'r', 'a', 'k'), FOURCC('m', 'o', 'o', 'v'), NULL }, |
| { FOURCC('u', 'd', 't', 'a'), FOURCC('t', 'r', 'a', 'k'), NULL }, |
| { FOURCC('u', 'd', 't', 'a'), FOURCC('m', 'o', 'o', 'v'), NULL }, |
| { FOURCC('m', 'e', 't', 'a'), FOURCC('u', 'd', 't', 'a'), NULL }, |
| { FOURCC('i', 'l', 's', 't'), FOURCC('m', 'e', 't', 'a'), NULL }, |
| |
| { FOURCC('t', 'k', 'h', 'd'), FOURCC('t', 'r', 'a', 'k'), |
| &Parser::parseTrackHeader |
| }, |
| |
| { FOURCC('m', 'v', 'e', 'x'), FOURCC('m', 'o', 'o', 'v'), NULL }, |
| |
| { FOURCC('t', 'r', 'e', 'x'), FOURCC('m', 'v', 'e', 'x'), |
| &Parser::parseTrackExtends |
| }, |
| |
| { FOURCC('e', 'd', 't', 's'), FOURCC('t', 'r', 'a', 'k'), NULL }, |
| { FOURCC('m', 'd', 'i', 'a'), FOURCC('t', 'r', 'a', 'k'), NULL }, |
| |
| { FOURCC('m', 'd', 'h', 'd'), FOURCC('m', 'd', 'i', 'a'), |
| &Parser::parseMediaHeader |
| }, |
| |
| { FOURCC('h', 'd', 'l', 'r'), FOURCC('m', 'd', 'i', 'a'), |
| &Parser::parseMediaHandler |
| }, |
| |
| { FOURCC('m', 'i', 'n', 'f'), FOURCC('m', 'd', 'i', 'a'), NULL }, |
| { FOURCC('d', 'i', 'n', 'f'), FOURCC('m', 'i', 'n', 'f'), NULL }, |
| { FOURCC('s', 't', 'b', 'l'), FOURCC('m', 'i', 'n', 'f'), NULL }, |
| { FOURCC('s', 't', 's', 'd'), FOURCC('s', 't', 'b', 'l'), NULL }, |
| |
| { FOURCC('s', 't', 's', 'z'), FOURCC('s', 't', 'b', 'l'), |
| &Parser::parseSampleSizes }, |
| |
| { FOURCC('s', 't', 'z', '2'), FOURCC('s', 't', 'b', 'l'), |
| &Parser::parseCompactSampleSizes }, |
| |
| { FOURCC('s', 't', 's', 'c'), FOURCC('s', 't', 'b', 'l'), |
| &Parser::parseSampleToChunk }, |
| |
| { FOURCC('s', 't', 'c', 'o'), FOURCC('s', 't', 'b', 'l'), |
| &Parser::parseChunkOffsets }, |
| |
| { FOURCC('c', 'o', '6', '4'), FOURCC('s', 't', 'b', 'l'), |
| &Parser::parseChunkOffsets64 }, |
| |
| { FOURCC('a', 'v', 'c', 'C'), FOURCC('a', 'v', 'c', '1'), |
| &Parser::parseAVCCodecSpecificData }, |
| |
| { FOURCC('e', 's', 'd', 's'), FOURCC('m', 'p', '4', 'a'), |
| &Parser::parseESDSCodecSpecificData }, |
| |
| { FOURCC('e', 's', 'd', 's'), FOURCC('m', 'p', '4', 'v'), |
| &Parser::parseESDSCodecSpecificData }, |
| |
| { FOURCC('m', 'd', 'a', 't'), 0, &Parser::parseMediaData }, |
| |
| { FOURCC('m', 'o', 'o', 'f'), 0, NULL }, |
| { FOURCC('t', 'r', 'a', 'f'), FOURCC('m', 'o', 'o', 'f'), NULL }, |
| |
| { FOURCC('t', 'f', 'h', 'd'), FOURCC('t', 'r', 'a', 'f'), |
| &Parser::parseTrackFragmentHeader |
| }, |
| { FOURCC('t', 'r', 'u', 'n'), FOURCC('t', 'r', 'a', 'f'), |
| &Parser::parseTrackFragmentRun |
| }, |
| |
| { FOURCC('m', 'f', 'r', 'a'), 0, NULL }, |
| }; |
| |
| struct FileSource : public Parser::Source { |
| FileSource(const char *filename) |
| : mFile(fopen(filename, "rb")) { |
| CHECK(mFile != NULL); |
| } |
| |
| virtual ssize_t readAt(off64_t offset, void *data, size_t size) { |
| fseek(mFile, offset, SEEK_SET); |
| return fread(data, 1, size, mFile); |
| } |
| |
| private: |
| FILE *mFile; |
| |
| DISALLOW_EVIL_CONSTRUCTORS(FileSource); |
| }; |
| |
| Parser::Parser() |
| : mBufferPos(0), |
| mSuspended(false), |
| mFinalResult(OK) { |
| } |
| |
| Parser::~Parser() { |
| } |
| |
| void Parser::start(const char *filename) { |
| sp<AMessage> msg = new AMessage(kWhatStart, id()); |
| msg->setObject("source", new FileSource(filename)); |
| msg->post(); |
| } |
| |
| void Parser::start(const sp<Source> &source) { |
| sp<AMessage> msg = new AMessage(kWhatStart, id()); |
| msg->setObject("source", source); |
| msg->post(); |
| } |
| |
| sp<AMessage> Parser::getFormat(bool audio) { |
| sp<AMessage> msg = new AMessage(kWhatGetFormat, id()); |
| msg->setInt32("audio", audio); |
| |
| sp<AMessage> response; |
| status_t err = msg->postAndAwaitResponse(&response); |
| |
| if (err != OK) { |
| return NULL; |
| } |
| |
| if (response->findInt32("err", &err) && err != OK) { |
| return NULL; |
| } |
| |
| sp<AMessage> format; |
| CHECK(response->findMessage("format", &format)); |
| |
| ALOGV("returning format %s", format->debugString().c_str()); |
| return format; |
| } |
| |
| status_t Parser::dequeueAccessUnit(bool audio, sp<ABuffer> *accessUnit) { |
| sp<AMessage> msg = new AMessage(kWhatDequeueAccessUnit, id()); |
| msg->setInt32("audio", audio); |
| |
| sp<AMessage> response; |
| status_t err = msg->postAndAwaitResponse(&response); |
| |
| if (err != OK) { |
| return err; |
| } |
| |
| if (response->findInt32("err", &err) && err != OK) { |
| return err; |
| } |
| |
| CHECK(response->findBuffer("accessUnit", accessUnit)); |
| |
| return OK; |
| } |
| |
| ssize_t Parser::findTrack(bool wantAudio) const { |
| for (size_t i = 0; i < mTracks.size(); ++i) { |
| const TrackInfo *info = &mTracks.valueAt(i); |
| |
| bool isAudio = |
| info->mMediaHandlerType == FOURCC('s', 'o', 'u', 'n'); |
| |
| bool isVideo = |
| info->mMediaHandlerType == FOURCC('v', 'i', 'd', 'e'); |
| |
| if ((wantAudio && isAudio) || (!wantAudio && !isAudio)) { |
| if (info->mSampleDescs.empty()) { |
| break; |
| } |
| |
| return i; |
| } |
| } |
| |
| return -EWOULDBLOCK; |
| } |
| |
| void Parser::onMessageReceived(const sp<AMessage> &msg) { |
| switch (msg->what()) { |
| case kWhatStart: |
| { |
| sp<RefBase> obj; |
| CHECK(msg->findObject("source", &obj)); |
| |
| mSource = static_cast<Source *>(obj.get()); |
| |
| mBuffer = new ABuffer(512 * 1024); |
| mBuffer->setRange(0, 0); |
| |
| enter(0ll, 0, 0); |
| |
| (new AMessage(kWhatProceed, id()))->post(); |
| break; |
| } |
| |
| case kWhatProceed: |
| { |
| CHECK(!mSuspended); |
| |
| status_t err = onProceed(); |
| |
| if (err == OK) { |
| if (!mSuspended) { |
| msg->post(); |
| } |
| } else if (err != -EAGAIN) { |
| ALOGE("onProceed returned error %d", err); |
| } |
| |
| break; |
| } |
| |
| case kWhatReadMore: |
| { |
| size_t needed; |
| CHECK(msg->findSize("needed", &needed)); |
| |
| memmove(mBuffer->base(), mBuffer->data(), mBuffer->size()); |
| mBufferPos += mBuffer->offset(); |
| mBuffer->setRange(0, mBuffer->size()); |
| |
| size_t maxBytesToRead = mBuffer->capacity() - mBuffer->size(); |
| |
| if (maxBytesToRead < needed) { |
| ALOGI("resizing buffer."); |
| |
| sp<ABuffer> newBuffer = |
| new ABuffer((mBuffer->size() + needed + 1023) & ~1023); |
| memcpy(newBuffer->data(), mBuffer->data(), mBuffer->size()); |
| newBuffer->setRange(0, mBuffer->size()); |
| |
| mBuffer = newBuffer; |
| maxBytesToRead = mBuffer->capacity() - mBuffer->size(); |
| } |
| |
| CHECK_GE(maxBytesToRead, needed); |
| |
| ssize_t n = mSource->readAt( |
| mBufferPos + mBuffer->size(), |
| mBuffer->data() + mBuffer->size(), needed); |
| |
| if (n < (ssize_t)needed) { |
| ALOGI("%s", "Reached EOF"); |
| if (n < 0) { |
| mFinalResult = n; |
| } else if (n == 0) { |
| mFinalResult = ERROR_END_OF_STREAM; |
| } else { |
| mFinalResult = ERROR_IO; |
| } |
| } else { |
| mBuffer->setRange(0, mBuffer->size() + n); |
| (new AMessage(kWhatProceed, id()))->post(); |
| } |
| |
| break; |
| } |
| |
| case kWhatGetFormat: |
| { |
| int32_t wantAudio; |
| CHECK(msg->findInt32("audio", &wantAudio)); |
| |
| status_t err = -EWOULDBLOCK; |
| sp<AMessage> response = new AMessage; |
| |
| ssize_t trackIndex = findTrack(wantAudio); |
| |
| if (trackIndex < 0) { |
| err = trackIndex; |
| } else { |
| TrackInfo *info = &mTracks.editValueAt(trackIndex); |
| |
| response->setMessage( |
| "format", info->mSampleDescs.itemAt(0).mFormat); |
| |
| err = OK; |
| } |
| |
| response->setInt32("err", err); |
| |
| uint32_t replyID; |
| CHECK(msg->senderAwaitsResponse(&replyID)); |
| |
| response->postReply(replyID); |
| break; |
| } |
| |
| case kWhatDequeueAccessUnit: |
| { |
| int32_t wantAudio; |
| CHECK(msg->findInt32("audio", &wantAudio)); |
| |
| status_t err = -EWOULDBLOCK; |
| sp<AMessage> response = new AMessage; |
| |
| ssize_t trackIndex = findTrack(wantAudio); |
| |
| if (trackIndex < 0) { |
| err = trackIndex; |
| } else { |
| sp<ABuffer> accessUnit; |
| err = onDequeueAccessUnit(trackIndex, &accessUnit); |
| |
| if (err == OK) { |
| response->setBuffer("accessUnit", accessUnit); |
| } |
| } |
| |
| response->setInt32("err", err); |
| |
| uint32_t replyID; |
| CHECK(msg->senderAwaitsResponse(&replyID)); |
| |
| response->postReply(replyID); |
| break; |
| } |
| |
| default: |
| TRESPASS(); |
| } |
| } |
| |
| status_t Parser::onProceed() { |
| status_t err; |
| |
| if ((err = need(8)) != OK) { |
| return err; |
| } |
| |
| uint64_t size = readU32(0); |
| uint32_t type = readU32(4); |
| |
| size_t offset = 8; |
| |
| if (size == 1) { |
| if ((err = need(16)) != OK) { |
| return err; |
| } |
| |
| size = readU64(offset); |
| offset += 8; |
| } |
| |
| uint8_t userType[16]; |
| |
| if (type == FOURCC('u', 'u', 'i', 'd')) { |
| if ((err = need(offset + 16)) != OK) { |
| return err; |
| } |
| |
| memcpy(userType, mBuffer->data() + offset, 16); |
| offset += 16; |
| } |
| |
| CHECK(!mStack.isEmpty()); |
| uint32_t ptype = mStack.itemAt(mStack.size() - 1).mType; |
| |
| static const size_t kNumDispatchers = |
| sizeof(kDispatchTable) / sizeof(kDispatchTable[0]); |
| |
| size_t i; |
| for (i = 0; i < kNumDispatchers; ++i) { |
| if (kDispatchTable[i].mType == type |
| && kDispatchTable[i].mParentType == ptype) { |
| break; |
| } |
| } |
| |
| // SampleEntry boxes are container boxes that start with a variable |
| // amount of data depending on the media handler type. |
| // We don't look inside 'hint' type SampleEntry boxes. |
| |
| bool isSampleEntryBox = |
| (ptype == FOURCC('s', 't', 's', 'd')) |
| && editTrack(mCurrentTrackID)->mMediaHandlerType |
| != FOURCC('h', 'i', 'n', 't'); |
| |
| if ((i < kNumDispatchers && kDispatchTable[i].mHandler == 0) |
| || isSampleEntryBox || ptype == FOURCC('i', 'l', 's', 't')) { |
| // This is a container box. |
| if (type == FOURCC('m', 'e', 't', 'a')) { |
| if ((err = need(offset + 4)) < OK) { |
| return err; |
| } |
| |
| if (readU32(offset) != 0) { |
| return -EINVAL; |
| } |
| |
| offset += 4; |
| } else if (type == FOURCC('s', 't', 's', 'd')) { |
| if ((err = need(offset + 8)) < OK) { |
| return err; |
| } |
| |
| if (readU32(offset) != 0) { |
| return -EINVAL; |
| } |
| |
| if (readU32(offset + 4) == 0) { |
| // We need at least some entries. |
| return -EINVAL; |
| } |
| |
| offset += 8; |
| } else if (isSampleEntryBox) { |
| size_t headerSize; |
| |
| switch (editTrack(mCurrentTrackID)->mMediaHandlerType) { |
| case FOURCC('v', 'i', 'd', 'e'): |
| { |
| // 8 bytes SampleEntry + 70 bytes VisualSampleEntry |
| headerSize = 78; |
| break; |
| } |
| |
| case FOURCC('s', 'o', 'u', 'n'): |
| { |
| // 8 bytes SampleEntry + 20 bytes AudioSampleEntry |
| headerSize = 28; |
| break; |
| } |
| |
| case FOURCC('m', 'e', 't', 'a'): |
| { |
| headerSize = 8; // 8 bytes SampleEntry |
| break; |
| } |
| |
| default: |
| TRESPASS(); |
| } |
| |
| if (offset + headerSize > size) { |
| return -EINVAL; |
| } |
| |
| if ((err = need(offset + headerSize)) != OK) { |
| return err; |
| } |
| |
| switch (editTrack(mCurrentTrackID)->mMediaHandlerType) { |
| case FOURCC('v', 'i', 'd', 'e'): |
| { |
| err = parseVisualSampleEntry( |
| type, offset, offset + headerSize); |
| break; |
| } |
| |
| case FOURCC('s', 'o', 'u', 'n'): |
| { |
| err = parseAudioSampleEntry( |
| type, offset, offset + headerSize); |
| break; |
| } |
| |
| case FOURCC('m', 'e', 't', 'a'): |
| { |
| err = OK; |
| break; |
| } |
| |
| default: |
| TRESPASS(); |
| } |
| |
| if (err != OK) { |
| return err; |
| } |
| |
| offset += headerSize; |
| } |
| |
| skip(offset); |
| |
| ALOGV("%sentering box of type '%s'", |
| IndentString(mStack.size()), Fourcc2String(type)); |
| |
| enter(mBufferPos - offset, type, size - offset); |
| } else { |
| if (!fitsContainer(size)) { |
| return -EINVAL; |
| } |
| |
| if (i < kNumDispatchers && kDispatchTable[i].mHandler != 0) { |
| // We have a handler for this box type. |
| |
| if ((err = need(size)) != OK) { |
| return err; |
| } |
| |
| ALOGV("%sparsing box of type '%s'", |
| IndentString(mStack.size()), Fourcc2String(type)); |
| |
| if ((err = (this->*kDispatchTable[i].mHandler)( |
| type, offset, size)) != OK) { |
| return err; |
| } |
| } else { |
| // Unknown box type |
| |
| ALOGV("%sskipping box of type '%s', size %llu", |
| IndentString(mStack.size()), |
| Fourcc2String(type), size); |
| |
| } |
| |
| skip(size); |
| } |
| |
| return OK; |
| } |
| |
| // static |
| int Parser::CompareSampleLocation( |
| const SampleInfo &sample, const MediaDataInfo &mdatInfo) { |
| if (sample.mOffset + sample.mSize < mdatInfo.mOffset) { |
| return -1; |
| } |
| |
| if (sample.mOffset >= mdatInfo.mOffset + mdatInfo.mBuffer->size()) { |
| return 1; |
| } |
| |
| // Otherwise make sure the sample is completely contained within this |
| // media data block. |
| |
| CHECK_GE(sample.mOffset, mdatInfo.mOffset); |
| |
| CHECK_LE(sample.mOffset + sample.mSize, |
| mdatInfo.mOffset + mdatInfo.mBuffer->size()); |
| |
| return 0; |
| } |
| |
| void Parser::resumeIfNecessary() { |
| if (!mSuspended) { |
| return; |
| } |
| |
| ALOGI("resuming."); |
| |
| mSuspended = false; |
| (new AMessage(kWhatProceed, id()))->post(); |
| } |
| |
| status_t Parser::getSample( |
| TrackInfo *info, sp<TrackFragment> *fragment, SampleInfo *sampleInfo) { |
| for (;;) { |
| if (info->mFragments.empty()) { |
| if (mFinalResult != OK) { |
| return mFinalResult; |
| } |
| |
| resumeIfNecessary(); |
| return -EWOULDBLOCK; |
| } |
| |
| *fragment = *info->mFragments.begin(); |
| |
| status_t err = (*fragment)->getSample(sampleInfo); |
| |
| if (err == OK) { |
| return OK; |
| } else if (err != ERROR_END_OF_STREAM) { |
| return err; |
| } |
| |
| // Really, end of this fragment... |
| |
| info->mFragments.erase(info->mFragments.begin()); |
| } |
| } |
| |
| status_t Parser::onDequeueAccessUnit( |
| size_t trackIndex, sp<ABuffer> *accessUnit) { |
| TrackInfo *info = &mTracks.editValueAt(trackIndex); |
| |
| sp<TrackFragment> fragment; |
| SampleInfo sampleInfo; |
| status_t err = getSample(info, &fragment, &sampleInfo); |
| |
| if (err == -EWOULDBLOCK) { |
| resumeIfNecessary(); |
| return err; |
| } else if (err != OK) { |
| return err; |
| } |
| |
| err = -EWOULDBLOCK; |
| |
| bool checkDroppable = false; |
| |
| for (size_t i = 0; i < mMediaData.size(); ++i) { |
| const MediaDataInfo &mdatInfo = mMediaData.itemAt(i); |
| |
| int cmp = CompareSampleLocation(sampleInfo, mdatInfo); |
| |
| if (cmp < 0) { |
| return -EPIPE; |
| } else if (cmp == 0) { |
| if (i > 0) { |
| checkDroppable = true; |
| } |
| |
| err = makeAccessUnit(info, sampleInfo, mdatInfo, accessUnit); |
| break; |
| } |
| } |
| |
| if (err != OK) { |
| return err; |
| } |
| |
| fragment->advance(); |
| |
| if (!mMediaData.empty() && checkDroppable) { |
| size_t numDroppable = 0; |
| bool done = false; |
| |
| for (size_t i = 0; !done && i < mMediaData.size(); ++i) { |
| const MediaDataInfo &mdatInfo = mMediaData.itemAt(i); |
| |
| for (size_t j = 0; j < mTracks.size(); ++j) { |
| TrackInfo *info = &mTracks.editValueAt(j); |
| |
| sp<TrackFragment> fragment; |
| SampleInfo sampleInfo; |
| err = getSample(info, &fragment, &sampleInfo); |
| |
| if (err != OK) { |
| done = true; |
| break; |
| } |
| |
| int cmp = CompareSampleLocation(sampleInfo, mdatInfo); |
| |
| if (cmp <= 0) { |
| done = true; |
| break; |
| } |
| } |
| |
| if (!done) { |
| ++numDroppable; |
| } |
| } |
| |
| if (numDroppable > 0) { |
| mMediaData.removeItemsAt(0, numDroppable); |
| |
| if (mMediaData.size() < 5) { |
| resumeIfNecessary(); |
| } |
| } |
| } |
| |
| return err; |
| } |
| |
| static size_t parseNALSize(size_t nalLengthSize, const uint8_t *data) { |
| switch (nalLengthSize) { |
| 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); |
| } |
| |
| // This cannot happen, mNALLengthSize springs to life by adding 1 to |
| // a 2-bit integer. |
| TRESPASS(); |
| |
| return 0; |
| } |
| |
| status_t Parser::makeAccessUnit( |
| TrackInfo *info, |
| const SampleInfo &sample, |
| const MediaDataInfo &mdatInfo, |
| sp<ABuffer> *accessUnit) { |
| if (sample.mSampleDescIndex < 1 |
| || sample.mSampleDescIndex > info->mSampleDescs.size()) { |
| return ERROR_MALFORMED; |
| } |
| |
| int64_t presentationTimeUs = |
| 1000000ll * sample.mPresentationTime / info->mMediaTimeScale; |
| |
| const SampleDescription &sampleDesc = |
| info->mSampleDescs.itemAt(sample.mSampleDescIndex - 1); |
| |
| size_t nalLengthSize; |
| if (!sampleDesc.mFormat->findSize("nal-length-size", &nalLengthSize)) { |
| *accessUnit = new ABuffer(sample.mSize); |
| |
| memcpy((*accessUnit)->data(), |
| mdatInfo.mBuffer->data() + (sample.mOffset - mdatInfo.mOffset), |
| sample.mSize); |
| |
| (*accessUnit)->meta()->setInt64("timeUs", presentationTimeUs); |
| return OK; |
| } |
| |
| const uint8_t *srcPtr = |
| mdatInfo.mBuffer->data() + (sample.mOffset - mdatInfo.mOffset); |
| |
| for (int i = 0; i < 2 ; ++i) { |
| size_t srcOffset = 0; |
| size_t dstOffset = 0; |
| |
| while (srcOffset < sample.mSize) { |
| if (srcOffset + nalLengthSize > sample.mSize) { |
| return ERROR_MALFORMED; |
| } |
| |
| size_t nalSize = parseNALSize(nalLengthSize, &srcPtr[srcOffset]); |
| srcOffset += nalLengthSize; |
| |
| if (srcOffset + nalSize > sample.mSize) { |
| return ERROR_MALFORMED; |
| } |
| |
| if (i == 1) { |
| memcpy((*accessUnit)->data() + dstOffset, |
| "\x00\x00\x00\x01", |
| 4); |
| |
| memcpy((*accessUnit)->data() + dstOffset + 4, |
| srcPtr + srcOffset, |
| nalSize); |
| } |
| |
| srcOffset += nalSize; |
| dstOffset += nalSize + 4; |
| } |
| |
| if (i == 0) { |
| (*accessUnit) = new ABuffer(dstOffset); |
| (*accessUnit)->meta()->setInt64( |
| "timeUs", presentationTimeUs); |
| } |
| } |
| |
| return OK; |
| } |
| |
| status_t Parser::need(size_t size) { |
| if (!fitsContainer(size)) { |
| return -EINVAL; |
| } |
| |
| if (size <= mBuffer->size()) { |
| return OK; |
| } |
| |
| sp<AMessage> msg = new AMessage(kWhatReadMore, id()); |
| msg->setSize("needed", size - mBuffer->size()); |
| msg->post(); |
| |
| // ALOGV("need(%d) returning -EAGAIN, only have %d", size, mBuffer->size()); |
| |
| return -EAGAIN; |
| } |
| |
| void Parser::enter(off64_t offset, uint32_t type, uint64_t size) { |
| Container container; |
| container.mOffset = offset; |
| container.mType = type; |
| container.mExtendsToEOF = (size == 0); |
| container.mBytesRemaining = size; |
| |
| mStack.push(container); |
| } |
| |
| bool Parser::fitsContainer(uint64_t size) const { |
| CHECK(!mStack.isEmpty()); |
| const Container &container = mStack.itemAt(mStack.size() - 1); |
| |
| return container.mExtendsToEOF || size <= container.mBytesRemaining; |
| } |
| |
| uint16_t Parser::readU16(size_t offset) { |
| CHECK_LE(offset + 2, mBuffer->size()); |
| |
| const uint8_t *ptr = mBuffer->data() + offset; |
| return (ptr[0] << 8) | ptr[1]; |
| } |
| |
| uint32_t Parser::readU32(size_t offset) { |
| CHECK_LE(offset + 4, mBuffer->size()); |
| |
| const uint8_t *ptr = mBuffer->data() + offset; |
| return (ptr[0] << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3]; |
| } |
| |
| uint64_t Parser::readU64(size_t offset) { |
| return (((uint64_t)readU32(offset)) << 32) | readU32(offset + 4); |
| } |
| |
| void Parser::skip(off_t distance) { |
| CHECK(!mStack.isEmpty()); |
| for (size_t i = mStack.size(); i-- > 0;) { |
| Container *container = &mStack.editItemAt(i); |
| if (!container->mExtendsToEOF) { |
| CHECK_LE(distance, (off_t)container->mBytesRemaining); |
| |
| container->mBytesRemaining -= distance; |
| |
| if (container->mBytesRemaining == 0) { |
| ALOGV("%sleaving box of type '%s'", |
| IndentString(mStack.size() - 1), |
| Fourcc2String(container->mType)); |
| |
| #if 0 |
| if (container->mType == FOURCC('s', 't', 's', 'd')) { |
| TrackInfo *trackInfo = editTrack(mCurrentTrackID); |
| for (size_t i = 0; |
| i < trackInfo->mSampleDescs.size(); ++i) { |
| ALOGI("format #%d: %s", |
| i, |
| trackInfo->mSampleDescs.itemAt(i) |
| .mFormat->debugString().c_str()); |
| } |
| } |
| #endif |
| |
| if (container->mType == FOURCC('s', 't', 'b', 'l')) { |
| TrackInfo *trackInfo = editTrack(mCurrentTrackID); |
| |
| trackInfo->mStaticFragment->signalCompletion(); |
| |
| CHECK(trackInfo->mFragments.empty()); |
| trackInfo->mFragments.push_back(trackInfo->mStaticFragment); |
| trackInfo->mStaticFragment.clear(); |
| } else if (container->mType == FOURCC('t', 'r', 'a', 'f')) { |
| TrackInfo *trackInfo = |
| editTrack(mTrackFragmentHeaderInfo.mTrackID); |
| |
| const sp<TrackFragment> &fragment = |
| *--trackInfo->mFragments.end(); |
| |
| static_cast<DynamicTrackFragment *>( |
| fragment.get())->signalCompletion(); |
| } |
| |
| container = NULL; |
| mStack.removeItemsAt(i); |
| } |
| } |
| } |
| |
| if (distance < (off_t)mBuffer->size()) { |
| mBuffer->setRange(mBuffer->offset() + distance, mBuffer->size() - distance); |
| mBufferPos += distance; |
| return; |
| } |
| |
| mBuffer->setRange(0, 0); |
| mBufferPos += distance; |
| } |
| |
| status_t Parser::parseTrackHeader( |
| uint32_t type, size_t offset, uint64_t size) { |
| if (offset + 4 > size) { |
| return -EINVAL; |
| } |
| |
| uint32_t flags = readU32(offset); |
| |
| uint32_t version = flags >> 24; |
| flags &= 0xffffff; |
| |
| uint32_t trackID; |
| uint64_t duration; |
| |
| if (version == 1) { |
| if (offset + 36 > size) { |
| return -EINVAL; |
| } |
| |
| trackID = readU32(offset + 20); |
| duration = readU64(offset + 28); |
| |
| offset += 36; |
| } else if (version == 0) { |
| if (offset + 24 > size) { |
| return -EINVAL; |
| } |
| |
| trackID = readU32(offset + 12); |
| duration = readU32(offset + 20); |
| |
| offset += 24; |
| } else { |
| return -EINVAL; |
| } |
| |
| TrackInfo *info = editTrack(trackID, true /* createIfNecessary */); |
| info->mFlags = flags; |
| info->mDuration = duration; |
| |
| info->mStaticFragment = new StaticTrackFragment; |
| |
| mCurrentTrackID = trackID; |
| |
| return OK; |
| } |
| |
| status_t Parser::parseMediaHeader( |
| uint32_t type, size_t offset, uint64_t size) { |
| if (offset + 4 > size) { |
| return -EINVAL; |
| } |
| |
| uint32_t versionAndFlags = readU32(offset); |
| |
| if (versionAndFlags & 0xffffff) { |
| return ERROR_MALFORMED; |
| } |
| |
| uint32_t version = versionAndFlags >> 24; |
| |
| TrackInfo *info = editTrack(mCurrentTrackID); |
| |
| if (version == 1) { |
| if (offset + 4 + 32 > size) { |
| return -EINVAL; |
| } |
| info->mMediaTimeScale = U32_AT(mBuffer->data() + offset + 20); |
| } else if (version == 0) { |
| if (offset + 4 + 20 > size) { |
| return -EINVAL; |
| } |
| info->mMediaTimeScale = U32_AT(mBuffer->data() + offset + 12); |
| } else { |
| return ERROR_MALFORMED; |
| } |
| |
| return OK; |
| } |
| |
| status_t Parser::parseMediaHandler( |
| uint32_t type, size_t offset, uint64_t size) { |
| if (offset + 12 > size) { |
| return -EINVAL; |
| } |
| |
| if (readU32(offset) != 0) { |
| return -EINVAL; |
| } |
| |
| uint32_t handlerType = readU32(offset + 8); |
| |
| switch (handlerType) { |
| case FOURCC('v', 'i', 'd', 'e'): |
| case FOURCC('s', 'o', 'u', 'n'): |
| case FOURCC('h', 'i', 'n', 't'): |
| case FOURCC('m', 'e', 't', 'a'): |
| break; |
| |
| default: |
| return -EINVAL; |
| } |
| |
| editTrack(mCurrentTrackID)->mMediaHandlerType = handlerType; |
| |
| return OK; |
| } |
| |
| status_t Parser::parseVisualSampleEntry( |
| uint32_t type, size_t offset, uint64_t size) { |
| if (offset + 78 > size) { |
| return -EINVAL; |
| } |
| |
| TrackInfo *trackInfo = editTrack(mCurrentTrackID); |
| |
| trackInfo->mSampleDescs.push(); |
| SampleDescription *sampleDesc = |
| &trackInfo->mSampleDescs.editItemAt( |
| trackInfo->mSampleDescs.size() - 1); |
| |
| sampleDesc->mType = type; |
| sampleDesc->mDataRefIndex = readU16(offset + 6); |
| |
| sp<AMessage> format = new AMessage; |
| |
| switch (type) { |
| case FOURCC('a', 'v', 'c', '1'): |
| format->setString("mime", MEDIA_MIMETYPE_VIDEO_AVC); |
| break; |
| case FOURCC('m', 'p', '4', 'v'): |
| format->setString("mime", MEDIA_MIMETYPE_VIDEO_MPEG4); |
| break; |
| case FOURCC('s', '2', '6', '3'): |
| case FOURCC('h', '2', '6', '3'): |
| case FOURCC('H', '2', '6', '3'): |
| format->setString("mime", MEDIA_MIMETYPE_VIDEO_H263); |
| break; |
| default: |
| format->setString("mime", "application/octet-stream"); |
| break; |
| } |
| |
| format->setInt32("width", readU16(offset + 8 + 16)); |
| format->setInt32("height", readU16(offset + 8 + 18)); |
| |
| sampleDesc->mFormat = format; |
| |
| return OK; |
| } |
| |
| status_t Parser::parseAudioSampleEntry( |
| uint32_t type, size_t offset, uint64_t size) { |
| if (offset + 28 > size) { |
| return -EINVAL; |
| } |
| |
| TrackInfo *trackInfo = editTrack(mCurrentTrackID); |
| |
| trackInfo->mSampleDescs.push(); |
| SampleDescription *sampleDesc = |
| &trackInfo->mSampleDescs.editItemAt( |
| trackInfo->mSampleDescs.size() - 1); |
| |
| sampleDesc->mType = type; |
| sampleDesc->mDataRefIndex = readU16(offset + 6); |
| |
| sp<AMessage> format = new AMessage; |
| |
| format->setInt32("channel-count", readU16(offset + 8 + 8)); |
| format->setInt32("sample-size", readU16(offset + 8 + 10)); |
| format->setInt32("sample-rate", readU32(offset + 8 + 16) / 65536.0f); |
| |
| switch (type) { |
| case FOURCC('m', 'p', '4', 'a'): |
| format->setString("mime", MEDIA_MIMETYPE_AUDIO_AAC); |
| break; |
| |
| case FOURCC('s', 'a', 'm', 'r'): |
| format->setString("mime", MEDIA_MIMETYPE_AUDIO_AMR_NB); |
| format->setInt32("channel-count", 1); |
| format->setInt32("sample-rate", 8000); |
| break; |
| |
| case FOURCC('s', 'a', 'w', 'b'): |
| format->setString("mime", MEDIA_MIMETYPE_AUDIO_AMR_WB); |
| format->setInt32("channel-count", 1); |
| format->setInt32("sample-rate", 16000); |
| break; |
| default: |
| format->setString("mime", "application/octet-stream"); |
| break; |
| } |
| |
| sampleDesc->mFormat = format; |
| |
| return OK; |
| } |
| |
| static void addCodecSpecificData( |
| const sp<AMessage> &format, int32_t index, |
| const void *data, size_t size, |
| bool insertStartCode = false) { |
| sp<ABuffer> csd = new ABuffer(insertStartCode ? size + 4 : size); |
| |
| memcpy(csd->data() + (insertStartCode ? 4 : 0), data, size); |
| |
| if (insertStartCode) { |
| memcpy(csd->data(), "\x00\x00\x00\x01", 4); |
| } |
| |
| csd->meta()->setInt32("csd", true); |
| csd->meta()->setInt64("timeUs", 0ll); |
| |
| format->setBuffer(StringPrintf("csd-%d", index).c_str(), csd); |
| } |
| |
| status_t Parser::parseSampleSizes( |
| uint32_t type, size_t offset, uint64_t size) { |
| return editTrack(mCurrentTrackID)->mStaticFragment->parseSampleSizes( |
| this, type, offset, size); |
| } |
| |
| status_t Parser::parseCompactSampleSizes( |
| uint32_t type, size_t offset, uint64_t size) { |
| return editTrack(mCurrentTrackID)->mStaticFragment->parseCompactSampleSizes( |
| this, type, offset, size); |
| } |
| |
| status_t Parser::parseSampleToChunk( |
| uint32_t type, size_t offset, uint64_t size) { |
| return editTrack(mCurrentTrackID)->mStaticFragment->parseSampleToChunk( |
| this, type, offset, size); |
| } |
| |
| status_t Parser::parseChunkOffsets( |
| uint32_t type, size_t offset, uint64_t size) { |
| return editTrack(mCurrentTrackID)->mStaticFragment->parseChunkOffsets( |
| this, type, offset, size); |
| } |
| |
| status_t Parser::parseChunkOffsets64( |
| uint32_t type, size_t offset, uint64_t size) { |
| return editTrack(mCurrentTrackID)->mStaticFragment->parseChunkOffsets64( |
| this, type, offset, size); |
| } |
| |
| status_t Parser::parseAVCCodecSpecificData( |
| uint32_t type, size_t offset, uint64_t size) { |
| TrackInfo *trackInfo = editTrack(mCurrentTrackID); |
| |
| SampleDescription *sampleDesc = |
| &trackInfo->mSampleDescs.editItemAt( |
| trackInfo->mSampleDescs.size() - 1); |
| |
| if (sampleDesc->mType != FOURCC('a', 'v', 'c', '1')) { |
| return -EINVAL; |
| } |
| |
| const uint8_t *ptr = mBuffer->data() + offset; |
| |
| size -= offset; |
| offset = 0; |
| |
| if (size < 7 || ptr[0] != 0x01) { |
| return ERROR_MALFORMED; |
| } |
| |
| sampleDesc->mFormat->setSize("nal-length-size", 1 + (ptr[4] & 3)); |
| |
| size_t numSPS = ptr[5] & 31; |
| |
| ptr += 6; |
| size -= 6; |
| |
| for (size_t i = 0; i < numSPS; ++i) { |
| if (size < 2) { |
| return ERROR_MALFORMED; |
| } |
| |
| size_t length = U16_AT(ptr); |
| |
| ptr += 2; |
| size -= 2; |
| |
| if (size < length) { |
| return ERROR_MALFORMED; |
| } |
| |
| addCodecSpecificData( |
| sampleDesc->mFormat, i, ptr, length, |
| true /* insertStartCode */); |
| |
| ptr += length; |
| size -= length; |
| } |
| |
| if (size < 1) { |
| return ERROR_MALFORMED; |
| } |
| |
| size_t numPPS = *ptr; |
| ++ptr; |
| --size; |
| |
| for (size_t i = 0; i < numPPS; ++i) { |
| if (size < 2) { |
| return ERROR_MALFORMED; |
| } |
| |
| size_t length = U16_AT(ptr); |
| |
| ptr += 2; |
| size -= 2; |
| |
| if (size < length) { |
| return ERROR_MALFORMED; |
| } |
| |
| addCodecSpecificData( |
| sampleDesc->mFormat, numSPS + i, ptr, length, |
| true /* insertStartCode */); |
| |
| ptr += length; |
| size -= length; |
| } |
| |
| return OK; |
| } |
| |
| status_t Parser::parseESDSCodecSpecificData( |
| uint32_t type, size_t offset, uint64_t size) { |
| TrackInfo *trackInfo = editTrack(mCurrentTrackID); |
| |
| SampleDescription *sampleDesc = |
| &trackInfo->mSampleDescs.editItemAt( |
| trackInfo->mSampleDescs.size() - 1); |
| |
| if (sampleDesc->mType != FOURCC('m', 'p', '4', 'a') |
| && sampleDesc->mType != FOURCC('m', 'p', '4', 'v')) { |
| return -EINVAL; |
| } |
| |
| const uint8_t *ptr = mBuffer->data() + offset; |
| |
| size -= offset; |
| offset = 0; |
| |
| if (size < 4) { |
| return -EINVAL; |
| } |
| |
| if (U32_AT(ptr) != 0) { |
| return -EINVAL; |
| } |
| |
| ptr += 4; |
| size -=4; |
| |
| ESDS esds(ptr, size); |
| |
| uint8_t objectTypeIndication; |
| if (esds.getObjectTypeIndication(&objectTypeIndication) != OK) { |
| return ERROR_MALFORMED; |
| } |
| |
| const uint8_t *csd; |
| size_t csd_size; |
| if (esds.getCodecSpecificInfo( |
| (const void **)&csd, &csd_size) != OK) { |
| return ERROR_MALFORMED; |
| } |
| |
| addCodecSpecificData(sampleDesc->mFormat, 0, csd, csd_size); |
| |
| if (sampleDesc->mType != FOURCC('m', 'p', '4', 'a')) { |
| return OK; |
| } |
| |
| if (csd_size == 0) { |
| // There's no further information, i.e. no codec specific data |
| // Let's assume that the information provided in the mpeg4 headers |
| // is accurate and hope for the best. |
| |
| return OK; |
| } |
| |
| if (csd_size < 2) { |
| return ERROR_MALFORMED; |
| } |
| |
| uint32_t objectType = csd[0] >> 3; |
| |
| if (objectType == 31) { |
| return ERROR_UNSUPPORTED; |
| } |
| |
| uint32_t freqIndex = (csd[0] & 7) << 1 | (csd[1] >> 7); |
| int32_t sampleRate = 0; |
| int32_t numChannels = 0; |
| if (freqIndex == 15) { |
| if (csd_size < 5) { |
| return ERROR_MALFORMED; |
| } |
| |
| sampleRate = (csd[1] & 0x7f) << 17 |
| | csd[2] << 9 |
| | csd[3] << 1 |
| | (csd[4] >> 7); |
| |
| numChannels = (csd[4] >> 3) & 15; |
| } else { |
| static uint32_t kSamplingRate[] = { |
| 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, |
| 16000, 12000, 11025, 8000, 7350 |
| }; |
| |
| if (freqIndex == 13 || freqIndex == 14) { |
| return ERROR_MALFORMED; |
| } |
| |
| sampleRate = kSamplingRate[freqIndex]; |
| numChannels = (csd[1] >> 3) & 15; |
| } |
| |
| if (numChannels == 0) { |
| return ERROR_UNSUPPORTED; |
| } |
| |
| sampleDesc->mFormat->setInt32("sample-rate", sampleRate); |
| sampleDesc->mFormat->setInt32("channel-count", numChannels); |
| |
| return OK; |
| } |
| |
| status_t Parser::parseMediaData( |
| uint32_t type, size_t offset, uint64_t size) { |
| ALOGV("skipping 'mdat' chunk at offsets 0x%08lx-0x%08llx.", |
| mBufferPos + offset, mBufferPos + size); |
| |
| sp<ABuffer> buffer = new ABuffer(size - offset); |
| memcpy(buffer->data(), mBuffer->data() + offset, size - offset); |
| |
| mMediaData.push(); |
| MediaDataInfo *info = &mMediaData.editItemAt(mMediaData.size() - 1); |
| info->mBuffer = buffer; |
| info->mOffset = mBufferPos + offset; |
| |
| if (mMediaData.size() > 10) { |
| ALOGI("suspending for now."); |
| mSuspended = true; |
| } |
| |
| return OK; |
| } |
| |
| status_t Parser::parseTrackExtends( |
| uint32_t type, size_t offset, uint64_t size) { |
| if (offset + 24 > size) { |
| return -EINVAL; |
| } |
| |
| if (readU32(offset) != 0) { |
| return -EINVAL; |
| } |
| |
| uint32_t trackID = readU32(offset + 4); |
| |
| TrackInfo *info = editTrack(trackID, true /* createIfNecessary */); |
| info->mDefaultSampleDescriptionIndex = readU32(offset + 8); |
| info->mDefaultSampleDuration = readU32(offset + 12); |
| info->mDefaultSampleSize = readU32(offset + 16); |
| info->mDefaultSampleFlags = readU32(offset + 20); |
| |
| return OK; |
| } |
| |
| Parser::TrackInfo *Parser::editTrack( |
| uint32_t trackID, bool createIfNecessary) { |
| ssize_t i = mTracks.indexOfKey(trackID); |
| |
| if (i >= 0) { |
| return &mTracks.editValueAt(i); |
| } |
| |
| if (!createIfNecessary) { |
| return NULL; |
| } |
| |
| TrackInfo info; |
| info.mTrackID = trackID; |
| info.mFlags = 0; |
| info.mDuration = 0xffffffff; |
| info.mMediaTimeScale = 0; |
| info.mMediaHandlerType = 0; |
| info.mDefaultSampleDescriptionIndex = 0; |
| info.mDefaultSampleDuration = 0; |
| info.mDefaultSampleSize = 0; |
| info.mDefaultSampleFlags = 0; |
| |
| info.mDecodingTime = 0; |
| |
| mTracks.add(trackID, info); |
| return &mTracks.editValueAt(mTracks.indexOfKey(trackID)); |
| } |
| |
| status_t Parser::parseTrackFragmentHeader( |
| uint32_t type, size_t offset, uint64_t size) { |
| if (offset + 8 > size) { |
| return -EINVAL; |
| } |
| |
| uint32_t flags = readU32(offset); |
| |
| if (flags & 0xff000000) { |
| return -EINVAL; |
| } |
| |
| mTrackFragmentHeaderInfo.mFlags = flags; |
| |
| mTrackFragmentHeaderInfo.mTrackID = readU32(offset + 4); |
| offset += 8; |
| |
| if (flags & TrackFragmentHeaderInfo::kBaseDataOffsetPresent) { |
| if (offset + 8 > size) { |
| return -EINVAL; |
| } |
| |
| mTrackFragmentHeaderInfo.mBaseDataOffset = readU64(offset); |
| offset += 8; |
| } |
| |
| if (flags & TrackFragmentHeaderInfo::kSampleDescriptionIndexPresent) { |
| if (offset + 4 > size) { |
| return -EINVAL; |
| } |
| |
| mTrackFragmentHeaderInfo.mSampleDescriptionIndex = readU32(offset); |
| offset += 4; |
| } |
| |
| if (flags & TrackFragmentHeaderInfo::kDefaultSampleDurationPresent) { |
| if (offset + 4 > size) { |
| return -EINVAL; |
| } |
| |
| mTrackFragmentHeaderInfo.mDefaultSampleDuration = readU32(offset); |
| offset += 4; |
| } |
| |
| if (flags & TrackFragmentHeaderInfo::kDefaultSampleSizePresent) { |
| if (offset + 4 > size) { |
| return -EINVAL; |
| } |
| |
| mTrackFragmentHeaderInfo.mDefaultSampleSize = readU32(offset); |
| offset += 4; |
| } |
| |
| if (flags & TrackFragmentHeaderInfo::kDefaultSampleFlagsPresent) { |
| if (offset + 4 > size) { |
| return -EINVAL; |
| } |
| |
| mTrackFragmentHeaderInfo.mDefaultSampleFlags = readU32(offset); |
| offset += 4; |
| } |
| |
| if (!(flags & TrackFragmentHeaderInfo::kBaseDataOffsetPresent)) { |
| // This should point to the position of the first byte of the |
| // enclosing 'moof' container for the first track and |
| // the end of the data of the preceding fragment for subsequent |
| // tracks. |
| |
| CHECK_GE(mStack.size(), 2u); |
| |
| mTrackFragmentHeaderInfo.mBaseDataOffset = |
| mStack.itemAt(mStack.size() - 2).mOffset; |
| |
| // XXX TODO: This does not do the right thing for the 2nd and |
| // subsequent tracks yet. |
| } |
| |
| mTrackFragmentHeaderInfo.mDataOffset = |
| mTrackFragmentHeaderInfo.mBaseDataOffset; |
| |
| TrackInfo *trackInfo = editTrack(mTrackFragmentHeaderInfo.mTrackID); |
| |
| if (trackInfo->mFragments.empty() |
| || (*trackInfo->mFragments.begin())->complete()) { |
| trackInfo->mFragments.push_back(new DynamicTrackFragment); |
| } |
| |
| return OK; |
| } |
| |
| status_t Parser::parseTrackFragmentRun( |
| uint32_t type, size_t offset, uint64_t size) { |
| if (offset + 8 > size) { |
| return -EINVAL; |
| } |
| |
| enum { |
| kDataOffsetPresent = 0x01, |
| kFirstSampleFlagsPresent = 0x04, |
| kSampleDurationPresent = 0x100, |
| kSampleSizePresent = 0x200, |
| kSampleFlagsPresent = 0x400, |
| kSampleCompositionTimeOffsetPresent = 0x800, |
| }; |
| |
| uint32_t flags = readU32(offset); |
| |
| if (flags & 0xff000000) { |
| return -EINVAL; |
| } |
| |
| if ((flags & kFirstSampleFlagsPresent) && (flags & kSampleFlagsPresent)) { |
| // These two shall not be used together. |
| return -EINVAL; |
| } |
| |
| uint32_t sampleCount = readU32(offset + 4); |
| offset += 8; |
| |
| uint64_t dataOffset = mTrackFragmentHeaderInfo.mDataOffset; |
| |
| uint32_t firstSampleFlags = 0; |
| |
| if (flags & kDataOffsetPresent) { |
| if (offset + 4 > size) { |
| return -EINVAL; |
| } |
| |
| int32_t dataOffsetDelta = (int32_t)readU32(offset); |
| |
| dataOffset = mTrackFragmentHeaderInfo.mBaseDataOffset + dataOffsetDelta; |
| |
| offset += 4; |
| } |
| |
| if (flags & kFirstSampleFlagsPresent) { |
| if (offset + 4 > size) { |
| return -EINVAL; |
| } |
| |
| firstSampleFlags = readU32(offset); |
| offset += 4; |
| } |
| |
| TrackInfo *info = editTrack(mTrackFragmentHeaderInfo.mTrackID); |
| |
| if (info == NULL) { |
| return -EINVAL; |
| } |
| |
| uint32_t sampleDuration = 0, sampleSize = 0, sampleFlags = 0, |
| sampleCtsOffset = 0; |
| |
| size_t bytesPerSample = 0; |
| if (flags & kSampleDurationPresent) { |
| bytesPerSample += 4; |
| } else if (mTrackFragmentHeaderInfo.mFlags |
| & TrackFragmentHeaderInfo::kDefaultSampleDurationPresent) { |
| sampleDuration = mTrackFragmentHeaderInfo.mDefaultSampleDuration; |
| } else { |
| sampleDuration = info->mDefaultSampleDuration; |
| } |
| |
| if (flags & kSampleSizePresent) { |
| bytesPerSample += 4; |
| } else if (mTrackFragmentHeaderInfo.mFlags |
| & TrackFragmentHeaderInfo::kDefaultSampleSizePresent) { |
| sampleSize = mTrackFragmentHeaderInfo.mDefaultSampleSize; |
| } else { |
| sampleSize = info->mDefaultSampleSize; |
| } |
| |
| if (flags & kSampleFlagsPresent) { |
| bytesPerSample += 4; |
| } else if (mTrackFragmentHeaderInfo.mFlags |
| & TrackFragmentHeaderInfo::kDefaultSampleFlagsPresent) { |
| sampleFlags = mTrackFragmentHeaderInfo.mDefaultSampleFlags; |
| } else { |
| sampleFlags = info->mDefaultSampleFlags; |
| } |
| |
| if (flags & kSampleCompositionTimeOffsetPresent) { |
| bytesPerSample += 4; |
| } else { |
| sampleCtsOffset = 0; |
| } |
| |
| if (offset + sampleCount * bytesPerSample > size) { |
| return -EINVAL; |
| } |
| |
| uint32_t sampleDescIndex = |
| (mTrackFragmentHeaderInfo.mFlags |
| & TrackFragmentHeaderInfo::kSampleDescriptionIndexPresent) |
| ? mTrackFragmentHeaderInfo.mSampleDescriptionIndex |
| : info->mDefaultSampleDescriptionIndex; |
| |
| for (uint32_t i = 0; i < sampleCount; ++i) { |
| if (flags & kSampleDurationPresent) { |
| sampleDuration = readU32(offset); |
| offset += 4; |
| } |
| |
| if (flags & kSampleSizePresent) { |
| sampleSize = readU32(offset); |
| offset += 4; |
| } |
| |
| if (flags & kSampleFlagsPresent) { |
| sampleFlags = readU32(offset); |
| offset += 4; |
| } |
| |
| if (flags & kSampleCompositionTimeOffsetPresent) { |
| sampleCtsOffset = readU32(offset); |
| offset += 4; |
| } |
| |
| ALOGV("adding sample at offset 0x%08llx, size %u, duration %u, " |
| "sampleDescIndex=%u, flags 0x%08x", |
| dataOffset, sampleSize, sampleDuration, |
| sampleDescIndex, |
| (flags & kFirstSampleFlagsPresent) && i == 0 |
| ? firstSampleFlags : sampleFlags); |
| |
| const sp<TrackFragment> &fragment = *--info->mFragments.end(); |
| |
| uint32_t decodingTime = info->mDecodingTime; |
| info->mDecodingTime += sampleDuration; |
| uint32_t presentationTime = decodingTime + sampleCtsOffset; |
| |
| static_cast<DynamicTrackFragment *>( |
| fragment.get())->addSample( |
| dataOffset, |
| sampleSize, |
| presentationTime, |
| sampleDescIndex, |
| ((flags & kFirstSampleFlagsPresent) && i == 0) |
| ? firstSampleFlags : sampleFlags); |
| |
| dataOffset += sampleSize; |
| } |
| |
| mTrackFragmentHeaderInfo.mDataOffset = dataOffset; |
| |
| return OK; |
| } |
| |
| void Parser::copyBuffer( |
| sp<ABuffer> *dst, size_t offset, uint64_t size, size_t extra) const { |
| sp<ABuffer> buf = new ABuffer(size + extra); |
| memcpy(buf->data(), mBuffer->data() + offset, size); |
| |
| *dst = buf; |
| } |
| |
| } // namespace android |