Merge "Enable B frame support in MPEG4Writer"

This commit is contained in:
James Dong
2012-02-08 10:42:35 -08:00
committed by Android (Google) Code Review
3 changed files with 83 additions and 77 deletions

View File

@@ -335,7 +335,7 @@ private:
status_t applyRotation();
status_t waitForBufferFilled_l();
int64_t retrieveDecodingTimeUs(bool isCodecSpecific);
int64_t getDecodingTimeUs();
status_t parseAVCCodecSpecificData(
const void *data, size_t size,

View File

@@ -70,6 +70,10 @@ public:
status_t dump(int fd, const Vector<String16>& args) const;
private:
enum {
kMaxCttsOffsetTimeUs = 1000000LL, // 1 second
};
MPEG4Writer *mOwner;
sp<MetaData> mMeta;
sp<MediaSource> mSource;
@@ -139,9 +143,10 @@ private:
uint32_t sampleCount;
int32_t sampleDuration; // time scale based
};
bool mHasNegativeCttsDeltaDuration;
size_t mNumCttsTableEntries;
List<CttsTableEntry> 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<MetaData>& 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<SttsTableEntry>::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<CttsTableEntry>::iterator it = mCttsTableEntries.begin();
it != mCttsTableEntries.end(); ++it) {
// 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);
mOwner->writeInt32(it->sampleDuration - mMinCttsOffsetTimeUs);
totalCount += it->sampleCount;
}
CHECK(totalCount == mNumSamples);

View File

@@ -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<int64_t>::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);
}