codec2: component start/stop implementation

This CL only cover component start and stop function without buffer allocation
and any decoding procedure.

Bug: 63828275
Test: run native tests (on nyc branch, cheets_arm device). Steps:
1) mmm external/v4l2_codec2/tests/
2) adb push out/target/product/cheets_arm/data/nativetest/C2VDAComponent_test /data/local/tmp/
3) adb shell /data/local/tmp/C2VDAComponent_test/C2VDAComponent_test

Change-Id: I96d9f4b2d6f9bd6bb28be93be3b7b5bea62e9ea7
diff --git a/C2VDAComponent.cpp b/C2VDAComponent.cpp
index 7c0ba9d..f9c4be9 100644
--- a/C2VDAComponent.cpp
+++ b/C2VDAComponent.cpp
@@ -9,7 +9,8 @@
 
 #include "C2VDAAdaptor.h"
 #include "C2VDAComponent.h"
-#include "video_codecs.h"
+#include "base/bind.h"
+#include "base/bind_helpers.h"
 #include "videodev2.h"
 
 #include <media/stagefright/MediaDefs.h>
@@ -428,10 +429,83 @@
                                node_id id,
                                const std::shared_ptr<C2ComponentListener>& listener)
     : mIntf(std::make_shared<C2VDAComponentIntf>(name, id)),
