From 000e18370baae60ffd9f25b509501dd8c26deabf Mon Sep 17 00:00:00 2001 From: James Dong Date: Mon, 6 Feb 2012 23:46:37 -0800 Subject: Enable B frame support in MPEG4Writer This patch allows us to automatically detect whether ctts box is needed in MPEG4Writer. MPEG4Writer uses ctts version 0 (non-negative offset value) store the composition time offset on a needed basis. Currently, the size of the ctts box is not optimized. Optimization will be addressed in a subsequent patch. o also changed the private method retrieveDecodingTime(bool) in OMXCodec to getDecodingTime() o related-to-bug: 4232183 Change-Id: Ic6dc7b25ecd258c2506ca4b9c25156e922456e51 --- media/libstagefright/MPEG4Writer.cpp | 147 +++++++++++++++++++---------------- media/libstagefright/OMXCodec.cpp | 11 +-- 2 files changed, 82 insertions(+), 76 deletions(-) (limited to 'media/libstagefright') diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp index 068660b..c7a8b32 100755 --- a/media/libstagefright/MPEG4Writer.cpp +++ b/media/libstagefright/MPEG4Writer.cpp @@ -70,6 +70,10 @@ public: status_t dump(int fd, const Vector& args) const; private: + enum { + kMaxCttsOffsetTimeUs = 1000000LL, // 1 second + }; + MPEG4Writer *mOwner; sp mMeta; sp mSource; @@ -139,9 +143,10 @@ private: uint32_t sampleCount; int32_t sampleDuration; // time scale based }; - bool mHasNegativeCttsDeltaDuration; size_t mNumCttsTableEntries; List mCttsTableEntries; + int64_t mMinCttsOffsetTimeUs; + int64_t mMaxCttsOffsetTimeUs; // Sequence parameter set or picture parameter set struct AVCParamSet { @@ -172,6 +177,8 @@ private: // Update the audio track's drift information. void updateDriftTime(const sp& meta); + int32_t getStartTimeOffsetScaledTime() const; + static void *ThreadWrapper(void *me); status_t threadEntry(); @@ -1186,9 +1193,6 @@ void MPEG4Writer::Track::addOneCttsTableEntry( if (mIsAudio) { return; } - if (duration < 0 && !mHasNegativeCttsDeltaDuration) { - mHasNegativeCttsDeltaDuration = true; - } CttsTableEntry cttsEntry(sampleCount, duration); mCttsTableEntries.push_back(cttsEntry); ++mNumCttsTableEntries; @@ -1509,7 +1513,6 @@ status_t MPEG4Writer::Track::start(MetaData *params) { mMdatSizeBytes = 0; mMaxChunkDurationUs = 0; - mHasNegativeCttsDeltaDuration = false; pthread_create(&mThread, &attr, ThreadWrapper, this); pthread_attr_destroy(&attr); @@ -1833,29 +1836,18 @@ status_t MPEG4Writer::Track::threadEntry() { int32_t nChunks = 0; int32_t nZeroLengthFrames = 0; int64_t lastTimestampUs = 0; // Previous sample time stamp - int64_t lastCttsTimeUs = 0; // Previous sample time stamp int64_t lastDurationUs = 0; // Between the previous two samples int64_t currDurationTicks = 0; // Timescale based ticks int64_t lastDurationTicks = 0; // Timescale based ticks int32_t sampleCount = 1; // Sample count in the current stts table entry - int64_t currCttsDurTicks = 0; // Timescale based ticks - int64_t lastCttsDurTicks = 0; // Timescale based ticks - int32_t cttsSampleCount = 1; // Sample count in the current ctts table entry - uint32_t previousSampleSize = 0; // Size of the previous sample + uint32_t previousSampleSize = 0; // Size of the previous sample int64_t previousPausedDurationUs = 0; int64_t timestampUs = 0; - int64_t cttsDeltaTimeUs = 0; - bool hasBFrames = false; + int64_t cttsOffsetTimeUs = 0; + int64_t currCttsOffsetTimeTicks = 0; // Timescale based ticks + int64_t lastCttsOffsetTimeTicks = -1; // Timescale based ticks + int32_t cttsSampleCount = 1; // Sample count in the current ctts table entry -#if 1 - // XXX: Samsung's video encoder's output buffer timestamp - // is not correct. see bug 4724339 - char value[PROPERTY_VALUE_MAX]; - if (property_get("rw.media.record.hasb", value, NULL) && - (!strcasecmp(value, "true") || !strcasecmp(value, "1"))) { - hasBFrames = true; - } -#endif if (mIsAudio) { prctl(PR_SET_NAME, (unsigned long)"AudioTrackEncoding", 0, 0, 0); } else { @@ -1972,23 +1964,48 @@ status_t MPEG4Writer::Track::threadEntry() { timestampUs -= previousPausedDurationUs; CHECK(timestampUs >= 0); - if (!mIsAudio && hasBFrames) { + if (!mIsAudio) { /* * Composition time: timestampUs * Decoding time: decodingTimeUs - * Composition time delta = composition time - decoding time - * - * We save picture decoding time stamp delta in stts table entries, - * and composition time delta duration in ctts table entries. + * Composition time offset = composition time - decoding time */ int64_t decodingTimeUs; CHECK(meta_data->findInt64(kKeyDecodingTime, &decodingTimeUs)); decodingTimeUs -= previousPausedDurationUs; - int64_t timeUs = decodingTimeUs; - cttsDeltaTimeUs = timestampUs - decodingTimeUs; + cttsOffsetTimeUs = + timestampUs + kMaxCttsOffsetTimeUs - decodingTimeUs; + CHECK(cttsOffsetTimeUs >= 0); timestampUs = decodingTimeUs; - ALOGV("decoding time: %lld and ctts delta time: %lld", - timestampUs, cttsDeltaTimeUs); + ALOGV("decoding time: %lld and ctts offset time: %lld", + timestampUs, cttsOffsetTimeUs); + + // Update ctts box table if necessary + currCttsOffsetTimeTicks = + (cttsOffsetTimeUs * mTimeScale + 500000LL) / 1000000LL; + CHECK(currCttsOffsetTimeTicks <= 0x7FFFFFFFLL); +#if 0 + // FIXME: + // Optimize to reduce the number of ctts table entries. + // Also, make sure that the very first ctts table entry contains + // only a single sample. +#else + addOneCttsTableEntry(1, currCttsOffsetTimeTicks); +#endif + lastCttsOffsetTimeTicks = currCttsOffsetTimeTicks; + + // Update ctts time offset range + if (mNumSamples == 0) { + mMinCttsOffsetTimeUs = currCttsOffsetTimeTicks; + mMaxCttsOffsetTimeUs = currCttsOffsetTimeTicks; + } else { + if (currCttsOffsetTimeTicks > mMaxCttsOffsetTimeUs) { + mMaxCttsOffsetTimeUs = currCttsOffsetTimeTicks; + } else if (currCttsOffsetTimeTicks < mMinCttsOffsetTimeUs) { + mMinCttsOffsetTimeUs = currCttsOffsetTimeTicks; + } + } + } if (mIsRealTimeRecording) { @@ -2012,6 +2029,7 @@ status_t MPEG4Writer::Track::threadEntry() { currDurationTicks = ((timestampUs * mTimeScale + 500000LL) / 1000000LL - (lastTimestampUs * mTimeScale + 500000LL) / 1000000LL); + CHECK(currDurationTicks >= 0); mSampleSizes.push_back(sampleSize); ++mNumSamples; @@ -2020,25 +2038,12 @@ status_t MPEG4Writer::Track::threadEntry() { // 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) { - ALOGV("%s lastDurationUs: %lld us, currDurationTicks: %lld us", - mIsAudio? "Audio": "Video", lastDurationUs, currDurationTicks); addOneSttsTableEntry(sampleCount, lastDurationTicks); sampleCount = 1; } else { ++sampleCount; } - if (!mIsAudio) { - currCttsDurTicks = - ((cttsDeltaTimeUs * mTimeScale + 500000LL) / 1000000LL - - (lastCttsTimeUs * mTimeScale + 500000LL) / 1000000LL); - if (currCttsDurTicks != lastCttsDurTicks) { - addOneCttsTableEntry(cttsSampleCount, lastCttsDurTicks); - cttsSampleCount = 1; - } else { - ++cttsSampleCount; - } - } } if (mSamplesHaveSameSize) { if (mNumSamples >= 2 && previousSampleSize != sampleSize) { @@ -2052,11 +2057,6 @@ status_t MPEG4Writer::Track::threadEntry() { lastDurationTicks = currDurationTicks; lastTimestampUs = timestampUs; - if (!mIsAudio) { - lastCttsDurTicks = currCttsDurTicks; - lastCttsTimeUs = cttsDeltaTimeUs; - } - if (isSync != 0) { addOneStssTableEntry(mNumSamples); } @@ -2125,7 +2125,6 @@ status_t MPEG4Writer::Track::threadEntry() { if (mNumSamples == 1) { lastDurationUs = 0; // A single sample's duration lastDurationTicks = 0; - lastCttsDurTicks = 0; } else { ++sampleCount; // Count for the last sample ++cttsSampleCount; @@ -2140,7 +2139,6 @@ status_t MPEG4Writer::Track::threadEntry() { addOneSttsTableEntry(sampleCount, lastDurationTicks); } - addOneCttsTableEntry(cttsSampleCount, lastCttsDurTicks); mTrackDurationUs += lastDurationUs; mReachedEOS = true; @@ -2690,23 +2688,26 @@ void MPEG4Writer::Track::writePaspBox() { mOwner->endBox(); // pasp } -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 +int32_t MPEG4Writer::Track::getStartTimeOffsetScaledTime() const { int64_t trackStartTimeOffsetUs = 0; int64_t moovStartTimeUs = mOwner->getStartTimestampUs(); if (mStartTimestampUs != moovStartTimeUs) { CHECK(mStartTimestampUs > moovStartTimeUs); trackStartTimeOffsetUs = mStartTimestampUs - moovStartTimeUs; } + return (trackStartTimeOffsetUs * mTimeScale + 500000LL) / 1000000LL; +} + +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::iterator it = mSttsTableEntries.begin(); CHECK(it != mSttsTableEntries.end() && it->sampleCount == 1); mOwner->writeInt32(it->sampleCount); - int32_t dur = (trackStartTimeOffsetUs * mTimeScale + 500000LL) / 1000000LL; - mOwner->writeInt32(dur + it->sampleDuration); + mOwner->writeInt32(getStartTimeOffsetScaledTime() + it->sampleDuration); int64_t totalCount = 1; while (++it != mSttsTableEntries.end()) { @@ -2723,6 +2724,11 @@ void MPEG4Writer::Track::writeCttsBox() { return; } + // There is no B frame at all + if (mMinCttsOffsetTimeUs == mMaxCttsOffsetTimeUs) { + return; + } + // Do not write ctts box when there is no need to have it. if ((mNumCttsTableEntries == 1 && mCttsTableEntries.begin()->sampleDuration == 0) || @@ -2730,21 +2736,26 @@ void MPEG4Writer::Track::writeCttsBox() { return; } - ALOGV("ctts box has %d entries", mNumCttsTableEntries); + ALOGD("ctts box has %d entries with range [%lld, %lld]", + mNumCttsTableEntries, mMinCttsOffsetTimeUs, mMaxCttsOffsetTimeUs); mOwner->beginBox("ctts"); - if (mHasNegativeCttsDeltaDuration) { - mOwner->writeInt32(0x00010000); // version=1, flags=0 - } else { - mOwner->writeInt32(0); // version=0, flags=0 - } + // 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); - int64_t totalCount = 0; - for (List::iterator it = mCttsTableEntries.begin(); - it != mCttsTableEntries.end(); ++it) { + // Compensate for small start time difference from different media tracks + List::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); + mOwner->writeInt32(it->sampleDuration - mMinCttsOffsetTimeUs); totalCount += it->sampleCount; } CHECK(totalCount == mNumSamples); diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index 381320b..470f750 100755 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -2187,7 +2187,7 @@ error: } } -int64_t OMXCodec::retrieveDecodingTimeUs(bool isCodecSpecific) { +int64_t OMXCodec::getDecodingTimeUs() { CHECK(mIsEncoder && mIsVideo); if (mDecodingTimeList.empty()) { @@ -2199,12 +2199,7 @@ int64_t OMXCodec::retrieveDecodingTimeUs(bool isCodecSpecific) { List::iterator it = mDecodingTimeList.begin(); int64_t timeUs = *it; - - // If the output buffer is codec specific configuration, - // do not remove the decoding time from the list. - if (!isCodecSpecific) { - mDecodingTimeList.erase(it); - } + mDecodingTimeList.erase(it); return timeUs; } @@ -2384,7 +2379,7 @@ void OMXCodec::on_message(const omx_message &msg) { } if (mIsEncoder && mIsVideo) { - int64_t decodingTimeUs = retrieveDecodingTimeUs(isCodecSpecific); + int64_t decodingTimeUs = isCodecSpecific? 0: getDecodingTimeUs(); buffer->meta_data()->setInt64(kKeyDecodingTime, decodingTimeUs); } -- cgit v1.1