diff options
author | Matthieu Bouron <matthieu.bouron@collabora.com> | 2014-06-02 12:37:09 +0100 |
---|---|---|
committer | Sebastian Dröge <sebastian@centricular.com> | 2015-03-15 16:28:03 +0000 |
commit | be2378a071728c48f49cbf43ebaa8d1336c7b12e (patch) | |
tree | 453c4d3a28e51ce4c2f11aa047fa7233799f8f09 /sys/androidmedia | |
parent | 5c3652dd22e0201402abdcb80ad9c4ace21e0f4c (diff) | |
download | gstreamer-plugins-bad-be2378a071728c48f49cbf43ebaa8d1336c7b12e.tar.gz |
androidmedia: Split jni code to gstjniutils.c
Diffstat (limited to 'sys/androidmedia')
-rw-r--r-- | sys/androidmedia/Makefile.am | 6 | ||||
-rw-r--r-- | sys/androidmedia/gstamc.c | 724 | ||||
-rw-r--r-- | sys/androidmedia/gstjniutils.c | 484 | ||||
-rw-r--r-- | sys/androidmedia/gstjniutils.h | 45 |
4 files changed, 676 insertions, 583 deletions
diff --git a/sys/androidmedia/Makefile.am b/sys/androidmedia/Makefile.am index 3f1758803..937ce32ac 100644 --- a/sys/androidmedia/Makefile.am +++ b/sys/androidmedia/Makefile.am @@ -4,14 +4,16 @@ libgstandroidmedia_la_SOURCES = \ gstamc.c \ gstamcaudiodec.c \ gstamcvideodec.c \ - gstamcvideoenc.c + gstamcvideoenc.c \ + gstjniutils.c noinst_HEADERS = \ gstamc.h \ gstamc-constants.h \ gstamcaudiodec.h \ gstamcvideodec.h \ - gstamcvideoenc.h + gstamcvideoenc.h \ + gstjniutils.h libgstandroidmedia_la_CFLAGS = \ $(GST_PLUGINS_BASE_CFLAGS) \ diff --git a/sys/androidmedia/gstamc.c b/sys/androidmedia/gstamc.c index 36e437285..57024f27a 100644 --- a/sys/androidmedia/gstamc.c +++ b/sys/androidmedia/gstamc.c @@ -34,215 +34,12 @@ #include "gstamcvideodec.h" #include "gstamcvideoenc.h" #include "gstamcaudiodec.h" +#include "gstjniutils.h" -#include <gmodule.h> #include <gst/gst.h> #include <gst/video/video.h> #include <gst/audio/audio.h> #include <string.h> -#include <jni.h> - -/* 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); -} - -#include <pthread.h> GST_DEBUG_CATEGORY (gst_amc_debug); #define GST_CAT_DEFAULT gst_amc_debug @@ -256,13 +53,6 @@ static gboolean ignore_unknown_color_formats = TRUE; static gboolean ignore_unknown_color_formats = FALSE; #endif -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 gboolean accepted_color_formats (GstAmcCodecType * type, gboolean is_encoder); @@ -315,233 +105,6 @@ static struct jmethodID set_byte_buffer; } media_format; -static pthread_key_t current_jni_env; - -static JNIEnv * -gst_amc_attach_current_thread (void) -{ - JNIEnv *env; - JavaVMAttachArgs args; - - GST_DEBUG ("Attaching thread %p", g_thread_self ()); - args.version = JNI_VERSION_1_6; - args.name = NULL; - args.group = NULL; - - if ((*java_vm)->AttachCurrentThread (java_vm, &env, &args) < 0) { - GST_ERROR ("Failed to attach current thread"); - return NULL; - } - - return env; -} - -static void -gst_amc_detach_current_thread (void *env) -{ - GST_DEBUG ("Detaching thread %p", g_thread_self ()); - (*java_vm)->DetachCurrentThread (java_vm); -} - -static JNIEnv * -gst_amc_get_jni_env (void) -{ - JNIEnv *env; - - if ((env = pthread_getspecific (current_jni_env)) == NULL) { - env = gst_amc_attach_current_thread (); - pthread_setspecific (current_jni_env, env); - } - - return env; -} - -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)) - goto symbol_error; - - 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_java_vm (void) -{ - jsize n_vms; - - /* 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 (get_created_java_vms (&java_vm, 1, &n_vms) < 0) - goto get_created_failed; - - if (n_vms > 0) { - GST_DEBUG ("Successfully got existing Java VM %p", java_vm); - } else { - 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 (create_java_vm (&java_vm, &env, &vm_args) < 0) - goto create_failed; - GST_DEBUG ("Successfully created Java VM %p", java_vm); - - started_java_vm = TRUE; - } - - return java_vm != NULL; - -get_created_failed: - { - GST_ERROR ("Failed to get already created VMs"); - g_module_close (java_module); - java_module = NULL; - return FALSE; - } -create_failed: - { - GST_ERROR ("Failed to create a Java VM"); - g_module_close (java_module); - java_module = NULL; - return FALSE; - } -} - -static void -gst_amc_set_error_string (JNIEnv * env, GQuark domain, gint code, GError ** err, - 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) - static void - gst_amc_set_error (JNIEnv * env, GQuark domain, gint code, - GError ** err, 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_set_error_string (env, domain, code, err, message); - - g_free (message); -} - GstAmcCodec * gst_amc_codec_new (const gchar * name, GError ** err) { @@ -552,11 +115,11 @@ gst_amc_codec_new (const gchar * name, GError ** err) g_return_val_if_fail (name != NULL, NULL); - env = gst_amc_get_jni_env (); + env = gst_amc_jni_get_env (); name_str = (*env)->NewStringUTF (env, name); if (name_str == NULL) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_INIT, err, + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_INIT, err, "Failed to create Java String"); goto error; } @@ -567,14 +130,14 @@ gst_amc_codec_new (const gchar * name, GError ** err) (*env)->CallStaticObjectMethod (env, media_codec.klass, media_codec.create_by_codec_name, name_str); if ((*env)->ExceptionCheck (env) || !object) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_INIT, err, + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_INIT, err, "Failed to create codec '%s'", name); goto error; } codec->object = (*env)->NewGlobalRef (env, object); if (!codec->object) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_INIT, err, + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_INIT, err, "Failed to create global codec reference"); goto error; } @@ -602,7 +165,7 @@ gst_amc_codec_free (GstAmcCodec * codec) g_return_if_fail (codec != NULL); - env = gst_amc_get_jni_env (); + env = gst_amc_jni_get_env (); (*env)->DeleteGlobalRef (env, codec->object); g_slice_free (GstAmcCodec, codec); } @@ -617,13 +180,13 @@ gst_amc_codec_configure (GstAmcCodec * codec, GstAmcFormat * format, gint flags, g_return_val_if_fail (codec != NULL, FALSE); g_return_val_if_fail (format != NULL, FALSE); - env = gst_amc_get_jni_env (); + env = gst_amc_jni_get_env (); (*env)->CallVoidMethod (env, codec->object, media_codec.configure, format->object, NULL, NULL, flags); if ((*env)->ExceptionCheck (env)) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_SETTINGS, err, - "Failed to configure codec"); + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_SETTINGS, + err, "Failed to configure codec"); ret = FALSE; goto done; } @@ -642,14 +205,14 @@ gst_amc_codec_get_output_format (GstAmcCodec * codec, GError ** err) g_return_val_if_fail (codec != NULL, NULL); - env = gst_amc_get_jni_env (); + env = gst_amc_jni_get_env (); object = (*env)->CallObjectMethod (env, codec->object, media_codec.get_output_format); if ((*env)->ExceptionCheck (env)) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_SETTINGS, err, - "Failed to get output format"); + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_SETTINGS, + err, "Failed to get output format"); goto done; } @@ -657,8 +220,8 @@ gst_amc_codec_get_output_format (GstAmcCodec * codec, GError ** err) ret->object = (*env)->NewGlobalRef (env, object); if (!ret->object) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_SETTINGS, err, - "Failed to create global format reference"); + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_SETTINGS, + err, "Failed to create global format reference"); g_slice_free (GstAmcFormat, ret); ret = NULL; } @@ -678,12 +241,12 @@ gst_amc_codec_start (GstAmcCodec * codec, GError ** err) g_return_val_if_fail (codec != NULL, FALSE); - env = gst_amc_get_jni_env (); + env = gst_amc_jni_get_env (); (*env)->CallVoidMethod (env, codec->object, media_codec.start); if ((*env)->ExceptionCheck (env)) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, err, - "Failed to start codec"); + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + err, "Failed to start codec"); ret = FALSE; goto done; } @@ -701,12 +264,12 @@ gst_amc_codec_stop (GstAmcCodec * codec, GError ** err) g_return_val_if_fail (codec != NULL, FALSE); - env = gst_amc_get_jni_env (); + env = gst_amc_jni_get_env (); (*env)->CallVoidMethod (env, codec->object, media_codec.stop); if ((*env)->ExceptionCheck (env)) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, err, - "Failed to stop codec"); + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + err, "Failed to stop codec"); ret = FALSE; goto done; } @@ -724,12 +287,12 @@ gst_amc_codec_flush (GstAmcCodec * codec, GError ** err) g_return_val_if_fail (codec != NULL, FALSE); - env = gst_amc_get_jni_env (); + env = gst_amc_jni_get_env (); (*env)->CallVoidMethod (env, codec->object, media_codec.flush); if ((*env)->ExceptionCheck (env)) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, err, - "Failed to flush codec"); + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + err, "Failed to flush codec"); ret = FALSE; goto done; } @@ -747,12 +310,12 @@ gst_amc_codec_release (GstAmcCodec * codec, GError ** err) g_return_val_if_fail (codec != NULL, FALSE); - env = gst_amc_get_jni_env (); + env = gst_amc_jni_get_env (); (*env)->CallVoidMethod (env, codec->object, media_codec.release); if ((*env)->ExceptionCheck (env)) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, err, - "Failed to release codec"); + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + err, "Failed to release codec"); ret = FALSE; goto done; } @@ -770,7 +333,7 @@ gst_amc_codec_free_buffers (GstAmcBuffer * buffers, gsize n_buffers) g_return_if_fail (buffers != NULL); - env = gst_amc_get_jni_env (); + env = gst_amc_jni_get_env (); for (i = 0; i < n_buffers; i++) { if (buffers[i].object) @@ -793,21 +356,21 @@ gst_amc_codec_get_output_buffers (GstAmcCodec * codec, gsize * n_buffers, g_return_val_if_fail (n_buffers != NULL, NULL); *n_buffers = 0; - env = gst_amc_get_jni_env (); + env = gst_amc_jni_get_env (); output_buffers = (*env)->CallObjectMethod (env, codec->object, media_codec.get_output_buffers); if ((*env)->ExceptionCheck (env) || !output_buffers) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, err, - "Failed to get output buffers"); + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + err, "Failed to get output buffers"); goto done; } n_output_buffers = (*env)->GetArrayLength (env, output_buffers); if ((*env)->ExceptionCheck (env)) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, err, - "Failed to get output buffers array length"); + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + err, "Failed to get output buffers array length"); goto done; } @@ -819,23 +382,23 @@ gst_amc_codec_get_output_buffers (GstAmcCodec * codec, gsize * n_buffers, buffer = (*env)->GetObjectArrayElement (env, output_buffers, i); if ((*env)->ExceptionCheck (env) || !buffer) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, err, - "Failed to get output buffer %d", i); + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + err, "Failed to get output buffer %d", i); goto error; } ret[i].object = (*env)->NewGlobalRef (env, buffer); (*env)->DeleteLocalRef (env, buffer); if (!ret[i].object) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, err, - "Failed to create global output buffer reference %d", i); + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + err, "Failed to create global output buffer reference %d", i); goto error; } ret[i].data = (*env)->GetDirectBufferAddress (env, ret[i].object); if (!ret[i].data) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, err, - "Failed to get output buffer address %d", i); + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + err, "Failed to get output buffer address %d", i); goto error; } ret[i].size = (*env)->GetDirectBufferCapacity (env, ret[i].object); @@ -869,21 +432,21 @@ gst_amc_codec_get_input_buffers (GstAmcCodec * codec, gsize * n_buffers, g_return_val_if_fail (n_buffers != NULL, NULL); *n_buffers = 0; - env = gst_amc_get_jni_env (); + env = gst_amc_jni_get_env (); input_buffers = (*env)->CallObjectMethod (env, codec->object, media_codec.get_input_buffers); if ((*env)->ExceptionCheck (env) || !input_buffers) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, err, - "Failed to get input buffers"); + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + err, "Failed to get input buffers"); goto done; } n_input_buffers = (*env)->GetArrayLength (env, input_buffers); if ((*env)->ExceptionCheck (env)) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, err, - "Failed to get input buffers array length"); + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + err, "Failed to get input buffers array length"); goto done; } @@ -895,23 +458,23 @@ gst_amc_codec_get_input_buffers (GstAmcCodec * codec, gsize * n_buffers, buffer = (*env)->GetObjectArrayElement (env, input_buffers, i); if ((*env)->ExceptionCheck (env) || !buffer) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, err, - "Failed to get input buffer %d", i); + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + err, "Failed to get input buffer %d", i); goto error; } ret[i].object = (*env)->NewGlobalRef (env, buffer); (*env)->DeleteLocalRef (env, buffer); if (!ret[i].object) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, err, - "Failed to create global input buffer reference %d", i); + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + err, "Failed to create global input buffer reference %d", i); goto error; } ret[i].data = (*env)->GetDirectBufferAddress (env, ret[i].object); if (!ret[i].data) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, err, - "Failed to get input buffer address %d", i); + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + err, "Failed to get input buffer address %d", i); goto error; } ret[i].size = (*env)->GetDirectBufferCapacity (env, ret[i].object); @@ -940,14 +503,14 @@ gst_amc_codec_dequeue_input_buffer (GstAmcCodec * codec, gint64 timeoutUs, g_return_val_if_fail (codec != NULL, G_MININT); - env = gst_amc_get_jni_env (); + env = gst_amc_jni_get_env (); ret = (*env)->CallIntMethod (env, codec->object, media_codec.dequeue_input_buffer, timeoutUs); if ((*env)->ExceptionCheck (env)) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, err, - "Failed to dequeue input buffer"); + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + err, "Failed to dequeue input buffer"); ret = G_MININT; goto done; } @@ -966,16 +529,16 @@ gst_amc_codec_fill_buffer_info (JNIEnv * env, jobject buffer_info, info->flags = (*env)->GetIntField (env, buffer_info, media_codec_buffer_info.flags); if ((*env)->ExceptionCheck (env)) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, err, - "Failed to get buffer info flags"); + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + err, "Failed to get buffer info flags"); return FALSE; } info->offset = (*env)->GetIntField (env, buffer_info, media_codec_buffer_info.offset); if ((*env)->ExceptionCheck (env)) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, err, - "Failed to get buffer info offset"); + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + err, "Failed to get buffer info offset"); return FALSE; } @@ -983,8 +546,8 @@ gst_amc_codec_fill_buffer_info (JNIEnv * env, jobject buffer_info, (*env)->GetLongField (env, buffer_info, media_codec_buffer_info.presentation_time_us); if ((*env)->ExceptionCheck (env)) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, err, - "Failed to get buffer info pts"); + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + err, "Failed to get buffer info pts"); return FALSE; } @@ -992,8 +555,8 @@ gst_amc_codec_fill_buffer_info (JNIEnv * env, jobject buffer_info, (*env)->GetIntField (env, buffer_info, media_codec_buffer_info.size); if ((*env)->ExceptionCheck (env)) { (*env)->ExceptionClear (env); - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, err, - "Failed to get buffer info size"); + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + err, "Failed to get buffer info size"); return FALSE; } @@ -1010,14 +573,14 @@ gst_amc_codec_dequeue_output_buffer (GstAmcCodec * codec, g_return_val_if_fail (codec != NULL, G_MININT); - env = gst_amc_get_jni_env (); + env = gst_amc_jni_get_env (); info_o = (*env)->NewObject (env, media_codec_buffer_info.klass, media_codec_buffer_info.constructor); if (!info_o) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, err, - "Failed to create buffer info instance"); + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + err, "Failed to create buffer info instance"); goto done; } @@ -1025,8 +588,8 @@ gst_amc_codec_dequeue_output_buffer (GstAmcCodec * codec, (*env)->CallIntMethod (env, codec->object, media_codec.dequeue_output_buffer, info_o, timeoutUs); if ((*env)->ExceptionCheck (env)) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, err, - "Failed to dequeue output buffer"); + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + err, "Failed to dequeue output buffer"); ret = G_MININT; goto done; } @@ -1054,13 +617,13 @@ gst_amc_codec_queue_input_buffer (GstAmcCodec * codec, gint index, g_return_val_if_fail (codec != NULL, FALSE); g_return_val_if_fail (info != NULL, FALSE); - env = gst_amc_get_jni_env (); + env = gst_amc_jni_get_env (); (*env)->CallVoidMethod (env, codec->object, media_codec.queue_input_buffer, index, info->offset, info->size, info->presentation_time_us, info->flags); if ((*env)->ExceptionCheck (env)) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, err, - "Failed to queue input buffer"); + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + err, "Failed to queue input buffer"); ret = FALSE; goto done; } @@ -1079,13 +642,13 @@ gst_amc_codec_release_output_buffer (GstAmcCodec * codec, gint index, g_return_val_if_fail (codec != NULL, FALSE); - env = gst_amc_get_jni_env (); + env = gst_amc_jni_get_env (); (*env)->CallVoidMethod (env, codec->object, media_codec.release_output_buffer, index, JNI_FALSE); if ((*env)->ExceptionCheck (env)) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, err, - "Failed to release output buffer"); + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + err, "Failed to release output buffer"); ret = FALSE; goto done; } @@ -1106,11 +669,11 @@ gst_amc_format_new_audio (const gchar * mime, gint sample_rate, gint channels, g_return_val_if_fail (mime != NULL, NULL); - env = gst_amc_get_jni_env (); + env = gst_amc_jni_get_env (); mime_str = (*env)->NewStringUTF (env, mime); if (mime_str == NULL) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_INIT, err, + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_INIT, err, "Failed to create Java string"); goto error; } @@ -1121,14 +684,14 @@ gst_amc_format_new_audio (const gchar * mime, gint sample_rate, gint channels, (*env)->CallStaticObjectMethod (env, media_format.klass, media_format.create_audio_format, mime_str, sample_rate, channels); if ((*env)->ExceptionCheck (env) || !object) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_INIT, err, + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_INIT, err, "Failed to create format instance '%s'", mime); goto error; } format->object = (*env)->NewGlobalRef (env, object); if (!format->object) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_INIT, err, + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_INIT, err, "Failed to create global format reference"); goto error; } @@ -1160,11 +723,11 @@ gst_amc_format_new_video (const gchar * mime, gint width, gint height, g_return_val_if_fail (mime != NULL, NULL); - env = gst_amc_get_jni_env (); + env = gst_amc_jni_get_env (); mime_str = (*env)->NewStringUTF (env, mime); if (mime_str == NULL) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_INIT, err, + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_INIT, err, "Failed to create Java string"); goto error; } @@ -1175,14 +738,14 @@ gst_amc_format_new_video (const gchar * mime, gint width, gint height, (*env)->CallStaticObjectMethod (env, media_format.klass, media_format.create_video_format, mime_str, width, height); if ((*env)->ExceptionCheck (env) || !object) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_INIT, err, + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_INIT, err, "Failed to create format instance '%s'", mime); goto error; } format->object = (*env)->NewGlobalRef (env, object); if (!format->object) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_INIT, err, + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_INIT, err, "Failed to create global format reference"); goto error; } @@ -1210,7 +773,7 @@ gst_amc_format_free (GstAmcFormat * format) g_return_if_fail (format != NULL); - env = gst_amc_get_jni_env (); + env = gst_amc_jni_get_env (); (*env)->DeleteGlobalRef (env, format->object); g_slice_free (GstAmcFormat, format); } @@ -1225,20 +788,20 @@ gst_amc_format_to_string (GstAmcFormat * format, GError ** err) g_return_val_if_fail (format != NULL, FALSE); - env = gst_amc_get_jni_env (); + env = gst_amc_jni_get_env (); v_str = (*env)->CallObjectMethod (env, format->object, media_format.to_string); if ((*env)->ExceptionCheck (env)) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, err, - "Failed to convert format to string"); + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + err, "Failed to convert format to string"); goto done; } v = (*env)->GetStringUTFChars (env, v_str, NULL); if (!v) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, err, - "Failed to get UTF8 string"); + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + err, "Failed to get UTF8 string"); goto done; } @@ -1264,12 +827,12 @@ gst_amc_format_contains_key (GstAmcFormat * format, const gchar * key, g_return_val_if_fail (format != NULL, FALSE); g_return_val_if_fail (key != NULL, FALSE); - env = gst_amc_get_jni_env (); + env = gst_amc_jni_get_env (); key_str = (*env)->NewStringUTF (env, key); if (!key_str) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, err, - "Failed to create Java string"); + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + err, "Failed to create Java string"); goto done; } @@ -1277,8 +840,8 @@ gst_amc_format_contains_key (GstAmcFormat * format, const gchar * key, (*env)->CallBooleanMethod (env, format->object, media_format.contains_key, key_str); if ((*env)->ExceptionCheck (env)) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, err, - "Failed to check if format contains key '%s'", key); + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + err, "Failed to check if format contains key '%s'", key); goto done; } @@ -1302,12 +865,12 @@ gst_amc_format_get_float (GstAmcFormat * format, const gchar * key, g_return_val_if_fail (value != NULL, FALSE); *value = 0; - env = gst_amc_get_jni_env (); + env = gst_amc_jni_get_env (); key_str = (*env)->NewStringUTF (env, key); if (!key_str) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, err, - "Failed to create Java string"); + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + err, "Failed to create Java string"); goto done; } @@ -1315,8 +878,8 @@ gst_amc_format_get_float (GstAmcFormat * format, const gchar * key, (*env)->CallFloatMethod (env, format->object, media_format.get_float, key_str); if ((*env)->ExceptionCheck (env)) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, err, - "Failed get float key '%s'", key); + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + err, "Failed get float key '%s'", key); goto done; } ret = TRUE; @@ -1338,20 +901,20 @@ gst_amc_format_set_float (GstAmcFormat * format, const gchar * key, g_return_if_fail (format != NULL); g_return_if_fail (key != NULL); - env = gst_amc_get_jni_env (); + env = gst_amc_jni_get_env (); key_str = (*env)->NewStringUTF (env, key); if (!key_str) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, err, - "Failed to create Java string"); + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + err, "Failed to create Java string"); goto done; } (*env)->CallVoidMethod (env, format->object, media_format.set_float, key_str, value); if ((*env)->ExceptionCheck (env)) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, err, - "Failed set float key '%s'", key); + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + err, "Failed set float key '%s'", key); goto done; } @@ -1373,12 +936,12 @@ gst_amc_format_get_int (GstAmcFormat * format, const gchar * key, gint * value, g_return_val_if_fail (value != NULL, FALSE); *value = 0; - env = gst_amc_get_jni_env (); + env = gst_amc_jni_get_env (); key_str = (*env)->NewStringUTF (env, key); if (!key_str) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, err, - "Failed to create Java string"); + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + err, "Failed to create Java string"); goto done; } @@ -1386,8 +949,8 @@ gst_amc_format_get_int (GstAmcFormat * format, const gchar * key, gint * value, (*env)->CallIntMethod (env, format->object, media_format.get_integer, key_str); if ((*env)->ExceptionCheck (env)) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, err, - "Failed get integer key '%s'", key); + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + err, "Failed get integer key '%s'", key); goto done; } ret = TRUE; @@ -1410,20 +973,20 @@ gst_amc_format_set_int (GstAmcFormat * format, const gchar * key, gint value, g_return_if_fail (format != NULL); g_return_if_fail (key != NULL); - env = gst_amc_get_jni_env (); + env = gst_amc_jni_get_env (); key_str = (*env)->NewStringUTF (env, key); if (!key_str) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, err, - "Failed to create Java string"); + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + err, "Failed to create Java string"); goto done; } (*env)->CallVoidMethod (env, format->object, media_format.set_integer, key_str, value); if ((*env)->ExceptionCheck (env)) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, err, - "Failed set integer key '%s'", key); + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + err, "Failed set integer key '%s'", key); goto done; } @@ -1447,12 +1010,12 @@ gst_amc_format_get_string (GstAmcFormat * format, const gchar * key, g_return_val_if_fail (value != NULL, FALSE); *value = 0; - env = gst_amc_get_jni_env (); + env = gst_amc_jni_get_env (); key_str = (*env)->NewStringUTF (env, key); if (!key_str) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, err, - "Failed to create Java string"); + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + err, "Failed to create Java string"); goto done; } @@ -1460,15 +1023,15 @@ gst_amc_format_get_string (GstAmcFormat * format, const gchar * key, (*env)->CallObjectMethod (env, format->object, media_format.get_string, key_str); if ((*env)->ExceptionCheck (env)) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, err, - "Failed get string key '%s'", key); + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + err, "Failed get string key '%s'", key); goto done; } v = (*env)->GetStringUTFChars (env, v_str, NULL); if (!v) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, err, - "Failed get string UTF8 characters"); + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + err, "Failed get string UTF8 characters"); goto done; } @@ -1499,27 +1062,27 @@ gst_amc_format_set_string (GstAmcFormat * format, const gchar * key, g_return_if_fail (key != NULL); g_return_if_fail (value != NULL); - env = gst_amc_get_jni_env (); + env = gst_amc_jni_get_env (); key_str = (*env)->NewStringUTF (env, key); if (!key_str) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, err, - "Failed to create Java string"); + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + err, "Failed to create Java string"); goto done; } v_str = (*env)->NewStringUTF (env, value); if (!v_str) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, err, - "Failed to create Java string"); + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + err, "Failed to create Java string"); goto done; } (*env)->CallVoidMethod (env, format->object, media_format.set_string, key_str, v_str); if ((*env)->ExceptionCheck (env)) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, err, - "Failed set string key '%s'", key); + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + err, "Failed set string key '%s'", key); goto done; } @@ -1546,27 +1109,27 @@ gst_amc_format_get_buffer (GstAmcFormat * format, const gchar * key, *data = NULL; *size = 0; - env = gst_amc_get_jni_env (); + env = gst_amc_jni_get_env (); key_str = (*env)->NewStringUTF (env, key); if (!key_str) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, err, - "Failed to create Java string"); + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + err, "Failed to create Java string"); goto done; } v = (*env)->CallObjectMethod (env, format->object, media_format.get_byte_buffer, key_str); if ((*env)->ExceptionCheck (env)) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, err, - "Failed get buffer key '%s'", key); + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + err, "Failed get buffer key '%s'", key); goto done; } *data = (*env)->GetDirectBufferAddress (env, v); if (!data) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, err, - "Failed get buffer address"); + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + err, "Failed get buffer address"); goto done; } *size = (*env)->GetDirectBufferCapacity (env, v); @@ -1595,28 +1158,28 @@ gst_amc_format_set_buffer (GstAmcFormat * format, const gchar * key, g_return_if_fail (key != NULL); g_return_if_fail (data != NULL); - env = gst_amc_get_jni_env (); + env = gst_amc_jni_get_env (); key_str = (*env)->NewStringUTF (env, key); if (!key_str) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, err, - "Failed to create Java string"); + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + err, "Failed to create Java string"); goto done; } /* FIXME: The memory must remain valid until the codec is stopped */ v = (*env)->NewDirectByteBuffer (env, data, size); if (!v) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, err, - "Failed create Java byte buffer"); + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + err, "Failed create Java byte buffer"); goto done; } (*env)->CallVoidMethod (env, format->object, media_format.set_byte_buffer, key_str, v); if ((*env)->ExceptionCheck (env)) { - gst_amc_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, err, - "Failed set buffer key '%s'", key); + gst_amc_jni_set_error (env, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + err, "Failed set buffer key '%s'", key); goto done; } @@ -1636,7 +1199,7 @@ get_java_classes (void) GST_DEBUG ("Retrieving Java classes"); - env = gst_amc_get_jni_env (); + env = gst_amc_jni_get_env (); tmp = (*env)->FindClass (env, "java/lang/String"); if (!tmp) { @@ -1968,7 +1531,7 @@ scan_codecs (GstPlugin * plugin) return TRUE; } - env = gst_amc_get_jni_env (); + env = gst_amc_jni_get_env (); codec_list_class = (*env)->FindClass (env, "android/media/MediaCodecList"); if (!codec_list_class) { @@ -2108,7 +1671,8 @@ scan_codecs (GstPlugin * plugin) * VM via the non-public AndroidRuntime class. Can we somehow * initialize all this? */ - if (started_java_vm && !g_str_has_prefix (name_str, "OMX.google.")) { + if (gst_amc_jni_is_vm_started () && + !g_str_has_prefix (name_str, "OMX.google.")) { GST_INFO ("Skipping non-Google codec '%s' in standalone mode", name_str); valid_codec = FALSE; goto next_codec; @@ -3709,9 +3273,7 @@ plugin_init (GstPlugin * plugin) GST_DEBUG_CATEGORY_INIT (gst_amc_debug, "amc", 0, "android-media-codec"); - pthread_key_create (¤t_jni_env, gst_amc_detach_current_thread); - - if (!initialize_java_vm ()) + if (!gst_amc_jni_initialize ()) return FALSE; gst_plugin_add_dependency_simple (plugin, NULL, "/etc", "media_codecs.xml", diff --git a/sys/androidmedia/gstjniutils.c b/sys/androidmedia/gstjniutils.c new file mode 100644 index 000000000..9d8a4777f --- /dev/null +++ b/sys/androidmedia/gstjniutils.c @@ -0,0 +1,484 @@ +/* + * 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> + * + * 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 initialized = FALSE; +static gboolean started_java_vm = FALSE; +static pthread_key_t current_jni_env; + +/* 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; + + GST_DEBUG ("Attaching thread %p", g_thread_self ()); + args.version = JNI_VERSION_1_6; + args.name = NULL; + args.group = NULL; + + if ((*java_vm)->AttachCurrentThread (java_vm, &env, &args) < 0) { + GST_ERROR ("Failed to attach current thread"); + return NULL; + } + + return env; +} + +static void +gst_amc_jni_detach_current_thread (void *env) +{ + GST_DEBUG ("Detaching thread %p", g_thread_self ()); + (*java_vm)->DetachCurrentThread (java_vm); +} + +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)) + goto symbol_error; + + 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 +gst_amc_jni_initialize_java_vm (void) +{ + jsize n_vms; + + /* 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 (get_created_java_vms (&java_vm, 1, &n_vms) < 0) + goto get_created_failed; + + if (n_vms > 0) { + GST_DEBUG ("Successfully got existing Java VM %p", java_vm); + } else { + 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 (create_java_vm (&java_vm, &env, &vm_args) < 0) + goto create_failed; + GST_DEBUG ("Successfully created Java VM %p", java_vm); + + started_java_vm = TRUE; + } + + return java_vm != NULL; + +get_created_failed: + { + GST_ERROR ("Failed to get already created VMs"); + g_module_close (java_module); + java_module = NULL; + return FALSE; + } +create_failed: + { + GST_ERROR ("Failed to create a Java VM"); + g_module_close (java_module); + java_module = NULL; + return FALSE; + } +} + +static void +gst_amc_jni_set_error_string (JNIEnv * env, GQuark domain, gint code, + GError ** err, 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, GQuark domain, gint code, + GError ** err, 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, domain, code, err, message); + + g_free (message); +} + + +gboolean +gst_amc_jni_initialize (void) +{ + if (!initialized) { + pthread_key_create (¤t_jni_env, gst_amc_jni_detach_current_thread); + initialized = gst_amc_jni_initialize_java_vm (); + } + return initialized; +} + +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; +} diff --git a/sys/androidmedia/gstjniutils.h b/sys/androidmedia/gstjniutils.h new file mode 100644 index 000000000..ad4e2f424 --- /dev/null +++ b/sys/androidmedia/gstjniutils.h @@ -0,0 +1,45 @@ +/* + * 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> + * + * 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 + * + */ +#ifndef __GST_AMC_JNI_UTILS_H__ +#define __GST_AMC_JNI_UTILS_H__ + +#include <jni.h> +#include <glib.h> +#include <gst/gst.h> + +G_GNUC_PRINTF (5, 6) +void gst_amc_jni_set_error (JNIEnv * env, + GQuark domain, + gint code, + GError ** error, + const gchar * format, ...); + +gboolean gst_amc_jni_initialize (void); + +gboolean gst_amc_jni_is_vm_started (void); + +JNIEnv *gst_amc_jni_get_env (void); + +#endif |