-      mListener(listener) {
+      mListener(listener),
+      mThread("C2VDAComponentThread"),
+      mVDAInitResult(VideoDecodeAcceleratorAdaptor::Result::ILLEGAL_STATE),
+      mComponentState(ComponentState::UNINITIALIZED),
+      mCodecProfile(media::VIDEO_CODEC_PROFILE_UNKNOWN),
+      mState(State::UNLOADED) {
+    if (!mThread.Start()) {
+        ALOGE("Component thread failed to start");
+        return;
+    }
+    mTaskRunner = mThread.task_runner();
+    mTaskRunner->PostTask(FROM_HERE, base::Bind(&C2VDAComponent::onCreate, base::Unretained(this)));
+    mState = State::LOADED;
 }
 
 C2VDAComponent::~C2VDAComponent() {
+    CHECK_EQ(mState, State::LOADED);
+
+    if (mThread.IsRunning()) {
+        mTaskRunner->PostTask(
+                FROM_HERE, base::Bind(&C2VDAComponent::onDestroy, base::Unretained(this)));
+        mThread.Stop();
+    }
+    CHECK(!mThread.IsRunning());
+}
+
+void C2VDAComponent::getParameters() {
+    C2StreamFormatConfig::input codecProfile;
+    std::vector<C2Param* const> stackParams{ &codecProfile };
+    CHECK_EQ(mIntf->query_nb(stackParams, {}, nullptr), C2_OK);
+    // The value should be guaranteed to be within media::VideoCodecProfile enum range by component
+    // interface.
+    mCodecProfile = static_cast<media::VideoCodecProfile>(codecProfile.mValue);
+    ALOGI("get parameter: mCodecProfile = %d", static_cast<int>(mCodecProfile));
+}
+
+void C2VDAComponent::onCreate() {
+    DCHECK(mTaskRunner->BelongsToCurrentThread());
+    ALOGV("onCreate");
+    mVDAAdaptor.reset(new C2VDAAdaptor());
+}
+
+void C2VDAComponent::onDestroy() {
+    DCHECK(mTaskRunner->BelongsToCurrentThread());
+    ALOGV("onDestroy");
+    mVDAAdaptor.reset(nullptr);
+}
+
+void C2VDAComponent::onStart(media::VideoCodecProfile profile, base::WaitableEvent* done) {
+    DCHECK(mTaskRunner->BelongsToCurrentThread());
+    ALOGV("onStart");
+    CHECK(mComponentState == ComponentState::UNINITIALIZED);
+    mVDAInitResult = mVDAAdaptor->initialize(profile, this);
+    if (mVDAInitResult == VideoDecodeAcceleratorAdaptor::Result::SUCCESS) {
+        mComponentState = ComponentState::STARTED;
+    }
+    done->Signal();
+}
+
+void C2VDAComponent::onStop(base::WaitableEvent* done) {
+    DCHECK(mTaskRunner->BelongsToCurrentThread());
+    ALOGV("onStop");
+    CHECK(mComponentState != ComponentState::UNINITIALIZED);
+    mVDAAdaptor->reset();
+    mStopDoneEvent = done;  // restore done event which shoud be signaled in onStopDone().
+    mComponentState = ComponentState::STOPPING;
+}
+
+void C2VDAComponent::onStopDone() {
+    DCHECK(mTaskRunner->BelongsToCurrentThread());
+    ALOGV("onStopDone");
+    CHECK(mComponentState == ComponentState::STOPPING);
+    CHECK(mStopDoneEvent);
+    mVDAAdaptor->destroy();
+    mStopDoneEvent->Signal();
+    mStopDoneEvent = nullptr;
+    mComponentState = ComponentState::UNINITIALIZED;
 }
 
 status_t C2VDAComponent::queue_nb(std::list<std::unique_ptr<C2Work>>* const items) {
@@ -459,17 +533,46 @@
 }
 
 status_t C2VDAComponent::start() {
-    return C2_NOT_IMPLEMENTED;
+    if (mState != State::LOADED) {
+        return C2_BAD_STATE;  // start() is only supported when component is in LOADED state.
+    }
+
+    getParameters();
+    base::WaitableEvent done(base::WaitableEvent::ResetPolicy::AUTOMATIC,
+                             base::WaitableEvent::InitialState::NOT_SIGNALED);
+    mTaskRunner->PostTask(
+            FROM_HERE,
+            base::Bind(&C2VDAComponent::onStart, base::Unretained(this), mCodecProfile, &done));
+    done.Wait();
+    if (mVDAInitResult != VideoDecodeAcceleratorAdaptor::Result::SUCCESS) {
+        ALOGE("Failed to start component due to VDA error: %d", static_cast<int>(mVDAInitResult));
+        return C2_CORRUPTED;
+    }
+    mState = State::RUNNING;
+    return C2_OK;
 }
 
 status_t C2VDAComponent::stop() {
-    return C2_NOT_IMPLEMENTED;
+    if (!(mState == State::RUNNING || mState == State::ERROR)) {
+        return C2_BAD_STATE;  // component is already in stopped state.
+    }
+
+    base::WaitableEvent done(base::WaitableEvent::ResetPolicy::AUTOMATIC,
+                             base::WaitableEvent::InitialState::NOT_SIGNALED);
+    mTaskRunner->PostTask(FROM_HERE,
+                          base::Bind(&C2VDAComponent::onStop, base::Unretained(this), &done));
+    done.Wait();
+    mState = State::LOADED;
+    return C2_OK;
 }
 
 void C2VDAComponent::reset() {
+    stop();
+    // TODO(johnylin): what is the use case for calling reset() instead of stop()?
 }
 
 void C2VDAComponent::release() {
+    // TODO(johnylin): what should we do for release?
 }
 
 std::shared_ptr<C2ComponentInterface> C2VDAComponent::intf() {
@@ -483,25 +586,27 @@
     UNUSED(codedSize);
 }
 
-void C2VDAComponent::dismissPictureBuffer(int32_t picture_id) {
-    UNUSED(picture_id);
+void C2VDAComponent::dismissPictureBuffer(int32_t pictureBufferId) {
+    UNUSED(pictureBufferId);
 }
 
 void C2VDAComponent::pictureReady(
-        int32_t picture_id, int32_t bitstream_id, const media::Rect& cropRect) {
-    UNUSED(picture_id);
-    UNUSED(bitstream_id);
+        int32_t pictureBufferId, int32_t bitstreamId, const media::Rect& cropRect) {
+    UNUSED(pictureBufferId);
+    UNUSED(bitstreamId);
     UNUSED(cropRect);
 }
 
-void C2VDAComponent::notifyEndOfBitstreamBuffer(int32_t bitstream_id) {
-    UNUSED(bitstream_id);
+void C2VDAComponent::notifyEndOfBitstreamBuffer(int32_t bitstreamId) {
+    UNUSED(bitstreamId);
 }
 
 void C2VDAComponent::notifyFlushDone() {
 }
 
 void C2VDAComponent::notifyResetDone() {
+    mTaskRunner->PostTask(FROM_HERE,
+                          base::Bind(&C2VDAComponent::onStopDone, base::Unretained(this)));
 }
 
 void C2VDAComponent::notifyError(VideoDecodeAcceleratorAdaptor::Result error) {
diff --git a/C2VDAComponent.h b/C2VDAComponent.h
index 4c5d79d..c7caa67 100644
--- a/C2VDAComponent.h
+++ b/C2VDAComponent.h
@@ -10,8 +10,13 @@
 
 #include "VideoDecodeAcceleratorAdaptor.h"
 #include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/single_thread_task_runner.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
 #include "rect.h"
 #include "size.h"
+#include "video_codecs.h"
 #include "video_decode_accelerator.h"
 
 #include <C2Component.h>
@@ -125,18 +130,82 @@
     virtual void providePictureBuffers(uint32_t pixelFormat,
                                        uint32_t minNumBuffers,
                                        const media::Size& codedSize) override;
-    virtual void dismissPictureBuffer(int32_t picture_id) override;
-    virtual void pictureReady(int32_t picture_id, int32_t bitstream_id,
+    virtual void dismissPictureBuffer(int32_t pictureBufferId) override;
+    virtual void pictureReady(int32_t pictureBufferId, int32_t bitstreamId,
                               const media::Rect& cropRect) override;
-    virtual void notifyEndOfBitstreamBuffer(int32_t bitstream_id) override;
+    virtual void notifyEndOfBitstreamBuffer(int32_t bitstreamId) override;
     virtual void notifyFlushDone() override;
     virtual void notifyResetDone() override;
     virtual void notifyError(VideoDecodeAcceleratorAdaptor::Result error) override;
 private:
+    // The state machine enumeration on parent thread.
+    enum class State : int32_t {
+        // The initial state of component. State will change to LOADED after the component is
+        // created.
+        UNLOADED,
+        // The component is stopped. State will change to RUNNING when start() is called by
+        // framework.
+        LOADED,
+        // The component is running, State will change to LOADED when stop() or reset() is called by
+        // framework.
+        RUNNING,
+        // The component is in error state.
+        ERROR,
+    };
+    // The state machine enumeration on component thread.
+    enum class ComponentState : int32_t {
+        // This is the initial state until VDA initialization returns successfully.
+        UNINITIALIZED,
+        // VDA initialization returns successfully. VDA is ready to make progress.
+        STARTED,
+        // onFlush() is called. VDA is flushing. State will change to STARTED after onFlushDone().
+        FLUSHING,
+        // onStop() is called. VDA is shutting down. State will change to UNINITIALIZED after
+        // onStopDone().
+        STOPPING,
+        // onError() is called.
+        ERROR,
+    };
+
+    // Get configured parameters from component interface. This should be called once framework
+    // wants to start the component.
+    void getParameters();
+
+    // These tasks should be run on the component thread |mThread|.
+    void onCreate();
+    void onDestroy();
+    void onStart(media::VideoCodecProfile profile, base::WaitableEvent* done);
+    void onStop(base::WaitableEvent* done);
+    void onStopDone();
+
+    // The pointer of component interface.
     const std::shared_ptr<C2VDAComponentIntf> mIntf;
+    // The pointer of component listener.
     const std::shared_ptr<C2ComponentListener> mListener;
 
+    // The main component thread.
+    base::Thread mThread;
+    // The task runner on component thread.
+    scoped_refptr<base::SingleThreadTaskRunner> mTaskRunner;
+
+    // The following members should be utilized on component thread |mThread|.
+
+    // The initialization result retrieved from VDA.
+    VideoDecodeAcceleratorAdaptor::Result mVDAInitResult;
+    // The pointer of VideoDecodeAcceleratorAdaptor.
     std::unique_ptr<VideoDecodeAcceleratorAdaptor> mVDAAdaptor;
+    // The done event pointer of stop procedure. It should be restored in onStop() and signaled in
+    // onStopDone().
+    base::WaitableEvent* mStopDoneEvent;
+    // The state machine on component thread.
+    ComponentState mComponentState;
+
+    // The following members should be utilized on parent thread.
+
+    // The input codec profile which is configured in component interface.
+    media::VideoCodecProfile mCodecProfile;
+    // The state machine on parent thread.
+    State mState;
 
     DISALLOW_COPY_AND_ASSIGN(C2VDAComponent);
 };
diff --git a/tests/C2VDAComponent_test.cpp b/tests/C2VDAComponent_test.cpp
index b85d868..2d785b3 100644
--- a/tests/C2VDAComponent_test.cpp
+++ b/tests/C2VDAComponent_test.cpp
@@ -410,6 +410,7 @@
                                   &profileValues));
     ASSERT_EQ(1u, profileValues.size());
 
+    // TODO(johnylin): refactor this after supporting VALUES type for profile values.
     uint32_t profileMin =   profileValues[0].range.min.u32;
     uint32_t profileMax =   profileValues[0].range.max.u32;
     //uint32_t profileStep =  profileValues[0].range.step.u32;
@@ -466,4 +467,32 @@
     }
 }
 
