API that allows usage of MediaCodec APIs without polling.

Change-Id: Iebccdd3aec74a2cfa9ad0bf16c0c6006a3b72999
related-to-bug: 11990118
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 3ebb0ed..1dcb459 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -21,6 +21,9 @@
 import android.media.MediaCrypto;
 import android.media.MediaFormat;
 import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
 import android.view.Surface;
 
 import java.io.IOException;
@@ -173,6 +176,33 @@
      */
     public static final int BUFFER_FLAG_END_OF_STREAM         = 4;
 
+    private EventHandler mEventHandler;
+    private NotificationCallback mNotificationCallback;
+
+    static final int EVENT_NOTIFY = 1;
+
+    private class EventHandler extends Handler {
+        private MediaCodec mCodec;
+
+        public EventHandler(MediaCodec codec, Looper looper) {
+            super(looper);
+            mCodec = codec;
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case EVENT_NOTIFY:
+                {
+                    if (mNotificationCallback != null) {
+                        mNotificationCallback.onCodecNotify(mCodec);
+                    }
+                    break;
+                }
+            }
+        }
+    }
+
     /**
      * Instantiate a decoder supporting input data of the given mime type.
      *
@@ -228,6 +258,15 @@
 
     private MediaCodec(
             String name, boolean nameIsType, boolean encoder) {
+        Looper looper;
+        if ((looper = Looper.myLooper()) != null) {
+            mEventHandler = new EventHandler(this, looper);
+        } else if ((looper = Looper.getMainLooper()) != null) {
+            mEventHandler = new EventHandler(this, looper);
+        } else {
+            mEventHandler = null;
+        }
+
         native_setup(name, nameIsType, encoder);
     }
 
@@ -308,7 +347,15 @@
      * To ensure that it is available to other client call {@link #release}
      * and don't just rely on garbage collection to eventually do this for you.
      */
-    public native final void stop();
+    public final void stop() {
+        native_stop();
+
+        if (mEventHandler != null) {
+            mEventHandler.removeMessages(EVENT_NOTIFY);
+        }
+    }
+
+    private native final void native_stop();
 
     /**
      * Flush both input and output ports of the component, all indices
@@ -639,6 +686,22 @@
         setParameters(keys, values);
     }
 
+    public void setNotificationCallback(NotificationCallback cb) {
+        mNotificationCallback = cb;
+    }
+
+    public interface NotificationCallback {
+        void onCodecNotify(MediaCodec codec);
+    }
+
+    private void postEventFromNative(
+            int what, int arg1, int arg2, Object obj) {
+        if (mEventHandler != null) {
+            Message msg = mEventHandler.obtainMessage(what, arg1, arg2, obj);
+            mEventHandler.sendMessage(msg);
+        }
+    }
+
     private native final void setParameters(String[] keys, Object[] values);
 
     /**
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 221ea57..3ce483d 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -51,6 +51,10 @@
     DEQUEUE_INFO_OUTPUT_BUFFERS_CHANGED     = -3,
 };
 
+enum {
+    EVENT_NOTIFY = 1,
+};
+
 struct CryptoErrorCodes {
     jint cryptoErrorNoKey;
     jint cryptoErrorKeyExpired;
@@ -59,6 +63,7 @@
 
 struct fields_t {
     jfieldID context;
+    jmethodID postEventFromNativeID;
     jfieldID cryptoInfoNumSubSamplesID;
     jfieldID cryptoInfoNumBytesOfClearDataID;
     jfieldID cryptoInfoNumBytesOfEncryptedDataID;
@@ -75,7 +80,9 @@
         JNIEnv *env, jobject thiz,
         const char *name, bool nameIsType, bool encoder)
     : mClass(NULL),
-      mObject(NULL) {
+      mObject(NULL),
+      mGeneration(1),
+      mRequestedActivityNotification(false) {
     jclass clazz = env->GetObjectClass(thiz);
     CHECK(clazz != NULL);
 
@@ -87,7 +94,7 @@
 
     mLooper->start(
             false,      // runOnCallingThread
-            false,       // canCallJava
+            true,       // canCallJava
             PRIORITY_FOREGROUND);
 
     if (nameIsType) {
@@ -101,6 +108,10 @@
     return mCodec != NULL ? OK : NO_INIT;
 }
 
+void JMediaCodec::registerSelf() {
+    mLooper->registerHandler(this);
+}
+
 JMediaCodec::~JMediaCodec() {
     if (mCodec != NULL) {
         mCodec->release();
@@ -122,7 +133,8 @@
         int flags) {
     sp<Surface> client;
     if (bufferProducer != NULL) {
-        mSurfaceTextureClient = new Surface(bufferProducer, true /* controlledByApp */);
+        mSurfaceTextureClient =
+            new Surface(bufferProducer, true /* controlledByApp */);
     } else {
         mSurfaceTextureClient.clear();
     }
