X-Git-Url: http://vcs.maemo.org/git/?a=blobdiff_plain;f=gst-plugins-base-subtitles0.10%2Fgst-libs%2Fgst%2Fpbutils%2Finstall-plugins.c;fp=gst-plugins-base-subtitles0.10%2Fgst-libs%2Fgst%2Fpbutils%2Finstall-plugins.c;h=9b5f45e1df15d5108c9af756009a7903477add08;hb=57ba96e291a055f69dbfd4ae9f1ae2390e36986e;hp=0000000000000000000000000000000000000000;hpb=be2c98fb83895d10ac44af7b9a9c3e00ca54bf49;p=mafwsubrenderer diff --git a/gst-plugins-base-subtitles0.10/gst-libs/gst/pbutils/install-plugins.c b/gst-plugins-base-subtitles0.10/gst-libs/gst/pbutils/install-plugins.c new file mode 100644 index 0000000..9b5f45e --- /dev/null +++ b/gst-plugins-base-subtitles0.10/gst-libs/gst/pbutils/install-plugins.c @@ -0,0 +1,783 @@ +/* GStreamer base utils library plugin install support for applications + * Copyright (C) 2007 Tim-Philipp Müller + * Copyright (C) 2006 Ryan Lortie + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/** + * SECTION:gstpbutilsinstallplugins + * @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 + * + * + * + * Detail Strings + * + * 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(). + * + * Plugin Installation from the Application Perspective + * + * 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 + * + * g_main_context_iteration (NULL,FALSE); + * + * from your code. + * + * + * Plugin Installation from the Vendor/Distribution Perspective + * + * 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-ffmpegcolorspace + * + * + * 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-0.10 --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 + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#ifdef HAVE_SYS_WAIT_H +#include +#endif + +#include + +/* 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 +{ + guint xid; +}; + +/** + * 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 + * ... + * + * + * Since: 0.10.12 + */ +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 + * + * Since: 0.10.12 + */ +GstInstallPluginsContext * +gst_install_plugins_context_new (void) +{ + return g_new0 (GstInstallPluginsContext, 1); +} + +/** + * gst_install_plugins_context_free: + * @ctx: a #GstInstallPluginsContext + * + * Frees a #GstInstallPluginsContext. + * + * Since: 0.10.12 + */ +void +gst_install_plugins_context_free (GstInstallPluginsContext * ctx) +{ + g_return_if_fail (ctx != NULL); + + g_free (ctx); +} + +static GstInstallPluginsContext * +gst_install_plugins_context_copy (GstInstallPluginsContext * ctx) +{ + GstInstallPluginsContext *ret; + + ret = gst_install_plugins_context_new (); + ret->xid = ctx->xid; + + return ret; +} + +GType +gst_install_plugins_context_get_type (void) +{ + static GType gst_ipc_type = 0; + + if (G_UNLIKELY (gst_ipc_type == 0)) { + gst_ipc_type = g_boxed_type_register_static ("GstInstallPluginsContext", + (GBoxedCopyFunc) gst_install_plugins_context_copy, + (GBoxedFreeFunc) gst_install_plugins_context_free); + } + return gst_ipc_type; +} + +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 (gchar ** details, + GstInstallPluginsContext * ctx, GPid * child_pid, gint * exit_status) +{ + GPtrArray *arr; + gboolean ret; + GError *err = NULL; + gchar **argv, xid_str[64] = { 0, }; + + arr = g_ptr_array_new (); + + /* argv[0] = helper path */ + g_ptr_array_add (arr, (gchar *) gst_install_plugins_get_helper ()); + + /* add any additional command line args from the context */ + if (ctx != NULL && ctx->xid != 0) { + g_snprintf (xid_str, sizeof (xid_str), "--transient-for=%u", ctx->xid); + g_ptr_array_add (arr, xid_str); + } + + /* 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, 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_free (arr, TRUE); + 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 ((ret < 0 || 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: NULL-terminated array of installer string details (see below) + * @ctx: 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 + * + * Since: 0.10.12 + */ + +GstInstallPluginsReturn +gst_install_plugins_async (gchar ** 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: NULL-terminated array of installer string details + * @ctx: 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. + * + * Since: 0.10.12 + */ +GstInstallPluginsReturn +gst_install_plugins_sync (gchar ** 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 + * + * Since: 0.10.12 + */ +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 + * + * Since: 0.10.12 + */ +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. + * + * Since: 0.10.15 + */ +gboolean +gst_install_plugins_supported (void) +{ + return g_file_test (gst_install_plugins_get_helper (), + G_FILE_TEST_IS_EXECUTABLE); +}