summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOlivier CrĂȘte <olivier.crete@collabora.com>2012-03-15 14:12:21 -0400
committerOlivier CrĂȘte <olivier.crete@collabora.com>2013-01-23 21:13:03 -0500
commitd1023646f9ef0437d134ae0ba006855e1b9982d6 (patch)
treed2ab938ecb4d168d65929ca3d76dc347dd29ec78
parent07a51b16eb92e998a831e65a4a54466da29ea469 (diff)
insertbin: Add bin to dynamically insert elements in a running pipeline
This element automatically links in any element added using it's action signals. These elements must have a single source pad and a single sink pad.
-rw-r--r--.gitignore2
-rw-r--r--Android.mk2
-rw-r--r--configure.ac6
-rw-r--r--docs/libs/Makefile.am1
-rw-r--r--docs/libs/gst-plugins-bad-libs-docs.sgml5
-rw-r--r--docs/libs/gst-plugins-bad-libs-sections.txt24
-rw-r--r--docs/libs/gst-plugins-bad-libs.types3
-rw-r--r--gst-libs/gst/Makefile.am6
-rw-r--r--gst-libs/gst/insertbin/Makefile.am90
-rw-r--r--gst-libs/gst/insertbin/gstinsertbin.c1029
-rw-r--r--gst-libs/gst/insertbin/gstinsertbin.h101
-rw-r--r--gst-plugins-bad.spec.in4
-rw-r--r--pkgconfig/Makefile.am3
-rw-r--r--pkgconfig/gstreamer-insertbin-uninstalled.pc.in12
-rw-r--r--pkgconfig/gstreamer-insertbin.pc.in12
-rw-r--r--tests/check/Makefile.am8
-rw-r--r--tests/check/libs/.gitignore1
-rw-r--r--tests/check/libs/insertbin.c354
18 files changed, 1661 insertions, 2 deletions
diff --git a/.gitignore b/.gitignore
index 141f5cb81..d98a98596 100644
--- a/.gitignore
+++ b/.gitignore
@@ -38,6 +38,8 @@ gst-plugins-bad-*.tar.*
/m4
Makefile.in
Makefile
+*.gir
+*.typelib
tmp-orc.c
gst*orc.h
diff --git a/Android.mk b/Android.mk
index b8b9fb662..b53166d3d 100644
--- a/Android.mk
+++ b/Android.mk
@@ -9,6 +9,7 @@ GST_PLUGINS_BAD_BUILT_SOURCES := \
gst-libs/gst/baseparse/Android.mk \
gst-libs/gst/basecamerabinsrc/Android.mk \
gst-libs/gst/codecparsers/Android.mk \
+ gst-libs/gst/insertbin/Android.mk
gst-libs/gst/interfaces/Android.mk \
gst/h264parse/Android.mk \
gst/videoparsers/Android.mk \
@@ -98,6 +99,7 @@ CONFIGURE_TARGETS += gst-plugins-bad-configure
-include $(GST_PLUGINS_BAD_TOP)/gst-libs/gst/baseparse/Android.mk
-include $(GST_PLUGINS_BAD_TOP)/gst-libs/gst/basecamerabinsrc/Android.mk
-include $(GST_PLUGINS_BAD_TOP)/gst-libs/gst/codecparsers/Android.mk
+-include $(GST_PLUGINS_BAD_TOP)/gst-libs/gst/insertbin/Android.mk
-include $(GST_PLUGINS_BAD_TOP)/gst-libs/gst/interfaces/Android.mk
-include $(GST_PLUGINS_BAD_TOP)/gst/h264parse/Android.mk
-include $(GST_PLUGINS_BAD_TOP)/gst/audiobuffer/Android.mk
diff --git a/configure.ac b/configure.ac
index ffc86cbd4..94d3a851b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -123,6 +123,9 @@ AG_GST_CHECK_DOWHILE_MACROS
AC_PATH_PROG(VALGRIND_PATH, valgrind, no)
AM_CONDITIONAL(HAVE_VALGRIND, test ! "x$VALGRIND_PATH" = "xno")
+dnl check for gobject-introspection
+GOBJECT_INTROSPECTION_CHECK([1.31.1])
+
dnl check for documentation tools
GTK_DOC_CHECK([1.12])
AG_GST_PLUGIN_DOCS([1.12])
@@ -2257,6 +2260,7 @@ gst/y4m/Makefile
gst-libs/Makefile
gst-libs/gst/Makefile
gst-libs/gst/basecamerabinsrc/Makefile
+gst-libs/gst/insertbin/Makefile
gst-libs/gst/interfaces/Makefile
gst-libs/gst/signalprocessor/Makefile
gst-libs/gst/codecparsers/Makefile
@@ -2371,6 +2375,8 @@ pkgconfig/gstreamer-plugins-bad.pc
pkgconfig/gstreamer-plugins-bad-uninstalled.pc
pkgconfig/gstreamer-codecparsers.pc
pkgconfig/gstreamer-codecparsers-uninstalled.pc
+pkgconfig/gstreamer-insertbin.pc
+pkgconfig/gstreamer-insertbin-uninstalled.pc
pkgconfig/gstreamer-basevideo.pc
pkgconfig/gstreamer-basevideo-uninstalled.pc
tools/Makefile
diff --git a/docs/libs/Makefile.am b/docs/libs/Makefile.am
index 7e9b966a8..3ffe796bb 100644
--- a/docs/libs/Makefile.am
+++ b/docs/libs/Makefile.am
@@ -56,6 +56,7 @@ GTKDOC_LIBS = \
$(top_builddir)/gst-libs/gst/interfaces/libgstphotography-@GST_API_VERSION@.la \
$(top_builddir)/gst-libs/gst/signalprocessor/libgstsignalprocessor-@GST_API_VERSION@.la \
$(top_builddir)/gst-libs/gst/video/libgstbasevideo-@GST_API_VERSION@.la \
+ $(top_builddir)/gst-libs/gst/insertbin/libgstinsertbin-@GST_API_VERSION@.la \
$(GST_BASE_LIBS)
GTKDOC_CC=$(LIBTOOL) --tag=CC --mode=compile $(CC)
diff --git a/docs/libs/gst-plugins-bad-libs-docs.sgml b/docs/libs/gst-plugins-bad-libs-docs.sgml
index 3384e9fd4..d4e0060ca 100644
--- a/docs/libs/gst-plugins-bad-libs-docs.sgml
+++ b/docs/libs/gst-plugins-bad-libs-docs.sgml
@@ -44,6 +44,11 @@
<xi:include href="xml/gstsurfacebuffer.xml" />
<xi:include href="xml/gstsurfaceconverter.xml" />
</chapter>
+
+ <chapter id="tools">
+ <title>Useful elements</title>
+ <xi:include href="xml/gstinsertbin.xml" />
+ </chapter>
</part>
<part id="gstreamer-libs-hierarchy">
diff --git a/docs/libs/gst-plugins-bad-libs-sections.txt b/docs/libs/gst-plugins-bad-libs-sections.txt
index 59439f9bb..433c4b962 100644
--- a/docs/libs/gst-plugins-bad-libs-sections.txt
+++ b/docs/libs/gst-plugins-bad-libs-sections.txt
@@ -486,3 +486,27 @@ GstSurfaceConverterInterface
gst_surface_converter_get_type
gst_surface_converter_upload
</SECTION>
+
+<SECTION>
+<FILE>gstinsertbin</FILE>
+<TITLE>GstInsertbin</TITLE>
+GstInsertBin
+GstInsertBinClass
+GstInsertBinCallback
+gst_insert_bin_new
+gst_insert_bin_append
+gst_insert_bin_prepend
+gst_insert_bin_insert_after
+gst_insert_bin_insert_before
+gst_insert_bin_remove
+<SUBSECTION Standard>
+GST_INSERT_BIN
+GST_INSERT_BIN_CLASS
+GST_INSERT_BIN_GET_CLASS
+GST_IS_INSERT_BIN
+GST_IS_INSERT_BIN_CLASS
+GST_TYPE_INSERT_BIN
+gst_insert_bin_get_type
+<SUBSECTION Private>
+GstInsertBinPrivate
+</SECTION>
diff --git a/docs/libs/gst-plugins-bad-libs.types b/docs/libs/gst-plugins-bad-libs.types
index 493215718..caa3918aa 100644
--- a/docs/libs/gst-plugins-bad-libs.types
+++ b/docs/libs/gst-plugins-bad-libs.types
@@ -2,3 +2,6 @@
#include <gst/codecparsers/gsth264parser.h>
#include <gst/codecparsers/gstmpegvideoparser.h>
+#include <gst/insertbin/gstinsertbin.h>
+
+gst_insert_bin_get_type \ No newline at end of file
diff --git a/gst-libs/gst/Makefile.am b/gst-libs/gst/Makefile.am
index ab1c26e84..2868042aa 100644
--- a/gst-libs/gst/Makefile.am
+++ b/gst-libs/gst/Makefile.am
@@ -1,6 +1,8 @@
-SUBDIRS = interfaces signalprocessor video basecamerabinsrc codecparsers
+SUBDIRS = interfaces signalprocessor video basecamerabinsrc codecparsers \
+ insertbin
noinst_HEADERS = gst-i18n-plugin.h gettext.h glib-compat-private.h
-DIST_SUBDIRS = interfaces signalprocessor video basecamerabinsrc codecparsers
+DIST_SUBDIRS = interfaces signalprocessor video basecamerabinsrc codecparsers \
+ insertbin
diff --git a/gst-libs/gst/insertbin/Makefile.am b/gst-libs/gst/insertbin/Makefile.am
new file mode 100644
index 000000000..8c4cfba0d
--- /dev/null
+++ b/gst-libs/gst/insertbin/Makefile.am
@@ -0,0 +1,90 @@
+lib_LTLIBRARIES = libgstinsertbin-@GST_API_VERSION@.la
+
+libgstinsertbin_@GST_API_VERSION@_la_SOURCES = gstinsertbin.c
+
+libgstinsertbin_@GST_API_VERSION@includedir = \
+ $(includedir)/gstreamer-@GST_API_VERSION@/gst/insertbin
+
+libgstinsertbin_@GST_API_VERSION@include_HEADERS = gstinsertbin.h
+
+libgstinsertbin_@GST_API_VERSION@_la_CFLAGS = \
+ $(GST_PLUGINS_BAD_CFLAGS) \
+ $(GST_CFLAGS)
+
+libgstinsertbin_@GST_API_VERSION@_la_LIBADD = \
+ $(GST_LIBS)
+
+libgstinsertbin_@GST_API_VERSION@_la_LDFLAGS = \
+ $(GST_LIB_LDFLAGS) \
+ $(GST_ALL_LDFLAGS) \
+ $(GST_LT_LDFLAGS)
+
+
+if HAVE_INTROSPECTION
+BUILT_GIRSOURCES = GstInsertBin-@GST_API_VERSION@.gir
+
+gir_headers=$(patsubst %,$(srcdir)/%, $(libgstinsertbin_@GST_API_VERSION@include_HEADERS))
+gir_headers+=$(patsubst %,$(builddir)/%, $(built_headers))
+gir_sources=$(patsubst %,$(srcdir)/%, $(libgstinsertbin_@GST_API_VERSION@_la_SOURCES))
+gir_sources+=$(patsubst %,$(builddir)/%, $(built_sources))
+
+GstInsertBin-@GST_API_VERSION@.gir: $(INTROSPECTION_SCANNER) libgstinsertbin-@GST_API_VERSION@.la
+ $(AM_V_GEN)PKG_CONFIG_PATH="$(GST_PKG_CONFIG_PATH)" \
+ GST_PLUGIN_SYSTEM_PATH_1_0="" GST_PLUGIN_PATH_1_0="" GST_REGISTRY_UPDATE=no \
+ $(INTROSPECTION_SCANNER) -v --namespace GstInsertBin \
+ --nsversion=@GST_API_VERSION@ \
+ --strip-prefix=Gst \
+ --warn-all \
+ --c-include "gst/insertbin/gstinsertbin.h" \
+ -I$(top_srcdir)/gst-libs \
+ -I$(top_builddir)/gst-libs \
+ --add-include-path=`PKG_CONFIG_PATH="$(GST_PKG_CONFIG_PATH)" $(PKG_CONFIG) --variable=girdir gstreamer-@GST_API_VERSION@` \
+ --add-include-path=`PKG_CONFIG_PATH="$(GST_PKG_CONFIG_PATH)" $(PKG_CONFIG) --variable=girdir gstreamer-base-@GST_API_VERSION@` \
+ --library=libgstinsertbin-@GST_API_VERSION@.la \
+ --include=Gst-@GST_API_VERSION@ \
+ --include=GstBase-@GST_API_VERSION@ \
+ --libtool="$(top_builddir)/libtool" \
+ --pkg gstreamer-@GST_API_VERSION@ \
+ --pkg gstreamer-base-@GST_API_VERSION@ \
+ --pkg-export gstreamer-insertbin-@GST_API_VERSION@ \
+ --add-init-section="gst_init(NULL,NULL);" \
+ --output $@ \
+ $(gir_headers) \
+ $(gir_sources)
+
+# INTROSPECTION_GIRDIR/INTROSPECTION_TYPELIBDIR aren't the right place to
+# install anything - we need to install inside our prefix.
+girdir = $(datadir)/gir-1.0
+gir_DATA = $(BUILT_GIRSOURCES)
+
+typelibsdir = $(libdir)/girepository-1.0/
+
+typelibs_DATA = $(BUILT_GIRSOURCES:.gir=.typelib)
+
+%.typelib: %.gir $(INTROSPECTION_COMPILER)
+ $(AM_V_GEN)PKG_CONFIG_PATH="$(GST_PKG_CONFIG_PATH)" \
+ $(INTROSPECTION_COMPILER) \
+ --includedir=$(srcdir) \
+ --includedir=$(builddir) \
+ --includedir=`PKG_CONFIG_PATH="$(GST_PKG_CONFIG_PATH)" $(PKG_CONFIG) --variable=girdir gstreamer-@GST_API_VERSION@` \
+ --includedir=`PKG_CONFIG_PATH="$(GST_PKG_CONFIG_PATH)" $(PKG_CONFIG) --variable=girdir gstreamer-base-@GST_API_VERSION@` \
+ $(INTROSPECTION_COMPILER_OPTS) $< -o $(@F)
+
+CLEANFILES = $(BUILT_GIRSOURCES) $(typelibs_DATA)
+endif
+
+Android.mk: $(BUILT_SOURCES) Makefile.am
+ androgenizer -:PROJECT libgstinsertbin -:STATIC libgstinsertbin-@GST_API_VERSION@ \
+ -:TAGS eng debug \
+ -:REL_TOP $(top_srcdir) -:ABS_TOP $(abs_top_srcdir) \
+ -:SOURCES $(libgstinsertbin_@GST_API_VERSION@_la_SOURCES) \
+ $(built_sources) \
+ -:CFLAGS $(DEFS) $(libgstinsertbin_@GST_API_VERSION@_la_CFLAGS) \
+ -:LDFLAGS $(libgstinsertbin_@GST_API_VERSION@_la_LDFLAGS) \
+ $(libgstinsertbin@GST_API_VERSION@_la_LIBADD) \
+ -ldl \
+ -:HEADER_TARGET gstreamer-@GST_API_VERSION@/gst/insertbin \
+ -:HEADERS $(libgstinsertbininclude_HEADERS) \
+ $(built_headers) \
+ -:PASSTHROUGH LOCAL_ARM_MODE:=arm \
+ > $@
diff --git a/gst-libs/gst/insertbin/gstinsertbin.c b/gst-libs/gst/insertbin/gstinsertbin.c
new file mode 100644
index 000000000..295d2bc1b
--- /dev/null
+++ b/gst-libs/gst/insertbin/gstinsertbin.c
@@ -0,0 +1,1029 @@
+/*
+ * GStreamer
+ *
+ * Copyright 2013 Collabora Ltd
+ * @author: Olivier Crete <olivier.crete@collabora.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+/**
+ * SECTION:gstinsertbin
+ * @short_description: A #GstBin to insertally link filter-like elements.
+ *
+ * This element is a #GstBin that has a single source and sink pad. It allows
+ * the user (the application) to easily add and remove filter-like element
+ * (that has a single source and sink pad), to the pipeline while it is running.
+ * It features a fully asynchronous API inspired by GLib's GAsyncResult based
+ * APIs.
+ *
+ * Each operation (addition or removal) can take a callback, this callback
+ * is guaranteed to be called. Unlike GIO, there is no guarantee about where
+ * this callback will be called from, it could be called before the action
+ * returns or it could be called later from another thread. The signature of
+ * this callback GstInsertBinCallback().
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstinsertbin.h"
+
+
+GST_DEBUG_CATEGORY_STATIC (insert_bin_debug);
+#define GST_CAT_DEFAULT (insert_bin_debug)
+
+
+static GstStaticPadTemplate gst_insert_bin_sink_template =
+GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS_ANY);
+
+static GstStaticPadTemplate gst_insert_bin_src_template =
+GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS_ANY);
+
+enum
+{
+ SIG_PREPEND,
+ SIG_APPEND,
+ SIG_INSERT_BEFORE,
+ SIG_INSERT_AFTER,
+ SIG_REMOVE,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+enum
+{
+ PROP_0,
+};
+
+struct _GstInsertBinPrivate
+{
+ GstPad *srcpad;
+ GstPad *sinkpad;
+
+ GQueue change_queue;
+};
+
+typedef enum
+{
+ GST_INSERT_BIN_ACTION_ADD,
+ GST_INSERT_BIN_ACTION_REMOVE
+} GstInsertBinAction;
+
+
+typedef enum
+{
+ DIRECTION_NONE,
+ DIRECTION_AFTER,
+ DIRECTION_BEFORE
+} GstInsertBinDirection;
+
+struct ChangeData
+{
+ GstElement *element;
+ GstInsertBinAction action;
+ GstElement *sibling;
+ GstInsertBinDirection direction;
+
+ GstInsertBinCallback callback;
+ gpointer user_data;
+};
+
+static void gst_insert_bin_dispose (GObject * object);
+
+
+static void gst_insert_bin_do_change (GstInsertBin * self, GstPad * pad);
+static GstPadProbeReturn pad_blocked_cb (GstPad * pad, GstPadProbeInfo * info,
+ gpointer user_data);
+
+G_DEFINE_TYPE (GstInsertBin, gst_insert_bin, GST_TYPE_BIN);
+
+static void
+gst_insert_bin_class_init (GstInsertBinClass * klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GstElementClass *gstelement_class = (GstElementClass *) klass;
+
+ GST_DEBUG_CATEGORY_INIT (insert_bin_debug, "insertbin", 0, "Insert Bin");
+
+ g_type_class_add_private (klass, sizeof (GstInsertBinPrivate));
+
+ gst_element_class_add_pad_template (gstelement_class,
+ gst_static_pad_template_get (&gst_insert_bin_src_template));
+ gst_element_class_add_pad_template (gstelement_class,
+ gst_static_pad_template_get (&gst_insert_bin_sink_template));
+ gst_element_class_set_static_metadata (gstelement_class, "Insert Bin",
+ "Generic/Bin/Filter",
+ "Auto-links filter style elements insertally",
+ "Olivier Crete <olivier.crete@collabora.com>");
+
+ gobject_class->dispose = gst_insert_bin_dispose;
+
+ /**
+ * GstInsertBin::prepend:
+ * @element: the #GstElement to add
+ * @callback: the callback to call when the element has been added or not, or
+ * %NULL
+ * @user_data: The data to pass to the callback
+ *
+ * This action signal adds the filter like element before any other element
+ * in the bin.
+ *
+ * Same as gst_insert_bin_prepend()
+ */
+ signals[SIG_PREPEND] = g_signal_new_class_handler ("prepend",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_CALLBACK (gst_insert_bin_prepend),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 3, GST_TYPE_ELEMENT, G_TYPE_POINTER, G_TYPE_POINTER);
+
+ /**
+ * GstInsertBin::append:
+ * @element: the #GstElement to add
+ * @callback: the callback to call when the element has been added or not, or
+ * %NULL
+ * @user_data: The data to pass to the callback
+ *
+ * This action signal adds the filter like element after any other element
+ * in the bin.
+ *
+ * Same as gst_insert_bin_append()
+ */
+ signals[SIG_APPEND] = g_signal_new_class_handler ("append",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_CALLBACK (gst_insert_bin_append),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 3, GST_TYPE_ELEMENT, G_TYPE_POINTER, G_TYPE_POINTER);
+
+ /**
+ * GstInsertBin::insert-before:
+ * @element: the #GstElement to add
+ * @sibling: the #GstElement to add @element before
+ * @callback: the callback to call when the element has been added or not, or
+ * %NULL
+ * @user_data: The data to pass to the callback
+ *
+ * This action signal adds the filter like element before the @sibling
+ * element in the bin.
+ *
+ * Same as gst_insert_bin_insert_before()
+ */
+ signals[SIG_INSERT_BEFORE] = g_signal_new_class_handler ("insert-before",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_CALLBACK (gst_insert_bin_insert_before),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 4, GST_TYPE_ELEMENT, GST_TYPE_ELEMENT,
+ G_TYPE_POINTER, G_TYPE_POINTER);
+
+ /**
+ * GstInsertBin::insert-after:
+ * @element: the #GstElement to add
+ * @sibling: the #GstElement to add @element after
+ * @callback: the callback to call when the element has been added or not, or
+ * %NULL
+ * @user_data: The data to pass to the callback
+ *
+ * This action signal adds the filter like element after the @sibling
+ * element in the bin.
+ * element in the bin.
+ *
+ * Same as gst_insert_bin_insert_after()
+ */
+ signals[SIG_INSERT_AFTER] = g_signal_new_class_handler ("insert-after",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_CALLBACK (gst_insert_bin_insert_after),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 4, GST_TYPE_ELEMENT, GST_TYPE_ELEMENT,
+ G_TYPE_POINTER, G_TYPE_POINTER);
+
+
+ /**
+ * GstInsertBin::remove:
+ * @element: the #GstElement to remove
+ * @callback: the callback to call when the element has been removed or not,
+ * or %NULL
+ * @user_data: The data to pass to the callback
+ *
+ * This action signal removed the filter like element from the bin.
+ *
+ * Same as gst_insert_bin_remove()
+ */
+ signals[SIG_REMOVE] = g_signal_new_class_handler ("remove",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_CALLBACK (gst_insert_bin_remove),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 3, GST_TYPE_ELEMENT, G_TYPE_POINTER, G_TYPE_POINTER);
+}
+
+static void
+change_data_free (struct ChangeData *data)
+{
+ gst_object_unref (data->element);
+ if (data->sibling)
+ gst_object_unref (data->sibling);
+ g_slice_free (struct ChangeData, data);
+}
+
+
+static void
+gst_insert_bin_change_data_complete (GstInsertBin * self,
+ struct ChangeData *data, gboolean success)
+{
+ if (data->callback)
+ data->callback (self, data->element, success, data->user_data);
+
+ change_data_free (data);
+}
+
+static void
+gst_insert_bin_init (GstInsertBin * self)
+{
+ GstProxyPad *internal;
+
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GST_TYPE_INSERT_BIN,
+ GstInsertBinPrivate);
+
+ g_queue_init (&self->priv->change_queue);
+
+ self->priv->sinkpad = gst_ghost_pad_new_no_target_from_template ("sink",
+ gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (self),
+ "sink"));
+ gst_element_add_pad (GST_ELEMENT (self), self->priv->sinkpad);
+
+ internal = gst_proxy_pad_get_internal (GST_PROXY_PAD (self->priv->sinkpad));
+ self->priv->srcpad = gst_ghost_pad_new_from_template ("src",
+ GST_PAD (internal),
+ gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (self), "src"));
+ gst_object_unref (internal);
+ gst_element_add_pad (GST_ELEMENT (self), self->priv->srcpad);
+}
+
+static void
+gst_insert_bin_dispose (GObject * object)
+{
+ GstInsertBin *self = GST_INSERT_BIN (object);
+ struct ChangeData *data;
+
+ while ((data = g_queue_pop_head (&self->priv->change_queue)))
+ gst_insert_bin_change_data_complete (self, data, FALSE);
+
+ gst_ghost_pad_set_target (GST_GHOST_PAD (self->priv->srcpad), NULL);
+ gst_ghost_pad_set_target (GST_GHOST_PAD (self->priv->sinkpad), NULL);
+
+ G_OBJECT_CLASS (gst_insert_bin_parent_class)->dispose (object);
+}
+
+static gboolean
+validate_element (GstInsertBin * self, GstElement * element)
+{
+ gboolean valid = TRUE;;
+
+ GST_OBJECT_LOCK (element);
+ if (element->numsrcpads != 1 || element->numsinkpads != 1) {
+ GST_WARNING_OBJECT (self,
+ "Element does not have a single src and sink pad");
+ valid = FALSE;
+ }
+
+ if (GST_OBJECT_PARENT (element) != NULL) {
+ GST_WARNING_OBJECT (self, "Element already has a parent");
+ valid = FALSE;
+ }
+ GST_OBJECT_UNLOCK (element);
+
+ return valid;
+}
+
+static GstPad *
+get_single_pad (GstElement * element, GstPadDirection direction)
+{
+ GstPad *pad = NULL;
+
+ GST_OBJECT_LOCK (element);
+ if (direction == GST_PAD_SRC) {
+ if (element->numsrcpads == 1)
+ pad = element->srcpads->data;
+ } else {
+ if (element->numsinkpads == 1)
+ pad = element->sinkpads->data;
+ }
+
+ if (pad)
+ gst_object_ref (pad);
+ GST_OBJECT_UNLOCK (element);
+
+ return pad;
+}
+
+static gboolean
+is_right_direction_for_block (GstPad * pad)
+{
+ gboolean right_direction;
+
+ GST_OBJECT_LOCK (pad);
+ switch (pad->mode) {
+ case GST_PAD_MODE_NONE:
+ right_direction = TRUE;
+ break;
+ case GST_PAD_MODE_PUSH:
+ right_direction = (pad->direction == GST_PAD_SRC);
+ break;
+ case GST_PAD_MODE_PULL:
+ right_direction = (pad->direction == GST_PAD_SINK);
+ break;
+ default:
+ right_direction = FALSE;
+ g_assert_not_reached ();
+ }
+ GST_OBJECT_UNLOCK (pad);
+
+ return right_direction;
+}
+
+static void
+gst_insert_bin_block_pad_unlock (GstInsertBin * self)
+{
+ struct ChangeData *data;
+ GstPad *pad;
+ GstPadProbeType probetype;
+
+again:
+
+ data = g_queue_peek_head (&self->priv->change_queue);
+ if (!data) {
+ GST_OBJECT_UNLOCK (self);
+ return;
+ }
+
+ if (data->action == GST_INSERT_BIN_ACTION_ADD &&
+ !validate_element (self, data->element))
+ goto error;
+
+ if (data->action == GST_INSERT_BIN_ACTION_ADD) {
+ if (data->sibling) {
+ if (data->direction == DIRECTION_BEFORE)
+ pad = get_single_pad (data->sibling, GST_PAD_SINK);
+ else
+ pad = get_single_pad (data->sibling, GST_PAD_SRC);
+ } else {
+ if (data->direction == DIRECTION_BEFORE)
+ pad = (GstPad *)
+ gst_proxy_pad_get_internal (GST_PROXY_PAD (self->priv->srcpad));
+ else
+ pad = (GstPad *)
+ gst_proxy_pad_get_internal (GST_PROXY_PAD (self->priv->sinkpad));
+ }
+
+ if (!pad) {
+ GST_WARNING_OBJECT (self, "Can not obtain a sibling pad to block"
+ " before adding");
+ goto error;
+ }
+
+ if (!is_right_direction_for_block (pad)) {
+ GstPad *peer = gst_pad_get_peer (pad);
+
+ if (peer) {
+ gst_object_unref (pad);
+ pad = peer;
+ }
+ }
+ } else {
+ GstPad *element_pad;
+
+ element_pad = get_single_pad (data->element, GST_PAD_SINK);
+ if (!element_pad) {
+ GST_WARNING_OBJECT (self, "Can not obtain the element's sink pad");
+ goto error;
+ }
+
+ if (!is_right_direction_for_block (element_pad)) {
+ pad = gst_pad_get_peer (element_pad);
+ } else {
+ gst_object_unref (element_pad);
+
+ element_pad = get_single_pad (data->element, GST_PAD_SRC);
+ if (!element_pad) {
+ GST_WARNING_OBJECT (self, "Can not obtain the element's src pad");
+ goto error;
+ }
+
+ pad = gst_pad_get_peer (element_pad);
+ }
+ gst_object_unref (element_pad);
+
+ if (!pad) {
+ GST_WARNING_OBJECT (self, "Can not obtain a sibling pad for removing");
+ goto error;
+ }
+ }
+
+ if (GST_PAD_IS_SRC (pad))
+ probetype = GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM;
+ else
+ probetype = GST_PAD_PROBE_TYPE_BLOCK_UPSTREAM;
+
+ GST_OBJECT_UNLOCK (self);
+ gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_IDLE | probetype, pad_blocked_cb,
+ self, NULL);
+ gst_object_unref (pad);
+ return;
+
+error:
+ g_queue_pop_head (&self->priv->change_queue);
+ gst_insert_bin_change_data_complete (self, data, FALSE);
+ goto again;
+}
+
+static GstPadProbeReturn
+wait_and_drop_eos_cb (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
+{
+ GstPad *peer;
+
+ if (GST_EVENT_TYPE (GST_PAD_PROBE_INFO_EVENT (info)) != GST_EVENT_EOS)
+ return GST_PAD_PROBE_PASS;
+
+ peer = gst_pad_get_peer (pad);
+ if (peer) {
+ gst_pad_unlink (pad, peer);
+ gst_object_unref (peer);
+ }
+
+ return GST_PAD_PROBE_DROP;
+}
+
+static void
+gst_insert_bin_do_change (GstInsertBin * self, GstPad * pad)
+{
+ struct ChangeData *data;
+
+ GST_OBJECT_LOCK (self);
+
+ if (!is_right_direction_for_block (pad)) {
+ GST_WARNING_OBJECT (self, "Block pad does not have the expected direction");
+ goto next;
+ }
+
+ while ((data = g_queue_pop_head (&self->priv->change_queue)) != NULL) {
+ GstPad *peer = NULL;
+ GstPad *other_peer = NULL;
+
+ GST_OBJECT_UNLOCK (self);
+
+
+ if (data->action == GST_INSERT_BIN_ACTION_ADD &&
+ !validate_element (self, data->element))
+ goto error;
+
+ peer = gst_pad_get_peer (pad);
+
+ if (peer == NULL) {
+ GST_WARNING_OBJECT (self, "Blocked pad has no peer");
+ goto error;
+ }
+
+ if (data->action == GST_INSERT_BIN_ACTION_ADD) {
+ GstPad *srcpad = NULL, *sinkpad = NULL;
+ GstPad *peersrcpad, *peersinkpad;
+
+ /* First let's make sure we have the right pad */
+ if (data->sibling) {
+ GstElement *parent = NULL;
+ GstPad *siblingpad;
+
+ if ((gst_pad_get_direction (pad) == GST_PAD_SRC &&
+ data->direction == DIRECTION_BEFORE) ||
+ (gst_pad_get_direction (pad) == GST_PAD_SINK &&
+ data->direction == DIRECTION_AFTER))
+ siblingpad = peer;
+ else
+ siblingpad = pad;
+
+ parent = gst_pad_get_parent_element (siblingpad);
+ if (parent != NULL)
+ gst_object_unref (parent);
+
+ if (parent != data->sibling)
+ goto retry;
+ } else {
+ GstObject *parent;
+ GstPad *ghost;
+ GstPad *proxypad;
+
+ if (data->direction == DIRECTION_BEFORE) {
+ ghost = self->priv->srcpad;
+ if (gst_pad_get_direction (pad) == GST_PAD_SINK)
+ proxypad = pad;
+ else
+ proxypad = peer;
+ } else {
+ ghost = self->priv->sinkpad;
+ if (gst_pad_get_direction (pad) == GST_PAD_SINK)
+ proxypad = peer;
+ else
+ proxypad = pad;
+ }
+
+ if (!GST_IS_PROXY_PAD (proxypad))
+ goto retry;
+ parent = gst_pad_get_parent (proxypad);
+ if (!parent)
+ goto retry;
+ gst_object_unref (parent);
+ if (GST_PAD_CAST (parent) != ghost)
+ goto retry;
+ }
+
+ if (gst_pad_get_direction (pad) == GST_PAD_SRC) {
+ peersrcpad = pad;
+ peersinkpad = peer;
+ } else {
+ peersrcpad = peer;
+ peersinkpad = pad;
+ }
+
+ if (GST_IS_PROXY_PAD (peersrcpad)) {
+ GstObject *parent = gst_pad_get_parent (peersrcpad);
+
+ if (GST_PAD_CAST (parent) == self->priv->sinkpad)
+ peersrcpad = NULL;
+
+ if (parent)
+ gst_object_unref (parent);
+ }
+
+ if (GST_IS_PROXY_PAD (peersinkpad)) {
+ GstObject *parent = gst_pad_get_parent (peersinkpad);
+
+ if (GST_PAD_CAST (parent) == self->priv->srcpad)
+ peersinkpad = NULL;
+
+ if (parent)
+ gst_object_unref (parent);
+ }
+
+ if (peersinkpad && peersrcpad) {
+ gst_pad_unlink (peersrcpad, peersinkpad);
+ } else {
+ if (!peersinkpad)
+ gst_ghost_pad_set_target (GST_GHOST_PAD (self->priv->srcpad), NULL);
+ if (!peersrcpad)
+ gst_ghost_pad_set_target (GST_GHOST_PAD (self->priv->sinkpad), NULL);
+ }
+
+ srcpad = get_single_pad (data->element, GST_PAD_SRC);
+ sinkpad = get_single_pad (data->element, GST_PAD_SINK);
+
+ if (srcpad == NULL || sinkpad == NULL) {
+ GST_WARNING_OBJECT (self, "Can not get element src or sink pad");
+ goto error;
+ }
+
+ if (!gst_bin_add (GST_BIN (self), data->element)) {
+ GST_WARNING_OBJECT (self, "Can not add element to bin");
+ goto error;
+ }
+
+ if (peersrcpad) {
+ if (GST_PAD_LINK_FAILED (gst_pad_link (peersrcpad, sinkpad))) {
+ GST_WARNING_OBJECT (self, "Can not link sibling's %s:%s pad"
+ " to element's %s:%s pad", GST_DEBUG_PAD_NAME (peersrcpad),
+ GST_DEBUG_PAD_NAME (sinkpad));
+ goto error;
+ }
+ } else {
+ if (!gst_ghost_pad_set_target (GST_GHOST_PAD (self->priv->sinkpad),
+ sinkpad)) {
+ GST_WARNING_OBJECT (self, "Can not set %s:%s as target for %s",
+ GST_DEBUG_PAD_NAME (sinkpad),
+ GST_DEBUG_PAD_NAME (self->priv->sinkpad));
+ goto error;
+ }
+ }
+
+ if (peersinkpad) {
+ if (GST_PAD_LINK_FAILED (gst_pad_link (srcpad, peersinkpad))) {
+ GST_WARNING_OBJECT (self, "Can not link element's %s:%s pad"
+ " to sibling's %s:%s pad", GST_DEBUG_PAD_NAME (srcpad),
+ GST_DEBUG_PAD_NAME (peersinkpad));
+ goto error;
+ }
+ } else {
+ if (!gst_ghost_pad_set_target (GST_GHOST_PAD (self->priv->srcpad),
+ srcpad)) {
+ GST_WARNING_OBJECT (self, "Can not set %s:%s as target for %s",
+ GST_DEBUG_PAD_NAME (srcpad),
+ GST_DEBUG_PAD_NAME (self->priv->srcpad));
+ goto error;
+ }
+ }
+
+ gst_object_unref (srcpad);
+ gst_object_unref (sinkpad);
+
+ if (!gst_element_sync_state_with_parent (data->element)) {
+ GST_WARNING_OBJECT (self, "Can not sync element's state with parent");
+ goto error;
+ }
+ } else {
+ GstElement *parent = NULL;
+ GstPad *other_pad;
+ GstCaps *caps = NULL, *peercaps = NULL;
+ gboolean can_intersect;
+ gboolean success;
+
+ parent = gst_pad_get_parent_element (peer);
+ if (parent != NULL)
+ gst_object_unref (parent);
+
+ if (parent != data->element)
+ goto retry;
+
+ if (gst_pad_get_direction (peer) == GST_PAD_SRC)
+ other_pad = get_single_pad (data->element, GST_PAD_SINK);
+ else
+ other_pad = get_single_pad (data->element, GST_PAD_SRC);
+
+ if (!other_pad) {
+ GST_WARNING_OBJECT (self, "Can not get element's other pad");
+ goto error;
+ }
+
+ other_peer = gst_pad_get_peer (other_pad);
+ gst_object_unref (other_pad);
+
+ if (!other_peer) {
+ GST_WARNING_OBJECT (self, "Can not get element's other peer");
+ goto error;
+ }
+
+ /* Get the negotiated caps for the source pad peer,
+ * because renegotiation while the pipeline is playing doesn't work
+ * that fast.
+ */
+ if (gst_pad_get_direction (pad) == GST_PAD_SRC)
+ caps = gst_pad_get_current_caps (pad);
+ else
+ peercaps = gst_pad_get_current_caps (other_peer);
+ if (!caps)
+ caps = gst_pad_query_caps (pad, NULL);
+ if (!peercaps)
+ peercaps = gst_pad_query_caps (other_peer, NULL);
+ can_intersect = gst_caps_can_intersect (caps, peercaps);
+ gst_caps_unref (caps);
+ gst_caps_unref (peercaps);
+
+ if (!can_intersect) {
+ GST_WARNING_OBJECT (self, "Pads are incompatible without the element");
+ goto error;
+ }
+
+ if (gst_pad_get_direction (other_peer) == GST_PAD_SRC &&
+ gst_pad_is_active (other_peer)) {
+ gulong probe_id;
+
+ probe_id = gst_pad_add_probe (other_peer,
+ GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
+ wait_and_drop_eos_cb, NULL, NULL);
+ gst_pad_send_event (peer, gst_event_new_eos ());
+ gst_pad_remove_probe (other_peer, probe_id);
+ }
+
+ gst_element_set_locked_state (data->element, TRUE);
+ gst_element_set_state (data->element, GST_STATE_NULL);
+ if (!gst_bin_remove (GST_BIN (self), data->element)) {
+ GST_WARNING_OBJECT (self, "Element removal rejected");
+ goto error;
+ }
+ gst_element_set_locked_state (data->element, FALSE);
+
+ if (gst_pad_get_direction (pad) == GST_PAD_SRC)
+ success = GST_PAD_LINK_SUCCESSFUL (gst_pad_link_full (pad, other_peer,
+ GST_PAD_LINK_CHECK_HIERARCHY |
+ GST_PAD_LINK_CHECK_TEMPLATE_CAPS));
+ else
+ success = GST_PAD_LINK_SUCCESSFUL (gst_pad_link_full (other_peer, pad,
+ GST_PAD_LINK_CHECK_HIERARCHY |
+ GST_PAD_LINK_CHECK_TEMPLATE_CAPS));
+ gst_object_unref (other_peer);
+ other_peer = NULL;
+
+ if (!success) {
+ GST_ERROR_OBJECT (self, "Could not re-link after the element's"
+ " removal");
+ goto error;
+ }
+ }
+
+
+ gst_insert_bin_change_data_complete (self, data, TRUE);
+ gst_object_unref (peer);
+
+ GST_OBJECT_LOCK (self);
+ continue;
+ done:
+ if (other_peer != NULL)
+ gst_object_unref (other_peer);
+
+ if (peer != NULL)
+ gst_object_unref (peer);
+ break;
+ retry:
+ GST_OBJECT_LOCK (self);
+ g_queue_push_head (&self->priv->change_queue, data);
+ goto done;
+ error:
+ /* Handle error */
+ gst_insert_bin_change_data_complete (self, data, FALSE);
+ GST_OBJECT_LOCK (self);
+ goto done;
+ }
+
+next:
+ gst_insert_bin_block_pad_unlock (self);
+}
+
+
+
+static GstPadProbeReturn
+pad_blocked_cb (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
+{
+ GstInsertBin *self = GST_INSERT_BIN (user_data);
+
+ g_assert (GST_PAD_PROBE_INFO_TYPE (info) &
+ (GST_PAD_PROBE_TYPE_BLOCK | GST_PAD_PROBE_TYPE_IDLE));
+
+ gst_insert_bin_do_change (self, pad);
+
+ return GST_PAD_PROBE_REMOVE;
+}
+
+static void
+gst_insert_bin_add_operation (GstInsertBin * self,
+ GstElement * element, GstInsertBinAction action, GstElement * sibling,
+ GstInsertBinDirection direction, GstInsertBinCallback callback,
+ gpointer user_data)
+{
+ struct ChangeData *data = g_slice_new (struct ChangeData);
+ gboolean block_pad;
+
+ data->element = element;
+ data->action = action;
+ data->sibling = sibling;
+ if (data->sibling)
+ gst_object_ref (data->sibling);
+ data->direction = direction;
+ data->callback = callback;
+ data->user_data = user_data;
+
+ GST_OBJECT_LOCK (self);
+ block_pad = g_queue_is_empty (&self->priv->change_queue);
+ g_queue_push_tail (&self->priv->change_queue, data);
+
+ if (block_pad)
+ gst_insert_bin_block_pad_unlock (self);
+ else
+ GST_OBJECT_UNLOCK (self);
+}
+
+static void
+gst_insert_bin_add (GstInsertBin * self, GstElement * element,
+ GstElement * sibling, GstInsertBinDirection direction,
+ GstInsertBinCallback callback, gpointer user_data)
+{
+ gst_object_ref_sink (element);
+
+ if (!validate_element (self, element))
+ goto reject;
+
+ if (sibling) {
+ gboolean is_parent;
+
+ GST_OBJECT_LOCK (sibling);
+ is_parent = (GST_OBJECT_PARENT (sibling) == GST_OBJECT_CAST (self));
+ GST_OBJECT_UNLOCK (sibling);
+
+ if (!is_parent)
+ goto reject;
+ }
+
+
+ gst_insert_bin_add_operation (self, element, GST_INSERT_BIN_ACTION_ADD,
+ sibling, direction, callback, user_data);
+ return;
+
+reject:
+ if (callback)
+ callback (self, element, FALSE, user_data);
+ gst_object_unref (element);
+}
+
+
+/**
+ * gst_insert_bin_prepend:
+ * @element: the #GstElement to add
+ * @callback: the callback to call when the element has been added or not, or
+ * %NULL
+ * @user_data: The data to pass to the callback
+ *
+ * This action signal adds the filter like element before any other element
+ * in the bin.
+ *
+ * Same as the #GstInsertBin::prepend signal.
+ */
+
+void
+gst_insert_bin_prepend (GstInsertBin * self, GstElement * element,
+ GstInsertBinCallback callback, gpointer user_data)
+{
+ g_return_if_fail (GST_IS_ELEMENT (element));
+
+ gst_insert_bin_add (self, element, NULL, DIRECTION_AFTER,
+ callback, user_data);
+}
+
+/**
+ * gst_insert_bin_append:
+ * @element: the #GstElement to add
+ * @callback: the callback to call when the element has been added or not, or
+ * %NULL
+ * @user_data: The data to pass to the callback
+ *
+ * This action signal adds the filter like element after any other element
+ * in the bin.
+ *
+ * Same as the #GstInsertBin::append signal.
+ */
+
+void
+gst_insert_bin_append (GstInsertBin * self, GstElement * element,
+ GstInsertBinCallback callback, gpointer user_data)
+{
+ g_return_if_fail (GST_IS_ELEMENT (element));
+
+ gst_insert_bin_add (self, element, NULL, DIRECTION_BEFORE,
+ callback, user_data);
+}
+
+/**
+ * gst_insert_bin_insert_before
+ * @element: the #GstElement to add
+ * @sibling: the #GstElement to add @element before
+ * @callback: the callback to call when the element has been added or not, or
+ * %NULL
+ * @user_data: The data to pass to the callback
+ *
+ * This action signal adds the filter like element before the @sibling
+ * element in the bin.
+ *
+ * Same as the #GstInsertBin::insert-before signal.
+ */
+void
+gst_insert_bin_insert_before (GstInsertBin * self, GstElement * element,
+ GstElement * sibling, GstInsertBinCallback callback, gpointer user_data)
+{
+ g_return_if_fail (GST_IS_ELEMENT (element));
+ g_return_if_fail (GST_IS_ELEMENT (sibling));
+
+ gst_insert_bin_add (self, element, sibling, DIRECTION_BEFORE,
+ callback, user_data);
+}
+
+/**
+ * gst_insert_bin_insert_after:
+ * @element: the #GstElement to add
+ * @sibling: the #GstElement to add @element after
+ * @callback: the callback to call when the element has been added or not, or
+ * %NULL
+ * @user_data: The data to pass to the callback
+ *
+ * This action signal adds the filter like element after the @sibling
+ * element in the bin.
+ *
+ * Same as the #GstInsertBin::insert-after signal.
+ */
+void
+gst_insert_bin_insert_after (GstInsertBin * self, GstElement * element,
+ GstElement * sibling, GstInsertBinCallback callback, gpointer user_data)
+{
+ g_return_if_fail (GST_IS_ELEMENT (element));
+ g_return_if_fail (GST_IS_ELEMENT (sibling));
+
+ gst_insert_bin_add (self, element, sibling, DIRECTION_AFTER,
+ callback, user_data);
+}
+
+/**
+ * gst_insert_bin_remove:
+ * @element: the #GstElement to remove
+ * @callback: the callback to call when the element has been removed or not,
+ * or %NULL
+ * @user_data: The data to pass to the callback
+ *
+ * This action signal removed the filter like element from the bin.
+ *
+ * Same as the #GstInsertBin::remove signal.
+ */
+
+void
+gst_insert_bin_remove (GstInsertBin * self, GstElement * element,
+ GstInsertBinCallback callback, gpointer user_data)
+{
+ GstObject *parent;
+
+ g_return_if_fail (GST_IS_ELEMENT (element));
+
+ parent = gst_element_get_parent (element);
+
+ if (parent) {
+ gboolean is_parent;
+
+ is_parent = (parent == GST_OBJECT_CAST (self));
+ gst_object_unref (parent);
+
+ if (!is_parent) {
+ if (callback)
+ callback (self, element, FALSE, user_data);
+ return;
+ }
+ } else {
+ GList *item;
+ struct ChangeData *data = NULL;
+
+ GST_OBJECT_LOCK (self);
+ for (item = self->priv->change_queue.head; item; item = item->next) {
+ data = item->data;
+
+ if (data->element == element) {
+ if (data->action == GST_INSERT_BIN_ACTION_ADD)
+ g_queue_delete_link (&self->priv->change_queue, item);
+ break;
+ }
+ data = NULL;
+ }
+ GST_OBJECT_UNLOCK (self);
+
+ if (data) {
+ gst_object_ref (element);
+ gst_insert_bin_change_data_complete (self, data, TRUE);
+ if (callback)
+ callback (self, element, TRUE, user_data);
+ gst_object_unref (element);
+ } else {
+ if (callback)
+ callback (self, element, FALSE, user_data);
+ }
+ return;
+ }
+
+ gst_object_ref (element);
+
+ gst_insert_bin_add_operation (self, element, GST_INSERT_BIN_ACTION_REMOVE,
+ NULL, FALSE, callback, user_data);
+}
+
+/**
+ * gst_insert_bin_new:
+ * @name: (allow-none): The name of the new #GstInsertBin element (or %NULL)
+ *
+ * Creates a new #GstInsertBin
+ *
+ * Returns: The new #GstInsertBin
+ */
+
+GstElement *
+gst_insert_bin_new (const gchar * name)
+{
+ if (name)
+ return g_object_new (GST_TYPE_INSERT_BIN, "name", name, NULL);
+ else
+ return g_object_new (GST_TYPE_INSERT_BIN, NULL);
+}
diff --git a/gst-libs/gst/insertbin/gstinsertbin.h b/gst-libs/gst/insertbin/gstinsertbin.h
new file mode 100644
index 000000000..21e5d5430
--- /dev/null
+++ b/gst-libs/gst/insertbin/gstinsertbin.h
@@ -0,0 +1,101 @@
+/*
+ * GStreamer
+ *
+ * Copyright 2013 Collabora Ltd
+ * @author: Olivier Crete <olivier.crete@collabora.com>
+ *
+ * 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_INSERT_BIN_H__
+#define __GST_INSERT_BIN_H__
+
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+#define GST_TYPE_INSERT_BIN (gst_insert_bin_get_type())
+#define GST_INSERT_BIN(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_INSERT_BIN,GstInsertBin))
+#define GST_IS_INSERT_BIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_INSERT_BIN))
+#define GST_INSERT_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass) ,GST_TYPE_INSERT_BIN,GstInsertBinClass))
+#define GST_IS_INSERT_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass) ,GST_TYPE_INSERT_BIN))
+#define GST_INSERT_BIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) ,GST_TYPE_INSERT_BIN,GstInsertBinClass))
+typedef struct _GstInsertBin GstInsertBin;
+typedef struct _GstInsertBinClass GstInsertBinClass;
+typedef struct _GstInsertBinPrivate GstInsertBinPrivate;
+
+/**
+ * GstInsertBinCallback:
+ * @insertbin: A #GstInsertBin
+ * @element: The #GstElement on which the operation was performed
+ * @success: %TRUE if the operation was successful
+ * @user_data: The user data passed
+ *
+ * This is the prototype of callbacks to be called when the operation completes.
+ * It could be called at any time, including as a re-entrant call while the
+ * operation is requested.
+ */
+
+typedef void (*GstInsertBinCallback) (GstInsertBin *insertbin,
+ GstElement *element,
+ gboolean success,
+ gpointer user_data);
+
+/**
+ * GstInsertBin:
+ *
+ * The object structure.
+ */
+struct _GstInsertBin
+{
+ GstBin parent;
+
+ /*< private >*/
+ GstInsertBinPrivate *priv;
+};
+
+/**
+ * GstInsertBinClass:
+ *
+ * The object class structure.
+ */
+struct _GstInsertBinClass
+{
+ GstBinClass parent_class;
+};
+
+GType gst_insert_bin_get_type (void);
+
+GstElement *gst_insert_bin_new (const gchar * name);
+
+void gst_insert_bin_prepend (GstInsertBin * self, GstElement * element,
+ GstInsertBinCallback callback, gpointer user_data);
+void gst_insert_bin_append (GstInsertBin * self, GstElement * element,
+ GstInsertBinCallback callback, gpointer user_data);
+void gst_insert_bin_insert_before (GstInsertBin * self,
+ GstElement * element, GstElement * sibling,
+ GstInsertBinCallback callback, gpointer user_data);
+void gst_insert_bin_insert_after (GstInsertBin * self,
+ GstElement * element, GstElement * sibling,
+ GstInsertBinCallback callback, gpointer user_data);
+void gst_insert_bin_remove (GstInsertBin * self, GstElement * element,
+ GstInsertBinCallback callback, gpointer user_data);
+
+
+G_END_DECLS
+#endif /* __GST_INSERT_BIN_H__ */
diff --git a/gst-plugins-bad.spec.in b/gst-plugins-bad.spec.in
index e20fd5341..08d690716 100644
--- a/gst-plugins-bad.spec.in
+++ b/gst-plugins-bad.spec.in
@@ -201,6 +201,7 @@ make ERROR_CFLAGS='' ERROR_CXXFLAGS=''
%{_libdir}/libgstphotography-%{majorminor}.so.*
%{_libdir}/libgstsignalprocessor-%{majorminor}.so.*
%{_libdir}/libgstcodecparsers-%{majorminor}.so.*
+%{_libdir}/libgstinsertbin-%{majorminor}.so.*
# Plugins without external dependencies
%{_libdir}/gstreamer-%{majorminor}/libgstadpcmdec.so
%{_libdir}/gstreamer-%{majorminor}/libgstadpcmenc.so
@@ -353,10 +354,12 @@ make ERROR_CFLAGS='' ERROR_CXXFLAGS=''
%{_libdir}/libgstbasevideo-%{majorminor}.so
%{_libdir}/libgstphotography-%{majorminor}.so
%{_libdir}/libgstsignalprocessor-%{majorminor}.so
+%{_libdir}/libgstinsertbin-%{majorminor}.so
%{_libdir}/libgstcodecparsers-%{majorminor}.so
%{_libdir}/libgstbasecamerabinsrc-%{majorminor}.so
%{_includedir}/gstreamer-%{majorminor}/gst/interfaces/photography*
%{_includedir}/gstreamer-%{majorminor}/gst/codecparsers
+%{_includedir}/gstreamer-%{majorminor}/gst/insertbin
%{_includedir}/gstreamer-%{majorminor}/gst/signalprocessor
%{_includedir}/gstreamer-%{majorminor}/gst/video
%{_includedir}/gstreamer-%{majorminor}/gst/basecamerabinsrc/gstbasecamerasrc.h
@@ -367,6 +370,7 @@ make ERROR_CFLAGS='' ERROR_CXXFLAGS=''
# pkg-config files
%{_libdir}/pkgconfig/gstreamer-plugins-bad-%{majorminor}.pc
%{_libdir}/pkgconfig/gstreamer-codecparsers-%{majorminor}.pc
+%{_libdir}/pkgconfig/gstreamer-insertbin-%{majorminor}.pc
%{_libdir}/pkgconfig/gstreamer-basevideo-%{majorminor}.pc
%files devel-docs
diff --git a/pkgconfig/Makefile.am b/pkgconfig/Makefile.am
index 6ab9bfd72..93f7de34c 100644
--- a/pkgconfig/Makefile.am
+++ b/pkgconfig/Makefile.am
@@ -2,11 +2,13 @@
pcverfiles = \
gstreamer-plugins-bad-@GST_API_VERSION@.pc \
gstreamer-codecparsers-@GST_API_VERSION@.pc \
+ gstreamer-insertbin-@GST_API_VERSION@.pc \
gstreamer-basevideo-@GST_API_VERSION@.pc
pcverfiles_uninstalled = \
gstreamer-plugins-bad-@GST_API_VERSION@-uninstalled.pc \
gstreamer-codecparsers-@GST_API_VERSION@-uninstalled.pc \
+ gstreamer-insertbin-@GST_API_VERSION@-uninstalled.pc \
gstreamer-basevideo-@GST_API_VERSION@-uninstalled.pc
all-local: $(pcverfiles) $(pcverfiles_uninstalled)
@@ -28,6 +30,7 @@ CLEANFILES = $(pcverfiles) $(pcverfiles_uninstalled)
pcinfiles = \
gstreamer-plugins-bad.pc.in gstreamer-plugins-bad-uninstalled.pc.in \
gstreamer-codecparsers.pc.in gstreamer-codecparsers-uninstalled.pc.in \
+ gstreamer-insertbin.pc.in gstreamer-insertbin-uninstalled.pc.in \
gstreamer-basevideo.pc.in gstreamer-basevideo-uninstalled.pc.in
DISTCLEANFILES = $(pcinfiles:.in=)
diff --git a/pkgconfig/gstreamer-insertbin-uninstalled.pc.in b/pkgconfig/gstreamer-insertbin-uninstalled.pc.in
new file mode 100644
index 000000000..0a01e7996
--- /dev/null
+++ b/pkgconfig/gstreamer-insertbin-uninstalled.pc.in
@@ -0,0 +1,12 @@
+prefix=
+exec_prefix=
+libdir=${pcfiledir}/../gst-libs/gst/insertbin
+includedir=${pcfiledir}/../gst-libs
+
+Name: GStreamer Insert Bin, Uninstalled
+Description: Bin to automatically and insertally link elements, uninstalled
+Requires: gstreamer-@GST_API_VERSION@
+Version: @VERSION@
+Libs: -L${libdir} ${libdir}/libgstinsertbin-@GST_API_VERSION@.la
+Cflags: -I${includedir}
+
diff --git a/pkgconfig/gstreamer-insertbin.pc.in b/pkgconfig/gstreamer-insertbin.pc.in
new file mode 100644
index 000000000..cbf76d327
--- /dev/null
+++ b/pkgconfig/gstreamer-insertbin.pc.in
@@ -0,0 +1,12 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@/gstreamer-@GST_API_VERSION@
+
+Name: GStreamer Insert Bin
+Description: Bin to automatically and insertally link elements
+Requires: gstreamer-@GST_API_VERSION@
+Version: @VERSION@
+Libs: -L${libdir} -lgstinsertbin-@GST_API_VERSION@
+Cflags: -I${includedir}
+
diff --git a/tests/check/Makefile.am b/tests/check/Makefile.am
index 51d560eca..66a72dac5 100644
--- a/tests/check/Makefile.am
+++ b/tests/check/Makefile.am
@@ -232,6 +232,7 @@ check_PROGRAMS = \
elements/viewfinderbin \
$(check_zbar) \
$(check_orc) \
+ libs/insertbin \
$(EXPERIMENTAL_CHECKS)
noinst_HEADERS = elements/mxfdemux.h
@@ -365,6 +366,13 @@ elements_uvch264demux_CFLAGS = -DUVCH264DEMUX_DATADIR="$(srcdir)/elements/uvch26
pipelines_streamheader_CFLAGS = $(GIO_CFLAGS) $(AM_CFLAGS)
pipelines_streamheader_LDADD = $(GIO_LIBS) $(LDADD)
+libs_insertbin_LDADD = \
+ $(GST_PLUGINS_BAD_LIBS) $(GST_BASE_LIBS) $(GST_LIBS) $(LDADD) \
+ $(top_builddir)/gst-libs/gst/insertbin/libgstinsertbin-@GST_API_VERSION@.la
+libs_insertbin_CFLAGS = \
+ $(GST_PLUGINS_BAD_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(AM_CFLAGS)
+
+
EXTRA_DIST = gst-plugins-bad.supp $(uvch264_dist_data)
orc_bayer_CFLAGS = $(ORC_CFLAGS)
diff --git a/tests/check/libs/.gitignore b/tests/check/libs/.gitignore
index 238cb3d11..728f9e9c2 100644
--- a/tests/check/libs/.gitignore
+++ b/tests/check/libs/.gitignore
@@ -2,3 +2,4 @@
h264parser
mpegvideoparser
vc1parser
+insertbin
diff --git a/tests/check/libs/insertbin.c b/tests/check/libs/insertbin.c
new file mode 100644
index 000000000..b98c94a54
--- /dev/null
+++ b/tests/check/libs/insertbin.c
@@ -0,0 +1,354 @@
+/* GStreamer
+ *
+ * unit test for autoconvert element
+ * Copyright (C) 2009 Jan Schmidt <thaytan@noraisin.net>
+ *
+ * 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 <gst/gst.h>
+#include <gst/check/gstcheck.h>
+#include <gst/insertbin/gstinsertbin.h>
+
+GstStaticPadTemplate sinkpad_template = GST_STATIC_PAD_TEMPLATE ("sink", // the name of the pad
+ GST_PAD_SINK, // the direction of the pad
+ GST_PAD_ALWAYS, // when this pad will be present
+ GST_STATIC_CAPS ( // the capabilities of the padtemplate
+ "video/test")
+ );
+
+GstStaticPadTemplate srcpad_template = GST_STATIC_PAD_TEMPLATE ("src", // the name of the pad
+ GST_PAD_SRC, // the direction of the pad
+ GST_PAD_ALWAYS, // when this pad will be present
+ GST_STATIC_CAPS ( // the capabilities of the padtemplate
+ "video/test")
+ );
+
+gint cb_count = 0;
+
+GMutex mutex;
+GCond cond;
+
+GThread *push_thread = NULL;
+gulong block_probe_id = 0;
+gboolean is_blocked = FALSE;
+
+static void
+success_cb (GstInsertBin * insertbin, GstElement * element, gboolean success,
+ gpointer user_data)
+{
+ fail_unless (g_thread_self () != push_thread);
+ fail_unless (success == TRUE);
+ fail_unless (GST_IS_ELEMENT (insertbin));
+ fail_unless (GST_IS_ELEMENT (element));
+ cb_count++;
+}
+
+static void
+fail_cb (GstInsertBin * insertbin, GstElement * element, gboolean success,
+ gpointer user_data)
+{
+ fail_unless (GST_IS_ELEMENT (insertbin));
+ fail_unless (GST_IS_ELEMENT (element));
+ fail_unless (success == FALSE);
+ cb_count++;
+}
+
+/*
+ * This is a macro so the line number of any error is more useful
+ */
+#define push_buffer(srcpad, count) \
+ { \
+ fail_unless (cb_count == 0); \
+ gst_pad_push (srcpad, gst_buffer_new ()); \
+ fail_unless (g_list_length (buffers) == 1); \
+ gst_check_drop_buffers (); \
+ fail_unless (cb_count == (count)); \
+ cb_count = 0; \
+ }
+
+#define check_reset_cb_count(count) \
+ { \
+ fail_unless (cb_count == (count)); \
+ cb_count = 0; \
+ }
+
+static gpointer
+thread_push_buffer (gpointer data)
+{
+ GstPad *pad = data;
+
+ gst_pad_push (pad, gst_buffer_new ());
+ return NULL;
+}
+
+static GstPadProbeReturn
+got_buffer_block (GstPad * pad, GstPadProbeInfo * info, gpointer data)
+{
+ g_mutex_lock (&mutex);
+ is_blocked = TRUE;
+ g_cond_broadcast (&cond);
+ g_mutex_unlock (&mutex);
+
+ return GST_PAD_PROBE_OK;
+}
+
+#define block_thread() \
+{ \
+ fail_unless (cb_count == 0); \
+ fail_unless (block_probe_id == 0); \
+ fail_unless (is_blocked == FALSE); \
+ fail_unless (push_thread == NULL); \
+ block_probe_id = gst_pad_add_probe (sinkpad, \
+ GST_PAD_PROBE_TYPE_BLOCK | GST_PAD_PROBE_TYPE_BUFFER, \
+ got_buffer_block, NULL, NULL); \
+ push_thread = g_thread_new ("push block", thread_push_buffer, srcpad); \
+ fail_unless (push_thread != NULL); \
+ g_mutex_lock (&mutex); \
+ while (is_blocked == FALSE) \
+ g_cond_wait (&cond, &mutex); \
+ g_mutex_unlock (&mutex); \
+}
+
+#define unblock_thread() \
+{ \
+ fail_unless (cb_count == 0); \
+ fail_unless (push_thread != NULL); \
+ fail_unless (is_blocked == TRUE); \
+ fail_unless (block_probe_id != 0); \
+ gst_pad_remove_probe (sinkpad, block_probe_id); \
+ g_thread_join (push_thread); \
+ fail_unless (g_list_length (buffers) == 1); \
+ gst_check_drop_buffers (); \
+ block_probe_id = 0; \
+ push_thread = NULL; \
+ is_blocked = FALSE; \
+}
+
+GST_START_TEST (test_insertbin_simple)
+{
+ GstElement *insertbin;
+ GstElement *elem;
+ GstElement *elem2;
+ GstElement *elem3;
+ GstElement *elem4;
+ GstPad *srcpad;
+ GstPad *sinkpad;
+
+ g_mutex_init (&mutex);
+ g_cond_init (&cond);
+
+ insertbin = gst_insert_bin_new (NULL);
+ fail_unless (insertbin != NULL);
+ ASSERT_OBJECT_REFCOUNT (insertbin, insertbin, 1);
+ srcpad = gst_check_setup_src_pad (insertbin, &srcpad_template);
+ sinkpad = gst_check_setup_sink_pad (insertbin, &sinkpad_template);
+
+ g_assert (srcpad && sinkpad);
+
+ ASSERT_CRITICAL (gst_insert_bin_append (GST_INSERT_BIN (insertbin), NULL,
+ NULL, NULL));
+ ASSERT_CRITICAL (gst_insert_bin_append (GST_INSERT_BIN (insertbin), NULL,
+ fail_cb, NULL));
+ fail_unless (cb_count == 0);
+
+
+ elem = gst_element_factory_make ("identity", NULL);
+ gst_insert_bin_append (GST_INSERT_BIN (insertbin), elem, success_cb, NULL);
+ check_reset_cb_count (1);
+
+ gst_insert_bin_remove (GST_INSERT_BIN (insertbin), elem, success_cb, NULL);
+ check_reset_cb_count (1);
+
+ fail_unless (gst_pad_set_active (srcpad, TRUE));
+ fail_unless (gst_pad_set_active (sinkpad, TRUE));
+ fail_unless (gst_element_set_state (insertbin,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS);
+
+ fail_unless (cb_count == 0);
+ fail_unless (buffers == NULL);
+
+ push_buffer (srcpad, 0);
+
+ block_thread ();
+ elem = gst_element_factory_make ("identity", NULL);
+ gst_insert_bin_prepend (GST_INSERT_BIN (insertbin), elem, success_cb, NULL);
+ unblock_thread ();
+ push_buffer (srcpad, 1);
+ push_buffer (srcpad, 0);
+
+ block_thread ();
+ gst_insert_bin_append (GST_INSERT_BIN (insertbin), elem, fail_cb, NULL);
+ check_reset_cb_count (1);
+ unblock_thread ();
+ push_buffer (srcpad, 0);
+
+ block_thread ();
+ gst_insert_bin_remove (GST_INSERT_BIN (insertbin), elem, success_cb, NULL);
+ unblock_thread ();
+ push_buffer (srcpad, 1);
+ push_buffer (srcpad, 0);
+
+ block_thread ();
+ elem = gst_element_factory_make ("identity", NULL);
+ gst_insert_bin_append (GST_INSERT_BIN (insertbin), elem, success_cb, NULL);
+ unblock_thread ();
+ push_buffer (srcpad, 1);
+ push_buffer (srcpad, 0);
+
+ block_thread ();
+ elem2 = gst_element_factory_make ("identity", NULL);
+ gst_insert_bin_append (GST_INSERT_BIN (insertbin), elem2, success_cb, NULL);
+ unblock_thread ();
+ push_buffer (srcpad, 1);
+ push_buffer (srcpad, 0);
+
+ block_thread ();
+ elem3 = gst_element_factory_make ("identity", NULL);
+ gst_insert_bin_append (GST_INSERT_BIN (insertbin), elem3, success_cb, NULL);
+ unblock_thread ();
+ push_buffer (srcpad, 1);
+ push_buffer (srcpad, 0);
+
+ block_thread ();
+ elem4 = gst_element_factory_make ("identity", NULL);
+ gst_insert_bin_prepend (GST_INSERT_BIN (insertbin), elem4, success_cb, NULL);
+ unblock_thread ();
+ push_buffer (srcpad, 1);
+ push_buffer (srcpad, 0);
+
+ block_thread ();
+ gst_insert_bin_remove (GST_INSERT_BIN (insertbin), elem3, success_cb, NULL);
+ gst_insert_bin_remove (GST_INSERT_BIN (insertbin), elem2, success_cb, NULL);
+ unblock_thread ();
+ push_buffer (srcpad, 1);
+ push_buffer (srcpad, 1);
+ push_buffer (srcpad, 0);
+
+ block_thread ();
+ elem2 = gst_element_factory_make ("identity", NULL);
+ elem3 = gst_element_factory_make ("identity", NULL);
+ gst_insert_bin_insert_after (GST_INSERT_BIN (insertbin), elem2, elem,
+ success_cb, NULL);
+ gst_insert_bin_insert_before (GST_INSERT_BIN (insertbin), elem3, elem4,
+ success_cb, NULL);
+ unblock_thread ();
+ push_buffer (srcpad, 1);
+ push_buffer (srcpad, 1);
+ push_buffer (srcpad, 0);
+
+ block_thread ();
+ gst_insert_bin_remove (GST_INSERT_BIN (insertbin), elem3, success_cb, NULL);
+ gst_insert_bin_remove (GST_INSERT_BIN (insertbin), elem2, success_cb, NULL);
+ unblock_thread ();
+ push_buffer (srcpad, 2);
+ push_buffer (srcpad, 0);
+
+ block_thread ();
+ elem2 = gst_element_factory_make ("identity", NULL);
+ elem3 = gst_element_factory_make ("identity", NULL);
+ gst_insert_bin_insert_before (GST_INSERT_BIN (insertbin), elem3, elem4,
+ success_cb, NULL);
+ gst_insert_bin_insert_after (GST_INSERT_BIN (insertbin), elem2, elem,
+ success_cb, NULL);
+ unblock_thread ();
+ push_buffer (srcpad, 2);
+ push_buffer (srcpad, 0);
+
+ block_thread ();
+ elem = gst_bin_new (NULL);
+ gst_insert_bin_append (GST_INSERT_BIN (insertbin), elem, fail_cb, NULL);
+ check_reset_cb_count (1);
+ unblock_thread ();
+
+ block_thread ();
+ elem = gst_bin_new (NULL);
+ elem2 = gst_element_factory_make ("identity", NULL);
+ gst_bin_add (GST_BIN (elem), elem2);
+ gst_insert_bin_append (GST_INSERT_BIN (insertbin), elem2, fail_cb, NULL);
+ check_reset_cb_count (1);
+ gst_insert_bin_remove (GST_INSERT_BIN (insertbin), elem2, fail_cb, NULL);
+ check_reset_cb_count (1);
+ unblock_thread ();
+ gst_object_unref (elem);
+
+ push_buffer (srcpad, 0);
+
+ block_thread ();
+ elem = gst_element_factory_make ("identity", NULL);
+ elem2 = gst_element_factory_make ("identity", NULL);
+ gst_insert_bin_append (GST_INSERT_BIN (insertbin), elem, success_cb, NULL);
+ gst_insert_bin_append (GST_INSERT_BIN (insertbin), elem2, success_cb, NULL);
+ gst_insert_bin_remove (GST_INSERT_BIN (insertbin), elem2, success_cb, NULL);
+ check_reset_cb_count (2);
+ unblock_thread ();
+ push_buffer (srcpad, 1);
+ push_buffer (srcpad, 0);
+
+ block_thread ();
+ elem = gst_element_factory_make ("identity", NULL);
+ elem2 = gst_element_factory_make ("identity", NULL);
+ gst_insert_bin_insert_before (GST_INSERT_BIN (insertbin), elem, elem2,
+ fail_cb, NULL);
+ check_reset_cb_count (1);
+ unblock_thread ();
+ push_buffer (srcpad, 0);
+ gst_object_unref (elem2);
+
+ fail_unless (gst_element_set_state (insertbin,
+ GST_STATE_NULL) == GST_STATE_CHANGE_SUCCESS);
+ gst_pad_set_active (srcpad, FALSE);
+ gst_pad_set_active (sinkpad, FALSE);
+
+
+ elem = gst_element_factory_make ("identity", NULL);
+ gst_insert_bin_remove (GST_INSERT_BIN (insertbin), elem, fail_cb, NULL);
+ check_reset_cb_count (1);
+
+ gst_insert_bin_append (GST_INSERT_BIN (insertbin), elem, success_cb, NULL);
+ check_reset_cb_count (1);
+
+ gst_check_teardown_sink_pad (insertbin);
+ gst_check_teardown_src_pad (insertbin);
+ gst_check_teardown_element (insertbin);
+
+ fail_unless (cb_count == 0);
+
+ g_mutex_clear (&mutex);
+ g_cond_clear (&cond);
+}
+
+GST_END_TEST;
+
+
+static Suite *
+insert_bin_suite (void)
+{
+ Suite *s = suite_create ("insertbin");
+ TCase *tc_basic = tcase_create ("general");
+
+ suite_add_tcase (s, tc_basic);
+ tcase_add_test (tc_basic, test_insertbin_simple);
+
+ return s;
+}
+
+
+GST_CHECK_MAIN (insert_bin);