Merge "Separate the notion of "stop" from that of "release", i.e."
diff --git a/cmds/stagefright/SimplePlayer.cpp b/cmds/stagefright/SimplePlayer.cpp
index f269e80..fac3a8c 100644
--- a/cmds/stagefright/SimplePlayer.cpp
+++ b/cmds/stagefright/SimplePlayer.cpp
@@ -396,7 +396,7 @@
     for (size_t i = 0; i < mStateByTrackIndex.size(); ++i) {
         CodecState *state = &mStateByTrackIndex.editValueAt(i);
 
-        CHECK_EQ(state->mCodec->stop(), (status_t)OK);
+        CHECK_EQ(state->mCodec->release(), (status_t)OK);
     }
 
     mStartTimeRealUs = -1ll;
diff --git a/cmds/stagefright/codec.cpp b/cmds/stagefright/codec.cpp
index b850190..fbf800c 100644
--- a/cmds/stagefright/codec.cpp
+++ b/cmds/stagefright/codec.cpp
@@ -295,7 +295,7 @@
     for (size_t i = 0; i < stateByTrack.size(); ++i) {
         CodecState *state = &stateByTrack.editValueAt(i);
 
-        CHECK_EQ((status_t)OK, state->mCodec->stop());
+        CHECK_EQ((status_t)OK, state->mCodec->release());
     }
 
     return 0;
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index 70799a6..6735aff 100644
--- a/include/media/stagefright/ACodec.h
+++ b/include/media/stagefright/ACodec.h
@@ -49,7 +49,7 @@
     void initiateSetup(const sp<AMessage> &msg);
     void signalFlush();
     void signalResume();
-    void initiateShutdown();
+    void initiateShutdown(bool keepComponentAllocated = false);
 
     void initiateAllocateComponent(const sp<AMessage> &msg);
     void initiateConfigureComponent(const sp<AMessage> &msg);
@@ -61,6 +61,7 @@
 private:
     struct BaseState;
     struct UninitializedState;
+    struct LoadedState;
     struct LoadedToIdleState;
     struct IdleToExecutingState;
     struct ExecutingState;
@@ -107,6 +108,7 @@
     sp<AMessage> mNotify;
 
     sp<UninitializedState> mUninitializedState;
+    sp<LoadedState> mLoadedState;
     sp<LoadedToIdleState> mLoadedToIdleState;
     sp<IdleToExecutingState> mIdleToExecutingState;
     sp<ExecutingState> mExecutingState;
@@ -131,6 +133,12 @@
     bool mSentFormat;
     bool mIsEncoder;
 
+    bool mShutdownInProgress;
+
+    // If "mKeepComponentAllocated" we only transition back to Loaded state
+    // and do not release the component instance.
+    bool mKeepComponentAllocated;
+
     status_t allocateBuffersOnPort(OMX_U32 portIndex);
     status_t freeBuffersOnPort(OMX_U32 portIndex);
     status_t freeBuffer(OMX_U32 portIndex, size_t i);
diff --git a/include/media/stagefright/MediaCodec.h b/include/media/stagefright/MediaCodec.h
index 8c11c9c..72ac56a 100644
--- a/include/media/stagefright/MediaCodec.h
+++ b/include/media/stagefright/MediaCodec.h
@@ -53,8 +53,15 @@
             uint32_t flags);
 
     status_t start();
+
+    // Returns to a state in which the component remains allocated but
+    // unconfigured.
     status_t stop();
 