@@ -136,13 +148,32 @@
 }
 
 status_t JMediaCodec::start() {
-    return mCodec->start();
+    status_t err = mCodec->start();
+
+    if (err != OK) {
+        return err;
+    }
+
+    mActivityNotification = new AMessage(kWhatActivityNotify, id());
+    mActivityNotification->setInt32("generation", mGeneration);
+
+    requestActivityNotification();
+
+    return err;
 }
 
 status_t JMediaCodec::stop() {
     mSurfaceTextureClient.clear();
 
-    return mCodec->stop();
+    status_t err = mCodec->stop();
+
+    sp<AMessage> msg = new AMessage(kWhatStopActivityNotifications, id());
+    sp<AMessage> response;
+    msg->postAndAwaitResponse(&response);
+
+    mActivityNotification.clear();
+
+    return err;
 }
 
 status_t JMediaCodec::flush() {
@@ -174,7 +205,11 @@
 }
 
 status_t JMediaCodec::dequeueInputBuffer(size_t *index, int64_t timeoutUs) {
-    return mCodec->dequeueInputBuffer(index, timeoutUs);
+    status_t err = mCodec->dequeueInputBuffer(index, timeoutUs);
+
+    requestActivityNotification();
+
+    return err;
 }
 
 status_t JMediaCodec::dequeueOutputBuffer(
@@ -182,9 +217,12 @@
     size_t size, offset;
     int64_t timeUs;
     uint32_t flags;
-    status_t err;
-    if ((err = mCodec->dequeueOutputBuffer(
-                    index, &offset, &size, &timeUs, &flags, timeoutUs)) != OK) {
+    status_t err = mCodec->dequeueOutputBuffer(
+            index, &offset, &size, &timeUs, &flags, timeoutUs);
+
+    requestActivityNotification();
+
+    if (err != OK) {
         return err;
     }
 
@@ -320,6 +358,67 @@
     }
 }
 
+void JMediaCodec::onMessageReceived(const sp<AMessage> &msg) {
+    switch (msg->what()) {
+        case kWhatRequestActivityNotifications:
+        {
+            if (mRequestedActivityNotification) {
+                break;
+            }
+
+            mCodec->requestActivityNotification(mActivityNotification);
+            mRequestedActivityNotification = true;
+            break;
+        }
+
+        case kWhatActivityNotify:
+        {
+            {
+                int32_t generation;
+                CHECK(msg->findInt32("generation", &generation));
+
+                if (generation != mGeneration) {
+                    // stale
+                    break;
+                }
+
+                mRequestedActivityNotification = false;
+            }
+
+            JNIEnv *env = AndroidRuntime::getJNIEnv();
+            env->CallVoidMethod(
+                    mObject,
+                    gFields.postEventFromNativeID,
+                    EVENT_NOTIFY,
+                    0 /* arg1 */,
+                    0 /* arg2 */,
+                    NULL /* obj */);
+
+            break;
+        }
+
+        case kWhatStopActivityNotifications:
+        {
+            uint32_t replyID;
+            CHECK(msg->senderAwaitsResponse(&replyID));
+
+            ++mGeneration;
+            mRequestedActivityNotification = false;
+
+            sp<AMessage> response = new AMessage;
+            response->postReply(replyID);
+            break;
+        }
+
+        default:
+            TRESPASS();
+    }
+}
+
+void JMediaCodec::requestActivityNotification() {
+    (new AMessage(kWhatRequestActivityNotifications, id()))->post();
+}
+
 }  // namespace android
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -888,6 +987,12 @@
     gFields.context = env->GetFieldID(clazz.get(), "mNativeContext", "J");
     CHECK(gFields.context != NULL);
 
+    gFields.postEventFromNativeID =
+        env->GetMethodID(
+                clazz.get(), "postEventFromNative", "(IIILjava/lang/Object;)V");
+
+    CHECK(gFields.postEventFromNativeID != NULL);
+
     clazz.reset(env->FindClass("android/media/MediaCodec$CryptoInfo"));
     CHECK(clazz.get() != NULL);
 
@@ -961,6 +1066,8 @@
         return;
     }
 
+    codec->registerSelf();
+
     setMediaCodec(env,thiz, codec);
 }
 
@@ -981,7 +1088,7 @@
       (void *)android_media_MediaCodec_createInputSurface },
 
     { "start", "()V", (void *)android_media_MediaCodec_start },
-    { "stop", "()V", (void *)android_media_MediaCodec_stop },
+    { "native_stop", "()V", (void *)android_media_MediaCodec_stop },
     { "flush", "()V", (void *)android_media_MediaCodec_flush },
 
     { "queueInputBuffer", "(IIIJI)V",
diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h
index 2fbbd72..53254c9 100644
--- a/media/jni/android_media_MediaCodec.h
+++ b/media/jni/android_media_MediaCodec.h
@@ -21,8 +21,8 @@
 
 #include <media/hardware/CryptoAPI.h>
 #include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/foundation/AHandler.h>
 #include <utils/Errors.h>
-#include <utils/RefBase.h>
 
 namespace android {
 
@@ -34,13 +34,15 @@
 struct MediaCodec;
 class Surface;
 
-struct JMediaCodec : public RefBase {
+struct JMediaCodec : public AHandler {
     JMediaCodec(
             JNIEnv *env, jobject thiz,
             const char *name, bool nameIsType, bool encoder);
 
     status_t initCheck() const;
 
+    void registerSelf();
+
     status_t configure(
             const sp<AMessage> &format,
             const sp<IGraphicBufferProducer> &bufferProducer,
@@ -94,7 +96,15 @@
 protected:
     virtual ~JMediaCodec();
 
+    virtual void onMessageReceived(const sp<AMessage> &msg);
+
 private:
+    enum {
+        kWhatActivityNotify,
+        kWhatRequestActivityNotifications,
+        kWhatStopActivityNotifications,
+    };
+
     jclass mClass;
     jweak mObject;
     sp<Surface> mSurfaceTextureClient;
@@ -102,6 +112,12 @@
     sp<ALooper> mLooper;
     sp<MediaCodec> mCodec;
 
+    sp<AMessage> mActivityNotification;
+    int32_t mGeneration;
+    bool mRequestedActivityNotification;
+
+    void requestActivityNotification();
+
     DISALLOW_EVIL_CONSTRUCTORS(JMediaCodec);
 };