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