API that allows usage of MediaCodec APIs without polling.
Change-Id: Iebccdd3aec74a2cfa9ad0bf16c0c6006a3b72999
related-to-bug: 11990118
diff --git a/api/current.txt b/api/current.txt
index 2889e62..fd901f2 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -12845,6 +12845,7 @@
method public final void queueSecureInputBuffer(int, int, android.media.MediaCodec.CryptoInfo, long, int) throws android.media.MediaCodec.CryptoException;
method public final void release();
method public final void releaseOutputBuffer(int, boolean);
+ method public void setNotificationCallback(android.media.MediaCodec.NotificationCallback);
method public final void setParameters(android.os.Bundle);
method public final void setVideoScalingMode(int);
method public final void signalEndOfInputStream();
@@ -12894,6 +12895,10 @@
field public int numSubSamples;
}
+ public static abstract interface MediaCodec.NotificationCallback {
+ method public abstract void onCodecNotify(android.media.MediaCodec);
+ }
+
public final class MediaCodecInfo {
method public final android.media.MediaCodecInfo.CodecCapabilities getCapabilitiesForType(java.lang.String);
method public final java.lang.String getName();
@@ -52412,7 +52417,7 @@
method public java.lang.String getString(java.lang.String) throws org.json.JSONException;
method public boolean has(java.lang.String);
method public boolean isNull(java.lang.String);
- method public java.util.Iterator keys();
+ method public java.util.Iterator<java.lang.String> keys();
method public int length();
method public org.json.JSONArray names();
method public static java.lang.String numberToString(java.lang.Number) throws org.json.JSONException;
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);
};