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:
@@ -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();
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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; }
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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());
|
||||||
|
|||||||
Reference in New Issue
Block a user