Merge "MediaCodec: ensure reply does not get lost"
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 5d17f97..ef0eed8 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -1008,6 +1008,12 @@
return err;
}
+void MediaCodec::PostReplyWithError(const sp<AMessage> &msg, int32_t err) {
+ sp<AReplyToken> replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+ PostReplyWithError(replyID, err);
+}
+
void MediaCodec::PostReplyWithError(const sp<AReplyToken> &replyID, int32_t err) {
int32_t finalErr = err;
if (mReleasedByResourceManager) {
@@ -1511,7 +1517,6 @@
mStickyError = OK;
// reset state not reset by setState(UNINITIALIZED)
- mReplyID = 0;
mDequeueInputReplyID = 0;
mDequeueOutputReplyID = 0;
mDequeueInputTimeoutGeneration = 0;
@@ -2159,7 +2164,7 @@
if (mState == RELEASING) {
mComponentName.clear();
}
- (new AMessage)->postReply(mReplyID);
+ postPendingRepliesAndDeferredMessages();
sendErrorResponse = false;
}
break;
@@ -2185,7 +2190,7 @@
case FLUSHED:
case STARTED:
{
- sendErrorResponse = false;
+ sendErrorResponse = (mReplyID != nullptr);
setStickyError(err);
postActivityNotificationIfPossible();
@@ -2215,7 +2220,7 @@
default:
{
- sendErrorResponse = false;
+ sendErrorResponse = (mReplyID != nullptr);
setStickyError(err);
postActivityNotificationIfPossible();
@@ -2242,7 +2247,15 @@
}
if (sendErrorResponse) {
- PostReplyWithError(mReplyID, err);
+ // TRICKY: replicate PostReplyWithError logic for
+ // err code override
+ int32_t finalErr = err;
+ if (mReleasedByResourceManager) {
+ // override the err code if MediaCodec has been
+ // released by ResourceManager.
+ finalErr = DEAD_OBJECT;
+ }
+ postPendingRepliesAndDeferredMessages(finalErr);
}
break;
}
@@ -2290,7 +2303,7 @@
MediaResource::CodecResource(mFlags & kFlagIsSecure, mIsVideo));
}
- (new AMessage)->postReply(mReplyID);
+ postPendingRepliesAndDeferredMessages();
break;
}
@@ -2329,7 +2342,7 @@
mFlags |= kFlagUsesSoftwareRenderer;
}
setState(CONFIGURED);
- (new AMessage)->postReply(mReplyID);
+ postPendingRepliesAndDeferredMessages();
// augment our media metrics info, now that we know more things
// such as what the codec extracted from any CSD passed in.
@@ -2374,6 +2387,12 @@
case kWhatInputSurfaceCreated:
{
+ if (mState != CONFIGURED) {
+ // state transitioned unexpectedly; we should have replied already.
+ ALOGD("received kWhatInputSurfaceCreated message in state %s",
+ stateString(mState).c_str());
+ break;
+ }
// response to initiateCreateInputSurface()
status_t err = NO_ERROR;
sp<AMessage> response = new AMessage;
@@ -2392,12 +2411,18 @@
} else {
response->setInt32("err", err);
}
- response->postReply(mReplyID);
+ postPendingRepliesAndDeferredMessages(response);
break;
}
case kWhatInputSurfaceAccepted:
{
+ if (mState != CONFIGURED) {
+ // state transitioned unexpectedly; we should have replied already.
+ ALOGD("received kWhatInputSurfaceAccepted message in state %s",
+ stateString(mState).c_str());
+ break;
+ }
// response to initiateSetInputSurface()
status_t err = NO_ERROR;
sp<AMessage> response = new AMessage();
@@ -2408,19 +2433,25 @@
} else {
response->setInt32("err", err);
}
- response->postReply(mReplyID);
+ postPendingRepliesAndDeferredMessages(response);
break;
}
case kWhatSignaledInputEOS:
{
+ if (!isExecuting()) {
+ // state transitioned unexpectedly; we should have replied already.
+ ALOGD("received kWhatSignaledInputEOS message in state %s",
+ stateString(mState).c_str());
+ break;
+ }
// response to signalEndOfInputStream()
sp<AMessage> response = new AMessage;
status_t err;
if (msg->findInt32("err", &err)) {
response->setInt32("err", err);
}
- response->postReply(mReplyID);
+ postPendingRepliesAndDeferredMessages(response);
break;
}
@@ -2439,7 +2470,7 @@
MediaResource::GraphicMemoryResource(getGraphicBufferSize()));
}
setState(STARTED);
- (new AMessage)->postReply(mReplyID);
+ postPendingRepliesAndDeferredMessages();
break;
}
@@ -2669,7 +2700,7 @@
break;
}
setState(INITIALIZED);
- (new AMessage)->postReply(mReplyID);
+ postPendingRepliesAndDeferredMessages();
break;
}
@@ -2693,7 +2724,7 @@
mReleaseSurface.reset();
if (mReplyID != nullptr) {
- (new AMessage)->postReply(mReplyID);
+ postPendingRepliesAndDeferredMessages();
}
if (mAsyncReleaseCompleteNotification != nullptr) {
flushMediametrics();
@@ -2718,7 +2749,7 @@
mCodec->signalResume();
}
- (new AMessage)->postReply(mReplyID);
+ postPendingRepliesAndDeferredMessages();
break;
}
@@ -2730,14 +2761,18 @@
case kWhatInit:
{
- sp<AReplyToken> replyID;
- CHECK(msg->senderAwaitsResponse(&replyID));
-
if (mState != UNINITIALIZED) {
- PostReplyWithError(replyID, INVALID_OPERATION);
+ PostReplyWithError(msg, INVALID_OPERATION);
break;
}
+ if (mReplyID) {
+ mDeferredMessages.push_back(msg);
+ break;
+ }
+ sp<AReplyToken> replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
mReplyID = replyID;
setState(INITIALIZING);
@@ -2799,14 +2834,18 @@
case kWhatConfigure:
{
- sp<AReplyToken> replyID;
- CHECK(msg->senderAwaitsResponse(&replyID));
-
if (mState != INITIALIZED) {
- PostReplyWithError(replyID, INVALID_OPERATION);
+ PostReplyWithError(msg, INVALID_OPERATION);
break;
}
+ if (mReplyID) {
+ mDeferredMessages.push_back(msg);
+ break;
+ }
+ sp<AReplyToken> replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
sp<RefBase> obj;
CHECK(msg->findObject("surface", &obj));
@@ -2944,15 +2983,19 @@
case kWhatCreateInputSurface:
case kWhatSetInputSurface:
{
- sp<AReplyToken> replyID;
- CHECK(msg->senderAwaitsResponse(&replyID));
-
// Must be configured, but can't have been started yet.
if (mState != CONFIGURED) {
- PostReplyWithError(replyID, INVALID_OPERATION);
+ PostReplyWithError(msg, INVALID_OPERATION);
break;
}
+ if (mReplyID) {
+ mDeferredMessages.push_back(msg);
+ break;
+ }
+ sp<AReplyToken> replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
mReplyID = replyID;
if (msg->what() == kWhatCreateInputSurface) {
mCodec->initiateCreateInputSurface();
@@ -2967,9 +3010,6 @@
}
case kWhatStart:
{
- sp<AReplyToken> replyID;
- CHECK(msg->senderAwaitsResponse(&replyID));
-
if (mState == FLUSHED) {
setState(STARTED);
if (mHavePendingInputBuffers) {
@@ -2977,13 +3017,20 @@
mHavePendingInputBuffers = false;
}
mCodec->signalResume();
- PostReplyWithError(replyID, OK);
+ PostReplyWithError(msg, OK);
break;
} else if (mState != CONFIGURED) {
- PostReplyWithError(replyID, INVALID_OPERATION);
+ PostReplyWithError(msg, INVALID_OPERATION);
break;
}
+ if (mReplyID) {
+ mDeferredMessages.push_back(msg);
+ break;
+ }
+ sp<AReplyToken> replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
mReplyID = replyID;
setState(STARTING);
@@ -2991,15 +3038,42 @@
break;
}
- case kWhatStop:
+ case kWhatStop: {
+ if (mReplyID) {
+ mDeferredMessages.push_back(msg);
+ break;
+ }
+ [[fallthrough]];
+ }
case kWhatRelease:
{
State targetState =
(msg->what() == kWhatStop) ? INITIALIZED : UNINITIALIZED;
+ if ((mState == RELEASING && targetState == UNINITIALIZED)
+ || (mState == STOPPING && targetState == INITIALIZED)) {
+ mDeferredMessages.push_back(msg);
+ break;
+ }
+
sp<AReplyToken> replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
+ sp<AMessage> asyncNotify;
+ (void)msg->findMessage("async", &asyncNotify);
+ // post asyncNotify if going out of scope.
+ struct AsyncNotifyPost {
+ AsyncNotifyPost(const sp<AMessage> &asyncNotify) : mAsyncNotify(asyncNotify) {}
+ ~AsyncNotifyPost() {
+ if (mAsyncNotify) {
+ mAsyncNotify->post();
+ }
+ }
+ void clear() { mAsyncNotify.clear(); }
+ private:
+ sp<AMessage> mAsyncNotify;
+ } asyncNotifyPost{asyncNotify};
+
// already stopped/released
if (mState == UNINITIALIZED && mReleasedByResourceManager) {
sp<AMessage> response = new AMessage;
@@ -3063,12 +3137,14 @@
// after this, and we'll no longer be able to reply.
if (mState == FLUSHING || mState == STOPPING
|| mState == CONFIGURING || mState == STARTING) {
- (new AMessage)->postReply(mReplyID);
+ // mReply is always set if in these states.
+ postPendingRepliesAndDeferredMessages();
}
if (mFlags & kFlagSawMediaServerDie) {
// It's dead, Jim. Don't expect initiateShutdown to yield
// any useful results now...
+ // Any pending reply would have been handled at kWhatError.
setState(UNINITIALIZED);
if (targetState == UNINITIALIZED) {
mComponentName.clear();
@@ -3082,12 +3158,12 @@
// reply now with an error to unblock the client, client can
// release after the failure (instead of ANR).
if (msg->what() == kWhatStop && (mFlags & kFlagStickyError)) {
+ // Any pending reply would have been handled at kWhatError.
PostReplyWithError(replyID, getStickyError());
break;
}
- sp<AMessage> asyncNotify;
- if (msg->findMessage("async", &asyncNotify) && asyncNotify != nullptr) {
+ if (asyncNotify != nullptr) {
if (mSurface != NULL) {
if (!mReleaseSurface) {
mReleaseSurface.reset(new ReleaseSurface);
@@ -3107,6 +3183,12 @@
}
}
+ if (mReplyID) {
+ // State transition replies are handled above, so this reply
+ // would not be related to state transition. As we are
+ // shutting down the component, just fail the operation.
+ postPendingRepliesAndDeferredMessages(UNKNOWN_ERROR);
+ }
mReplyID = replyID;
setState(msg->what() == kWhatStop ? STOPPING : RELEASING);
@@ -3121,8 +3203,8 @@
if (asyncNotify != nullptr) {
mResourceManagerProxy->markClientForPendingRemoval();
- (new AMessage)->postReply(mReplyID);
- mReplyID = 0;
+ postPendingRepliesAndDeferredMessages();
+ asyncNotifyPost.clear();
mAsyncReleaseCompleteNotification = asyncNotify;
}
@@ -3293,17 +3375,21 @@
case kWhatSignalEndOfInputStream:
{
- sp<AReplyToken> replyID;
- CHECK(msg->senderAwaitsResponse(&replyID));
-
if (!isExecuting() || !mHaveInputSurface) {
- PostReplyWithError(replyID, INVALID_OPERATION);
+ PostReplyWithError(msg, INVALID_OPERATION);
break;
} else if (mFlags & kFlagStickyError) {
- PostReplyWithError(replyID, getStickyError());
+ PostReplyWithError(msg, getStickyError());
break;
}
+ if (mReplyID) {
+ mDeferredMessages.push_back(msg);
+ break;
+ }
+ sp<AReplyToken> replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
mReplyID = replyID;
mCodec->signalEndOfInputStream();
break;
@@ -3345,17 +3431,21 @@
case kWhatFlush:
{
- sp<AReplyToken> replyID;
- CHECK(msg->senderAwaitsResponse(&replyID));
-
if (!isExecuting()) {
- PostReplyWithError(replyID, INVALID_OPERATION);
+ PostReplyWithError(msg, INVALID_OPERATION);
break;
} else if (mFlags & kFlagStickyError) {
- PostReplyWithError(replyID, getStickyError());
+ PostReplyWithError(msg, getStickyError());
break;
}
+ if (mReplyID) {
+ mDeferredMessages.push_back(msg);
+ break;
+ }
+ sp<AReplyToken> replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
mReplyID = replyID;
// TODO: skip flushing if already FLUSHED
setState(FLUSHING);
@@ -4188,6 +4278,26 @@
return OK;
}
+void MediaCodec::postPendingRepliesAndDeferredMessages(status_t err /* = OK */) {
+ sp<AMessage> response{new AMessage};
+ if (err != OK) {
+ response->setInt32("err", err);
+ }
+ postPendingRepliesAndDeferredMessages(response);
+}
+
+void MediaCodec::postPendingRepliesAndDeferredMessages(const sp<AMessage> &response) {
+ CHECK(mReplyID);
+ response->postReply(mReplyID);
+ mReplyID.clear();
+ ALOGV_IF(!mDeferredMessages.empty(),
+ "posting %zu deferred messages", mDeferredMessages.size());
+ for (sp<AMessage> msg : mDeferredMessages) {
+ msg->post();
+ }
+ mDeferredMessages.clear();
+}
+
std::string MediaCodec::stateString(State state) {
const char *rval = NULL;
char rawbuffer[16]; // room for "%d"
diff --git a/media/libstagefright/include/media/stagefright/MediaCodec.h b/media/libstagefright/include/media/stagefright/MediaCodec.h
index f7e6c27..9bff99a 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodec.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodec.h
@@ -366,6 +366,7 @@
AString mOwnerName;
sp<MediaCodecInfo> mCodecInfo;
sp<AReplyToken> mReplyID;
+ std::vector<sp<AMessage>> mDeferredMessages;
uint32_t mFlags;
status_t mStickyError;
sp<Surface> mSurface;
@@ -435,6 +436,7 @@
static status_t PostAndAwaitResponse(
const sp<AMessage> &msg, sp<AMessage> *response);
+ void PostReplyWithError(const sp<AMessage> &msg, int32_t err);
void PostReplyWithError(const sp<AReplyToken> &replyID, int32_t err);
status_t init(const AString &name);
@@ -484,6 +486,9 @@
bool hasPendingBuffer(int portIndex);
bool hasPendingBuffer();
+ void postPendingRepliesAndDeferredMessages(status_t err = OK);
+ void postPendingRepliesAndDeferredMessages(const sp<AMessage> &response);
+
/* called to get the last codec error when the sticky flag is set.
* if no such codec error is found, returns UNKNOWN_ERROR.
*/