1 /* GStreamer base utils library plugin install support for applications
2 * Copyright (C) 2007 Tim-Philipp Müller <tim centricular net>
3 * Copyright (C) 2006 Ryan Lortie <desrt desrt ca>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
22 * SECTION:gstbaseutilsinstallplugins
23 * @short_description: Missing plugin installation support for applications
26 * <title>Overview</title>
28 * Using this API, applications can request the installation of missing
29 * GStreamer plugins. These may be missing decoders/demuxers or encoders/muxers
30 * for a certain format, sources or sinks for a certain URI protocol
31 * (e.g. 'http'), or certain elements known by their element factory name
35 * Whether plugin installation is supported or not depends on the operating
36 * system and/or distribution in question. The vendor of the operating system
37 * needs to make sure the necessary hooks and mechanisms are in place for
38 * plugin installation to work. See below for more detailed information.
41 * From the application perspective, plugin installation is usually triggered
45 * when the application itself has found that it wants or needs to install a
49 * when the application has been notified by an element (such as playbin or
50 * decodebin) that one or more plugins are missing <emphasis>and</emphasis>
51 * the application has decided that it wants to install one or more of those
56 * <title>Detail Strings</title>
58 * The install functions in this section all take one or more 'detail strings'.
59 * These detail strings contain information about the type of plugin that
60 * needs to be installed (decoder, encoder, source, sink, or named element),
61 * and some additional information such GStreamer version used and a
62 * human-readable description of the component to install for user dialogs.
65 * Applications should not concern themselves with the composition of the
66 * string itself. They should regard the string as if it was a shared secret
67 * between GStreamer and the plugin installer application.
70 * Detail strings can be obtained using the function
71 * gst_missing_plugin_message_get_installer_detail() on a missing-plugin
72 * message. Such a message will either have been found by the application on
73 * a pipeline's #GstBus, or the application will have created it itself using
74 * gst_missing_element_message_new(), gst_missing_decoder_message_new(),
75 * gst_missing_encoder_message_new(), gst_missing_uri_sink_message_new(), or
76 * gst_missing_uri_source_message_new().
78 * <title>Plugin Installation from the Application Perspective</title>
80 * For each GStreamer element/plugin/component that should be installed, the
81 * application needs one of those 'installer detail' string mentioned in the
82 * previous section. This string can be obtained, as already mentioned above,
83 * from a missing-plugin message using the function
84 * gst_missing_plugin_message_get_installer_detail(). The missing-plugin
85 * message is either posted by another element and then found on the bus
86 * by the application, or the application has created it itself as described
90 * The application will then call gst_install_plugins_async(), passing a
91 * #NULL-terminated array of installer detail strings, and a function that
92 * should be called when the installation of the plugins has finished
93 * (successfully or not). Optionally, a #GstInstallPluginsContext created
94 * with gst_install_plugins_context_new() may be passed as well. This way
95 * additional optional arguments like the application window's XID can be
96 * passed to the external installer application.
99 * gst_install_plugins_async() will return almost immediately, with the
100 * return code indicating whether plugin installation was started or not.
101 * If the necessary hooks for plugin installation are in place and an
102 * external installer application has in fact been called, the passed in
103 * function will be called with a result code as soon as the external installer
104 * has finished. If the result code indicates that new plugins have been
105 * installed, the application will want to call gst_update_registry() so the
106 * run-time plugin registry is updated and the new plugins are made available
107 * to the application.
109 * A Gtk/GLib main loop must be running in order for the result function to
110 * be called when the external installer has finished. If this is not the case,
111 * make sure to regularly call
113 * g_main_context_iteration (NULL,FALSE);
118 * <title>Plugin Installation from the Vendor/Distribution Perspective</title>
120 * <emphasis>1. Installer hook</emphasis>
123 * When GStreamer applications initiate plugin installation via
124 * gst_install_plugins_async() or gst_install_plugins_sync(), a pre-defined
125 * helper application will be called.
128 * The exact path of the helper application to be called is set at compile
129 * time, usually by the <literal>./configure</literal> script based on the
130 * install prefix. For a normal package build into the <literal>/usr</literal>
131 * prefix, this will usually default to
132 * <filename>/usr/libexec/gst-install-plugins-helper</filename> or
133 * <filename>/usr/lib/gst-install-plugins-helper</filename>.
136 * Vendors/distros who want to support GStreamer plugin installation should
137 * either provide such a helper script/application or use the
138 * <literal>./configure</literal> option
139 * <literal>--with-install-plugins-helper=/path/to/installer</literal> to
140 * make GStreamer call an installer of their own directly.
143 * It is strongly recommended that vendors provide a small helper application
144 * as interlocutor to the real installer though, even more so if command line
145 * argument munging is required to transform the command line arguments
146 * passed by GStreamer to the helper application into arguments that are
147 * understood by the reeal installer.
150 * The helper application path defined at compile time can be overriden at
151 * runtime by setting the <envar>GST_INSTALL_PLUGINS_HELPER</envar>
152 * environment variable. This can be useful for testing/debugging purposes.
155 * <emphasis>2. Arguments passed to the install helper</emphasis>
158 * GStreamer will pass the following arguments to the install helper (this is
159 * in addition to the path of the executable itself, which is by convention
163 * none to many optional arguments in the form of
164 * <literal>--foo-bar=val</literal>. Example:
165 * <literal>--transient-for=XID</literal> where XID is the X Window ID of
166 * the main window of the calling application (so the installer can make
167 * itself transient to that window). Unknown optional arguments should
168 * be ignored by the installer.
171 * one 'installer detail string' argument for each plugin to be installed;
172 * these strings will have a <literal>gstreamer.net</literal> prefix; the
173 * exact format of the detail string is explained below
178 * <emphasis>3. Detail string describing the missing plugin</emphasis>
181 * The string is in UTF-8 encoding and is made up of several fields, separated
182 * by '|' characters (but neither the first nor the last character is a '|').
186 * plugin system identifier, ie. "gstreamer.net"
188 * This identifier determines the format of the rest of the detail string.
189 * Automatic plugin installers should not process detail strings with
190 * unknown identifiers. This allows other plugin-based libraries to use
191 * the same mechanism for their automatic plugin installation needs, or
192 * for the format to be changed should it turn out to be insufficient.
195 * plugin system version, e.g. "0.10"
197 * This is required so that when there is a GStreamer-0.12 or GStreamer-1.0
198 * at some point in future, the different major versions can still co-exist
199 * and use the same plugin install mechanism in the same way.
202 * application identifier, e.g. "totem"
204 * This may also be in the form of "pid/12345" if the program name can't
205 * be obtained for some reason.
208 * human-readable localised description of the required component,
209 * e.g. "Vorbis audio decoder"
212 * identifier string for the required component (see below for details about
213 * how to map this to the package/plugin that needs installing), e.g.
216 * urisource-$(PROTOCOL_REQUIRED), e.g. urisource-http or urisource-mms
219 * element-$(ELEMENT_REQUIRED), e.g. element-ffmpegcolorspace
222 * decoder-$(CAPS_REQUIRED), e.g. decoder-audio/x-vorbis or
223 * decoder-application/ogg
226 * encoder-$(CAPS_REQUIRED), e.g. encoder-audio/x-vorbis
231 * optional further fields not yet specified
236 * An entire ID string might then look like this, for example:
238 * gstreamer.net|0.10|totem|Vorbis audio decoder|decoder-audio/x-vorbis
242 * Plugin installers parsing this ID string should expect further fields also
243 * separated by '|' symbols and either ignore them, warn the user, or error
244 * out when encountering them.
247 * <emphasis>4. Exit codes the installer should return</emphasis>
250 * The installer should return one of the following exit codes when it exits:
253 * 0 if all of the requested plugins could be installed
254 * (#GST_INSTALL_PLUGINS_SUCCESS)
257 * 1 if no appropriate installation candidate for any of the requested
258 * plugins could be found. Only return this if nothing has been installed
259 * (#GST_INSTALL_PLUGINS_NOT_FOUND)
262 * 2 if an error occured during the installation. The application will
263 * assume that the user will already have seen an error message by the
264 * installer in this case and will usually not show another one
265 * (#GST_INSTALL_PLUGINS_ERROR)
268 * 3 if some of the requested plugins could be installed, but not all
269 * (#GST_INSTALL_PLUGINS_PARTIAL_SUCCESS)
272 * 4 if the user aborted the installation (#GST_INSTALL_PLUGINS_USER_ABORT)
277 * <emphasis>5. How to map the required detail string to packages</emphasis>
280 * It is up to the vendor to find mechanism to map required components from
281 * the detail string to the actual packages/plugins to install. This could
282 * be a hardcoded list of mappings, for example, or be part of the packaging
286 * GStreamer plugin files can be introspected for this information. The
287 * <literal>gst-inspect</literal> utility has a special command line option
288 * that will output information similar to what is required. For example
290 * $ gst-inspect-0.10 --print-plugin-auto-install-info /path/to/libgstvorbis.so
292 * should output something along the lines of
294 * decoder-audio/x-vorbis
297 * element-vorbisparse
299 * encoder-audio/x-vorbis
301 * Note that in the encoder and decoder case the introspected caps can be more
302 * complex with additional fields, e.g.
303 * <literal>audio/mpeg,mpegversion=(int){2,4}</literal>, so they will not
304 * always exactly match the caps wanted by the application. It is up to the
305 * installer to deal with this (either by doing proper caps intersection using
306 * the GStreamer #GstCaps API, or by only taking into account the media type).
309 * Another potential source of problems are plugins such as ladspa or
310 * libvisual where the list of elements depends on the installed
311 * ladspa/libvisual plugins at the time. This is also up to the distribution
312 * to handle (but usually not relevant for playback applications).
321 #include "install-plugins.h"
323 #include <gst/gstinfo.h>
325 #ifdef HAVE_SYS_TYPES_H
326 #include <sys/types.h>
329 #ifdef HAVE_SYS_WAIT_H
330 #include <sys/wait.h>
333 /* best effort to make things compile and possibly even work on win32 */
335 # define WEXITSTATUS(status) ((((guint)(status)) & 0xff00) >> 8)
338 # define WIFEXITED(status) ((((guint)(status)) & 0x7f) == 0)
341 static gboolean install_in_progress; /* FALSE */
344 struct _GstInstallPluginsContext
350 * gst_install_plugins_context_set_xid:
351 * @ctx: a #GstInstallPluginsContext
352 * @xid: the XWindow ID (XID) of the top-level application
354 * This function is for X11-based applications (such as most Gtk/Qt
355 * applications on linux/unix) only. You can use it to tell the external
356 * the XID of your main application window, so the installer can make its
357 * own window transient to your application windonw during the installation.
359 * If set, the XID will be passed to the installer via a --transient-for=XID
360 * command line option.
362 * Gtk+/Gnome application should be able to obtain the XID of the top-level
365 * ##include <gtk/gtk.h>
366 * ##ifdef GDK_WINDOWING_X11
367 * ##include <gdk/gdkx.h>
370 * ##ifdef GDK_WINDOWING_X11
371 * xid = GDK_WINDOW_XWINDOW (GTK_WIDGET (application_window)->window);
379 gst_install_plugins_context_set_xid (GstInstallPluginsContext * ctx, guint xid)
381 g_return_if_fail (ctx != NULL);
387 * gst_install_plugins_context_new:
389 * Creates a new #GstInstallPluginsContext.
391 * Returns: a new #GstInstallPluginsContext. Free with
392 * gst_install_plugins_context_free() when no longer needed
396 GstInstallPluginsContext *
397 gst_install_plugins_context_new (void)
399 return g_new0 (GstInstallPluginsContext, 1);
403 * gst_install_plugins_context_free:
404 * @ctx: a #GstInstallPluginsContext
406 * Frees a #GstInstallPluginsContext.
411 gst_install_plugins_context_free (GstInstallPluginsContext * ctx)
413 g_return_if_fail (ctx != NULL);
419 gst_install_plugins_get_helper (void)
423 helper = g_getenv ("GST_INSTALL_PLUGINS_HELPER");
425 helper = GST_INSTALL_PLUGINS_HELPER;
427 GST_LOG ("Using plugin install helper '%s'", helper);
432 gst_install_plugins_spawn_child (gchar ** details,
433 GstInstallPluginsContext * ctx, GPid * child_pid, gint * exit_status)
438 gchar **argv, xid_str[64] = { 0, };
440 arr = g_ptr_array_new ();
442 /* argv[0] = helper path */
443 g_ptr_array_add (arr, (gchar *) gst_install_plugins_get_helper ());
445 /* add any additional command line args from the context */
446 if (ctx != NULL && ctx->xid != 0) {
447 g_snprintf (xid_str, sizeof (xid_str), "--transient-for=%u", ctx->xid);
448 g_ptr_array_add (arr, xid_str);
451 /* finally, add the detail strings */
452 while (details != NULL && details[0] != NULL) {
453 g_ptr_array_add (arr, details[0]);
457 /* and NULL-terminate */
458 g_ptr_array_add (arr, NULL);
460 argv = (gchar **) arr->pdata;
462 if (child_pid == NULL && exit_status != NULL) {
463 install_in_progress = TRUE;
464 ret = g_spawn_sync (NULL, argv, NULL, (GSpawnFlags) 0, NULL, NULL,
465 NULL, NULL, exit_status, &err);
466 install_in_progress = FALSE;
467 } else if (child_pid != NULL && exit_status == NULL) {
468 install_in_progress = TRUE;
469 ret = g_spawn_async (NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD, NULL,
470 NULL, child_pid, &err);
472 g_assert_not_reached ();
476 GST_ERROR ("Error spawning plugin install helper: %s", err->message);
480 g_ptr_array_free (arr, TRUE);
484 static GstInstallPluginsReturn
485 gst_install_plugins_return_from_status (gint status)
487 GstInstallPluginsReturn ret;
489 /* did we exit cleanly? */
490 if (!WIFEXITED (status)) {
491 ret = GST_INSTALL_PLUGINS_CRASHED;
493 ret = (GstInstallPluginsReturn) WEXITSTATUS (status);
495 /* did the helper return an invalid status code? */
496 if ((ret < 0 || ret >= GST_INSTALL_PLUGINS_STARTED_OK) &&
497 ret != GST_INSTALL_PLUGINS_INTERNAL_FAILURE) {
498 ret = GST_INSTALL_PLUGINS_INVALID;
502 GST_LOG ("plugin installer exited with status 0x%04x = %s", status,
503 gst_install_plugins_return_get_name (ret));
510 GstInstallPluginsResultFunc func;
512 } GstInstallPluginsAsyncHelper;
515 gst_install_plugins_installer_exited (GPid pid, gint status, gpointer data)
517 GstInstallPluginsAsyncHelper *helper;
518 GstInstallPluginsReturn ret;
520 install_in_progress = FALSE;
522 helper = (GstInstallPluginsAsyncHelper *) data;
523 ret = gst_install_plugins_return_from_status (status);
525 GST_LOG ("calling plugin install result function %p", helper->func);
526 helper->func (ret, helper->user_data);
532 * gst_install_plugins_async:
533 * @details: NULL-terminated array of installer string details (see below)
534 * @ctx: a #GstInstallPluginsContext, or NULL
535 * @func: the function to call when the installer program returns
536 * @user_data: the user data to pass to @func when called, or NULL
538 * Requests plugin installation without blocking. Once the plugins have been
539 * installed or installation has failed, @func will be called with the result
540 * of the installation and your provided @user_data pointer.
542 * This function requires a running GLib/Gtk main loop. If you are not
543 * running a GLib/Gtk main loop, make sure to regularly call
544 * g_main_context_iteration(NULL,FALSE).
546 * The installer strings that make up @detail are typically obtained by
547 * calling gst_missing_plugin_message_get_installer_detail() on missing-plugin
548 * messages that have been caught on a pipeline's bus or created by the
549 * application via the provided API, such as gst_missing_element_message_new().
551 * Returns: result code whether an external installer could be started
556 GstInstallPluginsReturn
557 gst_install_plugins_async (gchar ** details, GstInstallPluginsContext * ctx,
558 GstInstallPluginsResultFunc func, gpointer user_data)
560 GstInstallPluginsAsyncHelper *helper;
563 g_return_val_if_fail (details != NULL, GST_INSTALL_PLUGINS_INTERNAL_FAILURE);
564 g_return_val_if_fail (func != NULL, GST_INSTALL_PLUGINS_INTERNAL_FAILURE);
566 if (install_in_progress)
567 return GST_INSTALL_PLUGINS_INSTALL_IN_PROGRESS;
569 /* if we can't access our helper, don't bother */
570 if (!g_file_test (gst_install_plugins_get_helper (),
571 G_FILE_TEST_IS_EXECUTABLE))
572 return GST_INSTALL_PLUGINS_HELPER_MISSING;
574 if (!gst_install_plugins_spawn_child (details, ctx, &pid, NULL))
575 return GST_INSTALL_PLUGINS_INTERNAL_FAILURE;
577 helper = g_new (GstInstallPluginsAsyncHelper, 1);
579 helper->user_data = user_data;
581 g_child_watch_add (pid, gst_install_plugins_installer_exited, helper);
583 return GST_INSTALL_PLUGINS_STARTED_OK;
587 * gst_install_plugins_sync:
588 * @details: NULL-terminated array of installer string details
589 * @ctx: a #GstInstallPluginsContext, or NULL
591 * Requests plugin installation and block until the plugins have been
592 * installed or installation has failed.
594 * This function should almost never be used, it only exists for cases where
595 * a non-GLib main loop is running and the user wants to run it in a separate
596 * thread and marshal the result back asynchronously into the main thread
597 * using the other non-GLib main loop. You should almost always use
598 * gst_install_plugins_async() instead of this function.
600 * Returns: the result of the installation.
604 GstInstallPluginsReturn
605 gst_install_plugins_sync (gchar ** details, GstInstallPluginsContext * ctx)
609 g_return_val_if_fail (details != NULL, GST_INSTALL_PLUGINS_INTERNAL_FAILURE);
611 if (install_in_progress)
612 return GST_INSTALL_PLUGINS_INSTALL_IN_PROGRESS;
614 /* if we can't access our helper, don't bother */
615 if (!g_file_test (gst_install_plugins_get_helper (),
616 G_FILE_TEST_IS_EXECUTABLE))
617 return GST_INSTALL_PLUGINS_HELPER_MISSING;
619 if (!gst_install_plugins_spawn_child (details, ctx, NULL, &status))
620 return GST_INSTALL_PLUGINS_INTERNAL_FAILURE;
622 return gst_install_plugins_return_from_status (status);
626 * gst_install_plugins_return_get_name:
627 * @ret: the return status code
629 * Convenience function to return the descriptive string associated
630 * with a status code. This function returns English strings and
631 * should not be used for user messages. It is here only to assist
634 * Returns: a descriptive string for the status code in @ret
639 gst_install_plugins_return_get_name (GstInstallPluginsReturn ret)
642 case GST_INSTALL_PLUGINS_SUCCESS:
644 case GST_INSTALL_PLUGINS_NOT_FOUND:
646 case GST_INSTALL_PLUGINS_ERROR:
647 return "install-error";
648 case GST_INSTALL_PLUGINS_CRASHED:
649 return "installer-exit-unclean";
650 case GST_INSTALL_PLUGINS_PARTIAL_SUCCESS:
651 return "partial-success";
652 case GST_INSTALL_PLUGINS_USER_ABORT:
654 case GST_INSTALL_PLUGINS_STARTED_OK:
656 case GST_INSTALL_PLUGINS_INTERNAL_FAILURE:
657 return "internal-failure";
658 case GST_INSTALL_PLUGINS_HELPER_MISSING:
659 return "helper-missing";
660 case GST_INSTALL_PLUGINS_INSTALL_IN_PROGRESS:
661 return "install-in-progress";
662 case GST_INSTALL_PLUGINS_INVALID:
671 * gst_install_plugins_installation_in_progress:
673 * Checks whether plugin installation (initiated by this application only)
674 * is currently in progress.
676 * Returns: TRUE if plugin installation is in progress, otherwise FALSE
681 gst_install_plugins_installation_in_progress (void)
683 return install_in_progress;