C2SoftMpeg4Enc: add support for syncFramePeriod, profile and level information

Test: cts -m CtsMediaTestCases -t android.media.cts.VideoEncoderTest
Bug: 110265995

Change-Id: Ic01691039ef2c4c928a45731bc78ea3bcad8de37
diff --git a/media/codecs/mpeg4_h263/C2SoftMpeg4Enc.cpp b/media/codecs/mpeg4_h263/C2SoftMpeg4Enc.cpp
index ad97bad..4fca683 100644
--- a/media/codecs/mpeg4_h263/C2SoftMpeg4Enc.cpp
+++ b/media/codecs/mpeg4_h263/C2SoftMpeg4Enc.cpp
@@ -89,8 +89,13 @@
             DefineParam(mSize, C2_NAME_STREAM_VIDEO_SIZE_SETTING)
                 .withDefault(new C2VideoSizeStreamTuning::input(0u, 176, 144))
                 .withFields({
+#ifdef MPEG4
                     C2F(mSize, width).inRange(16, 176, 16),
                     C2F(mSize, height).inRange(16, 144, 16),
+#else
+                    C2F(mSize, width).inRange(176, 176, 16),
+                    C2F(mSize, height).inRange(144, 144, 16),
+#endif
                 })
                 .withSetter(SizeSetter)
                 .build());
@@ -107,30 +112,109 @@
         addParameter(
             DefineParam(mBitrate, C2_NAME_STREAM_BITRATE_SETTING)
                 .withDefault(new C2BitrateTuning::output(0u, 64000))
-                .withFields({C2F(mBitrate, value).inRange(1, 12000000)})
-                .withSetter(
-                    Setter<decltype(*mBitrate)>::NonStrictValueWithNoDeps)
+                .withFields({C2F(mBitrate, value).inRange(4096, 12000000)})
+                .withSetter(BitrateSetter)
                 .build());
+
+        addParameter(
+                DefineParam(mSyncFramePeriod, C2_PARAMKEY_SYNC_FRAME_INTERVAL)
+                .withDefault(new C2StreamSyncFrameIntervalTuning::output(0u, 1000000))
+                .withFields({C2F(mSyncFramePeriod, value).any()})
+                .withSetter(Setter<decltype(*mSyncFramePeriod)>::StrictValueWithNoDeps)
+                .build());
+
+#ifdef MPEG4
+        addParameter(
+                DefineParam(mProfileLevel, C2_PARAMKEY_PROFILE_LEVEL)
+                .withDefault(new C2StreamProfileLevelInfo::output(
+                        0u, PROFILE_MP4V_SIMPLE, LEVEL_MP4V_2))
+                .withFields({
+                    C2F(mProfileLevel, profile).equalTo(
+                            PROFILE_MP4V_SIMPLE),
+                    C2F(mProfileLevel, level).oneOf({
+                            C2Config::LEVEL_MP4V_0,
+                            C2Config::LEVEL_MP4V_0B,
+                            C2Config::LEVEL_MP4V_1,
+                            C2Config::LEVEL_MP4V_2})
+                })
+                .withSetter(ProfileLevelSetter)
+                .build());
+#else
+        addParameter(
+                DefineParam(mProfileLevel, C2_PARAMKEY_PROFILE_LEVEL)
+                .withDefault(new C2StreamProfileLevelInfo::output(
+                        0u, PROFILE_H263_BASELINE, LEVEL_H263_45))
+                .withFields({
+                    C2F(mProfileLevel, profile).equalTo(
+                            PROFILE_H263_BASELINE),
+                    C2F(mProfileLevel, level).oneOf({
+                            C2Config::LEVEL_H263_10,
+                            C2Config::LEVEL_H263_20,
+                            C2Config::LEVEL_H263_30,
+                            C2Config::LEVEL_H263_40,
+                            C2Config::LEVEL_H263_45})
+                })
+                .withSetter(ProfileLevelSetter)
+                .build());
+#endif
     }
 
-    static C2R SizeSetter(bool mayBlock,
-                          C2P<C2VideoSizeStreamTuning::input>& me) {
+    static C2R BitrateSetter(bool mayBlock, C2P<C2StreamBitrateInfo::output> &me) {
         (void)mayBlock;
-        // TODO: maybe apply block limit?
         C2R res = C2R::Ok();
-        if (!me.F(me.v.width).supportsAtAll(me.v.width)) {
-            res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.width)));
-        }
-        if (!me.F(me.v.height).supportsAtAll(me.v.height)) {
-            res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.height)));
+        if (me.v.value <= 4096) {
+            me.set().value = 4096;
         }
         return res;
     }
 
