stagefright: Forward-port HFR and HSR support

 * CAF commit bd42a7ac3a60c0d8a079b4567484c9b006bac8ad upstream

Change-Id: I457ccab603647f3139ea2199a544f64ac3d1a214
diff --git a/include/media/stagefright/MPEG4Writer.h b/include/media/stagefright/MPEG4Writer.h
index 055c079..aeaad8f 100644
--- a/include/media/stagefright/MPEG4Writer.h
+++ b/include/media/stagefright/MPEG4Writer.h
@@ -186,6 +186,9 @@
     // To use 3gp4 box for clips with AMR audio
     bool mIsAudioAMR;
 
+    // HFR scale
+    uint32_t mHFRRatio;
+
     void lock();
     void unlock();
 
diff --git a/media/libavextensions/stagefright/AVExtensions.h b/media/libavextensions/stagefright/AVExtensions.h
index f14d217..968eb6d 100644
--- a/media/libavextensions/stagefright/AVExtensions.h
+++ b/media/libavextensions/stagefright/AVExtensions.h
@@ -153,7 +153,7 @@
             uint64_t /*eAacProfile*/);
 
     virtual void extractCustomCameraKeys(
-            const CameraParameters& /*params*/, sp<MetaData> &/*meta*/) {}
+            const CameraParameters& /*params*/, sp<MetaData> &/*meta*/);
     virtual void printFileName(int /*fd*/) {}
     virtual void addDecodingTimesFromBatch(MediaBuffer * /*buf*/,
             List<int64_t> &/*decodeTimeQueue*/) {}
@@ -226,12 +226,66 @@
                 int nPFrames, int nBFrames, const sp<IOMX> OMXhandle,
                 IOMX::node_id nodeID);
 
+    /*
+     * This class is a placeholder for the set of methods used
+     * to enable HFR (High Frame Rate) Recording
+     *
+     * HFR is a slow-motion recording feature where framerate
+     * is increased at capture, but file is composed to play
+     * back at normal rate, giving a net result of slow-motion.
+     * If HFR factor = N
+     *   framerate (at capture and encoder) = N * actual value
+     *   bitrate = N * actual value
+     *      (as the encoder still gets actual timestamps)
+     *   timeStamps (at composition) = actual value
+     *   timeScale (at composition) = actual value / N
+     *      (when parser re-generates timestamps, they will be
+     *       up-scaled by factor N, which results in slow-motion)
+     *
+     * HSR is a high-framerate recording variant where timestamps
+     * are not meddled with, yielding a video mux'ed at captured
+     * fps
+     */
+    struct HFR {
+        // set kKeyHFR when 'video-hfr' paramater is enabled
+        // or set kKeyHSR when 'video-hsr' paramater is enabled
+        virtual void setHFRIfEnabled(
+                const CameraParameters& params, sp<MetaData> &meta);
+
+        // recalculate file-duration when HFR is enabled
+        virtual status_t initializeHFR(
+                const sp<MetaData> &meta, sp<AMessage> &format,
+                int64_t &maxFileDurationUs, video_encoder videoEncoder);
+
+        virtual void setHFRRatio(
+                sp<MetaData> &meta, const int32_t hfrRatio);
+
+        virtual int32_t getHFRRatio(
+                const sp<MetaData> &meta);
+
+        protected:
+        HFR() {};
+        virtual ~HFR() {};
+        friend struct AVUtils;
+
+        private:
+        // Query supported capabilities from target-specific profiles
+        virtual int32_t getHFRCapabilities(
+                video_encoder codec,
+                int& maxHFRWidth, int& maxHFRHeight, int& maxHFRFps,
+                int& maxBitrate);
+    };
+    virtual inline HFR& HFRUtils() {
+         return mHFR;
+    }
+
 private:
     HEVCMuxer mHEVCMuxer;
+    HFR mHFR;
     // ----- NO TRESSPASSING BEYOND THIS LINE ------
     DECLARE_LOADABLE_SINGLETON(AVUtils);
