/* * 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 #include "Parser.h" #include "TrackFragment.h" #include "ESDS.h" #include #include #include #include #include #include #include #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 msg = new AMessage(kWhatStart, id()); msg->setObject("source", new FileSource(filename)); msg->post(); } void Parser::start(const sp &source) { sp msg = new AMessage(kWhatStart, id()); msg->setObject("source", source); msg->post(); } sp Parser::getFormat(bool audio) { sp msg = new AMessage(kWhatGetFormat, id()); msg->setInt32("audio", audio); sp response; status_t err = msg->postAndAwaitResponse(&response); if (err != OK) { return NULL; } if (response->findInt32("err", &err) && err != OK) { return NULL; } sp format; CHECK(response->findMessage("format", &format)); ALOGV("returning format %s", format->debugString().c_str()); return format; } status_t Parser::dequeueAccessUnit(bool audio, sp *accessUnit) { sp msg = new AMessage(kWhatDequeueAccessUnit, id()); msg->setInt32("audio", audio); sp 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 &msg) { switch (msg->what()) { case kWhatStart: { sp obj; CHECK(msg->findObject("source", &obj)); mSource = static_cast(obj.get()); mBuffer = new ABuffer(512 * 1024); mBuffer->setRange(0, 0); enter(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 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 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 response = new AMessage; ssize_t trackIndex = findTrack(wantAudio); if (trackIndex < 0) { err = trackIndex; } else { sp 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(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 *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 *accessUnit) { TrackInfo *info = &mTracks.editValueAt(trackIndex); sp 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 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 *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 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(uint32_t type, uint64_t size) { Container container; 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 &fragment = *--trackInfo->mFragments.end(); static_cast( 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 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 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 &format, int32_t index, const void *data, size_t size, bool insertStartCode = false) { sp 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 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)) { CHECK(!mStack.isEmpty()); // This should point to the start of the data inside the 'mdat' box // following the current 'moof' box. mTrackFragmentHeaderInfo.mBaseDataOffset = mBufferPos + mStack.itemAt(mStack.size() - 1).mBytesRemaining + 8; } 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 &fragment = *--info->mFragments.end(); uint32_t decodingTime = info->mDecodingTime; info->mDecodingTime += sampleDuration; uint32_t presentationTime = decodingTime + sampleCtsOffset; static_cast( fragment.get())->addSample( dataOffset, sampleSize, presentationTime, sampleDescIndex, ((flags & kFirstSampleFlagsPresent) && i == 0) ? firstSampleFlags : sampleFlags); dataOffset += sampleSize; } mTrackFragmentHeaderInfo.mDataOffset = dataOffset; return OK; } void Parser::copyBuffer( sp *dst, size_t offset, uint64_t size, size_t extra) const { sp buf = new ABuffer(size + extra); memcpy(buf->data(), mBuffer->data() + offset, size); *dst = buf; } } // namespace android