Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 39e93781 authored by Praveen Chavan's avatar Praveen Chavan Committed by Linux Build Service Account
Browse files

stagefright: Add support for high frame rate video 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.

Include timeless HFR fixes:
  Correct total duration of HFR clips
  Avoid multiplying video duration by HFR ratio
  HSR: Check if HSR enabled, use HFR value for HSR
  HSR: Read framerate from HSR key
  Correct timescale in MvHd for HFR
  Remove target specific checks for HFR

Change-Id: Ic54495f9efc9afb5e0a262d993308a40491f1035
parent bd42a7ac
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -96,6 +96,7 @@ private:
    int mLongitudex10000;
    bool mAreGeoTagsAvailable;
    int32_t mStartTimeOffsetMs;
    int mHFRRatio;

    Mutex mLock;

+9 −0
Original line number Diff line number Diff line
@@ -1674,6 +1674,15 @@ status_t StagefrightRecorder::setupVideoEncoder(
        format->setInt32("time-scale", mVideoTimeScale);
    }

    if (cameraSource != NULL) {
        sp<MetaData> meta = cameraSource->getFormat();
        status_t retVal = ExtendedUtils::HFR::initializeHFR(
                meta, format, mMaxFileDurationUs, mVideoEncoder);
        if (retVal != OK) {
            return retVal;
        }
    }

    ExtendedUtils::ShellProp::setEncoderProfile(mVideoEncoder,
            mVideoEncoderProfile, mVideoEncoderLevel);

+4 −0
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@
#include <gui/Surface.h>
#include <utils/String8.h>
#include <cutils/properties.h>
#include "include/ExtendedUtils.h"

#if LOG_NDEBUG
#define UNUSED_UNLESS_VERBOSE(x) (void)(x)
@@ -578,6 +579,9 @@ status_t CameraSource::initWithCameraAccess(
    mMeta->setInt32(kKeyStride,      mVideoSize.width);
    mMeta->setInt32(kKeySliceHeight, mVideoSize.height);
    mMeta->setInt32(kKeyFrameRate,   mVideoFrameRate);

    ExtendedUtils::HFR::setHFRIfEnabled(params, mMeta);

    return OK;
}

+157 −0
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@
#include <media/stagefright/OMXCodec.h>
#include <cutils/properties.h>
#include <media/stagefright/MediaExtractor.h>
#include <media/MediaProfiles.h>

#include "include/ExtendedUtils.h"

@@ -69,6 +70,125 @@ static const uint8_t kHEVCNalUnitTypePicParamSet = 0x22;

namespace android {

void ExtendedUtils::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 ExtendedUtils::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 ExtendedUtils::HFR::setHFRRatio(
        sp<MetaData> &meta, const int32_t hfrRatio) {
    if (hfrRatio > 0) {
        meta->setInt32(kKeyHFR, hfrRatio);
    }
}

int32_t ExtendedUtils::HFR::getHFRRatio(
        const sp<MetaData> &meta) {
    int32_t hfrRatio = 0;
    meta->findInt32(kKeyHFR, &hfrRatio);
    return hfrRatio ? hfrRatio : 1;
}

int32_t ExtendedUtils::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;
}

bool ExtendedUtils::HEVCMuxer::isVideoHEVC(const char* mime) {
    return (!strncasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC,
                         strlen(MEDIA_MIMETYPE_VIDEO_HEVC)));
@@ -981,6 +1101,43 @@ bool ExtendedUtils::checkIsThumbNailMode(const uint32_t flags, char* componentNa

namespace android {

void ExtendedUtils::HFR::setHFRIfEnabled(
        const CameraParameters& params, sp<MetaData> &meta) {
    ARG_TOUCH(params);
    ARG_TOUCH(meta);
}

status_t ExtendedUtils::HFR::initializeHFR(
        const sp<MetaData> &meta, sp<AMessage> &format,
        int64_t &maxFileDurationUs, video_encoder videoEncoder) {
    ARG_TOUCH(meta);
    ARG_TOUCH(format);
    ARG_TOUCH(maxFileDurationUs);
    ARG_TOUCH(videoEncoder);
    return OK;
}

void ExtendedUtils::HFR::setHFRRatio(
        sp<MetaData> &meta, const int32_t hfrRatio) {
    ARG_TOUCH(meta);
    ARG_TOUCH(hfrRatio);
}

int32_t ExtendedUtils::HFR::getHFRRatio(
        const sp<MetaData> &meta) {
    ARG_TOUCH(meta);
    return 1;
}

int32_t ExtendedUtils::HFR::getHFRCapabilities(
        video_encoder codec,
        int& maxHFRWidth, int& maxHFRHeight, int& maxHFRFps,
        int& maxBitRate) {
    ARG_TOUCH(codec);
    maxHFRWidth = maxHFRHeight = maxHFRFps = maxBitRate = 0;
    return -1;
}

bool ExtendedUtils::ShellProp::isAudioDisabled(bool isEncoder) {
    return false;
}
+17 −5
Original line number Diff line number Diff line
@@ -302,6 +302,7 @@ private:
    // 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);
@@ -360,7 +361,8 @@ MPEG4Writer::MPEG4Writer(const char *filename)
      mLatitudex10000(0),
      mLongitudex10000(0),
      mAreGeoTagsAvailable(false),
      mStartTimeOffsetMs(-1) {
      mStartTimeOffsetMs(-1),
      mHFRRatio(1) {

    mFd = open(filename, O_CREAT | O_LARGEFILE | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR);
    if (mFd >= 0) {
@@ -385,7 +387,8 @@ MPEG4Writer::MPEG4Writer(int fd)
      mLatitudex10000(0),
      mLongitudex10000(0),
      mAreGeoTagsAvailable(false),
      mStartTimeOffsetMs(-1) {
      mStartTimeOffsetMs(-1),
      mHFRRatio(1) {
}

MPEG4Writer::~MPEG4Writer() {
@@ -492,6 +495,8 @@ status_t MPEG4Writer::addSource(const sp<MediaSource> &source) {
    Track *track = new Track(this, source, 1 + mTracks.size());
    mTracks.push_back(track);

    mHFRRatio = ExtendedUtils::HFR::getHFRRatio(meta);

    return OK;
}

@@ -956,7 +961,7 @@ void MPEG4Writer::writeMvhdBox(int64_t durationUs) {
    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
@@ -1383,7 +1388,10 @@ MPEG4Writer::Track::Track(
      mCodecSpecificDataSize(0),
      mGotAllCodecSpecificData(false),
      mReachedEOS(false),
      mRotation(0) {
      mRotation(0),
      mHFRRatio(1) {
    getCodecSpecificDataFromInputFormatIfPossible();

    const char *mime;
    mMeta->findCString(kKeyMIMEType, &mime);

@@ -1796,6 +1804,8 @@ status_t MPEG4Writer::Track::start(MetaData *params) {
    pthread_create(&mThread, &attr, ThreadWrapper, this);
    pthread_attr_destroy(&attr);

    mHFRRatio = ExtendedUtils::HFR::getHFRRatio(mMeta);

    return OK;
}

@@ -2978,7 +2988,9 @@ void MPEG4Writer::Track::writeMdhdBox(uint32_t now) {
    mOwner->writeInt32(0);             // version=0, flags=0
    mOwner->writeInt32(now);           // creation time
    mOwner->writeInt32(now);           // modification time
    mOwner->writeInt32(mTimeScale);    // media timescale

    int32_t timeScale = mTimeScale / mHFRRatio;
    mOwner->writeInt32(timeScale);    // 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
Loading