gstreamer/sys/androidmedia/gstjniutils.c
Sebastian Dröge 6b2800e324 androidmedia: Allow the application to provide the Java VM
In JNI_OnLoad() we will already get the Java VM passed and could
just directly use that. gstreamer_android-1.0.c will now provide
this to us.

Reason for this is that apparently not all Android system are
providing the JNI functions to get the currently running Java VMs, so
we would fail to get. With this we will always be able to get the Java
VM on such systems.
2015-06-18 14:38:04 +02:00

1100 lines
33 KiB
C

/*
* Copyright (C) 2012, Collabora Ltd.
* Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>
* Copyright (C) 2013, Fluendo S.A.
* Author: Andoni Morales <amorales@fluendo.com>
* Copyright (C) 2014, Sebastian Dröge <sebastian@centricular.com>
* Copyright (C) 2014, Collabora Ltd.
* Author: Matthieu Bouron <matthieu.bouron@collabora.com>
* Copyright (C) 2015, Sebastian Dröge <sebastian@centricular.com>
*
* 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 <gst/gst.h>
#include <pthread.h>
#include <gmodule.h>
#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, "<error getting class name>");
goto done;
}
classNameChars = (*env)->GetStringUTFChars (env, classNameStr, NULL);
if (classNameChars == NULL) {
if ((*env)->ExceptionCheck (env))
(*env)->ExceptionClear (env);
g_string_append (gs, "<error getting class name UTF-8>");
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, "<error getting message>");
}
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, "<error getting java.io.StringWriter class>");
goto done;
}
stringWriterCtor =
(*env)->GetMethodID (env, stringWriterClass, "<init>", "()V");
stringWriterToStringMethod =
(*env)->GetMethodID (env, stringWriterClass, "toString",
"()Ljava/lang/String;");
printWriterClass = (*env)->FindClass (env, "java/io/PrintWriter");
if (printWriterClass == NULL) {
g_string_append (gs, "<error getting java.io.PrintWriter class>");
goto done;
}
printWriterCtor =
(*env)->GetMethodID (env, printWriterClass, "<init>",
"(Ljava/io/Writer;)V");
stringWriter = (*env)->NewObject (env, stringWriterClass, stringWriterCtor);
if (stringWriter == NULL) {
if ((*env)->ExceptionCheck (env))
(*env)->ExceptionClear (env);
g_string_append (gs, "<error creating new StringWriter instance>");
goto done;
}
printWriter =
(*env)->NewObject (env, printWriterClass, printWriterCtor, stringWriter);
if (printWriter == NULL) {
if ((*env)->ExceptionCheck (env))
(*env)->ExceptionClear (env);
g_string_append (gs, "<error creating new PrintWriter instance>");
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, "<exception while printing stack trace>");
goto done;
}
messageStr = (jstring) (*env)->CallObjectMethod (env, stringWriter,
stringWriterToStringMethod);
if (messageStr == NULL) {
if ((*env)->ExceptionCheck (env))
(*env)->ExceptionClear (env);
g_string_append (gs, "<failed to call StringWriter.toString()>");
goto done;
}
utfChars = (*env)->GetStringUTFChars (env, messageStr, NULL);
if (utfChars == NULL) {
if ((*env)->ExceptionCheck (env))
(*env)->ExceptionClear (env);
g_string_append (gs, "<failed to get UTF chars for message>");
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 (&current_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;
}