Provide progress status report during authoring

- Track either the number of A/V frames authored, or the time elapsed
- Track the completion of the authoring
- Add multiple camera support for authoring by accepting a camera id parameter
- Set file type based on the OUTPUT_FORMAT requested

Change-Id: I0f9d31b3b7a8fa43eb53f572410fb0ebd4fa0bb7
diff --git a/include/media/mediarecorder.h b/include/media/mediarecorder.h
index eead166..4f4ec43 100644
--- a/include/media/mediarecorder.h
+++ b/include/media/mediarecorder.h
@@ -136,7 +136,9 @@
     MEDIA_RECORDER_INFO_UNKNOWN                   = 1,
     MEDIA_RECORDER_INFO_MAX_DURATION_REACHED      = 800,
     MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED      = 801,
-    MEDIA_RECORDER_INFO_STOP_PREMATURELY          = 802
+    MEDIA_RECORDER_INFO_COMPLETION_STATUS         = 802,
+    MEDIA_RECORDER_INFO_PROGRESS_FRAME_STATUS     = 803,
+    MEDIA_RECORDER_INFO_PROGRESS_TIME_STATUS      = 804,
 };
 
 // ----------------------------------------------------------------------------
diff --git a/include/media/stagefright/MPEG4Writer.h b/include/media/stagefright/MPEG4Writer.h
index 7a2de1e..962b38b 100644
--- a/include/media/stagefright/MPEG4Writer.h
+++ b/include/media/stagefright/MPEG4Writer.h
@@ -81,7 +81,7 @@
 
     void setStartTimestampUs(int64_t timeUs);
     int64_t getStartTimestampUs();  // Not const
-    status_t startTracks();
+    status_t startTracks(MetaData *params);
     size_t numTracks();
     int64_t estimateMoovBoxSize(int32_t bitRate);
 
diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h
index d28d1ca..95fe6f6 100644
--- a/include/media/stagefright/MetaData.h
+++ b/include/media/stagefright/MetaData.h
@@ -43,7 +43,6 @@
     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)
@@ -69,6 +68,21 @@
     kKeyDiscNumber        = 'dnum',  // cstring
     kKeyDate              = 'date',  // cstring
     kKeyWriter            = 'writ',  // cstring