+    // Client MUST call release before releasing final reference to this
+    // object.
+    status_t release();
+
     status_t flush();
 
     status_t queueInputBuffer(
@@ -97,6 +104,7 @@
         STARTED,
         FLUSHING,
         STOPPING,
+        RELEASING,
     };
 
     enum {
@@ -109,6 +117,7 @@
         kWhatConfigure                  = 'conf',
         kWhatStart                      = 'strt',
         kWhatStop                       = 'stop',
+        kWhatRelease                    = 'rele',
         kWhatDequeueInputBuffer         = 'deqI',
         kWhatQueueInputBuffer           = 'queI',
         kWhatDequeueOutputBuffer        = 'deqO',
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 675339e..71e698f 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -84,7 +84,7 @@
 }
 
 JMediaCodec::~JMediaCodec() {
-    mCodec->stop();
+    mCodec->release();
 
     JNIEnv *env = AndroidRuntime::getJNIEnv();
 
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 85bd7ba..9a9d094 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -165,18 +165,36 @@
 
 protected:
     virtual bool onMessageReceived(const sp<AMessage> &msg);
+    virtual void stateEntered();
 
 private:
     void onSetup(const sp<AMessage> &msg);
-    void onAllocateComponent(const sp<AMessage> &msg);
-    void onConfigureComponent(const sp<AMessage> &msg);
-    void onStart();
+    bool onAllocateComponent(const sp<AMessage> &msg);
 
     DISALLOW_EVIL_CONSTRUCTORS(UninitializedState);
 };
 
 ////////////////////////////////////////////////////////////////////////////////
 
