Error handling in ACodec and Nuplayer.

Codec errors (and codec not found errors) now trigger a controlled shutdown
of playback and signal errors to the MediaPlayer client.

Change-Id: I2ee23ff2a1422d05a1a21e50ecb87d7c7ab958cc
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index f13e9bb..e965f14 100644
--- a/include/media/stagefright/ACodec.h
+++ b/include/media/stagefright/ACodec.h
@@ -36,6 +36,7 @@
         kWhatShutdownCompleted   = 'scom',
         kWhatFlushCompleted      = 'fcom',
         kWhatOutputFormatChanged = 'outC',
+        kWhatError               = 'erro',
     };
 
     ACodec();
@@ -58,7 +59,6 @@
     struct OutputPortSettingsChangedState;
     struct ExecutingToIdleState;
     struct IdleToLoadedState;
-    struct ErrorState;
     struct FlushingState;
 
     enum {
@@ -102,7 +102,6 @@
     sp<OutputPortSettingsChangedState> mOutputPortSettingsChangedState;
     sp<ExecutingToIdleState> mExecutingToIdleState;
     sp<IdleToLoadedState> mIdleToLoadedState;
-    sp<ErrorState> mErrorState;
     sp<FlushingState> mFlushingState;
 
     AString mComponentName;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index b06f20d..7fb141a 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -340,6 +340,11 @@
                 }
 
                 finishFlushIfPossible();
+            } else if (what == ACodec::kWhatError) {
+                LOGE("Received error from %s decoder, aborting playback.",
+                     audio ? "audio" : "video");
+
+                mRenderer->queueEOS(audio, UNKNOWN_ERROR);
             } else {
                 CHECK_EQ((int)what, (int)ACodec::kWhatDrainThisBuffer);
 
@@ -358,13 +363,24 @@
                 int32_t audio;
                 CHECK(msg->findInt32("audio", &audio));
 
+                int32_t finalResult;
+                CHECK(msg->findInt32("finalResult", &finalResult));
+
                 if (audio) {
                     mAudioEOS = true;
                 } else {
                     mVideoEOS = true;
                 }
 
-                LOGV("reached %s EOS", audio ? "audio" : "video");
+                if (finalResult == ERROR_END_OF_STREAM) {
+                    LOGV("reached %s EOS", audio ? "audio" : "video");
+                } else {
+                    LOGE("%s track encountered an error (0x%08x)",
+                         audio ? "audio" : "video", finalResult);
+
+                    notifyListener(
+                            MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, finalResult);
+                }
 
                 if ((mAudioEOS || mAudioDecoder == NULL)
                         && (mVideoEOS || mVideoDecoder == NULL)) {
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index 828e008..35ed43f 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -200,6 +200,22 @@
 void NuPlayer::Renderer::onDrainAudioQueue() {
 
     for (;;) {
+        if (mAudioQueue.empty()) {
+            break;
+        }
+
+        QueueEntry *entry = &*mAudioQueue.begin();
+
+        if (entry->mBuffer == NULL) {
+            // EOS
+
+            notifyEOS(true /* audio */, entry->mFinalResult);
+
+            mAudioQueue.erase(mAudioQueue.begin());
+            entry = NULL;
+            return;
+        }
+
         uint32_t numFramesPlayed;
         CHECK_EQ(mAudioSink->getPosition(&numFramesPlayed), (status_t)OK);
 
@@ -213,22 +229,6 @@
             break;
         }
 
-        if (mAudioQueue.empty()) {
-            break;
-        }
-
-        QueueEntry *entry = &*mAudioQueue.begin();
-
-        if (entry->mBuffer == NULL) {
-            // EOS
-
-            notifyEOS(true /* audio */);
-
-            mAudioQueue.erase(mAudioQueue.begin());
-            entry = NULL;
-            return;
-        }
-
         if (entry->mOffset == 0) {
             int64_t mediaTimeUs;
             CHECK(entry->mBuffer->meta()->findInt64("timeUs", &mediaTimeUs));
@@ -330,7 +330,7 @@
     if (entry->mBuffer == NULL) {
         // EOS
 
-        notifyEOS(false /* audio */);
+        notifyEOS(false /* audio */, entry->mFinalResult);
 
         mVideoQueue.erase(mVideoQueue.begin());
         entry = NULL;
@@ -352,10 +352,11 @@
     notifyPosition();
 }
 
-void NuPlayer::Renderer::notifyEOS(bool audio) {
+void NuPlayer::Renderer::notifyEOS(bool audio, status_t finalResult) {
     sp<AMessage> notify = mNotify->dup();
     notify->setInt32("what", kWhatEOS);
     notify->setInt32("audio", static_cast<int32_t>(audio));
+    notify->setInt32("finalResult", finalResult);
     notify->post();
 }
 
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
index 703e971..2713031 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
@@ -111,7 +111,7 @@
     void onPause();
     void onResume();
 
-    void notifyEOS(bool audio);
+    void notifyEOS(bool audio, status_t finalResult);
     void notifyFlushComplete(bool audio);
     void notifyPosition();
 
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 174ec92..5d91f6a 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -285,21 +285,6 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-struct ACodec::ErrorState : public ACodec::BaseState {
-    ErrorState(ACodec *codec);
-
-protected:
-    virtual bool onMessageReceived(const sp<AMessage> &msg);
-    virtual void stateEntered();
-
-    virtual bool onOMXEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2);
-
-private:
-    DISALLOW_EVIL_CONSTRUCTORS(ErrorState);
-};
-
-////////////////////////////////////////////////////////////////////////////////
-
 struct ACodec::FlushingState : public ACodec::BaseState {
     FlushingState(ACodec *codec);
 
@@ -335,7 +320,6 @@
 
     mExecutingToIdleState = new ExecutingToIdleState(this);
     mIdleToLoadedState = new IdleToLoadedState(this);
-    mErrorState = new ErrorState(this);
     mFlushingState = new FlushingState(this);
 
     mPortEOS[kPortIndexInput] = mPortEOS[kPortIndexOutput] = false;
@@ -594,7 +578,10 @@
 
 ACodec::BufferInfo *ACodec::dequeueBufferFromNativeWindow() {
     ANativeWindowBuffer *buf;
-    CHECK_EQ(mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buf), 0);
+    if (mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buf) != 0) {
+        LOGE("dequeueBuffer failed.");
+        return NULL;
+    }
 
     for (size_t i = mBuffers[kPortIndexOutput].size(); i-- > 0;) {
         BufferInfo *info =
@@ -1263,10 +1250,12 @@
         return false;
     }
 
-    LOGE("[%s] ERROR(0x%08lx, 0x%08lx)",
-         mCodec->mComponentName.c_str(), data1, data2);
+    LOGE("[%s] ERROR(0x%08lx)", mCodec->mComponentName.c_str(), data1);
 
-    mCodec->changeState(mCodec->mErrorState);
+    sp<AMessage> notify = mCodec->mNotify->dup();
+    notify->setInt32("what", ACodec::kWhatError);
+    notify->setInt32("omx-error", data1);
+    notify->post();
 
     return true;
 }
@@ -1595,13 +1584,15 @@
                     info = mCodec->dequeueBufferFromNativeWindow();
                 }
 
-                LOGV("[%s] calling fillBuffer %p",
-                     mCodec->mComponentName.c_str(), info->mBufferID);
+                if (info != NULL) {
+                    LOGV("[%s] calling fillBuffer %p",
+                         mCodec->mComponentName.c_str(), info->mBufferID);
 
-                CHECK_EQ(mCodec->mOMX->fillBuffer(mCodec->mNode, info->mBufferID),
-                         (status_t)OK);
+                    CHECK_EQ(mCodec->mOMX->fillBuffer(mCodec->mNode, info->mBufferID),
+                             (status_t)OK);
 
-                info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
+                    info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
+                }
             }
             break;
         }
