+2007-02-02 Tim-Philipp Müller <tim at centricular dot net>
+
+ * gst-libs/gst/utils/Makefile.am:
+ * gst-libs/gst/utils/base-utils.h:
+ * gst-libs/gst/utils/install-plugins.c:
+ (gst_install_plugins_context_set_xid),
+ (gst_install_plugins_context_new),
+ (gst_install_plugins_context_free),
+ (gst_install_plugins_get_helper),
+ (gst_install_plugins_spawn_child),
+ (gst_install_plugins_return_from_status),
+ (gst_install_plugins_installer_exited),
+ (gst_install_plugins_async), (gst_install_plugins_sync),
+ (gst_install_plugins_return_get_name),
+ (gst_install_plugins_installation_in_progress):
+ * gst-libs/gst/utils/install-plugins.h:
+ API: add API for applications to initiate installation of missing
+ plugins, ie. gst_install_plugins_async() primarily.
+ Based on libgimme-codec by Ryan Lortie.
+
+ * configure.ac:
+ Add --with-install-plugins-helper configure option so distros can specify
+ the path of the helper script or program to call when plugin installation
+ is requested (distros: please do any argument munging in this helper
+ script instead of patching GStreamer to pass arguments differently
+ to another program directly).
+
+ * docs/libs/gst-plugins-base-libs-docs.sgml:
+ * docs/libs/gst-plugins-base-libs-sections.txt:
+ Build and document new API.
+
+ * tests/check/libs/utils.c: (result_cb),
+ (test_base_utils_install_plugins_do_callout), (GST_START_TEST),
+ (libgstbaseutils_suite):
+ Some simple checks for the new API.
+
2007-02-02 Tim-Philipp Müller <tim at centricular dot net>
* tests/check/elements/audioconvert.c: (test_float_conversion):
GST_ARG_WITH_PACKAGE_NAME
GST_ARG_WITH_PACKAGE_ORIGIN
+dnl let distro override plugin install helper path
+AC_ARG_WITH(install-plugins-helper,
+ AC_HELP_STRING([--with-install-plugins-helper],
+ [specify path of helper script to call to install plugins]),
+ [
+ case "${withval}" in
+ yes) AC_MSG_ERROR(bad value ${withval} for --with-install-plugins-helper) ;;
+ no) AC_MSG_ERROR(bad value ${withval} for --with-install-plugins-helper) ;;
+ *) GST_INSTALL_PLUGINS_HELPER="${withval}" ;;
+ esac
+ ],
+ [
+ dnl Default value
+ AS_AC_EXPAND(GST_INSTALL_PLUGINS_HELPER,${libexecdir}/gst-install-plugins-helper)
+ ]
+)
+AC_MSG_NOTICE(Using $GST_INSTALL_PLUGINS_HELPER as plugin install helper)
+AC_DEFINE_UNQUOTED(GST_INSTALL_PLUGINS_HELPER, "$GST_INSTALL_PLUGINS_HELPER",
+ [plugin install helper script])
+AC_SUBST(GST_INSTALL_PLUGINS_HELPER)
+
+
dnl these are all the gst plug-ins, compilable without additional libs
GST_PLUGINS_ALL="\
adder \
HAVE_SYS_SOCKET_H="yes", HAVE_SYS_SOCKET_H="no")
AM_CONDITIONAL(HAVE_SYS_SOCKET_H, test "x$HAVE_SYS_SOCKET_H" = "xyes")
-dnl used in gst-libs/gst/utils
-AC_CHECK_HEADERS([process.h])
+dnl used in gst-libs/gst/utils and associated unit test
+AC_CHECK_HEADERS([process.h sys/types.h sys/wait.h sys/stat.h])
dnl ffmpegcolorspace includes _stdint.h
dnl also, Windows does not have long long
<!ENTITY GstBaseUtils SYSTEM "xml/gstbaseutils.xml">
<!ENTITY GstBaseUtilsDescriptions SYSTEM "xml/gstbaseutilsdescriptions.xml">
<!ENTITY GstBaseUtilsMissingPlugins SYSTEM "xml/gstbaseutilsmissingplugins.xml">
+<!ENTITY GstBaseUtilsInstallPlugins SYSTEM "xml/gstbaseutilsinstallplugins.xml">
<!-- video -->
<!ENTITY GstVideo SYSTEM "xml/gstvideo.xml">
<!ENTITY GstVideoFilter SYSTEM "xml/gstvideofilter.xml">
&GstBaseUtils;
&GstBaseUtilsDescriptions;
&GstBaseUtilsMissingPlugins;
+ &GstBaseUtilsInstallPlugins;
</chapter>
<chapter id="gstreamer-video">
gst_missing_element_message_new
</SECTION>
+<SECTION>
+<FILE>gstbaseutilsinstallplugins</FILE>
+<INCLUDE>gst/utils/install-plugins.h</INCLUDE>
+<SUBSECTION>
+GstInstallPluginsReturn
+GstInstallPluginsResultFunc
+gst_install_plugins_async
+gst_install_plugins_sync
+gst_install_plugins_return_get_name
+gst_install_plugins_installation_in_progress
+<SUBSECTION>
+GstInstallPluginsContext
+gst_install_plugins_context_new
+gst_install_plugins_context_free
+gst_install_plugins_context_set_xid
+</SECTION>
+
<SECTION>
<FILE>gstbaseutilsdescriptions</FILE>
<INCLUDE>gst/utils/descriptions.h</INCLUDE>
base-utils.h \
descriptions.c \
descriptions.h \
+ install-plugins.c \
+ install-plugins.h \
missing-plugins.c \
missing-plugins.h
libgstbaseutils_@GST_MAJORMINOR@include_HEADERS = \
base-utils.h \
descriptions.h \
+ install-plugins.h \
missing-plugins.h
libgstbaseutils_@GST_MAJORMINOR@_la_LIBADD = $(GST_LIBS)
--- /dev/null
+/* 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#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
+
+/* 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
+ * the XID of your main application window, so the installer can make its
+ * own window transient to your application windonw 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:
+ * <programlisting>
+ * ##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
+ * ...
+ * </programlisting>
+ *
+ * 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 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
+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 */
+ while (details != NULL && details[0] != NULL) {
+ 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_assert_not_reached ();
+ }
+
+ if (!ret) {
+ GST_WARNING ("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: the function to call when the installer program returns
+ * @user_data: 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().
+ *
+ * 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;
+}
--- /dev/null
+/* 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GST_BASE_UTILS_INSTALL_PLUGINS_H__
+#define __GST_BASE_UTILS_INSTALL_PLUGINS_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+/*
+ * functions for use by applications to initiate installation of missing plugins
+ */
+
+/**
+ * GstInstallPluginsReturn:
+ * @GST_INSTALL_PLUGINS_SUCCESS: all of the requested plugins could be
+ * installed
+ * @GST_INSTALL_PLUGINS_NOT_FOUND: no appropriate installation candidate for
+ * any of the requested plugins could be found. Only return this if nothing
+ * has been installed. Return #GST_INSTALL_PLUGINS_PARTIAL_SUCCESS if
+ * some (but not all) of the requested plugins could be installed.
+ * @GST_INSTALL_PLUGINS_ERROR: an error occured during the installation. If
+ * this happens, the user has already seen an error message and another
+ * one should not be displayed
+ * @GST_INSTALL_PLUGINS_CRASHED: the installer had an unclean exit code
+ * (ie. death by signal)
+ * @GST_INSTALL_PLUGINS_PARTIAL_SUCCESS: some of the requested plugins could
+ * be installed, but not all
+ * @GST_INSTALL_PLUGINS_USER_ABORT: the user has aborted the installation
+ * @GST_INSTALL_PLUGINS_INVALID: the helper returned an invalid status code
+ * @GST_INSTALL_PLUGINS_STARTED_OK: returned by gst_install_plugins_async() to
+ * indicate that everything went fine so far and the provided callback
+ * will be called with the result of the installation later
+ * @GST_INSTALL_PLUGINS_INTERNAL_FAILURE: some internal failure has
+ * occured when trying to start the installer
+ * @GST_INSTALL_PLUGINS_HELPER_MISSING: the helper script to call the
+ * actual installer is not installed
+ * @GST_INSTALL_PLUGINS_INSTALL_IN_PROGRESS: a previously-started plugin
+ * installation is still in progress, try again later
+ *
+ * Result codes returned by gst_install_plugins_async() and
+ * gst_install_plugins_sync(), and also the result code passed to the
+ * #GstInstallPluginsResultFunc specified with gst_install_plugin_async().
+ *
+ * These codes indicate success or failure of starting an external installer
+ * program and to what extent the requested plugins could be installed.
+ *
+ * Since: 0.10.12
+ */
+typedef enum {
+ /* Return codes from the installer. Returned by gst_install_plugins_sync(),
+ * or passed as result code to your #GstInstallPluginsResultFunc */
+ GST_INSTALL_PLUGINS_SUCCESS = 0,
+ GST_INSTALL_PLUGINS_NOT_FOUND = 1,
+ GST_INSTALL_PLUGINS_ERROR = 2,
+ GST_INSTALL_PLUGINS_PARTIAL_SUCCESS = 3,
+ GST_INSTALL_PLUGINS_USER_ABORT = 4,
+
+ /* Returned by gst_install_plugins_sync(), or passed as result code to your
+ * #GstInstallPluginsResultFunc */
+ GST_INSTALL_PLUGINS_CRASHED = 100,
+ GST_INSTALL_PLUGINS_INVALID,
+
+ /* Return codes from starting the external helper, may be returned by both
+ * gst_install_plugins_sync() and gst_install_plugins_async(), but should
+ * never be seen by a #GstInstallPluginsResultFunc */
+ GST_INSTALL_PLUGINS_STARTED_OK = 200,
+ GST_INSTALL_PLUGINS_INTERNAL_FAILURE,
+ GST_INSTALL_PLUGINS_HELPER_MISSING,
+ GST_INSTALL_PLUGINS_INSTALL_IN_PROGRESS
+} GstInstallPluginsReturn;
+
+/**
+ * GstInstallPluginsContext:
+ *
+ * Opaque context structure for the plugin installation. Use the provided
+ * API to set details on it.
+ *
+ * Since: 0.10.12
+ */
+typedef struct _GstInstallPluginsContext GstInstallPluginsContext;
+
+GstInstallPluginsContext * gst_install_plugins_context_new (void);
+
+void gst_install_plugins_context_free (GstInstallPluginsContext * ctx);
+
+void gst_install_plugins_context_set_xid (GstInstallPluginsContext * ctx,
+ guint xid);
+
+/**
+ * GstInstallPluginsResultFunc:
+ * @result: whether the installation of the requested plugins succeeded or not
+ * @user_data: the user data passed to gst_install_plugins_async()
+ *
+ * The prototype of the callback function that will be called once the
+ * external plugin installer program has returned. You only need to provide
+ * a callback function if you are using the asynchronous interface.
+ *
+ * Since: 0.10.12
+ */
+typedef void (*GstInstallPluginsResultFunc) (GstInstallPluginsReturn result,
+ gpointer user_data);
+
+GstInstallPluginsReturn gst_install_plugins_async (gchar ** details,
+ GstInstallPluginsContext * ctx,
+ GstInstallPluginsResultFunc func,
+ gpointer user_data);
+
+GstInstallPluginsReturn gst_install_plugins_sync (gchar ** details,
+ GstInstallPluginsContext * ctx);
+
+const gchar * gst_install_plugins_return_get_name (GstInstallPluginsReturn ret);
+
+gboolean gst_install_plugins_installation_in_progress (void);
+
+G_END_DECLS
+
+#endif /* __GST_BASE_UTILS_INSTALL_PLUGINS_H__ */
+
#include <gst/utils/descriptions.h>
#include <gst/utils/missing-plugins.h>
+#include <gst/utils/install-plugins.h>
G_BEGIN_DECLS
base-utils.h \
descriptions.c \
descriptions.h \
+ install-plugins.c \
+ install-plugins.h \
missing-plugins.c \
missing-plugins.h
libgstbaseutils_@GST_MAJORMINOR@include_HEADERS = \
base-utils.h \
descriptions.h \
+ install-plugins.h \
missing-plugins.h
libgstbaseutils_@GST_MAJORMINOR@_la_LIBADD = $(GST_LIBS)
#include <gst/utils/descriptions.h>
#include <gst/utils/missing-plugins.h>
+#include <gst/utils/install-plugins.h>
G_BEGIN_DECLS
--- /dev/null
+/* 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#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
+
+/* 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
+ * the XID of your main application window, so the installer can make its
+ * own window transient to your application windonw 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:
+ * <programlisting>
+ * ##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
+ * ...
+ * </programlisting>
+ *
+ * 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 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
+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 */
+ while (details != NULL && details[0] != NULL) {
+ 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_assert_not_reached ();
+ }
+
+ if (!ret) {
+ GST_WARNING ("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: the function to call when the installer program returns
+ * @user_data: 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().
+ *
+ * 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;
+}
--- /dev/null
+/* 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GST_BASE_UTILS_INSTALL_PLUGINS_H__
+#define __GST_BASE_UTILS_INSTALL_PLUGINS_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+/*
+ * functions for use by applications to initiate installation of missing plugins
+ */
+
+/**
+ * GstInstallPluginsReturn:
+ * @GST_INSTALL_PLUGINS_SUCCESS: all of the requested plugins could be
+ * installed
+ * @GST_INSTALL_PLUGINS_NOT_FOUND: no appropriate installation candidate for
+ * any of the requested plugins could be found. Only return this if nothing
+ * has been installed. Return #GST_INSTALL_PLUGINS_PARTIAL_SUCCESS if
+ * some (but not all) of the requested plugins could be installed.
+ * @GST_INSTALL_PLUGINS_ERROR: an error occured during the installation. If
+ * this happens, the user has already seen an error message and another
+ * one should not be displayed
+ * @GST_INSTALL_PLUGINS_CRASHED: the installer had an unclean exit code
+ * (ie. death by signal)
+ * @GST_INSTALL_PLUGINS_PARTIAL_SUCCESS: some of the requested plugins could
+ * be installed, but not all
+ * @GST_INSTALL_PLUGINS_USER_ABORT: the user has aborted the installation
+ * @GST_INSTALL_PLUGINS_INVALID: the helper returned an invalid status code
+ * @GST_INSTALL_PLUGINS_STARTED_OK: returned by gst_install_plugins_async() to
+ * indicate that everything went fine so far and the provided callback
+ * will be called with the result of the installation later
+ * @GST_INSTALL_PLUGINS_INTERNAL_FAILURE: some internal failure has
+ * occured when trying to start the installer
+ * @GST_INSTALL_PLUGINS_HELPER_MISSING: the helper script to call the
+ * actual installer is not installed
+ * @GST_INSTALL_PLUGINS_INSTALL_IN_PROGRESS: a previously-started plugin
+ * installation is still in progress, try again later
+ *
+ * Result codes returned by gst_install_plugins_async() and
+ * gst_install_plugins_sync(), and also the result code passed to the
+ * #GstInstallPluginsResultFunc specified with gst_install_plugin_async().
+ *
+ * These codes indicate success or failure of starting an external installer
+ * program and to what extent the requested plugins could be installed.
+ *
+ * Since: 0.10.12
+ */
+typedef enum {
+ /* Return codes from the installer. Returned by gst_install_plugins_sync(),
+ * or passed as result code to your #GstInstallPluginsResultFunc */
+ GST_INSTALL_PLUGINS_SUCCESS = 0,
+ GST_INSTALL_PLUGINS_NOT_FOUND = 1,
+ GST_INSTALL_PLUGINS_ERROR = 2,
+ GST_INSTALL_PLUGINS_PARTIAL_SUCCESS = 3,
+ GST_INSTALL_PLUGINS_USER_ABORT = 4,
+
+ /* Returned by gst_install_plugins_sync(), or passed as result code to your
+ * #GstInstallPluginsResultFunc */
+ GST_INSTALL_PLUGINS_CRASHED = 100,
+ GST_INSTALL_PLUGINS_INVALID,
+
+ /* Return codes from starting the external helper, may be returned by both
+ * gst_install_plugins_sync() and gst_install_plugins_async(), but should
+ * never be seen by a #GstInstallPluginsResultFunc */
+ GST_INSTALL_PLUGINS_STARTED_OK = 200,
+ GST_INSTALL_PLUGINS_INTERNAL_FAILURE,
+ GST_INSTALL_PLUGINS_HELPER_MISSING,
+ GST_INSTALL_PLUGINS_INSTALL_IN_PROGRESS
+} GstInstallPluginsReturn;
+
+/**
+ * GstInstallPluginsContext:
+ *
+ * Opaque context structure for the plugin installation. Use the provided
+ * API to set details on it.
+ *
+ * Since: 0.10.12
+ */
+typedef struct _GstInstallPluginsContext GstInstallPluginsContext;
+
+GstInstallPluginsContext * gst_install_plugins_context_new (void);
+
+void gst_install_plugins_context_free (GstInstallPluginsContext * ctx);
+
+void gst_install_plugins_context_set_xid (GstInstallPluginsContext * ctx,
+ guint xid);
+
+/**
+ * GstInstallPluginsResultFunc:
+ * @result: whether the installation of the requested plugins succeeded or not
+ * @user_data: the user data passed to gst_install_plugins_async()
+ *
+ * The prototype of the callback function that will be called once the
+ * external plugin installer program has returned. You only need to provide
+ * a callback function if you are using the asynchronous interface.
+ *
+ * Since: 0.10.12
+ */
+typedef void (*GstInstallPluginsResultFunc) (GstInstallPluginsReturn result,
+ gpointer user_data);
+
+GstInstallPluginsReturn gst_install_plugins_async (gchar ** details,
+ GstInstallPluginsContext * ctx,
+ GstInstallPluginsResultFunc func,
+ gpointer user_data);
+
+GstInstallPluginsReturn gst_install_plugins_sync (gchar ** details,
+ GstInstallPluginsContext * ctx);
+
+const gchar * gst_install_plugins_return_get_name (GstInstallPluginsReturn ret);
+
+gboolean gst_install_plugins_installation_in_progress (void);
+
+G_END_DECLS
+
+#endif /* __GST_BASE_UTILS_INSTALL_PLUGINS_H__ */
+
#include <gst/check/gstcheck.h>
#include <gst/utils/base-utils.h>
-#include <unistd.h>
+
+#include <stdio.h>
+#include <glib/gstdio.h>
+#include <glib/gprintf.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h> /* for unlink() */
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h> /* for fchmod() */
+#endif
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h> /* for fchmod() */
+#endif
static void
missing_msg_check_getters (GstMessage * msg)
}
GST_END_TEST;
+
+
GST_START_TEST (test_base_utils_taglist_add_codec_info)
{
GstTagList *list;
}
GST_END_TEST;
+
+static gint marker;
+
+static void
+result_cb (GstInstallPluginsReturn result, gpointer user_data)
+{
+ GST_LOG ("result = %u, user_data = %p", result, user_data);
+
+ fail_unless (user_data == (gpointer) & marker);
+
+ marker = result;
+}
+
+#define FAKE_INSTALL_PLUGINS_HELPER "/tmp/gst-plugins-base-unit-test-helper"
+#define SCRIPT_NO_XID \
+ "#!/bin/sh\n" \
+ "if test x$1 != xdetail1; then exit 21; fi;\n" \
+ "if test x$2 != xdetail2; then exit 22; fi;\n" \
+ "exit 1\n"
+
+#define SCRIPT_WITH_XID \
+ "#!/bin/sh\n" \
+ "if test x$1 != 'x--transient-for=42'; then exit 21; fi;\n" \
+ "if test x$2 != xdetail1; then exit 22; fi;\n" \
+ "if test x$3 != xdetail2; then exit 23; fi;\n" \
+ "exit 0\n"
+
+/* make sure our script gets called with the right parameters */
+static void
+test_base_utils_install_plugins_do_callout (gchar ** details,
+ GstInstallPluginsContext * ctx, const gchar * script,
+ GstInstallPluginsReturn expected_result)
+{
+#ifdef G_OS_UNIX
+ GstInstallPluginsReturn ret;
+ FILE *f;
+
+ unlink (FAKE_INSTALL_PLUGINS_HELPER);
+
+ f = g_fopen (FAKE_INSTALL_PLUGINS_HELPER, "w");
+ if (f == NULL)
+ return;
+ if (g_fprintf (f, "%s", script) > 0 &&
+ fchmod (fileno (f), S_IRUSR | S_IWUSR | S_IXUSR) == 0) {
+ fclose (f);
+ g_setenv ("GST_INSTALL_PLUGINS_HELPER", FAKE_INSTALL_PLUGINS_HELPER, 1);
+
+ /* test sync callout */
+ ret = gst_install_plugins_sync (details, ctx);
+ fail_unless (ret == GST_INSTALL_PLUGINS_HELPER_MISSING ||
+ ret == expected_result,
+ "gst_install_plugins_sync() failed with unexpected ret %d, which is"
+ "neither HELPER_MISSING NOR %d", ret, expected_result);
+
+ /* test async callout */
+ marker = -333;
+ ret = gst_install_plugins_async (details, ctx, result_cb,
+ (gpointer) & marker);
+ fail_unless (ret == GST_INSTALL_PLUGINS_HELPER_MISSING ||
+ ret == GST_INSTALL_PLUGINS_STARTED_OK,
+ "gst_install_plugins_async() failed with unexpected ret %d", ret);
+ if (ret == GST_INSTALL_PLUGINS_STARTED_OK) {
+ while (marker == -333) {
+ g_usleep (500);
+ g_main_context_iteration (NULL, FALSE);
+ }
+ /* and check that the callback was called with the expected code */
+ fail_unless_equals_int (marker, expected_result);
+ }
+ } else {
+ fclose (f);
+ }
+ unlink (FAKE_INSTALL_PLUGINS_HELPER);
+#endif /* G_OS_UNIX */
+}
+
+GST_START_TEST (test_base_utils_install_plugins)
+{
+ GstInstallPluginsContext *ctx;
+ GstInstallPluginsReturn ret;
+ gchar *details[] = { "detail1", "detail2" };
+
+ ctx = gst_install_plugins_context_new ();
+
+ ASSERT_CRITICAL (ret = gst_install_plugins_sync (NULL, ctx);
+ );
+ ASSERT_CRITICAL (ret =
+ gst_install_plugins_async (NULL, ctx, result_cb, (gpointer) & marker);
+ );
+ ASSERT_CRITICAL (ret =
+ gst_install_plugins_async (details, ctx, NULL, (gpointer) & marker);
+ );
+
+ /* make sure the functions return the right error code if the helper does
+ * not exist */
+ g_setenv ("GST_INSTALL_PLUGINS_HELPER", "/does/not/ex/is.t", 1);
+ ret = gst_install_plugins_sync (details, NULL);
+ fail_unless_equals_int (ret, GST_INSTALL_PLUGINS_HELPER_MISSING);
+
+ marker = -333;
+ ret =
+ gst_install_plugins_async (details, NULL, result_cb, (gpointer) & marker);
+ fail_unless_equals_int (ret, GST_INSTALL_PLUGINS_HELPER_MISSING);
+ /* and check that the callback wasn't called */
+ fail_unless_equals_int (marker, -333);
+
+ /* now make sure our scripts are actually called as expected (if possible) */
+ test_base_utils_install_plugins_do_callout (details, NULL, SCRIPT_NO_XID,
+ GST_INSTALL_PLUGINS_NOT_FOUND);
+
+ /* and again with context */
+ gst_install_plugins_context_set_xid (ctx, 42);
+ test_base_utils_install_plugins_do_callout (details, ctx, SCRIPT_WITH_XID,
+ GST_INSTALL_PLUGINS_SUCCESS);
+
+ /* and free the context now that we don't need it any longer */
+ gst_install_plugins_context_free (ctx);
+
+ /* completely silly test to check gst_install_plugins_return_get_name()
+ * is somewhat well-behaved */
+ {
+ gint i;
+
+ for (i = -99; i < 16738; ++i) {
+ const gchar *s;
+
+ s = gst_install_plugins_return_get_name ((GstInstallPluginsReturn) i);
+ fail_unless (s != NULL);
+ /* GST_LOG ("%5d = %s", i, s); */
+ }
+ }
+}
+
+GST_END_TEST;
+
static Suite *
libgstbaseutils_suite (void)
{
tcase_add_test (tc_chain, test_base_utils_post_missing_messages);
tcase_add_test (tc_chain, test_base_utils_taglist_add_codec_info);
tcase_add_test (tc_chain, test_base_utils_get_codec_description);
+ tcase_add_test (tc_chain, test_base_utils_install_plugins);
return s;
}
#include <gst/check/gstcheck.h>
#include <gst/utils/base-utils.h>
-#include <unistd.h>
+
+#include <stdio.h>
+#include <glib/gstdio.h>
+#include <glib/gprintf.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h> /* for unlink() */
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h> /* for fchmod() */
+#endif
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h> /* for fchmod() */
+#endif
static void
missing_msg_check_getters (GstMessage * msg)
}
GST_END_TEST;
+
+
GST_START_TEST (test_base_utils_taglist_add_codec_info)
{
GstTagList *list;
}
GST_END_TEST;
+
+static gint marker;
+
+static void
+result_cb (GstInstallPluginsReturn result, gpointer user_data)
+{
+ GST_LOG ("result = %u, user_data = %p", result, user_data);
+
+ fail_unless (user_data == (gpointer) & marker);
+
+ marker = result;
+}
+
+#define FAKE_INSTALL_PLUGINS_HELPER "/tmp/gst-plugins-base-unit-test-helper"
+#define SCRIPT_NO_XID \
+ "#!/bin/sh\n" \
+ "if test x$1 != xdetail1; then exit 21; fi;\n" \
+ "if test x$2 != xdetail2; then exit 22; fi;\n" \
+ "exit 1\n"
+
+#define SCRIPT_WITH_XID \
+ "#!/bin/sh\n" \
+ "if test x$1 != 'x--transient-for=42'; then exit 21; fi;\n" \
+ "if test x$2 != xdetail1; then exit 22; fi;\n" \
+ "if test x$3 != xdetail2; then exit 23; fi;\n" \
+ "exit 0\n"
+
+/* make sure our script gets called with the right parameters */
+static void
+test_base_utils_install_plugins_do_callout (gchar ** details,
+ GstInstallPluginsContext * ctx, const gchar * script,
+ GstInstallPluginsReturn expected_result)
+{
+#ifdef G_OS_UNIX
+ GstInstallPluginsReturn ret;
+ FILE *f;
+
+ unlink (FAKE_INSTALL_PLUGINS_HELPER);
+
+ f = g_fopen (FAKE_INSTALL_PLUGINS_HELPER, "w");
+ if (f == NULL)
+ return;
+ if (g_fprintf (f, "%s", script) > 0 &&
+ fchmod (fileno (f), S_IRUSR | S_IWUSR | S_IXUSR) == 0) {
+ fclose (f);
+ g_setenv ("GST_INSTALL_PLUGINS_HELPER", FAKE_INSTALL_PLUGINS_HELPER, 1);
+
+ /* test sync callout */
+ ret = gst_install_plugins_sync (details, ctx);
+ fail_unless (ret == GST_INSTALL_PLUGINS_HELPER_MISSING ||
+ ret == expected_result,
+ "gst_install_plugins_sync() failed with unexpected ret %d, which is"
+ "neither HELPER_MISSING NOR %d", ret, expected_result);
+
+ /* test async callout */
+ marker = -333;
+ ret = gst_install_plugins_async (details, ctx, result_cb,
+ (gpointer) & marker);
+ fail_unless (ret == GST_INSTALL_PLUGINS_HELPER_MISSING ||
+ ret == GST_INSTALL_PLUGINS_STARTED_OK,
+ "gst_install_plugins_async() failed with unexpected ret %d", ret);
+ if (ret == GST_INSTALL_PLUGINS_STARTED_OK) {
+ while (marker == -333) {
+ g_usleep (500);
+ g_main_context_iteration (NULL, FALSE);
+ }
+ /* and check that the callback was called with the expected code */
+ fail_unless_equals_int (marker, expected_result);
+ }
+ } else {
+ fclose (f);
+ }
+ unlink (FAKE_INSTALL_PLUGINS_HELPER);
+#endif /* G_OS_UNIX */
+}
+
+GST_START_TEST (test_base_utils_install_plugins)
+{
+ GstInstallPluginsContext *ctx;
+ GstInstallPluginsReturn ret;
+ gchar *details[] = { "detail1", "detail2" };
+
+ ctx = gst_install_plugins_context_new ();
+
+ ASSERT_CRITICAL (ret = gst_install_plugins_sync (NULL, ctx);
+ );
+ ASSERT_CRITICAL (ret =
+ gst_install_plugins_async (NULL, ctx, result_cb, (gpointer) & marker);
+ );
+ ASSERT_CRITICAL (ret =
+ gst_install_plugins_async (details, ctx, NULL, (gpointer) & marker);
+ );
+
+ /* make sure the functions return the right error code if the helper does
+ * not exist */
+ g_setenv ("GST_INSTALL_PLUGINS_HELPER", "/does/not/ex/is.t", 1);
+ ret = gst_install_plugins_sync (details, NULL);
+ fail_unless_equals_int (ret, GST_INSTALL_PLUGINS_HELPER_MISSING);
+
+ marker = -333;
+ ret =
+ gst_install_plugins_async (details, NULL, result_cb, (gpointer) & marker);
+ fail_unless_equals_int (ret, GST_INSTALL_PLUGINS_HELPER_MISSING);
+ /* and check that the callback wasn't called */
+ fail_unless_equals_int (marker, -333);
+
+ /* now make sure our scripts are actually called as expected (if possible) */
+ test_base_utils_install_plugins_do_callout (details, NULL, SCRIPT_NO_XID,
+ GST_INSTALL_PLUGINS_NOT_FOUND);
+
+ /* and again with context */
+ gst_install_plugins_context_set_xid (ctx, 42);
+ test_base_utils_install_plugins_do_callout (details, ctx, SCRIPT_WITH_XID,
+ GST_INSTALL_PLUGINS_SUCCESS);
+
+ /* and free the context now that we don't need it any longer */
+ gst_install_plugins_context_free (ctx);
+
+ /* completely silly test to check gst_install_plugins_return_get_name()
+ * is somewhat well-behaved */
+ {
+ gint i;
+
+ for (i = -99; i < 16738; ++i) {
+ const gchar *s;
+
+ s = gst_install_plugins_return_get_name ((GstInstallPluginsReturn) i);
+ fail_unless (s != NULL);
+ /* GST_LOG ("%5d = %s", i, s); */
+ }
+ }
+}
+
+GST_END_TEST;
+
static Suite *
libgstbaseutils_suite (void)
{
tcase_add_test (tc_chain, test_base_utils_post_missing_messages);
tcase_add_test (tc_chain, test_base_utils_taglist_add_codec_info);
tcase_add_test (tc_chain, test_base_utils_get_codec_description);
+ tcase_add_test (tc_chain, test_base_utils_install_plugins);
return s;
}