Enable passing parameters to the MediaWriter at runtime (at start() call).

- estimate the moov box size for mp4 file writer based on the file
  size/duration limit and target bit rate.
- can switch to use 64 bit file offset at runtime

rebased

Change-Id: Ibbe1f57e91ab2605820d5d96e8048d11e5559c53
This commit is contained in:
James Dong
2010-06-20 08:20:54 -07:00
parent 5a905ceb06
commit 6feaa46496
8 changed files with 103 additions and 15 deletions

View File

@@ -26,6 +26,7 @@
namespace android { namespace android {
struct MediaSource; struct MediaSource;
struct MetaData;
struct AMRWriter : public MediaWriter { struct AMRWriter : public MediaWriter {
AMRWriter(const char *filename); AMRWriter(const char *filename);
@@ -35,7 +36,7 @@ struct AMRWriter : public MediaWriter {
virtual status_t addSource(const sp<MediaSource> &source); virtual status_t addSource(const sp<MediaSource> &source);
virtual bool reachedEOS(); virtual bool reachedEOS();
virtual status_t start(); virtual status_t start(MetaData *params = NULL);
virtual void stop(); virtual void stop();
virtual void pause(); virtual void pause();

View File

@@ -36,7 +36,7 @@ public:
MPEG4Writer(int fd); MPEG4Writer(int fd);
virtual status_t addSource(const sp<MediaSource> &source); virtual status_t addSource(const sp<MediaSource> &source);
virtual status_t start(); virtual status_t start(MetaData *param = NULL);
virtual bool reachedEOS(); virtual bool reachedEOS();
virtual void stop(); virtual void stop();
virtual void pause(); virtual void pause();
@@ -83,6 +83,7 @@ private:
int64_t getStartTimestampUs(); // Not const int64_t getStartTimestampUs(); // Not const
status_t startTracks(); status_t startTracks();
size_t numTracks(); size_t numTracks();
int64_t estimateMoovBoxSize(int32_t bitRate);
void lock(); void lock();
void unlock(); void unlock();

View File

@@ -24,13 +24,14 @@
namespace android { namespace android {
struct MediaSource; struct MediaSource;
struct MetaData;
struct MediaWriter : public RefBase { struct MediaWriter : public RefBase {
MediaWriter() {} MediaWriter() {}
virtual status_t addSource(const sp<MediaSource> &source) = 0; virtual status_t addSource(const sp<MediaSource> &source) = 0;
virtual bool reachedEOS() = 0; virtual bool reachedEOS() = 0;
virtual status_t start() = 0; virtual status_t start(MetaData *params = NULL) = 0;
virtual void stop() = 0; virtual void stop() = 0;
virtual void pause() = 0; virtual void pause() = 0;
virtual void setMaxFileSize(int64_t bytes) { mMaxFileSizeLimitBytes = bytes; } virtual void setMaxFileSize(int64_t bytes) { mMaxFileSizeLimitBytes = bytes; }

View File

@@ -36,13 +36,14 @@ enum {
kKeyStride = 'strd', // int32_t kKeyStride = 'strd', // int32_t
kKeySliceHeight = 'slht', // int32_t kKeySliceHeight = 'slht', // int32_t
kKeyChannelCount = '#chn', // int32_t kKeyChannelCount = '#chn', // int32_t
kKeySampleRate = 'srte', // int32_t kKeySampleRate = 'srte', // int32_t (also video frame rate)
kKeyBitRate = 'brte', // int32_t (bps) kKeyBitRate = 'brte', // int32_t (bps)
kKeyESDS = 'esds', // raw data kKeyESDS = 'esds', // raw data
kKeyAVCC = 'avcc', // raw data kKeyAVCC = 'avcc', // raw data
kKeyVorbisInfo = 'vinf', // raw data kKeyVorbisInfo = 'vinf', // raw data
kKeyVorbisBooks = 'vboo', // raw data kKeyVorbisBooks = 'vboo', // raw data
kKeyWantsNALFragments = 'NALf', kKeyWantsNALFragments = 'NALf',
kKey64BitFileOffset = 'fobt', // int32_t (bool)
kKeyIsSyncFrame = 'sync', // int32_t (bool) kKeyIsSyncFrame = 'sync', // int32_t (bool)
kKeyIsCodecConfig = 'conf', // int32_t (bool) kKeyIsCodecConfig = 'conf', // int32_t (bool)
kKeyTime = 'time', // int64_t (usecs) kKeyTime = 'time', // int64_t (usecs)

View File

@@ -316,6 +316,13 @@ status_t StagefrightRecorder::setParamIFramesInterval(int32_t interval) {
return OK; return OK;
} }
status_t StagefrightRecorder::setParam64BitFileOffset(bool use64Bit) {
LOGV("setParam64BitFileOffset: %s",
use64Bit? "use 64 bit file offset": "use 32 bit file offset");
mUse64BitFileOffset = use64Bit;
return OK;
}
status_t StagefrightRecorder::setParameter( status_t StagefrightRecorder::setParameter(
const String8 &key, const String8 &value) { const String8 &key, const String8 &value) {
LOGV("setParameter: key (%s) => value (%s)", key.string(), value.string()); LOGV("setParameter: key (%s) => value (%s)", key.string(), value.string());
@@ -361,6 +368,11 @@ status_t StagefrightRecorder::setParameter(
if (safe_strtoi32(value.string(), &interval)) { if (safe_strtoi32(value.string(), &interval)) {
return setParamIFramesInterval(interval); return setParamIFramesInterval(interval);
} }
} else if (key == "param-use-64bit-offset") {
int32_t use64BitOffset;
if (safe_strtoi32(value.string(), &use64BitOffset)) {
return setParam64BitFileOffset(use64BitOffset != 0);
}
} else { } else {
LOGE("setParameter: failed to find key %s", key.string()); LOGE("setParameter: failed to find key %s", key.string());
} }
@@ -633,6 +645,7 @@ void StagefrightRecorder::clipVideoFrameHeight() {
status_t StagefrightRecorder::startMPEG4Recording() { status_t StagefrightRecorder::startMPEG4Recording() {
mWriter = new MPEG4Writer(dup(mOutputFd)); mWriter = new MPEG4Writer(dup(mOutputFd));
int32_t totalBitRate = 0;
// Add audio source first if it exists // Add audio source first if it exists
if (mAudioSource != AUDIO_SOURCE_LIST_END) { if (mAudioSource != AUDIO_SOURCE_LIST_END) {
@@ -651,7 +664,7 @@ status_t StagefrightRecorder::startMPEG4Recording() {
if (audioEncoder == NULL) { if (audioEncoder == NULL) {
return UNKNOWN_ERROR; return UNKNOWN_ERROR;
} }
totalBitRate += mAudioBitRate;
mWriter->addSource(audioEncoder); mWriter->addSource(audioEncoder);
} }
if (mVideoSource == VIDEO_SOURCE_DEFAULT if (mVideoSource == VIDEO_SOURCE_DEFAULT
@@ -704,7 +717,7 @@ status_t StagefrightRecorder::startMPEG4Recording() {
sp<MetaData> enc_meta = new MetaData; sp<MetaData> enc_meta = new MetaData;
enc_meta->setInt32(kKeyBitRate, mVideoBitRate); enc_meta->setInt32(kKeyBitRate, mVideoBitRate);
enc_meta->setInt32(kKeySampleRate, mFrameRate); // XXX: kKeySampleRate? enc_meta->setInt32(kKeySampleRate, mFrameRate);
switch (mVideoEncoder) { switch (mVideoEncoder) {
case VIDEO_ENCODER_H263: case VIDEO_ENCODER_H263:
@@ -747,12 +760,13 @@ status_t StagefrightRecorder::startMPEG4Recording() {
true /* createEncoder */, cameraSource); true /* createEncoder */, cameraSource);
CHECK(mOutputFd >= 0); CHECK(mOutputFd >= 0);
totalBitRate += mVideoBitRate;
mWriter->addSource(encoder); mWriter->addSource(encoder);
} }
{ {
// MPEGWriter specific handling // MPEGWriter specific handling
MPEG4Writer *writer = ((MPEG4Writer *) mWriter.get()); // mWriter is an MPEGWriter MPEG4Writer *writer = ((MPEG4Writer *) mWriter.get());
writer->setInterleaveDuration(mInterleaveDurationUs); writer->setInterleaveDuration(mInterleaveDurationUs);
} }
@@ -763,7 +777,10 @@ status_t StagefrightRecorder::startMPEG4Recording() {
mWriter->setMaxFileSize(mMaxFileSizeBytes); mWriter->setMaxFileSize(mMaxFileSizeBytes);
} }
mWriter->setListener(mListener); mWriter->setListener(mListener);
mWriter->start(); sp<MetaData> meta = new MetaData;
meta->setInt32(kKeyBitRate, totalBitRate);
meta->setInt32(kKey64BitFileOffset, mUse64BitFileOffset);
mWriter->start(meta.get());
return OK; return OK;
} }
@@ -824,6 +841,7 @@ status_t StagefrightRecorder::reset() {
mInterleaveDurationUs = 0; mInterleaveDurationUs = 0;
mIFramesInterval = 1; mIFramesInterval = 1;
mAudioSourceNode = 0; mAudioSourceNode = 0;
mUse64BitFileOffset = false;
mEncoderProfiles = MediaProfiles::getInstance(); mEncoderProfiles = MediaProfiles::getInstance();
mOutputFd = -1; mOutputFd = -1;

View File

@@ -72,6 +72,7 @@ private:
output_format mOutputFormat; output_format mOutputFormat;
audio_encoder mAudioEncoder; audio_encoder mAudioEncoder;
video_encoder mVideoEncoder; video_encoder mVideoEncoder;
bool mUse64BitFileOffset;
int32_t mVideoWidth, mVideoHeight; int32_t mVideoWidth, mVideoHeight;
int32_t mFrameRate; int32_t mFrameRate;
int32_t mVideoBitRate; int32_t mVideoBitRate;
@@ -100,6 +101,7 @@ private:
status_t setParamAudioSamplingRate(int32_t sampleRate); status_t setParamAudioSamplingRate(int32_t sampleRate);
status_t setParamInterleaveDuration(int32_t durationUs); status_t setParamInterleaveDuration(int32_t durationUs);
status_t setParamIFramesInterval(int32_t interval); status_t setParamIFramesInterval(int32_t interval);
status_t setParam64BitFileOffset(bool use64BitFileOffset);
status_t setParamMaxDurationOrFileSize(int64_t limit, bool limit_is_duration); status_t setParamMaxDurationOrFileSize(int64_t limit, bool limit_is_duration);
void clipVideoBitRate(); void clipVideoBitRate();
void clipVideoFrameRate(); void clipVideoFrameRate();

View File

@@ -97,7 +97,7 @@ status_t AMRWriter::addSource(const sp<MediaSource> &source) {
return OK; return OK;
} }
status_t AMRWriter::start() { status_t AMRWriter::start(MetaData *params) {
if (mInitCheck != OK) { if (mInitCheck != OK) {
return mInitCheck; return mInitCheck;
} }

View File

@@ -180,11 +180,72 @@ status_t MPEG4Writer::startTracks() {
return OK; return OK;
} }
status_t MPEG4Writer::start() { int64_t MPEG4Writer::estimateMoovBoxSize(int32_t bitRate) {
// This implementation is highly experimental/heurisitic.
//
// Statistical analysis shows that metadata usually accounts
// for a small portion of the total file size, usually < 0.6%.
// Currently, lets set to 0.4% for now.
// The default MIN_MOOV_BOX_SIZE is set to 0.4% x 1MB,
// where 1MB is the common file size limit for MMS application.
// The default MAX _MOOV_BOX_SIZE value is based on about 4
// minute video recording with a bit rate about 3 Mbps, because
// statistics also show that most of the video captured are going
// to be less than 3 minutes.
// If the estimation is wrong, we will pay the price of wasting
// some reserved space. This should not happen so often statistically.
static const int32_t factor = mUse32BitOffset? 1: 2;
static const int64_t MIN_MOOV_BOX_SIZE = 4 * 1024; // 4 KB
static const int64_t MAX_MOOV_BOX_SIZE = (180 * 3000000 * 6LL / 8000);
int64_t size = MIN_MOOV_BOX_SIZE;
if (mMaxFileSizeLimitBytes != 0) {
size = mMaxFileSizeLimitBytes * 4 / 1000;
} else if (mMaxFileDurationLimitUs != 0) {
if (bitRate <= 0) {
// We could not estimate the file size since bitRate is not set.
size = MIN_MOOV_BOX_SIZE;
} else {
size = ((mMaxFileDurationLimitUs * bitRate * 4) / 1000 / 8000000);
}
}
if (size < MIN_MOOV_BOX_SIZE) {
size = MIN_MOOV_BOX_SIZE;
}
// Any long duration recording will be probably end up with
// non-streamable mp4 file.
if (size > MAX_MOOV_BOX_SIZE) {
size = MAX_MOOV_BOX_SIZE;
}
LOGI("limits: %lld/%lld bytes/us, bit rate: %d bps and the estimated"
" moov size %lld bytes",
mMaxFileSizeLimitBytes, mMaxFileDurationLimitUs, bitRate, size);
return factor * size;
}
status_t MPEG4Writer::start(MetaData *param) {
if (mFile == NULL) { if (mFile == NULL) {
return UNKNOWN_ERROR; return UNKNOWN_ERROR;
} }
int32_t use64BitOffset;
if (param &&
param->findInt32(kKey64BitFileOffset, &use64BitOffset) &&
use64BitOffset) {
mUse32BitOffset = false;
}
// System property can overwrite the file offset bits parameter
char value[PROPERTY_VALUE_MAX];
if (property_get("media.stagefright.record-64bits", value, NULL)
&& (!strcmp(value, "1") || !strcasecmp(value, "true"))) {
mUse32BitOffset = false;
}
mStartTimestampUs = -1; mStartTimestampUs = -1;
if (mStarted) { if (mStarted) {
if (mPaused) { if (mPaused) {
@@ -208,9 +269,11 @@ status_t MPEG4Writer::start() {
mFreeBoxOffset = mOffset; mFreeBoxOffset = mOffset;
if (mEstimatedMoovBoxSize == 0) { if (mEstimatedMoovBoxSize == 0) {
// XXX: Estimate the moov box size int32_t bitRate = -1;
// based on max file size or duration limit if (param) {
mEstimatedMoovBoxSize = 0x0F00; param->findInt32(kKeyBitRate, &bitRate);
}
mEstimatedMoovBoxSize = estimateMoovBoxSize(bitRate);
} }
CHECK(mEstimatedMoovBoxSize >= 8); CHECK(mEstimatedMoovBoxSize >= 8);
fseeko(mFile, mFreeBoxOffset, SEEK_SET); fseeko(mFile, mFreeBoxOffset, SEEK_SET);
@@ -332,8 +395,7 @@ void MPEG4Writer::stop() {
write(mMoovBoxBuffer, 1, mMoovBoxBufferOffset, mFile); write(mMoovBoxBuffer, 1, mMoovBoxBufferOffset, mFile);
// Free box // Free box
mFreeBoxOffset = mStreamableFile? mOffset: mFreeBoxOffset; fseeko(mFile, mOffset, SEEK_SET);
fseeko(mFile, mFreeBoxOffset, SEEK_SET);
writeInt32(mEstimatedMoovBoxSize - mMoovBoxBufferOffset); writeInt32(mEstimatedMoovBoxSize - mMoovBoxBufferOffset);
write("free", 4); write("free", 4);
@@ -341,6 +403,8 @@ void MPEG4Writer::stop() {
free(mMoovBoxBuffer); free(mMoovBoxBuffer);
mMoovBoxBuffer = NULL; mMoovBoxBuffer = NULL;
mMoovBoxBufferOffset = 0; mMoovBoxBufferOffset = 0;
} else {
LOGI("The mp4 file will not be streamable.");
} }
CHECK(mBoxes.empty()); CHECK(mBoxes.empty());