@@ -1642,6 +1633,7 @@
             notify->post();
 
             handled = true;
+            break;
         }
 
         case ACodec::kWhatFlush:
@@ -1651,6 +1643,7 @@
             notify->post();
 
             handled = true;
+            break;
         }
 
         default:
@@ -1696,7 +1689,16 @@
         node = NULL;
     }
 
-    CHECK(node != NULL);
+    if (node == NULL) {
+        LOGE("Unable to instantiate a decoder for type '%s'.", mime.c_str());
+
+        sp<AMessage> notify = mCodec->mNotify->dup();
+        notify->setInt32("what", ACodec::kWhatError);
+        notify->setInt32("omx-error", OMX_ErrorComponentNotFound);
+        notify->post();
+
+        return;
+    }
 
     sp<AMessage> notify = new AMessage(kWhatOMXMessage, mCodec->id());
     observer->setNotificationMessage(notify);
@@ -2236,26 +2238,6 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-ACodec::ErrorState::ErrorState(ACodec *codec)
-    : BaseState(codec) {
-}
-
-bool ACodec::ErrorState::onMessageReceived(const sp<AMessage> &msg) {
-    return BaseState::onMessageReceived(msg);
-}
-
-void ACodec::ErrorState::stateEntered() {
-    LOGV("[%s] Now in ErrorState", mCodec->mComponentName.c_str());
-}
-
-bool ACodec::ErrorState::onOMXEvent(
-        OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
-    LOGV("EVENT(%d, 0x%08lx, 0x%08lx)", event, data1, data2);
-    return true;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
 ACodec::FlushingState::FlushingState(ACodec *codec)
     : BaseState(codec) {
 }