-};
 
+};
 }
 
 #endif // _AV_EXTENSIONS__H_
diff --git a/media/libavextensions/stagefright/AVUtils.cpp b/media/libavextensions/stagefright/AVUtils.cpp
index 35ae36b..298434f 100644
--- a/media/libavextensions/stagefright/AVUtils.cpp
+++ b/media/libavextensions/stagefright/AVUtils.cpp
@@ -41,6 +41,7 @@
 #include <media/stagefright/MediaCodec.h>
 #include <media/stagefright/MPEG4Writer.h>
 #include <media/stagefright/Utils.h>
+#include <media/MediaProfiles.h>
 
 #if defined(QCOM_HARDWARE) || defined(FLAC_OFFLOAD_ENABLED)
 #include "QCMediaDefs.h"
@@ -974,6 +975,130 @@
     return;
 }
 
+void AVUtils::HFR::setHFRIfEnabled(
+        const CameraParameters& params,
+        sp<MetaData> &meta) {
+    const char *hfrParam = params.get("video-hfr");
+    int32_t hfr = -1;
+    if (hfrParam != NULL) {
+        hfr = atoi(hfrParam);
+        if (hfr > 0) {
+            ALOGI("Enabling HFR @ %d fps", hfr);
+            meta->setInt32(kKeyHFR, hfr);
+            return;
+        } else {
+            ALOGI("Invalid HFR rate specified : %d", hfr);
+        }
+    }
+
+    const char *hsrParam = params.get("video-hsr");
+    int32_t hsr = -1;
+    if (hsrParam != NULL ) {
+        hsr = atoi(hsrParam);
+        if (hsr > 0) {
+            ALOGI("Enabling HSR @ %d fps", hsr);
+            meta->setInt32(kKeyHSR, hsr);
+        } else {
+            ALOGI("Invalid HSR rate specified : %d", hfr);
+        }
+    }
+}
+
+status_t AVUtils::HFR::initializeHFR(
+        const sp<MetaData> &meta, sp<AMessage> &format,
+        int64_t & /*maxFileDurationUs*/, video_encoder videoEncoder) {
+    status_t retVal = OK;
+
+    int32_t hsr = 0;
+    if (meta->findInt32(kKeyHSR, &hsr) && hsr > 0) {
+        ALOGI("HSR cue found. Override encode fps to %d", hsr);
+        format->setInt32("frame-rate", hsr);
+        return retVal;
+    }
+
+    int32_t hfr = 0;
+    if (!meta->findInt32(kKeyHFR, &hfr) || (hfr <= 0)) {
+        ALOGW("Invalid HFR rate specified");
+        return retVal;
+    }
+
+    int32_t width = 0, height = 0;
+    CHECK(meta->findInt32(kKeyWidth, &width));
+    CHECK(meta->findInt32(kKeyHeight, &height));
+
+    int maxW, maxH, MaxFrameRate, maxBitRate = 0;
+    if (getHFRCapabilities(videoEncoder,
+            maxW, maxH, MaxFrameRate, maxBitRate) < 0) {
+        ALOGE("Failed to query HFR target capabilities");
+        return ERROR_UNSUPPORTED;
+    }
+
+    if ((width * height * hfr) > (maxW * maxH * MaxFrameRate)) {
+        ALOGE("HFR request [%d x %d @%d fps] exceeds "
+                "[%d x %d @%d fps]. Will stay disabled",
+                width, height, hfr, maxW, maxH, MaxFrameRate);
+        return ERROR_UNSUPPORTED;
+    }
+
+    int32_t frameRate = 0, bitRate = 0;
+    CHECK(meta->findInt32(kKeyFrameRate, &frameRate));
+    CHECK(format->findInt32("bitrate", &bitRate));
+
+    if (frameRate) {
+        // scale the bitrate proportional to the hfr ratio
+        // to maintain quality, but cap it to max-supported.
+        bitRate = (hfr * bitRate) / frameRate;
+        bitRate = bitRate > maxBitRate ? maxBitRate : bitRate;
+        format->setInt32("bitrate", bitRate);
+
+        int32_t hfrRatio = hfr / frameRate;
+        format->setInt32("frame-rate", hfr);
+        format->setInt32("hfr-ratio", hfrRatio);
+    } else {
+        ALOGE("HFR: Invalid framerate");
+        return BAD_VALUE;
+    }
+
+    return retVal;
+}
+
+void AVUtils::HFR::setHFRRatio(
+        sp<MetaData> &meta, const int32_t hfrRatio) {
+    if (hfrRatio > 0) {
+        meta->setInt32(kKeyHFR, hfrRatio);
+    }
+}
+
+int32_t AVUtils::HFR::getHFRRatio(
+        const sp<MetaData> &meta) {
+    int32_t hfrRatio = 0;
+    meta->findInt32(kKeyHFR, &hfrRatio);
+    return hfrRatio ? hfrRatio : 1;
+}
+
+int32_t AVUtils::HFR::getHFRCapabilities(
+        video_encoder codec,
+        int& maxHFRWidth, int& maxHFRHeight, int& maxHFRFps,
+        int& maxBitRate) {
+    maxHFRWidth = maxHFRHeight = maxHFRFps = maxBitRate = 0;
+    MediaProfiles *profiles = MediaProfiles::getInstance();
+
+    if (profiles) {
+        maxHFRWidth = profiles->getVideoEncoderParamByName("enc.vid.hfr.width.max", codec);
+        maxHFRHeight = profiles->getVideoEncoderParamByName("enc.vid.hfr.height.max", codec);
+        maxHFRFps = profiles->getVideoEncoderParamByName("enc.vid.hfr.mode.max", codec);
+        maxBitRate = profiles->getVideoEncoderParamByName("enc.vid.bps.max", codec);
+    }
+
+    return (maxHFRWidth > 0) && (maxHFRHeight > 0) &&
+            (maxHFRFps > 0) && (maxBitRate > 0) ? 1 : -1;
+}
+
+void AVUtils::extractCustomCameraKeys(
+        const CameraParameters& params, sp<MetaData> &meta) {
+    mHFR.setHFRIfEnabled(params, meta);
+}
+
 // ----- NO TRESSPASSING BEYOND THIS LINE ------
 AVUtils::AVUtils() {}
 
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index 6baca4d..189bf8f 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -1622,12 +1622,12 @@
         }
     }
 
