Reduce memory usage by the MP4 file writer

- Don't store timestamp for each output sample
- Don't store timestamp for statistical data collection if the collection of statistical data is not requested

TODO:
1. Reduce CPU load by elimnating the list cost associated with List.size() call.

Change-Id: I590bc17176596a65952c982574b82ee3b15b7d1c
This commit is contained in:
James Dong
2010-07-28 10:24:39 -07:00
parent 5587bb7e37
commit 7e397842d5

View File

@ -66,11 +66,7 @@ private:
pthread_t mThread; pthread_t mThread;
struct SampleInfo { List<size_t> mSampleSizes;
size_t size;
int64_t timestampUs;
};
List<SampleInfo> mSampleInfos;
bool mSamplesHaveSameSize; bool mSamplesHaveSameSize;
List<MediaBuffer *> mChunkSamples; List<MediaBuffer *> mChunkSamples;
@ -916,6 +912,15 @@ status_t MPEG4Writer::Track::makeAVCCodecSpecificData(
return OK; return OK;
} }
static bool collectStatisticalData() {
char value[PROPERTY_VALUE_MAX];
if (property_get("media.stagefright.record-stats", value, NULL)
&& (!strcmp(value, "1") || !strcasecmp(value, "true"))) {
return true;
}
return false;
}
void MPEG4Writer::Track::threadEntry() { void MPEG4Writer::Track::threadEntry() {
sp<MetaData> meta = mSource->getFormat(); sp<MetaData> meta = mSource->getFormat();
const char *mime; const char *mime;
@ -935,6 +940,7 @@ void MPEG4Writer::Track::threadEntry() {
uint32_t previousSampleSize = 0; // Size of the previous sample uint32_t previousSampleSize = 0; // Size of the previous sample
int64_t previousPausedDurationUs = 0; int64_t previousPausedDurationUs = 0;
sp<MetaData> meta_data; sp<MetaData> meta_data;
bool collectStats = collectStatisticalData();
status_t err = OK; status_t err = OK;
MediaBuffer *buffer; MediaBuffer *buffer;
@ -1081,8 +1087,8 @@ void MPEG4Writer::Track::threadEntry() {
if (is_avc) StripStartcode(copy); if (is_avc) StripStartcode(copy);
SampleInfo info; size_t sampleSize;
info.size = is_avc sampleSize = is_avc
#if USE_NALLEN_FOUR #if USE_NALLEN_FOUR
? copy->range_length() + 4 ? copy->range_length() + 4
#else #else
@ -1091,7 +1097,7 @@ void MPEG4Writer::Track::threadEntry() {
: copy->range_length(); : copy->range_length();
// Max file size or duration handling // Max file size or duration handling
mEstimatedTrackSizeBytes += info.size; mEstimatedTrackSizeBytes += sampleSize;
if (mOwner->exceedsFileSizeLimit()) { if (mOwner->exceedsFileSizeLimit()) {
mOwner->notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED, 0); mOwner->notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED, 0);
break; break;
@ -1109,7 +1115,7 @@ void MPEG4Writer::Track::threadEntry() {
CHECK(meta_data->findInt64(kKeyTime, &timestampUs)); CHECK(meta_data->findInt64(kKeyTime, &timestampUs));
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
if (mSampleInfos.empty()) { if (mSampleSizes.empty()) {
mStartTimestampUs = timestampUs; mStartTimestampUs = timestampUs;
mOwner->setStartTimestampUs(mStartTimestampUs); mOwner->setStartTimestampUs(mStartTimestampUs);
} }
@ -1126,10 +1132,9 @@ void MPEG4Writer::Track::threadEntry() {
mMaxTimeStampUs = timestampUs; mMaxTimeStampUs = timestampUs;
} }
info.timestampUs = timestampUs; mSampleSizes.push_back(sampleSize);
mSampleInfos.push_back(info); if (mSampleSizes.size() > 2) {
if (mSampleInfos.size() > 2) { if (lastDurationUs != timestampUs - lastTimestampUs) {
if (lastDurationUs != info.timestampUs - lastTimestampUs) {
SttsTableEntry sttsEntry(sampleCount, lastDurationUs); SttsTableEntry sttsEntry(sampleCount, lastDurationUs);
mSttsTableEntries.push_back(sttsEntry); mSttsTableEntries.push_back(sttsEntry);
sampleCount = 1; sampleCount = 1;
@ -1138,16 +1143,16 @@ void MPEG4Writer::Track::threadEntry() {
} }
} }
if (mSamplesHaveSameSize) { if (mSamplesHaveSameSize) {
if (mSampleInfos.size() >= 2 && previousSampleSize != info.size) { if (mSampleSizes.size() >= 2 && previousSampleSize != sampleSize) {
mSamplesHaveSameSize = false; mSamplesHaveSameSize = false;
} }
previousSampleSize = info.size; previousSampleSize = sampleSize;
} }
lastDurationUs = info.timestampUs - lastTimestampUs; lastDurationUs = timestampUs - lastTimestampUs;
lastTimestampUs = info.timestampUs; lastTimestampUs = timestampUs;
if (isSync != 0) { if (isSync != 0) {
mStssTableEntries.push_back(mSampleInfos.size()); mStssTableEntries.push_back(mSampleSizes.size());
} }
if (mTrackingProgressStatus) { if (mTrackingProgressStatus) {
@ -1178,7 +1183,9 @@ void MPEG4Writer::Track::threadEntry() {
} else { } else {
if (timestampUs - chunkTimestampUs > interleaveDurationUs) { if (timestampUs - chunkTimestampUs > interleaveDurationUs) {
++nChunks; ++nChunks;
if (collectStats) {
mChunkDurations.push_back(timestampUs - chunkTimestampUs); mChunkDurations.push_back(timestampUs - chunkTimestampUs);
}
if (nChunks == 1 || // First chunk if (nChunks == 1 || // First chunk
(--(mStscTableEntries.end()))->samplesPerChunk != (--(mStscTableEntries.end()))->samplesPerChunk !=
mChunkSamples.size()) { mChunkSamples.size()) {
@ -1194,14 +1201,14 @@ void MPEG4Writer::Track::threadEntry() {
} }
if (mSampleInfos.empty()) { if (mSampleSizes.empty()) {
err = UNKNOWN_ERROR; err = UNKNOWN_ERROR;
} }
mOwner->trackProgressStatus(this, -1, err); mOwner->trackProgressStatus(this, -1, err);
// Last chunk // Last chunk
if (mOwner->numTracks() == 1) { if (mOwner->numTracks() == 1) {
StscTableEntry stscEntry(1, mSampleInfos.size(), 1); StscTableEntry stscEntry(1, mSampleSizes.size(), 1);
mStscTableEntries.push_back(stscEntry); mStscTableEntries.push_back(stscEntry);
} else if (!mChunkSamples.empty()) { } else if (!mChunkSamples.empty()) {
++nChunks; ++nChunks;
@ -1213,7 +1220,7 @@ void MPEG4Writer::Track::threadEntry() {
// We don't really know how long the last frame lasts, since // We don't really know how long the last frame lasts, since
// there is no frame time after it, just repeat the previous // there is no frame time after it, just repeat the previous
// frame's duration. // frame's duration.
if (mSampleInfos.size() == 1) { if (mSampleSizes.size() == 1) {
lastDurationUs = 0; // A single sample's duration lastDurationUs = 0; // A single sample's duration
} else { } else {
++sampleCount; // Count for the last sample ++sampleCount; // Count for the last sample
@ -1222,7 +1229,7 @@ void MPEG4Writer::Track::threadEntry() {
mSttsTableEntries.push_back(sttsEntry); mSttsTableEntries.push_back(sttsEntry);
mReachedEOS = true; mReachedEOS = true;
LOGI("Received total/0-length (%d/%d) buffers and encoded %d frames - %s", LOGI("Received total/0-length (%d/%d) buffers and encoded %d frames - %s",
count, nZeroLengthFrames, mSampleInfos.size(), is_audio? "audio": "video"); count, nZeroLengthFrames, mSampleSizes.size(), is_audio? "audio": "video");
logStatisticalData(is_audio); logStatisticalData(is_audio);
} }
@ -1284,8 +1291,8 @@ void MPEG4Writer::trackProgressStatus(
void MPEG4Writer::Track::findMinAvgMaxSampleDurationMs( void MPEG4Writer::Track::findMinAvgMaxSampleDurationMs(
int32_t *min, int32_t *avg, int32_t *max) { int32_t *min, int32_t *avg, int32_t *max) {
CHECK(!mSampleInfos.empty()); CHECK(!mSampleSizes.empty());
int32_t avgSampleDurationMs = mMaxTimeStampUs / 1000 / mSampleInfos.size(); int32_t avgSampleDurationMs = mMaxTimeStampUs / 1000 / mSampleSizes.size();
int32_t minSampleDurationMs = 0x7FFFFFFF; int32_t minSampleDurationMs = 0x7FFFFFFF;
int32_t maxSampleDurationMs = 0; int32_t maxSampleDurationMs = 0;
for (List<SttsTableEntry>::iterator it = mSttsTableEntries.begin(); for (List<SttsTableEntry>::iterator it = mSttsTableEntries.begin();
@ -1327,22 +1334,17 @@ void MPEG4Writer::Track::findMinMaxChunkDurations(int64_t *min, int64_t *max) {
} }
void MPEG4Writer::Track::logStatisticalData(bool isAudio) { void MPEG4Writer::Track::logStatisticalData(bool isAudio) {
if (mMaxTimeStampUs <= 0 || mSampleInfos.empty()) { if (mMaxTimeStampUs <= 0 || mSampleSizes.empty()) {
LOGI("nothing is recorded"); LOGI("nothing is recorded");
return; return;
} }
bool collectStats = false; bool collectStats = collectStatisticalData();
char value[PROPERTY_VALUE_MAX];
if (property_get("media.stagefright.record-stats", value, NULL)
&& (!strcmp(value, "1") || !strcasecmp(value, "true"))) {
collectStats = true;
}
if (collectStats) { if (collectStats) {
LOGI("%s track - duration %lld us, total %d frames", LOGI("%s track - duration %lld us, total %d frames",
isAudio? "audio": "video", mMaxTimeStampUs, isAudio? "audio": "video", mMaxTimeStampUs,
mSampleInfos.size()); mSampleSizes.size());
int32_t min, avg, max; int32_t min, avg, max;
findMinAvgMaxSampleDurationMs(&min, &avg, &max); findMinAvgMaxSampleDurationMs(&min, &avg, &max);
LOGI("min/avg/max sample duration (ms): %d/%d/%d", min, avg, max); LOGI("min/avg/max sample duration (ms): %d/%d/%d", min, avg, max);
@ -1355,9 +1357,9 @@ void MPEG4Writer::Track::logStatisticalData(bool isAudio) {
} }
int64_t totalBytes = 0; int64_t totalBytes = 0;
for (List<SampleInfo>::iterator it = mSampleInfos.begin(); for (List<size_t>::iterator it = mSampleSizes.begin();
it != mSampleInfos.end(); ++it) { it != mSampleSizes.end(); ++it) {
totalBytes += it->size; totalBytes += (*it);
} }
float bitRate = (totalBytes * 8000000.0) / mMaxTimeStampUs; float bitRate = (totalBytes * 8000000.0) / mMaxTimeStampUs;
LOGI("avg bit rate (bps): %.2f", bitRate); LOGI("avg bit rate (bps): %.2f", bitRate);
@ -1731,16 +1733,16 @@ void MPEG4Writer::Track::writeTrackHeader(
mOwner->beginBox("stsz"); mOwner->beginBox("stsz");
mOwner->writeInt32(0); // version=0, flags=0 mOwner->writeInt32(0); // version=0, flags=0
if (mSamplesHaveSameSize) { if (mSamplesHaveSameSize) {
List<SampleInfo>::iterator it = mSampleInfos.begin(); List<size_t>::iterator it = mSampleSizes.begin();
mOwner->writeInt32(it->size); // default sample size mOwner->writeInt32(*it); // default sample size
} else { } else {
mOwner->writeInt32(0); mOwner->writeInt32(0);
} }
mOwner->writeInt32(mSampleInfos.size()); mOwner->writeInt32(mSampleSizes.size());
if (!mSamplesHaveSameSize) { if (!mSamplesHaveSameSize) {
for (List<SampleInfo>::iterator it = mSampleInfos.begin(); for (List<size_t>::iterator it = mSampleSizes.begin();
it != mSampleInfos.end(); ++it) { it != mSampleSizes.end(); ++it) {
mOwner->writeInt32((*it).size); mOwner->writeInt32(*it);
} }
} }
mOwner->endBox(); // stsz mOwner->endBox(); // stsz