gst-libs/gst/utils/: API: add API for applications to initiate installation of missin...
authorTim-Philipp Müller <tim@centricular.net>
Fri, 2 Feb 2007 20:42:08 +0000 (20:42 +0000)
committerTim-Philipp Müller <tim@centricular.net>
Fri, 2 Feb 2007 20:42:08 +0000 (20:42 +0000)
Original commit message from CVS:
* 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.

14 files changed:
ChangeLog
configure.ac
docs/libs/gst-plugins-base-libs-docs.sgml
docs/libs/gst-plugins-base-libs-sections.txt
gst-libs/gst/pbutils/Makefile.am
gst-libs/gst/pbutils/install-plugins.c [new file with mode: 0644]
gst-libs/gst/pbutils/install-plugins.h [new file with mode: 0644]
gst-libs/gst/pbutils/pbutils.h
gst-libs/gst/utils/Makefile.am
gst-libs/gst/utils/base-utils.h
gst-libs/gst/utils/install-plugins.c [new file with mode: 0644]
gst-libs/gst/utils/install-plugins.h [new file with mode: 0644]
tests/check/libs/pbutils.c
tests/check/libs/utils.c

index 7f4891b71297c71053d6ee08ac04473c439418cb..112e9fdaf8dfc4b059856aaddab9380e0da509d6 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,39 @@
+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):
index 5ddd0a779e2bea3938dbb35d33d6f90634757c70..b941b2ab43e028cf8585dd7fa8c107542e928f48 100644 (file)
@@ -81,6 +81,28 @@ GST_ARG_WITH_PKG_CONFIG_PATH
 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 \
