amc: actually use the provided application class loader
[platform/upstream/gst-plugins-bad.git] / sys / androidmedia / gstjniutils.c
1 /*
2  * Copyright (C) 2012, Collabora Ltd.
3  *   Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>
4  * Copyright (C) 2013, Fluendo S.A.
5  *   Author: Andoni Morales <amorales@fluendo.com>
6  * Copyright (C) 2014, Sebastian Dröge <sebastian@centricular.com>
7  * Copyright (C) 2014, Collabora Ltd.
8  *   Author: Matthieu Bouron <matthieu.bouron@collabora.com>
9  * Copyright (C) 2015, Sebastian Dröge <sebastian@centricular.com>
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public
13  * License as published by the Free Software Foundation
14  * version 2.1 of the License.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with this library; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
24  *
25  */
26
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30
31 #include <gst/gst.h>
32 #include <pthread.h>
33 #include <gmodule.h>
34
35 #include "gstjniutils.h"
36
37 static GModule *java_module;
38 static jint (*get_created_java_vms) (JavaVM ** vmBuf, jsize bufLen,
39     jsize * nVMs);
40 static jint (*create_java_vm) (JavaVM ** p_vm, JNIEnv ** p_env, void *vm_args);
41 static JavaVM *java_vm;
42 static gboolean started_java_vm = FALSE;
43 static pthread_key_t current_jni_env;
44 static jobject (*get_class_loader) (void);
45
46 static struct
47 {
48   jclass klass;
49   jmethodID get_limit, get_position;
50   jmethodID set_limit, set_position;
51   jmethodID clear;
52 } java_nio_buffer;
53
54 jclass
55 gst_amc_jni_get_class (JNIEnv * env, GError ** err, const gchar * name)
56 {
57   jclass tmp, ret = NULL;
58
59   GST_DEBUG ("Retrieving Java class %s", name);
60
61   tmp = (*env)->FindClass (env, name);
62   if ((*env)->ExceptionCheck (env) || !tmp) {
63     gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR,
64         GST_LIBRARY_ERROR_FAILED, "Failed to find class %s", name);
65     goto done;
66   }
67
68   ret = (*env)->NewGlobalRef (env, tmp);
69   if (!ret) {
70     GST_ERROR ("Failed to get %s class global reference", name);
71   }
72
73 done:
74   if (tmp)
75     (*env)->DeleteLocalRef (env, tmp);
76   tmp = NULL;
77
78   return ret;
79 }
80
81 jmethodID
82 gst_amc_jni_get_method_id (JNIEnv * env, GError ** err, jclass klass,
83     const gchar * name, const gchar * signature)
84 {
85   jmethodID ret;
86
87   ret = (*env)->GetMethodID (env, klass, name, signature);
88   if ((*env)->ExceptionCheck (env) || !ret) {
89     gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR,
90         GST_LIBRARY_ERROR_FAILED, "Failed to get method ID %s (%s)", name,
91         signature);
92   }
93   return ret;
94 }
95
96 jmethodID
97 gst_amc_jni_get_static_method_id (JNIEnv * env, GError ** err, jclass klass,
98     const gchar * name, const gchar * signature)
99 {
100   jmethodID ret;
101
102   ret = (*env)->GetStaticMethodID (env, klass, name, signature);
103   if ((*env)->ExceptionCheck (env) || !ret) {
104     gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR,
105         GST_LIBRARY_ERROR_FAILED, "Failed to get static method ID %s (%s)",
106         name, signature);
107   }
108   return ret;
109 }
110
111 jfieldID
112 gst_amc_jni_get_field_id (JNIEnv * env, GError ** err, jclass klass,
113     const gchar * name, const gchar * type)
114 {
115   jfieldID ret;
116
117   ret = (*env)->GetFieldID (env, klass, name, type);
118   if ((*env)->ExceptionCheck (env) || !ret) {
119     gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR,
120         GST_LIBRARY_ERROR_FAILED, "Failed to get field ID %s (%s)", name, type);
121   }
122   return ret;
123 }
124
125 jfieldID
126 gst_amc_jni_get_static_field_id (JNIEnv * env, GError ** err, jclass klass,
127     const gchar * name, const gchar * type)
128 {
129   jfieldID ret;
130
131   ret = (*env)->GetStaticFieldID (env, klass, name, type);
132   if ((*env)->ExceptionCheck (env) || !ret) {
133     gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR,
134         GST_LIBRARY_ERROR_FAILED, "Failed to get static field ID %s (%s)", name,
135         type);
136   }
137   return ret;
138 }
139
140 jobject
141 gst_amc_jni_new_object (JNIEnv * env, GError ** err, gboolean global,
142     jclass klass, jmethodID constructor, ...)
143 {
144   jobject tmp;
145   va_list args;
146
147   va_start (args, constructor);
148   tmp = (*env)->NewObjectV (env, klass, constructor, args);
149   va_end (args);
150
151   if ((*env)->ExceptionCheck (env) || !tmp) {
152     gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR,
153         GST_LIBRARY_ERROR_FAILED, "Failed to create object");
154     return NULL;
155   }
156
157   if (global)
158     return gst_amc_jni_object_make_global (env, tmp);
159   else
160     return tmp;
161 }
162
163 jobject
164 gst_amc_jni_new_object_from_static (JNIEnv * env, GError ** err,
165     gboolean global, jclass klass, jmethodID method, ...)
166 {
167   jobject tmp;
168   va_list args;
169
170   va_start (args, method);
171   tmp = (*env)->CallStaticObjectMethodV (env, klass, method, args);
172   va_end (args);
173
174   if ((*env)->ExceptionCheck (env) || !tmp) {
175     gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR,
176         GST_LIBRARY_ERROR_FAILED, "Failed to create object");
177     return NULL;
178   }
179
180   if (global)
181     return gst_amc_jni_object_make_global (env, tmp);
182   else
183     return tmp;
184 }
185
186 jobject
187 gst_amc_jni_object_make_global (JNIEnv * env, jobject object)
188 {
189   jobject ret;
190
191   ret = (*env)->NewGlobalRef (env, object);
192   if (!ret) {
193     GST_ERROR ("Failed to create global reference");
194   }
195   gst_amc_jni_object_local_unref (env, object);
196
197   return ret;
198 }
199
200 jobject
201 gst_amc_jni_object_ref (JNIEnv * env, jobject object)
202 {
203   jobject ret;
204
205   ret = (*env)->NewGlobalRef (env, object);
206   if (!ret) {
207     GST_ERROR ("Failed to create global reference");
208   }
209   return ret;
210 }
211
212 void
213 gst_amc_jni_object_unref (JNIEnv * env, jobject object)
214 {
215   g_return_if_fail (object != NULL);
216
217   (*env)->DeleteGlobalRef (env, object);
218 }
219
220 void
221 gst_amc_jni_object_local_unref (JNIEnv * env, jobject object)
222 {
223   g_return_if_fail (object != NULL);
224
225   (*env)->DeleteLocalRef (env, object);
226 }
227
228 jstring
229 gst_amc_jni_string_from_gchar (JNIEnv * env, GError ** err,
230     gboolean global, const gchar * string)
231 {
232   jstring tmp;
233
234   tmp = (*env)->NewStringUTF (env, string);
235   if ((*env)->ExceptionCheck (env)) {
236     gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR,
237         GST_LIBRARY_ERROR_FAILED, "Failed to call Java method");
238     tmp = NULL;
239   }
240
241   if (global)
242     return gst_amc_jni_object_make_global (env, tmp);
243   else
244     return tmp;
245 }
246
247 gchar *
248 gst_amc_jni_string_to_gchar (JNIEnv * env, jstring string, gboolean release)
249 {
250   const gchar *s = NULL;
251   gchar *ret = NULL;
252
253   s = (*env)->GetStringUTFChars (env, string, NULL);
254   if (!s) {
255     GST_ERROR ("Failed to convert string to UTF8");
256     goto done;
257   }
258
259   ret = g_strdup (s);
260   (*env)->ReleaseStringUTFChars (env, string, s);
261
262 done:
263   if (release) {
264     (*env)->DeleteLocalRef (env, string);
265   }
266   return ret;
267 }
268
269 /* getExceptionSummary() and getStackTrace() taken from Android's
270  *   platform/libnativehelper/JNIHelp.cpp
271  * Modified to work with normal C strings and without C++.
272  *
273  * Copyright (C) 2006 The Android Open Source Project
274  *
275  * Licensed under the Apache License, Version 2.0 (the "License");
276  * you may not use this file except in compliance with the License.
277  * You may obtain a copy of the License at
278  *
279  * http://www.apache.org/licenses/LICENSE-2.0
280  *
281  * Unless required by applicable law or agreed to in writing, software
282  * distributed under the License is distributed on an "AS IS" BASIS,
283  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
284  * See the License for the specific language governing permissions and
285  * limitations under the License.
286  */
287
288 /*
289  * Returns a human-readable summary of an exception object. The buffer will
290  * be populated with the "binary" class name and, if present, the
291  * exception message.
292  */
293 static gchar *
294 getExceptionSummary (JNIEnv * env, jthrowable exception)
295 {
296   GString *gs = g_string_new ("");
297   jclass exceptionClass = NULL, classClass = NULL;
298   jmethodID classGetNameMethod, getMessage;
299   jstring classNameStr = NULL, messageStr = NULL;
300   const char *classNameChars, *messageChars;
301
302   /* get the name of the exception's class */
303   exceptionClass = (*env)->GetObjectClass (env, exception);
304   classClass = (*env)->GetObjectClass (env, exceptionClass);
305   classGetNameMethod =
306       (*env)->GetMethodID (env, classClass, "getName", "()Ljava/lang/String;");
307
308   classNameStr =
309       (jstring) (*env)->CallObjectMethod (env, exceptionClass,
310       classGetNameMethod);
311
312   if (classNameStr == NULL) {
313     if ((*env)->ExceptionCheck (env))
314       (*env)->ExceptionClear (env);
315     g_string_append (gs, "<error getting class name>");
316     goto done;
317   }
318
319   classNameChars = (*env)->GetStringUTFChars (env, classNameStr, NULL);
320   if (classNameChars == NULL) {
321     if ((*env)->ExceptionCheck (env))
322       (*env)->ExceptionClear (env);
323     g_string_append (gs, "<error getting class name UTF-8>");
324     goto done;
325   }
326
327   g_string_append (gs, classNameChars);
328
329   (*env)->ReleaseStringUTFChars (env, classNameStr, classNameChars);
330
331   /* if the exception has a detail message, get that */
332   getMessage =
333       (*env)->GetMethodID (env, exceptionClass, "getMessage",
334       "()Ljava/lang/String;");
335   messageStr = (jstring) (*env)->CallObjectMethod (env, exception, getMessage);
336   if (messageStr == NULL) {
337     if ((*env)->ExceptionCheck (env))
338       (*env)->ExceptionClear (env);
339     goto done;
340   }
341   g_string_append (gs, ": ");
342
343   messageChars = (*env)->GetStringUTFChars (env, messageStr, NULL);
344   if (messageChars != NULL) {
345     g_string_append (gs, messageChars);
346     (*env)->ReleaseStringUTFChars (env, messageStr, messageChars);
347   } else {
348     if ((*env)->ExceptionCheck (env))
349       (*env)->ExceptionClear (env);
350     g_string_append (gs, "<error getting message>");
351   }
352
353 done:
354   if (exceptionClass)
355     (*env)->DeleteLocalRef (env, exceptionClass);
356   if (classClass)
357     (*env)->DeleteLocalRef (env, classClass);
358   if (classNameStr)
359     (*env)->DeleteLocalRef (env, classNameStr);
360   if (messageStr)
361     (*env)->DeleteLocalRef (env, messageStr);
362
363   return g_string_free (gs, FALSE);
364 }
365
366 /*
367  * Returns an exception (with stack trace) as a string.
368  */
369 static gchar *
370 getStackTrace (JNIEnv * env, jthrowable exception)
371 {
372   GString *gs = g_string_new ("");
373   jclass stringWriterClass = NULL, printWriterClass = NULL;
374   jclass exceptionClass = NULL;
375   jmethodID stringWriterCtor, stringWriterToStringMethod;
376   jmethodID printWriterCtor, printStackTraceMethod;
377   jobject stringWriter = NULL, printWriter = NULL;
378   jstring messageStr = NULL;
379   const char *utfChars;
380
381   stringWriterClass = (*env)->FindClass (env, "java/io/StringWriter");
382
383   if (stringWriterClass == NULL) {
384     g_string_append (gs, "<error getting java.io.StringWriter class>");
385     goto done;
386   }
387
388   stringWriterCtor =
389       (*env)->GetMethodID (env, stringWriterClass, "<init>", "()V");
390   stringWriterToStringMethod =
391       (*env)->GetMethodID (env, stringWriterClass, "toString",
392       "()Ljava/lang/String;");
393
394   printWriterClass = (*env)->FindClass (env, "java/io/PrintWriter");
395   if (printWriterClass == NULL) {
396     g_string_append (gs, "<error getting java.io.PrintWriter class>");
397     goto done;
398   }
399
400   printWriterCtor =
401       (*env)->GetMethodID (env, printWriterClass, "<init>",
402       "(Ljava/io/Writer;)V");
403   stringWriter = (*env)->NewObject (env, stringWriterClass, stringWriterCtor);
404   if (stringWriter == NULL) {
405     if ((*env)->ExceptionCheck (env))
406       (*env)->ExceptionClear (env);
407     g_string_append (gs, "<error creating new StringWriter instance>");
408     goto done;
409   }
410
411   printWriter =
412       (*env)->NewObject (env, printWriterClass, printWriterCtor, stringWriter);
413   if (printWriter == NULL) {
414     if ((*env)->ExceptionCheck (env))
415       (*env)->ExceptionClear (env);
416     g_string_append (gs, "<error creating new PrintWriter instance>");
417     goto done;
418   }
419
420   exceptionClass = (*env)->GetObjectClass (env, exception);
421   printStackTraceMethod =
422       (*env)->GetMethodID (env, exceptionClass, "printStackTrace",
423       "(Ljava/io/PrintWriter;)V");
424   (*env)->CallVoidMethod (env, exception, printStackTraceMethod, printWriter);
425   if ((*env)->ExceptionCheck (env)) {
426     (*env)->ExceptionClear (env);
427     g_string_append (gs, "<exception while printing stack trace>");
428     goto done;
429   }
430
431   messageStr = (jstring) (*env)->CallObjectMethod (env, stringWriter,
432       stringWriterToStringMethod);
433   if (messageStr == NULL) {
434     if ((*env)->ExceptionCheck (env))
435       (*env)->ExceptionClear (env);
436     g_string_append (gs, "<failed to call StringWriter.toString()>");
437     goto done;
438   }
439
440   utfChars = (*env)->GetStringUTFChars (env, messageStr, NULL);
441   if (utfChars == NULL) {
442     if ((*env)->ExceptionCheck (env))
443       (*env)->ExceptionClear (env);
444     g_string_append (gs, "<failed to get UTF chars for message>");
445     goto done;
446   }
447
448   g_string_append (gs, utfChars);
449
450   (*env)->ReleaseStringUTFChars (env, messageStr, utfChars);
451
452 done:
453   if (stringWriterClass)
454     (*env)->DeleteLocalRef (env, stringWriterClass);
455   if (printWriterClass)
456     (*env)->DeleteLocalRef (env, printWriterClass);
457   if (exceptionClass)
458     (*env)->DeleteLocalRef (env, exceptionClass);
459   if (stringWriter)
460     (*env)->DeleteLocalRef (env, stringWriter);
461   if (printWriter)
462     (*env)->DeleteLocalRef (env, printWriter);
463   if (messageStr)
464     (*env)->DeleteLocalRef (env, messageStr);
465
466   return g_string_free (gs, FALSE);
467 }
468
469 static JNIEnv *
470 gst_amc_jni_attach_current_thread (void)
471 {
472   JNIEnv *env;
473   JavaVMAttachArgs args;
474   gint ret;
475
476   GST_DEBUG ("Attaching thread %p", g_thread_self ());
477   args.version = JNI_VERSION_1_6;
478   args.name = NULL;
479   args.group = NULL;
480
481   if ((ret = (*java_vm)->AttachCurrentThread (java_vm, &env, &args)) != JNI_OK) {
482     GST_ERROR ("Failed to attach current thread: %d", ret);
483     return NULL;
484   }
485
486   return env;
487 }
488
489 static void
490 gst_amc_jni_detach_current_thread (void *env)
491 {
492   gint ret;
493
494   GST_DEBUG ("Detaching thread %p", g_thread_self ());
495   if ((ret = (*java_vm)->DetachCurrentThread (java_vm)) != JNI_OK) {
496     GST_DEBUG ("Failed to detach current thread: %d", ret);
497   }
498 }
499
500 static JavaVM *
501 get_application_java_vm (void)
502 {
503   GModule *module = NULL;
504   JavaVM *(*get_java_vm) (void);
505   JavaVM *vm = NULL;
506
507   module = g_module_open (NULL, G_MODULE_BIND_LOCAL);
508
509   if (!module) {
510     return NULL;
511   }
512
513   if (g_module_symbol (module, "gst_android_get_java_vm",
514           (gpointer *) & get_java_vm) && get_java_vm) {
515     vm = get_java_vm ();
516   }
517
518   g_module_close (module);
519
520   return vm;
521 }
522
523 static gboolean
524 check_nativehelper (void)
525 {
526   GModule *module;
527   void **jni_invocation = NULL;
528   gboolean ret = FALSE;
529
530   module = g_module_open (NULL, G_MODULE_BIND_LOCAL);
531   if (!module)
532     return ret;
533
534   /* Check if libnativehelper is loaded in the process and if
535    * it has these awful wrappers for JNI_CreateJavaVM and
536    * JNI_GetCreatedJavaVMs that crash the app if you don't
537    * create a JniInvocation instance first. If it isn't we
538    * just fail here and don't initialize anything.
539    * See this code for reference:
540    * https://android.googlesource.com/platform/libnativehelper/+/master/JniInvocation.cpp
541    */
542   if (!g_module_symbol (module, "_ZN13JniInvocation15jni_invocation_E",
543           (gpointer *) & jni_invocation)) {
544     ret = TRUE;
545   } else {
546     ret = (jni_invocation != NULL && *jni_invocation != NULL);
547   }
548
549   g_module_close (module);
550
551   return ret;
552 }
553
554 static gboolean
555 load_java_module (const gchar * name)
556 {
557   java_module = g_module_open (name, G_MODULE_BIND_LOCAL);
558   if (!java_module)
559     goto load_failed;
560
561   if (!g_module_symbol (java_module, "JNI_CreateJavaVM",
562           (gpointer *) & create_java_vm)) {
563     GST_ERROR ("Could not find 'JNI_CreateJavaVM' in '%s': %s",
564         GST_STR_NULL (name), g_module_error ());
565     create_java_vm = NULL;
566   }
567
568   if (!g_module_symbol (java_module, "JNI_GetCreatedJavaVMs",
569           (gpointer *) & get_created_java_vms))
570     goto symbol_error;
571
572   return TRUE;
573
574 load_failed:
575   {
576     GST_ERROR ("Failed to load Java module '%s': %s", GST_STR_NULL (name),
577         g_module_error ());
578     return FALSE;
579   }
580 symbol_error:
581   {
582     GST_ERROR ("Failed to locate required JNI symbols in '%s': %s",
583         GST_STR_NULL (name), g_module_error ());
584     g_module_close (java_module);
585     java_module = NULL;
586     return FALSE;
587   }
588 }
589
590 static gboolean
591 check_application_class_loader (void)
592 {
593   gboolean ret = TRUE;
594   GModule *module = NULL;
595
596   module = g_module_open (NULL, G_MODULE_BIND_LOCAL);
597   if (!module) {
598     return FALSE;
599   }
600   if (!g_module_symbol (module, "gst_android_get_application_class_loader",
601           (gpointer *) & get_class_loader)) {
602     ret = FALSE;
603   }
604
605   g_module_close (module);
606
607   return ret;
608 }
609
610 static gboolean
611 initialize_classes (void)
612 {
613   JNIEnv *env;
614   GError *err = NULL;
615
616   env = gst_amc_jni_get_env ();
617
618   java_nio_buffer.klass = gst_amc_jni_get_class (env, &err, "java/nio/Buffer");
619   if (!java_nio_buffer.klass) {
620     GST_ERROR ("Failed to get java.nio.Buffer class: %s", err->message);
621     g_clear_error (&err);
622     return FALSE;
623   }
624
625   java_nio_buffer.get_limit =
626       gst_amc_jni_get_method_id (env, &err, java_nio_buffer.klass, "limit",
627       "()I");
628   if (!java_nio_buffer.get_limit) {
629     GST_ERROR ("Failed to get java.nio.Buffer limit(): %s", err->message);
630     g_clear_error (&err);
631     return FALSE;
632   }
633
634   java_nio_buffer.get_position =
635       gst_amc_jni_get_method_id (env, &err, java_nio_buffer.klass, "position",
636       "()I");
637   if (!java_nio_buffer.get_position) {
638     GST_ERROR ("Failed to get java.nio.Buffer position(): %s", err->message);
639     g_clear_error (&err);
640     return FALSE;
641   }
642
643   java_nio_buffer.set_limit =
644       gst_amc_jni_get_method_id (env, &err, java_nio_buffer.klass, "limit",
645       "(I)Ljava/nio/Buffer;");
646   if (!java_nio_buffer.set_limit) {
647     GST_ERROR ("Failed to get java.nio.Buffer limit(): %s", err->message);
648     g_clear_error (&err);
649     return FALSE;
650   }
651
652   java_nio_buffer.set_position =
653       gst_amc_jni_get_method_id (env, &err, java_nio_buffer.klass, "position",
654       "(I)Ljava/nio/Buffer;");
655   if (!java_nio_buffer.set_position) {
656     GST_ERROR ("Failed to get java.nio.Buffer position(): %s", err->message);
657     g_clear_error (&err);
658     return FALSE;
659   }
660
661   java_nio_buffer.clear =
662       gst_amc_jni_get_method_id (env, &err, java_nio_buffer.klass, "clear",
663       "()Ljava/nio/Buffer;");
664   if (!java_nio_buffer.clear) {
665     GST_ERROR ("Failed to get java.nio.Buffer clear(): %s", err->message);
666     g_clear_error (&err);
667     return FALSE;
668   }
669
670   if (!check_application_class_loader ()) {
671     GST_ERROR ("Could not find application class loader provider");
672     return FALSE;
673   }
674
675   return TRUE;
676 }
677
678 static gboolean
679 gst_amc_jni_initialize_java_vm (void)
680 {
681   jsize n_vms;
682   gint ret;
683
684   if (java_vm) {
685     GST_DEBUG ("Java VM already provided by the application");
686     return initialize_classes ();
687   }
688
689   java_vm = get_application_java_vm ();
690   if (java_vm) {
691     GST_DEBUG ("Java VM successfully requested from the application");
692     return initialize_classes ();
693   }
694
695   /* Returns TRUE if we can safely
696    * a) get the current VMs and
697    * b) start a VM if none is started yet
698    *
699    * FIXME: On Android >= 4.4 we won't be able to safely start a
700    * VM on our own without using private C++ API!
701    */
702   if (!check_nativehelper ()) {
703     GST_ERROR ("Can't safely check for VMs or start a VM");
704     return FALSE;
705   }
706
707   if (!load_java_module (NULL)) {
708     if (!load_java_module ("libdvm"))
709       return FALSE;
710   }
711
712   n_vms = 0;
713   if ((ret = get_created_java_vms (&java_vm, 1, &n_vms)) != JNI_OK)
714     goto get_created_failed;
715
716   if (n_vms > 0) {
717     GST_DEBUG ("Successfully got existing Java VM %p", java_vm);
718   } else if (create_java_vm) {
719     JNIEnv *env;
720     JavaVMInitArgs vm_args;
721     JavaVMOption options[4];
722
723     GST_DEBUG ("Found no existing Java VM, trying to start one");
724
725     options[0].optionString = "-verbose:jni";
726     options[1].optionString = "-verbose:gc";
727     options[2].optionString = "-Xcheck:jni";
728     options[3].optionString = "-Xdebug";
729
730     vm_args.version = JNI_VERSION_1_4;
731     vm_args.options = options;
732     vm_args.nOptions = 4;
733     vm_args.ignoreUnrecognized = JNI_TRUE;
734     if ((ret = create_java_vm (&java_vm, &env, &vm_args)) != JNI_OK)
735       goto create_failed;
736     GST_DEBUG ("Successfully created Java VM %p", java_vm);
737
738     started_java_vm = TRUE;
739   } else {
740     GST_ERROR ("JNI_CreateJavaVM not available");
741     java_vm = NULL;
742   }
743
744   if (java_vm == NULL)
745     return FALSE;
746
747   return initialize_classes ();
748
749 get_created_failed:
750   {
751     GST_ERROR ("Failed to get already created VMs: %d", ret);
752     g_module_close (java_module);
753     java_module = NULL;
754     return FALSE;
755   }
756 create_failed:
757   {
758     GST_ERROR ("Failed to create a Java VM: %d", ret);
759     g_module_close (java_module);
760     java_module = NULL;
761     return FALSE;
762   }
763 }
764
765 static void
766 gst_amc_jni_set_error_string (JNIEnv * env, GError ** err, GQuark domain,
767     gint code, const gchar * message)
768 {
769   jthrowable exception;
770
771   if (!err) {
772     if ((*env)->ExceptionCheck (env))
773       (*env)->ExceptionClear (env);
774     return;
775   }
776
777   if ((*env)->ExceptionCheck (env)) {
778     if ((exception = (*env)->ExceptionOccurred (env))) {
779       gchar *exception_description, *exception_stacktrace;
780
781       /* Clear exception so that we can call Java methods again */
782       (*env)->ExceptionClear (env);
783
784       exception_description = getExceptionSummary (env, exception);
785       exception_stacktrace = getStackTrace (env, exception);
786       g_set_error (err, domain, code, "%s: %s\n%s", message,
787           exception_description, exception_stacktrace);
788       g_free (exception_description);
789       g_free (exception_stacktrace);
790
791       (*env)->DeleteLocalRef (env, exception);
792     } else {
793       (*env)->ExceptionClear (env);
794       g_set_error (err, domain, code, "%s", message);
795     }
796   } else {
797     g_set_error (err, domain, code, "%s", message);
798   }
799 }
800
801 G_GNUC_PRINTF (5, 6)
802      void gst_amc_jni_set_error (JNIEnv * env, GError ** err, GQuark domain,
803     gint code, const gchar * format, ...)
804 {
805   gchar *message;
806   va_list var_args;
807
808   va_start (var_args, format);
809   message = g_strdup_vprintf (format, var_args);
810   va_end (var_args);
811
812   gst_amc_jni_set_error_string (env, err, domain, code, message);
813
814   g_free (message);
815 }
816
817 static gpointer
818 gst_amc_jni_initialize_internal (gpointer data)
819 {
820   pthread_key_create (&current_jni_env, gst_amc_jni_detach_current_thread);
821   return gst_amc_jni_initialize_java_vm ()? GINT_TO_POINTER (1) : NULL;
822 }
823
824 /* Allow the application to set the Java VM */
825 void
826 gst_amc_jni_set_java_vm (JavaVM * vm)
827 {
828   GST_DEBUG ("Application provides Java VM %p", vm);
829   java_vm = vm;
830 }
831
832 gboolean
833 gst_amc_jni_initialize (void)
834 {
835   GOnce once = G_ONCE_INIT;
836
837   g_once (&once, gst_amc_jni_initialize_internal, NULL);
838   return once.retval != NULL;
839 }
840
841 JNIEnv *
842 gst_amc_jni_get_env (void)
843 {
844   JNIEnv *env;
845
846   if ((env = pthread_getspecific (current_jni_env)) == NULL) {
847     env = gst_amc_jni_attach_current_thread ();
848     pthread_setspecific (current_jni_env, env);
849   }
850
851   return env;
852 }
853
854 gboolean
855 gst_amc_jni_is_vm_started (void)
856 {
857   return started_java_vm;
858 }
859
860 jclass
861 gst_amc_jni_get_application_class (JNIEnv * env, const gchar * name,
862     GError ** err)
863 {
864   jclass tmp = NULL;
865   jclass class = NULL;
866   jstring name_jstr = NULL;
867
868   jobject class_loader = NULL;
869   jclass class_loader_cls = NULL;
870   jmethodID load_class_id = 0;
871
872   GST_LOG ("attempting to retrieve class %s", name);
873
874   if (!get_class_loader) {
875     g_set_error (err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED,
876         "Could not retreive application class loader function");
877     goto done;
878   }
879
880   class_loader = get_class_loader ();
881   if (!class_loader) {
882     g_set_error (err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED,
883         "Could not retreive application class loader");
884     goto done;
885   }
886
887   class_loader_cls = (*env)->GetObjectClass (env, class_loader);
888   if (!class_loader_cls) {
889     g_set_error (err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED,
890         "Could not retreive application class loader java class");
891     goto done;
892   }
893
894   load_class_id =
895       gst_amc_jni_get_method_id (env, err, class_loader_cls, "loadClass",
896       "(Ljava/lang/String;)Ljava/lang/Class;");
897   if (!load_class_id) {
898     goto done;
899   }
900
901   name_jstr = gst_amc_jni_string_from_gchar (env, err, FALSE, name);
902   if (!name_jstr) {
903     goto done;
904   }
905
906   if (gst_amc_jni_call_object_method (env, err, class_loader,
907           load_class_id, &tmp, name_jstr)) {
908     class = gst_amc_jni_object_make_global (env, tmp);
909   }
910
911 done:
912   gst_amc_jni_object_local_unref (env, name_jstr);
913   gst_amc_jni_object_local_unref (env, class_loader_cls);
914
915   return class;
916 }
917
918 #define CALL_STATIC_TYPE_METHOD(_type, _name,  _jname)                                                     \
919 gboolean gst_amc_jni_call_static_##_name##_method (JNIEnv *env, GError ** err, jclass klass, jmethodID methodID, _type * value, ...)   \
920   {                                                                                                          \
921     gboolean ret = TRUE;                                                                                     \
922     va_list args;                                                                                            \
923     va_start(args, value);                                                                                \
924     *value = (*env)->CallStatic##_jname##MethodV(env, klass, methodID, args);                                \
925     if ((*env)->ExceptionCheck (env)) {                                                                      \
926       gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED,                               \
927           "Failed to call static Java method");                                                         \
928       ret = FALSE;                                                                                           \
929     }                                                                                                        \
930     va_end(args);                                                                                            \
931     return ret;                                                                                              \
932   }
933
934 CALL_STATIC_TYPE_METHOD (gboolean, boolean, Boolean);
935 CALL_STATIC_TYPE_METHOD (gint8, byte, Byte);
936 CALL_STATIC_TYPE_METHOD (gshort, short, Short);
937 CALL_STATIC_TYPE_METHOD (gint, int, Int);
938 CALL_STATIC_TYPE_METHOD (gchar, char, Char);
939 CALL_STATIC_TYPE_METHOD (gint64, long, Long);
940 CALL_STATIC_TYPE_METHOD (gfloat, float, Float);
941 CALL_STATIC_TYPE_METHOD (gdouble, double, Double);
942 CALL_STATIC_TYPE_METHOD (jobject, object, Object);
943
944 gboolean
945 gst_amc_jni_call_static_void_method (JNIEnv * env, GError ** err, jclass klass,
946     jmethodID methodID, ...)
947 {
948   gboolean ret = TRUE;
949   va_list args;
950   va_start (args, methodID);
951
952   (*env)->CallStaticVoidMethodV (env, klass, methodID, args);
953   if ((*env)->ExceptionCheck (env)) {
954     gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR,
955         GST_LIBRARY_ERROR_FAILED, "Failed to call static Java method");
956     ret = FALSE;
957   }
958   va_end (args);
959   return ret;
960 }
961
962 #define CALL_TYPE_METHOD(_type, _name,  _jname)                                                              \
963 gboolean gst_amc_jni_call_##_name##_method (JNIEnv *env, GError ** err, jobject obj, jmethodID methodID, _type *value, ...)   \
964   {                                                                                                          \
965     gboolean ret = TRUE;                                                                                     \
966     va_list args;                                                                                            \
967     va_start(args, value);                                                                                \
968     *value = (*env)->Call##_jname##MethodV(env, obj, methodID, args);                                        \
969     if ((*env)->ExceptionCheck (env)) {                                                                      \
970       gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED,                               \
971           "Failed to call Java method");                                                                \
972       ret = FALSE;                                                                                           \
973     }                                                                                                        \
974     va_end(args);                                                                                            \
975     return ret;                                                                                              \
976   }
977
978 CALL_TYPE_METHOD (gboolean, boolean, Boolean);
979 CALL_TYPE_METHOD (gint8, byte, Byte);
980 CALL_TYPE_METHOD (gshort, short, Short);
981 CALL_TYPE_METHOD (gint, int, Int);
982 CALL_TYPE_METHOD (gchar, char, Char);
983 CALL_TYPE_METHOD (gint64, long, Long);
984 CALL_TYPE_METHOD (gfloat, float, Float);
985 CALL_TYPE_METHOD (gdouble, double, Double);
986 CALL_TYPE_METHOD (jobject, object, Object);
987
988 gboolean
989 gst_amc_jni_call_void_method (JNIEnv * env, GError ** err, jobject obj,
990     jmethodID methodID, ...)
991 {
992   gboolean ret = TRUE;
993   va_list args;
994   va_start (args, methodID);
995
996   (*env)->CallVoidMethodV (env, obj, methodID, args);
997   if ((*env)->ExceptionCheck (env)) {
998     gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR,
999         GST_LIBRARY_ERROR_FAILED, "Failed to call Java method");
1000     ret = FALSE;
1001   }
1002   va_end (args);
1003   return ret;
1004 }
1005
1006 #define GET_TYPE_FIELD(_type, _name, _jname)                                               \
1007 gboolean gst_amc_jni_get_##_name##_field (JNIEnv *env, GError ** err, jobject obj, jfieldID fieldID, _type *value)   \
1008   {                                                                                                 \
1009     gboolean ret = TRUE;                                                                            \
1010                                                                                                     \
1011     *value = (*env)->Get##_jname##Field(env, obj, fieldID);                                         \
1012     if ((*env)->ExceptionCheck (env)) {                                                             \
1013       gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED,                      \
1014           "Failed to get Java field");                                                         \
1015       ret = FALSE;                                                                                  \
1016     }                                                                                               \
1017     return ret;                                                                                     \
1018   }
1019
1020 GET_TYPE_FIELD (gboolean, boolean, Boolean);
1021 GET_TYPE_FIELD (gint8, byte, Byte);
1022 GET_TYPE_FIELD (gshort, short, Short);
1023 GET_TYPE_FIELD (gint, int, Int);
1024 GET_TYPE_FIELD (gchar, char, Char);
1025 GET_TYPE_FIELD (gint64, long, Long);
1026 GET_TYPE_FIELD (gfloat, float, Float);
1027 GET_TYPE_FIELD (gdouble, double, Double);
1028 GET_TYPE_FIELD (jobject, object, Object);
1029
1030 #define GET_STATIC_TYPE_FIELD(_type, _name, _jname)                                               \
1031 gboolean gst_amc_jni_get_static_##_name##_field (JNIEnv *env, GError ** err, jclass klass, jfieldID fieldID, _type *value)   \
1032   {                                                                                                 \
1033     gboolean ret = TRUE;                                                                            \
1034                                                                                                     \
1035     *value = (*env)->GetStatic##_jname##Field(env, klass, fieldID);                                 \
1036     if ((*env)->ExceptionCheck (env)) {                                                             \
1037       gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED,                      \
1038           "Failed to get static Java field");                                                  \
1039       ret = FALSE;                                                                                  \
1040     }                                                                                               \
1041     return ret;                                                                                     \
1042   }
1043
1044 GET_STATIC_TYPE_FIELD (gboolean, boolean, Boolean);
1045 GET_STATIC_TYPE_FIELD (gint8, byte, Byte);
1046 GET_STATIC_TYPE_FIELD (gshort, short, Short);
1047 GET_STATIC_TYPE_FIELD (gint, int, Int);
1048 GET_STATIC_TYPE_FIELD (gchar, char, Char);
1049 GET_STATIC_TYPE_FIELD (gint64, long, Long);
1050 GET_STATIC_TYPE_FIELD (gfloat, float, Float);
1051 GET_STATIC_TYPE_FIELD (gdouble, double, Double);
1052 GET_STATIC_TYPE_FIELD (jobject, object, Object);
1053
1054 gboolean
1055 gst_amc_jni_get_buffer_array (JNIEnv * env, GError ** err, jobject array,
1056     GstAmcBuffer ** buffers, gsize * n_buffers)
1057 {
1058   jsize i;
1059
1060   *n_buffers = (*env)->GetArrayLength (env, array);
1061   *buffers = g_new0 (GstAmcBuffer, *n_buffers);
1062
1063   for (i = 0; i < *n_buffers; i++) {
1064     jobject buffer = NULL;
1065
1066     buffer = (*env)->GetObjectArrayElement (env, array, i);
1067     if ((*env)->ExceptionCheck (env)) {
1068       gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR,
1069           GST_LIBRARY_ERROR_FAILED, "Failed to get buffer %d", i);
1070       goto error;
1071     }
1072
1073     /* NULL buffers are not a problem and are happening when we configured
1074      * a surface as input/output */
1075     if (!buffer)
1076       continue;
1077
1078     (*buffers)[i].object = gst_amc_jni_object_make_global (env, buffer);
1079     if (!(*buffers)[i].object) {
1080       gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR,
1081           GST_LIBRARY_ERROR_FAILED,
1082           "Failed to create global buffer reference %d", i);
1083       goto error;
1084     }
1085
1086     (*buffers)[i].data =
1087         (*env)->GetDirectBufferAddress (env, (*buffers)[i].object);
1088     if (!(*buffers)[i].data) {
1089       gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR,
1090           GST_LIBRARY_ERROR_FAILED, "Failed to get buffer address %d", i);
1091       goto error;
1092     }
1093     (*buffers)[i].size =
1094         (*env)->GetDirectBufferCapacity (env, (*buffers)[i].object);
1095   }
1096
1097   return TRUE;
1098
1099 error:
1100   if (*buffers)
1101     gst_amc_jni_free_buffer_array (env, *buffers, *n_buffers);
1102   *buffers = NULL;
1103   *n_buffers = 0;
1104   return FALSE;
1105 }
1106
1107 void
1108 gst_amc_jni_free_buffer_array (JNIEnv * env, GstAmcBuffer * buffers,
1109     gsize n_buffers)
1110 {
1111   jsize i;
1112
1113   g_return_if_fail (buffers != NULL);
1114
1115   for (i = 0; i < n_buffers; i++) {
1116     if (buffers[i].object)
1117       gst_amc_jni_object_unref (env, buffers[i].object);
1118   }
1119   g_free (buffers);
1120 }
1121
1122 void
1123 gst_amc_buffer_free (GstAmcBuffer * buffer)
1124 {
1125   JNIEnv *env;
1126
1127   g_return_if_fail (buffer != NULL);
1128
1129   env = gst_amc_jni_get_env ();
1130
1131   if (buffer->object)
1132     gst_amc_jni_object_unref (env, buffer->object);
1133   g_free (buffer);
1134 }
1135
1136 GstAmcBuffer *
1137 gst_amc_buffer_copy (GstAmcBuffer * buffer)
1138 {
1139   JNIEnv *env;
1140   GstAmcBuffer *ret;
1141
1142   g_return_val_if_fail (buffer != NULL, NULL);
1143
1144   env = gst_amc_jni_get_env ();
1145
1146   ret = g_new0 (GstAmcBuffer, 1);
1147
1148   ret->object = gst_amc_jni_object_ref (env, buffer->object);
1149   ret->data = buffer->data;
1150   ret->size = buffer->size;
1151
1152   return ret;
1153 }
1154
1155 gboolean
1156 gst_amc_buffer_get_position_and_limit (GstAmcBuffer * buffer, GError ** err,
1157     gint * position, gint * limit)
1158 {
1159   JNIEnv *env;
1160
1161   g_return_val_if_fail (buffer != NULL, FALSE);
1162   g_return_val_if_fail (buffer->object != NULL, FALSE);
1163
1164   env = gst_amc_jni_get_env ();
1165
1166   if (!gst_amc_jni_call_int_method (env, err, buffer->object,
1167           java_nio_buffer.get_position, position))
1168     return FALSE;
1169
1170   if (!gst_amc_jni_call_int_method (env, err, buffer->object,
1171           java_nio_buffer.get_limit, limit))
1172     return FALSE;
1173
1174   return TRUE;
1175 }
1176
1177 gboolean
1178 gst_amc_buffer_set_position_and_limit (GstAmcBuffer * buffer, GError ** err,
1179     gint position, gint limit)
1180 {
1181   JNIEnv *env;
1182   jobject tmp;
1183
1184   g_return_val_if_fail (buffer != NULL, FALSE);
1185   g_return_val_if_fail (buffer->object != NULL, FALSE);
1186
1187   env = gst_amc_jni_get_env ();
1188
1189   if (!gst_amc_jni_call_object_method (env, err, buffer->object,
1190           java_nio_buffer.set_limit, &tmp, limit))
1191     return FALSE;
1192
1193   gst_amc_jni_object_local_unref (env, tmp);
1194
1195   if (!gst_amc_jni_call_object_method (env, err, buffer->object,
1196           java_nio_buffer.set_position, &tmp, position))
1197     return FALSE;
1198
1199   gst_amc_jni_object_local_unref (env, tmp);
1200
1201   return TRUE;
1202 }
1203
1204 gboolean
1205 gst_amc_buffer_clear (GstAmcBuffer * buffer, GError ** err)
1206 {
1207   JNIEnv *env;
1208   jobject tmp;
1209
1210   g_return_val_if_fail (buffer != NULL, FALSE);
1211   g_return_val_if_fail (buffer->object != NULL, FALSE);
1212
1213   env = gst_amc_jni_get_env ();
1214
1215   if (!gst_amc_jni_call_object_method (env, err, buffer->object,
1216           java_nio_buffer.clear, &tmp))
1217     return FALSE;
1218
1219   gst_amc_jni_object_local_unref (env, tmp);
1220
1221   return TRUE;
1222 }