+
+    // Set this key to enable authoring files in 64-bit offset
+    kKey64BitFileOffset   = 'fobt',  // int32_t (bool)
+
+    // Identify the file output format for authoring
+    // Please see <media/mediarecorder.h> for the supported
+    // file output formats.
+    kKeyFileType          = 'ftyp',  // int32_t
+
+    // Track authoring progress status
+    // kKeyTrackTimeStatus is used to track progress in elapsed time
+    // kKeyTrackFrameStatus is used to track progress in authored frames
+    kKeyTrackFrameStatus  = 'tkfm',  // int32_t
+    kKeyTrackTimeStatus   = 'tktm',  // int64_t
+
 };
 
 enum {
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index 6834491..1e20f7e 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -310,8 +310,8 @@
 // If interval <  0, only the first frame is I frame, and rest are all P frames
 // If interval == 0, all frames are encoded as I frames. No P frames
 // If interval >  0, it is the time spacing between 2 neighboring I frames
-status_t StagefrightRecorder::setParamIFramesInterval(int32_t interval) {
-    LOGV("setParamIFramesInterval: %d seconds", interval);
+status_t StagefrightRecorder::setParamVideoIFramesInterval(int32_t interval) {
+    LOGV("setParamVideoIFramesInterval: %d seconds", interval);
     mIFramesInterval = interval;
     return OK;
 }
@@ -323,6 +323,33 @@
     return OK;
 }
 
+status_t StagefrightRecorder::setParamVideoCameraId(int32_t cameraId) {
+    LOGV("setParamVideoCameraId: %d", cameraId);
+    if (cameraId < 0) {
+        return BAD_VALUE;
+    }
+    mCameraId = cameraId;
+    return OK;
+}
+
+status_t StagefrightRecorder::setParamTrackFrameStatus(int32_t nFrames) {
+    LOGV("setParamTrackFrameStatus: %d", nFrames);
+    if (nFrames <= 0) {
+        return BAD_VALUE;
+    }
+    mTrackEveryNumberOfFrames = nFrames;
+    return OK;
+}
+
+status_t StagefrightRecorder::setParamTrackTimeStatus(int64_t timeDurationUs) {
+    LOGV("setParamTrackTimeStatus: %lld", timeDurationUs);
+    if (timeDurationUs < 20000) {  // Infeasible if shorter than 20 ms?
+        return BAD_VALUE;
+    }
+    mTrackEveryTimeDurationUs = timeDurationUs;
+    return OK;
+}
+
 status_t StagefrightRecorder::setParameter(
         const String8 &key, const String8 &value) {
     LOGV("setParameter: key (%s) => value (%s)", key.string(), value.string());
@@ -338,6 +365,26 @@
             return setParamMaxDurationOrFileSize(
                     max_filesize_bytes, false /* limit is filesize */);
         }
+    } else if (key == "interleave-duration-us") {
+        int32_t durationUs;
+        if (safe_strtoi32(value.string(), &durationUs)) {
+            return setParamInterleaveDuration(durationUs);
+        }
+    } else if (key == "param-use-64bit-offset") {
+        int32_t use64BitOffset;
+        if (safe_strtoi32(value.string(), &use64BitOffset)) {
+            return setParam64BitFileOffset(use64BitOffset != 0);
+        }
+    } else if (key == "param-track-frame-status") {
+        int32_t nFrames;
+        if (safe_strtoi32(value.string(), &nFrames)) {
+            return setParamTrackFrameStatus(nFrames);
+        }
+    } else if (key == "param-track-time-status") {
+        int64_t timeDurationUs;
+        if (safe_strtoi64(value.string(), &timeDurationUs)) {
+            return setParamTrackTimeStatus(timeDurationUs);
+        }
     } else if (key == "audio-param-sampling-rate") {
         int32_t sampling_rate;
         if (safe_strtoi32(value.string(), &sampling_rate)) {
@@ -358,20 +405,15 @@
         if (safe_strtoi32(value.string(), &video_bitrate)) {
             return setParamVideoEncodingBitRate(video_bitrate);
         }
-    } else if (key == "param-interleave-duration-us") {
-        int32_t durationUs;
-        if (safe_strtoi32(value.string(), &durationUs)) {
-            return setParamInterleaveDuration(durationUs);
-        }
-    } else if (key == "param-i-frames-interval") {
+    } else if (key == "video-param-i-frames-interval") {
         int32_t interval;
         if (safe_strtoi32(value.string(), &interval)) {
-            return setParamIFramesInterval(interval);
+            return setParamVideoIFramesInterval(interval);
         }
-    } else if (key == "param-use-64bit-offset") {
-        int32_t use64BitOffset;
-        if (safe_strtoi32(value.string(), &use64BitOffset)) {
-            return setParam64BitFileOffset(use64BitOffset != 0);
+    } else if (key == "video-param-camera-id") {
+        int32_t cameraId;
+        if (safe_strtoi32(value.string(), &cameraId)) {
+            return setParamVideoCameraId(cameraId);
         }
     } else {
         LOGE("setParameter: failed to find key %s", key.string());
@@ -677,7 +719,7 @@
 
         int64_t token = IPCThreadState::self()->clearCallingIdentity();
         if (mCamera == 0) {
-            mCamera = Camera::connect(0);
+            mCamera = Camera::connect(mCameraId);
             mCamera->lock();
         }
 
@@ -778,8 +820,16 @@
     }
     mWriter->setListener(mListener);
     sp<MetaData> meta = new MetaData;
+    meta->setInt64(kKeyTime, systemTime() / 1000);
+    meta->setInt32(kKeyFileType, mOutputFormat);
     meta->setInt32(kKeyBitRate, totalBitRate);
     meta->setInt32(kKey64BitFileOffset, mUse64BitFileOffset);
+    if (mTrackEveryNumberOfFrames > 0) {
+        meta->setInt32(kKeyTrackFrameStatus, mTrackEveryNumberOfFrames);
+    }
+    if (mTrackEveryTimeDurationUs > 0) {
+        meta->setInt64(kKeyTrackTimeStatus, mTrackEveryTimeDurationUs);
+    }
     mWriter->start(meta.get());
     return OK;
 }
@@ -842,6 +892,9 @@
     mIFramesInterval = 1;
     mAudioSourceNode = 0;
     mUse64BitFileOffset = false;
+    mCameraId        = 0;
+    mTrackEveryNumberOfFrames = 0;
+    mTrackEveryTimeDurationUs = 0;
     mEncoderProfiles = MediaProfiles::getInstance();
 
     mOutputFd = -1;
diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h
index 2943e97..9fb7e8f 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.h
+++ b/media/libmediaplayerservice/StagefrightRecorder.h
@@ -81,8 +81,11 @@
     int32_t mSampleRate;
     int32_t mInterleaveDurationUs;
     int32_t mIFramesInterval;
+    int32_t mCameraId;
     int64_t mMaxFileSizeBytes;
     int64_t mMaxFileDurationUs;
+    int32_t mTrackEveryNumberOfFrames;
+    int64_t mTrackEveryTimeDurationUs;
 
     String8 mParams;
     int mOutputFd;
@@ -95,12 +98,15 @@
     status_t startAACRecording();
     sp<MediaSource> createAudioSource();
     status_t setParameter(const String8 &key, const String8 &value);
-    status_t setParamVideoEncodingBitRate(int32_t bitRate);
     status_t setParamAudioEncodingBitRate(int32_t bitRate);
     status_t setParamAudioNumberOfChannels(int32_t channles);
     status_t setParamAudioSamplingRate(int32_t sampleRate);
+    status_t setParamVideoEncodingBitRate(int32_t bitRate);
+    status_t setParamVideoIFramesInterval(int32_t interval);
+    status_t setParamVideoCameraId(int32_t cameraId);
+    status_t setParamTrackTimeStatus(int64_t timeDurationUs);
+    status_t setParamTrackFrameStatus(int32_t nFrames);
     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();
diff --git a/media/libstagefright/AMRWriter.cpp b/media/libstagefright/AMRWriter.cpp
index 6d1dd16..c71743e 100644
--- a/media/libstagefright/AMRWriter.cpp
+++ b/media/libstagefright/AMRWriter.cpp
@@ -253,7 +253,7 @@
     }
 
     if (stoppedPrematurely) {
-        notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_STOP_PREMATURELY, 0);
+        notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_COMPLETION_STATUS, UNKNOWN_ERROR);
     }
 
     fflush(mFile);
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 65d109b..b3e1a01 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -41,7 +41,7 @@
     Track(MPEG4Writer *owner, const sp<MediaSource> &source);
     ~Track();
 
