Allow creation of input surface in CCodec

Currently, an input surface to an encoder is created in the codec
process, but the queue operation has to be issued from the application
process. This CL allows an input surface to be created in the
application process, hence reducing the number of IPCs issued per frame.
This option can be chosen by setting "debug.stagefright.c2inputsurface"
to -1. (If the property is not set, it defaults to 0, which picks the
current behavior.)

Test: adb shell setprop debug.stagefright.c2inputsurface -1
Then, record a video.

Bug: 131800183
Bug: 134017277

Change-Id: Idbf2ba87689e1e876a215850aa0260539183a4ee
diff --git a/media/codec2/sfplugin/Android.bp b/media/codec2/sfplugin/Android.bp
index 8ae80ee..9c84c71 100644
--- a/media/codec2/sfplugin/Android.bp
+++ b/media/codec2/sfplugin/Android.bp
@@ -9,6 +9,7 @@
         "CCodecConfig.cpp",
         "Codec2Buffer.cpp",
         "Codec2InfoBuilder.cpp",
+        "Omx2IGraphicBufferSource.cpp",
         "PipelineWatcher.cpp",
         "ReflectedParamUpdater.cpp",
         "SkipCutBuffer.cpp",
@@ -41,8 +42,10 @@
         "libmedia",
         "libmedia_omx",
         "libsfplugin_ccodec_utils",
+        "libstagefright_bufferqueue_helper",
         "libstagefright_codecbase",
         "libstagefright_foundation",
+        "libstagefright_omx",
         "libstagefright_omx_utils",
         "libstagefright_xmlparser",
         "libui",
diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp
index aa7189c..9d1cc60 100644
--- a/media/codec2/sfplugin/CCodec.cpp
+++ b/media/codec2/sfplugin/CCodec.cpp
@@ -45,6 +45,7 @@
 #include "CCodec.h"
 #include "CCodecBufferChannel.h"
 #include "InputSurfaceWrapper.h"
+#include "Omx2IGraphicBufferSource.h"
 
 extern "C" android::PersistentSurface *CreateInputSurface();
 
@@ -1067,6 +1068,7 @@
     OmxStatus s;
     android::sp<HGraphicBufferProducer> gbp;
     android::sp<HGraphicBufferSource> gbs;
