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());