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
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/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 ba04d1b..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());
     }
@@ -633,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) {
@@ -651,7 +664,7 @@
         if (audioEncoder == NULL) {
             return UNKNOWN_ERROR;
         }
-
+        totalBitRate += mAudioBitRate;
         mWriter->addSource(audioEncoder);
     }
     if (mVideoSource == VIDEO_SOURCE_DEFAULT
@@ -704,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:
@@ -747,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);
     }
 
@@ -763,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;
 }
 
@@ -824,6 +841,7 @@
     mInterleaveDurationUs = 0;
     mIFramesInterval = 1;
     mAudioSourceNode = 0;
+    mUse64BitFileOffset = false;
     mEncoderProfiles = MediaProfiles::getInstance();
 
     mOutputFd = -1;
diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h
index 6eedf0c..2943e97 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.h
+++ b/media/libmediaplayerservice/StagefrightRecorder.h
@@ -72,6 +72,7 @@
     output_format mOutputFormat;
     audio_encoder mAudioEncoder;
     video_encoder mVideoEncoder;
+    bool mUse64BitFileOffset;
     int32_t mVideoWidth, mVideoHeight;
     int32_t mFrameRate;
     int32_t mVideoBitRate;
@@ -100,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/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());