Merge "MediaCodec async callbacks"
diff --git a/include/media/stagefright/MediaCodec.h b/include/media/stagefright/MediaCodec.h
index 3fa7b48..046496c 100644
--- a/include/media/stagefright/MediaCodec.h
+++ b/include/media/stagefright/MediaCodec.h
@@ -44,6 +44,13 @@
         BUFFER_FLAG_EOS         = 4,
     };
 
+    enum {
+        CB_INPUT_AVAILABLE = 1,
+        CB_OUTPUT_AVAILABLE = 2,
+        CB_ERROR = 3,
+        CB_OUTPUT_FORMAT_CHANGED = 4,
+    };
+
     static sp<MediaCodec> CreateByType(
             const sp<ALooper> &looper, const char *mime, bool encoder);
 
@@ -56,6 +63,8 @@
             const sp<ICrypto> &crypto,
             uint32_t flags);
 
+    status_t setCallback(const sp<AMessage> &callback);
+
     status_t createInputSurface(sp<IGraphicBufferProducer>* bufferProducer);
 
     status_t start();
@@ -173,6 +182,7 @@
         kWhatRequestActivityNotification    = 'racN',
         kWhatGetName                        = 'getN',
         kWhatSetParameters                  = 'setP',
+        kWhatSetCallback                    = 'setC',
     };
 
     enum {
@@ -186,6 +196,7 @@
         kFlagSawMediaServerDie          = 128,
         kFlagIsEncoder                  = 256,
         kFlagGatherCodecSpecificData    = 512,
+        kFlagIsAsync                    = 1024,
     };
 
     struct BufferInfo {
@@ -208,6 +219,7 @@
     SoftwareRenderer *mSoftRenderer;
     sp<AMessage> mOutputFormat;
     sp<AMessage> mInputFormat;
+    sp<AMessage> mCallback;
 
     // Used only to synchronize asynchronous getBufferAndFormat
     // across all the other (synchronous) buffer state change
@@ -237,6 +249,8 @@
     static status_t PostAndAwaitResponse(
             const sp<AMessage> &msg, sp<AMessage> *response);
 
+    static void PostReplyWithError(int32_t replyID, int32_t err);
+
     status_t init(const char *name, bool nameIsType, bool encoder);
 
     void setState(State newState);
@@ -263,6 +277,11 @@
 
     void postActivityNotificationIfPossible();
 
+    void onInputBufferAvailable();
+    void onOutputBufferAvailable();
+    void onError(int32_t actionCode, status_t err);
+    void onOutputFormatChanged();
+
     status_t onSetParameters(const sp<AMessage> &params);
 
     status_t amendOutputFormatWithCodecSpecificData(const sp<ABuffer> &buffer);
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 1e6ac45..f286659 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -98,6 +98,13 @@
     return err;
 }
 
+// static
+void MediaCodec::PostReplyWithError(int32_t replyID, int32_t err) {
+    sp<AMessage> response = new AMessage;
+    response->setInt32("err", err);
+    response->postReply(replyID);
+}
+
 status_t MediaCodec::init(const char *name, bool nameIsType, bool encoder) {
     // Current video decoders do not return from OMX_FillThisBuffer
     // quickly, violating the OpenMAX specs, until that is remedied
@@ -155,6 +162,14 @@
     return PostAndAwaitResponse(msg, &response);
 }
 