-    uint32_t getWidth() const { return mSize->width; }
-    uint32_t getHeight() const { return mSize->height; }
-    float getFrameRate() const { return mFrameRate->value; }
-    uint32_t getBitrate() const { return mBitrate->value; }
+    static C2R SizeSetter(bool mayBlock, const C2P<C2StreamPictureSizeInfo::input> &oldMe,
+                          C2P<C2StreamPictureSizeInfo::input> &me) {
+        (void)mayBlock;
+        C2R res = C2R::Ok();
+        if (!me.F(me.v.width).supportsAtAll(me.v.width)) {
+            res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.width)));
+            me.set().width = oldMe.v.width;
+        }
+        if (!me.F(me.v.height).supportsAtAll(me.v.height)) {
+            res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.height)));
+            me.set().height = oldMe.v.height;
+        }
+        return res;
+    }
+
+    static C2R ProfileLevelSetter(
+            bool mayBlock,
+            C2P<C2StreamProfileLevelInfo::output> &me) {
+        (void)mayBlock;
+        if (!me.F(me.v.profile).supportsAtAll(me.v.profile)) {
+#ifdef MPEG4
+            me.set().profile = PROFILE_MP4V_SIMPLE;
+#else
+            me.set().profile = PROFILE_H263_BASELINE;
+#endif
+        }
+        if (!me.F(me.v.level).supportsAtAll(me.v.level)) {
+#ifdef MPEG4
+            me.set().level = LEVEL_MP4V_2;
+#else
+            me.set().level = LEVEL_H263_45;
+#endif
+        }
+        return C2R::Ok();
+    }
+
+    // unsafe getters
+    std::shared_ptr<C2StreamPictureSizeInfo::input> getSize_l() const { return mSize; }
+    std::shared_ptr<C2StreamFrameRateInfo::output> getFrameRate_l() const { return mFrameRate; }
+    std::shared_ptr<C2StreamBitrateInfo::output> getBitrate_l() const { return mBitrate; }
+    uint32_t getSyncFramePeriod() const {
+        if (mSyncFramePeriod->value < 0 || mSyncFramePeriod->value == INT64_MAX) {
+            return 0;
+        }
+        double period = mSyncFramePeriod->value / 1e6 * mFrameRate->value;
+        return (uint32_t)c2_max(c2_min(period + 0.5, double(UINT32_MAX)), 1.);
+    }
 
    private:
     std::shared_ptr<C2StreamFormatConfig::input> mInputFormat;
@@ -141,6 +225,8 @@
     std::shared_ptr<C2VideoSizeStreamTuning::input> mSize;
     std::shared_ptr<C2StreamFrameRateInfo::output> mFrameRate;
     std::shared_ptr<C2BitrateTuning::output> mBitrate;