-    status_t start(int64_t startTimeUs);
+    status_t start(MetaData *params);
     void stop();
     void pause();
     bool reachedEOS();
@@ -101,9 +101,13 @@
     void *mCodecSpecificData;
     size_t mCodecSpecificDataSize;
     bool mGotAllCodecSpecificData;
+    bool mTrackingProgressStatus;
 
     bool mReachedEOS;
     int64_t mStartTimestampUs;
+    int64_t mPreviousTrackTimeUs;
+    int64_t mTrackEveryTimeDurationUs;
+    int32_t mTrackEveryNumberOfFrames;
 
     static void *ThreadWrapper(void *me);
     void threadEntry();
@@ -114,6 +118,8 @@
     void logStatisticalData(bool isAudio);
     void findMinMaxFrameRates(float *minFps, float *maxFps);
     void findMinMaxChunkDurations(int64_t *min, int64_t *max);
+    void trackProgressStatus(int32_t nFrames, int64_t timeUs);
+    void initTrackingProgressStatus(MetaData *params);
 
     Track(const Track &);
     Track &operator=(const Track &);
@@ -162,11 +168,10 @@
     return OK;
 }
 
-status_t MPEG4Writer::startTracks() {
-    int64_t startTimeUs = systemTime() / 1000;
+status_t MPEG4Writer::startTracks(MetaData *params) {
     for (List<Track *>::iterator it = mTracks.begin();
          it != mTracks.end(); ++it) {
-        status_t err = (*it)->start(startTimeUs);
+        status_t err = (*it)->start(params);
 
         if (err != OK) {
             for (List<Track *>::iterator it2 = mTracks.begin();
@@ -247,10 +252,11 @@
     }
 
     mStartTimestampUs = -1;
+
     if (mStarted) {
         if (mPaused) {
             mPaused = false;
-            return startTracks();
+            return startTracks(param);
         }
         return OK;
     }
@@ -261,9 +267,18 @@
     mMoovBoxBufferOffset = 0;
 
     beginBox("ftyp");
-      writeFourcc("isom");
+      {
+        int32_t fileType;
+        if (param && param->findInt32(kKeyFileType, &fileType) &&
+            fileType != OUTPUT_FORMAT_MPEG_4) {
+            writeFourcc("3gp4");
+        } else {
+            writeFourcc("isom");
+        }
+      }
       writeInt32(0);
       writeFourcc("isom");
+      writeFourcc("3gp4");
     endBox();
 
     mFreeBoxOffset = mOffset;
@@ -288,8 +303,7 @@
     } else {
         write("\x00\x00\x00\x01mdat????????", 16);
     }
-
-    status_t err = startTracks();
+    status_t err = startTracks(param);
     if (err != OK) {
         return err;
     }
@@ -670,13 +684,41 @@
     }
 }
 
-status_t MPEG4Writer::Track::start(int64_t startTimeUs) {
+void MPEG4Writer::Track::initTrackingProgressStatus(MetaData *params) {
+    LOGV("initTrackingProgressStatus");
+    mPreviousTrackTimeUs = -1;
+    mTrackingProgressStatus = false;
+    mTrackEveryTimeDurationUs = 0;
+    mTrackEveryNumberOfFrames = 0;
+    {
+        int64_t timeUs;
+        if (params && params->findInt64(kKeyTrackTimeStatus, &timeUs)) {
+            LOGV("Receive request to track progress status for every %lld us", timeUs);
+            mTrackEveryTimeDurationUs = timeUs;
+            mTrackingProgressStatus = true;
+        }
+    }
+    {
+        int32_t nFrames;
+        if (params && params->findInt32(kKeyTrackFrameStatus, &nFrames)) {
+            LOGV("Receive request to track progress status for every %d frames", nFrames);
+            mTrackEveryNumberOfFrames = nFrames;
+            mTrackingProgressStatus = true;
+        }
+    }
+}
+
+status_t MPEG4Writer::Track::start(MetaData *params) {
     if (!mDone && mPaused) {
         mPaused = false;
         mResumed = true;
         return OK;
     }
 
+    int64_t startTimeUs;
+    CHECK(params && params->findInt64(kKeyTime, &startTimeUs));
+    initTrackingProgressStatus(params);
+
     sp<MetaData> meta = new MetaData;
     meta->setInt64(kKeyTime, startTimeUs);
     status_t err = mSource->start(meta.get());
@@ -848,8 +890,9 @@
     int64_t previousPausedDurationUs = 0;
     sp<MetaData> meta_data;
 
+    status_t err = OK;
     MediaBuffer *buffer;
-    while (!mDone && mSource->read(&buffer) == OK) {
+    while (!mDone && (err = mSource->read(&buffer)) == OK) {
         if (buffer->range_length() == 0) {
             buffer->release();
             buffer = NULL;
@@ -1062,6 +1105,12 @@
             mStssTableEntries.push_back(mSampleInfos.size());
         }
 
+        if (mTrackingProgressStatus) {
+            if (mPreviousTrackTimeUs <= 0) {
+                mPreviousTrackTimeUs = mStartTimestampUs;
+            }
+            trackProgressStatus(mSampleInfos.size(), timestampUs);
+        }
         if (mOwner->numTracks() == 1) {
             off_t offset = is_avc? mOwner->addLengthPrefixedSample_l(copy)
                                  : mOwner->addSample_l(copy);
@@ -1101,8 +1150,9 @@
     }
 
     if (mSampleInfos.empty()) {
-        mOwner->notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_STOP_PREMATURELY, 0);
+        err = UNKNOWN_ERROR;
     }
+    mOwner->notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_COMPLETION_STATUS, err);
 
     // Last chunk
     if (mOwner->numTracks() == 1) {
@@ -1132,6 +1182,24 @@
     logStatisticalData(is_audio);
 }
 
+void MPEG4Writer::Track::trackProgressStatus(int32_t nFrames, int64_t timeUs) {
+    LOGV("trackProgressStatus: %d frames and %lld us", nFrames, timeUs);
+    if (nFrames % mTrackEveryNumberOfFrames == 0) {
+        LOGV("Fire frame tracking progress status at frame %d", nFrames);
+        mOwner->notify(MEDIA_RECORDER_EVENT_INFO,
+                       MEDIA_RECORDER_INFO_PROGRESS_FRAME_STATUS,
+                       nFrames);
+    }
+
+    if (timeUs - mPreviousTrackTimeUs >= mTrackEveryTimeDurationUs) {
+        LOGV("Fire time tracking progress status at %lld us", timeUs);
+        mOwner->notify(MEDIA_RECORDER_EVENT_INFO,
+                       MEDIA_RECORDER_INFO_PROGRESS_TIME_STATUS,
+                       timeUs / 1000);
+        mPreviousTrackTimeUs = timeUs;
+    }
+}
+
 void MPEG4Writer::Track::findMinMaxFrameRates(float *minFps, float *maxFps) {
     int32_t minSampleDuration = 0x7FFFFFFF;
     int32_t maxSampleDuration = 0;