+status_t MediaCodec::setCallback(const sp<AMessage> &callback) {
+    sp<AMessage> msg = new AMessage(kWhatSetCallback, id());
+    msg->setMessage("callback", callback);
+
+    sp<AMessage> response;
+    return PostAndAwaitResponse(msg, &response);
+}
+
 status_t MediaCodec::configure(
         const sp<AMessage> &format,
         const sp<Surface> &nativeWindow,
@@ -473,9 +488,7 @@
 
 void MediaCodec::cancelPendingDequeueOperations() {
     if (mFlags & kFlagDequeueInputPending) {
-        sp<AMessage> response = new AMessage;
-        response->setInt32("err", INVALID_OPERATION);
-        response->postReply(mDequeueInputReplyID);
+        PostReplyWithError(mDequeueInputReplyID, INVALID_OPERATION);
 
         ++mDequeueInputTimeoutGeneration;
         mDequeueInputReplyID = 0;
@@ -483,9 +496,7 @@
     }
 
     if (mFlags & kFlagDequeueOutputPending) {
-        sp<AMessage> response = new AMessage;
-        response->setInt32("err", INVALID_OPERATION);
-        response->postReply(mDequeueOutputReplyID);
+        PostReplyWithError(mDequeueOutputReplyID, INVALID_OPERATION);
 
         ++mDequeueOutputTimeoutGeneration;
         mDequeueOutputReplyID = 0;
@@ -497,11 +508,7 @@
     if (mState != STARTED
             || (mFlags & kFlagStickyError)
             || (newRequest && (mFlags & kFlagDequeueInputPending))) {
-        sp<AMessage> response = new AMessage;
-        response->setInt32("err", INVALID_OPERATION);
-
-        response->postReply(replyID);
-
+        PostReplyWithError(replyID, INVALID_OPERATION);
         return true;
     }
 
@@ -655,6 +662,10 @@
                             postActivityNotificationIfPossible();
 
                             cancelPendingDequeueOperations();
+
+                            if (mFlags & kFlagIsAsync) {
+                                onError(0, omxError);
+                            }
                             setState(UNINITIALIZED);
                             break;
                         }
@@ -665,16 +676,17 @@
 
                             mFlags |= kFlagStickyError;
                             postActivityNotificationIfPossible();
+
+                            if (mFlags & kFlagIsAsync) {
+                                onError(0, omxError);
+                            }
                             setState(UNINITIALIZED);
                             break;
                         }
                     }
 
                     if (sendErrorReponse) {
-                        sp<AMessage> response = new AMessage;
-                        response->setInt32("err", UNKNOWN_ERROR);
-
-                        response->postReply(mReplyID);
+                        PostReplyWithError(mReplyID, UNKNOWN_ERROR);
                     }
                     break;
                 }
@@ -845,6 +857,8 @@
                         // collect codec specific data and amend the output
                         // format as necessary.
                         mFlags |= kFlagGatherCodecSpecificData;
+                    } else if (mFlags & kFlagIsAsync) {
+                        onOutputFormatChanged();
                     } else {
                         mFlags |= kFlagOutputFormatChanged;
                         postActivityNotificationIfPossible();
@@ -887,7 +901,9 @@
                         break;
                     }
 
-                    if (mFlags & kFlagDequeueInputPending) {
+                    if (mFlags & kFlagIsAsync) {
+                        onInputBufferAvailable();
+                    } else if (mFlags & kFlagDequeueInputPending) {
                         CHECK(handleDequeueInputBuffer(mDequeueInputReplyID));
 
                         ++mDequeueInputTimeoutGeneration;
@@ -934,10 +950,16 @@
                         }
 
                         mFlags &= ~kFlagGatherCodecSpecificData;
-                        mFlags |= kFlagOutputFormatChanged;
+                        if (mFlags & kFlagIsAsync) {
+                            onOutputFormatChanged();
+                        } else {
+                            mFlags |= kFlagOutputFormatChanged;
+                        }
                     }
 
