Merge "Finetune some of the prefetcher parameters to a) buffer as much as froyo did b) ensure that keepalives actually trigger a network read instead of just draining internal buffers" into gingerbread
diff --git a/include/media/stagefright/AMRWriter.h b/include/media/stagefright/AMRWriter.h
index b0eaba4..813dd43 100644
--- a/include/media/stagefright/AMRWriter.h
+++ b/include/media/stagefright/AMRWriter.h
@@ -26,6 +26,7 @@
namespace android {
struct MediaSource;
+struct MetaData;
struct AMRWriter : public MediaWriter {
AMRWriter(const char *filename);
@@ -35,7 +36,7 @@
virtual status_t addSource(const sp<MediaSource> &source);
virtual bool reachedEOS();
- virtual status_t start();
+ virtual status_t start(MetaData *params = NULL);
virtual void stop();
virtual void pause();
diff --git a/include/media/stagefright/AudioSource.h b/include/media/stagefright/AudioSource.h
index f2001e1..628200d 100644
--- a/include/media/stagefright/AudioSource.h
+++ b/include/media/stagefright/AudioSource.h
@@ -39,6 +39,9 @@
virtual status_t stop();
virtual sp<MetaData> getFormat();
+ // Returns the maximum amplitude since last call.
+ int16_t getMaxAmplitude();
+
virtual status_t read(
MediaBuffer **buffer, const ReadOptions *options = NULL);
@@ -53,13 +56,17 @@
bool mStarted;
bool mCollectStats;
+ bool mTrackMaxAmplitude;
int64_t mTotalReadTimeUs;
int64_t mTotalReadBytes;
int64_t mTotalReads;
int64_t mStartTimeUs;
+ int16_t mMaxAmplitude;
MediaBufferGroup *mGroup;
+ void trackMaxAmplitude(int16_t *data, int nSamples);
+
AudioSource(const AudioSource &);
AudioSource &operator=(const AudioSource &);
};
diff --git a/include/media/stagefright/MPEG4Writer.h b/include/media/stagefright/MPEG4Writer.h
index 39d0ea1..7a2de1e 100644
--- a/include/media/stagefright/MPEG4Writer.h
+++ b/include/media/stagefright/MPEG4Writer.h
@@ -36,7 +36,7 @@
MPEG4Writer(int fd);
virtual status_t addSource(const sp<MediaSource> &source);
- virtual status_t start();
+ virtual status_t start(MetaData *param = NULL);
virtual bool reachedEOS();
virtual void stop();
virtual void pause();
@@ -83,6 +83,7 @@
int64_t getStartTimestampUs(); // Not const
status_t startTracks();
size_t numTracks();
+ int64_t estimateMoovBoxSize(int32_t bitRate);
void lock();
void unlock();
diff --git a/include/media/stagefright/MediaWriter.h b/include/media/stagefright/MediaWriter.h
index 8528203..46aaf7c 100644
--- a/include/media/stagefright/MediaWriter.h
+++ b/include/media/stagefright/MediaWriter.h
@@ -24,13 +24,14 @@
namespace android {
struct MediaSource;
+struct MetaData;
struct MediaWriter : public RefBase {
MediaWriter() {}
virtual status_t addSource(const sp<MediaSource> &source) = 0;
virtual bool reachedEOS() = 0;
- virtual status_t start() = 0;
+ virtual status_t start(MetaData *params = NULL) = 0;
virtual void stop() = 0;
virtual void pause() = 0;
virtual void setMaxFileSize(int64_t bytes) { mMaxFileSizeLimitBytes = bytes; }
diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h
index 6a20602..d28d1ca 100644
--- a/include/media/stagefright/MetaData.h
+++ b/include/media/stagefright/MetaData.h
@@ -36,13 +36,14 @@
kKeyStride = 'strd', // int32_t
kKeySliceHeight = 'slht', // int32_t
kKeyChannelCount = '#chn', // int32_t
- kKeySampleRate = 'srte', // int32_t
+ kKeySampleRate = 'srte', // int32_t (also video frame rate)
kKeyBitRate = 'brte', // int32_t (bps)
kKeyESDS = 'esds', // raw data
kKeyAVCC = 'avcc', // raw data
kKeyVorbisInfo = 'vinf', // raw data
kKeyVorbisBooks = 'vboo', // raw data
kKeyWantsNALFragments = 'NALf',
+ kKey64BitFileOffset = 'fobt', // int32_t (bool)
kKeyIsSyncFrame = 'sync', // int32_t (bool)
kKeyIsCodecConfig = 'conf', // int32_t (bool)
kKeyTime = 'time', // int64_t (usecs)
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index d49c4e0..6834491 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -316,6 +316,13 @@
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(
const String8 &key, const String8 &value) {
LOGV("setParameter: key (%s) => value (%s)", key.string(), value.string());
@@ -361,6 +368,11 @@
if (safe_strtoi32(value.string(), &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 {
LOGE("setParameter: failed to find key %s", key.string());
}
@@ -484,6 +496,7 @@
sp<MediaSource> audioEncoder =
OMXCodec::Create(client.interface(), encMeta,
true /* createEncoder */, audioSource);
+ mAudioSourceNode = audioSource;
return audioEncoder;
}
@@ -632,6 +645,7 @@
status_t StagefrightRecorder::startMPEG4Recording() {
mWriter = new MPEG4Writer(dup(mOutputFd));
+ int32_t totalBitRate = 0;
// Add audio source first if it exists
if (mAudioSource != AUDIO_SOURCE_LIST_END) {
@@ -650,7 +664,7 @@
if (audioEncoder == NULL) {
return UNKNOWN_ERROR;
}
-
+ totalBitRate += mAudioBitRate;
mWriter->addSource(audioEncoder);
}
if (mVideoSource == VIDEO_SOURCE_DEFAULT
@@ -703,7 +717,7 @@
sp<MetaData> enc_meta = new MetaData;
enc_meta->setInt32(kKeyBitRate, mVideoBitRate);
- enc_meta->setInt32(kKeySampleRate, mFrameRate); // XXX: kKeySampleRate?
+ enc_meta->setInt32(kKeySampleRate, mFrameRate);
switch (mVideoEncoder) {
case VIDEO_ENCODER_H263:
@@ -746,12 +760,13 @@
true /* createEncoder */, cameraSource);
CHECK(mOutputFd >= 0);
+ totalBitRate += mVideoBitRate;
mWriter->addSource(encoder);
}
{
// MPEGWriter specific handling
- MPEG4Writer *writer = ((MPEG4Writer *) mWriter.get()); // mWriter is an MPEGWriter
+ MPEG4Writer *writer = ((MPEG4Writer *) mWriter.get());
writer->setInterleaveDuration(mInterleaveDurationUs);
}
@@ -762,7 +777,10 @@
mWriter->setMaxFileSize(mMaxFileSizeBytes);
}
mWriter->setListener(mListener);
- mWriter->start();
+ sp<MetaData> meta = new MetaData;
+ meta->setInt32(kKeyBitRate, totalBitRate);
+ meta->setInt32(kKey64BitFileOffset, mUse64BitFileOffset);
+ mWriter->start(meta.get());
return OK;
}
@@ -822,6 +840,8 @@
mAudioBitRate = 12200;
mInterleaveDurationUs = 0;
mIFramesInterval = 1;
+ mAudioSourceNode = 0;
+ mUse64BitFileOffset = false;
mEncoderProfiles = MediaProfiles::getInstance();
mOutputFd = -1;
@@ -831,7 +851,11 @@
}
status_t StagefrightRecorder::getMaxAmplitude(int *max) {
- *max = 0;
+ if (mAudioSourceNode != 0) {
+ *max = mAudioSourceNode->getMaxAmplitude();
+ } else {
+ *max = 0;
+ }
return OK;
}
diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h
index 7de96f6..2943e97 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.h
+++ b/media/libmediaplayerservice/StagefrightRecorder.h
@@ -26,6 +26,7 @@
class Camera;
struct MediaSource;
struct MediaWriter;
+struct AudioSource;
class MediaProfiles;
struct StagefrightRecorder : public MediaRecorderBase {
@@ -64,12 +65,14 @@
sp<ISurface> mPreviewSurface;
sp<IMediaPlayerClient> mListener;
sp<MediaWriter> mWriter;
+ sp<AudioSource> mAudioSourceNode;
audio_source mAudioSource;
video_source mVideoSource;
output_format mOutputFormat;
audio_encoder mAudioEncoder;
video_encoder mVideoEncoder;
+ bool mUse64BitFileOffset;
int32_t mVideoWidth, mVideoHeight;
int32_t mFrameRate;
int32_t mVideoBitRate;
@@ -98,6 +101,7 @@
status_t setParamAudioSamplingRate(int32_t sampleRate);
status_t setParamInterleaveDuration(int32_t durationUs);
status_t setParamIFramesInterval(int32_t interval);
+ status_t setParam64BitFileOffset(bool use64BitFileOffset);
status_t setParamMaxDurationOrFileSize(int64_t limit, bool limit_is_duration);
void clipVideoBitRate();
void clipVideoFrameRate();
diff --git a/media/libstagefright/AMRWriter.cpp b/media/libstagefright/AMRWriter.cpp
index 8951f5b..6d1dd16 100644
--- a/media/libstagefright/AMRWriter.cpp
+++ b/media/libstagefright/AMRWriter.cpp
@@ -97,7 +97,7 @@
return OK;
}
-status_t AMRWriter::start() {
+status_t AMRWriter::start(MetaData *params) {
if (mInitCheck != OK) {
return mInitCheck;
}
diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp
index d203dbf..6031797 100644
--- a/media/libstagefright/AudioSource.cpp
+++ b/media/libstagefright/AudioSource.cpp
@@ -78,6 +78,8 @@
mCollectStats = true;
}
+ mTrackMaxAmplitude = false;
+ mMaxAmplitude = 0;
mStartTimeUs = 0;
int64_t startTimeUs;
if (params && params->findInt64(kKeyTime, &startTimeUs)) {
@@ -168,6 +170,10 @@
return (status_t)n;
}
+ if (mTrackMaxAmplitude) {
+ trackMaxAmplitude((int16_t *) buffer->data(), n >> 1);
+ }
+
uint32_t sampleRate = mRecord->getSampleRate();
int64_t timestampUs = (1000000LL * numFramesRecorded) / sampleRate + mStartTimeUs;
buffer->meta_data()->setInt64(kKeyTime, timestampUs);
@@ -181,4 +187,27 @@
return OK;
}
+void AudioSource::trackMaxAmplitude(int16_t *data, int nSamples) {
+ for (int i = nSamples; i > 0; --i) {
+ int16_t value = *data++;
+ if (value < 0) {
+ value = -value;
+ }
+ if (mMaxAmplitude < value) {
+ mMaxAmplitude = value;
+ }
+ }
+}
+
+int16_t AudioSource::getMaxAmplitude() {
+ // First call activates the tracking.
+ if (!mTrackMaxAmplitude) {
+ mTrackMaxAmplitude = true;
+ }
+ int16_t value = mMaxAmplitude;
+ mMaxAmplitude = 0;
+ LOGV("max amplitude since last call: %d", value);
+ return value;
+}
+
} // namespace android
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index f16b225..65d109b 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -180,11 +180,72 @@
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) {
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;
if (mStarted) {
if (mPaused) {
@@ -208,9 +269,11 @@
mFreeBoxOffset = mOffset;
if (mEstimatedMoovBoxSize == 0) {
- // XXX: Estimate the moov box size
- // based on max file size or duration limit
- mEstimatedMoovBoxSize = 0x0F00;
+ int32_t bitRate = -1;
+ if (param) {
+ param->findInt32(kKeyBitRate, &bitRate);
+ }
+ mEstimatedMoovBoxSize = estimateMoovBoxSize(bitRate);
}
CHECK(mEstimatedMoovBoxSize >= 8);
fseeko(mFile, mFreeBoxOffset, SEEK_SET);
@@ -332,8 +395,7 @@
write(mMoovBoxBuffer, 1, mMoovBoxBufferOffset, mFile);
// Free box
- mFreeBoxOffset = mStreamableFile? mOffset: mFreeBoxOffset;
- fseeko(mFile, mFreeBoxOffset, SEEK_SET);
+ fseeko(mFile, mOffset, SEEK_SET);
writeInt32(mEstimatedMoovBoxSize - mMoovBoxBufferOffset);
write("free", 4);
@@ -341,6 +403,8 @@
free(mMoovBoxBuffer);
mMoovBoxBuffer = NULL;
mMoovBoxBufferOffset = 0;
+ } else {
+ LOGI("The mp4 file will not be streamable.");
}
CHECK(mBoxes.empty());