-    setupCustomVideoEncoderParams(cameraSource, format);
-
     format->setInt32("bitrate", mVideoBitRate);
     format->setInt32("frame-rate", mFrameRate);
     format->setInt32("i-frame-interval", mIFramesIntervalSec);
 
+    setupCustomVideoEncoderParams(cameraSource, format);
+
     if (mVideoTimeScale > 0) {
         format->setInt32("time-scale", mVideoTimeScale);
     }
@@ -2094,4 +2094,13 @@
     }
     return err;
 }
+
+void StagefrightRecorder::setupCustomVideoEncoderParams(sp<MediaSource> cameraSource,
+        sp<AMessage> &format) {
+
+    // Setup HFR if needed
+    AVUtils::get()->HFRUtils().initializeHFR(cameraSource->getFormat(), format,
+            mMaxFileDurationUs, mVideoEncoder);
+}
+
 }  // namespace android
diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h
index 72dc77b..d93fc3b 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.h
+++ b/media/libmediaplayerservice/StagefrightRecorder.h
@@ -154,8 +154,8 @@
     status_t setupCameraSource(sp<CameraSource> *cameraSource);
     status_t setupAudioEncoder(const sp<MediaWriter>& writer);
     virtual status_t setupVideoEncoder(sp<MediaSource> cameraSource, sp<MediaSource> *source);