+
     using ::android::hardware::Return;
     Return<void> transStatus = omx->createInputSurface(
             [&s, &gbp, &gbs](
@@ -1852,15 +1854,30 @@
 
 // Create Codec 2.0 input surface
 extern "C" android::PersistentSurface *CreateInputSurface() {
+    using namespace android;
     // Attempt to create a Codec2's input surface.
-    std::shared_ptr<android::Codec2Client::InputSurface> inputSurface =
-            android::Codec2Client::CreateInputSurface();
+    std::shared_ptr<Codec2Client::InputSurface> inputSurface =
+            Codec2Client::CreateInputSurface();
     if (!inputSurface) {
-        return nullptr;
+        if (property_get_int32("debug.stagefright.c2inputsurface", 0) == -1) {
+            sp<IGraphicBufferProducer> gbp;
+            sp<OmxGraphicBufferSource> gbs = new OmxGraphicBufferSource();
+            status_t err = gbs->initCheck();
+            if (err != OK) {
+                ALOGE("Failed to create persistent input surface: error %d", err);
+                return nullptr;
+            }
+            return new PersistentSurface(
+                    gbs->getIGraphicBufferProducer(),
+                    sp<IGraphicBufferSource>(
+                        new Omx2IGraphicBufferSource(gbs)));
+        } else {
+            return nullptr;
+        }
     }
-    return new android::PersistentSurface(
+    return new PersistentSurface(
             inputSurface->getGraphicBufferProducer(),
-            static_cast<android::sp<android::hidl::base::V1_0::IBase>>(
+            static_cast<sp<android::hidl::base::V1_0::IBase>>(
             inputSurface->getHalInterface()));
 }
 
diff --git a/media/codec2/sfplugin/Omx2IGraphicBufferSource.cpp b/media/codec2/sfplugin/Omx2IGraphicBufferSource.cpp
new file mode 100644
index 0000000..764fa00
--- /dev/null
+++ b/media/codec2/sfplugin/Omx2IGraphicBufferSource.cpp
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifdef __LP64__
+#define OMX_ANDROID_COMPILE_AS_32BIT_ON_64BIT_PLATFORMS
+#endif
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "Omx2IGraphicBufferSource"
+#include <android-base/logging.h>
+
+#include "Omx2IGraphicBufferSource.h"
+
+#include <android/BnOMXBufferSource.h>
+#include <media/OMXBuffer.h>
+#include <media/stagefright/omx/OMXUtils.h>
+
+#include <OMX_Component.h>
+#include <OMX_Index.h>
+#include <OMX_IndexExt.h>
+
+namespace android {
+
+namespace /* unnamed */ {
+
+// OmxGraphicBufferSource -> IOMXBufferSource
+
+struct OmxGbs2IOmxBs : public BnOMXBufferSource {
+    sp<OmxGraphicBufferSource> mBase;
+    OmxGbs2IOmxBs(sp<OmxGraphicBufferSource> const& base) : mBase{base} {}
+    BnStatus onOmxExecuting() override {
+        return mBase->onOmxExecuting();
+    }
+    BnStatus onOmxIdle() override {
+        return mBase->onOmxIdle();
+    }
+    BnStatus onOmxLoaded() override {
+        return mBase->onOmxLoaded();
+    }
+    BnStatus onInputBufferAdded(int32_t bufferId) override {
+        return mBase->onInputBufferAdded(bufferId);
+    }
+    BnStatus onInputBufferEmptied(
+            int32_t bufferId,
+            OMXFenceParcelable const& fenceParcel) override {
+        return mBase->onInputBufferEmptied(bufferId, fenceParcel.get());
+    }
+};
+
+struct OmxNodeWrapper : public IOmxNodeWrapper {
+    sp<IOMXNode> mBase;
+    OmxNodeWrapper(sp<IOMXNode> const& base) : mBase{base} {}
+    status_t emptyBuffer(
+            int32_t bufferId, uint32_t flags,
+            const sp<GraphicBuffer> &buffer,
+            int64_t timestamp, int fenceFd) override {
+        return mBase->emptyBuffer(bufferId, buffer, flags, timestamp, fenceFd);
+    }
+    void dispatchDataSpaceChanged(
+            int32_t dataSpace, int32_t aspects, int32_t pixelFormat) override {
+        omx_message msg{};
+        msg.type = omx_message::EVENT;
+        msg.fenceFd = -1;
+        msg.u.event_data.event = OMX_EventDataSpaceChanged;
+        msg.u.event_data.data1 = dataSpace;
+        msg.u.event_data.data2 = aspects;
+        msg.u.event_data.data3 = pixelFormat;
+        mBase->dispatchMessage(msg);
+    }
+};
+
+} // unnamed namespace
+
+// Omx2IGraphicBufferSource
+Omx2IGraphicBufferSource::Omx2IGraphicBufferSource(
+        sp<OmxGraphicBufferSource> const& base)
+      : mBase{base},
+        mOMXBufferSource{new OmxGbs2IOmxBs(base)} {
+}
+
+BnStatus Omx2IGraphicBufferSource::setSuspend(
+        bool suspend, int64_t timeUs) {
+    return BnStatus::fromStatusT(mBase->setSuspend(suspend, timeUs));
+}
+
+BnStatus Omx2IGraphicBufferSource::setRepeatPreviousFrameDelayUs(
+        int64_t repeatAfterUs) {
+    return BnStatus::fromStatusT(mBase->setRepeatPreviousFrameDelayUs(repeatAfterUs));
+}
+
+BnStatus Omx2IGraphicBufferSource::setMaxFps(float maxFps) {
+    return BnStatus::fromStatusT(mBase->setMaxFps(maxFps));
+}
+
+BnStatus Omx2IGraphicBufferSource::setTimeLapseConfig(
+        double fps, double captureFps) {
+    return BnStatus::fromStatusT(mBase->setTimeLapseConfig(fps, captureFps));
+}
+
+BnStatus Omx2IGraphicBufferSource::setStartTimeUs(
+        int64_t startTimeUs) {
+    return BnStatus::fromStatusT(mBase->setStartTimeUs(startTimeUs));
+}
+
+BnStatus Omx2IGraphicBufferSource::setStopTimeUs(
+        int64_t stopTimeUs) {
+    return BnStatus::fromStatusT(mBase->setStopTimeUs(stopTimeUs));
+}
+
+BnStatus Omx2IGraphicBufferSource::getStopTimeOffsetUs(
+        int64_t *stopTimeOffsetUs) {
+    return BnStatus::fromStatusT(mBase->getStopTimeOffsetUs(stopTimeOffsetUs));
+}
+
+BnStatus Omx2IGraphicBufferSource::setColorAspects(
+        int32_t aspects) {
+    return BnStatus::fromStatusT(mBase->setColorAspects(aspects));
+}
+
+BnStatus Omx2IGraphicBufferSource::setTimeOffsetUs(
+        int64_t timeOffsetsUs) {
+    return BnStatus::fromStatusT(mBase->setTimeOffsetUs(timeOffsetsUs));
+}
+
+BnStatus Omx2IGraphicBufferSource::signalEndOfInputStream() {
+    return BnStatus::fromStatusT(mBase->signalEndOfInputStream());
+}
+
+BnStatus Omx2IGraphicBufferSource::configure(
+        const sp<IOMXNode>& omxNode, int32_t dataSpace) {
+    if (omxNode == NULL) {
+        return BnStatus::fromServiceSpecificError(BAD_VALUE);
+    }
+
+    // Do setInputSurface() first, the node will try to enable metadata
+    // mode on input, and does necessary error checking. If this fails,
+    // we can't use this input surface on the node.
+    status_t err = omxNode->setInputSurface(mOMXBufferSource);
+    if (err != NO_ERROR) {
+        ALOGE("Unable to set input surface: %d", err);
+        return BnStatus::fromServiceSpecificError(err);
+    }
+
+    uint32_t consumerUsage;
+    if (omxNode->getParameter(
+            (OMX_INDEXTYPE)OMX_IndexParamConsumerUsageBits,
+            &consumerUsage, sizeof(consumerUsage)) != OK) {
+        consumerUsage = 0;
+    }
+
+    OMX_PARAM_PORTDEFINITIONTYPE def;
+    InitOMXParams(&def);
+    def.nPortIndex = 0; // kPortIndexInput
+
+    err = omxNode->getParameter(
+            OMX_IndexParamPortDefinition, &def, sizeof(def));
+    if (err != NO_ERROR) {
+        ALOGE("Failed to get port definition: %d", err);
+        return BnStatus::fromServiceSpecificError(UNKNOWN_ERROR);
+    }
+
+    return BnStatus::fromStatusT(mBase->configure(
+            new OmxNodeWrapper(omxNode),
+            dataSpace,
+            def.nBufferCountActual,
+            def.format.video.nFrameWidth,
+            def.format.video.nFrameHeight,
+            consumerUsage));
+}
+
+} // namespace android
+
diff --git a/media/codec2/sfplugin/Omx2IGraphicBufferSource.h b/media/codec2/sfplugin/Omx2IGraphicBufferSource.h
new file mode 100644
index 0000000..20fd1ec
--- /dev/null
+++ b/media/codec2/sfplugin/Omx2IGraphicBufferSource.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef OMX_2_IGRAPHICBUFFERSOURCE_H_
+#define OMX_2_IGRAPHICBUFFERSOURCE_H_
+
+#include <android/BnGraphicBufferSource.h>
+#include <media/stagefright/omx/OmxGraphicBufferSource.h>
+
+namespace android {
+
+using BnStatus = ::android::binder::Status;
+
+struct Omx2IGraphicBufferSource : public BnGraphicBufferSource {
+    sp<OmxGraphicBufferSource> mBase;
+    sp<IOMXBufferSource> mOMXBufferSource;
+    Omx2IGraphicBufferSource(sp<OmxGraphicBufferSource> const& base);
+    BnStatus configure(const sp<IOMXNode>& omxNode, int32_t dataSpace) override;
+    BnStatus setSuspend(bool suspend, int64_t timeUs) override;
+    BnStatus setRepeatPreviousFrameDelayUs(int64_t repeatAfterUs) override;
+    BnStatus setMaxFps(float maxFps) override;
+    BnStatus setTimeLapseConfig(double fps, double captureFps) override;
+    BnStatus setStartTimeUs(int64_t startTimeUs) override;
+    BnStatus setStopTimeUs(int64_t stopTimeUs) override;
+    BnStatus getStopTimeOffsetUs(int64_t *stopTimeOffsetUs) override;
+    BnStatus setColorAspects(int32_t aspects) override;
+    BnStatus setTimeOffsetUs(int64_t timeOffsetsUs) override;
+    BnStatus signalEndOfInputStream() override;
+};
+
+} // namespace android
+
+#endif // OMX_2_IGRAPHICBUFFERSOURCE_H_
+