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