+TEST_F(C2VDAComponentTest, InitializeVDA) {
+    C2StreamFormatConfig::input codecProfile;
+    std::vector<C2FieldSupportedValues> profileValues;
+    ASSERT_EQ(
+        C2_OK,
+        mIntf->getSupportedValues({ C2ParamField(&codecProfile, &C2StreamFormatConfig::mValue) },
+                                  &profileValues));
+    ASSERT_EQ(1u, profileValues.size());
+
+    // TODO(johnylin): refactor this after supporting VALUES type for profile values.
+    uint32_t profileMin = profileValues[0].range.min.u32;
+    uint32_t profileMax = profileValues[0].range.max.u32;
+    uint32_t profileStep =  profileValues[0].range.step.u32;
+    for (uint32_t p = profileMin; p <= profileMax; p += profileStep) {
+        printf("Configure profile = %u\n", p);
+        codecProfile.mValue = p;
+        std::vector<C2Param * const> params{ &codecProfile };
+        std::vector<std::unique_ptr<C2SettingResult>> failures;
+        ASSERT_EQ(C2_OK, mIntf->config_nb(params, &failures));
+        EXPECT_EQ(0u, failures.size());
+
+        ASSERT_EQ(mComponent->start(), C2_OK);
+        ASSERT_EQ(mComponent->start(), C2_BAD_STATE);  // already started
+        ASSERT_EQ(mComponent->stop(), C2_OK);
+        ASSERT_EQ(mComponent->stop(), C2_BAD_STATE);  // already stopped
+    }
+}
+
 }  // namespace android