-                    if (mFlags & kFlagDequeueOutputPending) {
+                    if (mFlags & kFlagIsAsync) {
+                        onOutputBufferAvailable();
+                    } else if (mFlags & kFlagDequeueOutputPending) {
                         CHECK(handleDequeueOutputBuffer(mDequeueOutputReplyID));
 
                         ++mDequeueOutputTimeoutGeneration;
@@ -993,10 +1015,7 @@
             CHECK(msg->senderAwaitsResponse(&replyID));
 
             if (mState != UNINITIALIZED) {
-                sp<AMessage> response = new AMessage;
-                response->setInt32("err", INVALID_OPERATION);
-
-                response->postReply(replyID);
+                PostReplyWithError(replyID, INVALID_OPERATION);
                 break;
             }
 
@@ -1026,16 +1045,45 @@
             break;
         }
 
+        case kWhatSetCallback:
+        {
+            uint32_t replyID;
+            CHECK(msg->senderAwaitsResponse(&replyID));
+
+            if (mState == UNINITIALIZED
+                    || mState == INITIALIZING
+                    || mState == STARTED) {
+                // callback can't be set after codec is started,
+                // or before it's initialized (as the callback
+                // will be cleared when it goes to INITIALIZED)
+                PostReplyWithError(replyID, INVALID_OPERATION);
+                break;
+            }
+
+            sp<AMessage> callback;
+            CHECK(msg->findMessage("callback", &callback));
+
+            mCallback = callback;
+
+            if (mCallback != NULL) {
+                ALOGI("MediaCodec will operate in async mode");
+                mFlags |= kFlagIsAsync;
+            } else {
+                mFlags &= ~kFlagIsAsync;
+            }
+
+            sp<AMessage> response = new AMessage;
+            response->postReply(replyID);
+            break;
+        }
+
         case kWhatConfigure:
         {
             uint32_t replyID;
             CHECK(msg->senderAwaitsResponse(&replyID));
 
             if (mState != INITIALIZED) {
-                sp<AMessage> response = new AMessage;
-                response->setInt32("err", INVALID_OPERATION);
-
-                response->postReply(replyID);
+                PostReplyWithError(replyID, INVALID_OPERATION);
                 break;
             }
 
@@ -1055,10 +1103,7 @@
                         ->getSurfaceTextureClient());
 
                 if (err != OK) {
-                    sp<AMessage> response = new AMessage;
-                    response->setInt32("err", err);
-
-                    response->postReply(replyID);
+                    PostReplyWithError(replyID, err);
                     break;
                 }
             } else {
@@ -1096,10 +1141,7 @@
 
             // Must be configured, but can't have been started yet.
             if (mState != CONFIGURED) {
-                sp<AMessage> response = new AMessage;
-                response->setInt32("err", INVALID_OPERATION);
-
-                response->postReply(replyID);
+                PostReplyWithError(replyID, INVALID_OPERATION);
                 break;
             }
 
@@ -1114,10 +1156,7 @@
             CHECK(msg->senderAwaitsResponse(&replyID));
 
             if (mState != CONFIGURED) {
-                sp<AMessage> response = new AMessage;
-                response->setInt32("err", INVALID_OPERATION);
-
-                response->postReply(replyID);
+                PostReplyWithError(replyID, INVALID_OPERATION);
                 break;
             }
 
@@ -1178,11 +1217,15 @@
             uint32_t replyID;
             CHECK(msg->senderAwaitsResponse(&replyID));
 
+            if (mFlags & kFlagIsAsync) {
+                ALOGE("dequeueOutputBuffer can't be used in async mode");
+                PostReplyWithError(replyID, INVALID_OPERATION);
+                break;
+            }
+
             if (mHaveInputSurface) {
                 ALOGE("dequeueInputBuffer can't be used with input surface");
-                sp<AMessage> response = new AMessage;
-                response->setInt32("err", INVALID_OPERATION);
-                response->postReply(replyID);
+                PostReplyWithError(replyID, INVALID_OPERATION);
                 break;
             }
 
@@ -1194,9 +1237,7 @@
             CHECK(msg->findInt64("timeoutUs", &timeoutUs));
 
             if (timeoutUs == 0ll) {
-                sp<AMessage> response = new AMessage;
-                response->setInt32("err", -EAGAIN);
-                response->postReply(replyID);
+                PostReplyWithError(replyID, -EAGAIN);
                 break;
             }
 
@@ -1225,9 +1266,7 @@
 
             CHECK(mFlags & kFlagDequeueInputPending);
 
-            sp<AMessage> response = new AMessage;
-            response->setInt32("err", -EAGAIN);
-            response->postReply(mDequeueInputReplyID);
+            PostReplyWithError(mDequeueInputReplyID, -EAGAIN);
 
             mFlags &= ~kFlagDequeueInputPending;
             mDequeueInputReplyID = 0;
@@ -1240,18 +1279,13 @@
             CHECK(msg->senderAwaitsResponse(&replyID));
 
             if (mState != STARTED || (mFlags & kFlagStickyError)) {
-                sp<AMessage> response = new AMessage;
-                response->setInt32("err", INVALID_OPERATION);
-
-                response->postReply(replyID);
+                PostReplyWithError(replyID, INVALID_OPERATION);
                 break;
             }
 
             status_t err = onQueueInputBuffer(msg);
 
-            sp<AMessage> response = new AMessage;
-            response->setInt32("err", err);
-            response->postReply(replyID);
+            PostReplyWithError(replyID, err);
             break;
         }
 