+    std::shared_ptr<C2StreamProfileLevelInfo::output> mProfileLevel;
+    std::shared_ptr<C2StreamSyncFrameIntervalTuning::output> mSyncFramePeriod;
 };
 
 C2SoftMpeg4Enc::C2SoftMpeg4Enc(const char* name, c2_node_id_t id,
@@ -151,8 +237,7 @@
       mHandle(nullptr),
       mEncParams(nullptr),
       mStarted(false),
-      mOutBufferSize(524288),
-      mKeyFrameInterval(10) {
+      mOutBufferSize(524288) {
 }
 
 C2SoftMpeg4Enc::~C2SoftMpeg4Enc() {
@@ -236,39 +321,38 @@
         return C2_CORRUPTED;
     }
 
-    if (mIntf->getFrameRate() == 0) {
+    if (mFrameRate->value == 0) {
         ALOGE("Framerate should not be 0");
         return C2_BAD_VALUE;
     }
 
     mEncParams->encMode = mEncodeMode;
-    mEncParams->encWidth[0] = mIntf->getWidth();
-    mEncParams->encHeight[0] = mIntf->getHeight();
-    mEncParams->encFrameRate[0] = mIntf->getFrameRate();
+    mEncParams->encWidth[0] = mSize->width;
+    mEncParams->encHeight[0] = mSize->height;
+    mEncParams->encFrameRate[0] = mFrameRate->value + 0.5;
     mEncParams->rcType = VBR_1;
     mEncParams->vbvDelay = 5.0f;
-
     mEncParams->profile_level = CORE_PROFILE_LEVEL2;
     mEncParams->packetSize = 32;
     mEncParams->rvlcEnable = PV_OFF;
     mEncParams->numLayers = 1;
     mEncParams->timeIncRes = 1000;
-    mEncParams->tickPerSrc = mEncParams->timeIncRes / mIntf->getFrameRate();
-    mEncParams->bitRate[0] = mIntf->getBitrate();
+    mEncParams->tickPerSrc = mEncParams->timeIncRes / (mFrameRate->value + 0.5);
+    mEncParams->bitRate[0] = mBitrate->value;
     mEncParams->iQuant[0] = 15;
     mEncParams->pQuant[0] = 12;
     mEncParams->quantType[0] = 0;
     mEncParams->noFrameSkipped = PV_OFF;
 
     // PV's MPEG4 encoder requires the video dimension of multiple
-    if (mIntf->getWidth() % 16 != 0 || mIntf->getHeight() % 16 != 0) {
+    if (mSize->width % 16 != 0 || mSize->height % 16 != 0) {
         ALOGE("Video frame size %dx%d must be a multiple of 16",
-              mIntf->getWidth(), mIntf->getHeight());
+              mSize->width, mSize->height);
         return C2_BAD_VALUE;
     }
 
     // Set IDR frame refresh interval
-    mEncParams->intraPeriod = mKeyFrameInterval;
+    mEncParams->intraPeriod = mIntf->getSyncFramePeriod();
     mEncParams->numIntraMB = 0;
     mEncParams->sceneDetect = PV_ON;
     mEncParams->searchRange = 16;
@@ -284,6 +368,12 @@
     if (mStarted) {
         return C2_OK;
     }
+    {
+        IntfImpl::Lock lock = mIntf->lock();
+        mSize = mIntf->getSize_l();
+        mBitrate = mIntf->getBitrate_l();
+        mFrameRate = mIntf->getFrameRate_l();
+    }
     c2_status_t err = initEncParams();
     if (C2_OK != err) {
         ALOGE("Failed to initialized encoder params");
@@ -379,11 +469,11 @@
 
     uint64_t inputTimeStamp = work->input.ordinal.timestamp.peekull();
     const C2ConstGraphicBlock inBuffer = inputBuffer->data().graphicBlocks().front();
-    if (inBuffer.width() < mIntf->getWidth() ||
-        inBuffer.height() < mIntf->getHeight()) {
+    if (inBuffer.width() < mSize->width ||
+        inBuffer.height() < mSize->height) {
         /* Expect width height to be configured */
         ALOGW("unexpected Capacity Aspect %d(%d) x %d(%d)", inBuffer.width(),
-              mIntf->getWidth(), inBuffer.height(), mIntf->getHeight());
+              mSize->width, inBuffer.height(), mSize->height);
         work->result = C2_BAD_VALUE;
         return;
     }
@@ -395,8 +485,8 @@
     int32_t yStride = layout.planes[C2PlanarLayout::PLANE_Y].rowInc;
     int32_t uStride = layout.planes[C2PlanarLayout::PLANE_U].rowInc;
     int32_t vStride = layout.planes[C2PlanarLayout::PLANE_V].rowInc;
-    uint32_t width = mIntf->getWidth();
-    uint32_t height = mIntf->getHeight();
+    uint32_t width = mSize->width;
+    uint32_t height = mSize->height;
     // width and height are always even (as block size is 16x16)
     CHECK_EQ((width & 1u), 0u);
     CHECK_EQ((height & 1u), 0u);
@@ -488,12 +578,12 @@
     fillEmptyWork(work);
     if (outputSize) {
         std::shared_ptr<C2Buffer> buffer = createLinearBuffer(block, 0, outputSize);
-        work->worklets.front()->output.buffers.push_back(buffer);
         work->worklets.front()->output.ordinal.timestamp = inputTimeStamp;
         if (hintTrack.CodeType == 0) {
             buffer->setInfo(std::make_shared<C2StreamPictureTypeMaskInfo::output>(
                     0u /* stream id */, C2PictureTypeKeyFrame));
         }
+        work->worklets.front()->output.buffers.push_back(buffer);
     }
     if (eos) {
         mSignalledOutputEos = true;
diff --git a/media/codecs/mpeg4_h263/C2SoftMpeg4Enc.h b/media/codecs/mpeg4_h263/C2SoftMpeg4Enc.h
index 7631f30..43461fc 100644
--- a/media/codecs/mpeg4_h263/C2SoftMpeg4Enc.h
+++ b/media/codecs/mpeg4_h263/C2SoftMpeg4Enc.h
@@ -60,8 +60,12 @@
     bool     mSignalledError;
 
     uint32_t mOutBufferSize;
-    // 1: all I-frames, <0: infinite
-    int32_t  mKeyFrameInterval;
+    // configurations used by component in process
+    // (TODO: keep this in intf but make them internal only)
+    std::shared_ptr<C2StreamPictureSizeInfo::input> mSize;
+    std::shared_ptr<C2StreamFrameRateInfo::output> mFrameRate;
+    std::shared_ptr<C2StreamBitrateInfo::output> mBitrate;
+
     int64_t  mNumInputFrames;
     MP4EncodingMode mEncodeMode;