codec2: use atomic state and synchronize start/stop calls
Since the client would call component API from different threads, we should
process mState atomically.
In addition, client would try to start/stop/reset asynchronously. All those
calls should be blocked until component started/stopped successfully which is
signaled by the first call.
Bug: 77202102
Test: adb shell setprop debug.stagefright.ccodec yes
Test: adb shell setprop debug.stagefright.ccodec_v4l2 yes
Test: adb shell killall mediaserver
Test: cts-tradefed run cts -m CtsMediaTestCases -t android.media.cts.MediaCodecTest#testAsyncFlushAndReset
Test: cts-tradefed run cts -m CtsMediaTestCases -t android.media.cts.MediaCodecTest#testAsyncStopAndReset
Change-Id: Ieeb3c7a38fb872b84230ac3aaf3939647bf2f2b5
diff --git a/C2VDAComponent.cpp b/C2VDAComponent.cpp
index e3f9886..7718229 100644
--- a/C2VDAComponent.cpp
+++ b/C2VDAComponent.cpp
@@ -506,11 +506,11 @@
}
mTaskRunner = mThread.task_runner();
mTaskRunner->PostTask(FROM_HERE, base::Bind(&C2VDAComponent::onCreate, base::Unretained(this)));
- mState = State::LOADED;
+ mState.store(State::LOADED);
}
C2VDAComponent::~C2VDAComponent() {
- CHECK_EQ(mState, State::LOADED);
+ CHECK_EQ(mState.load(), State::LOADED);
if (mThread.IsRunning()) {
mTaskRunner->PostTask(FROM_HERE,
@@ -773,6 +773,9 @@
void C2VDAComponent::onFlush() {
DCHECK(mTaskRunner->BelongsToCurrentThread());
ALOGV("onFlush");
+ if (mComponentState == ComponentState::FLUSHING) {
+ return; // Ignore other flush request when component is flushing.
+ }
EXPECT_STATE_OR_RETURN_ON_ERROR(STARTED);
mVDAAdaptor->reset();
@@ -852,7 +855,7 @@
UNUSED(mayBlock);
// TODO(johnylin): API says this method must be supported in all states, however I'm quite not
// sure what is the use case.
- if (mState != State::LOADED) {
+ if (mState.load() != State::LOADED) {
return C2_BAD_STATE;
}
mListener = listener;
@@ -1096,7 +1099,7 @@
}
c2_status_t C2VDAComponent::queue_nb(std::list<std::unique_ptr<C2Work>>* const items) {
- if (mState != State::RUNNING) {
+ if (mState.load() != State::RUNNING) {
return C2_BAD_STATE;
}
while (!items->empty()) {
@@ -1118,7 +1121,7 @@
if (mode != FLUSH_COMPONENT) {
return C2_OMITTED; // Tunneling is not supported by now
}
- if (mState != State::RUNNING) {
+ if (mState.load() != State::RUNNING) {
return C2_BAD_STATE;
}
mTaskRunner->PostTask(FROM_HERE, base::Bind(&C2VDAComponent::onFlush, base::Unretained(this)));
@@ -1130,7 +1133,7 @@
if (mode != DRAIN_COMPONENT_WITH_EOS && mode != DRAIN_COMPONENT_NO_EOS) {
return C2_OMITTED; // Tunneling is not supported by now
}
- if (mState != State::RUNNING) {
+ if (mState.load() != State::RUNNING) {
return C2_BAD_STATE;
}
mTaskRunner->PostTask(FROM_HERE, base::Bind(&C2VDAComponent::onDrain, base::Unretained(this),
@@ -1139,7 +1142,10 @@
}
c2_status_t C2VDAComponent::start() {
- if (mState != State::LOADED) {
+ // Use mStartStopLock to block other asynchronously start/stop calls.
+ std::lock_guard<std::mutex> lock(mStartStopLock);
+
+ if (mState.load() != State::LOADED) {
return C2_BAD_STATE; // start() is only supported when component is in LOADED state.
}
@@ -1153,13 +1159,17 @@
ALOGE("Failed to start component due to VDA error: %d", static_cast<int>(mVDAInitResult));
return C2_CORRUPTED;
}
- mState = State::RUNNING;
+ mState.store(State::RUNNING);
return C2_OK;
}
c2_status_t C2VDAComponent::stop() {
- if (!(mState == State::RUNNING || mState == State::ERROR)) {
- return C2_BAD_STATE; // component is already in stopped state.
+ // Use mStartStopLock to block other asynchronously start/stop calls.
+ std::lock_guard<std::mutex> lock(mStartStopLock);
+
+ auto state = mState.load();
+ if (!(state == State::RUNNING || state == State::ERROR)) {
+ return C2_OK; // Component is already in stopped state.
}
base::WaitableEvent done(base::WaitableEvent::ResetPolicy::AUTOMATIC,
@@ -1167,7 +1177,7 @@
mTaskRunner->PostTask(FROM_HERE,
base::Bind(&C2VDAComponent::onStop, base::Unretained(this), &done));
done.Wait();
- mState = State::LOADED;
+ mState.store(State::LOADED);
return C2_OK;
}
@@ -1178,8 +1188,7 @@
}
c2_status_t C2VDAComponent::release() {
- // TODO(johnylin): what should we do for release?
- return C2_OMITTED;
+ return reset();
}
std::shared_ptr<C2ComponentInterface> C2VDAComponent::intf() {