@@ -182,8 +204,8 @@ AC_CHECK_HEADERS([sys/socket.h],
   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
index 79ff2e26042ead75d36e1539f1df40bbe3ab5e2e..8fa84cbb5aa2fa0eed1ee4074fd2eb8e3340ab49 100644 (file)
@@ -49,6 +49,7 @@
 <!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">
index 5bcba8dacf957e818f64acf2d96433133e267bcb..c8b4d5dea2d14277408b2e7bd21aaa2b575c17e6 100644 (file)
@@ -941,6 +941,23 @@ gst_missing_uri_sink_message_new
 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>
index 2ba8f6b0037dd7bf73616ac4b656cab4fdfbaf2f..59dd18a0b0b6e922cc18068d53833d0fc1329217 100644 (file)
@@ -5,6 +5,8 @@ libgstbaseutils_@GST_MAJORMINOR@_la_SOURCES = \
        base-utils.h      \
        descriptions.c    \
        descriptions.h    \
+       install-plugins.c \
+       install-plugins.h \
        missing-plugins.c \
        missing-plugins.h
 
@@ -12,6 +14,7 @@ libgstbaseutils_@GST_MAJORMINOR@includedir = $(includedir)/gstreamer-@GST_MAJORM
 libgstbaseutils_@GST_MAJORMINOR@include_HEADERS = \
        base-utils.h      \
        descriptions.h    \
+       install-plugins.h \
        missing-plugins.h
 
 libgstbaseutils_@GST_MAJORMINOR@_la_LIBADD = $(GST_LIBS)
diff --git a/gst-libs/gst/pbutils/install-plugins.c b/gst-libs/gst/pbutils/install-plugins.c
new file mode 100644 (file)
index 0000000..ac23975
--- /dev/null
@@ -0,0 +1,388 @@
+/* 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 &lt;gtk/gtk.h&gt;
+ * ##ifdef GDK_WINDOWING_X11
+ * ##include &lt;gdk/gdkx.h&gt;
+ * ##endif
+ * ...
+ * ##ifdef GDK_WINDOWING_X11
+ *   xid = GDK_WINDOW_XWINDOW (GTK_WIDGET (application_window)-&gt;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;
+}
diff --git a/gst-libs/gst/pbutils/install-plugins.h b/gst-libs/gst/pbutils/install-plugins.h
new file mode 100644 (file)
index 0000000..835ffa8
--- /dev/null
@@ -0,0 +1,137 @@
+/* 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__ */
+
index 10c62d38ab39cec9da231cc9e7d8d869861b5f82..cfa026c6eb66e89c8945b5a20aa6bda39dcf1d68 100644 (file)
@@ -24,6 +24,7 @@
 
 #include <gst/utils/descriptions.h>
 #include <gst/utils/missing-plugins.h>
+#include <gst/utils/install-plugins.h>
 
 G_BEGIN_DECLS
 
index 2ba8f6b0037dd7bf73616ac4b656cab4fdfbaf2f..59dd18a0b0b6e922cc18068d53833d0fc1329217 100644 (file)
@@ -5,6 +5,8 @@ libgstbaseutils_@GST_MAJORMINOR@_la_SOURCES = \
        base-utils.h      \
        descriptions.c    \
        descriptions.h    \
+       install-plugins.c \
+       install-plugins.h \
        missing-plugins.c \
        missing-plugins.h
 
@@ -12,6 +14,7 @@ libgstbaseutils_@GST_MAJORMINOR@includedir = $(includedir)/gstreamer-@GST_MAJORM
 libgstbaseutils_@GST_MAJORMINOR@include_HEADERS = \
        base-utils.h      \
        descriptions.h    \
+       install-plugins.h \
        missing-plugins.h
 
 libgstbaseutils_@GST_MAJORMINOR@_la_LIBADD = $(GST_LIBS)
index 10c62d38ab39cec9da231cc9e7d8d869861b5f82..cfa026c6eb66e89c8945b5a20aa6bda39dcf1d68 100644 (file)
@@ -24,6 +24,7 @@
 
 #include <gst/utils/descriptions.h>
 #include <gst/utils/missing-plugins.h>
+#include <gst/utils/install-plugins.h>
 
 G_BEGIN_DECLS
 
diff --git a/gst-libs/gst/utils/install-plugins.c b/gst-libs/gst/utils/install-plugins.c
new file mode 100644 (file)
index 0000000..ac23975
--- /dev/null
@@ -0,0 +1,388 @@
+/* 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 &lt;gtk/gtk.h&gt;
+ * ##ifdef GDK_WINDOWING_X11
+ * ##include &lt;gdk/gdkx.h&gt;
+ * ##endif
+ * ...
+ * ##ifdef GDK_WINDOWING_X11
+ *   xid = GDK_WINDOW_XWINDOW (GTK_WIDGET (application_window)-&gt;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;
+}
diff --git a/gst-libs/gst/utils/install-plugins.h b/gst-libs/gst/utils/install-plugins.h
new file mode 100644 (file)
index 0000000..835ffa8
--- /dev/null
@@ -0,0 +1,137 @@
+/* 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__ */
+
index 342691ffdb76c9302d9cebb449067bc9fa22f9f0..efa381ca29e474f64143810cfbdc47bd70da013d 100644 (file)
 
 #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)
@@ -400,6 +415,8 @@ GST_START_TEST (test_base_utils_get_codec_description)
 }
 
 GST_END_TEST;
+
+
 GST_START_TEST (test_base_utils_taglist_add_codec_info)
 {
   GstTagList *list;
@@ -430,6 +447,141 @@ GST_START_TEST (test_base_utils_taglist_add_codec_info)
 }
 
 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)
 {
@@ -441,6 +593,7 @@ 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;
 }
 
index 342691ffdb76c9302d9cebb449067bc9fa22f9f0..efa381ca29e474f64143810cfbdc47bd70da013d 100644 (file)
 
 #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)
@@ -400,6 +415,8 @@ GST_START_TEST (test_base_utils_get_codec_description)
 }
 
 GST_END_TEST;
+
+
 GST_START_TEST (test_base_utils_taglist_add_codec_info)
 {
   GstTagList *list;
@@ -430,6 +447,141 @@ GST_START_TEST (test_base_utils_taglist_add_codec_info)
 }
 
 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)
 {
@@ -441,6 +593,7 @@ 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;
 }