@@ -1260,6 +1294,12 @@
             uint32_t replyID;
             CHECK(msg->senderAwaitsResponse(&replyID));
 
+            if (mFlags & kFlagIsAsync) {
+                ALOGE("dequeueOutputBuffer can't be used in async mode");
+                PostReplyWithError(replyID, INVALID_OPERATION);
+                break;
+            }
+
             if (handleDequeueOutputBuffer(replyID, true /* new request */)) {
                 break;
             }
@@ -1268,9 +1308,7 @@
             CHECK(msg->findInt64("timeoutUs", &timeoutUs));
 
             if (timeoutUs == 0ll) {
-                sp<AMessage> response = new AMessage;
-                response->setInt32("err", -EAGAIN);
-                response->postReply(replyID);
+                PostReplyWithError(replyID, -EAGAIN);
                 break;
             }
 
@@ -1299,9 +1337,7 @@
 
             CHECK(mFlags & kFlagDequeueOutputPending);
 
-            sp<AMessage> response = new AMessage;
-            response->setInt32("err", -EAGAIN);
-            response->postReply(mDequeueOutputReplyID);
+            PostReplyWithError(mDequeueOutputReplyID, -EAGAIN);
 
             mFlags &= ~kFlagDequeueOutputPending;
             mDequeueOutputReplyID = 0;
@@ -1314,18 +1350,13 @@
             CHECK(msg->senderAwaitsResponse(&replyID));
 
             if (mState != STARTED || (mFlags & kFlagStickyError)) {
-                sp<AMessage> response = new AMessage;
-                response->setInt32("err", INVALID_OPERATION);
-
-                response->postReply(replyID);
+                PostReplyWithError(replyID, INVALID_OPERATION);
                 break;
             }
 
             status_t err = onReleaseOutputBuffer(msg);
 
-            sp<AMessage> response = new AMessage;
-            response->setInt32("err", err);
-            response->postReply(replyID);
+            PostReplyWithError(replyID, err);
             break;
         }
 
@@ -1335,10 +1366,7 @@
             CHECK(msg->senderAwaitsResponse(&replyID));
 
             if (mState != STARTED || (mFlags & kFlagStickyError)) {
-                sp<AMessage> response = new AMessage;
-                response->setInt32("err", INVALID_OPERATION);
-
-                response->postReply(replyID);
+                PostReplyWithError(replyID, INVALID_OPERATION);
                 break;
             }
 
@@ -1353,10 +1381,7 @@
             CHECK(msg->senderAwaitsResponse(&replyID));
 
             if (mState != STARTED || (mFlags & kFlagStickyError)) {
-                sp<AMessage> response = new AMessage;
-                response->setInt32("err", INVALID_OPERATION);
-
-                response->postReply(replyID);
+                PostReplyWithError(replyID, INVALID_OPERATION);
                 break;
             }
 
@@ -1387,10 +1412,7 @@
             CHECK(msg->senderAwaitsResponse(&replyID));
 
             if (mState != STARTED || (mFlags & kFlagStickyError)) {
-                sp<AMessage> response = new AMessage;
-                response->setInt32("err", INVALID_OPERATION);
-
-                response->postReply(replyID);
+                PostReplyWithError(replyID, INVALID_OPERATION);
                 break;
             }
 
