From 16e79115e497386eaf010af388627f94314a55a3 Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Fri, 1 Aug 2014 10:30:26 -0700 Subject: MediaCodecSource: stop puller from caller's thread instead of looper Currently CameraSource/AudioSource's stop() and read() are both called from the puller's looper. This works if source operates normally (i.e. read() returns regularly before source is stopped), as the stop() will eventually be handled by the looper. However, if for some reason the source hang, it will get stuck in read(), and the stop() will never be processed, which could lead to ANR (in addition to the source hang). We need to move the source's stop out of the puller's looper. It also can't be on MediaCodecSource's looper, because the source's stop synchrounously waits for all outstanding buffers to return, these are only returned when MediaCodecSource's looper processes the buffer. This change moves the stop to MediaCodecSource::stop, after encoder is shutdown. Bug: 16522726 Change-Id: Ie91f563c5d8a98ab091bf1945af4e51f662b9403 --- media/libstagefright/MediaCodecSource.cpp | 74 ++++++++++++++++++------------- 1 file changed, 42 insertions(+), 32 deletions(-) (limited to 'media/libstagefright/MediaCodecSource.cpp') diff --git a/media/libstagefright/MediaCodecSource.cpp b/media/libstagefright/MediaCodecSource.cpp index 9868ecf..1a80dcc 100644 --- a/media/libstagefright/MediaCodecSource.cpp +++ b/media/libstagefright/MediaCodecSource.cpp @@ -54,7 +54,7 @@ struct MediaCodecSource::Puller : public AHandler { Puller(const sp &source); status_t start(const sp &meta, const sp ¬ify); - void stopAsync(); + void stop(); void pause(); void resume(); @@ -139,8 +139,17 @@ status_t MediaCodecSource::Puller::start(const sp &meta, return postSynchronouslyAndReturnError(msg); } -void MediaCodecSource::Puller::stopAsync() { - ALOGV("puller (%s) stopAsync", mIsAudio ? "audio" : "video"); +void MediaCodecSource::Puller::stop() { + // Stop source from caller's thread instead of puller's looper. + // mSource->stop() is thread-safe, doing it outside the puller's + // looper allows us to at least stop if source gets stuck. + // If source gets stuck in read(), the looper would never + // be able to process the stop(), which could lead to ANR. + + ALOGV("source (%s) stopping", mIsAudio ? "audio" : "video"); + mSource->stop(); + ALOGV("source (%s) stopped", mIsAudio ? "audio" : "video"); + (new AMessage(kWhatStop, id()))->post(); } @@ -194,9 +203,6 @@ void MediaCodecSource::Puller::onMessageReceived(const sp &msg) { case kWhatStop: { - ALOGV("source (%s) stopping", mIsAudio ? "audio" : "video"); - mSource->stop(); - ALOGV("source (%s) stopped", mIsAudio ? "audio" : "video"); ++mPullGeneration; handleEOS(); @@ -283,7 +289,21 @@ status_t MediaCodecSource::start(MetaData* params) { status_t MediaCodecSource::stop() { sp msg = new AMessage(kWhatStop, mReflector->id()); - return postSynchronouslyAndReturnError(msg); + status_t err = postSynchronouslyAndReturnError(msg); + + // mPuller->stop() needs to be done outside MediaCodecSource's looper, + // as it contains a synchronous call to stop the underlying MediaSource, + // which often waits for all outstanding MediaBuffers to return, but + // MediaBuffers are only returned when MediaCodecSource looper gets + // to process them. + + if (mPuller != NULL) { + ALOGI("puller (%s) stopping", mIsVideo ? "video" : "audio"); + mPuller->stop(); + ALOGI("puller (%s) stopped", mIsVideo ? "video" : "audio"); + } + + return err; } status_t MediaCodecSource::pause() { @@ -301,10 +321,10 @@ status_t MediaCodecSource::read( Mutex::Autolock autolock(mOutputBufferLock); *buffer = NULL; - while (mOutputBufferQueue.size() == 0 && !mEncodedReachedEOS) { + while (mOutputBufferQueue.size() == 0 && !mEncoderReachedEOS) { mOutputBufferCond.wait(mOutputBufferLock); } - if (!mEncodedReachedEOS) { + if (!mEncoderReachedEOS) { *buffer = *mOutputBufferQueue.begin(); mOutputBufferQueue.erase(mOutputBufferQueue.begin()); return OK; @@ -330,9 +350,8 @@ MediaCodecSource::MediaCodecSource( mStarted(false), mStopping(false), mDoMoreWorkPending(false), - mPullerReachedEOS(false), mFirstSampleTimeUs(-1ll), - mEncodedReachedEOS(false), + mEncoderReachedEOS(false), mErrorCode(OK) { CHECK(mLooper != NULL); @@ -434,7 +453,7 @@ status_t MediaCodecSource::initEncoder() { return err; } - mEncodedReachedEOS = false; + mEncoderReachedEOS = false; mErrorCode = OK; return OK; @@ -465,10 +484,6 @@ void MediaCodecSource::releaseEncoder() { mEncoderOutputBuffers.clear(); } -bool MediaCodecSource::reachedEOS() { - return mEncodedReachedEOS && ((mPuller == NULL) || mPullerReachedEOS); -} - status_t MediaCodecSource::postSynchronouslyAndReturnError( const sp &msg) { sp response; @@ -486,8 +501,8 @@ status_t MediaCodecSource::postSynchronouslyAndReturnError( } void MediaCodecSource::signalEOS(status_t err) { - if (!mEncodedReachedEOS) { - ALOGI("encoder (%s) reached EOS", mIsVideo ? "video" : "audio"); + if (!mEncoderReachedEOS) { + ALOGV("encoder (%s) reached EOS", mIsVideo ? "video" : "audio"); { Mutex::Autolock autoLock(mOutputBufferLock); // release all unread media buffers @@ -496,16 +511,15 @@ void MediaCodecSource::signalEOS(status_t err) { (*it)->release(); } mOutputBufferQueue.clear(); - mEncodedReachedEOS = true; + mEncoderReachedEOS = true; mErrorCode = err; mOutputBufferCond.signal(); } releaseEncoder(); } - if (mStopping && reachedEOS()) { - ALOGI("MediaCodecSource (%s) fully stopped", - mIsVideo ? "video" : "audio"); + if (mStopping && mEncoderReachedEOS) { + ALOGI("encoder (%s) stopped", mIsVideo ? "video" : "audio"); // posting reply to everyone that's waiting List::iterator it; for (it = mStopReplyIDQueue.begin(); @@ -755,7 +769,6 @@ status_t MediaCodecSource::onStart(MetaData *params) { kWhatPullerNotify, mReflector->id()); err = mPuller->start(params, notify); if (err != OK) { - mPullerReachedEOS = true; return err; } } @@ -774,9 +787,9 @@ void MediaCodecSource::onMessageReceived(const sp &msg) { CHECK(msg->findPointer("accessUnit", (void**)&mbuf)); if (mbuf == NULL) { - ALOGI("puller (%s) reached EOS", + ALOGV("puller (%s) reached EOS", mIsVideo ? "video" : "audio"); - mPullerReachedEOS = true; + signalEOS(); } if (mEncoder == NULL) { @@ -785,9 +798,8 @@ void MediaCodecSource::onMessageReceived(const sp &msg) { if (mbuf != NULL) { mbuf->release(); - } else { - signalEOS(); } + break; } @@ -833,14 +845,14 @@ void MediaCodecSource::onMessageReceived(const sp &msg) { } case kWhatStop: { - ALOGI("MediaCodecSource (%s) stopping", mIsVideo ? "video" : "audio"); + ALOGI("encoder (%s) stopping", mIsVideo ? "video" : "audio"); uint32_t replyID; CHECK(msg->senderAwaitsResponse(&replyID)); - if (reachedEOS()) { + if (mEncoderReachedEOS) { // if we already reached EOS, reply and return now - ALOGI("MediaCodecSource (%s) already stopped", + ALOGI("encoder (%s) already stopped", mIsVideo ? "video" : "audio"); (new AMessage)->postReply(replyID); break; @@ -860,8 +872,6 @@ void MediaCodecSource::onMessageReceived(const sp &msg) { if (mFlags & FLAG_USE_SURFACE_INPUT) { mEncoder->signalEndOfInputStream(); } else { - CHECK(mPuller != NULL); - mPuller->stopAsync(); signalEOS(); } break; -- cgit v1.1