+struct ACodec::LoadedState : public ACodec::BaseState {
+    LoadedState(ACodec *codec);
+
+protected:
+    virtual bool onMessageReceived(const sp<AMessage> &msg);
+    virtual void stateEntered();
+
+private:
+    friend struct ACodec::UninitializedState;
+
+    bool onConfigureComponent(const sp<AMessage> &msg);
+    void onStart();
+    void onShutdown(bool keepComponentAllocated);
+
+    DISALLOW_EVIL_CONSTRUCTORS(LoadedState);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
 struct ACodec::LoadedToIdleState : public ACodec::BaseState {
     LoadedToIdleState(ACodec *codec);
 
@@ -312,8 +330,10 @@
 ACodec::ACodec()
     : mNode(NULL),
       mSentFormat(false),
-      mIsEncoder(false) {
+      mIsEncoder(false),
+      mShutdownInProgress(false) {
     mUninitializedState = new UninitializedState(this);
+    mLoadedState = new LoadedState(this);
     mLoadedToIdleState = new LoadedToIdleState(this);
     mIdleToExecutingState = new IdleToExecutingState(this);
     mExecutingState = new ExecutingState(this);
@@ -369,8 +389,10 @@
     (new AMessage(kWhatResume, id()))->post();
 }
 
-void ACodec::initiateShutdown() {
-    (new AMessage(kWhatShutdown, id()))->post();
+void ACodec::initiateShutdown(bool keepComponentAllocated) {
+    sp<AMessage> msg = new AMessage(kWhatShutdown, id());
+    msg->setInt32("keepComponentAllocated", keepComponentAllocated);
+    msg->post();
 }
 
 status_t ACodec::allocateBuffersOnPort(OMX_U32 portIndex) {
@@ -2492,6 +2514,10 @@
     : BaseState(codec) {
 }
 
+void ACodec::UninitializedState::stateEntered() {
+    ALOGV("Now uninitialized");
+}
+
 bool ACodec::UninitializedState::onMessageReceived(const sp<AMessage> &msg) {
     bool handled = false;
 
@@ -2511,22 +2537,13 @@
             break;
         }
 
-        case ACodec::kWhatConfigureComponent:
-        {
-            onConfigureComponent(msg);
-            handled = true;
-            break;
-        }
-
-        case ACodec::kWhatStart:
-        {
-            onStart();
-            handled = true;
-            break;
-        }
-
         case ACodec::kWhatShutdown:
         {
+            int32_t keepComponentAllocated;
+            CHECK(msg->findInt32(
+                        "keepComponentAllocated", &keepComponentAllocated));
+            CHECK(!keepComponentAllocated);
+
             sp<AMessage> notify = mCodec->mNotify->dup();
             notify->setInt32("what", ACodec::kWhatShutdownCompleted);
             notify->post();
@@ -2554,22 +2571,16 @@
 
 void ACodec::UninitializedState::onSetup(
         const sp<AMessage> &msg) {
-    onAllocateComponent(msg);
-    onConfigureComponent(msg);
-    onStart();
+    if (onAllocateComponent(msg)
+            && mCodec->mLoadedState->onConfigureComponent(msg)) {
+        mCodec->mLoadedState->onStart();
+    }
 }
 
-void ACodec::UninitializedState::onAllocateComponent(const sp<AMessage> &msg) {
+bool ACodec::UninitializedState::onAllocateComponent(const sp<AMessage> &msg) {
     ALOGV("onAllocateComponent");
 
-    if (mCodec->mNode != NULL) {
-        CHECK_EQ(mCodec->mOMX->freeNode(mCodec->mNode), (status_t)OK);
-
-        mCodec->mNativeWindow.clear();
-        mCodec->mNode = NULL;
-        mCodec->mOMX.clear();
-        mCodec->mComponentName.clear();
-    }
+    CHECK(mCodec->mNode == NULL);
 
     OMXClient client;
     CHECK_EQ(client.connect(), (status_t)OK);
@@ -2628,7 +2639,7 @@
         }
 
         mCodec->signalError(OMX_ErrorComponentNotFound);
-        return;
+        return false;
     }
 
     sp<AMessage> notify = new AMessage(kWhatOMXMessage, mCodec->id());
@@ -2649,9 +2660,96 @@
         notify->setString("componentName", mCodec->mComponentName.c_str());
         notify->post();
     }
+
+    mCodec->changeState(mCodec->mLoadedState);
+
+    return true;
 }
 
-void ACodec::UninitializedState::onConfigureComponent(
+////////////////////////////////////////////////////////////////////////////////
+
+ACodec::LoadedState::LoadedState(ACodec *codec)
+    : BaseState(codec) {
+}
+
+void ACodec::LoadedState::stateEntered() {
+    ALOGV("[%s] Now Loaded", mCodec->mComponentName.c_str());
+
+    if (mCodec->mShutdownInProgress) {
+        bool keepComponentAllocated = mCodec->mKeepComponentAllocated;
+
+        mCodec->mShutdownInProgress = false;
+        mCodec->mKeepComponentAllocated = false;
+
+        onShutdown(keepComponentAllocated);
+    }
+}
+
+void ACodec::LoadedState::onShutdown(bool keepComponentAllocated) {
+    if (!keepComponentAllocated) {
+        CHECK_EQ(mCodec->mOMX->freeNode(mCodec->mNode), (status_t)OK);
+
+        mCodec->mNativeWindow.clear();
+        mCodec->mNode = NULL;
+        mCodec->mOMX.clear();
+        mCodec->mComponentName.clear();
+
+        mCodec->changeState(mCodec->mUninitializedState);
+    }
+
+    sp<AMessage> notify = mCodec->mNotify->dup();
+    notify->setInt32("what", ACodec::kWhatShutdownCompleted);
+    notify->post();
+}
+
+bool ACodec::LoadedState::onMessageReceived(const sp<AMessage> &msg) {
+    bool handled = false;
+
+    switch (msg->what()) {
+        case ACodec::kWhatConfigureComponent:
+        {
+            onConfigureComponent(msg);
+            handled = true;
+            break;
+        }
+
+        case ACodec::kWhatStart:
+        {
+            onStart();
+            handled = true;
+            break;
+        }
+
+        case ACodec::kWhatShutdown:
+        {
+            int32_t keepComponentAllocated;
+            CHECK(msg->findInt32(
+                        "keepComponentAllocated", &keepComponentAllocated));
+
+            onShutdown(keepComponentAllocated);
+
+            handled = true;
+            break;
+        }
+
+        case ACodec::kWhatFlush:
+        {
+            sp<AMessage> notify = mCodec->mNotify->dup();
+            notify->setInt32("what", ACodec::kWhatFlushCompleted);
+            notify->post();
+
+            handled = true;
+            break;
+        }
+
+        default:
+            return BaseState::onMessageReceived(msg);
+    }
+
+    return handled;
+}
+
+bool ACodec::LoadedState::onConfigureComponent(
         const sp<AMessage> &msg) {
     ALOGV("onConfigureComponent");
 
@@ -2664,7 +2762,7 @@
 
     if (err != OK) {
         mCodec->signalError(OMX_ErrorUndefined, err);
-        return;
+        return false;
     }
 
     sp<RefBase> obj;
@@ -2682,9 +2780,11 @@
         notify->setInt32("what", ACodec::kWhatComponentConfigured);
         notify->post();
     }
+
+    return true;
 }
 
-void ACodec::UninitializedState::onStart() {
+void ACodec::LoadedState::onStart() {
     ALOGV("onStart");
 
     CHECK_EQ(mCodec->mOMX->sendCommand(
@@ -2873,6 +2973,13 @@
     switch (msg->what()) {
         case kWhatShutdown:
         {
+            int32_t keepComponentAllocated;
+            CHECK(msg->findInt32(
+                        "keepComponentAllocated", &keepComponentAllocated));
+
+            mCodec->mShutdownInProgress = true;
+            mCodec->mKeepComponentAllocated = keepComponentAllocated;
+
             mActive = false;
 
             CHECK_EQ(mCodec->mOMX->sendCommand(
@@ -3210,20 +3317,7 @@
             CHECK_EQ(data1, (OMX_U32)OMX_CommandStateSet);
             CHECK_EQ(data2, (OMX_U32)OMX_StateLoaded);
 
-            ALOGV("[%s] Now Loaded", mCodec->mComponentName.c_str());
-
-            CHECK_EQ(mCodec->mOMX->freeNode(mCodec->mNode), (status_t)OK);
-
-            mCodec->mNativeWindow.clear();
-            mCodec->mNode = NULL;
-            mCodec->mOMX.clear();
-            mCodec->mComponentName.clear();
-
-            mCodec->changeState(mCodec->mUninitializedState);
-
-            sp<AMessage> notify = mCodec->mNotify->dup();
-            notify->setInt32("what", ACodec::kWhatShutdownCompleted);
-            notify->post();
+            mCodec->changeState(mCodec->mLoadedState);
 
             return true;
         }
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index e14b1c4..a9e7f360 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -164,6 +164,13 @@
     return PostAndAwaitResponse(msg, &response);
 }
 
+status_t MediaCodec::release() {
+    sp<AMessage> msg = new AMessage(kWhatRelease, id());
+
+    sp<AMessage> response;
+    return PostAndAwaitResponse(msg, &response);
+}
+
 status_t MediaCodec::queueInputBuffer(
         size_t index,
         size_t offset,
@@ -422,6 +429,7 @@
                         }
 
                         case STOPPING:
+                        case RELEASING:
                         {
                             // Ignore the error, assuming we'll still get
                             // the shutdown complete notification.
@@ -577,7 +585,9 @@
                 {
                     /* size_t index = */updateBuffers(kPortIndexInput, msg);
 
-                    if (mState == FLUSHING || mState == STOPPING) {
+                    if (mState == FLUSHING
+                            || mState == STOPPING
+                            || mState == RELEASING) {
                         returnBuffersToCodecOnPort(kPortIndexInput);
                         break;
                     }
@@ -596,7 +606,9 @@
                 {
                     /* size_t index = */updateBuffers(kPortIndexOutput, msg);
 
-                    if (mState == FLUSHING || mState == STOPPING) {
+                    if (mState == FLUSHING
+                            || mState == STOPPING
+                            || mState == RELEASING) {
                         returnBuffersToCodecOnPort(kPortIndexOutput);
                         break;
                     }
@@ -628,8 +640,12 @@
 
                 case ACodec::kWhatShutdownCompleted:
                 {
-                    CHECK_EQ(mState, STOPPING);
-                    setState(UNINITIALIZED);
+                    if (mState == STOPPING) {
+                        setState(INITIALIZED);
+                    } else {
+                        CHECK_EQ(mState, RELEASING);
+                        setState(UNINITIALIZED);
+                    }
 
                     (new AMessage)->postReply(mReplyID);
                     break;
@@ -767,6 +783,28 @@
             mReplyID = replyID;
             setState(STOPPING);
 
+            mCodec->initiateShutdown(true /* keepComponentAllocated */);
+            returnBuffersToCodec();
+            break;
+        }
+
+        case kWhatRelease:
+        {
+            uint32_t replyID;
+            CHECK(msg->senderAwaitsResponse(&replyID));
+
+            if (mState != INITIALIZED
+                    && mState != CONFIGURED && mState != STARTED) {
+                sp<AMessage> response = new AMessage;
+                response->setInt32("err", INVALID_OPERATION);
+
+                response->postReply(replyID);
+                break;
+            }
+
+            mReplyID = replyID;
+            setState(RELEASING);
+
             mCodec->initiateShutdown();
             returnBuffersToCodec();
             break;