@@ -1415,10 +1437,7 @@
                  mState != STARTED && mState != FLUSHING)
                     || (mFlags & kFlagStickyError)
                     || format == NULL) {
-                sp<AMessage> response = new AMessage;
-                response->setInt32("err", INVALID_OPERATION);
-
-                response->postReply(replyID);
+                PostReplyWithError(replyID, INVALID_OPERATION);
                 break;
             }
 
@@ -1449,10 +1468,7 @@
             CHECK(msg->senderAwaitsResponse(&replyID));
 
             if (mComponentName.empty()) {
-                sp<AMessage> response = new AMessage;
-                response->setInt32("err", INVALID_OPERATION);
-
-                response->postReply(replyID);
+                PostReplyWithError(replyID, INVALID_OPERATION);
                 break;
             }
 
@@ -1472,10 +1488,7 @@
 
             status_t err = onSetParameters(params);
 
-            sp<AMessage> response = new AMessage;
-            response->setInt32("err", err);
-
-            response->postReply(replyID);
+            PostReplyWithError(replyID, err);
             break;
         }
 
@@ -1546,8 +1559,10 @@
         mFlags &= ~kFlagStickyError;
         mFlags &= ~kFlagIsEncoder;
         mFlags &= ~kFlagGatherCodecSpecificData;
+        mFlags &= ~kFlagIsAsync;
 
         mActivityNotify.clear();
+        mCallback.clear();
     }
 
     if (newState == UNINITIALIZED) {
@@ -1855,6 +1870,73 @@
     return OK;
 }
 
+void MediaCodec::onInputBufferAvailable() {
+    int32_t index;
+    while ((index = dequeuePortBuffer(kPortIndexInput)) >= 0) {
+        sp<AMessage> msg = mCallback->dup();
+        msg->setInt32("callbackID", CB_INPUT_AVAILABLE);
+        msg->setInt32("index", index);
+        msg->post();
+    }
+}
+
+void MediaCodec::onOutputBufferAvailable() {
+    int32_t index;
+    while ((index = dequeuePortBuffer(kPortIndexOutput)) >= 0) {
+        const sp<ABuffer> &buffer =
+            mPortBuffers[kPortIndexOutput].itemAt(index).mData;
+        sp<AMessage> msg = mCallback->dup();
+        msg->setInt32("callbackID", CB_OUTPUT_AVAILABLE);
+        msg->setInt32("index", index);
+        msg->setSize("offset", buffer->offset());
+        msg->setSize("size", buffer->size());
+
+        int64_t timeUs;
+        CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
+
+        msg->setInt64("timeUs", timeUs);
+
+        int32_t omxFlags;
+        CHECK(buffer->meta()->findInt32("omxFlags", &omxFlags));
+
+        uint32_t flags = 0;
+        if (omxFlags & OMX_BUFFERFLAG_SYNCFRAME) {
+            flags |= BUFFER_FLAG_SYNCFRAME;
+        }
+        if (omxFlags & OMX_BUFFERFLAG_CODECCONFIG) {
+            flags |= BUFFER_FLAG_CODECCONFIG;
+        }
+        if (omxFlags & OMX_BUFFERFLAG_EOS) {
+            flags |= BUFFER_FLAG_EOS;
+        }
+
+        msg->setInt32("flags", flags);
+
+        msg->post();
+    }
+}
+
+void MediaCodec::onError(int32_t actionCode, status_t err) {
+    if (mCallback != NULL) {
+        sp<AMessage> msg = mCallback->dup();
+        msg->setInt32("callbackID", CB_ERROR);
+        msg->setInt32("actionCode", actionCode);
+        msg->setInt32("err", err);
+
+        msg->post();
+    }
+}
+
+void MediaCodec::onOutputFormatChanged() {
+    if (mCallback != NULL) {
+        sp<AMessage> msg = mCallback->dup();
+        msg->setInt32("callbackID", CB_OUTPUT_FORMAT_CHANGED);
+        msg->setMessage("format", mOutputFormat);
+        msg->post();
+    }
+}
+
+
 void MediaCodec::postActivityNotificationIfPossible() {
     if (mActivityNotify == NULL) {
         return;