[FFmpeg-devel] [PATCH 2/5] lavu/jni: add helpers to manage Android application context
Matthieu Bouron
matthieu.bouron at gmail.com
Mon Feb 15 18:52:27 CET 2016
From: Matthieu Bouron <matthieu.bouron at stupeflix.com>
---
libavutil/jni.c | 109 +++++++++++++++++++++++++++++++++++++++++++++++
libavutil/jni.h | 29 +++++++++++++
libavutil/jni_internal.c | 36 ++++++++++++++--
libavutil/jni_internal.h | 11 +++++
4 files changed, 182 insertions(+), 3 deletions(-)
diff --git a/libavutil/jni.c b/libavutil/jni.c
index fb89426..296e27b 100644
--- a/libavutil/jni.c
+++ b/libavutil/jni.c
@@ -20,6 +20,7 @@
#include "config.h"
#include "jni.h"
+#include "jni_internal.h"
#include "log.h"
#include <jni.h>
@@ -30,6 +31,11 @@ static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
JavaVM *java_vm = NULL;
+jobject application_context = NULL;
+
+jobject application_class_loader = NULL;
+jmethodID find_class_id = NULL;
+
void av_jni_register_java_vm(JavaVM *vm)
{
pthread_mutex_lock(&lock);
@@ -53,3 +59,106 @@ JavaVM *av_jni_get_java_vm(void)
return vm;
}
+
+#ifdef __ANDROID__
+int av_jni_register_application_context(JNIEnv *env, jobject context)
+{
+ int ret = 0;
+
+ jclass application_context_class;
+ jmethodID get_class_loader_id;
+
+ jclass application_class_loader_class;
+
+ pthread_mutex_lock(&lock);
+
+ if (application_context && application_class_loader) {
+ pthread_mutex_unlock(&lock);
+
+ av_log(NULL, AV_LOG_INFO, "The application context has already been registered\n");
+ return ret;
+ }
+
+ application_context_class = (*env)->GetObjectClass(env, context);
+ if ((ret = avpriv_jni_exception_check(env, 1, NULL)) < 0) {
+ goto done;
+ }
+
+ get_class_loader_id = (*env)->GetMethodID(env, application_context_class, "getClassLoader", "()Ljava/lang/ClassLoader;");
+ if ((ret = avpriv_jni_exception_check(env, 1, NULL)) < 0) {
+ goto done;
+ }
+
+ application_context = (*env)->NewGlobalRef(env, context);
+ application_class_loader = (*env)->CallObjectMethod(env, application_context, get_class_loader_id);
+ if ((ret = avpriv_jni_exception_check(env, 1, NULL)) < 0) {
+ goto done;
+ }
+
+ application_class_loader = (*env)->NewGlobalRef(env, application_class_loader);
+ if ((ret = avpriv_jni_exception_check(env, 1, NULL)) < 0) {
+ goto done;
+ }
+
+ application_class_loader_class = (*env)->GetObjectClass(env, application_class_loader);
+ if ((ret = avpriv_jni_exception_check(env, 1, NULL)) < 0) {
+ goto done;
+ }
+
+ find_class_id = (*env)->GetMethodID(env, application_class_loader_class, "findClass", "(Ljava/lang/String;)Ljava/lang/Class;");
+ if ((ret = avpriv_jni_exception_check(env, 1, NULL)) < 0) {
+ goto done;
+ }
+
+done:
+ if (application_context_class) {
+ (*env)->DeleteLocalRef(env, application_context_class);
+ }
+
+ if (application_class_loader_class) {
+ (*env)->DeleteLocalRef(env, application_class_loader_class);
+ }
+
+ if (ret != 0) {
+
+ if (application_context) {
+ (*env)->DeleteGlobalRef(env, application_context);
+ application_context = NULL;
+ }
+
+ if (application_class_loader) {
+ (*env)->DeleteGlobalRef(env, application_class_loader);
+ application_context = NULL;
+ }
+ }
+
+ pthread_mutex_unlock(&lock);
+
+ return ret;
+}
+
+jobject av_jni_get_application_context(void)
+{
+ return application_context;
+}
+
+int av_jni_unregister_application_context(JNIEnv *env)
+{
+ pthread_mutex_lock(&lock);
+
+ if (application_context) {
+ (*env)->DeleteGlobalRef(env, application_context);
+ application_context = NULL;
+ }
+
+ if (application_class_loader) {
+ (*env)->DeleteGlobalRef(env, application_class_loader);
+ application_class_loader = NULL;
+ }
+
+ pthread_mutex_unlock(&lock);
+
+ return 0;
+}
+
+#endif
diff --git a/libavutil/jni.h b/libavutil/jni.h
index a6c5c16..320a887 100644
--- a/libavutil/jni.h
+++ b/libavutil/jni.h
@@ -39,4 +39,33 @@ void av_jni_register_java_vm(JavaVM *vm);
*/
JavaVM *av_jni_get_java_vm(void);
+#ifdef __ANDROID__
+/*
+ * Register an Android application context that will be used later on
+ * to load application classes.
+ *
+ * @param env JNI environment
+ * @param context application context
+ * @return 0 on success, a negative number otherwise
+ */
+int av_jni_register_application_context(JNIEnv *env, jobject context);
+
+/*
+ * Get the registered android application context.
+ *
+ * @return the registered android application context
+ */
+jobject av_jni_get_application_context(void);
+
+/*
+ * Unregister the previously registered Android application context
+ * and delete the global reference associated with it.
+ *
+ * @param env JNI environment
+ * @return 0 on success, a negative number otherwise
+ *
+ */
+int av_jni_unregister_application_context(JNIEnv *env);
+#endif
+
#endif /* AVUTIL_JNI_H */
diff --git a/libavutil/jni_internal.c b/libavutil/jni_internal.c
index f095469..5e2f7c3 100644
--- a/libavutil/jni_internal.c
+++ b/libavutil/jni_internal.c
@@ -29,6 +29,11 @@
extern JavaVM *java_vm;
+extern jobject application_context;
+
+extern jobject application_class_loader;
+extern jmethodID find_class_id;
+
JNIEnv *avpriv_jni_attach_env(int *attached, void *log_ctx)
{
int ret = 0;
@@ -243,7 +248,7 @@ int avpriv_jni_exception_check(JNIEnv *env, int log, void *log_ctx)
if (!log) {
(*(env))->ExceptionClear((env));
- return -1;
+ return AVERROR_EXTERNAL;
}
exception = (*env)->ExceptionOccurred(env);
@@ -259,7 +264,7 @@ int avpriv_jni_exception_check(JNIEnv *env, int log, void *log_ctx)
av_log(log_ctx, AV_LOG_ERROR, "%s\n", message);
av_free(message);
- return -1;
+ return AVERROR_EXTERNAL;
}
int avpriv_jni_init_jfields(JNIEnv *env, void *jfields, const struct FFJniField *jfields_mapping, int global, void *log_ctx)
@@ -277,7 +282,7 @@ int avpriv_jni_init_jfields(JNIEnv *env, void *jfields, const struct FFJniField
last_clazz = NULL;
clazz = (*env)->FindClass(env, jfields_mapping[i].name);
- if ((ret = avpriv_jni_exception_check(env, mandatory, log_ctx) && mandatory) < 0) {
+ if ((ret = avpriv_jni_exception_check(env, mandatory, log_ctx)) < 0 && mandatory) {
goto done;
}
@@ -389,3 +394,28 @@ int avpriv_jni_reset_jfields(JNIEnv *env, void *jfields, const struct FFJniField
return 0;
}
+
+jclass avpriv_jni_find_application_class(JNIEnv *env, const char *name, void *log_ctx)
+{
+ jobject ret;
+ jobject tmp;
+
+ if (!application_class_loader || !find_class_id) {
+ av_log(log_ctx, AV_LOG_ERROR, "No application class loader has been registered\n");
+ return NULL;
+ }
+
+ tmp = avpriv_jni_utf_chars_to_jstring(env, name, log_ctx);
+ if (!tmp) {
+ return NULL;
+ }
+
+ ret = (*env)->CallObjectMethod(env, application_class_loader, find_class_id, tmp);
+ if (avpriv_jni_exception_check(env, 1, log_ctx) < 0) {
+ ret = NULL;
+ }
+
+ (*env)->DeleteLocalRef(env, tmp);
+
+ return ret;
+}
diff --git a/libavutil/jni_internal.h b/libavutil/jni_internal.h
index 6e66cc2..4d86f02 100644
--- a/libavutil/jni_internal.h
+++ b/libavutil/jni_internal.h
@@ -144,4 +144,15 @@ int avpriv_jni_init_jfields(JNIEnv *env, void *jfields, const struct FFJniField
*/
int avpriv_jni_reset_jfields(JNIEnv *env, void *jfields, const struct FFJniField *jfields_mapping, int global, void *log_ctx);
+/*
+ * Find an application class using the registered Android application context.
+ *
+ * @param env JNI environment
+ * @param name class name (for example: java/lang/String)
+ * @param log_ctx context used for logging, can be NULL
+ * @return a local reference to a class object corresponding the provided
+ * name, or NULL if the class cannot be found
+ */
+jclass avpriv_jni_find_application_class(JNIEnv *env, const char *name, void *log_ctx);
+
#endif /* AVUTIL_JNI_INTERNAL_H */
--
2.7.1
More information about the ffmpeg-devel
mailing list