stagefright: prevent component to be stuck on error
Bug: 36818684
Bug: 34983882
Test: Run the command below on Nexus Player overnight:
while true ; do adb shell am instrument -e class 'android.platform.tv.tests.aupt.YouTubeTests' -w 'android.platform.tv.tests.aupt/android.support.test.aupt.AuptTestRunner' ; done
Test: adb shell am instrument -e size small -w 'android.media.cts/android.support.test.runner.AndroidJUnitRunner'
Change-Id: I9663c3d62d97c3f5db0af6c30f39bb59cbc7e3e7
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 8b91541..2877ba2 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -251,6 +251,7 @@
virtual PortMode getPortMode(OMX_U32 portIndex);
+ virtual void stateExited();
virtual bool onMessageReceived(const sp<AMessage> &msg);
virtual bool onOMXEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2);
@@ -553,6 +554,7 @@
mTunneled(false),
mDescribeColorAspectsIndex((OMX_INDEXTYPE)0),
mDescribeHDRStaticInfoIndex((OMX_INDEXTYPE)0),
+ mStateGeneration(0),
mVendorExtensionsStatus(kExtensionsUnchecked) {
mUninitializedState = new UninitializedState(this);
mLoadedState = new LoadedState(this);
@@ -5322,6 +5324,10 @@
return KEEP_BUFFERS;
}
+void ACodec::BaseState::stateExited() {
+ ++mCodec->mStateGeneration;
+}
+
bool ACodec::BaseState::onMessageReceived(const sp<AMessage> &msg) {
switch (msg->what()) {
case kWhatInputBufferFilled:
@@ -5398,6 +5404,12 @@
break;
}
+ case ACodec::kWhatForceStateTransition:
+ {
+ ALOGV("Already transitioned --- ignore");
+ break;
+ }
+
default:
return false;
}
@@ -6988,7 +7000,6 @@
void ACodec::ExecutingState::stateEntered() {
ALOGV("[%s] Now Executing", mCodec->mComponentName.c_str());
-
mCodec->mRenderTracker.clear(systemTime(CLOCK_MONOTONIC));
mCodec->processDeferredMessages();
}
@@ -7614,6 +7625,30 @@
mCallback->onSignaledInputEOS(err);
}
+void ACodec::forceStateTransition(int generation) {
+ if (generation != mStateGeneration) {
+ ALOGV("Ignoring stale force state transition message: #%d (now #%d)",
+ generation, mStateGeneration);
+ return;
+ }
+ ALOGE("State machine stuck");
+ // Error must have already been signalled to the client.
+
+ // Deferred messages will be handled at LoadedState at the end of the
+ // transition.
+ mShutdownInProgress = true;
+ // No shutdown complete callback at the end of the transition.
+ mExplicitShutdown = false;
+ mKeepComponentAllocated = true;
+
+ status_t err = mOMXNode->sendCommand(OMX_CommandStateSet, OMX_StateIdle);
+ if (err != OK) {
+ // TODO: do some recovery here.
+ } else {
+ changeState(mExecutingToIdleState);
+ }
+}
+
bool ACodec::ExecutingState::onOMXFrameRendered(int64_t mediaTimeUs, nsecs_t systemNano) {
mCodec->onFrameRendered(mediaTimeUs, systemNano);
return true;
@@ -7680,7 +7715,14 @@
switch (msg->what()) {
case kWhatFlush:
- case kWhatShutdown:
+ case kWhatShutdown: {
+ if (mCodec->mFatalError) {
+ sp<AMessage> msg = new AMessage(ACodec::kWhatForceStateTransition, mCodec);
+ msg->setInt32("generation", mCodec->mStateGeneration);
+ msg->post(3000000);
+ }
+ // fall-through
+ }
case kWhatResume:
case kWhatSetParameters:
{
@@ -7693,6 +7735,16 @@
break;
}
+ case kWhatForceStateTransition:
+ {
+ int32_t generation = 0;
+ CHECK(msg->findInt32("generation", &generation));
+ mCodec->forceStateTransition(generation);
+
+ handled = true;
+ break;
+ }
+
default:
handled = BaseState::onMessageReceived(msg);
break;
@@ -7752,15 +7804,7 @@
if (err != OK) {
mCodec->signalError(OMX_ErrorUndefined, makeNoSideEffectStatus(err));
-
- // This is technically not correct, but appears to be
- // the only way to free the component instance.
- // Controlled transitioning from excecuting->idle
- // and idle->loaded seem impossible probably because
- // the output port never finishes re-enabling.
- mCodec->mShutdownInProgress = true;
- mCodec->mKeepComponentAllocated = false;
- mCodec->changeState(mCodec->mLoadedState);
+ ALOGE("Error occurred while disabling the output port");
}
return true;
@@ -7785,7 +7829,7 @@
}
default:
- return false;
+ return BaseState::onOMXEvent(event, data1, data2);
}
}
@@ -7987,6 +8031,11 @@
case kWhatShutdown:
{
mCodec->deferMessage(msg);
+ if (mCodec->mFatalError) {
+ sp<AMessage> msg = new AMessage(ACodec::kWhatForceStateTransition, mCodec);
+ msg->setInt32("generation", mCodec->mStateGeneration);
+ msg->post(3000000);
+ }
break;
}
@@ -7997,6 +8046,16 @@
break;
}
+ case kWhatForceStateTransition:
+ {
+ int32_t generation = 0;
+ CHECK(msg->findInt32("generation", &generation));
+ mCodec->forceStateTransition(generation);
+
+ handled = true;
+ break;
+ }
+
default:
handled = BaseState::onMessageReceived(msg);
break;
diff --git a/media/libstagefright/include/ACodec.h b/media/libstagefright/include/ACodec.h
index 06ee0e8..22b8657 100644
--- a/media/libstagefright/include/ACodec.h
+++ b/media/libstagefright/include/ACodec.h
@@ -150,6 +150,7 @@
kWhatSubmitOutputMetadataBufferIfEOS = 'subm',
kWhatOMXDied = 'OMXd',
kWhatReleaseCodecInstance = 'relC',
+ kWhatForceStateTransition = 'fstt',
};
enum {
@@ -305,6 +306,8 @@
std::shared_ptr<ACodecBufferChannel> mBufferChannel;
+ int32_t mStateGeneration;
+
enum {
kExtensionsUnchecked,
kExtensionsNone,
@@ -569,6 +572,9 @@
// Send EOS on input stream.
void onSignalEndOfInputStream();
+ // Force EXEC->IDLE->LOADED shutdown sequence if not stale.
+ void forceStateTransition(int generation);
+
DISALLOW_EVIL_CONSTRUCTORS(ACodec);
};