diff options
Diffstat (limited to 'media/libstagefright/MPEG4Writer.cpp')
-rwxr-xr-x | media/libstagefright/MPEG4Writer.cpp | 461 |
1 files changed, 259 insertions, 202 deletions
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp index 6108298..8b52e15 100755 --- a/media/libstagefright/MPEG4Writer.cpp +++ b/media/libstagefright/MPEG4Writer.cpp @@ -42,6 +42,7 @@ namespace android { +static const int64_t kMinStreamableFileSizeInBytes = 5 * 1024 * 1024; static const int64_t kMax32BitFileSize = 0x007fffffffLL; static const uint8_t kNalUnitTypeSeqParamSet = 0x07; static const uint8_t kNalUnitTypePicParamSet = 0x08; @@ -75,6 +76,128 @@ private: kSampleArraySize = 1000, }; + // A helper class to handle faster write box with table entries + template<class TYPE> + struct ListTableEntries { + ListTableEntries(uint32_t elementCapacity, uint32_t entryCapacity) + : mElementCapacity(elementCapacity), + mEntryCapacity(entryCapacity), + mTotalNumTableEntries(0), + mNumValuesInCurrEntry(0), + mCurrTableEntriesElement(NULL) { + CHECK_GT(mElementCapacity, 0); + CHECK_GT(mEntryCapacity, 0); + } + + // Free the allocated memory. + ~ListTableEntries() { + while (!mTableEntryList.empty()) { + typename List<TYPE *>::iterator it = mTableEntryList.begin(); + delete[] (*it); + mTableEntryList.erase(it); + } + } + + // Replace the value at the given position by the given value. + // There must be an existing value at the given position. + // @arg value must be in network byte order + // @arg pos location the value must be in. + void set(const TYPE& value, uint32_t pos) { + CHECK_LT(pos, mTotalNumTableEntries * mEntryCapacity); + + typename List<TYPE *>::iterator it = mTableEntryList.begin(); + uint32_t iterations = (pos / (mElementCapacity * mEntryCapacity)); + while (it != mTableEntryList.end() && iterations > 0) { + ++it; + --iterations; + } + CHECK(it != mTableEntryList.end()); + CHECK_EQ(iterations, 0); + + (*it)[(pos % (mElementCapacity * mEntryCapacity))] = value; + } + + // Get the value at the given position by the given value. + // @arg value the retrieved value at the position in network byte order. + // @arg pos location the value must be in. + // @return true if a value is found. + bool get(TYPE& value, uint32_t pos) const { + if (pos >= mTotalNumTableEntries * mEntryCapacity) { + return false; + } + + typename List<TYPE *>::iterator it = mTableEntryList.begin(); + uint32_t iterations = (pos / (mElementCapacity * mEntryCapacity)); + while (it != mTableEntryList.end() && iterations > 0) { + ++it; + --iterations; + } + CHECK(it != mTableEntryList.end()); + CHECK_EQ(iterations, 0); + + value = (*it)[(pos % (mElementCapacity * mEntryCapacity))]; + return true; + } + + // Store a single value. + // @arg value must be in network byte order. + void add(const TYPE& value) { + CHECK_LT(mNumValuesInCurrEntry, mElementCapacity); + uint32_t nEntries = mTotalNumTableEntries % mElementCapacity; + uint32_t nValues = mNumValuesInCurrEntry % mEntryCapacity; + if (nEntries == 0 && nValues == 0) { + mCurrTableEntriesElement = new TYPE[mEntryCapacity * mElementCapacity]; + CHECK(mCurrTableEntriesElement != NULL); + mTableEntryList.push_back(mCurrTableEntriesElement); + } + + uint32_t pos = nEntries * mEntryCapacity + nValues; + mCurrTableEntriesElement[pos] = value; + + ++mNumValuesInCurrEntry; + if ((mNumValuesInCurrEntry % mEntryCapacity) == 0) { + ++mTotalNumTableEntries; + mNumValuesInCurrEntry = 0; + } + } + + // Write out the table entries: + // 1. the number of entries goes first + // 2. followed by the values in the table enties in order + // @arg writer the writer to actual write to the storage + void write(MPEG4Writer *writer) const { + CHECK_EQ(mNumValuesInCurrEntry % mEntryCapacity, 0); + uint32_t nEntries = mTotalNumTableEntries; + writer->writeInt32(nEntries); + for (typename List<TYPE *>::iterator it = mTableEntryList.begin(); + it != mTableEntryList.end(); ++it) { + CHECK_GT(nEntries, 0); + if (nEntries >= mElementCapacity) { + writer->write(*it, sizeof(TYPE) * mEntryCapacity, mElementCapacity); + nEntries -= mElementCapacity; + } else { + writer->write(*it, sizeof(TYPE) * mEntryCapacity, nEntries); + break; + } + } + } + + // Return the number of entries in the table. + uint32_t count() const { return mTotalNumTableEntries; } + + private: + uint32_t mElementCapacity; // # entries in an element + uint32_t mEntryCapacity; // # of values in each entry + uint32_t mTotalNumTableEntries; + uint32_t mNumValuesInCurrEntry; // up to mEntryCapacity + TYPE *mCurrTableEntriesElement; + mutable List<TYPE *> mTableEntryList; + + DISALLOW_EVIL_CONSTRUCTORS(ListTableEntries); + }; + + + MPEG4Writer *mOwner; sp<MetaData> mMeta; sp<MediaSource> mSource; @@ -90,67 +213,25 @@ private: int64_t mMaxChunkDurationUs; bool mIsRealTimeRecording; - int64_t mMaxTimeStampUs; int64_t mEstimatedTrackSizeBytes; int64_t mMdatSizeBytes; int32_t mTimeScale; pthread_t mThread; - /* - * mNumSamples is used to track the total number of samples in - * mSampleSizes List. - * - * A linked list of fixed sized array is used here to reduce the time - * to write out stsz box. - */ - uint32_t mNumSamples; - uint32_t* mCurrentSampleSizeArr; - List<uint32_t *> mSampleSizes; - bool mSamplesHaveSameSize; List<MediaBuffer *> mChunkSamples; - size_t mNumStcoTableEntries; - List<off64_t> mChunkOffsets; - - size_t mNumStscTableEntries; - struct StscTableEntry { - - StscTableEntry(uint32_t chunk, uint32_t samples, uint32_t id) - : firstChunk(chunk), - samplesPerChunk(samples), - sampleDescriptionId(id) {} - - uint32_t firstChunk; - uint32_t samplesPerChunk; - uint32_t sampleDescriptionId; - }; - List<StscTableEntry> mStscTableEntries; - - size_t mNumStssTableEntries; - List<int32_t> mStssTableEntries; - - struct SttsTableEntry { - - SttsTableEntry(uint32_t count, uint32_t duration) - : sampleCount(count), sampleDuration(duration) {} - - uint32_t sampleCount; - uint32_t sampleDuration; // time scale based - }; - size_t mNumSttsTableEntries; - List<SttsTableEntry> mSttsTableEntries; + bool mSamplesHaveSameSize; + ListTableEntries<uint32_t> *mStszTableEntries; - struct CttsTableEntry { - CttsTableEntry(uint32_t count, int32_t timescaledDur) - : sampleCount(count), sampleDuration(timescaledDur) {} + ListTableEntries<uint32_t> *mStcoTableEntries; + ListTableEntries<off64_t> *mCo64TableEntries; + ListTableEntries<uint32_t> *mStscTableEntries; + ListTableEntries<uint32_t> *mStssTableEntries; + ListTableEntries<uint32_t> *mSttsTableEntries; + ListTableEntries<uint32_t> *mCttsTableEntries; - uint32_t sampleCount; - uint32_t sampleDuration; // time scale based - }; - size_t mNumCttsTableEntries; - List<CttsTableEntry> mCttsTableEntries; int64_t mMinCttsOffsetTimeUs; int64_t mMaxCttsOffsetTimeUs; @@ -269,7 +350,7 @@ MPEG4Writer::MPEG4Writer(const char *filename) mAreGeoTagsAvailable(false), mStartTimeOffsetMs(-1) { - mFd = open(filename, O_CREAT | O_LARGEFILE | O_TRUNC | O_RDWR); + mFd = open(filename, O_CREAT | O_LARGEFILE | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR); if (mFd >= 0) { mInitCheck = OK; } @@ -333,6 +414,10 @@ status_t MPEG4Writer::Track::dump( snprintf(buffer, SIZE, " reached EOS: %s\n", mReachedEOS? "true": "false"); result.append(buffer); + snprintf(buffer, SIZE, " frames encoded : %d\n", mStszTableEntries->count()); + result.append(buffer); + snprintf(buffer, SIZE, " duration encoded : %lld us\n", mTrackDurationUs); + result.append(buffer); ::write(fd, result.string(), result.size()); return OK; } @@ -343,7 +428,7 @@ status_t MPEG4Writer::addSource(const sp<MediaSource> &source) { ALOGE("Attempt to add source AFTER recording is started"); return UNKNOWN_ERROR; } - Track *track = new Track(this, source, mTracks.size()); + Track *track = new Track(this, source, 1 + mTracks.size()); mTracks.push_back(track); return OK; @@ -487,8 +572,16 @@ status_t MPEG4Writer::start(MetaData *param) { CHECK_GT(mTimeScale, 0); ALOGV("movie time scale: %d", mTimeScale); - mStreamableFile = true; - mWriteMoovBoxToMemory = false; + /* + * When the requested file size limit is small, the priority + * is to meet the file size limit requirement, rather than + * to make the file streamable. + */ + mStreamableFile = + (mMaxFileSizeLimitBytes != 0 && + mMaxFileSizeLimitBytes >= kMinStreamableFileSizeInBytes); + + mWriteMoovBoxToMemory = mStreamableFile; mMoovBoxBuffer = NULL; mMoovBoxBufferOffset = 0; @@ -504,11 +597,16 @@ status_t MPEG4Writer::start(MetaData *param) { mEstimatedMoovBoxSize = estimateMoovBoxSize(bitRate); } CHECK_GE(mEstimatedMoovBoxSize, 8); - lseek64(mFd, mFreeBoxOffset, SEEK_SET); - writeInt32(mEstimatedMoovBoxSize); - write("free", 4); + if (mStreamableFile) { + // Reserve a 'free' box only for streamable file + lseek64(mFd, mFreeBoxOffset, SEEK_SET); + writeInt32(mEstimatedMoovBoxSize); + write("free", 4); + mMdatOffset = mFreeBoxOffset + mEstimatedMoovBoxSize; + } else { + mMdatOffset = mOffset; + } - mMdatOffset = mFreeBoxOffset + mEstimatedMoovBoxSize; mOffset = mMdatOffset; lseek64(mFd, mMdatOffset, SEEK_SET); if (mUse32BitOffset) { @@ -689,7 +787,7 @@ status_t MPEG4Writer::reset() { lseek64(mFd, mOffset, SEEK_SET); const off64_t moovOffset = mOffset; - mWriteMoovBoxToMemory = true; + mWriteMoovBoxToMemory = mStreamableFile; mMoovBoxBuffer = (uint8_t *) malloc(mEstimatedMoovBoxSize); mMoovBoxBufferOffset = 0; CHECK(mMoovBoxBuffer != NULL); @@ -1071,6 +1169,10 @@ bool MPEG4Writer::exceedsFileSizeLimit() { nTotalBytesEstimate += (*it)->getEstimatedTrackSizeBytes(); } + if (!mStreamableFile) { + // Add 1024 bytes as error tolerance + return nTotalBytesEstimate + 1024 >= mMaxFileSizeLimitBytes; + } // Be conservative in the estimate: do not exceed 95% of // the target file limit. For small target file size limit, though, // this will not help. @@ -1140,6 +1242,13 @@ MPEG4Writer::Track::Track( mTrackDurationUs(0), mEstimatedTrackSizeBytes(0), mSamplesHaveSameSize(true), + mStszTableEntries(new ListTableEntries<uint32_t>(1000, 1)), + mStcoTableEntries(new ListTableEntries<uint32_t>(1000, 1)), + mCo64TableEntries(new ListTableEntries<off64_t>(1000, 1)), + mStscTableEntries(new ListTableEntries<uint32_t>(1000, 3)), + mStssTableEntries(new ListTableEntries<uint32_t>(1000, 1)), + mSttsTableEntries(new ListTableEntries<uint32_t>(1000, 2)), + mCttsTableEntries(new ListTableEntries<uint32_t>(1000, 2)), mCodecSpecificData(NULL), mCodecSpecificDataSize(0), mGotAllCodecSpecificData(false), @@ -1159,20 +1268,20 @@ MPEG4Writer::Track::Track( void MPEG4Writer::Track::updateTrackSizeEstimate() { - int64_t stcoBoxSizeBytes = mOwner->use32BitFileOffset() - ? mNumStcoTableEntries * 4 - : mNumStcoTableEntries * 8; - - int64_t stszBoxSizeBytes = mSamplesHaveSameSize? 4: (mNumSamples * 4); + uint32_t stcoBoxCount = (mOwner->use32BitFileOffset() + ? mStcoTableEntries->count() + : mCo64TableEntries->count()); + int64_t stcoBoxSizeBytes = stcoBoxCount * 4; + int64_t stszBoxSizeBytes = mSamplesHaveSameSize? 4: (mStszTableEntries->count() * 4); mEstimatedTrackSizeBytes = mMdatSizeBytes; // media data size if (!mOwner->isFileStreamable()) { // Reserved free space is not large enough to hold // all meta data and thus wasted. - mEstimatedTrackSizeBytes += mNumStscTableEntries * 12 + // stsc box size - mNumStssTableEntries * 4 + // stss box size - mNumSttsTableEntries * 8 + // stts box size - mNumCttsTableEntries * 8 + // ctts box size + mEstimatedTrackSizeBytes += mStscTableEntries->count() * 12 + // stsc box size + mStssTableEntries->count() * 4 + // stss box size + mSttsTableEntries->count() * 8 + // stts box size + mCttsTableEntries->count() * 8 + // ctts box size stcoBoxSizeBytes + // stco box size stszBoxSizeBytes; // stsz box size } @@ -1181,14 +1290,13 @@ void MPEG4Writer::Track::updateTrackSizeEstimate() { void MPEG4Writer::Track::addOneStscTableEntry( size_t chunkId, size_t sampleId) { - StscTableEntry stscEntry(chunkId, sampleId, 1); - mStscTableEntries.push_back(stscEntry); - ++mNumStscTableEntries; + mStscTableEntries->add(htonl(chunkId)); + mStscTableEntries->add(htonl(sampleId)); + mStscTableEntries->add(htonl(1)); } void MPEG4Writer::Track::addOneStssTableEntry(size_t sampleId) { - mStssTableEntries.push_back(sampleId); - ++mNumStssTableEntries; + mStssTableEntries->add(htonl(sampleId)); } void MPEG4Writer::Track::addOneSttsTableEntry( @@ -1197,9 +1305,8 @@ void MPEG4Writer::Track::addOneSttsTableEntry( if (duration == 0) { ALOGW("0-duration samples found: %d", sampleCount); } - SttsTableEntry sttsEntry(sampleCount, duration); - mSttsTableEntries.push_back(sttsEntry); - ++mNumSttsTableEntries; + mSttsTableEntries->add(htonl(sampleCount)); + mSttsTableEntries->add(htonl(duration)); } void MPEG4Writer::Track::addOneCttsTableEntry( @@ -1208,14 +1315,17 @@ void MPEG4Writer::Track::addOneCttsTableEntry( if (mIsAudio) { return; } - CttsTableEntry cttsEntry(sampleCount, duration); - mCttsTableEntries.push_back(cttsEntry); - ++mNumCttsTableEntries; + mCttsTableEntries->add(htonl(sampleCount)); + mCttsTableEntries->add(htonl(duration)); } void MPEG4Writer::Track::addChunkOffset(off64_t offset) { - ++mNumStcoTableEntries; - mChunkOffsets.push_back(offset); + if (mOwner->use32BitFileOffset()) { + uint32_t value = offset; + mStcoTableEntries->add(htonl(value)); + } else { + mCo64TableEntries->add(hton64(offset)); + } } void MPEG4Writer::Track::setTimeScale() { @@ -1274,16 +1384,26 @@ void MPEG4Writer::Track::getCodecSpecificDataFromInputFormatIfPossible() { MPEG4Writer::Track::~Track() { stop(); + delete mStszTableEntries; + delete mStcoTableEntries; + delete mCo64TableEntries; + delete mStscTableEntries; + delete mSttsTableEntries; + delete mStssTableEntries; + delete mCttsTableEntries; + + mStszTableEntries = NULL; + mStcoTableEntries = NULL; + mCo64TableEntries = NULL; + mStscTableEntries = NULL; + mSttsTableEntries = NULL; + mStssTableEntries = NULL; + mCttsTableEntries = NULL; + if (mCodecSpecificData != NULL) { free(mCodecSpecificData); mCodecSpecificData = NULL; } - - while (!mSampleSizes.empty()) { - List<uint32_t *>::iterator it = mSampleSizes.begin(); - delete[] (*it); - mSampleSizes.erase(it); - } } void MPEG4Writer::Track::initTrackingProgressStatus(MetaData *params) { @@ -1526,13 +1646,7 @@ status_t MPEG4Writer::Track::start(MetaData *params) { mTrackDurationUs = 0; mReachedEOS = false; mEstimatedTrackSizeBytes = 0; - mNumStcoTableEntries = 0; - mNumStssTableEntries = 0; - mNumStscTableEntries = 0; - mNumSttsTableEntries = 0; - mNumCttsTableEntries = 0; mMdatSizeBytes = 0; - mMaxChunkDurationUs = 0; pthread_create(&mThread, &attr, ThreadWrapper, this); @@ -1868,6 +1982,7 @@ status_t MPEG4Writer::Track::threadEntry() { int64_t currCttsOffsetTimeTicks = 0; // Timescale based ticks int64_t lastCttsOffsetTimeTicks = -1; // Timescale based ticks int32_t cttsSampleCount = 0; // Sample count in the current ctts table entry + uint32_t lastSamplesPerChunk = 0; if (mIsAudio) { prctl(PR_SET_NAME, (unsigned long)"AudioTrackEncoding", 0, 0, 0); @@ -1878,7 +1993,6 @@ status_t MPEG4Writer::Track::threadEntry() { sp<MetaData> meta_data; - mNumSamples = 0; status_t err = OK; MediaBuffer *buffer; while (!mDone && (err = mSource->read(&buffer)) == OK) { @@ -1967,7 +2081,7 @@ status_t MPEG4Writer::Track::threadEntry() { CHECK(meta_data->findInt64(kKeyTime, ×tampUs)); //////////////////////////////////////////////////////////////////////////////// - if (mNumSamples == 0) { + if (mStszTableEntries->count() == 0) { mFirstSampleTimeRealUs = systemTime() / 1000; mStartTimestampUs = timestampUs; mOwner->setStartTimestampUs(mStartTimestampUs); @@ -2005,7 +2119,7 @@ status_t MPEG4Writer::Track::threadEntry() { currCttsOffsetTimeTicks = (cttsOffsetTimeUs * mTimeScale + 500000LL) / 1000000LL; CHECK_LE(currCttsOffsetTimeTicks, 0x0FFFFFFFFLL); - if (mNumSamples == 0) { + if (mStszTableEntries->count() == 0) { // Force the first ctts table entry to have one single entry // so that we can do adjustment for the initial track start // time offset easily in writeCttsBox(). @@ -2023,7 +2137,7 @@ status_t MPEG4Writer::Track::threadEntry() { } // Update ctts time offset range - if (mNumSamples == 0) { + if (mStszTableEntries->count() == 0) { mMinCttsOffsetTimeUs = currCttsOffsetTimeTicks; mMaxCttsOffsetTimeUs = currCttsOffsetTimeTicks; } else { @@ -2057,22 +2171,18 @@ status_t MPEG4Writer::Track::threadEntry() { currDurationTicks = ((timestampUs * mTimeScale + 500000LL) / 1000000LL - (lastTimestampUs * mTimeScale + 500000LL) / 1000000LL); - CHECK_GE(currDurationTicks, 0ll); - - if ((mNumSamples % kSampleArraySize) == 0) { - uint32_t *arr = new uint32_t[kSampleArraySize]; - CHECK(arr != NULL); - mSampleSizes.push_back(arr); - mCurrentSampleSizeArr = arr; + if (currDurationTicks < 0ll) { + ALOGE("timestampUs %lld < lastTimestampUs %lld for %s track", + timestampUs, lastTimestampUs, mIsAudio? "Audio": "Video"); + return UNKNOWN_ERROR; } - mCurrentSampleSizeArr[mNumSamples % kSampleArraySize] = htonl(sampleSize); - ++mNumSamples; - if (mNumSamples > 2) { + mStszTableEntries->add(htonl(sampleSize)); + if (mStszTableEntries->count() > 2) { // Force the first sample to have its own stts entry so that // we can adjust its value later to maintain the A/V sync. - if (mNumSamples == 3 || currDurationTicks != lastDurationTicks) { + if (mStszTableEntries->count() == 3 || currDurationTicks != lastDurationTicks) { addOneSttsTableEntry(sampleCount, lastDurationTicks); sampleCount = 1; } else { @@ -2081,7 +2191,7 @@ status_t MPEG4Writer::Track::threadEntry() { } if (mSamplesHaveSameSize) { - if (mNumSamples >= 2 && previousSampleSize != sampleSize) { + if (mStszTableEntries->count() >= 2 && previousSampleSize != sampleSize) { mSamplesHaveSameSize = false; } previousSampleSize = sampleSize; @@ -2093,7 +2203,7 @@ status_t MPEG4Writer::Track::threadEntry() { lastTimestampUs = timestampUs; if (isSync != 0) { - addOneStssTableEntry(mNumSamples); + addOneStssTableEntry(mStszTableEntries->count()); } if (mTrackingProgressStatus) { @@ -2105,7 +2215,12 @@ status_t MPEG4Writer::Track::threadEntry() { if (!hasMultipleTracks) { off64_t offset = mIsAvc? mOwner->addLengthPrefixedSample_l(copy) : mOwner->addSample_l(copy); - if (mChunkOffsets.empty()) { + + uint32_t count = (mOwner->use32BitFileOffset() + ? mStcoTableEntries->count() + : mCo64TableEntries->count()); + + if (count == 0) { addChunkOffset(offset); } copy->release(); @@ -2128,9 +2243,9 @@ status_t MPEG4Writer::Track::threadEntry() { } ++nChunks; if (nChunks == 1 || // First chunk - (--(mStscTableEntries.end()))->samplesPerChunk != - mChunkSamples.size()) { - addOneStscTableEntry(nChunks, mChunkSamples.size()); + lastSamplesPerChunk != mChunkSamples.size()) { + lastSamplesPerChunk = mChunkSamples.size(); + addOneStscTableEntry(nChunks, lastSamplesPerChunk); } bufferChunk(timestampUs); chunkTimestampUs = timestampUs; @@ -2148,7 +2263,7 @@ status_t MPEG4Writer::Track::threadEntry() { // Last chunk if (!hasMultipleTracks) { - addOneStscTableEntry(1, mNumSamples); + addOneStscTableEntry(1, mStszTableEntries->count()); } else if (!mChunkSamples.empty()) { addOneStscTableEntry(++nChunks, mChunkSamples.size()); bufferChunk(timestampUs); @@ -2157,14 +2272,14 @@ status_t MPEG4Writer::Track::threadEntry() { // We don't really know how long the last frame lasts, since // there is no frame time after it, just repeat the previous // frame's duration. - if (mNumSamples == 1) { + if (mStszTableEntries->count() == 1) { lastDurationUs = 0; // A single sample's duration lastDurationTicks = 0; } else { ++sampleCount; // Count for the last sample } - if (mNumSamples <= 2) { + if (mStszTableEntries->count() <= 2) { addOneSttsTableEntry(1, lastDurationTicks); if (sampleCount - 1 > 0) { addOneSttsTableEntry(sampleCount - 1, lastDurationTicks); @@ -2187,7 +2302,7 @@ status_t MPEG4Writer::Track::threadEntry() { sendTrackSummary(hasMultipleTracks); ALOGI("Received total/0-length (%d/%d) buffers and encoded %d frames. - %s", - count, nZeroLengthFrames, mNumSamples, mIsAudio? "audio": "video"); + count, nZeroLengthFrames, mStszTableEntries->count(), mIsAudio? "audio": "video"); if (mIsAudio) { ALOGI("Audio track drift time: %lld us", mOwner->getDriftTimeUs()); } @@ -2199,12 +2314,12 @@ status_t MPEG4Writer::Track::threadEntry() { } bool MPEG4Writer::Track::isTrackMalFormed() const { - if (mSampleSizes.empty()) { // no samples written + if (mStszTableEntries->count() == 0) { // no samples written ALOGE("The number of recorded samples is 0"); return true; } - if (!mIsAudio && mNumStssTableEntries == 0) { // no sync frames for video + if (!mIsAudio && mStssTableEntries->count() == 0) { // no sync frames for video ALOGE("There are no sync frames for video track"); return true; } @@ -2235,7 +2350,7 @@ void MPEG4Writer::Track::sendTrackSummary(bool hasMultipleTracks) { mOwner->notify(MEDIA_RECORDER_TRACK_EVENT_INFO, trackNum | MEDIA_RECORDER_TRACK_INFO_ENCODED_FRAMES, - mNumSamples); + mStszTableEntries->count()); { // The system delay time excluding the requested initial delay that @@ -2273,6 +2388,7 @@ void MPEG4Writer::Track::sendTrackSummary(bool hasMultipleTracks) { void MPEG4Writer::Track::trackProgressStatus(int64_t timeUs, status_t err) { ALOGV("trackProgressStatus: %lld us", timeUs); + if (mTrackEveryTimeDurationUs > 0 && timeUs - mPreviousTrackTimeUs >= mTrackEveryTimeDurationUs) { ALOGV("Fire time tracking progress status at %lld us", timeUs); @@ -2586,7 +2702,7 @@ void MPEG4Writer::Track::writeTkhdBox(uint32_t now) { mOwner->writeInt32(0x07); // version=0, flags=7 mOwner->writeInt32(now); // creation time mOwner->writeInt32(now); // modification time - mOwner->writeInt32(mTrackId + 1); // track id starts with 1 + mOwner->writeInt32(mTrackId); // track id starts with 1 mOwner->writeInt32(0); // reserved int64_t trakDurationUs = getDurationUs(); int32_t mvhdTimeScale = mOwner->getTimeScale(); @@ -2743,21 +2859,11 @@ int32_t MPEG4Writer::Track::getStartTimeOffsetScaledTime() const { void MPEG4Writer::Track::writeSttsBox() { mOwner->beginBox("stts"); mOwner->writeInt32(0); // version=0, flags=0 - mOwner->writeInt32(mNumSttsTableEntries); - - // Compensate for small start time difference from different media tracks - List<SttsTableEntry>::iterator it = mSttsTableEntries.begin(); - CHECK(it != mSttsTableEntries.end() && it->sampleCount == 1); - mOwner->writeInt32(it->sampleCount); - mOwner->writeInt32(getStartTimeOffsetScaledTime() + it->sampleDuration); - - int64_t totalCount = 1; - while (++it != mSttsTableEntries.end()) { - mOwner->writeInt32(it->sampleCount); - mOwner->writeInt32(it->sampleDuration); - totalCount += it->sampleCount; - } - CHECK_EQ(totalCount, mNumSamples); + uint32_t duration; + CHECK(mSttsTableEntries->get(duration, 1)); + duration = htonl(duration); // Back to host byte order + mSttsTableEntries->set(htonl(duration + getStartTimeOffsetScaledTime()), 1); + mSttsTableEntries->write(mOwner); mOwner->endBox(); // stts } @@ -2772,101 +2878,52 @@ void MPEG4Writer::Track::writeCttsBox() { } // Do not write ctts box when there is no need to have it. - if ((mNumCttsTableEntries == 1 && - mCttsTableEntries.begin()->sampleDuration == 0) || - mNumCttsTableEntries == 0) { + if (mCttsTableEntries->count() == 0) { return; } - ALOGD("ctts box has %d entries with range [%lld, %lld]", - mNumCttsTableEntries, mMinCttsOffsetTimeUs, mMaxCttsOffsetTimeUs); + ALOGV("ctts box has %d entries with range [%lld, %lld]", + mCttsTableEntries->count(), mMinCttsOffsetTimeUs, mMaxCttsOffsetTimeUs); mOwner->beginBox("ctts"); - // Version 1 allows to use negative offset time value, but - // we are sticking to version 0 for now. mOwner->writeInt32(0); // version=0, flags=0 - mOwner->writeInt32(mNumCttsTableEntries); - - // Compensate for small start time difference from different media tracks - List<CttsTableEntry>::iterator it = mCttsTableEntries.begin(); - CHECK(it != mCttsTableEntries.end() && it->sampleCount == 1); - mOwner->writeInt32(it->sampleCount); - mOwner->writeInt32(getStartTimeOffsetScaledTime() + - it->sampleDuration - mMinCttsOffsetTimeUs); - - int64_t totalCount = 1; - while (++it != mCttsTableEntries.end()) { - mOwner->writeInt32(it->sampleCount); - mOwner->writeInt32(it->sampleDuration - mMinCttsOffsetTimeUs); - totalCount += it->sampleCount; - } - CHECK_EQ(totalCount, mNumSamples); + uint32_t duration; + CHECK(mCttsTableEntries->get(duration, 1)); + duration = htonl(duration); // Back host byte order + mCttsTableEntries->set(htonl(duration + getStartTimeOffsetScaledTime() - mMinCttsOffsetTimeUs), 1); + mCttsTableEntries->write(mOwner); mOwner->endBox(); // ctts } void MPEG4Writer::Track::writeStssBox() { mOwner->beginBox("stss"); mOwner->writeInt32(0); // version=0, flags=0 - mOwner->writeInt32(mNumStssTableEntries); // number of sync frames - for (List<int32_t>::iterator it = mStssTableEntries.begin(); - it != mStssTableEntries.end(); ++it) { - mOwner->writeInt32(*it); - } + mStssTableEntries->write(mOwner); mOwner->endBox(); // stss } void MPEG4Writer::Track::writeStszBox() { - ALOGD("writeStszBox for %s track", isAudio()? "Audio": "Video"); mOwner->beginBox("stsz"); mOwner->writeInt32(0); // version=0, flags=0 - if (mSamplesHaveSameSize) { - CHECK(mCurrentSampleSizeArr != 0); - mOwner->write(mCurrentSampleSizeArr, 4, 1); // default sample size - } else { - mOwner->writeInt32(0); - } - mOwner->writeInt32(mNumSamples); - uint32_t nSamples = mNumSamples; - if (!mSamplesHaveSameSize) { - for (List<uint32_t *>::iterator it = mSampleSizes.begin(); - it != mSampleSizes.end(); ++it) { - if (nSamples >= kSampleArraySize) { - mOwner->write(*it, 4, kSampleArraySize); - nSamples -= kSampleArraySize; - } else { - mOwner->write(*it, 4, nSamples); - break; - } - } - } + mOwner->writeInt32(0); + mStszTableEntries->write(mOwner); mOwner->endBox(); // stsz - ALOGD("writeStszBox: X"); } void MPEG4Writer::Track::writeStscBox() { mOwner->beginBox("stsc"); mOwner->writeInt32(0); // version=0, flags=0 - mOwner->writeInt32(mNumStscTableEntries); - for (List<StscTableEntry>::iterator it = mStscTableEntries.begin(); - it != mStscTableEntries.end(); ++it) { - mOwner->writeInt32(it->firstChunk); - mOwner->writeInt32(it->samplesPerChunk); - mOwner->writeInt32(it->sampleDescriptionId); - } + mStscTableEntries->write(mOwner); mOwner->endBox(); // stsc } void MPEG4Writer::Track::writeStcoBox(bool use32BitOffset) { mOwner->beginBox(use32BitOffset? "stco": "co64"); mOwner->writeInt32(0); // version=0, flags=0 - mOwner->writeInt32(mNumStcoTableEntries); - for (List<off64_t>::iterator it = mChunkOffsets.begin(); - it != mChunkOffsets.end(); ++it) { - if (use32BitOffset) { - mOwner->writeInt32(static_cast<int32_t>(*it)); - } else { - mOwner->writeInt64((*it)); - } + if (use32BitOffset) { + mStcoTableEntries->write(mOwner); + } else { + mCo64TableEntries->write(mOwner); } mOwner->endBox(); // stco or co64 } |