-    virtual void setupCustomVideoEncoderParams(sp<MediaSource> /*cameraSource*/,
-            sp<AMessage> &/*format*/) {}
+    virtual void setupCustomVideoEncoderParams(sp<MediaSource> cameraSource,
+            sp<AMessage> &format);
     virtual bool setCustomVideoEncoderMime(const video_encoder videoEncoder, sp<AMessage> format);
 
     // Encoding parameter handling utilities
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 7f7ddf7..2f1b6ac 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -321,6 +321,7 @@
     // Simple validation on the codec specific data
     status_t checkCodecSpecificData() const;
     int32_t mRotation;
+    int32_t mHFRRatio;
 
     void updateTrackSizeEstimate();
     void addOneStscTableEntry(size_t chunkId, size_t sampleId);
@@ -390,7 +391,8 @@
       mStartTimeOffsetMs(-1),
       mMetaKeys(new AMessage()),
       mIsVideoHEVC(false),
-      mIsAudioAMR(false) {
+      mIsAudioAMR(false),
+      mHFRRatio(1) {
     addDeviceMeta();
 
     // Verify mFd is seekable
@@ -532,6 +534,8 @@
     Track *track = new Track(this, source, 1 + mTracks.size());
     mTracks.push_back(track);
 
+    mHFRRatio = AVUtils::get()->HFRUtils().getHFRRatio(source->getFormat());
+
     return OK;
 }
 
@@ -1029,7 +1033,7 @@
     writeInt32(0);             // version=0, flags=0
     writeInt32(now);           // creation time
     writeInt32(now);           // modification time
-    writeInt32(mTimeScale);    // mvhd timescale
+    writeInt32(mTimeScale / mHFRRatio);    // mvhd timescale
     int32_t duration = (durationUs * mTimeScale + 5E5) / 1E6;
     writeInt32(duration);
     writeInt32(0x10000);       // rate: 1.0
@@ -1477,7 +1481,8 @@
       mCodecSpecificDataSize(0),
       mGotAllCodecSpecificData(false),
       mReachedEOS(false),
-      mRotation(0) {
+      mRotation(0),
+      mHFRRatio(1) {
     getCodecSpecificDataFromInputFormatIfPossible();
 
     const char *mime;
@@ -1882,6 +1887,8 @@
     pthread_create(&mThread, &attr, ThreadWrapper, this);
     pthread_attr_destroy(&attr);
 
+    mHFRRatio = AVUtils::get()->HFRUtils().getHFRRatio(mMeta);
+
     return OK;
 }
 
@@ -3069,7 +3076,7 @@
     mOwner->writeInt32(0);             // version=0, flags=0
     mOwner->writeInt32(now);           // creation time
     mOwner->writeInt32(now);           // modification time
-    mOwner->writeInt32(mTimeScale);    // media timescale
+    mOwner->writeInt32(mTimeScale / mHFRRatio);    // media timescale
     int32_t mdhdDuration = (trakDurationUs * mTimeScale + 5E5) / 1E6;
     mOwner->writeInt32(mdhdDuration);  // use media timescale
     // Language follows the three letter standard ISO-639-2/T
diff --git a/media/libstagefright/MediaCodecSource.cpp b/media/libstagefright/MediaCodecSource.cpp
index aef6715..adbde54 100644
--- a/media/libstagefright/MediaCodecSource.cpp
+++ b/media/libstagefright/MediaCodecSource.cpp
@@ -429,9 +429,14 @@
         return err;
     }
 
+    int32_t hfrRatio = 0;
+    mOutputFormat->findInt32("hfr-ratio", &hfrRatio);
+
     mEncoder->getOutputFormat(&mOutputFormat);
     convertMessageToMetaData(mOutputFormat, mMeta);
 
+    AVUtils::get()->HFRUtils().setHFRRatio(mMeta, hfrRatio);
+
     if (mFlags & FLAG_USE_SURFACE_INPUT) {
         CHECK(mIsVideo);