diff --git a/subprojects/gstreamer/data/android/GStreamer.java b/subprojects/gstreamer/data/android/GStreamer.java new file mode 100644 index 0000000000..066b2152b4 --- /dev/null +++ b/subprojects/gstreamer/data/android/GStreamer.java @@ -0,0 +1,105 @@ +/** + * Copy this file into your Android project and call init(). If your project + * contains fonts and/or certificates in assets, uncomment copyFonts() and/or + * copyCaCertificates() lines in init(). + */ +package org.freedesktop.gstreamer; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import android.content.Context; +import android.content.res.AssetManager; +import android.system.Os; + +public class GStreamer { + private static native void nativeInit(Context context) throws Exception; + + public static void init(Context context) throws Exception { + //copyFonts(context); + //copyCaCertificates(context); + nativeInit(context); + } + + private static void copyFonts(Context context) { + AssetManager assetManager = context.getAssets(); + File filesDir = context.getFilesDir(); + File fontsFCDir = new File (filesDir, "fontconfig"); + File fontsDir = new File (fontsFCDir, "fonts"); + File fontsCfg = new File (fontsFCDir, "fonts.conf"); + + fontsDir.mkdirs(); + + try { + /* Copy the config file */ + copyFile (assetManager, "fontconfig/fonts.conf", fontsCfg); + /* Copy the fonts */ + for(String filename : assetManager.list("fontconfig/fonts/truetype")) { + File font = new File(fontsDir, filename); + copyFile (assetManager, "fontconfig/fonts/truetype/" + filename, font); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + private static void copyCaCertificates(Context context) { + AssetManager assetManager = context.getAssets(); + File filesDir = context.getFilesDir(); + File sslDir = new File (filesDir, "ssl"); + File certsDir = new File (sslDir, "certs"); + File certs = new File (certsDir, "ca-certificates.crt"); + + certsDir.mkdirs(); + + try { + /* Copy the certificates file */ + copyFile (assetManager, "ssl/certs/ca-certificates.crt", certs); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private static void copyFile(AssetManager assetManager, String assetPath, File outFile) throws IOException { + InputStream in = null; + OutputStream out = null; + IOException exception = null; + + if (outFile.exists()) + outFile.delete(); + + try { + in = assetManager.open(assetPath); + out = new FileOutputStream(outFile); + + byte[] buffer = new byte[1024]; + int read; + while ((read = in.read(buffer)) != -1) { + out.write(buffer, 0, read); + } + out.flush(); + } catch (IOException e) { + exception = e; + } finally { + if (in != null) + try { + in.close(); + } catch (IOException e) { + if (exception == null) + exception = e; + } + if (out != null) + try { + out.close(); + } catch (IOException e) { + if (exception == null) + exception = e; + } + if (exception != null) + throw exception; + } + } +} diff --git a/subprojects/gstreamer/data/meson.build b/subprojects/gstreamer/data/meson.build index 25f2643f09..aafcb4f350 100644 --- a/subprojects/gstreamer/data/meson.build +++ b/subprojects/gstreamer/data/meson.build @@ -7,3 +7,7 @@ if (bashcomp_found) install_dir : bash_completions_dir, install_tag : 'runtime') endif + +if host_system == 'android' + install_data('android/GStreamer.java', install_dir: get_option('datadir') / 'gst-android/ndk-build') +endif diff --git a/subprojects/gstreamer/gst/gstandroid.c b/subprojects/gstreamer/gst/gstandroid.c new file mode 100644 index 0000000000..4212a544b9 --- /dev/null +++ b/subprojects/gstreamer/gst/gstandroid.c @@ -0,0 +1,610 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gst_private.h" +#include "gst.h" + +#include +#include +#include + +/* AMC plugin uses g_module_symbol() to find those symbols */ +GST_API jobject gst_android_get_application_context (void); +GST_API jobject gst_android_get_application_class_loader (void); +GST_API JavaVM *gst_android_get_java_vm (void); + +/* XXX: Workaround for Android <21 making signal() an inline function + * around bsd_signal(), and Android >= 21 not having any bsd_signal() + * symbol but only signal(). + * See https://bugzilla.gnome.org/show_bug.cgi?id=766235 + */ +static gpointer +load_real_signal (gpointer data) +{ + GModule *module; + gpointer ret = NULL; + + module = g_module_open ("libc.so", G_MODULE_BIND_LOCAL); + g_module_symbol (module, "signal", &ret); + + /* As fallback, let's try bsd_signal */ + if (ret == NULL) { + g_warning ("Can't find signal(3) in libc.so!"); + g_module_symbol (module, "bsd_signal", &ret); + } + + g_module_close (module); + + return ret; +} + +__sighandler_t bsd_signal (int signum, __sighandler_t handler) + __attribute__ ((weak)); +__sighandler_t +bsd_signal (int signum, __sighandler_t handler) +{ + static GOnce gonce = G_ONCE_INIT; + __sighandler_t (*real_signal) (int signum, __sighandler_t handler); + + g_once (&gonce, load_real_signal, NULL); + + real_signal = gonce.retval; + g_assert (real_signal != NULL); + + return real_signal (signum, handler); +} + +static jobject _context = NULL; +static jobject _class_loader = NULL; +static JavaVM *_java_vm = NULL; +static GstClockTime _priv_gst_info_start_time; + +static void +glib_print_handler (const gchar * string) +{ + __android_log_print (ANDROID_LOG_INFO, "GLib+stdout", "%s", string); +} + +static void +glib_printerr_handler (const gchar * string) +{ + __android_log_print (ANDROID_LOG_ERROR, "GLib+stderr", "%s", string); +} + + +/* Based on GLib's default handler */ +#define CHAR_IS_SAFE(wc) (!((wc < 0x20 && wc != '\t' && wc != '\n' && wc != '\r') || \ + (wc == 0x7f) || \ + (wc >= 0x80 && wc < 0xa0))) +#define FORMAT_UNSIGNED_BUFSIZE ((GLIB_SIZEOF_LONG * 3) + 3) +#define STRING_BUFFER_SIZE (FORMAT_UNSIGNED_BUFSIZE + 32) +#define ALERT_LEVELS (G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING) +#define DEFAULT_LEVELS (G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING | G_LOG_LEVEL_MESSAGE) +#define INFO_LEVELS (G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG) + +static void +escape_string (GString * string) +{ + const char *p = string->str; + gunichar wc; + + while (p < string->str + string->len) { + gboolean safe; + + wc = g_utf8_get_char_validated (p, -1); + if (wc == (gunichar) - 1 || wc == (gunichar) - 2) { + gchar *tmp; + guint pos; + + pos = p - string->str; + + /* Emit invalid UTF-8 as hex escapes + */ + tmp = g_strdup_printf ("\\x%02x", (guint) (guchar) * p); + g_string_erase (string, pos, 1); + g_string_insert (string, pos, tmp); + + p = string->str + (pos + 4); /* Skip over escape sequence */ + + g_free (tmp); + continue; + } + if (wc == '\r') { + safe = *(p + 1) == '\n'; + } else { + safe = CHAR_IS_SAFE (wc); + } + + if (!safe) { + gchar *tmp; + guint pos; + + pos = p - string->str; + + /* Largest char we escape is 0x0a, so we don't have to worry + * about 8-digit \Uxxxxyyyy + */ + tmp = g_strdup_printf ("\\u%04x", wc); + g_string_erase (string, pos, g_utf8_next_char (p) - p); + g_string_insert (string, pos, tmp); + g_free (tmp); + + p = string->str + (pos + 6); /* Skip over escape sequence */ + } else + p = g_utf8_next_char (p); + } +} + +static void +glib_log_handler (const gchar * log_domain, GLogLevelFlags log_level, + const gchar * message, gpointer user_data) +{ + gchar *string; + GString *gstring; + const gchar *domains; + gint android_log_level; + gchar *tag; + + if ((log_level & DEFAULT_LEVELS) || (log_level >> G_LOG_LEVEL_USER_SHIFT)) + goto emit; + + domains = g_getenv ("G_MESSAGES_DEBUG"); + if (((log_level & INFO_LEVELS) == 0) || + domains == NULL || + (strcmp (domains, "all") != 0 && (!log_domain + || !strstr (domains, log_domain)))) + return; + +emit: + + if (log_domain) + tag = g_strdup_printf ("GLib+%s", log_domain); + else + tag = g_strdup ("GLib"); + + switch (log_level & G_LOG_LEVEL_MASK) { + case G_LOG_LEVEL_ERROR: + case G_LOG_LEVEL_CRITICAL: + android_log_level = ANDROID_LOG_ERROR; + break; + case G_LOG_LEVEL_WARNING: + android_log_level = ANDROID_LOG_WARN; + break; + case G_LOG_LEVEL_MESSAGE: + case G_LOG_LEVEL_INFO: + android_log_level = ANDROID_LOG_INFO; + break; + case G_LOG_LEVEL_DEBUG: + android_log_level = ANDROID_LOG_DEBUG; + break; + default: + android_log_level = ANDROID_LOG_VERBOSE; + break; + } + + gstring = g_string_new (NULL); + if (!message) { + g_string_append (gstring, "(NULL) message"); + } else { + GString *msg = g_string_new (message); + escape_string (msg); + g_string_append (gstring, msg->str); + g_string_free (msg, TRUE); + } + string = g_string_free (gstring, FALSE); + + __android_log_print (android_log_level, tag, "%s", string); + + g_free (string); + g_free (tag); +} + +static void +gst_debug_logcat (GstDebugCategory * category, GstDebugLevel level, + const gchar * file, const gchar * function, gint line, + GObject * object, GstDebugMessage * message, gpointer unused) +{ + GstClockTime elapsed; + gint android_log_level; + gchar *tag; + + if (level > gst_debug_category_get_threshold (category)) + return; + + elapsed = GST_CLOCK_DIFF (_priv_gst_info_start_time, + gst_util_get_timestamp ()); + + switch (level) { + case GST_LEVEL_ERROR: + android_log_level = ANDROID_LOG_ERROR; + break; + case GST_LEVEL_WARNING: + android_log_level = ANDROID_LOG_WARN; + break; + case GST_LEVEL_FIXME: + case GST_LEVEL_INFO: + android_log_level = ANDROID_LOG_INFO; + break; + case GST_LEVEL_DEBUG: + android_log_level = ANDROID_LOG_DEBUG; + break; + default: + android_log_level = ANDROID_LOG_VERBOSE; + break; + } + + tag = g_strdup_printf ("GStreamer+%s", + gst_debug_category_get_name (category)); + + if (object) { + gchar *obj; + + if (GST_IS_PAD (object) && GST_OBJECT_NAME (object)) { + obj = g_strdup_printf ("<%s:%s>", GST_DEBUG_PAD_NAME (object)); + } else if (GST_IS_OBJECT (object) && GST_OBJECT_NAME (object)) { + obj = g_strdup_printf ("<%s>", GST_OBJECT_NAME (object)); + } else if (G_IS_OBJECT (object)) { + obj = g_strdup_printf ("<%s@%p>", G_OBJECT_TYPE_NAME (object), object); + } else { + obj = g_strdup_printf ("<%p>", object); + } + + __android_log_print (android_log_level, tag, + "%" GST_TIME_FORMAT " %p %s:%d:%s:%s %s\n", + GST_TIME_ARGS (elapsed), g_thread_self (), + file, line, function, obj, gst_debug_message_get (message)); + + g_free (obj); + } else { + __android_log_print (android_log_level, tag, + "%" GST_TIME_FORMAT " %p %s:%d:%s %s\n", + GST_TIME_ARGS (elapsed), g_thread_self (), + file, line, function, gst_debug_message_get (message)); + } + g_free (tag); +} + +static gboolean +get_application_dirs (JNIEnv * env, jobject context, gchar ** cache_dir, + gchar ** files_dir) +{ + jclass context_class; + jmethodID get_cache_dir_id, get_files_dir_id; + jclass file_class; + jmethodID get_absolute_path_id; + jobject dir; + jstring abs_path; + const gchar *abs_path_str; + + *cache_dir = *files_dir = NULL; + + context_class = (*env)->GetObjectClass (env, context); + if (!context_class) { + return FALSE; + } + get_cache_dir_id = + (*env)->GetMethodID (env, context_class, "getCacheDir", + "()Ljava/io/File;"); + get_files_dir_id = + (*env)->GetMethodID (env, context_class, "getFilesDir", + "()Ljava/io/File;"); + if (!get_cache_dir_id || !get_files_dir_id) { + (*env)->DeleteLocalRef (env, context_class); + return FALSE; + } + + file_class = (*env)->FindClass (env, "java/io/File"); + if (!file_class) { + (*env)->DeleteLocalRef (env, context_class); + return FALSE; + } + get_absolute_path_id = + (*env)->GetMethodID (env, file_class, "getAbsolutePath", + "()Ljava/lang/String;"); + if (!get_absolute_path_id) { + (*env)->DeleteLocalRef (env, context_class); + (*env)->DeleteLocalRef (env, file_class); + return FALSE; + } + + dir = (*env)->CallObjectMethod (env, context, get_cache_dir_id); + if ((*env)->ExceptionCheck (env)) { + (*env)->ExceptionDescribe (env); + (*env)->ExceptionClear (env); + (*env)->DeleteLocalRef (env, context_class); + (*env)->DeleteLocalRef (env, file_class); + return FALSE; + } + + if (dir) { + abs_path = (*env)->CallObjectMethod (env, dir, get_absolute_path_id); + if ((*env)->ExceptionCheck (env)) { + (*env)->ExceptionDescribe (env); + (*env)->ExceptionClear (env); + (*env)->DeleteLocalRef (env, dir); + (*env)->DeleteLocalRef (env, context_class); + (*env)->DeleteLocalRef (env, file_class); + return FALSE; + } + abs_path_str = (*env)->GetStringUTFChars (env, abs_path, NULL); + if ((*env)->ExceptionCheck (env)) { + (*env)->ExceptionDescribe (env); + (*env)->ExceptionClear (env); + (*env)->DeleteLocalRef (env, abs_path); + (*env)->DeleteLocalRef (env, dir); + (*env)->DeleteLocalRef (env, context_class); + (*env)->DeleteLocalRef (env, file_class); + return FALSE; + } + *cache_dir = abs_path ? g_strdup (abs_path_str) : NULL; + + (*env)->ReleaseStringUTFChars (env, abs_path, abs_path_str); + (*env)->DeleteLocalRef (env, abs_path); + (*env)->DeleteLocalRef (env, dir); + } + + dir = (*env)->CallObjectMethod (env, context, get_files_dir_id); + if ((*env)->ExceptionCheck (env)) { + (*env)->ExceptionDescribe (env); + (*env)->ExceptionClear (env); + (*env)->DeleteLocalRef (env, context_class); + (*env)->DeleteLocalRef (env, file_class); + return FALSE; + } + if (dir) { + abs_path = (*env)->CallObjectMethod (env, dir, get_absolute_path_id); + if ((*env)->ExceptionCheck (env)) { + (*env)->ExceptionDescribe (env); + (*env)->ExceptionClear (env); + (*env)->DeleteLocalRef (env, dir); + (*env)->DeleteLocalRef (env, context_class); + (*env)->DeleteLocalRef (env, file_class); + return FALSE; + } + abs_path_str = (*env)->GetStringUTFChars (env, abs_path, NULL); + if ((*env)->ExceptionCheck (env)) { + (*env)->ExceptionDescribe (env); + (*env)->ExceptionClear (env); + (*env)->DeleteLocalRef (env, abs_path); + (*env)->DeleteLocalRef (env, dir); + (*env)->DeleteLocalRef (env, context_class); + (*env)->DeleteLocalRef (env, file_class); + return FALSE; + } + *files_dir = files_dir ? g_strdup (abs_path_str) : NULL; + + (*env)->ReleaseStringUTFChars (env, abs_path, abs_path_str); + (*env)->DeleteLocalRef (env, abs_path); + (*env)->DeleteLocalRef (env, dir); + } + + (*env)->DeleteLocalRef (env, file_class); + (*env)->DeleteLocalRef (env, context_class); + + return TRUE; +} + +jobject +gst_android_get_application_context (void) +{ + return _context; +} + +jobject +gst_android_get_application_class_loader (void) +{ + return _class_loader; +} + +JavaVM * +gst_android_get_java_vm (void) +{ + return _java_vm; +} + +static gboolean +init (JNIEnv * env, jobject context) +{ + jclass context_cls = NULL; + jmethodID get_class_loader_id = 0; + + jobject class_loader = NULL; + + context_cls = (*env)->GetObjectClass (env, context); + if (!context_cls) { + return FALSE; + } + + get_class_loader_id = (*env)->GetMethodID (env, context_cls, + "getClassLoader", "()Ljava/lang/ClassLoader;"); + if ((*env)->ExceptionCheck (env)) { + (*env)->ExceptionDescribe (env); + (*env)->ExceptionClear (env); + return FALSE; + } + + class_loader = (*env)->CallObjectMethod (env, context, get_class_loader_id); + if ((*env)->ExceptionCheck (env)) { + (*env)->ExceptionDescribe (env); + (*env)->ExceptionClear (env); + return FALSE; + } + + if (_context) { + (*env)->DeleteGlobalRef (env, _context); + } + _context = (*env)->NewGlobalRef (env, context); + + if (_class_loader) { + (*env)->DeleteGlobalRef (env, _class_loader); + } + _class_loader = (*env)->NewGlobalRef (env, class_loader); + + return TRUE; +} + +static void +gst_android_init (JNIEnv * env, jobject context) +{ + gchar *cache_dir; + gchar *files_dir; + gchar *registry; + GError *error = NULL; + + if (!init (env, context)) { + __android_log_print (ANDROID_LOG_INFO, "GStreamer", + "GStreamer failed to initialize"); + } + + if (gst_is_initialized ()) { + __android_log_print (ANDROID_LOG_INFO, "GStreamer", + "GStreamer already initialized"); + return; + } + + if (!get_application_dirs (env, context, &cache_dir, &files_dir)) { + __android_log_print (ANDROID_LOG_ERROR, "GStreamer", + "Failed to get application dirs"); + } + + if (cache_dir) { + g_setenv ("TMP", cache_dir, TRUE); + g_setenv ("TEMP", cache_dir, TRUE); + g_setenv ("TMPDIR", cache_dir, TRUE); + g_setenv ("XDG_RUNTIME_DIR", cache_dir, TRUE); + g_setenv ("XDG_CACHE_HOME", cache_dir, TRUE); + registry = g_build_filename (cache_dir, "registry.bin", NULL); + g_setenv ("GST_REGISTRY", registry, TRUE); + g_free (registry); + g_setenv ("GST_REGISTRY_REUSE_PLUGIN_SCANNER", "no", TRUE); + /* TODO: Should probably also set GST_PLUGIN_SCANNER and GST_PLUGIN_SYSTEM_PATH */ + } + if (files_dir) { + gchar *fontconfig, *certs; + + g_setenv ("HOME", files_dir, TRUE); + g_setenv ("XDG_DATA_DIRS", files_dir, TRUE); + g_setenv ("XDG_CONFIG_DIRS", files_dir, TRUE); + g_setenv ("XDG_CONFIG_HOME", files_dir, TRUE); + g_setenv ("XDG_DATA_HOME", files_dir, TRUE); + + fontconfig = g_build_filename (files_dir, "fontconfig", NULL); + g_setenv ("FONTCONFIG_PATH", fontconfig, TRUE); + g_free (fontconfig); + + certs = + g_build_filename (files_dir, "ssl", "certs", "ca-certificates.crt", + NULL); + g_setenv ("CA_CERTIFICATES", certs, TRUE); + g_free (certs); + } + g_free (cache_dir); + g_free (files_dir); + + /* Set GLib print handlers */ + g_set_print_handler (glib_print_handler); + g_set_printerr_handler (glib_printerr_handler); + g_log_set_default_handler (glib_log_handler, NULL); + + /* Set GStreamer log handlers */ + gst_debug_remove_log_function (NULL); + gst_debug_set_default_threshold (GST_LEVEL_WARNING); + gst_debug_add_log_function ((GstLogFunction) gst_debug_logcat, NULL, NULL); + + /* get time we started for debugging messages */ + _priv_gst_info_start_time = gst_util_get_timestamp (); + + if (!gst_init_check (NULL, NULL, &error)) { + gchar *message = g_strdup_printf ("GStreamer initialization failed: %s", + error && error->message ? error->message : "(no message)"); + jclass exception_class = (*env)->FindClass (env, "java/lang/Exception"); + __android_log_print (ANDROID_LOG_ERROR, "GStreamer", "%s", message); + (*env)->ThrowNew (env, exception_class, message); + g_free (message); + return; + } + __android_log_print (ANDROID_LOG_INFO, "GStreamer", + "GStreamer initialization complete"); +} + +static void +gst_android_init_jni (JNIEnv * env, jobject gstreamer, jobject context) +{ + gst_android_init (env, context); +} + +static JNINativeMethod native_methods[] = { + {"nativeInit", "(Landroid/content/Context;)V", (void *) gst_android_init_jni} +}; + +jint +JNI_OnLoad (JavaVM * vm, void *reserved) +{ + JNIEnv *env = NULL; + + if ((*vm)->GetEnv (vm, (void **) &env, JNI_VERSION_1_4) != JNI_OK) { + __android_log_print (ANDROID_LOG_ERROR, "GStreamer", + "Could not retrieve JNIEnv"); + return 0; + } + jclass klass = (*env)->FindClass (env, "org/freedesktop/gstreamer/GStreamer"); + if (!klass) { + __android_log_print (ANDROID_LOG_ERROR, "GStreamer", + "Could not retrieve class org.freedesktop.gstreamer.GStreamer. " + "Please copy GStreamer.java file into your project: " + "https://gitlab.freedesktop.org/gstreamer/gstreamer/-/tree/master/data/android/GStreamer.java"); + return 0; + } + if ((*env)->RegisterNatives (env, klass, native_methods, + G_N_ELEMENTS (native_methods))) { + __android_log_print (ANDROID_LOG_ERROR, "GStreamer", + "Could not register native methods for org.freedesktop.gstreamer.GStreamer"); + return 0; + } + + /* Remember Java VM */ + _java_vm = vm; + + return JNI_VERSION_1_4; +} + +void +JNI_OnUnload (JavaVM * vm, void *reversed) +{ + JNIEnv *env = NULL; + + if ((*vm)->GetEnv (vm, (void **) &env, JNI_VERSION_1_4) != JNI_OK) { + __android_log_print (ANDROID_LOG_ERROR, "GStreamer", + "Could not retrieve JNIEnv"); + return; + } + + if (_context) { + (*env)->DeleteGlobalRef (env, _context); + _context = NULL; + } + + if (_class_loader) { + (*env)->DeleteGlobalRef (env, _class_loader); + _class_loader = NULL; + } + + _java_vm = NULL; +} diff --git a/subprojects/gstreamer/gst/meson.build b/subprojects/gstreamer/gst/meson.build index 914e18d98a..2692e195fd 100644 --- a/subprojects/gstreamer/gst/meson.build +++ b/subprojects/gstreamer/gst/meson.build @@ -150,6 +150,12 @@ gst_headers = files( ) install_headers(gst_headers, subdir : 'gstreamer-1.0/gst') +extra_deps = [] +if host_system == 'android' + gst_sources += 'gstandroid.c' + extra_deps += cc.find_library('log') +endif + gst_registry = get_option('registry') if gst_registry gst_registry_sources = files('gstregistrybinary.c') @@ -246,8 +252,8 @@ libgst = library('gstreamer-1.0', gst_sources, include_directories('parse')], link_with : printf_lib, install : true, - dependencies : [gobject_dep, gmodule_dep, glib_dep, mathlib, dl_dep] + backtrace_deps - + platform_deps, + dependencies : [gobject_dep, gmodule_dep, glib_dep, mathlib, dl_dep, + backtrace_deps, platform_deps, extra_deps], ) pkg_name = 'gstreamer-1.0'