/* * Copyright (C) 2012, Collabora Ltd. * Author: Sebastian Dröge * Copyright (C) 2013, Fluendo S.A. * Author: Andoni Morales * Copyright (C) 2014, Sebastian Dröge * Copyright (C) 2014, Collabora Ltd. * Author: Matthieu Bouron * Copyright (C) 2015, Sebastian Dröge * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include "gstjniutils.h" static GModule *java_module; static jint (*get_created_java_vms) (JavaVM ** vmBuf, jsize bufLen, jsize * nVMs); static jint (*create_java_vm) (JavaVM ** p_vm, JNIEnv ** p_env, void *vm_args); static JavaVM *java_vm; static gboolean started_java_vm = FALSE; static pthread_key_t current_jni_env; static struct { jclass klass; jmethodID get_limit, get_position; jmethodID set_limit, set_position; jmethodID clear; } java_nio_buffer; jclass gst_amc_jni_get_class (JNIEnv * env, GError ** err, const gchar * name) { jclass tmp, ret = NULL; GST_DEBUG ("Retrieving Java class %s", name); tmp = (*env)->FindClass (env, name); if ((*env)->ExceptionCheck (env) || !tmp) { gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, "Failed to find class %s", name); goto done; } ret = (*env)->NewGlobalRef (env, tmp); if (!ret) { GST_ERROR ("Failed to get %s class global reference", name); } done: if (tmp) (*env)->DeleteLocalRef (env, tmp); tmp = NULL; return ret; } jmethodID gst_amc_jni_get_method_id (JNIEnv * env, GError ** err, jclass klass, const gchar * name, const gchar * signature) { jmethodID ret; ret = (*env)->GetMethodID (env, klass, name, signature); if ((*env)->ExceptionCheck (env) || !ret) { gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, "Failed to get method ID %s (%s)", name, signature); } return ret; } jmethodID gst_amc_jni_get_static_method_id (JNIEnv * env, GError ** err, jclass klass, const gchar * name, const gchar * signature) { jmethodID ret; ret = (*env)->GetStaticMethodID (env, klass, name, signature); if ((*env)->ExceptionCheck (env) || !ret) { gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, "Failed to get static method ID %s (%s)", name, signature); } return ret; } jfieldID gst_amc_jni_get_field_id (JNIEnv * env, GError ** err, jclass klass, const gchar * name, const gchar * type) { jfieldID ret; ret = (*env)->GetFieldID (env, klass, name, type); if ((*env)->ExceptionCheck (env) || !ret) { gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, "Failed to get field ID %s (%s)", name, type); } return ret; } jfieldID gst_amc_jni_get_static_field_id (JNIEnv * env, GError ** err, jclass klass, const gchar * name, const gchar * type) { jfieldID ret; ret = (*env)->GetStaticFieldID (env, klass, name, type); if ((*env)->ExceptionCheck (env) || !ret) { gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, "Failed to get static field ID %s (%s)", name, type); } return ret; } jobject gst_amc_jni_new_object (JNIEnv * env, GError ** err, gboolean global, jclass klass, jmethodID constructor, ...) { jobject tmp; va_list args; va_start (args, constructor); tmp = (*env)->NewObjectV (env, klass, constructor, args); va_end (args); if ((*env)->ExceptionCheck (env) || !tmp) { gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, "Failed to create object"); return NULL; } if (global) return gst_amc_jni_object_make_global (env, tmp); else return tmp; } jobject gst_amc_jni_new_object_from_static (JNIEnv * env, GError ** err, gboolean global, jclass klass, jmethodID method, ...) { jobject tmp; va_list args; va_start (args, method); tmp = (*env)->CallStaticObjectMethodV (env, klass, method, args); va_end (args); if ((*env)->ExceptionCheck (env) || !tmp) { gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, "Failed to create object"); return NULL; } if (global) return gst_amc_jni_object_make_global (env, tmp); else return tmp; } jobject gst_amc_jni_object_make_global (JNIEnv * env, jobject object) { jobject ret; ret = (*env)->NewGlobalRef (env, object); if (!ret) { GST_ERROR ("Failed to create global reference"); } gst_amc_jni_object_local_unref (env, object); return ret; } jobject gst_amc_jni_object_ref (JNIEnv * env, jobject object) { jobject ret; ret = (*env)->NewGlobalRef (env, object); if (!ret) { GST_ERROR ("Failed to create global reference"); } return ret; } void gst_amc_jni_object_unref (JNIEnv * env, jobject object) { (*env)->DeleteGlobalRef (env, object); } void gst_amc_jni_object_local_unref (JNIEnv * env, jobject object) { (*env)->DeleteLocalRef (env, object); } jstring gst_amc_jni_string_from_gchar (JNIEnv * env, GError ** err, gboolean global, const gchar * string) { jstring tmp; tmp = (*env)->NewStringUTF (env, string); if ((*env)->ExceptionCheck (env)) { gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, "Failed to call Java method"); tmp = NULL; } if (global) return gst_amc_jni_object_make_global (env, tmp); else return tmp; } gchar * gst_amc_jni_string_to_gchar (JNIEnv * env, jstring string, gboolean release) { const gchar *s = NULL; gchar *ret = NULL; s = (*env)->GetStringUTFChars (env, string, NULL); if (!s) { GST_ERROR ("Failed to convert string to UTF8"); goto done; } ret = g_strdup (s); (*env)->ReleaseStringUTFChars (env, string, s); done: if (release) { (*env)->DeleteLocalRef (env, string); } return ret; } /* getExceptionSummary() and getStackTrace() taken from Android's * platform/libnativehelper/JNIHelp.cpp * Modified to work with normal C strings and without C++. * * Copyright (C) 2006 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Returns a human-readable summary of an exception object. The buffer will * be populated with the "binary" class name and, if present, the * exception message. */ static gchar * getExceptionSummary (JNIEnv * env, jthrowable exception) { GString *gs = g_string_new (""); jclass exceptionClass = NULL, classClass = NULL; jmethodID classGetNameMethod, getMessage; jstring classNameStr = NULL, messageStr = NULL; const char *classNameChars, *messageChars; /* get the name of the exception's class */ exceptionClass = (*env)->GetObjectClass (env, exception); classClass = (*env)->GetObjectClass (env, exceptionClass); classGetNameMethod = (*env)->GetMethodID (env, classClass, "getName", "()Ljava/lang/String;"); classNameStr = (jstring) (*env)->CallObjectMethod (env, exceptionClass, classGetNameMethod); if (classNameStr == NULL) { if ((*env)->ExceptionCheck (env)) (*env)->ExceptionClear (env); g_string_append (gs, ""); goto done; } classNameChars = (*env)->GetStringUTFChars (env, classNameStr, NULL); if (classNameChars == NULL) { if ((*env)->ExceptionCheck (env)) (*env)->ExceptionClear (env); g_string_append (gs, ""); goto done; } g_string_append (gs, classNameChars); (*env)->ReleaseStringUTFChars (env, classNameStr, classNameChars); /* if the exception has a detail message, get that */ getMessage = (*env)->GetMethodID (env, exceptionClass, "getMessage", "()Ljava/lang/String;"); messageStr = (jstring) (*env)->CallObjectMethod (env, exception, getMessage); if (messageStr == NULL) { if ((*env)->ExceptionCheck (env)) (*env)->ExceptionClear (env); goto done; } g_string_append (gs, ": "); messageChars = (*env)->GetStringUTFChars (env, messageStr, NULL); if (messageChars != NULL) { g_string_append (gs, messageChars); (*env)->ReleaseStringUTFChars (env, messageStr, messageChars); } else { if ((*env)->ExceptionCheck (env)) (*env)->ExceptionClear (env); g_string_append (gs, ""); } done: if (exceptionClass) (*env)->DeleteLocalRef (env, exceptionClass); if (classClass) (*env)->DeleteLocalRef (env, classClass); if (classNameStr) (*env)->DeleteLocalRef (env, classNameStr); if (messageStr) (*env)->DeleteLocalRef (env, messageStr); return g_string_free (gs, FALSE); } /* * Returns an exception (with stack trace) as a string. */ static gchar * getStackTrace (JNIEnv * env, jthrowable exception) { GString *gs = g_string_new (""); jclass stringWriterClass = NULL, printWriterClass = NULL; jclass exceptionClass = NULL; jmethodID stringWriterCtor, stringWriterToStringMethod; jmethodID printWriterCtor, printStackTraceMethod; jobject stringWriter = NULL, printWriter = NULL; jstring messageStr = NULL; const char *utfChars; stringWriterClass = (*env)->FindClass (env, "java/io/StringWriter"); if (stringWriterClass == NULL) { g_string_append (gs, ""); goto done; } stringWriterCtor = (*env)->GetMethodID (env, stringWriterClass, "", "()V"); stringWriterToStringMethod = (*env)->GetMethodID (env, stringWriterClass, "toString", "()Ljava/lang/String;"); printWriterClass = (*env)->FindClass (env, "java/io/PrintWriter"); if (printWriterClass == NULL) { g_string_append (gs, ""); goto done; } printWriterCtor = (*env)->GetMethodID (env, printWriterClass, "", "(Ljava/io/Writer;)V"); stringWriter = (*env)->NewObject (env, stringWriterClass, stringWriterCtor); if (stringWriter == NULL) { if ((*env)->ExceptionCheck (env)) (*env)->ExceptionClear (env); g_string_append (gs, ""); goto done; } printWriter = (*env)->NewObject (env, printWriterClass, printWriterCtor, stringWriter); if (printWriter == NULL) { if ((*env)->ExceptionCheck (env)) (*env)->ExceptionClear (env); g_string_append (gs, ""); goto done; } exceptionClass = (*env)->GetObjectClass (env, exception); printStackTraceMethod = (*env)->GetMethodID (env, exceptionClass, "printStackTrace", "(Ljava/io/PrintWriter;)V"); (*env)->CallVoidMethod (env, exception, printStackTraceMethod, printWriter); if ((*env)->ExceptionCheck (env)) { (*env)->ExceptionClear (env); g_string_append (gs, ""); goto done; } messageStr = (jstring) (*env)->CallObjectMethod (env, stringWriter, stringWriterToStringMethod); if (messageStr == NULL) { if ((*env)->ExceptionCheck (env)) (*env)->ExceptionClear (env); g_string_append (gs, ""); goto done; } utfChars = (*env)->GetStringUTFChars (env, messageStr, NULL); if (utfChars == NULL) { if ((*env)->ExceptionCheck (env)) (*env)->ExceptionClear (env); g_string_append (gs, ""); goto done; } g_string_append (gs, utfChars); (*env)->ReleaseStringUTFChars (env, messageStr, utfChars); done: if (stringWriterClass) (*env)->DeleteLocalRef (env, stringWriterClass); if (printWriterClass) (*env)->DeleteLocalRef (env, printWriterClass); if (exceptionClass) (*env)->DeleteLocalRef (env, exceptionClass); if (stringWriter) (*env)->DeleteLocalRef (env, stringWriter); if (printWriter) (*env)->DeleteLocalRef (env, printWriter); if (messageStr) (*env)->DeleteLocalRef (env, messageStr); return g_string_free (gs, FALSE); } static JNIEnv * gst_amc_jni_attach_current_thread (void) { JNIEnv *env; JavaVMAttachArgs args; gint ret; GST_DEBUG ("Attaching thread %p", g_thread_self ()); args.version = JNI_VERSION_1_6; args.name = NULL; args.group = NULL; if ((ret = (*java_vm)->AttachCurrentThread (java_vm, &env, &args)) != JNI_OK) { GST_ERROR ("Failed to attach current thread: %d", ret); return NULL; } return env; } static void gst_amc_jni_detach_current_thread (void *env) { gint ret; GST_DEBUG ("Detaching thread %p", g_thread_self ()); if ((ret = (*java_vm)->DetachCurrentThread (java_vm)) != JNI_OK) { GST_DEBUG ("Failed to detach current thread: %d", ret); } } static gboolean check_nativehelper (void) { GModule *module; void **jni_invocation = NULL; gboolean ret = FALSE; module = g_module_open (NULL, G_MODULE_BIND_LOCAL); if (!module) return ret; /* Check if libnativehelper is loaded in the process and if * it has these awful wrappers for JNI_CreateJavaVM and * JNI_GetCreatedJavaVMs that crash the app if you don't * create a JniInvocation instance first. If it isn't we * just fail here and don't initialize anything. * See this code for reference: * https://android.googlesource.com/platform/libnativehelper/+/master/JniInvocation.cpp */ if (!g_module_symbol (module, "_ZN13JniInvocation15jni_invocation_E", (gpointer *) & jni_invocation)) { ret = TRUE; } else { ret = (jni_invocation != NULL && *jni_invocation != NULL); } g_module_close (module); return ret; } static gboolean load_java_module (const gchar * name) { java_module = g_module_open (name, G_MODULE_BIND_LOCAL); if (!java_module) goto load_failed; if (!g_module_symbol (java_module, "JNI_CreateJavaVM", (gpointer *) & create_java_vm)) { GST_ERROR ("Could not find 'JNI_CreateJavaVM' in '%s': %s", GST_STR_NULL (name), g_module_error ()); create_java_vm = NULL; } if (!g_module_symbol (java_module, "JNI_GetCreatedJavaVMs", (gpointer *) & get_created_java_vms)) goto symbol_error; return TRUE; load_failed: { GST_ERROR ("Failed to load Java module '%s': %s", GST_STR_NULL (name), g_module_error ()); return FALSE; } symbol_error: { GST_ERROR ("Failed to locate required JNI symbols in '%s': %s", GST_STR_NULL (name), g_module_error ()); g_module_close (java_module); java_module = NULL; return FALSE; } } static gboolean initialize_classes (void) { JNIEnv *env; GError *err = NULL; env = gst_amc_jni_get_env (); java_nio_buffer.klass = gst_amc_jni_get_class (env, &err, "java/nio/Buffer"); if (!java_nio_buffer.klass) { GST_ERROR ("Failed to get java.nio.Buffer class: %s", err->message); g_clear_error (&err); return FALSE; } java_nio_buffer.get_limit = gst_amc_jni_get_method_id (env, &err, java_nio_buffer.klass, "limit", "()I"); if (!java_nio_buffer.get_limit) { GST_ERROR ("Failed to get java.nio.Buffer limit(): %s", err->message); g_clear_error (&err); return FALSE; } java_nio_buffer.get_position = gst_amc_jni_get_method_id (env, &err, java_nio_buffer.klass, "position", "()I"); if (!java_nio_buffer.get_position) { GST_ERROR ("Failed to get java.nio.Buffer position(): %s", err->message); g_clear_error (&err); return FALSE; } java_nio_buffer.set_limit = gst_amc_jni_get_method_id (env, &err, java_nio_buffer.klass, "limit", "(I)Ljava/nio/Buffer;"); if (!java_nio_buffer.set_limit) { GST_ERROR ("Failed to get java.nio.Buffer limit(): %s", err->message); g_clear_error (&err); return FALSE; } java_nio_buffer.set_position = gst_amc_jni_get_method_id (env, &err, java_nio_buffer.klass, "position", "(I)Ljava/nio/Buffer;"); if (!java_nio_buffer.set_position) { GST_ERROR ("Failed to get java.nio.Buffer position(): %s", err->message); g_clear_error (&err); return FALSE; } java_nio_buffer.clear = gst_amc_jni_get_method_id (env, &err, java_nio_buffer.klass, "clear", "()Ljava/nio/Buffer;"); if (!java_nio_buffer.clear) { GST_ERROR ("Failed to get java.nio.Buffer clear(): %s", err->message); g_clear_error (&err); return FALSE; } return TRUE; } static gboolean gst_amc_jni_initialize_java_vm (void) { jsize n_vms; gint ret; if (java_vm) { GST_DEBUG ("Java VM already provided by the application"); return TRUE; } /* Returns TRUE if we can safely * a) get the current VMs and * b) start a VM if none is started yet * * FIXME: On Android >= 4.4 we won't be able to safely start a * VM on our own without using private C++ API! */ if (!check_nativehelper ()) { GST_ERROR ("Can't safely check for VMs or start a VM"); return FALSE; } if (!load_java_module (NULL)) { if (!load_java_module ("libdvm")) return FALSE; } n_vms = 0; if ((ret = get_created_java_vms (&java_vm, 1, &n_vms)) != JNI_OK) goto get_created_failed; if (n_vms > 0) { GST_DEBUG ("Successfully got existing Java VM %p", java_vm); } else if (create_java_vm) { JNIEnv *env; JavaVMInitArgs vm_args; JavaVMOption options[4]; GST_DEBUG ("Found no existing Java VM, trying to start one"); options[0].optionString = "-verbose:jni"; options[1].optionString = "-verbose:gc"; options[2].optionString = "-Xcheck:jni"; options[3].optionString = "-Xdebug"; vm_args.version = JNI_VERSION_1_4; vm_args.options = options; vm_args.nOptions = 4; vm_args.ignoreUnrecognized = JNI_TRUE; if ((ret = create_java_vm (&java_vm, &env, &vm_args)) != JNI_OK) goto create_failed; GST_DEBUG ("Successfully created Java VM %p", java_vm); started_java_vm = TRUE; } else { GST_ERROR ("JNI_CreateJavaVM not available"); java_vm = NULL; } if (java_vm == NULL) return FALSE; return initialize_classes (); get_created_failed: { GST_ERROR ("Failed to get already created VMs: %d", ret); g_module_close (java_module); java_module = NULL; return FALSE; } create_failed: { GST_ERROR ("Failed to create a Java VM: %d", ret); g_module_close (java_module); java_module = NULL; return FALSE; } } static void gst_amc_jni_set_error_string (JNIEnv * env, GError ** err, GQuark domain, gint code, const gchar * message) { jthrowable exception; if (!err) { if ((*env)->ExceptionCheck (env)) (*env)->ExceptionClear (env); return; } if ((*env)->ExceptionCheck (env)) { if ((exception = (*env)->ExceptionOccurred (env))) { gchar *exception_description, *exception_stacktrace; /* Clear exception so that we can call Java methods again */ (*env)->ExceptionClear (env); exception_description = getExceptionSummary (env, exception); exception_stacktrace = getStackTrace (env, exception); g_set_error (err, domain, code, "%s: %s\n%s", message, exception_description, exception_stacktrace); g_free (exception_description); g_free (exception_stacktrace); (*env)->DeleteLocalRef (env, exception); } else { (*env)->ExceptionClear (env); g_set_error (err, domain, code, "%s", message); } } else { g_set_error (err, domain, code, "%s", message); } } G_GNUC_PRINTF (5, 6) void gst_amc_jni_set_error (JNIEnv * env, GError ** err, GQuark domain, gint code, const gchar * format, ...) { gchar *message; va_list var_args; va_start (var_args, format); message = g_strdup_vprintf (format, var_args); va_end (var_args); gst_amc_jni_set_error_string (env, err, domain, code, message); g_free (message); } static gpointer gst_amc_jni_initialize_internal (gpointer data) { pthread_key_create (¤t_jni_env, gst_amc_jni_detach_current_thread); return gst_amc_jni_initialize_java_vm ()? GINT_TO_POINTER (1) : NULL; } /* Allow the application to set the Java VM */ void gst_amc_jni_set_java_vm (JavaVM * vm) { GST_DEBUG ("Application provides Java VM %p", vm); java_vm = vm; } gboolean gst_amc_jni_initialize (void) { GOnce once = G_ONCE_INIT; g_once (&once, gst_amc_jni_initialize_internal, NULL); return once.retval != NULL; } JNIEnv * gst_amc_jni_get_env (void) { JNIEnv *env; if ((env = pthread_getspecific (current_jni_env)) == NULL) { env = gst_amc_jni_attach_current_thread (); pthread_setspecific (current_jni_env, env); } return env; } gboolean gst_amc_jni_is_vm_started (void) { return started_java_vm; } #define CALL_STATIC_TYPE_METHOD(_type, _name, _jname) \ gboolean gst_amc_jni_call_static_##_name##_method (JNIEnv *env, GError ** err, jclass klass, jmethodID methodID, _type * value, ...) \ { \ gboolean ret = TRUE; \ va_list args; \ va_start(args, value); \ *value = (*env)->CallStatic##_jname##MethodV(env, klass, methodID, args); \ if ((*env)->ExceptionCheck (env)) { \ gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, \ "Failed to call static Java method"); \ ret = FALSE; \ } \ va_end(args); \ return ret; \ } CALL_STATIC_TYPE_METHOD (gboolean, boolean, Boolean); CALL_STATIC_TYPE_METHOD (gint8, byte, Byte); CALL_STATIC_TYPE_METHOD (gshort, short, Short); CALL_STATIC_TYPE_METHOD (gint, int, Int); CALL_STATIC_TYPE_METHOD (gchar, char, Char); CALL_STATIC_TYPE_METHOD (gint64, long, Long); CALL_STATIC_TYPE_METHOD (gfloat, float, Float); CALL_STATIC_TYPE_METHOD (gdouble, double, Double); CALL_STATIC_TYPE_METHOD (jobject, object, Object); gboolean gst_amc_jni_call_static_void_method (JNIEnv * env, GError ** err, jclass klass, jmethodID methodID, ...) { gboolean ret = TRUE; va_list args; va_start (args, methodID); (*env)->CallStaticVoidMethodV (env, klass, methodID, args); if ((*env)->ExceptionCheck (env)) { gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, "Failed to call static Java method"); ret = FALSE; } va_end (args); return ret; } #define CALL_TYPE_METHOD(_type, _name, _jname) \ gboolean gst_amc_jni_call_##_name##_method (JNIEnv *env, GError ** err, jobject obj, jmethodID methodID, _type *value, ...) \ { \ gboolean ret = TRUE; \ va_list args; \ va_start(args, value); \ *value = (*env)->Call##_jname##MethodV(env, obj, methodID, args); \ if ((*env)->ExceptionCheck (env)) { \ gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, \ "Failed to call Java method"); \ ret = FALSE; \ } \ va_end(args); \ return ret; \ } CALL_TYPE_METHOD (gboolean, boolean, Boolean); CALL_TYPE_METHOD (gint8, byte, Byte); CALL_TYPE_METHOD (gshort, short, Short); CALL_TYPE_METHOD (gint, int, Int); CALL_TYPE_METHOD (gchar, char, Char); CALL_TYPE_METHOD (gint64, long, Long); CALL_TYPE_METHOD (gfloat, float, Float); CALL_TYPE_METHOD (gdouble, double, Double); CALL_TYPE_METHOD (jobject, object, Object); gboolean gst_amc_jni_call_void_method (JNIEnv * env, GError ** err, jobject obj, jmethodID methodID, ...) { gboolean ret = TRUE; va_list args; va_start (args, methodID); (*env)->CallVoidMethodV (env, obj, methodID, args); if ((*env)->ExceptionCheck (env)) { gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, "Failed to call Java method"); ret = FALSE; } va_end (args); return ret; } #define GET_TYPE_FIELD(_type, _name, _jname) \ gboolean gst_amc_jni_get_##_name##_field (JNIEnv *env, GError ** err, jobject obj, jfieldID fieldID, _type *value) \ { \ gboolean ret = TRUE; \ \ *value = (*env)->Get##_jname##Field(env, obj, fieldID); \ if ((*env)->ExceptionCheck (env)) { \ gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, \ "Failed to get Java field"); \ ret = FALSE; \ } \ return ret; \ } GET_TYPE_FIELD (gboolean, boolean, Boolean); GET_TYPE_FIELD (gint8, byte, Byte); GET_TYPE_FIELD (gshort, short, Short); GET_TYPE_FIELD (gint, int, Int); GET_TYPE_FIELD (gchar, char, Char); GET_TYPE_FIELD (gint64, long, Long); GET_TYPE_FIELD (gfloat, float, Float); GET_TYPE_FIELD (gdouble, double, Double); GET_TYPE_FIELD (jobject, object, Object); #define GET_STATIC_TYPE_FIELD(_type, _name, _jname) \ gboolean gst_amc_jni_get_static_##_name##_field (JNIEnv *env, GError ** err, jclass klass, jfieldID fieldID, _type *value) \ { \ gboolean ret = TRUE; \ \ *value = (*env)->GetStatic##_jname##Field(env, klass, fieldID); \ if ((*env)->ExceptionCheck (env)) { \ gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, \ "Failed to get static Java field"); \ ret = FALSE; \ } \ return ret; \ } GET_STATIC_TYPE_FIELD (gboolean, boolean, Boolean); GET_STATIC_TYPE_FIELD (gint8, byte, Byte); GET_STATIC_TYPE_FIELD (gshort, short, Short); GET_STATIC_TYPE_FIELD (gint, int, Int); GET_STATIC_TYPE_FIELD (gchar, char, Char); GET_STATIC_TYPE_FIELD (gint64, long, Long); GET_STATIC_TYPE_FIELD (gfloat, float, Float); GET_STATIC_TYPE_FIELD (gdouble, double, Double); GET_STATIC_TYPE_FIELD (jobject, object, Object); gboolean gst_amc_jni_get_buffer_array (JNIEnv * env, GError ** err, jobject array, GstAmcBuffer ** buffers, gsize * n_buffers) { jsize i; *n_buffers = (*env)->GetArrayLength (env, array); *buffers = g_new0 (GstAmcBuffer, *n_buffers); for (i = 0; i < *n_buffers; i++) { jobject buffer = NULL; buffer = (*env)->GetObjectArrayElement (env, array, i); if ((*env)->ExceptionCheck (env) || !buffer) { gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, "Failed to get buffer %d", i); goto error; } (*buffers)[i].object = gst_amc_jni_object_make_global (env, buffer); if (!(*buffers)[i].object) { gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, "Failed to create global buffer reference %d", i); goto error; } (*buffers)[i].data = (*env)->GetDirectBufferAddress (env, (*buffers)[i].object); if (!(*buffers)[i].data) { gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, "Failed to get buffer address %d", i); goto error; } (*buffers)[i].size = (*env)->GetDirectBufferCapacity (env, (*buffers)[i].object); } return TRUE; error: if (*buffers) gst_amc_jni_free_buffer_array (env, *buffers, *n_buffers); *buffers = NULL; *n_buffers = 0; return FALSE; } void gst_amc_jni_free_buffer_array (JNIEnv * env, GstAmcBuffer * buffers, gsize n_buffers) { jsize i; g_return_if_fail (buffers != NULL); for (i = 0; i < n_buffers; i++) { if (buffers[i].object) gst_amc_jni_object_unref (env, buffers[i].object); } g_free (buffers); } void gst_amc_buffer_free (GstAmcBuffer * buffer) { JNIEnv *env; g_return_if_fail (buffer != NULL); env = gst_amc_jni_get_env (); if (buffer->object) gst_amc_jni_object_unref (env, buffer->object); g_free (buffer); } GstAmcBuffer * gst_amc_buffer_copy (GstAmcBuffer * buffer) { JNIEnv *env; GstAmcBuffer *ret; g_return_val_if_fail (buffer != NULL, NULL); env = gst_amc_jni_get_env (); ret = g_new0 (GstAmcBuffer, 1); ret->object = gst_amc_jni_object_ref (env, buffer->object); ret->data = buffer->data; ret->size = buffer->size; return ret; } gboolean gst_amc_buffer_get_position_and_limit (GstAmcBuffer * buffer, GError ** err, gint * position, gint * limit) { JNIEnv *env; g_return_val_if_fail (buffer != NULL, FALSE); g_return_val_if_fail (buffer->object != NULL, FALSE); env = gst_amc_jni_get_env (); if (!gst_amc_jni_call_int_method (env, err, buffer->object, java_nio_buffer.get_position, position)) return FALSE; if (!gst_amc_jni_call_int_method (env, err, buffer->object, java_nio_buffer.get_limit, limit)) return FALSE; return TRUE; } gboolean gst_amc_buffer_set_position_and_limit (GstAmcBuffer * buffer, GError ** err, gint position, gint limit) { JNIEnv *env; jobject tmp; g_return_val_if_fail (buffer != NULL, FALSE); g_return_val_if_fail (buffer->object != NULL, FALSE); env = gst_amc_jni_get_env (); if (!gst_amc_jni_call_object_method (env, err, buffer->object, java_nio_buffer.set_limit, &tmp, limit)) return FALSE; gst_amc_jni_object_local_unref (env, tmp); if (!gst_amc_jni_call_object_method (env, err, buffer->object, java_nio_buffer.set_position, &tmp, position)) return FALSE; gst_amc_jni_object_local_unref (env, tmp); return TRUE; } gboolean gst_amc_buffer_clear (GstAmcBuffer * buffer, GError ** err) { JNIEnv *env; jobject tmp; g_return_val_if_fail (buffer != NULL, FALSE); g_return_val_if_fail (buffer->object != NULL, FALSE); env = gst_amc_jni_get_env (); if (!gst_amc_jni_call_object_method (env, err, buffer->object, java_nio_buffer.clear, &tmp)) return FALSE; gst_amc_jni_object_local_unref (env, tmp); return TRUE; }