summaryrefslogtreecommitdiffstats
path: root/media/libstagefright/MPEG4Writer.cpp
diff options
context:
space:
mode:
authorJames Dong <jdong@google.com>2010-09-02 11:19:11 -0700
committerJames Dong <jdong@google.com>2010-09-02 20:10:00 -0700
commit1f90c4b3fda9dbd2824d67fd679f850572f114dc (patch)
tree74e1debb8712fcd84d5b7bb269052a277a74cec4 /media/libstagefright/MPEG4Writer.cpp
parentbd057752e8054ff516b9d2c17b70b666fc791559 (diff)
downloadframeworks_av-1f90c4b3fda9dbd2824d67fd679f850572f114dc.zip
frameworks_av-1f90c4b3fda9dbd2824d67fd679f850572f114dc.tar.gz
frameworks_av-1f90c4b3fda9dbd2824d67fd679f850572f114dc.tar.bz2
Better file size estimate
When the recorded file becomes large, the metadata size can no longer be ignored. This makes it possible to save the recorded file when the storage becomes almost full at the end of the recording session. Change-Id: Ief038080f825c9946ce550949c03e914aec1e31a
Diffstat (limited to 'media/libstagefright/MPEG4Writer.cpp')
-rw-r--r--media/libstagefright/MPEG4Writer.cpp137
1 files changed, 103 insertions, 34 deletions
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index af2b4c4..5dcbc22 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -38,6 +38,7 @@
namespace android {
+static const int64_t kMax32BitFileSize = 0x007fffffffLL;
static const uint8_t kNalUnitTypeSeqParamSet = 0x07;
static const uint8_t kNalUnitTypePicParamSet = 0x08;
@@ -59,7 +60,7 @@ public:
bool isAvc() const { return mIsAvc; }
bool isAudio() const { return mIsAudio; }
bool isMPEG4() const { return mIsMPEG4; }
- void addChunkOffset(off_t offset) { mChunkOffsets.push_back(offset); }
+ void addChunkOffset(off_t offset);
status_t dump(int fd, const Vector<String16>& args) const;
private:
@@ -79,7 +80,7 @@ private:
bool mIsRealTimeRecording;
int64_t mMaxTimeStampUs;
int64_t mEstimatedTrackSizeBytes;
- int64_t mMaxWriteTimeUs;
+ int64_t mMdatSizeBytes;
int32_t mTimeScale;
pthread_t mThread;
@@ -92,8 +93,11 @@ private:
bool mSamplesHaveSameSize;
List<MediaBuffer *> mChunkSamples;
+
+ size_t mNumStcoTableEntries;
List<off_t> mChunkOffsets;
+ size_t mNumStscTableEntries;
struct StscTableEntry {
StscTableEntry(uint32_t chunk, uint32_t samples, uint32_t id)
@@ -107,9 +111,11 @@ private:
};
List<StscTableEntry> mStscTableEntries;
+ size_t mNumStssTableEntries;
List<int32_t> mStssTableEntries;
List<int64_t> mChunkDurations;
+ size_t mNumSttsTableEntries;
struct SttsTableEntry {
SttsTableEntry(uint32_t count, uint32_t durationUs)
@@ -178,6 +184,11 @@ private:
// Simple validation on the codec specific data
status_t checkCodecSpecificData() const;
+ void updateTrackSizeEstimate();
+ void addOneStscTableEntry(size_t chunkId, size_t sampleId);
+ void addOneStssTableEntry(size_t sampleId);
+ void addOneSttsTableEntry(size_t sampleCount, int64_t durationUs);
+
Track(const Track &);
Track &operator=(const Track &);
};
@@ -211,9 +222,11 @@ MPEG4Writer::MPEG4Writer(int fd)
MPEG4Writer::~MPEG4Writer() {
stop();
- for (List<Track *>::iterator it = mTracks.begin();
- it != mTracks.end(); ++it) {
+ while (!mTracks.empty()) {
+ List<Track *>::iterator it = mTracks.begin();
delete *it;
+ (*it) = NULL;
+ mTracks.erase(it);
}
mTracks.clear();
}
@@ -332,6 +345,21 @@ status_t MPEG4Writer::start(MetaData *param) {
mUse32BitOffset = false;
}
+ if (mUse32BitOffset) {
+ // Implicit 32 bit file size limit
+ if (mMaxFileSizeLimitBytes == 0) {
+ mMaxFileSizeLimitBytes = kMax32BitFileSize;
+ }
+
+ // If file size is set to be larger than the 32 bit file
+ // size limit, treat it as an error.
+ if (mMaxFileSizeLimitBytes > kMax32BitFileSize) {
+ LOGE("32-bit file size limit too big: %lld bytes",
+ mMaxFileSizeLimitBytes);
+ return UNKNOWN_ERROR;
+ }
+ }
+
// System property can overwrite the file offset bits parameter
char value[PROPERTY_VALUE_MAX];
if (property_get("media.stagefright.record-64bits", value, NULL)
@@ -413,6 +441,10 @@ status_t MPEG4Writer::start(MetaData *param) {
return OK;
}
+bool MPEG4Writer::use32BitFileOffset() const {
+ return mUse32BitOffset;
+}
+
status_t MPEG4Writer::pause() {
if (mFile == NULL) {
return OK;
@@ -739,7 +771,8 @@ bool MPEG4Writer::exceedsFileSizeLimit() {
it != mTracks.end(); ++it) {
nTotalBytesEstimate += (*it)->getEstimatedTrackSizeBytes();
}
- return (nTotalBytesEstimate >= mMaxFileSizeLimitBytes);
+
+ return (nTotalBytesEstimate + 1024 >= mMaxFileSizeLimitBytes);
}
bool MPEG4Writer::exceedsFileDurationLimit() {
@@ -819,6 +852,48 @@ MPEG4Writer::Track::Track(
setTimeScale();
}
+void MPEG4Writer::Track::updateTrackSizeEstimate() {
+
+ int64_t stcoBoxSizeBytes = mOwner->use32BitFileOffset()
+ ? mNumStcoTableEntries * 4
+ : mNumStcoTableEntries * 8;
+
+ int64_t stszBoxSizeBytes = mSamplesHaveSameSize? 4: (mNumSamples * 4);
+
+ mEstimatedTrackSizeBytes = mMdatSizeBytes + // media data size
+ mNumStscTableEntries * 12 + // stsc box size
+ mNumStssTableEntries * 4 + // stss box size
+ mNumSttsTableEntries * 8 + // stts box size
+ stcoBoxSizeBytes + // stco box size
+ stszBoxSizeBytes; // stsz box size
+}
+
+void MPEG4Writer::Track::addOneStscTableEntry(
+ size_t chunkId, size_t sampleId) {
+
+ StscTableEntry stscEntry(chunkId, sampleId, 1);
+ mStscTableEntries.push_back(stscEntry);
+ ++mNumStscTableEntries;
+}
+
+void MPEG4Writer::Track::addOneStssTableEntry(size_t sampleId) {
+ mStssTableEntries.push_back(sampleId);
+ ++mNumStssTableEntries;
+}
+
+void MPEG4Writer::Track::addOneSttsTableEntry(
+ size_t sampleCount, int64_t durationUs) {
+
+ SttsTableEntry sttsEntry(sampleCount, durationUs);
+ mSttsTableEntries.push_back(sttsEntry);
+ ++mNumSttsTableEntries;
+}
+
+void MPEG4Writer::Track::addChunkOffset(off_t offset) {
+ ++mNumStcoTableEntries;
+ mChunkOffsets.push_back(offset);
+}
+
void MPEG4Writer::Track::setTimeScale() {
LOGV("setTimeScale");
// Default time scale
@@ -1039,6 +1114,7 @@ status_t MPEG4Writer::startWriterThread() {
return OK;
}
+
status_t MPEG4Writer::Track::start(MetaData *params) {
if (!mDone && mPaused) {
mPaused = false;
@@ -1077,6 +1153,11 @@ status_t MPEG4Writer::Track::start(MetaData *params) {
mTrackDurationUs = 0;
mReachedEOS = false;
mEstimatedTrackSizeBytes = 0;
+ mNumStcoTableEntries = 0;
+ mNumStssTableEntries = 0;
+ mNumStscTableEntries = 0;
+ mNumSttsTableEntries = 0;
+ mMdatSizeBytes = 0;
pthread_create(&mThread, &attr, ThreadWrapper, this);
pthread_attr_destroy(&attr);
@@ -1434,7 +1515,6 @@ status_t MPEG4Writer::Track::threadEntry() {
bool collectStats = collectStatisticalData();
mNumSamples = 0;
- mMaxWriteTimeUs = 0;
status_t err = OK;
MediaBuffer *buffer;
while (!mDone && (err = mSource->read(&buffer)) == OK) {
@@ -1505,7 +1585,9 @@ status_t MPEG4Writer::Track::threadEntry() {
: copy->range_length();
// Max file size or duration handling
- mEstimatedTrackSizeBytes += sampleSize;
+ mMdatSizeBytes += sampleSize;
+ updateTrackSizeEstimate();
+
if (mOwner->exceedsFileSizeLimit()) {
mOwner->notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED, 0);
break;
@@ -1587,8 +1669,7 @@ status_t MPEG4Writer::Track::threadEntry() {
(lastTimestampUs * mTimeScale + 500000LL) / 1000000LL);
if (currDurationTicks != lastDurationTicks) {
- SttsTableEntry sttsEntry(sampleCount, lastDurationUs);
- mSttsTableEntries.push_back(sttsEntry);
+ addOneSttsTableEntry(sampleCount, lastDurationUs);
sampleCount = 1;
} else {
++sampleCount;
@@ -1611,7 +1692,7 @@ status_t MPEG4Writer::Track::threadEntry() {
}
if (isSync != 0) {
- mStssTableEntries.push_back(mNumSamples);
+ addOneStssTableEntry(mNumSamples);
}
if (mTrackingProgressStatus) {
@@ -1624,7 +1705,7 @@ status_t MPEG4Writer::Track::threadEntry() {
off_t offset = mIsAvc? mOwner->addLengthPrefixedSample_l(copy)
: mOwner->addSample_l(copy);
if (mChunkOffsets.empty()) {
- mChunkOffsets.push_back(offset);
+ addChunkOffset(offset);
}
copy->release();
copy = NULL;
@@ -1633,8 +1714,7 @@ status_t MPEG4Writer::Track::threadEntry() {
mChunkSamples.push_back(copy);
if (interleaveDurationUs == 0) {
- StscTableEntry stscEntry(++nChunks, 1, 1);
- mStscTableEntries.push_back(stscEntry);
+ addOneStscTableEntry(++nChunks, 1);
bufferChunk(timestampUs);
} else {
if (chunkTimestampUs == 0) {
@@ -1648,9 +1728,7 @@ status_t MPEG4Writer::Track::threadEntry() {
if (nChunks == 1 || // First chunk
(--(mStscTableEntries.end()))->samplesPerChunk !=
mChunkSamples.size()) {
- StscTableEntry stscEntry(nChunks,
- mChunkSamples.size(), 1);
- mStscTableEntries.push_back(stscEntry);
+ addOneStscTableEntry(nChunks, mChunkSamples.size());
}
bufferChunk(timestampUs);
chunkTimestampUs = timestampUs;
@@ -1669,12 +1747,9 @@ status_t MPEG4Writer::Track::threadEntry() {
// Last chunk
if (mOwner->numTracks() == 1) {
- StscTableEntry stscEntry(1, mNumSamples, 1);
- mStscTableEntries.push_back(stscEntry);
+ addOneStscTableEntry(1, mNumSamples);
} else if (!mChunkSamples.empty()) {
- ++nChunks;
- StscTableEntry stscEntry(nChunks, mChunkSamples.size(), 1);
- mStscTableEntries.push_back(stscEntry);
+ addOneStscTableEntry(++nChunks, mChunkSamples.size());
bufferChunk(timestampUs);
}
@@ -1686,12 +1761,11 @@ status_t MPEG4Writer::Track::threadEntry() {
} else {
++sampleCount; // Count for the last sample
}
- SttsTableEntry sttsEntry(sampleCount, lastDurationUs);
- mSttsTableEntries.push_back(sttsEntry);
+ addOneSttsTableEntry(sampleCount, lastDurationUs);
mTrackDurationUs += lastDurationUs;
mReachedEOS = true;
- LOGI("Received total/0-length (%d/%d) buffers and encoded %d frames. Max write time: %lld us - %s",
- count, nZeroLengthFrames, mNumSamples, mMaxWriteTimeUs, mIsAudio? "audio": "video");
+ LOGI("Received total/0-length (%d/%d) buffers and encoded %d frames. - %s",
+ count, nZeroLengthFrames, mNumSamples, mIsAudio? "audio": "video");
logStatisticalData(mIsAudio);
if (err == ERROR_END_OF_STREAM) {
@@ -1855,14 +1929,9 @@ int64_t MPEG4Writer::getDriftTimeUs() {
void MPEG4Writer::Track::bufferChunk(int64_t timestampUs) {
LOGV("bufferChunk");
- int64_t startTimeUs = systemTime() / 1000;
Chunk chunk(this, timestampUs, mChunkSamples);
mOwner->bufferChunk(chunk);
mChunkSamples.clear();
- int64_t endTimeUs = systemTime() / 1000;
- if (mMaxWriteTimeUs < endTimeUs - startTimeUs) {
- mMaxWriteTimeUs = endTimeUs - startTimeUs;
- }
}
int64_t MPEG4Writer::Track::getDurationUs() const {
@@ -2215,7 +2284,7 @@ void MPEG4Writer::Track::writeTrackHeader(
mOwner->beginBox("stts");
mOwner->writeInt32(0); // version=0, flags=0
- mOwner->writeInt32(mSttsTableEntries.size());
+ mOwner->writeInt32(mNumSttsTableEntries);
int64_t prevTimestampUs = 0;
for (List<SttsTableEntry>::iterator it = mSttsTableEntries.begin();
it != mSttsTableEntries.end(); ++it) {
@@ -2235,7 +2304,7 @@ void MPEG4Writer::Track::writeTrackHeader(
if (!mIsAudio) {
mOwner->beginBox("stss");
mOwner->writeInt32(0); // version=0, flags=0
- mOwner->writeInt32(mStssTableEntries.size()); // number of sync frames
+ mOwner->writeInt32(mNumStssTableEntries); // number of sync frames
for (List<int32_t>::iterator it = mStssTableEntries.begin();
it != mStssTableEntries.end(); ++it) {
mOwner->writeInt32(*it);
@@ -2262,7 +2331,7 @@ void MPEG4Writer::Track::writeTrackHeader(
mOwner->beginBox("stsc");
mOwner->writeInt32(0); // version=0, flags=0
- mOwner->writeInt32(mStscTableEntries.size());
+ mOwner->writeInt32(mNumStscTableEntries);
for (List<StscTableEntry>::iterator it = mStscTableEntries.begin();
it != mStscTableEntries.end(); ++it) {
mOwner->writeInt32(it->firstChunk);
@@ -2272,7 +2341,7 @@ void MPEG4Writer::Track::writeTrackHeader(
mOwner->endBox(); // stsc
mOwner->beginBox(use32BitOffset? "stco": "co64");
mOwner->writeInt32(0); // version=0, flags=0
- mOwner->writeInt32(mChunkOffsets.size());
+ mOwner->writeInt32(mNumStcoTableEntries);
for (List<off_t>::iterator it = mChunkOffsets.begin();
it != mChunkOffsets.end(); ++it) {
if (use32BitOffset) {