gstreamer/gst/gstconfig.h.in: Add support for LoongArch
[platform/upstream/gstreamer.git] / subprojects / gstreamer / gst / gstandroid.c
1 /* This library is free software; you can redistribute it and/or
2  * modify it under the terms of the GNU Library General Public
3  * License as published by the Free Software Foundation; either
4  * version 2 of the License, or (at your option) any later version.
5  *
6  * This library is distributed in the hope that it will be useful,
7  * but WITHOUT ANY WARRANTY; without even the implied warranty of
8  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
9  * Library General Public License for more details.
10  *
11  * You should have received a copy of the GNU Library General Public
12  * License along with this library; if not, write to the
13  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
14  * Boston, MA 02110-1301, USA.
15  */
16
17 #ifdef HAVE_CONFIG_H
18 #include "config.h"
19 #endif
20
21 #include "gst_private.h"
22 #include "gst.h"
23
24 #include <jni.h>
25 #include <android/log.h>
26 #include <string.h>
27
28 /* AMC plugin uses g_module_symbol() to find those symbols */
29 GST_API jobject gst_android_get_application_context (void);
30 GST_API jobject gst_android_get_application_class_loader (void);
31 GST_API JavaVM *gst_android_get_java_vm (void);
32
33 /* XXX: Workaround for Android <21 making signal() an inline function
34  * around bsd_signal(), and Android >= 21 not having any bsd_signal()
35  * symbol but only signal().
36  * See https://bugzilla.gnome.org/show_bug.cgi?id=766235
37  */
38 static gpointer
39 load_real_signal (gpointer data)
40 {
41   GModule *module;
42   gpointer ret = NULL;
43
44   module = g_module_open ("libc.so", G_MODULE_BIND_LOCAL);
45   g_module_symbol (module, "signal", &ret);
46
47   /* As fallback, let's try bsd_signal */
48   if (ret == NULL) {
49     g_warning ("Can't find signal(3) in libc.so!");
50     g_module_symbol (module, "bsd_signal", &ret);
51   }
52
53   g_module_close (module);
54
55   return ret;
56 }
57
58 __sighandler_t bsd_signal (int signum, __sighandler_t handler)
59     __attribute__ ((weak));
60 __sighandler_t
61 bsd_signal (int signum, __sighandler_t handler)
62 {
63   static GOnce gonce = G_ONCE_INIT;
64   __sighandler_t (*real_signal) (int signum, __sighandler_t handler);
65
66   g_once (&gonce, load_real_signal, NULL);
67
68   real_signal = gonce.retval;
69   g_assert (real_signal != NULL);
70
71   return real_signal (signum, handler);
72 }
73
74 static jobject _context = NULL;
75 static jobject _class_loader = NULL;
76 static JavaVM *_java_vm = NULL;
77 static GstClockTime _priv_gst_info_start_time;
78
79 static void
80 glib_print_handler (const gchar * string)
81 {
82   __android_log_print (ANDROID_LOG_INFO, "GLib+stdout", "%s", string);
83 }
84
85 static void
86 glib_printerr_handler (const gchar * string)
87 {
88   __android_log_print (ANDROID_LOG_ERROR, "GLib+stderr", "%s", string);
89 }
90
91
92 /* Based on GLib's default handler */
93 #define CHAR_IS_SAFE(wc) (!((wc < 0x20 && wc != '\t' && wc != '\n' && wc != '\r') || \
94                             (wc == 0x7f) || \
95                             (wc >= 0x80 && wc < 0xa0)))
96 #define FORMAT_UNSIGNED_BUFSIZE ((GLIB_SIZEOF_LONG * 3) + 3)
97 #define STRING_BUFFER_SIZE      (FORMAT_UNSIGNED_BUFSIZE + 32)
98 #define ALERT_LEVELS            (G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING)
99 #define DEFAULT_LEVELS (G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING | G_LOG_LEVEL_MESSAGE)
100 #define INFO_LEVELS (G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG)
101
102 static void
103 escape_string (GString * string)
104 {
105   const char *p = string->str;
106   gunichar wc;
107
108   while (p < string->str + string->len) {
109     gboolean safe;
110
111     wc = g_utf8_get_char_validated (p, -1);
112     if (wc == (gunichar) - 1 || wc == (gunichar) - 2) {
113       gchar *tmp;
114       guint pos;
115
116       pos = p - string->str;
117
118       /* Emit invalid UTF-8 as hex escapes
119        */
120       tmp = g_strdup_printf ("\\x%02x", (guint) (guchar) * p);
121       g_string_erase (string, pos, 1);
122       g_string_insert (string, pos, tmp);
123
124       p = string->str + (pos + 4);      /* Skip over escape sequence */
125
126       g_free (tmp);
127       continue;
128     }
129     if (wc == '\r') {
130       safe = *(p + 1) == '\n';
131     } else {
132       safe = CHAR_IS_SAFE (wc);
133     }
134
135     if (!safe) {
136       gchar *tmp;
137       guint pos;
138
139       pos = p - string->str;
140
141       /* Largest char we escape is 0x0a, so we don't have to worry
142        * about 8-digit \Uxxxxyyyy
143        */
144       tmp = g_strdup_printf ("\\u%04x", wc);
145       g_string_erase (string, pos, g_utf8_next_char (p) - p);
146       g_string_insert (string, pos, tmp);
147       g_free (tmp);
148
149       p = string->str + (pos + 6);      /* Skip over escape sequence */
150     } else
151       p = g_utf8_next_char (p);
152   }
153 }
154
155 static void
156 glib_log_handler (const gchar * log_domain, GLogLevelFlags log_level,
157     const gchar * message, gpointer user_data)
158 {
159   gchar *string;
160   GString *gstring;
161   const gchar *domains;
162   gint android_log_level;
163   gchar *tag;
164
165   if ((log_level & DEFAULT_LEVELS) || (log_level >> G_LOG_LEVEL_USER_SHIFT))
166     goto emit;
167
168   domains = g_getenv ("G_MESSAGES_DEBUG");
169   if (((log_level & INFO_LEVELS) == 0) ||
170       domains == NULL ||
171       (strcmp (domains, "all") != 0 && (!log_domain
172               || !strstr (domains, log_domain))))
173     return;
174
175 emit:
176
177   if (log_domain)
178     tag = g_strdup_printf ("GLib+%s", log_domain);
179   else
180     tag = g_strdup ("GLib");
181
182   switch (log_level & G_LOG_LEVEL_MASK) {
183     case G_LOG_LEVEL_ERROR:
184     case G_LOG_LEVEL_CRITICAL:
185       android_log_level = ANDROID_LOG_ERROR;
186       break;
187     case G_LOG_LEVEL_WARNING:
188       android_log_level = ANDROID_LOG_WARN;
189       break;
190     case G_LOG_LEVEL_MESSAGE:
191     case G_LOG_LEVEL_INFO:
192       android_log_level = ANDROID_LOG_INFO;
193       break;
194     case G_LOG_LEVEL_DEBUG:
195       android_log_level = ANDROID_LOG_DEBUG;
196       break;
197     default:
198       android_log_level = ANDROID_LOG_VERBOSE;
199       break;
200   }
201
202   gstring = g_string_new (NULL);
203   if (!message) {
204     g_string_append (gstring, "(NULL) message");
205   } else {
206     GString *msg = g_string_new (message);
207     escape_string (msg);
208     g_string_append (gstring, msg->str);
209     g_string_free (msg, TRUE);
210   }
211   string = g_string_free (gstring, FALSE);
212
213   __android_log_print (android_log_level, tag, "%s", string);
214
215   g_free (string);
216   g_free (tag);
217 }
218
219 static void
220 gst_debug_logcat (GstDebugCategory * category, GstDebugLevel level,
221     const gchar * file, const gchar * function, gint line,
222     GObject * object, GstDebugMessage * message, gpointer unused)
223 {
224   GstClockTime elapsed;
225   gint android_log_level;
226   gchar *tag;
227
228   if (level > gst_debug_category_get_threshold (category))
229     return;
230
231   elapsed = GST_CLOCK_DIFF (_priv_gst_info_start_time,
232       gst_util_get_timestamp ());
233
234   switch (level) {
235     case GST_LEVEL_ERROR:
236       android_log_level = ANDROID_LOG_ERROR;
237       break;
238     case GST_LEVEL_WARNING:
239       android_log_level = ANDROID_LOG_WARN;
240       break;
241     case GST_LEVEL_FIXME:
242     case GST_LEVEL_INFO:
243       android_log_level = ANDROID_LOG_INFO;
244       break;
245     case GST_LEVEL_DEBUG:
246       android_log_level = ANDROID_LOG_DEBUG;
247       break;
248     default:
249       android_log_level = ANDROID_LOG_VERBOSE;
250       break;
251   }
252
253   tag = g_strdup_printf ("GStreamer+%s",
254       gst_debug_category_get_name (category));
255
256   if (object) {
257     gchar *obj;
258
259     if (GST_IS_PAD (object) && GST_OBJECT_NAME (object)) {
260       obj = g_strdup_printf ("<%s:%s>", GST_DEBUG_PAD_NAME (object));
261     } else if (GST_IS_OBJECT (object) && GST_OBJECT_NAME (object)) {
262       obj = g_strdup_printf ("<%s>", GST_OBJECT_NAME (object));
263     } else if (G_IS_OBJECT (object)) {
264       obj = g_strdup_printf ("<%s@%p>", G_OBJECT_TYPE_NAME (object), object);
265     } else {
266       obj = g_strdup_printf ("<%p>", object);
267     }
268
269     __android_log_print (android_log_level, tag,
270         "%" GST_TIME_FORMAT " %p %s:%d:%s:%s %s\n",
271         GST_TIME_ARGS (elapsed), g_thread_self (),
272         file, line, function, obj, gst_debug_message_get (message));
273
274     g_free (obj);
275   } else {
276     __android_log_print (android_log_level, tag,
277         "%" GST_TIME_FORMAT " %p %s:%d:%s %s\n",
278         GST_TIME_ARGS (elapsed), g_thread_self (),
279         file, line, function, gst_debug_message_get (message));
280   }
281   g_free (tag);
282 }
283
284 static gboolean
285 get_application_dirs (JNIEnv * env, jobject context, gchar ** cache_dir,
286     gchar ** files_dir)
287 {
288   jclass context_class;
289   jmethodID get_cache_dir_id, get_files_dir_id;
290   jclass file_class;
291   jmethodID get_absolute_path_id;
292   jobject dir;
293   jstring abs_path;
294   const gchar *abs_path_str;
295
296   *cache_dir = *files_dir = NULL;
297
298   context_class = (*env)->GetObjectClass (env, context);
299   if (!context_class) {
300     return FALSE;
301   }
302   get_cache_dir_id =
303       (*env)->GetMethodID (env, context_class, "getCacheDir",
304       "()Ljava/io/File;");
305   get_files_dir_id =
306       (*env)->GetMethodID (env, context_class, "getFilesDir",
307       "()Ljava/io/File;");
308   if (!get_cache_dir_id || !get_files_dir_id) {
309     (*env)->DeleteLocalRef (env, context_class);
310     return FALSE;
311   }
312
313   file_class = (*env)->FindClass (env, "java/io/File");
314   if (!file_class) {
315     (*env)->DeleteLocalRef (env, context_class);
316     return FALSE;
317   }
318   get_absolute_path_id =
319       (*env)->GetMethodID (env, file_class, "getAbsolutePath",
320       "()Ljava/lang/String;");
321   if (!get_absolute_path_id) {
322     (*env)->DeleteLocalRef (env, context_class);
323     (*env)->DeleteLocalRef (env, file_class);
324     return FALSE;
325   }
326
327   dir = (*env)->CallObjectMethod (env, context, get_cache_dir_id);
328   if ((*env)->ExceptionCheck (env)) {
329     (*env)->ExceptionDescribe (env);
330     (*env)->ExceptionClear (env);
331     (*env)->DeleteLocalRef (env, context_class);
332     (*env)->DeleteLocalRef (env, file_class);
333     return FALSE;
334   }
335
336   if (dir) {
337     abs_path = (*env)->CallObjectMethod (env, dir, get_absolute_path_id);
338     if ((*env)->ExceptionCheck (env)) {
339       (*env)->ExceptionDescribe (env);
340       (*env)->ExceptionClear (env);
341       (*env)->DeleteLocalRef (env, dir);
342       (*env)->DeleteLocalRef (env, context_class);
343       (*env)->DeleteLocalRef (env, file_class);
344       return FALSE;
345     }
346     abs_path_str = (*env)->GetStringUTFChars (env, abs_path, NULL);
347     if ((*env)->ExceptionCheck (env)) {
348       (*env)->ExceptionDescribe (env);
349       (*env)->ExceptionClear (env);
350       (*env)->DeleteLocalRef (env, abs_path);
351       (*env)->DeleteLocalRef (env, dir);
352       (*env)->DeleteLocalRef (env, context_class);
353       (*env)->DeleteLocalRef (env, file_class);
354       return FALSE;
355     }
356     *cache_dir = abs_path ? g_strdup (abs_path_str) : NULL;
357
358     (*env)->ReleaseStringUTFChars (env, abs_path, abs_path_str);
359     (*env)->DeleteLocalRef (env, abs_path);
360     (*env)->DeleteLocalRef (env, dir);
361   }
362
363   dir = (*env)->CallObjectMethod (env, context, get_files_dir_id);
364   if ((*env)->ExceptionCheck (env)) {
365     (*env)->ExceptionDescribe (env);
366     (*env)->ExceptionClear (env);
367     (*env)->DeleteLocalRef (env, context_class);
368     (*env)->DeleteLocalRef (env, file_class);
369     return FALSE;
370   }
371   if (dir) {
372     abs_path = (*env)->CallObjectMethod (env, dir, get_absolute_path_id);
373     if ((*env)->ExceptionCheck (env)) {
374       (*env)->ExceptionDescribe (env);
375       (*env)->ExceptionClear (env);
376       (*env)->DeleteLocalRef (env, dir);
377       (*env)->DeleteLocalRef (env, context_class);
378       (*env)->DeleteLocalRef (env, file_class);
379       return FALSE;
380     }
381     abs_path_str = (*env)->GetStringUTFChars (env, abs_path, NULL);
382     if ((*env)->ExceptionCheck (env)) {
383       (*env)->ExceptionDescribe (env);
384       (*env)->ExceptionClear (env);
385       (*env)->DeleteLocalRef (env, abs_path);
386       (*env)->DeleteLocalRef (env, dir);
387       (*env)->DeleteLocalRef (env, context_class);
388       (*env)->DeleteLocalRef (env, file_class);
389       return FALSE;
390     }
391     *files_dir = files_dir ? g_strdup (abs_path_str) : NULL;
392
393     (*env)->ReleaseStringUTFChars (env, abs_path, abs_path_str);
394     (*env)->DeleteLocalRef (env, abs_path);
395     (*env)->DeleteLocalRef (env, dir);
396   }
397
398   (*env)->DeleteLocalRef (env, file_class);
399   (*env)->DeleteLocalRef (env, context_class);
400
401   return TRUE;
402 }
403
404 jobject
405 gst_android_get_application_context (void)
406 {
407   return _context;
408 }
409
410 jobject
411 gst_android_get_application_class_loader (void)
412 {
413   return _class_loader;
414 }
415
416 JavaVM *
417 gst_android_get_java_vm (void)
418 {
419   return _java_vm;
420 }
421
422 static gboolean
423 init (JNIEnv * env, jobject context)
424 {
425   jclass context_cls = NULL;
426   jmethodID get_class_loader_id = 0;
427
428   jobject class_loader = NULL;
429
430   context_cls = (*env)->GetObjectClass (env, context);
431   if (!context_cls) {
432     return FALSE;
433   }
434
435   get_class_loader_id = (*env)->GetMethodID (env, context_cls,
436       "getClassLoader", "()Ljava/lang/ClassLoader;");
437   if ((*env)->ExceptionCheck (env)) {
438     (*env)->ExceptionDescribe (env);
439     (*env)->ExceptionClear (env);
440     return FALSE;
441   }
442
443   class_loader = (*env)->CallObjectMethod (env, context, get_class_loader_id);
444   if ((*env)->ExceptionCheck (env)) {
445     (*env)->ExceptionDescribe (env);
446     (*env)->ExceptionClear (env);
447     return FALSE;
448   }
449
450   if (_context) {
451     (*env)->DeleteGlobalRef (env, _context);
452   }
453   _context = (*env)->NewGlobalRef (env, context);
454
455   if (_class_loader) {
456     (*env)->DeleteGlobalRef (env, _class_loader);
457   }
458   _class_loader = (*env)->NewGlobalRef (env, class_loader);
459
460   return TRUE;
461 }
462
463 static void
464 gst_android_init (JNIEnv * env, jobject context)
465 {
466   gchar *cache_dir;
467   gchar *files_dir;
468   gchar *registry;
469   GError *error = NULL;
470
471   if (!init (env, context)) {
472     __android_log_print (ANDROID_LOG_INFO, "GStreamer",
473         "GStreamer failed to initialize");
474   }
475
476   if (gst_is_initialized ()) {
477     __android_log_print (ANDROID_LOG_INFO, "GStreamer",
478         "GStreamer already initialized");
479     return;
480   }
481
482   if (!get_application_dirs (env, context, &cache_dir, &files_dir)) {
483     __android_log_print (ANDROID_LOG_ERROR, "GStreamer",
484         "Failed to get application dirs");
485   }
486
487   if (cache_dir) {
488     g_setenv ("TMP", cache_dir, TRUE);
489     g_setenv ("TEMP", cache_dir, TRUE);
490     g_setenv ("TMPDIR", cache_dir, TRUE);
491     g_setenv ("XDG_RUNTIME_DIR", cache_dir, TRUE);
492     g_setenv ("XDG_CACHE_HOME", cache_dir, TRUE);
493     registry = g_build_filename (cache_dir, "registry.bin", NULL);
494     g_setenv ("GST_REGISTRY", registry, TRUE);
495     g_free (registry);
496     g_setenv ("GST_REGISTRY_REUSE_PLUGIN_SCANNER", "no", TRUE);
497     /* TODO: Should probably also set GST_PLUGIN_SCANNER and GST_PLUGIN_SYSTEM_PATH */
498   }
499   if (files_dir) {
500     gchar *fontconfig, *certs;
501
502     g_setenv ("HOME", files_dir, TRUE);
503     g_setenv ("XDG_DATA_DIRS", files_dir, TRUE);
504     g_setenv ("XDG_CONFIG_DIRS", files_dir, TRUE);
505     g_setenv ("XDG_CONFIG_HOME", files_dir, TRUE);
506     g_setenv ("XDG_DATA_HOME", files_dir, TRUE);
507
508     fontconfig = g_build_filename (files_dir, "fontconfig", NULL);
509     g_setenv ("FONTCONFIG_PATH", fontconfig, TRUE);
510     g_free (fontconfig);
511
512     certs =
513         g_build_filename (files_dir, "ssl", "certs", "ca-certificates.crt",
514         NULL);
515     g_setenv ("CA_CERTIFICATES", certs, TRUE);
516     g_free (certs);
517   }
518   g_free (cache_dir);
519   g_free (files_dir);
520
521   /* Set GLib print handlers */
522   g_set_print_handler (glib_print_handler);
523   g_set_printerr_handler (glib_printerr_handler);
524   g_log_set_default_handler (glib_log_handler, NULL);
525
526   /* Set GStreamer log handlers */
527   gst_debug_remove_log_function (NULL);
528   gst_debug_set_default_threshold (GST_LEVEL_WARNING);
529   gst_debug_add_log_function ((GstLogFunction) gst_debug_logcat, NULL, NULL);
530
531   /* get time we started for debugging messages */
532   _priv_gst_info_start_time = gst_util_get_timestamp ();
533
534   if (!gst_init_check (NULL, NULL, &error)) {
535     gchar *message = g_strdup_printf ("GStreamer initialization failed: %s",
536         error && error->message ? error->message : "(no message)");
537     jclass exception_class = (*env)->FindClass (env, "java/lang/Exception");
538     __android_log_print (ANDROID_LOG_ERROR, "GStreamer", "%s", message);
539     (*env)->ThrowNew (env, exception_class, message);
540     g_free (message);
541     return;
542   }
543   __android_log_print (ANDROID_LOG_INFO, "GStreamer",
544       "GStreamer initialization complete");
545 }
546
547 static void
548 gst_android_init_jni (JNIEnv * env, jobject gstreamer, jobject context)
549 {
550   gst_android_init (env, context);
551 }
552
553 static JNINativeMethod native_methods[] = {
554   {"nativeInit", "(Landroid/content/Context;)V", (void *) gst_android_init_jni}
555 };
556
557 jint
558 JNI_OnLoad (JavaVM * vm, void *reserved)
559 {
560   JNIEnv *env = NULL;
561
562   if ((*vm)->GetEnv (vm, (void **) &env, JNI_VERSION_1_4) != JNI_OK) {
563     __android_log_print (ANDROID_LOG_ERROR, "GStreamer",
564         "Could not retrieve JNIEnv");
565     return 0;
566   }
567   jclass klass = (*env)->FindClass (env, "org/freedesktop/gstreamer/GStreamer");
568   if (!klass) {
569     __android_log_print (ANDROID_LOG_ERROR, "GStreamer",
570         "Could not retrieve class org.freedesktop.gstreamer.GStreamer. "
571         "Please copy GStreamer.java file into your project: "
572         "https://gitlab.freedesktop.org/gstreamer/gstreamer/-/tree/master/data/android/GStreamer.java");
573     return 0;
574   }
575   if ((*env)->RegisterNatives (env, klass, native_methods,
576           G_N_ELEMENTS (native_methods))) {
577     __android_log_print (ANDROID_LOG_ERROR, "GStreamer",
578         "Could not register native methods for org.freedesktop.gstreamer.GStreamer");
579     return 0;
580   }
581
582   /* Remember Java VM */
583   _java_vm = vm;
584
585   return JNI_VERSION_1_4;
586 }
587
588 void
589 JNI_OnUnload (JavaVM * vm, void *reversed)
590 {
591   JNIEnv *env = NULL;
592
593   if ((*vm)->GetEnv (vm, (void **) &env, JNI_VERSION_1_4) != JNI_OK) {
594     __android_log_print (ANDROID_LOG_ERROR, "GStreamer",
595         "Could not retrieve JNIEnv");
596     return;
597   }
598
599   if (_context) {
600     (*env)->DeleteGlobalRef (env, _context);
601     _context = NULL;
602   }
603
604   if (_class_loader) {
605     (*env)->DeleteGlobalRef (env, _class_loader);
606     _class_loader = NULL;
607   }
608
609   _java_vm = NULL;
610 }