| /* GStreamer base utils library plugin install support for applications |
| * Copyright (C) 2007 Tim-Philipp Müller <tim centricular net> |
| * Copyright (C) 2006 Ryan Lortie <desrt desrt ca> |
| * |
| * 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. |
| */ |
| |
| /** |
| * SECTION:gstpbutilsinstallplugins |
| * @title: Install-plugins |
| * @short_description: Missing plugin installation support for applications |
| * |
| * ## Overview |
| * |
| * Using this API, applications can request the installation of missing |
| * GStreamer plugins. These may be missing decoders/demuxers or |
| * encoders/muxers for a certain format, sources or sinks for a certain URI |
| * protocol (e.g. 'http'), or certain elements known by their element |
| * factory name ('audioresample'). |
| * |
| * Whether plugin installation is supported or not depends on the operating |
| * system and/or distribution in question. The vendor of the operating |
| * system needs to make sure the necessary hooks and mechanisms are in |
| * place for plugin installation to work. See below for more detailed |
| * information. |
| * |
| * From the application perspective, plugin installation is usually |
| * triggered either |
| * |
| * - when the application itself has found that it wants or needs to |
| * install a certain element |
| * - when the application has been notified by an element (such as |
| * playbin or decodebin) that one or more plugins are missing *and* the |
| * application has decided that it wants to install one or more of |
| * those missing plugins |
| * |
| * The install functions in this section all take one or more 'detail |
| * strings'. These detail strings contain information about the type of |
| * plugin that needs to be installed (decoder, encoder, source, sink, or |
| * named element), and some additional information such GStreamer version |
| * used and a human-readable description of the component to install for |
| * user dialogs. |
| * |
| * Applications should not concern themselves with the composition of the |
| * string itself. They should regard the string as if it was a shared |
| * secret between GStreamer and the plugin installer application. |
| * |
| * Detail strings can be obtained using the function |
| * gst_missing_plugin_message_get_installer_detail() on a |
| * missing-plugin message. Such a message will either have been found by |
| * the application on a pipeline's #GstBus, or the application will have |
| * created it itself using gst_missing_element_message_new(), |
| * gst_missing_decoder_message_new(), |
| * gst_missing_encoder_message_new(), |
| * gst_missing_uri_sink_message_new(), or |
| * gst_missing_uri_source_message_new(). |
| * |
| * For each GStreamer element/plugin/component that should be installed, |
| * the application needs one of those 'installer detail' string mentioned |
| * in the previous section. This string can be obtained, as already |
| * mentioned above, from a missing-plugin message using the function |
| * gst_missing_plugin_message_get_installer_detail(). The |
| * missing-plugin message is either posted by another element and then |
| * found on the bus by the application, or the application has created it |
| * itself as described above. |
| * |
| * The application will then call gst_install_plugins_async(), passing a |
| * NULL-terminated array of installer detail strings, and a function that |
| * should be called when the installation of the plugins has finished |
| * (successfully or not). Optionally, a #GstInstallPluginsContext created |
| * with gst_install_plugins_context_new() may be passed as well. This |
| * way additional optional arguments like the application window's XID can |
| * be passed to the external installer application. |
| * |
| * gst_install_plugins_async() will return almost immediately, with the |
| * return code indicating whether plugin installation was started or not. |
| * If the necessary hooks for plugin installation are in place and an |
| * external installer application has in fact been called, the passed in |
| * function will be called with a result code as soon as the external |
| * installer has finished. If the result code indicates that new plugins |
| * have been installed, the application will want to call |
| * gst_update_registry() so the run-time plugin registry is updated and |
| * the new plugins are made available to the application. |
| * |
| * > A Gtk/GLib main loop must be running in order for the result function |
| * > to be called when the external installer has finished. If this is not |
| * > the case, make sure to regularly call in your code: |
| * > |
| * > g_main_context_iteration (NULL,FALSE); |
| * |
| * ## 1. Installer hook |
| * |
| * When GStreamer applications initiate plugin installation via |
| * gst_install_plugins_async() or gst_install_plugins_sync(), a |
| * pre-defined helper application will be called. |
| * |
| * The exact path of the helper application to be called is set at compile |
| * time, usually by the `./configure` script based on the install prefix. |
| * For a normal package build into the `/usr` prefix, this will usually |
| * default to `/usr/libexec/gst-install-plugins-helper` or |
| * `/usr/lib/gst-install-plugins-helper`. |
| * |
| * Vendors/distros who want to support GStreamer plugin installation should |
| * either provide such a helper script/application or use the `./configure` |
| * option `--with-install-plugins-helper=/path/to/installer` to make |
| * GStreamer call an installer of their own directly. |
| * |
| * It is strongly recommended that vendors provide a small helper |
| * application as interlocutor to the real installer though, even more so |
| * if command line argument munging is required to transform the command |
| * line arguments passed by GStreamer to the helper application into |
| * arguments that are understood by the real installer. |
| * |
| * The helper application path defined at compile time can be overriden at |
| * runtime by setting the GST_INSTALL_PLUGINS_HELPER environment |
| * variable. This can be useful for testing/debugging purposes. |
| * |
| * ## 2. Arguments passed to the install helper |
| * |
| * GStreamer will pass the following arguments to the install helper (this |
| * is in addition to the path of the executable itself, which is by |
| * convention argv[0]): |
| * |
| * - none to many optional arguments in the form of `--foo-bar=val`. |
| * Example: `--transient-for=XID` where XID is the X Window ID of the |
| * main window of the calling application (so the installer can make |
| * itself transient to that window). Unknown optional arguments should |
| * be ignored by the installer. |
| * |
| * - one 'installer detail string' argument for each plugin to be |
| * installed; these strings will have a `gstreamer` prefix; the exact |
| * format of the detail string is explained below |
| * |
| * ## 3. Detail string describing the missing plugin |
| * |
| * The string is in UTF-8 encoding and is made up of several fields, |
| * separated by '|' characters (but neither the first nor the last |
| * character is a '|'). The fields are: |
| * |
| * - plugin system identifier, ie. "gstreamer" |
| * This identifier determines the format of the rest of the detail |
| * string. Automatic plugin installers should not process detail |
| * strings with unknown identifiers. This allows other plugin-based |
| * libraries to use the same mechanism for their automatic plugin |
| * installation needs, or for the format to be changed should it turn |
| * out to be insufficient. |
| * - plugin system version, e.g. "0.10" |
| * This is required so that when there is a GStreamer-0.12 or |
| * GStreamer-1.0 at some point in future, the different major versions |
| * can still co-exist and use the same plugin install mechanism in the |
| * same way. |
| * - application identifier, e.g. "totem" |
| * This may also be in the form of "pid/12345" if the program name |
| * can't be obtained for some reason. |
| * - human-readable localised description of the required component, e.g. |
| * "Vorbis audio decoder" |
| * - identifier string for the required component (see below for details |
| * about how to map this to the package/plugin that needs installing), |
| * e.g. |
| * - urisource-$(PROTOCOL_REQUIRED), e.g. urisource-http or |
| * urisource-mms |
| * - element-$(ELEMENT_REQUIRED), e.g. element-videoconvert |
| * - decoder-$(CAPS_REQUIRED), e.g. (do read below for more |
| * details!): |
| * - decoder-audio/x-vorbis |
| * - decoder-application/ogg |
| * - decoder-audio/mpeg, mpegversion=(int)4 |
| * - decoder-video/mpeg, systemstream=(boolean)true, |
| * mpegversion=(int)2 |
| * - encoder-$(CAPS_REQUIRED), e.g. encoder-audio/x-vorbis |
| * - optional further fields not yet specified |
| * |
| * An entire ID string might then look like this, for example: ` |
| * gstreamer|0.10|totem|Vorbis audio decoder|decoder-audio/x-vorbis` |
| * |
| * Plugin installers parsing this ID string should expect further fields |
| * also separated by '|' symbols and either ignore them, warn the user, or |
| * error out when encountering them. |
| * |
| * Those unfamiliar with the GStreamer 'caps' system should note a few |
| * things about the caps string used in the above decoder/encoder case: |
| * |
| * - the first part ("video/mpeg") of the caps string is a GStreamer |
| * media type and *not* a MIME type. Wherever possible, the GStreamer |
| * media type will be the same as the corresponding MIME type, but |
| * often it is not. |
| * - a caps string may or may not have additional comma-separated fields |
| * of various types (as seen in the examples above) |
| * - the caps string of a 'required' component (as above) will always |
| * have fields with fixed values, whereas an introspected string (see |
| * below) may have fields with non-fixed values. Compare for example: |
| * - `audio/mpeg, mpegversion=(int)4` vs. |
| * `audio/mpeg, mpegversion=(int){2, 4}` |
| * - `video/mpeg, mpegversion=(int)2` vs. |
| * `video/mpeg, systemstream=(boolean){ true, false}, mpegversion=(int)[1, 2]` |
| * |
| * ## 4. Exit codes the installer should return |
| * |
| * The installer should return one of the following exit codes when it |
| * exits: |
| * |
| * - 0 if all of the requested plugins could be installed |
| * (#GST_INSTALL_PLUGINS_SUCCESS) |
| * - 1 if no appropriate installation candidate for any of the requested |
| * plugins could be found. Only return this if nothing has been |
| * installed (#GST_INSTALL_PLUGINS_NOT_FOUND) |
| * - 2 if an error occured during the installation. The application will |
| * assume that the user will already have seen an error message by the |
| * installer in this case and will usually not show another one |
| * (#GST_INSTALL_PLUGINS_ERROR) |
| * - 3 if some of the requested plugins could be installed, but not all |
| * (#GST_INSTALL_PLUGINS_PARTIAL_SUCCESS) |
| * - 4 if the user aborted the installation |
| * (#GST_INSTALL_PLUGINS_USER_ABORT) |
| * |
| * ## 5. How to map the required detail string to packages |
| * |
| * It is up to the vendor to find mechanism to map required components from |
| * the detail string to the actual packages/plugins to install. This could |
| * be a hardcoded list of mappings, for example, or be part of the |
| * packaging system metadata. |
| * |
| * GStreamer plugin files can be introspected for this information. The |
| * `gst-inspect` utility has a special command line option that will output |
| * information similar to what is required. For example ` |
| * $ gst-inspect-1.0 --print-plugin-auto-install-info /path/to/libgstvorbis.so |
| * should output something along the lines of |
| * `decoder-audio/x-vorbis`, `element-vorbisdec` `element-vorbisenc` |
| * `element-vorbisparse`, `element-vorbistag`, `encoder-audio/x-vorbis` |
| * |
| * Note that in the encoder and decoder case the introspected caps can be |
| * more complex with additional fields, e.g. |
| * `audio/mpeg,mpegversion=(int){2,4}`, so they will not always exactly |
| * match the caps wanted by the application. It is up to the installer to |
| * deal with this (either by doing proper caps intersection using the |
| * GStreamer #GstCaps API, or by only taking into account the media type). |
| * |
| * Another potential source of problems are plugins such as ladspa or |
| * libvisual where the list of elements depends on the installed |
| * ladspa/libvisual plugins at the time. This is also up to the |
| * distribution to handle (but usually not relevant for playback |
| * applications). |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include "config.h" |
| #endif |
| |
| #include "install-plugins.h" |
| |
| #include <gst/gstinfo.h> |
| |
| #ifdef HAVE_SYS_TYPES_H |
| #include <sys/types.h> |
| #endif |
| |
| #ifdef HAVE_SYS_WAIT_H |
| #include <sys/wait.h> |
| #endif |
| |
| #include <string.h> |
| |
| /* best effort to make things compile and possibly even work on win32 */ |
| #ifndef WEXITSTATUS |
| # define WEXITSTATUS(status) ((((guint)(status)) & 0xff00) >> 8) |
| #endif |
| #ifndef WIFEXITED |
| # define WIFEXITED(status) ((((guint)(status)) & 0x7f) == 0) |
| #endif |
| |
| static gboolean install_in_progress; /* FALSE */ |
| |
| /* private struct */ |
| struct _GstInstallPluginsContext |
| { |
| gchar *confirm_search; |
| gchar *desktop_id; |
| gchar *startup_notification_id; |
| guint xid; |
| }; |
| |
| /** |
| * gst_install_plugins_context_set_confirm_search: |
| * @ctx: a #GstInstallPluginsContext |
| * @confirm_search: whether to ask for confirmation before searching for plugins |
| * |
| * This function is used to tell the external installer process whether it |
| * should ask for confirmation or not before searching for missing plugins. |
| * |
| * If set, this option will be passed to the installer via a |
| * --interaction=[show-confirm-search|hide-confirm-search] command line option. |
| * |
| * Since: 1.6 |
| */ |
| void |
| gst_install_plugins_context_set_confirm_search (GstInstallPluginsContext * ctx, |
| gboolean confirm_search) |
| { |
| g_return_if_fail (ctx != NULL); |
| |
| if (confirm_search) |
| ctx->confirm_search = g_strdup ("show-confirm-search"); |
| else |
| ctx->confirm_search = g_strdup ("hide-confirm-search"); |
| } |
| |
| /** |
| * gst_install_plugins_context_set_desktop_id: |
| * @ctx: a #GstInstallPluginsContext |
| * @desktop_id: the desktop file ID of the calling application |
| * |
| * This function is used to pass the calling application's desktop file ID to |
| * the external installer process. |
| * |
| * A desktop file ID is the basename of the desktop file, including the |
| * .desktop extension. |
| * |
| * If set, the desktop file ID will be passed to the installer via a |
| * --desktop-id= command line option. |
| * |
| * Since: 1.6 |
| */ |
| void |
| gst_install_plugins_context_set_desktop_id (GstInstallPluginsContext * ctx, |
| const gchar * desktop_id) |
| { |
| g_return_if_fail (ctx != NULL); |
| |
| ctx->desktop_id = g_strdup (desktop_id); |
| } |
| |
| /** |
| * gst_install_plugins_context_set_startup_notification_id: |
| * @ctx: a #GstInstallPluginsContext |
| * @startup_id: the startup notification ID |
| * |
| * Sets the startup notification ID for the launched process. |
| * |
| * This is typically used to to pass the current X11 event timestamp to the |
| * external installer process. |
| * |
| * Startup notification IDs are defined in the |
| * [FreeDesktop.Org Startup Notifications standard](http://standards.freedesktop.org/startup-notification-spec/startup-notification-latest.txt). |
| * |
| * If set, the ID will be passed to the installer via a |
| * --startup-notification-id= command line option. |
| * |
| * GTK+/GNOME applications should be able to create a startup notification ID |
| * like this: |
| * |[ |
| * timestamp = gtk_get_current_event_time (); |
| * startup_id = g_strdup_printf ("_TIME%u", timestamp); |
| * ... |
| * ]| |
| * |
| * Since: 1.6 |
| */ |
| void gst_install_plugins_context_set_startup_notification_id |
| (GstInstallPluginsContext * ctx, const gchar * startup_id) |
| { |
| g_return_if_fail (ctx != NULL); |
| |
| ctx->startup_notification_id = g_strdup (startup_id); |
| } |
| |
| /** |
| * gst_install_plugins_context_set_xid: |
| * @ctx: a #GstInstallPluginsContext |
| * @xid: the XWindow ID (XID) of the top-level application |
| * |
| * This function is for X11-based applications (such as most Gtk/Qt |
| * applications on linux/unix) only. You can use it to tell the external |
| * installer the XID of your main application window. That way the installer |
| * can make its own window transient to your application window during the |
| * installation. |
| * |
| * If set, the XID will be passed to the installer via a --transient-for=XID |
| * command line option. |
| * |
| * Gtk+/Gnome application should be able to obtain the XID of the top-level |
| * window like this: |
| * |[ |
| * ##include <gtk/gtk.h> |
| * ##ifdef GDK_WINDOWING_X11 |
| * ##include <gdk/gdkx.h> |
| * ##endif |
| * ... |
| * ##ifdef GDK_WINDOWING_X11 |
| * xid = GDK_WINDOW_XWINDOW (GTK_WIDGET (application_window)->window); |
| * ##endif |
| * ... |
| * ]| |
| * |
| */ |
| void |
| gst_install_plugins_context_set_xid (GstInstallPluginsContext * ctx, guint xid) |
| { |
| g_return_if_fail (ctx != NULL); |
| |
| ctx->xid = xid; |
| } |
| |
| /** |
| * gst_install_plugins_context_new: |
| * |
| * Creates a new #GstInstallPluginsContext. |
| * |
| * Returns: a new #GstInstallPluginsContext. Free with |
| * gst_install_plugins_context_free() when no longer needed |
| */ |
| GstInstallPluginsContext * |
| gst_install_plugins_context_new (void) |
| { |
| return g_new0 (GstInstallPluginsContext, 1); |
| } |
| |
| /** |
| * gst_install_plugins_context_free: |
| * @ctx: a #GstInstallPluginsContext |
| * |
| * Frees a #GstInstallPluginsContext. |
| */ |
| void |
| gst_install_plugins_context_free (GstInstallPluginsContext * ctx) |
| { |
| g_return_if_fail (ctx != NULL); |
| |
| g_free (ctx->confirm_search); |
| g_free (ctx->desktop_id); |
| g_free (ctx->startup_notification_id); |
| g_free (ctx); |
| } |
| |
| GstInstallPluginsContext * |
| gst_install_plugins_context_copy (GstInstallPluginsContext * ctx) |
| { |
| GstInstallPluginsContext *ret; |
| |
| ret = gst_install_plugins_context_new (); |
| ret->confirm_search = g_strdup (ctx->confirm_search); |
| ret->desktop_id = g_strdup (ctx->desktop_id); |
| ret->startup_notification_id = g_strdup (ctx->startup_notification_id); |
| ret->xid = ctx->xid; |
| |
| return ret; |
| } |
| |
| G_DEFINE_BOXED_TYPE (GstInstallPluginsContext, gst_install_plugins_context, |
| (GBoxedCopyFunc) gst_install_plugins_context_copy, |
| (GBoxedFreeFunc) gst_install_plugins_context_free); |
| |
| static const gchar * |
| gst_install_plugins_get_helper (void) |
| { |
| const gchar *helper; |
| |
| helper = g_getenv ("GST_INSTALL_PLUGINS_HELPER"); |
| if (helper == NULL) |
| helper = GST_INSTALL_PLUGINS_HELPER; |
| |
| GST_LOG ("Using plugin install helper '%s'", helper); |
| return helper; |
| } |
| |
| static gboolean |
| ptr_array_contains_string (GPtrArray * arr, const gchar * s) |
| { |
| gint i; |
| |
| for (i = 0; i < arr->len; ++i) { |
| if (strcmp ((const char *) g_ptr_array_index (arr, i), s) == 0) |
| return TRUE; |
| } |
| return FALSE; |
| } |
| |
| static gboolean |
| gst_install_plugins_spawn_child (const gchar * const *details, |
| GstInstallPluginsContext * ctx, GPid * child_pid, gint * exit_status) |
| { |
| GPtrArray *arr; |
| gboolean ret; |
| GError *err = NULL; |
| gchar **argv; |
| |
| arr = g_ptr_array_new_with_free_func (g_free); |
| |
| /* argv[0] = helper path */ |
| g_ptr_array_add (arr, g_strdup (gst_install_plugins_get_helper ())); |
| |
| /* add any additional command line args from the context */ |
| if (ctx != NULL && ctx->confirm_search) { |
| g_ptr_array_add (arr, g_strdup_printf ("--interaction=%s", |
| ctx->confirm_search)); |
| } |
| if (ctx != NULL && ctx->desktop_id != NULL) { |
| g_ptr_array_add (arr, g_strdup_printf ("--desktop-id=%s", ctx->desktop_id)); |
| } |
| if (ctx != NULL && ctx->startup_notification_id != NULL) { |
| g_ptr_array_add (arr, g_strdup_printf ("--startup-notification-id=%s", |
| ctx->startup_notification_id)); |
| } |
| if (ctx != NULL && ctx->xid != 0) { |
| g_ptr_array_add (arr, g_strdup_printf ("--transient-for=%u", ctx->xid)); |
| } |
| |
| /* finally, add the detail strings, but without duplicates */ |
| while (details != NULL && details[0] != NULL) { |
| if (!ptr_array_contains_string (arr, details[0])) |
| g_ptr_array_add (arr, g_strdup (details[0])); |
| ++details; |
| } |
| |
| /* and NULL-terminate */ |
| g_ptr_array_add (arr, NULL); |
| |
| argv = (gchar **) arr->pdata; |
| |
| if (child_pid == NULL && exit_status != NULL) { |
| install_in_progress = TRUE; |
| ret = g_spawn_sync (NULL, argv, NULL, (GSpawnFlags) 0, NULL, NULL, |
| NULL, NULL, exit_status, &err); |
| install_in_progress = FALSE; |
| } else if (child_pid != NULL && exit_status == NULL) { |
| install_in_progress = TRUE; |
| ret = g_spawn_async (NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD, NULL, |
| NULL, child_pid, &err); |
| } else { |
| g_return_val_if_reached (FALSE); |
| } |
| |
| if (!ret) { |
| GST_ERROR ("Error spawning plugin install helper: %s", err->message); |
| g_error_free (err); |
| } |
| |
| g_ptr_array_unref (arr); |
| return ret; |
| } |
| |
| static GstInstallPluginsReturn |
| gst_install_plugins_return_from_status (gint status) |
| { |
| GstInstallPluginsReturn ret; |
| |
| /* did we exit cleanly? */ |
| if (!WIFEXITED (status)) { |
| ret = GST_INSTALL_PLUGINS_CRASHED; |
| } else { |
| ret = (GstInstallPluginsReturn) WEXITSTATUS (status); |
| |
| /* did the helper return an invalid status code? */ |
| if (((guint) ret) >= GST_INSTALL_PLUGINS_STARTED_OK && |
| ret != GST_INSTALL_PLUGINS_INTERNAL_FAILURE) { |
| ret = GST_INSTALL_PLUGINS_INVALID; |
| } |
| } |
| |
| GST_LOG ("plugin installer exited with status 0x%04x = %s", status, |
| gst_install_plugins_return_get_name (ret)); |
| |
| return ret; |
| } |
| |
| typedef struct |
| { |
| GstInstallPluginsResultFunc func; |
| gpointer user_data; |
| } GstInstallPluginsAsyncHelper; |
| |
| static void |
| gst_install_plugins_installer_exited (GPid pid, gint status, gpointer data) |
| { |
| GstInstallPluginsAsyncHelper *helper; |
| GstInstallPluginsReturn ret; |
| |
| install_in_progress = FALSE; |
| |
| helper = (GstInstallPluginsAsyncHelper *) data; |
| ret = gst_install_plugins_return_from_status (status); |
| |
| GST_LOG ("calling plugin install result function %p", helper->func); |
| helper->func (ret, helper->user_data); |
| |
| g_free (helper); |
| } |
| |
| /** |
| * gst_install_plugins_async: |
| * @details: (array zero-terminated=1) (transfer none): NULL-terminated array |
| * of installer string details (see below) |
| * @ctx: (allow-none): a #GstInstallPluginsContext, or NULL |
| * @func: (scope async): the function to call when the installer program returns |
| * @user_data: (closure): the user data to pass to @func when called, or NULL |
| * |
| * Requests plugin installation without blocking. Once the plugins have been |
| * installed or installation has failed, @func will be called with the result |
| * of the installation and your provided @user_data pointer. |
| * |
| * This function requires a running GLib/Gtk main loop. If you are not |
| * running a GLib/Gtk main loop, make sure to regularly call |
| * g_main_context_iteration(NULL,FALSE). |
| * |
| * The installer strings that make up @detail are typically obtained by |
| * calling gst_missing_plugin_message_get_installer_detail() on missing-plugin |
| * messages that have been caught on a pipeline's bus or created by the |
| * application via the provided API, such as gst_missing_element_message_new(). |
| * |
| * It is possible to request the installation of multiple missing plugins in |
| * one go (as might be required if there is a demuxer for a certain format |
| * installed but no suitable video decoder and no suitable audio decoder). |
| * |
| * Returns: result code whether an external installer could be started |
| */ |
| |
| GstInstallPluginsReturn |
| gst_install_plugins_async (const gchar * const *details, |
| GstInstallPluginsContext * ctx, GstInstallPluginsResultFunc func, |
| gpointer user_data) |
| { |
| GstInstallPluginsAsyncHelper *helper; |
| GPid pid; |
| |
| g_return_val_if_fail (details != NULL, GST_INSTALL_PLUGINS_INTERNAL_FAILURE); |
| g_return_val_if_fail (func != NULL, GST_INSTALL_PLUGINS_INTERNAL_FAILURE); |
| |
| if (install_in_progress) |
| return GST_INSTALL_PLUGINS_INSTALL_IN_PROGRESS; |
| |
| /* if we can't access our helper, don't bother */ |
| if (!g_file_test (gst_install_plugins_get_helper (), |
| G_FILE_TEST_IS_EXECUTABLE)) |
| return GST_INSTALL_PLUGINS_HELPER_MISSING; |
| |
| if (!gst_install_plugins_spawn_child (details, ctx, &pid, NULL)) |
| return GST_INSTALL_PLUGINS_INTERNAL_FAILURE; |
| |
| helper = g_new (GstInstallPluginsAsyncHelper, 1); |
| helper->func = func; |
| helper->user_data = user_data; |
| |
| g_child_watch_add (pid, gst_install_plugins_installer_exited, helper); |
| |
| return GST_INSTALL_PLUGINS_STARTED_OK; |
| } |
| |
| /** |
| * gst_install_plugins_sync: |
| * @details: (array zero-terminated=1) (transfer none): NULL-terminated array |
| * of installer string details |
| * @ctx: (allow-none): a #GstInstallPluginsContext, or NULL |
| * |
| * Requests plugin installation and block until the plugins have been |
| * installed or installation has failed. |
| * |
| * This function should almost never be used, it only exists for cases where |
| * a non-GLib main loop is running and the user wants to run it in a separate |
| * thread and marshal the result back asynchronously into the main thread |
| * using the other non-GLib main loop. You should almost always use |
| * gst_install_plugins_async() instead of this function. |
| * |
| * Returns: the result of the installation. |
| */ |
| GstInstallPluginsReturn |
| gst_install_plugins_sync (const gchar * const *details, |
| GstInstallPluginsContext * ctx) |
| { |
| gint status; |
| |
| g_return_val_if_fail (details != NULL, GST_INSTALL_PLUGINS_INTERNAL_FAILURE); |
| |
| if (install_in_progress) |
| return GST_INSTALL_PLUGINS_INSTALL_IN_PROGRESS; |
| |
| /* if we can't access our helper, don't bother */ |
| if (!g_file_test (gst_install_plugins_get_helper (), |
| G_FILE_TEST_IS_EXECUTABLE)) |
| return GST_INSTALL_PLUGINS_HELPER_MISSING; |
| |
| if (!gst_install_plugins_spawn_child (details, ctx, NULL, &status)) |
| return GST_INSTALL_PLUGINS_INTERNAL_FAILURE; |
| |
| return gst_install_plugins_return_from_status (status); |
| } |
| |
| /** |
| * gst_install_plugins_return_get_name: |
| * @ret: the return status code |
| * |
| * Convenience function to return the descriptive string associated |
| * with a status code. This function returns English strings and |
| * should not be used for user messages. It is here only to assist |
| * in debugging. |
| * |
| * Returns: a descriptive string for the status code in @ret |
| */ |
| const gchar * |
| gst_install_plugins_return_get_name (GstInstallPluginsReturn ret) |
| { |
| switch (ret) { |
| case GST_INSTALL_PLUGINS_SUCCESS: |
| return "success"; |
| case GST_INSTALL_PLUGINS_NOT_FOUND: |
| return "not-found"; |
| case GST_INSTALL_PLUGINS_ERROR: |
| return "install-error"; |
| case GST_INSTALL_PLUGINS_CRASHED: |
| return "installer-exit-unclean"; |
| case GST_INSTALL_PLUGINS_PARTIAL_SUCCESS: |
| return "partial-success"; |
| case GST_INSTALL_PLUGINS_USER_ABORT: |
| return "user-abort"; |
| case GST_INSTALL_PLUGINS_STARTED_OK: |
| return "started-ok"; |
| case GST_INSTALL_PLUGINS_INTERNAL_FAILURE: |
| return "internal-failure"; |
| case GST_INSTALL_PLUGINS_HELPER_MISSING: |
| return "helper-missing"; |
| case GST_INSTALL_PLUGINS_INSTALL_IN_PROGRESS: |
| return "install-in-progress"; |
| case GST_INSTALL_PLUGINS_INVALID: |
| return "invalid"; |
| default: |
| break; |
| } |
| return "(UNKNOWN)"; |
| } |
| |
| /** |
| * gst_install_plugins_installation_in_progress: |
| * |
| * Checks whether plugin installation (initiated by this application only) |
| * is currently in progress. |
| * |
| * Returns: TRUE if plugin installation is in progress, otherwise FALSE |
| */ |
| gboolean |
| gst_install_plugins_installation_in_progress (void) |
| { |
| return install_in_progress; |
| } |
| |
| /** |
| * gst_install_plugins_supported: |
| * |
| * Checks whether plugin installation is likely to be supported by the |
| * current environment. This currently only checks whether the helper script |
| * that is to be provided by the distribution or operating system vendor |
| * exists. |
| * |
| * Returns: TRUE if plugin installation is likely to be supported. |
| */ |
| gboolean |
| gst_install_plugins_supported (void) |
| { |
| return g_file_test (gst_install_plugins_get_helper (), |
| G_FILE_TEST_IS_EXECUTABLE); |
| } |