aboutsummaryrefslogtreecommitdiff
path: root/gst
diff options
context:
space:
mode:
authorNicolas Dechesne <n-dechesne@ti.com>2011-09-30 00:54:14 +0200
committerNicolas Dechesne <n-dechesne@ti.com>2011-09-30 00:54:14 +0200
commitbeb43201c942afa12dfc4225218b61fcd6e90923 (patch)
treea1d85bac4a9ad32ed087d08ecd99ac3c03b9aa0a /gst
Imported Upstream version 0.11.1upstream/0.11.1
Diffstat (limited to 'gst')
-rw-r--r--gst/Makefile.am319
-rw-r--r--gst/Makefile.in1879
-rw-r--r--gst/gettext.h69
-rw-r--r--gst/glib-compat-private.h55
-rw-r--r--gst/glib-compat.h37
-rw-r--r--gst/gst-i18n-app.h43
-rw-r--r--gst/gst-i18n-lib.h46
-rw-r--r--gst/gst.c1219
-rw-r--r--gst/gst.h112
-rw-r--r--gst/gst_private.h238
-rw-r--r--gst/gstatomicqueue.c419
-rw-r--r--gst/gstatomicqueue.h55
-rw-r--r--gst/gstbin.c3889
-rw-r--r--gst/gstbin.h190
-rw-r--r--gst/gstbuffer.c1618
-rw-r--r--gst/gstbuffer.h499
-rw-r--r--gst/gstbufferlist.c284
-rw-r--r--gst/gstbufferlist.h176
-rw-r--r--gst/gstbufferpool.c954
-rw-r--r--gst/gstbufferpool.h214
-rw-r--r--gst/gstbus.c1310
-rw-r--r--gst/gstbus.h193
-rw-r--r--gst/gstcaps.c1943
-rw-r--r--gst/gstcaps.h419
-rw-r--r--gst/gstchildproxy.c538
-rw-r--r--gst/gstchildproxy.h91
-rw-r--r--gst/gstclock.c1488
-rw-r--r--gst/gstclock.h574
-rw-r--r--gst/gstcompat.h61
-rw-r--r--gst/gstconfig.h.in199
-rw-r--r--gst/gstdatetime.c816
-rw-r--r--gst/gstdatetime.h66
-rw-r--r--gst/gstdebugutils.c745
-rw-r--r--gst/gstdebugutils.h109
-rw-r--r--gst/gstelement.c2992
-rw-r--r--gst/gstelement.h811
-rw-r--r--gst/gstelementfactory.c815
-rw-r--r--gst/gstelementfactory.h249
-rw-r--r--gst/gstelementmetadata.h77
-rw-r--r--gst/gsterror.c331
-rw-r--r--gst/gsterror.h255
-rw-r--r--gst/gstevent.c1311
-rw-r--r--gst/gstevent.h497
-rw-r--r--gst/gstfilter.c88
-rw-r--r--gst/gstfilter.h44
-rw-r--r--gst/gstformat.c264
-rw-r--r--gst/gstformat.h113
-rw-r--r--gst/gstghostpad.c1312
-rw-r--r--gst/gstghostpad.h133
-rw-r--r--gst/gstindex.c1009
-rw-r--r--gst/gstindex.h424
-rw-r--r--gst/gstindexfactory.c214
-rw-r--r--gst/gstindexfactory.h76
-rw-r--r--gst/gstinfo.c2003
-rw-r--r--gst/gstinfo.h1549
-rw-r--r--gst/gstiterator.c830
-rw-r--r--gst/gstiterator.h273
-rw-r--r--gst/gstmacros.h54
-rw-r--r--gst/gstmarshal.list25
-rw-r--r--gst/gstmemory.c677
-rw-r--r--gst/gstmemory.h302
-rw-r--r--gst/gstmessage.c2158
-rw-r--r--gst/gstmessage.h549
-rw-r--r--gst/gstmeta.c153
-rw-r--r--gst/gstmeta.h151
-rw-r--r--gst/gstminiobject.c443
-rw-r--r--gst/gstminiobject.h228
-rw-r--r--gst/gstobject.c952
-rw-r--r--gst/gstobject.h234
-rw-r--r--gst/gstpad.c4934
-rw-r--r--gst/gstpad.h927
-rw-r--r--gst/gstpadtemplate.c456
-rw-r--r--gst/gstpadtemplate.h192
-rw-r--r--gst/gstparamspecs.c207
-rw-r--r--gst/gstparamspecs.h128
-rw-r--r--gst/gstparse.c360
-rw-r--r--gst/gstparse.h119
-rw-r--r--gst/gstpipeline.c855
-rw-r--r--gst/gstpipeline.h110
-rw-r--r--gst/gstplugin.c1859
-rw-r--r--gst/gstplugin.h360
-rw-r--r--gst/gstpluginfeature.c387
-rw-r--r--gst/gstpluginfeature.h183
-rw-r--r--gst/gstpluginloader.c1026
-rw-r--r--gst/gstpluginloader.h39
-rw-r--r--gst/gstpoll.c1592
-rw-r--r--gst/gstpoll.h97
-rw-r--r--gst/gstpreset.c1122
-rw-r--r--gst/gstpreset.h98
-rw-r--r--gst/gstquark.c74
-rw-r--r--gst/gstquark.h166
-rw-r--r--gst/gstquery.c2004
-rw-r--r--gst/gstquery.h385
-rw-r--r--gst/gstregistry.c1745
-rw-r--r--gst/gstregistry.h229
-rw-r--r--gst/gstregistrybinary.c631
-rw-r--r--gst/gstregistrybinary.h76
-rw-r--r--gst/gstregistrychunks.c904
-rw-r--r--gst/gstregistrychunks.h168
-rw-r--r--gst/gstsegment.c720
-rw-r--r--gst/gstsegment.h178
-rw-r--r--gst/gststructure.c3205
-rw-r--r--gst/gststructure.h258
-rw-r--r--gst/gstsystemclock.c875
-rw-r--r--gst/gstsystemclock.h89
-rw-r--r--gst/gsttaglist.c1885
-rw-r--r--gst/gsttaglist.h1034
-rw-r--r--gst/gsttagsetter.c424
-rw-r--r--gst/gsttagsetter.h100
-rw-r--r--gst/gsttask.c855
-rw-r--r--gst/gsttask.h201
-rw-r--r--gst/gsttaskpool.c282
-rw-r--r--gst/gsttaskpool.h103
-rw-r--r--gst/gsttrace.c515
-rw-r--r--gst/gsttrace.h253
-rw-r--r--gst/gsttypefind.c231
-rw-r--r--gst/gsttypefind.h124
-rw-r--r--gst/gsttypefindfactory.c227
-rw-r--r--gst/gsttypefindfactory.h81
-rw-r--r--gst/gsturi.c888
-rw-r--r--gst/gsturi.h145
-rw-r--r--gst/gstutils.c3813
-rw-r--r--gst/gstutils.h983
-rw-r--r--gst/gstvalue.c4971
-rw-r--r--gst/gstvalue.h573
-rw-r--r--gst/gstversion.h.in90
-rw-r--r--gst/math-compat.h84
-rw-r--r--gst/parse/Makefile.am55
-rw-r--r--gst/parse/Makefile.in745
-rw-r--r--gst/parse/grammar.tab.h80
-rw-r--r--gst/parse/grammar.y1044
-rw-r--r--gst/parse/parse.l148
-rw-r--r--gst/parse/types.h101
133 files changed, 89256 insertions, 0 deletions
diff --git a/gst/Makefile.am b/gst/Makefile.am
new file mode 100644
index 0000000..dcb8c93
--- /dev/null
+++ b/gst/Makefile.am
@@ -0,0 +1,319 @@
+lib_LTLIBRARIES = libgstreamer-@GST_MAJORMINOR@.la
+
+if GST_DISABLE_REGISTRY
+GST_REGISTRY_SRC =
+else
+GST_REGISTRY_SRC = gstregistrybinary.c
+endif
+
+if GST_DISABLE_PARSE
+SUBDIRS_PARSE =
+GST_PARSE_LA =
+else
+SUBDIRS_PARSE = parse
+GST_PARSE_LA = parse/libgstparse.la
+endif
+
+if GST_DISABLE_TRACE
+GST_TRACE_SRC =
+else
+GST_TRACE_SRC = gsttrace.c
+endif
+
+if GST_DISABLE_PLUGIN
+GST_PLUGIN_SRC =
+else
+GST_PLUGIN_SRC = gstplugin.c
+endif
+
+SUBDIRS = $(SUBDIRS_PARSE)
+
+DIST_SUBDIRS = parse
+
+# make variables for all generated source and header files to make the
+# distinction clear
+
+built_header_configure = gstconfig.h gstversion.h
+built_header_make = gstenumtypes.h gstmarshal.h
+built_source_make = gstenumtypes.c gstmarshal.c
+
+EXTRA_libgstreamer_@GST_MAJORMINOR@_la_SOURCES = \
+ gstmarshal.list gsttrace.c \
+ gstregistrybinary.c
+
+
+# temporarily not used
+# glib-compat.c
+
+libgstreamer_@GST_MAJORMINOR@_la_SOURCES = \
+ gst.c \
+ gstobject.c \
+ gstbin.c \
+ gstbuffer.c \
+ gstbufferlist.c \
+ gstbufferpool.c \
+ gstbus.c \
+ gstcaps.c \
+ gstchildproxy.c \
+ gstclock.c \
+ gstdatetime.c \
+ gstdebugutils.c \
+ gstelement.c \
+ gstelementfactory.c \
+ gsterror.c \
+ gstevent.c \
+ gstfilter.c \
+ gstformat.c \
+ gstghostpad.c \
+ gstindex.c \
+ gstindexfactory.c \
+ gstinfo.c \
+ gstiterator.c \
+ gstatomicqueue.c \
+ gstmessage.c \
+ gstmeta.c \
+ gstmemory.c \
+ gstminiobject.c \
+ gstpad.c \
+ gstpadtemplate.c \
+ gstparamspecs.c \
+ gstpipeline.c \
+ gstplugin.c \
+ gstpluginfeature.c \
+ gstpluginloader.c \
+ gstpoll.c \
+ gstpreset.c \
+ gstquark.c \
+ gstquery.c \
+ gstregistry.c \
+ gstregistrychunks.c \
+ gstsegment.c \
+ gststructure.c \
+ gstsystemclock.c \
+ gsttaglist.c \
+ gsttagsetter.c \
+ gsttask.c \
+ gsttaskpool.c \
+ $(GST_TRACE_SRC) \
+ gsttypefind.c \
+ gsttypefindfactory.c \
+ gsturi.c \
+ gstutils.c \
+ gstvalue.c \
+ gstparse.c \
+ $(GST_REGISTRY_SRC)
+
+# do not put files in the distribution that are generated
+nodist_libgstreamer_@GST_MAJORMINOR@_la_SOURCES = $(built_source_make)
+
+# BUILT_SOURCES are built on make all/check/install before all other targets
+BUILT_SOURCES = \
+ $(built_header_configure) \
+ $(built_header_make) \
+ $(built_source_make)
+# CLEANFILES is for files generated by make
+CLEANFILES = $(built_header_make) $(built_source_make) $(as_dll_cleanfiles) *.gcno *.gcda *.gcov *.gcov.out
+# DISTCLEANFILES is for files generated by configure
+DISTCLEANFILES = $(built_header_configure)
+
+libgstreamer_@GST_MAJORMINOR@_la_CFLAGS = \
+ -D_GNU_SOURCE \
+ -DGST_EXPORTS \
+ -DG_LOG_DOMAIN=g_log_domain_gstreamer \
+ -DGST_MAJORMINOR=\""$(GST_MAJORMINOR)"\" \
+ -DGST_DISABLE_DEPRECATED \
+ $(VALGRIND_CFLAGS) \
+ $(GST_ALL_CFLAGS)
+
+libgstreamer_@GST_MAJORMINOR@_la_LIBADD = \
+ $(GST_PARSE_LA) \
+ $(GST_ALL_LIBS) \
+ $(WIN32_LIBS) \
+ $(LIBM)
+
+libgstreamer_@GST_MAJORMINOR@_la_LDFLAGS = \
+ $(GST_LIB_LDFLAGS) $(GST_ALL_LDFLAGS) $(GST_LT_LDFLAGS)
+
+libgstreamer_@GST_MAJORMINOR@includedir = $(includedir)/gstreamer-@GST_MAJORMINOR@/gst
+
+gst_headers = \
+ gst.h \
+ glib-compat.h \
+ gstobject.h \
+ gstbin.h \
+ gstbuffer.h \
+ gstbufferlist.h \
+ gstbufferpool.h \
+ gstbus.h \
+ gstcaps.h \
+ gstchildproxy.h \
+ gstclock.h \
+ gstcompat.h \
+ gstdatetime.h \
+ gstdebugutils.h \
+ gstelement.h \
+ gstelementmetadata.h \
+ gstelementfactory.h \
+ gsterror.h \
+ gstevent.h \
+ gstfilter.h \
+ gstformat.h \
+ gstghostpad.h \
+ gstindex.h \
+ gstindexfactory.h \
+ gstinfo.h \
+ gstiterator.h \
+ gstatomicqueue.h \
+ gstmacros.h \
+ gstmessage.h \
+ gstmeta.h \
+ gstmemory.h \
+ gstminiobject.h \
+ gstpad.h \
+ gstpadtemplate.h \
+ gstparamspecs.h \
+ gstpipeline.h \
+ gstplugin.h \
+ gstpluginfeature.h \
+ gstpoll.h \
+ gstpreset.h \
+ gstquery.h \
+ gstsegment.h \
+ gststructure.h \
+ gstsystemclock.h \
+ gsttaglist.h \
+ gsttagsetter.h \
+ gsttask.h \
+ gsttaskpool.h \
+ gsttrace.h \
+ gsttypefind.h \
+ gsttypefindfactory.h \
+ gsturi.h \
+ gstutils.h \
+ gstvalue.h \
+ gstregistry.h \
+ gstparse.h
+
+libgstreamer_@GST_MAJORMINOR@include_HEADERS = $(gst_headers) math-compat.h
+
+nodist_libgstreamer_@GST_MAJORMINOR@include_HEADERS = \
+ $(built_header_configure) $(built_header_make)
+
+noinst_HEADERS = \
+ gettext.h \
+ glib-compat-private.h \
+ gst-i18n-lib.h \
+ gst-i18n-app.h \
+ gstelementmetadata.h \
+ gstpluginloader.h \
+ gstquark.h \
+ gstregistrybinary.h \
+ gstregistrychunks.h \
+ gst_private.h
+
+gstmarshal.h: gstmarshal.list
+ $(AM_V_GEN)glib-genmarshal --header --prefix=gst_marshal $(srcdir)/gstmarshal.list > gstmarshal.h.tmp && \
+ mv gstmarshal.h.tmp gstmarshal.h
+
+gstmarshal.c: gstmarshal.list gst_private.h
+ $(AM_V_GEN)echo "#include \"gst_private.h\"" > gstmarshal.c.tmp && \
+ echo "#include \"glib-object.h\"" >> gstmarshal.c.tmp && \
+ echo "#include \"gstmarshal.h\"" >> gstmarshal.c.tmp && \
+ glib-genmarshal --body --prefix=gst_marshal $(srcdir)/gstmarshal.list >> gstmarshal.c.tmp && \
+ mv gstmarshal.c.tmp gstmarshal.c
+
+gstenumtypes.h: $(gst_headers)
+ $(AM_V_GEN)glib-mkenums \
+ --fhead "#ifndef __GST_ENUM_TYPES_H__\n#define __GST_ENUM_TYPES_H__\n\n#include <glib-object.h>\n\nG_BEGIN_DECLS\n" \
+ --fprod "\n/* enumerations from \"@filename@\" */\n" \
+ --vhead "GType @enum_name@_get_type (void);\n#define GST_TYPE_@ENUMSHORT@ (@enum_name@_get_type())\n" \
+ --ftail "G_END_DECLS\n\n#endif /* __GST_ENUM_TYPES_H__ */" \
+ $^ > gstenumtypes.h
+
+gstenumtypes.c: $(gst_headers)
+ $(AM_V_GEN)glib-mkenums \
+ --fhead "#include \"gst_private.h\"\n#include <gst/gst.h>\n#define C_ENUM(v) ((gint) v)\n#define C_FLAGS(v) ((guint) v)\n " \
+ --fprod "\n/* enumerations from \"@filename@\" */" \
+ --vhead "GType\n@enum_name@_get_type (void)\n{\n static gsize id = 0;\n static const G@Type@Value values[] = {" \
+ --vprod " { C_@TYPE@(@VALUENAME@), \"@VALUENAME@\", \"@valuenick@\" }," \
+ --vtail " { 0, NULL, NULL }\n };\n\n if (g_once_init_enter (&id)) {\n GType tmp = g_@type@_register_static (\"@EnumName@\", values);\n g_once_init_leave (&id, tmp);\n }\n\n return (GType) id;\n}" \
+ $^ > gstenumtypes.c
+
+%.c.gcov: .libs/libgstreamer_@GST_MAJORMINOR@_la-%.gcda %.c
+ $(GCOV) -b -f -o $^ > $@.out
+
+gcov: $(libgstreamer_@GST_MAJORMINOR@_la_SOURCES:=.gcov)
+
+Android.mk: Makefile.am
+ androgenizer -:PROJECT gstreamer -:SHARED libgstreamer-@GST_MAJORMINOR@ \
+ -:TAGS eng debug \
+ -:REL_TOP $(top_srcdir) -:ABS_TOP $(abs_top_srcdir) \
+ -:SOURCES $(libgstreamer_@GST_MAJORMINOR@_la_SOURCES) \
+ $(nodist_libgstreamer_@GST_MAJORMINOR@_la_SOURCES) \
+ -:CFLAGS $(DEFS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) \
+ -:LDFLAGS $(libgstreamer_@GST_MAJORMINOR@_la_LDFLAGS) \
+ $(libgstreamer_@GST_MAJORMINOR@_la_LIBADD) \
+ -ldl \
+ -:SUBDIR gst/parse \
+ -:HEADER_TARGET gstreamer-@GST_MAJORMINOR@/gst \
+ -:HEADERS $(libgstreamer_@GST_MAJORMINOR@include_HEADERS) \
+ -:LIBFILTER_STATIC gstparse \
+ -:PASSTHROUGH LOCAL_ARM_MODE:=arm \
+ > $@
+
+if HAVE_INTROSPECTION
+BUILT_GIRSOURCES = Gst-@GST_MAJORMINOR@.gir
+
+gir_headers=$(patsubst %,$(srcdir)/%, $(libgstreamer_@GST_MAJORMINOR@include_HEADERS))
+gir_sources=$(patsubst %,$(srcdir)/%, $(libgstreamer_@GST_MAJORMINOR@_la_SOURCES))
+
+Gst-@GST_MAJORMINOR@.gir: $(INTROSPECTION_SCANNER) libgstreamer-@GST_MAJORMINOR@.la
+ $(AM_V_GEN)GST_PLUGIN_SYSTEM_PATH="" GST_PLUGIN_PATH="" GST_REGISTRY_UPDATE=no \
+ $(INTROSPECTION_SCANNER) -v --namespace Gst \
+ --nsversion=@GST_MAJORMINOR@ \
+ -I$(top_srcdir) \
+ -I$(top_builddir) \
+ -DIN_GOBJECT_INTROSPECTION=1 \
+ -DGST_USE_UNSTABLE_API \
+ --c-include='gst/gst.h' \
+ --library=libgstreamer-0.11.la \
+ --include=GLib-2.0 \
+ --include=GObject-2.0 \
+ --include=GModule-2.0 \
+ --libtool="$(top_builddir)/libtool" \
+ --pkg glib-2.0 \
+ --pkg gobject-2.0 \
+ --pkg gmodule-no-export-2.0 \
+ --pkg gthread-2.0 \
+ --pkg-export gstreamer-@GST_MAJORMINOR@ \
+ --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)$(INTROSPECTION_COMPILER) --includedir=$(srcdir) --includedir=$(builddir) $(INTROSPECTION_COMPILER_OPTS) $< -o $(@F)
+
+CLEANFILES += $(BUILT_GIRSOURCES) $(typelibs_DATA)
+endif
+
+# try to prevent packaging errors
+check-libexecdir-consistency:
+ @if test "${GST_PLUGIN_SCANNER_INSTALLED}" != "${libexecdir}/gstreamer-$(GST_MAJORMINOR)/gst-plugin-scanner"; then \
+ echo "*** Inconsistent libexecdir! Please use ./configure --libexecdir=/foo/bar"; \
+ echo "*** to set the libexecdir and not make libexecdir=/foo/bar or the like."; \
+ echo "*** The same goes for prefix, libdir etc."; \
+ echo "*** ${GST_PLUGIN_SCANNER_INSTALLED} != ${libexecdir}/gstreamer-$(GST_MAJORMINOR)/gst-plugin-scanner"; \
+ exit 1; \
+ fi
+
+all-local: check-libexecdir-consistency
diff --git a/gst/Makefile.in b/gst/Makefile.in
new file mode 100644
index 0000000..aa21f3a
--- /dev/null
+++ b/gst/Makefile.in
@@ -0,0 +1,1879 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+@HAVE_INTROSPECTION_TRUE@am__append_1 = $(BUILT_GIRSOURCES) $(typelibs_DATA)
+subdir = gst
+DIST_COMMON = $(libgstreamer_@GST_MAJORMINOR@include_HEADERS) \
+ $(noinst_HEADERS) $(srcdir)/Makefile.am $(srcdir)/Makefile.in \
+ $(srcdir)/gstconfig.h.in $(srcdir)/gstversion.h.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/common/m4/as-ac-expand.m4 \
+ $(top_srcdir)/common/m4/as-auto-alt.m4 \
+ $(top_srcdir)/common/m4/as-compiler-flag.m4 \
+ $(top_srcdir)/common/m4/as-docbook.m4 \
+ $(top_srcdir)/common/m4/as-libtool.m4 \
+ $(top_srcdir)/common/m4/as-python.m4 \
+ $(top_srcdir)/common/m4/as-scrub-include.m4 \
+ $(top_srcdir)/common/m4/as-version.m4 \
+ $(top_srcdir)/common/m4/ax_create_stdint_h.m4 \
+ $(top_srcdir)/common/m4/gst-arch.m4 \
+ $(top_srcdir)/common/m4/gst-args.m4 \
+ $(top_srcdir)/common/m4/gst-check.m4 \
+ $(top_srcdir)/common/m4/gst-doc.m4 \
+ $(top_srcdir)/common/m4/gst-error.m4 \
+ $(top_srcdir)/common/m4/gst-feature.m4 \
+ $(top_srcdir)/common/m4/gst-function.m4 \
+ $(top_srcdir)/common/m4/gst-gettext.m4 \
+ $(top_srcdir)/common/m4/gst-glib2.m4 \
+ $(top_srcdir)/common/m4/gst-package-release-datetime.m4 \
+ $(top_srcdir)/common/m4/gst-parser.m4 \
+ $(top_srcdir)/common/m4/gst-platform.m4 \
+ $(top_srcdir)/common/m4/gst-plugin-docs.m4 \
+ $(top_srcdir)/common/m4/gst-plugindir.m4 \
+ $(top_srcdir)/common/m4/gst.m4 \
+ $(top_srcdir)/common/m4/gtk-doc.m4 \
+ $(top_srcdir)/common/m4/introspection.m4 \
+ $(top_srcdir)/common/m4/pkg.m4 \
+ $(top_srcdir)/m4/check-checks.m4 $(top_srcdir)/m4/gettext.m4 \
+ $(top_srcdir)/m4/iconv.m4 $(top_srcdir)/m4/intlmacosx.m4 \
+ $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \
+ $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libtool.m4 \
+ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
+ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
+ $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/po.m4 \
+ $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES = gstconfig.h gstversion.h
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(girdir)" \
+ "$(DESTDIR)$(typelibsdir)" \
+ "$(DESTDIR)$(libgstreamer_@GST_MAJORMINOR@includedir)" \
+ "$(DESTDIR)$(libgstreamer_@GST_MAJORMINOR@includedir)"
+LTLIBRARIES = $(lib_LTLIBRARIES)
+@GST_DISABLE_PARSE_FALSE@am__DEPENDENCIES_1 = parse/libgstparse.la
+am__DEPENDENCIES_2 =
+libgstreamer_@GST_MAJORMINOR@_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_2) \
+ $(am__DEPENDENCIES_2)
+am__libgstreamer_@GST_MAJORMINOR@_la_SOURCES_DIST = gst.c gstobject.c \
+ gstbin.c gstbuffer.c gstbufferlist.c gstbufferpool.c gstbus.c \
+ gstcaps.c gstchildproxy.c gstclock.c gstdatetime.c \
+ gstdebugutils.c gstelement.c gstelementfactory.c gsterror.c \
+ gstevent.c gstfilter.c gstformat.c gstghostpad.c gstindex.c \
+ gstindexfactory.c gstinfo.c gstiterator.c gstatomicqueue.c \
+ gstmessage.c gstmeta.c gstmemory.c gstminiobject.c gstpad.c \
+ gstpadtemplate.c gstparamspecs.c gstpipeline.c gstplugin.c \
+ gstpluginfeature.c gstpluginloader.c gstpoll.c gstpreset.c \
+ gstquark.c gstquery.c gstregistry.c gstregistrychunks.c \
+ gstsegment.c gststructure.c gstsystemclock.c gsttaglist.c \
+ gsttagsetter.c gsttask.c gsttaskpool.c gsttrace.c \
+ gsttypefind.c gsttypefindfactory.c gsturi.c gstutils.c \
+ gstvalue.c gstparse.c gstregistrybinary.c
+@GST_DISABLE_TRACE_FALSE@am__objects_1 = libgstreamer_@GST_MAJORMINOR@_la-gsttrace.lo
+@GST_DISABLE_REGISTRY_FALSE@am__objects_2 = libgstreamer_@GST_MAJORMINOR@_la-gstregistrybinary.lo
+am_libgstreamer_@GST_MAJORMINOR@_la_OBJECTS = \
+ libgstreamer_@GST_MAJORMINOR@_la-gst.lo \
+ libgstreamer_@GST_MAJORMINOR@_la-gstobject.lo \
+ libgstreamer_@GST_MAJORMINOR@_la-gstbin.lo \
+ libgstreamer_@GST_MAJORMINOR@_la-gstbuffer.lo \
+ libgstreamer_@GST_MAJORMINOR@_la-gstbufferlist.lo \
+ libgstreamer_@GST_MAJORMINOR@_la-gstbufferpool.lo \
+ libgstreamer_@GST_MAJORMINOR@_la-gstbus.lo \
+ libgstreamer_@GST_MAJORMINOR@_la-gstcaps.lo \
+ libgstreamer_@GST_MAJORMINOR@_la-gstchildproxy.lo \
+ libgstreamer_@GST_MAJORMINOR@_la-gstclock.lo \
+ libgstreamer_@GST_MAJORMINOR@_la-gstdatetime.lo \
+ libgstreamer_@GST_MAJORMINOR@_la-gstdebugutils.lo \
+ libgstreamer_@GST_MAJORMINOR@_la-gstelement.lo \
+ libgstreamer_@GST_MAJORMINOR@_la-gstelementfactory.lo \
+ libgstreamer_@GST_MAJORMINOR@_la-gsterror.lo \
+ libgstreamer_@GST_MAJORMINOR@_la-gstevent.lo \
+ libgstreamer_@GST_MAJORMINOR@_la-gstfilter.lo \
+ libgstreamer_@GST_MAJORMINOR@_la-gstformat.lo \
+ libgstreamer_@GST_MAJORMINOR@_la-gstghostpad.lo \
+ libgstreamer_@GST_MAJORMINOR@_la-gstindex.lo \
+ libgstreamer_@GST_MAJORMINOR@_la-gstindexfactory.lo \
+ libgstreamer_@GST_MAJORMINOR@_la-gstinfo.lo \
+ libgstreamer_@GST_MAJORMINOR@_la-gstiterator.lo \
+ libgstreamer_@GST_MAJORMINOR@_la-gstatomicqueue.lo \
+ libgstreamer_@GST_MAJORMINOR@_la-gstmessage.lo \
+ libgstreamer_@GST_MAJORMINOR@_la-gstmeta.lo \
+ libgstreamer_@GST_MAJORMINOR@_la-gstmemory.lo \
+ libgstreamer_@GST_MAJORMINOR@_la-gstminiobject.lo \
+ libgstreamer_@GST_MAJORMINOR@_la-gstpad.lo \
+ libgstreamer_@GST_MAJORMINOR@_la-gstpadtemplate.lo \
+ libgstreamer_@GST_MAJORMINOR@_la-gstparamspecs.lo \
+ libgstreamer_@GST_MAJORMINOR@_la-gstpipeline.lo \
+ libgstreamer_@GST_MAJORMINOR@_la-gstplugin.lo \
+ libgstreamer_@GST_MAJORMINOR@_la-gstpluginfeature.lo \
+ libgstreamer_@GST_MAJORMINOR@_la-gstpluginloader.lo \
+ libgstreamer_@GST_MAJORMINOR@_la-gstpoll.lo \
+ libgstreamer_@GST_MAJORMINOR@_la-gstpreset.lo \
+ libgstreamer_@GST_MAJORMINOR@_la-gstquark.lo \
+ libgstreamer_@GST_MAJORMINOR@_la-gstquery.lo \
+ libgstreamer_@GST_MAJORMINOR@_la-gstregistry.lo \
+ libgstreamer_@GST_MAJORMINOR@_la-gstregistrychunks.lo \
+ libgstreamer_@GST_MAJORMINOR@_la-gstsegment.lo \
+ libgstreamer_@GST_MAJORMINOR@_la-gststructure.lo \
+ libgstreamer_@GST_MAJORMINOR@_la-gstsystemclock.lo \
+ libgstreamer_@GST_MAJORMINOR@_la-gsttaglist.lo \
+ libgstreamer_@GST_MAJORMINOR@_la-gsttagsetter.lo \
+ libgstreamer_@GST_MAJORMINOR@_la-gsttask.lo \
+ libgstreamer_@GST_MAJORMINOR@_la-gsttaskpool.lo \
+ $(am__objects_1) \
+ libgstreamer_@GST_MAJORMINOR@_la-gsttypefind.lo \
+ libgstreamer_@GST_MAJORMINOR@_la-gsttypefindfactory.lo \
+ libgstreamer_@GST_MAJORMINOR@_la-gsturi.lo \
+ libgstreamer_@GST_MAJORMINOR@_la-gstutils.lo \
+ libgstreamer_@GST_MAJORMINOR@_la-gstvalue.lo \
+ libgstreamer_@GST_MAJORMINOR@_la-gstparse.lo $(am__objects_2)
+am__objects_3 = libgstreamer_@GST_MAJORMINOR@_la-gstenumtypes.lo \
+ libgstreamer_@GST_MAJORMINOR@_la-gstmarshal.lo
+nodist_libgstreamer_@GST_MAJORMINOR@_la_OBJECTS = $(am__objects_3)
+libgstreamer_@GST_MAJORMINOR@_la_OBJECTS = \
+ $(am_libgstreamer_@GST_MAJORMINOR@_la_OBJECTS) \
+ $(nodist_libgstreamer_@GST_MAJORMINOR@_la_OBJECTS)
+AM_V_lt = $(am__v_lt_$(V))
+am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY))
+am__v_lt_0 = --silent
+libgstreamer_@GST_MAJORMINOR@_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) \
+ $(libgstreamer_@GST_MAJORMINOR@_la_LDFLAGS) $(LDFLAGS) -o $@
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_$(V))
+am__v_CC_ = $(am__v_CC_$(AM_DEFAULT_VERBOSITY))
+am__v_CC_0 = @echo " CC " $@;
+AM_V_at = $(am__v_at_$(V))
+am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY))
+am__v_at_0 = @
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_$(V))
+am__v_CCLD_ = $(am__v_CCLD_$(AM_DEFAULT_VERBOSITY))
+am__v_CCLD_0 = @echo " CCLD " $@;
+AM_V_GEN = $(am__v_GEN_$(V))
+am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY))
+am__v_GEN_0 = @echo " GEN " $@;
+SOURCES = $(libgstreamer_@GST_MAJORMINOR@_la_SOURCES) \
+ $(EXTRA_libgstreamer_@GST_MAJORMINOR@_la_SOURCES) \
+ $(nodist_libgstreamer_@GST_MAJORMINOR@_la_SOURCES)
+DIST_SOURCES = $(am__libgstreamer_@GST_MAJORMINOR@_la_SOURCES_DIST) \
+ $(EXTRA_libgstreamer_@GST_MAJORMINOR@_la_SOURCES)
+RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \
+ html-recursive info-recursive install-data-recursive \
+ install-dvi-recursive install-exec-recursive \
+ install-html-recursive install-info-recursive \
+ install-pdf-recursive install-ps-recursive install-recursive \
+ installcheck-recursive installdirs-recursive pdf-recursive \
+ ps-recursive uninstall-recursive
+DATA = $(gir_DATA) $(typelibs_DATA)
+HEADERS = $(libgstreamer_@GST_MAJORMINOR@include_HEADERS) \
+ $(nodist_libgstreamer_@GST_MAJORMINOR@include_HEADERS) \
+ $(noinst_HEADERS)
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
+ distclean-recursive maintainer-clean-recursive
+AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \
+ $(RECURSIVE_CLEAN_TARGETS:-recursive=) tags TAGS ctags CTAGS \
+ distdir
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+am__relativize = \
+ dir0=`pwd`; \
+ sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+ sed_rest='s,^[^/]*/*,,'; \
+ sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+ sed_butlast='s,/*[^/]*$$,,'; \
+ while test -n "$$dir1"; do \
+ first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+ if test "$$first" != "."; then \
+ if test "$$first" = ".."; then \
+ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+ else \
+ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+ if test "$$first2" = "$$first"; then \
+ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+ else \
+ dir2="../$$dir2"; \
+ fi; \
+ dir0="$$dir0"/"$$first"; \
+ fi; \
+ fi; \
+ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+ done; \
+ reldir="$$dir2"
+ACLOCAL = @ACLOCAL@
+ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AS = @AS@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BISON_PATH = @BISON_PATH@
+CAT_ENTRY_END = @CAT_ENTRY_END@
+CAT_ENTRY_START = @CAT_ENTRY_START@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CHECK_MAJOR_VERSION = @CHECK_MAJOR_VERSION@
+CHECK_MICRO_VERSION = @CHECK_MICRO_VERSION@
+CHECK_MINOR_VERSION = @CHECK_MINOR_VERSION@
+CHECK_VERSION = @CHECK_VERSION@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIR = @DATADIR@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DEPRECATED_CFLAGS = @DEPRECATED_CFLAGS@
+DLLTOOL = @DLLTOOL@
+DOCBOOK_ROOT = @DOCBOOK_ROOT@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+ENABLE_SUBUNIT = @ENABLE_SUBUNIT@
+ERROR_CFLAGS = @ERROR_CFLAGS@
+EXEEXT = @EXEEXT@
+FFLAGS = @FFLAGS@
+FGREP = @FGREP@
+FLEX_PATH = @FLEX_PATH@
+GCOV = @GCOV@
+GCOV_CFLAGS = @GCOV_CFLAGS@
+GCOV_LIBS = @GCOV_LIBS@
+GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_ONLY_CFLAGS = @GLIB_ONLY_CFLAGS@
+GLIB_ONLY_LIBS = @GLIB_ONLY_LIBS@
+GLIB_PREFIX = @GLIB_PREFIX@
+GLIB_REQ = @GLIB_REQ@
+GMP_LIBS = @GMP_LIBS@
+GMSGFMT = @GMSGFMT@
+GMSGFMT_015 = @GMSGFMT_015@
+GREP = @GREP@
+GSL_LIBS = @GSL_LIBS@
+GST_AGE = @GST_AGE@
+GST_ALL_CFLAGS = @GST_ALL_CFLAGS@
+GST_ALL_CXXFLAGS = @GST_ALL_CXXFLAGS@
+GST_ALL_LDFLAGS = @GST_ALL_LDFLAGS@
+GST_ALL_LIBS = @GST_ALL_LIBS@
+GST_CURRENT = @GST_CURRENT@
+GST_DISABLE_ALLOC_TRACE_DEFINE = @GST_DISABLE_ALLOC_TRACE_DEFINE@
+GST_DISABLE_GST_DEBUG_DEFINE = @GST_DISABLE_GST_DEBUG_DEFINE@
+GST_DISABLE_NET_DEFINE = @GST_DISABLE_NET_DEFINE@
+GST_DISABLE_OPTION_PARSING_DEFINE = @GST_DISABLE_OPTION_PARSING_DEFINE@
+GST_DISABLE_PARSE_DEFINE = @GST_DISABLE_PARSE_DEFINE@
+GST_DISABLE_PLUGIN_DEFINE = @GST_DISABLE_PLUGIN_DEFINE@
+GST_DISABLE_REGISTRY_DEFINE = @GST_DISABLE_REGISTRY_DEFINE@
+GST_DISABLE_TRACE_DEFINE = @GST_DISABLE_TRACE_DEFINE@
+GST_HAVE_MONOTONIC_CLOCK_DEFINE = @GST_HAVE_MONOTONIC_CLOCK_DEFINE@
+GST_HAVE_POSIX_TIMERS_DEFINE = @GST_HAVE_POSIX_TIMERS_DEFINE@
+GST_HAVE_UNALIGNED_ACCESS_DEFINE = @GST_HAVE_UNALIGNED_ACCESS_DEFINE@
+GST_LEVEL_DEFAULT = @GST_LEVEL_DEFAULT@
+GST_LIBVERSION = @GST_LIBVERSION@
+GST_LIB_LDFLAGS = @GST_LIB_LDFLAGS@
+GST_LICENSE = @GST_LICENSE@
+GST_LT_LDFLAGS = @GST_LT_LDFLAGS@
+GST_MAJORMINOR = @GST_MAJORMINOR@
+GST_OBJ_CFLAGS = @GST_OBJ_CFLAGS@
+GST_OBJ_CXXFLAGS = @GST_OBJ_CXXFLAGS@
+GST_OBJ_LIBS = @GST_OBJ_LIBS@
+GST_OPTION_CFLAGS = @GST_OPTION_CFLAGS@
+GST_OPTION_CXXFLAGS = @GST_OPTION_CXXFLAGS@
+GST_PACKAGE_NAME = @GST_PACKAGE_NAME@
+GST_PACKAGE_ORIGIN = @GST_PACKAGE_ORIGIN@
+GST_PKG_CONFIG_PATH = @GST_PKG_CONFIG_PATH@
+GST_PKG_DEPS = @GST_PKG_DEPS@
+GST_PLUGIN_LDFLAGS = @GST_PLUGIN_LDFLAGS@
+GST_PLUGIN_SCANNER_INSTALLED = @GST_PLUGIN_SCANNER_INSTALLED@
+GST_PRINTF_EXTENSION_POINTER_FORMAT_DEFINE = @GST_PRINTF_EXTENSION_POINTER_FORMAT_DEFINE@
+GST_PRINTF_EXTENSION_SEGMENT_FORMAT_DEFINE = @GST_PRINTF_EXTENSION_SEGMENT_FORMAT_DEFINE@
+GST_REGISTRY_DOC_TYPES = @GST_REGISTRY_DOC_TYPES@
+GST_REVISION = @GST_REVISION@
+GST_USING_PRINTF_EXTENSION_DEFINE = @GST_USING_PRINTF_EXTENSION_DEFINE@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+HAVE_DOCBOOK2HTML = @HAVE_DOCBOOK2HTML@
+HAVE_DOCBOOK2PS = @HAVE_DOCBOOK2PS@
+HAVE_DVIPS = @HAVE_DVIPS@
+HAVE_EPSTOPDF = @HAVE_EPSTOPDF@
+HAVE_FIG2DEV = @HAVE_FIG2DEV@
+HAVE_GMP = @HAVE_GMP@
+HAVE_GSL = @HAVE_GSL@
+HAVE_JADETEX = @HAVE_JADETEX@
+HAVE_PNGTOPNM = @HAVE_PNGTOPNM@
+HAVE_PNMTOPS = @HAVE_PNMTOPS@
+HAVE_PS2PDF = @HAVE_PS2PDF@
+HAVE_XMLLINT = @HAVE_XMLLINT@
+HOST_CPU = @HOST_CPU@
+HTML_DIR = @HTML_DIR@
+INET_ATON_LIBS = @INET_ATON_LIBS@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTLLIBS = @INTLLIBS@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+INTROSPECTION_CFLAGS = @INTROSPECTION_CFLAGS@
+INTROSPECTION_COMPILER = @INTROSPECTION_COMPILER@
+INTROSPECTION_GENERATE = @INTROSPECTION_GENERATE@
+INTROSPECTION_GIRDIR = @INTROSPECTION_GIRDIR@
+INTROSPECTION_LIBS = @INTROSPECTION_LIBS@
+INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@
+INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@
+INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBDIR = @LIBDIR@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBM = @LIBM@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LOCALEDIR = @LOCALEDIR@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MSGFMT = @MSGFMT@
+MSGFMT_015 = @MSGFMT_015@
+MSGMERGE = @MSGMERGE@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PACKAGE_VERSION_MAJOR = @PACKAGE_VERSION_MAJOR@
+PACKAGE_VERSION_MICRO = @PACKAGE_VERSION_MICRO@
+PACKAGE_VERSION_MINOR = @PACKAGE_VERSION_MINOR@
+PACKAGE_VERSION_NANO = @PACKAGE_VERSION_NANO@
+PACKAGE_VERSION_RELEASE = @PACKAGE_VERSION_RELEASE@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL_PATH = @PERL_PATH@
+PKG_CONFIG = @PKG_CONFIG@
+PLUGINDIR = @PLUGINDIR@
+POSUB = @POSUB@
+PROFILE_CFLAGS = @PROFILE_CFLAGS@
+PYTHON = @PYTHON@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+USE_NLS = @USE_NLS@
+VALGRIND_CFLAGS = @VALGRIND_CFLAGS@
+VALGRIND_LIBS = @VALGRIND_LIBS@
+VALGRIND_PATH = @VALGRIND_PATH@
+VERSION = @VERSION@
+WARNING_CFLAGS = @WARNING_CFLAGS@
+WIN32_LIBS = @WIN32_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_015 = @XGETTEXT_015@
+XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@
+XML_CATALOG = @XML_CATALOG@
+XSLTPROC = @XSLTPROC@
+XSLTPROC_FLAGS = @XSLTPROC_FLAGS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+plugindir = @plugindir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+lib_LTLIBRARIES = libgstreamer-@GST_MAJORMINOR@.la
+@GST_DISABLE_REGISTRY_FALSE@GST_REGISTRY_SRC = gstregistrybinary.c
+@GST_DISABLE_REGISTRY_TRUE@GST_REGISTRY_SRC =
+@GST_DISABLE_PARSE_FALSE@SUBDIRS_PARSE = parse
+@GST_DISABLE_PARSE_TRUE@SUBDIRS_PARSE =
+@GST_DISABLE_PARSE_FALSE@GST_PARSE_LA = parse/libgstparse.la
+@GST_DISABLE_PARSE_TRUE@GST_PARSE_LA =
+@GST_DISABLE_TRACE_FALSE@GST_TRACE_SRC = gsttrace.c
+@GST_DISABLE_TRACE_TRUE@GST_TRACE_SRC =
+@GST_DISABLE_PLUGIN_FALSE@GST_PLUGIN_SRC = gstplugin.c
+@GST_DISABLE_PLUGIN_TRUE@GST_PLUGIN_SRC =
+SUBDIRS = $(SUBDIRS_PARSE)
+DIST_SUBDIRS = parse
+
+# make variables for all generated source and header files to make the
+# distinction clear
+built_header_configure = gstconfig.h gstversion.h
+built_header_make = gstenumtypes.h gstmarshal.h
+built_source_make = gstenumtypes.c gstmarshal.c
+EXTRA_libgstreamer_@GST_MAJORMINOR@_la_SOURCES = \
+ gstmarshal.list gsttrace.c \
+ gstregistrybinary.c
+
+
+# temporarily not used
+# glib-compat.c
+libgstreamer_@GST_MAJORMINOR@_la_SOURCES = \
+ gst.c \
+ gstobject.c \
+ gstbin.c \
+ gstbuffer.c \
+ gstbufferlist.c \
+ gstbufferpool.c \
+ gstbus.c \
+ gstcaps.c \
+ gstchildproxy.c \
+ gstclock.c \
+ gstdatetime.c \
+ gstdebugutils.c \
+ gstelement.c \
+ gstelementfactory.c \
+ gsterror.c \
+ gstevent.c \
+ gstfilter.c \
+ gstformat.c \
+ gstghostpad.c \
+ gstindex.c \
+ gstindexfactory.c \
+ gstinfo.c \
+ gstiterator.c \
+ gstatomicqueue.c \
+ gstmessage.c \
+ gstmeta.c \
+ gstmemory.c \
+ gstminiobject.c \
+ gstpad.c \
+ gstpadtemplate.c \
+ gstparamspecs.c \
+ gstpipeline.c \
+ gstplugin.c \
+ gstpluginfeature.c \
+ gstpluginloader.c \
+ gstpoll.c \
+ gstpreset.c \
+ gstquark.c \
+ gstquery.c \
+ gstregistry.c \
+ gstregistrychunks.c \
+ gstsegment.c \
+ gststructure.c \
+ gstsystemclock.c \
+ gsttaglist.c \
+ gsttagsetter.c \
+ gsttask.c \
+ gsttaskpool.c \
+ $(GST_TRACE_SRC) \
+ gsttypefind.c \
+ gsttypefindfactory.c \
+ gsturi.c \
+ gstutils.c \
+ gstvalue.c \
+ gstparse.c \
+ $(GST_REGISTRY_SRC)
+
+
+# do not put files in the distribution that are generated
+nodist_libgstreamer_@GST_MAJORMINOR@_la_SOURCES = $(built_source_make)
+
+# BUILT_SOURCES are built on make all/check/install before all other targets
+BUILT_SOURCES = \
+ $(built_header_configure) \
+ $(built_header_make) \
+ $(built_source_make)
+
+# CLEANFILES is for files generated by make
+CLEANFILES = $(built_header_make) $(built_source_make) \
+ $(as_dll_cleanfiles) *.gcno *.gcda *.gcov *.gcov.out \
+ $(am__append_1)
+# DISTCLEANFILES is for files generated by configure
+DISTCLEANFILES = $(built_header_configure)
+libgstreamer_@GST_MAJORMINOR@_la_CFLAGS = \
+ -D_GNU_SOURCE \
+ -DGST_EXPORTS \
+ -DG_LOG_DOMAIN=g_log_domain_gstreamer \
+ -DGST_MAJORMINOR=\""$(GST_MAJORMINOR)"\" \
+ -DGST_DISABLE_DEPRECATED \
+ $(VALGRIND_CFLAGS) \
+ $(GST_ALL_CFLAGS)
+
+libgstreamer_@GST_MAJORMINOR@_la_LIBADD = \
+ $(GST_PARSE_LA) \
+ $(GST_ALL_LIBS) \
+ $(WIN32_LIBS) \
+ $(LIBM)
+
+libgstreamer_@GST_MAJORMINOR@_la_LDFLAGS = \
+ $(GST_LIB_LDFLAGS) $(GST_ALL_LDFLAGS) $(GST_LT_LDFLAGS)
+
+libgstreamer_@GST_MAJORMINOR@includedir = $(includedir)/gstreamer-@GST_MAJORMINOR@/gst
+gst_headers = \
+ gst.h \
+ glib-compat.h \
+ gstobject.h \
+ gstbin.h \
+ gstbuffer.h \
+ gstbufferlist.h \
+ gstbufferpool.h \
+ gstbus.h \
+ gstcaps.h \
+ gstchildproxy.h \
+ gstclock.h \
+ gstcompat.h \
+ gstdatetime.h \
+ gstdebugutils.h \
+ gstelement.h \
+ gstelementmetadata.h \
+ gstelementfactory.h \
+ gsterror.h \
+ gstevent.h \
+ gstfilter.h \
+ gstformat.h \
+ gstghostpad.h \
+ gstindex.h \
+ gstindexfactory.h \
+ gstinfo.h \
+ gstiterator.h \
+ gstatomicqueue.h \
+ gstmacros.h \
+ gstmessage.h \
+ gstmeta.h \
+ gstmemory.h \
+ gstminiobject.h \
+ gstpad.h \
+ gstpadtemplate.h \
+ gstparamspecs.h \
+ gstpipeline.h \
+ gstplugin.h \
+ gstpluginfeature.h \
+ gstpoll.h \
+ gstpreset.h \
+ gstquery.h \
+ gstsegment.h \
+ gststructure.h \
+ gstsystemclock.h \
+ gsttaglist.h \
+ gsttagsetter.h \
+ gsttask.h \
+ gsttaskpool.h \
+ gsttrace.h \
+ gsttypefind.h \
+ gsttypefindfactory.h \
+ gsturi.h \
+ gstutils.h \
+ gstvalue.h \
+ gstregistry.h \
+ gstparse.h
+
+libgstreamer_@GST_MAJORMINOR@include_HEADERS = $(gst_headers) math-compat.h
+nodist_libgstreamer_@GST_MAJORMINOR@include_HEADERS = \
+ $(built_header_configure) $(built_header_make)
+
+noinst_HEADERS = \
+ gettext.h \
+ glib-compat-private.h \
+ gst-i18n-lib.h \
+ gst-i18n-app.h \
+ gstelementmetadata.h \
+ gstpluginloader.h \
+ gstquark.h \
+ gstregistrybinary.h \
+ gstregistrychunks.h \
+ gst_private.h
+
+@HAVE_INTROSPECTION_TRUE@BUILT_GIRSOURCES = Gst-@GST_MAJORMINOR@.gir
+@HAVE_INTROSPECTION_TRUE@gir_headers = $(patsubst %,$(srcdir)/%, $(libgstreamer_@GST_MAJORMINOR@include_HEADERS))
+@HAVE_INTROSPECTION_TRUE@gir_sources = $(patsubst %,$(srcdir)/%, $(libgstreamer_@GST_MAJORMINOR@_la_SOURCES))
+
+# INTROSPECTION_GIRDIR/INTROSPECTION_TYPELIBDIR aren't the right place to
+# install anything - we need to install inside our prefix.
+@HAVE_INTROSPECTION_TRUE@girdir = $(datadir)/gir-1.0
+@HAVE_INTROSPECTION_TRUE@gir_DATA = $(BUILT_GIRSOURCES)
+@HAVE_INTROSPECTION_TRUE@typelibsdir = $(libdir)/girepository-1.0/
+@HAVE_INTROSPECTION_TRUE@typelibs_DATA = $(BUILT_GIRSOURCES:.gir=.typelib)
+all: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) all-recursive
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu gst/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu gst/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+gstconfig.h: $(top_builddir)/config.status $(srcdir)/gstconfig.h.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+gstversion.h: $(top_builddir)/config.status $(srcdir)/gstversion.h.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)"
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ list2=; for p in $$list; do \
+ if test -f $$p; then \
+ list2="$$list2 $$p"; \
+ else :; fi; \
+ done; \
+ test -z "$$list2" || { \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
+ }
+
+uninstall-libLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \
+ done
+
+clean-libLTLIBRARIES:
+ -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+ @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+ test "$$dir" != "$$p" || dir=.; \
+ echo "rm -f \"$${dir}/so_locations\""; \
+ rm -f "$${dir}/so_locations"; \
+ done
+libgstreamer-@GST_MAJORMINOR@.la: $(libgstreamer_@GST_MAJORMINOR@_la_OBJECTS) $(libgstreamer_@GST_MAJORMINOR@_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libgstreamer_@GST_MAJORMINOR@_la_LINK) -rpath $(libdir) $(libgstreamer_@GST_MAJORMINOR@_la_OBJECTS) $(libgstreamer_@GST_MAJORMINOR@_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gst.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstatomicqueue.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstbin.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstbuffer.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstbufferlist.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstbufferpool.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstbus.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstcaps.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstchildproxy.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstclock.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstdatetime.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstdebugutils.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstelement.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstelementfactory.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstenumtypes.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gsterror.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstevent.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstfilter.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstformat.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstghostpad.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstindex.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstindexfactory.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstinfo.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstiterator.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstmarshal.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstmemory.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstmessage.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstmeta.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstminiobject.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstobject.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstpad.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstpadtemplate.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstparamspecs.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstparse.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstpipeline.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstplugin.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstpluginfeature.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstpluginloader.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstpoll.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstpreset.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstquark.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstquery.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstregistry.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstregistrybinary.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstregistrychunks.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstsegment.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gststructure.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstsystemclock.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gsttaglist.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gsttagsetter.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gsttask.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gsttaskpool.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gsttrace.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gsttypefind.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gsttypefindfactory.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gsturi.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstutils.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstvalue.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+
+libgstreamer_@GST_MAJORMINOR@_la-gst.lo: gst.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gst.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gst.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gst.lo `test -f 'gst.c' || echo '$(srcdir)/'`gst.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gst.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gst.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gst.c' object='libgstreamer_@GST_MAJORMINOR@_la-gst.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gst.lo `test -f 'gst.c' || echo '$(srcdir)/'`gst.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gstobject.lo: gstobject.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gstobject.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstobject.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gstobject.lo `test -f 'gstobject.c' || echo '$(srcdir)/'`gstobject.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstobject.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstobject.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstobject.c' object='libgstreamer_@GST_MAJORMINOR@_la-gstobject.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gstobject.lo `test -f 'gstobject.c' || echo '$(srcdir)/'`gstobject.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gstbin.lo: gstbin.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gstbin.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstbin.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gstbin.lo `test -f 'gstbin.c' || echo '$(srcdir)/'`gstbin.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstbin.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstbin.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstbin.c' object='libgstreamer_@GST_MAJORMINOR@_la-gstbin.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gstbin.lo `test -f 'gstbin.c' || echo '$(srcdir)/'`gstbin.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gstbuffer.lo: gstbuffer.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gstbuffer.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstbuffer.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gstbuffer.lo `test -f 'gstbuffer.c' || echo '$(srcdir)/'`gstbuffer.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstbuffer.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstbuffer.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstbuffer.c' object='libgstreamer_@GST_MAJORMINOR@_la-gstbuffer.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gstbuffer.lo `test -f 'gstbuffer.c' || echo '$(srcdir)/'`gstbuffer.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gstbufferlist.lo: gstbufferlist.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gstbufferlist.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstbufferlist.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gstbufferlist.lo `test -f 'gstbufferlist.c' || echo '$(srcdir)/'`gstbufferlist.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstbufferlist.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstbufferlist.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstbufferlist.c' object='libgstreamer_@GST_MAJORMINOR@_la-gstbufferlist.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gstbufferlist.lo `test -f 'gstbufferlist.c' || echo '$(srcdir)/'`gstbufferlist.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gstbufferpool.lo: gstbufferpool.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gstbufferpool.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstbufferpool.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gstbufferpool.lo `test -f 'gstbufferpool.c' || echo '$(srcdir)/'`gstbufferpool.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstbufferpool.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstbufferpool.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstbufferpool.c' object='libgstreamer_@GST_MAJORMINOR@_la-gstbufferpool.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gstbufferpool.lo `test -f 'gstbufferpool.c' || echo '$(srcdir)/'`gstbufferpool.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gstbus.lo: gstbus.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gstbus.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstbus.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gstbus.lo `test -f 'gstbus.c' || echo '$(srcdir)/'`gstbus.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstbus.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstbus.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstbus.c' object='libgstreamer_@GST_MAJORMINOR@_la-gstbus.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gstbus.lo `test -f 'gstbus.c' || echo '$(srcdir)/'`gstbus.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gstcaps.lo: gstcaps.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gstcaps.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstcaps.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gstcaps.lo `test -f 'gstcaps.c' || echo '$(srcdir)/'`gstcaps.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstcaps.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstcaps.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstcaps.c' object='libgstreamer_@GST_MAJORMINOR@_la-gstcaps.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gstcaps.lo `test -f 'gstcaps.c' || echo '$(srcdir)/'`gstcaps.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gstchildproxy.lo: gstchildproxy.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gstchildproxy.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstchildproxy.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gstchildproxy.lo `test -f 'gstchildproxy.c' || echo '$(srcdir)/'`gstchildproxy.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstchildproxy.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstchildproxy.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstchildproxy.c' object='libgstreamer_@GST_MAJORMINOR@_la-gstchildproxy.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gstchildproxy.lo `test -f 'gstchildproxy.c' || echo '$(srcdir)/'`gstchildproxy.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gstclock.lo: gstclock.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gstclock.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstclock.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gstclock.lo `test -f 'gstclock.c' || echo '$(srcdir)/'`gstclock.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstclock.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstclock.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstclock.c' object='libgstreamer_@GST_MAJORMINOR@_la-gstclock.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gstclock.lo `test -f 'gstclock.c' || echo '$(srcdir)/'`gstclock.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gstdatetime.lo: gstdatetime.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gstdatetime.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstdatetime.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gstdatetime.lo `test -f 'gstdatetime.c' || echo '$(srcdir)/'`gstdatetime.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstdatetime.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstdatetime.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstdatetime.c' object='libgstreamer_@GST_MAJORMINOR@_la-gstdatetime.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gstdatetime.lo `test -f 'gstdatetime.c' || echo '$(srcdir)/'`gstdatetime.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gstdebugutils.lo: gstdebugutils.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gstdebugutils.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstdebugutils.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gstdebugutils.lo `test -f 'gstdebugutils.c' || echo '$(srcdir)/'`gstdebugutils.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstdebugutils.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstdebugutils.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstdebugutils.c' object='libgstreamer_@GST_MAJORMINOR@_la-gstdebugutils.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gstdebugutils.lo `test -f 'gstdebugutils.c' || echo '$(srcdir)/'`gstdebugutils.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gstelement.lo: gstelement.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gstelement.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstelement.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gstelement.lo `test -f 'gstelement.c' || echo '$(srcdir)/'`gstelement.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstelement.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstelement.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstelement.c' object='libgstreamer_@GST_MAJORMINOR@_la-gstelement.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gstelement.lo `test -f 'gstelement.c' || echo '$(srcdir)/'`gstelement.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gstelementfactory.lo: gstelementfactory.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gstelementfactory.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstelementfactory.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gstelementfactory.lo `test -f 'gstelementfactory.c' || echo '$(srcdir)/'`gstelementfactory.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstelementfactory.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstelementfactory.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstelementfactory.c' object='libgstreamer_@GST_MAJORMINOR@_la-gstelementfactory.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gstelementfactory.lo `test -f 'gstelementfactory.c' || echo '$(srcdir)/'`gstelementfactory.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gsterror.lo: gsterror.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gsterror.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gsterror.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gsterror.lo `test -f 'gsterror.c' || echo '$(srcdir)/'`gsterror.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gsterror.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gsterror.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gsterror.c' object='libgstreamer_@GST_MAJORMINOR@_la-gsterror.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gsterror.lo `test -f 'gsterror.c' || echo '$(srcdir)/'`gsterror.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gstevent.lo: gstevent.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gstevent.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstevent.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gstevent.lo `test -f 'gstevent.c' || echo '$(srcdir)/'`gstevent.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstevent.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstevent.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstevent.c' object='libgstreamer_@GST_MAJORMINOR@_la-gstevent.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gstevent.lo `test -f 'gstevent.c' || echo '$(srcdir)/'`gstevent.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gstfilter.lo: gstfilter.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gstfilter.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstfilter.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gstfilter.lo `test -f 'gstfilter.c' || echo '$(srcdir)/'`gstfilter.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstfilter.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstfilter.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstfilter.c' object='libgstreamer_@GST_MAJORMINOR@_la-gstfilter.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gstfilter.lo `test -f 'gstfilter.c' || echo '$(srcdir)/'`gstfilter.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gstformat.lo: gstformat.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gstformat.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstformat.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gstformat.lo `test -f 'gstformat.c' || echo '$(srcdir)/'`gstformat.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstformat.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstformat.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstformat.c' object='libgstreamer_@GST_MAJORMINOR@_la-gstformat.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gstformat.lo `test -f 'gstformat.c' || echo '$(srcdir)/'`gstformat.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gstghostpad.lo: gstghostpad.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gstghostpad.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstghostpad.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gstghostpad.lo `test -f 'gstghostpad.c' || echo '$(srcdir)/'`gstghostpad.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstghostpad.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstghostpad.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstghostpad.c' object='libgstreamer_@GST_MAJORMINOR@_la-gstghostpad.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gstghostpad.lo `test -f 'gstghostpad.c' || echo '$(srcdir)/'`gstghostpad.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gstindex.lo: gstindex.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gstindex.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstindex.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gstindex.lo `test -f 'gstindex.c' || echo '$(srcdir)/'`gstindex.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstindex.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstindex.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstindex.c' object='libgstreamer_@GST_MAJORMINOR@_la-gstindex.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gstindex.lo `test -f 'gstindex.c' || echo '$(srcdir)/'`gstindex.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gstindexfactory.lo: gstindexfactory.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gstindexfactory.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstindexfactory.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gstindexfactory.lo `test -f 'gstindexfactory.c' || echo '$(srcdir)/'`gstindexfactory.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstindexfactory.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstindexfactory.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstindexfactory.c' object='libgstreamer_@GST_MAJORMINOR@_la-gstindexfactory.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gstindexfactory.lo `test -f 'gstindexfactory.c' || echo '$(srcdir)/'`gstindexfactory.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gstinfo.lo: gstinfo.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gstinfo.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstinfo.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gstinfo.lo `test -f 'gstinfo.c' || echo '$(srcdir)/'`gstinfo.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstinfo.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstinfo.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstinfo.c' object='libgstreamer_@GST_MAJORMINOR@_la-gstinfo.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gstinfo.lo `test -f 'gstinfo.c' || echo '$(srcdir)/'`gstinfo.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gstiterator.lo: gstiterator.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gstiterator.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstiterator.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gstiterator.lo `test -f 'gstiterator.c' || echo '$(srcdir)/'`gstiterator.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstiterator.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstiterator.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstiterator.c' object='libgstreamer_@GST_MAJORMINOR@_la-gstiterator.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gstiterator.lo `test -f 'gstiterator.c' || echo '$(srcdir)/'`gstiterator.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gstatomicqueue.lo: gstatomicqueue.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gstatomicqueue.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstatomicqueue.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gstatomicqueue.lo `test -f 'gstatomicqueue.c' || echo '$(srcdir)/'`gstatomicqueue.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstatomicqueue.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstatomicqueue.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstatomicqueue.c' object='libgstreamer_@GST_MAJORMINOR@_la-gstatomicqueue.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gstatomicqueue.lo `test -f 'gstatomicqueue.c' || echo '$(srcdir)/'`gstatomicqueue.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gstmessage.lo: gstmessage.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gstmessage.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstmessage.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gstmessage.lo `test -f 'gstmessage.c' || echo '$(srcdir)/'`gstmessage.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstmessage.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstmessage.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstmessage.c' object='libgstreamer_@GST_MAJORMINOR@_la-gstmessage.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gstmessage.lo `test -f 'gstmessage.c' || echo '$(srcdir)/'`gstmessage.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gstmeta.lo: gstmeta.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gstmeta.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstmeta.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gstmeta.lo `test -f 'gstmeta.c' || echo '$(srcdir)/'`gstmeta.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstmeta.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstmeta.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstmeta.c' object='libgstreamer_@GST_MAJORMINOR@_la-gstmeta.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gstmeta.lo `test -f 'gstmeta.c' || echo '$(srcdir)/'`gstmeta.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gstmemory.lo: gstmemory.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gstmemory.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstmemory.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gstmemory.lo `test -f 'gstmemory.c' || echo '$(srcdir)/'`gstmemory.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstmemory.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstmemory.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstmemory.c' object='libgstreamer_@GST_MAJORMINOR@_la-gstmemory.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gstmemory.lo `test -f 'gstmemory.c' || echo '$(srcdir)/'`gstmemory.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gstminiobject.lo: gstminiobject.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gstminiobject.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstminiobject.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gstminiobject.lo `test -f 'gstminiobject.c' || echo '$(srcdir)/'`gstminiobject.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstminiobject.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstminiobject.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstminiobject.c' object='libgstreamer_@GST_MAJORMINOR@_la-gstminiobject.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gstminiobject.lo `test -f 'gstminiobject.c' || echo '$(srcdir)/'`gstminiobject.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gstpad.lo: gstpad.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gstpad.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstpad.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gstpad.lo `test -f 'gstpad.c' || echo '$(srcdir)/'`gstpad.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstpad.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstpad.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstpad.c' object='libgstreamer_@GST_MAJORMINOR@_la-gstpad.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gstpad.lo `test -f 'gstpad.c' || echo '$(srcdir)/'`gstpad.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gstpadtemplate.lo: gstpadtemplate.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gstpadtemplate.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstpadtemplate.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gstpadtemplate.lo `test -f 'gstpadtemplate.c' || echo '$(srcdir)/'`gstpadtemplate.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstpadtemplate.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstpadtemplate.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstpadtemplate.c' object='libgstreamer_@GST_MAJORMINOR@_la-gstpadtemplate.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gstpadtemplate.lo `test -f 'gstpadtemplate.c' || echo '$(srcdir)/'`gstpadtemplate.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gstparamspecs.lo: gstparamspecs.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gstparamspecs.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstparamspecs.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gstparamspecs.lo `test -f 'gstparamspecs.c' || echo '$(srcdir)/'`gstparamspecs.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstparamspecs.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstparamspecs.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstparamspecs.c' object='libgstreamer_@GST_MAJORMINOR@_la-gstparamspecs.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gstparamspecs.lo `test -f 'gstparamspecs.c' || echo '$(srcdir)/'`gstparamspecs.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gstpipeline.lo: gstpipeline.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gstpipeline.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstpipeline.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gstpipeline.lo `test -f 'gstpipeline.c' || echo '$(srcdir)/'`gstpipeline.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstpipeline.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstpipeline.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstpipeline.c' object='libgstreamer_@GST_MAJORMINOR@_la-gstpipeline.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gstpipeline.lo `test -f 'gstpipeline.c' || echo '$(srcdir)/'`gstpipeline.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gstplugin.lo: gstplugin.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gstplugin.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstplugin.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gstplugin.lo `test -f 'gstplugin.c' || echo '$(srcdir)/'`gstplugin.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstplugin.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstplugin.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstplugin.c' object='libgstreamer_@GST_MAJORMINOR@_la-gstplugin.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gstplugin.lo `test -f 'gstplugin.c' || echo '$(srcdir)/'`gstplugin.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gstpluginfeature.lo: gstpluginfeature.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gstpluginfeature.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstpluginfeature.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gstpluginfeature.lo `test -f 'gstpluginfeature.c' || echo '$(srcdir)/'`gstpluginfeature.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstpluginfeature.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstpluginfeature.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstpluginfeature.c' object='libgstreamer_@GST_MAJORMINOR@_la-gstpluginfeature.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gstpluginfeature.lo `test -f 'gstpluginfeature.c' || echo '$(srcdir)/'`gstpluginfeature.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gstpluginloader.lo: gstpluginloader.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gstpluginloader.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstpluginloader.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gstpluginloader.lo `test -f 'gstpluginloader.c' || echo '$(srcdir)/'`gstpluginloader.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstpluginloader.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstpluginloader.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstpluginloader.c' object='libgstreamer_@GST_MAJORMINOR@_la-gstpluginloader.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gstpluginloader.lo `test -f 'gstpluginloader.c' || echo '$(srcdir)/'`gstpluginloader.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gstpoll.lo: gstpoll.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gstpoll.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstpoll.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gstpoll.lo `test -f 'gstpoll.c' || echo '$(srcdir)/'`gstpoll.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstpoll.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstpoll.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstpoll.c' object='libgstreamer_@GST_MAJORMINOR@_la-gstpoll.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gstpoll.lo `test -f 'gstpoll.c' || echo '$(srcdir)/'`gstpoll.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gstpreset.lo: gstpreset.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gstpreset.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstpreset.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gstpreset.lo `test -f 'gstpreset.c' || echo '$(srcdir)/'`gstpreset.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstpreset.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstpreset.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstpreset.c' object='libgstreamer_@GST_MAJORMINOR@_la-gstpreset.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gstpreset.lo `test -f 'gstpreset.c' || echo '$(srcdir)/'`gstpreset.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gstquark.lo: gstquark.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gstquark.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstquark.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gstquark.lo `test -f 'gstquark.c' || echo '$(srcdir)/'`gstquark.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstquark.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstquark.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstquark.c' object='libgstreamer_@GST_MAJORMINOR@_la-gstquark.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gstquark.lo `test -f 'gstquark.c' || echo '$(srcdir)/'`gstquark.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gstquery.lo: gstquery.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gstquery.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstquery.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gstquery.lo `test -f 'gstquery.c' || echo '$(srcdir)/'`gstquery.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstquery.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstquery.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstquery.c' object='libgstreamer_@GST_MAJORMINOR@_la-gstquery.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gstquery.lo `test -f 'gstquery.c' || echo '$(srcdir)/'`gstquery.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gstregistry.lo: gstregistry.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gstregistry.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstregistry.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gstregistry.lo `test -f 'gstregistry.c' || echo '$(srcdir)/'`gstregistry.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstregistry.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstregistry.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstregistry.c' object='libgstreamer_@GST_MAJORMINOR@_la-gstregistry.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gstregistry.lo `test -f 'gstregistry.c' || echo '$(srcdir)/'`gstregistry.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gstregistrychunks.lo: gstregistrychunks.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gstregistrychunks.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstregistrychunks.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gstregistrychunks.lo `test -f 'gstregistrychunks.c' || echo '$(srcdir)/'`gstregistrychunks.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstregistrychunks.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstregistrychunks.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstregistrychunks.c' object='libgstreamer_@GST_MAJORMINOR@_la-gstregistrychunks.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gstregistrychunks.lo `test -f 'gstregistrychunks.c' || echo '$(srcdir)/'`gstregistrychunks.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gstsegment.lo: gstsegment.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gstsegment.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstsegment.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gstsegment.lo `test -f 'gstsegment.c' || echo '$(srcdir)/'`gstsegment.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstsegment.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstsegment.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstsegment.c' object='libgstreamer_@GST_MAJORMINOR@_la-gstsegment.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gstsegment.lo `test -f 'gstsegment.c' || echo '$(srcdir)/'`gstsegment.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gststructure.lo: gststructure.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gststructure.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gststructure.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gststructure.lo `test -f 'gststructure.c' || echo '$(srcdir)/'`gststructure.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gststructure.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gststructure.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gststructure.c' object='libgstreamer_@GST_MAJORMINOR@_la-gststructure.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gststructure.lo `test -f 'gststructure.c' || echo '$(srcdir)/'`gststructure.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gstsystemclock.lo: gstsystemclock.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gstsystemclock.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstsystemclock.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gstsystemclock.lo `test -f 'gstsystemclock.c' || echo '$(srcdir)/'`gstsystemclock.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstsystemclock.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstsystemclock.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstsystemclock.c' object='libgstreamer_@GST_MAJORMINOR@_la-gstsystemclock.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gstsystemclock.lo `test -f 'gstsystemclock.c' || echo '$(srcdir)/'`gstsystemclock.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gsttaglist.lo: gsttaglist.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gsttaglist.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gsttaglist.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gsttaglist.lo `test -f 'gsttaglist.c' || echo '$(srcdir)/'`gsttaglist.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gsttaglist.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gsttaglist.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gsttaglist.c' object='libgstreamer_@GST_MAJORMINOR@_la-gsttaglist.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gsttaglist.lo `test -f 'gsttaglist.c' || echo '$(srcdir)/'`gsttaglist.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gsttagsetter.lo: gsttagsetter.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gsttagsetter.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gsttagsetter.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gsttagsetter.lo `test -f 'gsttagsetter.c' || echo '$(srcdir)/'`gsttagsetter.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gsttagsetter.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gsttagsetter.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gsttagsetter.c' object='libgstreamer_@GST_MAJORMINOR@_la-gsttagsetter.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gsttagsetter.lo `test -f 'gsttagsetter.c' || echo '$(srcdir)/'`gsttagsetter.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gsttask.lo: gsttask.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gsttask.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gsttask.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gsttask.lo `test -f 'gsttask.c' || echo '$(srcdir)/'`gsttask.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gsttask.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gsttask.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gsttask.c' object='libgstreamer_@GST_MAJORMINOR@_la-gsttask.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gsttask.lo `test -f 'gsttask.c' || echo '$(srcdir)/'`gsttask.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gsttaskpool.lo: gsttaskpool.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gsttaskpool.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gsttaskpool.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gsttaskpool.lo `test -f 'gsttaskpool.c' || echo '$(srcdir)/'`gsttaskpool.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gsttaskpool.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gsttaskpool.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gsttaskpool.c' object='libgstreamer_@GST_MAJORMINOR@_la-gsttaskpool.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gsttaskpool.lo `test -f 'gsttaskpool.c' || echo '$(srcdir)/'`gsttaskpool.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gsttrace.lo: gsttrace.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gsttrace.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gsttrace.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gsttrace.lo `test -f 'gsttrace.c' || echo '$(srcdir)/'`gsttrace.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gsttrace.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gsttrace.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gsttrace.c' object='libgstreamer_@GST_MAJORMINOR@_la-gsttrace.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gsttrace.lo `test -f 'gsttrace.c' || echo '$(srcdir)/'`gsttrace.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gsttypefind.lo: gsttypefind.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gsttypefind.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gsttypefind.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gsttypefind.lo `test -f 'gsttypefind.c' || echo '$(srcdir)/'`gsttypefind.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gsttypefind.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gsttypefind.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gsttypefind.c' object='libgstreamer_@GST_MAJORMINOR@_la-gsttypefind.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gsttypefind.lo `test -f 'gsttypefind.c' || echo '$(srcdir)/'`gsttypefind.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gsttypefindfactory.lo: gsttypefindfactory.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gsttypefindfactory.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gsttypefindfactory.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gsttypefindfactory.lo `test -f 'gsttypefindfactory.c' || echo '$(srcdir)/'`gsttypefindfactory.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gsttypefindfactory.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gsttypefindfactory.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gsttypefindfactory.c' object='libgstreamer_@GST_MAJORMINOR@_la-gsttypefindfactory.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gsttypefindfactory.lo `test -f 'gsttypefindfactory.c' || echo '$(srcdir)/'`gsttypefindfactory.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gsturi.lo: gsturi.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gsturi.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gsturi.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gsturi.lo `test -f 'gsturi.c' || echo '$(srcdir)/'`gsturi.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gsturi.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gsturi.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gsturi.c' object='libgstreamer_@GST_MAJORMINOR@_la-gsturi.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gsturi.lo `test -f 'gsturi.c' || echo '$(srcdir)/'`gsturi.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gstutils.lo: gstutils.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gstutils.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstutils.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gstutils.lo `test -f 'gstutils.c' || echo '$(srcdir)/'`gstutils.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstutils.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstutils.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstutils.c' object='libgstreamer_@GST_MAJORMINOR@_la-gstutils.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gstutils.lo `test -f 'gstutils.c' || echo '$(srcdir)/'`gstutils.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gstvalue.lo: gstvalue.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gstvalue.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstvalue.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gstvalue.lo `test -f 'gstvalue.c' || echo '$(srcdir)/'`gstvalue.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstvalue.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstvalue.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstvalue.c' object='libgstreamer_@GST_MAJORMINOR@_la-gstvalue.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gstvalue.lo `test -f 'gstvalue.c' || echo '$(srcdir)/'`gstvalue.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gstparse.lo: gstparse.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gstparse.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstparse.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gstparse.lo `test -f 'gstparse.c' || echo '$(srcdir)/'`gstparse.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstparse.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstparse.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstparse.c' object='libgstreamer_@GST_MAJORMINOR@_la-gstparse.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gstparse.lo `test -f 'gstparse.c' || echo '$(srcdir)/'`gstparse.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gstregistrybinary.lo: gstregistrybinary.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gstregistrybinary.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstregistrybinary.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gstregistrybinary.lo `test -f 'gstregistrybinary.c' || echo '$(srcdir)/'`gstregistrybinary.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstregistrybinary.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstregistrybinary.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstregistrybinary.c' object='libgstreamer_@GST_MAJORMINOR@_la-gstregistrybinary.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gstregistrybinary.lo `test -f 'gstregistrybinary.c' || echo '$(srcdir)/'`gstregistrybinary.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gstenumtypes.lo: gstenumtypes.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gstenumtypes.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstenumtypes.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gstenumtypes.lo `test -f 'gstenumtypes.c' || echo '$(srcdir)/'`gstenumtypes.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstenumtypes.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstenumtypes.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstenumtypes.c' object='libgstreamer_@GST_MAJORMINOR@_la-gstenumtypes.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gstenumtypes.lo `test -f 'gstenumtypes.c' || echo '$(srcdir)/'`gstenumtypes.c
+
+libgstreamer_@GST_MAJORMINOR@_la-gstmarshal.lo: gstmarshal.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT libgstreamer_@GST_MAJORMINOR@_la-gstmarshal.lo -MD -MP -MF $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstmarshal.Tpo -c -o libgstreamer_@GST_MAJORMINOR@_la-gstmarshal.lo `test -f 'gstmarshal.c' || echo '$(srcdir)/'`gstmarshal.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstmarshal.Tpo $(DEPDIR)/libgstreamer_@GST_MAJORMINOR@_la-gstmarshal.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstmarshal.c' object='libgstreamer_@GST_MAJORMINOR@_la-gstmarshal.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o libgstreamer_@GST_MAJORMINOR@_la-gstmarshal.lo `test -f 'gstmarshal.c' || echo '$(srcdir)/'`gstmarshal.c
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-girDATA: $(gir_DATA)
+ @$(NORMAL_INSTALL)
+ test -z "$(girdir)" || $(MKDIR_P) "$(DESTDIR)$(girdir)"
+ @list='$(gir_DATA)'; test -n "$(girdir)" || list=; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(girdir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(girdir)" || exit $$?; \
+ done
+
+uninstall-girDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(gir_DATA)'; test -n "$(girdir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ test -n "$$files" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(girdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(girdir)" && rm -f $$files
+install-typelibsDATA: $(typelibs_DATA)
+ @$(NORMAL_INSTALL)
+ test -z "$(typelibsdir)" || $(MKDIR_P) "$(DESTDIR)$(typelibsdir)"
+ @list='$(typelibs_DATA)'; test -n "$(typelibsdir)" || list=; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(typelibsdir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(typelibsdir)" || exit $$?; \
+ done
+
+uninstall-typelibsDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(typelibs_DATA)'; test -n "$(typelibsdir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ test -n "$$files" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(typelibsdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(typelibsdir)" && rm -f $$files
+install-libgstreamer_@GST_MAJORMINOR@includeHEADERS: $(libgstreamer_@GST_MAJORMINOR@include_HEADERS)
+ @$(NORMAL_INSTALL)
+ test -z "$(libgstreamer_@GST_MAJORMINOR@includedir)" || $(MKDIR_P) "$(DESTDIR)$(libgstreamer_@GST_MAJORMINOR@includedir)"
+ @list='$(libgstreamer_@GST_MAJORMINOR@include_HEADERS)'; test -n "$(libgstreamer_@GST_MAJORMINOR@includedir)" || list=; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(libgstreamer_@GST_MAJORMINOR@includedir)'"; \
+ $(INSTALL_HEADER) $$files "$(DESTDIR)$(libgstreamer_@GST_MAJORMINOR@includedir)" || exit $$?; \
+ done
+
+uninstall-libgstreamer_@GST_MAJORMINOR@includeHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(libgstreamer_@GST_MAJORMINOR@include_HEADERS)'; test -n "$(libgstreamer_@GST_MAJORMINOR@includedir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ test -n "$$files" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(libgstreamer_@GST_MAJORMINOR@includedir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(libgstreamer_@GST_MAJORMINOR@includedir)" && rm -f $$files
+install-nodist_libgstreamer_@GST_MAJORMINOR@includeHEADERS: $(nodist_libgstreamer_@GST_MAJORMINOR@include_HEADERS)
+ @$(NORMAL_INSTALL)
+ test -z "$(libgstreamer_@GST_MAJORMINOR@includedir)" || $(MKDIR_P) "$(DESTDIR)$(libgstreamer_@GST_MAJORMINOR@includedir)"
+ @list='$(nodist_libgstreamer_@GST_MAJORMINOR@include_HEADERS)'; test -n "$(libgstreamer_@GST_MAJORMINOR@includedir)" || list=; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(libgstreamer_@GST_MAJORMINOR@includedir)'"; \
+ $(INSTALL_HEADER) $$files "$(DESTDIR)$(libgstreamer_@GST_MAJORMINOR@includedir)" || exit $$?; \
+ done
+
+uninstall-nodist_libgstreamer_@GST_MAJORMINOR@includeHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(nodist_libgstreamer_@GST_MAJORMINOR@include_HEADERS)'; test -n "$(libgstreamer_@GST_MAJORMINOR@includedir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ test -n "$$files" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(libgstreamer_@GST_MAJORMINOR@includedir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(libgstreamer_@GST_MAJORMINOR@includedir)" && rm -f $$files
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run `make' without going through this Makefile.
+# To change the values of `make' variables: instead of editing Makefiles,
+# (1) if the variable is set in `config.status', edit `config.status'
+# (which will cause the Makefiles to be regenerated when you run `make');
+# (2) otherwise, pass the desired values on the `make' command line.
+$(RECURSIVE_TARGETS):
+ @fail= failcom='exit 1'; \
+ for f in x $$MAKEFLAGS; do \
+ case $$f in \
+ *=* | --[!k]*);; \
+ *k*) failcom='fail=yes';; \
+ esac; \
+ done; \
+ dot_seen=no; \
+ target=`echo $@ | sed s/-recursive//`; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ dot_seen=yes; \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done; \
+ if test "$$dot_seen" = "no"; then \
+ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+ fi; test -z "$$fail"
+
+$(RECURSIVE_CLEAN_TARGETS):
+ @fail= failcom='exit 1'; \
+ for f in x $$MAKEFLAGS; do \
+ case $$f in \
+ *=* | --[!k]*);; \
+ *k*) failcom='fail=yes';; \
+ esac; \
+ done; \
+ dot_seen=no; \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ rev=''; for subdir in $$list; do \
+ if test "$$subdir" = "."; then :; else \
+ rev="$$subdir $$rev"; \
+ fi; \
+ done; \
+ rev="$$rev ."; \
+ target=`echo $@ | sed s/-recursive//`; \
+ for subdir in $$rev; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done && test -z "$$fail"
+tags-recursive:
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \
+ done
+ctags-recursive:
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \
+ done
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ set x; \
+ here=`pwd`; \
+ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+ include_option=--etags-include; \
+ empty_fix=.; \
+ else \
+ include_option=--include; \
+ empty_fix=; \
+ fi; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test ! -f $$subdir/TAGS || \
+ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+ fi; \
+ done; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: CTAGS
+CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+ @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test -d "$(distdir)/$$subdir" \
+ || $(MKDIR_P) "$(distdir)/$$subdir" \
+ || exit 1; \
+ fi; \
+ done
+ @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+ $(am__relativize); \
+ new_distdir=$$reldir; \
+ dir1=$$subdir; dir2="$(top_distdir)"; \
+ $(am__relativize); \
+ new_top_distdir=$$reldir; \
+ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+ ($(am__cd) $$subdir && \
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$$new_top_distdir" \
+ distdir="$$new_distdir" \
+ am__remove_distdir=: \
+ am__skip_length_check=: \
+ am__skip_mode_fix=: \
+ distdir) \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) check-recursive
+all-am: Makefile $(LTLIBRARIES) $(DATA) $(HEADERS) all-local
+installdirs: installdirs-recursive
+installdirs-am:
+ for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(girdir)" "$(DESTDIR)$(typelibsdir)" "$(DESTDIR)$(libgstreamer_@GST_MAJORMINOR@includedir)" "$(DESTDIR)$(libgstreamer_@GST_MAJORMINOR@includedir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+ -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+ -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
+clean: clean-recursive
+
+clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am: install-girDATA \
+ install-libgstreamer_@GST_MAJORMINOR@includeHEADERS \
+ install-nodist_libgstreamer_@GST_MAJORMINOR@includeHEADERS \
+ install-typelibsDATA
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am: install-libLTLIBRARIES
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am: uninstall-girDATA uninstall-libLTLIBRARIES \
+ uninstall-libgstreamer_@GST_MAJORMINOR@includeHEADERS \
+ uninstall-nodist_libgstreamer_@GST_MAJORMINOR@includeHEADERS \
+ uninstall-typelibsDATA
+
+.MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) all check \
+ ctags-recursive install install-am install-strip \
+ tags-recursive
+
+.PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \
+ all all-am all-local check check-am clean clean-generic \
+ clean-libLTLIBRARIES clean-libtool ctags ctags-recursive \
+ distclean distclean-compile distclean-generic \
+ distclean-libtool distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-girDATA install-html install-html-am \
+ install-info install-info-am install-libLTLIBRARIES \
+ install-libgstreamer_@GST_MAJORMINOR@includeHEADERS \
+ install-man \
+ install-nodist_libgstreamer_@GST_MAJORMINOR@includeHEADERS \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip install-typelibsDATA installcheck \
+ installcheck-am installdirs installdirs-am maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-recursive uninstall uninstall-am uninstall-girDATA \
+ uninstall-libLTLIBRARIES \
+ uninstall-libgstreamer_@GST_MAJORMINOR@includeHEADERS \
+ uninstall-nodist_libgstreamer_@GST_MAJORMINOR@includeHEADERS \
+ uninstall-typelibsDATA
+
+
+gstmarshal.h: gstmarshal.list
+ $(AM_V_GEN)glib-genmarshal --header --prefix=gst_marshal $(srcdir)/gstmarshal.list > gstmarshal.h.tmp && \
+ mv gstmarshal.h.tmp gstmarshal.h
+
+gstmarshal.c: gstmarshal.list gst_private.h
+ $(AM_V_GEN)echo "#include \"gst_private.h\"" > gstmarshal.c.tmp && \
+ echo "#include \"glib-object.h\"" >> gstmarshal.c.tmp && \
+ echo "#include \"gstmarshal.h\"" >> gstmarshal.c.tmp && \
+ glib-genmarshal --body --prefix=gst_marshal $(srcdir)/gstmarshal.list >> gstmarshal.c.tmp && \
+ mv gstmarshal.c.tmp gstmarshal.c
+
+gstenumtypes.h: $(gst_headers)
+ $(AM_V_GEN)glib-mkenums \
+ --fhead "#ifndef __GST_ENUM_TYPES_H__\n#define __GST_ENUM_TYPES_H__\n\n#include <glib-object.h>\n\nG_BEGIN_DECLS\n" \
+ --fprod "\n/* enumerations from \"@filename@\" */\n" \
+ --vhead "GType @enum_name@_get_type (void);\n#define GST_TYPE_@ENUMSHORT@ (@enum_name@_get_type())\n" \
+ --ftail "G_END_DECLS\n\n#endif /* __GST_ENUM_TYPES_H__ */" \
+ $^ > gstenumtypes.h
+
+gstenumtypes.c: $(gst_headers)
+ $(AM_V_GEN)glib-mkenums \
+ --fhead "#include \"gst_private.h\"\n#include <gst/gst.h>\n#define C_ENUM(v) ((gint) v)\n#define C_FLAGS(v) ((guint) v)\n " \
+ --fprod "\n/* enumerations from \"@filename@\" */" \
+ --vhead "GType\n@enum_name@_get_type (void)\n{\n static gsize id = 0;\n static const G@Type@Value values[] = {" \
+ --vprod " { C_@TYPE@(@VALUENAME@), \"@VALUENAME@\", \"@valuenick@\" }," \
+ --vtail " { 0, NULL, NULL }\n };\n\n if (g_once_init_enter (&id)) {\n GType tmp = g_@type@_register_static (\"@EnumName@\", values);\n g_once_init_leave (&id, tmp);\n }\n\n return (GType) id;\n}" \
+ $^ > gstenumtypes.c
+
+%.c.gcov: .libs/libgstreamer_@GST_MAJORMINOR@_la-%.gcda %.c
+ $(GCOV) -b -f -o $^ > $@.out
+
+gcov: $(libgstreamer_@GST_MAJORMINOR@_la_SOURCES:=.gcov)
+
+Android.mk: Makefile.am
+ androgenizer -:PROJECT gstreamer -:SHARED libgstreamer-@GST_MAJORMINOR@ \
+ -:TAGS eng debug \
+ -:REL_TOP $(top_srcdir) -:ABS_TOP $(abs_top_srcdir) \
+ -:SOURCES $(libgstreamer_@GST_MAJORMINOR@_la_SOURCES) \
+ $(nodist_libgstreamer_@GST_MAJORMINOR@_la_SOURCES) \
+ -:CFLAGS $(DEFS) $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) \
+ -:LDFLAGS $(libgstreamer_@GST_MAJORMINOR@_la_LDFLAGS) \
+ $(libgstreamer_@GST_MAJORMINOR@_la_LIBADD) \
+ -ldl \
+ -:SUBDIR gst/parse \
+ -:HEADER_TARGET gstreamer-@GST_MAJORMINOR@/gst \
+ -:HEADERS $(libgstreamer_@GST_MAJORMINOR@include_HEADERS) \
+ -:LIBFILTER_STATIC gstparse \
+ -:PASSTHROUGH LOCAL_ARM_MODE:=arm \
+ > $@
+
+@HAVE_INTROSPECTION_TRUE@Gst-@GST_MAJORMINOR@.gir: $(INTROSPECTION_SCANNER) libgstreamer-@GST_MAJORMINOR@.la
+@HAVE_INTROSPECTION_TRUE@ $(AM_V_GEN)GST_PLUGIN_SYSTEM_PATH="" GST_PLUGIN_PATH="" GST_REGISTRY_UPDATE=no \
+@HAVE_INTROSPECTION_TRUE@ $(INTROSPECTION_SCANNER) -v --namespace Gst \
+@HAVE_INTROSPECTION_TRUE@ --nsversion=@GST_MAJORMINOR@ \
+@HAVE_INTROSPECTION_TRUE@ -I$(top_srcdir) \
+@HAVE_INTROSPECTION_TRUE@ -I$(top_builddir) \
+@HAVE_INTROSPECTION_TRUE@ -DIN_GOBJECT_INTROSPECTION=1 \
+@HAVE_INTROSPECTION_TRUE@ -DGST_USE_UNSTABLE_API \
+@HAVE_INTROSPECTION_TRUE@ --c-include='gst/gst.h' \
+@HAVE_INTROSPECTION_TRUE@ --library=libgstreamer-0.11.la \
+@HAVE_INTROSPECTION_TRUE@ --include=GLib-2.0 \
+@HAVE_INTROSPECTION_TRUE@ --include=GObject-2.0 \
+@HAVE_INTROSPECTION_TRUE@ --include=GModule-2.0 \
+@HAVE_INTROSPECTION_TRUE@ --libtool="$(top_builddir)/libtool" \
+@HAVE_INTROSPECTION_TRUE@ --pkg glib-2.0 \
+@HAVE_INTROSPECTION_TRUE@ --pkg gobject-2.0 \
+@HAVE_INTROSPECTION_TRUE@ --pkg gmodule-no-export-2.0 \
+@HAVE_INTROSPECTION_TRUE@ --pkg gthread-2.0 \
+@HAVE_INTROSPECTION_TRUE@ --pkg-export gstreamer-@GST_MAJORMINOR@ \
+@HAVE_INTROSPECTION_TRUE@ --add-init-section="gst_init(NULL, NULL);" \
+@HAVE_INTROSPECTION_TRUE@ --output $@ \
+@HAVE_INTROSPECTION_TRUE@ $(gir_headers) \
+@HAVE_INTROSPECTION_TRUE@ $(gir_sources)
+
+@HAVE_INTROSPECTION_TRUE@%.typelib: %.gir $(INTROSPECTION_COMPILER)
+@HAVE_INTROSPECTION_TRUE@ $(AM_V_GEN)$(INTROSPECTION_COMPILER) --includedir=$(srcdir) --includedir=$(builddir) $(INTROSPECTION_COMPILER_OPTS) $< -o $(@F)
+
+# try to prevent packaging errors
+check-libexecdir-consistency:
+ @if test "${GST_PLUGIN_SCANNER_INSTALLED}" != "${libexecdir}/gstreamer-$(GST_MAJORMINOR)/gst-plugin-scanner"; then \
+ echo "*** Inconsistent libexecdir! Please use ./configure --libexecdir=/foo/bar"; \
+ echo "*** to set the libexecdir and not make libexecdir=/foo/bar or the like."; \
+ echo "*** The same goes for prefix, libdir etc."; \
+ echo "*** ${GST_PLUGIN_SCANNER_INSTALLED} != ${libexecdir}/gstreamer-$(GST_MAJORMINOR)/gst-plugin-scanner"; \
+ exit 1; \
+ fi
+
+all-local: check-libexecdir-consistency
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/gst/gettext.h b/gst/gettext.h
new file mode 100644
index 0000000..59902b3
--- /dev/null
+++ b/gst/gettext.h
@@ -0,0 +1,69 @@
+/* Convenience header for conditional use of GNU <libintl.h>.
+ Copyright (C) 1995-1998, 2000-2002 Free Software Foundation, Inc.
+
+ This program 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, or (at your option)
+ any later version.
+
+ This program 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 program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ USA. */
+
+#ifndef _LIBGETTEXT_H
+#define _LIBGETTEXT_H 1
+
+/* NLS can be disabled through the configure --disable-nls option. */
+#ifdef ENABLE_NLS
+
+/* Get declarations of GNU message catalog functions. */
+# include <libintl.h>
+
+#else
+
+/* Solaris /usr/include/locale.h includes /usr/include/libintl.h, which
+ chokes if dcgettext is defined as a macro. So include it now, to make
+ later inclusions of <locale.h> a NOP. We don't include <libintl.h>
+ as well because people using "gettext.h" will not include <libintl.h>,
+ and also including <libintl.h> would fail on SunOS 4, whereas <locale.h>
+ is OK. */
+#if defined(__sun)
+# include <locale.h>
+#endif
+
+/* Disabled NLS.
+ The casts to 'const char *' serve the purpose of producing warnings
+ for invalid uses of the value returned from these functions.
+ On pre-ANSI systems without 'const', the config.h file is supposed to
+ contain "#define const". */
+# define gettext(Msgid) ((const char *) (Msgid))
+# define dgettext(Domainname, Msgid) ((const char *) (Msgid))
+# define dcgettext(Domainname, Msgid, Category) ((const char *) (Msgid))
+# define ngettext(Msgid1, Msgid2, N) \
+ ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2))
+# define dngettext(Domainname, Msgid1, Msgid2, N) \
+ ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2))
+# define dcngettext(Domainname, Msgid1, Msgid2, N, Category) \
+ ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2))
+# define textdomain(Domainname) ((const char *) (Domainname))
+# define bindtextdomain(Domainname, Dirname) ((const char *) (Dirname))
+# define bind_textdomain_codeset(Domainname, Codeset) ((const char *) (Codeset))
+
+#endif
+
+/* A pseudo function call that serves as a marker for the automated
+ extraction of messages, but does not call gettext(). The run-time
+ translation is done at a different place in the code.
+ The argument, String, should be a literal string. Concatenated strings
+ and other string expressions won't work.
+ The macro's expansion is not parenthesized, so that it is suitable as
+ initializer for static 'char[]' or 'const char[]' variables. */
+#define gettext_noop(String) String
+
+#endif /* _LIBGETTEXT_H */
diff --git a/gst/glib-compat-private.h b/gst/glib-compat-private.h
new file mode 100644
index 0000000..7b5d6cf
--- /dev/null
+++ b/gst/glib-compat-private.h
@@ -0,0 +1,55 @@
+/*
+ * glib-compat.c
+ * Functions copied from glib 2.10
+ *
+ * Copyright 2005 David Schleef <ds@schleef.org>
+ */
+
+#ifndef __GLIB_COMPAT_PRIVATE_H__
+#define __GLIB_COMPAT_PRIVATE_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+#if !GLIB_CHECK_VERSION(2,25,0)
+
+#if defined (_MSC_VER) && !defined(_WIN64)
+typedef struct _stat32 GStatBuf;
+#else
+typedef struct stat GStatBuf;
+#endif
+
+#endif
+
+#if GLIB_CHECK_VERSION(2,26,0)
+#define GLIB_HAS_GDATETIME
+#endif
+
+/* See bug #651514 */
+#if GLIB_CHECK_VERSION(2,29,5)
+#define G_ATOMIC_POINTER_COMPARE_AND_EXCHANGE(a,b,c) \
+ g_atomic_pointer_compare_and_exchange ((a),(b),(c))
+#define G_ATOMIC_INT_COMPARE_AND_EXCHANGE(a,b,c) \
+ g_atomic_int_compare_and_exchange ((a),(b),(c))
+#else
+#define G_ATOMIC_POINTER_COMPARE_AND_EXCHANGE(a,b,c) \
+ g_atomic_pointer_compare_and_exchange ((volatile gpointer *)(a),(b),(c))
+#define G_ATOMIC_INT_COMPARE_AND_EXCHANGE(a,b,c) \
+ g_atomic_int_compare_and_exchange ((volatile int *)(a),(b),(c))
+#endif
+
+/* See bug #651514 */
+#if GLIB_CHECK_VERSION(2,29,5)
+#define G_ATOMIC_INT_ADD(a,b) g_atomic_int_add ((a),(b))
+#else
+#define G_ATOMIC_INT_ADD(a,b) g_atomic_int_exchange_and_add ((a),(b))
+#endif
+
+/* copies */
+
+/* adaptations */
+
+G_END_DECLS
+
+#endif
diff --git a/gst/glib-compat.h b/gst/glib-compat.h
new file mode 100644
index 0000000..7794306
--- /dev/null
+++ b/gst/glib-compat.h
@@ -0,0 +1,37 @@
+/* GStreamer
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * glib-compat.h: Public GLib compatibility shims
+ *
+ * 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.
+ */
+
+
+/*
+ * Modified by the GLib Team and others 1997-2000. See the AUTHORS file from
+ * glib-2.8.0 for a list of people on the GLib Team. See the ChangeLog files
+ * from glib-2.8.0 for a list of changes. These files are distributed with GLib
+ * at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+#ifndef __GST_GLIB_COMPAT_H__
+#define __GST_GLIB_COMPAT_H__
+
+G_BEGIN_DECLS
+
+G_END_DECLS
+
+#endif /* __GST_GLIB_COMPAT_H__ */
diff --git a/gst/gst-i18n-app.h b/gst/gst-i18n-app.h
new file mode 100644
index 0000000..06339b8
--- /dev/null
+++ b/gst/gst-i18n-app.h
@@ -0,0 +1,43 @@
+/* GStreamer
+ * Copyright (C) 2004 Thomas Vander Stichele <thomas@apestaart.org>
+ *
+ * gst-i18n-app.h: internationalization macros for the GStreamer tools
+ *
+ * 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_I18N_APP_H__
+#define __GST_I18N_APP_H__
+
+#ifdef ENABLE_NLS
+
+#include "gettext.h" /* included with gettext distribution and copied */
+
+/* we want to use shorthand _() for translating and N_() for marking */
+#define _(String) gettext (String)
+#define N_(String) gettext_noop (String)
+/* FIXME: if we need it, we can add Q_ as well, like in glib */
+
+#else
+
+#define _(String) String
+#define N_(String) String
+#define ngettext(Singular,Plural,Count) ((Count>1)?Plural:Singular)
+
+#endif
+
+#endif /* __GST_I18N_APP_H__ */
diff --git a/gst/gst-i18n-lib.h b/gst/gst-i18n-lib.h
new file mode 100644
index 0000000..e4eb45e
--- /dev/null
+++ b/gst/gst-i18n-lib.h
@@ -0,0 +1,46 @@
+/* GStreamer
+ * Copyright (C) 2004 Thomas Vander Stichele <thomas@apestaart.org>
+ *
+ * gst-i18n-lib.h: internationalization macros for the GStreamer libraries
+ *
+ * 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_I18N_LIB_H__
+#define __GST_I18N_LIB_H__
+
+#ifndef PACKAGE_NAME
+#error You must include config.h before including this header.
+#endif
+
+#ifdef ENABLE_NLS
+
+#include "gettext.h" /* included with gettext distribution and copied */
+
+/* we want to use shorthand _() for translating and N_() for marking */
+#define _(String) dgettext (GETTEXT_PACKAGE, String)
+#define N_(String) gettext_noop (String)
+/* FIXME: if we need it, we can add Q_ as well, like in glib */
+
+#else
+#define _(String) String
+#define N_(String) String
+#define ngettext(Singular,Plural,Count) ((Count>1)?Plural:Singular)
+
+#endif
+
+#endif /* __GST_I18N_LIB_H__ */
diff --git a/gst/gst.c b/gst/gst.c
new file mode 100644
index 0000000..4634758
--- /dev/null
+++ b/gst/gst.c
@@ -0,0 +1,1219 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2000 Wim Taymans <wtay@chello.be>
+ *
+ * gst.c: Initialization and non-pipeline operations
+ *
+ * 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:gst
+ * @short_description: Media library supporting arbitrary formats and filter
+ * graphs.
+ *
+ * GStreamer is a framework for constructing graphs of various filters
+ * (termed elements here) that will handle streaming media. Any discreet
+ * (packetizable) media type is supported, with provisions for automatically
+ * determining source type. Formatting/framing information is provided with
+ * a powerful negotiation framework. Plugins are heavily used to provide for
+ * all elements, allowing one to construct plugins outside of the GST
+ * library, even released binary-only if license require (please don't).
+ * GStreamer covers a wide range of use cases including: playback, recording,
+ * editing, serving streams, voice over ip and video calls.
+ *
+ * The <application>GStreamer</application> library should be initialized with
+ * gst_init() before it can be used. You should pass pointers to the main argc
+ * and argv variables so that GStreamer can process its own command line
+ * options, as shown in the following example.
+ *
+ * <example>
+ * <title>Initializing the gstreamer library</title>
+ * <programlisting language="c">
+ * int
+ * main (int argc, char *argv[])
+ * {
+ * // initialize the GStreamer library
+ * gst_init (&amp;argc, &amp;argv);
+ * ...
+ * }
+ * </programlisting>
+ * </example>
+ *
+ * It's allowed to pass two NULL pointers to gst_init() in case you don't want
+ * to pass the command line args to GStreamer.
+ *
+ * You can also use GOption to initialize your own parameters as shown in
+ * the next code fragment:
+ * <example>
+ * <title>Initializing own parameters when initializing gstreamer</title>
+ * <programlisting>
+ * static gboolean stats = FALSE;
+ * ...
+ * int
+ * main (int argc, char *argv[])
+ * {
+ * GOptionEntry options[] = {
+ * {"tags", 't', 0, G_OPTION_ARG_NONE, &amp;tags,
+ * N_("Output tags (also known as metadata)"), NULL},
+ * {NULL}
+ * };
+ * // must initialise the threading system before using any other GLib funtion
+ * if (!g_thread_supported ())
+ * g_thread_init (NULL);
+ * ctx = g_option_context_new ("[ADDITIONAL ARGUMENTS]");
+ * g_option_context_add_main_entries (ctx, options, GETTEXT_PACKAGE);
+ * g_option_context_add_group (ctx, gst_init_get_option_group ());
+ * if (!g_option_context_parse (ctx, &amp;argc, &amp;argv, &amp;err)) {
+ * g_print ("Error initializing: &percnt;s\n", GST_STR_NULL (err->message));
+ * exit (1);
+ * }
+ * g_option_context_free (ctx);
+ * ...
+ * }
+ * </programlisting>
+ * </example>
+ *
+ * Use gst_version() to query the library version at runtime or use the
+ * GST_VERSION_* macros to find the version at compile time. Optionally
+ * gst_version_string() returns a printable string.
+ *
+ * The gst_deinit() call is used to clean up all internal resources used
+ * by <application>GStreamer</application>. It is mostly used in unit tests
+ * to check for leaks.
+ *
+ * Last reviewed on 2006-08-11 (0.10.10)
+ */
+
+#include "gst_private.h"
+#include "gstconfig.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_UTSNAME_H
+#include <sys/utsname.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef G_OS_WIN32
+#define WIN32_LEAN_AND_MEAN /* prevents from including too many things */
+#include <windows.h> /* GetStdHandle, windows console */
+#endif
+
+#include "gst-i18n-lib.h"
+#include <locale.h> /* for LC_ALL */
+
+#include "gst.h"
+
+#define GST_CAT_DEFAULT GST_CAT_GST_INIT
+
+#define MAX_PATH_SPLIT 16
+#define GST_PLUGIN_SEPARATOR ","
+
+static gboolean gst_initialized = FALSE;
+static gboolean gst_deinitialized = FALSE;
+
+#ifdef G_OS_WIN32
+HMODULE _priv_gst_dll_handle = NULL;
+#endif
+
+#ifndef GST_DISABLE_REGISTRY
+GList *_priv_gst_plugin_paths = NULL; /* for delayed processing in post_init */
+
+extern gboolean _priv_gst_disable_registry_update;
+#endif
+
+#ifndef GST_DISABLE_GST_DEBUG
+extern const gchar *priv_gst_dump_dot_dir;
+#endif
+
+/* defaults */
+
+/* set to TRUE when segfaults need to be left as is */
+static gboolean _gst_disable_segtrap = FALSE;
+
+static gboolean init_pre (GOptionContext * context, GOptionGroup * group,
+ gpointer data, GError ** error);
+static gboolean init_post (GOptionContext * context, GOptionGroup * group,
+ gpointer data, GError ** error);
+#ifndef GST_DISABLE_OPTION_PARSING
+static gboolean parse_goption_arg (const gchar * s_opt,
+ const gchar * arg, gpointer data, GError ** err);
+#endif
+
+GSList *_priv_gst_preload_plugins = NULL;
+
+const gchar g_log_domain_gstreamer[] = "GStreamer";
+
+static void
+debug_log_handler (const gchar * log_domain,
+ GLogLevelFlags log_level, const gchar * message, gpointer user_data)
+{
+ g_log_default_handler (log_domain, log_level, message, user_data);
+ /* FIXME: do we still need this ? fatal errors these days are all
+ * other than core errors */
+ /* g_on_error_query (NULL); */
+}
+
+enum
+{
+ ARG_VERSION = 1,
+ ARG_FATAL_WARNINGS,
+#ifndef GST_DISABLE_GST_DEBUG
+ ARG_DEBUG_LEVEL,
+ ARG_DEBUG,
+ ARG_DEBUG_DISABLE,
+ ARG_DEBUG_NO_COLOR,
+ ARG_DEBUG_HELP,
+#endif
+ ARG_PLUGIN_SPEW,
+ ARG_PLUGIN_PATH,
+ ARG_PLUGIN_LOAD,
+ ARG_SEGTRAP_DISABLE,
+ ARG_REGISTRY_UPDATE_DISABLE,
+ ARG_REGISTRY_FORK_DISABLE
+};
+
+/* debug-spec ::= category-spec [, category-spec]*
+ * category-spec ::= category:val | val
+ * category ::= [^:]+
+ * val ::= [0-5]
+ */
+
+#ifndef NUL
+#define NUL '\0'
+#endif
+
+#ifndef GST_DISABLE_GST_DEBUG
+static gboolean
+parse_debug_category (gchar * str, const gchar ** category)
+{
+ if (!str)
+ return FALSE;
+
+ /* works in place */
+ g_strstrip (str);
+
+ if (str[0] != NUL) {
+ *category = str;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+parse_debug_level (gchar * str, GstDebugLevel * level)
+{
+ if (!str)
+ return FALSE;
+
+ /* works in place */
+ g_strstrip (str);
+
+ if (str[0] != NUL && str[1] == NUL
+ && str[0] >= '0' && str[0] < '0' + GST_LEVEL_COUNT) {
+ *level = (GstDebugLevel) (str[0] - '0');
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+parse_debug_list (const gchar * list)
+{
+ gchar **split;
+ gchar **walk;
+
+ g_assert (list);
+
+ split = g_strsplit (list, ",", 0);
+
+ for (walk = split; *walk; walk++) {
+ if (strchr (*walk, ':')) {
+ gchar **values = g_strsplit (*walk, ":", 2);
+
+ if (values[0] && values[1]) {
+ GstDebugLevel level;
+ const gchar *category;
+
+ if (parse_debug_category (values[0], &category)
+ && parse_debug_level (values[1], &level))
+ gst_debug_set_threshold_for_name (category, level);
+ }
+
+ g_strfreev (values);
+ } else {
+ GstDebugLevel level;
+
+ if (parse_debug_level (*walk, &level))
+ gst_debug_set_default_threshold (level);
+ }
+ }
+
+ g_strfreev (split);
+}
+#endif
+
+#ifdef G_OS_WIN32
+BOOL WINAPI DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved);
+BOOL WINAPI
+DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
+{
+ if (fdwReason == DLL_PROCESS_ATTACH)
+ _priv_gst_dll_handle = (HMODULE) hinstDLL;
+ return TRUE;
+}
+
+#endif
+
+/**
+ * gst_init_get_option_group:
+ *
+ * Returns a #GOptionGroup with GStreamer's argument specifications. The
+ * group is set up to use standard GOption callbacks, so when using this
+ * group in combination with GOption parsing methods, all argument parsing
+ * and initialization is automated.
+ *
+ * This function is useful if you want to integrate GStreamer with other
+ * libraries that use GOption (see g_option_context_add_group() ).
+ *
+ * If you use this function, you should make sure you initialise the GLib
+ * threading system as one of the very first things in your program
+ * (see the example at the beginning of this section).
+ *
+ * Returns: (transfer full): a pointer to GStreamer's option group.
+ */
+
+GOptionGroup *
+gst_init_get_option_group (void)
+{
+#ifndef GST_DISABLE_OPTION_PARSING
+ GOptionGroup *group;
+ static const GOptionEntry gst_args[] = {
+ {"gst-version", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
+ (gpointer) parse_goption_arg, N_("Print the GStreamer version"), NULL},
+ {"gst-fatal-warnings", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
+ (gpointer) parse_goption_arg, N_("Make all warnings fatal"), NULL},
+#ifndef GST_DISABLE_GST_DEBUG
+ {"gst-debug-help", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
+ (gpointer) parse_goption_arg,
+ N_("Print available debug categories and exit"),
+ NULL},
+ {"gst-debug-level", 0, 0, G_OPTION_ARG_CALLBACK,
+ (gpointer) parse_goption_arg,
+ N_("Default debug level from 1 (only error) to 5 (anything) or "
+ "0 for no output"),
+ N_("LEVEL")},
+ {"gst-debug", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) parse_goption_arg,
+ N_("Comma-separated list of category_name:level pairs to set "
+ "specific levels for the individual categories. Example: "
+ "GST_AUTOPLUG:5,GST_ELEMENT_*:3"),
+ N_("LIST")},
+ {"gst-debug-no-color", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
+ (gpointer) parse_goption_arg, N_("Disable colored debugging output"),
+ NULL},
+ {"gst-debug-disable", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
+ (gpointer) parse_goption_arg, N_("Disable debugging"), NULL},
+#endif
+ {"gst-plugin-spew", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
+ (gpointer) parse_goption_arg,
+ N_("Enable verbose plugin loading diagnostics"),
+ NULL},
+ {"gst-plugin-path", 0, 0, G_OPTION_ARG_CALLBACK,
+ (gpointer) parse_goption_arg,
+ N_("Colon-separated paths containing plugins"), N_("PATHS")},
+ {"gst-plugin-load", 0, 0, G_OPTION_ARG_CALLBACK,
+ (gpointer) parse_goption_arg,
+ N_("Comma-separated list of plugins to preload in addition to the "
+ "list stored in environment variable GST_PLUGIN_PATH"),
+ N_("PLUGINS")},
+ {"gst-disable-segtrap", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
+ (gpointer) parse_goption_arg,
+ N_("Disable trapping of segmentation faults during plugin loading"),
+ NULL},
+ {"gst-disable-registry-update", 0, G_OPTION_FLAG_NO_ARG,
+ G_OPTION_ARG_CALLBACK,
+ (gpointer) parse_goption_arg,
+ N_("Disable updating the registry"),
+ NULL},
+ {"gst-disable-registry-fork", 0, G_OPTION_FLAG_NO_ARG,
+ G_OPTION_ARG_CALLBACK,
+ (gpointer) parse_goption_arg,
+ N_("Disable spawning a helper process while scanning the registry"),
+ NULL},
+ {NULL}
+ };
+
+ /* Since GLib 2.23.2 calling g_thread_init() 'late' is allowed and is
+ * automatically done as part of g_type_init() */
+ if (glib_check_version (2, 23, 3)) {
+ /* The GLib threading system must be initialised before calling any other
+ * GLib function according to the documentation; if the application hasn't
+ * called gst_init() yet or initialised the threading system otherwise, we
+ * better issue a warning here (since chances are high that the application
+ * has already called other GLib functions such as g_option_context_new() */
+ if (!g_thread_get_initialized ()) {
+ g_warning ("The GStreamer function gst_init_get_option_group() was\n"
+ "\tcalled, but the GLib threading system has not been initialised\n"
+ "\tyet, something that must happen before any other GLib function\n"
+ "\tis called. The application needs to be fixed so that it calls\n"
+ "\t if (!g_thread_get_initialized ()) g_thread_init(NULL);\n"
+ "\tas very first thing in its main() function. Please file a bug\n"
+ "\tagainst this application.");
+ g_thread_init (NULL);
+ }
+ } else {
+ /* GLib >= 2.23.2 */
+ }
+
+ group = g_option_group_new ("gst", _("GStreamer Options"),
+ _("Show GStreamer Options"), NULL, NULL);
+ g_option_group_set_parse_hooks (group, (GOptionParseFunc) init_pre,
+ (GOptionParseFunc) init_post);
+
+ g_option_group_add_entries (group, gst_args);
+ g_option_group_set_translation_domain (group, GETTEXT_PACKAGE);
+
+ return group;
+#else
+ return NULL;
+#endif
+}
+
+/**
+ * gst_init_check:
+ * @argc: (inout) (allow-none): pointer to application's argc
+ * @argv: (inout) (array length=argc) (allow-none): pointer to application's argv
+ * @err: pointer to a #GError to which a message will be posted on error
+ *
+ * Initializes the GStreamer library, setting up internal path lists,
+ * registering built-in elements, and loading standard plugins.
+ *
+ * This function will return %FALSE if GStreamer could not be initialized
+ * for some reason. If you want your program to fail fatally,
+ * use gst_init() instead.
+ *
+ * This function should be called before calling any other GLib functions. If
+ * this is not an option, your program must initialise the GLib thread system
+ * using g_thread_init() before any other GLib functions are called.
+ *
+ * Returns: %TRUE if GStreamer could be initialized.
+ */
+gboolean
+gst_init_check (int *argc, char **argv[], GError ** err)
+{
+#ifndef GST_DISABLE_OPTION_PARSING
+ GOptionGroup *group;
+ GOptionContext *ctx;
+#endif
+ gboolean res;
+
+ if (!g_thread_get_initialized ())
+ g_thread_init (NULL);
+
+ if (gst_initialized) {
+ GST_DEBUG ("already initialized gst");
+ return TRUE;
+ }
+#ifndef GST_DISABLE_OPTION_PARSING
+ ctx = g_option_context_new ("- GStreamer initialization");
+ g_option_context_set_ignore_unknown_options (ctx, TRUE);
+ group = gst_init_get_option_group ();
+ g_option_context_add_group (ctx, group);
+ res = g_option_context_parse (ctx, argc, argv, err);
+ g_option_context_free (ctx);
+#else
+ init_pre (NULL, NULL, NULL, NULL);
+ init_post (NULL, NULL, NULL, NULL);
+ res = TRUE;
+#endif
+
+ gst_initialized = res;
+
+ if (res) {
+ GST_INFO ("initialized GStreamer successfully");
+ } else {
+ GST_INFO ("failed to initialize GStreamer");
+ }
+
+ return res;
+}
+
+/**
+ * gst_init:
+ * @argc: (inout) (allow-none): pointer to application's argc
+ * @argv: (inout) (array length=argc) (allow-none): pointer to application's argv
+ *
+ * Initializes the GStreamer library, setting up internal path lists,
+ * registering built-in elements, and loading standard plugins.
+ *
+ * Unless the plugin registry is disabled at compile time, the registry will be
+ * loaded. By default this will also check if the registry cache needs to be
+ * updated and rescan all plugins if needed. See gst_update_registry() for
+ * details and section
+ * <link linkend="gst-running">Running GStreamer Applications</link>
+ * for how to disable automatic registry updates.
+ *
+ * This function should be called before calling any other GLib functions. If
+ * this is not an option, your program must initialise the GLib thread system
+ * using g_thread_init() before any other GLib functions are called.
+ *
+ * <note><para>
+ * This function will terminate your program if it was unable to initialize
+ * GStreamer for some reason. If you want your program to fall back,
+ * use gst_init_check() instead.
+ * </para></note>
+ *
+ * WARNING: This function does not work in the same way as corresponding
+ * functions in other glib-style libraries, such as gtk_init(). In
+ * particular, unknown command line options cause this function to
+ * abort program execution.
+ */
+void
+gst_init (int *argc, char **argv[])
+{
+ GError *err = NULL;
+
+ if (!gst_init_check (argc, argv, &err)) {
+ g_print ("Could not initialize GStreamer: %s\n",
+ err ? err->message : "unknown error occurred");
+ if (err) {
+ g_error_free (err);
+ }
+ exit (1);
+ }
+}
+
+/**
+ * gst_is_initialized:
+ *
+ * Use this function to check if GStreamer has been initialized with gst_init()
+ * or gst_init_check().
+ *
+ * Returns: TRUE if initialization has been done, FALSE otherwise.
+ *
+ * Since: 0.10.31
+ */
+gboolean
+gst_is_initialized (void)
+{
+ return gst_initialized;
+}
+
+#ifndef GST_DISABLE_REGISTRY
+static void
+add_path_func (gpointer data, gpointer user_data)
+{
+ GST_INFO ("Adding plugin path: \"%s\", will scan later", (gchar *) data);
+ _priv_gst_plugin_paths =
+ g_list_append (_priv_gst_plugin_paths, g_strdup (data));
+}
+#endif
+
+#ifndef GST_DISABLE_OPTION_PARSING
+static void
+prepare_for_load_plugin_func (gpointer data, gpointer user_data)
+{
+ _priv_gst_preload_plugins =
+ g_slist_prepend (_priv_gst_preload_plugins, g_strdup (data));
+}
+#endif
+
+#ifndef GST_DISABLE_OPTION_PARSING
+static void
+split_and_iterate (const gchar * stringlist, const gchar * separator,
+ GFunc iterator, gpointer user_data)
+{
+ gchar **strings;
+ gint j = 0;
+ gchar *lastlist = g_strdup (stringlist);
+
+ while (lastlist) {
+ strings = g_strsplit (lastlist, separator, MAX_PATH_SPLIT);
+ g_free (lastlist);
+ lastlist = NULL;
+
+ while (strings[j]) {
+ iterator (strings[j], user_data);
+ if (++j == MAX_PATH_SPLIT) {
+ lastlist = g_strdup (strings[j]);
+ j = 0;
+ break;
+ }
+ }
+ g_strfreev (strings);
+ }
+}
+#endif
+
+/* we have no fail cases yet, but maybe in the future */
+static gboolean
+init_pre (GOptionContext * context, GOptionGroup * group, gpointer data,
+ GError ** error)
+{
+ if (gst_initialized) {
+ GST_DEBUG ("already initialized");
+ return TRUE;
+ }
+
+ g_type_init ();
+
+ /* we need threading to be enabled right here */
+ g_assert (g_thread_get_initialized ());
+
+ _gst_debug_init ();
+
+#ifdef ENABLE_NLS
+ setlocale (LC_ALL, "");
+ bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+#endif /* ENABLE_NLS */
+
+#ifndef GST_DISABLE_GST_DEBUG
+ {
+ const gchar *debug_list;
+
+ if (g_getenv ("GST_DEBUG_NO_COLOR") != NULL)
+ gst_debug_set_colored (FALSE);
+
+ debug_list = g_getenv ("GST_DEBUG");
+ if (debug_list) {
+ parse_debug_list (debug_list);
+ }
+ }
+
+ priv_gst_dump_dot_dir = g_getenv ("GST_DEBUG_DUMP_DOT_DIR");
+#endif
+ /* This is the earliest we can make stuff show up in the logs.
+ * So give some useful info about GStreamer here */
+ GST_INFO ("Initializing GStreamer Core Library version %s", VERSION);
+ GST_INFO ("Using library installed in %s", LIBDIR);
+
+ /* Print some basic system details if possible (OS/architecture) */
+#ifdef HAVE_SYS_UTSNAME_H
+ {
+ struct utsname sys_details;
+
+ if (uname (&sys_details) == 0) {
+ GST_INFO ("%s %s %s %s %s", sys_details.sysname,
+ sys_details.nodename, sys_details.release, sys_details.version,
+ sys_details.machine);
+ }
+ }
+#endif
+
+ return TRUE;
+}
+
+static gboolean
+gst_register_core_elements (GstPlugin * plugin)
+{
+ /* register some standard builtin types */
+ if (!gst_element_register (plugin, "bin", GST_RANK_PRIMARY,
+ GST_TYPE_BIN) ||
+ !gst_element_register (plugin, "pipeline", GST_RANK_PRIMARY,
+ GST_TYPE_PIPELINE)
+ )
+ g_assert_not_reached ();
+
+ return TRUE;
+}
+
+/*
+ * this bit handles:
+ * - initalization of threads if we use them
+ * - log handler
+ * - initial output
+ * - initializes gst_format
+ * - registers a bunch of types for gst_objects
+ *
+ * - we don't have cases yet where this fails, but in the future
+ * we might and then it's nice to be able to return that
+ */
+static gboolean
+init_post (GOptionContext * context, GOptionGroup * group, gpointer data,
+ GError ** error)
+{
+ GLogLevelFlags llf;
+
+#ifndef GST_DISABLE_TRACE
+ GstTrace *gst_trace;
+#endif /* GST_DISABLE_TRACE */
+
+ if (gst_initialized) {
+ GST_DEBUG ("already initialized");
+ return TRUE;
+ }
+
+ llf = G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_ERROR | G_LOG_FLAG_FATAL;
+ g_log_set_handler (g_log_domain_gstreamer, llf, debug_log_handler, NULL);
+
+ _priv_gst_quarks_initialize ();
+ _priv_gst_memory_initialize ();
+ _priv_gst_format_initialize ();
+ _priv_gst_query_initialize ();
+ _priv_gst_structure_initialize ();
+ _priv_gst_caps_initialize ();
+ _priv_gst_meta_initialize ();
+
+ g_type_class_ref (gst_object_get_type ());
+ g_type_class_ref (gst_pad_get_type ());
+ g_type_class_ref (gst_element_factory_get_type ());
+ g_type_class_ref (gst_element_get_type ());
+ g_type_class_ref (gst_type_find_factory_get_type ());
+ g_type_class_ref (gst_bin_get_type ());
+ g_type_class_ref (gst_bus_get_type ());
+ g_type_class_ref (gst_task_get_type ());
+ g_type_class_ref (gst_clock_get_type ());
+
+ g_type_class_ref (gst_index_factory_get_type ());
+ gst_uri_handler_get_type ();
+
+ g_type_class_ref (gst_object_flags_get_type ());
+ g_type_class_ref (gst_bin_flags_get_type ());
+ g_type_class_ref (gst_buffer_flags_get_type ());
+ g_type_class_ref (gst_buffer_copy_flags_get_type ());
+ g_type_class_ref (gst_bus_flags_get_type ());
+ g_type_class_ref (gst_bus_sync_reply_get_type ());
+ g_type_class_ref (gst_caps_flags_get_type ());
+ g_type_class_ref (gst_clock_return_get_type ());
+ g_type_class_ref (gst_clock_entry_type_get_type ());
+ g_type_class_ref (gst_clock_flags_get_type ());
+ g_type_class_ref (gst_clock_type_get_type ());
+ g_type_class_ref (gst_debug_graph_details_get_type ());
+ g_type_class_ref (gst_state_get_type ());
+ g_type_class_ref (gst_state_change_return_get_type ());
+ g_type_class_ref (gst_state_change_get_type ());
+ g_type_class_ref (gst_element_flags_get_type ());
+ g_type_class_ref (gst_core_error_get_type ());
+ g_type_class_ref (gst_library_error_get_type ());
+ g_type_class_ref (gst_resource_error_get_type ());
+ g_type_class_ref (gst_stream_error_get_type ());
+ g_type_class_ref (gst_event_type_flags_get_type ());
+ g_type_class_ref (gst_event_type_get_type ());
+ g_type_class_ref (gst_seek_type_get_type ());
+ g_type_class_ref (gst_seek_flags_get_type ());
+ g_type_class_ref (gst_qos_type_get_type ());
+ g_type_class_ref (gst_format_get_type ());
+ g_type_class_ref (gst_index_certainty_get_type ());
+ g_type_class_ref (gst_index_entry_type_get_type ());
+ g_type_class_ref (gst_index_lookup_method_get_type ());
+ g_type_class_ref (gst_assoc_flags_get_type ());
+ g_type_class_ref (gst_index_resolver_method_get_type ());
+ g_type_class_ref (gst_index_flags_get_type ());
+ g_type_class_ref (gst_debug_level_get_type ());
+ g_type_class_ref (gst_debug_color_flags_get_type ());
+ g_type_class_ref (gst_iterator_result_get_type ());
+ g_type_class_ref (gst_iterator_item_get_type ());
+ g_type_class_ref (gst_message_type_get_type ());
+ g_type_class_ref (gst_mini_object_flags_get_type ());
+ g_type_class_ref (gst_pad_link_return_get_type ());
+ g_type_class_ref (gst_pad_link_check_get_type ());
+ g_type_class_ref (gst_flow_return_get_type ());
+ g_type_class_ref (gst_activate_mode_get_type ());
+ g_type_class_ref (gst_pad_direction_get_type ());
+ g_type_class_ref (gst_pad_flags_get_type ());
+ g_type_class_ref (gst_pad_presence_get_type ());
+ g_type_class_ref (gst_pad_template_flags_get_type ());
+ g_type_class_ref (gst_pipeline_flags_get_type ());
+ g_type_class_ref (gst_plugin_error_get_type ());
+ g_type_class_ref (gst_plugin_flags_get_type ());
+ g_type_class_ref (gst_plugin_dependency_flags_get_type ());
+ g_type_class_ref (gst_rank_get_type ());
+ g_type_class_ref (gst_query_type_get_type ());
+ g_type_class_ref (gst_buffering_mode_get_type ());
+ g_type_class_ref (gst_stream_status_type_get_type ());
+ g_type_class_ref (gst_structure_change_type_get_type ());
+ g_type_class_ref (gst_tag_merge_mode_get_type ());
+ g_type_class_ref (gst_tag_flag_get_type ());
+ g_type_class_ref (gst_task_pool_get_type ());
+ g_type_class_ref (gst_task_state_get_type ());
+ g_type_class_ref (gst_alloc_trace_flags_get_type ());
+ g_type_class_ref (gst_type_find_probability_get_type ());
+ g_type_class_ref (gst_uri_type_get_type ());
+ g_type_class_ref (gst_parse_error_get_type ());
+ g_type_class_ref (gst_parse_flags_get_type ());
+ g_type_class_ref (gst_search_mode_get_type ());
+ g_type_class_ref (gst_progress_type_get_type ());
+ g_type_class_ref (gst_buffer_pool_flags_get_type ());
+ g_type_class_ref (gst_memory_flags_get_type ());
+ g_type_class_ref (gst_map_flags_get_type ());
+ g_type_class_ref (gst_caps_intersect_mode_get_type ());
+ g_type_class_ref (gst_probe_type_get_type ());
+ g_type_class_ref (gst_probe_return_get_type ());
+ g_type_class_ref (gst_segment_flags_get_type ());
+
+ _priv_gst_event_initialize ();
+ _priv_gst_buffer_initialize ();
+ _priv_gst_message_initialize ();
+ _priv_gst_buffer_list_initialize ();
+ _priv_gst_value_initialize ();
+ g_type_class_ref (gst_param_spec_fraction_get_type ());
+ _priv_gst_tag_initialize ();
+ gst_parse_context_get_type ();
+
+ _priv_gst_plugin_initialize ();
+
+ gst_g_error_get_type ();
+
+ /* register core plugins */
+ gst_plugin_register_static (GST_VERSION_MAJOR, GST_VERSION_MINOR,
+ "staticelements", "core elements linked into the GStreamer library",
+ gst_register_core_elements, VERSION, GST_LICENSE, PACKAGE,
+ GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);
+
+ /*
+ * Any errors happening below this point are non-fatal, we therefore mark
+ * gstreamer as being initialized, since it is the case from a plugin point of
+ * view.
+ *
+ * If anything fails, it will be put back to FALSE in gst_init_check().
+ * This allows some special plugins that would call gst_init() to not cause a
+ * looping effect (i.e. initializing GStreamer twice).
+ */
+ gst_initialized = TRUE;
+
+ if (!gst_update_registry ())
+ return FALSE;
+
+#ifndef GST_DISABLE_TRACE
+ _gst_trace_on = 0;
+ if (_gst_trace_on) {
+ gst_trace = gst_trace_new ("gst.trace", 1024);
+ gst_trace_set_default (gst_trace);
+ }
+#endif /* GST_DISABLE_TRACE */
+
+ GST_INFO ("GLib runtime version: %d.%d.%d", glib_major_version,
+ glib_minor_version, glib_micro_version);
+ GST_INFO ("GLib headers version: %d.%d.%d", GLIB_MAJOR_VERSION,
+ GLIB_MINOR_VERSION, GLIB_MICRO_VERSION);
+
+ return TRUE;
+}
+
+#ifndef GST_DISABLE_GST_DEBUG
+static gboolean
+select_all (GstPlugin * plugin, gpointer user_data)
+{
+ return TRUE;
+}
+
+static gint
+sort_by_category_name (gconstpointer a, gconstpointer b)
+{
+ return strcmp (gst_debug_category_get_name ((GstDebugCategory *) a),
+ gst_debug_category_get_name ((GstDebugCategory *) b));
+}
+
+static void
+gst_debug_help (void)
+{
+ GSList *list, *walk;
+ GList *list2, *g;
+
+ /* Need to ensure the registry is loaded to get debug categories */
+ if (!init_post (NULL, NULL, NULL, NULL))
+ exit (1);
+
+ list2 = gst_registry_plugin_filter (gst_registry_get_default (),
+ select_all, FALSE, NULL);
+
+ /* FIXME this is gross. why don't debug have categories PluginFeatures? */
+ for (g = list2; g; g = g_list_next (g)) {
+ GstPlugin *plugin = GST_PLUGIN_CAST (g->data);
+
+ gst_plugin_load (plugin);
+ }
+ g_list_free (list2);
+
+ list = gst_debug_get_all_categories ();
+ walk = list = g_slist_sort (list, sort_by_category_name);
+
+ g_print ("\n");
+ g_print ("name level description\n");
+ g_print ("---------------------+--------+--------------------------------\n");
+
+ while (walk) {
+ GstDebugCategory *cat = (GstDebugCategory *) walk->data;
+
+ if (gst_debug_is_colored ()) {
+#ifdef G_OS_WIN32
+ gint color = gst_debug_construct_win_color (cat->color);
+ const gint clear = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
+
+ SetConsoleTextAttribute (GetStdHandle (STD_OUTPUT_HANDLE), color);
+ g_print ("%-20s", gst_debug_category_get_name (cat));
+ SetConsoleTextAttribute (GetStdHandle (STD_OUTPUT_HANDLE), clear);
+ g_print (" %1d %s ", gst_debug_category_get_threshold (cat),
+ gst_debug_level_get_name (gst_debug_category_get_threshold (cat)));
+ SetConsoleTextAttribute (GetStdHandle (STD_OUTPUT_HANDLE), color);
+ g_print ("%s", gst_debug_category_get_description (cat));
+ SetConsoleTextAttribute (GetStdHandle (STD_OUTPUT_HANDLE), clear);
+ g_print ("\n");
+#else /* G_OS_WIN32 */
+ gchar *color = gst_debug_construct_term_color (cat->color);
+
+ g_print ("%s%-20s\033[00m %1d %s %s%s\033[00m\n",
+ color,
+ gst_debug_category_get_name (cat),
+ gst_debug_category_get_threshold (cat),
+ gst_debug_level_get_name (gst_debug_category_get_threshold (cat)),
+ color, gst_debug_category_get_description (cat));
+ g_free (color);
+#endif /* G_OS_WIN32 */
+ } else {
+ g_print ("%-20s %1d %s %s\n", gst_debug_category_get_name (cat),
+ gst_debug_category_get_threshold (cat),
+ gst_debug_level_get_name (gst_debug_category_get_threshold (cat)),
+ gst_debug_category_get_description (cat));
+ }
+ walk = g_slist_next (walk);
+ }
+ g_slist_free (list);
+ g_print ("\n");
+}
+#endif
+
+#ifndef GST_DISABLE_OPTION_PARSING
+static gboolean
+parse_one_option (gint opt, const gchar * arg, GError ** err)
+{
+ switch (opt) {
+ case ARG_VERSION:
+ g_print ("GStreamer Core Library version %s\n", PACKAGE_VERSION);
+ exit (0);
+ case ARG_FATAL_WARNINGS:{
+ GLogLevelFlags fatal_mask;
+
+ fatal_mask = g_log_set_always_fatal (G_LOG_FATAL_MASK);
+ fatal_mask |= G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL;
+ g_log_set_always_fatal (fatal_mask);
+ break;
+ }
+#ifndef GST_DISABLE_GST_DEBUG
+ case ARG_DEBUG_LEVEL:{
+ GstDebugLevel tmp = GST_LEVEL_NONE;
+
+ tmp = (GstDebugLevel) strtol (arg, NULL, 0);
+ if (tmp >= 0 && tmp < GST_LEVEL_COUNT) {
+ gst_debug_set_default_threshold (tmp);
+ }
+ break;
+ }
+ case ARG_DEBUG:
+ parse_debug_list (arg);
+ break;
+ case ARG_DEBUG_NO_COLOR:
+ gst_debug_set_colored (FALSE);
+ break;
+ case ARG_DEBUG_DISABLE:
+ gst_debug_set_active (FALSE);
+ break;
+ case ARG_DEBUG_HELP:
+ gst_debug_help ();
+ exit (0);
+#endif
+ case ARG_PLUGIN_SPEW:
+ break;
+ case ARG_PLUGIN_PATH:
+#ifndef GST_DISABLE_REGISTRY
+ split_and_iterate (arg, G_SEARCHPATH_SEPARATOR_S, add_path_func, NULL);
+#endif /* GST_DISABLE_REGISTRY */
+ break;
+ case ARG_PLUGIN_LOAD:
+ split_and_iterate (arg, ",", prepare_for_load_plugin_func, NULL);
+ break;
+ case ARG_SEGTRAP_DISABLE:
+ _gst_disable_segtrap = TRUE;
+ break;
+ case ARG_REGISTRY_UPDATE_DISABLE:
+#ifndef GST_DISABLE_REGISTRY
+ _priv_gst_disable_registry_update = TRUE;
+#endif
+ break;
+ case ARG_REGISTRY_FORK_DISABLE:
+ gst_registry_fork_set_enabled (FALSE);
+ break;
+ default:
+ g_set_error (err, G_OPTION_ERROR, G_OPTION_ERROR_UNKNOWN_OPTION,
+ _("Unknown option"));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+parse_goption_arg (const gchar * opt,
+ const gchar * arg, gpointer data, GError ** err)
+{
+ static const struct
+ {
+ const gchar *opt;
+ int val;
+ } options[] = {
+ {
+ "--gst-version", ARG_VERSION}, {
+ "--gst-fatal-warnings", ARG_FATAL_WARNINGS},
+#ifndef GST_DISABLE_GST_DEBUG
+ {
+ "--gst-debug-level", ARG_DEBUG_LEVEL}, {
+ "--gst-debug", ARG_DEBUG}, {
+ "--gst-debug-disable", ARG_DEBUG_DISABLE}, {
+ "--gst-debug-no-color", ARG_DEBUG_NO_COLOR}, {
+ "--gst-debug-help", ARG_DEBUG_HELP},
+#endif
+ {
+ "--gst-plugin-spew", ARG_PLUGIN_SPEW}, {
+ "--gst-plugin-path", ARG_PLUGIN_PATH}, {
+ "--gst-plugin-load", ARG_PLUGIN_LOAD}, {
+ "--gst-disable-segtrap", ARG_SEGTRAP_DISABLE}, {
+ "--gst-disable-registry-update", ARG_REGISTRY_UPDATE_DISABLE}, {
+ "--gst-disable-registry-fork", ARG_REGISTRY_FORK_DISABLE}, {
+ NULL}
+ };
+ gint val = 0, n;
+
+ for (n = 0; options[n].opt; n++) {
+ if (!strcmp (opt, options[n].opt)) {
+ val = options[n].val;
+ break;
+ }
+ }
+
+ return parse_one_option (val, arg, err);
+}
+#endif
+
+/**
+ * gst_deinit:
+ *
+ * Clean up any resources created by GStreamer in gst_init().
+ *
+ * It is normally not needed to call this function in a normal application
+ * as the resources will automatically be freed when the program terminates.
+ * This function is therefore mostly used by testsuites and other memory
+ * profiling tools.
+ *
+ * After this call GStreamer (including this method) should not be used anymore.
+ */
+void
+gst_deinit (void)
+{
+ GstClock *clock;
+
+ GST_INFO ("deinitializing GStreamer");
+
+ if (gst_deinitialized) {
+ GST_DEBUG ("already deinitialized");
+ return;
+ }
+
+ g_slist_foreach (_priv_gst_preload_plugins, (GFunc) g_free, NULL);
+ g_slist_free (_priv_gst_preload_plugins);
+ _priv_gst_preload_plugins = NULL;
+
+#ifndef GST_DISABLE_REGISTRY
+ g_list_foreach (_priv_gst_plugin_paths, (GFunc) g_free, NULL);
+ g_list_free (_priv_gst_plugin_paths);
+ _priv_gst_plugin_paths = NULL;
+#endif
+
+ clock = gst_system_clock_obtain ();
+ gst_object_unref (clock);
+ gst_object_unref (clock);
+
+ _priv_gst_registry_cleanup ();
+
+ g_type_class_unref (g_type_class_peek (gst_object_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_pad_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_element_factory_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_element_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_type_find_factory_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_bin_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_bus_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_task_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_index_factory_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_object_flags_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_bin_flags_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_buffer_flags_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_buffer_copy_flags_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_bus_flags_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_bus_sync_reply_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_caps_flags_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_clock_type_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_clock_return_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_clock_entry_type_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_clock_flags_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_debug_graph_details_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_state_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_state_change_return_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_state_change_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_element_flags_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_core_error_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_library_error_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_plugin_dependency_flags_get_type
+ ()));
+ g_type_class_unref (g_type_class_peek (gst_parse_flags_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_resource_error_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_search_mode_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_stream_error_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_stream_status_type_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_structure_change_type_get_type
+ ()));
+ g_type_class_unref (g_type_class_peek (gst_event_type_flags_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_event_type_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_seek_type_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_seek_flags_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_qos_type_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_format_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_index_certainty_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_index_entry_type_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_index_lookup_method_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_assoc_flags_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_index_resolver_method_get_type
+ ()));
+ g_type_class_unref (g_type_class_peek (gst_index_flags_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_debug_level_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_debug_color_flags_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_iterator_result_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_iterator_item_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_message_type_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_mini_object_flags_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_pad_link_return_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_pad_link_check_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_flow_return_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_activate_mode_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_pad_direction_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_pad_flags_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_pad_presence_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_pad_template_flags_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_pipeline_flags_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_plugin_error_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_plugin_flags_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_rank_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_query_type_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_buffering_mode_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_tag_merge_mode_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_tag_flag_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_task_state_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_alloc_trace_flags_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_type_find_probability_get_type
+ ()));
+ g_type_class_unref (g_type_class_peek (gst_uri_type_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_parse_error_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_param_spec_fraction_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_progress_type_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_buffer_pool_flags_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_memory_flags_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_map_flags_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_caps_intersect_mode_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_probe_type_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_probe_return_get_type ()));
+ g_type_class_unref (g_type_class_peek (gst_segment_flags_get_type ()));
+
+ gst_deinitialized = TRUE;
+ GST_INFO ("deinitialized GStreamer");
+}
+
+/**
+ * gst_version:
+ * @major: (out): pointer to a guint to store the major version number
+ * @minor: (out): pointer to a guint to store the minor version number
+ * @micro: (out): pointer to a guint to store the micro version number
+ * @nano: (out): pointer to a guint to store the nano version number
+ *
+ * Gets the version number of the GStreamer library.
+ */
+void
+gst_version (guint * major, guint * minor, guint * micro, guint * nano)
+{
+ g_return_if_fail (major);
+ g_return_if_fail (minor);
+ g_return_if_fail (micro);
+ g_return_if_fail (nano);
+
+ *major = GST_VERSION_MAJOR;
+ *minor = GST_VERSION_MINOR;
+ *micro = GST_VERSION_MICRO;
+ *nano = GST_VERSION_NANO;
+}
+
+/**
+ * gst_version_string:
+ *
+ * This function returns a string that is useful for describing this version
+ * of GStreamer to the outside world: user agent strings, logging, ...
+ *
+ * Returns: (transfer full): a newly allocated string describing this version
+ * of GStreamer.
+ */
+
+gchar *
+gst_version_string (void)
+{
+ guint major, minor, micro, nano;
+
+ gst_version (&major, &minor, &micro, &nano);
+ if (nano == 0)
+ return g_strdup_printf ("GStreamer %d.%d.%d", major, minor, micro);
+ else if (nano == 1)
+ return g_strdup_printf ("GStreamer %d.%d.%d (GIT)", major, minor, micro);
+ else
+ return g_strdup_printf ("GStreamer %d.%d.%d (prerelease)", major, minor,
+ micro);
+}
+
+/**
+ * gst_segtrap_is_enabled:
+ *
+ * Some functions in the GStreamer core might install a custom SIGSEGV handler
+ * to better catch and report errors to the application. Currently this feature
+ * is enabled by default when loading plugins.
+ *
+ * Applications might want to disable this behaviour with the
+ * gst_segtrap_set_enabled() function. This is typically done if the application
+ * wants to install its own handler without GStreamer interfering.
+ *
+ * Returns: %TRUE if GStreamer is allowed to install a custom SIGSEGV handler.
+ *
+ * Since: 0.10.10
+ */
+gboolean
+gst_segtrap_is_enabled (void)
+{
+ /* yeps, it's enabled when it's not disabled */
+ return !_gst_disable_segtrap;
+}
+
+/**
+ * gst_segtrap_set_enabled:
+ * @enabled: whether a custom SIGSEGV handler should be installed.
+ *
+ * Applications might want to disable/enable the SIGSEGV handling of
+ * the GStreamer core. See gst_segtrap_is_enabled() for more information.
+ *
+ * Since: 0.10.10
+ */
+void
+gst_segtrap_set_enabled (gboolean enabled)
+{
+ _gst_disable_segtrap = !enabled;
+}
diff --git a/gst/gst.h b/gst/gst.h
new file mode 100644
index 0000000..da578d9
--- /dev/null
+++ b/gst/gst.h
@@ -0,0 +1,112 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2000 Wim Taymans <wtay@chello.be>
+ *
+ * gst.h: Main header for GStreamer, apps should include this
+ *
+ * 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_H__
+#define __GST_H__
+
+#ifndef GST_USE_UNSTABLE_API
+#warning "The GStreamer 0.11 API is still unstable and will change in future."
+#warning "Define GST_USE_UNSTABLE_API to avoid this warning."
+#endif
+
+#include <glib.h>
+
+#include <gst/glib-compat.h>
+
+#include <gst/gstenumtypes.h>
+#include <gst/gstversion.h>
+
+#include <gst/gstbin.h>
+#include <gst/gstbuffer.h>
+#include <gst/gstbufferlist.h>
+#include <gst/gstbufferpool.h>
+#include <gst/gstcaps.h>
+#include <gst/gstchildproxy.h>
+#include <gst/gstclock.h>
+#include <gst/gstdatetime.h>
+#include <gst/gstdebugutils.h>
+#include <gst/gstelement.h>
+#include <gst/gstelementmetadata.h>
+#include <gst/gsterror.h>
+#include <gst/gstevent.h>
+#include <gst/gstghostpad.h>
+#include <gst/gstindex.h>
+#include <gst/gstindexfactory.h>
+#include <gst/gstinfo.h>
+#include <gst/gstiterator.h>
+#include <gst/gstmarshal.h>
+#include <gst/gstmessage.h>
+#include <gst/gstmemory.h>
+#include <gst/gstminiobject.h>
+#include <gst/gstobject.h>
+#include <gst/gstpad.h>
+#include <gst/gstparamspecs.h>
+#include <gst/gstpipeline.h>
+#include <gst/gstplugin.h>
+#include <gst/gstpoll.h>
+#include <gst/gstpreset.h>
+#include <gst/gstquery.h>
+#include <gst/gstregistry.h>
+#include <gst/gstsegment.h>
+#include <gst/gststructure.h>
+#include <gst/gstsystemclock.h>
+#include <gst/gsttaglist.h>
+#include <gst/gsttagsetter.h>
+#include <gst/gsttask.h>
+#include <gst/gsttaskpool.h>
+#include <gst/gsttrace.h>
+#include <gst/gsttypefind.h>
+#include <gst/gsttypefindfactory.h>
+#include <gst/gsturi.h>
+#include <gst/gstutils.h>
+#include <gst/gstvalue.h>
+
+#include <gst/gstparse.h>
+
+/* API compatibility stuff */
+#include <gst/gstcompat.h>
+
+G_BEGIN_DECLS
+
+void gst_init (int *argc, char **argv[]);
+gboolean gst_init_check (int *argc, char **argv[],
+ GError ** err);
+gboolean gst_is_initialized (void);
+GOptionGroup * gst_init_get_option_group (void);
+void gst_deinit (void);
+
+void gst_version (guint *major, guint *minor,
+ guint *micro, guint *nano);
+gchar * gst_version_string (void);
+
+gboolean gst_segtrap_is_enabled (void);
+void gst_segtrap_set_enabled (gboolean enabled);
+
+gboolean gst_registry_fork_is_enabled (void);
+void gst_registry_fork_set_enabled (gboolean enabled);
+
+gboolean gst_update_registry (void);
+
+G_END_DECLS
+
+#endif /* __GST_H__ */
diff --git a/gst/gst_private.h b/gst/gst_private.h
new file mode 100644
index 0000000..3380c88
--- /dev/null
+++ b/gst/gst_private.h
@@ -0,0 +1,238 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2000 Wim Taymans <wtay@chello.be>
+ *
+ * gst_private.h: Private header for within libgst
+ *
+ * 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_PRIVATE_H__
+#define __GST_PRIVATE_H__
+
+#ifdef HAVE_CONFIG_H
+# ifndef GST_LICENSE /* don't include config.h twice, it has no guards */
+# include "config.h"
+# endif
+#endif
+
+/* This needs to be before glib.h, since it might be used in inline
+ * functions */
+extern const char g_log_domain_gstreamer[];
+
+#include <glib.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+/* Needed for GstRegistry * */
+#include "gstregistry.h"
+#include "gststructure.h"
+
+/* we need this in pretty much all files */
+#include "gstinfo.h"
+
+/* for the flags in the GstPluginDep structure below */
+#include "gstplugin.h"
+
+/* for the pad cache */
+#include "gstpad.h"
+
+/* for GstElement */
+#include "gstelement.h"
+
+G_BEGIN_DECLS
+
+/* used by gstparse.c and grammar.y */
+struct _GstParseContext {
+ GList * missing_elements;
+};
+
+/* used by gstplugin.c and gstregistrybinary.c */
+typedef struct {
+ /* details registered via gst_plugin_add_dependency() */
+ GstPluginDependencyFlags flags;
+ gchar **env_vars;
+ gchar **paths;
+ gchar **names;
+
+ /* information saved from the last time the plugin was loaded (-1 = unset) */
+ guint env_hash; /* hash of content of environment variables in env_vars */
+ guint stat_hash; /* hash of stat() on all relevant files and directories */
+} GstPluginDep;
+
+struct _GstPluginPrivate {
+ GList *deps; /* list of GstPluginDep structures */
+ GstStructure *cache_data;
+};
+
+gboolean priv_gst_plugin_loading_have_whitelist (void);
+
+guint32 priv_gst_plugin_loading_get_whitelist_hash (void);
+
+gboolean priv_gst_plugin_desc_is_whitelisted (GstPluginDesc * desc,
+ const gchar * filename);
+
+gboolean _priv_plugin_deps_env_vars_changed (GstPlugin * plugin);
+gboolean _priv_plugin_deps_files_changed (GstPlugin * plugin);
+
+gboolean _priv_gst_in_valgrind (void);
+
+/* init functions called from gst_init(). */
+void _priv_gst_quarks_initialize (void);
+void _priv_gst_buffer_initialize (void);
+void _priv_gst_buffer_list_initialize (void);
+void _priv_gst_structure_initialize (void);
+void _priv_gst_caps_initialize (void);
+void _priv_gst_event_initialize (void);
+void _priv_gst_format_initialize (void);
+void _priv_gst_message_initialize (void);
+void _priv_gst_memory_initialize (void);
+void _priv_gst_meta_initialize (void);
+void _priv_gst_plugin_initialize (void);
+void _priv_gst_query_initialize (void);
+void _priv_gst_tag_initialize (void);
+void _priv_gst_value_initialize (void);
+
+/* Private registry functions */
+gboolean _priv_gst_registry_remove_cache_plugins (GstRegistry *registry);
+void _priv_gst_registry_cleanup (void);
+gboolean _gst_plugin_loader_client_run (void);
+
+/* Used in GstBin for manual state handling */
+void _priv_gst_element_state_changed (GstElement *element, GstState oldstate,
+ GstState newstate, GstState pending);
+
+/* used in both gststructure.c and gstcaps.c; numbers are completely made up */
+#define STRUCTURE_ESTIMATED_STRING_LEN(s) (16 + gst_structure_n_fields(s) * 22)
+
+gboolean priv_gst_structure_append_to_gstring (const GstStructure * structure,
+ GString * s);
+/* registry cache backends */
+gboolean priv_gst_registry_binary_read_cache (GstRegistry * registry, const char *location);
+gboolean priv_gst_registry_binary_write_cache (GstRegistry * registry, const char *location);
+
+/* used in gstvalue.c and gststructure.c */
+#define GST_ASCII_IS_STRING(c) (g_ascii_isalnum((c)) || ((c) == '_') || \
+ ((c) == '-') || ((c) == '+') || ((c) == '/') || ((c) == ':') || \
+ ((c) == '.'))
+
+/* This is only meant for internal uses */
+gint priv_gst_date_time_compare (gconstpointer dt1, gconstpointer dt2);
+
+#ifndef GST_DISABLE_REGISTRY
+/* Secret variable to initialise gst without registry cache */
+extern gboolean _gst_disable_registry_cache;
+#endif
+
+/* provide inline gst_g_value_get_foo_unchecked(), used in gststructure.c */
+#define DEFINE_INLINE_G_VALUE_GET_UNCHECKED(ret_type,name_type,v_field) \
+static inline ret_type \
+gst_g_value_get_##name_type##_unchecked (const GValue *value) \
+{ \
+ return value->data[0].v_field; \
+}
+
+DEFINE_INLINE_G_VALUE_GET_UNCHECKED(gboolean,boolean,v_int)
+DEFINE_INLINE_G_VALUE_GET_UNCHECKED(gint,int,v_int)
+DEFINE_INLINE_G_VALUE_GET_UNCHECKED(guint,uint,v_uint)
+DEFINE_INLINE_G_VALUE_GET_UNCHECKED(gint64,int64,v_int64)
+DEFINE_INLINE_G_VALUE_GET_UNCHECKED(guint64,uint64,v_uint64)
+DEFINE_INLINE_G_VALUE_GET_UNCHECKED(gfloat,float,v_float)
+DEFINE_INLINE_G_VALUE_GET_UNCHECKED(gdouble,double,v_double)
+DEFINE_INLINE_G_VALUE_GET_UNCHECKED(const gchar *,string,v_pointer)
+
+
+/*** debugging categories *****************************************************/
+
+#ifndef GST_REMOVE_GST_DEBUG
+
+GST_EXPORT GstDebugCategory *GST_CAT_GST_INIT;
+GST_EXPORT GstDebugCategory *GST_CAT_AUTOPLUG; /* FIXME 0.11: remove? */
+GST_EXPORT GstDebugCategory *GST_CAT_AUTOPLUG_ATTEMPT; /* FIXME 0.11: remove? */
+GST_EXPORT GstDebugCategory *GST_CAT_PARENTAGE;
+GST_EXPORT GstDebugCategory *GST_CAT_STATES;
+GST_EXPORT GstDebugCategory *GST_CAT_SCHEDULING;
+GST_EXPORT GstDebugCategory *GST_CAT_BUFFER;
+GST_EXPORT GstDebugCategory *GST_CAT_BUFFER_LIST;
+GST_EXPORT GstDebugCategory *GST_CAT_BUS;
+GST_EXPORT GstDebugCategory *GST_CAT_CAPS;
+GST_EXPORT GstDebugCategory *GST_CAT_CLOCK;
+GST_EXPORT GstDebugCategory *GST_CAT_ELEMENT_PADS;
+GST_EXPORT GstDebugCategory *GST_CAT_PADS;
+GST_EXPORT GstDebugCategory *GST_CAT_PERFORMANCE;
+GST_EXPORT GstDebugCategory *GST_CAT_PIPELINE;
+GST_EXPORT GstDebugCategory *GST_CAT_PLUGIN_LOADING;
+GST_EXPORT GstDebugCategory *GST_CAT_PLUGIN_INFO;
+GST_EXPORT GstDebugCategory *GST_CAT_PROPERTIES;
+GST_EXPORT GstDebugCategory *GST_CAT_NEGOTIATION;
+GST_EXPORT GstDebugCategory *GST_CAT_REFCOUNTING;
+GST_EXPORT GstDebugCategory *GST_CAT_ERROR_SYSTEM;
+GST_EXPORT GstDebugCategory *GST_CAT_EVENT;
+GST_EXPORT GstDebugCategory *GST_CAT_MESSAGE;
+GST_EXPORT GstDebugCategory *GST_CAT_PARAMS;
+GST_EXPORT GstDebugCategory *GST_CAT_CALL_TRACE;
+GST_EXPORT GstDebugCategory *GST_CAT_SIGNAL;
+GST_EXPORT GstDebugCategory *GST_CAT_PROBE;
+GST_EXPORT GstDebugCategory *GST_CAT_REGISTRY;
+GST_EXPORT GstDebugCategory *GST_CAT_QOS;
+GST_EXPORT GstDebugCategory *GST_CAT_TYPES; /* FIXME 0.11: remove? */
+
+/* Categories that should be completely private to
+ * libgstreamer should be done like this: */
+#define GST_CAT_POLL _priv_GST_CAT_POLL
+extern GstDebugCategory *_priv_GST_CAT_POLL;
+
+#else
+
+#define GST_CAT_GST_INIT NULL
+#define GST_CAT_AUTOPLUG NULL
+#define GST_CAT_AUTOPLUG_ATTEMPT NULL
+#define GST_CAT_PARENTAGE NULL
+#define GST_CAT_STATES NULL
+#define GST_CAT_SCHEDULING NULL
+#define GST_CAT_DATAFLOW NULL
+#define GST_CAT_BUFFER NULL
+#define GST_CAT_BUFFER_LIST NULL
+#define GST_CAT_BUS NULL
+#define GST_CAT_CAPS NULL
+#define GST_CAT_CLOCK NULL
+#define GST_CAT_ELEMENT_PADS NULL
+#define GST_CAT_PADS NULL
+#define GST_CAT_PERFORMANCE NULL
+#define GST_CAT_PIPELINE NULL
+#define GST_CAT_PLUGIN_LOADING NULL
+#define GST_CAT_PLUGIN_INFO NULL
+#define GST_CAT_PROPERTIES NULL
+#define GST_CAT_NEGOTIATION NULL
+#define GST_CAT_REFCOUNTING NULL
+#define GST_CAT_ERROR_SYSTEM NULL
+#define GST_CAT_EVENT NULL
+#define GST_CAT_MESSAGE NULL
+#define GST_CAT_PARAMS NULL
+#define GST_CAT_CALL_TRACE NULL
+#define GST_CAT_SIGNAL NULL
+#define GST_CAT_PROBE NULL
+#define GST_CAT_REGISTRY NULL
+#define GST_CAT_QOS NULL
+#define GST_CAT_TYPES NULL
+#define GST_CAT_POLL NULL
+
+#endif
+
+
+G_END_DECLS
+#endif /* __GST_PRIVATE_H__ */
diff --git a/gst/gstatomicqueue.c b/gst/gstatomicqueue.c
new file mode 100644
index 0000000..5b21be6
--- /dev/null
+++ b/gst/gstatomicqueue.c
@@ -0,0 +1,419 @@
+/* GStreamer
+ * Copyright (C) 2009 Edward Hervey <bilboed@bilboed.com>
+ * 2011 Wim Taymans <wim.taymans@gmail.com>
+ *
+ * gstatomicqueue.c:
+ *
+ * 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.
+ */
+
+#include "gst_private.h"
+
+#include <string.h>
+
+#include <gst/gst.h>
+#include "gstatomicqueue.h"
+#include "glib-compat-private.h"
+
+/**
+ * SECTION:gstatomicqueue
+ * @title: GstAtomicQueue
+ * @short_description: An atomic queue implementation
+ *
+ * The #GstAtomicQueue object implements a queue that can be used from multiple
+ * threads without performing any blocking operations.
+ *
+ * Since: 0.10.33
+ */
+
+/* By default the queue uses 2 * sizeof(gpointer) * clp2 (max_items) of
+ * memory. clp2(x) is the next power of two >= than x.
+ *
+ * The queue can operate in low memory mode, in which it consumes almost
+ * half the memory at the expense of extra overhead in the readers. This
+ * is disabled by default because even without LOW_MEM mode, the memory
+ * consumption is still lower than a plain GList.
+ */
+#undef LOW_MEM
+
+typedef struct _GstAQueueMem GstAQueueMem;
+
+struct _GstAQueueMem
+{
+ gint size;
+ gpointer *array;
+ volatile gint head;
+ volatile gint tail;
+ GstAQueueMem *next;
+ GstAQueueMem *free;
+};
+
+static guint
+clp2 (guint n)
+{
+ guint res = 1;
+
+ while (res < n)
+ res <<= 1;
+
+ return res;
+}
+
+static GstAQueueMem *
+new_queue_mem (guint size, gint pos)
+{
+ GstAQueueMem *mem;
+
+ mem = g_new (GstAQueueMem, 1);
+
+ /* we keep the size as a mask for performance */
+ mem->size = clp2 (MAX (size, 16)) - 1;
+ mem->array = g_new0 (gpointer, mem->size + 1);
+ mem->head = pos;
+ mem->tail = pos;
+ mem->next = NULL;
+ mem->free = NULL;
+
+ return mem;
+}
+
+static void
+free_queue_mem (GstAQueueMem * mem)
+{
+ g_free (mem->array);
+ g_free (mem);
+}
+
+struct _GstAtomicQueue
+{
+ volatile gint refcount;
+#ifdef LOW_MEM
+ gint num_readers;
+#endif
+ GstAQueueMem *head_mem;
+ GstAQueueMem *tail_mem;
+ GstAQueueMem *free_list;
+};
+
+static void
+add_to_free_list (GstAtomicQueue * queue, GstAQueueMem * mem)
+{
+ do {
+ mem->free = g_atomic_pointer_get (&queue->free_list);
+ } while (!G_ATOMIC_POINTER_COMPARE_AND_EXCHANGE (&queue->free_list,
+ mem->free, mem));
+}
+
+static void
+clear_free_list (GstAtomicQueue * queue)
+{
+ GstAQueueMem *free_list;
+
+ /* take the free list and replace with NULL */
+ do {
+ free_list = g_atomic_pointer_get (&queue->free_list);
+ if (free_list == NULL)
+ return;
+ } while (!G_ATOMIC_POINTER_COMPARE_AND_EXCHANGE (&queue->free_list, free_list,
+ NULL));
+
+ while (free_list) {
+ GstAQueueMem *next = free_list->free;
+
+ free_queue_mem (free_list);
+
+ free_list = next;
+ }
+}
+
+/**
+ * gst_atomic_queue_new:
+ * @initial_size: initial queue size
+ *
+ * Create a new atomic queue instance. @initial_size will be rounded up to the
+ * nearest power of 2 and used as the initial size of the queue.
+ *
+ * Returns: a new #GstAtomicQueue
+ *
+ * Since: 0.10.33
+ */
+GstAtomicQueue *
+gst_atomic_queue_new (guint initial_size)
+{
+ GstAtomicQueue *queue;
+
+ queue = g_new (GstAtomicQueue, 1);
+
+ queue->refcount = 1;
+#ifdef LOW_MEM
+ queue->num_readers = 0;
+#endif
+ queue->head_mem = queue->tail_mem = new_queue_mem (initial_size, 0);
+ queue->free_list = NULL;
+
+ return queue;
+}
+
+/**
+ * gst_atomic_queue_ref:
+ * @queue: a #GstAtomicQueue
+ *
+ * Increase the refcount of @queue.
+ *
+ * Since: 0.10.33
+ */
+void
+gst_atomic_queue_ref (GstAtomicQueue * queue)
+{
+ g_return_if_fail (queue != NULL);
+
+ g_atomic_int_inc (&queue->refcount);
+}
+
+static void
+gst_atomic_queue_free (GstAtomicQueue * queue)
+{
+ free_queue_mem (queue->head_mem);
+ if (queue->head_mem != queue->tail_mem)
+ free_queue_mem (queue->tail_mem);
+ clear_free_list (queue);
+ g_free (queue);
+}
+
+/**
+ * gst_atomic_queue_unref:
+ * @queue: a #GstAtomicQueue
+ *
+ * Unref @queue and free the memory when the refcount reaches 0.
+ *
+ * Since: 0.10.33
+ */
+void
+gst_atomic_queue_unref (GstAtomicQueue * queue)
+{
+ g_return_if_fail (queue != NULL);
+
+ if (g_atomic_int_dec_and_test (&queue->refcount))
+ gst_atomic_queue_free (queue);
+}
+
+/**
+ * gst_atomic_queue_peek:
+ * @queue: a #GstAtomicQueue
+ *
+ * Peek the head element of the queue without removing it from the queue.
+ *
+ * Returns: the head element of @queue or NULL when the queue is empty.
+ *
+ * Since: 0.10.33
+ */
+gpointer
+gst_atomic_queue_peek (GstAtomicQueue * queue)
+{
+ GstAQueueMem *head_mem;
+ gint head, tail, size;
+
+ g_return_val_if_fail (queue != NULL, NULL);
+
+ while (TRUE) {
+ GstAQueueMem *next;
+
+ head_mem = g_atomic_pointer_get (&queue->head_mem);
+
+ head = g_atomic_int_get (&head_mem->head);
+ tail = g_atomic_int_get (&head_mem->tail);
+ size = head_mem->size;
+
+ /* when we are not empty, we can continue */
+ if (G_LIKELY (head != tail))
+ break;
+
+ /* else array empty, try to take next */
+ next = g_atomic_pointer_get (&head_mem->next);
+ if (next == NULL)
+ return NULL;
+
+ /* now we try to move the next array as the head memory. If we fail to do that,
+ * some other reader managed to do it first and we retry */
+ if (!G_ATOMIC_POINTER_COMPARE_AND_EXCHANGE (&queue->head_mem, head_mem,
+ next))
+ continue;
+
+ /* when we managed to swing the head pointer the old head is now
+ * useless and we add it to the freelist. We can't free the memory yet
+ * because we first need to make sure no reader is accessing it anymore. */
+ add_to_free_list (queue, head_mem);
+ }
+
+ return head_mem->array[head & size];
+}
+
+/**
+ * gst_atomic_queue_pop:
+ * @queue: a #GstAtomicQueue
+ *
+ * Get the head element of the queue.
+ *
+ * Returns: the head element of @queue or NULL when the queue is empty.
+ *
+ * Since: 0.10.33
+ */
+gpointer
+gst_atomic_queue_pop (GstAtomicQueue * queue)
+{
+ gpointer ret;
+ GstAQueueMem *head_mem;
+ gint head, tail, size;
+
+ g_return_val_if_fail (queue != NULL, NULL);
+
+#ifdef LOW_MEM
+ g_atomic_int_inc (&queue->num_readers);
+#endif
+
+ do {
+ while (TRUE) {
+ GstAQueueMem *next;
+
+ head_mem = g_atomic_pointer_get (&queue->head_mem);
+
+ head = g_atomic_int_get (&head_mem->head);
+ tail = g_atomic_int_get (&head_mem->tail);
+ size = head_mem->size;
+
+ /* when we are not empty, we can continue */
+ if (G_LIKELY (head != tail))
+ break;
+
+ /* else array empty, try to take next */
+ next = g_atomic_pointer_get (&head_mem->next);
+ if (next == NULL)
+ return NULL;
+
+ /* now we try to move the next array as the head memory. If we fail to do that,
+ * some other reader managed to do it first and we retry */
+ if (!G_ATOMIC_POINTER_COMPARE_AND_EXCHANGE (&queue->head_mem, head_mem,
+ next))
+ continue;
+
+ /* when we managed to swing the head pointer the old head is now
+ * useless and we add it to the freelist. We can't free the memory yet
+ * because we first need to make sure no reader is accessing it anymore. */
+ add_to_free_list (queue, head_mem);
+ }
+
+ ret = head_mem->array[head & size];
+ } while (!g_atomic_int_compare_and_exchange (&head_mem->head, head,
+ head + 1));
+
+#ifdef LOW_MEM
+ /* decrement number of readers, when we reach 0 readers we can be sure that
+ * none is accessing the memory in the free list and we can try to clean up */
+ if (g_atomic_int_dec_and_test (&queue->num_readers))
+ clear_free_list (queue);
+#endif
+
+ return ret;
+}
+
+/**
+ * gst_atomic_queue_push:
+ * @queue: a #GstAtomicQueue
+ * @data: the data
+ *
+ * Append @data to the tail of the queue.
+ *
+ * Since: 0.10.33
+ */
+void
+gst_atomic_queue_push (GstAtomicQueue * queue, gpointer data)
+{
+ GstAQueueMem *tail_mem;
+ gint head, tail, size;
+
+ g_return_if_fail (queue != NULL);
+
+ do {
+ while (TRUE) {
+ GstAQueueMem *mem;
+
+ tail_mem = g_atomic_pointer_get (&queue->tail_mem);
+ head = g_atomic_int_get (&tail_mem->head);
+ tail = g_atomic_int_get (&tail_mem->tail);
+ size = tail_mem->size;
+
+ /* we're not full, continue */
+ if (tail - head <= size)
+ break;
+
+ /* else we need to grow the array, we store a mask so we have to add 1 */
+ mem = new_queue_mem ((size << 1) + 1, tail);
+
+ /* try to make our new array visible to other writers */
+ if (!G_ATOMIC_POINTER_COMPARE_AND_EXCHANGE (&queue->tail_mem, tail_mem,
+ mem)) {
+ /* we tried to swap the new writer array but something changed. This is
+ * because some other writer beat us to it, we free our memory and try
+ * again */
+ free_queue_mem (mem);
+ continue;
+ }
+ /* make sure that readers can find our new array as well. The one who
+ * manages to swap the pointer is the only one who can set the next
+ * pointer to the new array */
+ g_atomic_pointer_set (&tail_mem->next, mem);
+ }
+ } while (!g_atomic_int_compare_and_exchange (&tail_mem->tail, tail,
+ tail + 1));
+
+ tail_mem->array[tail & size] = data;
+}
+
+/**
+ * gst_atomic_queue_length:
+ * @queue: a #GstAtomicQueue
+ *
+ * Get the amount of items in the queue.
+ *
+ * Returns: the number of elements in the queue.
+ *
+ * Since: 0.10.33
+ */
+guint
+gst_atomic_queue_length (GstAtomicQueue * queue)
+{
+ GstAQueueMem *head_mem, *tail_mem;
+ gint head, tail;
+
+ g_return_val_if_fail (queue != NULL, 0);
+
+#ifdef LOW_MEM
+ g_atomic_int_inc (&queue->num_readers);
+#endif
+
+ head_mem = g_atomic_pointer_get (&queue->head_mem);
+ head = g_atomic_int_get (&head_mem->head);
+
+ tail_mem = g_atomic_pointer_get (&queue->tail_mem);
+ tail = g_atomic_int_get (&tail_mem->tail);
+
+#ifdef LOW_MEM
+ if (g_atomic_int_dec_and_test (&queue->num_readers))
+ clear_free_list (queue);
+#endif
+
+ return tail - head;
+}
diff --git a/gst/gstatomicqueue.h b/gst/gstatomicqueue.h
new file mode 100644
index 0000000..6d71010
--- /dev/null
+++ b/gst/gstatomicqueue.h
@@ -0,0 +1,55 @@
+/* GStreamer
+ * Copyright (C) 2009-2010 Edward Hervey <bilboed@bilboed.com>
+ * (C) 2011 Wim Taymans <wim.taymans@gmail.com>
+ *
+ * gstatomicqueue.h:
+ *
+ * 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.
+ */
+
+#include <glib.h>
+
+#ifndef __GST_ATOMIC_QUEUE_H__
+#define __GST_ATOMIC_QUEUE_H__
+
+G_BEGIN_DECLS
+
+/**
+ * GstAtomicQueue:
+ *
+ * Opaque atomic data queue.
+ *
+ * Use the acessor functions to get the stored values.
+ *
+ * Since: 0.10.33
+ */
+typedef struct _GstAtomicQueue GstAtomicQueue;
+
+
+GstAtomicQueue * gst_atomic_queue_new (guint initial_size);
+
+void gst_atomic_queue_ref (GstAtomicQueue * queue);
+void gst_atomic_queue_unref (GstAtomicQueue * queue);
+
+void gst_atomic_queue_push (GstAtomicQueue* queue, gpointer data);
+gpointer gst_atomic_queue_pop (GstAtomicQueue* queue);
+gpointer gst_atomic_queue_peek (GstAtomicQueue* queue);
+
+guint gst_atomic_queue_length (GstAtomicQueue * queue);
+
+G_END_DECLS
+
+#endif /* __GST_ATOMIC_QUEUE_H__ */
diff --git a/gst/gstbin.c b/gst/gstbin.c
new file mode 100644
index 0000000..1eebc0f
--- /dev/null
+++ b/gst/gstbin.c
@@ -0,0 +1,3889 @@
+/* GStreamer
+ *
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2004 Wim Taymans <wim.taymans@gmail.com>
+ *
+ * gstbin.c: GstBin container object and support code
+ *
+ * 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.
+ *
+ * MT safe.
+ */
+
+/**
+ * SECTION:gstbin
+ * @short_description: Base class and element that can contain other elements
+ *
+ * #GstBin is an element that can contain other #GstElement, allowing them to be
+ * managed as a group.
+ * Pads from the child elements can be ghosted to the bin, see #GstGhostPad.
+ * This makes the bin look like any other elements and enables creation of
+ * higher-level abstraction elements.
+ *
+ * A new #GstBin is created with gst_bin_new(). Use a #GstPipeline instead if you
+ * want to create a toplevel bin because a normal bin doesn't have a bus or
+ * handle clock distribution of its own.
+ *
+ * After the bin has been created you will typically add elements to it with
+ * gst_bin_add(). You can remove elements with gst_bin_remove().
+ *
+ * An element can be retrieved from a bin with gst_bin_get_by_name(), using the
+ * elements name. gst_bin_get_by_name_recurse_up() is mainly used for internal
+ * purposes and will query the parent bins when the element is not found in the
+ * current bin.
+ *
+ * An iterator of elements in a bin can be retrieved with
+ * gst_bin_iterate_elements(). Various other iterators exist to retrieve the
+ * elements in a bin.
+ *
+ * gst_object_unref() is used to drop your reference to the bin.
+ *
+ * The #GstBin::element-added signal is fired whenever a new element is added to
+ * the bin. Likewise the #GstBin::element-removed signal is fired whenever an
+ * element is removed from the bin.
+ *
+ * <refsect2><title>Notes</title>
+ * <para>
+ * A #GstBin internally intercepts every #GstMessage posted by its children and
+ * implements the following default behaviour for each of them:
+ * <variablelist>
+ * <varlistentry>
+ * <term>GST_MESSAGE_EOS</term>
+ * <listitem><para>This message is only posted by sinks in the PLAYING
+ * state. If all sinks posted the EOS message, this bin will post and EOS
+ * message upwards.</para></listitem>
+ * </varlistentry>
+ * <varlistentry>
+ * <term>GST_MESSAGE_SEGMENT_START</term>
+ * <listitem><para>just collected and never forwarded upwards.
+ * The messages are used to decide when all elements have completed playback
+ * of their segment.</para></listitem>
+ * </varlistentry>
+ * <varlistentry>
+ * <term>GST_MESSAGE_SEGMENT_DONE</term>
+ * <listitem><para> Is posted by #GstBin when all elements that posted
+ * a SEGMENT_START have posted a SEGMENT_DONE.</para></listitem>
+ * </varlistentry>
+ * <varlistentry>
+ * <term>GST_MESSAGE_DURATION</term>
+ * <listitem><para> Is posted by an element that detected a change
+ * in the stream duration. The default bin behaviour is to clear any
+ * cached duration values so that the next duration query will perform
+ * a full duration recalculation. The duration change is posted to the
+ * application so that it can refetch the new duration with a duration
+ * query. Note that these messages can be posted before the bin is
+ * prerolled, in which case the duration query might fail.
+ * </para></listitem>
+ * </varlistentry>
+ * <varlistentry>
+ * <term>GST_MESSAGE_CLOCK_LOST</term>
+ * <listitem><para> This message is posted by an element when it
+ * can no longer provide a clock. The default bin behaviour is to
+ * check if the lost clock was the one provided by the bin. If so and
+ * the bin is currently in the PLAYING state, the message is forwarded to
+ * the bin parent.
+ * This message is also generated when a clock provider is removed from
+ * the bin. If this message is received by the application, it should
+ * PAUSE the pipeline and set it back to PLAYING to force a new clock
+ * distribution.
+ * </para></listitem>
+ * </varlistentry>
+ * <varlistentry>
+ * <term>GST_MESSAGE_CLOCK_PROVIDE</term>
+ * <listitem><para> This message is generated when an element
+ * can provide a clock. This mostly happens when a new clock
+ * provider is added to the bin. The default behaviour of the bin is to
+ * mark the currently selected clock as dirty, which will perform a clock
+ * recalculation the next time the bin is asked to provide a clock.
+ * This message is never sent tot the application but is forwarded to
+ * the parent of the bin.
+ * </para></listitem>
+ * </varlistentry>
+ * <varlistentry>
+ * <term>OTHERS</term>
+ * <listitem><para> posted upwards.</para></listitem>
+ * </varlistentry>
+ * </variablelist>
+ *
+ *
+ * A #GstBin implements the following default behaviour for answering to a
+ * #GstQuery:
+ * <variablelist>
+ * <varlistentry>
+ * <term>GST_QUERY_DURATION</term>
+ * <listitem><para>If the query has been asked before with the same format
+ * and the bin is a toplevel bin (ie. has no parent),
+ * use the cached previous value. If no previous value was cached, the
+ * query is sent to all sink elements in the bin and the MAXIMUM of all
+ * values is returned. If the bin is a toplevel bin the value is cached.
+ * If no sinks are available in the bin, the query fails.
+ * </para></listitem>
+ * </varlistentry>
+ * <varlistentry>
+ * <term>GST_QUERY_POSITION</term>
+ * <listitem><para>The query is sent to all sink elements in the bin and the
+ * MAXIMUM of all values is returned. If no sinks are available in the bin,
+ * the query fails.
+ * </para></listitem>
+ * </varlistentry>
+ * <varlistentry>
+ * <term>OTHERS</term>
+ * <listitem><para>the query is forwarded to all sink elements, the result
+ * of the first sink that answers the query successfully is returned. If no
+ * sink is in the bin, the query fails.</para></listitem>
+ * </varlistentry>
+ * </variablelist>
+ *
+ * A #GstBin will by default forward any event sent to it to all sink elements.
+ * If all the sinks return TRUE, the bin will also return TRUE, else FALSE is
+ * returned. If no sinks are in the bin, the event handler will return TRUE.
+ *
+ * </para>
+ * </refsect2>
+ *
+ * Last reviewed on 2006-04-28 (0.10.6)
+ */
+
+#include "gst_private.h"
+
+#include "gstevent.h"
+#include "gstbin.h"
+#include "gstmarshal.h"
+#include "gstinfo.h"
+#include "gsterror.h"
+
+#include "gstindex.h"
+#include "gstindexfactory.h"
+#include "gstutils.h"
+#include "gstchildproxy.h"
+
+/* latency is by default enabled now.
+ * live-preroll and no-live-preroll in the environment var GST_COMPAT
+ * to enables or disable it respectively.
+ */
+static gboolean enable_latency = TRUE;
+
+GST_DEBUG_CATEGORY_STATIC (bin_debug);
+#define GST_CAT_DEFAULT bin_debug
+
+/* a bin is toplevel if it has no parent or when it is configured to behave like
+ * a toplevel bin */
+#define BIN_IS_TOPLEVEL(bin) ((GST_OBJECT_PARENT (bin) == NULL) || bin->priv->asynchandling)
+
+#define GST_BIN_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_BIN, GstBinPrivate))
+
+struct _GstBinPrivate
+{
+ gboolean asynchandling;
+ /* if we get an ASYNC_DONE message from ourselves, this means that the
+ * subclass will simulate ASYNC behaviour without having ASYNC children. When
+ * such an ASYNC_DONE message is posted while we are doing a state change, we
+ * have to process the message after finishing the state change even when no
+ * child returned GST_STATE_CHANGE_ASYNC. */
+ gboolean pending_async_done;
+
+ guint32 structure_cookie;
+
+ /* cached index */
+ GstIndex *index;
+ /* forward messages from our children */
+ gboolean message_forward;
+
+ gboolean posted_eos;
+};
+
+typedef struct
+{
+ GstBin *bin;
+ guint32 cookie;
+ GstState pending;
+} BinContinueData;
+
+static void gst_bin_dispose (GObject * object);
+
+static void gst_bin_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+static void gst_bin_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
+
+static GstStateChangeReturn gst_bin_change_state_func (GstElement * element,
+ GstStateChange transition);
+static void gst_bin_state_changed (GstElement * element, GstState oldstate,
+ GstState newstate, GstState pending);
+static GstStateChangeReturn gst_bin_get_state_func (GstElement * element,
+ GstState * state, GstState * pending, GstClockTime timeout);
+static void bin_handle_async_done (GstBin * bin, GstStateChangeReturn ret,
+ gboolean flag_pending, gboolean reset_time);
+static void bin_handle_async_start (GstBin * bin);
+static void bin_push_state_continue (BinContinueData * data);
+static void bin_do_eos (GstBin * bin);
+
+static gboolean gst_bin_add_func (GstBin * bin, GstElement * element);
+static gboolean gst_bin_remove_func (GstBin * bin, GstElement * element);
+
+static void gst_bin_set_index_func (GstElement * element, GstIndex * index);
+static GstIndex *gst_bin_get_index_func (GstElement * element);
+
+static GstClock *gst_bin_provide_clock_func (GstElement * element);
+static gboolean gst_bin_set_clock_func (GstElement * element, GstClock * clock);
+
+static void gst_bin_handle_message_func (GstBin * bin, GstMessage * message);
+static gboolean gst_bin_send_event (GstElement * element, GstEvent * event);
+static GstBusSyncReply bin_bus_handler (GstBus * bus,
+ GstMessage * message, GstBin * bin);
+static gboolean gst_bin_query (GstElement * element, GstQuery * query);
+
+static gboolean gst_bin_do_latency_func (GstBin * bin);
+
+static void bin_remove_messages (GstBin * bin, GstObject * src,
+ GstMessageType types);
+static void gst_bin_continue_func (BinContinueData * data);
+static gint bin_element_is_sink (GstElement * child, GstBin * bin);
+static gint bin_element_is_src (GstElement * child, GstBin * bin);
+
+static GstIterator *gst_bin_sort_iterator_new (GstBin * bin);
+
+/* Bin signals and properties */
+enum
+{
+ ELEMENT_ADDED,
+ ELEMENT_REMOVED,
+ DO_LATENCY,
+ LAST_SIGNAL
+};
+
+#define DEFAULT_ASYNC_HANDLING FALSE
+#define DEFAULT_MESSAGE_FORWARD FALSE
+
+enum
+{
+ PROP_0,
+ PROP_ASYNC_HANDLING,
+ PROP_MESSAGE_FORWARD,
+ PROP_LAST
+};
+
+static void gst_bin_child_proxy_init (gpointer g_iface, gpointer iface_data);
+
+static guint gst_bin_signals[LAST_SIGNAL] = { 0 };
+
+#define _do_init \
+{ \
+ const gchar *compat; \
+ static const GInterfaceInfo iface_info = { \
+ gst_bin_child_proxy_init, \
+ NULL, \
+ NULL}; \
+ \
+ g_type_add_interface_static (g_define_type_id, GST_TYPE_CHILD_PROXY, &iface_info); \
+ \
+ GST_DEBUG_CATEGORY_INIT (bin_debug, "bin", GST_DEBUG_BOLD, \
+ "debugging info for the 'bin' container element"); \
+ \
+ /* compatibility stuff */ \
+ compat = g_getenv ("GST_COMPAT"); \
+ if (compat != NULL) { \
+ if (strstr (compat, "no-live-preroll")) \
+ enable_latency = FALSE; \
+ else if (strstr (compat, "live-preroll")) \
+ enable_latency = TRUE; \
+ } \
+}
+
+#define gst_bin_parent_class parent_class
+G_DEFINE_TYPE_WITH_CODE (GstBin, gst_bin, GST_TYPE_ELEMENT, _do_init);
+
+static GstObject *
+gst_bin_child_proxy_get_child_by_index (GstChildProxy * child_proxy,
+ guint index)
+{
+ GstObject *res;
+ GstBin *bin;
+
+ bin = GST_BIN_CAST (child_proxy);
+
+ GST_OBJECT_LOCK (bin);
+ if ((res = g_list_nth_data (bin->children, index)))
+ gst_object_ref (res);
+ GST_OBJECT_UNLOCK (bin);
+
+ return res;
+}
+
+static guint
+gst_bin_child_proxy_get_children_count (GstChildProxy * child_proxy)
+{
+ guint num;
+ GstBin *bin;
+
+ bin = GST_BIN_CAST (child_proxy);
+
+ GST_OBJECT_LOCK (bin);
+ num = bin->numchildren;
+ GST_OBJECT_UNLOCK (bin);
+
+ return num;
+}
+
+static void
+gst_bin_child_proxy_init (gpointer g_iface, gpointer iface_data)
+{
+ GstChildProxyInterface *iface = g_iface;
+
+ iface->get_children_count = gst_bin_child_proxy_get_children_count;
+ iface->get_child_by_index = gst_bin_child_proxy_get_child_by_index;
+}
+
+static gboolean
+_gst_boolean_accumulator (GSignalInvocationHint * ihint,
+ GValue * return_accu, const GValue * handler_return, gpointer dummy)
+{
+ gboolean myboolean;
+
+ myboolean = g_value_get_boolean (handler_return);
+ if (!(ihint->run_type & G_SIGNAL_RUN_CLEANUP))
+ g_value_set_boolean (return_accu, myboolean);
+
+ GST_DEBUG ("invocation %d, %d", ihint->run_type, myboolean);
+
+ /* stop emission */
+ return FALSE;
+}
+
+static void
+gst_bin_class_init (GstBinClass * klass)
+{
+ GObjectClass *gobject_class;
+ GstElementClass *gstelement_class;
+ GError *err;
+
+ gobject_class = (GObjectClass *) klass;
+ gstelement_class = (GstElementClass *) klass;
+
+ g_type_class_add_private (klass, sizeof (GstBinPrivate));
+
+ gobject_class->set_property = gst_bin_set_property;
+ gobject_class->get_property = gst_bin_get_property;
+
+ /**
+ * GstBin:async-handling
+ *
+ * If set to #TRUE, the bin will handle asynchronous state changes.
+ * This should be used only if the bin subclass is modifying the state
+ * of its children on its own.
+ *
+ * Since: 0.10.13
+ */
+ g_object_class_install_property (gobject_class, PROP_ASYNC_HANDLING,
+ g_param_spec_boolean ("async-handling", "Async Handling",
+ "The bin will handle Asynchronous state changes",
+ DEFAULT_ASYNC_HANDLING, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GstBin::element-added:
+ * @bin: the #GstBin
+ * @element: the #GstElement that was added to the bin
+ *
+ * Will be emitted after the element was added to the bin.
+ */
+ gst_bin_signals[ELEMENT_ADDED] =
+ g_signal_new ("element-added", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GstBinClass, element_added), NULL,
+ NULL, gst_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GST_TYPE_ELEMENT);
+ /**
+ * GstBin::element-removed:
+ * @bin: the #GstBin
+ * @element: the #GstElement that was removed from the bin
+ *
+ * Will be emitted after the element was removed from the bin.
+ */
+ gst_bin_signals[ELEMENT_REMOVED] =
+ g_signal_new ("element-removed", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GstBinClass, element_removed), NULL,
+ NULL, gst_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GST_TYPE_ELEMENT);
+ /**
+ * GstBin::do-latency:
+ * @bin: the #GstBin
+ *
+ * Will be emitted when the bin needs to perform latency calculations. This
+ * signal is only emited for toplevel bins or when async-handling is
+ * enabled.
+ *
+ * Only one signal handler is invoked. If no signals are connected, the
+ * default handler is invoked, which will query and distribute the lowest
+ * possible latency to all sinks.
+ *
+ * Connect to this signal if the default latency calculations are not
+ * sufficient, like when you need different latencies for different sinks in
+ * the same pipeline.
+ *
+ * Since: 0.10.22
+ */
+ gst_bin_signals[DO_LATENCY] =
+ g_signal_new ("do-latency", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstBinClass, do_latency),
+ _gst_boolean_accumulator, NULL, gst_marshal_BOOLEAN__VOID,
+ G_TYPE_BOOLEAN, 0, G_TYPE_NONE);
+
+ /**
+ * GstBin:message-forward
+ *
+ * Forward all children messages, even those that would normally be filtered by
+ * the bin. This can be interesting when one wants to be notified of the EOS
+ * state of individual elements, for example.
+ *
+ * The messages are converted to an ELEMENT message with the bin as the
+ * source. The structure of the message is named 'GstBinForwarded' and contains
+ * a field named 'message' of type GST_TYPE_MESSAGE that contains the original
+ * forwarded message.
+ *
+ * Since: 0.10.31
+ */
+ g_object_class_install_property (gobject_class, PROP_MESSAGE_FORWARD,
+ g_param_spec_boolean ("message-forward", "Message Forward",
+ "Forwards all children messages",
+ DEFAULT_MESSAGE_FORWARD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ gobject_class->dispose = gst_bin_dispose;
+
+ gst_element_class_set_metadata (gstelement_class, "Generic bin",
+ "Generic/Bin",
+ "Simple container object",
+ "Erik Walthinsen <omega@cse.ogi.edu>,"
+ "Wim Taymans <wim.taymans@gmail.com>");
+
+ gstelement_class->change_state =
+ GST_DEBUG_FUNCPTR (gst_bin_change_state_func);
+ gstelement_class->state_changed = GST_DEBUG_FUNCPTR (gst_bin_state_changed);
+ gstelement_class->get_state = GST_DEBUG_FUNCPTR (gst_bin_get_state_func);
+ gstelement_class->get_index = GST_DEBUG_FUNCPTR (gst_bin_get_index_func);
+ gstelement_class->set_index = GST_DEBUG_FUNCPTR (gst_bin_set_index_func);
+ gstelement_class->provide_clock =
+ GST_DEBUG_FUNCPTR (gst_bin_provide_clock_func);
+ gstelement_class->set_clock = GST_DEBUG_FUNCPTR (gst_bin_set_clock_func);
+
+ gstelement_class->send_event = GST_DEBUG_FUNCPTR (gst_bin_send_event);
+ gstelement_class->query = GST_DEBUG_FUNCPTR (gst_bin_query);
+
+ klass->add_element = GST_DEBUG_FUNCPTR (gst_bin_add_func);
+ klass->remove_element = GST_DEBUG_FUNCPTR (gst_bin_remove_func);
+ klass->handle_message = GST_DEBUG_FUNCPTR (gst_bin_handle_message_func);
+
+ klass->do_latency = GST_DEBUG_FUNCPTR (gst_bin_do_latency_func);
+
+ GST_DEBUG ("creating bin thread pool");
+ err = NULL;
+ klass->pool =
+ g_thread_pool_new ((GFunc) gst_bin_continue_func, NULL, -1, FALSE, &err);
+ if (err != NULL) {
+ g_critical ("could alloc threadpool %s", err->message);
+ }
+}
+
+static void
+gst_bin_init (GstBin * bin)
+{
+ GstBus *bus;
+
+ bin->numchildren = 0;
+ bin->children = NULL;
+ bin->children_cookie = 0;
+ bin->messages = NULL;
+ bin->provided_clock = NULL;
+ bin->clock_dirty = FALSE;
+
+ /* Set up a bus for listening to child elements */
+ bus = g_object_new (GST_TYPE_BUS, "enable-async", FALSE, NULL);
+ bin->child_bus = bus;
+ GST_DEBUG_OBJECT (bin, "using bus %" GST_PTR_FORMAT " to listen to children",
+ bus);
+ gst_bus_set_sync_handler (bus, (GstBusSyncHandler) bin_bus_handler, bin);
+
+ bin->priv = GST_BIN_GET_PRIVATE (bin);
+ bin->priv->asynchandling = DEFAULT_ASYNC_HANDLING;
+ bin->priv->structure_cookie = 0;
+ bin->priv->message_forward = DEFAULT_MESSAGE_FORWARD;
+}
+
+static void
+gst_bin_dispose (GObject * object)
+{
+ GstBin *bin = GST_BIN_CAST (object);
+ GstBus **child_bus_p = &bin->child_bus;
+ GstClock **provided_clock_p = &bin->provided_clock;
+ GstElement **clock_provider_p = &bin->clock_provider;
+ GstIndex **index_p = &bin->priv->index;
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_REFCOUNTING, object, "dispose");
+
+ GST_OBJECT_LOCK (object);
+ gst_object_replace ((GstObject **) child_bus_p, NULL);
+ gst_object_replace ((GstObject **) provided_clock_p, NULL);
+ gst_object_replace ((GstObject **) clock_provider_p, NULL);
+ gst_object_replace ((GstObject **) index_p, NULL);
+ bin_remove_messages (bin, NULL, GST_MESSAGE_ANY);
+ GST_OBJECT_UNLOCK (object);
+
+ while (bin->children) {
+ gst_bin_remove (bin, GST_ELEMENT_CAST (bin->children->data));
+ }
+ if (G_UNLIKELY (bin->children != NULL)) {
+ g_critical ("could not remove elements from bin '%s'",
+ GST_STR_NULL (GST_OBJECT_NAME (object)));
+ }
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+/**
+ * gst_bin_new:
+ * @name: the name of the new bin
+ *
+ * Creates a new bin with the given name.
+ *
+ * Returns: (transfer full): a new #GstBin
+ */
+GstElement *
+gst_bin_new (const gchar * name)
+{
+ return gst_element_factory_make ("bin", name);
+}
+
+static void
+gst_bin_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstBin *gstbin;
+
+ gstbin = GST_BIN_CAST (object);
+
+ switch (prop_id) {
+ case PROP_ASYNC_HANDLING:
+ GST_OBJECT_LOCK (gstbin);
+ gstbin->priv->asynchandling = g_value_get_boolean (value);
+ GST_OBJECT_UNLOCK (gstbin);
+ break;
+ case PROP_MESSAGE_FORWARD:
+ GST_OBJECT_LOCK (gstbin);
+ gstbin->priv->message_forward = g_value_get_boolean (value);
+ GST_OBJECT_UNLOCK (gstbin);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_bin_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GstBin *gstbin;
+
+ gstbin = GST_BIN_CAST (object);
+
+ switch (prop_id) {
+ case PROP_ASYNC_HANDLING:
+ GST_OBJECT_LOCK (gstbin);
+ g_value_set_boolean (value, gstbin->priv->asynchandling);
+ GST_OBJECT_UNLOCK (gstbin);
+ break;
+ case PROP_MESSAGE_FORWARD:
+ GST_OBJECT_LOCK (gstbin);
+ g_value_set_boolean (value, gstbin->priv->message_forward);
+ GST_OBJECT_UNLOCK (gstbin);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+/* return the cached index */
+static GstIndex *
+gst_bin_get_index_func (GstElement * element)
+{
+ GstBin *bin;
+ GstIndex *result;
+
+ bin = GST_BIN_CAST (element);
+
+ GST_OBJECT_LOCK (bin);
+ if ((result = bin->priv->index))
+ gst_object_ref (result);
+ GST_OBJECT_UNLOCK (bin);
+
+ return result;
+}
+
+/* set the index on all elements in this bin
+ *
+ * MT safe
+ */
+static void
+gst_bin_set_index_func (GstElement * element, GstIndex * index)
+{
+ GstBin *bin;
+ gboolean done;
+ GstIterator *it;
+ GstIndex *old;
+ GValue data = { 0, };
+
+ bin = GST_BIN_CAST (element);
+
+ GST_OBJECT_LOCK (bin);
+ old = bin->priv->index;
+ if (G_UNLIKELY (old == index))
+ goto was_set;
+ if (index)
+ gst_object_ref (index);
+ bin->priv->index = index;
+ GST_OBJECT_UNLOCK (bin);
+
+ if (old)
+ gst_object_unref (old);
+
+ it = gst_bin_iterate_elements (bin);
+
+ /* set the index on all elements in the bin */
+ done = FALSE;
+ while (!done) {
+ switch (gst_iterator_next (it, &data)) {
+ case GST_ITERATOR_OK:
+ {
+ GstElement *child = g_value_get_object (&data);
+
+ GST_DEBUG_OBJECT (bin, "setting index on '%s'",
+ GST_ELEMENT_NAME (child));
+ gst_element_set_index (child, index);
+
+ g_value_reset (&data);
+ break;
+ }
+ case GST_ITERATOR_RESYNC:
+ GST_DEBUG_OBJECT (bin, "iterator doing resync");
+ gst_iterator_resync (it);
+ break;
+ default:
+ case GST_ITERATOR_DONE:
+ GST_DEBUG_OBJECT (bin, "iterator done");
+ done = TRUE;
+ break;
+ }
+ }
+ g_value_unset (&data);
+ gst_iterator_free (it);
+ return;
+
+was_set:
+ {
+ GST_DEBUG_OBJECT (bin, "index was already set");
+ GST_OBJECT_UNLOCK (bin);
+ return;
+ }
+}
+
+/* set the clock on all elements in this bin
+ *
+ * MT safe
+ */
+static gboolean
+gst_bin_set_clock_func (GstElement * element, GstClock * clock)
+{
+ GstBin *bin;
+ gboolean done;
+ GstIterator *it;
+ gboolean res = TRUE;
+ GValue data = { 0, };
+
+ bin = GST_BIN_CAST (element);
+
+ it = gst_bin_iterate_elements (bin);
+
+ done = FALSE;
+ while (!done) {
+ switch (gst_iterator_next (it, &data)) {
+ case GST_ITERATOR_OK:
+ {
+ GstElement *child = g_value_get_object (&data);
+
+ res &= gst_element_set_clock (child, clock);
+
+ g_value_reset (&data);
+ break;
+ }
+ case GST_ITERATOR_RESYNC:
+ GST_DEBUG_OBJECT (bin, "iterator doing resync");
+ gst_iterator_resync (it);
+ res = TRUE;
+ break;
+ default:
+ case GST_ITERATOR_DONE:
+ GST_DEBUG_OBJECT (bin, "iterator done");
+ done = TRUE;
+ break;
+ }
+ }
+ g_value_unset (&data);
+ gst_iterator_free (it);
+
+ return res;
+}
+
+/* get the clock for this bin by asking all of the children in this bin
+ *
+ * The ref of the returned clock in increased so unref after usage.
+ *
+ * We loop the elements in state order and pick the last clock we can
+ * get. This makes sure we get a clock from the source.
+ *
+ * MT safe
+ */
+static GstClock *
+gst_bin_provide_clock_func (GstElement * element)
+{
+ GstClock *result = NULL;
+ GstElement *provider = NULL;
+ GstBin *bin;
+ GstIterator *it;
+ gboolean done;
+ GValue val = { 0, };
+ GstClock **provided_clock_p;
+ GstElement **clock_provider_p;
+
+ bin = GST_BIN_CAST (element);
+
+ GST_OBJECT_LOCK (bin);
+ if (!bin->clock_dirty)
+ goto not_dirty;
+
+ GST_DEBUG_OBJECT (bin, "finding new clock");
+
+ it = gst_bin_sort_iterator_new (bin);
+ GST_OBJECT_UNLOCK (bin);
+
+ done = FALSE;
+ while (!done) {
+ switch (gst_iterator_next (it, &val)) {
+ case GST_ITERATOR_OK:
+ {
+ GstElement *child = g_value_get_object (&val);
+ GstClock *clock;
+
+ clock = gst_element_provide_clock (child);
+ if (clock) {
+ GST_DEBUG_OBJECT (bin, "found candidate clock %p by element %s",
+ clock, GST_ELEMENT_NAME (child));
+ if (result) {
+ gst_object_unref (result);
+ gst_object_unref (provider);
+ }
+ result = clock;
+ provider = gst_object_ref (child);
+ }
+
+ g_value_reset (&val);
+ break;
+ }
+ case GST_ITERATOR_RESYNC:
+ gst_iterator_resync (it);
+ break;
+ default:
+ case GST_ITERATOR_DONE:
+ done = TRUE;
+ break;
+ }
+ }
+ g_value_unset (&val);
+ gst_iterator_free (it);
+
+ GST_OBJECT_LOCK (bin);
+ if (!bin->clock_dirty) {
+ if (provider)
+ gst_object_unref (provider);
+ if (result)
+ gst_object_unref (result);
+ result = NULL;
+
+ goto not_dirty;
+ }
+
+ provided_clock_p = &bin->provided_clock;
+ clock_provider_p = &bin->clock_provider;
+ gst_object_replace ((GstObject **) provided_clock_p, (GstObject *) result);
+ gst_object_replace ((GstObject **) clock_provider_p, (GstObject *) provider);
+ bin->clock_dirty = FALSE;
+ GST_DEBUG_OBJECT (bin,
+ "provided new clock %" GST_PTR_FORMAT " by provider %" GST_PTR_FORMAT,
+ result, provider);
+ /* Provider is not being returned to caller, just the result */
+ if (provider)
+ gst_object_unref (provider);
+ GST_OBJECT_UNLOCK (bin);
+
+ return result;
+
+not_dirty:
+ {
+ if ((result = bin->provided_clock))
+ gst_object_ref (result);
+ GST_DEBUG_OBJECT (bin, "returning old clock %p", result);
+ GST_OBJECT_UNLOCK (bin);
+
+ return result;
+ }
+}
+
+/*
+ * functions for manipulating cached messages
+ */
+typedef struct
+{
+ GstObject *src;
+ GstMessageType types;
+} MessageFind;
+
+/* check if a message is of given src and type */
+static gint
+message_check (GstMessage * message, MessageFind * target)
+{
+ gboolean eq = TRUE;
+
+ if (target->src)
+ eq &= GST_MESSAGE_SRC (message) == target->src;
+ if (target->types)
+ eq &= (GST_MESSAGE_TYPE (message) & target->types) != 0;
+ GST_LOG ("looking at message %p: %d", message, eq);
+
+ return (eq ? 0 : 1);
+}
+
+static GList *
+find_message (GstBin * bin, GstObject * src, GstMessageType types)
+{
+ GList *result;
+ MessageFind find;
+
+ find.src = src;
+ find.types = types;
+
+ result = g_list_find_custom (bin->messages, &find,
+ (GCompareFunc) message_check);
+
+ if (result) {
+ GST_DEBUG_OBJECT (bin, "we found a message %p from %s matching types %08x",
+ result->data, GST_OBJECT_NAME (GST_MESSAGE_CAST (result->data)->src),
+ types);
+ } else {
+ GST_DEBUG_OBJECT (bin, "no message found matching types %08x", types);
+#ifndef GST_DISABLE_GST_DEBUG
+ {
+ guint i;
+
+ for (i = 0; i < 32; i++)
+ if (types & (1 << i))
+ GST_DEBUG_OBJECT (bin, " %s", gst_message_type_get_name (1 << i));
+ }
+#endif
+ }
+
+ return result;
+}
+
+/* with LOCK, returns TRUE if message had a valid SRC, takes ownership of
+ * the message.
+ *
+ * A message that is cached and has the same SRC and type is replaced
+ * by the given message.
+ */
+static gboolean
+bin_replace_message (GstBin * bin, GstMessage * message, GstMessageType types)
+{
+ GList *previous;
+ GstObject *src;
+ gboolean res = TRUE;
+
+ if ((src = GST_MESSAGE_SRC (message))) {
+ /* first find the previous message posted by this element */
+ if ((previous = find_message (bin, src, types))) {
+ GstMessage *previous_msg;
+
+ /* if we found a previous message, replace it */
+ previous_msg = previous->data;
+ previous->data = message;
+
+ GST_DEBUG_OBJECT (bin, "replace old message %s from %s with %s message",
+ GST_MESSAGE_TYPE_NAME (previous_msg), GST_ELEMENT_NAME (src),
+ GST_MESSAGE_TYPE_NAME (message));
+
+ gst_message_unref (previous_msg);
+ } else {
+ /* keep new message */
+ bin->messages = g_list_prepend (bin->messages, message);
+
+ GST_DEBUG_OBJECT (bin, "got new message %p, %s from %s",
+ message, GST_MESSAGE_TYPE_NAME (message), GST_ELEMENT_NAME (src));
+ }
+ } else {
+ GST_DEBUG_OBJECT (bin, "got message %s from (NULL), not processing",
+ GST_MESSAGE_TYPE_NAME (message));
+ res = FALSE;
+ gst_message_unref (message);
+ }
+ return res;
+}
+
+/* with LOCK. Remove all messages of given types */
+static void
+bin_remove_messages (GstBin * bin, GstObject * src, GstMessageType types)
+{
+ MessageFind find;
+ GList *walk, *next;
+
+ find.src = src;
+ find.types = types;
+
+ for (walk = bin->messages; walk; walk = next) {
+ GstMessage *message = (GstMessage *) walk->data;
+
+ next = g_list_next (walk);
+
+ if (message_check (message, &find) == 0) {
+ GST_DEBUG_OBJECT (GST_MESSAGE_SRC (message),
+ "deleting message %p of types 0x%08x", message, types);
+ bin->messages = g_list_delete_link (bin->messages, walk);
+ gst_message_unref (message);
+ } else {
+ GST_DEBUG_OBJECT (GST_MESSAGE_SRC (message),
+ "not deleting message %p of type 0x%08x", message,
+ GST_MESSAGE_TYPE (message));
+ }
+ }
+}
+
+
+/* Check if the bin is EOS. We do this by scanning all sinks and
+ * checking if they posted an EOS message.
+ *
+ * call with bin LOCK */
+static gboolean
+is_eos (GstBin * bin, guint32 * seqnum)
+{
+ gboolean result;
+ gint n_eos = 0;
+ GList *walk, *msgs;
+
+ result = TRUE;
+ for (walk = bin->children; walk; walk = g_list_next (walk)) {
+ GstElement *element;
+
+ element = GST_ELEMENT_CAST (walk->data);
+ if (bin_element_is_sink (element, bin) == 0) {
+ /* check if element posted EOS */
+ if ((msgs =
+ find_message (bin, GST_OBJECT_CAST (element), GST_MESSAGE_EOS))) {
+ GST_DEBUG ("sink '%s' posted EOS", GST_ELEMENT_NAME (element));
+ *seqnum = gst_message_get_seqnum (GST_MESSAGE_CAST (msgs->data));
+ n_eos++;
+ } else {
+ GST_DEBUG ("sink '%s' did not post EOS yet",
+ GST_ELEMENT_NAME (element));
+ result = FALSE;
+ break;
+ }
+ }
+ }
+ /* FIXME: Some tests (e.g. elements/capsfilter) use
+ * pipelines with a dangling sinkpad but no sink element.
+ * These tests assume that no EOS message is ever
+ * posted on the bus so let's keep that behaviour.
+ * In valid pipelines this doesn't make a difference.
+ */
+ return result && n_eos > 0;
+}
+
+static void
+unlink_pads (const GValue * item, gpointer user_data)
+{
+ GstPad *pad;
+ GstPad *peer;
+
+ pad = g_value_get_object (item);
+
+ if ((peer = gst_pad_get_peer (pad))) {
+ if (gst_pad_get_direction (pad) == GST_PAD_SRC)
+ gst_pad_unlink (pad, peer);
+ else
+ gst_pad_unlink (peer, pad);
+ gst_object_unref (peer);
+ }
+}
+
+/* vmethod that adds an element to a bin
+ *
+ * MT safe
+ */
+static gboolean
+gst_bin_add_func (GstBin * bin, GstElement * element)
+{
+ gchar *elem_name;
+ GstIterator *it;
+ gboolean is_sink, is_source;
+ GstMessage *clock_message = NULL, *async_message = NULL;
+ GstStateChangeReturn ret;
+
+ GST_DEBUG_OBJECT (bin, "element :%s", GST_ELEMENT_NAME (element));
+
+ /* we obviously can't add ourself to ourself */
+ if (G_UNLIKELY (GST_ELEMENT_CAST (element) == GST_ELEMENT_CAST (bin)))
+ goto adding_itself;
+
+ /* get the element name to make sure it is unique in this bin. */
+ GST_OBJECT_LOCK (element);
+ elem_name = g_strdup (GST_ELEMENT_NAME (element));
+ is_sink = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_IS_SINK);
+ is_source = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_IS_SOURCE);
+ GST_OBJECT_UNLOCK (element);
+
+ GST_OBJECT_LOCK (bin);
+
+ /* then check to see if the element's name is already taken in the bin,
+ * we can safely take the lock here. This check is probably bogus because
+ * you can safely change the element name after this check and before setting
+ * the object parent. The window is very small though... */
+ if (G_UNLIKELY (!gst_object_check_uniqueness (bin->children, elem_name)))
+ goto duplicate_name;
+
+ /* set the element's parent and add the element to the bin's list of children */
+ if (G_UNLIKELY (!gst_object_set_parent (GST_OBJECT_CAST (element),
+ GST_OBJECT_CAST (bin))))
+ goto had_parent;
+
+ /* if we add a sink we become a sink */
+ if (is_sink) {
+ GST_CAT_DEBUG_OBJECT (GST_CAT_PARENTAGE, bin, "element \"%s\" was sink",
+ elem_name);
+ GST_OBJECT_FLAG_SET (bin, GST_ELEMENT_IS_SINK);
+ }
+ if (is_source) {
+ GST_CAT_DEBUG_OBJECT (GST_CAT_PARENTAGE, bin, "element \"%s\" was source",
+ elem_name);
+ GST_OBJECT_FLAG_SET (bin, GST_ELEMENT_IS_SOURCE);
+ }
+ if (gst_element_provides_clock (element)) {
+ GST_DEBUG_OBJECT (bin, "element \"%s\" can provide a clock", elem_name);
+ clock_message =
+ gst_message_new_clock_provide (GST_OBJECT_CAST (element), NULL, TRUE);
+ }
+
+ bin->children = g_list_prepend (bin->children, element);
+ bin->numchildren++;
+ bin->children_cookie++;
+ bin->priv->structure_cookie++;
+
+ /* distribute the bus */
+ gst_element_set_bus (element, bin->child_bus);
+
+ /* propagate the current base_time, start_time and clock */
+ gst_element_set_base_time (element, GST_ELEMENT_CAST (bin)->base_time);
+ gst_element_set_start_time (element, GST_ELEMENT_START_TIME (bin));
+ /* it's possible that the element did not accept the clock but
+ * that is not important right now. When the pipeline goes to PLAYING,
+ * a new clock will be selected */
+ gst_element_set_clock (element, GST_ELEMENT_CLOCK (bin));
+ /* set the cached index on the children */
+ if (bin->priv->index)
+ gst_element_set_index (element, bin->priv->index);
+
+ ret = GST_STATE_RETURN (bin);
+ /* no need to update the state if we are in error */
+ if (ret == GST_STATE_CHANGE_FAILURE)
+ goto no_state_recalc;
+
+ /* update the bin state, the new element could have been an ASYNC or
+ * NO_PREROLL element */
+ ret = GST_STATE_RETURN (element);
+ GST_DEBUG_OBJECT (bin, "added %s element",
+ gst_element_state_change_return_get_name (ret));
+
+ switch (ret) {
+ case GST_STATE_CHANGE_ASYNC:
+ {
+ /* create message to track this aync element when it posts an async-done
+ * message */
+ async_message = gst_message_new_async_start (GST_OBJECT_CAST (element));
+ break;
+ }
+ case GST_STATE_CHANGE_NO_PREROLL:
+ /* ignore all async elements we might have and commit our state */
+ bin_handle_async_done (bin, ret, FALSE, FALSE);
+ break;
+ case GST_STATE_CHANGE_FAILURE:
+ break;
+ default:
+ break;
+ }
+
+no_state_recalc:
+ GST_OBJECT_UNLOCK (bin);
+
+ /* post the messages on the bus of the element so that the bin can handle
+ * them */
+ if (clock_message)
+ gst_element_post_message (GST_ELEMENT_CAST (element), clock_message);
+
+ if (async_message)
+ gst_element_post_message (GST_ELEMENT_CAST (element), async_message);
+
+ /* unlink all linked pads */
+ it = gst_element_iterate_pads (element);
+ gst_iterator_foreach (it, (GstIteratorForeachFunction) unlink_pads, NULL);
+ gst_iterator_free (it);
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_PARENTAGE, bin, "added element \"%s\"",
+ elem_name);
+ g_free (elem_name);
+
+ g_signal_emit (bin, gst_bin_signals[ELEMENT_ADDED], 0, element);
+ gst_child_proxy_child_added ((GstObject *) bin, (GstObject *) element);
+
+ return TRUE;
+
+ /* ERROR handling here */
+adding_itself:
+ {
+ GST_OBJECT_LOCK (bin);
+ g_warning ("Cannot add bin '%s' to itself", GST_ELEMENT_NAME (bin));
+ GST_OBJECT_UNLOCK (bin);
+ return FALSE;
+ }
+duplicate_name:
+ {
+ g_warning ("Name '%s' is not unique in bin '%s', not adding",
+ elem_name, GST_ELEMENT_NAME (bin));
+ GST_OBJECT_UNLOCK (bin);
+ g_free (elem_name);
+ return FALSE;
+ }
+had_parent:
+ {
+ g_warning ("Element '%s' already has parent", elem_name);
+ GST_OBJECT_UNLOCK (bin);
+ g_free (elem_name);
+ return FALSE;
+ }
+}
+
+/**
+ * gst_bin_add:
+ * @bin: a #GstBin
+ * @element: (transfer full): the #GstElement to add
+ *
+ * Adds the given element to the bin. Sets the element's parent, and thus
+ * takes ownership of the element. An element can only be added to one bin.
+ *
+ * If the element's pads are linked to other pads, the pads will be unlinked
+ * before the element is added to the bin.
+ *
+ * <note>
+ * When you add an element to an already-running pipeline, you will have to
+ * take care to set the state of the newly-added element to the desired
+ * state (usually PLAYING or PAUSED, same you set the pipeline to originally)
+ * with gst_element_set_state(), or use gst_element_sync_state_with_parent().
+ * The bin or pipeline will not take care of this for you.
+ * </note>
+ *
+ * MT safe.
+ *
+ * Returns: TRUE if the element could be added, FALSE if
+ * the bin does not want to accept the element.
+ */
+gboolean
+gst_bin_add (GstBin * bin, GstElement * element)
+{
+ GstBinClass *bclass;
+ gboolean result;
+
+ g_return_val_if_fail (GST_IS_BIN (bin), FALSE);
+ g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
+
+ bclass = GST_BIN_GET_CLASS (bin);
+
+ if (G_UNLIKELY (bclass->add_element == NULL))
+ goto no_function;
+
+ GST_CAT_DEBUG (GST_CAT_PARENTAGE, "adding element %s to bin %s",
+ GST_STR_NULL (GST_ELEMENT_NAME (element)),
+ GST_STR_NULL (GST_ELEMENT_NAME (bin)));
+
+ result = bclass->add_element (bin, element);
+
+ return result;
+
+ /* ERROR handling */
+no_function:
+ {
+ g_warning ("adding elements to bin '%s' is not supported",
+ GST_ELEMENT_NAME (bin));
+ return FALSE;
+ }
+}
+
+/* remove an element from the bin
+ *
+ * MT safe
+ */
+static gboolean
+gst_bin_remove_func (GstBin * bin, GstElement * element)
+{
+ gchar *elem_name;
+ GstIterator *it;
+ gboolean is_sink, is_source, othersink, othersource, found;
+ GstMessage *clock_message = NULL;
+ GstClock **provided_clock_p;
+ GstElement **clock_provider_p;
+ GList *walk, *next;
+ gboolean other_async, this_async, have_no_preroll;
+ GstStateChangeReturn ret;
+
+ GST_DEBUG_OBJECT (bin, "element :%s", GST_ELEMENT_NAME (element));
+
+ GST_OBJECT_LOCK (element);
+ /* Check if the element is already being removed and immediately
+ * return */
+ if (G_UNLIKELY (GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_UNPARENTING)))
+ goto already_removing;
+
+ GST_OBJECT_FLAG_SET (element, GST_ELEMENT_UNPARENTING);
+ /* grab element name so we can print it */
+ elem_name = g_strdup (GST_ELEMENT_NAME (element));
+ is_sink = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_IS_SINK);
+ is_source = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_IS_SOURCE);
+ GST_OBJECT_UNLOCK (element);
+
+ /* unlink all linked pads */
+ it = gst_element_iterate_pads (element);
+ gst_iterator_foreach (it, (GstIteratorForeachFunction) unlink_pads, NULL);
+ gst_iterator_free (it);
+
+ GST_OBJECT_LOCK (bin);
+ found = FALSE;
+ othersink = FALSE;
+ othersource = FALSE;
+ have_no_preroll = FALSE;
+ /* iterate the elements, we collect which ones are async and no_preroll. We
+ * also remove the element when we find it. */
+ for (walk = bin->children; walk; walk = next) {
+ GstElement *child = GST_ELEMENT_CAST (walk->data);
+
+ next = g_list_next (walk);
+
+ if (child == element) {
+ found = TRUE;
+ /* remove the element */
+ bin->children = g_list_delete_link (bin->children, walk);
+ } else {
+ gboolean child_sink, child_source;
+
+ GST_OBJECT_LOCK (child);
+ child_sink = GST_OBJECT_FLAG_IS_SET (child, GST_ELEMENT_IS_SINK);
+ child_source = GST_OBJECT_FLAG_IS_SET (child, GST_ELEMENT_IS_SOURCE);
+ /* when we remove a sink, check if there are other sinks. */
+ if (is_sink && !othersink && child_sink)
+ othersink = TRUE;
+ if (is_source && !othersource && child_source)
+ othersource = TRUE;
+ /* check if we have NO_PREROLL children */
+ if (GST_STATE_RETURN (child) == GST_STATE_CHANGE_NO_PREROLL)
+ have_no_preroll = TRUE;
+ GST_OBJECT_UNLOCK (child);
+ }
+ }
+
+ /* the element must have been in the bin's list of children */
+ if (G_UNLIKELY (!found))
+ goto not_in_bin;
+
+ /* we now removed the element from the list of elements, increment the cookie
+ * so that others can detect a change in the children list. */
+ bin->numchildren--;
+ bin->children_cookie++;
+ bin->priv->structure_cookie++;
+
+ if (is_sink && !othersink) {
+ /* we're not a sink anymore */
+ GST_DEBUG_OBJECT (bin, "we removed the last sink");
+ GST_OBJECT_FLAG_UNSET (bin, GST_ELEMENT_IS_SINK);
+ }
+ if (is_source && !othersource) {
+ /* we're not a source anymore */
+ GST_DEBUG_OBJECT (bin, "we removed the last source");
+ GST_OBJECT_FLAG_UNSET (bin, GST_ELEMENT_IS_SOURCE);
+ }
+
+
+ /* if the clock provider for this element is removed, we lost
+ * the clock as well, we need to inform the parent of this
+ * so that it can select a new clock */
+ if (bin->clock_provider == element) {
+ GST_DEBUG_OBJECT (bin, "element \"%s\" provided the clock", elem_name);
+ bin->clock_dirty = TRUE;
+ clock_message =
+ gst_message_new_clock_lost (GST_OBJECT_CAST (bin), bin->provided_clock);
+ provided_clock_p = &bin->provided_clock;
+ clock_provider_p = &bin->clock_provider;
+ gst_object_replace ((GstObject **) provided_clock_p, NULL);
+ gst_object_replace ((GstObject **) clock_provider_p, NULL);
+ }
+
+ /* remove messages for the element, if there was a pending ASYNC_START
+ * message we must see if removing the element caused the bin to lose its
+ * async state. */
+ this_async = FALSE;
+ other_async = FALSE;
+ for (walk = bin->messages; walk; walk = next) {
+ GstMessage *message = (GstMessage *) walk->data;
+ GstElement *src = GST_ELEMENT_CAST (GST_MESSAGE_SRC (message));
+ gboolean remove;
+
+ next = g_list_next (walk);
+ remove = FALSE;
+
+ switch (GST_MESSAGE_TYPE (message)) {
+ case GST_MESSAGE_ASYNC_START:
+ if (src == element)
+ this_async = TRUE;
+ else
+ other_async = TRUE;
+
+ GST_DEBUG_OBJECT (src, "looking at message %p", message);
+ break;
+ case GST_MESSAGE_STRUCTURE_CHANGE:
+ {
+ GstElement *owner;
+
+ GST_DEBUG_OBJECT (src, "looking at structure change message %p",
+ message);
+ /* it's unlikely that this message is still in the list of messages
+ * because this would mean that a link/unlink is busy in another thread
+ * while we remove the element. We still have to remove the message
+ * because we might not receive the done message anymore when the element
+ * is removed from the bin. */
+ gst_message_parse_structure_change (message, NULL, &owner, NULL);
+ if (owner == element)
+ remove = TRUE;
+ break;
+ }
+ default:
+ break;
+ }
+ if (src == element)
+ remove = TRUE;
+
+ if (remove) {
+ /* delete all message types */
+ GST_DEBUG_OBJECT (src, "deleting message %p of element \"%s\"",
+ message, elem_name);
+ bin->messages = g_list_delete_link (bin->messages, walk);
+ gst_message_unref (message);
+ }
+ }
+
+ /* get last return */
+ ret = GST_STATE_RETURN (bin);
+
+ /* no need to update the state if we are in error */
+ if (ret == GST_STATE_CHANGE_FAILURE)
+ goto no_state_recalc;
+
+ if (!other_async && this_async) {
+ /* all other elements were not async and we removed the async one,
+ * handle the async-done case because we are not async anymore now. */
+ GST_DEBUG_OBJECT (bin,
+ "we removed the last async element, have no_preroll %d",
+ have_no_preroll);
+
+ /* the current state return of the bin depends on if there are no_preroll
+ * elements in the pipeline or not */
+ if (have_no_preroll)
+ ret = GST_STATE_CHANGE_NO_PREROLL;
+ else
+ ret = GST_STATE_CHANGE_SUCCESS;
+
+ bin_handle_async_done (bin, ret, FALSE, FALSE);
+ } else {
+ GST_DEBUG_OBJECT (bin,
+ "recalc state preroll: %d, other async: %d, this async %d",
+ have_no_preroll, other_async, this_async);
+
+ if (have_no_preroll) {
+ ret = GST_STATE_CHANGE_NO_PREROLL;
+ } else if (other_async) {
+ /* there are other async elements and we were not doing an async state
+ * change, change our pending state and go async */
+ if (GST_STATE_PENDING (bin) == GST_STATE_VOID_PENDING) {
+ GST_STATE_NEXT (bin) = GST_STATE (bin);
+ GST_STATE_PENDING (bin) = GST_STATE (bin);
+ }
+ ret = GST_STATE_CHANGE_ASYNC;
+ }
+ GST_STATE_RETURN (bin) = ret;
+ }
+no_state_recalc:
+ GST_OBJECT_UNLOCK (bin);
+
+ if (clock_message)
+ gst_element_post_message (GST_ELEMENT_CAST (bin), clock_message);
+
+ GST_CAT_INFO_OBJECT (GST_CAT_PARENTAGE, bin, "removed child \"%s\"",
+ elem_name);
+ g_free (elem_name);
+
+ gst_element_set_bus (element, NULL);
+
+ /* Clear the clock we provided to the element */
+ gst_element_set_clock (element, NULL);
+
+ /* we ref here because after the _unparent() the element can be disposed
+ * and we still need it to reset the UNPARENTING flag and fire a signal. */
+ gst_object_ref (element);
+ gst_object_unparent (GST_OBJECT_CAST (element));
+
+ GST_OBJECT_LOCK (element);
+ GST_OBJECT_FLAG_UNSET (element, GST_ELEMENT_UNPARENTING);
+ GST_OBJECT_UNLOCK (element);
+
+ g_signal_emit (bin, gst_bin_signals[ELEMENT_REMOVED], 0, element);
+ gst_child_proxy_child_removed ((GstObject *) bin, (GstObject *) element);
+
+ /* element is really out of our control now */
+ gst_object_unref (element);
+
+ return TRUE;
+
+ /* ERROR handling */
+not_in_bin:
+ {
+ g_warning ("Element '%s' is not in bin '%s'", elem_name,
+ GST_ELEMENT_NAME (bin));
+ GST_OBJECT_UNLOCK (bin);
+ g_free (elem_name);
+ return FALSE;
+ }
+already_removing:
+ {
+ GST_CAT_INFO_OBJECT (GST_CAT_PARENTAGE, bin, "already removing child");
+ GST_OBJECT_UNLOCK (element);
+ return FALSE;
+ }
+}
+
+/**
+ * gst_bin_remove:
+ * @bin: a #GstBin
+ * @element: (transfer none): the #GstElement to remove
+ *
+ * Removes the element from the bin, unparenting it as well.
+ * Unparenting the element means that the element will be dereferenced,
+ * so if the bin holds the only reference to the element, the element
+ * will be freed in the process of removing it from the bin. If you
+ * want the element to still exist after removing, you need to call
+ * gst_object_ref() before removing it from the bin.
+ *
+ * If the element's pads are linked to other pads, the pads will be unlinked
+ * before the element is removed from the bin.
+ *
+ * MT safe.
+ *
+ * Returns: TRUE if the element could be removed, FALSE if
+ * the bin does not want to remove the element.
+ */
+gboolean
+gst_bin_remove (GstBin * bin, GstElement * element)
+{
+ GstBinClass *bclass;
+ gboolean result;
+
+ g_return_val_if_fail (GST_IS_BIN (bin), FALSE);
+ g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
+
+ bclass = GST_BIN_GET_CLASS (bin);
+
+ if (G_UNLIKELY (bclass->remove_element == NULL))
+ goto no_function;
+
+ GST_CAT_DEBUG (GST_CAT_PARENTAGE, "removing element %s from bin %s",
+ GST_ELEMENT_NAME (element), GST_ELEMENT_NAME (bin));
+
+ result = bclass->remove_element (bin, element);
+
+ return result;
+
+ /* ERROR handling */
+no_function:
+ {
+ g_warning ("removing elements from bin '%s' is not supported",
+ GST_ELEMENT_NAME (bin));
+ return FALSE;
+ }
+}
+
+/**
+ * gst_bin_iterate_elements:
+ * @bin: a #GstBin
+ *
+ * Gets an iterator for the elements in this bin.
+ *
+ * Each element yielded by the iterator will have its refcount increased, so
+ * unref after use.
+ *
+ * MT safe. Caller owns returned value.
+ *
+ * Returns: (transfer full): a #GstIterator of #GstElement, or NULL
+ */
+GstIterator *
+gst_bin_iterate_elements (GstBin * bin)
+{
+ GstIterator *result;
+
+ g_return_val_if_fail (GST_IS_BIN (bin), NULL);
+
+ GST_OBJECT_LOCK (bin);
+ result = gst_iterator_new_list (GST_TYPE_ELEMENT,
+ GST_OBJECT_GET_LOCK (bin),
+ &bin->children_cookie, &bin->children, (GObject *) bin, NULL);
+ GST_OBJECT_UNLOCK (bin);
+
+ return result;
+}
+
+static GstIteratorItem
+iterate_child_recurse (GstIterator * it, const GValue * item)
+{
+ GstElement *child = g_value_get_object (item);
+
+ if (GST_IS_BIN (child)) {
+ GstIterator *other = gst_bin_iterate_recurse (GST_BIN_CAST (child));
+
+ gst_iterator_push (it, other);
+ }
+ return GST_ITERATOR_ITEM_PASS;
+}
+
+/**
+ * gst_bin_iterate_recurse:
+ * @bin: a #GstBin
+ *
+ * Gets an iterator for the elements in this bin.
+ * This iterator recurses into GstBin children.
+ *
+ * Each element yielded by the iterator will have its refcount increased, so
+ * unref after use.
+ *
+ * MT safe. Caller owns returned value.
+ *
+ * Returns: (transfer full): a #GstIterator of #GstElement, or NULL
+ */
+GstIterator *
+gst_bin_iterate_recurse (GstBin * bin)
+{
+ GstIterator *result;
+
+ g_return_val_if_fail (GST_IS_BIN (bin), NULL);
+
+ GST_OBJECT_LOCK (bin);
+ result = gst_iterator_new_list (GST_TYPE_ELEMENT,
+ GST_OBJECT_GET_LOCK (bin),
+ &bin->children_cookie,
+ &bin->children,
+ (GObject *) bin, (GstIteratorItemFunction) iterate_child_recurse);
+ GST_OBJECT_UNLOCK (bin);
+
+ return result;
+}
+
+/* returns 0 when TRUE because this is a GCompareFunc */
+/* MT safe */
+static gint
+bin_element_is_sink (GstElement * child, GstBin * bin)
+{
+ gboolean is_sink;
+
+ /* we lock the child here for the remainder of the function to
+ * get its name and flag safely. */
+ GST_OBJECT_LOCK (child);
+ is_sink = GST_OBJECT_FLAG_IS_SET (child, GST_ELEMENT_IS_SINK);
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, bin,
+ "child %s %s sink", GST_OBJECT_NAME (child), is_sink ? "is" : "is not");
+
+ GST_OBJECT_UNLOCK (child);
+ return is_sink ? 0 : 1;
+}
+
+static gint
+sink_iterator_filter (const GValue * vchild, GValue * vbin)
+{
+ GstBin *bin = g_value_get_object (vbin);
+ GstElement *child = g_value_get_object (vchild);
+
+ return (bin_element_is_sink (child, bin));
+}
+
+/**
+ * gst_bin_iterate_sinks:
+ * @bin: a #GstBin
+ *
+ * Gets an iterator for all elements in the bin that have the
+ * #GST_ELEMENT_IS_SINK flag set.
+ *
+ * Each element yielded by the iterator will have its refcount increased, so
+ * unref after use.
+ *
+ * MT safe. Caller owns returned value.
+ *
+ * Returns: (transfer full): a #GstIterator of #GstElement, or NULL
+ */
+GstIterator *
+gst_bin_iterate_sinks (GstBin * bin)
+{
+ GstIterator *children;
+ GstIterator *result;
+ GValue vbin = { 0, };
+
+ g_return_val_if_fail (GST_IS_BIN (bin), NULL);
+
+ g_value_init (&vbin, GST_TYPE_BIN);
+ g_value_set_object (&vbin, bin);
+
+ children = gst_bin_iterate_elements (bin);
+ result = gst_iterator_filter (children,
+ (GCompareFunc) sink_iterator_filter, &vbin);
+
+ g_value_unset (&vbin);
+
+ return result;
+}
+
+/* returns 0 when TRUE because this is a GCompareFunc */
+/* MT safe */
+static gint
+bin_element_is_src (GstElement * child, GstBin * bin)
+{
+ gboolean is_src;
+
+ /* we lock the child here for the remainder of the function to
+ * get its name and other info safely. */
+ GST_OBJECT_LOCK (child);
+ is_src = GST_OBJECT_FLAG_IS_SET (child, GST_ELEMENT_IS_SOURCE);
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, bin,
+ "child %s %s src", GST_OBJECT_NAME (child), is_src ? "is" : "is not");
+
+ GST_OBJECT_UNLOCK (child);
+ return is_src ? 0 : 1;
+}
+
+static gint
+src_iterator_filter (const GValue * vchild, GValue * vbin)
+{
+ GstBin *bin = g_value_get_object (vbin);
+ GstElement *child = g_value_get_object (vchild);
+
+ return (bin_element_is_src (child, bin));
+}
+
+/**
+ * gst_bin_iterate_sources:
+ * @bin: a #GstBin
+ *
+ * Gets an iterator for all elements in the bin that have the
+ * #GST_ELEMENT_IS_SOURCE flag set.
+ *
+ * Each element yielded by the iterator will have its refcount increased, so
+ * unref after use.
+ *
+ * MT safe. Caller owns returned value.
+ *
+ * Returns: (transfer full): a #GstIterator of #GstElement, or NULL
+ */
+GstIterator *
+gst_bin_iterate_sources (GstBin * bin)
+{
+ GstIterator *children;
+ GstIterator *result;
+ GValue vbin = { 0, };
+
+ g_return_val_if_fail (GST_IS_BIN (bin), NULL);
+
+ g_value_init (&vbin, GST_TYPE_BIN);
+ g_value_set_object (&vbin, bin);
+
+ children = gst_bin_iterate_elements (bin);
+ result = gst_iterator_filter (children,
+ (GCompareFunc) src_iterator_filter, &vbin);
+
+ g_value_unset (&vbin);
+
+ return result;
+}
+
+/*
+ * MT safe
+ */
+static GstStateChangeReturn
+gst_bin_get_state_func (GstElement * element, GstState * state,
+ GstState * pending, GstClockTime timeout)
+{
+ GstStateChangeReturn ret;
+
+ GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "getting state");
+
+ ret =
+ GST_ELEMENT_CLASS (parent_class)->get_state (element, state, pending,
+ timeout);
+
+ return ret;
+}
+
+/***********************************************
+ * Topologically sorted iterator
+ * see http://en.wikipedia.org/wiki/Topological_sorting
+ *
+ * For each element in the graph, an entry is kept in a HashTable
+ * with its number of srcpad connections (degree).
+ * We then change state of all elements without dependencies
+ * (degree 0) and decrement the degree of all elements connected
+ * on the sinkpads. When an element reaches degree 0, its state is
+ * changed next.
+ * When all elements are handled the algorithm stops.
+ */
+typedef struct _GstBinSortIterator
+{
+ GstIterator it;
+ GQueue *queue; /* elements queued for state change */
+ GstBin *bin; /* bin we iterate */
+ gint mode; /* adding or removing dependency */
+ GstElement *best; /* next element with least dependencies */
+ gint best_deg; /* best degree */
+ GHashTable *hash; /* hashtable with element dependencies */
+ gboolean dirty; /* we detected structure change */
+} GstBinSortIterator;
+
+static void
+gst_bin_sort_iterator_copy (const GstBinSortIterator * it,
+ GstBinSortIterator * copy)
+{
+ GHashTableIter iter;
+ gpointer key, value;
+
+ copy->queue = g_queue_copy (it->queue);
+ g_queue_foreach (copy->queue, (GFunc) gst_object_ref, NULL);
+
+ copy->bin = gst_object_ref (it->bin);
+ if (it->best)
+ copy->best = gst_object_ref (it->best);
+
+ copy->hash = g_hash_table_new (NULL, NULL);
+ g_hash_table_iter_init (&iter, it->hash);
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ g_hash_table_insert (copy->hash, key, value);
+}
+
+/* we add and subtract 1 to make sure we don't confuse NULL and 0 */
+#define HASH_SET_DEGREE(bit, elem, deg) \
+ g_hash_table_replace (bit->hash, elem, GINT_TO_POINTER(deg+1))
+#define HASH_GET_DEGREE(bit, elem) \
+ (GPOINTER_TO_INT(g_hash_table_lookup (bit->hash, elem))-1)
+
+/* add element to queue of next elements in the iterator.
+ * We push at the tail to give higher priority elements a
+ * chance first */
+static void
+add_to_queue (GstBinSortIterator * bit, GstElement * element)
+{
+ GST_DEBUG_OBJECT (bit->bin, "adding '%s' to queue",
+ GST_ELEMENT_NAME (element));
+ gst_object_ref (element);
+ g_queue_push_tail (bit->queue, element);
+ HASH_SET_DEGREE (bit, element, -1);
+}
+
+static void
+remove_from_queue (GstBinSortIterator * bit, GstElement * element)
+{
+ GList *find;
+
+ if ((find = g_queue_find (bit->queue, element))) {
+ GST_DEBUG_OBJECT (bit->bin, "removing '%s' from queue",
+ GST_ELEMENT_NAME (element));
+
+ g_queue_delete_link (bit->queue, find);
+ gst_object_unref (element);
+ } else {
+ GST_DEBUG_OBJECT (bit->bin, "unable to remove '%s' from queue",
+ GST_ELEMENT_NAME (element));
+ }
+}
+
+/* clear the queue, unref all objects as we took a ref when
+ * we added them to the queue */
+static void
+clear_queue (GQueue * queue)
+{
+ gpointer p;
+
+ while ((p = g_queue_pop_head (queue)))
+ gst_object_unref (p);
+}
+
+/* set all degrees to 0. Elements marked as a sink are
+ * added to the queue immediately. Since we only look at the SINK flag of the
+ * element, it is possible that we add non-sinks to the queue. These will be
+ * removed from the queue again when we can prove that it provides data for some
+ * other element. */
+static void
+reset_degree (GstElement * element, GstBinSortIterator * bit)
+{
+ gboolean is_sink;
+
+ /* sinks are added right away */
+ GST_OBJECT_LOCK (element);
+ is_sink = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_IS_SINK);
+ GST_OBJECT_UNLOCK (element);
+
+ if (is_sink) {
+ add_to_queue (bit, element);
+ } else {
+ /* others are marked with 0 and handled when sinks are done */
+ HASH_SET_DEGREE (bit, element, 0);
+ }
+}
+
+/* adjust the degree of all elements connected to the given
+ * element. If a degree of an element drops to 0, it is
+ * added to the queue of elements to schedule next.
+ *
+ * We have to make sure not to cross the bin boundary this element
+ * belongs to.
+ */
+static void
+update_degree (GstElement * element, GstBinSortIterator * bit)
+{
+ gboolean linked = FALSE;
+
+ GST_OBJECT_LOCK (element);
+ /* don't touch degree if element has no sinkpads */
+ if (element->numsinkpads != 0) {
+ /* loop over all sinkpads, decrement degree for all connected
+ * elements in this bin */
+ GList *pads;
+
+ for (pads = element->sinkpads; pads; pads = g_list_next (pads)) {
+ GstPad *pad, *peer;
+
+ pad = GST_PAD_CAST (pads->data);
+
+ /* we're iterating over the sinkpads, check if it's busy in a link/unlink */
+ if (G_UNLIKELY (find_message (bit->bin, GST_OBJECT_CAST (pad),
+ GST_MESSAGE_STRUCTURE_CHANGE))) {
+ /* mark the iterator as dirty because we won't be updating the degree
+ * of the peer parent now. This would result in the 'loop detected'
+ * later on because the peer parent element could become the best next
+ * element with a degree > 0. We will simply continue our state
+ * changes and we'll eventually resync when the unlink completed and
+ * the iterator cookie is updated. */
+ bit->dirty = TRUE;
+ continue;
+ }
+
+ if ((peer = gst_pad_get_peer (pad))) {
+ GstElement *peer_element;
+
+ if ((peer_element = gst_pad_get_parent_element (peer))) {
+ GST_OBJECT_LOCK (peer_element);
+ /* check that we don't go outside of this bin */
+ if (GST_OBJECT_CAST (peer_element)->parent ==
+ GST_OBJECT_CAST (bit->bin)) {
+ gint old_deg, new_deg;
+
+ old_deg = HASH_GET_DEGREE (bit, peer_element);
+
+ /* check to see if we added an element as sink that was not really a
+ * sink because it was connected to some other element. */
+ if (old_deg == -1) {
+ remove_from_queue (bit, peer_element);
+ old_deg = 0;
+ }
+ new_deg = old_deg + bit->mode;
+
+ GST_DEBUG_OBJECT (bit->bin,
+ "change element %s, degree %d->%d, linked to %s",
+ GST_ELEMENT_NAME (peer_element), old_deg, new_deg,
+ GST_ELEMENT_NAME (element));
+
+ /* update degree, it is possible that an element was in 0 and
+ * reaches -1 here. This would mean that the element had no sinkpads
+ * but became linked while the state change was happening. We will
+ * resync on this with the structure change message. */
+ if (new_deg == 0) {
+ /* degree hit 0, add to queue */
+ add_to_queue (bit, peer_element);
+ } else {
+ HASH_SET_DEGREE (bit, peer_element, new_deg);
+ }
+ linked = TRUE;
+ }
+ GST_OBJECT_UNLOCK (peer_element);
+ gst_object_unref (peer_element);
+ }
+ gst_object_unref (peer);
+ }
+ }
+ }
+ if (!linked) {
+ GST_DEBUG_OBJECT (bit->bin, "element %s not linked on any sinkpads",
+ GST_ELEMENT_NAME (element));
+ }
+ GST_OBJECT_UNLOCK (element);
+}
+
+/* find the next best element not handled yet. This is the one
+ * with the lowest non-negative degree */
+static void
+find_element (GstElement * element, GstBinSortIterator * bit)
+{
+ gint degree;
+
+ /* element is already handled */
+ if ((degree = HASH_GET_DEGREE (bit, element)) < 0)
+ return;
+
+ /* first element or element with smaller degree */
+ if (bit->best == NULL || bit->best_deg > degree) {
+ bit->best = element;
+ bit->best_deg = degree;
+ }
+}
+
+/* get next element in iterator. the returned element has the
+ * refcount increased */
+static GstIteratorResult
+gst_bin_sort_iterator_next (GstBinSortIterator * bit, GValue * result)
+{
+ GstElement *best;
+ GstBin *bin = bit->bin;
+
+ /* empty queue, we have to find a next best element */
+ if (g_queue_is_empty (bit->queue)) {
+
+ bit->best = NULL;
+ bit->best_deg = G_MAXINT;
+ g_list_foreach (bin->children, (GFunc) find_element, bit);
+ if ((best = bit->best)) {
+ /* when we detected an unlink, don't warn because our degrees might be
+ * screwed up. We will resync later */
+ if (bit->best_deg != 0 && !bit->dirty) {
+ /* we don't fail on this one yet */
+ GST_WARNING_OBJECT (bin, "loop dected in graph");
+ g_warning ("loop detected in the graph of bin '%s'!!",
+ GST_ELEMENT_NAME (bin));
+ }
+ /* best unhandled element, schedule as next element */
+ GST_DEBUG_OBJECT (bin, "queue empty, next best: %s",
+ GST_ELEMENT_NAME (best));
+ HASH_SET_DEGREE (bit, best, -1);
+ g_value_set_object (result, best);
+ } else {
+ GST_DEBUG_OBJECT (bin, "queue empty, elements exhausted");
+ /* no more unhandled elements, we are done */
+ return GST_ITERATOR_DONE;
+ }
+ } else {
+ /* everything added to the queue got reffed */
+ best = g_queue_pop_head (bit->queue);
+ g_value_set_object (result, best);
+ gst_object_unref (best);
+ }
+
+ GST_DEBUG_OBJECT (bin, "queue head gives %s", GST_ELEMENT_NAME (best));
+ /* update degrees of linked elements */
+ update_degree (best, bit);
+
+ return GST_ITERATOR_OK;
+}
+
+/* clear queues, recalculate the degrees and restart. */
+static void
+gst_bin_sort_iterator_resync (GstBinSortIterator * bit)
+{
+ GstBin *bin = bit->bin;
+
+ GST_DEBUG_OBJECT (bin, "resync");
+ bit->dirty = FALSE;
+ clear_queue (bit->queue);
+ /* reset degrees */
+ g_list_foreach (bin->children, (GFunc) reset_degree, bit);
+ /* calc degrees, incrementing */
+ bit->mode = 1;
+ g_list_foreach (bin->children, (GFunc) update_degree, bit);
+ /* for the rest of the function we decrement the degrees */
+ bit->mode = -1;
+}
+
+/* clear queues, unref bin and free iterator. */
+static void
+gst_bin_sort_iterator_free (GstBinSortIterator * bit)
+{
+ GstBin *bin = bit->bin;
+
+ GST_DEBUG_OBJECT (bin, "free");
+ clear_queue (bit->queue);
+ g_queue_free (bit->queue);
+ g_hash_table_destroy (bit->hash);
+ gst_object_unref (bin);
+}
+
+/* should be called with the bin LOCK held */
+static GstIterator *
+gst_bin_sort_iterator_new (GstBin * bin)
+{
+ GstBinSortIterator *result;
+
+ /* we don't need an ItemFunction because we ref the items in the _next
+ * method already */
+ result = (GstBinSortIterator *)
+ gst_iterator_new (sizeof (GstBinSortIterator),
+ GST_TYPE_ELEMENT,
+ GST_OBJECT_GET_LOCK (bin),
+ &bin->priv->structure_cookie,
+ (GstIteratorCopyFunction) gst_bin_sort_iterator_copy,
+ (GstIteratorNextFunction) gst_bin_sort_iterator_next,
+ (GstIteratorItemFunction) NULL,
+ (GstIteratorResyncFunction) gst_bin_sort_iterator_resync,
+ (GstIteratorFreeFunction) gst_bin_sort_iterator_free);
+ result->queue = g_queue_new ();
+ result->hash = g_hash_table_new (NULL, NULL);
+ gst_object_ref (bin);
+ result->bin = bin;
+ gst_bin_sort_iterator_resync (result);
+
+ return (GstIterator *) result;
+}
+
+/**
+ * gst_bin_iterate_sorted:
+ * @bin: a #GstBin
+ *
+ * Gets an iterator for the elements in this bin in topologically
+ * sorted order. This means that the elements are returned from
+ * the most downstream elements (sinks) to the sources.
+ *
+ * This function is used internally to perform the state changes
+ * of the bin elements and for clock selection.
+ *
+ * Each element yielded by the iterator will have its refcount increased, so
+ * unref after use.
+ *
+ * MT safe. Caller owns returned value.
+ *
+ * Returns: (transfer full): a #GstIterator of #GstElement, or NULL
+ */
+GstIterator *
+gst_bin_iterate_sorted (GstBin * bin)
+{
+ GstIterator *result;
+
+ g_return_val_if_fail (GST_IS_BIN (bin), NULL);
+
+ GST_OBJECT_LOCK (bin);
+ result = gst_bin_sort_iterator_new (bin);
+ GST_OBJECT_UNLOCK (bin);
+
+ return result;
+}
+
+static GstStateChangeReturn
+gst_bin_element_set_state (GstBin * bin, GstElement * element,
+ GstClockTime base_time, GstClockTime start_time, GstState current,
+ GstState next)
+{
+ GstStateChangeReturn ret;
+ GstState pending, child_current, child_pending;
+ gboolean locked;
+ GList *found;
+
+ GST_STATE_LOCK (element);
+
+ GST_OBJECT_LOCK (element);
+ /* set base_time and start time on child */
+ GST_ELEMENT_START_TIME (element) = start_time;
+ element->base_time = base_time;
+ /* peel off the locked flag */
+ locked = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_LOCKED_STATE);
+ /* Get the previous set_state result to preserve NO_PREROLL and ASYNC */
+ ret = GST_STATE_RETURN (element);
+ child_current = GST_STATE (element);
+ child_pending = GST_STATE_PENDING (element);
+ GST_OBJECT_UNLOCK (element);
+
+ /* skip locked elements */
+ if (G_UNLIKELY (locked))
+ goto locked;
+
+ /* if the element was no preroll, just start changing the state regardless
+ * if it had async elements (in the case of a bin) because they won't preroll
+ * anyway. */
+ if (G_UNLIKELY (ret == GST_STATE_CHANGE_NO_PREROLL)) {
+ GST_DEBUG_OBJECT (element, "element is NO_PREROLL, ignore async elements");
+ goto no_preroll;
+ }
+
+ GST_OBJECT_LOCK (bin);
+ pending = GST_STATE_PENDING (bin);
+
+ /* Try not to change the state of elements that are already in the state we're
+ * going to */
+ if (!(next == GST_STATE_PLAYING || child_pending != GST_STATE_VOID_PENDING ||
+ (child_pending == GST_STATE_VOID_PENDING &&
+ ((pending > child_current && next > child_current) ||
+ (pending < child_current && next < child_current)))))
+ goto unneeded;
+
+ /* the element was busy with an upwards async state change, we must wait for
+ * an ASYNC_DONE message before we attemp to change the state. */
+ if ((found =
+ find_message (bin, GST_OBJECT_CAST (element),
+ GST_MESSAGE_ASYNC_START))) {
+#ifndef GST_DISABLE_GST_DEBUG
+ GstMessage *message = GST_MESSAGE_CAST (found->data);
+
+ GST_DEBUG_OBJECT (element, "element message %p, %s async busy",
+ message, GST_ELEMENT_NAME (GST_MESSAGE_SRC (message)));
+#endif
+ /* only wait for upward state changes */
+ if (next > current) {
+ /* We found an async element check if we can force its state to change or
+ * if we have to wait for it to preroll. */
+ if (G_UNLIKELY (!enable_latency)) {
+ g_warning ("Future versions of GStreamer will wait for element \"%s\"\n"
+ "\tto preroll in order to perform correct latency calculations.\n"
+ "\tPlease verify that the application continues to work correctly by\n"
+ "\tsetting the environment variable GST_COMPAT to a value containing\n"
+ "\tthe string 'live-preroll'.", GST_ELEMENT_NAME (element));
+ goto no_latency;
+ }
+ goto was_busy;
+ }
+ }
+no_latency:
+ GST_OBJECT_UNLOCK (bin);
+
+no_preroll:
+ GST_DEBUG_OBJECT (bin,
+ "setting element %s to %s, base_time %" GST_TIME_FORMAT,
+ GST_ELEMENT_NAME (element), gst_element_state_get_name (next),
+ GST_TIME_ARGS (base_time));
+
+ /* change state */
+ ret = gst_element_set_state (element, next);
+
+ GST_STATE_UNLOCK (element);
+
+ return ret;
+
+locked:
+ {
+ GST_DEBUG_OBJECT (element,
+ "element is locked, return previous return %s",
+ gst_element_state_change_return_get_name (ret));
+ GST_STATE_UNLOCK (element);
+ return ret;
+ }
+was_busy:
+ {
+ GST_DEBUG_OBJECT (element, "element was busy, delaying state change");
+ GST_OBJECT_UNLOCK (bin);
+ GST_STATE_UNLOCK (element);
+ return GST_STATE_CHANGE_ASYNC;
+ }
+unneeded:
+ {
+ GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
+ "skipping transition from %s to %s, since bin pending"
+ " is %s : last change state return follows",
+ gst_element_state_get_name (child_current),
+ gst_element_state_get_name (next),
+ gst_element_state_get_name (pending));
+ GST_OBJECT_UNLOCK (bin);
+ GST_STATE_UNLOCK (element);
+ return ret;
+ }
+}
+
+/* gst_iterator_fold functions for pads_activate
+ * Stop the iterator if activating one pad failed. */
+static gboolean
+activate_pads (const GValue * vpad, GValue * ret, gboolean * active)
+{
+ GstPad *pad = g_value_get_object (vpad);
+ gboolean cont = TRUE;
+
+ if (!(cont = gst_pad_set_active (pad, *active)))
+ g_value_set_boolean (ret, FALSE);
+
+ return cont;
+}
+
+/* returns false on error or early cutout of the fold, true if all
+ * pads in @iter were (de)activated successfully. */
+static gboolean
+iterator_activate_fold_with_resync (GstIterator * iter, gpointer user_data)
+{
+ GstIteratorResult ires;
+ GValue ret = { 0 };
+
+ /* no need to unset this later, it's just a boolean */
+ g_value_init (&ret, G_TYPE_BOOLEAN);
+ g_value_set_boolean (&ret, TRUE);
+
+ while (1) {
+ ires = gst_iterator_fold (iter, (GstIteratorFoldFunction) activate_pads,
+ &ret, user_data);
+ switch (ires) {
+ case GST_ITERATOR_RESYNC:
+ /* need to reset the result again */
+ g_value_set_boolean (&ret, TRUE);
+ gst_iterator_resync (iter);
+ break;
+ case GST_ITERATOR_DONE:
+ /* all pads iterated, return collected value */
+ goto done;
+ default:
+ /* iterator returned _ERROR or premature end with _OK,
+ * mark an error and exit */
+ g_value_set_boolean (&ret, FALSE);
+ goto done;
+ }
+ }
+done:
+ /* return collected value */
+ return g_value_get_boolean (&ret);
+}
+
+/* is called with STATE_LOCK
+ */
+static gboolean
+gst_bin_src_pads_activate (GstBin * bin, gboolean active)
+{
+ GstIterator *iter;
+ gboolean fold_ok;
+
+ GST_DEBUG_OBJECT (bin, "src_pads_activate with active %d", active);
+
+ iter = gst_element_iterate_src_pads ((GstElement *) bin);
+ fold_ok = iterator_activate_fold_with_resync (iter, &active);
+ gst_iterator_free (iter);
+ if (G_UNLIKELY (!fold_ok))
+ goto failed;
+
+ GST_DEBUG_OBJECT (bin, "pads_activate successful");
+
+ return TRUE;
+
+ /* ERRORS */
+failed:
+ {
+ GST_DEBUG_OBJECT (bin, "source pads_activate failed");
+ return FALSE;
+ }
+}
+
+/**
+ * gst_bin_recalculate_latency:
+ * @bin: a #GstBin
+ *
+ * Query @bin for the current latency using and reconfigures this latency to all the
+ * elements with a LATENCY event.
+ *
+ * This method is typically called on the pipeline when a #GST_MESSAGE_LATENCY
+ * is posted on the bus.
+ *
+ * This function simply emits the 'do-latency' signal so any custom latency
+ * calculations will be performed.
+ *
+ * Returns: %TRUE if the latency could be queried and reconfigured.
+ *
+ * Since: 0.10.22.
+ */
+gboolean
+gst_bin_recalculate_latency (GstBin * bin)
+{
+ gboolean res;
+
+ g_signal_emit (bin, gst_bin_signals[DO_LATENCY], 0, &res);
+ GST_DEBUG_OBJECT (bin, "latency returned %d", res);
+
+ return res;
+}
+
+static gboolean
+gst_bin_do_latency_func (GstBin * bin)
+{
+ GstQuery *query;
+ GstElement *element;
+ GstClockTime min_latency, max_latency;
+ gboolean res;
+
+ g_return_val_if_fail (GST_IS_BIN (bin), FALSE);
+
+ element = GST_ELEMENT_CAST (bin);
+
+ GST_DEBUG_OBJECT (element, "querying latency");
+
+ query = gst_query_new_latency ();
+ if ((res = gst_element_query (element, query))) {
+ gboolean live;
+
+ gst_query_parse_latency (query, &live, &min_latency, &max_latency);
+
+ GST_DEBUG_OBJECT (element,
+ "got min latency %" GST_TIME_FORMAT ", max latency %"
+ GST_TIME_FORMAT ", live %d", GST_TIME_ARGS (min_latency),
+ GST_TIME_ARGS (max_latency), live);
+
+ if (max_latency < min_latency) {
+ /* this is an impossible situation, some parts of the pipeline might not
+ * work correctly. We post a warning for now. */
+ GST_ELEMENT_WARNING (element, CORE, CLOCK, (NULL),
+ ("Impossible to configure latency: max %" GST_TIME_FORMAT " < min %"
+ GST_TIME_FORMAT ". Add queues or other buffering elements.",
+ GST_TIME_ARGS (max_latency), GST_TIME_ARGS (min_latency)));
+ }
+
+ /* configure latency on elements */
+ res = gst_element_send_event (element, gst_event_new_latency (min_latency));
+ if (res) {
+ GST_INFO_OBJECT (element, "configured latency of %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (min_latency));
+ } else {
+ GST_WARNING_OBJECT (element,
+ "did not really configure latency of %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (min_latency));
+ }
+ } else {
+ /* this is not a real problem, we just don't configure any latency. */
+ GST_WARNING_OBJECT (element, "failed to query latency");
+ }
+ gst_query_unref (query);
+
+ return res;
+}
+
+static void
+gst_bin_state_changed (GstElement * element, GstState oldstate,
+ GstState newstate, GstState pending)
+{
+ GstElementClass *pklass = (GstElementClass *) parent_class;
+
+ if (newstate == GST_STATE_PLAYING && pending == GST_STATE_VOID_PENDING)
+ bin_do_eos (GST_BIN_CAST (element));
+
+ if (pklass->state_changed)
+ pklass->state_changed (element, oldstate, newstate, pending);
+}
+
+static GstStateChangeReturn
+gst_bin_change_state_func (GstElement * element, GstStateChange transition)
+{
+ GstBin *bin;
+ GstStateChangeReturn ret;
+ GstState current, next;
+ gboolean have_async;
+ gboolean have_no_preroll;
+ GstClockTime base_time, start_time;
+ GstIterator *it;
+ gboolean done;
+ GValue data = { 0, };
+
+ /* we don't need to take the STATE_LOCK, it is already taken */
+ current = (GstState) GST_STATE_TRANSITION_CURRENT (transition);
+ next = (GstState) GST_STATE_TRANSITION_NEXT (transition);
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
+ "changing state of children from %s to %s",
+ gst_element_state_get_name (current), gst_element_state_get_name (next));
+
+ bin = GST_BIN_CAST (element);
+
+ switch (next) {
+ case GST_STATE_PLAYING:
+ {
+ gboolean toplevel;
+
+ GST_OBJECT_LOCK (bin);
+ toplevel = BIN_IS_TOPLEVEL (bin);
+ GST_OBJECT_UNLOCK (bin);
+
+ if (toplevel)
+ gst_bin_recalculate_latency (bin);
+ break;
+ }
+ case GST_STATE_PAUSED:
+ /* Clear EOS list on next PAUSED */
+ GST_OBJECT_LOCK (bin);
+ GST_DEBUG_OBJECT (element, "clearing EOS elements");
+ bin_remove_messages (bin, NULL, GST_MESSAGE_EOS);
+ bin->priv->posted_eos = FALSE;
+ GST_OBJECT_UNLOCK (bin);
+ if (current == GST_STATE_READY)
+ if (!(gst_bin_src_pads_activate (bin, TRUE)))
+ goto activate_failure;
+ break;
+ case GST_STATE_READY:
+ /* Clear message list on next READY */
+ GST_OBJECT_LOCK (bin);
+ GST_DEBUG_OBJECT (element, "clearing all cached messages");
+ bin_remove_messages (bin, NULL, GST_MESSAGE_ANY);
+ GST_OBJECT_UNLOCK (bin);
+ if (current == GST_STATE_PAUSED)
+ if (!(gst_bin_src_pads_activate (bin, FALSE)))
+ goto activate_failure;
+ break;
+ case GST_STATE_NULL:
+ if (current == GST_STATE_READY)
+ if (!(gst_bin_src_pads_activate (bin, FALSE)))
+ goto activate_failure;
+ break;
+ default:
+ break;
+ }
+
+ /* this flag is used to make the async state changes return immediately. We
+ * don't want them to interfere with this state change */
+ GST_OBJECT_LOCK (bin);
+ bin->polling = TRUE;
+ GST_OBJECT_UNLOCK (bin);
+
+ /* iterate in state change order */
+ it = gst_bin_iterate_sorted (bin);
+
+ /* mark if we've seen an ASYNC element in the bin when we did a state change.
+ * Note how we don't reset this value when a resync happens, the reason being
+ * that the async element posted ASYNC_START and we want to post ASYNC_DONE
+ * even after a resync when the async element is gone */
+ have_async = FALSE;
+
+restart:
+ /* take base_time */
+ base_time = gst_element_get_base_time (element);
+ start_time = gst_element_get_start_time (element);
+
+ have_no_preroll = FALSE;
+
+ done = FALSE;
+ while (!done) {
+ switch (gst_iterator_next (it, &data)) {
+ case GST_ITERATOR_OK:
+ {
+ GstElement *child;
+
+ child = g_value_get_object (&data);
+
+ /* set state and base_time now */
+ ret = gst_bin_element_set_state (bin, child, base_time, start_time,
+ current, next);
+
+ switch (ret) {
+ case GST_STATE_CHANGE_SUCCESS:
+ GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
+ "child '%s' changed state to %d(%s) successfully",
+ GST_ELEMENT_NAME (child), next,
+ gst_element_state_get_name (next));
+ break;
+ case GST_STATE_CHANGE_ASYNC:
+ {
+ GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
+ "child '%s' is changing state asynchronously to %s",
+ GST_ELEMENT_NAME (child), gst_element_state_get_name (next));
+ have_async = TRUE;
+ break;
+ }
+ case GST_STATE_CHANGE_FAILURE:{
+ GstObject *parent;
+
+ GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
+ "child '%s' failed to go to state %d(%s)",
+ GST_ELEMENT_NAME (child),
+ next, gst_element_state_get_name (next));
+
+ /* Only fail if the child is still inside
+ * this bin. It might've been removed already
+ * because of the error by the bin subclass
+ * to ignore the error. */
+ parent = gst_object_get_parent (GST_OBJECT_CAST (child));
+ if (parent == GST_OBJECT_CAST (element)) {
+ /* element is still in bin, really error now */
+ gst_object_unref (parent);
+ goto done;
+ }
+ /* child removed from bin, let the resync code redo the state
+ * change */
+ GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
+ "child '%s' was removed from the bin",
+ GST_ELEMENT_NAME (child));
+
+ if (parent)
+ gst_object_unref (parent);
+
+ break;
+ }
+ case GST_STATE_CHANGE_NO_PREROLL:
+ GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
+ "child '%s' changed state to %d(%s) successfully without preroll",
+ GST_ELEMENT_NAME (child), next,
+ gst_element_state_get_name (next));
+ have_no_preroll = TRUE;
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+ g_value_reset (&data);
+ break;
+ }
+ case GST_ITERATOR_RESYNC:
+ GST_CAT_DEBUG (GST_CAT_STATES, "iterator doing resync");
+ gst_iterator_resync (it);
+ goto restart;
+ default:
+ case GST_ITERATOR_DONE:
+ GST_CAT_DEBUG (GST_CAT_STATES, "iterator done");
+ done = TRUE;
+ break;
+ }
+ }
+
+ ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+ if (G_UNLIKELY (ret == GST_STATE_CHANGE_FAILURE))
+ goto done;
+
+ if (have_no_preroll) {
+ GST_CAT_DEBUG (GST_CAT_STATES,
+ "we have NO_PREROLL elements %s -> NO_PREROLL",
+ gst_element_state_change_return_get_name (ret));
+ ret = GST_STATE_CHANGE_NO_PREROLL;
+ } else if (have_async) {
+ GST_CAT_DEBUG (GST_CAT_STATES, "we have ASYNC elements %s -> ASYNC",
+ gst_element_state_change_return_get_name (ret));
+ ret = GST_STATE_CHANGE_ASYNC;
+ }
+
+done:
+ g_value_unset (&data);
+ gst_iterator_free (it);
+
+ GST_OBJECT_LOCK (bin);
+ bin->polling = FALSE;
+ /* it's possible that we did not get ASYNC from the children while the bin is
+ * simulating ASYNC behaviour by posting an ASYNC_DONE message on the bus with
+ * itself as the source. In that case we still want to check if the state
+ * change completed. */
+ if (ret != GST_STATE_CHANGE_ASYNC && !bin->priv->pending_async_done) {
+ /* no element returned ASYNC and there are no pending async_done messages,
+ * we can just complete. */
+ GST_DEBUG_OBJECT (bin, "no async elements");
+ goto state_end;
+ }
+ /* when we get here an ASYNC element was found */
+ if (GST_STATE_TARGET (bin) <= GST_STATE_READY) {
+ /* we ignore ASYNC state changes when we go to READY or NULL */
+ GST_DEBUG_OBJECT (bin, "target state %s <= READY",
+ gst_element_state_get_name (GST_STATE_TARGET (bin)));
+ goto state_end;
+ }
+
+ GST_DEBUG_OBJECT (bin, "check async elements");
+ /* check if all elements managed to commit their state already */
+ if (!find_message (bin, NULL, GST_MESSAGE_ASYNC_START)) {
+ /* nothing found, remove all old ASYNC_DONE messages. This can happen when
+ * all the elements commited their state while we were doing the state
+ * change. We will still return ASYNC for consistency but we commit the
+ * state already so that a _get_state() will return immediately. */
+ bin_remove_messages (bin, NULL, GST_MESSAGE_ASYNC_DONE);
+
+ GST_DEBUG_OBJECT (bin, "async elements commited");
+ bin_handle_async_done (bin, GST_STATE_CHANGE_SUCCESS, FALSE, FALSE);
+ }
+
+state_end:
+ bin->priv->pending_async_done = FALSE;
+ GST_OBJECT_UNLOCK (bin);
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
+ "done changing bin's state from %s to %s, now in %s, ret %s",
+ gst_element_state_get_name (current),
+ gst_element_state_get_name (next),
+ gst_element_state_get_name (GST_STATE (element)),
+ gst_element_state_change_return_get_name (ret));
+
+ return ret;
+
+ /* ERRORS */
+activate_failure:
+ {
+ GST_CAT_WARNING_OBJECT (GST_CAT_STATES, element,
+ "failure (de)activating src pads");
+ return GST_STATE_CHANGE_FAILURE;
+ }
+}
+
+/*
+ * This function is a utility event handler for seek events.
+ * It will send the event to all sinks or sources depending on the
+ * event-direction.
+ *
+ * Applications are free to override this behaviour and
+ * implement their own seek handler, but this will work for
+ * pretty much all cases in practice.
+ */
+static gboolean
+gst_bin_send_event (GstElement * element, GstEvent * event)
+{
+ GstBin *bin = GST_BIN_CAST (element);
+ GstIterator *iter;
+ gboolean res = TRUE;
+ gboolean done = FALSE;
+ GValue data = { 0, };
+
+ if (GST_EVENT_IS_DOWNSTREAM (event)) {
+ iter = gst_bin_iterate_sources (bin);
+ GST_DEBUG_OBJECT (bin, "Sending %s event to src children",
+ GST_EVENT_TYPE_NAME (event));
+ } else {
+ iter = gst_bin_iterate_sinks (bin);
+ GST_DEBUG_OBJECT (bin, "Sending %s event to sink children",
+ GST_EVENT_TYPE_NAME (event));
+ }
+
+ while (!done) {
+ switch (gst_iterator_next (iter, &data)) {
+ case GST_ITERATOR_OK:
+ {
+ GstElement *child = g_value_get_object (&data);;
+
+ gst_event_ref (event);
+ res &= gst_element_send_event (child, event);
+ g_value_reset (&data);
+ break;
+ }
+ case GST_ITERATOR_RESYNC:
+ gst_iterator_resync (iter);
+ res = TRUE;
+ break;
+ case GST_ITERATOR_DONE:
+ done = TRUE;
+ break;
+ case GST_ITERATOR_ERROR:
+ g_assert_not_reached ();
+ break;
+ }
+ }
+ g_value_unset (&data);
+ gst_iterator_free (iter);
+ gst_event_unref (event);
+
+ return res;
+}
+
+/* this is the function called by the threadpool. When async elements commit
+ * their state, this function will attempt to bring the bin to the next state.
+ */
+static void
+gst_bin_continue_func (BinContinueData * data)
+{
+ GstBin *bin;
+ GstState current, next, pending;
+ GstStateChange transition;
+
+ bin = data->bin;
+ pending = data->pending;
+
+ GST_DEBUG_OBJECT (bin, "waiting for state lock");
+ GST_STATE_LOCK (bin);
+
+ GST_DEBUG_OBJECT (bin, "doing state continue");
+ GST_OBJECT_LOCK (bin);
+
+ /* if a new state change happened after this thread was scheduled, we return
+ * immediately. */
+ if (data->cookie != GST_ELEMENT_CAST (bin)->state_cookie)
+ goto interrupted;
+
+ current = GST_STATE (bin);
+ next = GST_STATE_GET_NEXT (current, pending);
+ transition = (GstStateChange) GST_STATE_TRANSITION (current, next);
+
+ GST_STATE_NEXT (bin) = next;
+ GST_STATE_PENDING (bin) = pending;
+ /* mark busy */
+ GST_STATE_RETURN (bin) = GST_STATE_CHANGE_ASYNC;
+ GST_OBJECT_UNLOCK (bin);
+
+ GST_CAT_INFO_OBJECT (GST_CAT_STATES, bin,
+ "continue state change %s to %s, final %s",
+ gst_element_state_get_name (current),
+ gst_element_state_get_name (next), gst_element_state_get_name (pending));
+
+ gst_element_change_state (GST_ELEMENT_CAST (bin), transition);
+
+ GST_STATE_UNLOCK (bin);
+ GST_DEBUG_OBJECT (bin, "state continue done");
+
+ gst_object_unref (bin);
+ g_slice_free (BinContinueData, data);
+ return;
+
+interrupted:
+ {
+ GST_OBJECT_UNLOCK (bin);
+ GST_STATE_UNLOCK (bin);
+ GST_DEBUG_OBJECT (bin, "state continue aborted due to intervening change");
+ gst_object_unref (bin);
+ g_slice_free (BinContinueData, data);
+ return;
+ }
+}
+
+static GstBusSyncReply
+bin_bus_handler (GstBus * bus, GstMessage * message, GstBin * bin)
+{
+ GstBinClass *bclass;
+
+ bclass = GST_BIN_GET_CLASS (bin);
+ if (bclass->handle_message)
+ bclass->handle_message (bin, message);
+ else
+ gst_message_unref (message);
+
+ return GST_BUS_DROP;
+}
+
+static void
+bin_push_state_continue (BinContinueData * data)
+{
+ GstBinClass *klass;
+ GstBin *bin;
+
+ /* ref was taken */
+ bin = data->bin;
+ klass = GST_BIN_GET_CLASS (bin);
+
+ GST_DEBUG_OBJECT (bin, "pushing continue on thread pool");
+ g_thread_pool_push (klass->pool, data, NULL);
+}
+
+/* an element started an async state change, if we were not busy with a state
+ * change, we perform a lost state.
+ * This function is called with the OBJECT lock.
+ */
+static void
+bin_handle_async_start (GstBin * bin)
+{
+ GstState old_state, new_state;
+ gboolean toplevel;
+ GstMessage *amessage = NULL;
+
+ if (GST_STATE_RETURN (bin) == GST_STATE_CHANGE_FAILURE)
+ goto had_error;
+
+ /* get our toplevel state */
+ toplevel = BIN_IS_TOPLEVEL (bin);
+
+ /* prepare an ASYNC_START message, we always post the start message even if we
+ * are busy with a state change or when we are NO_PREROLL. */
+ if (!toplevel)
+ /* non toplevel bin, prepare async-start for the parent */
+ amessage = gst_message_new_async_start (GST_OBJECT_CAST (bin));
+
+ if (bin->polling || GST_STATE_PENDING (bin) != GST_STATE_VOID_PENDING)
+ goto was_busy;
+
+ /* async starts are ignored when we are NO_PREROLL */
+ if (GST_STATE_RETURN (bin) == GST_STATE_CHANGE_NO_PREROLL)
+ goto was_no_preroll;
+
+ old_state = GST_STATE (bin);
+
+ /* when we PLAYING we go back to PAUSED, when preroll happens, we go back to
+ * PLAYING after optionally redistributing the base_time. */
+ if (old_state > GST_STATE_PAUSED)
+ new_state = GST_STATE_PAUSED;
+ else
+ new_state = old_state;
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, bin,
+ "lost state of %s, new %s", gst_element_state_get_name (old_state),
+ gst_element_state_get_name (new_state));
+
+ GST_STATE (bin) = new_state;
+ GST_STATE_NEXT (bin) = new_state;
+ GST_STATE_PENDING (bin) = new_state;
+ GST_STATE_RETURN (bin) = GST_STATE_CHANGE_ASYNC;
+ GST_OBJECT_UNLOCK (bin);
+
+ /* post message */
+ _priv_gst_element_state_changed (GST_ELEMENT_CAST (bin), new_state, new_state,
+ new_state);
+
+post_start:
+ if (amessage) {
+ /* post our ASYNC_START. */
+ GST_DEBUG_OBJECT (bin, "posting ASYNC_START to parent");
+ gst_element_post_message (GST_ELEMENT_CAST (bin), amessage);
+ }
+ GST_OBJECT_LOCK (bin);
+
+ return;
+
+had_error:
+ {
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, bin, "we had an error");
+ return;
+ }
+was_busy:
+ {
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, bin, "state change busy");
+ GST_OBJECT_UNLOCK (bin);
+ goto post_start;
+ }
+was_no_preroll:
+ {
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, bin, "ignoring, we are NO_PREROLL");
+ GST_OBJECT_UNLOCK (bin);
+ goto post_start;
+ }
+}
+
+/* this function is called when there are no more async elements in the bin. We
+ * post a state changed message and an ASYNC_DONE message.
+ * This function is called with the OBJECT lock.
+ */
+static void
+bin_handle_async_done (GstBin * bin, GstStateChangeReturn ret,
+ gboolean flag_pending, gboolean reset_time)
+{
+ GstState current, pending, target;
+ GstStateChangeReturn old_ret;
+ GstState old_state, old_next;
+ gboolean toplevel, state_changed = FALSE;
+ GstMessage *amessage = NULL;
+ BinContinueData *cont = NULL;
+
+ if (GST_STATE_RETURN (bin) == GST_STATE_CHANGE_FAILURE)
+ goto had_error;
+
+ pending = GST_STATE_PENDING (bin);
+
+ if (bin->polling)
+ goto was_busy;
+
+ /* check if there is something to commit */
+ if (pending == GST_STATE_VOID_PENDING)
+ goto nothing_pending;
+
+ old_ret = GST_STATE_RETURN (bin);
+ GST_STATE_RETURN (bin) = ret;
+
+ /* move to the next target state */
+ target = GST_STATE_TARGET (bin);
+ pending = GST_STATE_PENDING (bin) = target;
+
+ amessage = gst_message_new_async_done (GST_OBJECT_CAST (bin), reset_time);
+
+ old_state = GST_STATE (bin);
+ /* this is the state we should go to next */
+ old_next = GST_STATE_NEXT (bin);
+
+ if (old_next != GST_STATE_PLAYING) {
+ GST_CAT_INFO_OBJECT (GST_CAT_STATES, bin,
+ "committing state from %s to %s, old pending %s",
+ gst_element_state_get_name (old_state),
+ gst_element_state_get_name (old_next),
+ gst_element_state_get_name (pending));
+
+ /* update current state */
+ current = GST_STATE (bin) = old_next;
+ } else {
+ GST_CAT_INFO_OBJECT (GST_CAT_STATES, bin,
+ "setting state from %s to %s, pending %s",
+ gst_element_state_get_name (old_state),
+ gst_element_state_get_name (old_state),
+ gst_element_state_get_name (pending));
+ current = old_state;
+ }
+
+ /* get our toplevel state */
+ toplevel = BIN_IS_TOPLEVEL (bin);
+
+ /* see if we reached the final state. If we are not toplevel, we also have to
+ * stop here, the parent will continue our state. */
+ if ((pending == current) || !toplevel) {
+ GST_CAT_INFO_OBJECT (GST_CAT_STATES, bin,
+ "completed state change, pending VOID");
+
+ /* mark VOID pending */
+ pending = GST_STATE_VOID_PENDING;
+ GST_STATE_PENDING (bin) = pending;
+ GST_STATE_NEXT (bin) = GST_STATE_VOID_PENDING;
+ } else {
+ GST_CAT_INFO_OBJECT (GST_CAT_STATES, bin,
+ "continue state change, pending %s",
+ gst_element_state_get_name (pending));
+
+ cont = g_slice_new (BinContinueData);
+
+ /* ref to the bin */
+ cont->bin = gst_object_ref (bin);
+ /* cookie to detect concurrent state change */
+ cont->cookie = GST_ELEMENT_CAST (bin)->state_cookie;
+ /* pending target state */
+ cont->pending = pending;
+ /* mark busy */
+ GST_STATE_RETURN (bin) = GST_STATE_CHANGE_ASYNC;
+ GST_STATE_NEXT (bin) = GST_STATE_GET_NEXT (old_state, pending);
+ }
+
+ if (old_next != GST_STATE_PLAYING) {
+ if (old_state != old_next || old_ret == GST_STATE_CHANGE_ASYNC) {
+ state_changed = TRUE;
+ }
+ }
+ GST_OBJECT_UNLOCK (bin);
+
+ if (state_changed) {
+ _priv_gst_element_state_changed (GST_ELEMENT_CAST (bin), old_state,
+ old_next, pending);
+ }
+ if (amessage) {
+ /* post our combined ASYNC_DONE when all is ASYNC_DONE. */
+ GST_DEBUG_OBJECT (bin, "posting ASYNC_DONE to parent");
+ gst_element_post_message (GST_ELEMENT_CAST (bin), amessage);
+ }
+
+ GST_OBJECT_LOCK (bin);
+ if (cont) {
+ /* toplevel, start continue state */
+ GST_DEBUG_OBJECT (bin, "all async-done, starting state continue");
+ bin_push_state_continue (cont);
+ } else {
+ GST_DEBUG_OBJECT (bin, "state change complete");
+ GST_STATE_BROADCAST (bin);
+ }
+ return;
+
+had_error:
+ {
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, bin, "we had an error");
+ return;
+ }
+was_busy:
+ {
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, bin, "state change busy");
+ /* if we were busy with a state change and we are requested to flag a
+ * pending async done, we do so here */
+ if (flag_pending)
+ bin->priv->pending_async_done = TRUE;
+ return;
+ }
+nothing_pending:
+ {
+ GST_CAT_INFO_OBJECT (GST_CAT_STATES, bin, "nothing pending");
+ return;
+ }
+}
+
+static void
+bin_do_eos (GstBin * bin)
+{
+ guint32 seqnum = 0;
+ gboolean eos;
+
+ GST_OBJECT_LOCK (bin);
+ /* If all sinks are EOS, we're in PLAYING and no state change is pending
+ * we forward the EOS message to the parent bin or application
+ */
+ eos = GST_STATE (bin) == GST_STATE_PLAYING
+ && GST_STATE_PENDING (bin) == GST_STATE_VOID_PENDING
+ && is_eos (bin, &seqnum);
+ GST_OBJECT_UNLOCK (bin);
+
+ if (eos
+ && g_atomic_int_compare_and_exchange (&bin->priv->posted_eos, FALSE,
+ TRUE)) {
+ GstMessage *tmessage;
+ tmessage = gst_message_new_eos (GST_OBJECT_CAST (bin));
+ gst_message_set_seqnum (tmessage, seqnum);
+ GST_DEBUG_OBJECT (bin,
+ "all sinks posted EOS, posting seqnum #%" G_GUINT32_FORMAT, seqnum);
+ gst_element_post_message (GST_ELEMENT_CAST (bin), tmessage);
+ }
+}
+
+/* must be called with the object lock. This function releases the lock to post
+ * the message. */
+static void
+bin_do_message_forward (GstBin * bin, GstMessage * message)
+{
+ if (bin->priv->message_forward) {
+ GstMessage *forwarded;
+
+ GST_DEBUG_OBJECT (bin, "pass %s message upward",
+ GST_MESSAGE_TYPE_NAME (message));
+ GST_OBJECT_UNLOCK (bin);
+
+ /* we need to convert these messages to element messages so that our parent
+ * bin can easily ignore them and so that the application can easily
+ * distinguish between the internally forwarded and the real messages. */
+ forwarded = gst_message_new_element (GST_OBJECT_CAST (bin),
+ gst_structure_new ("GstBinForwarded",
+ "message", GST_TYPE_MESSAGE, message, NULL));
+
+ gst_element_post_message (GST_ELEMENT_CAST (bin), forwarded);
+
+ GST_OBJECT_LOCK (bin);
+ }
+}
+
+/* handle child messages:
+ *
+ * This method is called synchronously when a child posts a message on
+ * the internal bus.
+ *
+ * GST_MESSAGE_EOS: This message is only posted by sinks
+ * in the PLAYING state. If all sinks posted the EOS message, post
+ * one upwards.
+ *
+ * GST_MESSAGE_STATE_DIRTY: Deprecated
+ *
+ * GST_MESSAGE_SEGMENT_START: just collect, never forward upwards. If an
+ * element posts segment_start twice, only the last message is kept.
+ *
+ * GST_MESSAGE_SEGMENT_DONE: replace SEGMENT_START message from same poster
+ * with the segment_done message. If there are no more segment_start
+ * messages, post segment_done message upwards.
+ *
+ * GST_MESSAGE_DURATION: remove all previously cached duration messages.
+ * Whenever someone performs a duration query on the bin, we store the
+ * result so we can answer it quicker the next time. Any element that
+ * changes its duration marks our cached values invalid.
+ * This message is also posted upwards. This is currently disabled
+ * because too many elements don't post DURATION messages when the
+ * duration changes.
+ *
+ * GST_MESSAGE_CLOCK_LOST: This message is posted by an element when it
+ * can no longer provide a clock. The default bin behaviour is to
+ * check if the lost clock was the one provided by the bin. If so and
+ * we are currently in the PLAYING state, we forward the message to
+ * our parent.
+ * This message is also generated when we remove a clock provider from
+ * a bin. If this message is received by the application, it should
+ * PAUSE the pipeline and set it back to PLAYING to force a new clock
+ * and a new base_time distribution.
+ *
+ * GST_MESSAGE_CLOCK_PROVIDE: This message is generated when an element
+ * can provide a clock. This mostly happens when we add a new clock
+ * provider to the bin. The default behaviour of the bin is to mark the
+ * currently selected clock as dirty, which will perform a clock
+ * recalculation the next time we are asked to provide a clock.
+ * This message is never sent to the application but is forwarded to
+ * the parent.
+ *
+ * GST_MESSAGE_ASYNC_START: Create an internal ELEMENT message that stores
+ * the state of the element and the fact that the element will need a
+ * new base_time. This message is not forwarded to the application.
+ *
+ * GST_MESSAGE_ASYNC_DONE: Find the internal ELEMENT message we kept for the
+ * element when it posted ASYNC_START. If all elements are done, post a
+ * ASYNC_DONE message to the parent.
+ *
+ * OTHER: post upwards.
+ */
+static void
+gst_bin_handle_message_func (GstBin * bin, GstMessage * message)
+{
+ GstObject *src;
+ GstMessageType type;
+ GstMessage *tmessage;
+ guint32 seqnum;
+
+ src = GST_MESSAGE_SRC (message);
+ type = GST_MESSAGE_TYPE (message);
+
+ GST_DEBUG_OBJECT (bin, "[msg %p] handling child %s message of type %s",
+ message, src ? GST_ELEMENT_NAME (src) : "(NULL)",
+ GST_MESSAGE_TYPE_NAME (message));
+
+ switch (type) {
+ case GST_MESSAGE_ERROR:
+ {
+ GST_OBJECT_LOCK (bin);
+ /* flag error */
+ GST_DEBUG_OBJECT (bin, "got ERROR message, unlocking state change");
+ GST_STATE_RETURN (bin) = GST_STATE_CHANGE_FAILURE;
+ GST_STATE_BROADCAST (bin);
+ GST_OBJECT_UNLOCK (bin);
+
+ goto forward;
+ }
+ case GST_MESSAGE_EOS:
+ {
+
+ /* collect all eos messages from the children */
+ GST_OBJECT_LOCK (bin);
+ bin_do_message_forward (bin, message);
+ /* ref message for future use */
+ bin_replace_message (bin, message, GST_MESSAGE_EOS);
+ GST_OBJECT_UNLOCK (bin);
+
+ bin_do_eos (bin);
+ break;
+ }
+ case GST_MESSAGE_STATE_DIRTY:
+ {
+ GST_WARNING_OBJECT (bin, "received deprecated STATE_DIRTY message");
+
+ /* free message */
+ gst_message_unref (message);
+ break;
+ }
+ case GST_MESSAGE_SEGMENT_START:{
+ gboolean post = FALSE;
+ GstFormat format;
+ gint64 position;
+
+ gst_message_parse_segment_start (message, &format, &position);
+ seqnum = gst_message_get_seqnum (message);
+
+ GST_OBJECT_LOCK (bin);
+ bin_do_message_forward (bin, message);
+ /* if this is the first segment-start, post to parent but not to the
+ * application */
+ if (!find_message (bin, NULL, GST_MESSAGE_SEGMENT_START) &&
+ (GST_OBJECT_PARENT (bin) != NULL)) {
+ post = TRUE;
+ }
+ /* replace any previous segment_start message from this source
+ * with the new segment start message */
+ bin_replace_message (bin, message, GST_MESSAGE_SEGMENT_START);
+ GST_OBJECT_UNLOCK (bin);
+ if (post) {
+ tmessage = gst_message_new_segment_start (GST_OBJECT_CAST (bin),
+ format, position);
+ gst_message_set_seqnum (tmessage, seqnum);
+
+ /* post segment start with initial format and position. */
+ GST_DEBUG_OBJECT (bin, "posting SEGMENT_START (%u) bus message: %p",
+ seqnum, message);
+ gst_element_post_message (GST_ELEMENT_CAST (bin), tmessage);
+ }
+ break;
+ }
+ case GST_MESSAGE_SEGMENT_DONE:
+ {
+ gboolean post = FALSE;
+ GstFormat format;
+ gint64 position;
+
+ gst_message_parse_segment_done (message, &format, &position);
+ seqnum = gst_message_get_seqnum (message);
+
+ GST_OBJECT_LOCK (bin);
+ bin_do_message_forward (bin, message);
+ bin_replace_message (bin, message, GST_MESSAGE_SEGMENT_START);
+ /* if there are no more segment_start messages, everybody posted
+ * a segment_done and we can post one on the bus. */
+
+ /* we don't care who still has a pending segment start */
+ if (!find_message (bin, NULL, GST_MESSAGE_SEGMENT_START)) {
+ /* nothing found */
+ post = TRUE;
+ /* remove all old segment_done messages */
+ bin_remove_messages (bin, NULL, GST_MESSAGE_SEGMENT_DONE);
+ }
+ GST_OBJECT_UNLOCK (bin);
+ if (post) {
+ tmessage = gst_message_new_segment_done (GST_OBJECT_CAST (bin),
+ format, position);
+ gst_message_set_seqnum (tmessage, seqnum);
+
+ /* post segment done with latest format and position. */
+ GST_DEBUG_OBJECT (bin, "posting SEGMENT_DONE (%u) bus message: %p",
+ seqnum, message);
+ gst_element_post_message (GST_ELEMENT_CAST (bin), tmessage);
+ }
+ break;
+ }
+ case GST_MESSAGE_DURATION:
+ {
+ /* remove all cached duration messages, next time somebody asks
+ * for duration, we will recalculate. */
+ GST_OBJECT_LOCK (bin);
+ bin_remove_messages (bin, NULL, GST_MESSAGE_DURATION);
+ GST_OBJECT_UNLOCK (bin);
+ goto forward;
+ }
+ case GST_MESSAGE_CLOCK_LOST:
+ {
+ GstClock **provided_clock_p;
+ GstElement **clock_provider_p;
+ gboolean playing, provided, forward;
+ GstClock *clock;
+
+ gst_message_parse_clock_lost (message, &clock);
+
+ GST_OBJECT_LOCK (bin);
+ bin->clock_dirty = TRUE;
+ /* if we lost the clock that we provided, post to parent but
+ * only if we are PLAYING. */
+ provided = (clock == bin->provided_clock);
+ playing = (GST_STATE (bin) == GST_STATE_PLAYING);
+ forward = playing & provided;
+ if (provided) {
+ GST_DEBUG_OBJECT (bin,
+ "Lost clock %" GST_PTR_FORMAT " provided by %" GST_PTR_FORMAT,
+ bin->provided_clock, bin->clock_provider);
+ provided_clock_p = &bin->provided_clock;
+ clock_provider_p = &bin->clock_provider;
+ gst_object_replace ((GstObject **) provided_clock_p, NULL);
+ gst_object_replace ((GstObject **) clock_provider_p, NULL);
+ }
+ GST_DEBUG_OBJECT (bin, "provided %d, playing %d, forward %d",
+ provided, playing, forward);
+ GST_OBJECT_UNLOCK (bin);
+
+ if (forward)
+ goto forward;
+
+ /* free message */
+ gst_message_unref (message);
+ break;
+ }
+ case GST_MESSAGE_CLOCK_PROVIDE:
+ {
+ gboolean forward;
+
+ GST_OBJECT_LOCK (bin);
+ bin->clock_dirty = TRUE;
+ /* a new clock is available, post to parent but not
+ * to the application */
+ forward = GST_OBJECT_PARENT (bin) != NULL;
+ GST_OBJECT_UNLOCK (bin);
+
+ if (forward)
+ goto forward;
+
+ /* free message */
+ gst_message_unref (message);
+ break;
+ }
+ case GST_MESSAGE_ASYNC_START:
+ {
+ GstState target;
+
+ GST_DEBUG_OBJECT (bin, "ASYNC_START message %p, %s", message,
+ src ? GST_OBJECT_NAME (src) : "(NULL)");
+
+ GST_OBJECT_LOCK (bin);
+ bin_do_message_forward (bin, message);
+
+ /* we ignore the message if we are going to <= READY */
+ if ((target = GST_STATE_TARGET (bin)) <= GST_STATE_READY)
+ goto ignore_start_message;
+
+ /* takes ownership of the message */
+ bin_replace_message (bin, message, GST_MESSAGE_ASYNC_START);
+
+ bin_handle_async_start (bin);
+ GST_OBJECT_UNLOCK (bin);
+ break;
+
+ ignore_start_message:
+ {
+ GST_DEBUG_OBJECT (bin, "ignoring message, target %s",
+ gst_element_state_get_name (target));
+ GST_OBJECT_UNLOCK (bin);
+ gst_message_unref (message);
+ break;
+ }
+ }
+ case GST_MESSAGE_ASYNC_DONE:
+ {
+ gboolean reset_time;
+ GstState target;
+
+ GST_DEBUG_OBJECT (bin, "ASYNC_DONE message %p, %s", message,
+ src ? GST_OBJECT_NAME (src) : "(NULL)");
+
+ gst_message_parse_async_done (message, &reset_time);
+
+ GST_OBJECT_LOCK (bin);
+ bin_do_message_forward (bin, message);
+
+ /* ignore messages if we are shutting down */
+ if ((target = GST_STATE_TARGET (bin)) <= GST_STATE_READY)
+ goto ignore_done_message;
+
+ bin_replace_message (bin, message, GST_MESSAGE_ASYNC_START);
+ /* if there are no more ASYNC_START messages, everybody posted
+ * a ASYNC_DONE and we can post one on the bus. When checking, we
+ * don't care who still has a pending ASYNC_START */
+ if (!find_message (bin, NULL, GST_MESSAGE_ASYNC_START)) {
+ /* nothing found, remove all old ASYNC_DONE messages */
+ bin_remove_messages (bin, NULL, GST_MESSAGE_ASYNC_DONE);
+
+ GST_DEBUG_OBJECT (bin, "async elements commited");
+ /* when we get an async done message when a state change was busy, we
+ * need to set the pending_done flag so that at the end of the state
+ * change we can see if we need to verify pending async elements, hence
+ * the TRUE argument here. */
+ bin_handle_async_done (bin, GST_STATE_CHANGE_SUCCESS, TRUE, reset_time);
+ } else {
+ GST_DEBUG_OBJECT (bin, "there are more async elements pending");
+ }
+ GST_OBJECT_UNLOCK (bin);
+ break;
+
+ ignore_done_message:
+ {
+ GST_DEBUG_OBJECT (bin, "ignoring message, target %s",
+ gst_element_state_get_name (target));
+ GST_OBJECT_UNLOCK (bin);
+ gst_message_unref (message);
+ break;
+ }
+ }
+ case GST_MESSAGE_STRUCTURE_CHANGE:
+ {
+ gboolean busy;
+
+ gst_message_parse_structure_change (message, NULL, NULL, &busy);
+
+ GST_OBJECT_LOCK (bin);
+ if (busy) {
+ /* while the pad is busy, avoid following it when doing state changes.
+ * Don't update the cookie yet, we will do that after the structure
+ * change finished and we are ready to inspect the new updated
+ * structure. */
+ bin_replace_message (bin, message, GST_MESSAGE_STRUCTURE_CHANGE);
+ message = NULL;
+ } else {
+ /* a pad link/unlink ended, signal the state change iterator that we
+ * need to resync by updating the structure_cookie. */
+ bin_remove_messages (bin, GST_MESSAGE_SRC (message),
+ GST_MESSAGE_STRUCTURE_CHANGE);
+ bin->priv->structure_cookie++;
+ }
+ GST_OBJECT_UNLOCK (bin);
+
+ if (message)
+ gst_message_unref (message);
+
+ break;
+ }
+ default:
+ goto forward;
+ }
+ return;
+
+forward:
+ {
+ /* Send all other messages upward */
+ GST_DEBUG_OBJECT (bin, "posting message upward");
+ gst_element_post_message (GST_ELEMENT_CAST (bin), message);
+ return;
+ }
+}
+
+/* generic struct passed to all query fold methods */
+typedef struct
+{
+ GstQuery *query;
+ gint64 min;
+ gint64 max;
+ gboolean live;
+} QueryFold;
+
+typedef void (*QueryInitFunction) (GstBin * bin, QueryFold * fold);
+typedef void (*QueryDoneFunction) (GstBin * bin, QueryFold * fold);
+
+/* for duration/position we collect all durations/positions and take
+ * the MAX of all valid results */
+static void
+bin_query_min_max_init (GstBin * bin, QueryFold * fold)
+{
+ fold->min = 0;
+ fold->max = -1;
+ fold->live = FALSE;
+}
+
+static gboolean
+bin_query_duration_fold (const GValue * vitem, GValue * ret, QueryFold * fold)
+{
+ GstElement *item = g_value_get_object (vitem);
+
+ if (gst_element_query (item, fold->query)) {
+ gint64 duration;
+
+ g_value_set_boolean (ret, TRUE);
+
+ gst_query_parse_duration (fold->query, NULL, &duration);
+
+ GST_DEBUG_OBJECT (item, "got duration %" G_GINT64_FORMAT, duration);
+
+ if (duration > fold->max)
+ fold->max = duration;
+ }
+
+ return TRUE;
+}
+
+static void
+bin_query_duration_done (GstBin * bin, QueryFold * fold)
+{
+ GstFormat format;
+
+ gst_query_parse_duration (fold->query, &format, NULL);
+ /* store max in query result */
+ gst_query_set_duration (fold->query, format, fold->max);
+
+ GST_DEBUG_OBJECT (bin, "max duration %" G_GINT64_FORMAT, fold->max);
+
+ /* and cache now */
+ GST_OBJECT_LOCK (bin);
+ bin->messages = g_list_prepend (bin->messages,
+ gst_message_new_duration (GST_OBJECT_CAST (bin), format, fold->max));
+ GST_OBJECT_UNLOCK (bin);
+}
+
+static gboolean
+bin_query_position_fold (const GValue * vitem, GValue * ret, QueryFold * fold)
+{
+ GstElement *item = g_value_get_object (vitem);
+
+ if (gst_element_query (item, fold->query)) {
+ gint64 position;
+
+ g_value_set_boolean (ret, TRUE);
+
+ gst_query_parse_position (fold->query, NULL, &position);
+
+ GST_DEBUG_OBJECT (item, "got position %" G_GINT64_FORMAT, position);
+
+ if (position > fold->max)
+ fold->max = position;
+ }
+
+ return TRUE;
+}
+
+static void
+bin_query_position_done (GstBin * bin, QueryFold * fold)
+{
+ GstFormat format;
+
+ gst_query_parse_position (fold->query, &format, NULL);
+ /* store max in query result */
+ gst_query_set_position (fold->query, format, fold->max);
+
+ GST_DEBUG_OBJECT (bin, "max position %" G_GINT64_FORMAT, fold->max);
+}
+
+static gboolean
+bin_query_latency_fold (const GValue * vitem, GValue * ret, QueryFold * fold)
+{
+ GstElement *item = g_value_get_object (vitem);
+
+ if (gst_element_query (item, fold->query)) {
+ GstClockTime min, max;
+ gboolean live;
+
+ gst_query_parse_latency (fold->query, &live, &min, &max);
+
+ GST_DEBUG_OBJECT (item,
+ "got latency min %" GST_TIME_FORMAT ", max %" GST_TIME_FORMAT
+ ", live %d", GST_TIME_ARGS (min), GST_TIME_ARGS (max), live);
+
+ /* for the combined latency we collect the MAX of all min latencies and
+ * the MIN of all max latencies */
+ if (live) {
+ if (min > fold->min)
+ fold->min = min;
+ if (fold->max == -1)
+ fold->max = max;
+ else if (max < fold->max)
+ fold->max = max;
+ if (fold->live == FALSE)
+ fold->live = live;
+ }
+ } else {
+ g_value_set_boolean (ret, FALSE);
+ GST_DEBUG_OBJECT (item, "failed query");
+ }
+
+ return TRUE;
+}
+
+static void
+bin_query_latency_done (GstBin * bin, QueryFold * fold)
+{
+ /* store max in query result */
+ gst_query_set_latency (fold->query, fold->live, fold->min, fold->max);
+
+ GST_DEBUG_OBJECT (bin,
+ "latency min %" GST_TIME_FORMAT ", max %" GST_TIME_FORMAT
+ ", live %d", GST_TIME_ARGS (fold->min), GST_TIME_ARGS (fold->max),
+ fold->live);
+}
+
+/* generic fold, return first valid result */
+static gboolean
+bin_query_generic_fold (const GValue * vitem, GValue * ret, QueryFold * fold)
+{
+ GstElement *item = g_value_get_object (vitem);
+ gboolean res;
+
+ if ((res = gst_element_query (item, fold->query))) {
+ g_value_set_boolean (ret, TRUE);
+ GST_DEBUG_OBJECT (item, "answered query %p", fold->query);
+ }
+
+ /* and stop as soon as we have a valid result */
+ return !res;
+}
+
+static gboolean
+gst_bin_query (GstElement * element, GstQuery * query)
+{
+ GstBin *bin = GST_BIN_CAST (element);
+ GstIterator *iter;
+ gboolean res = FALSE;
+ GstIteratorFoldFunction fold_func;
+ QueryInitFunction fold_init = NULL;
+ QueryDoneFunction fold_done = NULL;
+ QueryFold fold_data;
+ GValue ret = { 0 };
+
+ switch (GST_QUERY_TYPE (query)) {
+ case GST_QUERY_DURATION:
+ {
+ GList *cached;
+ GstFormat qformat;
+
+ gst_query_parse_duration (query, &qformat, NULL);
+
+ /* find cached duration query */
+ GST_OBJECT_LOCK (bin);
+ for (cached = bin->messages; cached; cached = g_list_next (cached)) {
+ GstMessage *message = (GstMessage *) cached->data;
+
+ if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_DURATION &&
+ GST_MESSAGE_SRC (message) == GST_OBJECT_CAST (bin)) {
+ GstFormat format;
+ gint64 duration;
+
+ gst_message_parse_duration (message, &format, &duration);
+
+ /* if cached same format, copy duration in query result */
+ if (format == qformat) {
+ GST_DEBUG_OBJECT (bin, "return cached duration %" G_GINT64_FORMAT,
+ duration);
+ GST_OBJECT_UNLOCK (bin);
+
+ gst_query_set_duration (query, qformat, duration);
+ res = TRUE;
+ goto exit;
+ }
+ }
+ }
+ GST_OBJECT_UNLOCK (bin);
+ /* no cached value found, iterate and collect durations */
+ fold_func = (GstIteratorFoldFunction) bin_query_duration_fold;
+ fold_init = bin_query_min_max_init;
+ fold_done = bin_query_duration_done;
+ break;
+ }
+ case GST_QUERY_POSITION:
+ {
+ fold_func = (GstIteratorFoldFunction) bin_query_position_fold;
+ fold_init = bin_query_min_max_init;
+ fold_done = bin_query_position_done;
+ break;
+ }
+ case GST_QUERY_LATENCY:
+ {
+ fold_func = (GstIteratorFoldFunction) bin_query_latency_fold;
+ fold_init = bin_query_min_max_init;
+ fold_done = bin_query_latency_done;
+ res = TRUE;
+ break;
+ }
+ default:
+ fold_func = (GstIteratorFoldFunction) bin_query_generic_fold;
+ break;
+ }
+
+ fold_data.query = query;
+
+ /* set the result of the query to FALSE initially */
+ g_value_init (&ret, G_TYPE_BOOLEAN);
+ g_value_set_boolean (&ret, res);
+
+ iter = gst_bin_iterate_sinks (bin);
+ GST_DEBUG_OBJECT (bin, "Sending query %p (type %s) to sink children",
+ query, GST_QUERY_TYPE_NAME (query));
+
+ if (fold_init)
+ fold_init (bin, &fold_data);
+
+ while (TRUE) {
+ GstIteratorResult ires;
+
+ ires = gst_iterator_fold (iter, fold_func, &ret, &fold_data);
+
+ switch (ires) {
+ case GST_ITERATOR_RESYNC:
+ gst_iterator_resync (iter);
+ if (fold_init)
+ fold_init (bin, &fold_data);
+ g_value_set_boolean (&ret, res);
+ break;
+ case GST_ITERATOR_OK:
+ case GST_ITERATOR_DONE:
+ res = g_value_get_boolean (&ret);
+ if (fold_done != NULL && res)
+ fold_done (bin, &fold_data);
+ goto done;
+ default:
+ res = FALSE;
+ goto done;
+ }
+ }
+done:
+ gst_iterator_free (iter);
+
+exit:
+ GST_DEBUG_OBJECT (bin, "query %p result %d", query, res);
+
+ return res;
+}
+
+static gint
+compare_name (const GValue * velement, const gchar * name)
+{
+ gint eq;
+ GstElement *element = g_value_get_object (velement);
+
+ GST_OBJECT_LOCK (element);
+ eq = strcmp (GST_ELEMENT_NAME (element), name);
+ GST_OBJECT_UNLOCK (element);
+
+ return eq;
+}
+
+/**
+ * gst_bin_get_by_name:
+ * @bin: a #GstBin
+ * @name: the element name to search for
+ *
+ * Gets the element with the given name from a bin. This
+ * function recurses into child bins.
+ *
+ * Returns NULL if no element with the given name is found in the bin.
+ *
+ * MT safe. Caller owns returned reference.
+ *
+ * Returns: (transfer full): the #GstElement with the given name, or NULL
+ */
+GstElement *
+gst_bin_get_by_name (GstBin * bin, const gchar * name)
+{
+ GstIterator *children;
+ GValue result = { 0, };
+ GstElement *element;
+ gboolean found;
+
+ g_return_val_if_fail (GST_IS_BIN (bin), NULL);
+
+ GST_CAT_INFO (GST_CAT_PARENTAGE, "[%s]: looking up child element %s",
+ GST_ELEMENT_NAME (bin), name);
+
+ children = gst_bin_iterate_recurse (bin);
+ found = gst_iterator_find_custom (children,
+ (GCompareFunc) compare_name, &result, (gpointer) name);
+ gst_iterator_free (children);
+
+ if (found) {
+ element = g_value_dup_object (&result);
+ g_value_unset (&result);
+ } else {
+ element = NULL;
+ }
+
+ return element;
+}
+
+/**
+ * gst_bin_get_by_name_recurse_up:
+ * @bin: a #GstBin
+ * @name: the element name to search for
+ *
+ * Gets the element with the given name from this bin. If the
+ * element is not found, a recursion is performed on the parent bin.
+ *
+ * Returns NULL if:
+ * - no element with the given name is found in the bin
+ *
+ * MT safe. Caller owns returned reference.
+ *
+ * Returns: (transfer full): the #GstElement with the given name, or NULL
+ */
+GstElement *
+gst_bin_get_by_name_recurse_up (GstBin * bin, const gchar * name)
+{
+ GstElement *result;
+
+ g_return_val_if_fail (GST_IS_BIN (bin), NULL);
+ g_return_val_if_fail (name != NULL, NULL);
+
+ result = gst_bin_get_by_name (bin, name);
+
+ if (!result) {
+ GstObject *parent;
+
+ parent = gst_object_get_parent (GST_OBJECT_CAST (bin));
+ if (parent) {
+ if (GST_IS_BIN (parent)) {
+ result = gst_bin_get_by_name_recurse_up (GST_BIN_CAST (parent), name);
+ }
+ gst_object_unref (parent);
+ }
+ }
+
+ return result;
+}
+
+static gint
+compare_interface (const GValue * velement, GValue * interface)
+{
+ GstElement *element = g_value_get_object (velement);
+ GType interface_type = (GType) g_value_get_pointer (interface);
+ gint ret;
+
+ if (G_TYPE_CHECK_INSTANCE_TYPE (element, interface_type)) {
+ ret = 0;
+ } else {
+ ret = 1;
+ }
+ return ret;
+}
+
+/**
+ * gst_bin_get_by_interface:
+ * @bin: a #GstBin
+ * @iface: the #GType of an interface
+ *
+ * Looks for an element inside the bin that implements the given
+ * interface. If such an element is found, it returns the element.
+ * You can cast this element to the given interface afterwards. If you want
+ * all elements that implement the interface, use
+ * gst_bin_iterate_all_by_interface(). This function recurses into child bins.
+ *
+ * MT safe. Caller owns returned reference.
+ *
+ * Returns: (transfer full): A #GstElement inside the bin implementing the interface
+ */
+GstElement *
+gst_bin_get_by_interface (GstBin * bin, GType iface)
+{
+ GstIterator *children;
+ GValue result = { 0, };
+ GstElement *element;
+ gboolean found;
+ GValue viface = { 0, };
+
+ g_return_val_if_fail (GST_IS_BIN (bin), NULL);
+ g_return_val_if_fail (G_TYPE_IS_INTERFACE (iface), NULL);
+
+ g_value_init (&viface, G_TYPE_POINTER);
+ g_value_set_pointer (&viface, (gpointer) iface);
+
+ children = gst_bin_iterate_recurse (bin);
+ found = gst_iterator_find_custom (children, (GCompareFunc) compare_interface,
+ &result, &viface);
+ gst_iterator_free (children);
+
+ if (found) {
+ element = g_value_dup_object (&result);
+ g_value_unset (&result);
+ } else {
+ element = NULL;
+ }
+ g_value_unset (&viface);
+
+ return element;
+}
+
+/**
+ * gst_bin_iterate_all_by_interface:
+ * @bin: a #GstBin
+ * @iface: the #GType of an interface
+ *
+ * Looks for all elements inside the bin that implements the given
+ * interface. You can safely cast all returned elements to the given interface.
+ * The function recurses inside child bins. The iterator will yield a series
+ * of #GstElement that should be unreffed after use.
+ *
+ * Each element yielded by the iterator will have its refcount increased, so
+ * unref after use.
+ *
+ * MT safe. Caller owns returned value.
+ *
+ * Returns: (transfer full): a #GstIterator of #GstElement for all elements
+ * in the bin implementing the given interface, or NULL
+ */
+GstIterator *
+gst_bin_iterate_all_by_interface (GstBin * bin, GType iface)
+{
+ GstIterator *children;
+ GstIterator *result;
+ GValue viface = { 0, };
+
+ g_return_val_if_fail (GST_IS_BIN (bin), NULL);
+ g_return_val_if_fail (G_TYPE_IS_INTERFACE (iface), NULL);
+
+ g_value_init (&viface, G_TYPE_POINTER);
+ g_value_set_pointer (&viface, (gpointer) iface);
+
+ children = gst_bin_iterate_recurse (bin);
+ result = gst_iterator_filter (children, (GCompareFunc) compare_interface,
+ &viface);
+
+ g_value_unset (&viface);
+
+ return result;
+}
diff --git a/gst/gstbin.h b/gst/gstbin.h
new file mode 100644
index 0000000..19c7634
--- /dev/null
+++ b/gst/gstbin.h
@@ -0,0 +1,190 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2000 Wim Taymans <wtay@chello.be>
+ *
+ * gstbin.h: Header for GstBin container object
+ *
+ * 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_BIN_H__
+#define __GST_BIN_H__
+
+#include <gst/gstelement.h>
+#include <gst/gstiterator.h>
+#include <gst/gstbus.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_BIN (gst_bin_get_type ())
+#define GST_IS_BIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_BIN))
+#define GST_IS_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_BIN))
+#define GST_BIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_BIN, GstBinClass))
+#define GST_BIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_BIN, GstBin))
+#define GST_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_BIN, GstBinClass))
+#define GST_BIN_CAST(obj) ((GstBin*)(obj))
+
+/**
+ * GstBinFlags:
+ * @GST_BIN_FLAG_LAST: the last enum in the series of flags for bins.
+ * Derived classes can use this as first value in a list of flags.
+ *
+ * GstBinFlags are a set of flags specific to bins. Most are set/used
+ * internally. They can be checked using the GST_OBJECT_FLAG_IS_SET () macro,
+ * and (un)set using GST_OBJECT_FLAG_SET () and GST_OBJECT_FLAG_UNSET ().
+ */
+typedef enum {
+ /* padding */
+ GST_BIN_FLAG_LAST = (GST_ELEMENT_FLAG_LAST << 5)
+} GstBinFlags;
+
+typedef struct _GstBin GstBin;
+typedef struct _GstBinClass GstBinClass;
+typedef struct _GstBinPrivate GstBinPrivate;
+
+/**
+ * GST_BIN_NUMCHILDREN:
+ * @bin: a #GstBin
+ *
+ * Gets the number of children in a bin.
+ */
+#define GST_BIN_NUMCHILDREN(bin) (GST_BIN_CAST(bin)->numchildren)
+/**
+ * GST_BIN_CHILDREN:
+ * @bin: a #GstBin
+ *
+ * Gets the list with children in a bin.
+ */
+#define GST_BIN_CHILDREN(bin) (GST_BIN_CAST(bin)->children)
+/**
+ * GST_BIN_CHILDREN_COOKIE:
+ * @bin: a #GstBin
+ *
+ * Gets the children cookie that watches the children list.
+ */
+#define GST_BIN_CHILDREN_COOKIE(bin) (GST_BIN_CAST(bin)->children_cookie)
+
+/**
+ * GstBin:
+ * @numchildren: the number of children in this bin
+ * @children: the list of children in this bin
+ * @children_cookie: updated whenever @children changes
+ * @child_bus: internal bus for handling child messages
+ * @messages: queued and cached messages
+ * @polling: the bin is currently calculating its state
+ * @state_dirty: the bin needs to recalculate its state (deprecated)
+ * @clock_dirty: the bin needs to select a new clock
+ * @provided_clock: the last clock selected
+ * @clock_provider: the element that provided @provided_clock
+ *
+ * The GstBin base class. Subclasses can access these fields provided
+ * the LOCK is taken.
+ */
+struct _GstBin {
+ GstElement element;
+
+ /*< public >*/ /* with LOCK */
+ /* our children, subclass are supposed to update these
+ * fields to reflect their state with _iterate_*() */
+ gint numchildren;
+ GList *children;
+ guint32 children_cookie;
+
+ GstBus *child_bus;
+ GList *messages;
+
+ gboolean polling;
+ gboolean state_dirty;
+
+ gboolean clock_dirty;
+ GstClock *provided_clock;
+ GstElement *clock_provider;
+
+ /*< private >*/
+ GstBinPrivate *priv;
+
+ gpointer _gst_reserved[GST_PADDING];
+};
+
+/**
+ * GstBinClass:
+ * @parent_class: bin parent class
+ * @add_element: method to add an element to a bin
+ * @remove_element: method to remove an element from a bin
+ * @handle_message: method to handle a message from the children
+ *
+ * Subclasses can override the @add_element and @remove_element to
+ * update the list of children in the bin.
+ *
+ * The @handle_message method can be overridden to implement custom
+ * message handling. @handle_message takes ownership of the message, just like
+ * #gst_element_post_message.
+ */
+struct _GstBinClass {
+ GstElementClass parent_class;
+
+ /*< private >*/
+ GThreadPool *pool;
+
+ /* signals */
+ void (*element_added) (GstBin *bin, GstElement *child);
+ void (*element_removed) (GstBin *bin, GstElement *child);
+
+ /*< public >*/
+ /* virtual methods for subclasses */
+ gboolean (*add_element) (GstBin *bin, GstElement *element);
+ gboolean (*remove_element) (GstBin *bin, GstElement *element);
+
+ void (*handle_message) (GstBin *bin, GstMessage *message);
+
+ /*< private >*/
+ /* signal added 0.10.22 */
+ gboolean (*do_latency) (GstBin *bin);
+
+ /*< private >*/
+ gpointer _gst_reserved[GST_PADDING];
+};
+
+GType gst_bin_get_type (void);
+GstElement* gst_bin_new (const gchar *name);
+
+/* add and remove elements from the bin */
+gboolean gst_bin_add (GstBin *bin, GstElement *element);
+gboolean gst_bin_remove (GstBin *bin, GstElement *element);
+
+/* retrieve a single child */
+GstElement* gst_bin_get_by_name (GstBin *bin, const gchar *name);
+GstElement* gst_bin_get_by_name_recurse_up (GstBin *bin, const gchar *name);
+GstElement* gst_bin_get_by_interface (GstBin *bin, GType iface);
+
+/* retrieve multiple children */
+GstIterator* gst_bin_iterate_elements (GstBin *bin);
+GstIterator* gst_bin_iterate_sorted (GstBin *bin);
+GstIterator* gst_bin_iterate_recurse (GstBin *bin);
+
+GstIterator* gst_bin_iterate_sinks (GstBin *bin);
+GstIterator* gst_bin_iterate_sources (GstBin *bin);
+GstIterator* gst_bin_iterate_all_by_interface (GstBin *bin, GType iface);
+
+/* latency */
+gboolean gst_bin_recalculate_latency (GstBin * bin);
+
+
+G_END_DECLS
+
+
+#endif /* __GST_BIN_H__ */
diff --git a/gst/gstbuffer.c b/gst/gstbuffer.c
new file mode 100644
index 0000000..2366e2b
--- /dev/null
+++ b/gst/gstbuffer.c
@@ -0,0 +1,1618 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2000 Wim Taymans <wtay@chello.be>
+ *
+ * gstbuffer.c: Buffer operations
+ *
+ * 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:gstbuffer
+ * @short_description: Data-passing buffer type, supporting sub-buffers.
+ * @see_also: #GstPad, #GstMiniObject
+ *
+ * Buffers are the basic unit of data transfer in GStreamer. The #GstBuffer
+ * type provides all the state necessary to define the regions of memory as
+ * part of a stream. Region copies are also supported, allowing a smaller
+ * region of a buffer to become its own buffer, with mechanisms in place to
+ * ensure that neither memory space goes away prematurely.
+ *
+ * Buffers are usually created with gst_buffer_new(). After a buffer has been
+ * created one will typically allocate memory for it and set the size of the
+ * buffer data. The following example creates a buffer that can hold a given
+ * video frame with a given width, height and bits per plane.
+ * <example>
+ * <title>Creating a buffer for a video frame</title>
+ * <programlisting>
+ * GstBuffer *buffer;
+ * gint size, width, height, bpp;
+ * ...
+ * size = width * height * bpp;
+ * buffer = gst_buffer_new ();
+ * GST_BUFFER_SIZE (buffer) = size;
+ * GST_BUFFER_MALLOCDATA (buffer) = g_malloc (size);
+ * GST_BUFFER_DATA (buffer) = GST_BUFFER_MALLOCDATA (buffer);
+ * ...
+ * </programlisting>
+ * </example>
+ *
+ * Alternatively, use gst_buffer_new_and_alloc()
+ * to create a buffer with preallocated data of a given size.
+ *
+ * The data pointed to by the buffer can be retrieved with the GST_BUFFER_DATA()
+ * macro. The size of the data can be found with GST_BUFFER_SIZE(). For buffers
+ * of size 0, the data pointer is undefined (usually NULL) and should never be used.
+ *
+ * If an element knows what pad you will push the buffer out on, it should use
+ * gst_pad_alloc_buffer() instead to create a buffer. This allows downstream
+ * elements to provide special buffers to write in, like hardware buffers.
+ *
+ * A buffer has a pointer to a #GstCaps describing the media type of the data
+ * in the buffer. Attach caps to the buffer with gst_buffer_set_caps(); this
+ * is typically done before pushing out a buffer using gst_pad_push() so that
+ * the downstream element knows the type of the buffer.
+ *
+ * A buffer will usually have a timestamp, and a duration, but neither of these
+ * are guaranteed (they may be set to #GST_CLOCK_TIME_NONE). Whenever a
+ * meaningful value can be given for these, they should be set. The timestamp
+ * and duration are measured in nanoseconds (they are #GstClockTime values).
+ *
+ * A buffer can also have one or both of a start and an end offset. These are
+ * media-type specific. For video buffers, the start offset will generally be
+ * the frame number. For audio buffers, it will be the number of samples
+ * produced so far. For compressed data, it could be the byte offset in a
+ * source or destination file. Likewise, the end offset will be the offset of
+ * the end of the buffer. These can only be meaningfully interpreted if you
+ * know the media type of the buffer (the #GstCaps set on it). Either or both
+ * can be set to #GST_BUFFER_OFFSET_NONE.
+ *
+ * gst_buffer_ref() is used to increase the refcount of a buffer. This must be
+ * done when you want to keep a handle to the buffer after pushing it to the
+ * next element.
+ *
+ * To efficiently create a smaller buffer out of an existing one, you can
+ * use gst_buffer_copy_region().
+ *
+ * If a plug-in wants to modify the buffer data or metadata in-place, it should
+ * first obtain a buffer that is safe to modify by using
+ * gst_buffer_make_writable(). This function is optimized so that a copy will
+ * only be made when it is necessary.
+ *
+ * Several flags of the buffer can be set and unset with the
+ * GST_BUFFER_FLAG_SET() and GST_BUFFER_FLAG_UNSET() macros. Use
+ * GST_BUFFER_FLAG_IS_SET() to test if a certain #GstBufferFlag is set.
+ *
+ * Buffers can be efficiently merged into a larger buffer with
+ * gst_buffer_span(), which avoids memory copies when the gst_buffer_is_span_fast()
+ * function returns TRUE.
+ *
+ * An element should either unref the buffer or push it out on a src pad
+ * using gst_pad_push() (see #GstPad).
+ *
+ * Buffers are usually freed by unreffing them with gst_buffer_unref(). When
+ * the refcount drops to 0, any data pointed to by the buffer is unreffed as
+ * well.
+ *
+ * Last reviewed on March 30, 2011 (0.11.0)
+ */
+#include "gst_private.h"
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include "gstbuffer.h"
+#include "gstbufferpool.h"
+#include "gstinfo.h"
+#include "gstutils.h"
+#include "gstminiobject.h"
+#include "gstversion.h"
+
+GType _gst_buffer_type = 0;
+
+static GstMemory *_gst_buffer_arr_span (GstMemory ** mem[], gsize len[],
+ guint n, gsize offset, gsize size, gboolean writable);
+
+typedef struct _GstMetaItem GstMetaItem;
+
+struct _GstMetaItem
+{
+ GstMetaItem *next;
+ GstMeta meta;
+};
+#define ITEM_SIZE(info) ((info)->size + sizeof (GstMetaItem))
+
+#define GST_BUFFER_MEM_MAX 16
+
+#define GST_BUFFER_MEM_LEN(b) (((GstBufferImpl *)(b))->len)
+#define GST_BUFFER_MEM_ARRAY(b) (((GstBufferImpl *)(b))->mem)
+#define GST_BUFFER_MEM_PTR(b,i) (((GstBufferImpl *)(b))->mem[i])
+#define GST_BUFFER_BUFMEM(b) (((GstBufferImpl *)(b))->bufmem)
+#define GST_BUFFER_META(b) (((GstBufferImpl *)(b))->item)
+
+typedef struct
+{
+ GstBuffer buffer;
+
+ /* the memory blocks */
+ guint len;
+ GstMemory *mem[GST_BUFFER_MEM_MAX];
+
+ /* memory of the buffer when allocated from 1 chunk */
+ GstMemory *bufmem;
+
+ /* FIXME, make metadata allocation more efficient by using part of the
+ * GstBufferImpl */
+ GstMetaItem *item;
+} GstBufferImpl;
+
+static GstMemory *
+_span_memory (GstBuffer * buffer, gsize offset, gsize size, gboolean writable)
+{
+ GstMemory *span, **mem[1];
+ gsize len[1];
+
+ /* not enough room, span buffers */
+ mem[0] = GST_BUFFER_MEM_ARRAY (buffer);
+ len[0] = GST_BUFFER_MEM_LEN (buffer);
+
+ if (size == -1)
+ size = gst_buffer_get_size (buffer);
+
+ span = _gst_buffer_arr_span (mem, len, 1, offset, size, writable);
+
+ return span;
+}
+
+static void
+_replace_memory (GstBuffer * buffer, GstMemory * mem)
+{
+ gsize len, i;
+
+ /* unref old buffers */
+ len = GST_BUFFER_MEM_LEN (buffer);
+ for (i = 0; i < len; i++)
+ gst_memory_unref (GST_BUFFER_MEM_PTR (buffer, i));
+
+ /* replace with single spanned buffer */
+ GST_BUFFER_MEM_PTR (buffer, 0) = mem;
+ GST_BUFFER_MEM_LEN (buffer) = 1;
+}
+
+static inline void
+_memory_add (GstBuffer * buffer, guint idx, GstMemory * mem)
+{
+ guint i, len = GST_BUFFER_MEM_LEN (buffer);
+
+ if (G_UNLIKELY (len >= GST_BUFFER_MEM_MAX)) {
+ /* too many buffer, span them. */
+ /* FIXME, there is room for improvement here: We could only try to merge
+ * 2 buffers to make some room. If we can't efficiently merge 2 buffers we
+ * could try to only merge the two smallest buffers to avoid memcpy, etc. */
+ _replace_memory (buffer, _span_memory (buffer, 0, -1, FALSE));
+ /* we now have 1 single spanned buffer */
+ len = 1;
+ }
+
+ if (idx == -1)
+ idx = len;
+
+ for (i = len; i > idx; i--) {
+ /* move buffers to insert, FIXME, we need to insert first and then merge */
+ GST_BUFFER_MEM_PTR (buffer, i) = GST_BUFFER_MEM_PTR (buffer, i - 1);
+ }
+ /* and insert the new buffer */
+ GST_BUFFER_MEM_PTR (buffer, idx) = mem;
+ GST_BUFFER_MEM_LEN (buffer) = len + 1;
+}
+
+GST_DEFINE_MINI_OBJECT_TYPE (GstBuffer, gst_buffer);
+
+void
+_priv_gst_buffer_initialize (void)
+{
+ _gst_buffer_type = gst_buffer_get_type ();
+}
+
+/**
+ * gst_buffer_copy_into:
+ * @dest: a destination #GstBuffer
+ * @src: a source #GstBuffer
+ * @flags: flags indicating what metadata fields should be copied.
+ * @offset: offset to copy from
+ * @size: total size to copy. If -1, all data is copied.
+ *
+ * Copies the information from @src into @dest.
+ *
+ * @flags indicate which fields will be copied.
+ */
+void
+gst_buffer_copy_into (GstBuffer * dest, GstBuffer * src,
+ GstBufferCopyFlags flags, gsize offset, gsize size)
+{
+ GstMetaItem *walk;
+ gsize bufsize;
+
+ g_return_if_fail (dest != NULL);
+ g_return_if_fail (src != NULL);
+
+ /* nothing to copy if the buffers are the same */
+ if (G_UNLIKELY (dest == src))
+ return;
+
+ g_return_if_fail (gst_buffer_is_writable (dest));
+
+ bufsize = gst_buffer_get_size (src);
+ g_return_if_fail (bufsize >= offset);
+ if (size == -1)
+ size = bufsize - offset;
+ g_return_if_fail (bufsize >= offset + size);
+
+ GST_CAT_LOG (GST_CAT_BUFFER, "copy %p to %p, offset %" G_GSIZE_FORMAT
+ "-%" G_GSIZE_FORMAT "/%" G_GSIZE_FORMAT, src, dest, offset, size,
+ bufsize);
+
+ if (flags & GST_BUFFER_COPY_FLAGS) {
+ /* copy flags */
+ GST_MINI_OBJECT_FLAGS (dest) = GST_MINI_OBJECT_FLAGS (src);
+ }
+
+ if (flags & GST_BUFFER_COPY_TIMESTAMPS) {
+ if (offset == 0) {
+ GST_BUFFER_TIMESTAMP (dest) = GST_BUFFER_TIMESTAMP (src);
+ GST_BUFFER_OFFSET (dest) = GST_BUFFER_OFFSET (src);
+ if (size == bufsize) {
+ GST_BUFFER_DURATION (dest) = GST_BUFFER_DURATION (src);
+ GST_BUFFER_OFFSET_END (dest) = GST_BUFFER_OFFSET_END (src);
+ }
+ } else {
+ GST_BUFFER_TIMESTAMP (dest) = GST_CLOCK_TIME_NONE;
+ GST_BUFFER_DURATION (dest) = GST_CLOCK_TIME_NONE;
+ GST_BUFFER_OFFSET (dest) = GST_BUFFER_OFFSET_NONE;
+ GST_BUFFER_OFFSET_END (dest) = GST_BUFFER_OFFSET_NONE;
+ }
+ }
+
+ if (flags & GST_BUFFER_COPY_MEMORY) {
+ GstMemory *mem;
+ gsize skip, left, len, i, bsize;
+
+ len = GST_BUFFER_MEM_LEN (src);
+ left = size;
+ skip = offset;
+
+ /* copy and make regions of the memory */
+ for (i = 0; i < len && left > 0; i++) {
+ mem = GST_BUFFER_MEM_PTR (src, i);
+ bsize = gst_memory_get_sizes (mem, NULL, NULL);
+
+ if (bsize <= skip) {
+ /* don't copy buffer */
+ skip -= bsize;
+ } else {
+ gsize tocopy;
+
+ tocopy = MIN (bsize - skip, left);
+ if (mem->flags & GST_MEMORY_FLAG_NO_SHARE) {
+ /* no share, always copy then */
+ mem = gst_memory_copy (mem, skip, tocopy);
+ skip = 0;
+ } else if (tocopy < bsize) {
+ /* we need to clip something */
+ mem = gst_memory_share (mem, skip, tocopy);
+ skip = 0;
+ } else {
+ mem = gst_memory_ref (mem);
+ }
+ _memory_add (dest, -1, mem);
+ left -= tocopy;
+ }
+ }
+ if (flags & GST_BUFFER_COPY_MERGE) {
+ _replace_memory (dest, _span_memory (dest, 0, size, FALSE));
+ }
+ }
+
+ for (walk = GST_BUFFER_META (src); walk; walk = walk->next) {
+ GstMeta *meta = &walk->meta;
+ const GstMetaInfo *info = meta->info;
+
+ if (info->copy_func)
+ info->copy_func (dest, meta, src, offset, size);
+ }
+}
+
+static GstBuffer *
+_gst_buffer_copy (GstBuffer * buffer)
+{
+ GstBuffer *copy;
+
+ g_return_val_if_fail (buffer != NULL, NULL);
+
+ /* create a fresh new buffer */
+ copy = gst_buffer_new ();
+
+ /* we simply copy everything from our parent */
+ gst_buffer_copy_into (copy, buffer, GST_BUFFER_COPY_ALL, 0, -1);
+
+ return copy;
+}
+
+/* the default dispose function revives the buffer and returns it to the
+ * pool when there is a pool */
+static gboolean
+_gst_buffer_dispose (GstBuffer * buffer)
+{
+ GstBufferPool *pool;
+
+ /* no pool, do free */
+ if ((pool = buffer->pool) == NULL)
+ return TRUE;
+
+ /* keep the buffer alive */
+ gst_buffer_ref (buffer);
+ /* return the buffer to the pool */
+ GST_CAT_LOG (GST_CAT_BUFFER, "release %p to pool %p", buffer, pool);
+ gst_buffer_pool_release_buffer (pool, buffer);
+
+ return FALSE;
+}
+
+static void
+_gst_buffer_free (GstBuffer * buffer)
+{
+ GstMetaItem *walk, *next;
+ guint i, len;
+ gsize msize;
+
+ g_return_if_fail (buffer != NULL);
+
+ GST_CAT_LOG (GST_CAT_BUFFER, "finalize %p", buffer);
+
+ /* free metadata */
+ for (walk = GST_BUFFER_META (buffer); walk; walk = next) {
+ GstMeta *meta = &walk->meta;
+ const GstMetaInfo *info = meta->info;
+
+ /* call free_func if any */
+ if (info->free_func)
+ info->free_func (meta, buffer);
+
+ next = walk->next;
+ /* and free the slice */
+ g_slice_free1 (ITEM_SIZE (info), walk);
+ }
+
+ /* get the size, when unreffing the memory, we could also unref the buffer
+ * itself */
+ msize = GST_MINI_OBJECT_SIZE (buffer);
+
+ /* free our memory */
+ len = GST_BUFFER_MEM_LEN (buffer);
+ for (i = 0; i < len; i++)
+ gst_memory_unref (GST_BUFFER_MEM_PTR (buffer, i));
+
+ /* we set msize to 0 when the buffer is part of the memory block */
+ if (msize)
+ g_slice_free1 (msize, buffer);
+ else
+ gst_memory_unref (GST_BUFFER_BUFMEM (buffer));
+}
+
+static void
+gst_buffer_init (GstBufferImpl * buffer, gsize size)
+{
+ gst_mini_object_init (GST_MINI_OBJECT_CAST (buffer), _gst_buffer_type, size);
+
+ buffer->buffer.mini_object.copy =
+ (GstMiniObjectCopyFunction) _gst_buffer_copy;
+ buffer->buffer.mini_object.dispose =
+ (GstMiniObjectDisposeFunction) _gst_buffer_dispose;
+ buffer->buffer.mini_object.free =
+ (GstMiniObjectFreeFunction) _gst_buffer_free;
+
+ GST_BUFFER (buffer)->pool = NULL;
+ GST_BUFFER_TIMESTAMP (buffer) = GST_CLOCK_TIME_NONE;
+ GST_BUFFER_DURATION (buffer) = GST_CLOCK_TIME_NONE;
+ GST_BUFFER_OFFSET (buffer) = GST_BUFFER_OFFSET_NONE;
+ GST_BUFFER_OFFSET_END (buffer) = GST_BUFFER_OFFSET_NONE;
+
+ GST_BUFFER_MEM_LEN (buffer) = 0;
+ GST_BUFFER_META (buffer) = NULL;
+}
+
+/**
+ * gst_buffer_new:
+ *
+ * Creates a newly allocated buffer without any data.
+ *
+ * MT safe.
+ *
+ * Returns: (transfer full): the new #GstBuffer.
+ */
+GstBuffer *
+gst_buffer_new (void)
+{
+ GstBufferImpl *newbuf;
+
+ newbuf = g_slice_new (GstBufferImpl);
+ GST_CAT_LOG (GST_CAT_BUFFER, "new %p", newbuf);
+
+ gst_buffer_init (newbuf, sizeof (GstBufferImpl));
+
+ return GST_BUFFER_CAST (newbuf);
+}
+
+/**
+ * gst_buffer_new_allocate:
+ * @allocator: the #GstAllocator to use
+ * @size: the size in bytes of the new buffer's data.
+ * @align: the alignment of the buffer memory
+ *
+ * Tries to create a newly allocated buffer with data of the given size and
+ * alignment from @allocator. If the requested amount of memory can't be
+ * allocated, NULL will be returned. The allocated buffer memory is not cleared.
+ *
+ * When @allocator is NULL, the default memory allocator will be used.
+ *
+ * Allocator buffer memory will be aligned to multiples of (@align + 1) bytes.
+ *
+ * Note that when @size == 0, the buffer will not have memory associated with it.
+ *
+ * MT safe.
+ *
+ * Returns: (transfer full): a new #GstBuffer, or NULL if the memory couldn't
+ * be allocated.
+ */
+GstBuffer *
+gst_buffer_new_allocate (const GstAllocator * allocator, gsize size,
+ gsize align)
+{
+ GstBuffer *newbuf;
+ GstMemory *mem;
+#if 0
+ guint8 *data;
+ gsize asize;
+#endif
+
+#if 1
+ if (size > 0) {
+ mem = gst_allocator_alloc (allocator, size, align);
+ if (G_UNLIKELY (mem == NULL))
+ goto no_memory;
+ } else {
+ mem = NULL;
+ }
+
+ newbuf = gst_buffer_new ();
+
+ if (mem != NULL)
+ _memory_add (newbuf, -1, mem);
+
+ GST_CAT_LOG (GST_CAT_BUFFER,
+ "new buffer %p of size %" G_GSIZE_FORMAT " from allocator %p", newbuf,
+ size, allocator);
+#endif
+
+#if 0
+ asize = sizeof (GstBufferImpl) + size;
+ data = g_slice_alloc (asize);
+ if (G_UNLIKELY (data == NULL))
+ goto no_memory;
+
+ newbuf = GST_BUFFER_CAST (data);
+
+ gst_buffer_init ((GstBufferImpl *) data, asize);
+ if (size > 0) {
+ mem = gst_memory_new_wrapped (0, data + sizeof (GstBufferImpl), NULL,
+ size, 0, size);
+ _memory_add (newbuf, -1, mem);
+ }
+#endif
+
+#if 0
+ /* allocate memory and buffer, it might be interesting to do this but there
+ * are many complications. We need to keep the memory mapped to access the
+ * buffer fields and the memory for the buffer might be just very slow. We
+ * also need to do some more magic to get the alignment right. */
+ asize = sizeof (GstBufferImpl) + size;
+ mem = gst_allocator_alloc (allocator, asize, align);
+ if (G_UNLIKELY (mem == NULL))
+ goto no_memory;
+
+ /* map the data part and init the buffer in it, set the buffer size to 0 so
+ * that a finalize won't free the buffer */
+ data = gst_memory_map (mem, &asize, NULL, GST_MAP_WRITE);
+ gst_buffer_init ((GstBufferImpl *) data, 0);
+ gst_memory_unmap (mem, data, asize);
+
+ /* strip off the buffer */
+ gst_memory_resize (mem, sizeof (GstBufferImpl), size);
+
+ newbuf = GST_BUFFER_CAST (data);
+ GST_BUFFER_BUFMEM (newbuf) = mem;
+
+ if (size > 0)
+ _memory_add (newbuf, -1, gst_memory_ref (mem));
+#endif
+
+ return newbuf;
+
+ /* ERRORS */
+no_memory:
+ {
+ GST_CAT_WARNING (GST_CAT_BUFFER,
+ "failed to allocate %" G_GSIZE_FORMAT " bytes", size);
+ return NULL;
+ }
+}
+
+/**
+ * gst_buffer_new_wrapped_full:
+ * @data: data to wrap
+ * @free_func: function to free @data
+ * @offset: offset in @data of valid data
+ * @size: size of valid data in @data starting at @offset
+ *
+ * Creates a new buffer that wraps the given @data. Valid data is set
+ * to start at @offset and up to @size. If no @free_func is provided,
+ * buffer memory is marked READONLY.
+ *
+ * MT safe.
+ *
+ * Returns: (transfer full): a new #GstBuffer
+ */
+GstBuffer *
+gst_buffer_new_wrapped_full (gpointer data, GFreeFunc free_func, gsize offset,
+ gsize size)
+{
+ GstBuffer *newbuf;
+
+ g_return_val_if_fail (offset <= size, NULL);
+
+ newbuf = gst_buffer_new ();
+ gst_buffer_take_memory (newbuf, -1,
+ gst_memory_new_wrapped (free_func ? 0 : GST_MEMORY_FLAG_READONLY,
+ data, free_func, offset + size, offset, size));
+
+ return newbuf;
+}
+
+/**
+ * gst_buffer_new_wrapped:
+ * @data: data to wrap
+ * @size: allocated size of @data
+ *
+ * Creates a new buffer that wraps the given @data.
+ *
+ * MT safe.
+ *
+ * Returns: (transfer full): a new #GstBuffer
+ */
+GstBuffer *
+gst_buffer_new_wrapped (gpointer data, gsize size)
+{
+ return gst_buffer_new_wrapped_full (data, g_free, 0, size);
+}
+
+/**
+ * gst_buffer_n_memory:
+ * @buffer: a #GstBuffer.
+ *
+ * Get the amount of memory blocks that this buffer has.
+ *
+ * Returns: (transfer full): the amount of memory block in this buffer.
+ */
+guint
+gst_buffer_n_memory (GstBuffer * buffer)
+{
+ g_return_val_if_fail (GST_IS_BUFFER (buffer), 0);
+
+ return GST_BUFFER_MEM_LEN (buffer);
+}
+
+/**
+ * gst_buffer_take_memory:
+ * @buffer: a #GstBuffer.
+ * @idx: the index to add the memory at, or -1 to append it to the end
+ * @mem: (transfer full): a #GstMemory.
+ *
+ * Add the memory block @mem to @buffer at @idx. This function takes ownership
+ * of @mem and thus doesn't increase its refcount.
+ */
+void
+gst_buffer_take_memory (GstBuffer * buffer, gint idx, GstMemory * mem)
+{
+ g_return_if_fail (GST_IS_BUFFER (buffer));
+ g_return_if_fail (gst_buffer_is_writable (buffer));
+ g_return_if_fail (mem != NULL);
+ g_return_if_fail (idx == -1 ||
+ (idx >= 0 && idx <= GST_BUFFER_MEM_LEN (buffer)));
+
+ _memory_add (buffer, idx, mem);
+}
+
+static GstMemory *
+_get_memory (GstBuffer * buffer, guint idx, gboolean write)
+{
+ GstMemory *mem;
+
+ mem = GST_BUFFER_MEM_PTR (buffer, idx);
+
+ if (G_UNLIKELY (write && !GST_MEMORY_IS_WRITABLE (mem))) {
+ GstMemory *copy;
+ GST_CAT_LOG (GST_CAT_BUFFER,
+ "making writable copy of memory %p in buffer %p", mem, buffer);
+ /* replace with a writable copy */
+ copy = gst_memory_copy (mem, 0, -1);
+ GST_BUFFER_MEM_PTR (buffer, idx) = copy;
+ gst_memory_unref (mem);
+ mem = copy;
+ }
+ return mem;
+}
+
+/**
+ * gst_buffer_peek_memory:
+ * @buffer: a #GstBuffer.
+ * @idx: an index
+ * @flags: #GstMapFlags
+ *
+ * Get the memory block in @buffer at @idx for memory access in @flags.
+ * This function does not return a refcount to the memory block. The memory
+ * block stays valid for as long as the caller has a valid reference to @buffer.
+ *
+ * @buffer should be writable when @flags contains #GST_MAP_WRITE. If the memory
+ * at @idx is not writable, a new writable copy will be installed in @buffer and
+ * returned.
+ *
+ * Returns: a #GstMemory at @idx.
+ */
+GstMemory *
+gst_buffer_peek_memory (GstBuffer * buffer, guint idx, GstMapFlags flags)
+{
+ GstMemory *mem;
+ gboolean write;
+
+ write = (flags & GST_MAP_WRITE) != 0;
+
+ g_return_val_if_fail (GST_IS_BUFFER (buffer), NULL);
+ g_return_val_if_fail (idx < GST_BUFFER_MEM_LEN (buffer), NULL);
+
+ /* check if we can write when asked for write access */
+ if (G_UNLIKELY (write && !gst_buffer_is_writable (buffer)))
+ goto not_writable;
+
+ mem = _get_memory (buffer, idx, write);
+
+ return mem;
+
+ /* ERRORS */
+not_writable:
+ {
+ g_return_val_if_fail (gst_buffer_is_writable (buffer), NULL);
+ return NULL;
+ }
+}
+
+/**
+ * gst_buffer_remove_memory_range:
+ * @buffer: a #GstBuffer.
+ * @idx: an index
+ * @length: a length
+ *
+ * Remove @len memory blocks in @buffer starting from @idx.
+ *
+ * @length can be -1, in which case all memory starting from @idx is removed.
+ */
+void
+gst_buffer_remove_memory_range (GstBuffer * buffer, guint idx, guint length)
+{
+ guint len, i, end;
+
+ g_return_if_fail (GST_IS_BUFFER (buffer));
+ g_return_if_fail (gst_buffer_is_writable (buffer));
+
+ len = GST_BUFFER_MEM_LEN (buffer);
+ if (length == -1) {
+ g_return_if_fail (idx < len);
+ length = len - idx;
+ }
+
+ end = idx + length;
+ for (i = idx; i < end; i++)
+ gst_memory_unref (GST_BUFFER_MEM_PTR (buffer, i));
+
+ if (end != len) {
+ g_memmove (&GST_BUFFER_MEM_PTR (buffer, idx),
+ &GST_BUFFER_MEM_PTR (buffer, end), (len - end) * sizeof (gpointer));
+ }
+ GST_BUFFER_MEM_LEN (buffer) = len - length;
+}
+
+/**
+ * gst_buffer_get_sizes:
+ * @buffer: a #GstBuffer.
+ * @offset: a pointer to the offset
+ * @maxsize: a pointer to the maxsize
+ *
+ * Get the total size of all memory blocks in @buffer.
+ *
+ * When not %NULL, @offset will contain the offset of the data in the first
+ * memory block in @buffer and @maxsize will contain the sum of the size
+ * and @offset and the amount of extra padding on the last memory block.
+ * @offset and @maxsize can be used to resize the buffer with
+ * gst_buffer_resize().
+ *
+ * Returns: the total size of the memory in @buffer.
+ */
+gsize
+gst_buffer_get_sizes (GstBuffer * buffer, gsize * offset, gsize * maxsize)
+{
+ guint len;
+ gsize size;
+ GstMemory *mem;
+
+ g_return_val_if_fail (GST_IS_BUFFER (buffer), 0);
+
+ len = GST_BUFFER_MEM_LEN (buffer);
+
+ if (G_LIKELY (len == 1)) {
+ /* common case */
+ mem = GST_BUFFER_MEM_PTR (buffer, 0);
+ size = gst_memory_get_sizes (mem, offset, maxsize);
+ } else {
+ guint i;
+ gsize extra, offs;
+
+ size = offs = extra = 0;
+ for (i = 0; i < len; i++) {
+ gsize s, o, ms;
+
+ mem = GST_BUFFER_MEM_PTR (buffer, i);
+ s = gst_memory_get_sizes (mem, &o, &ms);
+
+ if (s) {
+ if (size == 0)
+ /* first size, take accumulated data before as the offset */
+ offs = extra + o;
+ /* add sizes */
+ size += s;
+ /* save the amount of data after this block */
+ extra = ms - (o + s);
+ } else {
+ /* empty block, add as extra */
+ extra += ms;
+ }
+ }
+ if (offset)
+ *offset = offs;
+ if (maxsize)
+ *maxsize = offs + size + extra;
+ }
+ return size;
+}
+
+/**
+ * gst_buffer_resize:
+ * @buffer: a #GstBuffer.
+ * @offset: the offset adjustement
+ * @size: the new size
+ *
+ * Set the total size of the buffer
+ */
+void
+gst_buffer_resize (GstBuffer * buffer, gssize offset, gsize size)
+{
+ guint len;
+ guint i;
+ gsize bsize, bufsize, bufoffs, bufmax;
+ GstMemory *mem;
+
+ g_return_if_fail (gst_buffer_is_writable (buffer));
+
+ bufsize = gst_buffer_get_sizes (buffer, &bufoffs, &bufmax);
+
+ GST_CAT_LOG (GST_CAT_BUFFER, "trim %p %" G_GSSIZE_FORMAT "-%" G_GSIZE_FORMAT
+ " size:%" G_GSIZE_FORMAT " offs:%" G_GSIZE_FORMAT " max:%" G_GSIZE_FORMAT,
+ buffer, offset, size, bufsize, bufoffs, bufmax);
+
+ /* we can't go back further than the current offset or past the end of the
+ * buffer */
+ g_return_if_fail ((offset < 0 && bufoffs >= -offset) || (offset >= 0
+ && bufoffs + offset <= bufmax));
+ if (size == -1) {
+ g_return_if_fail (bufsize >= offset);
+ size = bufsize - offset;
+ }
+ g_return_if_fail (bufmax >= bufoffs + offset + size);
+
+ len = GST_BUFFER_MEM_LEN (buffer);
+
+ /* copy and trim */
+ for (i = 0; i < len; i++) {
+ gsize left, noffs;
+
+ mem = GST_BUFFER_MEM_PTR (buffer, i);
+ bsize = gst_memory_get_sizes (mem, NULL, NULL);
+
+ noffs = 0;
+ /* last buffer always gets resized to the remaining size */
+ if (i + 1 == len)
+ left = size;
+ /* shrink buffers before the offset */
+ else if ((gssize) bsize <= offset) {
+ left = 0;
+ noffs = offset - bsize;
+ offset = 0;
+ }
+ /* clip other buffers */
+ else
+ left = MIN (bsize - offset, size);
+
+ if (offset != 0 || left != bsize) {
+ /* we need to clip something */
+ if (GST_MEMORY_IS_WRITABLE (mem)) {
+ gst_memory_resize (mem, offset, left);
+ } else {
+ GstMemory *tmp;
+
+ if (mem->flags & GST_MEMORY_FLAG_NO_SHARE)
+ tmp = gst_memory_copy (mem, offset, left);
+ else
+ tmp = gst_memory_share (mem, offset, left);
+
+ gst_memory_unref (mem);
+ mem = tmp;
+ }
+ }
+ offset = noffs;
+ size -= left;
+
+ GST_BUFFER_MEM_PTR (buffer, i) = mem;
+ }
+}
+
+/**
+ * gst_buffer_map:
+ * @buffer: a #GstBuffer.
+ * @size: a location for the size
+ * @maxsize: a location for the max size
+ * @flags: flags for the mapping
+ *
+ * This function return a pointer to the memory in @buffer. @flags describe the
+ * desired access of the memory. When @flags is #GST_MAP_WRITE, @buffer should
+ * be writable (as returned from gst_buffer_is_writable()).
+ *
+ * @size and @maxsize will contain the current valid number of bytes in the
+ * returned memory area and the total maximum mount of bytes available in the
+ * returned memory area respectively.
+ *
+ * When @buffer is writable but the memory isn't, a writable copy will
+ * automatically be created and returned. The readonly copy of the buffer memory
+ * will then also be replaced with this writable copy.
+ *
+ * When the buffer contains multiple memory blocks, the returned pointer will be
+ * a concatenation of the memory blocks.
+ *
+ * Returns: a pointer to the memory for the buffer.
+ */
+gpointer
+gst_buffer_map (GstBuffer * buffer, gsize * size, gsize * maxsize,
+ GstMapFlags flags)
+{
+ guint len;
+ gpointer data;
+ GstMemory *mem;
+ gboolean write, writable;
+
+ g_return_val_if_fail (GST_IS_BUFFER (buffer), NULL);
+
+ write = (flags & GST_MAP_WRITE) != 0;
+ writable = gst_buffer_is_writable (buffer);
+
+ /* check if we can write when asked for write access */
+ if (G_UNLIKELY (write && !writable))
+ goto not_writable;
+
+ len = GST_BUFFER_MEM_LEN (buffer);
+
+ if (G_UNLIKELY (len == 0)) {
+ /* no memory, return immediately */
+ if (size)
+ *size = 0;
+ if (maxsize)
+ *maxsize = 0;
+ return NULL;
+ }
+
+ if (G_LIKELY (len == 1)) {
+ /* we can take the first one */
+ mem = GST_BUFFER_MEM_PTR (buffer, 0);
+ } else {
+ /* we need to span memory */
+ if (writable) {
+ /* if we can write, we can change the memory with the spanned
+ * memory */
+ mem = _span_memory (buffer, 0, -1, write);
+ _replace_memory (buffer, mem);
+ } else {
+ gsize bsize;
+
+ /* extract all data in new memory, FIXME slow!! */
+ bsize = gst_buffer_get_size (buffer);
+
+ data = g_malloc (bsize);
+ gst_buffer_extract (buffer, 0, data, bsize);
+ if (size)
+ *size = bsize;
+ if (maxsize)
+ *maxsize = bsize;
+ return data;
+ }
+ }
+
+ if (G_UNLIKELY (write && !GST_MEMORY_IS_WRITABLE (mem))) {
+ GstMemory *copy;
+ /* replace with a writable copy */
+ copy = gst_memory_copy (mem, 0, -1);
+ GST_BUFFER_MEM_PTR (buffer, 0) = copy;
+ gst_memory_unref (mem);
+ mem = copy;
+ }
+
+ data = gst_memory_map (mem, size, maxsize, flags);
+
+ return data;
+
+ /* ERROR */
+not_writable:
+ {
+ g_return_val_if_fail (gst_buffer_is_writable (buffer), NULL);
+ return NULL;
+ }
+}
+
+/**
+ * gst_buffer_unmap:
+ * @buffer: a #GstBuffer.
+ * @data: the previously mapped data
+ * @size: the size of @data
+ *
+ * Release the memory previously mapped with gst_buffer_map().
+ *
+ * Returns: #TRUE on success. #FALSE can be returned when the new size is larger
+ * than the maxsize of the memory.
+ */
+gboolean
+gst_buffer_unmap (GstBuffer * buffer, gpointer data, gsize size)
+{
+ gboolean result;
+ guint len;
+
+ g_return_val_if_fail (GST_IS_BUFFER (buffer), FALSE);
+
+ len = GST_BUFFER_MEM_LEN (buffer);
+
+ if (G_LIKELY (len == 1)) {
+ GstMemory *mem = GST_BUFFER_MEM_PTR (buffer, 0);
+
+ result = gst_memory_unmap (mem, data, size);
+ } else {
+ /* this must have been from read-only access. After _map, the buffer either
+ * only contains 1 memory block or it allocated memory to join memory
+ * blocks. It's not allowed to add buffers between _map and _unmap. */
+ g_free (data);
+ result = TRUE;
+ }
+ return result;
+}
+
+/**
+ * gst_buffer_fill:
+ * @buffer: a #GstBuffer.
+ * @offset: the offset to fill
+ * @src: the source address
+ * @size: the size to fill
+ *
+ * Copy @size bytes from @src to @buffer at @offset.
+ *
+ * Returns: The amount of bytes copied. This value can be lower than @size
+ * when @buffer did not contain enough data.
+ */
+gsize
+gst_buffer_fill (GstBuffer * buffer, gsize offset, gconstpointer src,
+ gsize size)
+{
+ gsize i, len, left;
+ const guint8 *ptr = src;
+
+ g_return_val_if_fail (GST_IS_BUFFER (buffer), 0);
+ g_return_val_if_fail (gst_buffer_is_writable (buffer), 0);
+ g_return_val_if_fail (src != NULL, 0);
+
+ len = GST_BUFFER_MEM_LEN (buffer);
+ left = size;
+
+ for (i = 0; i < len && left > 0; i++) {
+ guint8 *data;
+ gsize ssize, tocopy;
+ GstMemory *mem;
+
+ mem = _get_memory (buffer, i, TRUE);
+
+ data = gst_memory_map (mem, &ssize, NULL, GST_MAP_WRITE);
+ if (ssize > offset) {
+ /* we have enough */
+ tocopy = MIN (ssize - offset, left);
+ memcpy (data + offset, ptr, tocopy);
+ left -= tocopy;
+ ptr += tocopy;
+ offset = 0;
+ } else {
+ /* offset past buffer, skip */
+ offset -= ssize;
+ }
+ gst_memory_unmap (mem, data, ssize);
+ }
+ return size - left;
+}
+
+/**
+ * gst_buffer_extract:
+ * @buffer: a #GstBuffer.
+ * @offset: the offset to extract
+ * @dest: the destination address
+ * @size: the size to extract
+ *
+ * Copy @size bytes starting from @offset in @buffer to @dest.
+ *
+ * Returns: The amount of bytes extracted. This value can be lower than @size
+ * when @buffer did not contain enough data.
+ */
+gsize
+gst_buffer_extract (GstBuffer * buffer, gsize offset, gpointer dest, gsize size)
+{
+ gsize i, len, left;
+ guint8 *ptr = dest;
+
+ g_return_val_if_fail (GST_IS_BUFFER (buffer), 0);
+ g_return_val_if_fail (dest != NULL, 0);
+
+ len = GST_BUFFER_MEM_LEN (buffer);
+ left = size;
+
+ for (i = 0; i < len && left > 0; i++) {
+ guint8 *data;
+ gsize ssize, tocopy;
+ GstMemory *mem;
+
+ mem = GST_BUFFER_MEM_PTR (buffer, i);
+
+ data = gst_memory_map (mem, &ssize, NULL, GST_MAP_READ);
+ if (ssize > offset) {
+ /* we have enough */
+ tocopy = MIN (ssize - offset, left);
+ memcpy (ptr, data + offset, tocopy);
+ left -= tocopy;
+ ptr += tocopy;
+ offset = 0;
+ } else {
+ /* offset past buffer, skip */
+ offset -= ssize;
+ }
+ gst_memory_unmap (mem, data, ssize);
+ }
+ return size - left;
+}
+
+/**
+ * gst_buffer_memcmp:
+ * @buffer: a #GstBuffer.
+ * @offset: the offset in @buffer
+ * @mem: the memory to compare
+ * @size: the size to compare
+ *
+ * Compare @size bytes starting from @offset in @buffer with the memory in @mem.
+ *
+ * Returns: 0 if the memory is equal.
+ */
+gint
+gst_buffer_memcmp (GstBuffer * buffer, gsize offset, gconstpointer mem,
+ gsize size)
+{
+ gsize i, len;
+ const guint8 *ptr = mem;
+ gint res = 0;
+
+ g_return_val_if_fail (GST_IS_BUFFER (buffer), 0);
+ g_return_val_if_fail (mem != NULL, 0);
+
+ len = GST_BUFFER_MEM_LEN (buffer);
+
+ for (i = 0; i < len && size > 0 && res == 0; i++) {
+ guint8 *data;
+ gsize ssize, tocmp;
+ GstMemory *mem;
+
+ mem = GST_BUFFER_MEM_PTR (buffer, i);
+
+ data = gst_memory_map (mem, &ssize, NULL, GST_MAP_READ);
+ if (ssize > offset) {
+ /* we have enough */
+ tocmp = MIN (ssize - offset, size);
+ res = memcmp (ptr, data + offset, tocmp);
+ size -= tocmp;
+ ptr += tocmp;
+ offset = 0;
+ } else {
+ /* offset past buffer, skip */
+ offset -= ssize;
+ }
+ gst_memory_unmap (mem, data, ssize);
+ }
+ return res;
+}
+
+/**
+ * gst_buffer_memset:
+ * @buffer: a #GstBuffer.
+ * @offset: the offset in @buffer
+ * @val: the value to set
+ * @size: the size to set
+ *
+ * Fill @buf with @size bytes with @val starting from @offset.
+ *
+ * Returns: The amount of bytes filled. This value can be lower than @size
+ * when @buffer did not contain enough data.
+ */
+gsize
+gst_buffer_memset (GstBuffer * buffer, gsize offset, guint8 val, gsize size)
+{
+ gsize i, len, left;
+
+ g_return_val_if_fail (GST_IS_BUFFER (buffer), 0);
+ g_return_val_if_fail (gst_buffer_is_writable (buffer), 0);
+
+ len = GST_BUFFER_MEM_LEN (buffer);
+ left = size;
+
+ for (i = 0; i < len && left > 0; i++) {
+ guint8 *data;
+ gsize ssize, toset;
+ GstMemory *mem;
+
+ mem = GST_BUFFER_MEM_PTR (buffer, i);
+
+ data = gst_memory_map (mem, &ssize, NULL, GST_MAP_WRITE);
+ if (ssize > offset) {
+ /* we have enough */
+ toset = MIN (ssize - offset, left);
+ memset (data + offset, val, toset);
+ left -= toset;
+ offset = 0;
+ } else {
+ /* offset past buffer, skip */
+ offset -= ssize;
+ }
+ gst_memory_unmap (mem, data, ssize);
+ }
+ return size - left;
+}
+
+/**
+ * gst_buffer_copy_region:
+ * @parent: a #GstBuffer.
+ * @flags: the #GstBufferCopyFlags
+ * @offset: the offset into parent #GstBuffer at which the new sub-buffer
+ * begins.
+ * @size: the size of the new #GstBuffer sub-buffer, in bytes.
+ *
+ * Creates a sub-buffer from @parent at @offset and @size.
+ * This sub-buffer uses the actual memory space of the parent buffer.
+ * This function will copy the offset and timestamp fields when the
+ * offset is 0. If not, they will be set to #GST_CLOCK_TIME_NONE and
+ * #GST_BUFFER_OFFSET_NONE.
+ * If @offset equals 0 and @size equals the total size of @buffer, the
+ * duration and offset end fields are also copied. If not they will be set
+ * to #GST_CLOCK_TIME_NONE and #GST_BUFFER_OFFSET_NONE.
+ *
+ * MT safe.
+ *
+ * Returns: (transfer full): the new #GstBuffer or NULL if the arguments were
+ * invalid.
+ */
+GstBuffer *
+gst_buffer_copy_region (GstBuffer * buffer, GstBufferCopyFlags flags,
+ gsize offset, gsize size)
+{
+ GstBuffer *copy;
+
+ g_return_val_if_fail (buffer != NULL, NULL);
+
+ /* create the new buffer */
+ copy = gst_buffer_new ();
+
+ GST_CAT_LOG (GST_CAT_BUFFER, "new region copy %p of %p %" G_GSIZE_FORMAT
+ "-%" G_GSIZE_FORMAT, copy, buffer, offset, size);
+
+ gst_buffer_copy_into (copy, buffer, flags, offset, size);
+
+ return copy;
+}
+
+static gboolean
+_gst_buffer_arr_is_span_fast (GstMemory ** mem[], gsize len[], guint n,
+ gsize * offset, GstMemory ** parent)
+{
+ GstMemory *mcur, *mprv;
+ gboolean have_offset = FALSE;
+ guint count, i;
+
+ mcur = mprv = NULL;
+ for (count = 0; count < n; count++) {
+ gsize offs, clen;
+ GstMemory **cmem;
+
+ cmem = mem[count];
+ clen = len[count];
+
+ for (i = 0; i < clen; i++) {
+ if (mcur)
+ mprv = mcur;
+ mcur = cmem[i];
+
+ if (mprv && mcur) {
+ /* check is memory is contiguous */
+ if (!gst_memory_is_span (mprv, mcur, &offs))
+ return FALSE;
+
+ if (!have_offset) {
+ if (offset)
+ *offset = offs;
+ if (parent)
+ *parent = mprv->parent;
+
+ have_offset = TRUE;
+ }
+ }
+ }
+ }
+ return have_offset;
+}
+
+static GstMemory *
+_gst_buffer_arr_span (GstMemory ** mem[], gsize len[], guint n, gsize offset,
+ gsize size, gboolean writable)
+{
+ GstMemory *span, *parent = NULL;
+ gsize poffset = 0;
+
+ if (!writable
+ && _gst_buffer_arr_is_span_fast (mem, len, n, &poffset, &parent)) {
+ if (parent->flags & GST_MEMORY_FLAG_NO_SHARE)
+ span = gst_memory_copy (parent, offset + poffset, size);
+ else
+ span = gst_memory_share (parent, offset + poffset, size);
+ } else {
+ gsize count, left;
+ guint8 *dest, *ptr;
+
+ span = gst_allocator_alloc (NULL, size, 0);
+ dest = gst_memory_map (span, NULL, NULL, GST_MAP_WRITE);
+
+ ptr = dest;
+ left = size;
+
+ for (count = 0; count < n; count++) {
+ gsize i, tocopy, clen, ssize;
+ guint8 *src;
+ GstMemory **cmem;
+
+ cmem = mem[count];
+ clen = len[count];
+
+ for (i = 0; i < clen && left > 0; i++) {
+ src = gst_memory_map (cmem[i], &ssize, NULL, GST_MAP_READ);
+ tocopy = MIN (ssize, left);
+ if (tocopy > offset) {
+ memcpy (ptr, src + offset, tocopy - offset);
+ left -= tocopy;
+ ptr += tocopy;
+ offset = 0;
+ } else {
+ offset -= tocopy;
+ }
+ gst_memory_unmap (cmem[i], src, ssize);
+ }
+ }
+ gst_memory_unmap (span, dest, size);
+ }
+ return span;
+}
+
+/**
+ * gst_buffer_is_span_fast:
+ * @buf1: the first #GstBuffer.
+ * @buf2: the second #GstBuffer.
+ *
+ * Determines whether a gst_buffer_span() can be done without copying
+ * the contents, that is, whether the data areas are contiguous sub-buffers of
+ * the same buffer.
+ *
+ * MT safe.
+ * Returns: TRUE if the buffers are contiguous,
+ * FALSE if a copy would be required.
+ */
+gboolean
+gst_buffer_is_span_fast (GstBuffer * buf1, GstBuffer * buf2)
+{
+ GstMemory **mem[2];
+ gsize len[2];
+
+ g_return_val_if_fail (GST_IS_BUFFER (buf1), FALSE);
+ g_return_val_if_fail (GST_IS_BUFFER (buf2), FALSE);
+ g_return_val_if_fail (buf1->mini_object.refcount > 0, FALSE);
+ g_return_val_if_fail (buf2->mini_object.refcount > 0, FALSE);
+
+ mem[0] = GST_BUFFER_MEM_ARRAY (buf1);
+ len[0] = GST_BUFFER_MEM_LEN (buf1);
+ mem[1] = GST_BUFFER_MEM_ARRAY (buf2);
+ len[1] = GST_BUFFER_MEM_LEN (buf2);
+
+ return _gst_buffer_arr_is_span_fast (mem, len, 2, NULL, NULL);
+}
+
+/**
+ * gst_buffer_span:
+ * @buf1: the first source #GstBuffer to merge.
+ * @offset: the offset in the first buffer from where the new
+ * buffer should start.
+ * @buf2: the second source #GstBuffer to merge.
+ * @size: the total size of the new buffer.
+ *
+ * Creates a new buffer that consists of part of buf1 and buf2.
+ * Logically, buf1 and buf2 are concatenated into a single larger
+ * buffer, and a new buffer is created at the given offset inside
+ * this space, with a given length.
+ *
+ * If the two source buffers are children of the same larger buffer,
+ * and are contiguous, the new buffer will be a child of the shared
+ * parent, and thus no copying is necessary. you can use
+ * gst_buffer_is_span_fast() to determine if a memcpy will be needed.
+ *
+ * MT safe.
+ *
+ * Returns: (transfer full): the new #GstBuffer that spans the two source
+ * buffers, or NULL if the arguments are invalid.
+ */
+GstBuffer *
+gst_buffer_span (GstBuffer * buf1, gsize offset, GstBuffer * buf2, gsize size)
+{
+ GstBuffer *newbuf;
+ GstMemory *span;
+ GstMemory **mem[2];
+ gsize len[2], len1, len2;
+
+ g_return_val_if_fail (GST_IS_BUFFER (buf1), NULL);
+ g_return_val_if_fail (GST_IS_BUFFER (buf2), NULL);
+ g_return_val_if_fail (buf1->mini_object.refcount > 0, NULL);
+ g_return_val_if_fail (buf2->mini_object.refcount > 0, NULL);
+ len1 = gst_buffer_get_size (buf1);
+ len2 = gst_buffer_get_size (buf2);
+ g_return_val_if_fail (len1 + len2 > offset, NULL);
+ if (size == -1)
+ size = len1 + len2 - offset;
+ else
+ g_return_val_if_fail (size <= len1 + len2 - offset, NULL);
+
+ mem[0] = GST_BUFFER_MEM_ARRAY (buf1);
+ len[0] = GST_BUFFER_MEM_LEN (buf1);
+ mem[1] = GST_BUFFER_MEM_ARRAY (buf2);
+ len[1] = GST_BUFFER_MEM_LEN (buf2);
+
+ span = _gst_buffer_arr_span (mem, len, 2, offset, size, FALSE);
+
+ newbuf = gst_buffer_new ();
+ _memory_add (newbuf, -1, span);
+
+#if 0
+ /* if the offset is 0, the new buffer has the same timestamp as buf1 */
+ if (offset == 0) {
+ GST_BUFFER_OFFSET (newbuf) = GST_BUFFER_OFFSET (buf1);
+ GST_BUFFER_TIMESTAMP (newbuf) = GST_BUFFER_TIMESTAMP (buf1);
+
+ /* if we completely merged the two buffers (appended), we can
+ * calculate the duration too. Also make sure we's not messing with
+ * invalid DURATIONS */
+ if (buf1->size + buf2->size == len) {
+ if (GST_BUFFER_DURATION_IS_VALID (buf1) &&
+ GST_BUFFER_DURATION_IS_VALID (buf2)) {
+ /* add duration */
+ GST_BUFFER_DURATION (newbuf) = GST_BUFFER_DURATION (buf1) +
+ GST_BUFFER_DURATION (buf2);
+ }
+ if (GST_BUFFER_OFFSET_END_IS_VALID (buf2)) {
+ /* add offset_end */
+ GST_BUFFER_OFFSET_END (newbuf) = GST_BUFFER_OFFSET_END (buf2);
+ }
+ }
+ }
+#endif
+
+ return newbuf;
+}
+
+/**
+ * gst_buffer_get_meta:
+ * @buffer: a #GstBuffer
+ * @info: a #GstMetaInfo
+ *
+ * Get the metadata for the api in @info on buffer. When there is no such
+ * metadata, NULL is returned.
+ *
+ * Note that the result metadata might not be of the implementation @info.
+ *
+ * Returns: the metadata for the api in @info on @buffer.
+ */
+GstMeta *
+gst_buffer_get_meta (GstBuffer * buffer, const GstMetaInfo * info)
+{
+ GstMetaItem *item;
+ GstMeta *result = NULL;
+
+ g_return_val_if_fail (buffer != NULL, NULL);
+ g_return_val_if_fail (info != NULL, NULL);
+
+ /* find GstMeta of the requested API */
+ for (item = GST_BUFFER_META (buffer); item; item = item->next) {
+ GstMeta *meta = &item->meta;
+ if (meta->info->api == info->api) {
+ result = meta;
+ break;
+ }
+ }
+ return result;
+}
+
+/**
+ * gst_buffer_add_meta:
+ * @buffer: a #GstBuffer
+ * @info: a #GstMetaInfo
+ * @params: params for @info
+ *
+ * Add metadata for @info to @buffer using the parameters in @params.
+ *
+ * Returns: the metadata for the api in @info on @buffer.
+ */
+GstMeta *
+gst_buffer_add_meta (GstBuffer * buffer, const GstMetaInfo * info,
+ gpointer params)
+{
+ GstMetaItem *item;
+ GstMeta *result = NULL;
+ gsize size;
+
+ g_return_val_if_fail (buffer != NULL, NULL);
+ g_return_val_if_fail (info != NULL, NULL);
+
+ /* create a new slice */
+ GST_CAT_DEBUG (GST_CAT_BUFFER, "alloc metadata %s of size %" G_GSIZE_FORMAT,
+ g_type_name (info->type), info->size);
+
+ size = ITEM_SIZE (info);
+ item = g_slice_alloc (size);
+ result = &item->meta;
+ result->info = info;
+
+ /* call the init_func when needed */
+ if (info->init_func)
+ if (!info->init_func (result, params, buffer))
+ goto init_failed;
+
+ /* and add to the list of metadata */
+ item->next = GST_BUFFER_META (buffer);
+ GST_BUFFER_META (buffer) = item;
+
+ return result;
+
+init_failed:
+ {
+ g_slice_free1 (size, item);
+ return NULL;
+ }
+}
+
+/**
+ * gst_buffer_remove_meta:
+ * @buffer: a #GstBuffer
+ * @meta: a #GstMeta
+ *
+ * Remove the metadata for @meta on @buffer.
+ *
+ * Returns: %TRUE if the metadata existed and was removed, %FALSE if no such
+ * metadata was on @buffer.
+ */
+gboolean
+gst_buffer_remove_meta (GstBuffer * buffer, GstMeta * meta)
+{
+ GstMetaItem *walk, *prev;
+
+ g_return_val_if_fail (buffer != NULL, FALSE);
+ g_return_val_if_fail (meta != NULL, FALSE);
+
+ /* find the metadata and delete */
+ prev = GST_BUFFER_META (buffer);
+ for (walk = prev; walk; walk = walk->next) {
+ GstMeta *m = &walk->meta;
+ if (m == meta) {
+ const GstMetaInfo *info = meta->info;
+
+ /* remove from list */
+ if (GST_BUFFER_META (buffer) == walk)
+ GST_BUFFER_META (buffer) = walk->next;
+ else
+ prev->next = walk->next;
+ /* call free_func if any */
+ if (info->free_func)
+ info->free_func (m, buffer);
+
+ /* and free the slice */
+ g_slice_free1 (ITEM_SIZE (info), walk);
+ break;
+ }
+ prev = walk;
+ }
+ return walk != NULL;
+}
+
+/**
+ * gst_buffer_iterate_meta:
+ * @buffer: a #GstBuffer
+ * @state: an opaque state pointer
+ *
+ * Retrieve the next #GstMeta after @current. If @state points
+ * to %NULL, the first metadata is returned.
+ *
+ * @state will be updated with an opage state pointer
+ *
+ * Returns: The next #GstMeta or %NULL when there are no more items.
+ */
+GstMeta *
+gst_buffer_iterate_meta (GstBuffer * buffer, gpointer * state)
+{
+ GstMetaItem **meta;
+
+ g_return_val_if_fail (buffer != NULL, NULL);
+ g_return_val_if_fail (state != NULL, NULL);
+
+ meta = (GstMetaItem **) state;
+ if (*meta == NULL)
+ /* state NULL, move to first item */
+ *meta = GST_BUFFER_META (buffer);
+ else
+ /* state !NULL, move to next item in list */
+ *meta = (*meta)->next;
+
+ if (*meta)
+ return &(*meta)->meta;
+ else
+ return NULL;
+}
diff --git a/gst/gstbuffer.h b/gst/gstbuffer.h
new file mode 100644
index 0000000..a25af4d
--- /dev/null
+++ b/gst/gstbuffer.h
@@ -0,0 +1,499 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2000 Wim Taymans <wtay@chello.be>
+ *
+ * gstbuffer.h: Header for GstBuffer object
+ *
+ * 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_BUFFER_H__
+#define __GST_BUFFER_H__
+
+#include <gst/gstminiobject.h>
+#include <gst/gstclock.h>
+#include <gst/gstcaps.h>
+#include <gst/gstmemory.h>
+
+G_BEGIN_DECLS
+
+extern GType _gst_buffer_type;
+
+typedef struct _GstBuffer GstBuffer;
+typedef struct _GstBufferPool GstBufferPool;
+
+/**
+ * GST_BUFFER_TRACE_NAME:
+ *
+ * The name used for tracing memory allocations.
+ */
+#define GST_BUFFER_TRACE_NAME "GstBuffer"
+
+#define GST_TYPE_BUFFER (_gst_buffer_type)
+#define GST_IS_BUFFER(obj) (GST_IS_MINI_OBJECT_TYPE(obj, GST_TYPE_BUFFER))
+#define GST_BUFFER_CAST(obj) ((GstBuffer *)(obj))
+#define GST_BUFFER(obj) (GST_BUFFER_CAST(obj))
+
+/**
+ * GST_BUFFER_FLAGS:
+ * @buf: a #GstBuffer.
+ *
+ * A flags word containing #GstBufferFlag flags set on this buffer.
+ */
+#define GST_BUFFER_FLAGS(buf) GST_MINI_OBJECT_FLAGS(buf)
+/**
+ * GST_BUFFER_FLAG_IS_SET:
+ * @buf: a #GstBuffer.
+ * @flag: the #GstBufferFlag to check.
+ *
+ * Gives the status of a specific flag on a buffer.
+ */
+#define GST_BUFFER_FLAG_IS_SET(buf,flag) GST_MINI_OBJECT_FLAG_IS_SET (buf, flag)
+/**
+ * GST_BUFFER_FLAG_SET:
+ * @buf: a #GstBuffer.
+ * @flag: the #GstBufferFlag to set.
+ *
+ * Sets a buffer flag on a buffer.
+ */
+#define GST_BUFFER_FLAG_SET(buf,flag) GST_MINI_OBJECT_FLAG_SET (buf, flag)
+/**
+ * GST_BUFFER_FLAG_UNSET:
+ * @buf: a #GstBuffer.
+ * @flag: the #GstBufferFlag to clear.
+ *
+ * Clears a buffer flag.
+ */
+#define GST_BUFFER_FLAG_UNSET(buf,flag) GST_MINI_OBJECT_FLAG_UNSET (buf, flag)
+
+/**
+ * GST_BUFFER_TIMESTAMP:
+ * @buf: a #GstBuffer.:
+ *
+ * The timestamp in nanoseconds (as a #GstClockTime) of the data in the buffer.
+ * Value will be %GST_CLOCK_TIME_NONE if the timestamp is unknown.
+ *
+ */
+#define GST_BUFFER_TIMESTAMP(buf) (GST_BUFFER_CAST(buf)->timestamp)
+/**
+ * GST_BUFFER_DURATION:
+ * @buf: a #GstBuffer.
+ *
+ * The duration in nanoseconds (as a #GstClockTime) of the data in the buffer.
+ * Value will be %GST_CLOCK_TIME_NONE if the duration is unknown.
+ */
+#define GST_BUFFER_DURATION(buf) (GST_BUFFER_CAST(buf)->duration)
+/**
+ * GST_BUFFER_OFFSET:
+ * @buf: a #GstBuffer.
+ *
+ * The offset in the source file of the beginning of this buffer.
+ */
+#define GST_BUFFER_OFFSET(buf) (GST_BUFFER_CAST(buf)->offset)
+/**
+ * GST_BUFFER_OFFSET_END:
+ * @buf: a #GstBuffer.
+ *
+ * The offset in the source file of the end of this buffer.
+ */
+#define GST_BUFFER_OFFSET_END(buf) (GST_BUFFER_CAST(buf)->offset_end)
+
+/**
+ * GST_BUFFER_OFFSET_NONE:
+ *
+ * Constant for no-offset return results.
+ */
+#define GST_BUFFER_OFFSET_NONE ((guint64)-1)
+
+/**
+ * GST_BUFFER_DURATION_IS_VALID:
+ * @buffer: a #GstBuffer
+ *
+ * Tests if the duration is known.
+ */
+#define GST_BUFFER_DURATION_IS_VALID(buffer) (GST_CLOCK_TIME_IS_VALID (GST_BUFFER_DURATION (buffer)))
+/**
+ * GST_BUFFER_TIMESTAMP_IS_VALID:
+ * @buffer: a #GstBuffer
+ *
+ * Tests if the timestamp is known.
+ */
+#define GST_BUFFER_TIMESTAMP_IS_VALID(buffer) (GST_CLOCK_TIME_IS_VALID (GST_BUFFER_TIMESTAMP (buffer)))
+/**
+ * GST_BUFFER_OFFSET_IS_VALID:
+ * @buffer: a #GstBuffer
+ *
+ * Tests if the start offset is known.
+ */
+#define GST_BUFFER_OFFSET_IS_VALID(buffer) (GST_BUFFER_OFFSET (buffer) != GST_BUFFER_OFFSET_NONE)
+/**
+ * GST_BUFFER_OFFSET_END_IS_VALID:
+ * @buffer: a #GstBuffer
+ *
+ * Tests if the end offset is known.
+ */
+#define GST_BUFFER_OFFSET_END_IS_VALID(buffer) (GST_BUFFER_OFFSET_END (buffer) != GST_BUFFER_OFFSET_NONE)
+/**
+ * GST_BUFFER_IS_DISCONT:
+ * @buffer: a #GstBuffer
+ *
+ * Tests if the buffer marks a discontinuity in the stream.
+ *
+ * Since: 0.10.9
+ */
+#define GST_BUFFER_IS_DISCONT(buffer) (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT))
+
+/**
+ * GstBufferFlags:
+ * @GST_BUFFER_FLAG_LIVE: the buffer is live data and should be discarded in
+ * the PAUSED state.
+ * @GST_BUFFER_FLAG_DECODE_ONLY: the buffer contains data that should be dropped
+ * because it will be clipped against the segment
+ * boundaries or because it does not contain data
+ * that should be shown to the user.
+ * @GST_BUFFER_FLAG_DISCONT: the buffer marks a data discontinuity in the stream.
+ * This typically occurs after a seek or a dropped buffer
+ * from a live or network source.
+ * @GST_BUFFER_FLAG_RESYNC: the buffer timestamp might have a discontinuity
+ * and this buffer is a good point to resynchronize.
+ * @GST_BUFFER_FLAG_CORRUPTED: the buffer data is corrupted.
+ * @GST_BUFFER_FLAG_MARKER: the buffer contains a media specific marker. for
+ * video this is typically the end of a frame boundary, for audio
+ * this is usually the end of a talkspurt.
+ * @GST_BUFFER_FLAG_HEADER: the buffer contains header information that is
+ * needed to decode the following data
+ * @GST_BUFFER_FLAG_GAP: the buffer has been created to fill a gap in the
+ * stream and contains media neutral data (elements can
+ * switch to optimized code path that ignores the buffer
+ * content).
+ * @GST_BUFFER_FLAG_DROPPABLE: the buffer can be dropped without breaking the
+ * stream, for example to reduce bandwidth.
+ * @GST_BUFFER_FLAG_DELTA_UNIT: this unit cannot be decoded independently.
+ * @GST_BUFFER_FLAG_IN_CAPS: the buffer has been added as a field in a #GstCaps.
+ * @GST_BUFFER_FLAG_LAST: additional media specific flags can be added starting from
+ * this flag.
+ *
+ * A set of buffer flags used to describe properties of a #GstBuffer.
+ */
+typedef enum {
+ GST_BUFFER_FLAG_LIVE = (GST_MINI_OBJECT_FLAG_LAST << 0),
+ GST_BUFFER_FLAG_DECODE_ONLY = (GST_MINI_OBJECT_FLAG_LAST << 1),
+ GST_BUFFER_FLAG_DISCONT = (GST_MINI_OBJECT_FLAG_LAST << 2),
+ GST_BUFFER_FLAG_RESYNC = (GST_MINI_OBJECT_FLAG_LAST << 3),
+ GST_BUFFER_FLAG_CORRUPTED = (GST_MINI_OBJECT_FLAG_LAST << 4),
+ GST_BUFFER_FLAG_MARKER = (GST_MINI_OBJECT_FLAG_LAST << 5),
+ GST_BUFFER_FLAG_HEADER = (GST_MINI_OBJECT_FLAG_LAST << 6),
+ GST_BUFFER_FLAG_GAP = (GST_MINI_OBJECT_FLAG_LAST << 7),
+ GST_BUFFER_FLAG_DROPPABLE = (GST_MINI_OBJECT_FLAG_LAST << 8),
+ GST_BUFFER_FLAG_DELTA_UNIT = (GST_MINI_OBJECT_FLAG_LAST << 9),
+ GST_BUFFER_FLAG_IN_CAPS = (GST_MINI_OBJECT_FLAG_LAST << 10),
+
+ GST_BUFFER_FLAG_LAST = (GST_MINI_OBJECT_FLAG_LAST << 20)
+} GstBufferFlags;
+
+/**
+ * GstBuffer:
+ * @mini_object: the parent structure
+ * @pool: pointer to the pool owner of the buffer
+ * @timestamp: timestamp of the buffer, can be #GST_CLOCK_TIME_NONE when the
+ * timestamp is not known or relevant.
+ * @duration: duration in time of the buffer data, can be #GST_CLOCK_TIME_NONE
+ * when the duration is not known or relevant.
+ * @offset: a media specific offset for the buffer data.
+ * For video frames, this is the frame number of this buffer.
+ * For audio samples, this is the offset of the first sample in this buffer.
+ * For file data or compressed data this is the byte offset of the first
+ * byte in this buffer.
+ * @offset_end: the last offset contained in this buffer. It has the same
+ * format as @offset.
+ *
+ * The structure of a #GstBuffer. Use the associated macros to access the public
+ * variables.
+ */
+struct _GstBuffer {
+ GstMiniObject mini_object;
+
+ /*< public >*/ /* with COW */
+ GstBufferPool *pool;
+
+ /* timestamp */
+ GstClockTime timestamp;
+ GstClockTime duration;
+
+ /* media specific offset */
+ guint64 offset;
+ guint64 offset_end;
+};
+
+GType gst_buffer_get_type (void);
+
+/* allocation */
+GstBuffer * gst_buffer_new (void);
+GstBuffer * gst_buffer_new_allocate (const GstAllocator * allocator, gsize size, gsize align);
+GstBuffer * gst_buffer_new_wrapped_full (gpointer data, GFreeFunc free_func, gsize offset, gsize size);
+GstBuffer * gst_buffer_new_wrapped (gpointer data, gsize size);
+
+/* memory blocks */
+guint gst_buffer_n_memory (GstBuffer *buffer);
+void gst_buffer_take_memory (GstBuffer *buffer, gint idx, GstMemory *mem);
+GstMemory * gst_buffer_peek_memory (GstBuffer *buffer, guint idx, GstMapFlags flags);
+void gst_buffer_remove_memory_range (GstBuffer *buffer, guint idx, guint length);
+
+/**
+ * gst_buffer_remove_memory:
+ * @b: a #GstBuffer.
+ * @i: an index
+ *
+ * Remove the memory block in @b at @i.
+ */
+#define gst_buffer_remove_memory(b,i) gst_buffer_remove_memory_range ((b), (i), 1)
+
+gsize gst_buffer_fill (GstBuffer *buffer, gsize offset,
+ gconstpointer src, gsize size);
+gsize gst_buffer_extract (GstBuffer *buffer, gsize offset,
+ gpointer dest, gsize size);
+gint gst_buffer_memcmp (GstBuffer *buffer, gsize offset,
+ gconstpointer mem, gsize size);
+gsize gst_buffer_memset (GstBuffer *buffer, gsize offset,
+ guint8 val, gsize size);
+
+gsize gst_buffer_get_sizes (GstBuffer *buffer, gsize *offset, gsize *maxsize);
+void gst_buffer_resize (GstBuffer *buffer, gssize offset, gsize size);
+
+/**
+ * gst_buffer_get_size:
+ * @b: a #GstBuffer.
+ *
+ * Get the size of @b.
+ */
+#define gst_buffer_get_size(b) gst_buffer_get_sizes ((b), NULL, NULL)
+/**
+ * gst_buffer_set_size:
+ * @b: a #GstBuffer.
+ * @s: a new size
+ *
+ * Set the size of @b to @s. This will remove or trim the memory blocks
+ * in the buffer.
+ */
+#define gst_buffer_set_size(b,s) gst_buffer_resize ((b), 0, (s))
+
+/* getting memory */
+gpointer gst_buffer_map (GstBuffer *buffer, gsize *size, gsize *maxsize,
+ GstMapFlags flags);
+gboolean gst_buffer_unmap (GstBuffer *buffer, gpointer data, gsize size);
+
+/* refcounting */
+/**
+ * gst_buffer_ref:
+ * @buf: a #GstBuffer.
+ *
+ * Increases the refcount of the given buffer by one.
+ *
+ * Note that the refcount affects the writeability
+ * of @buf and its metadata, see gst_buffer_is_writable() and
+ * gst_buffer_is_metadata_writable(). It is
+ * important to note that keeping additional references to
+ * GstBuffer instances can potentially increase the number
+ * of memcpy operations in a pipeline.
+ *
+ * Returns: (transfer full): @buf
+ */
+#ifdef _FOOL_GTK_DOC_
+G_INLINE_FUNC GstBuffer * gst_buffer_ref (GstBuffer * buf);
+#endif
+
+static inline GstBuffer *
+gst_buffer_ref (GstBuffer * buf)
+{
+ return (GstBuffer *) gst_mini_object_ref (GST_MINI_OBJECT_CAST (buf));
+}
+
+/**
+ * gst_buffer_unref:
+ * @buf: (transfer full): a #GstBuffer.
+ *
+ * Decreases the refcount of the buffer. If the refcount reaches 0, the buffer
+ * will be freed. If GST_BUFFER_MALLOCDATA() is non-NULL, this pointer will
+ * also be freed at this time.
+ */
+#ifdef _FOOL_GTK_DOC_
+G_INLINE_FUNC void gst_buffer_unref (GstBuffer * buf);
+#endif
+
+static inline void
+gst_buffer_unref (GstBuffer * buf)
+{
+ gst_mini_object_unref (GST_MINI_OBJECT_CAST (buf));
+}
+
+/* copy buffer */
+/**
+ * gst_buffer_copy:
+ * @buf: a #GstBuffer.
+ *
+ * Create a copy of the given buffer. This will also make a newly allocated
+ * copy of the data the source buffer contains.
+ *
+ * Returns: (transfer full): a new copy of @buf.
+ */
+#ifdef _FOOL_GTK_DOC_
+G_INLINE_FUNC GstBuffer * gst_buffer_copy (const GstBuffer * buf);
+#endif
+
+static inline GstBuffer *
+gst_buffer_copy (const GstBuffer * buf)
+{
+ return GST_BUFFER (gst_mini_object_copy (GST_MINI_OBJECT_CONST_CAST (buf)));
+}
+
+
+/**
+ * GstBufferCopyFlags:
+ * @GST_BUFFER_COPY_NONE: copy nothing
+ * @GST_BUFFER_COPY_FLAGS: flag indicating that buffer flags should be copied
+ * @GST_BUFFER_COPY_TIMESTAMPS: flag indicating that buffer timestamp, duration,
+ * offset and offset_end should be copied
+ * @GST_BUFFER_COPY_MEMORY: flag indicating that buffer memory should be copied
+ * and appended to already existing memory
+ * @GST_BUFFER_COPY_MERGE: flag indicating that buffer memory should be
+ * merged
+ *
+ * A set of flags that can be provided to the gst_buffer_copy_into()
+ * function to specify which items should be copied.
+ */
+typedef enum {
+ GST_BUFFER_COPY_NONE = 0,
+ GST_BUFFER_COPY_FLAGS = (1 << 0),
+ GST_BUFFER_COPY_TIMESTAMPS = (1 << 1),
+ GST_BUFFER_COPY_MEMORY = (1 << 2),
+ GST_BUFFER_COPY_MERGE = (1 << 3)
+} GstBufferCopyFlags;
+
+/**
+ * GST_BUFFER_COPY_METADATA:
+ *
+ * Combination of all possible metadata fields that can be copied with
+ * gst_buffer_copy_into().
+ */
+#define GST_BUFFER_COPY_METADATA (GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS)
+
+/**
+ * GST_BUFFER_COPY_ALL:
+ *
+ * Combination of all possible fields that can be copied with
+ * gst_buffer_copy_into().
+ */
+#define GST_BUFFER_COPY_ALL ((GstBufferCopyFlags)(GST_BUFFER_COPY_METADATA | GST_BUFFER_COPY_MEMORY))
+
+/* copies memory or metadata into newly allocated buffer */
+void gst_buffer_copy_into (GstBuffer *dest, GstBuffer *src,
+ GstBufferCopyFlags flags,
+ gsize offset, gsize size);
+
+/**
+ * gst_buffer_is_writable:
+ * @buf: a #GstBuffer
+ *
+ * Tests if you can safely write data into a buffer's data array or validly
+ * modify the caps and timestamp metadata. Metadata in a GstBuffer is always
+ * writable, but it is only safe to change it when there is only one owner
+ * of the buffer - ie, the refcount is 1.
+ */
+#define gst_buffer_is_writable(buf) gst_mini_object_is_writable (GST_MINI_OBJECT_CAST (buf))
+/**
+ * gst_buffer_make_writable:
+ * @buf: (transfer full): a #GstBuffer
+ *
+ * Makes a writable buffer from the given buffer. If the source buffer is
+ * already writable, this will simply return the same buffer. A copy will
+ * otherwise be made using gst_buffer_copy().
+ *
+ * Returns: (transfer full): a writable buffer which may or may not be the
+ * same as @buf
+ */
+#define gst_buffer_make_writable(buf) GST_BUFFER_CAST (gst_mini_object_make_writable (GST_MINI_OBJECT_CAST (buf)))
+
+/**
+ * gst_buffer_replace:
+ * @obuf: (inout) (transfer full): pointer to a pointer to a #GstBuffer to be
+ * replaced.
+ * @nbuf: (transfer none) (allow-none): pointer to a #GstBuffer that will
+ * replace the buffer pointed to by @obuf.
+ *
+ * Modifies a pointer to a #GstBuffer to point to a different #GstBuffer. The
+ * modification is done atomically (so this is useful for ensuring thread safety
+ * in some cases), and the reference counts are updated appropriately (the old
+ * buffer is unreffed, the new is reffed).
+ *
+ * Either @nbuf or the #GstBuffer pointed to by @obuf may be NULL.
+ */
+#define gst_buffer_replace(obuf,nbuf) \
+G_STMT_START { \
+ GstBuffer **___obufaddr = (GstBuffer **)(obuf); \
+ gst_mini_object_replace ((GstMiniObject **)___obufaddr, \
+ GST_MINI_OBJECT_CAST (nbuf)); \
+} G_STMT_END
+
+/* creating a region */
+GstBuffer* gst_buffer_copy_region (GstBuffer *parent, GstBufferCopyFlags flags,
+ gsize offset, gsize size);
+
+/* span, two buffers, intelligently */
+gboolean gst_buffer_is_span_fast (GstBuffer *buf1, GstBuffer *buf2);
+GstBuffer* gst_buffer_span (GstBuffer *buf1, gsize offset, GstBuffer *buf2, gsize size);
+
+/* metadata */
+#include <gst/gstmeta.h>
+
+GstMeta * gst_buffer_get_meta (GstBuffer *buffer, const GstMetaInfo *info);
+GstMeta * gst_buffer_add_meta (GstBuffer *buffer, const GstMetaInfo *info,
+ gpointer params);
+gboolean gst_buffer_remove_meta (GstBuffer *buffer, GstMeta *meta);
+
+GstMeta * gst_buffer_iterate_meta (GstBuffer *buffer, gpointer *state);
+
+/**
+ * gst_value_set_buffer:
+ * @v: a #GValue to receive the data
+ * @b: (transfer none): a #GstBuffer to assign to the GstValue
+ *
+ * Sets @b as the value of @v. Caller retains reference to buffer.
+ */
+#define gst_value_set_buffer(v,b) g_value_set_boxed((v),(b))
+/**
+ * gst_value_take_buffer:
+ * @v: a #GValue to receive the data
+ * @b: (transfer full): a #GstBuffer to assign to the GstValue
+ *
+ * Sets @b as the value of @v. Caller gives away reference to buffer.
+ */
+#define gst_value_take_buffer(v,b) g_value_take_boxed(v,(b))
+/**
+ * gst_value_get_buffer:
+ * @v: a #GValue to query
+ *
+ * Receives a #GstBuffer as the value of @v. Does not return a reference to
+ * the buffer, so the pointer is only valid for as long as the caller owns
+ * a reference to @v.
+ *
+ * Returns: (transfer none): buffer
+ */
+#define gst_value_get_buffer(v) GST_BUFFER_CAST (g_value_get_boxed(v))
+
+G_END_DECLS
+
+#endif /* __GST_BUFFER_H__ */
diff --git a/gst/gstbufferlist.c b/gst/gstbufferlist.c
new file mode 100644
index 0000000..06f817f
--- /dev/null
+++ b/gst/gstbufferlist.c
@@ -0,0 +1,284 @@
+/* GStreamer
+ * Copyright (C) 2009 Axis Communications <dev-gstreamer at axis dot com>
+ * @author Jonas Holmberg <jonas dot holmberg at axis dot com>
+ *
+ * gstbufferlist.c: Buffer list
+ *
+ * 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:gstbufferlist
+ * @short_description: Lists of buffers for data-passing
+ * @see_also: #GstPad, #GstMiniObject
+ *
+ * Buffer lists are an object containing a list of buffers.
+ *
+ * Buffer lists are created with gst_buffer_list_new() and filled with data
+ * using a gst_buffer_list_insert().
+ *
+ */
+#include "gst_private.h"
+
+#include "gstbuffer.h"
+#include "gstbufferlist.h"
+
+#define GST_CAT_DEFAULT GST_CAT_BUFFER_LIST
+
+/**
+ * GstBufferList:
+ *
+ * Opaque list of grouped buffers.
+ *
+ * Since: 0.10.24
+ */
+struct _GstBufferList
+{
+ GstMiniObject mini_object;
+
+ GArray *array;
+};
+
+GType _gst_buffer_list_type = 0;
+
+GST_DEFINE_MINI_OBJECT_TYPE (GstBufferList, gst_buffer_list);
+
+void
+_priv_gst_buffer_list_initialize (void)
+{
+ _gst_buffer_list_type = gst_buffer_list_get_type ();
+}
+
+static GstBufferList *
+_gst_buffer_list_copy (GstBufferList * list)
+{
+ GstBufferList *copy;
+ guint i, len;
+
+ len = list->array->len;
+ copy = gst_buffer_list_sized_new (len);
+
+ /* add and ref all buffers in the array */
+ for (i = 0; i < len; i++) {
+ GstBuffer *buf = g_array_index (list->array, GstBuffer *, i);
+ buf = gst_buffer_ref (buf);
+ g_array_append_val (copy->array, buf);
+ }
+ return copy;
+}
+
+static void
+_gst_buffer_list_free (GstBufferList * list)
+{
+ guint i, len;
+ GST_LOG ("free %p", list);
+
+ /* unrefs all buffers too */
+ len = list->array->len;
+ for (i = 0; i < len; i++)
+ gst_buffer_unref (g_array_index (list->array, GstBuffer *, i));
+ g_array_free (list->array, TRUE);
+
+ g_slice_free1 (GST_MINI_OBJECT_SIZE (list), list);
+}
+
+static void
+gst_buffer_list_init (GstBufferList * list, gsize size, guint asize)
+{
+ gst_mini_object_init (GST_MINI_OBJECT_CAST (list), _gst_buffer_list_type,
+ size);
+
+ list->mini_object.copy = (GstMiniObjectCopyFunction) _gst_buffer_list_copy;
+ list->mini_object.free = (GstMiniObjectFreeFunction) _gst_buffer_list_free;
+
+ list->array = g_array_sized_new (FALSE, FALSE, sizeof (GstBuffer *), asize);
+
+ GST_LOG ("init %p", list);
+}
+
+/**
+ * gst_buffer_list_sized_new:
+ * @size: an initial reserved size
+ *
+ * Creates a new, empty #GstBufferList. The caller is responsible for unreffing
+ * the returned #GstBufferList. The list will have @size space preallocated so
+ * that memory reallocations can be avoided.
+ *
+ * Free-function: gst_buffer_list_unref
+ *
+ * Returns: (transfer full): the new #GstBufferList. gst_buffer_list_unref()
+ * after usage.
+ *
+ * Since: 0.10.24
+ */
+GstBufferList *
+gst_buffer_list_sized_new (guint size)
+{
+ GstBufferList *list;
+
+ list = g_slice_new0 (GstBufferList);
+
+ GST_LOG ("new %p", list);
+
+ gst_buffer_list_init (list, sizeof (GstBufferList), size);
+
+ return list;
+}
+
+/**
+ * gst_buffer_list_new:
+ *
+ * Creates a new, empty #GstBufferList. The caller is responsible for unreffing
+ * the returned #GstBufferList.
+ *
+ * Free-function: gst_buffer_list_unref
+ *
+ * Returns: (transfer full): the new #GstBufferList. gst_buffer_list_unref()
+ * after usage.
+ *
+ * Since: 0.10.24
+ */
+GstBufferList *
+gst_buffer_list_new (void)
+{
+ return gst_buffer_list_sized_new (8);
+}
+
+/**
+ * gst_buffer_list_len:
+ * @list: a #GstBufferList
+ *
+ * Returns the number of buffers in @list.
+ *
+ * Returns: the number of buffers in the buffer list
+ *
+ * Since: 0.10.24
+ */
+guint
+gst_buffer_list_len (GstBufferList * list)
+{
+ g_return_val_if_fail (GST_IS_BUFFER_LIST (list), 0);
+
+ return list->array->len;
+}
+
+/**
+ * gst_buffer_list_foreach:
+ * @list: a #GstBufferList
+ * @func: (scope call): a #GstBufferListFunc to call
+ * @user_data: (closure): user data passed to @func
+ *
+ * Call @func with @data for each buffer in @list.
+ *
+ * @func can modify the passed buffer pointer or its contents. The return value
+ * of @func define if this function returns or if the remaining buffers in a
+ * group should be skipped.
+ *
+ * Since: 0.10.24
+ */
+void
+gst_buffer_list_foreach (GstBufferList * list, GstBufferListFunc func,
+ gpointer user_data)
+{
+ guint i, len;
+
+ g_return_if_fail (GST_IS_BUFFER_LIST (list));
+ g_return_if_fail (func != NULL);
+
+ len = list->array->len;
+ for (i = 0; i < len;) {
+ GstBuffer *buf, *buf_ret;
+ gboolean ret;
+
+ buf = buf_ret = g_array_index (list->array, GstBuffer *, i);
+ ret = func (&buf_ret, i, user_data);
+
+ /* Check if the function changed the buffer */
+ if (buf != buf_ret) {
+ if (buf_ret == NULL) {
+ g_array_remove_index (list->array, i);
+ } else {
+ g_array_index (list->array, GstBuffer *, i) = buf_ret;
+ }
+ }
+
+ if (!ret)
+ break;
+
+ /* If the buffer was not removed by func go to the next buffer */
+ if (buf_ret != NULL)
+ i++;
+ }
+}
+
+/**
+ * gst_buffer_list_get:
+ * @list: a #GstBufferList
+ * @idx: the index
+ *
+ * Get the buffer at @idx.
+ *
+ * Returns: (transfer none): the buffer at @idx in @group or NULL when there
+ * is no buffer. The buffer remains valid as long as @list is valid.
+ *
+ * Since: 0.10.24
+ */
+GstBuffer *
+gst_buffer_list_get (GstBufferList * list, guint idx)
+{
+ GstBuffer *buf;
+
+ g_return_val_if_fail (GST_IS_BUFFER_LIST (list), NULL);
+ g_return_val_if_fail (idx < list->array->len, NULL);
+
+ buf = g_array_index (list->array, GstBuffer *, idx);
+
+ return buf;
+}
+
+/**
+ * gst_buffer_list_insert:
+ * @list: a #GstBufferList
+ * @idx: the index
+ * @buffer: a #GstBuffer
+ *
+ * Insert @buffer at @idx in @list. Other buffers are moved to make room for
+ * this new buffer.
+ *
+ * A -1 value for @idx will append the buffer at the end.
+ */
+void
+gst_buffer_list_insert (GstBufferList * list, guint idx, GstBuffer * buffer)
+{
+ g_return_if_fail (GST_IS_BUFFER_LIST (list));
+ g_return_if_fail (buffer != NULL);
+
+ if (idx == -1)
+ g_array_append_val (list->array, buffer);
+ else {
+ g_return_if_fail (idx < list->array->len);
+ g_array_insert_val (list->array, idx, buffer);
+ }
+}
+
+void
+gst_buffer_list_remove (GstBufferList * list, guint idx, guint length)
+{
+ g_return_if_fail (GST_IS_BUFFER_LIST (list));
+ g_return_if_fail (idx < list->array->len);
+
+ g_array_remove_range (list->array, idx, length);
+}
diff --git a/gst/gstbufferlist.h b/gst/gstbufferlist.h
new file mode 100644
index 0000000..7275dad
--- /dev/null
+++ b/gst/gstbufferlist.h
@@ -0,0 +1,176 @@
+/* GStreamer
+ * Copyright (C) 2009 Axis Communications <dev-gstreamer at axis dot com>
+ * @author Jonas Holmberg <jonas dot holmberg at axis dot com>
+ *
+ * gstbufferlist.h: Header for GstBufferList object
+ *
+ * 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_BUFFER_LIST_H__
+#define __GST_BUFFER_LIST_H__
+
+#include <gst/gstbuffer.h>
+
+G_BEGIN_DECLS
+
+extern GType _gst_buffer_list_type;
+
+#define GST_TYPE_BUFFER_LIST (_gst_buffer_list_type)
+#define GST_IS_BUFFER_LIST(obj) (GST_IS_MINI_OBJECT_TYPE(obj, GST_TYPE_BUFFER_LIST))
+#define GST_BUFFER_LIST_CAST(obj) ((GstBufferList *)obj)
+#define GST_BUFFER_LIST(obj) (GST_BUFFER_LIST_CAST(obj))
+
+typedef struct _GstBufferList GstBufferList;
+
+/**
+ * GstBufferListFunc:
+ * @buffer: pointer the buffer
+ * @idx: the index of @buffer
+ * @user_data: user data passed to gst_buffer_list_foreach()
+ *
+ * A function that will be called from gst_buffer_list_foreach(). The @buffer
+ * field will point to a the reference of the buffer at @idx.
+ *
+ * When this function returns %TRUE, the next buffer will be
+ * returned. When %FALSE is returned, gst_buffer_list_foreach() will return.
+ *
+ * When @buffer is set to NULL, the item will be removed from the bufferlist.
+ * When @buffer has been made writable, the new buffer reference can be assigned
+ * to @buffer. This function is responsible for unreffing the old buffer when
+ * removing or modifying.
+ *
+ * Returns: %FALSE when gst_buffer_list_foreach() should stop
+ */
+typedef gboolean (*GstBufferListFunc) (GstBuffer **buffer, guint idx,
+ gpointer user_data);
+
+
+/* refcounting */
+/**
+ * gst_buffer_list_ref:
+ * @list: a #GstBufferList
+ *
+ * Increases the refcount of the given buffer list by one.
+ *
+ * Note that the refcount affects the writeability of @list and its data, see
+ * gst_buffer_list_make_writable(). It is important to note that keeping
+ * additional references to GstBufferList instances can potentially increase
+ * the number of memcpy operations in a pipeline.
+ *
+ * Returns: (transfer full): @list
+ *
+ * Since: 0.10.24
+ */
+#ifdef _FOOL_GTK_DOC_
+G_INLINE_FUNC GstBufferList * gst_buffer_list_ref (GstBufferList * list);
+#endif
+
+static inline GstBufferList *
+gst_buffer_list_ref (GstBufferList * list)
+{
+ return GST_BUFFER_LIST_CAST (gst_mini_object_ref (GST_MINI_OBJECT_CAST (
+ list)));
+}
+
+/**
+ * gst_buffer_list_unref:
+ * @list: (transfer full): a #GstBufferList
+ *
+ * Decreases the refcount of the buffer list. If the refcount reaches 0, the
+ * buffer list will be freed.
+ *
+ * Since: 0.10.24
+ */
+#ifdef _FOOL_GTK_DOC_
+G_INLINE_FUNC void gst_buffer_list_unref (GstBufferList * list);
+#endif
+
+static inline void
+gst_buffer_list_unref (GstBufferList * list)
+{
+ gst_mini_object_unref (GST_MINI_OBJECT_CAST (list));
+}
+
+/* copy */
+/**
+ * gst_buffer_list_copy:
+ * @list: a #GstBufferList
+ *
+ * Create a shallow copy of the given buffer list. This will make a newly
+ * allocated copy of the source list with copies of buffer pointers. The
+ * refcount of buffers pointed to will be increased by one.
+ *
+ * Returns: (transfer full): a new copy of @list.
+ *
+ * Since: 0.10.24
+ */
+#ifdef _FOOL_GTK_DOC_
+G_INLINE_FUNC GstBufferList * gst_buffer_list_copy (const GstBufferList * list);
+#endif
+
+static inline GstBufferList *
+gst_buffer_list_copy (const GstBufferList * list)
+{
+ return GST_BUFFER_LIST_CAST (gst_mini_object_copy (GST_MINI_OBJECT_CONST_CAST (list)));
+}
+
+/**
+ * gst_buffer_list_is_writable:
+ * @list: a #GstBufferList
+ *
+ * Tests if you can safely add buffers and groups into a buffer list.
+ *
+ * Since: 0.10.24
+ */
+#define gst_buffer_list_is_writable(list) gst_mini_object_is_writable (GST_MINI_OBJECT_CAST (list))
+
+/**
+ * gst_buffer_list_make_writable:
+ * @list: (transfer full): a #GstBufferList
+ *
+ * Makes a writable buffer list from the given buffer list. If the source buffer
+ * list is already writable, this will simply return the same buffer list. A
+ * copy will otherwise be made using gst_buffer_list_copy().
+ *
+ * Returns: (transfer full): a writable list, which may or may not be the
+ * same as @list
+ *
+ * Since: 0.10.24
+ */
+#define gst_buffer_list_make_writable(list) GST_BUFFER_LIST_CAST (gst_mini_object_make_writable (GST_MINI_OBJECT_CAST (list)))
+
+GType gst_buffer_list_get_type (void);
+
+/* allocation */
+GstBufferList * gst_buffer_list_new (void);
+GstBufferList * gst_buffer_list_sized_new (guint size);
+
+guint gst_buffer_list_len (GstBufferList *list);
+
+GstBuffer * gst_buffer_list_get (GstBufferList *list, guint idx);
+void gst_buffer_list_insert (GstBufferList *list, guint idx, GstBuffer *buffer);
+void gst_buffer_list_remove (GstBufferList *list, guint idx, guint length);
+
+void gst_buffer_list_foreach (GstBufferList *list,
+ GstBufferListFunc func,
+ gpointer user_data);
+
+#define gst_buffer_list_add(l,b) gst_buffer_list_insert((l),-1,(b));
+
+G_END_DECLS
+
+#endif /* __GST_BUFFER_LIST_H__ */
diff --git a/gst/gstbufferpool.c b/gst/gstbufferpool.c
new file mode 100644
index 0000000..bb91167
--- /dev/null
+++ b/gst/gstbufferpool.c
@@ -0,0 +1,954 @@
+/* GStreamer
+ * Copyright (C) 2010 Wim Taymans <wim.taymans@gmail.com>
+ *
+ * gstbufferpool.c: GstBufferPool baseclass
+ *
+ * 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:gstbufferpool
+ * @short_description: Pool for buffers
+ * @see_also: #GstBuffer
+ *
+ */
+
+#include "gst_private.h"
+
+#include <errno.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#include <sys/types.h>
+
+#include "gstinfo.h"
+#include "gstquark.h"
+
+#include "gstbufferpool.h"
+
+GST_DEBUG_CATEGORY_STATIC (gst_buffer_pool_debug);
+#define GST_CAT_DEFAULT gst_buffer_pool_debug
+
+#define GST_BUFFER_POOL_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_BUFFER_POOL, GstBufferPoolPrivate))
+
+#define GST_BUFFER_POOL_LOCK(pool) (g_static_rec_mutex_lock(&pool->priv->rec_lock))
+#define GST_BUFFER_POOL_UNLOCK(pool) (g_static_rec_mutex_unlock(&pool->priv->rec_lock))
+
+struct _GstBufferPoolPrivate
+{
+ GStaticRecMutex rec_lock;
+ guint size;
+ guint min_buffers;
+ guint max_buffers;
+ guint prefix;
+ guint align;
+};
+
+enum
+{
+ /* add more above */
+ LAST_SIGNAL
+};
+
+static void gst_buffer_pool_finalize (GObject * object);
+
+G_DEFINE_TYPE (GstBufferPool, gst_buffer_pool, GST_TYPE_OBJECT);
+
+static gboolean default_start (GstBufferPool * pool);
+static gboolean default_stop (GstBufferPool * pool);
+static gboolean default_set_config (GstBufferPool * pool,
+ GstStructure * config);
+static GstFlowReturn default_alloc_buffer (GstBufferPool * pool,
+ GstBuffer ** buffer, GstBufferPoolParams * params);
+static GstFlowReturn default_acquire_buffer (GstBufferPool * pool,
+ GstBuffer ** buffer, GstBufferPoolParams * params);
+static void default_reset_buffer (GstBufferPool * pool, GstBuffer * buffer,
+ GstBufferPoolParams * params);
+static void default_free_buffer (GstBufferPool * pool, GstBuffer * buffer);
+static void default_release_buffer (GstBufferPool * pool, GstBuffer * buffer);
+
+static void
+gst_buffer_pool_class_init (GstBufferPoolClass * klass)
+{
+ GObjectClass *gobject_class = (GObjectClass *) klass;
+
+ g_type_class_add_private (klass, sizeof (GstBufferPoolPrivate));
+
+ gobject_class->finalize = gst_buffer_pool_finalize;
+
+ klass->start = default_start;
+ klass->stop = default_stop;
+ klass->set_config = default_set_config;
+ klass->acquire_buffer = default_acquire_buffer;
+ klass->reset_buffer = default_reset_buffer;
+ klass->alloc_buffer = default_alloc_buffer;
+ klass->release_buffer = default_release_buffer;
+ klass->free_buffer = default_free_buffer;
+
+ GST_DEBUG_CATEGORY_INIT (gst_buffer_pool_debug, "bufferpool", 0,
+ "bufferpool debug");
+}
+
+static void
+gst_buffer_pool_init (GstBufferPool * pool)
+{
+ pool->priv = GST_BUFFER_POOL_GET_PRIVATE (pool);
+
+ g_static_rec_mutex_init (&pool->priv->rec_lock);
+
+ pool->poll = gst_poll_new_timer ();
+ pool->queue = gst_atomic_queue_new (10);
+ pool->flushing = TRUE;
+ pool->active = FALSE;
+ pool->configured = FALSE;
+ pool->started = FALSE;
+ pool->config = gst_structure_id_empty_new (GST_QUARK (BUFFER_POOL_CONFIG));
+ gst_buffer_pool_config_set (pool->config, NULL, 0, 0, 0, 0, 0);
+ gst_poll_write_control (pool->poll);
+
+ GST_DEBUG_OBJECT (pool, "created");
+}
+
+static void
+gst_buffer_pool_finalize (GObject * object)
+{
+ GstBufferPool *pool;
+
+ pool = GST_BUFFER_POOL_CAST (object);
+
+ GST_DEBUG_OBJECT (pool, "finalize");
+
+ gst_buffer_pool_set_active (pool, FALSE);
+ gst_atomic_queue_unref (pool->queue);
+ gst_poll_free (pool->poll);
+ gst_structure_free (pool->config);
+ g_static_rec_mutex_free (&pool->priv->rec_lock);
+
+ G_OBJECT_CLASS (gst_buffer_pool_parent_class)->finalize (object);
+}
+
+/**
+ * gst_buffer_pool_new:
+ *
+ * Creates a new #GstBufferPool instance.
+ *
+ * Returns: a new #GstBufferPool instance
+ */
+GstBufferPool *
+gst_buffer_pool_new (void)
+{
+ GstBufferPool *result;
+
+ result = g_object_newv (GST_TYPE_BUFFER_POOL, 0, NULL);
+ GST_DEBUG_OBJECT (result, "created new buffer pool");
+
+ return result;
+}
+
+static GstFlowReturn
+default_alloc_buffer (GstBufferPool * pool, GstBuffer ** buffer,
+ GstBufferPoolParams * params)
+{
+ GstBufferPoolPrivate *priv = pool->priv;
+ GstMemory *mem;
+
+ *buffer = gst_buffer_new ();
+
+ mem = gst_allocator_alloc (NULL, priv->size + priv->prefix, priv->align);
+ gst_memory_resize (mem, priv->prefix, priv->size);
+ gst_buffer_take_memory (*buffer, -1, mem);
+
+ return GST_FLOW_OK;
+}
+
+/* the default implementation for preallocating the buffers
+ * in the pool */
+static gboolean
+default_start (GstBufferPool * pool)
+{
+ guint i;
+ GstBufferPoolPrivate *priv = pool->priv;
+ GstBufferPoolClass *pclass;
+
+ pclass = GST_BUFFER_POOL_GET_CLASS (pool);
+
+ /* no alloc function, error */
+ if (G_UNLIKELY (pclass->alloc_buffer == NULL))
+ goto no_alloc;
+
+ /* we need to prealloc buffers */
+ for (i = 0; i < priv->min_buffers; i++) {
+ GstBuffer *buffer;
+
+ if (pclass->alloc_buffer (pool, &buffer, NULL) != GST_FLOW_OK)
+ goto alloc_failed;
+
+ GST_LOG_OBJECT (pool, "prealloced buffer %d: %p", i, buffer);
+ /* release to the queue, we call the vmethod directly, we don't need to do
+ * the other refcount handling right now. */
+ if (G_LIKELY (pclass->release_buffer))
+ pclass->release_buffer (pool, buffer);
+ }
+ return TRUE;
+
+ /* ERRORS */
+no_alloc:
+ {
+ GST_WARNING_OBJECT (pool, "no alloc function");
+ return FALSE;
+ }
+alloc_failed:
+ {
+ GST_WARNING_OBJECT (pool, "alloc function failed");
+ return FALSE;
+ }
+}
+
+/* must be called with the lock */
+static gboolean
+do_start (GstBufferPool * pool)
+{
+ if (!pool->started) {
+ GstBufferPoolClass *pclass;
+
+ pclass = GST_BUFFER_POOL_GET_CLASS (pool);
+
+ GST_LOG_OBJECT (pool, "starting");
+ /* start the pool, subclasses should allocate buffers and put them
+ * in the queue */
+ if (G_LIKELY (pclass->start)) {
+ if (!pclass->start (pool))
+ return FALSE;
+ }
+ pool->started = TRUE;
+ }
+ return TRUE;
+}
+
+
+static void
+default_free_buffer (GstBufferPool * pool, GstBuffer * buffer)
+{
+ gst_buffer_unref (buffer);
+}
+
+/* must be called with the lock */
+static gboolean
+default_stop (GstBufferPool * pool)
+{
+ GstBuffer *buffer;
+ GstBufferPoolClass *pclass;
+
+ pclass = GST_BUFFER_POOL_GET_CLASS (pool);
+
+ /* clear the pool */
+ while ((buffer = gst_atomic_queue_pop (pool->queue))) {
+ GST_LOG_OBJECT (pool, "freeing %p", buffer);
+ gst_poll_read_control (pool->poll);
+
+ if (G_LIKELY (pclass->free_buffer))
+ pclass->free_buffer (pool, buffer);
+ }
+ return TRUE;
+}
+
+/* must be called with the lock */
+static gboolean
+do_stop (GstBufferPool * pool)
+{
+ if (pool->started) {
+ GstBufferPoolClass *pclass;
+
+ pclass = GST_BUFFER_POOL_GET_CLASS (pool);
+
+ GST_LOG_OBJECT (pool, "stopping");
+ if (G_LIKELY (pclass->stop)) {
+ if (!pclass->stop (pool))
+ return FALSE;
+ }
+ pool->started = FALSE;
+ }
+ return TRUE;
+}
+
+/**
+ * gst_buffer_pool_set_active:
+ * @pool: a #GstBufferPool
+ * @active: the new active state
+ *
+ * Control the active state of @pool. When the pool is active, new calls to
+ * gst_buffer_pool_acquire_buffer() will return with GST_FLOW_WRONG_STATE.
+ *
+ * Activating the bufferpool will preallocate all resources in the pool based on
+ * the configuration of the pool.
+ *
+ * Deactivating will free the resources again when there are no outstanding
+ * buffers. When there are outstanding buffers, they will be freed as soon as
+ * they are all returned to the pool.
+ *
+ * Returns: %FALSE when the pool was not configured or when preallocation of the
+ * buffers failed.
+ */
+gboolean
+gst_buffer_pool_set_active (GstBufferPool * pool, gboolean active)
+{
+ gboolean res = TRUE;
+
+ g_return_val_if_fail (GST_IS_BUFFER_POOL (pool), FALSE);
+
+ GST_LOG_OBJECT (pool, "active %d", active);
+
+ GST_BUFFER_POOL_LOCK (pool);
+ /* just return if we are already in the right state */
+ if (pool->active == active)
+ goto was_ok;
+
+ /* we need to be configured */
+ if (!pool->configured)
+ goto not_configured;
+
+ if (active) {
+ if (!do_start (pool))
+ goto start_failed;
+
+ /* unset the flushing state now */
+ gst_poll_read_control (pool->poll);
+ g_atomic_int_set (&pool->flushing, FALSE);
+ } else {
+ gint outstanding;
+
+ /* set to flushing first */
+ g_atomic_int_set (&pool->flushing, TRUE);
+ gst_poll_write_control (pool->poll);
+
+ /* when all buffers are in the pool, free them. Else they will be
+ * freed when they are released */
+ outstanding = g_atomic_int_get (&pool->outstanding);
+ GST_LOG_OBJECT (pool, "outstanding buffers %d", outstanding);
+ if (outstanding == 0) {
+ if (!do_stop (pool))
+ goto stop_failed;
+ }
+ }
+ pool->active = active;
+ GST_BUFFER_POOL_UNLOCK (pool);
+
+ return res;
+
+was_ok:
+ {
+ GST_DEBUG_OBJECT (pool, "pool was in the right state");
+ GST_BUFFER_POOL_UNLOCK (pool);
+ return TRUE;
+ }
+not_configured:
+ {
+ GST_ERROR_OBJECT (pool, "pool was not configured");
+ GST_BUFFER_POOL_UNLOCK (pool);
+ return FALSE;
+ }
+start_failed:
+ {
+ GST_ERROR_OBJECT (pool, "start failed");
+ GST_BUFFER_POOL_UNLOCK (pool);
+ return FALSE;
+ }
+stop_failed:
+ {
+ GST_WARNING_OBJECT (pool, "stop failed");
+ GST_BUFFER_POOL_UNLOCK (pool);
+ return FALSE;
+ }
+}
+
+/**
+ * gst_buffer_pool_is_active:
+ * @pool: a #GstBufferPool
+ *
+ * Check if @pool is active. A pool can be activated with the
+ * gst_buffer_pool_set_active() call.
+ *
+ * Returns: %TRUE when the pool is active.
+ */
+gboolean
+gst_buffer_pool_is_active (GstBufferPool * pool)
+{
+ gboolean res;
+
+ GST_BUFFER_POOL_LOCK (pool);
+ res = pool->active;
+ GST_BUFFER_POOL_UNLOCK (pool);
+
+ return res;
+}
+
+static gboolean
+default_set_config (GstBufferPool * pool, GstStructure * config)
+{
+ GstBufferPoolPrivate *priv = pool->priv;
+ const GstCaps *caps;
+ guint size, min_buffers, max_buffers;
+ guint prefix, align;
+
+ /* parse the config and keep around */
+ if (!gst_buffer_pool_config_get (config, &caps, &size, &min_buffers,
+ &max_buffers, &prefix, &align))
+ goto wrong_config;
+
+ GST_DEBUG_OBJECT (pool, "config %" GST_PTR_FORMAT, config);
+
+ priv->size = size;
+ priv->min_buffers = min_buffers;
+ priv->max_buffers = max_buffers;
+ priv->prefix = prefix;
+ priv->align = align;
+
+ return TRUE;
+
+wrong_config:
+ {
+ GST_WARNING_OBJECT (pool, "invalid config %" GST_PTR_FORMAT, config);
+ return FALSE;
+ }
+}
+
+/**
+ * gst_buffer_pool_set_config:
+ * @pool: a #GstBufferPool
+ * @config: a #GstStructure
+ *
+ * Set the configuration of the pool. The pool must be inactive and all buffers
+ * allocated form this pool must be returned or else this function will do
+ * nothing and return FALSE.
+ *
+ * @config is a #GstStructure that contains the configuration parameters for
+ * the pool. A default and mandatory set of parameters can be configured with
+ * gst_buffer_pool_config_set(). This function takes ownership of @config.
+ *
+ * Returns: TRUE when the configuration could be set.
+ */
+gboolean
+gst_buffer_pool_set_config (GstBufferPool * pool, GstStructure * config)
+{
+ gboolean result;
+ GstBufferPoolClass *pclass;
+
+ g_return_val_if_fail (GST_IS_BUFFER_POOL (pool), FALSE);
+ g_return_val_if_fail (config != NULL, FALSE);
+
+ GST_BUFFER_POOL_LOCK (pool);
+ /* can't change the settings when active */
+ if (pool->active)
+ goto was_active;
+
+ /* we can't change when outstanding buffers */
+ if (g_atomic_int_get (&pool->outstanding) != 0)
+ goto have_outstanding;
+
+ pclass = GST_BUFFER_POOL_GET_CLASS (pool);
+
+ /* set the new config */
+ if (G_LIKELY (pclass->set_config))
+ result = pclass->set_config (pool, config);
+ else
+ result = FALSE;
+
+ if (result) {
+ if (pool->config)
+ gst_structure_free (pool->config);
+ pool->config = config;
+
+ /* now we are configured */
+ pool->configured = TRUE;
+ }
+ GST_BUFFER_POOL_UNLOCK (pool);
+
+ return result;
+
+ /* ERRORS */
+was_active:
+ {
+ GST_WARNING_OBJECT (pool, "can't change config, we are active");
+ GST_BUFFER_POOL_UNLOCK (pool);
+ return FALSE;
+ }
+have_outstanding:
+ {
+ GST_WARNING_OBJECT (pool, "can't change config, have outstanding buffers");
+ GST_BUFFER_POOL_UNLOCK (pool);
+ return FALSE;
+ }
+}
+
+/**
+ * gst_buffer_pool_get_config:
+ * @pool: a #GstBufferPool
+ *
+ * Get a copy of the current configuration of the pool. This configuration
+ * can either be modified and used for the gst_buffer_pool_set_config() call
+ * or it must be freed after usage.
+ *
+ * Returns: a copy of the current configuration of @pool. use
+ * gst_structure_free() after usage or gst_buffer_pool_set_config().
+ */
+GstStructure *
+gst_buffer_pool_get_config (GstBufferPool * pool)
+{
+ GstStructure *result;
+
+ g_return_val_if_fail (GST_IS_BUFFER_POOL (pool), NULL);
+
+ GST_BUFFER_POOL_UNLOCK (pool);
+ result = gst_structure_copy (pool->config);
+ GST_BUFFER_POOL_UNLOCK (pool);
+
+ return result;
+}
+
+static const gchar *empty_option[] = { NULL };
+
+/**
+ * gst_buffer_pool_get_options:
+ * @pool: a #GstBufferPool
+ *
+ * Get a NULL terminated array of string with supported bufferpool options for
+ * @pool. An option would typically be enabled with
+ * gst_buffer_pool_config_add_option().
+ *
+ * Returns: a NULL terminated array of strings.
+ */
+const gchar **
+gst_buffer_pool_get_options (GstBufferPool * pool)
+{
+ GstBufferPoolClass *pclass;
+ const gchar **result;
+
+ g_return_val_if_fail (GST_IS_BUFFER_POOL (pool), NULL);
+
+ pclass = GST_BUFFER_POOL_GET_CLASS (pool);
+
+ if (G_LIKELY (pclass->get_options)) {
+ if ((result = pclass->get_options (pool)) == NULL)
+ goto invalid_result;
+ } else
+ result = empty_option;
+
+ return result;
+
+ /* ERROR */
+invalid_result:
+ {
+ g_warning ("bufferpool subclass returned NULL options");
+ return empty_option;
+ }
+}
+
+/**
+ * gst_buffer_pool_has_option:
+ * @pool: a #GstBufferPool
+ * @option: an option
+ *
+ * Check if the bufferpool supports @option.
+ *
+ * Returns: a NULL terminated array of strings.
+ */
+gboolean
+gst_buffer_pool_has_option (GstBufferPool * pool, const gchar * option)
+{
+ guint i;
+ const gchar **options;
+
+ g_return_val_if_fail (GST_IS_BUFFER_POOL (pool), FALSE);
+ g_return_val_if_fail (option != NULL, FALSE);
+
+ options = gst_buffer_pool_get_options (pool);
+
+ for (i = 0; options[i]; i++) {
+ if (g_str_equal (options[i], option))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ * gst_buffer_pool_config_set:
+ * @config: a #GstBufferPool configuration
+ * @caps: caps for the buffers
+ * @size: the size of each buffer, not including prefix
+ * @min_buffers: the minimum amount of buffers to allocate.
+ * @max_buffers: the maximum amount of buffers to allocate or 0 for unlimited.
+ * @prefix: prefix each buffer with this many bytes
+ * @align: alignment of the buffer data.
+ *
+ * Configure @config with the given parameters.
+ */
+void
+gst_buffer_pool_config_set (GstStructure * config, const GstCaps * caps,
+ guint size, guint min_buffers, guint max_buffers, guint prefix, guint align)
+{
+ g_return_if_fail (config != NULL);
+
+ gst_structure_id_set (config,
+ GST_QUARK (CAPS), GST_TYPE_CAPS, caps,
+ GST_QUARK (SIZE), G_TYPE_UINT, size,
+ GST_QUARK (MIN_BUFFERS), G_TYPE_UINT, min_buffers,
+ GST_QUARK (MAX_BUFFERS), G_TYPE_UINT, max_buffers,
+ GST_QUARK (PREFIX), G_TYPE_UINT, prefix,
+ GST_QUARK (ALIGN), G_TYPE_UINT, align, NULL);
+}
+
+/**
+ * gst_buffer_pool_config_add_option:
+ * @config: a #GstBufferPool configuration
+ * @option: an option to add
+ *
+ * Enabled the option in @config. This will instruct the @bufferpool to enable
+ * the specified option on the buffers that it allocates.
+ *
+ * The supported options by @pool can be retrieved with gst_buffer_pool_get_options().
+ */
+void
+gst_buffer_pool_config_add_option (GstStructure * config, const gchar * option)
+{
+ GValueArray *array;
+ const GValue *value;
+ GValue option_value = { 0 };
+ gint i;
+
+ g_return_if_fail (config != NULL);
+
+ value = gst_structure_id_get_value (config, GST_QUARK (OPTIONS));
+ if (value) {
+ array = (GValueArray *) g_value_get_boxed (value);
+ } else {
+ GValue new_array_val = { 0, };
+
+ array = g_value_array_new (0);
+
+ g_value_init (&new_array_val, G_TYPE_VALUE_ARRAY);
+ g_value_take_boxed (&new_array_val, array);
+
+ gst_structure_id_take_value (config, GST_QUARK (OPTIONS), &new_array_val);
+ }
+ for (i = 0; i < array->n_values; i++) {
+ value = g_value_array_get_nth (array, i);
+ if (g_str_equal (option, g_value_get_string (value)))
+ return;
+ }
+ g_value_init (&option_value, G_TYPE_STRING);
+ g_value_set_string (&option_value, option);
+ g_value_array_append (array, &option_value);
+ g_value_unset (&option_value);
+}
+
+/**
+ * gst_buffer_pool_config_n_options:
+ * @config: a #GstBufferPool configuration
+ *
+ * Retrieve the number of values currently stored in the
+ * options array of the @config structure.
+ *
+ * Returns: the options array size as a #guint.
+ */
+guint
+gst_buffer_pool_config_n_options (GstStructure * config)
+{
+ GValueArray *array;
+ const GValue *value;
+ guint size = 0;
+
+ g_return_val_if_fail (config != NULL, 0);
+
+ value = gst_structure_id_get_value (config, GST_QUARK (OPTIONS));
+ if (value) {
+ array = (GValueArray *) g_value_get_boxed (value);
+ size = array->n_values;
+ }
+ return size;
+}
+
+/**
+ * gst_buffer_pool_config_get_option:
+ * @config: a #GstBufferPool configuration
+ * @index: position in the option array to read
+ *
+ * Parse an available @config and get the option
+ * at @index of the options API array.
+ *
+ * Returns: a #gchar of the option at @index.
+ */
+const gchar *
+gst_buffer_pool_config_get_option (GstStructure * config, guint index)
+{
+ const GValue *value;
+ const gchar *ret = NULL;
+
+ g_return_val_if_fail (config != NULL, 0);
+
+ value = gst_structure_id_get_value (config, GST_QUARK (OPTIONS));
+ if (value) {
+ GValueArray *array;
+ GValue *option_value;
+
+ array = (GValueArray *) g_value_get_boxed (value);
+ option_value = g_value_array_get_nth (array, index);
+
+ if (option_value)
+ ret = g_value_get_string (option_value);
+ }
+ return ret;
+}
+
+/**
+ * gst_buffer_pool_config_has_option:
+ * @config: a #GstBufferPool configuration
+ * @option: an option
+ *
+ * Check if @config contains @option
+ *
+ * Returns: TRUE if the options array contains @option.
+ */
+gboolean
+gst_buffer_pool_config_has_option (GstStructure * config, const gchar * option)
+{
+ const GValue *value;
+
+ g_return_val_if_fail (config != NULL, 0);
+
+ value = gst_structure_id_get_value (config, GST_QUARK (OPTIONS));
+ if (value) {
+ GValueArray *array;
+ GValue *option_value;
+ gint i;
+
+ array = (GValueArray *) g_value_get_boxed (value);
+ for (i = 0; i < array->n_values; i++) {
+ option_value = g_value_array_get_nth (array, i);
+ if (g_str_equal (option, g_value_get_string (option_value)))
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * gst_buffer_pool_config_get:
+ * @config: a #GstBufferPool configuration
+ * @caps: the caps of buffers
+ * @size: the size of each buffer, not including prefix
+ * @min_buffers: the minimum amount of buffers to allocate.
+ * @max_buffers: the maximum amount of buffers to allocate or 0 for unlimited.
+ * @prefix: prefix each buffer with this many bytes
+ * @align: alignment of the buffer data.
+ *
+ * Get the configuration values from @config.
+ */
+gboolean
+gst_buffer_pool_config_get (GstStructure * config, const GstCaps ** caps,
+ guint * size, guint * min_buffers, guint * max_buffers, guint * prefix,
+ guint * align)
+{
+ g_return_val_if_fail (config != NULL, FALSE);
+
+ return gst_structure_id_get (config,
+ GST_QUARK (CAPS), GST_TYPE_CAPS, caps,
+ GST_QUARK (SIZE), G_TYPE_UINT, size,
+ GST_QUARK (MIN_BUFFERS), G_TYPE_UINT, min_buffers,
+ GST_QUARK (MAX_BUFFERS), G_TYPE_UINT, max_buffers,
+ GST_QUARK (PREFIX), G_TYPE_UINT, prefix,
+ GST_QUARK (ALIGN), G_TYPE_UINT, align, NULL);
+}
+
+static GstFlowReturn
+default_acquire_buffer (GstBufferPool * pool, GstBuffer ** buffer,
+ GstBufferPoolParams * params)
+{
+ GstFlowReturn result;
+ GstBufferPoolClass *pclass;
+ GstBufferPoolPrivate *priv = pool->priv;
+
+ pclass = GST_BUFFER_POOL_GET_CLASS (pool);
+
+ while (TRUE) {
+ if (G_UNLIKELY (g_atomic_int_get (&pool->flushing)))
+ goto flushing;
+
+ /* try to get a buffer from the queue */
+ *buffer = gst_atomic_queue_pop (pool->queue);
+ if (G_LIKELY (*buffer)) {
+ gst_poll_read_control (pool->poll);
+ result = GST_FLOW_OK;
+ GST_LOG_OBJECT (pool, "acquired buffer %p", *buffer);
+ break;
+ }
+
+ /* no buffer */
+ if (priv->max_buffers == 0) {
+ /* no max_buffers, we allocate some more */
+ if (G_LIKELY (pclass->alloc_buffer)) {
+ result = pclass->alloc_buffer (pool, buffer, params);
+ } else
+ result = GST_FLOW_NOT_SUPPORTED;
+ GST_LOG_OBJECT (pool, "alloc buffer %p", *buffer);
+ break;
+ }
+
+ /* check if we need to wait */
+ if (params && (params->flags & GST_BUFFER_POOL_FLAG_DONTWAIT)) {
+ GST_LOG_OBJECT (pool, "no more buffers");
+ result = GST_FLOW_UNEXPECTED;
+ break;
+ }
+
+ /* now wait */
+ GST_LOG_OBJECT (pool, "waiting for free buffers");
+ gst_poll_wait (pool->poll, GST_CLOCK_TIME_NONE);
+ }
+
+ return result;
+
+ /* ERRORS */
+flushing:
+ {
+ GST_DEBUG_OBJECT (pool, "we are flushing");
+ return GST_FLOW_WRONG_STATE;
+ }
+}
+
+static inline void
+dec_outstanding (GstBufferPool * pool)
+{
+ if (g_atomic_int_dec_and_test (&pool->outstanding)) {
+ /* all buffers are returned to the pool, see if we need to free them */
+ if (g_atomic_int_get (&pool->flushing)) {
+ /* take the lock so that set_active is not run concurrently */
+ GST_BUFFER_POOL_LOCK (pool);
+ /* recheck the flushing state in the lock, the pool could have been
+ * set to active again */
+ if (g_atomic_int_get (&pool->flushing))
+ do_stop (pool);
+
+ GST_BUFFER_POOL_UNLOCK (pool);
+ }
+ }
+}
+
+static void
+default_reset_buffer (GstBufferPool * pool, GstBuffer * buffer,
+ GstBufferPoolParams * params)
+{
+ GST_BUFFER_FLAGS (buffer) = 0;
+
+ GST_BUFFER_TIMESTAMP (buffer) = GST_CLOCK_TIME_NONE;
+ GST_BUFFER_DURATION (buffer) = GST_CLOCK_TIME_NONE;
+ GST_BUFFER_OFFSET (buffer) = GST_BUFFER_OFFSET_NONE;
+ GST_BUFFER_OFFSET_END (buffer) = GST_BUFFER_OFFSET_NONE;
+}
+
+/**
+ * gst_buffer_pool_acquire_buffer:
+ * @pool: a #GstBufferPool
+ * @buffer: a location for a #GstBuffer
+ * @params: parameters.
+ *
+ * Acquire a buffer from @pool. @buffer should point to a memory location that
+ * can hold a pointer to the new buffer.
+ *
+ * @params can be NULL or contain optional parameters to influence the allocation.
+ *
+ * Returns: a #GstFlowReturn such as GST_FLOW_WRONG_STATE when the pool is
+ * inactive.
+ */
+GstFlowReturn
+gst_buffer_pool_acquire_buffer (GstBufferPool * pool, GstBuffer ** buffer,
+ GstBufferPoolParams * params)
+{
+ GstBufferPoolClass *pclass;
+ GstFlowReturn result;
+
+ g_return_val_if_fail (GST_IS_BUFFER_POOL (pool), GST_FLOW_ERROR);
+ g_return_val_if_fail (buffer != NULL, GST_FLOW_ERROR);
+
+ pclass = GST_BUFFER_POOL_GET_CLASS (pool);
+
+ /* assume we'll have one more outstanding buffer we need to do that so
+ * that concurrent set_active doesn't clear the buffers */
+ g_atomic_int_inc (&pool->outstanding);
+
+ if (G_LIKELY (pclass->acquire_buffer))
+ result = pclass->acquire_buffer (pool, buffer, params);
+ else
+ result = GST_FLOW_NOT_SUPPORTED;
+
+ if (G_LIKELY (result == GST_FLOW_OK)) {
+ /* all buffers from the pool point to the pool and have the refcount of the
+ * pool incremented */
+ (*buffer)->pool = gst_object_ref (pool);
+ /* now reset the buffer when needed */
+ if (G_LIKELY (pclass->reset_buffer))
+ pclass->reset_buffer (pool, *buffer, params);
+ } else {
+ dec_outstanding (pool);
+ }
+
+ return result;
+}
+
+static void
+default_release_buffer (GstBufferPool * pool, GstBuffer * buffer)
+{
+ /* keep it around in our queue */
+ GST_LOG_OBJECT (pool, "released buffer %p", buffer);
+ gst_atomic_queue_push (pool->queue, buffer);
+ gst_poll_write_control (pool->poll);
+}
+
+/**
+ * gst_buffer_pool_release_buffer:
+ * @pool: a #GstBufferPool
+ * @buffer: a #GstBuffer
+ *
+ * Release @buffer to @pool. @buffer should have previously been allocated from
+ * @pool with gst_buffer_pool_acquire_buffer().
+ *
+ * This function is usually called automatically when the last ref on @buffer
+ * disappears.
+ */
+void
+gst_buffer_pool_release_buffer (GstBufferPool * pool, GstBuffer * buffer)
+{
+ GstBufferPoolClass *pclass;
+
+ g_return_if_fail (GST_IS_BUFFER_POOL (pool));
+ g_return_if_fail (buffer != NULL);
+
+ /* check that the buffer is ours, all buffers returned to the pool have the
+ * pool member set to NULL and the pool refcount decreased */
+ if (!g_atomic_pointer_compare_and_exchange ((gpointer *) & buffer->pool,
+ pool, NULL))
+ return;
+
+ pclass = GST_BUFFER_POOL_GET_CLASS (pool);
+
+ if (G_LIKELY (pclass->release_buffer))
+ pclass->release_buffer (pool, buffer);
+
+ dec_outstanding (pool);
+
+ /* decrease the refcount that the buffer had to us */
+ gst_object_unref (pool);
+}
diff --git a/gst/gstbufferpool.h b/gst/gstbufferpool.h
new file mode 100644
index 0000000..639fd26
--- /dev/null
+++ b/gst/gstbufferpool.h
@@ -0,0 +1,214 @@
+/* GStreamer
+ * Copyright (C) 2010 Wim Taymans <wim.taymans@gmail.com>
+ *
+ * gstbufferpool.h: Header for GstBufferPool object
+ *
+ * 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_BUFFER_POOL_H__
+#define __GST_BUFFER_POOL_H__
+
+#include <gst/gstminiobject.h>
+#include <gst/gstatomicqueue.h>
+#include <gst/gstpoll.h>
+#include <gst/gstclock.h>
+#include <gst/gstpad.h>
+#include <gst/gstbuffer.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GstBufferPoolPrivate GstBufferPoolPrivate;
+typedef struct _GstBufferPoolClass GstBufferPoolClass;
+
+/**
+ * GST_BUFFER_POOL_TRACE_NAME:
+ *
+ * The name used for tracing memory allocations.
+ */
+#define GST_BUFFER_POOL_TRACE_NAME "GstBufferPool"
+
+#define GST_TYPE_BUFFER_POOL (gst_buffer_pool_get_type())
+#define GST_IS_BUFFER_POOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_BUFFER_POOL))
+#define GST_IS_BUFFER_POOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_BUFFER_POOL))
+#define GST_BUFFER_POOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_BUFFER_POOL, GstBufferPoolClass))
+#define GST_BUFFER_POOL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_BUFFER_POOL, GstBufferPool))
+#define GST_BUFFER_POOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_BUFFER_POOL, GstBufferPoolClass))
+#define GST_BUFFER_POOL_CAST(obj) ((GstBufferPool *)(obj))
+
+/**
+ * GstBufferPoolFlags:
+ * @GST_BUFFER_POOL_FLAG_NONE: no flags
+ * @GST_BUFFER_POOL_FLAG_KEY_UNIT: buffer is keyframe
+ * @GST_BUFFER_POOL_FLAG_DONTWAIT: don't wait for buffer. This makes the
+ * acquire_buffer method return GST_FLOW_UNEXPECTED.
+ * @GST_BUFFER_POOL_FLAG_DISCONT: buffer is discont
+ * @GST_BUFFER_POOL_FLAG_LAST: last flag, subclasses can use private flags
+ * starting from this value.
+ *
+ * Additional flags to control the allocation of a buffer
+ */
+typedef enum {
+ GST_BUFFER_POOL_FLAG_NONE = 0,
+ GST_BUFFER_POOL_FLAG_KEY_UNIT = (1 << 0),
+ GST_BUFFER_POOL_FLAG_DONTWAIT = (1 << 1),
+ GST_BUFFER_POOL_FLAG_DISCONT = (1 << 2),
+ GST_BUFFER_POOL_FLAG_LAST = (1 << 16),
+} GstBufferPoolFlags;
+
+/**
+ * GstBufferPoolParams:
+ * @format: the format of @start and @stop
+ * @start: the start position
+ * @stop: the stop position
+ * @flags: additional flags
+ *
+ * Parameters passed to the gst_buffer_pool_acquire_buffer() function to control the
+ * allocation of the buffer.
+ *
+ * The default implementation ignores the @start and @stop members but other
+ * implementations can use this extra information to decide what buffer to
+ * return.
+ */
+typedef struct _GstBufferPoolParams {
+ GstFormat format;
+ gint64 start;
+ gint64 stop;
+ GstBufferPoolFlags flags;
+} GstBufferPoolParams;
+
+/**
+ * GST_BUFFER_POOL_IS_FLUSHING:
+ * @pool: a GstBufferPool
+ *
+ * Check if the bufferpool is flushing. Subclasses might want to check the
+ * state of the pool in the acquire function.
+ */
+#define GST_BUFFER_POOL_IS_FLUSHING(pool) (g_atomic_int_get (&pool->flushing))
+
+/**
+ * GstBufferPool:
+ * @object: the parent structure
+ *
+ * The structure of a #GstBufferPool. Use the associated macros to access the public
+ * variables.
+ */
+struct _GstBufferPool {
+ GstObject object;
+
+ /*< private >*/
+ gboolean active;
+ gboolean flushing;
+ gboolean started;
+ gint outstanding;
+ GstAtomicQueue *queue;
+ GstPoll *poll;
+
+ gboolean configured;
+ GstStructure *config;
+
+ GstBufferPoolPrivate *priv;
+
+ gpointer _gst_reserved[GST_PADDING];
+};
+
+/**
+ * GstBufferPoolClass:
+ * @object_class: Object parent class
+ * @get_options: get a list of options supported by this pool
+ * @set_config: apply the bufferpool configuration. The default configuration
+ * will parse the default config parameters
+ * @start: start the bufferpool. The default implementation will preallocate
+ * min-buffers buffers and put them in the queue
+ * @stop: stop the bufferpool. the default implementation will free the
+ * preallocated buffers. This function is called when all the buffers are
+ * returned to the pool.
+ * @acquire_buffer: get a new buffer from the pool. The default implementation
+ * will take a buffer from the queue and optionally wait for a buffer to
+ * be released when there are no buffers available.
+ * @alloc_buffer: allocate a buffer. the default implementation allocates
+ * buffers from the default memory allocator and with the configured
+ * size, prefix and alignment.
+ * @reset_buffer: reset the buffer to its state when it was freshly allocated.
+ * The default implementation will clear the flags and timestamps.
+ * @release_buffer: release a buffer back in the pool. The default
+ * implementation will put the buffer back in the queue and notify any
+ * blocking acquire_buffer calls.
+ * @free_buffer: free a buffer. The default implementation unrefs the buffer.
+ *
+ * The GstBufferPool class.
+ */
+struct _GstBufferPoolClass {
+ GstObjectClass object_class;
+
+ /* vmethods */
+ const gchar ** (*get_options) (GstBufferPool *pool);
+ gboolean (*set_config) (GstBufferPool *pool, GstStructure *config);
+
+ gboolean (*start) (GstBufferPool *pool);
+ gboolean (*stop) (GstBufferPool *pool);
+
+ GstFlowReturn (*acquire_buffer) (GstBufferPool *pool, GstBuffer **buffer,
+ GstBufferPoolParams *params);
+ GstFlowReturn (*alloc_buffer) (GstBufferPool *pool, GstBuffer **buffer,
+ GstBufferPoolParams *params);
+ void (*reset_buffer) (GstBufferPool *pool, GstBuffer *buffer,
+ GstBufferPoolParams *params);
+ void (*release_buffer) (GstBufferPool *pool, GstBuffer *buffer);
+ void (*free_buffer) (GstBufferPool *pool, GstBuffer *buffer);
+
+ /*< private >*/
+ gpointer _gst_reserved[GST_PADDING];
+};
+
+GType gst_buffer_pool_get_type (void);
+
+/* allocation */
+GstBufferPool * gst_buffer_pool_new (void);
+
+/* state management */
+gboolean gst_buffer_pool_set_active (GstBufferPool *pool, gboolean active);
+gboolean gst_buffer_pool_is_active (GstBufferPool *pool);
+
+gboolean gst_buffer_pool_set_config (GstBufferPool *pool, GstStructure *config);
+GstStructure * gst_buffer_pool_get_config (GstBufferPool *pool);
+
+const gchar ** gst_buffer_pool_get_options (GstBufferPool *pool);
+gboolean gst_buffer_pool_has_option (GstBufferPool *pool, const gchar *option);
+
+/* helpers for configuring the config structure */
+void gst_buffer_pool_config_set (GstStructure *config, const GstCaps *caps,
+ guint size, guint min_buffers, guint max_buffers,
+ guint prefix, guint align);
+gboolean gst_buffer_pool_config_get (GstStructure *config, const GstCaps **caps,
+ guint *size, guint *min_buffers, guint *max_buffers,
+ guint *prefix, guint *align);
+
+/* options */
+guint gst_buffer_pool_config_n_options (GstStructure *config);
+void gst_buffer_pool_config_add_option (GstStructure *config, const gchar *option);
+const gchar * gst_buffer_pool_config_get_option (GstStructure *config, guint index);
+gboolean gst_buffer_pool_config_has_option (GstStructure *config, const gchar *option);
+
+/* buffer management */
+GstFlowReturn gst_buffer_pool_acquire_buffer (GstBufferPool *pool, GstBuffer **buffer,
+ GstBufferPoolParams *params);
+void gst_buffer_pool_release_buffer (GstBufferPool *pool, GstBuffer *buffer);
+
+G_END_DECLS
+
+#endif /* __GST_BUFFER_POOL_H__ */
diff --git a/gst/gstbus.c b/gst/gstbus.c
new file mode 100644
index 0000000..e3a15f7
--- /dev/null
+++ b/gst/gstbus.c
@@ -0,0 +1,1310 @@
+/* GStreamer
+ * Copyright (C) 2004 Wim Taymans <wim@fluendo.com>
+ *
+ * gstbus.c: GstBus subsystem
+ *
+ * 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:gstbus
+ * @short_description: Asynchronous message bus subsystem
+ * @see_also: #GstMessage, #GstElement
+ *
+ * The #GstBus is an object responsible for delivering #GstMessage packets in
+ * a first-in first-out way from the streaming threads (see #GstTask) to the
+ * application.
+ *
+ * Since the application typically only wants to deal with delivery of these
+ * messages from one thread, the GstBus will marshall the messages between
+ * different threads. This is important since the actual streaming of media
+ * is done in another thread than the application.
+ *
+ * The GstBus provides support for #GSource based notifications. This makes it
+ * possible to handle the delivery in the glib mainloop.
+ *
+ * The #GSource callback function gst_bus_async_signal_func() can be used to
+ * convert all bus messages into signal emissions.
+ *
+ * A message is posted on the bus with the gst_bus_post() method. With the
+ * gst_bus_peek() and gst_bus_pop() methods one can look at or retrieve a
+ * previously posted message.
+ *
+ * The bus can be polled with the gst_bus_poll() method. This methods blocks
+ * up to the specified timeout value until one of the specified messages types
+ * is posted on the bus. The application can then gst_bus_pop() the messages
+ * from the bus to handle them.
+ * Alternatively the application can register an asynchronous bus function
+ * using gst_bus_add_watch_full() or gst_bus_add_watch(). This function will
+ * install a #GSource in the default glib main loop and will deliver messages
+ * a short while after they have been posted. Note that the main loop should
+ * be running for the asynchronous callbacks.
+ *
+ * It is also possible to get messages from the bus without any thread
+ * marshalling with the gst_bus_set_sync_handler() method. This makes it
+ * possible to react to a message in the same thread that posted the
+ * message on the bus. This should only be used if the application is able
+ * to deal with messages from different threads.
+ *
+ * Every #GstPipeline has one bus.
+ *
+ * Note that a #GstPipeline will set its bus into flushing state when changing
+ * from READY to NULL state.
+ *
+ * Last reviewed on 2006-03-12 (0.10.5)
+ */
+
+#include "gst_private.h"
+#include <errno.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#include <sys/types.h>
+
+#include "gstinfo.h"
+#include "gstpoll.h"
+
+#include "gstbus.h"
+
+#define GST_CAT_DEFAULT GST_CAT_BUS
+/* bus signals */
+enum
+{
+ SYNC_MESSAGE,
+ ASYNC_MESSAGE,
+ /* add more above */
+ LAST_SIGNAL
+};
+
+#define DEFAULT_ENABLE_ASYNC (TRUE)
+
+enum
+{
+ PROP_0,
+ PROP_ENABLE_ASYNC
+};
+
+static void gst_bus_dispose (GObject * object);
+
+static GstObjectClass *parent_class = NULL;
+static guint gst_bus_signals[LAST_SIGNAL] = { 0 };
+
+struct _GstBusPrivate
+{
+ guint num_sync_message_emitters;
+ GSource *watch_id;
+
+ gboolean enable_async;
+ GstPoll *poll;
+ GPollFD pollfd;
+};
+
+G_DEFINE_TYPE (GstBus, gst_bus, GST_TYPE_OBJECT);
+
+static void
+gst_bus_set_property (GObject * object,
+ guint prop_id, const GValue * value, GParamSpec * pspec)
+{
+ GstBus *bus = GST_BUS_CAST (object);
+
+ switch (prop_id) {
+ case PROP_ENABLE_ASYNC:
+ bus->priv->enable_async = g_value_get_boolean (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_bus_constructed (GObject * object)
+{
+ GstBus *bus = GST_BUS_CAST (object);
+
+ if (bus->priv->enable_async) {
+ bus->priv->poll = gst_poll_new_timer ();
+ gst_poll_get_read_gpollfd (bus->priv->poll, &bus->priv->pollfd);
+ }
+}
+
+static void
+gst_bus_class_init (GstBusClass * klass)
+{
+ GObjectClass *gobject_class = (GObjectClass *) klass;
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ gobject_class->dispose = gst_bus_dispose;
+ gobject_class->set_property = gst_bus_set_property;
+ gobject_class->constructed = gst_bus_constructed;
+
+ /* GstBus:enable-async:
+ *
+ * Enable async message delivery support for bus watches,
+ * gst_bus_pop() and similar API. Without this only the
+ * synchronous message handlers are called.
+ *
+ * This property is used to create the child element buses
+ * in #GstBin.
+ *
+ * Since: 0.10.33
+ */
+ g_object_class_install_property (gobject_class, PROP_ENABLE_ASYNC,
+ g_param_spec_boolean ("enable-async", "Enable Async",
+ "Enable async message delivery for bus watches and gst_bus_pop()",
+ DEFAULT_ENABLE_ASYNC,
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GstBus::sync-message:
+ * @bus: the object which received the signal
+ * @message: the message that has been posted synchronously
+ *
+ * A message has been posted on the bus. This signal is emitted from the
+ * thread that posted the message so one has to be careful with locking.
+ *
+ * This signal will not be emitted by default, you have to set up
+ * gst_bus_sync_signal_handler() as a sync handler if you want this
+ * signal to be emitted when a message is posted on the bus, like this:
+ * <programlisting>
+ * gst_bus_set_sync_handler (bus, gst_bus_sync_signal_handler, yourdata);
+ * </programlisting>
+ */
+ gst_bus_signals[SYNC_MESSAGE] =
+ g_signal_new ("sync-message", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ G_STRUCT_OFFSET (GstBusClass, sync_message), NULL, NULL,
+ g_cclosure_marshal_VOID__BOXED, G_TYPE_NONE, 1, GST_TYPE_MESSAGE);
+
+ /**
+ * GstBus::message:
+ * @bus: the object which received the signal
+ * @message: the message that has been posted asynchronously
+ *
+ * A message has been posted on the bus. This signal is emitted from a
+ * GSource added to the mainloop. this signal will only be emitted when
+ * there is a mainloop running.
+ */
+ gst_bus_signals[ASYNC_MESSAGE] =
+ g_signal_new ("message", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ G_STRUCT_OFFSET (GstBusClass, message), NULL, NULL,
+ g_cclosure_marshal_VOID__BOXED, G_TYPE_NONE, 1, GST_TYPE_MESSAGE);
+
+ g_type_class_add_private (klass, sizeof (GstBusPrivate));
+}
+
+static void
+gst_bus_init (GstBus * bus)
+{
+ bus->queue = gst_atomic_queue_new (32);
+ bus->queue_lock = g_mutex_new ();
+
+ bus->priv = G_TYPE_INSTANCE_GET_PRIVATE (bus, GST_TYPE_BUS, GstBusPrivate);
+ bus->priv->enable_async = DEFAULT_ENABLE_ASYNC;
+
+ GST_DEBUG_OBJECT (bus, "created");
+}
+
+static void
+gst_bus_dispose (GObject * object)
+{
+ GstBus *bus = GST_BUS (object);
+
+ if (bus->queue) {
+ GstMessage *message;
+
+ g_mutex_lock (bus->queue_lock);
+ do {
+ message = gst_atomic_queue_pop (bus->queue);
+ if (message)
+ gst_message_unref (message);
+ } while (message != NULL);
+ gst_atomic_queue_unref (bus->queue);
+ bus->queue = NULL;
+ g_mutex_unlock (bus->queue_lock);
+ g_mutex_free (bus->queue_lock);
+ bus->queue_lock = NULL;
+
+ if (bus->priv->poll)
+ gst_poll_free (bus->priv->poll);
+ bus->priv->poll = NULL;
+ }
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+/**
+ * gst_bus_new:
+ *
+ * Creates a new #GstBus instance.
+ *
+ * Returns: (transfer full): a new #GstBus instance
+ */
+GstBus *
+gst_bus_new (void)
+{
+ GstBus *result;
+
+ result = g_object_newv (gst_bus_get_type (), 0, NULL);
+ GST_DEBUG_OBJECT (result, "created new bus");
+
+ return result;
+}
+
+/**
+ * gst_bus_post:
+ * @bus: a #GstBus to post on
+ * @message: (transfer full): the #GstMessage to post
+ *
+ * Post a message on the given bus. Ownership of the message
+ * is taken by the bus.
+ *
+ * Returns: TRUE if the message could be posted, FALSE if the bus is flushing.
+ *
+ * MT safe.
+ */
+gboolean
+gst_bus_post (GstBus * bus, GstMessage * message)
+{
+ GstBusSyncReply reply = GST_BUS_PASS;
+ GstBusSyncHandler handler;
+ gboolean emit_sync_message;
+ gpointer handler_data;
+
+ g_return_val_if_fail (GST_IS_BUS (bus), FALSE);
+ g_return_val_if_fail (GST_IS_MESSAGE (message), FALSE);
+
+ GST_DEBUG_OBJECT (bus, "[msg %p] posting on bus %" GST_PTR_FORMAT, message,
+ message);
+
+ GST_OBJECT_LOCK (bus);
+ /* check if the bus is flushing */
+ if (GST_OBJECT_FLAG_IS_SET (bus, GST_BUS_FLUSHING))
+ goto is_flushing;
+
+ handler = bus->sync_handler;
+ handler_data = bus->sync_handler_data;
+ emit_sync_message = bus->priv->num_sync_message_emitters > 0;
+ GST_OBJECT_UNLOCK (bus);
+
+ /* first call the sync handler if it is installed */
+ if (handler)
+ reply = handler (bus, message, handler_data);
+
+ /* emit sync-message if requested to do so via
+ gst_bus_enable_sync_message_emission. terrible but effective */
+ if (emit_sync_message && reply != GST_BUS_DROP
+ && handler != gst_bus_sync_signal_handler)
+ gst_bus_sync_signal_handler (bus, message, NULL);
+
+ /* If this is a bus without async message delivery
+ * always drop the message */
+ if (!bus->priv->poll)
+ reply = GST_BUS_DROP;
+
+ /* now see what we should do with the message */
+ switch (reply) {
+ case GST_BUS_DROP:
+ /* drop the message */
+ GST_DEBUG_OBJECT (bus, "[msg %p] dropped", message);
+ break;
+ case GST_BUS_PASS:
+ /* pass the message to the async queue, refcount passed in the queue */
+ GST_DEBUG_OBJECT (bus, "[msg %p] pushing on async queue", message);
+ gst_atomic_queue_push (bus->queue, message);
+ gst_poll_write_control (bus->priv->poll);
+ GST_DEBUG_OBJECT (bus, "[msg %p] pushed on async queue", message);
+
+ break;
+ case GST_BUS_ASYNC:
+ {
+ /* async delivery, we need a mutex and a cond to block
+ * on */
+ GMutex *lock = g_mutex_new ();
+ GCond *cond = g_cond_new ();
+
+ GST_MESSAGE_COND (message) = cond;
+ GST_MESSAGE_GET_LOCK (message) = lock;
+
+ GST_DEBUG_OBJECT (bus, "[msg %p] waiting for async delivery", message);
+
+ /* now we lock the message mutex, send the message to the async
+ * queue. When the message is handled by the app and destroyed,
+ * the cond will be signalled and we can continue */
+ g_mutex_lock (lock);
+
+ gst_atomic_queue_push (bus->queue, message);
+ gst_poll_write_control (bus->priv->poll);
+
+ /* now block till the message is freed */
+ g_cond_wait (cond, lock);
+ g_mutex_unlock (lock);
+
+ GST_DEBUG_OBJECT (bus, "[msg %p] delivered asynchronously", message);
+
+ g_mutex_free (lock);
+ g_cond_free (cond);
+ break;
+ }
+ default:
+ g_warning ("invalid return from bus sync handler");
+ break;
+ }
+ return TRUE;
+
+ /* ERRORS */
+is_flushing:
+ {
+ GST_DEBUG_OBJECT (bus, "bus is flushing");
+ gst_message_unref (message);
+ GST_OBJECT_UNLOCK (bus);
+
+ return FALSE;
+ }
+}
+
+/**
+ * gst_bus_have_pending:
+ * @bus: a #GstBus to check
+ *
+ * Check if there are pending messages on the bus that
+ * should be handled.
+ *
+ * Returns: TRUE if there are messages on the bus to be handled, FALSE
+ * otherwise.
+ *
+ * MT safe.
+ */
+gboolean
+gst_bus_have_pending (GstBus * bus)
+{
+ gboolean result;
+
+ g_return_val_if_fail (GST_IS_BUS (bus), FALSE);
+
+ /* see if there is a message on the bus */
+ result = gst_atomic_queue_length (bus->queue) != 0;
+
+ return result;
+}
+
+/**
+ * gst_bus_set_flushing:
+ * @bus: a #GstBus
+ * @flushing: whether or not to flush the bus
+ *
+ * If @flushing, flush out and unref any messages queued in the bus. Releases
+ * references to the message origin objects. Will flush future messages until
+ * gst_bus_set_flushing() sets @flushing to #FALSE.
+ *
+ * MT safe.
+ */
+void
+gst_bus_set_flushing (GstBus * bus, gboolean flushing)
+{
+ GstMessage *message;
+
+ GST_OBJECT_LOCK (bus);
+
+ if (flushing) {
+ GST_OBJECT_FLAG_SET (bus, GST_BUS_FLUSHING);
+
+ GST_DEBUG_OBJECT (bus, "set bus flushing");
+
+ while ((message = gst_bus_pop (bus)))
+ gst_message_unref (message);
+ } else {
+ GST_DEBUG_OBJECT (bus, "unset bus flushing");
+ GST_OBJECT_FLAG_UNSET (bus, GST_BUS_FLUSHING);
+ }
+
+ GST_OBJECT_UNLOCK (bus);
+}
+
+/**
+ * gst_bus_timed_pop_filtered:
+ * @bus: a #GstBus to pop from
+ * @timeout: a timeout in nanoseconds, or GST_CLOCK_TIME_NONE to wait forever
+ * @types: message types to take into account, GST_MESSAGE_ANY for any type
+ *
+ * Get a message from the bus whose type matches the message type mask @types,
+ * waiting up to the specified timeout (and discarding any messages that do not
+ * match the mask provided).
+ *
+ * If @timeout is 0, this function behaves like gst_bus_pop_filtered(). If
+ * @timeout is #GST_CLOCK_TIME_NONE, this function will block forever until a
+ * matching message was posted on the bus.
+ *
+ * Returns: (transfer full): a #GstMessage matching the filter in @types,
+ * or NULL if no matching message was found on the bus until the timeout
+ * expired. The message is taken from the bus and needs to be unreffed
+ * with gst_message_unref() after usage.
+ *
+ * MT safe.
+ *
+ * Since: 0.10.15
+ */
+GstMessage *
+gst_bus_timed_pop_filtered (GstBus * bus, GstClockTime timeout,
+ GstMessageType types)
+{
+ GstMessage *message;
+ GTimeVal now, then;
+ gboolean first_round = TRUE;
+ GstClockTime elapsed = 0;
+
+ g_return_val_if_fail (GST_IS_BUS (bus), NULL);
+ g_return_val_if_fail (types != 0, NULL);
+ g_return_val_if_fail (timeout == 0 || bus->priv->poll != NULL, NULL);
+
+ g_mutex_lock (bus->queue_lock);
+
+ while (TRUE) {
+ gint ret;
+
+ GST_LOG_OBJECT (bus, "have %d messages",
+ gst_atomic_queue_length (bus->queue));
+
+ while ((message = gst_atomic_queue_pop (bus->queue))) {
+ if (bus->priv->poll)
+ gst_poll_read_control (bus->priv->poll);
+ GST_DEBUG_OBJECT (bus, "got message %p, %s, type mask is %u",
+ message, GST_MESSAGE_TYPE_NAME (message), (guint) types);
+ if ((GST_MESSAGE_TYPE (message) & types) != 0) {
+ /* exit the loop, we have a message */
+ goto beach;
+ } else {
+ GST_DEBUG_OBJECT (bus, "discarding message, does not match mask");
+ gst_message_unref (message);
+ message = NULL;
+ }
+ }
+
+ /* no need to wait, exit loop */
+ if (timeout == 0)
+ break;
+
+ else if (timeout != GST_CLOCK_TIME_NONE) {
+ if (first_round) {
+ g_get_current_time (&then);
+ first_round = FALSE;
+ } else {
+ g_get_current_time (&now);
+
+ elapsed = GST_TIMEVAL_TO_TIME (now) - GST_TIMEVAL_TO_TIME (then);
+
+ if (elapsed > timeout)
+ break;
+ }
+ }
+
+ /* only here in timeout case */
+ g_assert (bus->priv->poll);
+ g_mutex_unlock (bus->queue_lock);
+ ret = gst_poll_wait (bus->priv->poll, timeout - elapsed);
+ g_mutex_lock (bus->queue_lock);
+
+ if (ret == 0) {
+ GST_INFO_OBJECT (bus, "timed out, breaking loop");
+ break;
+ } else {
+ GST_INFO_OBJECT (bus, "we got woken up, recheck for message");
+ }
+ }
+
+beach:
+
+ g_mutex_unlock (bus->queue_lock);
+
+ return message;
+}
+
+
+/**
+ * gst_bus_timed_pop:
+ * @bus: a #GstBus to pop
+ * @timeout: a timeout
+ *
+ * Get a message from the bus, waiting up to the specified timeout.
+ *
+ * If @timeout is 0, this function behaves like gst_bus_pop(). If @timeout is
+ * #GST_CLOCK_TIME_NONE, this function will block forever until a message was
+ * posted on the bus.
+ *
+ * Returns: (transfer full): the #GstMessage that is on the bus after the
+ * specified timeout or NULL if the bus is empty after the timeout expired.
+ * The message is taken from the bus and needs to be unreffed with
+ * gst_message_unref() after usage.
+ *
+ * MT safe.
+ *
+ * Since: 0.10.12
+ */
+GstMessage *
+gst_bus_timed_pop (GstBus * bus, GstClockTime timeout)
+{
+ g_return_val_if_fail (GST_IS_BUS (bus), NULL);
+
+ return gst_bus_timed_pop_filtered (bus, timeout, GST_MESSAGE_ANY);
+}
+
+/**
+ * gst_bus_pop_filtered:
+ * @bus: a #GstBus to pop
+ * @types: message types to take into account
+ *
+ * Get a message matching @type from the bus. Will discard all messages on
+ * the bus that do not match @type and that have been posted before the first
+ * message that does match @type. If there is no message matching @type on
+ * the bus, all messages will be discarded.
+ *
+ * Returns: (transfer full): the next #GstMessage matching @type that is on
+ * the bus, or NULL if the bus is empty or there is no message matching
+ * @type. The message is taken from the bus and needs to be unreffed with
+ * gst_message_unref() after usage.
+ *
+ * MT safe.
+ *
+ * Since: 0.10.15
+ */
+GstMessage *
+gst_bus_pop_filtered (GstBus * bus, GstMessageType types)
+{
+ g_return_val_if_fail (GST_IS_BUS (bus), NULL);
+ g_return_val_if_fail (types != 0, NULL);
+
+ return gst_bus_timed_pop_filtered (bus, 0, types);
+}
+
+/**
+ * gst_bus_pop:
+ * @bus: a #GstBus to pop
+ *
+ * Get a message from the bus.
+ *
+ * Returns: (transfer full): the #GstMessage that is on the bus, or NULL if the
+ * bus is empty. The message is taken from the bus and needs to be unreffed
+ * with gst_message_unref() after usage.
+ *
+ * MT safe.
+ */
+GstMessage *
+gst_bus_pop (GstBus * bus)
+{
+ g_return_val_if_fail (GST_IS_BUS (bus), NULL);
+
+ return gst_bus_timed_pop_filtered (bus, 0, GST_MESSAGE_ANY);
+}
+
+/**
+ * gst_bus_peek:
+ * @bus: a #GstBus
+ *
+ * Peek the message on the top of the bus' queue. The message will remain
+ * on the bus' message queue. A reference is returned, and needs to be unreffed
+ * by the caller.
+ *
+ * Returns: (transfer full): the #GstMessage that is on the bus, or NULL if the
+ * bus is empty.
+ *
+ * MT safe.
+ */
+GstMessage *
+gst_bus_peek (GstBus * bus)
+{
+ GstMessage *message;
+
+ g_return_val_if_fail (GST_IS_BUS (bus), NULL);
+
+ g_mutex_lock (bus->queue_lock);
+ message = gst_atomic_queue_peek (bus->queue);
+ if (message)
+ gst_message_ref (message);
+ g_mutex_unlock (bus->queue_lock);
+
+ GST_DEBUG_OBJECT (bus, "peek on bus, got message %p", message);
+
+ return message;
+}
+
+/**
+ * gst_bus_set_sync_handler:
+ * @bus: a #GstBus to install the handler on
+ * @func: The handler function to install
+ * @data: User data that will be sent to the handler function.
+ *
+ * Sets the synchronous handler on the bus. The function will be called
+ * every time a new message is posted on the bus. Note that the function
+ * will be called in the same thread context as the posting object. This
+ * function is usually only called by the creator of the bus. Applications
+ * should handle messages asynchronously using the gst_bus watch and poll
+ * functions.
+ *
+ * You cannot replace an existing sync_handler. You can pass NULL to this
+ * function, which will clear the existing handler.
+ */
+void
+gst_bus_set_sync_handler (GstBus * bus, GstBusSyncHandler func, gpointer data)
+{
+ g_return_if_fail (GST_IS_BUS (bus));
+
+ GST_OBJECT_LOCK (bus);
+
+ /* Assert if the user attempts to replace an existing sync_handler,
+ * other than to clear it */
+ if (func != NULL && bus->sync_handler != NULL)
+ goto no_replace;
+
+ bus->sync_handler = func;
+ bus->sync_handler_data = data;
+ GST_OBJECT_UNLOCK (bus);
+
+ return;
+
+no_replace:
+ {
+ GST_OBJECT_UNLOCK (bus);
+ g_warning ("cannot replace existing sync handler");
+ return;
+ }
+}
+
+/* GSource for the bus
+ */
+typedef struct
+{
+ GSource source;
+ GstBus *bus;
+} GstBusSource;
+
+static gboolean
+gst_bus_source_prepare (GSource * source, gint * timeout)
+{
+ *timeout = -1;
+ return FALSE;
+}
+
+static gboolean
+gst_bus_source_check (GSource * source)
+{
+ GstBusSource *bsrc = (GstBusSource *) source;
+
+ return bsrc->bus->priv->pollfd.revents & (G_IO_IN | G_IO_HUP | G_IO_ERR);
+}
+
+static gboolean
+gst_bus_source_dispatch (GSource * source, GSourceFunc callback,
+ gpointer user_data)
+{
+ GstBusFunc handler = (GstBusFunc) callback;
+ GstBusSource *bsource = (GstBusSource *) source;
+ GstMessage *message;
+ gboolean keep;
+ GstBus *bus;
+
+ g_return_val_if_fail (bsource != NULL, FALSE);
+
+ bus = bsource->bus;
+
+ g_return_val_if_fail (GST_IS_BUS (bus), FALSE);
+
+ message = gst_bus_pop (bus);
+
+ /* The message queue might be empty if some other thread or callback set
+ * the bus to flushing between check/prepare and dispatch */
+ if (G_UNLIKELY (message == NULL))
+ return TRUE;
+
+ if (!handler)
+ goto no_handler;
+
+ GST_DEBUG_OBJECT (bus, "source %p calling dispatch with %p", source, message);
+
+ keep = handler (bus, message, user_data);
+ gst_message_unref (message);
+
+ GST_DEBUG_OBJECT (bus, "source %p handler returns %d", source, keep);
+
+ return keep;
+
+no_handler:
+ {
+ g_warning ("GstBus watch dispatched without callback\n"
+ "You must call g_source_set_callback().");
+ gst_message_unref (message);
+ return FALSE;
+ }
+}
+
+static void
+gst_bus_source_finalize (GSource * source)
+{
+ GstBusSource *bsource = (GstBusSource *) source;
+ GstBus *bus;
+
+ bus = bsource->bus;
+
+ GST_DEBUG_OBJECT (bus, "finalize source %p", source);
+
+ GST_OBJECT_LOCK (bus);
+ if (bus->priv->watch_id == source)
+ bus->priv->watch_id = NULL;
+ GST_OBJECT_UNLOCK (bus);
+
+ gst_object_unref (bsource->bus);
+ bsource->bus = NULL;
+}
+
+static GSourceFuncs gst_bus_source_funcs = {
+ gst_bus_source_prepare,
+ gst_bus_source_check,
+ gst_bus_source_dispatch,
+ gst_bus_source_finalize
+};
+
+/**
+ * gst_bus_create_watch:
+ * @bus: a #GstBus to create the watch for
+ *
+ * Create watch for this bus. The GSource will be dispatched whenever
+ * a message is on the bus. After the GSource is dispatched, the
+ * message is popped off the bus and unreffed.
+ *
+ * Returns: (transfer full): a #GSource that can be added to a mainloop.
+ */
+GSource *
+gst_bus_create_watch (GstBus * bus)
+{
+ GstBusSource *source;
+
+ g_return_val_if_fail (GST_IS_BUS (bus), NULL);
+ g_return_val_if_fail (bus->priv->poll != NULL, NULL);
+
+ source = (GstBusSource *) g_source_new (&gst_bus_source_funcs,
+ sizeof (GstBusSource));
+ source->bus = gst_object_ref (bus);
+ g_source_add_poll ((GSource *) source, &bus->priv->pollfd);
+
+ return (GSource *) source;
+}
+
+/* must be called with the bus OBJECT LOCK */
+static guint
+gst_bus_add_watch_full_unlocked (GstBus * bus, gint priority,
+ GstBusFunc func, gpointer user_data, GDestroyNotify notify)
+{
+ GMainContext *ctx;
+ guint id;
+ GSource *source;
+
+ if (bus->priv->watch_id) {
+ GST_ERROR_OBJECT (bus,
+ "Tried to add new watch while one was already there");
+ return 0;
+ }
+
+ source = gst_bus_create_watch (bus);
+
+ if (priority != G_PRIORITY_DEFAULT)
+ g_source_set_priority (source, priority);
+
+ g_source_set_callback (source, (GSourceFunc) func, user_data, notify);
+
+ ctx = g_main_context_get_thread_default ();
+ id = g_source_attach (source, ctx);
+ g_source_unref (source);
+
+ if (id) {
+ bus->priv->watch_id = source;
+ }
+
+ GST_DEBUG_OBJECT (bus, "New source %p with id %u", source, id);
+ return id;
+}
+
+/**
+ * gst_bus_add_watch_full:
+ * @bus: a #GstBus to create the watch for.
+ * @priority: The priority of the watch.
+ * @func: A function to call when a message is received.
+ * @user_data: user data passed to @func.
+ * @notify: the function to call when the source is removed.
+ *
+ * Adds a bus watch to the default main context with the given @priority (e.g.
+ * %G_PRIORITY_DEFAULT). Since 0.10.33 it is also possible to use a non-default
+ * main context set up using g_main_context_push_thread_default() (before
+ * one had to create a bus watch source and attach it to the desired main
+ * context 'manually').
+ *
+ * This function is used to receive asynchronous messages in the main loop.
+ * There can only be a single bus watch per bus, you must remove it before you
+ * can set a new one.
+ *
+ * When @func is called, the message belongs to the caller; if you want to
+ * keep a copy of it, call gst_message_ref() before leaving @func.
+ *
+ * The watch can be removed using g_source_remove() or by returning FALSE
+ * from @func.
+ *
+ * Returns: The event source id.
+ *
+ * MT safe.
+ */
+guint
+gst_bus_add_watch_full (GstBus * bus, gint priority,
+ GstBusFunc func, gpointer user_data, GDestroyNotify notify)
+{
+ guint id;
+
+ g_return_val_if_fail (GST_IS_BUS (bus), 0);
+
+ GST_OBJECT_LOCK (bus);
+ id = gst_bus_add_watch_full_unlocked (bus, priority, func, user_data, notify);
+ GST_OBJECT_UNLOCK (bus);
+
+ return id;
+}
+
+/**
+ * gst_bus_add_watch:
+ * @bus: a #GstBus to create the watch for
+ * @func: A function to call when a message is received.
+ * @user_data: user data passed to @func.
+ *
+ * Adds a bus watch to the default main context with the default priority
+ * (%G_PRIORITY_DEFAULT). Since 0.10.33 it is also possible to use a non-default
+ * main context set up using g_main_context_push_thread_default() (before
+ * one had to create a bus watch source and attach it to the desired main
+ * context 'manually').
+ *
+ * This function is used to receive asynchronous messages in the main loop.
+ * There can only be a single bus watch per bus, you must remove it before you
+ * can set a new one.
+ *
+ * The watch can be removed using g_source_remove() or by returning FALSE
+ * from @func.
+ *
+ * Returns: The event source id.
+ *
+ * MT safe.
+ */
+guint
+gst_bus_add_watch (GstBus * bus, GstBusFunc func, gpointer user_data)
+{
+ return gst_bus_add_watch_full (bus, G_PRIORITY_DEFAULT, func,
+ user_data, NULL);
+}
+
+typedef struct
+{
+ GMainLoop *loop;
+ guint timeout_id;
+ gboolean source_running;
+ GstMessageType events;
+ GstMessage *message;
+} GstBusPollData;
+
+static void
+poll_func (GstBus * bus, GstMessage * message, GstBusPollData * poll_data)
+{
+ GstMessageType type;
+
+ if (!g_main_loop_is_running (poll_data->loop)) {
+ GST_DEBUG ("mainloop %p not running", poll_data->loop);
+ return;
+ }
+
+ type = GST_MESSAGE_TYPE (message);
+
+ if (type & poll_data->events) {
+ g_assert (poll_data->message == NULL);
+ /* keep ref to message */
+ poll_data->message = gst_message_ref (message);
+ GST_DEBUG ("mainloop %p quit", poll_data->loop);
+ g_main_loop_quit (poll_data->loop);
+ } else {
+ GST_DEBUG ("type %08x does not match %08x", type, poll_data->events);
+ }
+}
+
+static gboolean
+poll_timeout (GstBusPollData * poll_data)
+{
+ GST_DEBUG ("mainloop %p quit", poll_data->loop);
+ g_main_loop_quit (poll_data->loop);
+
+ /* we don't remove the GSource as this would free our poll_data,
+ * which we still need */
+ return TRUE;
+}
+
+static void
+poll_destroy (GstBusPollData * poll_data, gpointer unused)
+{
+ poll_data->source_running = FALSE;
+ if (!poll_data->timeout_id) {
+ g_main_loop_unref (poll_data->loop);
+ g_slice_free (GstBusPollData, poll_data);
+ }
+}
+
+static void
+poll_destroy_timeout (GstBusPollData * poll_data)
+{
+ poll_data->timeout_id = 0;
+ if (!poll_data->source_running) {
+ g_main_loop_unref (poll_data->loop);
+ g_slice_free (GstBusPollData, poll_data);
+ }
+}
+
+/**
+ * gst_bus_poll:
+ * @bus: a #GstBus
+ * @events: a mask of #GstMessageType, representing the set of message types to
+ * poll for.
+ * @timeout: the poll timeout, as a #GstClockTimeDiff, or -1 to poll
+ * indefinitely.
+ *
+ * Poll the bus for messages. Will block while waiting for messages to come.
+ * You can specify a maximum time to poll with the @timeout parameter. If
+ * @timeout is negative, this function will block indefinitely.
+ *
+ * All messages not in @events will be popped off the bus and will be ignored.
+ *
+ * Because poll is implemented using the "message" signal enabled by
+ * gst_bus_add_signal_watch(), calling gst_bus_poll() will cause the "message"
+ * signal to be emitted for every message that poll sees. Thus a "message"
+ * signal handler will see the same messages that this function sees -- neither
+ * will steal messages from the other.
+ *
+ * This function will run a main loop from the default main context when
+ * polling.
+ *
+ * You should never use this function, since it is pure evil. This is
+ * especially true for GUI applications based on Gtk+ or Qt, but also for any
+ * other non-trivial application that uses the GLib main loop. As this function
+ * runs a GLib main loop, any callback attached to the default GLib main
+ * context may be invoked. This could be timeouts, GUI events, I/O events etc.;
+ * even if gst_bus_poll() is called with a 0 timeout. Any of these callbacks
+ * may do things you do not expect, e.g. destroy the main application window or
+ * some other resource; change other application state; display a dialog and
+ * run another main loop until the user clicks it away. In short, using this
+ * function may add a lot of complexity to your code through unexpected
+ * re-entrancy and unexpected changes to your application's state.
+ *
+ * For 0 timeouts use gst_bus_pop_filtered() instead of this function; for
+ * other short timeouts use gst_bus_timed_pop_filtered(); everything else is
+ * better handled by setting up an asynchronous bus watch and doing things
+ * from there.
+ *
+ * Returns: (transfer full): the message that was received, or NULL if the
+ * poll timed out. The message is taken from the bus and needs to be
+ * unreffed with gst_message_unref() after usage.
+ */
+GstMessage *
+gst_bus_poll (GstBus * bus, GstMessageType events, GstClockTimeDiff timeout)
+{
+ GstBusPollData *poll_data;
+ GstMessage *ret;
+ gulong id;
+
+ poll_data = g_slice_new (GstBusPollData);
+ poll_data->source_running = TRUE;
+ poll_data->loop = g_main_loop_new (NULL, FALSE);
+ poll_data->events = events;
+ poll_data->message = NULL;
+
+ if (timeout >= 0)
+ poll_data->timeout_id = g_timeout_add_full (G_PRIORITY_DEFAULT_IDLE,
+ timeout / GST_MSECOND, (GSourceFunc) poll_timeout, poll_data,
+ (GDestroyNotify) poll_destroy_timeout);
+ else
+ poll_data->timeout_id = 0;
+
+ id = g_signal_connect_data (bus, "message", G_CALLBACK (poll_func), poll_data,
+ (GClosureNotify) poll_destroy, 0);
+
+ /* these can be nested, so it's ok */
+ gst_bus_add_signal_watch (bus);
+
+ GST_DEBUG ("running mainloop %p", poll_data->loop);
+ g_main_loop_run (poll_data->loop);
+ GST_DEBUG ("mainloop stopped %p", poll_data->loop);
+
+ gst_bus_remove_signal_watch (bus);
+
+ /* holds a ref */
+ ret = poll_data->message;
+
+ if (poll_data->timeout_id)
+ g_source_remove (poll_data->timeout_id);
+
+ /* poll_data will be freed now */
+ g_signal_handler_disconnect (bus, id);
+
+ GST_DEBUG_OBJECT (bus, "finished poll with message %p", ret);
+
+ return ret;
+}
+
+/**
+ * gst_bus_async_signal_func:
+ * @bus: a #GstBus
+ * @message: the #GstMessage received
+ * @data: user data
+ *
+ * A helper #GstBusFunc that can be used to convert all asynchronous messages
+ * into signals.
+ *
+ * Returns: TRUE
+ */
+gboolean
+gst_bus_async_signal_func (GstBus * bus, GstMessage * message, gpointer data)
+{
+ GQuark detail = 0;
+
+ g_return_val_if_fail (GST_IS_BUS (bus), TRUE);
+ g_return_val_if_fail (message != NULL, TRUE);
+
+ detail = gst_message_type_to_quark (GST_MESSAGE_TYPE (message));
+
+ g_signal_emit (bus, gst_bus_signals[ASYNC_MESSAGE], detail, message);
+
+ /* we never remove this source based on signal emission return values */
+ return TRUE;
+}
+
+/**
+ * gst_bus_sync_signal_handler:
+ * @bus: a #GstBus
+ * @message: the #GstMessage received
+ * @data: user data
+ *
+ * A helper GstBusSyncHandler that can be used to convert all synchronous
+ * messages into signals.
+ *
+ * Returns: GST_BUS_PASS
+ */
+GstBusSyncReply
+gst_bus_sync_signal_handler (GstBus * bus, GstMessage * message, gpointer data)
+{
+ GQuark detail = 0;
+
+ g_return_val_if_fail (GST_IS_BUS (bus), GST_BUS_DROP);
+ g_return_val_if_fail (message != NULL, GST_BUS_DROP);
+
+ detail = gst_message_type_to_quark (GST_MESSAGE_TYPE (message));
+
+ g_signal_emit (bus, gst_bus_signals[SYNC_MESSAGE], detail, message);
+
+ return GST_BUS_PASS;
+}
+
+/**
+ * gst_bus_enable_sync_message_emission:
+ * @bus: a #GstBus on which you want to receive the "sync-message" signal
+ *
+ * Instructs GStreamer to emit the "sync-message" signal after running the bus's
+ * sync handler. This function is here so that code can ensure that they can
+ * synchronously receive messages without having to affect what the bin's sync
+ * handler is.
+ *
+ * This function may be called multiple times. To clean up, the caller is
+ * responsible for calling gst_bus_disable_sync_message_emission() as many times
+ * as this function is called.
+ *
+ * While this function looks similar to gst_bus_add_signal_watch(), it is not
+ * exactly the same -- this function enables <emphasis>synchronous</emphasis> emission of
+ * signals when messages arrive; gst_bus_add_signal_watch() adds an idle callback
+ * to pop messages off the bus <emphasis>asynchronously</emphasis>. The sync-message signal
+ * comes from the thread of whatever object posted the message; the "message"
+ * signal is marshalled to the main thread via the main loop.
+ *
+ * MT safe.
+ */
+void
+gst_bus_enable_sync_message_emission (GstBus * bus)
+{
+ g_return_if_fail (GST_IS_BUS (bus));
+
+ GST_OBJECT_LOCK (bus);
+ bus->priv->num_sync_message_emitters++;
+ GST_OBJECT_UNLOCK (bus);
+}
+
+/**
+ * gst_bus_disable_sync_message_emission:
+ * @bus: a #GstBus on which you previously called
+ * gst_bus_enable_sync_message_emission()
+ *
+ * Instructs GStreamer to stop emitting the "sync-message" signal for this bus.
+ * See gst_bus_enable_sync_message_emission() for more information.
+ *
+ * In the event that multiple pieces of code have called
+ * gst_bus_enable_sync_message_emission(), the sync-message emissions will only
+ * be stopped after all calls to gst_bus_enable_sync_message_emission() were
+ * "cancelled" by calling this function. In this way the semantics are exactly
+ * the same as gst_object_ref() that which calls enable should also call
+ * disable.
+ *
+ * MT safe.
+ */
+void
+gst_bus_disable_sync_message_emission (GstBus * bus)
+{
+ g_return_if_fail (GST_IS_BUS (bus));
+ g_return_if_fail (bus->num_signal_watchers == 0);
+
+ GST_OBJECT_LOCK (bus);
+ bus->priv->num_sync_message_emitters--;
+ GST_OBJECT_UNLOCK (bus);
+}
+
+/**
+ * gst_bus_add_signal_watch_full:
+ * @bus: a #GstBus on which you want to receive the "message" signal
+ * @priority: The priority of the watch.
+ *
+ * Adds a bus signal watch to the default main context with the given @priority
+ * (e.g. %G_PRIORITY_DEFAULT). Since 0.10.33 it is also possible to use a
+ * non-default main context set up using g_main_context_push_thread_default()
+ * (before one had to create a bus watch source and attach it to the desired
+ * main context 'manually').
+ *
+ * After calling this statement, the bus will emit the "message" signal for each
+ * message posted on the bus when the main loop is running.
+ *
+ * This function may be called multiple times. To clean up, the caller is
+ * responsible for calling gst_bus_remove_signal_watch() as many times as this
+ * function is called.
+ *
+ * There can only be a single bus watch per bus, you most remove all signal watch
+ * before you can set another type of watch.
+ *
+ * MT safe.
+ */
+void
+gst_bus_add_signal_watch_full (GstBus * bus, gint priority)
+{
+ g_return_if_fail (GST_IS_BUS (bus));
+
+ /* I know the callees don't take this lock, so go ahead and abuse it */
+ GST_OBJECT_LOCK (bus);
+
+ if (bus->num_signal_watchers > 0)
+ goto done;
+
+ /* this should not fail because the counter above takes care of it */
+ g_assert (bus->signal_watch_id == 0);
+
+ bus->signal_watch_id =
+ gst_bus_add_watch_full_unlocked (bus, priority, gst_bus_async_signal_func,
+ NULL, NULL);
+
+ if (G_UNLIKELY (bus->signal_watch_id == 0))
+ goto add_failed;
+
+done:
+
+ bus->num_signal_watchers++;
+
+ GST_OBJECT_UNLOCK (bus);
+ return;
+
+ /* ERRORS */
+add_failed:
+ {
+ g_critical ("Could not add signal watch to bus %s", GST_OBJECT_NAME (bus));
+ GST_OBJECT_UNLOCK (bus);
+ return;
+ }
+}
+
+/**
+ * gst_bus_add_signal_watch:
+ * @bus: a #GstBus on which you want to receive the "message" signal
+ *
+ * Adds a bus signal watch to the default main context with the default priority
+ * (%G_PRIORITY_DEFAULT). Since 0.10.33 it is also possible to use a non-default
+ * main context set up using g_main_context_push_thread_default() (before
+ * one had to create a bus watch source and attach it to the desired main
+ * context 'manually').
+ *
+ * After calling this statement, the bus will emit the "message" signal for each
+ * message posted on the bus.
+ *
+ * This function may be called multiple times. To clean up, the caller is
+ * responsible for calling gst_bus_remove_signal_watch() as many times as this
+ * function is called.
+ *
+ * MT safe.
+ */
+void
+gst_bus_add_signal_watch (GstBus * bus)
+{
+ gst_bus_add_signal_watch_full (bus, G_PRIORITY_DEFAULT);
+}
+
+/**
+ * gst_bus_remove_signal_watch:
+ * @bus: a #GstBus you previously added a signal watch to
+ *
+ * Removes a signal watch previously added with gst_bus_add_signal_watch().
+ *
+ * MT safe.
+ */
+void
+gst_bus_remove_signal_watch (GstBus * bus)
+{
+ guint id = 0;
+
+ g_return_if_fail (GST_IS_BUS (bus));
+
+ /* I know the callees don't take this lock, so go ahead and abuse it */
+ GST_OBJECT_LOCK (bus);
+
+ if (bus->num_signal_watchers == 0)
+ goto error;
+
+ bus->num_signal_watchers--;
+
+ if (bus->num_signal_watchers > 0)
+ goto done;
+
+ id = bus->signal_watch_id;
+ bus->signal_watch_id = 0;
+
+ GST_DEBUG_OBJECT (bus, "removing signal watch %u", id);
+
+done:
+ GST_OBJECT_UNLOCK (bus);
+
+ if (id)
+ g_source_remove (id);
+
+ return;
+
+ /* ERRORS */
+error:
+ {
+ g_critical ("Bus %s has no signal watches attached", GST_OBJECT_NAME (bus));
+ GST_OBJECT_UNLOCK (bus);
+ return;
+ }
+}
diff --git a/gst/gstbus.h b/gst/gstbus.h
new file mode 100644
index 0000000..732591f
--- /dev/null
+++ b/gst/gstbus.h
@@ -0,0 +1,193 @@
+/* GStreamer
+ * Copyright (C) 2004 Wim Taymans <wim@fluendo.com>
+ *
+ * gstbus.h: Header for GstBus subsystem
+ *
+ * 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_BUS_H__
+#define __GST_BUS_H__
+
+typedef struct _GstBus GstBus;
+typedef struct _GstBusPrivate GstBusPrivate;
+typedef struct _GstBusClass GstBusClass;
+
+#include <gst/gstmessage.h>
+#include <gst/gstclock.h>
+#include <gst/gstatomicqueue.h>
+
+G_BEGIN_DECLS
+
+/* --- standard type macros --- */
+#define GST_TYPE_BUS (gst_bus_get_type ())
+#define GST_BUS(bus) (G_TYPE_CHECK_INSTANCE_CAST ((bus), GST_TYPE_BUS, GstBus))
+#define GST_IS_BUS(bus) (G_TYPE_CHECK_INSTANCE_TYPE ((bus), GST_TYPE_BUS))
+#define GST_BUS_CLASS(bclass) (G_TYPE_CHECK_CLASS_CAST ((bclass), GST_TYPE_BUS, GstBusClass))
+#define GST_IS_BUS_CLASS(bclass) (G_TYPE_CHECK_CLASS_TYPE ((bclass), GST_TYPE_BUS))
+#define GST_BUS_GET_CLASS(bus) (G_TYPE_INSTANCE_GET_CLASS ((bus), GST_TYPE_BUS, GstBusClass))
+#define GST_BUS_CAST(bus) ((GstBus*)(bus))
+
+/**
+ * GstBusFlags:
+ * @GST_BUS_FLUSHING: The bus is currently dropping all messages
+ * @GST_BUS_FLAG_LAST: offset to define more flags
+ *
+ * The standard flags that a bus may have.
+ */
+typedef enum {
+ GST_BUS_FLUSHING = (GST_OBJECT_FLAG_LAST << 0),
+ /* padding */
+ GST_BUS_FLAG_LAST = (GST_OBJECT_FLAG_LAST << 1)
+} GstBusFlags;
+
+/**
+ * GstBusSyncReply:
+ * @GST_BUS_DROP: drop the message
+ * @GST_BUS_PASS: pass the message to the async queue
+ * @GST_BUS_ASYNC: pass message to async queue, continue if message is handled
+ *
+ * The result values for a GstBusSyncHandler.
+ */
+typedef enum
+{
+ GST_BUS_DROP = 0,
+ GST_BUS_PASS = 1,
+ GST_BUS_ASYNC = 2
+} GstBusSyncReply;
+
+/**
+ * GstBusSyncHandler:
+ * @bus: the #GstBus that sent the message
+ * @message: the #GstMessage
+ * @data: user data that has been given, when registering the handler
+ *
+ * Handler will be invoked synchronously, when a new message has been injected
+ * into the bus. This function is mostly used internally. Only one sync handler
+ * can be attached to a given bus.
+ *
+ * If the handler returns GST_BUS_DROP, it should unref the message, else the
+ * message should not be unreffed by the sync handler.
+ *
+ * Returns: #GstBusSyncReply stating what to do with the message
+ */
+typedef GstBusSyncReply (*GstBusSyncHandler) (GstBus * bus, GstMessage * message, gpointer data);
+
+/**
+ * GstBusFunc:
+ * @bus: the #GstBus that sent the message
+ * @message: the #GstMessage
+ * @data: user data that has been given, when registering the handler
+ *
+ * Specifies the type of function passed to gst_bus_add_watch() or
+ * gst_bus_add_watch_full(), which is called from the mainloop when a message
+ * is available on the bus.
+ *
+ * The message passed to the function will be unreffed after execution of this
+ * function so it should not be freed in the function.
+ *
+ * Note that this function is used as a GSourceFunc which means that returning
+ * FALSE will remove the GSource from the mainloop.
+ *
+ * Returns: %FALSE if the event source should be removed.
+ */
+typedef gboolean (*GstBusFunc) (GstBus * bus, GstMessage * message, gpointer data);
+
+/**
+ * GstBus:
+ *
+ * The opaque #GstBus data structure.
+ */
+struct _GstBus
+{
+ GstObject object;
+
+ /*< private >*/
+ GstAtomicQueue *queue;
+ GMutex *queue_lock;
+
+ GstBusSyncHandler sync_handler;
+ gpointer sync_handler_data;
+
+ guint signal_watch_id;
+ guint num_signal_watchers;
+
+ /*< private >*/
+ GstBusPrivate *priv;
+ gpointer _gst_reserved[GST_PADDING - 1];
+};
+
+struct _GstBusClass
+{
+ GstObjectClass parent_class;
+
+ /* signals */
+ void (*message) (GstBus *bus, GstMessage *message);
+ void (*sync_message) (GstBus *bus, GstMessage *message);
+
+ /*< private >*/
+ gpointer _gst_reserved[GST_PADDING];
+};
+
+GType gst_bus_get_type (void);
+
+GstBus* gst_bus_new (void);
+
+gboolean gst_bus_post (GstBus * bus, GstMessage * message);
+
+gboolean gst_bus_have_pending (GstBus * bus);
+GstMessage * gst_bus_peek (GstBus * bus);
+GstMessage * gst_bus_pop (GstBus * bus);
+GstMessage * gst_bus_pop_filtered (GstBus * bus, GstMessageType types);
+GstMessage * gst_bus_timed_pop (GstBus * bus, GstClockTime timeout);
+GstMessage * gst_bus_timed_pop_filtered (GstBus * bus, GstClockTime timeout, GstMessageType types);
+void gst_bus_set_flushing (GstBus * bus, gboolean flushing);
+
+/* synchronous dispatching */
+void gst_bus_set_sync_handler (GstBus * bus, GstBusSyncHandler func,
+ gpointer data);
+/* GSource based dispatching */
+GSource * gst_bus_create_watch (GstBus * bus);
+guint gst_bus_add_watch_full (GstBus * bus,
+ gint priority,
+ GstBusFunc func,
+ gpointer user_data,
+ GDestroyNotify notify);
+guint gst_bus_add_watch (GstBus * bus,
+ GstBusFunc func,
+ gpointer user_data);
+
+/* polling the bus */
+GstMessage* gst_bus_poll (GstBus *bus, GstMessageType events,
+ GstClockTimeDiff timeout);
+
+/* signal based dispatching helper functions. */
+gboolean gst_bus_async_signal_func (GstBus *bus, GstMessage *message,
+ gpointer data);
+GstBusSyncReply gst_bus_sync_signal_handler (GstBus *bus, GstMessage *message,
+ gpointer data);
+
+/* convenience api to add/remove a gsource that emits the async signals */
+void gst_bus_add_signal_watch (GstBus * bus);
+void gst_bus_add_signal_watch_full (GstBus * bus, gint priority);
+void gst_bus_remove_signal_watch (GstBus * bus);
+
+void gst_bus_enable_sync_message_emission (GstBus * bus);
+void gst_bus_disable_sync_message_emission (GstBus * bus);
+
+G_END_DECLS
+
+#endif /* __GST_BUS_H__ */
diff --git a/gst/gstcaps.c b/gst/gstcaps.c
new file mode 100644
index 0000000..7d6acfe
--- /dev/null
+++ b/gst/gstcaps.c
@@ -0,0 +1,1943 @@
+/* GStreamer
+ * Copyright (C) <2003> David A. Schleef <ds@schleef.org>
+ *
+ * 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:gstcaps
+ * @short_description: Structure describing sets of media formats
+ * @see_also: #GstStructure
+ *
+ * Caps (capabilities) are lighweight refcounted objects describing media types.
+ * They are composed of an array of #GstStructure.
+ *
+ * Caps are exposed on #GstPadTemplate to describe all possible types a
+ * given pad can handle. They are also stored in the #GstRegistry along with
+ * a description of the #GstElement.
+ *
+ * Caps are exposed on the element pads using the gst_pad_get_caps() pad
+ * function. This function describes the possible types that the pad can
+ * handle or produce at runtime.
+ *
+ * Caps are also attached to buffers to describe to content of the data
+ * pointed to by the buffer with gst_buffer_set_caps(). Caps attached to
+ * a #GstBuffer allow for format negotiation upstream and downstream.
+ *
+ * A #GstCaps can be constructed with the following code fragment:
+ *
+ * <example>
+ * <title>Creating caps</title>
+ * <programlisting>
+ * GstCaps *caps;
+ * caps = gst_caps_new_simple ("video/x-raw",
+ * "format", G_TYPE_STRING, "I420"),
+ * "framerate", GST_TYPE_FRACTION, 25, 1,
+ * "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1,
+ * "width", G_TYPE_INT, 320,
+ * "height", G_TYPE_INT, 240,
+ * NULL);
+ * </programlisting>
+ * </example>
+ *
+ * A #GstCaps is fixed when it has no properties with ranges or lists. Use
+ * gst_caps_is_fixed() to test for fixed caps. Only fixed caps can be
+ * set on a #GstPad or #GstBuffer.
+ *
+ * Various methods exist to work with the media types such as subtracting
+ * or intersecting.
+ *
+ * Last reviewed on 2007-02-13 (0.10.10)
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <string.h>
+#include <signal.h>
+
+#include "gst_private.h"
+#include <gst/gst.h>
+#include <gobject/gvaluecollector.h>
+
+#define DEBUG_REFCOUNT
+
+
+#define GST_CAPS_ARRAY(c) ((GPtrArray *)((c)->priv))
+
+#define GST_CAPS_LEN(c) (GST_CAPS_ARRAY(c)->len)
+
+#define IS_WRITABLE(caps) \
+ (GST_CAPS_REFCOUNT_VALUE (caps) == 1)
+
+/* same as gst_caps_is_any () */
+#define CAPS_IS_ANY(caps) \
+ (GST_CAPS_FLAGS(caps) & GST_CAPS_FLAGS_ANY)
+
+/* same as gst_caps_is_empty () */
+#define CAPS_IS_EMPTY(caps) \
+ (!CAPS_IS_ANY(caps) && CAPS_IS_EMPTY_SIMPLE(caps))
+
+#define CAPS_IS_EMPTY_SIMPLE(caps) \
+ ((GST_CAPS_ARRAY (caps) == NULL) || (GST_CAPS_LEN (caps) == 0))
+
+/* quick way to get a caps structure at an index without doing a type or array
+ * length check */
+#define gst_caps_get_structure_unchecked(caps, index) \
+ ((GstStructure *)g_ptr_array_index (GST_CAPS_ARRAY (caps), (index)))
+/* quick way to append a structure without checking the args */
+#define gst_caps_append_structure_unchecked(caps, structure) G_STMT_START{\
+ GstStructure *__s=structure; \
+ if (gst_structure_set_parent_refcount (__s, &GST_MINI_OBJECT_REFCOUNT(caps))) \
+ g_ptr_array_add (GST_CAPS_ARRAY (caps), __s); \
+}G_STMT_END
+
+/* lock to protect multiple invocations of static caps to caps conversion */
+G_LOCK_DEFINE_STATIC (static_caps_lock);
+
+static void gst_caps_transform_to_string (const GValue * src_value,
+ GValue * dest_value);
+static gboolean gst_caps_from_string_inplace (GstCaps * caps,
+ const gchar * string);
+
+GType _gst_caps_type = 0;
+
+GST_DEFINE_MINI_OBJECT_TYPE (GstCaps, gst_caps);
+
+void
+_priv_gst_caps_initialize (void)
+{
+ _gst_caps_type = gst_caps_get_type ();
+
+ g_value_register_transform_func (_gst_caps_type,
+ G_TYPE_STRING, gst_caps_transform_to_string);
+}
+
+static GstCaps *
+_gst_caps_copy (const GstCaps * caps)
+{
+ GstCaps *newcaps;
+ GstStructure *structure;
+ guint i, n;
+
+ g_return_val_if_fail (GST_IS_CAPS (caps), NULL);
+
+ newcaps = gst_caps_new_empty ();
+ GST_CAPS_FLAGS (newcaps) = GST_CAPS_FLAGS (caps);
+ n = GST_CAPS_LEN (caps);
+
+ for (i = 0; i < n; i++) {
+ structure = gst_caps_get_structure_unchecked (caps, i);
+ gst_caps_append_structure (newcaps, gst_structure_copy (structure));
+ }
+
+ return newcaps;
+}
+
+/* creation/deletion */
+static void
+_gst_caps_free (GstCaps * caps)
+{
+ GstStructure *structure;
+ guint i, len;
+
+ /* The refcount must be 0, but since we're only called by gst_caps_unref,
+ * don't bother testing. */
+ len = GST_CAPS_LEN (caps);
+ /* This can be used to get statistics about caps sizes */
+ /*GST_CAT_INFO (GST_CAT_CAPS, "caps size: %d", len); */
+ for (i = 0; i < len; i++) {
+ structure = (GstStructure *) gst_caps_get_structure_unchecked (caps, i);
+ gst_structure_set_parent_refcount (structure, NULL);
+ gst_structure_free (structure);
+ }
+ g_ptr_array_free (GST_CAPS_ARRAY (caps), TRUE);
+
+#ifdef DEBUG_REFCOUNT
+ GST_CAT_LOG (GST_CAT_CAPS, "freeing caps %p", caps);
+#endif
+ g_slice_free1 (GST_MINI_OBJECT_SIZE (caps), caps);
+}
+
+static void
+gst_caps_init (GstCaps * caps, gsize size)
+{
+ gst_mini_object_init (GST_MINI_OBJECT_CAST (caps), _gst_caps_type, size);
+
+ caps->mini_object.copy = (GstMiniObjectCopyFunction) _gst_caps_copy;
+ caps->mini_object.dispose = NULL;
+ caps->mini_object.free = (GstMiniObjectFreeFunction) _gst_caps_free;
+
+ /* the 32 has been determined by logging caps sizes in _gst_caps_free
+ * but g_ptr_array uses 16 anyway if it expands once, so this does not help
+ * in practise
+ * GST_CAPS_ARRAY (caps) = g_ptr_array_sized_new (32);
+ */
+ caps->priv = g_ptr_array_new ();
+}
+
+/**
+ * gst_caps_new_empty:
+ *
+ * Creates a new #GstCaps that is empty. That is, the returned
+ * #GstCaps contains no media formats.
+ * Caller is responsible for unreffing the returned caps.
+ *
+ * Returns: (transfer full): the new #GstCaps
+ */
+GstCaps *
+gst_caps_new_empty (void)
+{
+ GstCaps *caps;
+
+ caps = g_slice_new (GstCaps);
+
+ gst_caps_init (caps, sizeof (GstCaps));
+
+#ifdef DEBUG_REFCOUNT
+ GST_CAT_TRACE (GST_CAT_CAPS, "created caps %p", caps);
+#endif
+
+ return caps;
+}
+
+/**
+ * gst_caps_new_any:
+ *
+ * Creates a new #GstCaps that indicates that it is compatible with
+ * any media format.
+ *
+ * Returns: (transfer full): the new #GstCaps
+ */
+GstCaps *
+gst_caps_new_any (void)
+{
+ GstCaps *caps = gst_caps_new_empty ();
+
+ GST_CAPS_FLAG_SET (caps, GST_CAPS_FLAGS_ANY);
+
+ return caps;
+}
+
+/**
+ * gst_caps_new_simple:
+ * @media_type: the media type of the structure
+ * @fieldname: first field to set
+ * @...: additional arguments
+ *
+ * Creates a new #GstCaps that contains one #GstStructure. The
+ * structure is defined by the arguments, which have the same format
+ * as gst_structure_new().
+ * Caller is responsible for unreffing the returned caps.
+ *
+ * Returns: (transfer full): the new #GstCaps
+ */
+GstCaps *
+gst_caps_new_simple (const char *media_type, const char *fieldname, ...)
+{
+ GstCaps *caps;
+ GstStructure *structure;
+ va_list var_args;
+
+ caps = gst_caps_new_empty ();
+
+ va_start (var_args, fieldname);
+ structure = gst_structure_new_valist (media_type, fieldname, var_args);
+ va_end (var_args);
+
+ if (structure)
+ gst_caps_append_structure_unchecked (caps, structure);
+ else
+ gst_caps_replace (&caps, NULL);
+
+ return caps;
+}
+
+/**
+ * gst_caps_new_full:
+ * @struct1: the first structure to add
+ * @...: additional structures to add
+ *
+ * Creates a new #GstCaps and adds all the structures listed as
+ * arguments. The list must be NULL-terminated. The structures
+ * are not copied; the returned #GstCaps owns the structures.
+ *
+ * Returns: (transfer full): the new #GstCaps
+ */
+GstCaps *
+gst_caps_new_full (GstStructure * struct1, ...)
+{
+ GstCaps *caps;
+ va_list var_args;
+
+ va_start (var_args, struct1);
+ caps = gst_caps_new_full_valist (struct1, var_args);
+ va_end (var_args);
+
+ return caps;
+}
+
+/**
+ * gst_caps_new_full_valist:
+ * @structure: the first structure to add
+ * @var_args: additional structures to add
+ *
+ * Creates a new #GstCaps and adds all the structures listed as
+ * arguments. The list must be NULL-terminated. The structures
+ * are not copied; the returned #GstCaps owns the structures.
+ *
+ * Returns: (transfer full): the new #GstCaps
+ */
+GstCaps *
+gst_caps_new_full_valist (GstStructure * structure, va_list var_args)
+{
+ GstCaps *caps;
+
+ caps = gst_caps_new_empty ();
+
+ while (structure) {
+ gst_caps_append_structure_unchecked (caps, structure);
+ structure = va_arg (var_args, GstStructure *);
+ }
+
+ return caps;
+}
+
+GType
+gst_static_caps_get_type (void)
+{
+ static GType staticcaps_type = 0;
+
+ if (G_UNLIKELY (staticcaps_type == 0)) {
+ staticcaps_type = g_pointer_type_register_static ("GstStaticCaps");
+ }
+ return staticcaps_type;
+}
+
+
+/**
+ * gst_static_caps_get:
+ * @static_caps: the #GstStaticCaps to convert
+ *
+ * Converts a #GstStaticCaps to a #GstCaps.
+ *
+ * Returns: (transfer full): a pointer to the #GstCaps. Unref after usage.
+ * Since the core holds an additional ref to the returned caps,
+ * use gst_caps_make_writable() on the returned caps to modify it.
+ */
+GstCaps *
+gst_static_caps_get (GstStaticCaps * static_caps)
+{
+ GstCaps *caps;
+
+ g_return_val_if_fail (static_caps != NULL, NULL);
+
+ caps = (GstCaps *) static_caps;
+
+ /* refcount is 0 when we need to convert */
+ if (G_UNLIKELY (GST_CAPS_REFCOUNT_VALUE (caps) == 0)) {
+ const char *string;
+ GstCaps temp;
+
+ G_LOCK (static_caps_lock);
+ /* check if other thread already updated */
+ if (G_UNLIKELY (GST_CAPS_REFCOUNT_VALUE (caps) > 0))
+ goto done;
+
+ string = static_caps->string;
+
+ if (G_UNLIKELY (string == NULL))
+ goto no_string;
+
+ GST_CAT_TRACE (GST_CAT_CAPS, "creating %p", static_caps);
+
+ /* we construct the caps on the stack, then copy over the struct into our
+ * real caps, refcount last. We do this because we must leave the refcount
+ * of the result caps to 0 so that other threads don't run away with the
+ * caps while we are constructing it. */
+ gst_caps_init (&temp, sizeof (GstCaps));
+
+ /* convert to string */
+ if (G_UNLIKELY (!gst_caps_from_string_inplace (&temp, string)))
+ g_critical ("Could not convert static caps \"%s\"", string);
+
+ gst_caps_init (caps, sizeof (GstCaps));
+ /* now copy stuff over to the real caps. */
+ GST_CAPS_FLAGS (caps) = GST_CAPS_FLAGS (&temp);
+ caps->priv = GST_CAPS_ARRAY (&temp);
+
+ GST_CAT_TRACE (GST_CAT_CAPS, "created %p", static_caps);
+ done:
+ G_UNLOCK (static_caps_lock);
+ }
+ /* ref the caps, makes it not writable */
+ gst_caps_ref (caps);
+
+ return caps;
+
+ /* ERRORS */
+no_string:
+ {
+ G_UNLOCK (static_caps_lock);
+ g_warning ("static caps %p string is NULL", static_caps);
+ return NULL;
+ }
+}
+
+/**
+ * gst_static_caps_cleanup:
+ * @static_caps: the #GstStaticCaps to convert
+ *
+ * Clean up the caps contained in @static_caps when the refcount is 0.
+ */
+void
+gst_static_caps_cleanup (GstStaticCaps * static_caps)
+{
+ GstCaps *caps = (GstCaps *) static_caps;
+
+ /* FIXME: this is not threadsafe */
+ if (GST_CAPS_REFCOUNT_VALUE (caps) == 1) {
+ GstStructure *structure;
+ guint i, clen;
+
+ clen = GST_CAPS_LEN (caps);
+
+ for (i = 0; i < clen; i++) {
+ structure = (GstStructure *) gst_caps_get_structure (caps, i);
+ gst_structure_set_parent_refcount (structure, NULL);
+ gst_structure_free (structure);
+ }
+ g_ptr_array_free (GST_CAPS_ARRAY (caps), TRUE);
+ GST_CAPS_REFCOUNT (caps) = 0;
+ }
+}
+
+/* manipulation */
+
+static GstStructure *
+gst_caps_remove_and_get_structure (GstCaps * caps, guint idx)
+{
+ /* don't use index_fast, gst_caps_do_simplify relies on the order */
+ GstStructure *s = g_ptr_array_remove_index (GST_CAPS_ARRAY (caps), idx);
+
+ gst_structure_set_parent_refcount (s, NULL);
+ return s;
+}
+
+/**
+ * gst_caps_steal_structure:
+ * @caps: the #GstCaps to retrieve from
+ * @index: Index of the structure to retrieve
+ *
+ * Retrieves the stucture with the given index from the list of structures
+ * contained in @caps. The caller becomes the owner of the returned structure.
+ *
+ * Returns: (transfer full): a pointer to the #GstStructure corresponding
+ * to @index.
+ *
+ * Since: 0.10.30
+ */
+GstStructure *
+gst_caps_steal_structure (GstCaps * caps, guint index)
+{
+ g_return_val_if_fail (caps != NULL, NULL);
+ g_return_val_if_fail (IS_WRITABLE (caps), NULL);
+
+ if (G_UNLIKELY (index >= GST_CAPS_LEN (caps)))
+ return NULL;
+
+ return gst_caps_remove_and_get_structure (caps, index);
+}
+
+/**
+ * gst_caps_append:
+ * @caps1: the #GstCaps that will be appended to
+ * @caps2: (transfer full): the #GstCaps to append
+ *
+ * Appends the structures contained in @caps2 to @caps1. The structures in
+ * @caps2 are not copied -- they are transferred to @caps1, and then @caps2 is
+ * freed. If either caps is ANY, the resulting caps will be ANY.
+ */
+void
+gst_caps_append (GstCaps * caps1, GstCaps * caps2)
+{
+ GstStructure *structure;
+ int i;
+
+ g_return_if_fail (GST_IS_CAPS (caps1));
+ g_return_if_fail (GST_IS_CAPS (caps2));
+ g_return_if_fail (IS_WRITABLE (caps1));
+ g_return_if_fail (IS_WRITABLE (caps2));
+
+ if (G_UNLIKELY (CAPS_IS_ANY (caps1) || CAPS_IS_ANY (caps2))) {
+ /* FIXME: this leaks */
+ GST_CAPS_FLAGS (caps1) |= GST_CAPS_FLAGS_ANY;
+ for (i = GST_CAPS_LEN (caps2) - 1; i >= 0; i--) {
+ structure = gst_caps_remove_and_get_structure (caps2, i);
+ gst_structure_free (structure);
+ }
+ } else {
+ for (i = GST_CAPS_LEN (caps2); i; i--) {
+ structure = gst_caps_remove_and_get_structure (caps2, 0);
+ gst_caps_append_structure_unchecked (caps1, structure);
+ }
+ }
+ gst_caps_unref (caps2); /* guaranteed to free it */
+}
+
+/**
+ * gst_caps_merge:
+ * @caps1: the #GstCaps that will take the new entries
+ * @caps2: (transfer full): the #GstCaps to merge in
+ *
+ * Appends the structures contained in @caps2 to @caps1 if they are not yet
+ * expressed by @caps1. The structures in @caps2 are not copied -- they are
+ * transferred to @caps1, and then @caps2 is freed.
+ * If either caps is ANY, the resulting caps will be ANY.
+ *
+ * Since: 0.10.10
+ */
+void
+gst_caps_merge (GstCaps * caps1, GstCaps * caps2)
+{
+ GstStructure *structure;
+ int i;
+
+ g_return_if_fail (GST_IS_CAPS (caps1));
+ g_return_if_fail (GST_IS_CAPS (caps2));
+ g_return_if_fail (IS_WRITABLE (caps1));
+ g_return_if_fail (IS_WRITABLE (caps2));
+
+ if (G_UNLIKELY (CAPS_IS_ANY (caps1))) {
+ for (i = GST_CAPS_LEN (caps2) - 1; i >= 0; i--) {
+ structure = gst_caps_remove_and_get_structure (caps2, i);
+ gst_structure_free (structure);
+ }
+ } else if (G_UNLIKELY (CAPS_IS_ANY (caps2))) {
+ GST_CAPS_FLAGS (caps1) |= GST_CAPS_FLAGS_ANY;
+ for (i = GST_CAPS_LEN (caps1) - 1; i >= 0; i--) {
+ structure = gst_caps_remove_and_get_structure (caps1, i);
+ gst_structure_free (structure);
+ }
+ } else {
+ for (i = GST_CAPS_LEN (caps2); i; i--) {
+ structure = gst_caps_remove_and_get_structure (caps2, 0);
+ gst_caps_merge_structure (caps1, structure);
+ }
+ /* this is too naive
+ GstCaps *com = gst_caps_intersect (caps1, caps2);
+ GstCaps *add = gst_caps_subtract (caps2, com);
+
+ GST_DEBUG ("common : %d", gst_caps_get_size (com));
+ GST_DEBUG ("adding : %d", gst_caps_get_size (add));
+ gst_caps_append (caps1, add);
+ gst_caps_unref (com);
+ */
+ }
+ gst_caps_unref (caps2); /* guaranteed to free it */
+}
+
+/**
+ * gst_caps_append_structure:
+ * @caps: the #GstCaps that will be appended to
+ * @structure: (transfer full): the #GstStructure to append
+ *
+ * Appends @structure to @caps. The structure is not copied; @caps
+ * becomes the owner of @structure.
+ */
+void
+gst_caps_append_structure (GstCaps * caps, GstStructure * structure)
+{
+ g_return_if_fail (GST_IS_CAPS (caps));
+ g_return_if_fail (IS_WRITABLE (caps));
+
+ if (G_LIKELY (structure)) {
+ gst_caps_append_structure_unchecked (caps, structure);
+ }
+}
+
+/**
+ * gst_caps_remove_structure:
+ * @caps: the #GstCaps to remove from
+ * @idx: Index of the structure to remove
+ *
+ * removes the stucture with the given index from the list of structures
+ * contained in @caps.
+ */
+void
+gst_caps_remove_structure (GstCaps * caps, guint idx)
+{
+ GstStructure *structure;
+
+ g_return_if_fail (caps != NULL);
+ g_return_if_fail (idx <= gst_caps_get_size (caps));
+ g_return_if_fail (IS_WRITABLE (caps));
+
+ structure = gst_caps_remove_and_get_structure (caps, idx);
+ gst_structure_free (structure);
+}
+
+/**
+ * gst_caps_merge_structure:
+ * @caps: the #GstCaps that will the new structure
+ * @structure: (transfer full): the #GstStructure to merge
+ *
+ * Appends @structure to @caps if its not already expressed by @caps. The
+ * structure is not copied; @caps becomes the owner of @structure.
+ */
+void
+gst_caps_merge_structure (GstCaps * caps, GstStructure * structure)
+{
+ g_return_if_fail (GST_IS_CAPS (caps));
+ g_return_if_fail (IS_WRITABLE (caps));
+
+ if (G_LIKELY (structure)) {
+ GstStructure *structure1;
+ int i;
+ gboolean unique = TRUE;
+
+ /* check each structure */
+ for (i = GST_CAPS_LEN (caps) - 1; i >= 0; i--) {
+ structure1 = gst_caps_get_structure_unchecked (caps, i);
+ /* if structure is a subset of structure1, then skip it */
+ if (gst_structure_is_subset (structure, structure1)) {
+ unique = FALSE;
+ break;
+ }
+ }
+ if (unique) {
+ gst_caps_append_structure_unchecked (caps, structure);
+ } else {
+ gst_structure_free (structure);
+ }
+ }
+}
+
+/**
+ * gst_caps_get_size:
+ * @caps: a #GstCaps
+ *
+ * Gets the number of structures contained in @caps.
+ *
+ * Returns: the number of structures that @caps contains
+ */
+guint
+gst_caps_get_size (const GstCaps * caps)
+{
+ g_return_val_if_fail (GST_IS_CAPS (caps), 0);
+
+ return GST_CAPS_LEN (caps);
+}
+
+/**
+ * gst_caps_get_structure:
+ * @caps: a #GstCaps
+ * @index: the index of the structure
+ *
+ * Finds the structure in @caps that has the index @index, and
+ * returns it.
+ *
+ * WARNING: This function takes a const GstCaps *, but returns a
+ * non-const GstStructure *. This is for programming convenience --
+ * the caller should be aware that structures inside a constant
+ * #GstCaps should not be modified. However, if you know the caps
+ * are writable, either because you have just copied them or made
+ * them writable with gst_caps_make_writable(), you may modify the
+ * structure returned in the usual way, e.g. with functions like
+ * gst_structure_set().
+ *
+ * You do not need to free or unref the structure returned, it
+ * belongs to the #GstCaps.
+ *
+ * Returns: (transfer none): a pointer to the #GstStructure corresponding
+ * to @index
+ */
+GstStructure *
+gst_caps_get_structure (const GstCaps * caps, guint index)
+{
+ g_return_val_if_fail (GST_IS_CAPS (caps), NULL);
+ g_return_val_if_fail (index < GST_CAPS_LEN (caps), NULL);
+
+ return gst_caps_get_structure_unchecked (caps, index);
+}
+
+/**
+ * gst_caps_copy_nth:
+ * @caps: the #GstCaps to copy
+ * @nth: the nth structure to copy
+ *
+ * Creates a new #GstCaps and appends a copy of the nth structure
+ * contained in @caps.
+ *
+ * Returns: (transfer full): the new #GstCaps
+ */
+GstCaps *
+gst_caps_copy_nth (const GstCaps * caps, guint nth)
+{
+ GstCaps *newcaps;
+ GstStructure *structure;
+
+ g_return_val_if_fail (GST_IS_CAPS (caps), NULL);
+
+ newcaps = gst_caps_new_empty ();
+ GST_CAPS_FLAGS (newcaps) = GST_CAPS_FLAGS (caps);
+
+ if (G_LIKELY (GST_CAPS_LEN (caps) > nth)) {
+ structure = gst_caps_get_structure_unchecked (caps, nth);
+ gst_caps_append_structure_unchecked (newcaps,
+ gst_structure_copy (structure));
+ }
+
+ return newcaps;
+}
+
+/**
+ * gst_caps_truncate:
+ * @caps: the #GstCaps to truncate
+ *
+ * Destructively discard all but the first structure from @caps. Useful when
+ * fixating. @caps must be writable.
+ */
+void
+gst_caps_truncate (GstCaps * caps)
+{
+ gint i;
+
+ g_return_if_fail (GST_IS_CAPS (caps));
+ g_return_if_fail (IS_WRITABLE (caps));
+
+ i = GST_CAPS_LEN (caps) - 1;
+
+ while (i > 0)
+ gst_caps_remove_structure (caps, i--);
+}
+
+/**
+ * gst_caps_set_value:
+ * @caps: a writable caps
+ * @field: name of the field to set
+ * @value: value to set the field to
+ *
+ * Sets the given @field on all structures of @caps to the given @value.
+ * This is a convenience function for calling gst_structure_set_value() on
+ * all structures of @caps.
+ *
+ * Since: 0.10.26
+ **/
+void
+gst_caps_set_value (GstCaps * caps, const char *field, const GValue * value)
+{
+ guint i, len;
+
+ g_return_if_fail (GST_IS_CAPS (caps));
+ g_return_if_fail (IS_WRITABLE (caps));
+ g_return_if_fail (field != NULL);
+ g_return_if_fail (G_IS_VALUE (value));
+
+ len = GST_CAPS_LEN (caps);
+ for (i = 0; i < len; i++) {
+ GstStructure *structure = gst_caps_get_structure_unchecked (caps, i);
+ gst_structure_set_value (structure, field, value);
+ }
+}
+
+/**
+ * gst_caps_set_simple_valist:
+ * @caps: the #GstCaps to set
+ * @field: first field to set
+ * @varargs: additional parameters
+ *
+ * Sets fields in a #GstCaps. The arguments must be passed in the same
+ * manner as gst_structure_set(), and be NULL-terminated.
+ * <note>Prior to GStreamer version 0.10.26, this function failed when
+ * @caps was not simple. If your code needs to work with those versions
+ * of GStreamer, you may only call this function when GST_CAPS_IS_SIMPLE()
+ * is %TRUE for @caps.</note>
+ */
+void
+gst_caps_set_simple_valist (GstCaps * caps, const char *field, va_list varargs)
+{
+ GValue value = { 0, };
+
+ g_return_if_fail (GST_IS_CAPS (caps));
+ g_return_if_fail (IS_WRITABLE (caps));
+
+ while (field) {
+ GType type;
+ char *err;
+
+ type = va_arg (varargs, GType);
+
+ if (G_UNLIKELY (type == G_TYPE_DATE)) {
+ g_warning ("Don't use G_TYPE_DATE, use GST_TYPE_DATE instead\n");
+ type = GST_TYPE_DATE;
+ }
+ G_VALUE_COLLECT_INIT (&value, type, varargs, 0, &err);
+ if (G_UNLIKELY (err)) {
+ g_critical ("%s", err);
+ return;
+ }
+
+ gst_caps_set_value (caps, field, &value);
+
+ g_value_unset (&value);
+
+ field = va_arg (varargs, const gchar *);
+ }
+}
+
+/**
+ * gst_caps_set_simple:
+ * @caps: the #GstCaps to set
+ * @field: first field to set
+ * @...: additional parameters
+ *
+ * Sets fields in a #GstCaps. The arguments must be passed in the same
+ * manner as gst_structure_set(), and be NULL-terminated.
+ * <note>Prior to GStreamer version 0.10.26, this function failed when
+ * @caps was not simple. If your code needs to work with those versions
+ * of GStreamer, you may only call this function when GST_CAPS_IS_SIMPLE()
+ * is %TRUE for @caps.</note>
+ */
+void
+gst_caps_set_simple (GstCaps * caps, const char *field, ...)
+{
+ va_list var_args;
+
+ g_return_if_fail (GST_IS_CAPS (caps));
+ g_return_if_fail (IS_WRITABLE (caps));
+
+ va_start (var_args, field);
+ gst_caps_set_simple_valist (caps, field, var_args);
+ va_end (var_args);
+}
+
+/* tests */
+
+/**
+ * gst_caps_is_any:
+ * @caps: the #GstCaps to test
+ *
+ * Determines if @caps represents any media format.
+ *
+ * Returns: TRUE if @caps represents any format.
+ */
+gboolean
+gst_caps_is_any (const GstCaps * caps)
+{
+ g_return_val_if_fail (GST_IS_CAPS (caps), FALSE);
+
+ return (CAPS_IS_ANY (caps));
+}
+
+/**
+ * gst_caps_is_empty:
+ * @caps: the #GstCaps to test
+ *
+ * Determines if @caps represents no media formats.
+ *
+ * Returns: TRUE if @caps represents no formats.
+ */
+gboolean
+gst_caps_is_empty (const GstCaps * caps)
+{
+ g_return_val_if_fail (GST_IS_CAPS (caps), FALSE);
+
+ if (CAPS_IS_ANY (caps))
+ return FALSE;
+
+ return CAPS_IS_EMPTY_SIMPLE (caps);
+}
+
+static gboolean
+gst_caps_is_fixed_foreach (GQuark field_id, const GValue * value,
+ gpointer unused)
+{
+ return gst_value_is_fixed (value);
+}
+
+/**
+ * gst_caps_is_fixed:
+ * @caps: the #GstCaps to test
+ *
+ * Fixed #GstCaps describe exactly one format, that is, they have exactly
+ * one structure, and each field in the structure describes a fixed type.
+ * Examples of non-fixed types are GST_TYPE_INT_RANGE and GST_TYPE_LIST.
+ *
+ * Returns: TRUE if @caps is fixed
+ */
+gboolean
+gst_caps_is_fixed (const GstCaps * caps)
+{
+ GstStructure *structure;
+
+ g_return_val_if_fail (GST_IS_CAPS (caps), FALSE);
+
+ if (GST_CAPS_LEN (caps) != 1)
+ return FALSE;
+
+ structure = gst_caps_get_structure_unchecked (caps, 0);
+
+ return gst_structure_foreach (structure, gst_caps_is_fixed_foreach, NULL);
+}
+
+/**
+ * gst_caps_is_equal_fixed:
+ * @caps1: the #GstCaps to test
+ * @caps2: the #GstCaps to test
+ *
+ * Tests if two #GstCaps are equal. This function only works on fixed
+ * #GstCaps.
+ *
+ * Returns: TRUE if the arguments represent the same format
+ */
+gboolean
+gst_caps_is_equal_fixed (const GstCaps * caps1, const GstCaps * caps2)
+{
+ GstStructure *struct1, *struct2;
+
+ g_return_val_if_fail (gst_caps_is_fixed (caps1), FALSE);
+ g_return_val_if_fail (gst_caps_is_fixed (caps2), FALSE);
+
+ struct1 = gst_caps_get_structure_unchecked (caps1, 0);
+ struct2 = gst_caps_get_structure_unchecked (caps2, 0);
+
+ return gst_structure_is_equal (struct1, struct2);
+}
+
+/**
+ * gst_caps_is_always_compatible:
+ * @caps1: the #GstCaps to test
+ * @caps2: the #GstCaps to test
+ *
+ * A given #GstCaps structure is always compatible with another if
+ * every media format that is in the first is also contained in the
+ * second. That is, @caps1 is a subset of @caps2.
+ *
+ * Returns: TRUE if @caps1 is a subset of @caps2.
+ */
+gboolean
+gst_caps_is_always_compatible (const GstCaps * caps1, const GstCaps * caps2)
+{
+ g_return_val_if_fail (GST_IS_CAPS (caps1), FALSE);
+ g_return_val_if_fail (GST_IS_CAPS (caps2), FALSE);
+
+ return gst_caps_is_subset (caps1, caps2);
+}
+
+/**
+ * gst_caps_is_subset:
+ * @subset: a #GstCaps
+ * @superset: a potentially greater #GstCaps
+ *
+ * Checks if all caps represented by @subset are also represented by @superset.
+ * <note>This function does not work reliably if optional properties for caps
+ * are included on one caps and omitted on the other.</note>
+ *
+ * Returns: %TRUE if @subset is a subset of @superset
+ */
+gboolean
+gst_caps_is_subset (const GstCaps * subset, const GstCaps * superset)
+{
+ GstStructure *s1, *s2;
+ gboolean ret = TRUE;
+ gint i, j;
+
+ g_return_val_if_fail (subset != NULL, FALSE);
+ g_return_val_if_fail (superset != NULL, FALSE);
+
+ if (CAPS_IS_EMPTY (subset) || CAPS_IS_ANY (superset))
+ return TRUE;
+ if (CAPS_IS_ANY (subset) || CAPS_IS_EMPTY (superset))
+ return FALSE;
+
+ for (i = GST_CAPS_LEN (subset) - 1; i >= 0; i--) {
+ for (j = GST_CAPS_LEN (superset) - 1; j >= 0; j--) {
+ s1 = gst_caps_get_structure_unchecked (subset, i);
+ s2 = gst_caps_get_structure_unchecked (superset, j);
+ if (gst_structure_is_subset (s1, s2)) {
+ /* If we found a superset, continue with the next
+ * subset structure */
+ break;
+ }
+ }
+ /* If we found no superset for this subset structure
+ * we return FALSE immediately */
+ if (j == -1) {
+ ret = FALSE;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * gst_caps_is_subset_structure:
+ * @caps: a #GstCaps
+ * @structure: a potential #GstStructure subset of @caps
+ *
+ * Checks if @structure is a subset of @caps. See gst_caps_is_subset()
+ * for more information.
+ *
+ * Returns: %TRUE if @structure is a subset of @caps
+ *
+ * Since: 0.10.35
+ */
+gboolean
+gst_caps_is_subset_structure (const GstCaps * caps,
+ const GstStructure * structure)
+{
+ GstStructure *s;
+ gint i;
+
+ g_return_val_if_fail (caps != NULL, FALSE);
+ g_return_val_if_fail (structure != NULL, FALSE);
+
+ if (CAPS_IS_ANY (caps))
+ return TRUE;
+ if (CAPS_IS_EMPTY (caps))
+ return FALSE;
+
+ for (i = GST_CAPS_LEN (caps) - 1; i >= 0; i--) {
+ s = gst_caps_get_structure_unchecked (caps, i);
+ if (gst_structure_is_subset (structure, s)) {
+ /* If we found a superset return TRUE */
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/**
+ * gst_caps_is_equal:
+ * @caps1: a #GstCaps
+ * @caps2: another #GstCaps
+ *
+ * Checks if the given caps represent the same set of caps.
+ * <note>This function does not work reliably if optional properties for caps
+ * are included on one caps and omitted on the other.</note>
+ *
+ * This function deals correctly with passing NULL for any of the caps.
+ *
+ * Returns: TRUE if both caps are equal.
+ */
+gboolean
+gst_caps_is_equal (const GstCaps * caps1, const GstCaps * caps2)
+{
+ /* FIXME 0.11: NULL pointers are no valid Caps but indicate an error
+ * So there should be an assertion that caps1 and caps2 != NULL */
+
+ /* NULL <-> NULL is allowed here */
+ if (G_UNLIKELY (caps1 == caps2))
+ return TRUE;
+
+ /* one of them NULL => they are different (can't be both NULL because
+ * we checked that above) */
+ if (G_UNLIKELY (caps1 == NULL || caps2 == NULL))
+ return FALSE;
+
+ if (G_UNLIKELY (gst_caps_is_fixed (caps1) && gst_caps_is_fixed (caps2)))
+ return gst_caps_is_equal_fixed (caps1, caps2);
+
+ return gst_caps_is_subset (caps1, caps2) && gst_caps_is_subset (caps2, caps1);
+}
+
+/* intersect operation */
+
+/**
+ * gst_caps_can_intersect:
+ * @caps1: a #GstCaps to intersect
+ * @caps2: a #GstCaps to intersect
+ *
+ * Tries intersecting @caps1 and @caps2 and reports whether the result would not
+ * be empty
+ *
+ * Returns: %TRUE if intersection would be not empty
+ *
+ * Since: 0.10.25
+ */
+gboolean
+gst_caps_can_intersect (const GstCaps * caps1, const GstCaps * caps2)
+{
+ guint64 i; /* index can be up to 2 * G_MAX_UINT */
+ guint j, k, len1, len2;
+ GstStructure *struct1;
+ GstStructure *struct2;
+
+ g_return_val_if_fail (GST_IS_CAPS (caps1), FALSE);
+ g_return_val_if_fail (GST_IS_CAPS (caps2), FALSE);
+
+ /* caps are exactly the same pointers */
+ if (G_UNLIKELY (caps1 == caps2))
+ return TRUE;
+
+ /* empty caps on either side, return empty */
+ if (G_UNLIKELY (CAPS_IS_EMPTY (caps1) || CAPS_IS_EMPTY (caps2)))
+ return FALSE;
+
+ /* one of the caps is any */
+ if (G_UNLIKELY (CAPS_IS_ANY (caps1) || CAPS_IS_ANY (caps2)))
+ return TRUE;
+
+ /* run zigzag on top line then right line, this preserves the caps order
+ * much better than a simple loop.
+ *
+ * This algorithm zigzags over the caps structures as demonstrated in
+ * the folowing matrix:
+ *
+ * caps1 0 1 2 3
+ * +------------- total distance: +-------------
+ * | 1 2 4 7 0 | 0 1 2 3
+ * caps2 | 3 5 8 10 1 | 1 2 3 4
+ * | 6 9 11 12 2 | 2 3 4 5
+ *
+ * First we iterate over the caps1 structures (top line) intersecting
+ * the structures diagonally down, then we iterate over the caps2
+ * structures. The result is that the intersections are ordered based on the
+ * sum of the indexes in the list.
+ */
+ len1 = GST_CAPS_LEN (caps1);
+ len2 = GST_CAPS_LEN (caps2);
+ for (i = 0; i < len1 + len2 - 1; i++) {
+ /* superset index goes from 0 to sgst_caps_structure_intersectuperset->structs->len-1 */
+ j = MIN (i, len1 - 1);
+ /* subset index stays 0 until i reaches superset->structs->len, then it
+ * counts up from 1 to subset->structs->len - 1 */
+ k = (i > j) ? (i - j) : 0; /* MAX (0, i - j) */
+
+ /* now run the diagonal line, end condition is the left or bottom
+ * border */
+ while (k < len2) {
+ struct1 = gst_caps_get_structure_unchecked (caps1, j);
+ struct2 = gst_caps_get_structure_unchecked (caps2, k);
+
+ if (gst_structure_can_intersect (struct1, struct2)) {
+ return TRUE;
+ }
+ /* move down left */
+ k++;
+ if (G_UNLIKELY (j == 0))
+ break; /* so we don't roll back to G_MAXUINT */
+ j--;
+ }
+ }
+ return FALSE;
+}
+
+static GstCaps *
+gst_caps_intersect_zig_zag (const GstCaps * caps1, const GstCaps * caps2)
+{
+ guint64 i; /* index can be up to 2 * G_MAX_UINT */
+ guint j, k, len1, len2;
+
+ GstStructure *struct1;
+ GstStructure *struct2;
+ GstCaps *dest;
+ GstStructure *istruct;
+
+ /* caps are exactly the same pointers, just copy one caps */
+ if (G_UNLIKELY (caps1 == caps2))
+ return _gst_caps_copy (caps1);
+
+ /* empty caps on either side, return empty */
+ if (G_UNLIKELY (CAPS_IS_EMPTY (caps1) || CAPS_IS_EMPTY (caps2)))
+ return gst_caps_new_empty ();
+
+ /* one of the caps is any, just copy the other caps */
+ if (G_UNLIKELY (CAPS_IS_ANY (caps1)))
+ return _gst_caps_copy (caps2);
+ if (G_UNLIKELY (CAPS_IS_ANY (caps2)))
+ return _gst_caps_copy (caps1);
+
+ dest = gst_caps_new_empty ();
+
+ /* run zigzag on top line then right line, this preserves the caps order
+ * much better than a simple loop.
+ *
+ * This algorithm zigzags over the caps structures as demonstrated in
+ * the folowing matrix:
+ *
+ * caps1
+ * +-------------
+ * | 1 2 4 7
+ * caps2 | 3 5 8 10
+ * | 6 9 11 12
+ *
+ * First we iterate over the caps1 structures (top line) intersecting
+ * the structures diagonally down, then we iterate over the caps2
+ * structures.
+ */
+ len1 = GST_CAPS_LEN (caps1);
+ len2 = GST_CAPS_LEN (caps2);
+ for (i = 0; i < len1 + len2 - 1; i++) {
+ /* caps1 index goes from 0 to GST_CAPS_LEN (caps1)-1 */
+ j = MIN (i, len1 - 1);
+ /* caps2 index stays 0 until i reaches GST_CAPS_LEN (caps1), then it counts
+ * up from 1 to GST_CAPS_LEN (caps2) - 1 */
+ k = (i > j) ? (i - j) : 0; /* MAX (0, i - j) */
+
+ /* now run the diagonal line, end condition is the left or bottom
+ * border */
+ while (k < len2) {
+ struct1 = gst_caps_get_structure_unchecked (caps1, j);
+ struct2 = gst_caps_get_structure_unchecked (caps2, k);
+
+ istruct = gst_structure_intersect (struct1, struct2);
+
+ gst_caps_merge_structure (dest, istruct);
+ /* move down left */
+ k++;
+ if (G_UNLIKELY (j == 0))
+ break; /* so we don't roll back to G_MAXUINT */
+ j--;
+ }
+ }
+ return dest;
+}
+
+/**
+ * gst_caps_intersect_first:
+ * @caps1: a #GstCaps to intersect
+ * @caps2: a #GstCaps to intersect
+ *
+ * Creates a new #GstCaps that contains all the formats that are common
+ * to both @caps1 and @caps2.
+ *
+ * Unlike @gst_caps_intersect, the returned caps will be ordered in a similar
+ * fashion as @caps1.
+ *
+ * Returns: the new #GstCaps
+ */
+static GstCaps *
+gst_caps_intersect_first (const GstCaps * caps1, const GstCaps * caps2)
+{
+ guint64 i; /* index can be up to 2 * G_MAX_UINT */
+ guint j, len1, len2;
+
+ GstStructure *struct1;
+ GstStructure *struct2;
+ GstCaps *dest;
+ GstStructure *istruct;
+
+ /* caps are exactly the same pointers, just copy one caps */
+ if (G_UNLIKELY (caps1 == caps2))
+ return gst_caps_copy (caps1);
+
+ /* empty caps on either side, return empty */
+ if (G_UNLIKELY (CAPS_IS_EMPTY (caps1) || CAPS_IS_EMPTY (caps2)))
+ return gst_caps_new_empty ();
+
+ /* one of the caps is any, just copy the other caps */
+ if (G_UNLIKELY (CAPS_IS_ANY (caps1)))
+ return gst_caps_copy (caps2);
+ if (G_UNLIKELY (CAPS_IS_ANY (caps2)))
+ return gst_caps_copy (caps1);
+
+ dest = gst_caps_new_empty ();
+
+ len1 = GST_CAPS_LEN (caps1);
+ len2 = GST_CAPS_LEN (caps2);
+ for (i = 0; i < len1; i++) {
+ struct1 = gst_caps_get_structure_unchecked (caps1, i);
+ for (j = 0; j < len2; j++) {
+ struct2 = gst_caps_get_structure_unchecked (caps2, j);
+ istruct = gst_structure_intersect (struct1, struct2);
+ if (istruct)
+ gst_caps_merge_structure (dest, istruct);
+ }
+ }
+
+ return dest;
+}
+
+/**
+ * gst_caps_intersect_full:
+ * @caps1: a #GstCaps to intersect
+ * @caps2: a #GstCaps to intersect
+ * @mode: The intersection algorithm/mode to use
+ *
+ * Creates a new #GstCaps that contains all the formats that are common
+ * to both @caps1 and @caps2, the order is defined by the #GstCapsIntersectMode
+ * used.
+ *
+ * Returns: the new #GstCaps
+ * Since: 0.10.33
+ */
+GstCaps *
+gst_caps_intersect_full (const GstCaps * caps1, const GstCaps * caps2,
+ GstCapsIntersectMode mode)
+{
+ g_return_val_if_fail (GST_IS_CAPS (caps1), NULL);
+ g_return_val_if_fail (GST_IS_CAPS (caps2), NULL);
+
+ switch (mode) {
+ case GST_CAPS_INTERSECT_FIRST:
+ return gst_caps_intersect_first (caps1, caps2);
+ default:
+ g_warning ("Unknown caps intersect mode: %d", mode);
+ /* fallthrough */
+ case GST_CAPS_INTERSECT_ZIG_ZAG:
+ return gst_caps_intersect_zig_zag (caps1, caps2);
+ }
+}
+
+/**
+ * gst_caps_intersect:
+ * @caps1: a #GstCaps to intersect
+ * @caps2: a #GstCaps to intersect
+ *
+ * Creates a new #GstCaps that contains all the formats that are common
+ * to both @caps1 and @caps2. Defaults to %GST_CAPS_INTERSECT_ZIG_ZAG mode.
+ *
+ * Returns: the new #GstCaps
+ */
+GstCaps *
+gst_caps_intersect (const GstCaps * caps1, const GstCaps * caps2)
+{
+ return gst_caps_intersect_full (caps1, caps2, GST_CAPS_INTERSECT_ZIG_ZAG);
+}
+
+
+/* subtract operation */
+
+typedef struct
+{
+ const GstStructure *subtract_from;
+ GSList *put_into;
+}
+SubtractionEntry;
+
+static gboolean
+gst_caps_structure_subtract_field (GQuark field_id, const GValue * value,
+ gpointer user_data)
+{
+ SubtractionEntry *e = user_data;
+ GValue subtraction = { 0, };
+ const GValue *other;
+ GstStructure *structure;
+
+ other = gst_structure_id_get_value (e->subtract_from, field_id);
+ if (!other) {
+ return FALSE;
+ }
+ if (!gst_value_subtract (&subtraction, other, value))
+ return TRUE;
+ if (gst_value_compare (&subtraction, other) == GST_VALUE_EQUAL) {
+ g_value_unset (&subtraction);
+ return FALSE;
+ } else {
+ structure = gst_structure_copy (e->subtract_from);
+ gst_structure_id_set_value (structure, field_id, &subtraction);
+ g_value_unset (&subtraction);
+ e->put_into = g_slist_prepend (e->put_into, structure);
+ return TRUE;
+ }
+}
+
+static gboolean
+gst_caps_structure_subtract (GSList ** into, const GstStructure * minuend,
+ const GstStructure * subtrahend)
+{
+ SubtractionEntry e;
+ gboolean ret;
+
+ e.subtract_from = minuend;
+ e.put_into = NULL;
+
+ ret = gst_structure_foreach ((GstStructure *) subtrahend,
+ gst_caps_structure_subtract_field, &e);
+ if (ret) {
+ *into = e.put_into;
+ } else {
+ GSList *walk;
+
+ for (walk = e.put_into; walk; walk = g_slist_next (walk)) {
+ gst_structure_free (walk->data);
+ }
+ g_slist_free (e.put_into);
+ }
+ return ret;
+}
+
+/**
+ * gst_caps_subtract:
+ * @minuend: #GstCaps to subtract from
+ * @subtrahend: #GstCaps to subtract
+ *
+ * Subtracts the @subtrahend from the @minuend.
+ * <note>This function does not work reliably if optional properties for caps
+ * are included on one caps and omitted on the other.</note>
+ *
+ * Returns: the resulting caps
+ */
+GstCaps *
+gst_caps_subtract (const GstCaps * minuend, const GstCaps * subtrahend)
+{
+ guint i, j, sublen;
+ GstStructure *min;
+ GstStructure *sub;
+ GstCaps *dest = NULL, *src;
+
+ g_return_val_if_fail (minuend != NULL, NULL);
+ g_return_val_if_fail (subtrahend != NULL, NULL);
+
+ if (CAPS_IS_EMPTY (minuend) || CAPS_IS_ANY (subtrahend)) {
+ return gst_caps_new_empty ();
+ }
+ if (CAPS_IS_EMPTY_SIMPLE (subtrahend))
+ return _gst_caps_copy (minuend);
+
+ /* FIXME: Do we want this here or above?
+ The reason we need this is that there is no definition about what
+ ANY means for specific types, so it's not possible to reduce ANY partially
+ You can only remove everything or nothing and that is done above.
+ Note: there's a test that checks this behaviour. */
+ g_return_val_if_fail (!CAPS_IS_ANY (minuend), NULL);
+ sublen = GST_CAPS_LEN (subtrahend);
+ g_assert (sublen > 0);
+
+ src = _gst_caps_copy (minuend);
+ for (i = 0; i < sublen; i++) {
+ guint srclen;
+
+ sub = gst_caps_get_structure_unchecked (subtrahend, i);
+ if (dest) {
+ gst_caps_unref (src);
+ src = dest;
+ }
+ dest = gst_caps_new_empty ();
+ srclen = GST_CAPS_LEN (src);
+ for (j = 0; j < srclen; j++) {
+ min = gst_caps_get_structure_unchecked (src, j);
+ if (gst_structure_get_name_id (min) == gst_structure_get_name_id (sub)) {
+ GSList *list;
+
+ if (gst_caps_structure_subtract (&list, min, sub)) {
+ GSList *walk;
+
+ for (walk = list; walk; walk = g_slist_next (walk)) {
+ gst_caps_append_structure_unchecked (dest,
+ (GstStructure *) walk->data);
+ }
+ g_slist_free (list);
+ } else {
+ gst_caps_append_structure_unchecked (dest, gst_structure_copy (min));
+ }
+ } else {
+ gst_caps_append_structure_unchecked (dest, gst_structure_copy (min));
+ }
+ }
+ if (CAPS_IS_EMPTY_SIMPLE (dest)) {
+ gst_caps_unref (src);
+ return dest;
+ }
+ }
+
+ gst_caps_unref (src);
+ gst_caps_do_simplify (dest);
+ return dest;
+}
+
+/* union operation */
+
+#if 0
+static GstStructure *
+gst_caps_structure_union (const GstStructure * struct1,
+ const GstStructure * struct2)
+{
+ int i;
+ GstStructure *dest;
+ const GstStructureField *field1;
+ const GstStructureField *field2;
+ int ret;
+
+ /* FIXME this doesn't actually work */
+
+ if (struct1->name != struct2->name)
+ return NULL;
+
+ dest = gst_structure_id_empty_new (struct1->name);
+
+ for (i = 0; i < struct1->fields->len; i++) {
+ GValue dest_value = { 0 };
+
+ field1 = GST_STRUCTURE_FIELD (struct1, i);
+ field2 = gst_structure_id_get_field (struct2, field1->name);
+
+ if (field2 == NULL) {
+ continue;
+ } else {
+ if (gst_value_union (&dest_value, &field1->value, &field2->value)) {
+ gst_structure_set_value (dest, g_quark_to_string (field1->name),
+ &dest_value);
+ } else {
+ ret = gst_value_compare (&field1->value, &field2->value);
+ }
+ }
+ }
+
+ return dest;
+}
+#endif
+
+/**
+ * gst_caps_union:
+ * @caps1: a #GstCaps to union
+ * @caps2: a #GstCaps to union
+ *
+ * Creates a new #GstCaps that contains all the formats that are in
+ * either @caps1 and @caps2.
+ *
+ * Returns: the new #GstCaps
+ */
+GstCaps *
+gst_caps_union (const GstCaps * caps1, const GstCaps * caps2)
+{
+ GstCaps *dest1;
+ GstCaps *dest2;
+
+ /* NULL pointers are no correct GstCaps */
+ g_return_val_if_fail (caps1 != NULL, NULL);
+ g_return_val_if_fail (caps2 != NULL, NULL);
+
+ if (CAPS_IS_EMPTY (caps1))
+ return _gst_caps_copy (caps2);
+
+ if (CAPS_IS_EMPTY (caps2))
+ return _gst_caps_copy (caps1);
+
+ if (CAPS_IS_ANY (caps1) || CAPS_IS_ANY (caps2))
+ return gst_caps_new_any ();
+
+ dest1 = _gst_caps_copy (caps1);
+ dest2 = _gst_caps_copy (caps2);
+ gst_caps_append (dest1, dest2);
+
+ gst_caps_do_simplify (dest1);
+ return dest1;
+}
+
+/* normalize/simplify operations */
+
+typedef struct _NormalizeForeach
+{
+ GstCaps *caps;
+ GstStructure *structure;
+}
+NormalizeForeach;
+
+static gboolean
+gst_caps_normalize_foreach (GQuark field_id, const GValue * value, gpointer ptr)
+{
+ NormalizeForeach *nf = (NormalizeForeach *) ptr;
+ GValue val = { 0 };
+ guint i;
+
+ if (G_VALUE_TYPE (value) == GST_TYPE_LIST) {
+ guint len = gst_value_list_get_size (value);
+ for (i = 1; i < len; i++) {
+ const GValue *v = gst_value_list_get_value (value, i);
+ GstStructure *structure = gst_structure_copy (nf->structure);
+
+ gst_structure_id_set_value (structure, field_id, v);
+ gst_caps_append_structure_unchecked (nf->caps, structure);
+ }
+
+ gst_value_init_and_copy (&val, gst_value_list_get_value (value, 0));
+ gst_structure_id_set_value (nf->structure, field_id, &val);
+ g_value_unset (&val);
+
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/**
+ * gst_caps_normalize:
+ * @caps: a #GstCaps to normalize
+ *
+ * Creates a new #GstCaps that represents the same set of formats as
+ * @caps, but contains no lists. Each list is expanded into separate
+ * @GstStructures.
+ *
+ * Returns: the new #GstCaps
+ */
+GstCaps *
+gst_caps_normalize (const GstCaps * caps)
+{
+ NormalizeForeach nf;
+ GstCaps *newcaps;
+ guint i;
+
+ g_return_val_if_fail (GST_IS_CAPS (caps), NULL);
+
+ newcaps = _gst_caps_copy (caps);
+ nf.caps = newcaps;
+
+ for (i = 0; i < gst_caps_get_size (newcaps); i++) {
+ nf.structure = gst_caps_get_structure_unchecked (newcaps, i);
+
+ while (!gst_structure_foreach (nf.structure,
+ gst_caps_normalize_foreach, &nf));
+ }
+
+ return newcaps;
+}
+
+static gint
+gst_caps_compare_structures (gconstpointer one, gconstpointer two)
+{
+ gint ret;
+ const GstStructure *struct1 = *((const GstStructure **) one);
+ const GstStructure *struct2 = *((const GstStructure **) two);
+
+ /* FIXME: this orders alphabetically, but ordering the quarks might be faster
+ So what's the best way? */
+ ret = strcmp (gst_structure_get_name (struct1),
+ gst_structure_get_name (struct2));
+ if (ret)
+ return ret;
+
+ return gst_structure_n_fields (struct2) - gst_structure_n_fields (struct1);
+}
+
+typedef struct
+{
+ GQuark name;
+ GValue value;
+ GstStructure *compare;
+}
+UnionField;
+
+static gboolean
+gst_caps_structure_figure_out_union (GQuark field_id, const GValue * value,
+ gpointer user_data)
+{
+ UnionField *u = user_data;
+ const GValue *val = gst_structure_id_get_value (u->compare, field_id);
+
+ if (!val) {
+ if (u->name)
+ g_value_unset (&u->value);
+ return FALSE;
+ }
+ if (gst_value_compare (val, value) == GST_VALUE_EQUAL)
+ return TRUE;
+ if (u->name) {
+ g_value_unset (&u->value);
+ return FALSE;
+ }
+ u->name = field_id;
+ gst_value_union (&u->value, val, value);
+ return TRUE;
+}
+
+static gboolean
+gst_caps_structure_simplify (GstStructure ** result,
+ const GstStructure * simplify, GstStructure * compare)
+{
+ GSList *list;
+ UnionField field = { 0, {0,}, NULL };
+
+ /* try to subtract to get a real subset */
+ if (gst_caps_structure_subtract (&list, simplify, compare)) {
+ if (list == NULL) { /* no result */
+ *result = NULL;
+ return TRUE;
+ } else if (list->next == NULL) { /* one result */
+ *result = list->data;
+ g_slist_free (list);
+ return TRUE;
+ } else { /* multiple results */
+ g_slist_foreach (list, (GFunc) gst_structure_free, NULL);
+ g_slist_free (list);
+ list = NULL;
+ }
+ }
+
+ /* try to union both structs */
+ field.compare = compare;
+ if (gst_structure_foreach ((GstStructure *) simplify,
+ gst_caps_structure_figure_out_union, &field)) {
+ gboolean ret = FALSE;
+
+ /* now we know all of simplify's fields are the same in compare
+ * but at most one field: field.name */
+ if (G_IS_VALUE (&field.value)) {
+ if (gst_structure_n_fields (simplify) == gst_structure_n_fields (compare)) {
+ gst_structure_id_set_value (compare, field.name, &field.value);
+ *result = NULL;
+ ret = TRUE;
+ }
+ g_value_unset (&field.value);
+ } else if (gst_structure_n_fields (simplify) <=
+ gst_structure_n_fields (compare)) {
+ /* compare is just more specific, will be optimized away later */
+ /* FIXME: do this here? */
+ GST_LOG ("found a case that will be optimized later.");
+ } else {
+ gchar *one = gst_structure_to_string (simplify);
+ gchar *two = gst_structure_to_string (compare);
+
+ GST_ERROR
+ ("caps mismatch: structures %s and %s claim to be possible to unify, but aren't",
+ one, two);
+ g_free (one);
+ g_free (two);
+ }
+ return ret;
+ }
+
+ return FALSE;
+}
+
+static void
+gst_caps_switch_structures (GstCaps * caps, GstStructure * old,
+ GstStructure * new, gint i)
+{
+ gst_structure_set_parent_refcount (old, NULL);
+ gst_structure_free (old);
+ gst_structure_set_parent_refcount (new, &GST_CAPS_REFCOUNT (caps));
+ g_ptr_array_index (GST_CAPS_ARRAY (caps), i) = new;
+}
+
+/**
+ * gst_caps_do_simplify:
+ * @caps: a #GstCaps to simplify
+ *
+ * Modifies the given @caps inplace into a representation that represents the
+ * same set of formats, but in a simpler form. Component structures that are
+ * identical are merged. Component structures that have values that can be
+ * merged are also merged.
+ *
+ * Returns: TRUE, if the caps could be simplified
+ */
+gboolean
+gst_caps_do_simplify (GstCaps * caps)
+{
+ GstStructure *simplify, *compare, *result = NULL;
+ gint i, j, start;
+ gboolean changed = FALSE;
+
+ g_return_val_if_fail (caps != NULL, FALSE);
+ g_return_val_if_fail (IS_WRITABLE (caps), FALSE);
+
+ if (gst_caps_get_size (caps) < 2)
+ return FALSE;
+
+ g_ptr_array_sort (GST_CAPS_ARRAY (caps), gst_caps_compare_structures);
+
+ start = GST_CAPS_LEN (caps) - 1;
+ for (i = GST_CAPS_LEN (caps) - 1; i >= 0; i--) {
+ simplify = gst_caps_get_structure_unchecked (caps, i);
+ if (gst_structure_get_name_id (simplify) !=
+ gst_structure_get_name_id (gst_caps_get_structure_unchecked (caps,
+ start)))
+ start = i;
+ for (j = start; j >= 0; j--) {
+ if (j == i)
+ continue;
+ compare = gst_caps_get_structure_unchecked (caps, j);
+ if (gst_structure_get_name_id (simplify) !=
+ gst_structure_get_name_id (compare)) {
+ break;
+ }
+ if (gst_caps_structure_simplify (&result, simplify, compare)) {
+ if (result) {
+ gst_caps_switch_structures (caps, simplify, result, i);
+ simplify = result;
+ } else {
+ gst_caps_remove_structure (caps, i);
+ start--;
+ break;
+ }
+ changed = TRUE;
+ }
+ }
+ }
+
+ if (!changed)
+ return FALSE;
+
+ /* gst_caps_do_simplify (caps); */
+ return TRUE;
+}
+
+/**
+ * gst_caps_fixate:
+ * @caps: a #GstCaps to fixate
+ *
+ * Modifies the given @caps inplace into a representation with only fixed
+ * values. First the caps will be truncated and then the first structure will be
+ * fixated with gst_structure_fixate(). @caps should be writable.
+ */
+void
+gst_caps_fixate (GstCaps * caps)
+{
+ GstStructure *s;
+
+ g_return_if_fail (GST_IS_CAPS (caps));
+ g_return_if_fail (IS_WRITABLE (caps));
+
+ /* default fixation */
+ gst_caps_truncate (caps);
+ s = gst_caps_get_structure (caps, 0);
+ gst_structure_fixate (s);
+}
+
+/* utility */
+
+/**
+ * gst_caps_to_string:
+ * @caps: a #GstCaps
+ *
+ * Converts @caps to a string representation. This string representation
+ * can be converted back to a #GstCaps by gst_caps_from_string().
+ *
+ * For debugging purposes its easier to do something like this:
+ * |[
+ * GST_LOG ("caps are %" GST_PTR_FORMAT, caps);
+ * ]|
+ * This prints the caps in human readble form.
+ *
+ * Returns: (transfer full): a newly allocated string representing @caps.
+ */
+gchar *
+gst_caps_to_string (const GstCaps * caps)
+{
+ guint i, slen, clen;
+ GString *s;
+
+ /* NOTE: This function is potentially called by the debug system,
+ * so any calls to gst_log() (and GST_DEBUG(), GST_LOG(), etc.)
+ * should be careful to avoid recursion. This includes any functions
+ * called by gst_caps_to_string. In particular, calls should
+ * not use the GST_PTR_FORMAT extension. */
+
+ if (caps == NULL) {
+ return g_strdup ("NULL");
+ }
+ if (CAPS_IS_ANY (caps)) {
+ return g_strdup ("ANY");
+ }
+ if (CAPS_IS_EMPTY_SIMPLE (caps)) {
+ return g_strdup ("EMPTY");
+ }
+
+ /* estimate a rough string length to avoid unnecessary reallocs in GString */
+ slen = 0;
+ clen = GST_CAPS_LEN (caps);
+ for (i = 0; i < clen; i++) {
+ slen +=
+ STRUCTURE_ESTIMATED_STRING_LEN (gst_caps_get_structure_unchecked (caps,
+ i));
+ }
+
+ s = g_string_sized_new (slen);
+ for (i = 0; i < clen; i++) {
+ GstStructure *structure;
+
+ if (i > 0) {
+ /* ';' is now added by gst_structure_to_string */
+ g_string_append_c (s, ' ');
+ }
+
+ structure = gst_caps_get_structure_unchecked (caps, i);
+ priv_gst_structure_append_to_gstring (structure, s);
+ }
+ if (s->len && s->str[s->len - 1] == ';') {
+ /* remove latest ';' */
+ s->str[--s->len] = '\0';
+ }
+ return g_string_free (s, FALSE);
+}
+
+static gboolean
+gst_caps_from_string_inplace (GstCaps * caps, const gchar * string)
+{
+ GstStructure *structure;
+ gchar *s;
+
+ if (strcmp ("ANY", string) == 0) {
+ GST_CAPS_FLAGS (caps) = GST_CAPS_FLAGS_ANY;
+ return TRUE;
+ }
+ if (strcmp ("EMPTY", string) == 0) {
+ return TRUE;
+ }
+
+ structure = gst_structure_from_string (string, &s);
+ if (structure == NULL) {
+ return FALSE;
+ }
+ gst_caps_append_structure_unchecked (caps, structure);
+
+ do {
+
+ while (g_ascii_isspace (*s))
+ s++;
+ if (*s == '\0') {
+ break;
+ }
+ structure = gst_structure_from_string (s, &s);
+ if (structure == NULL) {
+ return FALSE;
+ }
+ gst_caps_append_structure_unchecked (caps, structure);
+
+ } while (TRUE);
+
+ return TRUE;
+}
+
+/**
+ * gst_caps_from_string:
+ * @string: a string to convert to #GstCaps
+ *
+ * Converts @caps from a string representation.
+ *
+ * Returns: (transfer full): a newly allocated #GstCaps
+ */
+GstCaps *
+gst_caps_from_string (const gchar * string)
+{
+ GstCaps *caps;
+
+ g_return_val_if_fail (string, FALSE);
+
+ caps = gst_caps_new_empty ();
+ if (gst_caps_from_string_inplace (caps, string)) {
+ return caps;
+ } else {
+ gst_caps_unref (caps);
+ return NULL;
+ }
+}
+
+static void
+gst_caps_transform_to_string (const GValue * src_value, GValue * dest_value)
+{
+ g_return_if_fail (G_IS_VALUE (src_value));
+ g_return_if_fail (G_IS_VALUE (dest_value));
+ g_return_if_fail (G_VALUE_HOLDS (src_value, GST_TYPE_CAPS));
+ g_return_if_fail (G_VALUE_HOLDS (dest_value, G_TYPE_STRING)
+ || G_VALUE_HOLDS (dest_value, G_TYPE_POINTER));
+
+ g_value_take_string (dest_value,
+ gst_caps_to_string (gst_value_get_caps (src_value)));
+}
diff --git a/gst/gstcaps.h b/gst/gstcaps.h
new file mode 100644
index 0000000..567f7b8
--- /dev/null
+++ b/gst/gstcaps.h
@@ -0,0 +1,419 @@
+/* GStreamer
+ * Copyright (C) 2003 David A. Schleef <ds@schleef.org>
+ *
+ * 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_CAPS_H__
+#define __GST_CAPS_H__
+
+#include <gst/gstconfig.h>
+#include <gst/gstminiobject.h>
+#include <gst/gststructure.h>
+#include <gst/glib-compat.h>
+
+G_BEGIN_DECLS
+
+extern GType _gst_caps_type;
+
+#define GST_TYPE_CAPS (_gst_caps_type)
+#define GST_IS_CAPS(obj) (GST_IS_MINI_OBJECT_TYPE((obj), GST_TYPE_CAPS))
+#define GST_CAPS_CAST(obj) ((GstCaps*)(obj))
+#define GST_CAPS(obj) (GST_CAPS_CAST(obj))
+
+#define GST_TYPE_STATIC_CAPS (gst_static_caps_get_type())
+
+/**
+ * GstCapsFlags:
+ * @GST_CAPS_FLAGS_ANY: Caps has no specific content, but can contain
+ * anything.
+ *
+ * Extra flags for a caps.
+ */
+typedef enum {
+ GST_CAPS_FLAGS_ANY = (GST_MINI_OBJECT_FLAG_LAST << 0)
+} GstCapsFlags;
+
+/**
+ * GstCapsIntersectMode:
+ * @GST_CAPS_INTERSECT_ZIG_ZAG : Zig-zags over both caps.
+ * @GST_CAPS_INTERSECT_FIRST : Keeps the first caps order.
+ *
+ * Modes of caps intersection
+ *
+ * @GST_CAPS_INTERSECT_ZIG_ZAG tries to preserve overall order of both caps
+ * by iterating on the caps' structures as the following matrix shows:
+ * |[
+ * caps1
+ * +-------------
+ * | 1 2 4 7
+ * caps2 | 3 5 8 10
+ * | 6 9 11 12
+ * ]|
+ * Used when there is no explicit precedence of one caps over the other. e.g.
+ * tee's sink pad getcaps function, it will probe its src pad peers' for their
+ * caps and intersect them with this mode.
+ *
+ * @GST_CAPS_INTERSECT_FIRST is useful when an element wants to preserve
+ * another element's caps priority order when intersecting with its own caps.
+ * Example: If caps1 is [A, B, C] and caps2 is [E, B, D, A], the result
+ * would be [A, B], maintaining the first caps priority on the intersection.
+ *
+ * Since: 0.10.33
+ */
+typedef enum {
+ GST_CAPS_INTERSECT_ZIG_ZAG = 0,
+ GST_CAPS_INTERSECT_FIRST = 1
+} GstCapsIntersectMode;
+
+/**
+ * GST_CAPS_ANY:
+ *
+ * Means that the element/pad can output 'anything'. Useful for elements
+ * that output unknown media, such as filesrc.
+ */
+#define GST_CAPS_ANY gst_caps_new_any()
+/**
+ * GST_CAPS_NONE:
+ *
+ * The opposite of %GST_CAPS_ANY: it means that the pad/element outputs an
+ * undefined media type that can not be detected.
+ */
+#define GST_CAPS_NONE gst_caps_new_empty()
+
+/**
+ * GST_STATIC_CAPS_ANY:
+ *
+ * Creates a new #GstCaps static caps that matches anything.
+ * This can be used in pad templates.
+ */
+#define GST_STATIC_CAPS_ANY GST_STATIC_CAPS("ANY")
+/**
+ * GST_STATIC_CAPS_NONE:
+ *
+ * Creates a new #GstCaps static caps that matches nothing.
+ * This can be used in pad templates.
+ */
+#define GST_STATIC_CAPS_NONE GST_STATIC_CAPS("NONE")
+
+/**
+ * GST_CAPS_IS_SIMPLE:
+ * @caps: the #GstCaps instance to check
+ *
+ * Convenience macro that checks if the number of structures in the given caps
+ * is exactly one.
+ */
+#define GST_CAPS_IS_SIMPLE(caps) (gst_caps_get_size(caps) == 1)
+
+/**
+ * GST_STATIC_CAPS:
+ * @string: the string describing the caps
+ *
+ * Creates a new #GstCaps static caps from an input string.
+ * This can be used in pad templates.
+ */
+#define GST_STATIC_CAPS(string) \
+{ \
+ /* miniobject */ { { 0, 0, 0, 0, NULL, NULL, NULL }, \
+ /* caps */ NULL }, \
+ /* string */ string, \
+ GST_PADDING_INIT \
+}
+
+typedef struct _GstCaps GstCaps;
+typedef struct _GstStaticCaps GstStaticCaps;
+
+/**
+ * GST_CAPS_FLAGS:
+ * @caps: a #GstCaps.
+ *
+ * A flags word containing #GstCapsFlags flags set on this caps.
+ */
+#define GST_CAPS_FLAGS(caps) GST_MINI_OBJECT_FLAGS(caps)
+
+/* refcount */
+/**
+ * GST_CAPS_REFCOUNT:
+ * @caps: a #GstCaps
+ *
+ * Get access to the reference count field of the caps
+ */
+#define GST_CAPS_REFCOUNT(caps) GST_MINI_OBJECT_REFCOUNT(caps)
+/**
+ * GST_CAPS_REFCOUNT_VALUE:
+ * @caps: a #GstCaps
+ *
+ * Get the reference count value of the caps.
+ */
+#define GST_CAPS_REFCOUNT_VALUE(caps) GST_MINI_OBJECT_REFCOUNT_VALUE(caps)
+
+/**
+ * GST_CAPS_FLAG_IS_SET:
+ * @caps: a #GstCaps.
+ * @flag: the #GstCapsFlags to check.
+ *
+ * Gives the status of a specific flag on a caps.
+ */
+#define GST_CAPS_FLAG_IS_SET(caps,flag) GST_MINI_OBJECT_FLAG_IS_SET (caps, flag)
+/**
+ * GST_CAPS_FLAG_SET:
+ * @caps: a #GstCaps.
+ * @flag: the #GstCapsFlags to set.
+ *
+ * Sets a caps flag on a caps.
+ */
+#define GST_CAPS_FLAG_SET(caps,flag) GST_MINI_OBJECT_FLAG_SET (caps, flag)
+/**
+ * GST_CAPS_FLAG_UNSET:
+ * @caps: a #GstCaps.
+ * @flag: the #GstCapsFlags to clear.
+ *
+ * Clears a caps flag.
+ */
+#define GST_CAPS_FLAG_UNSET(caps,flag) GST_MINI_OBJECT_FLAG_UNSET (caps, flag)
+
+/* refcounting */
+/**
+ * gst_caps_ref:
+ * @caps: the #GstCaps to reference
+ *
+ * Add a reference to a #GstCaps object.
+ *
+ * From this point on, until the caller calls gst_caps_unref() or
+ * gst_caps_make_writable(), it is guaranteed that the caps object will not
+ * change. This means its structures won't change, etc. To use a #GstCaps
+ * object, you must always have a refcount on it -- either the one made
+ * implicitly by e.g. gst_caps_new_simple(), or via taking one explicitly with
+ * this function.
+ *
+ * Returns: the same #GstCaps object.
+ */
+#ifdef _FOOL_GTK_DOC_
+G_INLINE_FUNC GstCaps * gst_caps_ref (GstCaps * caps);
+#endif
+
+static inline GstCaps *
+gst_caps_ref (GstCaps * caps)
+{
+ return (GstCaps *) gst_mini_object_ref (GST_MINI_OBJECT_CAST (caps));
+}
+
+/**
+ * gst_caps_unref:
+ * @caps: a #GstCaps.
+ *
+ * Unref a #GstCaps and and free all its structures and the
+ * structures' values when the refcount reaches 0.
+ */
+#ifdef _FOOL_GTK_DOC_
+G_INLINE_FUNC void gst_caps_unref (GstCaps * caps);
+#endif
+
+static inline void
+gst_caps_unref (GstCaps * caps)
+{
+ gst_mini_object_unref (GST_MINI_OBJECT_CAST (caps));
+}
+
+/* copy caps */
+/**
+ * gst_caps_copy:
+ * @caps: a #GstCaps.
+ *
+ * Creates a new #GstCaps as a copy of the old @caps. The new caps will have a
+ * refcount of 1, owned by the caller. The structures are copied as well.
+ *
+ * Note that this function is the semantic equivalent of a gst_caps_ref()
+ * followed by a gst_caps_make_writable(). If you only want to hold on to a
+ * reference to the data, you should use gst_caps_ref().
+ *
+ * When you are finished with the caps, call gst_caps_unref() on it.
+ *
+ * Returns: the new #GstCaps
+ */
+#ifdef _FOOL_GTK_DOC_
+G_INLINE_FUNC GstCaps * gst_caps_copy (const GstCaps * caps);
+#endif
+
+static inline GstCaps *
+gst_caps_copy (const GstCaps * caps)
+{
+ return GST_CAPS (gst_mini_object_copy (GST_MINI_OBJECT_CAST (caps)));
+}
+
+/**
+ * gst_caps_is_writable:
+ * @caps: a #GstCaps
+ *
+ * Tests if you can safely modify @caps. It is only safe to modify caps when
+ * there is only one owner of the caps - ie, the refcount is 1.
+ */
+#define gst_caps_is_writable(caps) gst_mini_object_is_writable (GST_MINI_OBJECT_CAST (caps))
+
+/**
+ * gst_caps_make_writable:
+ * @caps: (transfer full): a #GstCaps
+ *
+ * Returns a writable copy of @caps.
+ *
+ * If there is only one reference count on @caps, the caller must be the owner,
+ * and so this function will return the caps object unchanged. If on the other
+ * hand there is more than one reference on the object, a new caps object will
+ * be returned. The caller's reference on @caps will be removed, and instead the
+ * caller will own a reference to the returned object.
+ *
+ * In short, this function unrefs the caps in the argument and refs the caps
+ * that it returns. Don't access the argument after calling this function. See
+ * also: gst_caps_ref().
+ *
+ * Returns: (transfer full): a writable caps which may or may not be the
+ * same as @caps
+ */
+#define gst_caps_make_writable(caps) GST_CAPS_CAST (gst_mini_object_make_writable (GST_MINI_OBJECT_CAST (caps)))
+
+/**
+ * gst_caps_replace:
+ * @ocaps: (inout) (transfer full): pointer to a pointer to a #GstCaps to be
+ * replaced.
+ * @ncaps: (transfer none) (allow-none): pointer to a #GstCaps that will
+ * replace the caps pointed to by @ocaps.
+ *
+ * Modifies a pointer to a #GstCaps to point to a different #GstCaps. The
+ * modification is done atomically (so this is useful for ensuring thread safety
+ * in some cases), and the reference counts are updated appropriately (the old
+ * caps is unreffed, the new is reffed).
+ *
+ * Either @ncaps or the #GstCaps pointed to by @ocaps may be NULL.
+ */
+#define gst_caps_replace(ocaps,ncaps) \
+G_STMT_START { \
+ GstCaps **___ocapsaddr = (GstCaps **)(ocaps); \
+ gst_mini_object_replace ((GstMiniObject **)___ocapsaddr, \
+ GST_MINI_OBJECT_CAST (ncaps)); \
+} G_STMT_END
+
+/**
+ * GstCaps:
+ * @mini_object: the parent type
+ *
+ * Object describing media types.
+ */
+struct _GstCaps {
+ GstMiniObject mini_object;
+
+ /*< private >*/
+ gpointer priv;
+};
+
+/**
+ * GstStaticCaps:
+ * @caps: the cached #GstCaps
+ * @string: a string describing a caps
+ *
+ * Datastructure to initialize #GstCaps from a string description usually
+ * used in conjunction with GST_STATIC_CAPS() and gst_static_caps_get() to
+ * instantiate a #GstCaps.
+ */
+struct _GstStaticCaps {
+ /*< public >*/
+ GstCaps caps;
+ const char *string;
+
+ /*< private >*/
+ gpointer _gst_reserved[GST_PADDING];
+};
+
+GType gst_caps_get_type (void);
+
+GstCaps * gst_caps_new_empty (void);
+GstCaps * gst_caps_new_any (void);
+GstCaps * gst_caps_new_simple (const char *media_type,
+ const char *fieldname,
+ ...);
+GstCaps * gst_caps_new_full (GstStructure *struct1, ...);
+GstCaps * gst_caps_new_full_valist (GstStructure *structure,
+ va_list var_args);
+
+GType gst_static_caps_get_type (void);
+GstCaps * gst_static_caps_get (GstStaticCaps *static_caps);
+void gst_static_caps_cleanup (GstStaticCaps *static_caps);
+
+/* manipulation */
+void gst_caps_append (GstCaps *caps1,
+ GstCaps *caps2);
+void gst_caps_merge (GstCaps *caps1,
+ GstCaps *caps2);
+void gst_caps_append_structure (GstCaps *caps,
+ GstStructure *structure);
+void gst_caps_remove_structure (GstCaps *caps, guint idx);
+void gst_caps_merge_structure (GstCaps *caps,
+ GstStructure *structure);
+guint gst_caps_get_size (const GstCaps *caps);
+GstStructure * gst_caps_get_structure (const GstCaps *caps,
+ guint index);
+GstStructure * gst_caps_steal_structure (GstCaps *caps,
+ guint index);
+GstCaps * gst_caps_copy_nth (const GstCaps *caps, guint nth);
+void gst_caps_truncate (GstCaps *caps);
+void gst_caps_set_value (GstCaps *caps,
+ const char *field,
+ const GValue *value);
+void gst_caps_set_simple (GstCaps *caps,
+ const char *field, ...) G_GNUC_NULL_TERMINATED;
+void gst_caps_set_simple_valist (GstCaps *caps,
+ const char *field,
+ va_list varargs);
+
+/* tests */
+gboolean gst_caps_is_any (const GstCaps *caps);
+gboolean gst_caps_is_empty (const GstCaps *caps);
+gboolean gst_caps_is_fixed (const GstCaps *caps);
+gboolean gst_caps_is_always_compatible (const GstCaps *caps1,
+ const GstCaps *caps2);
+gboolean gst_caps_is_subset (const GstCaps *subset,
+ const GstCaps *superset);
+gboolean gst_caps_is_subset_structure (const GstCaps *caps,
+ const GstStructure *structure);
+gboolean gst_caps_is_equal (const GstCaps *caps1,
+ const GstCaps *caps2);
+gboolean gst_caps_is_equal_fixed (const GstCaps *caps1,
+ const GstCaps *caps2);
+gboolean gst_caps_can_intersect (const GstCaps * caps1,
+ const GstCaps * caps2);
+
+
+/* operations */
+GstCaps * gst_caps_intersect (const GstCaps *caps1,
+ const GstCaps *caps2);
+GstCaps * gst_caps_intersect_full (const GstCaps *caps1,
+ const GstCaps *caps2,
+ GstCapsIntersectMode mode);
+GstCaps * gst_caps_subtract (const GstCaps *minuend,
+ const GstCaps *subtrahend);
+GstCaps * gst_caps_union (const GstCaps *caps1,
+ const GstCaps *caps2);
+GstCaps * gst_caps_normalize (const GstCaps *caps);
+gboolean gst_caps_do_simplify (GstCaps *caps);
+
+void gst_caps_fixate (GstCaps *caps);
+
+/* utility */
+gchar * gst_caps_to_string (const GstCaps *caps);
+GstCaps * gst_caps_from_string (const gchar *string);
+
+G_END_DECLS
+
+#endif /* __GST_CAPS_H__ */
diff --git a/gst/gstchildproxy.c b/gst/gstchildproxy.c
new file mode 100644
index 0000000..5607b49
--- /dev/null
+++ b/gst/gstchildproxy.c
@@ -0,0 +1,538 @@
+/* GStreamer
+ * Copyright (C) 2005 Stefan Kost <ensonic@users.sf.net>
+ *
+ * gstchildproxy.c: interface for multi child elements
+ *
+ * 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:gstchildproxy
+ * @short_description: Interface for multi child elements.
+ * @see_also: #GstBin
+ *
+ * This interface abstracts handling of property sets for elements with
+ * children. Imagine elements such as mixers or polyphonic generators. They all
+ * have multiple #GstPad or some kind of voice objects. Another use case are
+ * container elements like #GstBin.
+ * The element implementing the interface acts as a parent for those child
+ * objects.
+ *
+ * By implementing this interface the child properties can be accessed from the
+ * parent element by using gst_child_proxy_get() and gst_child_proxy_set().
+ *
+ * Property names are written as "child-name::property-name". The whole naming
+ * scheme is recursive. Thus "child1::child2::property" is valid too, if
+ * "child1" and "child2" implement the #GstChildProxy interface.
+ */
+/* FIXME-0.11:
+ * it would be nice to make gst_child_proxy_get_child_by_name virtual too and
+ * use GObject instead of GstObject. We could eventually provide the current
+ * implementation as a default if children are GstObjects.
+ * This change would allow to propose the interface for inclusion with
+ * glib/gobject. IMHO this is useful for GtkContainer and compound widgets too.
+ */
+
+#include "gst_private.h"
+
+#include "gstchildproxy.h"
+#include "gstmarshal.h"
+#include <gobject/gvaluecollector.h>
+
+/* signals */
+enum
+{
+ CHILD_ADDED,
+ CHILD_REMOVED,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+/**
+ * gst_child_proxy_get_child_by_name:
+ * @parent: the parent object to get the child from
+ * @name: the childs name
+ *
+ * Looks up a child element by the given name.
+ *
+ * Implementors can use #GstObject together with gst_object_get_name()
+ *
+ * Returns: (transfer full): the child object or %NULL if not found. Unref
+ * after usage.
+ *
+ * MT safe.
+ */
+GstObject *
+gst_child_proxy_get_child_by_name (GstChildProxy * parent, const gchar * name)
+{
+ guint count, i;
+ GstObject *object, *result;
+ gchar *object_name;
+
+ g_return_val_if_fail (GST_IS_CHILD_PROXY (parent), NULL);
+ g_return_val_if_fail (name != NULL, NULL);
+
+ result = NULL;
+
+ count = gst_child_proxy_get_children_count (parent);
+ for (i = 0; i < count; i++) {
+ gboolean eq;
+
+ if (!(object = gst_child_proxy_get_child_by_index (parent, i)))
+ continue;
+
+ object_name = gst_object_get_name (object);
+ if (object_name == NULL) {
+ g_warning ("child %u of parent %s has no name", i,
+ GST_OBJECT_NAME (parent));
+ goto next;
+ }
+ eq = g_str_equal (object_name, name);
+ g_free (object_name);
+
+ if (eq) {
+ result = object;
+ break;
+ }
+ next:
+ gst_object_unref (object);
+ }
+ return result;
+}
+
+/**
+ * gst_child_proxy_get_child_by_index:
+ * @parent: the parent object to get the child from
+ * @index: the childs position in the child list
+ *
+ * Fetches a child by its number.
+ *
+ * Returns: (transfer full): the child object or %NULL if not found (index
+ * too high). Unref after usage.
+ *
+ * MT safe.
+ */
+GstObject *
+gst_child_proxy_get_child_by_index (GstChildProxy * parent, guint index)
+{
+ g_return_val_if_fail (GST_IS_CHILD_PROXY (parent), NULL);
+
+ return (GST_CHILD_PROXY_GET_INTERFACE (parent)->get_child_by_index (parent,
+ index));
+}
+
+/**
+ * gst_child_proxy_get_children_count:
+ * @parent: the parent object
+ *
+ * Gets the number of child objects this parent contains.
+ *
+ * Returns: the number of child objects
+ *
+ * MT safe.
+ */
+guint
+gst_child_proxy_get_children_count (GstChildProxy * parent)
+{
+ g_return_val_if_fail (GST_IS_CHILD_PROXY (parent), 0);
+
+ return (GST_CHILD_PROXY_GET_INTERFACE (parent)->get_children_count (parent));
+}
+
+/**
+ * gst_child_proxy_lookup:
+ * @object: object to lookup the property in
+ * @name: name of the property to look up
+ * @target: (out) (allow-none) (transfer full): pointer to a #GstObject that
+ * takes the real object to set property on
+ * @pspec: (out) (allow-none) (transfer full): pointer to take the #GParamSpec
+ * describing the property
+ *
+ * Looks up which object and #GParamSpec would be effected by the given @name.
+ *
+ * Returns: TRUE if @target and @pspec could be found. FALSE otherwise. In that
+ * case the values for @pspec and @target are not modified. Unref @target after
+ * usage.
+ *
+ * MT safe.
+ */
+gboolean
+gst_child_proxy_lookup (GstObject * object, const gchar * name,
+ GstObject ** target, GParamSpec ** pspec)
+{
+ gboolean res = FALSE;
+ gchar **names, **current;
+
+ g_return_val_if_fail (GST_IS_OBJECT (object), FALSE);
+ g_return_val_if_fail (name != NULL, FALSE);
+
+ gst_object_ref (object);
+
+ current = names = g_strsplit (name, "::", -1);
+ while (current[1]) {
+ GstObject *next;
+
+ if (!GST_IS_CHILD_PROXY (object)) {
+ GST_INFO
+ ("object %s is not a parent, so you cannot request a child by name %s",
+ GST_OBJECT_NAME (object), current[0]);
+ break;
+ }
+ next = gst_child_proxy_get_child_by_name (GST_CHILD_PROXY (object),
+ current[0]);
+ if (!next) {
+ GST_INFO ("no such object %s", current[0]);
+ break;
+ }
+ gst_object_unref (object);
+ object = next;
+ current++;
+ }
+ if (current[1] == NULL) {
+ GParamSpec *spec =
+ g_object_class_find_property (G_OBJECT_GET_CLASS (object), current[0]);
+ if (spec == NULL) {
+ GST_INFO ("no param spec named %s", current[0]);
+ } else {
+ if (pspec)
+ *pspec = spec;
+ if (target) {
+ gst_object_ref (object);
+ *target = object;
+ }
+ res = TRUE;
+ }
+ }
+ gst_object_unref (object);
+ g_strfreev (names);
+ return res;
+}
+
+/**
+ * gst_child_proxy_get_property:
+ * @object: object to query
+ * @name: name of the property
+ * @value: (out caller-allocates): a #GValue that should take the result.
+ *
+ * Gets a single property using the GstChildProxy mechanism.
+ * You are responsible for freeing it by calling g_value_unset()
+ */
+void
+gst_child_proxy_get_property (GstObject * object, const gchar * name,
+ GValue * value)
+{
+ GParamSpec *pspec;
+ GstObject *target;
+
+ g_return_if_fail (GST_IS_OBJECT (object));
+ g_return_if_fail (name != NULL);
+ g_return_if_fail (G_IS_VALUE (value));
+
+ if (!gst_child_proxy_lookup (object, name, &target, &pspec))
+ goto not_found;
+
+ g_object_get_property (G_OBJECT (target), pspec->name, value);
+ gst_object_unref (target);
+
+ return;
+
+not_found:
+ {
+ g_warning ("no property %s in object %s", name, GST_OBJECT_NAME (object));
+ return;
+ }
+}
+
+/**
+ * gst_child_proxy_get_valist:
+ * @object: the object to query
+ * @first_property_name: name of the first property to get
+ * @var_args: return location for the first property, followed optionally by more name/return location pairs, followed by NULL
+ *
+ * Gets properties of the parent object and its children.
+ */
+void
+gst_child_proxy_get_valist (GstObject * object,
+ const gchar * first_property_name, va_list var_args)
+{
+ const gchar *name;
+ gchar *error = NULL;
+ GValue value = { 0, };
+ GParamSpec *pspec;
+ GstObject *target;
+
+ g_return_if_fail (G_IS_OBJECT (object));
+
+ name = first_property_name;
+
+ /* iterate over pairs */
+ while (name) {
+ if (!gst_child_proxy_lookup (object, name, &target, &pspec))
+ goto not_found;
+
+ g_value_init (&value, pspec->value_type);
+ g_object_get_property (G_OBJECT (target), pspec->name, &value);
+ gst_object_unref (target);
+
+ G_VALUE_LCOPY (&value, var_args, 0, &error);
+ if (error)
+ goto cant_copy;
+ g_value_unset (&value);
+ name = va_arg (var_args, gchar *);
+ }
+ return;
+
+not_found:
+ {
+ g_warning ("no property %s in object %s", name, GST_OBJECT_NAME (object));
+ return;
+ }
+cant_copy:
+ {
+ g_warning ("error copying value %s in object %s: %s", pspec->name,
+ GST_OBJECT_NAME (object), error);
+ g_value_unset (&value);
+ return;
+ }
+}
+
+/**
+ * gst_child_proxy_get:
+ * @object: the parent object
+ * @first_property_name: name of the first property to get
+ * @...: return location for the first property, followed optionally by more name/return location pairs, followed by NULL
+ *
+ * Gets properties of the parent object and its children.
+ */
+void
+gst_child_proxy_get (GstObject * object, const gchar * first_property_name, ...)
+{
+ va_list var_args;
+
+ g_return_if_fail (GST_IS_OBJECT (object));
+
+ va_start (var_args, first_property_name);
+ gst_child_proxy_get_valist (object, first_property_name, var_args);
+ va_end (var_args);
+}
+
+/**
+ * gst_child_proxy_set_property:
+ * @object: the parent object
+ * @name: name of the property to set
+ * @value: new #GValue for the property
+ *
+ * Sets a single property using the GstChildProxy mechanism.
+ */
+void
+gst_child_proxy_set_property (GstObject * object, const gchar * name,
+ const GValue * value)
+{
+ GParamSpec *pspec;
+ GstObject *target;
+
+ g_return_if_fail (GST_IS_OBJECT (object));
+ g_return_if_fail (name != NULL);
+ g_return_if_fail (G_IS_VALUE (value));
+
+ if (!gst_child_proxy_lookup (object, name, &target, &pspec))
+ goto not_found;
+
+ g_object_set_property (G_OBJECT (target), pspec->name, value);
+ gst_object_unref (target);
+ return;
+
+not_found:
+ {
+ g_warning ("cannot set property %s on object %s", name,
+ GST_OBJECT_NAME (object));
+ return;
+ }
+}
+
+/**
+ * gst_child_proxy_set_valist:
+ * @object: the parent object
+ * @first_property_name: name of the first property to set
+ * @var_args: value for the first property, followed optionally by more name/value pairs, followed by NULL
+ *
+ * Sets properties of the parent object and its children.
+ */
+void
+gst_child_proxy_set_valist (GstObject * object,
+ const gchar * first_property_name, va_list var_args)
+{
+ const gchar *name;
+ gchar *error = NULL;
+ GValue value = { 0, };
+ GParamSpec *pspec;
+ GstObject *target;
+
+ g_return_if_fail (G_IS_OBJECT (object));
+
+ name = first_property_name;
+
+ /* iterate over pairs */
+ while (name) {
+ if (!gst_child_proxy_lookup (object, name, &target, &pspec))
+ goto not_found;
+
+ G_VALUE_COLLECT_INIT (&value, pspec->value_type, var_args,
+ G_VALUE_NOCOPY_CONTENTS, &error);
+
+ if (error)
+ goto cant_copy;
+
+ g_object_set_property (G_OBJECT (target), pspec->name, &value);
+ gst_object_unref (target);
+
+ g_value_unset (&value);
+ name = va_arg (var_args, gchar *);
+ }
+ return;
+
+not_found:
+ {
+ g_warning ("no property %s in object %s", name, GST_OBJECT_NAME (object));
+ return;
+ }
+cant_copy:
+ {
+ g_warning ("error copying value %s in object %s: %s", pspec->name,
+ GST_OBJECT_NAME (object), error);
+ g_value_unset (&value);
+ gst_object_unref (target);
+ return;
+ }
+}
+
+/**
+ * gst_child_proxy_set:
+ * @object: the parent object
+ * @first_property_name: name of the first property to set
+ * @...: value for the first property, followed optionally by more name/value pairs, followed by NULL
+ *
+ * Sets properties of the parent object and its children.
+ */
+void
+gst_child_proxy_set (GstObject * object, const gchar * first_property_name, ...)
+{
+ va_list var_args;
+
+ g_return_if_fail (GST_IS_OBJECT (object));
+
+ va_start (var_args, first_property_name);
+ gst_child_proxy_set_valist (object, first_property_name, var_args);
+ va_end (var_args);
+}
+
+/**
+ * gst_child_proxy_child_added:
+ * @object: the parent object
+ * @child: the newly added child
+ *
+ * Emits the "child-added" signal.
+ */
+void
+gst_child_proxy_child_added (GstObject * object, GstObject * child)
+{
+ g_signal_emit (G_OBJECT (object), signals[CHILD_ADDED], 0, child);
+}
+
+/**
+ * gst_child_proxy_child_removed:
+ * @object: the parent object
+ * @child: the removed child
+ *
+ * Emits the "child-removed" signal.
+ */
+void
+gst_child_proxy_child_removed (GstObject * object, GstObject * child)
+{
+ g_signal_emit (G_OBJECT (object), signals[CHILD_REMOVED], 0, child);
+}
+
+/* gobject methods */
+
+static void
+gst_child_proxy_base_init (gpointer g_class)
+{
+ static gboolean initialized = FALSE;
+
+ if (!initialized) {
+ /* create interface signals and properties here. */
+ /**
+ * GstChildProxy::child-added:
+ * @child_proxy: the #GstChildProxy
+ * @object: the #GObject that was added
+ *
+ * Will be emitted after the @object was added to the @child_proxy.
+ */
+ /* FIXME 0.11: use GST_TYPE_OBJECT as GstChildProxy only
+ * supports GstObjects */
+ signals[CHILD_ADDED] =
+ g_signal_new ("child-added", G_TYPE_FROM_CLASS (g_class),
+ G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GstChildProxyInterface,
+ child_added), NULL, NULL, gst_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
+ G_TYPE_OBJECT);
+
+ /**
+ * GstChildProxy::child-removed:
+ * @child_proxy: the #GstChildProxy
+ * @object: the #GObject that was removed
+ *
+ * Will be emitted after the @object was removed from the @child_proxy.
+ */
+ /* FIXME 0.11: use GST_TYPE_OBJECT as GstChildProxy only
+ * supports GstObjects */
+ signals[CHILD_REMOVED] =
+ g_signal_new ("child-removed", G_TYPE_FROM_CLASS (g_class),
+ G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GstChildProxyInterface,
+ child_removed), NULL, NULL, gst_marshal_VOID__OBJECT, G_TYPE_NONE,
+ 1, G_TYPE_OBJECT);
+
+ initialized = TRUE;
+ }
+}
+
+GType
+gst_child_proxy_get_type (void)
+{
+ static volatile gsize type = 0;
+
+ if (g_once_init_enter (&type)) {
+ GType _type;
+ static const GTypeInfo info = {
+ sizeof (GstChildProxyInterface),
+ gst_child_proxy_base_init, /* base_init */
+ NULL, /* base_finalize */
+ NULL, /* class_init */
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ 0,
+ 0, /* n_preallocs */
+ NULL /* instance_init */
+ };
+
+ _type =
+ g_type_register_static (G_TYPE_INTERFACE, "GstChildProxy", &info, 0);
+
+ g_type_interface_add_prerequisite (_type, GST_TYPE_OBJECT);
+ g_once_init_leave (&type, (gsize) _type);
+ }
+ return type;
+}
diff --git a/gst/gstchildproxy.h b/gst/gstchildproxy.h
new file mode 100644
index 0000000..ebb2d14
--- /dev/null
+++ b/gst/gstchildproxy.h
@@ -0,0 +1,91 @@
+/* GStreamer
+ * Copyright (C) 2005 Stefan Kost <ensonic@users.sf.net>
+ *
+ * gstchildproxy.h: interface header for multi child elements
+ *
+ * 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_CHILD_PROXY_H__
+#define __GST_CHILD_PROXY_H__
+
+#include <glib-object.h>
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+
+
+#define GST_TYPE_CHILD_PROXY (gst_child_proxy_get_type ())
+#define GST_CHILD_PROXY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_CHILD_PROXY, GstChildProxy))
+#define GST_IS_CHILD_PROXY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_CHILD_PROXY))
+#define GST_CHILD_PROXY_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GST_TYPE_CHILD_PROXY, GstChildProxyInterface))
+
+/**
+ * GstChildProxy:
+ *
+ * Opaque #GstChildProxy data structure.
+ */
+typedef struct _GstChildProxy GstChildProxy; /* dummy object */
+typedef struct _GstChildProxyInterface GstChildProxyInterface;
+
+/**
+ * GstChildProxyInterface:
+ * @parent: parent interface type.
+ * @get_child_by_index: virtual method to fetch the child
+ * @get_children_count: virtual method to get the children count
+ *
+ * #GstChildProxy interface.
+ */
+struct _GstChildProxyInterface
+{
+ GTypeInterface parent;
+
+ /* methods */
+ GstObject *(*get_child_by_index) (GstChildProxy * parent, guint index);
+ guint (*get_children_count) (GstChildProxy * parent);
+ /*< private >*/
+ /* signals */
+ void (*child_added) (GstChildProxy * parent, GstObject * child);
+ void (*child_removed) (GstChildProxy * parent, GstObject * child);
+
+ /*< private >*/
+ gpointer _gst_reserved[GST_PADDING];
+};
+
+GType gst_child_proxy_get_type (void);
+
+GstObject *gst_child_proxy_get_child_by_name (GstChildProxy * parent, const gchar * name);
+GstObject *gst_child_proxy_get_child_by_index (GstChildProxy * parent, guint index);
+guint gst_child_proxy_get_children_count (GstChildProxy * parent);
+
+gboolean gst_child_proxy_lookup (GstObject *object, const gchar *name,
+ GstObject **target, GParamSpec **pspec);
+void gst_child_proxy_get_property (GstObject * object, const gchar *name, GValue *value);
+void gst_child_proxy_get_valist (GstObject * object,
+ const gchar * first_property_name, va_list var_args);
+void gst_child_proxy_get (GstObject * object, const gchar * first_property_name,
+ ...) G_GNUC_NULL_TERMINATED;
+void gst_child_proxy_set_property (GstObject * object, const gchar *name, const GValue *value);
+void gst_child_proxy_set_valist (GstObject* object,
+ const gchar * first_property_name, va_list var_args);
+void gst_child_proxy_set (GstObject * object, const gchar * first_property_name,
+ ...) G_GNUC_NULL_TERMINATED;
+void gst_child_proxy_child_added (GstObject * object, GstObject * child);
+void gst_child_proxy_child_removed (GstObject * object, GstObject * child);
+
+G_END_DECLS
+
+#endif /* __GST_CHILD_PROXY_H__ */
diff --git a/gst/gstclock.c b/gst/gstclock.c
new file mode 100644
index 0000000..b167238
--- /dev/null
+++ b/gst/gstclock.c
@@ -0,0 +1,1488 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2000 Wim Taymans <wtay@chello.be>
+ * 2004 Wim Taymans <wim@fluendo.com>
+ *
+ * gstclock.c: Clock subsystem for maintaining time sync
+ *
+ * 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:gstclock
+ * @short_description: Abstract class for global clocks
+ * @see_also: #GstSystemClock, #GstPipeline
+ *
+ * GStreamer uses a global clock to synchronize the plugins in a pipeline.
+ * Different clock implementations are possible by implementing this abstract
+ * base class or, more conveniently, by subclassing #GstSystemClock.
+ *
+ * The #GstClock returns a monotonically increasing time with the method
+ * gst_clock_get_time(). Its accuracy and base time depend on the specific
+ * clock implementation but time is always expressed in nanoseconds. Since the
+ * baseline of the clock is undefined, the clock time returned is not
+ * meaningful in itself, what matters are the deltas between two clock times.
+ * The time returned by a clock is called the absolute time.
+ *
+ * The pipeline uses the clock to calculate the running time. Usually all
+ * renderers synchronize to the global clock using the buffer timestamps, the
+ * newsegment events and the element's base time, see #GstPipeline.
+ *
+ * A clock implementation can support periodic and single shot clock
+ * notifications both synchronous and asynchronous.
+ *
+ * One first needs to create a #GstClockID for the periodic or single shot
+ * notification using gst_clock_new_single_shot_id() or
+ * gst_clock_new_periodic_id().
+ *
+ * To perform a blocking wait for the specific time of the #GstClockID use the
+ * gst_clock_id_wait(). To receive a callback when the specific time is reached
+ * in the clock use gst_clock_id_wait_async(). Both these calls can be
+ * interrupted with the gst_clock_id_unschedule() call. If the blocking wait is
+ * unscheduled a return value of #GST_CLOCK_UNSCHEDULED is returned.
+ *
+ * Periodic callbacks scheduled async will be repeatedly called automatically
+ * until it is unscheduled. To schedule a sync periodic callback,
+ * gst_clock_id_wait() should be called repeatedly.
+ *
+ * The async callbacks can happen from any thread, either provided by the core
+ * or from a streaming thread. The application should be prepared for this.
+ *
+ * A #GstClockID that has been unscheduled cannot be used again for any wait
+ * operation, a new #GstClockID should be created and the old unscheduled one
+ * should be destroyed with gst_clock_id_unref().
+ *
+ * It is possible to perform a blocking wait on the same #GstClockID from
+ * multiple threads. However, registering the same #GstClockID for multiple
+ * async notifications is not possible, the callback will only be called for
+ * the thread registering the entry last.
+ *
+ * None of the wait operations unref the #GstClockID, the owner is responsible
+ * for unreffing the ids itself. This holds for both periodic and single shot
+ * notifications. The reason being that the owner of the #GstClockID has to
+ * keep a handle to the #GstClockID to unblock the wait on FLUSHING events or
+ * state changes and if the entry would be unreffed automatically, the handle
+ * might become invalid without any notification.
+ *
+ * These clock operations do not operate on the running time, so the callbacks
+ * will also occur when not in PLAYING state as if the clock just keeps on
+ * running. Some clocks however do not progress when the element that provided
+ * the clock is not PLAYING.
+ *
+ * When a clock has the #GST_CLOCK_FLAG_CAN_SET_MASTER flag set, it can be
+ * slaved to another #GstClock with the gst_clock_set_master(). The clock will
+ * then automatically be synchronized to this master clock by repeatedly
+ * sampling the master clock and the slave clock and recalibrating the slave
+ * clock with gst_clock_set_calibration(). This feature is mostly useful for
+ * plugins that have an internal clock but must operate with another clock
+ * selected by the #GstPipeline. They can track the offset and rate difference
+ * of their internal clock relative to the master clock by using the
+ * gst_clock_get_calibration() function.
+ *
+ * The master/slave synchronisation can be tuned with the #GstClock:timeout,
+ * #GstClock:window-size and #GstClock:window-threshold properties.
+ * The #GstClock:timeout property defines the interval to sample the master
+ * clock and run the calibration functions. #GstClock:window-size defines the
+ * number of samples to use when calibrating and #GstClock:window-threshold
+ * defines the minimum number of samples before the calibration is performed.
+ *
+ * Last reviewed on 2009-05-21 (0.10.24)
+ */
+
+
+#include "gst_private.h"
+#include <time.h>
+
+#include "gstclock.h"
+#include "gstinfo.h"
+#include "gstutils.h"
+
+#ifndef GST_DISABLE_TRACE
+/* #define GST_WITH_ALLOC_TRACE */
+#include "gsttrace.h"
+static GstAllocTrace *_gst_clock_entry_trace;
+#endif
+
+/* #define DEBUGGING_ENABLED */
+
+#define DEFAULT_STATS FALSE
+#define DEFAULT_WINDOW_SIZE 32
+#define DEFAULT_WINDOW_THRESHOLD 4
+#define DEFAULT_TIMEOUT GST_SECOND / 10
+
+enum
+{
+ PROP_0,
+ PROP_STATS,
+ PROP_WINDOW_SIZE,
+ PROP_WINDOW_THRESHOLD,
+ PROP_TIMEOUT
+};
+
+struct _GstClockPrivate
+{
+ gint pre_count;
+ gint post_count;
+};
+
+/* seqlocks */
+#define read_seqbegin(clock) \
+ g_atomic_int_get (&clock->ABI.priv->post_count);
+
+static inline gboolean
+read_seqretry (GstClock * clock, gint seq)
+{
+ /* no retry if the seqnum did not change */
+ if (G_LIKELY (seq == g_atomic_int_get (&clock->ABI.priv->pre_count)))
+ return FALSE;
+
+ /* wait for the writer to finish and retry */
+ GST_OBJECT_LOCK (clock);
+ GST_OBJECT_UNLOCK (clock);
+ return TRUE;
+}
+
+#define write_seqlock(clock) \
+G_STMT_START { \
+ GST_OBJECT_LOCK (clock); \
+ g_atomic_int_inc (&clock->ABI.priv->pre_count); \
+} G_STMT_END;
+
+#define write_sequnlock(clock) \
+G_STMT_START { \
+ g_atomic_int_inc (&clock->ABI.priv->post_count); \
+ GST_OBJECT_UNLOCK (clock); \
+} G_STMT_END;
+
+static void gst_clock_dispose (GObject * object);
+static void gst_clock_finalize (GObject * object);
+
+static void gst_clock_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+static void gst_clock_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
+static void gst_clock_update_stats (GstClock * clock);
+
+
+static GstObjectClass *parent_class = NULL;
+
+/* static guint gst_clock_signals[LAST_SIGNAL] = { 0 }; */
+
+static GstClockID
+gst_clock_entry_new (GstClock * clock, GstClockTime time,
+ GstClockTime interval, GstClockEntryType type)
+{
+ GstClockEntry *entry;
+
+ entry = g_slice_new (GstClockEntry);
+#ifndef GST_DISABLE_TRACE
+ gst_alloc_trace_new (_gst_clock_entry_trace, entry);
+#endif
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
+ "created entry %p, time %" GST_TIME_FORMAT, entry, GST_TIME_ARGS (time));
+
+ entry->refcount = 1;
+ entry->clock = clock;
+ entry->type = type;
+ entry->time = time;
+ entry->interval = interval;
+ entry->status = GST_CLOCK_OK;
+ entry->func = NULL;
+ entry->user_data = NULL;
+ entry->destroy_data = NULL;
+ entry->unscheduled = FALSE;
+ entry->woken_up = FALSE;
+
+ return (GstClockID) entry;
+}
+
+/* WARNING : Does not modify the refcount
+ * WARNING : Do not use if a pending clock operation is happening on that entry */
+static gboolean
+gst_clock_entry_reinit (GstClock * clock, GstClockEntry * entry,
+ GstClockTime time, GstClockTime interval, GstClockEntryType type)
+{
+ g_return_val_if_fail (entry->status != GST_CLOCK_BUSY, FALSE);
+ g_return_val_if_fail (entry->clock == clock, FALSE);
+
+ entry->type = type;
+ entry->time = time;
+ entry->interval = interval;
+ entry->status = GST_CLOCK_OK;
+ entry->unscheduled = FALSE;
+ entry->woken_up = FALSE;
+
+ return TRUE;
+}
+
+/**
+ * gst_clock_single_shot_id_reinit:
+ * @clock: a #GstClock
+ * @id: a #GstClockID
+ * @time: The requested time.
+ *
+ * Reinitializes the provided single shot @id to the provided time. Does not
+ * modify the reference count.
+ *
+ * Returns: %TRUE if the GstClockID could be reinitialized to the provided
+ * @time, else %FALSE.
+ *
+ * Since: 0.10.32
+ */
+gboolean
+gst_clock_single_shot_id_reinit (GstClock * clock, GstClockID id,
+ GstClockTime time)
+{
+ return gst_clock_entry_reinit (clock, (GstClockEntry *) id, time,
+ GST_CLOCK_TIME_NONE, GST_CLOCK_ENTRY_SINGLE);
+}
+
+/**
+ * gst_clock_periodic_id_reinit:
+ * @clock: a #GstClock
+ * @id: a #GstClockID
+ * @start_time: the requested start time
+ * @interval: the requested interval
+ *
+ * Reinitializes the provided periodic @id to the provided start time and
+ * interval. Does not modify the reference count.
+ *
+ * Returns: %TRUE if the GstClockID could be reinitialized to the provided
+ * @time, else %FALSE.
+ *
+ * Since: 0.10.33
+ *
+ */
+gboolean
+gst_clock_periodic_id_reinit (GstClock * clock, GstClockID id,
+ GstClockTime start_time, GstClockTime interval)
+{
+ return gst_clock_entry_reinit (clock, (GstClockEntry *) id, start_time,
+ interval, GST_CLOCK_ENTRY_PERIODIC);
+}
+
+/**
+ * gst_clock_id_ref:
+ * @id: The #GstClockID to ref
+ *
+ * Increase the refcount of given @id.
+ *
+ * Returns: (transfer full): The same #GstClockID with increased refcount.
+ *
+ * MT safe.
+ */
+GstClockID
+gst_clock_id_ref (GstClockID id)
+{
+ g_return_val_if_fail (id != NULL, NULL);
+
+ g_atomic_int_inc (&((GstClockEntry *) id)->refcount);
+
+ return id;
+}
+
+static void
+_gst_clock_id_free (GstClockID id)
+{
+ GstClockEntry *entry;
+ g_return_if_fail (id != NULL);
+
+ GST_CAT_DEBUG (GST_CAT_CLOCK, "freed entry %p", id);
+ entry = (GstClockEntry *) id;
+ if (entry->destroy_data)
+ entry->destroy_data (entry->user_data);
+
+#ifndef GST_DISABLE_TRACE
+ gst_alloc_trace_free (_gst_clock_entry_trace, id);
+#endif
+ g_slice_free (GstClockEntry, id);
+}
+
+/**
+ * gst_clock_id_unref:
+ * @id: (transfer full): The #GstClockID to unref
+ *
+ * Unref given @id. When the refcount reaches 0 the
+ * #GstClockID will be freed.
+ *
+ * MT safe.
+ */
+void
+gst_clock_id_unref (GstClockID id)
+{
+ gint zero;
+
+ g_return_if_fail (id != NULL);
+
+ zero = g_atomic_int_dec_and_test (&((GstClockEntry *) id)->refcount);
+ /* if we ended up with the refcount at zero, free the id */
+ if (zero) {
+ _gst_clock_id_free (id);
+ }
+}
+
+/**
+ * gst_clock_new_single_shot_id:
+ * @clock: The #GstClockID to get a single shot notification from
+ * @time: the requested time
+ *
+ * Get a #GstClockID from @clock to trigger a single shot
+ * notification at the requested time. The single shot id should be
+ * unreffed after usage.
+ *
+ * Free-function: gst_clock_id_unref
+ *
+ * Returns: (transfer full): a #GstClockID that can be used to request the
+ * time notification.
+ *
+ * MT safe.
+ */
+GstClockID
+gst_clock_new_single_shot_id (GstClock * clock, GstClockTime time)
+{
+ g_return_val_if_fail (GST_IS_CLOCK (clock), NULL);
+
+ return gst_clock_entry_new (clock,
+ time, GST_CLOCK_TIME_NONE, GST_CLOCK_ENTRY_SINGLE);
+}
+
+/**
+ * gst_clock_new_periodic_id:
+ * @clock: The #GstClockID to get a periodic notification id from
+ * @start_time: the requested start time
+ * @interval: the requested interval
+ *
+ * Get an ID from @clock to trigger a periodic notification.
+ * The periodic notifications will start at time @start_time and
+ * will then be fired with the given @interval. @id should be unreffed
+ * after usage.
+ *
+ * Free-function: gst_clock_id_unref
+ *
+ * Returns: (transfer full): a #GstClockID that can be used to request the
+ * time notification.
+ *
+ * MT safe.
+ */
+GstClockID
+gst_clock_new_periodic_id (GstClock * clock, GstClockTime start_time,
+ GstClockTime interval)
+{
+ g_return_val_if_fail (GST_IS_CLOCK (clock), NULL);
+ g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (start_time), NULL);
+ g_return_val_if_fail (interval != 0, NULL);
+ g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (interval), NULL);
+
+ return gst_clock_entry_new (clock,
+ start_time, interval, GST_CLOCK_ENTRY_PERIODIC);
+}
+
+/**
+ * gst_clock_id_compare_func
+ * @id1: A #GstClockID
+ * @id2: A #GstClockID to compare with
+ *
+ * Compares the two #GstClockID instances. This function can be used
+ * as a GCompareFunc when sorting ids.
+ *
+ * Returns: negative value if a < b; zero if a = b; positive value if a > b
+ *
+ * MT safe.
+ */
+gint
+gst_clock_id_compare_func (gconstpointer id1, gconstpointer id2)
+{
+ GstClockEntry *entry1, *entry2;
+
+ entry1 = (GstClockEntry *) id1;
+ entry2 = (GstClockEntry *) id2;
+
+ if (GST_CLOCK_ENTRY_TIME (entry1) > GST_CLOCK_ENTRY_TIME (entry2)) {
+ return 1;
+ }
+ if (GST_CLOCK_ENTRY_TIME (entry1) < GST_CLOCK_ENTRY_TIME (entry2)) {
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * gst_clock_id_get_time
+ * @id: The #GstClockID to query
+ *
+ * Get the time of the clock ID
+ *
+ * Returns: the time of the given clock id.
+ *
+ * MT safe.
+ */
+GstClockTime
+gst_clock_id_get_time (GstClockID id)
+{
+ g_return_val_if_fail (id != NULL, GST_CLOCK_TIME_NONE);
+
+ return GST_CLOCK_ENTRY_TIME ((GstClockEntry *) id);
+}
+
+/**
+ * gst_clock_id_wait
+ * @id: The #GstClockID to wait on
+ * @jitter: (out) (allow-none): a pointer that will contain the jitter,
+ * can be %NULL.
+ *
+ * Perform a blocking wait on @id.
+ * @id should have been created with gst_clock_new_single_shot_id()
+ * or gst_clock_new_periodic_id() and should not have been unscheduled
+ * with a call to gst_clock_id_unschedule().
+ *
+ * If the @jitter argument is not %NULL and this function returns #GST_CLOCK_OK
+ * or #GST_CLOCK_EARLY, it will contain the difference
+ * against the clock and the time of @id when this method was
+ * called.
+ * Positive values indicate how late @id was relative to the clock
+ * (in which case this function will return #GST_CLOCK_EARLY).
+ * Negative values indicate how much time was spent waiting on the clock
+ * before this function returned.
+ *
+ * Returns: the result of the blocking wait. #GST_CLOCK_EARLY will be returned
+ * if the current clock time is past the time of @id, #GST_CLOCK_OK if
+ * @id was scheduled in time. #GST_CLOCK_UNSCHEDULED if @id was
+ * unscheduled with gst_clock_id_unschedule().
+ *
+ * MT safe.
+ */
+GstClockReturn
+gst_clock_id_wait (GstClockID id, GstClockTimeDiff * jitter)
+{
+ GstClockEntry *entry;
+ GstClock *clock;
+ GstClockReturn res;
+ GstClockTime requested;
+ GstClockClass *cclass;
+
+ g_return_val_if_fail (id != NULL, GST_CLOCK_ERROR);
+
+ entry = (GstClockEntry *) id;
+ requested = GST_CLOCK_ENTRY_TIME (entry);
+
+ clock = GST_CLOCK_ENTRY_CLOCK (entry);
+
+ /* can't sync on invalid times */
+ if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (requested)))
+ goto invalid_time;
+
+ cclass = GST_CLOCK_GET_CLASS (clock);
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "waiting on clock entry %p", id);
+
+ /* if we have a wait_jitter function, use that */
+ if (G_UNLIKELY (cclass->wait == NULL))
+ goto not_supported;
+
+ res = cclass->wait (clock, entry, jitter);
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
+ "done waiting entry %p, res: %d", id, res);
+
+ if (entry->type == GST_CLOCK_ENTRY_PERIODIC)
+ entry->time = requested + entry->interval;
+
+ if (G_UNLIKELY (clock->stats))
+ gst_clock_update_stats (clock);
+
+ return res;
+
+ /* ERRORS */
+invalid_time:
+ {
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
+ "invalid time requested, returning _BADTIME");
+ return GST_CLOCK_BADTIME;
+ }
+not_supported:
+ {
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "clock wait is not supported");
+ return GST_CLOCK_UNSUPPORTED;
+ }
+}
+
+/**
+ * gst_clock_id_wait_async_full:
+ * @id: a #GstClockID to wait on
+ * @func: The callback function
+ * @user_data: User data passed in the callback
+ * @destroy_data: #GDestroyNotify for user_data
+ *
+ * Register a callback on the given #GstClockID @id with the given
+ * function and user_data. When passing a #GstClockID with an invalid
+ * time to this function, the callback will be called immediately
+ * with a time set to GST_CLOCK_TIME_NONE. The callback will
+ * be called when the time of @id has been reached.
+ *
+ * The callback @func can be invoked from any thread, either provided by the
+ * core or from a streaming thread. The application should be prepared for this.
+ *
+ * Returns: the result of the non blocking wait.
+ *
+ * MT safe.
+ *
+ * Since: 0.10.30
+ */
+GstClockReturn
+gst_clock_id_wait_async_full (GstClockID id,
+ GstClockCallback func, gpointer user_data, GDestroyNotify destroy_data)
+{
+ GstClockEntry *entry;
+ GstClock *clock;
+ GstClockReturn res;
+ GstClockClass *cclass;
+ GstClockTime requested;
+
+ g_return_val_if_fail (id != NULL, GST_CLOCK_ERROR);
+ g_return_val_if_fail (func != NULL, GST_CLOCK_ERROR);
+
+ entry = (GstClockEntry *) id;
+ requested = GST_CLOCK_ENTRY_TIME (entry);
+ clock = GST_CLOCK_ENTRY_CLOCK (entry);
+
+ /* can't sync on invalid times */
+ if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (requested)))
+ goto invalid_time;
+
+ cclass = GST_CLOCK_GET_CLASS (clock);
+
+ if (G_UNLIKELY (cclass->wait_async == NULL))
+ goto not_supported;
+
+ entry->func = func;
+ entry->user_data = user_data;
+ entry->destroy_data = destroy_data;
+
+ res = cclass->wait_async (clock, entry);
+
+ return res;
+
+ /* ERRORS */
+invalid_time:
+ {
+ (func) (clock, GST_CLOCK_TIME_NONE, id, user_data);
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
+ "invalid time requested, returning _BADTIME");
+ return GST_CLOCK_BADTIME;
+ }
+not_supported:
+ {
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "clock wait is not supported");
+ return GST_CLOCK_UNSUPPORTED;
+ }
+}
+
+/**
+ * gst_clock_id_wait_async:
+ * @id: a #GstClockID to wait on
+ * @func: The callback function
+ * @user_data: User data passed in the callback
+ *
+ * Register a callback on the given #GstClockID @id with the given
+ * function and user_data. When passing a #GstClockID with an invalid
+ * time to this function, the callback will be called immediately
+ * with a time set to GST_CLOCK_TIME_NONE. The callback will
+ * be called when the time of @id has been reached.
+ *
+ * The callback @func can be invoked from any thread, either provided by the
+ * core or from a streaming thread. The application should be prepared for this.
+ *
+ * Returns: the result of the non blocking wait.
+ *
+ * MT safe.
+ */
+GstClockReturn
+gst_clock_id_wait_async (GstClockID id,
+ GstClockCallback func, gpointer user_data)
+{
+ return gst_clock_id_wait_async_full (id, func, user_data, NULL);
+}
+
+/**
+ * gst_clock_id_unschedule:
+ * @id: The id to unschedule
+ *
+ * Cancel an outstanding request with @id. This can either
+ * be an outstanding async notification or a pending sync notification.
+ * After this call, @id cannot be used anymore to receive sync or
+ * async notifications, you need to create a new #GstClockID.
+ *
+ * MT safe.
+ */
+void
+gst_clock_id_unschedule (GstClockID id)
+{
+ GstClockEntry *entry;
+ GstClock *clock;
+ GstClockClass *cclass;
+
+ g_return_if_fail (id != NULL);
+
+ entry = (GstClockEntry *) id;
+ clock = entry->clock;
+
+ cclass = GST_CLOCK_GET_CLASS (clock);
+
+ if (G_LIKELY (cclass->unschedule))
+ cclass->unschedule (clock, entry);
+}
+
+
+/*
+ * GstClock abstract base class implementation
+ */
+G_DEFINE_TYPE (GstClock, gst_clock, GST_TYPE_OBJECT);
+
+static void
+gst_clock_class_init (GstClockClass * klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ parent_class = g_type_class_peek_parent (klass);
+
+#ifndef GST_DISABLE_TRACE
+ _gst_clock_entry_trace =
+ gst_alloc_trace_register (GST_CLOCK_ENTRY_TRACE_NAME);
+#endif
+
+ gobject_class->dispose = gst_clock_dispose;
+ gobject_class->finalize = gst_clock_finalize;
+ gobject_class->set_property = gst_clock_set_property;
+ gobject_class->get_property = gst_clock_get_property;
+
+ g_object_class_install_property (gobject_class, PROP_STATS,
+ g_param_spec_boolean ("stats", "Stats",
+ "Enable clock stats (unimplemented)", DEFAULT_STATS,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (gobject_class, PROP_WINDOW_SIZE,
+ g_param_spec_int ("window-size", "Window size",
+ "The size of the window used to calculate rate and offset", 2, 1024,
+ DEFAULT_WINDOW_SIZE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (gobject_class, PROP_WINDOW_THRESHOLD,
+ g_param_spec_int ("window-threshold", "Window threshold",
+ "The threshold to start calculating rate and offset", 2, 1024,
+ DEFAULT_WINDOW_THRESHOLD,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (gobject_class, PROP_TIMEOUT,
+ g_param_spec_uint64 ("timeout", "Timeout",
+ "The amount of time, in nanoseconds, to sample master and slave clocks",
+ 0, G_MAXUINT64, DEFAULT_TIMEOUT,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_type_class_add_private (klass, sizeof (GstClockPrivate));
+}
+
+static void
+gst_clock_init (GstClock * clock)
+{
+ clock->last_time = 0;
+ clock->entries = NULL;
+ clock->entries_changed = g_cond_new ();
+ clock->stats = FALSE;
+
+ clock->ABI.priv =
+ G_TYPE_INSTANCE_GET_PRIVATE (clock, GST_TYPE_CLOCK, GstClockPrivate);
+
+ clock->internal_calibration = 0;
+ clock->external_calibration = 0;
+ clock->rate_numerator = 1;
+ clock->rate_denominator = 1;
+
+ clock->slave_lock = g_mutex_new ();
+ clock->window_size = DEFAULT_WINDOW_SIZE;
+ clock->window_threshold = DEFAULT_WINDOW_THRESHOLD;
+ clock->filling = TRUE;
+ clock->time_index = 0;
+ clock->timeout = DEFAULT_TIMEOUT;
+ clock->times = g_new0 (GstClockTime, 4 * clock->window_size);
+}
+
+static void
+gst_clock_dispose (GObject * object)
+{
+ GstClock *clock = GST_CLOCK (object);
+ GstClock **master_p;
+
+ GST_OBJECT_LOCK (clock);
+ master_p = &clock->master;
+ gst_object_replace ((GstObject **) master_p, NULL);
+ GST_OBJECT_UNLOCK (clock);
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+gst_clock_finalize (GObject * object)
+{
+ GstClock *clock = GST_CLOCK (object);
+
+ GST_CLOCK_SLAVE_LOCK (clock);
+ if (clock->clockid) {
+ gst_clock_id_unschedule (clock->clockid);
+ gst_clock_id_unref (clock->clockid);
+ clock->clockid = NULL;
+ }
+ g_free (clock->times);
+ clock->times = NULL;
+ GST_CLOCK_SLAVE_UNLOCK (clock);
+
+ g_cond_free (clock->entries_changed);
+ g_mutex_free (clock->slave_lock);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+/**
+ * gst_clock_set_resolution
+ * @clock: a #GstClock
+ * @resolution: The resolution to set
+ *
+ * Set the accuracy of the clock. Some clocks have the possibility to operate
+ * with different accuracy at the expense of more resource usage. There is
+ * normally no need to change the default resolution of a clock. The resolution
+ * of a clock can only be changed if the clock has the
+ * #GST_CLOCK_FLAG_CAN_SET_RESOLUTION flag set.
+ *
+ * Returns: the new resolution of the clock.
+ */
+GstClockTime
+gst_clock_set_resolution (GstClock * clock, GstClockTime resolution)
+{
+ GstClockClass *cclass;
+
+ g_return_val_if_fail (GST_IS_CLOCK (clock), 0);
+ g_return_val_if_fail (resolution != 0, 0);
+
+ cclass = GST_CLOCK_GET_CLASS (clock);
+
+ if (cclass->change_resolution)
+ clock->resolution =
+ cclass->change_resolution (clock, clock->resolution, resolution);
+
+ return clock->resolution;
+}
+
+/**
+ * gst_clock_get_resolution
+ * @clock: a #GstClock
+ *
+ * Get the accuracy of the clock. The accuracy of the clock is the granularity
+ * of the values returned by gst_clock_get_time().
+ *
+ * Returns: the resolution of the clock in units of #GstClockTime.
+ *
+ * MT safe.
+ */
+GstClockTime
+gst_clock_get_resolution (GstClock * clock)
+{
+ GstClockClass *cclass;
+
+ g_return_val_if_fail (GST_IS_CLOCK (clock), 0);
+
+ cclass = GST_CLOCK_GET_CLASS (clock);
+
+ if (cclass->get_resolution)
+ return cclass->get_resolution (clock);
+
+ return 1;
+}
+
+/**
+ * gst_clock_adjust_unlocked
+ * @clock: a #GstClock to use
+ * @internal: a clock time
+ *
+ * Converts the given @internal clock time to the external time, adjusting for the
+ * rate and reference time set with gst_clock_set_calibration() and making sure
+ * that the returned time is increasing. This function should be called with the
+ * clock's OBJECT_LOCK held and is mainly used by clock subclasses.
+ *
+ * This function is the reverse of gst_clock_unadjust_unlocked().
+ *
+ * Returns: the converted time of the clock.
+ */
+GstClockTime
+gst_clock_adjust_unlocked (GstClock * clock, GstClockTime internal)
+{
+ GstClockTime ret, cinternal, cexternal, cnum, cdenom;
+
+ /* get calibration values for readability */
+ cinternal = clock->internal_calibration;
+ cexternal = clock->external_calibration;
+ cnum = clock->rate_numerator;
+ cdenom = clock->rate_denominator;
+
+ /* avoid divide by 0 */
+ if (G_UNLIKELY (cdenom == 0))
+ cnum = cdenom = 1;
+
+ /* The formula is (internal - cinternal) * cnum / cdenom + cexternal
+ *
+ * Since we do math on unsigned 64-bit ints we have to special case for
+ * internal < cinternal to get the sign right. this case is not very common,
+ * though.
+ */
+ if (G_LIKELY (internal >= cinternal)) {
+ ret = internal - cinternal;
+ ret = gst_util_uint64_scale (ret, cnum, cdenom);
+ ret += cexternal;
+ } else {
+ ret = cinternal - internal;
+ ret = gst_util_uint64_scale (ret, cnum, cdenom);
+ /* clamp to 0 */
+ if (G_LIKELY (cexternal > ret))
+ ret = cexternal - ret;
+ else
+ ret = 0;
+ }
+
+ /* make sure the time is increasing */
+ clock->last_time = MAX (ret, clock->last_time);
+
+ return clock->last_time;
+}
+
+/**
+ * gst_clock_unadjust_unlocked
+ * @clock: a #GstClock to use
+ * @external: an external clock time
+ *
+ * Converts the given @external clock time to the internal time of @clock,
+ * using the rate and reference time set with gst_clock_set_calibration().
+ * This function should be called with the clock's OBJECT_LOCK held and
+ * is mainly used by clock subclasses.
+ *
+ * This function is the reverse of gst_clock_adjust_unlocked().
+ *
+ * Returns: the internal time of the clock corresponding to @external.
+ *
+ * Since: 0.10.13
+ */
+GstClockTime
+gst_clock_unadjust_unlocked (GstClock * clock, GstClockTime external)
+{
+ GstClockTime ret, cinternal, cexternal, cnum, cdenom;
+
+ /* get calibration values for readability */
+ cinternal = clock->internal_calibration;
+ cexternal = clock->external_calibration;
+ cnum = clock->rate_numerator;
+ cdenom = clock->rate_denominator;
+
+ /* avoid divide by 0 */
+ if (G_UNLIKELY (cnum == 0))
+ cnum = cdenom = 1;
+
+ /* The formula is (external - cexternal) * cdenom / cnum + cinternal */
+ if (G_LIKELY (external >= cexternal)) {
+ ret = external - cexternal;
+ ret = gst_util_uint64_scale (ret, cdenom, cnum);
+ ret += cinternal;
+ } else {
+ ret = cexternal - external;
+ ret = gst_util_uint64_scale (ret, cdenom, cnum);
+ if (G_LIKELY (cinternal > ret))
+ ret = cinternal - ret;
+ else
+ ret = 0;
+ }
+ return ret;
+}
+
+/**
+ * gst_clock_get_internal_time
+ * @clock: a #GstClock to query
+ *
+ * Gets the current internal time of the given clock. The time is returned
+ * unadjusted for the offset and the rate.
+ *
+ * Returns: the internal time of the clock. Or GST_CLOCK_TIME_NONE when
+ * given invalid input.
+ *
+ * MT safe.
+ */
+GstClockTime
+gst_clock_get_internal_time (GstClock * clock)
+{
+ GstClockTime ret;
+ GstClockClass *cclass;
+
+ g_return_val_if_fail (GST_IS_CLOCK (clock), GST_CLOCK_TIME_NONE);
+
+ cclass = GST_CLOCK_GET_CLASS (clock);
+
+ if (G_UNLIKELY (cclass->get_internal_time == NULL))
+ goto not_supported;
+
+ ret = cclass->get_internal_time (clock);
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "internal time %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (ret));
+
+ return ret;
+
+ /* ERRORS */
+not_supported:
+ {
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
+ "internal time not supported, return 0");
+ return G_GINT64_CONSTANT (0);
+ }
+}
+
+/**
+ * gst_clock_get_time
+ * @clock: a #GstClock to query
+ *
+ * Gets the current time of the given clock. The time is always
+ * monotonically increasing and adjusted according to the current
+ * offset and rate.
+ *
+ * Returns: the time of the clock. Or GST_CLOCK_TIME_NONE when
+ * given invalid input.
+ *
+ * MT safe.
+ */
+GstClockTime
+gst_clock_get_time (GstClock * clock)
+{
+ GstClockTime ret;
+ gint seq;
+
+ g_return_val_if_fail (GST_IS_CLOCK (clock), GST_CLOCK_TIME_NONE);
+
+ do {
+ /* reget the internal time when we retry to get the most current
+ * timevalue */
+ ret = gst_clock_get_internal_time (clock);
+
+ seq = read_seqbegin (clock);
+ /* this will scale for rate and offset */
+ ret = gst_clock_adjust_unlocked (clock, ret);
+ } while (read_seqretry (clock, seq));
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "adjusted time %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (ret));
+
+ return ret;
+}
+
+/**
+ * gst_clock_set_calibration
+ * @clock: a #GstClock to calibrate
+ * @internal: a reference internal time
+ * @external: a reference external time
+ * @rate_num: the numerator of the rate of the clock relative to its
+ * internal time
+ * @rate_denom: the denominator of the rate of the clock
+ *
+ * Adjusts the rate and time of @clock. A rate of 1/1 is the normal speed of
+ * the clock. Values bigger than 1/1 make the clock go faster.
+ *
+ * @internal and @external are calibration parameters that arrange that
+ * gst_clock_get_time() should have been @external at internal time @internal.
+ * This internal time should not be in the future; that is, it should be less
+ * than the value of gst_clock_get_internal_time() when this function is called.
+ *
+ * Subsequent calls to gst_clock_get_time() will return clock times computed as
+ * follows:
+ *
+ * <programlisting>
+ * time = (internal_time - internal) * rate_num / rate_denom + external
+ * </programlisting>
+ *
+ * This formula is implemented in gst_clock_adjust_unlocked(). Of course, it
+ * tries to do the integer arithmetic as precisely as possible.
+ *
+ * Note that gst_clock_get_time() always returns increasing values so when you
+ * move the clock backwards, gst_clock_get_time() will report the previous value
+ * until the clock catches up.
+ *
+ * MT safe.
+ */
+void
+gst_clock_set_calibration (GstClock * clock, GstClockTime internal, GstClockTime
+ external, GstClockTime rate_num, GstClockTime rate_denom)
+{
+ g_return_if_fail (GST_IS_CLOCK (clock));
+ g_return_if_fail (rate_num != GST_CLOCK_TIME_NONE);
+ g_return_if_fail (rate_denom > 0 && rate_denom != GST_CLOCK_TIME_NONE);
+
+ write_seqlock (clock);
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
+ "internal %" GST_TIME_FORMAT " external %" GST_TIME_FORMAT " %"
+ G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT " = %f", GST_TIME_ARGS (internal),
+ GST_TIME_ARGS (external), rate_num, rate_denom,
+ gst_guint64_to_gdouble (rate_num) / gst_guint64_to_gdouble (rate_denom));
+
+ clock->internal_calibration = internal;
+ clock->external_calibration = external;
+ clock->rate_numerator = rate_num;
+ clock->rate_denominator = rate_denom;
+ write_sequnlock (clock);
+}
+
+/**
+ * gst_clock_get_calibration
+ * @clock: a #GstClock
+ * @internal: (out) (allow-none): a location to store the internal time
+ * @external: (out) (allow-none): a location to store the external time
+ * @rate_num: (out) (allow-none): a location to store the rate numerator
+ * @rate_denom: (out) (allow-none): a location to store the rate denominator
+ *
+ * Gets the internal rate and reference time of @clock. See
+ * gst_clock_set_calibration() for more information.
+ *
+ * @internal, @external, @rate_num, and @rate_denom can be left %NULL if the
+ * caller is not interested in the values.
+ *
+ * MT safe.
+ */
+void
+gst_clock_get_calibration (GstClock * clock, GstClockTime * internal,
+ GstClockTime * external, GstClockTime * rate_num, GstClockTime * rate_denom)
+{
+ gint seq;
+
+ g_return_if_fail (GST_IS_CLOCK (clock));
+
+ do {
+ seq = read_seqbegin (clock);
+ if (rate_num)
+ *rate_num = clock->rate_numerator;
+ if (rate_denom)
+ *rate_denom = clock->rate_denominator;
+ if (external)
+ *external = clock->external_calibration;
+ if (internal)
+ *internal = clock->internal_calibration;
+ } while (read_seqretry (clock, seq));
+}
+
+/* will be called repeatedly to sample the master and slave clock
+ * to recalibrate the clock */
+static gboolean
+gst_clock_slave_callback (GstClock * master, GstClockTime time,
+ GstClockID id, GstClock * clock)
+{
+ GstClockTime stime, mtime;
+ gdouble r_squared;
+
+ stime = gst_clock_get_internal_time (clock);
+ mtime = gst_clock_get_time (master);
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
+ "master %" GST_TIME_FORMAT ", slave %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (mtime), GST_TIME_ARGS (stime));
+
+ gst_clock_add_observation (clock, stime, mtime, &r_squared);
+
+ /* FIXME, we can use the r_squared value to adjust the timeout
+ * value of the clockid */
+
+ return TRUE;
+}
+
+/**
+ * gst_clock_set_master
+ * @clock: a #GstClock
+ * @master: (allow-none): a master #GstClock
+ *
+ * Set @master as the master clock for @clock. @clock will be automatically
+ * calibrated so that gst_clock_get_time() reports the same time as the
+ * master clock.
+ *
+ * A clock provider that slaves its clock to a master can get the current
+ * calibration values with gst_clock_get_calibration().
+ *
+ * @master can be %NULL in which case @clock will not be slaved anymore. It will
+ * however keep reporting its time adjusted with the last configured rate
+ * and time offsets.
+ *
+ * Returns: %TRUE if the clock is capable of being slaved to a master clock.
+ * Trying to set a master on a clock without the
+ * #GST_CLOCK_FLAG_CAN_SET_MASTER flag will make this function return %FALSE.
+ *
+ * MT safe.
+ */
+gboolean
+gst_clock_set_master (GstClock * clock, GstClock * master)
+{
+ GstClock **master_p;
+
+ g_return_val_if_fail (GST_IS_CLOCK (clock), FALSE);
+ g_return_val_if_fail (master != clock, FALSE);
+
+ GST_OBJECT_LOCK (clock);
+ /* we always allow setting the master to NULL */
+ if (master && !GST_OBJECT_FLAG_IS_SET (clock, GST_CLOCK_FLAG_CAN_SET_MASTER))
+ goto not_supported;
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
+ "slaving %p to master clock %p", clock, master);
+ GST_OBJECT_UNLOCK (clock);
+
+ GST_CLOCK_SLAVE_LOCK (clock);
+ if (clock->clockid) {
+ gst_clock_id_unschedule (clock->clockid);
+ gst_clock_id_unref (clock->clockid);
+ clock->clockid = NULL;
+ }
+ if (master) {
+ clock->filling = TRUE;
+ clock->time_index = 0;
+ /* use the master periodic id to schedule sampling and
+ * clock calibration. */
+ clock->clockid = gst_clock_new_periodic_id (master,
+ gst_clock_get_time (master), clock->timeout);
+ gst_clock_id_wait_async_full (clock->clockid,
+ (GstClockCallback) gst_clock_slave_callback,
+ gst_object_ref (clock), (GDestroyNotify) gst_object_unref);
+ }
+ GST_CLOCK_SLAVE_UNLOCK (clock);
+
+ GST_OBJECT_LOCK (clock);
+ master_p = &clock->master;
+ gst_object_replace ((GstObject **) master_p, (GstObject *) master);
+ GST_OBJECT_UNLOCK (clock);
+
+ return TRUE;
+
+ /* ERRORS */
+not_supported:
+ {
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
+ "cannot be slaved to a master clock");
+ GST_OBJECT_UNLOCK (clock);
+ return FALSE;
+ }
+}
+
+/**
+ * gst_clock_get_master:
+ * @clock: a #GstClock
+ *
+ * Get the master clock that @clock is slaved to or %NULL when the clock is
+ * not slaved to any master clock.
+ *
+ * Returns: (transfer full): a master #GstClock or %NULL when this clock is
+ * not slaved to a master clock. Unref after usage.
+ *
+ * MT safe.
+ */
+GstClock *
+gst_clock_get_master (GstClock * clock)
+{
+ GstClock *result = NULL;
+
+ g_return_val_if_fail (GST_IS_CLOCK (clock), NULL);
+
+ GST_OBJECT_LOCK (clock);
+ if (clock->master)
+ result = gst_object_ref (clock->master);
+ GST_OBJECT_UNLOCK (clock);
+
+ return result;
+}
+
+/* http://mathworld.wolfram.com/LeastSquaresFitting.html
+ * with SLAVE_LOCK
+ */
+static gboolean
+do_linear_regression (GstClock * clock, GstClockTime * m_num,
+ GstClockTime * m_denom, GstClockTime * b, GstClockTime * xbase,
+ gdouble * r_squared)
+{
+ GstClockTime *newx, *newy;
+ GstClockTime xmin, ymin, xbar, ybar, xbar4, ybar4;
+ GstClockTimeDiff sxx, sxy, syy;
+ GstClockTime *x, *y;
+ gint i, j;
+ guint n;
+
+ xbar = ybar = sxx = syy = sxy = 0;
+
+ x = clock->times;
+ y = clock->times + 2;
+ n = clock->filling ? clock->time_index : clock->window_size;
+
+#ifdef DEBUGGING_ENABLED
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "doing regression on:");
+ for (i = j = 0; i < n; i++, j += 4)
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
+ " %" G_GUINT64_FORMAT " %" G_GUINT64_FORMAT, x[j], y[j]);
+#endif
+
+ xmin = ymin = G_MAXUINT64;
+ for (i = j = 0; i < n; i++, j += 4) {
+ xmin = MIN (xmin, x[j]);
+ ymin = MIN (ymin, y[j]);
+ }
+
+#ifdef DEBUGGING_ENABLED
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "min x: %" G_GUINT64_FORMAT,
+ xmin);
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "min y: %" G_GUINT64_FORMAT,
+ ymin);
+#endif
+
+ newx = clock->times + 1;
+ newy = clock->times + 3;
+
+ /* strip off unnecessary bits of precision */
+ for (i = j = 0; i < n; i++, j += 4) {
+ newx[j] = x[j] - xmin;
+ newy[j] = y[j] - ymin;
+ }
+
+#ifdef DEBUGGING_ENABLED
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "reduced numbers:");
+ for (i = j = 0; i < n; i++, j += 4)
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
+ " %" G_GUINT64_FORMAT " %" G_GUINT64_FORMAT, newx[j], newy[j]);
+#endif
+
+ /* have to do this precisely otherwise the results are pretty much useless.
+ * should guarantee that none of these accumulators can overflow */
+
+ /* quantities on the order of 1e10 -> 30 bits; window size a max of 2^10, so
+ this addition could end up around 2^40 or so -- ample headroom */
+ for (i = j = 0; i < n; i++, j += 4) {
+ xbar += newx[j];
+ ybar += newy[j];
+ }
+ xbar /= n;
+ ybar /= n;
+
+#ifdef DEBUGGING_ENABLED
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, " xbar = %" G_GUINT64_FORMAT,
+ xbar);
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, " ybar = %" G_GUINT64_FORMAT,
+ ybar);
+#endif
+
+ /* multiplying directly would give quantities on the order of 1e20 -> 60 bits;
+ times the window size that's 70 which is too much. Instead we (1) subtract
+ off the xbar*ybar in the loop instead of after, to avoid accumulation; (2)
+ shift off 4 bits from each multiplicand, giving an expected ceiling of 52
+ bits, which should be enough. Need to check the incoming range and domain
+ to ensure this is an appropriate loss of precision though. */
+ xbar4 = xbar >> 4;
+ ybar4 = ybar >> 4;
+ for (i = j = 0; i < n; i++, j += 4) {
+ GstClockTime newx4, newy4;
+
+ newx4 = newx[j] >> 4;
+ newy4 = newy[j] >> 4;
+
+ sxx += newx4 * newx4 - xbar4 * xbar4;
+ syy += newy4 * newy4 - ybar4 * ybar4;
+ sxy += newx4 * newy4 - xbar4 * ybar4;
+ }
+
+ if (G_UNLIKELY (sxx == 0))
+ goto invalid;
+
+ *m_num = sxy;
+ *m_denom = sxx;
+ *xbase = xmin;
+ *b = (ybar + ymin) - gst_util_uint64_scale (xbar, *m_num, *m_denom);
+ *r_squared = ((double) sxy * (double) sxy) / ((double) sxx * (double) syy);
+
+#ifdef DEBUGGING_ENABLED
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, " m = %g",
+ ((double) *m_num) / *m_denom);
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, " b = %" G_GUINT64_FORMAT,
+ *b);
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, " xbase = %" G_GUINT64_FORMAT,
+ *xbase);
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, " r2 = %g", *r_squared);
+#endif
+
+ return TRUE;
+
+invalid:
+ {
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "sxx == 0, regression failed");
+ return FALSE;
+ }
+}
+
+/**
+ * gst_clock_add_observation
+ * @clock: a #GstClock
+ * @slave: a time on the slave
+ * @master: a time on the master
+ * @r_squared: (out): a pointer to hold the result
+ *
+ * The time @master of the master clock and the time @slave of the slave
+ * clock are added to the list of observations. If enough observations
+ * are available, a linear regression algorithm is run on the
+ * observations and @clock is recalibrated.
+ *
+ * If this functions returns %TRUE, @r_squared will contain the
+ * correlation coefficient of the interpolation. A value of 1.0
+ * means a perfect regression was performed. This value can
+ * be used to control the sampling frequency of the master and slave
+ * clocks.
+ *
+ * Returns: %TRUE if enough observations were added to run the
+ * regression algorithm.
+ *
+ * MT safe.
+ */
+gboolean
+gst_clock_add_observation (GstClock * clock, GstClockTime slave,
+ GstClockTime master, gdouble * r_squared)
+{
+ GstClockTime m_num, m_denom, b, xbase;
+
+ g_return_val_if_fail (GST_IS_CLOCK (clock), FALSE);
+ g_return_val_if_fail (r_squared != NULL, FALSE);
+
+ GST_CLOCK_SLAVE_LOCK (clock);
+
+ GST_CAT_LOG_OBJECT (GST_CAT_CLOCK, clock,
+ "adding observation slave %" GST_TIME_FORMAT ", master %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (slave), GST_TIME_ARGS (master));
+
+ clock->times[(4 * clock->time_index)] = slave;
+ clock->times[(4 * clock->time_index) + 2] = master;
+
+ clock->time_index++;
+ if (G_UNLIKELY (clock->time_index == clock->window_size)) {
+ clock->filling = FALSE;
+ clock->time_index = 0;
+ }
+
+ if (G_UNLIKELY (clock->filling
+ && clock->time_index < clock->window_threshold))
+ goto filling;
+
+ if (!do_linear_regression (clock, &m_num, &m_denom, &b, &xbase, r_squared))
+ goto invalid;
+
+ GST_CLOCK_SLAVE_UNLOCK (clock);
+
+ GST_CAT_LOG_OBJECT (GST_CAT_CLOCK, clock,
+ "adjusting clock to m=%" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT ", b=%"
+ G_GUINT64_FORMAT " (rsquared=%g)", m_num, m_denom, b, *r_squared);
+
+ /* if we have a valid regression, adjust the clock */
+ gst_clock_set_calibration (clock, xbase, b, m_num, m_denom);
+
+ return TRUE;
+
+filling:
+ {
+ GST_CLOCK_SLAVE_UNLOCK (clock);
+ return FALSE;
+ }
+invalid:
+ {
+ /* no valid regression has been done, ignore the result then */
+ GST_CLOCK_SLAVE_UNLOCK (clock);
+ return TRUE;
+ }
+}
+
+static void
+gst_clock_update_stats (GstClock * clock)
+{
+}
+
+static void
+gst_clock_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstClock *clock;
+
+ clock = GST_CLOCK (object);
+
+ switch (prop_id) {
+ case PROP_STATS:
+ GST_OBJECT_LOCK (clock);
+ clock->stats = g_value_get_boolean (value);
+ GST_OBJECT_UNLOCK (clock);
+ break;
+ case PROP_WINDOW_SIZE:
+ GST_CLOCK_SLAVE_LOCK (clock);
+ clock->window_size = g_value_get_int (value);
+ clock->window_threshold =
+ MIN (clock->window_threshold, clock->window_size);
+ clock->times =
+ g_renew (GstClockTime, clock->times, 4 * clock->window_size);
+ /* restart calibration */
+ clock->filling = TRUE;
+ clock->time_index = 0;
+ GST_CLOCK_SLAVE_UNLOCK (clock);
+ break;
+ case PROP_WINDOW_THRESHOLD:
+ GST_CLOCK_SLAVE_LOCK (clock);
+ clock->window_threshold =
+ MIN (g_value_get_int (value), clock->window_size);
+ GST_CLOCK_SLAVE_UNLOCK (clock);
+ break;
+ case PROP_TIMEOUT:
+ GST_CLOCK_SLAVE_LOCK (clock);
+ clock->timeout = g_value_get_uint64 (value);
+ GST_CLOCK_SLAVE_UNLOCK (clock);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_clock_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GstClock *clock;
+
+ clock = GST_CLOCK (object);
+
+ switch (prop_id) {
+ case PROP_STATS:
+ GST_OBJECT_LOCK (clock);
+ g_value_set_boolean (value, clock->stats);
+ GST_OBJECT_UNLOCK (clock);
+ break;
+ case PROP_WINDOW_SIZE:
+ GST_CLOCK_SLAVE_LOCK (clock);
+ g_value_set_int (value, clock->window_size);
+ GST_CLOCK_SLAVE_UNLOCK (clock);
+ break;
+ case PROP_WINDOW_THRESHOLD:
+ GST_CLOCK_SLAVE_LOCK (clock);
+ g_value_set_int (value, clock->window_threshold);
+ GST_CLOCK_SLAVE_UNLOCK (clock);
+ break;
+ case PROP_TIMEOUT:
+ GST_CLOCK_SLAVE_LOCK (clock);
+ g_value_set_uint64 (value, clock->timeout);
+ GST_CLOCK_SLAVE_UNLOCK (clock);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
diff --git a/gst/gstclock.h b/gst/gstclock.h
new file mode 100644
index 0000000..2bc9d80
--- /dev/null
+++ b/gst/gstclock.h
@@ -0,0 +1,574 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2000 Wim Taymans <wtay@chello.be>
+ * 2005 Wim Taymans <wim@fluendo.com>
+ *
+ * gstclock.h: Header for clock subsystem
+ *
+ * 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_CLOCK_H__
+#define __GST_CLOCK_H__
+
+#include <gst/gstobject.h>
+
+G_BEGIN_DECLS
+
+/* --- standard type macros --- */
+#define GST_TYPE_CLOCK (gst_clock_get_type ())
+#define GST_CLOCK(clock) (G_TYPE_CHECK_INSTANCE_CAST ((clock), GST_TYPE_CLOCK, GstClock))
+#define GST_IS_CLOCK(clock) (G_TYPE_CHECK_INSTANCE_TYPE ((clock), GST_TYPE_CLOCK))
+#define GST_CLOCK_CLASS(cclass) (G_TYPE_CHECK_CLASS_CAST ((cclass), GST_TYPE_CLOCK, GstClockClass))
+#define GST_IS_CLOCK_CLASS(cclass) (G_TYPE_CHECK_CLASS_TYPE ((cclass), GST_TYPE_CLOCK))
+#define GST_CLOCK_GET_CLASS(clock) (G_TYPE_INSTANCE_GET_CLASS ((clock), GST_TYPE_CLOCK, GstClockClass))
+#define GST_CLOCK_CAST(clock) ((GstClock*)(clock))
+
+#define GST_CLOCK_SLAVE_LOCK(clock) g_mutex_lock (GST_CLOCK_CAST (clock)->slave_lock)
+#define GST_CLOCK_SLAVE_UNLOCK(clock) g_mutex_unlock (GST_CLOCK_CAST (clock)->slave_lock)
+
+/**
+ * GstClockTime:
+ *
+ * A datatype to hold a time, measured in nanoseconds.
+ */
+typedef guint64 GstClockTime;
+
+/**
+ * GST_TYPE_CLOCK_TIME:
+ *
+ * The #GType of a #GstClockTime.
+ */
+#define GST_TYPE_CLOCK_TIME G_TYPE_UINT64
+
+/**
+ * GstClockTimeDiff:
+ *
+ * A datatype to hold a time difference, measured in nanoseconds.
+ */
+typedef gint64 GstClockTimeDiff;
+/**
+ * GstClockID:
+ *
+ * A datatype to hold the handle to an outstanding sync or async clock callback.
+ */
+typedef gpointer GstClockID;
+
+/**
+ * GST_CLOCK_TIME_NONE:
+ *
+ * Constant to define an undefined clock time.
+ */
+#define GST_CLOCK_TIME_NONE ((GstClockTime) -1)
+/**
+ * GST_CLOCK_TIME_IS_VALID:
+ * @time: clock time to validate
+ *
+ * Tests if a given #GstClockTime represents a valid defined time.
+ */
+#define GST_CLOCK_TIME_IS_VALID(time) (((GstClockTime)(time)) != GST_CLOCK_TIME_NONE)
+
+/**
+ * GST_SECOND:
+ *
+ * Constant that defines one GStreamer second.
+ */
+#define GST_SECOND (G_USEC_PER_SEC * G_GINT64_CONSTANT (1000))
+/**
+ * GST_MSECOND:
+ *
+ * Constant that defines one GStreamer millisecond.
+ */
+#define GST_MSECOND (GST_SECOND / G_GINT64_CONSTANT (1000))
+/**
+ * GST_USECOND:
+ *
+ * Constant that defines one GStreamer microsecond.
+ */
+#define GST_USECOND (GST_SECOND / G_GINT64_CONSTANT (1000000))
+/**
+ * GST_NSECOND:
+ *
+ * Constant that defines one GStreamer nanosecond
+ */
+#define GST_NSECOND (GST_SECOND / G_GINT64_CONSTANT (1000000000))
+
+
+/**
+ * GST_TIME_AS_SECONDS:
+ * @time: the time
+ *
+ * Convert a #GstClockTime to seconds.
+ *
+ * Since: 0.10.16
+ */
+#define GST_TIME_AS_SECONDS(time) ((time) / GST_SECOND)
+/**
+ * GST_TIME_AS_MSECONDS:
+ * @time: the time
+ *
+ * Convert a #GstClockTime to milliseconds (1/1000 of a second).
+ *
+ * Since: 0.10.16
+ */
+#define GST_TIME_AS_MSECONDS(time) ((time) / G_GINT64_CONSTANT (1000000))
+/**
+ * GST_TIME_AS_USECONDS:
+ * @time: the time
+ *
+ * Convert a #GstClockTime to microseconds (1/1000000 of a second).
+ *
+ * Since: 0.10.16
+ */
+#define GST_TIME_AS_USECONDS(time) ((time) / G_GINT64_CONSTANT (1000))
+/**
+ * GST_TIME_AS_NSECONDS:
+ * @time: the time
+ *
+ * Convert a #GstClockTime to nanoseconds (1/1000000000 of a second).
+ *
+ * Since: 0.10.16
+ */
+#define GST_TIME_AS_NSECONDS(time) (time)
+
+/**
+ * GST_CLOCK_DIFF:
+ * @s: the first time
+ * @e: the second time
+ *
+ * Calculate a difference between two clock times as a #GstClockTimeDiff.
+ * The difference is calculated as @e - @s.
+ */
+#define GST_CLOCK_DIFF(s, e) (GstClockTimeDiff)((e) - (s))
+
+/**
+ * GST_TIMEVAL_TO_TIME:
+ * @tv: the timeval to convert
+ *
+ * Convert a #GTimeVal to a #GstClockTime.
+ */
+#define GST_TIMEVAL_TO_TIME(tv) (GstClockTime)((tv).tv_sec * GST_SECOND + (tv).tv_usec * GST_USECOND)
+
+/**
+ * GST_TIME_TO_TIMEVAL:
+ * @t: The #GstClockTime to convert
+ * @tv: The target timeval
+ *
+ * Convert a #GstClockTime to a #GTimeVal
+ *
+ * <note>on 32-bit systems, a timeval has a range of only 2^32 - 1 seconds,
+ * which is about 68 years. Expect trouble if you want to schedule stuff
+ * in your pipeline for 2038.</note>
+ */
+#define GST_TIME_TO_TIMEVAL(t,tv) \
+G_STMT_START { \
+ (tv).tv_sec = ((GstClockTime) (t)) / GST_SECOND; \
+ (tv).tv_usec = (((GstClockTime) (t)) - \
+ ((GstClockTime) (tv).tv_sec) * GST_SECOND) \
+ / GST_USECOND; \
+} G_STMT_END
+
+/**
+ * GST_TIMESPEC_TO_TIME:
+ * @ts: the timespec to convert
+ *
+ * Convert a struct timespec (see man pselect) to a #GstClockTime.
+ */
+#define GST_TIMESPEC_TO_TIME(ts) (GstClockTime)((ts).tv_sec * GST_SECOND + (ts).tv_nsec * GST_NSECOND)
+/**
+ * GST_TIME_TO_TIMESPEC:
+ * @t: The #GstClockTime to convert
+ * @ts: The target timespec
+ *
+ * Convert a #GstClockTime to a struct timespec (see man pselect)
+ */
+#define GST_TIME_TO_TIMESPEC(t,ts) \
+G_STMT_START { \
+ (ts).tv_sec = (t) / GST_SECOND; \
+ (ts).tv_nsec = ((t) - (ts).tv_sec * GST_SECOND) / GST_NSECOND; \
+} G_STMT_END
+
+/* timestamp debugging macros */
+/**
+ * GST_TIME_FORMAT:
+ *
+ * A string that can be used in printf-like format strings to display a
+ * #GstClockTime value in h:m:s format. Use GST_TIME_ARGS() to construct
+ * the matching arguments.
+ *
+ * Example:
+ * |[
+ * printf("%" GST_TIME_FORMAT "\n", GST_TIME_ARGS(ts));
+ * ]|
+ */
+#define GST_TIME_FORMAT "u:%02u:%02u.%09u"
+/**
+ * GST_TIME_ARGS:
+ * @t: a #GstClockTime
+ *
+ * Format @t for the #GST_TIME_FORMAT format string. Note: @t will be
+ * evaluated more than once.
+ */
+#define GST_TIME_ARGS(t) \
+ GST_CLOCK_TIME_IS_VALID (t) ? \
+ (guint) (((GstClockTime)(t)) / (GST_SECOND * 60 * 60)) : 99, \
+ GST_CLOCK_TIME_IS_VALID (t) ? \
+ (guint) ((((GstClockTime)(t)) / (GST_SECOND * 60)) % 60) : 99, \
+ GST_CLOCK_TIME_IS_VALID (t) ? \
+ (guint) ((((GstClockTime)(t)) / GST_SECOND) % 60) : 99, \
+ GST_CLOCK_TIME_IS_VALID (t) ? \
+ (guint) (((GstClockTime)(t)) % GST_SECOND) : 999999999
+
+/**
+ * GST_CLOCK_ENTRY_TRACE_NAME:
+ *
+ * The name used for tracing clock entry allocations.
+ */
+#define GST_CLOCK_ENTRY_TRACE_NAME "GstClockEntry"
+
+typedef struct _GstClockEntry GstClockEntry;
+typedef struct _GstClock GstClock;
+typedef struct _GstClockClass GstClockClass;
+typedef struct _GstClockPrivate GstClockPrivate;
+
+/* --- prototype for async callbacks --- */
+/**
+ * GstClockCallback:
+ * @clock: The clock that triggered the callback
+ * @time: The time it was triggered
+ * @id: The #GstClockID that expired
+ * @user_data: user data passed in the gst_clock_id_wait_async() function
+ *
+ * The function prototype of the callback.
+ *
+ * Returns: %TRUE or %FALSE (currently unused)
+ */
+typedef gboolean (*GstClockCallback) (GstClock *clock, GstClockTime time,
+ GstClockID id, gpointer user_data);
+/**
+ * GstClockReturn:
+ * @GST_CLOCK_OK: The operation succeeded.
+ * @GST_CLOCK_EARLY: The operation was scheduled too late.
+ * @GST_CLOCK_UNSCHEDULED: The clockID was unscheduled
+ * @GST_CLOCK_BUSY: The ClockID is busy
+ * @GST_CLOCK_BADTIME: A bad time was provided to a function.
+ * @GST_CLOCK_ERROR: An error occurred
+ * @GST_CLOCK_UNSUPPORTED: Operation is not supported
+ * @GST_CLOCK_DONE: The ClockID is done waiting (Since: 0.10.32)
+ *
+ * The return value of a clock operation.
+ */
+typedef enum
+{
+ GST_CLOCK_OK = 0,
+ GST_CLOCK_EARLY = 1,
+ GST_CLOCK_UNSCHEDULED = 2,
+ GST_CLOCK_BUSY = 3,
+ GST_CLOCK_BADTIME = 4,
+ GST_CLOCK_ERROR = 5,
+ GST_CLOCK_UNSUPPORTED = 6,
+ GST_CLOCK_DONE = 7
+} GstClockReturn;
+
+/**
+ * GstClockEntryType:
+ * @GST_CLOCK_ENTRY_SINGLE: a single shot timeout
+ * @GST_CLOCK_ENTRY_PERIODIC: a periodic timeout request
+ *
+ * The type of the clock entry
+ */
+typedef enum {
+ GST_CLOCK_ENTRY_SINGLE,
+ GST_CLOCK_ENTRY_PERIODIC
+} GstClockEntryType;
+
+/**
+ * GST_CLOCK_ENTRY:
+ * @entry: the entry to cast
+ *
+ * Cast to a clock entry
+ */
+#define GST_CLOCK_ENTRY(entry) ((GstClockEntry *)(entry))
+/**
+ * GST_CLOCK_ENTRY_CLOCK:
+ * @entry: the entry to query
+ *
+ * Get the owner clock of the entry
+ */
+#define GST_CLOCK_ENTRY_CLOCK(entry) ((entry)->clock)
+/**
+ * GST_CLOCK_ENTRY_TYPE:
+ * @entry: the entry to query
+ *
+ * Get the type of the clock entry
+ */
+#define GST_CLOCK_ENTRY_TYPE(entry) ((entry)->type)
+/**
+ * GST_CLOCK_ENTRY_TIME:
+ * @entry: the entry to query
+ *
+ * Get the requested time of this entry
+ */
+#define GST_CLOCK_ENTRY_TIME(entry) ((entry)->time)
+/**
+ * GST_CLOCK_ENTRY_INTERVAL:
+ * @entry: the entry to query
+ *
+ * Get the interval of this periodic entry
+ */
+#define GST_CLOCK_ENTRY_INTERVAL(entry) ((entry)->interval)
+/**
+ * GST_CLOCK_ENTRY_STATUS:
+ * @entry: the entry to query
+ *
+ * The status of the entry
+ */
+#define GST_CLOCK_ENTRY_STATUS(entry) ((entry)->status)
+
+/**
+ * GstClockEntry:
+ * @refcount: reference counter (read-only)
+ *
+ * All pending timeouts or periodic notifies are converted into
+ * an entry.
+ * Note that GstClockEntry should be treated as an opaque structure. It must
+ * not be extended or allocated using a custom allocator.
+ */
+struct _GstClockEntry {
+ gint refcount;
+ /*< protected >*/
+ GstClock *clock;
+ GstClockEntryType type;
+ GstClockTime time;
+ GstClockTime interval;
+ GstClockReturn status;
+ GstClockCallback func;
+ gpointer user_data;
+ GDestroyNotify destroy_data;
+ gboolean unscheduled;
+ gboolean woken_up;
+};
+
+/**
+ * GstClockFlags:
+ * @GST_CLOCK_FLAG_CAN_DO_SINGLE_SYNC: clock can do a single sync timeout request
+ * @GST_CLOCK_FLAG_CAN_DO_SINGLE_ASYNC: clock can do a single async timeout request
+ * @GST_CLOCK_FLAG_CAN_DO_PERIODIC_SYNC: clock can do sync periodic timeout requests
+ * @GST_CLOCK_FLAG_CAN_DO_PERIODIC_ASYNC: clock can do async periodic timeout callbacks
+ * @GST_CLOCK_FLAG_CAN_SET_RESOLUTION: clock's resolution can be changed
+ * @GST_CLOCK_FLAG_CAN_SET_MASTER: clock can be slaved to a master clock
+ * @GST_CLOCK_FLAG_LAST: subclasses can add additional flags starting from this flag
+ *
+ * The capabilities of this clock
+ */
+typedef enum {
+ GST_CLOCK_FLAG_CAN_DO_SINGLE_SYNC = (GST_OBJECT_FLAG_LAST << 0),
+ GST_CLOCK_FLAG_CAN_DO_SINGLE_ASYNC = (GST_OBJECT_FLAG_LAST << 1),
+ GST_CLOCK_FLAG_CAN_DO_PERIODIC_SYNC = (GST_OBJECT_FLAG_LAST << 2),
+ GST_CLOCK_FLAG_CAN_DO_PERIODIC_ASYNC = (GST_OBJECT_FLAG_LAST << 3),
+ GST_CLOCK_FLAG_CAN_SET_RESOLUTION = (GST_OBJECT_FLAG_LAST << 4),
+ GST_CLOCK_FLAG_CAN_SET_MASTER = (GST_OBJECT_FLAG_LAST << 5),
+ /* padding */
+ GST_CLOCK_FLAG_LAST = (GST_OBJECT_FLAG_LAST << 8)
+} GstClockFlags;
+
+/**
+ * GST_CLOCK_FLAGS:
+ * @clock: the clock to query
+ *
+ * Gets the #GstClockFlags clock flags.
+ */
+#define GST_CLOCK_FLAGS(clock) GST_OBJECT_FLAGS(clock)
+
+/**
+ * GST_CLOCK_COND:
+ * @clock: the clock to query
+ *
+ * Gets the #GCond that gets signalled when the entries of the clock
+ * changed.
+ */
+#define GST_CLOCK_COND(clock) (GST_CLOCK_CAST(clock)->entries_changed)
+/**
+ * GST_CLOCK_WAIT:
+ * @clock: the clock to wait on
+ *
+ * Wait on the clock until the entries changed.
+ */
+#define GST_CLOCK_WAIT(clock) g_cond_wait(GST_CLOCK_COND(clock),GST_OBJECT_GET_LOCK(clock))
+/**
+ * GST_CLOCK_TIMED_WAIT:
+ * @clock: the clock to wait on
+ * @tv: a #GTimeVal to wait.
+ *
+ * Wait on the clock until the entries changed or the specified timeout
+ * occurred.
+ */
+#define GST_CLOCK_TIMED_WAIT(clock,tv) g_cond_timed_wait(GST_CLOCK_COND(clock),GST_OBJECT_GET_LOCK(clock),tv)
+/**
+ * GST_CLOCK_BROADCAST:
+ * @clock: the clock to broadcast
+ *
+ * Signal that the entries in the clock have changed.
+ */
+#define GST_CLOCK_BROADCAST(clock) g_cond_broadcast(GST_CLOCK_COND(clock))
+
+/**
+ * GstClock:
+ *
+ * #GstClock base structure. The values of this structure are
+ * protected for subclasses, use the methods to use the #GstClock.
+ */
+struct _GstClock {
+ GstObject object;
+
+ GMutex *slave_lock; /* order: SLAVE_LOCK, OBJECT_LOCK */
+
+ /*< protected >*/ /* with LOCK */
+ GstClockTime internal_calibration;
+ GstClockTime external_calibration;
+ GstClockTime rate_numerator;
+ GstClockTime rate_denominator;
+ GstClockTime last_time;
+ GList *entries;
+ GCond *entries_changed;
+
+ /*< private >*/ /* with LOCK */
+ GstClockTime resolution;
+ gboolean stats;
+
+ /* for master/slave clocks */
+ GstClock *master;
+
+ /* with SLAVE_LOCK */
+ gboolean filling;
+ gint window_size;
+ gint window_threshold;
+ gint time_index;
+ GstClockTime timeout;
+ GstClockTime *times;
+ GstClockID clockid;
+
+ /*< private >*/
+ union {
+ GstClockPrivate *priv;
+ GstClockTime _gst_reserved[GST_PADDING];
+ } ABI;
+};
+
+/**
+ * GstClockClass:
+ * @parent_class: the parent class structure
+ * @change_resolution: change the resolution of the clock. Not all values might
+ * be acceptable. The new resolution should be returned.
+ * @get_resolution: get the resolution of the clock.
+ * @get_internal_time: get the internal unadjusted time of the clock.
+ * implement @wait_jitter instead.
+ * @wait: perform a blocking wait on the given #GstClockEntry and return
+ * the jitter.
+ * @wait_async: perform an asynchronous wait for the given #GstClockEntry.
+ * @unschedule: unblock a blocking or async wait operation.
+ *
+ * GStreamer clock class. Override the vmethods to implement the clock
+ * functionality.
+ */
+struct _GstClockClass {
+ GstObjectClass parent_class;
+
+ /*< public >*/
+ /* vtable */
+ GstClockTime (*change_resolution) (GstClock *clock,
+ GstClockTime old_resolution,
+ GstClockTime new_resolution);
+ GstClockTime (*get_resolution) (GstClock *clock);
+
+ GstClockTime (*get_internal_time) (GstClock *clock);
+
+ /* waiting on an ID */
+ GstClockReturn (*wait) (GstClock *clock, GstClockEntry *entry,
+ GstClockTimeDiff *jitter);
+ GstClockReturn (*wait_async) (GstClock *clock, GstClockEntry *entry);
+ void (*unschedule) (GstClock *clock, GstClockEntry *entry);
+
+ /*< private >*/
+ gpointer _gst_reserved[GST_PADDING];
+};
+
+GType gst_clock_get_type (void);
+
+GstClockTime gst_clock_set_resolution (GstClock *clock,
+ GstClockTime resolution);
+GstClockTime gst_clock_get_resolution (GstClock *clock);
+
+GstClockTime gst_clock_get_time (GstClock *clock);
+void gst_clock_set_calibration (GstClock *clock, GstClockTime internal,
+ GstClockTime external,
+ GstClockTime rate_num,
+ GstClockTime rate_denom);
+void gst_clock_get_calibration (GstClock *clock, GstClockTime *internal,
+ GstClockTime *external,
+ GstClockTime *rate_num,
+ GstClockTime *rate_denom);
+
+/* master/slave clocks */
+gboolean gst_clock_set_master (GstClock *clock, GstClock *master);
+GstClock* gst_clock_get_master (GstClock *clock);
+gboolean gst_clock_add_observation (GstClock *clock, GstClockTime slave,
+ GstClockTime master, gdouble *r_squared);
+
+
+/* getting and adjusting internal/external time */
+GstClockTime gst_clock_get_internal_time (GstClock *clock);
+GstClockTime gst_clock_adjust_unlocked (GstClock *clock, GstClockTime internal);
+GstClockTime gst_clock_unadjust_unlocked (GstClock * clock, GstClockTime external);
+
+
+/* creating IDs that can be used to get notifications */
+GstClockID gst_clock_new_single_shot_id (GstClock *clock,
+ GstClockTime time);
+GstClockID gst_clock_new_periodic_id (GstClock *clock,
+ GstClockTime start_time,
+ GstClockTime interval);
+
+/* reference counting */
+GstClockID gst_clock_id_ref (GstClockID id);
+void gst_clock_id_unref (GstClockID id);
+
+/* operations on IDs */
+gint gst_clock_id_compare_func (gconstpointer id1, gconstpointer id2);
+
+GstClockTime gst_clock_id_get_time (GstClockID id);
+GstClockReturn gst_clock_id_wait (GstClockID id,
+ GstClockTimeDiff *jitter);
+GstClockReturn gst_clock_id_wait_async (GstClockID id,
+ GstClockCallback func,
+ gpointer user_data);
+GstClockReturn gst_clock_id_wait_async_full (GstClockID id,
+ GstClockCallback func,
+ gpointer user_data,
+ GDestroyNotify destroy_data);
+void gst_clock_id_unschedule (GstClockID id);
+
+gboolean gst_clock_single_shot_id_reinit (GstClock * clock,
+ GstClockID id,
+ GstClockTime time);
+gboolean gst_clock_periodic_id_reinit (GstClock * clock,
+ GstClockID id,
+ GstClockTime start_time,
+ GstClockTime interval);
+
+G_END_DECLS
+
+#endif /* __GST_CLOCK_H__ */
diff --git a/gst/gstcompat.h b/gst/gstcompat.h
new file mode 100644
index 0000000..cba5fc2
--- /dev/null
+++ b/gst/gstcompat.h
@@ -0,0 +1,61 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2004 Wim Taymans <wim@fluendo.com>
+ *
+ * gstcompat.h: backwards compatibility stuff
+ *
+ * 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:gstcompat
+ * @short_description: Deprecated API entries
+ *
+ * Please do not use these in new code.
+ * These symbols are only available by defining GST_DISABLE_DEPRECATED.
+ * This can be done in CFLAGS for compiling old code.
+ */
+
+/* API compatibility stuff */
+#ifndef __GSTCOMPAT_H__
+#define __GSTCOMPAT_H__
+
+G_BEGIN_DECLS
+
+/* added to ease the transition to 0.11 */
+#define gst_element_class_set_details_simple gst_element_class_set_metadata
+
+#define gst_element_factory_get_longname(f) gst_element_factory_get_metadata(f, GST_ELEMENT_METADATA_LONGNAME)
+#define gst_element_factory_get_klass(f) gst_element_factory_get_metadata(f, GST_ELEMENT_METADATA_KLASS)
+#define gst_element_factory_get_description(f) gst_element_factory_get_metadata(f, GST_ELEMENT_METADATA_DESCRIPTION)
+#define gst_element_factory_get_author(f) gst_element_factory_get_metadata(f, GST_ELEMENT_METADATA_AUTHOR)
+#define gst_element_factory_get_documentation_uri(f) gst_element_factory_get_metadata(f, GST_ELEMENT_METADATA_DOC_URI)
+#define gst_element_factory_get_icon_name(f) gst_element_factory_get_metadata(f, GST_ELEMENT_METADATA_ICON_NAME)
+
+#define gst_pad_get_caps_reffed(p) gst_pad_get_caps(p)
+#define gst_pad_peer_get_caps_reffed(p) gst_pad_peer_get_caps(p)
+
+//#define gst_buffer_create_sub(b,o,s) gst_buffer_copy_region(b,GST_BUFFER_COPY_ALL,o,s)
+
+#define gst_buffer_new_and_alloc(s) gst_buffer_new_allocate(NULL, s, 0)
+
+
+#ifndef GST_DISABLE_DEPRECATED
+
+#endif /* not GST_DISABLE_DEPRECATED */
+
+G_END_DECLS
+
+#endif /* __GSTCOMPAT_H__ */
diff --git a/gst/gstconfig.h.in b/gst/gstconfig.h.in
new file mode 100644
index 0000000..7af2496
--- /dev/null
+++ b/gst/gstconfig.h.in
@@ -0,0 +1,199 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2004,2005 Wim Taymans <wim@fluendo.com>
+ *
+ * gstconfig.h: GST_DISABLE_* macros for build configuration
+ *
+ * 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:gstconfig
+ * @short_description: Build configuration options
+ *
+ * This describes the configuration options for GStreamer. When building
+ * GStreamer there are a lot of parts (known internally as "subsystems" ) that
+ * can be disabled for various reasons. The most common reasons are speed and
+ * size, which is important because GStreamer is designed to run on embedded
+ * systems.
+ *
+ * If a subsystem is disabled, most of this changes are done in an API
+ * compatible way, so you don't need to adapt your code in most cases. It is
+ * never done in an ABI compatible way though. So if you want to disable a
+ * suybsystem, you have to rebuild all programs depending on GStreamer, too.
+ *
+ * If a subsystem is disabled in GStreamer, a value is defined in
+ * &lt;gst/gst.h&gt;. You can check this if you do subsystem-specific stuff.
+ * <example id="example-gstconfig">
+ * <title>Doing subsystem specific things</title>
+ * <programlisting>
+ * &hash;ifndef GST_DISABLE_GST_DEBUG
+ * // do stuff specific to the debugging subsystem
+ * &hash;endif // GST_DISABLE_GST_DEBUG
+ * </programlisting>
+ * </example>
+ */
+
+#ifndef __GST_CONFIG_H__
+#define __GST_CONFIG_H__
+
+/* trick gtk-doc into believing these symbols are defined (yes, it's ugly) */
+
+#if 0
+#define GST_DISABLE_GST_DEBUG 1
+#define GST_DISABLE_PARSE 1
+#define GST_DISABLE_TRACE 1
+#define GST_DISABLE_ALLOC_TRACE 1
+#define GST_DISABLE_REGISTRY 1
+#define GST_DISABLE_PLUGIN 1
+#define GST_HAVE_GLIB_2_8 1
+#endif
+
+/***** default padding of structures *****/
+#define GST_PADDING 4
+#define GST_PADDING_INIT { NULL }
+
+/***** padding for very extensible base classes *****/
+#define GST_PADDING_LARGE 20
+
+/***** disabling of subsystems *****/
+
+/**
+ * GST_DISABLE_GST_DEBUG:
+ *
+ * Configures the inclusion of the debugging subsystem
+ */
+@GST_DISABLE_GST_DEBUG_DEFINE@
+
+/**
+ * GST_DISABLE_PARSE:
+ *
+ * Configures the inclusion of the gst-lauch parser
+ */
+@GST_DISABLE_PARSE_DEFINE@
+
+/**
+ * GST_DISABLE_TRACE:
+ *
+ * Configures the inclusion of a resource tracing facillity
+ * (seems to be unused)
+ */
+@GST_DISABLE_TRACE_DEFINE@
+
+/**
+ * GST_DISABLE_ALLOC_TRACE:
+ *
+ * Configures the use of a memory tracer based on the resource tracer
+ * if TRACE is disabled, ALLOC_TRACE is disabled as well
+ */
+@GST_DISABLE_ALLOC_TRACE_DEFINE@
+
+/**
+ * GST_DISABLE_REGISTRY:
+ *
+ * Configures the use of the plugin registry.
+ * If one disables this, required plugins need to be loaded and registered
+ * manually
+ */
+@GST_DISABLE_REGISTRY_DEFINE@
+
+/* FIXME: test and document these! */
+/* Configures the use of external plugins */
+@GST_DISABLE_PLUGIN_DEFINE@
+
+/* printf extension format */
+/**
+ * GST_PTR_FORMAT:
+ *
+ * printf format type used to debug GStreamer types.
+ * This can only be used on types whose size is >= sizeof(gpointer).
+ */
+@GST_PRINTF_EXTENSION_POINTER_FORMAT_DEFINE@
+/**
+ * GST_SEGMENT_FORMAT:
+ *
+ * printf format type used to debug GStreamer segments.
+ * This can only be used on pointers to GstSegment structures.
+ *
+ * Since: 0.10.10
+ */
+@GST_PRINTF_EXTENSION_SEGMENT_FORMAT_DEFINE@
+
+/* whether or not GST_PTR_FORMAT or GST_SEGMENT_FORMAT are using
+ * the printf extension mechanism. This is for internal use in our
+ * header files so we know whether we can use G_GNUC_PRINTF or not */
+@GST_USING_PRINTF_EXTENSION_DEFINE@
+
+/* GST_DISABLE_PRINTF_EXTENSION:
+ *
+ * Define this to debug your debug log messages and make gcc spew warnings
+ * if printf format string and arguments don't match up (this is usually
+ * not the case when libc and gcc are used because printf format warnings
+ * have to be disabled when the printf extension mechanism is in use).
+ *
+ * Note that using this option disables 'pretty logging' of GStreamer objects
+ * like caps, tags, structures, events, pads etc., so that only their address
+ * will be printed in the log.
+ *
+ * This define only disables use of the special registered printf format
+ * extensions in the code compiled with it defined. It does not stop
+ * GStreamer from registering these extensions in the first place if it
+ * was compiled against a libc that supports this.
+ *
+ * (not official API)
+ */
+/* If GLib is not using the system printf, we can't use the registered
+ * extensions because the GLib-internal printf won't know how to parse them */
+#if defined(GST_DISABLE_PRINTF_EXTENSION) || !defined(GLIB_USING_SYSTEM_PRINTF)
+ #undef GST_PTR_FORMAT
+ #define GST_PTR_FORMAT "p"
+ #undef GST_SEGMENT_FORMAT
+ #define GST_SEGMENT_FORMAT "p"
+ #undef GST_USING_PRINTF_EXTENSION
+#endif
+
+/* whether or not the CPU supports unaligned access */
+@GST_HAVE_UNALIGNED_ACCESS_DEFINE@
+
+/**
+ * GST_EXPORT:
+ *
+ * Export the given variable from the built shared object.
+ *
+ * On Windows, this exports the variable from the DLL.
+ * On other platforms, this gets defined to "extern".
+ */
+/**
+ * GST_PLUGIN_EXPORT:
+ *
+ * Export the plugin's definition.
+ *
+ * On Windows, this exports the plugin definition from the DLL.
+ * On other platforms, this gets defined as a no-op.
+ */
+#ifdef _MSC_VER
+#define GST_PLUGIN_EXPORT __declspec(dllexport) extern
+#ifdef GST_EXPORTS
+#define GST_EXPORT __declspec(dllexport) extern
+#else
+#define GST_EXPORT __declspec(dllimport) extern
+#endif
+#else /* not _MSC_VER */
+#define GST_PLUGIN_EXPORT
+#define GST_EXPORT extern
+#endif
+
+#endif /* __GST_CONFIG_H__ */
diff --git a/gst/gstdatetime.c b/gst/gstdatetime.c
new file mode 100644
index 0000000..60f709f
--- /dev/null
+++ b/gst/gstdatetime.c
@@ -0,0 +1,816 @@
+/* GStreamer
+ * Copyright (C) 2010 Thiago Santos <thiago.sousa.santos@collabora.co.uk>
+ *
+ * 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 "glib-compat-private.h"
+#include "gst_private.h"
+#include "gstdatetime.h"
+#include <glib.h>
+#include <math.h>
+
+/**
+ * SECTION:gstdatetime
+ * @title: GstDateTime
+ * @short_description: A date, time and timezone structure
+ *
+ * Struct to store date, time and timezone information altogether.
+ * #GstDateTime is refcounted and immutable.
+ *
+ * Date information is handled using the proleptic Gregorian calendar.
+ *
+ * Provides basic creation functions and accessor functions to its fields.
+ *
+ * Since: 0.10.31
+ */
+
+/**
+ * gst_date_time_get_year:
+ * @datetime: a #GstDateTime
+ *
+ * Returns the year of this #GstDateTime
+ *
+ * Return value: The year of this #GstDateTime
+ * Since: 0.10.31
+ */
+
+/**
+ * gst_date_time_get_month:
+ * @datetime: a #GstDateTime
+ *
+ * Returns the month of this #GstDateTime. January is 1, February is 2, etc..
+ *
+ * Return value: The month of this #GstDateTime
+ * Since: 0.10.31
+ */
+
+/**
+ * gst_date_time_get_day:
+ * @datetime: a #GstDateTime
+ *
+ * Returns the day of this #GstDateTime.
+ *
+ * Return value: The day of this #GstDateTime
+ * Since: 0.10.31
+ */
+
+/**
+ * gst_date_time_get_hour:
+ * @datetime: a #GstDateTime
+ *
+ * Retrieves the hour of the day represented by @datetime in the gregorian
+ * calendar. The return is in the range of 0 to 23.
+ *
+ * Return value: the hour of the day
+ *
+ * Since: 0.10.31
+ */
+
+/**
+ * gst_date_time_get_microsecond:
+ * @datetime: a #GstDateTime
+ *
+ * Retrieves the fractional part of the seconds in microseconds represented by
+ * @datetime in the gregorian calendar.
+ *
+ * Return value: the microsecond of the second
+ *
+ * Since: 0.10.31
+ */
+
+/**
+ * gst_date_time_get_minute:
+ * @datetime: a #GstDateTime
+ *
+ * Retrieves the minute of the hour represented by @datetime in the gregorian
+ * calendar.
+ *
+ * Return value: the minute of the hour
+ *
+ * Since: 0.10.31
+ */
+
+/**
+ * gst_date_time_get_second:
+ * @datetime: a #GstDateTime
+ *
+ * Retrieves the second of the minute represented by @datetime in the gregorian
+ * calendar.
+ *
+ * Return value: the second represented by @datetime
+ *
+ * Since: 0.10.31
+ */
+
+/**
+ * gst_date_time_get_second:
+ * @datetime: a #GstDateTime
+ *
+ * Retrieves the second of the minute represented by @datetime in the gregorian
+ * calendar.
+ *
+ * Return value: the second represented by @datetime
+ *
+ * Since: 0.10.31
+ */
+
+/**
+ * gst_date_time_get_time_zone_offset:
+ * @datetime: a #GstDateTime
+ *
+ * Retrieves the offset from UTC in hours that the timezone specified
+ * by @datetime represents. Timezones ahead (to the east) of UTC have positive
+ * values, timezones before (to the west) of UTC have negative values.
+ * If @datetime represents UTC time, then the offset is zero.
+ *
+ * Return value: the offset from UTC in hours
+ * Since: 0.10.31
+ */
+
+/**
+ * gst_date_time_new_from_unix_epoch_local_time:
+ * @secs: seconds from the Unix epoch
+ *
+ * Creates a new #GstDateTime using the time since Jan 1, 1970 specified by
+ * @secs. The #GstDateTime is in the local timezone.
+ *
+ * Free-function: gst_date_time_unref
+ *
+ * Return value: (transfer full): the newly created #GstDateTime
+ *
+ * Since: 0.10.31
+ */
+
+/**
+ * gst_date_time_new_from_unix_epoch_utc:
+ * @secs: seconds from the Unix epoch
+ *
+ * Creates a new #GstDateTime using the time since Jan 1, 1970 specified by
+ * @secs. The #GstDateTime is in the UTC timezone.
+ *
+ * Free-function: gst_date_time_unref
+ *
+ * Return value: (transfer full): the newly created #GstDateTime
+ *
+ * Since: 0.10.31
+ */
+
+/**
+ * gst_date_time_new_local_time:
+ * @year: the gregorian year
+ * @month: the gregorian month
+ * @day: the day of the gregorian month
+ * @hour: the hour of the day
+ * @minute: the minute of the hour
+ * @seconds: the second of the minute
+ *
+ * Creates a new #GstDateTime using the date and times in the gregorian calendar
+ * in the local timezone.
+ *
+ * @year should be from 1 to 9999, @month should be from 1 to 12, @day from
+ * 1 to 31, @hour from 0 to 23, @minutes and @seconds from 0 to 59.
+ *
+ * Free-function: gst_date_time_unref
+ *
+ * Return value: (transfer full): the newly created #GstDateTime
+ *
+ * Since: 0.10.31
+ */
+
+/**
+ * gst_date_time_new:
+ * @tzoffset: Offset from UTC in hours.
+ * @year: the gregorian year
+ * @month: the gregorian month
+ * @day: the day of the gregorian month
+ * @hour: the hour of the day
+ * @minute: the minute of the hour
+ * @seconds: the second of the minute
+ *
+ * Creates a new #GstDateTime using the date and times in the gregorian calendar
+ * in the supplied timezone.
+ *
+ * @year should be from 1 to 9999, @month should be from 1 to 12, @day from
+ * 1 to 31, @hour from 0 to 23, @minutes and @seconds from 0 to 59.
+ *
+ * Note that @tzoffset is a float and was chosen so for being able to handle
+ * some fractional timezones, while it still keeps the readability of
+ * represeting it in hours for most timezones.
+ *
+ * Free-function: gst_date_time_unref
+ *
+ * Return value: (transfer full): the newly created #GstDateTime
+ *
+ * Since: 0.10.31
+ */
+
+/**
+ * gst_date_time_new_now_local_time:
+ *
+ * Creates a new #GstDateTime representing the current date and time.
+ *
+ * Free-function: gst_date_time_unref
+ *
+ * Return value: (transfer full): the newly created #GstDateTime which should
+ * be freed with gst_date_time_unref().
+ *
+ * Since: 0.10.31
+ */
+
+/**
+ * gst_date_time_new_now_utc:
+ *
+ * Creates a new #GstDateTime that represents the current instant at Universal
+ * coordinated time.
+ *
+ * Free-function: gst_date_time_unref
+ *
+ * Return value: (transfer full): the newly created #GstDateTime which should
+ * be freed with gst_date_time_unref().
+ *
+ * Since: 0.10.31
+ */
+
+
+#define GST_DATE_TIME_SEC_PER_DAY (G_GINT64_CONSTANT (86400))
+#define GST_DATE_TIME_USEC_PER_DAY (G_GINT64_CONSTANT (86400000000))
+#define GST_DATE_TIME_USEC_PER_HOUR (G_GINT64_CONSTANT (3600000000))
+#define GST_DATE_TIME_USEC_PER_MINUTE (G_GINT64_CONSTANT (60000000))
+#define GST_DATE_TIME_USEC_PER_SECOND (G_GINT64_CONSTANT (1000000))
+#define GST_DATE_TIME_USEC_PER_MILLISECOND (G_GINT64_CONSTANT (1000))
+
+/* Jan 5th 2011 (Edward) : GLib's GDateTime is broken in regards to gmt offset
+ * on macosx. Re-enable it once the following bug is fixed:
+ * https://bugzilla.gnome.org/show_bug.cgi?id=638666 */
+#ifdef HAVE_OSX
+#undef GLIB_HAS_GDATETIME
+#endif
+
+
+#ifndef GLIB_HAS_GDATETIME
+
+#define MAX_SUPPORTED_YEAR 9999
+#define GREGORIAN_LEAP(y) (((y%4)==0)&&(!(((y%100)==0)&&((y%400)!=0))))
+
+static const guint16 days_in_months[2][13] = {
+ {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
+ {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
+};
+
+struct _GstDateTime
+{
+ /*
+ * As we don't have a datetime math API, we can have fields split here.
+ * (There is still some math done internally, but nothing really relevant).
+ *
+ * If we ever add one, we should go for a days since some epoch counter.
+ * (Proleptic Gregorian with 0001-01-01 as day 1)
+ */
+ gint16 year;
+ gint8 month;
+ gint8 day;
+ guint64 usec; /* Microsecond timekeeping within Day */
+
+ gint tzoffset;
+
+ volatile gint ref_count;
+};
+
+/*
+ * Returns the utc offset in seconds for this time structure
+ */
+static gint
+gmt_offset (struct tm *tm, time_t t)
+{
+#if defined (HAVE_TM_GMTOFF)
+ return tm->tm_gmtoff;
+#else
+ struct tm g;
+ time_t t2;
+#ifdef HAVE_GMTIME_R
+ gmtime_r (&t, &g);
+#else
+ g = *gmtime (&t);
+#endif
+ t2 = mktime (&g);
+ return (int) difftime (t, t2);
+#endif
+}
+
+static void
+gst_date_time_set_local_timezone (GstDateTime * dt)
+{
+ struct tm tt;
+ time_t t;
+
+ g_return_if_fail (dt != NULL);
+
+ memset (&tt, 0, sizeof (tt));
+
+ tt.tm_mday = gst_date_time_get_day (dt);
+ tt.tm_mon = gst_date_time_get_month (dt) - 1;
+ tt.tm_year = gst_date_time_get_year (dt) - 1900;
+ tt.tm_hour = gst_date_time_get_hour (dt);
+ tt.tm_min = gst_date_time_get_minute (dt);
+ tt.tm_sec = gst_date_time_get_second (dt);
+
+ t = mktime (&tt);
+
+ dt->tzoffset = gmt_offset (&tt, t) / 60;
+}
+
+static GstDateTime *
+gst_date_time_alloc (void)
+{
+ GstDateTime *datetime;
+
+ datetime = g_slice_new0 (GstDateTime);
+ datetime->ref_count = 1;
+
+ return datetime;
+}
+
+static void
+gst_date_time_free (GstDateTime * datetime)
+{
+ g_slice_free (GstDateTime, datetime);
+}
+
+static GstDateTime *
+gst_date_time_new_from_date (gint year, gint month, gint day)
+{
+ GstDateTime *dt;
+
+ g_return_val_if_fail (year > 0 && year <= 9999, NULL);
+ g_return_val_if_fail ((month > 0 && month <= 12), NULL);
+ g_return_val_if_fail ((day > 0 && day <= 31), NULL);
+
+ dt = gst_date_time_alloc ();
+
+ dt->year = year;
+ dt->month = month;
+ dt->day = day;
+ gst_date_time_set_local_timezone (dt);
+
+ return dt;
+}
+
+gint
+gst_date_time_get_year (const GstDateTime * datetime)
+{
+ g_return_val_if_fail (datetime != NULL, 0);
+
+ return datetime->year;
+}
+
+gint
+gst_date_time_get_month (const GstDateTime * datetime)
+{
+ g_return_val_if_fail (datetime != NULL, 0);
+
+ return datetime->month;
+}
+
+gint
+gst_date_time_get_day (const GstDateTime * datetime)
+{
+ g_return_val_if_fail (datetime != NULL, 0);
+
+ return datetime->day;
+}
+
+gint
+gst_date_time_get_hour (const GstDateTime * datetime)
+{
+ g_return_val_if_fail (datetime != NULL, 0);
+ return (datetime->usec / GST_DATE_TIME_USEC_PER_HOUR);
+}
+
+gint
+gst_date_time_get_microsecond (const GstDateTime * datetime)
+{
+ g_return_val_if_fail (datetime != NULL, 0);
+ return (datetime->usec % GST_DATE_TIME_USEC_PER_SECOND);
+}
+
+gint
+gst_date_time_get_minute (const GstDateTime * datetime)
+{
+ g_return_val_if_fail (datetime != NULL, 0);
+ return (datetime->usec % GST_DATE_TIME_USEC_PER_HOUR) /
+ GST_DATE_TIME_USEC_PER_MINUTE;
+}
+
+gint
+gst_date_time_get_second (const GstDateTime * datetime)
+{
+ g_return_val_if_fail (datetime != NULL, 0);
+ return (datetime->usec % GST_DATE_TIME_USEC_PER_MINUTE) /
+ GST_DATE_TIME_USEC_PER_SECOND;
+}
+
+gfloat
+gst_date_time_get_time_zone_offset (const GstDateTime * datetime)
+{
+ g_return_val_if_fail (datetime != NULL, 0);
+
+ return datetime->tzoffset / 60.0f;
+}
+
+GstDateTime *
+gst_date_time_new_from_unix_epoch_local_time (gint64 secs)
+{
+ GstDateTime *dt;
+ struct tm tm;
+ time_t tt;
+
+ memset (&tm, 0, sizeof (tm));
+ tt = (time_t) secs;
+
+#ifdef HAVE_LOCALTIME_R
+ localtime_r (&tt, &tm);
+#else
+ memcpy (&tm, localtime (&tt), sizeof (struct tm));
+#endif
+
+ dt = gst_date_time_new (0, tm.tm_year + 1900,
+ tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
+ gst_date_time_set_local_timezone (dt);
+ return dt;
+}
+
+GstDateTime *
+gst_date_time_new_from_unix_epoch_utc (gint64 secs)
+{
+ GstDateTime *dt;
+ struct tm tm;
+ time_t tt;
+
+ memset (&tm, 0, sizeof (tm));
+ tt = (time_t) secs;
+
+#ifdef HAVE_GMTIME_R
+ gmtime_r (&tt, &tm);
+#else
+ memcpy (&tm, gmtime (&tt), sizeof (struct tm));
+#endif
+
+ dt = gst_date_time_new (0, tm.tm_year + 1900,
+ tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
+ return dt;
+}
+
+GstDateTime *
+gst_date_time_new_local_time (gint year, gint month, gint day, gint hour,
+ gint minute, gdouble seconds)
+{
+ GstDateTime *dt;
+
+ dt = gst_date_time_new (0, year, month, day, hour, minute, seconds);
+
+ gst_date_time_set_local_timezone (dt);
+
+ return dt;
+}
+
+GstDateTime *
+gst_date_time_new (gfloat tzoffset, gint year, gint month, gint day, gint hour,
+ gint minute, gdouble seconds)
+{
+ GstDateTime *dt;
+
+ g_return_val_if_fail (hour >= 0 && hour < 24, NULL);
+ g_return_val_if_fail (minute >= 0 && minute < 60, NULL);
+ g_return_val_if_fail (seconds >= 0 && seconds < 60, NULL);
+ g_return_val_if_fail (tzoffset >= -12.0 && tzoffset <= 12.0, NULL);
+
+ if (!(dt = gst_date_time_new_from_date (year, month, day)))
+ return NULL;
+
+ dt->usec = (hour * GST_DATE_TIME_USEC_PER_HOUR)
+ + (minute * GST_DATE_TIME_USEC_PER_MINUTE)
+ + (guint64) (floor (seconds * GST_DATE_TIME_USEC_PER_SECOND + 0.5));
+
+ /* we store in minutes */
+ dt->tzoffset = (gint) (tzoffset * 60.0);
+
+ return dt;
+}
+
+GstDateTime *
+gst_date_time_new_now_local_time (void)
+{
+ GstDateTime *datetime;
+ GTimeVal tv;
+ g_get_current_time (&tv);
+
+ datetime = gst_date_time_new_from_unix_epoch_local_time (tv.tv_sec);
+ datetime->usec += tv.tv_usec;
+ gst_date_time_set_local_timezone (datetime);
+ return datetime;
+}
+
+static GstDateTime *
+gst_date_time_copy (const GstDateTime * dt)
+{
+ GstDateTime *copy = gst_date_time_alloc ();
+
+ memcpy (copy, dt, sizeof (GstDateTime));
+ copy->ref_count = 1;
+
+ return copy;
+}
+
+static GstDateTime *
+gst_date_time_to_utc (const GstDateTime * dt)
+{
+ GstDateTime *utc;
+ gint64 usec;
+ gint days;
+ gint leap;
+
+ g_return_val_if_fail (dt != NULL, NULL);
+
+ utc = gst_date_time_copy (dt);
+
+ usec = dt->usec - dt->tzoffset * GST_DATE_TIME_USEC_PER_MINUTE;
+ days = usec / GST_DATE_TIME_USEC_PER_DAY;
+ if (usec < 0)
+ days--;
+ utc->day += days;
+
+ leap = GREGORIAN_LEAP (utc->year) ? 1 : 0;
+
+ /* check if we should update month/year */
+ if (utc->day < 1) {
+ if (utc->month == 1) {
+ utc->year--;
+ utc->month = 12;
+ } else {
+ utc->month--;
+ }
+ if (GREGORIAN_LEAP (utc->year))
+ utc->day = days_in_months[1][utc->month];
+ else
+ utc->day = days_in_months[0][utc->month];
+ } else if (utc->day > days_in_months[leap][utc->month]) {
+ if (utc->month == 12) {
+ utc->year++;
+ utc->month = 1;
+ } else {
+ utc->month++;
+ }
+ utc->day = 1;
+ }
+
+ if (usec < 0)
+ utc->usec =
+ GST_DATE_TIME_USEC_PER_DAY + (usec % GST_DATE_TIME_USEC_PER_DAY);
+ else
+ utc->usec = usec % GST_DATE_TIME_USEC_PER_DAY;
+
+ return utc;
+}
+
+GstDateTime *
+gst_date_time_new_now_utc (void)
+{
+ GstDateTime *now, *utc;
+
+ now = gst_date_time_new_now_local_time ();
+ utc = gst_date_time_to_utc (now);
+ gst_date_time_unref (now);
+ return utc;
+}
+
+gint
+priv_gst_date_time_compare (gconstpointer dt1, gconstpointer dt2)
+{
+ GstDateTime *a, *b;
+ gint res = 0;
+
+ a = gst_date_time_to_utc (dt1);
+ b = gst_date_time_to_utc (dt2);
+
+#define GST_DATE_TIME_COMPARE_VALUE(a,b,v) \
+ if ((a)->v > (b)->v) { \
+ res = 1; \
+ goto done; \
+ } else if ((a)->v < (b)->v) { \
+ res = -1; \
+ goto done; \
+ }
+
+ GST_DATE_TIME_COMPARE_VALUE (a, b, year);
+ GST_DATE_TIME_COMPARE_VALUE (a, b, month);
+ GST_DATE_TIME_COMPARE_VALUE (a, b, day);
+ GST_DATE_TIME_COMPARE_VALUE (a, b, usec);
+
+#undef GST_DATE_TIME_COMPARE_VALUE
+
+done:
+ gst_date_time_unref (a);
+ gst_date_time_unref (b);
+ return res;
+}
+
+#else
+
+struct _GstDateTime
+{
+ GDateTime *datetime;
+
+ volatile gint ref_count;
+};
+
+static GstDateTime *
+gst_date_time_new_from_gdatetime (GDateTime * dt)
+{
+ GstDateTime *gst_dt;
+
+ if (!dt)
+ return NULL;
+
+ gst_dt = g_slice_new (GstDateTime);
+ gst_dt->datetime = dt;
+ gst_dt->ref_count = 1;
+ return gst_dt;
+}
+
+gint
+gst_date_time_get_year (const GstDateTime * datetime)
+{
+ return g_date_time_get_year (datetime->datetime);
+}
+
+gint
+gst_date_time_get_month (const GstDateTime * datetime)
+{
+ return g_date_time_get_month (datetime->datetime);
+}
+
+gint
+gst_date_time_get_day (const GstDateTime * datetime)
+{
+ return g_date_time_get_day_of_month (datetime->datetime);
+}
+
+gint
+gst_date_time_get_hour (const GstDateTime * datetime)
+{
+ return g_date_time_get_hour (datetime->datetime);
+}
+
+gint
+gst_date_time_get_minute (const GstDateTime * datetime)
+{
+ return g_date_time_get_minute (datetime->datetime);
+}
+
+gint
+gst_date_time_get_second (const GstDateTime * datetime)
+{
+ return g_date_time_get_second (datetime->datetime);
+}
+
+gint
+gst_date_time_get_microsecond (const GstDateTime * datetime)
+{
+ return g_date_time_get_microsecond (datetime->datetime);
+}
+
+gfloat
+gst_date_time_get_time_zone_offset (const GstDateTime * datetime)
+{
+ return (g_date_time_get_utc_offset (datetime->datetime) /
+ G_USEC_PER_SEC) / 3600.0;
+}
+
+GstDateTime *
+gst_date_time_new_from_unix_epoch_local_time (gint64 secs)
+{
+ return
+ gst_date_time_new_from_gdatetime (g_date_time_new_from_unix_local (secs));
+}
+
+GstDateTime *
+gst_date_time_new_from_unix_epoch_utc (gint64 secs)
+{
+ return
+ gst_date_time_new_from_gdatetime (g_date_time_new_from_unix_utc (secs));
+}
+
+GstDateTime *
+gst_date_time_new_local_time (gint year, gint month, gint day, gint hour,
+ gint minute, gdouble seconds)
+{
+ return gst_date_time_new_from_gdatetime (g_date_time_new_local (year, month,
+ day, hour, minute, seconds));
+}
+
+GstDateTime *
+gst_date_time_new_now_local_time (void)
+{
+ return gst_date_time_new_from_gdatetime (g_date_time_new_now_local ());
+}
+
+GstDateTime *
+gst_date_time_new_now_utc (void)
+{
+ return gst_date_time_new_from_gdatetime (g_date_time_new_now_utc ());
+}
+
+gint
+priv_gst_date_time_compare (gconstpointer dt1, gconstpointer dt2)
+{
+ const GstDateTime *datetime1 = dt1;
+ const GstDateTime *datetime2 = dt2;
+ return g_date_time_compare (datetime1->datetime, datetime2->datetime);
+}
+
+GstDateTime *
+gst_date_time_new (gfloat tzoffset, gint year, gint month, gint day, gint hour,
+ gint minute, gdouble seconds)
+{
+ gchar buf[6];
+ GTimeZone *tz;
+ GDateTime *dt;
+ gint tzhour, tzminute;
+
+ tzhour = (gint) ABS (tzoffset);
+ tzminute = (gint) ((ABS (tzoffset) - tzhour) * 60);
+
+ g_snprintf (buf, 6, "%c%02d%02d", tzoffset >= 0 ? '+' : '-', tzhour,
+ tzminute);
+
+ tz = g_time_zone_new (buf);
+
+ dt = g_date_time_new (tz, year, month, day, hour, minute, seconds);
+ g_time_zone_unref (tz);
+ return gst_date_time_new_from_gdatetime (dt);
+}
+
+static void
+gst_date_time_free (GstDateTime * datetime)
+{
+ g_date_time_unref (datetime->datetime);
+ g_slice_free (GstDateTime, datetime);
+}
+
+#endif
+
+/**
+ * gst_date_time_ref:
+ * @datetime: a #GstDateTime
+ *
+ * Atomically increments the reference count of @datetime by one.
+ *
+ * Return value: (transfer full): the reference @datetime
+ *
+ * Since: 0.10.31
+ */
+GstDateTime *
+gst_date_time_ref (GstDateTime * datetime)
+{
+ g_return_val_if_fail (datetime != NULL, NULL);
+ g_return_val_if_fail (datetime->ref_count > 0, NULL);
+ g_atomic_int_inc (&datetime->ref_count);
+ return datetime;
+}
+
+/**
+ * gst_date_time_unref:
+ * @datetime: (transfer full): a #GstDateTime
+ *
+ * Atomically decrements the reference count of @datetime by one. When the
+ * reference count reaches zero, the structure is freed.
+ *
+ * Since: 0.10.31
+ */
+void
+gst_date_time_unref (GstDateTime * datetime)
+{
+ g_return_if_fail (datetime != NULL);
+ g_return_if_fail (datetime->ref_count > 0);
+
+ if (g_atomic_int_dec_and_test (&datetime->ref_count))
+ gst_date_time_free (datetime);
+}
diff --git a/gst/gstdatetime.h b/gst/gstdatetime.h
new file mode 100644
index 0000000..eeeb020
--- /dev/null
+++ b/gst/gstdatetime.h
@@ -0,0 +1,66 @@
+/* GStreamer
+ * Copyright (C) 2010 Thiago Santos <thiago.sousa.santos@collabora.co.uk>
+ *
+ * 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_DATE_TIME_H__
+#define __GST_DATE_TIME_H__
+
+#include <time.h>
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+/**
+ * GstDateTime:
+ *
+ * Opaque, immutable, refcounted struct that stores date, time and timezone
+ * information. It currently supports ranges from 0001-01-01 to
+ * 9999-12-31 in the Gregorian proleptic calendar.
+ *
+ * Use the acessor functions to get the stored values.
+ */
+typedef struct _GstDateTime GstDateTime;
+
+gint gst_date_time_get_year (const GstDateTime * datetime);
+gint gst_date_time_get_month (const GstDateTime * datetime);
+gint gst_date_time_get_day (const GstDateTime * datetime);
+gint gst_date_time_get_hour (const GstDateTime * datetime);
+gint gst_date_time_get_minute (const GstDateTime * datetime);
+gint gst_date_time_get_second (const GstDateTime * datetime);
+gint gst_date_time_get_microsecond (const GstDateTime * datetime);
+gfloat gst_date_time_get_time_zone_offset (const GstDateTime * datetime);
+
+GstDateTime *gst_date_time_new_from_unix_epoch_local_time (gint64 secs);
+GstDateTime *gst_date_time_new_from_unix_epoch_utc (gint64 secs);
+GstDateTime *gst_date_time_new_local_time (gint year, gint month,
+ gint day, gint hour,
+ gint minute,
+ gdouble seconds);
+GstDateTime *gst_date_time_new (gfloat tzoffset,
+ gint year, gint month,
+ gint day, gint hour,
+ gint minute,
+ gdouble seconds);
+GstDateTime *gst_date_time_new_now_local_time (void);
+GstDateTime *gst_date_time_new_now_utc (void);
+
+GstDateTime *gst_date_time_ref (GstDateTime * datetime);
+void gst_date_time_unref (GstDateTime * datetime);
+
+G_END_DECLS
+#endif /* __GST_DATE_TIME_H__ */
diff --git a/gst/gstdebugutils.c b/gst/gstdebugutils.c
new file mode 100644
index 0000000..de74850
--- /dev/null
+++ b/gst/gstdebugutils.c
@@ -0,0 +1,745 @@
+/* GStreamer
+ * Copyright (C) 2007 Stefan Kost <ensonic@users.sf.net>
+ *
+ * gstdebugutils.c: debugging and analysis utillities
+ *
+ * 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.
+ */
+/* TODO:
+ * edge [ constraint=false ];
+ * this creates strange graphs ("minlen=0" is better)
+ * try puting src/sink ghostpads for each bin into invisible clusters
+ *
+ * for more compact nodes, try
+ * - changing node-shape from box into record
+ * - use labels like : element [ label="{element | <src> src | <sink> sink}"]
+ * - point to record-connectors : element1:src -> element2:sink
+ * - we use head/tail labels for pad-caps right now
+ * - this does not work well, as dot seems to not look at their size when
+ * doing the layout
+ * - we could add the caps to the pad itself, then we should use one line per
+ * caps (simple caps = one line)
+ */
+
+#include "gst_private.h"
+#include "gstdebugutils.h"
+
+#ifndef GST_DISABLE_GST_DEBUG
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include "gstinfo.h"
+#include "gstbin.h"
+#include "gstobject.h"
+#include "gstghostpad.h"
+#include "gstpad.h"
+#include "gstutils.h"
+#include "gstvalue.h"
+
+/*** PIPELINE GRAPHS **********************************************************/
+
+const gchar *priv_gst_dump_dot_dir; /* NULL *//* set from gst.c */
+
+const gchar spaces[] = {
+ " " /* 32 */
+ " " /* 64 */
+ " " /* 96 */
+ " " /* 128 */
+};
+
+extern GstClockTime _priv_gst_info_start_time;
+
+static gchar *
+debug_dump_make_object_name (GstObject * element)
+{
+ return g_strcanon (g_strdup_printf ("%s_%p", GST_OBJECT_NAME (element),
+ element), G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "_", '_');
+}
+
+static gchar *
+debug_dump_get_element_state (GstElement * element)
+{
+ gchar *state_name = NULL;
+ const gchar *state_icons = "~0-=>";
+ GstState state = GST_STATE_VOID_PENDING, pending = GST_STATE_VOID_PENDING;
+
+ gst_element_get_state (element, &state, &pending, 0);
+ if (pending == GST_STATE_VOID_PENDING) {
+ state_name = g_strdup_printf ("\\n[%c]", state_icons[state]);
+ } else {
+ state_name = g_strdup_printf ("\\n[%c] -> [%c]", state_icons[state],
+ state_icons[pending]);
+ }
+ return state_name;
+}
+
+static gchar *
+debug_dump_get_element_params (GstElement * element)
+{
+ gchar *param_name = NULL;
+ GParamSpec **properties, *property;
+ GValue value = { 0, };
+ guint i, number_of_properties;
+ gchar *tmp, *value_str;
+
+ /* get paramspecs and show non-default properties */
+ properties =
+ g_object_class_list_properties (G_OBJECT_CLASS (GST_ELEMENT_GET_CLASS
+ (element)), &number_of_properties);
+ if (properties) {
+ for (i = 0; i < number_of_properties; i++) {
+ property = properties[i];
+
+ /* ski some properties */
+ if (!(property->flags & G_PARAM_READABLE))
+ continue;
+ if (!strcmp (property->name, "name"))
+ continue;
+
+ g_value_init (&value, property->value_type);
+ g_object_get_property (G_OBJECT (element), property->name, &value);
+ if (!(g_param_value_defaults (property, &value))) {
+ tmp = g_strdup_value_contents (&value);
+ value_str = g_strescape (tmp, NULL);
+ g_free (tmp);
+ if (param_name) {
+ tmp = param_name;
+ param_name = g_strdup_printf ("%s\\n%s=%s",
+ tmp, property->name, value_str);
+ g_free (tmp);
+ } else {
+ param_name = g_strdup_printf ("\\n%s=%s", property->name, value_str);
+ }
+ g_free (value_str);
+ }
+ g_value_unset (&value);
+ }
+ g_free (properties);
+ }
+ return param_name;
+}
+
+static void
+debug_dump_pad (GstPad * pad, const gchar * color_name,
+ const gchar * element_name, GstDebugGraphDetails details, FILE * out,
+ const gint indent)
+{
+ GstPadTemplate *pad_templ;
+ GstPadPresence presence;
+ gchar *pad_name;
+ const gchar *style_name;
+ const gchar *spc = &spaces[MAX (sizeof (spaces) - (1 + indent * 2), 0)];
+
+ pad_name = debug_dump_make_object_name (GST_OBJECT (pad));
+
+ /* pad availability */
+ style_name = "filled,solid";
+ if ((pad_templ = gst_pad_get_pad_template (pad))) {
+ presence = GST_PAD_TEMPLATE_PRESENCE (pad_templ);
+ if (presence == GST_PAD_SOMETIMES) {
+ style_name = "filled,dotted";
+ } else if (presence == GST_PAD_REQUEST) {
+ style_name = "filled,dashed";
+ }
+ }
+ if (details & GST_DEBUG_GRAPH_SHOW_STATES) {
+ gchar pad_flags[5];
+ const gchar *activation_mode = "-><";
+
+ /* check if pad flags */
+ pad_flags[0] = GST_OBJECT_FLAG_IS_SET (pad, GST_PAD_BLOCKED) ? 'B' : 'b';
+ pad_flags[1] = GST_OBJECT_FLAG_IS_SET (pad, GST_PAD_FLUSHING) ? 'F' : 'f';
+ pad_flags[2] = GST_OBJECT_FLAG_IS_SET (pad, GST_PAD_IN_GETCAPS) ? 'G' : 'g';
+ pad_flags[3] = GST_OBJECT_FLAG_IS_SET (pad, GST_PAD_BLOCKING) ? 'B' : 'b';
+ pad_flags[4] = '\0';
+
+ fprintf (out,
+ "%s %s_%s [color=black, fillcolor=\"%s\", label=\"%s\\n[%c][%s]\", height=\"0.2\", style=\"%s\"];\n",
+ spc, element_name, pad_name, color_name, GST_OBJECT_NAME (pad),
+ activation_mode[pad->mode], pad_flags, style_name);
+ } else {
+ fprintf (out,
+ "%s %s_%s [color=black, fillcolor=\"%s\", label=\"%s\", height=\"0.2\", style=\"%s\"];\n",
+ spc, element_name, pad_name, color_name, GST_OBJECT_NAME (pad),
+ style_name);
+ }
+
+ g_free (pad_name);
+}
+
+static void
+debug_dump_element_pad (GstPad * pad, GstElement * element,
+ GstDebugGraphDetails details, FILE * out, const gint indent)
+{
+ GstElement *target_element;
+ GstPad *target_pad, *tmp_pad;
+ GstPadDirection dir;
+ gchar *element_name;
+ gchar *target_element_name;
+ const gchar *color_name;
+
+ dir = gst_pad_get_direction (pad);
+ element_name = debug_dump_make_object_name (GST_OBJECT (element));
+ if (GST_IS_GHOST_PAD (pad)) {
+ color_name =
+ (dir == GST_PAD_SRC) ? "#ffdddd" : ((dir ==
+ GST_PAD_SINK) ? "#ddddff" : "#ffffff");
+ /* output target-pad so that it belongs to this element */
+ if ((tmp_pad = gst_ghost_pad_get_target (GST_GHOST_PAD (pad)))) {
+ if ((target_pad = gst_pad_get_peer (tmp_pad))) {
+ if ((target_element = gst_pad_get_parent_element (target_pad))) {
+ target_element_name =
+ debug_dump_make_object_name (GST_OBJECT (target_element));
+ } else {
+ target_element_name = g_strdup ("");
+ }
+ debug_dump_pad (target_pad, color_name, target_element_name, details,
+ out, indent);
+ g_free (target_element_name);
+ if (target_element)
+ gst_object_unref (target_element);
+ gst_object_unref (target_pad);
+ }
+ gst_object_unref (tmp_pad);
+ }
+ } else {
+ color_name =
+ (dir == GST_PAD_SRC) ? "#ffaaaa" : ((dir ==
+ GST_PAD_SINK) ? "#aaaaff" : "#cccccc");
+ }
+ /* pads */
+ debug_dump_pad (pad, color_name, element_name, details, out, indent);
+ g_free (element_name);
+}
+
+static gboolean
+string_append_field (GQuark field, const GValue * value, gpointer ptr)
+{
+ GString *str = (GString *) ptr;
+ gchar *value_str = gst_value_serialize (value);
+ gchar *esc_value_str;
+
+ /* some enums can become really long */
+ if (strlen (value_str) > 25) {
+ gint pos = 24;
+
+ /* truncate */
+ value_str[25] = '\0';
+
+ /* mirror any brackets and quotes */
+ if (value_str[0] == '<')
+ value_str[pos--] = '>';
+ if (value_str[0] == '[')
+ value_str[pos--] = ']';
+ if (value_str[0] == '(')
+ value_str[pos--] = ')';
+ if (value_str[0] == '{')
+ value_str[pos--] = '}';
+ if (value_str[0] == '"')
+ value_str[pos--] = '"';
+ if (pos != 24)
+ value_str[pos--] = ' ';
+ /* elippsize */
+ value_str[pos--] = '.';
+ value_str[pos--] = '.';
+ value_str[pos--] = '.';
+ }
+ esc_value_str = g_strescape (value_str, NULL);
+
+ g_string_append_printf (str, " %18s: %s\\l", g_quark_to_string (field),
+ esc_value_str);
+
+ g_free (value_str);
+ g_free (esc_value_str);
+ return TRUE;
+}
+
+static gchar *
+debug_dump_describe_caps (GstCaps * caps, GstDebugGraphDetails details)
+{
+ gchar *media = NULL;
+
+ if (details & GST_DEBUG_GRAPH_SHOW_CAPS_DETAILS) {
+
+ if (gst_caps_is_any (caps) || gst_caps_is_empty (caps)) {
+ media = gst_caps_to_string (caps);
+
+ } else {
+ GString *str = NULL;
+ guint i;
+ guint slen = 0;
+
+ for (i = 0; i < gst_caps_get_size (caps); i++) {
+ slen += 25 +
+ STRUCTURE_ESTIMATED_STRING_LEN (gst_caps_get_structure (caps, i));
+ }
+
+ str = g_string_sized_new (slen);
+ for (i = 0; i < gst_caps_get_size (caps); i++) {
+ GstStructure *structure = gst_caps_get_structure (caps, i);
+
+ g_string_append (str, gst_structure_get_name (structure));
+ g_string_append (str, "\\l");
+
+ gst_structure_foreach (structure, string_append_field, (gpointer) str);
+ }
+
+ media = g_string_free (str, FALSE);
+ }
+
+ } else {
+ if (GST_CAPS_IS_SIMPLE (caps))
+ media =
+ g_strdup (gst_structure_get_name (gst_caps_get_structure (caps, 0)));
+ else
+ media = g_strdup ("*");
+ }
+ return media;
+}
+
+static void
+debug_dump_element_pad_link (GstPad * pad, GstElement * element,
+ GstDebugGraphDetails details, FILE * out, const gint indent)
+{
+ GstElement *peer_element, *target_element;
+ GstPad *peer_pad, *target_pad, *tmp_pad;
+ GstCaps *caps, *peer_caps;
+ gchar *media = NULL;
+ gchar *media_src = NULL, *media_sink = NULL;
+ gchar *pad_name, *element_name;
+ gchar *peer_pad_name, *peer_element_name;
+ gchar *target_pad_name, *target_element_name;
+ const gchar *spc = &spaces[MAX (sizeof (spaces) - (1 + indent * 2), 0)];
+
+ if ((peer_pad = gst_pad_get_peer (pad))) {
+ if ((details & GST_DEBUG_GRAPH_SHOW_MEDIA_TYPE) ||
+ (details & GST_DEBUG_GRAPH_SHOW_CAPS_DETAILS)
+ ) {
+ caps = gst_pad_get_current_caps (pad);
+ if (!caps)
+ caps = gst_caps_copy (gst_pad_get_pad_template_caps (pad));
+ peer_caps = gst_pad_get_current_caps (peer_pad);
+ if (!peer_caps)
+ peer_caps = gst_caps_copy (gst_pad_get_pad_template_caps (peer_pad));
+
+ media = debug_dump_describe_caps (caps, details);
+ /* check if peer caps are different */
+ if (peer_caps && !gst_caps_is_equal (caps, peer_caps)) {
+ gchar *tmp;
+
+ tmp = debug_dump_describe_caps (peer_caps, details);
+ if (gst_pad_get_direction (pad) == GST_PAD_SRC) {
+ media_src = media;
+ media_sink = tmp;
+ } else {
+ media_src = tmp;
+ media_sink = media;
+ }
+ media = NULL;
+ }
+ gst_caps_unref (peer_caps);
+ gst_caps_unref (caps);
+ }
+
+ pad_name = debug_dump_make_object_name (GST_OBJECT (pad));
+ if (element) {
+ element_name = debug_dump_make_object_name (GST_OBJECT (element));
+ } else {
+ element_name = g_strdup ("");
+ }
+ peer_pad_name = debug_dump_make_object_name (GST_OBJECT (peer_pad));
+ if ((peer_element = gst_pad_get_parent_element (peer_pad))) {
+ peer_element_name =
+ debug_dump_make_object_name (GST_OBJECT (peer_element));
+ } else {
+ peer_element_name = g_strdup ("");
+ }
+
+ if (GST_IS_GHOST_PAD (pad)) {
+ if ((tmp_pad = gst_ghost_pad_get_target (GST_GHOST_PAD (pad)))) {
+ if ((target_pad = gst_pad_get_peer (tmp_pad))) {
+ target_pad_name =
+ debug_dump_make_object_name (GST_OBJECT (target_pad));
+ if ((target_element = gst_pad_get_parent_element (target_pad))) {
+ target_element_name =
+ debug_dump_make_object_name (GST_OBJECT (target_element));
+ } else {
+ target_element_name = g_strdup ("");
+ }
+ /* src ghostpad relationship */
+ fprintf (out, "%s%s_%s -> %s_%s [style=dashed, minlen=0]\n", spc,
+ target_element_name, target_pad_name, element_name, pad_name);
+
+ g_free (target_pad_name);
+ g_free (target_element_name);
+ if (target_element)
+ gst_object_unref (target_element);
+ gst_object_unref (target_pad);
+ }
+ gst_object_unref (tmp_pad);
+ }
+ }
+ if (GST_IS_GHOST_PAD (peer_pad)) {
+ if ((tmp_pad = gst_ghost_pad_get_target (GST_GHOST_PAD (peer_pad)))) {
+ if ((target_pad = gst_pad_get_peer (tmp_pad))) {
+ target_pad_name =
+ debug_dump_make_object_name (GST_OBJECT (target_pad));
+ if ((target_element = gst_pad_get_parent_element (target_pad))) {
+ target_element_name =
+ debug_dump_make_object_name (GST_OBJECT (target_element));
+ } else {
+ target_element_name = g_strdup ("");
+ }
+ /* sink ghostpad relationship */
+ fprintf (out, "%s%s_%s -> %s_%s [style=dashed, minlen=0]\n", spc,
+ peer_element_name, peer_pad_name,
+ target_element_name, target_pad_name);
+ /* FIXME: we are missing links from the proxy pad
+ * theoretically we need to:
+ * pad=gst_object_ref(target_pad);
+ * goto line 280: if ((peer_pad = gst_pad_get_peer (pad)))
+ * as this would be ugly we need to refactor ...
+ */
+ debug_dump_element_pad_link (target_pad, target_element, details, out,
+ indent);
+ g_free (target_pad_name);
+ g_free (target_element_name);
+ if (target_element)
+ gst_object_unref (target_element);
+ gst_object_unref (target_pad);
+ }
+ gst_object_unref (tmp_pad);
+ }
+ }
+
+ /* pad link */
+ if (media) {
+ fprintf (out, "%s%s_%s -> %s_%s [label=\"%s\"]\n", spc,
+ element_name, pad_name, peer_element_name, peer_pad_name, media);
+ g_free (media);
+ } else if (media_src && media_sink) {
+ /* dot has some issues with placement of head and taillabels,
+ * we need an empty label to make space */
+ fprintf (out, "%s%s_%s -> %s_%s [labeldistance=\"10\", labelangle=\"0\", "
+ "label=\" \", "
+ "headlabel=\"%s\", taillabel=\"%s\"]\n",
+ spc, element_name, pad_name, peer_element_name, peer_pad_name,
+ media_src, media_sink);
+ g_free (media_src);
+ g_free (media_sink);
+ } else {
+ fprintf (out, "%s%s_%s -> %s_%s\n", spc,
+ element_name, pad_name, peer_element_name, peer_pad_name);
+ }
+
+ g_free (pad_name);
+ g_free (element_name);
+ g_free (peer_pad_name);
+ g_free (peer_element_name);
+ if (peer_element)
+ gst_object_unref (peer_element);
+ gst_object_unref (peer_pad);
+ }
+}
+
+static void
+debug_dump_element_pads (GstIterator * pad_iter, GstPad * pad,
+ GstElement * element, GstDebugGraphDetails details, FILE * out,
+ const gint indent, guint * src_pads, guint * sink_pads)
+{
+ GValue item = { 0, };
+ gboolean pads_done;
+ GstPadDirection dir;
+
+ pads_done = FALSE;
+ while (!pads_done) {
+ switch (gst_iterator_next (pad_iter, &item)) {
+ case GST_ITERATOR_OK:
+ pad = g_value_get_object (&item);
+ debug_dump_element_pad (pad, element, details, out, indent);
+ dir = gst_pad_get_direction (pad);
+ if (dir == GST_PAD_SRC)
+ (*src_pads)++;
+ else if (dir == GST_PAD_SINK)
+ (*sink_pads)++;
+ g_value_reset (&item);
+ break;
+ case GST_ITERATOR_RESYNC:
+ gst_iterator_resync (pad_iter);
+ break;
+ case GST_ITERATOR_ERROR:
+ case GST_ITERATOR_DONE:
+ pads_done = TRUE;
+ break;
+ }
+ }
+}
+
+/*
+ * debug_dump_element:
+ * @bin: the bin that should be analyzed
+ * @out: file to write to
+ * @indent: level of graph indentation
+ *
+ * Helper for _gst_debug_bin_to_dot_file() to recursively dump a pipeline.
+ */
+static void
+debug_dump_element (GstBin * bin, GstDebugGraphDetails details, FILE * out,
+ const gint indent)
+{
+ GstIterator *element_iter, *pad_iter;
+ gboolean elements_done, pads_done;
+ GValue item = { 0, };
+ GValue item2 = { 0, };
+ GstElement *element;
+ GstPad *pad = NULL;
+ guint src_pads, sink_pads;
+ gchar *element_name;
+ gchar *state_name = NULL;
+ gchar *param_name = NULL;
+ const gchar *spc = &spaces[MAX (sizeof (spaces) - (1 + indent * 2), 0)];
+
+ element_iter = gst_bin_iterate_elements (bin);
+ elements_done = FALSE;
+ while (!elements_done) {
+ switch (gst_iterator_next (element_iter, &item)) {
+ case GST_ITERATOR_OK:
+ element = g_value_get_object (&item);
+ element_name = debug_dump_make_object_name (GST_OBJECT (element));
+
+ if (details & GST_DEBUG_GRAPH_SHOW_STATES) {
+ state_name = debug_dump_get_element_state (GST_ELEMENT (element));
+ }
+ if (details & GST_DEBUG_GRAPH_SHOW_NON_DEFAULT_PARAMS) {
+ param_name = debug_dump_get_element_params (GST_ELEMENT (element));
+ }
+ /* elements */
+ fprintf (out, "%ssubgraph cluster_%s {\n", spc, element_name);
+ fprintf (out, "%s fontname=\"Bitstream Vera Sans\";\n", spc);
+ fprintf (out, "%s fontsize=\"8\";\n", spc);
+ fprintf (out, "%s style=filled;\n", spc);
+ fprintf (out, "%s color=black;\n\n", spc);
+ fprintf (out, "%s label=\"%s\\n%s%s%s\";\n", spc,
+ G_OBJECT_TYPE_NAME (element), GST_OBJECT_NAME (element),
+ (state_name ? state_name : ""), (param_name ? param_name : "")
+ );
+ if (state_name) {
+ g_free (state_name);
+ state_name = NULL;
+ }
+ if (param_name) {
+ g_free (param_name);
+ param_name = NULL;
+ }
+ g_free (element_name);
+
+ src_pads = sink_pads = 0;
+ if ((pad_iter = gst_element_iterate_sink_pads (element))) {
+ debug_dump_element_pads (pad_iter, pad, element, details, out, indent,
+ &src_pads, &sink_pads);
+ gst_iterator_free (pad_iter);
+ }
+ if ((pad_iter = gst_element_iterate_src_pads (element))) {
+ debug_dump_element_pads (pad_iter, pad, element, details, out, indent,
+ &src_pads, &sink_pads);
+ gst_iterator_free (pad_iter);
+ }
+ if (GST_IS_BIN (element)) {
+ fprintf (out, "%s fillcolor=\"#ffffff\";\n", spc);
+ /* recurse */
+ debug_dump_element (GST_BIN (element), details, out, indent + 1);
+ } else {
+ if (src_pads && !sink_pads)
+ fprintf (out, "%s fillcolor=\"#ffaaaa\";\n", spc);
+ else if (!src_pads && sink_pads)
+ fprintf (out, "%s fillcolor=\"#aaaaff\";\n", spc);
+ else if (src_pads && sink_pads)
+ fprintf (out, "%s fillcolor=\"#aaffaa\";\n", spc);
+ else
+ fprintf (out, "%s fillcolor=\"#ffffff\";\n", spc);
+ }
+ fprintf (out, "%s}\n\n", spc);
+ if ((pad_iter = gst_element_iterate_pads (element))) {
+ pads_done = FALSE;
+ while (!pads_done) {
+ switch (gst_iterator_next (pad_iter, &item2)) {
+ case GST_ITERATOR_OK:
+ pad = g_value_get_object (&item2);
+ if (gst_pad_is_linked (pad)
+ && gst_pad_get_direction (pad) == GST_PAD_SRC) {
+ debug_dump_element_pad_link (pad, element, details, out,
+ indent);
+ }
+ g_value_reset (&item2);
+ break;
+ case GST_ITERATOR_RESYNC:
+ gst_iterator_resync (pad_iter);
+ break;
+ case GST_ITERATOR_ERROR:
+ case GST_ITERATOR_DONE:
+ pads_done = TRUE;
+ break;
+ }
+ }
+ g_value_unset (&item2);
+ gst_iterator_free (pad_iter);
+ }
+ g_value_reset (&item);
+ break;
+ case GST_ITERATOR_RESYNC:
+ gst_iterator_resync (element_iter);
+ break;
+ case GST_ITERATOR_ERROR:
+ case GST_ITERATOR_DONE:
+ elements_done = TRUE;
+ break;
+ }
+ }
+ g_value_unset (&item);
+ gst_iterator_free (element_iter);
+}
+
+/*
+ * _gst_debug_bin_to_dot_file:
+ * @bin: the top-level pipeline that should be analyzed
+ * @file_name: output base filename (e.g. "myplayer")
+ *
+ * To aid debugging applications one can use this method to write out the whole
+ * network of gstreamer elements that form the pipeline into an dot file.
+ * This file can be processed with graphviz to get an image.
+ * <informalexample><programlisting>
+ * dot -Tpng -oimage.png graph_lowlevel.dot
+ * </programlisting></informalexample>
+ */
+void
+_gst_debug_bin_to_dot_file (GstBin * bin, GstDebugGraphDetails details,
+ const gchar * file_name)
+{
+ gchar *full_file_name = NULL;
+ FILE *out;
+
+ g_return_if_fail (GST_IS_BIN (bin));
+
+ if (G_LIKELY (priv_gst_dump_dot_dir == NULL))
+ return;
+
+ if (!file_name) {
+ file_name = g_get_application_name ();
+ if (!file_name)
+ file_name = "unnamed";
+ }
+
+ full_file_name = g_strdup_printf ("%s" G_DIR_SEPARATOR_S "%s.dot",
+ priv_gst_dump_dot_dir, file_name);
+
+ if ((out = fopen (full_file_name, "wb"))) {
+ gchar *state_name = NULL;
+ gchar *param_name = NULL;
+
+ if (details & GST_DEBUG_GRAPH_SHOW_STATES) {
+ state_name = debug_dump_get_element_state (GST_ELEMENT (bin));
+ }
+ if (details & GST_DEBUG_GRAPH_SHOW_NON_DEFAULT_PARAMS) {
+ param_name = debug_dump_get_element_params (GST_ELEMENT (bin));
+ }
+
+ /* write header */
+ fprintf (out,
+ "digraph pipeline {\n"
+ " rankdir=LR;\n"
+ " fontname=\"sans\";\n"
+ " fontsize=\"10\";\n"
+ " labelloc=t;\n"
+ " nodesep=.1;\n"
+ " ranksep=.2;\n"
+ " label=\"<%s>\\n%s%s%s\";\n"
+ " node [style=filled, shape=box, fontsize=\"9\", fontname=\"sans\", margin=\"0.0,0.0\"];\n"
+ " edge [labelfontsize=\"6\", fontsize=\"9\", fontname=\"monospace\"];\n"
+ "\n", G_OBJECT_TYPE_NAME (bin), GST_OBJECT_NAME (bin),
+ (state_name ? state_name : ""), (param_name ? param_name : "")
+ );
+ if (state_name)
+ g_free (state_name);
+ if (param_name)
+ g_free (param_name);
+
+ debug_dump_element (bin, details, out, 1);
+
+ /* write footer */
+ fprintf (out, "}\n");
+ fclose (out);
+ GST_INFO ("wrote bin graph to : '%s'", full_file_name);
+ } else {
+ GST_WARNING ("Failed to open file '%s' for writing: %s", full_file_name,
+ g_strerror (errno));
+ }
+ g_free (full_file_name);
+}
+
+/*
+ * _gst_debug_bin_to_dot_file_with_ts:
+ * @bin: the top-level pipeline that should be analyzed
+ * @file_name: output base filename (e.g. "myplayer")
+ *
+ * This works like _gst_debug_bin_to_dot_file(), but adds the current timestamp
+ * to the filename, so that it can be used to take multiple snapshots.
+ */
+void
+_gst_debug_bin_to_dot_file_with_ts (GstBin * bin, GstDebugGraphDetails details,
+ const gchar * file_name)
+{
+ gchar *ts_file_name = NULL;
+ GstClockTime elapsed;
+
+ g_return_if_fail (GST_IS_BIN (bin));
+
+ if (!file_name) {
+ file_name = g_get_application_name ();
+ if (!file_name)
+ file_name = "unnamed";
+ }
+
+ /* add timestamp */
+ elapsed = GST_CLOCK_DIFF (_priv_gst_info_start_time,
+ gst_util_get_timestamp ());
+
+ /* we don't use GST_TIME_FORMAT as such filenames would fail on some
+ * filesystems like fat */
+ ts_file_name =
+ g_strdup_printf ("%u.%02u.%02u.%09u-%s", GST_TIME_ARGS (elapsed),
+ file_name);
+
+ _gst_debug_bin_to_dot_file (bin, details, ts_file_name);
+ g_free (ts_file_name);
+}
+#else /* !GST_DISABLE_GST_DEBUG */
+#ifndef GST_REMOVE_DISABLED
+void
+_gst_debug_bin_to_dot_file (GstBin * bin, GstDebugGraphDetails details,
+ const gchar * file_name)
+{
+}
+
+void
+_gst_debug_bin_to_dot_file_with_ts (GstBin * bin, GstDebugGraphDetails details,
+ const gchar * file_name)
+{
+}
+#endif /* GST_REMOVE_DISABLED */
+#endif /* GST_DISABLE_GST_DEBUG */
diff --git a/gst/gstdebugutils.h b/gst/gstdebugutils.h
new file mode 100644
index 0000000..1f18308
--- /dev/null
+++ b/gst/gstdebugutils.h
@@ -0,0 +1,109 @@
+/* GStreamer
+ * Copyright (C) 2007 Stefan Kost <ensonic@users.sf.net>
+ *
+ * gstdebugutils.h: debugging and analysis utillities
+ *
+ * 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 __GSTDEBUGUTILS_H__
+#define __GSTDEBUGUTILS_H__
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gst/gstconfig.h>
+#include <gst/gstbin.h>
+
+G_BEGIN_DECLS
+
+/**
+ * GstDebugGraphDetails:
+ * @GST_DEBUG_GRAPH_SHOW_MEDIA_TYPE: show caps-name on edges
+ * @GST_DEBUG_GRAPH_SHOW_CAPS_DETAILS: show caps-details on edges
+ * @GST_DEBUG_GRAPH_SHOW_NON_DEFAULT_PARAMS: show modified parameters on elements
+ * @GST_DEBUG_GRAPH_SHOW_STATES: show element states
+ * @GST_DEBUG_GRAPH_SHOW_ALL: show all details
+ *
+ * Available details for pipeline graphs produced by GST_DEBUG_BIN_TO_DOT_FILE()
+ * and GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS().
+ *
+ * Since: 0.10.15
+ */
+typedef enum {
+ GST_DEBUG_GRAPH_SHOW_MEDIA_TYPE = (1<<0),
+ GST_DEBUG_GRAPH_SHOW_CAPS_DETAILS = (1<<1),
+ GST_DEBUG_GRAPH_SHOW_NON_DEFAULT_PARAMS = (1<<2),
+ GST_DEBUG_GRAPH_SHOW_STATES = (1<<3),
+ GST_DEBUG_GRAPH_SHOW_ALL = ((1<<4)-1)
+} GstDebugGraphDetails;
+
+
+/********** pipeline graphs **********/
+
+void _gst_debug_bin_to_dot_file (GstBin *bin, GstDebugGraphDetails details, const gchar *file_name);
+void _gst_debug_bin_to_dot_file_with_ts (GstBin *bin, GstDebugGraphDetails details, const gchar *file_name);
+
+#ifndef GST_DISABLE_GST_DEBUG
+
+/**
+ * GST_DEBUG_BIN_TO_DOT_FILE:
+ * @bin: the top-level pipeline that should be analyzed
+ * @details: details to show in the graph, e.g. #GST_DEBUG_GRAPH_SHOW_ALL or
+ * one or more other #GstDebugGraphDetails flags.
+ * @file_name: output base filename (e.g. "myplayer")
+ *
+ * To aid debugging applications one can use this method to write out the whole
+ * network of gstreamer elements that form the pipeline into an dot file.
+ * This file can be processed with graphviz to get an image.
+ * <informalexample><programlisting>
+ * dot -Tpng -oimage.png graph_lowlevel.dot
+ * </programlisting></informalexample>
+ *
+ * The macro is only active if gstreamer is configured with
+ * &quot;--gst-enable-gst-debug&quot; and the environment variable
+ * GST_DEBUG_DUMP_DOT_DIR is set to a basepath (e.g. /tmp).
+ *
+ * Since: 0.10.15
+ */
+#define GST_DEBUG_BIN_TO_DOT_FILE(bin, details, file_name) _gst_debug_bin_to_dot_file (bin, details, file_name)
+
+/**
+ * GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS:
+ * @bin: the top-level pipeline that should be analyzed
+ * @details: details to show in the graph, e.g. #GST_DEBUG_GRAPH_SHOW_ALL or
+ * one or more other #GstDebugGraphDetails flags.
+ * @file_name: output base filename (e.g. "myplayer")
+ *
+ * This works like GST_DEBUG_BIN_TO_DOT_FILE(), but adds the current timestamp
+ * to the filename, so that it can be used to take multiple snapshots.
+ *
+ * Since: 0.10.15
+ */
+#define GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(bin, details, file_name) _gst_debug_bin_to_dot_file_with_ts (bin, details, file_name)
+
+
+#else /* GST_DISABLE_GST_DEBUG */
+
+
+#define GST_DEBUG_BIN_TO_DOT_FILE(bin, details, file_name)
+#define GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(bin, details, file_name)
+
+#endif /* GST_DISABLE_GST_DEBUG */
+
+G_END_DECLS
+
+#endif /* __GSTDEBUGUTILS_H__ */
+
diff --git a/gst/gstelement.c b/gst/gstelement.c
new file mode 100644
index 0000000..91b12be
--- /dev/null
+++ b/gst/gstelement.c
@@ -0,0 +1,2992 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2004 Wim Taymans <wim@fluendo.com>
+ *
+ * gstelement.c: The base element, all elements derive from this
+ *
+ * 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:gstelement
+ * @short_description: Abstract base class for all pipeline elements
+ * @see_also: #GstElementFactory, #GstPad
+ *
+ * GstElement is the abstract base class needed to construct an element that
+ * can be used in a GStreamer pipeline. Please refer to the plugin writers
+ * guide for more information on creating #GstElement subclasses.
+ *
+ * The name of a #GstElement can be get with gst_element_get_name() and set with
+ * gst_element_set_name(). For speed, GST_ELEMENT_NAME() can be used in the
+ * core when using the appropriate locking. Do not use this in plug-ins or
+ * applications in order to retain ABI compatibility.
+ *
+ * All elements have pads (of the type #GstPad). These pads link to pads on
+ * other elements. #GstBuffer flow between these linked pads.
+ * A #GstElement has a #GList of #GstPad structures for all their input (or sink)
+ * and output (or source) pads.
+ * Core and plug-in writers can add and remove pads with gst_element_add_pad()
+ * and gst_element_remove_pad().
+ *
+ * An existing pad of an element can be retrieved by name with
+ * gst_element_get_static_pad(). A new dynamic pad can be created using
+ * gst_element_request_pad() with a #GstPadTemplate or
+ * gst_element_get_request_pad() with the template name such as "src_\%d".
+ * An iterator of all pads can be retrieved with gst_element_iterate_pads().
+ *
+ * Elements can be linked through their pads.
+ * If the link is straightforward, use the gst_element_link()
+ * convenience function to link two elements, or gst_element_link_many()
+ * for more elements in a row.
+ * Use gst_element_link_filtered() to link two elements constrained by
+ * a specified set of #GstCaps.
+ * For finer control, use gst_element_link_pads() and
+ * gst_element_link_pads_filtered() to specify the pads to link on
+ * each element by name.
+ *
+ * Each element has a state (see #GstState). You can get and set the state
+ * of an element with gst_element_get_state() and gst_element_set_state().
+ * Setting a state triggers a #GstStateChange. To get a string representation
+ * of a #GstState, use gst_element_state_get_name().
+ *
+ * You can get and set a #GstClock on an element using gst_element_get_clock()
+ * and gst_element_set_clock().
+ * Some elements can provide a clock for the pipeline if
+ * gst_element_provides_clock() returns %TRUE. With the
+ * gst_element_provide_clock() method one can retrieve the clock provided by
+ * such an element.
+ * Not all elements require a clock to operate correctly. If
+ * gst_element_requires_clock() returns %TRUE, a clock should be set on the
+ * element with gst_element_set_clock().
+ *
+ * Note that clock slection and distribution is normally handled by the
+ * toplevel #GstPipeline so the clock functions are only to be used in very
+ * specific situations.
+ *
+ * Last reviewed on 2009-05-29 (0.10.24)
+ */
+
+#include "gst_private.h"
+#include <glib.h>
+#include <stdarg.h>
+#include <gobject/gvaluecollector.h>
+
+#include "gstelement.h"
+#include "gstelementmetadata.h"
+#include "gstenumtypes.h"
+#include "gstbus.h"
+#include "gstmarshal.h"
+#include "gsterror.h"
+#include "gstevent.h"
+#include "gstutils.h"
+#include "gstinfo.h"
+#include "gstvalue.h"
+#include "gst-i18n-lib.h"
+
+/* Element signals and args */
+enum
+{
+ PAD_ADDED,
+ PAD_REMOVED,
+ NO_MORE_PADS,
+ /* add more above */
+ LAST_SIGNAL
+};
+
+enum
+{
+ ARG_0
+ /* FILL ME */
+};
+
+static void gst_element_class_init (GstElementClass * klass);
+static void gst_element_init (GstElement * element);
+static void gst_element_base_class_init (gpointer g_class);
+static void gst_element_base_class_finalize (gpointer g_class);
+
+static void gst_element_dispose (GObject * object);
+static void gst_element_finalize (GObject * object);
+
+static GstStateChangeReturn gst_element_change_state_func (GstElement * element,
+ GstStateChange transition);
+static GstStateChangeReturn gst_element_get_state_func (GstElement * element,
+ GstState * state, GstState * pending, GstClockTime timeout);
+static GstStateChangeReturn gst_element_set_state_func (GstElement * element,
+ GstState state);
+static void gst_element_set_bus_func (GstElement * element, GstBus * bus);
+
+static gboolean gst_element_default_send_event (GstElement * element,
+ GstEvent * event);
+static gboolean gst_element_default_query (GstElement * element,
+ GstQuery * query);
+
+static GstPadTemplate
+ * gst_element_class_get_request_pad_template (GstElementClass *
+ element_class, const gchar * name);
+
+static GstObjectClass *parent_class = NULL;
+static guint gst_element_signals[LAST_SIGNAL] = { 0 };
+
+/* this is used in gstelementfactory.c:gst_element_register() */
+GQuark _gst_elementclass_factory = 0;
+
+GType
+gst_element_get_type (void)
+{
+ static volatile gsize gst_element_type = 0;
+
+ if (g_once_init_enter (&gst_element_type)) {
+ GType _type;
+ static const GTypeInfo element_info = {
+ sizeof (GstElementClass),
+ gst_element_base_class_init,
+ gst_element_base_class_finalize,
+ (GClassInitFunc) gst_element_class_init,
+ NULL,
+ NULL,
+ sizeof (GstElement),
+ 0,
+ (GInstanceInitFunc) gst_element_init,
+ NULL
+ };
+
+ _type = g_type_register_static (GST_TYPE_OBJECT, "GstElement",
+ &element_info, G_TYPE_FLAG_ABSTRACT);
+
+ _gst_elementclass_factory =
+ g_quark_from_static_string ("GST_ELEMENTCLASS_FACTORY");
+ g_once_init_leave (&gst_element_type, _type);
+ }
+ return gst_element_type;
+}
+
+static void
+gst_element_class_init (GstElementClass * klass)
+{
+ GObjectClass *gobject_class;
+
+ gobject_class = (GObjectClass *) klass;
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ /**
+ * GstElement::pad-added:
+ * @gstelement: the object which received the signal
+ * @new_pad: the pad that has been added
+ *
+ * a new #GstPad has been added to the element. Note that this signal will
+ * usually be emitted from the context of the streaming thread. Also keep in
+ * mind that if you add new elements to the pipeline in the signal handler
+ * you will need to set them to the desired target state with
+ * gst_element_set_state() or gst_element_sync_state_with_parent().
+ */
+ gst_element_signals[PAD_ADDED] =
+ g_signal_new ("pad-added", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GstElementClass, pad_added), NULL, NULL,
+ gst_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GST_TYPE_PAD);
+ /**
+ * GstElement::pad-removed:
+ * @gstelement: the object which received the signal
+ * @old_pad: the pad that has been removed
+ *
+ * a #GstPad has been removed from the element
+ */
+ gst_element_signals[PAD_REMOVED] =
+ g_signal_new ("pad-removed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GstElementClass, pad_removed), NULL, NULL,
+ gst_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GST_TYPE_PAD);
+ /**
+ * GstElement::no-more-pads:
+ * @gstelement: the object which received the signal
+ *
+ * This signals that the element will not generate more dynamic pads.
+ * Note that this signal will usually be emitted from the context of
+ * the streaming thread.
+ */
+ gst_element_signals[NO_MORE_PADS] =
+ g_signal_new ("no-more-pads", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstElementClass, no_more_pads), NULL,
+ NULL, gst_marshal_VOID__VOID, G_TYPE_NONE, 0);
+
+ gobject_class->dispose = gst_element_dispose;
+ gobject_class->finalize = gst_element_finalize;
+
+ klass->change_state = GST_DEBUG_FUNCPTR (gst_element_change_state_func);
+ klass->set_state = GST_DEBUG_FUNCPTR (gst_element_set_state_func);
+ klass->get_state = GST_DEBUG_FUNCPTR (gst_element_get_state_func);
+ klass->set_bus = GST_DEBUG_FUNCPTR (gst_element_set_bus_func);
+ klass->query = GST_DEBUG_FUNCPTR (gst_element_default_query);
+ klass->send_event = GST_DEBUG_FUNCPTR (gst_element_default_send_event);
+ klass->numpadtemplates = 0;
+
+ klass->elementfactory = NULL;
+}
+
+static void
+gst_element_base_class_init (gpointer g_class)
+{
+ GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+ GList *node, *padtemplates;
+
+ /* Copy the element details here so elements can inherit the
+ * details from their base class and classes only need to set
+ * the details in class_init instead of base_init */
+ element_class->metadata =
+ element_class->metadata ? gst_structure_copy (element_class->metadata) :
+ gst_structure_empty_new ("metadata");
+
+ /* Copy the pad templates so elements inherit them
+ * from their base class but elements can add pad templates in class_init
+ * instead of base_init.
+ */
+ padtemplates = g_list_copy (element_class->padtemplates);
+ for (node = padtemplates; node != NULL; node = node->next) {
+ GstPadTemplate *tmpl = (GstPadTemplate *) node->data;
+ gst_object_ref (tmpl);
+ }
+ element_class->padtemplates = padtemplates;
+
+ /* set the factory, see gst_element_register() */
+ element_class->elementfactory =
+ g_type_get_qdata (G_TYPE_FROM_CLASS (element_class),
+ _gst_elementclass_factory);
+ GST_DEBUG ("type %s : factory %p", G_OBJECT_CLASS_NAME (element_class),
+ element_class->elementfactory);
+}
+
+static void
+gst_element_base_class_finalize (gpointer g_class)
+{
+ GstElementClass *klass = GST_ELEMENT_CLASS (g_class);
+
+ g_list_foreach (klass->padtemplates, (GFunc) gst_object_unref, NULL);
+ g_list_free (klass->padtemplates);
+
+ gst_structure_free (klass->metadata);
+}
+
+static void
+gst_element_init (GstElement * element)
+{
+ GST_STATE (element) = GST_STATE_NULL;
+ GST_STATE_TARGET (element) = GST_STATE_NULL;
+ GST_STATE_NEXT (element) = GST_STATE_VOID_PENDING;
+ GST_STATE_PENDING (element) = GST_STATE_VOID_PENDING;
+ GST_STATE_RETURN (element) = GST_STATE_CHANGE_SUCCESS;
+
+ g_static_rec_mutex_init (&element->state_lock);
+ element->state_cond = g_cond_new ();
+}
+
+/**
+ * gst_element_release_request_pad:
+ * @element: a #GstElement to release the request pad of.
+ * @pad: the #GstPad to release.
+ *
+ * Makes the element free the previously requested pad as obtained
+ * with gst_element_get_request_pad().
+ *
+ * This does not unref the pad. If the pad was created by using
+ * gst_element_get_request_pad(), gst_element_release_request_pad() needs to be
+ * followed by gst_object_unref() to free the @pad.
+ *
+ * MT safe.
+ */
+void
+gst_element_release_request_pad (GstElement * element, GstPad * pad)
+{
+ GstElementClass *oclass;
+
+ g_return_if_fail (GST_IS_ELEMENT (element));
+ g_return_if_fail (GST_IS_PAD (pad));
+
+ oclass = GST_ELEMENT_GET_CLASS (element);
+
+ /* if the element implements a custom release function we call that, else we
+ * simply remove the pad from the element */
+ if (oclass->release_pad)
+ (oclass->release_pad) (element, pad);
+ else
+ gst_element_remove_pad (element, pad);
+}
+
+/**
+ * gst_element_requires_clock:
+ * @element: a #GstElement to query
+ *
+ * Query if the element requires a clock.
+ *
+ * Returns: %TRUE if the element requires a clock
+ *
+ * MT safe.
+ */
+gboolean
+gst_element_requires_clock (GstElement * element)
+{
+ gboolean result;
+
+ g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
+
+ result = (GST_ELEMENT_GET_CLASS (element)->set_clock != NULL);
+
+ return result;
+}
+
+/**
+ * gst_element_provides_clock:
+ * @element: a #GstElement to query
+ *
+ * Query if the element provides a clock. A #GstClock provided by an
+ * element can be used as the global #GstClock for the pipeline.
+ * An element that can provide a clock is only required to do so in the PAUSED
+ * state, this means when it is fully negotiated and has allocated the resources
+ * to operate the clock.
+ *
+ * Returns: %TRUE if the element provides a clock
+ *
+ * MT safe.
+ */
+gboolean
+gst_element_provides_clock (GstElement * element)
+{
+ gboolean result;
+
+ g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
+
+ result = (GST_ELEMENT_GET_CLASS (element)->provide_clock != NULL);
+
+ return result;
+}
+
+/**
+ * gst_element_provide_clock:
+ * @element: a #GstElement to query
+ *
+ * Get the clock provided by the given element.
+ * <note>An element is only required to provide a clock in the PAUSED
+ * state. Some elements can provide a clock in other states.</note>
+ *
+ * Returns: (transfer full): the GstClock provided by the element or %NULL
+ * if no clock could be provided. Unref after usage.
+ *
+ * MT safe.
+ */
+GstClock *
+gst_element_provide_clock (GstElement * element)
+{
+ GstClock *result = NULL;
+ GstElementClass *oclass;
+
+ g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
+
+ oclass = GST_ELEMENT_GET_CLASS (element);
+
+ if (oclass->provide_clock)
+ result = oclass->provide_clock (element);
+
+ return result;
+}
+
+/**
+ * gst_element_set_clock:
+ * @element: a #GstElement to set the clock for.
+ * @clock: the #GstClock to set for the element.
+ *
+ * Sets the clock for the element. This function increases the
+ * refcount on the clock. Any previously set clock on the object
+ * is unreffed.
+ *
+ * Returns: %TRUE if the element accepted the clock. An element can refuse a
+ * clock when it, for example, is not able to slave its internal clock to the
+ * @clock or when it requires a specific clock to operate.
+ *
+ * MT safe.
+ */
+gboolean
+gst_element_set_clock (GstElement * element, GstClock * clock)
+{
+ GstElementClass *oclass;
+ gboolean res = TRUE;
+ GstClock **clock_p;
+
+ g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
+ g_return_val_if_fail (clock == NULL || GST_IS_CLOCK (clock), FALSE);
+
+ oclass = GST_ELEMENT_GET_CLASS (element);
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, element, "setting clock %p", clock);
+
+ if (oclass->set_clock)
+ res = oclass->set_clock (element, clock);
+
+ if (res) {
+ /* only update the clock pointer if the element accepted the clock */
+ GST_OBJECT_LOCK (element);
+ clock_p = &element->clock;
+ gst_object_replace ((GstObject **) clock_p, (GstObject *) clock);
+ GST_OBJECT_UNLOCK (element);
+ }
+ return res;
+}
+
+/**
+ * gst_element_get_clock:
+ * @element: a #GstElement to get the clock of.
+ *
+ * Gets the currently configured clock of the element. This is the clock as was
+ * last set with gst_element_set_clock().
+ *
+ * Returns: (transfer full): the #GstClock of the element. unref after usage.
+ *
+ * MT safe.
+ */
+GstClock *
+gst_element_get_clock (GstElement * element)
+{
+ GstClock *result;
+
+ g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
+
+ GST_OBJECT_LOCK (element);
+ if ((result = element->clock))
+ gst_object_ref (result);
+ GST_OBJECT_UNLOCK (element);
+
+ return result;
+}
+
+/**
+ * gst_element_set_base_time:
+ * @element: a #GstElement.
+ * @time: the base time to set.
+ *
+ * Set the base time of an element. See gst_element_get_base_time().
+ *
+ * MT safe.
+ */
+void
+gst_element_set_base_time (GstElement * element, GstClockTime time)
+{
+ GstClockTime old;
+
+ g_return_if_fail (GST_IS_ELEMENT (element));
+
+ GST_OBJECT_LOCK (element);
+ old = element->base_time;
+ element->base_time = time;
+ GST_OBJECT_UNLOCK (element);
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, element,
+ "set base_time=%" GST_TIME_FORMAT ", old %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (time), GST_TIME_ARGS (old));
+}
+
+/**
+ * gst_element_get_base_time:
+ * @element: a #GstElement.
+ *
+ * Returns the base time of the element. The base time is the
+ * absolute time of the clock when this element was last put to
+ * PLAYING. Subtracting the base time from the clock time gives
+ * the running time of the element.
+ *
+ * Returns: the base time of the element.
+ *
+ * MT safe.
+ */
+GstClockTime
+gst_element_get_base_time (GstElement * element)
+{
+ GstClockTime result;
+
+ g_return_val_if_fail (GST_IS_ELEMENT (element), GST_CLOCK_TIME_NONE);
+
+ GST_OBJECT_LOCK (element);
+ result = element->base_time;
+ GST_OBJECT_UNLOCK (element);
+
+ return result;
+}
+
+/**
+ * gst_element_set_start_time:
+ * @element: a #GstElement.
+ * @time: the base time to set.
+ *
+ * Set the start time of an element. The start time of the element is the
+ * running time of the element when it last went to the PAUSED state. In READY
+ * or after a flushing seek, it is set to 0.
+ *
+ * Toplevel elements like #GstPipeline will manage the start_time and
+ * base_time on its children. Setting the start_time to #GST_CLOCK_TIME_NONE
+ * on such a toplevel element will disable the distribution of the base_time to
+ * the children and can be useful if the application manages the base_time
+ * itself, for example if you want to synchronize capture from multiple
+ * pipelines, and you can also ensure that the pipelines have the same clock.
+ *
+ * MT safe.
+ *
+ * Since: 0.10.24
+ */
+void
+gst_element_set_start_time (GstElement * element, GstClockTime time)
+{
+ GstClockTime old;
+
+ g_return_if_fail (GST_IS_ELEMENT (element));
+
+ GST_OBJECT_LOCK (element);
+ old = GST_ELEMENT_START_TIME (element);
+ GST_ELEMENT_START_TIME (element) = time;
+ GST_OBJECT_UNLOCK (element);
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, element,
+ "set start_time=%" GST_TIME_FORMAT ", old %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (time), GST_TIME_ARGS (old));
+}
+
+/**
+ * gst_element_get_start_time:
+ * @element: a #GstElement.
+ *
+ * Returns the start time of the element. The start time is the
+ * running time of the clock when this element was last put to PAUSED.
+ *
+ * Usually the start_time is managed by a toplevel element such as
+ * #GstPipeline.
+ *
+ * MT safe.
+ *
+ * Returns: the start time of the element.
+ *
+ * Since: 0.10.24
+ */
+GstClockTime
+gst_element_get_start_time (GstElement * element)
+{
+ GstClockTime result;
+
+ g_return_val_if_fail (GST_IS_ELEMENT (element), GST_CLOCK_TIME_NONE);
+
+ GST_OBJECT_LOCK (element);
+ result = GST_ELEMENT_START_TIME (element);
+ GST_OBJECT_UNLOCK (element);
+
+ return result;
+}
+
+/**
+ * gst_element_is_indexable:
+ * @element: a #GstElement.
+ *
+ * Queries if the element can be indexed.
+ *
+ * Returns: TRUE if the element can be indexed.
+ *
+ * MT safe.
+ */
+gboolean
+gst_element_is_indexable (GstElement * element)
+{
+ gboolean result;
+
+ g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
+
+ result = (GST_ELEMENT_GET_CLASS (element)->set_index != NULL);
+
+ return result;
+}
+
+/**
+ * gst_element_set_index:
+ * @element: a #GstElement.
+ * @index: (transfer none): a #GstIndex.
+ *
+ * Set @index on the element. The refcount of the index
+ * will be increased, any previously set index is unreffed.
+ *
+ * MT safe.
+ */
+void
+gst_element_set_index (GstElement * element, GstIndex * index)
+{
+ GstElementClass *oclass;
+
+ g_return_if_fail (GST_IS_ELEMENT (element));
+ g_return_if_fail (index == NULL || GST_IS_INDEX (index));
+
+ oclass = GST_ELEMENT_GET_CLASS (element);
+
+ if (oclass->set_index)
+ oclass->set_index (element, index);
+}
+
+/**
+ * gst_element_get_index:
+ * @element: a #GstElement.
+ *
+ * Gets the index from the element.
+ *
+ * Returns: (transfer full): a #GstIndex or %NULL when no index was set on the
+ * element. unref after usage.
+ *
+ * MT safe.
+ */
+GstIndex *
+gst_element_get_index (GstElement * element)
+{
+ GstElementClass *oclass;
+ GstIndex *result = NULL;
+
+ g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
+
+ oclass = GST_ELEMENT_GET_CLASS (element);
+
+ if (oclass->get_index)
+ result = oclass->get_index (element);
+
+ return result;
+}
+
+/**
+ * gst_element_add_pad:
+ * @element: a #GstElement to add the pad to.
+ * @pad: (transfer full): the #GstPad to add to the element.
+ *
+ * Adds a pad (link point) to @element. @pad's parent will be set to @element;
+ * see gst_object_set_parent() for refcounting information.
+ *
+ * Pads are not automatically activated so elements should perform the needed
+ * steps to activate the pad in case this pad is added in the PAUSED or PLAYING
+ * state. See gst_pad_set_active() for more information about activating pads.
+ *
+ * The pad and the element should be unlocked when calling this function.
+ *
+ * This function will emit the #GstElement::pad-added signal on the element.
+ *
+ * Returns: %TRUE if the pad could be added. This function can fail when
+ * a pad with the same name already existed or the pad already had another
+ * parent.
+ *
+ * MT safe.
+ */
+gboolean
+gst_element_add_pad (GstElement * element, GstPad * pad)
+{
+ gchar *pad_name;
+ gboolean flushing;
+
+ g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
+ g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
+
+ /* locking pad to look at the name */
+ GST_OBJECT_LOCK (pad);
+ pad_name = g_strdup (GST_PAD_NAME (pad));
+ GST_CAT_INFO_OBJECT (GST_CAT_ELEMENT_PADS, element, "adding pad '%s'",
+ GST_STR_NULL (pad_name));
+ flushing = GST_PAD_IS_FLUSHING (pad);
+ GST_OBJECT_UNLOCK (pad);
+
+ /* then check to see if there's already a pad by that name here */
+ GST_OBJECT_LOCK (element);
+ if (G_UNLIKELY (!gst_object_check_uniqueness (element->pads, pad_name)))
+ goto name_exists;
+
+ /* try to set the pad's parent */
+ if (G_UNLIKELY (!gst_object_set_parent (GST_OBJECT_CAST (pad),
+ GST_OBJECT_CAST (element))))
+ goto had_parent;
+
+ /* check for flushing pads */
+ if (flushing && (GST_STATE (element) > GST_STATE_READY ||
+ GST_STATE_NEXT (element) == GST_STATE_PAUSED)) {
+ g_warning ("adding flushing pad '%s' to running element '%s', you need to "
+ "use gst_pad_set_active(pad,TRUE) before adding it.",
+ GST_STR_NULL (pad_name), GST_ELEMENT_NAME (element));
+ /* unset flushing */
+ GST_OBJECT_LOCK (pad);
+ GST_PAD_UNSET_FLUSHING (pad);
+ GST_OBJECT_UNLOCK (pad);
+ }
+
+ g_free (pad_name);
+
+ /* add it to the list */
+ switch (gst_pad_get_direction (pad)) {
+ case GST_PAD_SRC:
+ element->srcpads = g_list_prepend (element->srcpads, pad);
+ element->numsrcpads++;
+ break;
+ case GST_PAD_SINK:
+ element->sinkpads = g_list_prepend (element->sinkpads, pad);
+ element->numsinkpads++;
+ break;
+ default:
+ goto no_direction;
+ }
+ element->pads = g_list_prepend (element->pads, pad);
+ element->numpads++;
+ element->pads_cookie++;
+ GST_OBJECT_UNLOCK (element);
+
+ /* emit the PAD_ADDED signal */
+ g_signal_emit (element, gst_element_signals[PAD_ADDED], 0, pad);
+
+ return TRUE;
+
+ /* ERROR cases */
+name_exists:
+ {
+ g_critical ("Padname %s is not unique in element %s, not adding",
+ pad_name, GST_ELEMENT_NAME (element));
+ GST_OBJECT_UNLOCK (element);
+ g_free (pad_name);
+ return FALSE;
+ }
+had_parent:
+ {
+ g_critical
+ ("Pad %s already has parent when trying to add to element %s",
+ pad_name, GST_ELEMENT_NAME (element));
+ GST_OBJECT_UNLOCK (element);
+ g_free (pad_name);
+ return FALSE;
+ }
+no_direction:
+ {
+ GST_OBJECT_LOCK (pad);
+ g_critical
+ ("Trying to add pad %s to element %s, but it has no direction",
+ GST_OBJECT_NAME (pad), GST_ELEMENT_NAME (element));
+ GST_OBJECT_UNLOCK (pad);
+ GST_OBJECT_UNLOCK (element);
+ return FALSE;
+ }
+}
+
+/**
+ * gst_element_remove_pad:
+ * @element: a #GstElement to remove pad from.
+ * @pad: (transfer none): the #GstPad to remove from the element.
+ *
+ * Removes @pad from @element. @pad will be destroyed if it has not been
+ * referenced elsewhere using gst_object_unparent().
+ *
+ * This function is used by plugin developers and should not be used
+ * by applications. Pads that were dynamically requested from elements
+ * with gst_element_get_request_pad() should be released with the
+ * gst_element_release_request_pad() function instead.
+ *
+ * Pads are not automatically deactivated so elements should perform the needed
+ * steps to deactivate the pad in case this pad is removed in the PAUSED or
+ * PLAYING state. See gst_pad_set_active() for more information about
+ * deactivating pads.
+ *
+ * The pad and the element should be unlocked when calling this function.
+ *
+ * This function will emit the #GstElement::pad-removed signal on the element.
+ *
+ * Returns: %TRUE if the pad could be removed. Can return %FALSE if the
+ * pad does not belong to the provided element.
+ *
+ * MT safe.
+ */
+gboolean
+gst_element_remove_pad (GstElement * element, GstPad * pad)
+{
+ GstPad *peer;
+
+ g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
+ g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
+
+ /* locking pad to look at the name and parent */
+ GST_OBJECT_LOCK (pad);
+ GST_CAT_INFO_OBJECT (GST_CAT_ELEMENT_PADS, element, "removing pad '%s'",
+ GST_STR_NULL (GST_PAD_NAME (pad)));
+
+ if (G_UNLIKELY (GST_PAD_PARENT (pad) != element))
+ goto not_our_pad;
+ GST_OBJECT_UNLOCK (pad);
+
+ /* unlink */
+ if ((peer = gst_pad_get_peer (pad))) {
+ /* window for MT unsafeness, someone else could unlink here
+ * and then we call unlink with wrong pads. The unlink
+ * function would catch this and safely return failed. */
+ if (GST_PAD_IS_SRC (pad))
+ gst_pad_unlink (pad, peer);
+ else
+ gst_pad_unlink (peer, pad);
+
+ gst_object_unref (peer);
+ }
+
+ GST_OBJECT_LOCK (element);
+ /* remove it from the list */
+ switch (gst_pad_get_direction (pad)) {
+ case GST_PAD_SRC:
+ element->srcpads = g_list_remove (element->srcpads, pad);
+ element->numsrcpads--;
+ break;
+ case GST_PAD_SINK:
+ element->sinkpads = g_list_remove (element->sinkpads, pad);
+ element->numsinkpads--;
+ break;
+ default:
+ g_critical ("Removing pad without direction???");
+ break;
+ }
+ element->pads = g_list_remove (element->pads, pad);
+ element->numpads--;
+ element->pads_cookie++;
+ GST_OBJECT_UNLOCK (element);
+
+ /* emit the PAD_REMOVED signal before unparenting and losing the last ref. */
+ g_signal_emit (element, gst_element_signals[PAD_REMOVED], 0, pad);
+
+ gst_object_unparent (GST_OBJECT_CAST (pad));
+
+ return TRUE;
+
+ /* ERRORS */
+not_our_pad:
+ {
+ /* locking order is element > pad */
+ GST_OBJECT_UNLOCK (pad);
+
+ GST_OBJECT_LOCK (element);
+ GST_OBJECT_LOCK (pad);
+ g_critical ("Padname %s:%s does not belong to element %s when removing",
+ GST_DEBUG_PAD_NAME (pad), GST_ELEMENT_NAME (element));
+ GST_OBJECT_UNLOCK (pad);
+ GST_OBJECT_UNLOCK (element);
+ return FALSE;
+ }
+}
+
+/**
+ * gst_element_no_more_pads:
+ * @element: a #GstElement
+ *
+ * Use this function to signal that the element does not expect any more pads
+ * to show up in the current pipeline. This function should be called whenever
+ * pads have been added by the element itself. Elements with #GST_PAD_SOMETIMES
+ * pad templates use this in combination with autopluggers to figure out that
+ * the element is done initializing its pads.
+ *
+ * This function emits the #GstElement::no-more-pads signal.
+ *
+ * MT safe.
+ */
+void
+gst_element_no_more_pads (GstElement * element)
+{
+ g_return_if_fail (GST_IS_ELEMENT (element));
+
+ g_signal_emit (element, gst_element_signals[NO_MORE_PADS], 0);
+}
+
+static gint
+pad_compare_name (GstPad * pad1, const gchar * name)
+{
+ gint result;
+
+ GST_OBJECT_LOCK (pad1);
+ result = strcmp (GST_PAD_NAME (pad1), name);
+ GST_OBJECT_UNLOCK (pad1);
+
+ return result;
+}
+
+/**
+ * gst_element_get_static_pad:
+ * @element: a #GstElement to find a static pad of.
+ * @name: the name of the static #GstPad to retrieve.
+ *
+ * Retrieves a pad from @element by name. This version only retrieves
+ * already-existing (i.e. 'static') pads.
+ *
+ * Returns: (transfer full): the requested #GstPad if found, otherwise %NULL.
+ * unref after usage.
+ *
+ * MT safe.
+ */
+GstPad *
+gst_element_get_static_pad (GstElement * element, const gchar * name)
+{
+ GList *find;
+ GstPad *result = NULL;
+
+ g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
+ g_return_val_if_fail (name != NULL, NULL);
+
+ GST_OBJECT_LOCK (element);
+ find =
+ g_list_find_custom (element->pads, name, (GCompareFunc) pad_compare_name);
+ if (find) {
+ result = GST_PAD_CAST (find->data);
+ gst_object_ref (result);
+ }
+
+ if (result == NULL) {
+ GST_CAT_INFO (GST_CAT_ELEMENT_PADS, "no such pad '%s' in element \"%s\"",
+ name, GST_ELEMENT_NAME (element));
+ } else {
+ GST_CAT_INFO (GST_CAT_ELEMENT_PADS, "found pad %s:%s",
+ GST_ELEMENT_NAME (element), name);
+ }
+ GST_OBJECT_UNLOCK (element);
+
+ return result;
+}
+
+static GstPad *
+_gst_element_request_pad (GstElement * element, GstPadTemplate * templ,
+ const gchar * name, const GstCaps * caps)
+{
+ GstPad *newpad = NULL;
+ GstElementClass *oclass;
+
+ oclass = GST_ELEMENT_GET_CLASS (element);
+
+#ifndef G_DISABLE_CHECKS
+ /* Some sanity checking here */
+ if (name) {
+ GstPad *pad;
+
+ /* Is this the template name? */
+ if (strstr (name, "%") || !strchr (templ->name_template, '%')) {
+ g_return_val_if_fail (strcmp (name, templ->name_template) == 0, NULL);
+ } else {
+ const gchar *str, *data;
+ gchar *endptr;
+
+ /* Otherwise check if it's a valid name for the name template */
+ str = strchr (templ->name_template, '%');
+ g_return_val_if_fail (str != NULL, NULL);
+ g_return_val_if_fail (strncmp (templ->name_template, name,
+ str - templ->name_template) == 0, NULL);
+ g_return_val_if_fail (strlen (name) > str - templ->name_template, NULL);
+
+ data = name + (str - templ->name_template);
+
+ /* Can either be %s or %d or %u, do sanity checking for %d */
+ if (*(str + 1) == 'd') {
+ gint64 tmp;
+
+ /* it's an int */
+ tmp = g_ascii_strtoll (data, &endptr, 10);
+ g_return_val_if_fail (tmp >= G_MININT && tmp <= G_MAXINT
+ && *endptr == '\0', NULL);
+ } else if (*(str + 1) == 'u') {
+ guint64 tmp;
+
+ /* it's an int */
+ tmp = g_ascii_strtoull (data, &endptr, 10);
+ g_return_val_if_fail (tmp <= G_MAXUINT && *endptr == '\0', NULL);
+ }
+ }
+
+ pad = gst_element_get_static_pad (element, name);
+ if (pad) {
+ gst_object_unref (pad);
+ /* FIXME 0.11: Change this to g_return_val_if_fail() */
+ g_critical ("Element %s already has a pad named %s, the behaviour of "
+ " gst_element_get_request_pad() for existing pads is undefined!",
+ GST_ELEMENT_NAME (element), name);
+ }
+ }
+#endif
+
+ if (oclass->request_new_pad)
+ newpad = (oclass->request_new_pad) (element, templ, name, caps);
+
+ if (newpad)
+ gst_object_ref (newpad);
+
+ return newpad;
+}
+
+/**
+ * gst_element_get_request_pad:
+ * @element: a #GstElement to find a request pad of.
+ * @name: the name of the request #GstPad to retrieve.
+ *
+ * Retrieves a pad from the element by name (e.g. "src_\%d"). This version only
+ * retrieves request pads. The pad should be released with
+ * gst_element_release_request_pad().
+ *
+ * This method is slow and will be deprecated in the future. New code should
+ * use gst_element_request_pad() with the requested template.
+ *
+ * Returns: (transfer full): requested #GstPad if found, otherwise %NULL.
+ * Release after usage.
+ */
+GstPad *
+gst_element_get_request_pad (GstElement * element, const gchar * name)
+{
+ GstPadTemplate *templ = NULL;
+ GstPad *pad;
+ const gchar *req_name = NULL;
+ gboolean templ_found = FALSE;
+ GList *list;
+ const gchar *data;
+ gchar *str, *endptr = NULL;
+ GstElementClass *class;
+
+ g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
+ g_return_val_if_fail (name != NULL, NULL);
+
+ class = GST_ELEMENT_GET_CLASS (element);
+
+ /* if the name contains a %, we assume it's the complete template name. Get
+ * the template and try to get a pad */
+ if (strstr (name, "%")) {
+ templ = gst_element_class_get_request_pad_template (class, name);
+ req_name = NULL;
+ if (templ)
+ templ_found = TRUE;
+ } else {
+ /* there is no % in the name, try to find a matching template */
+ list = class->padtemplates;
+ while (!templ_found && list) {
+ templ = (GstPadTemplate *) list->data;
+ if (templ->presence == GST_PAD_REQUEST) {
+ GST_CAT_DEBUG (GST_CAT_PADS, "comparing %s to %s", name,
+ templ->name_template);
+ /* see if we find an exact match */
+ if (strcmp (name, templ->name_template) == 0) {
+ templ_found = TRUE;
+ req_name = name;
+ break;
+ }
+ /* Because of sanity checks in gst_pad_template_new(), we know that %s
+ and %d and %u, occurring at the end of the name_template, are the only
+ possibilities. */
+ else if ((str = strchr (templ->name_template, '%'))
+ && strncmp (templ->name_template, name,
+ str - templ->name_template) == 0
+ && strlen (name) > str - templ->name_template) {
+ data = name + (str - templ->name_template);
+ if (*(str + 1) == 'd') {
+ glong tmp;
+
+ /* it's an int */
+ tmp = strtol (data, &endptr, 10);
+ if (tmp != G_MINLONG && tmp != G_MAXLONG && endptr &&
+ *endptr == '\0') {
+ templ_found = TRUE;
+ req_name = name;
+ break;
+ }
+ } else if (*(str + 1) == 'u') {
+ gulong tmp;
+
+ /* it's an int */
+ tmp = strtoul (data, &endptr, 10);
+ if (tmp != G_MAXULONG && endptr && *endptr == '\0') {
+ templ_found = TRUE;
+ req_name = name;
+ break;
+ }
+ } else {
+ /* it's a string */
+ templ_found = TRUE;
+ req_name = name;
+ break;
+ }
+ }
+ }
+ list = list->next;
+ }
+ }
+
+ if (!templ_found)
+ return NULL;
+
+ pad = _gst_element_request_pad (element, templ, req_name, NULL);
+
+ return pad;
+}
+
+/**
+ * gst_element_request_pad:
+ * @element: a #GstElement to find a request pad of.
+ * @templ: a #GstPadTemplate of which we want a pad of.
+ * @name: (transfer none) (allow-none): the name of the request #GstPad
+ * to retrieve. Can be %NULL.
+ * @caps: (transfer none) (allow-none): the caps of the pad we want to
+ * request. Can be %NULL.
+ *
+ * Retrieves a request pad from the element according to the provided template.
+ * Pad templates can be looked up using
+ * gst_element_factory_get_static_pad_templates().
+ *
+ * If the @caps are specified and the element implements thew new
+ * request_new_pad_full virtual method, the element will use them to select
+ * which pad to create.
+ *
+ * The pad should be released with gst_element_release_request_pad().
+ *
+ * Returns: (transfer full): requested #GstPad if found, otherwise %NULL.
+ * Release after usage.
+ *
+ * Since: 0.10.32
+ */
+GstPad *
+gst_element_request_pad (GstElement * element,
+ GstPadTemplate * templ, const gchar * name, const GstCaps * caps)
+{
+ g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
+ g_return_val_if_fail (templ != NULL, NULL);
+
+ return _gst_element_request_pad (element, templ, name, caps);
+}
+
+static GstIterator *
+gst_element_iterate_pad_list (GstElement * element, GList ** padlist)
+{
+ GstIterator *result;
+
+ GST_OBJECT_LOCK (element);
+ result = gst_iterator_new_list (GST_TYPE_PAD,
+ GST_OBJECT_GET_LOCK (element),
+ &element->pads_cookie, padlist, (GObject *) element, NULL);
+ GST_OBJECT_UNLOCK (element);
+
+ return result;
+}
+
+/**
+ * gst_element_iterate_pads:
+ * @element: a #GstElement to iterate pads of.
+ *
+ * Retrieves an iterator of @element's pads. The iterator should
+ * be freed after usage. Also more specialized iterators exists such as
+ * gst_element_iterate_src_pads() or gst_element_iterate_sink_pads().
+ *
+ * Returns: (transfer full): the #GstIterator of #GstPad. Unref each pad
+ * after use.
+ *
+ * MT safe.
+ */
+GstIterator *
+gst_element_iterate_pads (GstElement * element)
+{
+ g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
+
+ return gst_element_iterate_pad_list (element, &element->pads);
+}
+
+/**
+ * gst_element_iterate_src_pads:
+ * @element: a #GstElement.
+ *
+ * Retrieves an iterator of @element's source pads.
+ *
+ * Returns: (transfer full): the #GstIterator of #GstPad. Unref each pad
+ * after use.
+ *
+ * MT safe.
+ */
+GstIterator *
+gst_element_iterate_src_pads (GstElement * element)
+{
+ g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
+
+ return gst_element_iterate_pad_list (element, &element->srcpads);
+}
+
+/**
+ * gst_element_iterate_sink_pads:
+ * @element: a #GstElement.
+ *
+ * Retrieves an iterator of @element's sink pads.
+ *
+ * Returns: (transfer full): the #GstIterator of #GstPad. Unref each pad
+ * after use.
+ *
+ * MT safe.
+ */
+GstIterator *
+gst_element_iterate_sink_pads (GstElement * element)
+{
+ g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
+
+ return gst_element_iterate_pad_list (element, &element->sinkpads);
+}
+
+/**
+ * gst_element_class_add_pad_template:
+ * @klass: the #GstElementClass to add the pad template to.
+ * @templ: (transfer full): a #GstPadTemplate to add to the element class.
+ *
+ * Adds a padtemplate to an element class. This is mainly used in the _class_init
+ * functions of classes. If a pad template with the same name as an already
+ * existing one is added the old one is replaced by the new one.
+ *
+ */
+void
+gst_element_class_add_pad_template (GstElementClass * klass,
+ GstPadTemplate * templ)
+{
+ GList *template_list = klass->padtemplates;
+
+ g_return_if_fail (GST_IS_ELEMENT_CLASS (klass));
+ g_return_if_fail (GST_IS_PAD_TEMPLATE (templ));
+
+ /* If we already have a pad template with the same name replace the
+ * old one. */
+ while (template_list) {
+ GstPadTemplate *padtempl = (GstPadTemplate *) template_list->data;
+
+ /* Found pad with the same name, replace and return */
+ if (strcmp (templ->name_template, padtempl->name_template) == 0) {
+ gst_object_unref (padtempl);
+ template_list->data = templ;
+ return;
+ }
+ template_list = g_list_next (template_list);
+ }
+
+ /* Take ownership of the floating ref */
+ g_object_ref_sink (templ);
+
+ klass->padtemplates = g_list_append (klass->padtemplates, templ);
+ klass->numpadtemplates++;
+}
+
+/**
+ * gst_element_class_add_metadata:
+ * @klass: class to set metadata for
+ * @key: the key to set
+ * @value: the value to set
+ *
+ * Set @key with @value as metadata in @klass.
+ */
+void
+gst_element_class_add_metadata (GstElementClass * klass,
+ const gchar * key, const gchar * value)
+{
+ g_return_if_fail (GST_IS_ELEMENT_CLASS (klass));
+ g_return_if_fail (key != NULL);
+ g_return_if_fail (value != NULL);
+
+ gst_structure_set ((GstStructure *) klass->metadata,
+ key, G_TYPE_STRING, value, NULL);
+}
+
+/**
+ * gst_element_class_set_metadata:
+ * @klass: class to set metadata for
+ * @longname: The long English name of the element. E.g. "File Sink"
+ * @classification: String describing the type of element, as an unordered list
+ * separated with slashes ('/'). See draft-klass.txt of the design docs
+ * for more details and common types. E.g: "Sink/File"
+ * @description: Sentence describing the purpose of the element.
+ * E.g: "Write stream to a file"
+ * @author: Name and contact details of the author(s). Use \n to separate
+ * multiple author metadata. E.g: "Joe Bloggs &lt;joe.blogs at foo.com&gt;"
+ *
+ * Sets the detailed information for a #GstElementClass.
+ * <note>This function is for use in _class_init functions only.</note>
+ */
+void
+gst_element_class_set_metadata (GstElementClass * klass,
+ const gchar * longname, const gchar * classification,
+ const gchar * description, const gchar * author)
+{
+ g_return_if_fail (GST_IS_ELEMENT_CLASS (klass));
+
+ gst_structure_set ((GstStructure *) klass->metadata,
+ GST_ELEMENT_METADATA_LONGNAME, G_TYPE_STRING, longname,
+ GST_ELEMENT_METADATA_KLASS, G_TYPE_STRING, classification,
+ GST_ELEMENT_METADATA_DESCRIPTION, G_TYPE_STRING, description,
+ GST_ELEMENT_METADATA_AUTHOR, G_TYPE_STRING, author, NULL);
+}
+
+/**
+ * gst_element_class_get_metadata:
+ * @klass: class to get metadata for
+ * @key: the key to get
+ *
+ * Get metadata with @key in @klass.
+ *
+ * Returns: the metadata for @key.
+ */
+const gchar *
+gst_element_class_get_metadata (GstElementClass * klass, const gchar * key)
+{
+ g_return_val_if_fail (GST_IS_ELEMENT_CLASS (klass), NULL);
+ g_return_val_if_fail (key != NULL, NULL);
+
+ return gst_structure_get_string ((GstStructure *) klass->metadata, key);
+}
+
+/**
+ * gst_element_class_get_pad_template_list:
+ * @element_class: a #GstElementClass to get pad templates of.
+ *
+ * Retrieves a list of the pad templates associated with @element_class. The
+ * list must not be modified by the calling code.
+ * <note>If you use this function in the #GInstanceInitFunc of an object class
+ * that has subclasses, make sure to pass the g_class parameter of the
+ * #GInstanceInitFunc here.</note>
+ *
+ * Returns: (transfer none) (element-type Gst.PadTemplate): the #GList of
+ * pad templates.
+ */
+GList *
+gst_element_class_get_pad_template_list (GstElementClass * element_class)
+{
+ g_return_val_if_fail (GST_IS_ELEMENT_CLASS (element_class), NULL);
+
+ return element_class->padtemplates;
+}
+
+/**
+ * gst_element_class_get_pad_template:
+ * @element_class: a #GstElementClass to get the pad template of.
+ * @name: the name of the #GstPadTemplate to get.
+ *
+ * Retrieves a padtemplate from @element_class with the given name.
+ * <note>If you use this function in the #GInstanceInitFunc of an object class
+ * that has subclasses, make sure to pass the g_class parameter of the
+ * #GInstanceInitFunc here.</note>
+ *
+ * Returns: (transfer none): the #GstPadTemplate with the given name, or %NULL
+ * if none was found. No unreferencing is necessary.
+ */
+GstPadTemplate *
+gst_element_class_get_pad_template (GstElementClass *
+ element_class, const gchar * name)
+{
+ GList *padlist;
+
+ g_return_val_if_fail (GST_IS_ELEMENT_CLASS (element_class), NULL);
+ g_return_val_if_fail (name != NULL, NULL);
+
+ padlist = element_class->padtemplates;
+
+ while (padlist) {
+ GstPadTemplate *padtempl = (GstPadTemplate *) padlist->data;
+
+ if (strcmp (padtempl->name_template, name) == 0)
+ return padtempl;
+
+ padlist = g_list_next (padlist);
+ }
+
+ return NULL;
+}
+
+static GstPadTemplate *
+gst_element_class_get_request_pad_template (GstElementClass *
+ element_class, const gchar * name)
+{
+ GstPadTemplate *tmpl;
+
+ tmpl = gst_element_class_get_pad_template (element_class, name);
+ if (tmpl != NULL && tmpl->presence == GST_PAD_REQUEST)
+ return tmpl;
+
+ return NULL;
+}
+
+/* get a random pad on element of the given direction.
+ * The pad is random in a sense that it is the first pad that is (optionaly) linked.
+ */
+static GstPad *
+gst_element_get_random_pad (GstElement * element,
+ gboolean need_linked, GstPadDirection dir)
+{
+ GstPad *result = NULL;
+ GList *pads;
+
+ GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "getting a random pad");
+
+ switch (dir) {
+ case GST_PAD_SRC:
+ GST_OBJECT_LOCK (element);
+ pads = element->srcpads;
+ break;
+ case GST_PAD_SINK:
+ GST_OBJECT_LOCK (element);
+ pads = element->sinkpads;
+ break;
+ default:
+ goto wrong_direction;
+ }
+ for (; pads; pads = g_list_next (pads)) {
+ GstPad *pad = GST_PAD_CAST (pads->data);
+
+ GST_OBJECT_LOCK (pad);
+ GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "checking pad %s:%s",
+ GST_DEBUG_PAD_NAME (pad));
+
+ if (need_linked && !GST_PAD_IS_LINKED (pad)) {
+ /* if we require a linked pad, and it is not linked, continue the
+ * search */
+ GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "pad %s:%s is not linked",
+ GST_DEBUG_PAD_NAME (pad));
+ GST_OBJECT_UNLOCK (pad);
+ continue;
+ } else {
+ /* found a pad, stop search */
+ GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "found pad %s:%s",
+ GST_DEBUG_PAD_NAME (pad));
+ GST_OBJECT_UNLOCK (pad);
+ result = pad;
+ break;
+ }
+ }
+ if (result)
+ gst_object_ref (result);
+
+ GST_OBJECT_UNLOCK (element);
+
+ return result;
+
+ /* ERROR handling */
+wrong_direction:
+ {
+ g_warning ("unknown pad direction %d", dir);
+ return NULL;
+ }
+}
+
+static gboolean
+gst_element_default_send_event (GstElement * element, GstEvent * event)
+{
+ gboolean result = FALSE;
+ GstPad *pad;
+
+ pad = GST_EVENT_IS_DOWNSTREAM (event) ?
+ gst_element_get_random_pad (element, TRUE, GST_PAD_SRC) :
+ gst_element_get_random_pad (element, TRUE, GST_PAD_SINK);
+
+ if (pad) {
+ GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS,
+ "pushing %s event to random %s pad %s:%s",
+ GST_EVENT_TYPE_NAME (event),
+ (GST_PAD_DIRECTION (pad) == GST_PAD_SRC ? "src" : "sink"),
+ GST_DEBUG_PAD_NAME (pad));
+
+ result = gst_pad_push_event (pad, event);
+ gst_object_unref (pad);
+ } else {
+ GST_CAT_INFO (GST_CAT_ELEMENT_PADS, "can't send %s event on element %s",
+ GST_EVENT_TYPE_NAME (event), GST_ELEMENT_NAME (element));
+ gst_event_unref (event);
+ }
+ return result;
+}
+
+/**
+ * gst_element_send_event:
+ * @element: a #GstElement to send the event to.
+ * @event: (transfer full): the #GstEvent to send to the element.
+ *
+ * Sends an event to an element. If the element doesn't implement an
+ * event handler, the event will be pushed on a random linked sink pad for
+ * upstream events or a random linked source pad for downstream events.
+ *
+ * This function takes owership of the provided event so you should
+ * gst_event_ref() it if you want to reuse the event after this call.
+ *
+ * Returns: %TRUE if the event was handled.
+ *
+ * MT safe.
+ */
+gboolean
+gst_element_send_event (GstElement * element, GstEvent * event)
+{
+ GstElementClass *oclass;
+ gboolean result = FALSE;
+
+ g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ oclass = GST_ELEMENT_GET_CLASS (element);
+
+ GST_STATE_LOCK (element);
+ if (oclass->send_event) {
+ GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "send %s event on element %s",
+ GST_EVENT_TYPE_NAME (event), GST_ELEMENT_NAME (element));
+ result = oclass->send_event (element, event);
+ } else {
+ result = gst_element_default_send_event (element, event);
+ }
+ GST_STATE_UNLOCK (element);
+
+ return result;
+}
+
+/**
+ * gst_element_seek:
+ * @element: a #GstElement to send the event to.
+ * @rate: The new playback rate
+ * @format: The format of the seek values
+ * @flags: The optional seek flags.
+ * @cur_type: The type and flags for the new current position
+ * @cur: The value of the new current position
+ * @stop_type: The type and flags for the new stop position
+ * @stop: The value of the new stop position
+ *
+ * Sends a seek event to an element. See gst_event_new_seek() for the details of
+ * the parameters. The seek event is sent to the element using
+ * gst_element_send_event().
+ *
+ * Returns: %TRUE if the event was handled.
+ *
+ * MT safe.
+ */
+gboolean
+gst_element_seek (GstElement * element, gdouble rate, GstFormat format,
+ GstSeekFlags flags, GstSeekType cur_type, gint64 cur,
+ GstSeekType stop_type, gint64 stop)
+{
+ GstEvent *event;
+ gboolean result;
+
+ g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
+
+ event =
+ gst_event_new_seek (rate, format, flags, cur_type, cur, stop_type, stop);
+ result = gst_element_send_event (element, event);
+
+ return result;
+}
+
+/**
+ * gst_element_get_query_types:
+ * @element: a #GstElement to query
+ *
+ * Get an array of query types from the element.
+ * If the element doesn't implement a query types function,
+ * the query will be forwarded to the peer of a random linked sink pad.
+ *
+ * Returns: An array of #GstQueryType elements that should not
+ * be freed or modified.
+ *
+ * MT safe.
+ */
+const GstQueryType *
+gst_element_get_query_types (GstElement * element)
+{
+ GstElementClass *oclass;
+ const GstQueryType *result = NULL;
+
+ g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
+
+ oclass = GST_ELEMENT_GET_CLASS (element);
+
+ if (oclass->get_query_types) {
+ result = oclass->get_query_types (element);
+ } else {
+ GstPad *pad = gst_element_get_random_pad (element, TRUE, GST_PAD_SINK);
+
+ if (pad) {
+ GstPad *peer = gst_pad_get_peer (pad);
+
+ if (peer) {
+ result = gst_pad_get_query_types (peer);
+
+ gst_object_unref (peer);
+ }
+ gst_object_unref (pad);
+ }
+ }
+ return result;
+}
+
+static gboolean
+gst_element_default_query (GstElement * element, GstQuery * query)
+{
+ gboolean result = FALSE;
+ GstPad *pad;
+
+ pad = gst_element_get_random_pad (element, FALSE, GST_PAD_SRC);
+ if (pad) {
+ result = gst_pad_query (pad, query);
+
+ gst_object_unref (pad);
+ } else {
+ pad = gst_element_get_random_pad (element, TRUE, GST_PAD_SINK);
+ if (pad) {
+ GstPad *peer = gst_pad_get_peer (pad);
+
+ if (peer) {
+ result = gst_pad_query (peer, query);
+
+ gst_object_unref (peer);
+ }
+ gst_object_unref (pad);
+ }
+ }
+ return result;
+}
+
+/**
+ * gst_element_query:
+ * @element: a #GstElement to perform the query on.
+ * @query: (transfer none): the #GstQuery.
+ *
+ * Performs a query on the given element.
+ *
+ * For elements that don't implement a query handler, this function
+ * forwards the query to a random srcpad or to the peer of a
+ * random linked sinkpad of this element.
+ *
+ * Please note that some queries might need a running pipeline to work.
+ *
+ * Returns: TRUE if the query could be performed.
+ *
+ * MT safe.
+ */
+gboolean
+gst_element_query (GstElement * element, GstQuery * query)
+{
+ GstElementClass *oclass;
+ gboolean result = FALSE;
+
+ g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
+ g_return_val_if_fail (query != NULL, FALSE);
+
+ oclass = GST_ELEMENT_GET_CLASS (element);
+
+ if (oclass->query) {
+ GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "send query on element %s",
+ GST_ELEMENT_NAME (element));
+ result = oclass->query (element, query);
+ } else {
+ result = gst_element_default_query (element, query);
+ }
+ return result;
+}
+
+/**
+ * gst_element_post_message:
+ * @element: a #GstElement posting the message
+ * @message: (transfer full): a #GstMessage to post
+ *
+ * Post a message on the element's #GstBus. This function takes ownership of the
+ * message; if you want to access the message after this call, you should add an
+ * additional reference before calling.
+ *
+ * Returns: %TRUE if the message was successfully posted. The function returns
+ * %FALSE if the element did not have a bus.
+ *
+ * MT safe.
+ */
+gboolean
+gst_element_post_message (GstElement * element, GstMessage * message)
+{
+ GstBus *bus;
+ gboolean result = FALSE;
+
+ g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
+ g_return_val_if_fail (message != NULL, FALSE);
+
+ GST_OBJECT_LOCK (element);
+ bus = element->bus;
+
+ if (G_UNLIKELY (bus == NULL))
+ goto no_bus;
+
+ gst_object_ref (bus);
+ GST_OBJECT_UNLOCK (element);
+
+ /* we release the element lock when posting the message so that any
+ * (synchronous) message handlers can operate on the element */
+ result = gst_bus_post (bus, message);
+ gst_object_unref (bus);
+
+ return result;
+
+ /* ERRORS */
+no_bus:
+ {
+ GST_CAT_DEBUG_OBJECT (GST_CAT_MESSAGE, element,
+ "not posting message %p: no bus", message);
+ GST_OBJECT_UNLOCK (element);
+ gst_message_unref (message);
+ return FALSE;
+ }
+}
+
+/**
+ * _gst_element_error_printf:
+ * @format: the printf-like format to use, or %NULL
+ *
+ * This function is only used internally by the gst_element_error() macro.
+ *
+ * Returns: (transfer full): a newly allocated string, or %NULL if the format
+ * was %NULL or ""
+ *
+ * MT safe.
+ */
+gchar *
+_gst_element_error_printf (const gchar * format, ...)
+{
+ va_list args;
+ gchar *buffer;
+
+ if (format == NULL)
+ return NULL;
+ if (format[0] == 0)
+ return NULL;
+
+ va_start (args, format);
+ buffer = g_strdup_vprintf (format, args);
+ va_end (args);
+ return buffer;
+}
+
+/**
+ * gst_element_message_full:
+ * @element: a #GstElement to send message from
+ * @type: the #GstMessageType
+ * @domain: the GStreamer GError domain this message belongs to
+ * @code: the GError code belonging to the domain
+ * @text: (allow-none) (transfer full): an allocated text string to be used
+ * as a replacement for the default message connected to code,
+ * or %NULL
+ * @debug: (allow-none) (transfer full): an allocated debug message to be
+ * used as a replacement for the default debugging information,
+ * or %NULL
+ * @file: the source code file where the error was generated
+ * @function: the source code function where the error was generated
+ * @line: the source code line where the error was generated
+ *
+ * Post an error, warning or info message on the bus from inside an element.
+ *
+ * @type must be of #GST_MESSAGE_ERROR, #GST_MESSAGE_WARNING or
+ * #GST_MESSAGE_INFO.
+ *
+ * MT safe.
+ */
+void gst_element_message_full
+ (GstElement * element, GstMessageType type,
+ GQuark domain, gint code, gchar * text,
+ gchar * debug, const gchar * file, const gchar * function, gint line)
+{
+ GError *gerror = NULL;
+ gchar *name;
+ gchar *sent_text;
+ gchar *sent_debug;
+ gboolean has_debug = TRUE;
+ GstMessage *message = NULL;
+
+ /* checks */
+ GST_CAT_DEBUG_OBJECT (GST_CAT_MESSAGE, element, "start");
+ g_return_if_fail (GST_IS_ELEMENT (element));
+ g_return_if_fail ((type == GST_MESSAGE_ERROR) ||
+ (type == GST_MESSAGE_WARNING) || (type == GST_MESSAGE_INFO));
+
+ /* check if we send the given text or the default error text */
+ if ((text == NULL) || (text[0] == 0)) {
+ /* text could have come from g_strdup_printf (""); */
+ g_free (text);
+ sent_text = gst_error_get_message (domain, code);
+ } else
+ sent_text = text;
+
+ /* construct a sent_debug with extra information from source */
+ if ((debug == NULL) || (debug[0] == 0)) {
+ /* debug could have come from g_strdup_printf (""); */
+ has_debug = FALSE;
+ }
+
+ name = gst_object_get_path_string (GST_OBJECT_CAST (element));
+ if (has_debug)
+ sent_debug = g_strdup_printf ("%s(%d): %s (): %s:\n%s",
+ file, line, function, name, debug);
+ else
+ sent_debug = g_strdup_printf ("%s(%d): %s (): %s",
+ file, line, function, name);
+ g_free (name);
+ g_free (debug);
+
+ /* create gerror and post message */
+ GST_CAT_INFO_OBJECT (GST_CAT_ERROR_SYSTEM, element, "posting message: %s",
+ sent_text);
+ gerror = g_error_new_literal (domain, code, sent_text);
+
+ switch (type) {
+ case GST_MESSAGE_ERROR:
+ message =
+ gst_message_new_error (GST_OBJECT_CAST (element), gerror, sent_debug);
+ break;
+ case GST_MESSAGE_WARNING:
+ message = gst_message_new_warning (GST_OBJECT_CAST (element), gerror,
+ sent_debug);
+ break;
+ case GST_MESSAGE_INFO:
+ message = gst_message_new_info (GST_OBJECT_CAST (element), gerror,
+ sent_debug);
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+ gst_element_post_message (element, message);
+
+ GST_CAT_INFO_OBJECT (GST_CAT_ERROR_SYSTEM, element, "posted %s message: %s",
+ (type == GST_MESSAGE_ERROR ? "error" : "warning"), sent_text);
+
+ /* cleanup */
+ g_error_free (gerror);
+ g_free (sent_debug);
+ g_free (sent_text);
+}
+
+/**
+ * gst_element_is_locked_state:
+ * @element: a #GstElement.
+ *
+ * Checks if the state of an element is locked.
+ * If the state of an element is locked, state changes of the parent don't
+ * affect the element.
+ * This way you can leave currently unused elements inside bins. Just lock their
+ * state before changing the state from #GST_STATE_NULL.
+ *
+ * MT safe.
+ *
+ * Returns: TRUE, if the element's state is locked.
+ */
+gboolean
+gst_element_is_locked_state (GstElement * element)
+{
+ gboolean result;
+
+ g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
+
+ GST_OBJECT_LOCK (element);
+ result = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_LOCKED_STATE);
+ GST_OBJECT_UNLOCK (element);
+
+ return result;
+}
+
+/**
+ * gst_element_set_locked_state:
+ * @element: a #GstElement
+ * @locked_state: TRUE to lock the element's state
+ *
+ * Locks the state of an element, so state changes of the parent don't affect
+ * this element anymore.
+ *
+ * MT safe.
+ *
+ * Returns: TRUE if the state was changed, FALSE if bad parameters were given
+ * or the elements state-locking needed no change.
+ */
+gboolean
+gst_element_set_locked_state (GstElement * element, gboolean locked_state)
+{
+ gboolean old;
+
+ g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
+
+ GST_OBJECT_LOCK (element);
+ old = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_LOCKED_STATE);
+
+ if (G_UNLIKELY (old == locked_state))
+ goto was_ok;
+
+ if (locked_state) {
+ GST_CAT_DEBUG (GST_CAT_STATES, "locking state of element %s",
+ GST_ELEMENT_NAME (element));
+ GST_OBJECT_FLAG_SET (element, GST_ELEMENT_LOCKED_STATE);
+ } else {
+ GST_CAT_DEBUG (GST_CAT_STATES, "unlocking state of element %s",
+ GST_ELEMENT_NAME (element));
+ GST_OBJECT_FLAG_UNSET (element, GST_ELEMENT_LOCKED_STATE);
+ }
+ GST_OBJECT_UNLOCK (element);
+
+ return TRUE;
+
+was_ok:
+ {
+ GST_CAT_DEBUG (GST_CAT_STATES,
+ "elements %s was already in locked state %d",
+ GST_ELEMENT_NAME (element), old);
+ GST_OBJECT_UNLOCK (element);
+
+ return FALSE;
+ }
+}
+
+/**
+ * gst_element_sync_state_with_parent:
+ * @element: a #GstElement.
+ *
+ * Tries to change the state of the element to the same as its parent.
+ * If this function returns FALSE, the state of element is undefined.
+ *
+ * Returns: TRUE, if the element's state could be synced to the parent's state.
+ *
+ * MT safe.
+ */
+gboolean
+gst_element_sync_state_with_parent (GstElement * element)
+{
+ GstElement *parent;
+ GstState target;
+ GstStateChangeReturn ret;
+
+ g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
+
+ if ((parent = GST_ELEMENT_CAST (gst_element_get_parent (element)))) {
+ GstState parent_current, parent_pending;
+
+ GST_OBJECT_LOCK (parent);
+ parent_current = GST_STATE (parent);
+ parent_pending = GST_STATE_PENDING (parent);
+ GST_OBJECT_UNLOCK (parent);
+
+ /* set to pending if there is one, else we set it to the current state of
+ * the parent */
+ if (parent_pending != GST_STATE_VOID_PENDING)
+ target = parent_pending;
+ else
+ target = parent_current;
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
+ "syncing state (%s) to parent %s %s (%s, %s)",
+ gst_element_state_get_name (GST_STATE (element)),
+ GST_ELEMENT_NAME (parent), gst_element_state_get_name (target),
+ gst_element_state_get_name (parent_current),
+ gst_element_state_get_name (parent_pending));
+
+ ret = gst_element_set_state (element, target);
+ if (ret == GST_STATE_CHANGE_FAILURE)
+ goto failed;
+
+ gst_object_unref (parent);
+
+ return TRUE;
+ } else {
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, "element has no parent");
+ }
+ return FALSE;
+
+ /* ERROR */
+failed:
+ {
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
+ "syncing state failed (%s)",
+ gst_element_state_change_return_get_name (ret));
+ gst_object_unref (parent);
+ return FALSE;
+ }
+}
+
+/* MT safe */
+static GstStateChangeReturn
+gst_element_get_state_func (GstElement * element,
+ GstState * state, GstState * pending, GstClockTime timeout)
+{
+ GstStateChangeReturn ret = GST_STATE_CHANGE_FAILURE;
+ GstState old_pending;
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, "getting state, timeout %"
+ GST_TIME_FORMAT, GST_TIME_ARGS (timeout));
+
+ GST_OBJECT_LOCK (element);
+ ret = GST_STATE_RETURN (element);
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, "RETURN is %s",
+ gst_element_state_change_return_get_name (ret));
+
+ /* we got an error, report immediately */
+ if (ret == GST_STATE_CHANGE_FAILURE)
+ goto done;
+
+ /* we got no_preroll, report immediately */
+ if (ret == GST_STATE_CHANGE_NO_PREROLL)
+ goto done;
+
+ /* no need to wait async if we are not async */
+ if (ret != GST_STATE_CHANGE_ASYNC)
+ goto done;
+
+ old_pending = GST_STATE_PENDING (element);
+ if (old_pending != GST_STATE_VOID_PENDING) {
+ GTimeVal *timeval, abstimeout;
+ guint32 cookie;
+
+ if (timeout != GST_CLOCK_TIME_NONE) {
+ glong add = timeout / 1000;
+
+ if (add == 0)
+ goto done;
+
+ /* make timeout absolute */
+ g_get_current_time (&abstimeout);
+ g_time_val_add (&abstimeout, add);
+ timeval = &abstimeout;
+ } else {
+ timeval = NULL;
+ }
+ /* get cookie to detect state changes during waiting */
+ cookie = element->state_cookie;
+
+ GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
+ "waiting for element to commit state");
+
+ /* we have a pending state change, wait for it to complete */
+ if (!GST_STATE_TIMED_WAIT (element, timeval)) {
+ GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "timed out");
+ /* timeout triggered */
+ ret = GST_STATE_CHANGE_ASYNC;
+ } else {
+ if (cookie != element->state_cookie)
+ goto interrupted;
+
+ /* could be success or failure */
+ if (old_pending == GST_STATE (element)) {
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, "got success");
+ ret = GST_STATE_CHANGE_SUCCESS;
+ } else {
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, "got failure");
+ ret = GST_STATE_CHANGE_FAILURE;
+ }
+ }
+ /* if nothing is pending anymore we can return SUCCESS */
+ if (GST_STATE_PENDING (element) == GST_STATE_VOID_PENDING) {
+ GST_CAT_LOG_OBJECT (GST_CAT_STATES, element, "nothing pending");
+ ret = GST_STATE_CHANGE_SUCCESS;
+ }
+ }
+
+done:
+ if (state)
+ *state = GST_STATE (element);
+ if (pending)
+ *pending = GST_STATE_PENDING (element);
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
+ "state current: %s, pending: %s, result: %s",
+ gst_element_state_get_name (GST_STATE (element)),
+ gst_element_state_get_name (GST_STATE_PENDING (element)),
+ gst_element_state_change_return_get_name (ret));
+ GST_OBJECT_UNLOCK (element);
+
+ return ret;
+
+interrupted:
+ {
+ if (state)
+ *state = GST_STATE_VOID_PENDING;
+ if (pending)
+ *pending = GST_STATE_VOID_PENDING;
+
+ GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "interruped");
+
+ GST_OBJECT_UNLOCK (element);
+
+ return GST_STATE_CHANGE_FAILURE;
+ }
+}
+
+/**
+ * gst_element_get_state:
+ * @element: a #GstElement to get the state of.
+ * @state: (out) (allow-none): a pointer to #GstState to hold the state.
+ * Can be %NULL.
+ * @pending: (out) (allow-none): a pointer to #GstState to hold the pending
+ * state. Can be %NULL.
+ * @timeout: a #GstClockTime to specify the timeout for an async
+ * state change or %GST_CLOCK_TIME_NONE for infinite timeout.
+ *
+ * Gets the state of the element.
+ *
+ * For elements that performed an ASYNC state change, as reported by
+ * gst_element_set_state(), this function will block up to the
+ * specified timeout value for the state change to complete.
+ * If the element completes the state change or goes into
+ * an error, this function returns immediately with a return value of
+ * %GST_STATE_CHANGE_SUCCESS or %GST_STATE_CHANGE_FAILURE respectively.
+ *
+ * For elements that did not return %GST_STATE_CHANGE_ASYNC, this function
+ * returns the current and pending state immediately.
+ *
+ * This function returns %GST_STATE_CHANGE_NO_PREROLL if the element
+ * successfully changed its state but is not able to provide data yet.
+ * This mostly happens for live sources that only produce data in
+ * %GST_STATE_PLAYING. While the state change return is equivalent to
+ * %GST_STATE_CHANGE_SUCCESS, it is returned to the application to signal that
+ * some sink elements might not be able to complete their state change because
+ * an element is not producing data to complete the preroll. When setting the
+ * element to playing, the preroll will complete and playback will start.
+ *
+ * Returns: %GST_STATE_CHANGE_SUCCESS if the element has no more pending state
+ * and the last state change succeeded, %GST_STATE_CHANGE_ASYNC if the
+ * element is still performing a state change or
+ * %GST_STATE_CHANGE_FAILURE if the last state change failed.
+ *
+ * MT safe.
+ */
+GstStateChangeReturn
+gst_element_get_state (GstElement * element,
+ GstState * state, GstState * pending, GstClockTime timeout)
+{
+ GstElementClass *oclass;
+ GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
+
+ g_return_val_if_fail (GST_IS_ELEMENT (element), GST_STATE_CHANGE_FAILURE);
+
+ oclass = GST_ELEMENT_GET_CLASS (element);
+
+ if (oclass->get_state)
+ result = (oclass->get_state) (element, state, pending, timeout);
+
+ return result;
+}
+
+/**
+ * gst_element_abort_state:
+ * @element: a #GstElement to abort the state of.
+ *
+ * Abort the state change of the element. This function is used
+ * by elements that do asynchronous state changes and find out
+ * something is wrong.
+ *
+ * This function should be called with the STATE_LOCK held.
+ *
+ * MT safe.
+ */
+void
+gst_element_abort_state (GstElement * element)
+{
+ GstState pending;
+
+#ifndef GST_DISABLE_GST_DEBUG
+ GstState old_state;
+#endif
+
+ g_return_if_fail (GST_IS_ELEMENT (element));
+
+ GST_OBJECT_LOCK (element);
+ pending = GST_STATE_PENDING (element);
+
+ if (pending == GST_STATE_VOID_PENDING ||
+ GST_STATE_RETURN (element) == GST_STATE_CHANGE_FAILURE)
+ goto nothing_aborted;
+
+#ifndef GST_DISABLE_GST_DEBUG
+ old_state = GST_STATE (element);
+
+ GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
+ "aborting state from %s to %s", gst_element_state_get_name (old_state),
+ gst_element_state_get_name (pending));
+#endif
+
+ /* flag error */
+ GST_STATE_RETURN (element) = GST_STATE_CHANGE_FAILURE;
+
+ GST_STATE_BROADCAST (element);
+ GST_OBJECT_UNLOCK (element);
+
+ return;
+
+nothing_aborted:
+ {
+ GST_OBJECT_UNLOCK (element);
+ return;
+ }
+}
+
+/* Not static because GstBin has manual state handling too */
+void
+_priv_gst_element_state_changed (GstElement * element, GstState oldstate,
+ GstState newstate, GstState pending)
+{
+ GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
+ GstMessage *message;
+
+ GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
+ "notifying about state-changed %s to %s (%s pending)",
+ gst_element_state_get_name (oldstate),
+ gst_element_state_get_name (newstate),
+ gst_element_state_get_name (pending));
+
+ if (klass->state_changed)
+ klass->state_changed (element, oldstate, newstate, pending);
+
+ message = gst_message_new_state_changed (GST_OBJECT_CAST (element),
+ oldstate, newstate, pending);
+ gst_element_post_message (element, message);
+}
+
+/**
+ * gst_element_continue_state:
+ * @element: a #GstElement to continue the state change of.
+ * @ret: The previous state return value
+ *
+ * Commit the state change of the element and proceed to the next
+ * pending state if any. This function is used
+ * by elements that do asynchronous state changes.
+ * The core will normally call this method automatically when an
+ * element returned %GST_STATE_CHANGE_SUCCESS from the state change function.
+ *
+ * If after calling this method the element still has not reached
+ * the pending state, the next state change is performed.
+ *
+ * This method is used internally and should normally not be called by plugins
+ * or applications.
+ *
+ * Returns: The result of the commit state change.
+ *
+ * MT safe.
+ */
+GstStateChangeReturn
+gst_element_continue_state (GstElement * element, GstStateChangeReturn ret)
+{
+ GstStateChangeReturn old_ret;
+ GstState old_state, old_next;
+ GstState current, next, pending;
+ GstStateChange transition;
+
+ GST_OBJECT_LOCK (element);
+ old_ret = GST_STATE_RETURN (element);
+ GST_STATE_RETURN (element) = ret;
+ pending = GST_STATE_PENDING (element);
+
+ /* check if there is something to commit */
+ if (pending == GST_STATE_VOID_PENDING)
+ goto nothing_pending;
+
+ old_state = GST_STATE (element);
+ /* this is the state we should go to next */
+ old_next = GST_STATE_NEXT (element);
+ /* update current state */
+ current = GST_STATE (element) = old_next;
+
+ /* see if we reached the final state */
+ if (pending == current)
+ goto complete;
+
+ next = GST_STATE_GET_NEXT (current, pending);
+ transition = (GstStateChange) GST_STATE_TRANSITION (current, next);
+
+ GST_STATE_NEXT (element) = next;
+ /* mark busy */
+ GST_STATE_RETURN (element) = GST_STATE_CHANGE_ASYNC;
+ GST_OBJECT_UNLOCK (element);
+
+ GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
+ "committing state from %s to %s, pending %s, next %s",
+ gst_element_state_get_name (old_state),
+ gst_element_state_get_name (old_next),
+ gst_element_state_get_name (pending), gst_element_state_get_name (next));
+
+ _priv_gst_element_state_changed (element, old_state, old_next, pending);
+
+ GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
+ "continue state change %s to %s, final %s",
+ gst_element_state_get_name (current),
+ gst_element_state_get_name (next), gst_element_state_get_name (pending));
+
+ ret = gst_element_change_state (element, transition);
+
+ return ret;
+
+nothing_pending:
+ {
+ GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "nothing pending");
+ GST_OBJECT_UNLOCK (element);
+ return ret;
+ }
+complete:
+ {
+ GST_STATE_PENDING (element) = GST_STATE_VOID_PENDING;
+ GST_STATE_NEXT (element) = GST_STATE_VOID_PENDING;
+
+ GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
+ "completed state change to %s", gst_element_state_get_name (pending));
+ GST_OBJECT_UNLOCK (element);
+
+ /* don't post silly messages with the same state. This can happen
+ * when an element state is changed to what it already was. For bins
+ * this can be the result of a lost state, which we check with the
+ * previous return value.
+ * We do signal the cond though as a _get_state() might be blocking
+ * on it. */
+ if (old_state != old_next || old_ret == GST_STATE_CHANGE_ASYNC)
+ _priv_gst_element_state_changed (element, old_state, old_next,
+ GST_STATE_VOID_PENDING);
+
+ GST_STATE_BROADCAST (element);
+
+ return ret;
+ }
+}
+
+/**
+ * gst_element_lost_state:
+ * @element: a #GstElement the state is lost of
+ *
+ * Brings the element to the lost state. The current state of the
+ * element is copied to the pending state so that any call to
+ * gst_element_get_state() will return %GST_STATE_CHANGE_ASYNC.
+ *
+ * An ASYNC_START message is posted. If the element was PLAYING, it will
+ * go to PAUSED. The element will be restored to its PLAYING state by
+ * the parent pipeline when it prerolls again.
+ *
+ * This is mostly used for elements that lost their preroll buffer
+ * in the %GST_STATE_PAUSED or %GST_STATE_PLAYING state after a flush,
+ * they will go to their pending state again when a new preroll buffer is
+ * queued. This function can only be called when the element is currently
+ * not in error or an async state change.
+ *
+ * This function is used internally and should normally not be called from
+ * plugins or applications.
+ */
+void
+gst_element_lost_state (GstElement * element)
+{
+ GstState old_state, new_state;
+ GstMessage *message;
+
+ g_return_if_fail (GST_IS_ELEMENT (element));
+
+ GST_OBJECT_LOCK (element);
+ if (GST_STATE_RETURN (element) == GST_STATE_CHANGE_FAILURE)
+ goto nothing_lost;
+
+ if (GST_STATE_PENDING (element) != GST_STATE_VOID_PENDING)
+ goto only_async_start;
+
+ old_state = GST_STATE (element);
+
+ /* when we were PLAYING, the new state is PAUSED. We will also not
+ * automatically go to PLAYING but let the parent bin(s) set us to PLAYING
+ * when we preroll. */
+ if (old_state > GST_STATE_PAUSED)
+ new_state = GST_STATE_PAUSED;
+ else
+ new_state = old_state;
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
+ "lost state of %s to %s", gst_element_state_get_name (old_state),
+ gst_element_state_get_name (new_state));
+
+ GST_STATE (element) = new_state;
+ GST_STATE_NEXT (element) = new_state;
+ GST_STATE_PENDING (element) = new_state;
+ GST_STATE_RETURN (element) = GST_STATE_CHANGE_ASYNC;
+ GST_OBJECT_UNLOCK (element);
+
+ _priv_gst_element_state_changed (element, new_state, new_state, new_state);
+
+ message = gst_message_new_async_start (GST_OBJECT_CAST (element));
+ gst_element_post_message (element, message);
+
+ return;
+
+nothing_lost:
+ {
+ GST_OBJECT_UNLOCK (element);
+ return;
+ }
+only_async_start:
+ {
+ GST_OBJECT_UNLOCK (element);
+
+ message = gst_message_new_async_start (GST_OBJECT_CAST (element));
+ gst_element_post_message (element, message);
+ return;
+ }
+}
+
+/**
+ * gst_element_set_state:
+ * @element: a #GstElement to change state of.
+ * @state: the element's new #GstState.
+ *
+ * Sets the state of the element. This function will try to set the
+ * requested state by going through all the intermediary states and calling
+ * the class's state change function for each.
+ *
+ * This function can return #GST_STATE_CHANGE_ASYNC, in which case the
+ * element will perform the remainder of the state change asynchronously in
+ * another thread.
+ * An application can use gst_element_get_state() to wait for the completion
+ * of the state change or it can wait for a state change message on the bus.
+ *
+ * State changes to %GST_STATE_READY or %GST_STATE_NULL never return
+ * #GST_STATE_CHANGE_ASYNC.
+ *
+ * Returns: Result of the state change using #GstStateChangeReturn.
+ *
+ * MT safe.
+ */
+GstStateChangeReturn
+gst_element_set_state (GstElement * element, GstState state)
+{
+ GstElementClass *oclass;
+ GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
+
+ g_return_val_if_fail (GST_IS_ELEMENT (element), GST_STATE_CHANGE_FAILURE);
+
+ oclass = GST_ELEMENT_GET_CLASS (element);
+
+ if (oclass->set_state)
+ result = (oclass->set_state) (element, state);
+
+ return result;
+}
+
+/*
+ * default set state function, calculates the next state based
+ * on current state and calls the change_state function
+ */
+static GstStateChangeReturn
+gst_element_set_state_func (GstElement * element, GstState state)
+{
+ GstState current, next, old_pending;
+ GstStateChangeReturn ret;
+ GstStateChange transition;
+ GstStateChangeReturn old_ret;
+
+ g_return_val_if_fail (GST_IS_ELEMENT (element), GST_STATE_CHANGE_FAILURE);
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, "set_state to %s",
+ gst_element_state_get_name (state));
+
+ /* state lock is taken to protect the set_state() and get_state()
+ * procedures, it does not lock any variables. */
+ GST_STATE_LOCK (element);
+
+ /* now calculate how to get to the new state */
+ GST_OBJECT_LOCK (element);
+ old_ret = GST_STATE_RETURN (element);
+ /* previous state change returned an error, remove all pending
+ * and next states */
+ if (old_ret == GST_STATE_CHANGE_FAILURE) {
+ GST_STATE_NEXT (element) = GST_STATE_VOID_PENDING;
+ GST_STATE_PENDING (element) = GST_STATE_VOID_PENDING;
+ GST_STATE_RETURN (element) = GST_STATE_CHANGE_SUCCESS;
+ }
+
+ current = GST_STATE (element);
+ next = GST_STATE_NEXT (element);
+ old_pending = GST_STATE_PENDING (element);
+
+ /* this is the (new) state we should go to. TARGET is the last state we set on
+ * the element. */
+ if (state != GST_STATE_TARGET (element)) {
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
+ "setting target state to %s", gst_element_state_get_name (state));
+ GST_STATE_TARGET (element) = state;
+ /* increment state cookie so that we can track each state change. We only do
+ * this if this is actually a new state change. */
+ element->state_cookie++;
+ }
+ GST_STATE_PENDING (element) = state;
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
+ "current %s, old_pending %s, next %s, old return %s",
+ gst_element_state_get_name (current),
+ gst_element_state_get_name (old_pending),
+ gst_element_state_get_name (next),
+ gst_element_state_change_return_get_name (old_ret));
+
+ /* if the element was busy doing a state change, we just update the
+ * target state, it'll get to it async then. */
+ if (old_pending != GST_STATE_VOID_PENDING) {
+ /* upwards state change will happen ASYNC */
+ if (old_pending <= state)
+ goto was_busy;
+ /* element is going to this state already */
+ else if (next == state)
+ goto was_busy;
+ /* element was performing an ASYNC upward state change and
+ * we request to go downward again. Start from the next pending
+ * state then. */
+ else if (next > state
+ && GST_STATE_RETURN (element) == GST_STATE_CHANGE_ASYNC) {
+ current = next;
+ }
+ }
+ next = GST_STATE_GET_NEXT (current, state);
+ /* now we store the next state */
+ GST_STATE_NEXT (element) = next;
+ /* mark busy, we need to check that there is actually a state change
+ * to be done else we could accidentally override SUCCESS/NO_PREROLL and
+ * the default element change_state function has no way to know what the
+ * old value was... could consider this a FIXME...*/
+ if (current != next)
+ GST_STATE_RETURN (element) = GST_STATE_CHANGE_ASYNC;
+
+ transition = (GstStateChange) GST_STATE_TRANSITION (current, next);
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
+ "%s: setting state from %s to %s",
+ (next != state ? "intermediate" : "final"),
+ gst_element_state_get_name (current), gst_element_state_get_name (next));
+
+ /* now signal any waiters, they will error since the cookie was incremented */
+ GST_STATE_BROADCAST (element);
+
+ GST_OBJECT_UNLOCK (element);
+
+ ret = gst_element_change_state (element, transition);
+
+ GST_STATE_UNLOCK (element);
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, "returned %s",
+ gst_element_state_change_return_get_name (ret));
+
+ return ret;
+
+was_busy:
+ {
+ GST_STATE_RETURN (element) = GST_STATE_CHANGE_ASYNC;
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
+ "element was busy with async state change");
+ GST_OBJECT_UNLOCK (element);
+
+ GST_STATE_UNLOCK (element);
+
+ return GST_STATE_CHANGE_ASYNC;
+ }
+}
+
+/**
+ * gst_element_change_state:
+ * @element: a #GstElement
+ * @transition: the requested transition
+ *
+ * Perform @transition on @element.
+ *
+ * This function must be called with STATE_LOCK held and is mainly used
+ * internally.
+ *
+ * Returns: the #GstStateChangeReturn of the state transition.
+ */
+GstStateChangeReturn
+gst_element_change_state (GstElement * element, GstStateChange transition)
+{
+ GstElementClass *oclass;
+ GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
+
+ oclass = GST_ELEMENT_GET_CLASS (element);
+
+ /* call the state change function so it can set the state */
+ if (oclass->change_state)
+ ret = (oclass->change_state) (element, transition);
+ else
+ ret = GST_STATE_CHANGE_FAILURE;
+
+ switch (ret) {
+ case GST_STATE_CHANGE_FAILURE:
+ GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
+ "have FAILURE change_state return");
+ /* state change failure */
+ gst_element_abort_state (element);
+ break;
+ case GST_STATE_CHANGE_ASYNC:
+ {
+ GstState target;
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
+ "element will change state ASYNC");
+
+ target = GST_STATE_TARGET (element);
+
+ if (target > GST_STATE_READY)
+ goto async;
+
+ /* else we just continue the state change downwards */
+ GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
+ "forcing commit state %s <= %s",
+ gst_element_state_get_name (target),
+ gst_element_state_get_name (GST_STATE_READY));
+
+ ret = gst_element_continue_state (element, GST_STATE_CHANGE_SUCCESS);
+ break;
+ }
+ case GST_STATE_CHANGE_SUCCESS:
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
+ "element changed state SUCCESS");
+ /* we can commit the state now which will proceeed to
+ * the next state */
+ ret = gst_element_continue_state (element, ret);
+ break;
+ case GST_STATE_CHANGE_NO_PREROLL:
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
+ "element changed state NO_PREROLL");
+ /* we can commit the state now which will proceeed to
+ * the next state */
+ ret = gst_element_continue_state (element, ret);
+ break;
+ default:
+ goto invalid_return;
+ }
+
+ GST_CAT_LOG_OBJECT (GST_CAT_STATES, element, "exit state change %d", ret);
+
+ return ret;
+
+async:
+ GST_CAT_LOG_OBJECT (GST_CAT_STATES, element, "exit async state change %d",
+ ret);
+
+ return ret;
+
+ /* ERROR */
+invalid_return:
+ {
+ GST_OBJECT_LOCK (element);
+ /* somebody added a GST_STATE_ and forgot to do stuff here ! */
+ g_critical ("%s: unknown return value %d from a state change function",
+ GST_ELEMENT_NAME (element), ret);
+
+ /* we are in error now */
+ ret = GST_STATE_CHANGE_FAILURE;
+ GST_STATE_RETURN (element) = ret;
+ GST_OBJECT_UNLOCK (element);
+
+ return ret;
+ }
+}
+
+/* gst_iterator_fold functions for pads_activate
+ * Stop the iterator if activating one pad failed. */
+static gboolean
+activate_pads (const GValue * vpad, GValue * ret, gboolean * active)
+{
+ GstPad *pad = g_value_get_object (vpad);
+ gboolean cont = TRUE;
+
+ if (!(cont = gst_pad_set_active (pad, *active)))
+ g_value_set_boolean (ret, FALSE);
+
+ return cont;
+}
+
+/* returns false on error or early cutout of the fold, true if all
+ * pads in @iter were (de)activated successfully. */
+static gboolean
+iterator_activate_fold_with_resync (GstIterator * iter,
+ GstIteratorFoldFunction func, gpointer user_data)
+{
+ GstIteratorResult ires;
+ GValue ret = { 0 };
+
+ /* no need to unset this later, it's just a boolean */
+ g_value_init (&ret, G_TYPE_BOOLEAN);
+ g_value_set_boolean (&ret, TRUE);
+
+ while (1) {
+ ires = gst_iterator_fold (iter, func, &ret, user_data);
+ switch (ires) {
+ case GST_ITERATOR_RESYNC:
+ /* need to reset the result again */
+ g_value_set_boolean (&ret, TRUE);
+ gst_iterator_resync (iter);
+ break;
+ case GST_ITERATOR_DONE:
+ /* all pads iterated, return collected value */
+ goto done;
+ default:
+ /* iterator returned _ERROR or premature end with _OK,
+ * mark an error and exit */
+ g_value_set_boolean (&ret, FALSE);
+ goto done;
+ }
+ }
+done:
+ /* return collected value */
+ return g_value_get_boolean (&ret);
+}
+
+/* is called with STATE_LOCK
+ *
+ * Pads are activated from source pads to sinkpads.
+ */
+static gboolean
+gst_element_pads_activate (GstElement * element, gboolean active)
+{
+ GstIterator *iter;
+ gboolean res;
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_ELEMENT_PADS, element,
+ "pads_activate with active %d", active);
+
+ iter = gst_element_iterate_src_pads (element);
+ res =
+ iterator_activate_fold_with_resync (iter,
+ (GstIteratorFoldFunction) activate_pads, &active);
+ gst_iterator_free (iter);
+ if (G_UNLIKELY (!res))
+ goto src_failed;
+
+ iter = gst_element_iterate_sink_pads (element);
+ res =
+ iterator_activate_fold_with_resync (iter,
+ (GstIteratorFoldFunction) activate_pads, &active);
+ gst_iterator_free (iter);
+ if (G_UNLIKELY (!res))
+ goto sink_failed;
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_ELEMENT_PADS, element,
+ "pads_activate successful");
+
+ return TRUE;
+
+ /* ERRORS */
+src_failed:
+ {
+ GST_CAT_DEBUG_OBJECT (GST_CAT_ELEMENT_PADS, element,
+ "source pads_activate failed");
+ return FALSE;
+ }
+sink_failed:
+ {
+ GST_CAT_DEBUG_OBJECT (GST_CAT_ELEMENT_PADS, element,
+ "sink pads_activate failed");
+ return FALSE;
+ }
+}
+
+/* is called with STATE_LOCK */
+static GstStateChangeReturn
+gst_element_change_state_func (GstElement * element, GstStateChange transition)
+{
+ GstState state, next;
+ GstStateChangeReturn result = GST_STATE_CHANGE_SUCCESS;
+
+ g_return_val_if_fail (GST_IS_ELEMENT (element), GST_STATE_CHANGE_FAILURE);
+
+ state = (GstState) GST_STATE_TRANSITION_CURRENT (transition);
+ next = GST_STATE_TRANSITION_NEXT (transition);
+
+ /* if the element already is in the given state, we just return success */
+ if (next == GST_STATE_VOID_PENDING || state == next)
+ goto was_ok;
+
+ GST_CAT_LOG_OBJECT (GST_CAT_STATES, element,
+ "default handler tries setting state from %s to %s (%04x)",
+ gst_element_state_get_name (state),
+ gst_element_state_get_name (next), transition);
+
+ switch (transition) {
+ case GST_STATE_CHANGE_NULL_TO_READY:
+ break;
+ case GST_STATE_CHANGE_READY_TO_PAUSED:
+ if (!gst_element_pads_activate (element, TRUE)) {
+ result = GST_STATE_CHANGE_FAILURE;
+ }
+ break;
+ case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
+ break;
+ case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
+ break;
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ case GST_STATE_CHANGE_READY_TO_NULL:
+ /* deactivate pads in both cases, since they are activated on
+ ready->paused but the element might not have made it to paused */
+ if (!gst_element_pads_activate (element, FALSE)) {
+ result = GST_STATE_CHANGE_FAILURE;
+ }
+ break;
+ default:
+ /* this will catch real but unhandled state changes;
+ * can only be caused by:
+ * - a new state was added
+ * - somehow the element was asked to jump across an intermediate state
+ */
+ g_warning ("Unhandled state change from %s to %s",
+ gst_element_state_get_name (state),
+ gst_element_state_get_name (next));
+ break;
+ }
+ return result;
+
+was_ok:
+ {
+ GST_OBJECT_LOCK (element);
+ result = GST_STATE_RETURN (element);
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
+ "element is already in the %s state",
+ gst_element_state_get_name (state));
+ GST_OBJECT_UNLOCK (element);
+
+ return result;
+ }
+}
+
+/**
+ * gst_element_get_factory:
+ * @element: a #GstElement to request the element factory of.
+ *
+ * Retrieves the factory that was used to create this element.
+ *
+ * Returns: (transfer none): the #GstElementFactory used for creating this
+ * element. no refcounting is needed.
+ */
+GstElementFactory *
+gst_element_get_factory (GstElement * element)
+{
+ g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
+
+ return GST_ELEMENT_GET_CLASS (element)->elementfactory;
+}
+
+static void
+gst_element_dispose (GObject * object)
+{
+ GstElement *element = GST_ELEMENT_CAST (object);
+ GstClock **clock_p;
+ GstBus **bus_p;
+
+ GST_CAT_INFO_OBJECT (GST_CAT_REFCOUNTING, element, "dispose");
+
+ if (GST_STATE (element) != GST_STATE_NULL)
+ goto not_null;
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_ELEMENT_PADS, element,
+ "removing %d pads", g_list_length (element->pads));
+ /* first we break all our links with the outside */
+ while (element->pads && element->pads->data) {
+ /* don't call _remove_pad with NULL */
+ gst_element_remove_pad (element, GST_PAD_CAST (element->pads->data));
+ }
+ if (G_UNLIKELY (element->pads != NULL)) {
+ g_critical ("could not remove pads from element %s",
+ GST_STR_NULL (GST_OBJECT_NAME (object)));
+ }
+
+ GST_OBJECT_LOCK (element);
+ clock_p = &element->clock;
+ bus_p = &element->bus;
+ gst_object_replace ((GstObject **) clock_p, NULL);
+ gst_object_replace ((GstObject **) bus_p, NULL);
+ GST_OBJECT_UNLOCK (element);
+
+ GST_CAT_INFO_OBJECT (GST_CAT_REFCOUNTING, element, "parent class dispose");
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+
+ return;
+
+ /* ERRORS */
+not_null:
+ {
+ gboolean is_locked;
+
+ is_locked = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_LOCKED_STATE);
+ g_critical
+ ("\nTrying to dispose element %s, but it is in %s%s instead of the NULL"
+ " state.\n"
+ "You need to explicitly set elements to the NULL state before\n"
+ "dropping the final reference, to allow them to clean up.\n"
+ "This problem may also be caused by a refcounting bug in the\n"
+ "application or some element.\n",
+ GST_OBJECT_NAME (element),
+ gst_element_state_get_name (GST_STATE (element)),
+ is_locked ? " (locked)" : "");
+ return;
+ }
+}
+
+static void
+gst_element_finalize (GObject * object)
+{
+ GstElement *element = GST_ELEMENT_CAST (object);
+
+ GST_CAT_INFO_OBJECT (GST_CAT_REFCOUNTING, element, "finalize");
+
+ g_cond_free (element->state_cond);
+ g_static_rec_mutex_free (&element->state_lock);
+
+ GST_CAT_INFO_OBJECT (GST_CAT_REFCOUNTING, element, "finalize parent");
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gst_element_set_bus_func (GstElement * element, GstBus * bus)
+{
+ GstBus **bus_p;
+
+ g_return_if_fail (GST_IS_ELEMENT (element));
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_PARENTAGE, element, "setting bus to %p", bus);
+
+ GST_OBJECT_LOCK (element);
+ bus_p = &GST_ELEMENT_BUS (element);
+ gst_object_replace ((GstObject **) bus_p, GST_OBJECT_CAST (bus));
+ GST_OBJECT_UNLOCK (element);
+}
+
+/**
+ * gst_element_set_bus:
+ * @element: a #GstElement to set the bus of.
+ * @bus: (transfer none): the #GstBus to set.
+ *
+ * Sets the bus of the element. Increases the refcount on the bus.
+ * For internal use only, unless you're testing elements.
+ *
+ * MT safe.
+ */
+void
+gst_element_set_bus (GstElement * element, GstBus * bus)
+{
+ GstElementClass *oclass;
+
+ g_return_if_fail (GST_IS_ELEMENT (element));
+
+ oclass = GST_ELEMENT_GET_CLASS (element);
+
+ if (oclass->set_bus)
+ oclass->set_bus (element, bus);
+}
+
+/**
+ * gst_element_get_bus:
+ * @element: a #GstElement to get the bus of.
+ *
+ * Returns the bus of the element. Note that only a #GstPipeline will provide a
+ * bus for the application.
+ *
+ * Returns: (transfer full): the element's #GstBus. unref after usage.
+ *
+ * MT safe.
+ */
+GstBus *
+gst_element_get_bus (GstElement * element)
+{
+ GstBus *result = NULL;
+
+ g_return_val_if_fail (GST_IS_ELEMENT (element), result);
+
+ GST_OBJECT_LOCK (element);
+ if ((result = GST_ELEMENT_BUS (element)))
+ gst_object_ref (result);
+ GST_OBJECT_UNLOCK (element);
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_BUS, element, "got bus %" GST_PTR_FORMAT,
+ result);
+
+ return result;
+}
diff --git a/gst/gstelement.h b/gst/gstelement.h
new file mode 100644
index 0000000..9cc30ed
--- /dev/null
+++ b/gst/gstelement.h
@@ -0,0 +1,811 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2000,2004 Wim Taymans <wim@fluendo.com>
+ *
+ * gstelement.h: Header for GstElement
+ *
+ * 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_ELEMENT_H__
+#define __GST_ELEMENT_H__
+
+/* gstelement.h and gstelementfactory.h include eachother */
+typedef struct _GstElement GstElement;
+typedef struct _GstElementClass GstElementClass;
+
+/* gstmessage.h needs State */
+/**
+ * GstState:
+ * @GST_STATE_VOID_PENDING: no pending state.
+ * @GST_STATE_NULL : the NULL state or initial state of an element.
+ * @GST_STATE_READY : the element is ready to go to PAUSED.
+ * @GST_STATE_PAUSED : the element is PAUSED, it is ready to accept and
+ * process data. Sink elements however only accept one
+ * buffer and then block.
+ * @GST_STATE_PLAYING : the element is PLAYING, the #GstClock is running and
+ * the data is flowing.
+ *
+ * The possible states an element can be in. States can be changed using
+ * gst_element_set_state() and checked using gst_element_get_state().
+ */
+typedef enum {
+ GST_STATE_VOID_PENDING = 0,
+ GST_STATE_NULL = 1,
+ GST_STATE_READY = 2,
+ GST_STATE_PAUSED = 3,
+ GST_STATE_PLAYING = 4
+} GstState;
+
+
+#include <gst/gstconfig.h>
+#include <gst/gstobject.h>
+#include <gst/gstpad.h>
+#include <gst/gstbus.h>
+#include <gst/gstclock.h>
+#include <gst/gstelementfactory.h>
+#include <gst/gstplugin.h>
+#include <gst/gstpluginfeature.h>
+#include <gst/gstindex.h>
+#include <gst/gstindexfactory.h>
+#include <gst/gstiterator.h>
+#include <gst/gstmessage.h>
+#include <gst/gstquery.h>
+#include <gst/gsttaglist.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_ELEMENT (gst_element_get_type ())
+#define GST_IS_ELEMENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_ELEMENT))
+#define GST_IS_ELEMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_ELEMENT))
+#define GST_ELEMENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_ELEMENT, GstElementClass))
+#define GST_ELEMENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_ELEMENT, GstElement))
+#define GST_ELEMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_ELEMENT, GstElementClass))
+#define GST_ELEMENT_CAST(obj) ((GstElement*)(obj))
+
+/**
+ * GstStateChangeReturn:
+ * @GST_STATE_CHANGE_FAILURE : the state change failed
+ * @GST_STATE_CHANGE_SUCCESS : the state change succeeded
+ * @GST_STATE_CHANGE_ASYNC : the state change will happen asynchronously
+ * @GST_STATE_CHANGE_NO_PREROLL: the state change succeeded but the element
+ * cannot produce data in %GST_STATE_PAUSED.
+ * This typically happens with live sources.
+ *
+ * The possible return values from a state change function such as
+ * gst_element_set_state(). Only @GST_STATE_CHANGE_FAILURE is a real failure.
+ */
+typedef enum {
+ GST_STATE_CHANGE_FAILURE = 0,
+ GST_STATE_CHANGE_SUCCESS = 1,
+ GST_STATE_CHANGE_ASYNC = 2,
+ GST_STATE_CHANGE_NO_PREROLL = 3
+} GstStateChangeReturn;
+
+/* NOTE: this probably should be done with an #ifdef to decide
+ * whether to safe-cast or to just do the non-checking cast.
+ */
+
+/**
+ * GST_STATE:
+ * @elem: a #GstElement to return state for.
+ *
+ * This macro returns the current #GstState of the element.
+ */
+#define GST_STATE(elem) (GST_ELEMENT_CAST(elem)->current_state)
+
+/**
+ * GST_STATE_NEXT:
+ * @elem: a #GstElement to return the next state for.
+ *
+ * This macro returns the next #GstState of the element.
+ */
+#define GST_STATE_NEXT(elem) (GST_ELEMENT_CAST(elem)->next_state)
+
+/**
+ * GST_STATE_PENDING:
+ * @elem: a #GstElement to return the pending state for.
+ *
+ * This macro returns the currently pending #GstState of the element.
+ */
+#define GST_STATE_PENDING(elem) (GST_ELEMENT_CAST(elem)->pending_state)
+
+/**
+ * GST_STATE_TARGET:
+ * @elem: a #GstElement to return the target state for.
+ *
+ * This macro returns the target #GstState of the element.
+ *
+ * Since: 0.10.13
+ */
+#define GST_STATE_TARGET(elem) (GST_ELEMENT_CAST(elem)->target_state)
+
+/**
+ * GST_STATE_RETURN:
+ * @elem: a #GstElement to return the last state result for.
+ *
+ * This macro returns the last #GstStateChangeReturn value.
+ */
+#define GST_STATE_RETURN(elem) (GST_ELEMENT_CAST(elem)->last_return)
+
+#define __GST_SIGN(val) ((val) < 0 ? -1 : ((val) > 0 ? 1 : 0))
+/**
+ * GST_STATE_GET_NEXT:
+ * @cur: A starting #GstState
+ * @pending: A target #GstState
+ *
+ * Given a current state @cur and a target state @pending, calculate the next (intermediate)
+ * #GstState.
+ */
+#define GST_STATE_GET_NEXT(cur,pending) ((GstState)((cur) + __GST_SIGN ((gint)(pending) - (gint)(cur))))
+/**
+ * GST_STATE_TRANSITION:
+ * @cur: A current state
+ * @next: A next state
+ *
+ * Given a current state @cur and a next state @next, calculate the associated
+ * #GstStateChange transition.
+ */
+#define GST_STATE_TRANSITION(cur,next) ((GstStateChange)(((cur)<<3)|(next)))
+/**
+ * GST_STATE_TRANSITION_CURRENT:
+ * @trans: A #GstStateChange
+ *
+ * Given a state transition @trans, extract the current #GstState.
+ */
+#define GST_STATE_TRANSITION_CURRENT(trans) ((GstState)((trans)>>3))
+/**
+ * GST_STATE_TRANSITION_NEXT:
+ * @trans: A #GstStateChange
+ *
+ * Given a state transition @trans, extract the next #GstState.
+ */
+#define GST_STATE_TRANSITION_NEXT(trans) ((GstState)((trans)&0x7))
+
+/**
+ * GstStateChange:
+ * @GST_STATE_CHANGE_NULL_TO_READY : state change from NULL to READY.
+ * <itemizedlist>
+ * <listitem><para>
+ * The element must check if the resources it needs are available. Device
+ * sinks and -sources typically try to probe the device to constrain their
+ * caps.
+ * </para></listitem>
+ * <listitem><para>
+ * The element opens the device (in case feature need to be probed).
+ * </para></listitem>
+ * </itemizedlist>
+ * @GST_STATE_CHANGE_READY_TO_PAUSED : state change from READY to PAUSED.
+ * <itemizedlist>
+ * <listitem><para>
+ * The element pads are activated in order to receive data in PAUSED.
+ * Streaming threads are started.
+ * </para></listitem>
+ * <listitem><para>
+ * Some elements might need to return %GST_STATE_CHANGE_ASYNC and complete
+ * the state change when they have enough information. It is a requirement
+ * for sinks to return %GST_STATE_CHANGE_ASYNC and complete the state change
+ * when they receive the first buffer or %GST_EVENT_EOS (preroll).
+ * Sinks also block the dataflow when in PAUSED.
+ * </para></listitem>
+ * <listitem><para>
+ * A pipeline resets the running_time to 0.
+ * </para></listitem>
+ * <listitem><para>
+ * Live sources return %GST_STATE_CHANGE_NO_PREROLL and don't generate data.
+ * </para></listitem>
+ * </itemizedlist>
+ * @GST_STATE_CHANGE_PAUSED_TO_PLAYING: state change from PAUSED to PLAYING.
+ * <itemizedlist>
+ * <listitem><para>
+ * Most elements ignore this state change.
+ * </para></listitem>
+ * <listitem><para>
+ * The pipeline selects a #GstClock and distributes this to all the children
+ * before setting them to PLAYING. This means that it is only alowed to
+ * synchronize on the #GstClock in the PLAYING state.
+ * </para></listitem>
+ * <listitem><para>
+ * The pipeline uses the #GstClock and the running_time to calculate the
+ * base_time. The base_time is distributed to all children when performing
+ * the state change.
+ * </para></listitem>
+ * <listitem><para>
+ * Sink elements stop blocking on the preroll buffer or event and start
+ * rendering the data.
+ * </para></listitem>
+ * <listitem><para>
+ * Sinks can post %GST_MESSAGE_EOS in the PLAYING state. It is not allowed
+ * to post %GST_MESSAGE_EOS when not in the PLAYING state.
+ * </para></listitem>
+ * <listitem><para>
+ * While streaming in PAUSED or PLAYING elements can create and remove
+ * sometimes pads.
+ * </para></listitem>
+ * <listitem><para>
+ * Live sources start generating data and return %GST_STATE_CHANGE_SUCCESS.
+ * </para></listitem>
+ * </itemizedlist>
+ * @GST_STATE_CHANGE_PLAYING_TO_PAUSED: state change from PLAYING to PAUSED.
+ * <itemizedlist>
+ * <listitem><para>
+ * Most elements ignore this state change.
+ * </para></listitem>
+ * <listitem><para>
+ * The pipeline calculates the running_time based on the last selected
+ * #GstClock and the base_time. It stores this information to continue
+ * playback when going back to the PLAYING state.
+ * </para></listitem>
+ * <listitem><para>
+ * Sinks unblock any #GstClock wait calls.
+ * </para></listitem>
+ * <listitem><para>
+ * When a sink does not have a pending buffer to play, it returns
+ * %GST_STATE_CHANGE_ASYNC from this state change and completes the state
+ * change when it receives a new buffer or an %GST_EVENT_EOS.
+ * </para></listitem>
+ * <listitem><para>
+ * Any queued %GST_MESSAGE_EOS items are removed since they will be reposted
+ * when going back to the PLAYING state. The EOS messages are queued in
+ * #GstBin containers.
+ * </para></listitem>
+ * <listitem><para>
+ * Live sources stop generating data and return %GST_STATE_CHANGE_NO_PREROLL.
+ * </para></listitem>
+ * </itemizedlist>
+ * @GST_STATE_CHANGE_PAUSED_TO_READY : state change from PAUSED to READY.
+ * <itemizedlist>
+ * <listitem><para>
+ * Sinks unblock any waits in the preroll.
+ * </para></listitem>
+ * <listitem><para>
+ * Elements unblock any waits on devices
+ * </para></listitem>
+ * <listitem><para>
+ * Chain or get_range functions return %GST_FLOW_WRONG_STATE.
+ * </para></listitem>
+ * <listitem><para>
+ * The element pads are deactivated so that streaming becomes impossible and
+ * all streaming threads are stopped.
+ * </para></listitem>
+ * <listitem><para>
+ * The sink forgets all negotiated formats
+ * </para></listitem>
+ * <listitem><para>
+ * Elements remove all sometimes pads
+ * </para></listitem>
+ * </itemizedlist>
+ * @GST_STATE_CHANGE_READY_TO_NULL : state change from READY to NULL.
+ * <itemizedlist>
+ * <listitem><para>
+ * Elements close devices
+ * </para></listitem>
+ * <listitem><para>
+ * Elements reset any internal state.
+ * </para></listitem>
+ * </itemizedlist>
+ *
+ * These are the different state changes an element goes through.
+ * %GST_STATE_NULL &rArr; %GST_STATE_PLAYING is called an upwards state change
+ * and %GST_STATE_PLAYING &rArr; %GST_STATE_NULL a downwards state change.
+ */
+typedef enum /*< flags=0 >*/
+{
+ GST_STATE_CHANGE_NULL_TO_READY = (GST_STATE_NULL<<3) | GST_STATE_READY,
+ GST_STATE_CHANGE_READY_TO_PAUSED = (GST_STATE_READY<<3) | GST_STATE_PAUSED,
+ GST_STATE_CHANGE_PAUSED_TO_PLAYING = (GST_STATE_PAUSED<<3) | GST_STATE_PLAYING,
+ GST_STATE_CHANGE_PLAYING_TO_PAUSED = (GST_STATE_PLAYING<<3) | GST_STATE_PAUSED,
+ GST_STATE_CHANGE_PAUSED_TO_READY = (GST_STATE_PAUSED<<3) | GST_STATE_READY,
+ GST_STATE_CHANGE_READY_TO_NULL = (GST_STATE_READY<<3) | GST_STATE_NULL
+} GstStateChange;
+
+/**
+ * GstElementFlags:
+ * @GST_ELEMENT_LOCKED_STATE: ignore state changes from parent
+ * @GST_ELEMENT_IS_SINK: the element is a sink
+ * @GST_ELEMENT_UNPARENTING: Child is being removed from the parent bin.
+ * gst_bin_remove() on a child already being removed immediately returns FALSE
+ * @GST_ELEMENT_IS_SOURCE: the element is a source. Since 0.10.31
+ * @GST_ELEMENT_FLAG_LAST: offset to define more flags
+ *
+ * The standard flags that an element may have.
+ */
+typedef enum
+{
+ GST_ELEMENT_LOCKED_STATE = (GST_OBJECT_FLAG_LAST << 0),
+ GST_ELEMENT_IS_SINK = (GST_OBJECT_FLAG_LAST << 1),
+ GST_ELEMENT_UNPARENTING = (GST_OBJECT_FLAG_LAST << 2),
+ GST_ELEMENT_IS_SOURCE = (GST_OBJECT_FLAG_LAST << 3),
+ /* padding */
+ GST_ELEMENT_FLAG_LAST = (GST_OBJECT_FLAG_LAST << 16)
+} GstElementFlags;
+
+/**
+ * GST_ELEMENT_IS_LOCKED_STATE:
+ * @elem: A #GstElement to query
+ *
+ * Check if the element is in the locked state and therefore will ignore state
+ * changes from its parent object.
+ */
+#define GST_ELEMENT_IS_LOCKED_STATE(elem) (GST_OBJECT_FLAG_IS_SET(elem,GST_ELEMENT_LOCKED_STATE))
+
+/**
+ * GST_ELEMENT_NAME:
+ * @elem: A #GstElement to query
+ *
+ * Gets the name of this element. Use only in core as this is not
+ * ABI-compatible. Others use gst_element_get_name()
+ */
+#define GST_ELEMENT_NAME(elem) (GST_OBJECT_NAME(elem))
+
+/**
+ * GST_ELEMENT_PARENT:
+ * @elem: A #GstElement to query
+ *
+ * Get the parent object of this element.
+ */
+#define GST_ELEMENT_PARENT(elem) (GST_ELEMENT_CAST(GST_OBJECT_PARENT(elem)))
+
+/**
+ * GST_ELEMENT_BUS:
+ * @elem: A #GstElement to query
+ *
+ * Get the message bus of this element.
+ */
+#define GST_ELEMENT_BUS(elem) (GST_ELEMENT_CAST(elem)->bus)
+
+/**
+ * GST_ELEMENT_CLOCK:
+ * @elem: A #GstElement to query
+ *
+ * Get the clock of this element
+ */
+#define GST_ELEMENT_CLOCK(elem) (GST_ELEMENT_CAST(elem)->clock)
+
+/**
+ * GST_ELEMENT_PADS:
+ * @elem: A #GstElement to query
+ *
+ * Get the pads of this elements.
+ */
+#define GST_ELEMENT_PADS(elem) (GST_ELEMENT_CAST(elem)->pads)
+
+/**
+ * GST_ELEMENT_START_TIME:
+ * @elem: a #GstElement to return the start time for.
+ *
+ * This macro returns the start_time of the @elem. The start_time is the
+ * running_time of the pipeline when the element went to PAUSED.
+ *
+ * Since: 0.10.24
+ */
+#define GST_ELEMENT_START_TIME(elem) (GST_ELEMENT_CAST(elem)->start_time)
+
+/**
+ * GST_ELEMENT_ERROR:
+ * @el: the element that generates the error
+ * @domain: like CORE, LIBRARY, RESOURCE or STREAM (see #gstreamer-GstGError)
+ * @code: error code defined for that domain (see #gstreamer-GstGError)
+ * @text: the message to display (format string and args enclosed in
+ parentheses)
+ * @debug: debugging information for the message (format string and args
+ enclosed in parentheses)
+ *
+ * Utility function that elements can use in case they encountered a fatal
+ * data processing error. The pipeline will post an error message and the
+ * application will be requested to stop further media processing.
+ */
+#define GST_ELEMENT_ERROR(el, domain, code, text, debug) \
+G_STMT_START { \
+ gchar *__txt = _gst_element_error_printf text; \
+ gchar *__dbg = _gst_element_error_printf debug; \
+ if (__txt) \
+ GST_WARNING_OBJECT (el, "error: %s", __txt); \
+ if (__dbg) \
+ GST_WARNING_OBJECT (el, "error: %s", __dbg); \
+ gst_element_message_full (GST_ELEMENT(el), GST_MESSAGE_ERROR, \
+ GST_ ## domain ## _ERROR, GST_ ## domain ## _ERROR_ ## code, \
+ __txt, __dbg, __FILE__, GST_FUNCTION, __LINE__); \
+} G_STMT_END
+
+/**
+ * GST_ELEMENT_WARNING:
+ * @el: the element that generates the warning
+ * @domain: like CORE, LIBRARY, RESOURCE or STREAM (see #gstreamer-GstGError)
+ * @code: error code defined for that domain (see #gstreamer-GstGError)
+ * @text: the message to display (format string and args enclosed in
+ parentheses)
+ * @debug: debugging information for the message (format string and args
+ enclosed in parentheses)
+ *
+ * Utility function that elements can use in case they encountered a non-fatal
+ * data processing problem. The pipeline will post a warning message and the
+ * application will be informed.
+ */
+#define GST_ELEMENT_WARNING(el, domain, code, text, debug) \
+G_STMT_START { \
+ gchar *__txt = _gst_element_error_printf text; \
+ gchar *__dbg = _gst_element_error_printf debug; \
+ if (__txt) \
+ GST_WARNING_OBJECT (el, "warning: %s", __txt); \
+ if (__dbg) \
+ GST_WARNING_OBJECT (el, "warning: %s", __dbg); \
+ gst_element_message_full (GST_ELEMENT(el), GST_MESSAGE_WARNING, \
+ GST_ ## domain ## _ERROR, GST_ ## domain ## _ERROR_ ## code, \
+ __txt, __dbg, __FILE__, GST_FUNCTION, __LINE__); \
+} G_STMT_END
+
+/**
+ * GST_ELEMENT_INFO:
+ * @el: the element that generates the information
+ * @domain: like CORE, LIBRARY, RESOURCE or STREAM (see #gstreamer-GstGError)
+ * @code: error code defined for that domain (see #gstreamer-GstGError)
+ * @text: the message to display (format string and args enclosed in
+ parentheses)
+ * @debug: debugging information for the message (format string and args
+ enclosed in parentheses)
+ *
+ * Utility function that elements can use in case they want to inform
+ * the application of something noteworthy that is not an error.
+ * The pipeline will post a info message and the
+ * application will be informed.
+ *
+ * Since: 0.10.12
+ */
+#define GST_ELEMENT_INFO(el, domain, code, text, debug) \
+G_STMT_START { \
+ gchar *__txt = _gst_element_error_printf text; \
+ gchar *__dbg = _gst_element_error_printf debug; \
+ if (__txt) \
+ GST_INFO_OBJECT (el, "info: %s", __txt); \
+ if (__dbg) \
+ GST_INFO_OBJECT (el, "info: %s", __dbg); \
+ gst_element_message_full (GST_ELEMENT(el), GST_MESSAGE_INFO, \
+ GST_ ## domain ## _ERROR, GST_ ## domain ## _ERROR_ ## code, \
+ __txt, __dbg, __FILE__, GST_FUNCTION, __LINE__); \
+} G_STMT_END
+
+/* the state change mutexes and conds */
+/**
+ * GST_STATE_GET_LOCK:
+ * @elem: a #GstElement
+ *
+ * Get a reference to the state lock of @elem.
+ * This lock is used by the core. It is taken while getting or setting
+ * the state, during state changes, and while finalizing.
+ */
+#define GST_STATE_GET_LOCK(elem) (&(GST_ELEMENT_CAST(elem)->state_lock))
+/**
+ * GST_STATE_GET_COND:
+ * @elem: a #GstElement
+ *
+ * Get the conditional used to signal the completion of a state change.
+ */
+#define GST_STATE_GET_COND(elem) (GST_ELEMENT_CAST(elem)->state_cond)
+
+#define GST_STATE_LOCK(elem) g_static_rec_mutex_lock(GST_STATE_GET_LOCK(elem))
+#define GST_STATE_TRYLOCK(elem) g_static_rec_mutex_trylock(GST_STATE_GET_LOCK(elem))
+#define GST_STATE_UNLOCK(elem) g_static_rec_mutex_unlock(GST_STATE_GET_LOCK(elem))
+#define GST_STATE_UNLOCK_FULL(elem) g_static_rec_mutex_unlock_full(GST_STATE_GET_LOCK(elem))
+#define GST_STATE_LOCK_FULL(elem,t) g_static_rec_mutex_lock_full(GST_STATE_GET_LOCK(elem), t)
+#define GST_STATE_WAIT(elem) g_cond_wait (GST_STATE_GET_COND (elem), \
+ GST_OBJECT_GET_LOCK (elem))
+#define GST_STATE_TIMED_WAIT(elem, timeval) g_cond_timed_wait (GST_STATE_GET_COND (elem), \
+ GST_OBJECT_GET_LOCK (elem), timeval)
+#define GST_STATE_SIGNAL(elem) g_cond_signal (GST_STATE_GET_COND (elem));
+#define GST_STATE_BROADCAST(elem) g_cond_broadcast (GST_STATE_GET_COND (elem));
+
+/**
+ * GstElement:
+ * @state_lock: Used to serialize execution of gst_element_set_state()
+ * @state_cond: Used to signal completion of a state change
+ * @state_cookie: Used to detect concurrent execution of
+ * gst_element_set_state() and gst_element_get_state()
+ * @target_state: the target state of an element as set by the application
+ * @current_state: the current state of an element
+ * @next_state: the next state of an element, can be #GST_STATE_VOID_PENDING if
+ * the element is in the correct state.
+ * @pending_state: the final state the element should go to, can be
+ * #GST_STATE_VOID_PENDING if the element is in the correct state
+ * @last_return: the last return value of an element state change
+ * @bus: the bus of the element. This bus is provided to the element by the
+ * parent element or the application. A #GstPipeline has a bus of its own.
+ * @clock: the clock of the element. This clock is usually provided to the
+ * element by the toplevel #GstPipeline.
+ * @base_time: the time of the clock right before the element is set to
+ * PLAYING. Subtracting @base_time from the current clock time in the PLAYING
+ * state will yield the running_time against the clock.
+ * @start_time: the running_time of the last PAUSED state
+ * @numpads: number of pads of the element, includes both source and sink pads.
+ * @pads: list of pads
+ * @numsrcpads: number of source pads of the element.
+ * @srcpads: list of source pads
+ * @numsinkpads: number of sink pads of the element.
+ * @sinkpads: list of sink pads
+ * @pads_cookie: updated whenever the a pad is added or removed
+ *
+ * GStreamer element abstract base class.
+ */
+struct _GstElement
+{
+ GstObject object;
+
+ /*< public >*/ /* with LOCK */
+ GStaticRecMutex state_lock;
+
+ /* element state */
+ GCond *state_cond;
+ guint32 state_cookie;
+ GstState target_state;
+ GstState current_state;
+ GstState next_state;
+ GstState pending_state;
+ GstStateChangeReturn last_return;
+
+ GstBus *bus;
+
+ /* allocated clock */
+ GstClock *clock;
+ GstClockTimeDiff base_time; /* NULL/READY: 0 - PAUSED: current time - PLAYING: difference to clock */
+ GstClockTime start_time;
+
+ /* element pads, these lists can only be iterated while holding
+ * the LOCK or checking the cookie after each LOCK. */
+ guint16 numpads;
+ GList *pads;
+ guint16 numsrcpads;
+ GList *srcpads;
+ guint16 numsinkpads;
+ GList *sinkpads;
+ guint32 pads_cookie;
+
+ /*< private >*/
+ gpointer _gst_reserved[GST_PADDING];
+};
+
+/**
+ * GstElementClass:
+ * @parent_class: the parent class structure
+ * @metadata: metadata for elements of this class
+ * @elementfactory: the #GstElementFactory that creates these elements
+ * @padtemplates: a #GList of #GstPadTemplate
+ * @numpadtemplates: the number of padtemplates
+ * @pad_templ_cookie: changed whenever the padtemplates change
+ * @request_new_pad: called when a new pad is requested
+ * @release_pad: called when a request pad is to be released
+ * @get_state: get the state of the element
+ * @set_state: set a new state on the element
+ * @change_state: called by @set_state to perform an incremental state change
+ * @set_bus: set a #GstBus on the element
+ * @provide_clock: gets the #GstClock provided by the element
+ * @set_clock: set the #GstClock on the element
+ * @get_index: set a #GstIndex on the element
+ * @set_index: get the #GstIndex of an element
+ * @send_event: send a #GstEvent to the element
+ * @get_query_types: get the supported #GstQueryType of this element
+ * @query: perform a #GstQuery on the element
+ * @state_changed: called immediately after a new state was set. Since: 0.10.35.
+ *
+ * GStreamer element class. Override the vmethods to implement the element
+ * functionality.
+ */
+struct _GstElementClass
+{
+ GstObjectClass parent_class;
+
+ /*< public >*/
+ /* the element metadata */
+ gpointer metadata;
+
+ /* factory that the element was created from */
+ GstElementFactory *elementfactory;
+
+ /* templates for our pads */
+ GList *padtemplates;
+ gint numpadtemplates;
+ guint32 pad_templ_cookie;
+
+ /*< private >*/
+ /* signal callbacks */
+ void (*pad_added) (GstElement *element, GstPad *pad);
+ void (*pad_removed) (GstElement *element, GstPad *pad);
+ void (*no_more_pads) (GstElement *element);
+
+ /*< public >*/
+ /* virtual methods for subclasses */
+
+ /* request/release pads */
+ GstPad* (*request_new_pad) (GstElement *element, GstPadTemplate *templ,
+ const gchar* name, const GstCaps *caps);
+ void (*release_pad) (GstElement *element, GstPad *pad);
+
+ /* state changes */
+ GstStateChangeReturn (*get_state) (GstElement * element, GstState * state,
+ GstState * pending, GstClockTime timeout);
+ GstStateChangeReturn (*set_state) (GstElement *element, GstState state);
+ GstStateChangeReturn (*change_state) (GstElement *element, GstStateChange transition);
+ void (*state_changed) (GstElement *element, GstState oldstate,
+ GstState newstate, GstState pending);
+
+ /* bus */
+ void (*set_bus) (GstElement * element, GstBus * bus);
+
+ /* set/get clocks */
+ GstClock* (*provide_clock) (GstElement *element);
+ gboolean (*set_clock) (GstElement *element, GstClock *clock);
+
+ /* index */
+ GstIndex* (*get_index) (GstElement *element);
+ void (*set_index) (GstElement *element, GstIndex *index);
+
+ /* query functions */
+ gboolean (*send_event) (GstElement *element, GstEvent *event);
+
+ const GstQueryType* (*get_query_types) (GstElement *element);
+ gboolean (*query) (GstElement *element, GstQuery *query);
+
+ /*< private >*/
+ gpointer _gst_reserved[GST_PADDING];
+};
+
+/* element class pad templates */
+void gst_element_class_add_pad_template (GstElementClass *klass, GstPadTemplate *templ);
+GstPadTemplate* gst_element_class_get_pad_template (GstElementClass *element_class, const gchar *name);
+GList* gst_element_class_get_pad_template_list (GstElementClass *element_class);
+
+/* element class meta data */
+void gst_element_class_set_metadata (GstElementClass *klass,
+ const gchar *longname,
+ const gchar *classification,
+ const gchar *description,
+ const gchar *author);
+void gst_element_class_add_metadata (GstElementClass * klass,
+ const gchar * key, const gchar * value);
+const gchar * gst_element_class_get_metadata (GstElementClass * klass,
+ const gchar * key);
+
+
+/* element instance */
+GType gst_element_get_type (void);
+
+/* basic name and parentage stuff from GstObject */
+
+/**
+ * gst_element_get_name:
+ * @elem: a #GstElement to get the name of @elem.
+ *
+ * Returns a copy of the name of @elem.
+ * Caller should g_free() the return value after usage.
+ * For a nameless element, this returns NULL, which you can safely g_free()
+ * as well.
+ *
+ * Returns: (transfer full): the name of @elem. g_free() after usage. MT safe.
+ *
+ */
+#define gst_element_get_name(elem) gst_object_get_name(GST_OBJECT_CAST(elem))
+
+/**
+ * gst_element_set_name:
+ * @elem: a #GstElement to set the name of.
+ * @name: the new name
+ *
+ * Sets the name of the element, getting rid of the old name if there was one.
+ */
+#define gst_element_set_name(elem,name) gst_object_set_name(GST_OBJECT_CAST(elem),name)
+
+/**
+ * gst_element_get_parent:
+ * @elem: a #GstElement to get the parent of.
+ *
+ * Get the parent of an element.
+ *
+ * Returns: (transfer full): the parent of an element.
+ */
+#define gst_element_get_parent(elem) gst_object_get_parent(GST_OBJECT_CAST(elem))
+
+/**
+ * gst_element_set_parent:
+ * @elem: a #GstElement to set the parent of.
+ * @parent: the new parent #GstObject of the element.
+ *
+ * Sets the parent of an element.
+ */
+#define gst_element_set_parent(elem,parent) gst_object_set_parent(GST_OBJECT_CAST(elem),parent)
+
+/* clocking */
+gboolean gst_element_requires_clock (GstElement *element);
+gboolean gst_element_provides_clock (GstElement *element);
+GstClock* gst_element_provide_clock (GstElement *element);
+GstClock* gst_element_get_clock (GstElement *element);
+gboolean gst_element_set_clock (GstElement *element, GstClock *clock);
+void gst_element_set_base_time (GstElement *element, GstClockTime time);
+GstClockTime gst_element_get_base_time (GstElement *element);
+void gst_element_set_start_time (GstElement *element, GstClockTime time);
+GstClockTime gst_element_get_start_time (GstElement *element);
+
+/* indexes */
+gboolean gst_element_is_indexable (GstElement *element);
+void gst_element_set_index (GstElement *element, GstIndex *index);
+GstIndex* gst_element_get_index (GstElement *element);
+
+/* bus */
+void gst_element_set_bus (GstElement * element, GstBus * bus);
+GstBus * gst_element_get_bus (GstElement * element);
+
+/* pad management */
+gboolean gst_element_add_pad (GstElement *element, GstPad *pad);
+gboolean gst_element_remove_pad (GstElement *element, GstPad *pad);
+void gst_element_no_more_pads (GstElement *element);
+
+GstPad* gst_element_get_static_pad (GstElement *element, const gchar *name);
+GstPad* gst_element_get_request_pad (GstElement *element, const gchar *name);
+GstPad* gst_element_request_pad (GstElement *element, GstPadTemplate *templ,
+ const gchar * name, const GstCaps *caps);
+void gst_element_release_request_pad (GstElement *element, GstPad *pad);
+
+GstIterator * gst_element_iterate_pads (GstElement * element);
+GstIterator * gst_element_iterate_src_pads (GstElement * element);
+GstIterator * gst_element_iterate_sink_pads (GstElement * element);
+
+/* event/query/format stuff */
+gboolean gst_element_send_event (GstElement *element, GstEvent *event);
+gboolean gst_element_seek (GstElement *element, gdouble rate,
+ GstFormat format, GstSeekFlags flags,
+ GstSeekType cur_type, gint64 cur,
+ GstSeekType stop_type, gint64 stop);
+const GstQueryType* gst_element_get_query_types (GstElement *element);
+gboolean gst_element_query (GstElement *element, GstQuery *query);
+
+/* messages */
+gboolean gst_element_post_message (GstElement * element, GstMessage * message);
+
+/* error handling */
+/* gcc versions < 3.3 warn about NULL being passed as format to printf */
+#if (defined(GST_USING_PRINTF_EXTENSION) || !defined(__GNUC__) || (__GNUC__ < 3) || (__GNUC__ == 3 && __GNUC_MINOR__ < 3))
+gchar * _gst_element_error_printf (const gchar *format, ...);
+#else
+gchar * _gst_element_error_printf (const gchar *format, ...) G_GNUC_PRINTF (1, 2);
+#endif
+void gst_element_message_full (GstElement * element, GstMessageType type,
+ GQuark domain, gint code, gchar * text,
+ gchar * debug, const gchar * file,
+ const gchar * function, gint line);
+
+/* state management */
+gboolean gst_element_is_locked_state (GstElement *element);
+gboolean gst_element_set_locked_state (GstElement *element, gboolean locked_state);
+gboolean gst_element_sync_state_with_parent (GstElement *element);
+
+GstStateChangeReturn gst_element_get_state (GstElement * element,
+ GstState * state,
+ GstState * pending,
+ GstClockTime timeout);
+GstStateChangeReturn gst_element_set_state (GstElement *element, GstState state);
+
+void gst_element_abort_state (GstElement * element);
+GstStateChangeReturn gst_element_change_state (GstElement * element,
+ GstStateChange transition);
+GstStateChangeReturn gst_element_continue_state (GstElement * element,
+ GstStateChangeReturn ret);
+void gst_element_lost_state (GstElement * element);
+
+/* factory management */
+GstElementFactory* gst_element_get_factory (GstElement *element);
+
+G_END_DECLS
+
+#endif /* __GST_ELEMENT_H__ */
diff --git a/gst/gstelementfactory.c b/gst/gstelementfactory.c
new file mode 100644
index 0000000..647674e
--- /dev/null
+++ b/gst/gstelementfactory.c
@@ -0,0 +1,815 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2000 Wim Taymans <wtay@chello.be>
+ * 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
+ *
+ * gstelementfactory.c: GstElementFactory object, support routines
+ *
+ * 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:gstelementfactory
+ * @short_description: Create GstElements from a factory
+ * @see_also: #GstElement, #GstPlugin, #GstPluginFeature, #GstPadTemplate.
+ *
+ * #GstElementFactory is used to create instances of elements. A
+ * GstElementfactory can be added to a #GstPlugin as it is also a
+ * #GstPluginFeature.
+ *
+ * Use the gst_element_factory_find() and gst_element_factory_create()
+ * functions to create element instances or use gst_element_factory_make() as a
+ * convenient shortcut.
+ *
+ * The following code example shows you how to create a GstFileSrc element.
+ *
+ * <example>
+ * <title>Using an element factory</title>
+ * <programlisting language="c">
+ * #include &lt;gst/gst.h&gt;
+ *
+ * GstElement *src;
+ * GstElementFactory *srcfactory;
+ *
+ * gst_init (&amp;argc, &amp;argv);
+ *
+ * srcfactory = gst_element_factory_find ("filesrc");
+ * g_return_if_fail (srcfactory != NULL);
+ * src = gst_element_factory_create (srcfactory, "src");
+ * g_return_if_fail (src != NULL);
+ * ...
+ * </programlisting>
+ * </example>
+ *
+ * Last reviewed on 2005-11-23 (0.9.5)
+ */
+
+#include "gst_private.h"
+
+#include "gstelement.h"
+#include "gstelementmetadata.h"
+#include "gstinfo.h"
+#include "gsturi.h"
+#include "gstregistry.h"
+#include "gst.h"
+
+#include "glib-compat-private.h"
+
+GST_DEBUG_CATEGORY_STATIC (element_factory_debug);
+#define GST_CAT_DEFAULT element_factory_debug
+
+static void gst_element_factory_finalize (GObject * object);
+static void gst_element_factory_cleanup (GstElementFactory * factory);
+
+/* static guint gst_element_factory_signals[LAST_SIGNAL] = { 0 }; */
+
+/* this is defined in gstelement.c */
+extern GQuark _gst_elementclass_factory;
+
+#define _do_init \
+{ \
+ GST_DEBUG_CATEGORY_INIT (element_factory_debug, "GST_ELEMENT_FACTORY", \
+ GST_DEBUG_BOLD | GST_DEBUG_FG_WHITE | GST_DEBUG_BG_RED, \
+ "element factories keep information about installed elements"); \
+}
+
+G_DEFINE_TYPE_WITH_CODE (GstElementFactory, gst_element_factory,
+ GST_TYPE_PLUGIN_FEATURE, _do_init);
+
+static void
+gst_element_factory_class_init (GstElementFactoryClass * klass)
+{
+ GObjectClass *gobject_class = (GObjectClass *) klass;
+
+ gobject_class->finalize = gst_element_factory_finalize;
+}
+
+static void
+gst_element_factory_init (GstElementFactory * factory)
+{
+ factory->staticpadtemplates = NULL;
+ factory->numpadtemplates = 0;
+
+ factory->uri_type = GST_URI_UNKNOWN;
+ factory->uri_protocols = NULL;
+
+ factory->interfaces = NULL;
+}
+
+static void
+gst_element_factory_finalize (GObject * object)
+{
+ GstElementFactory *factory = GST_ELEMENT_FACTORY (object);
+
+ gst_element_factory_cleanup (factory);
+ G_OBJECT_CLASS (gst_element_factory_parent_class)->finalize (object);
+}
+
+/**
+ * gst_element_factory_find:
+ * @name: name of factory to find
+ *
+ * Search for an element factory of the given name. Refs the returned
+ * element factory; caller is responsible for unreffing.
+ *
+ * Returns: (transfer full): #GstElementFactory if found, NULL otherwise
+ */
+GstElementFactory *
+gst_element_factory_find (const gchar * name)
+{
+ GstPluginFeature *feature;
+
+ g_return_val_if_fail (name != NULL, NULL);
+
+ feature = gst_registry_find_feature (gst_registry_get_default (), name,
+ GST_TYPE_ELEMENT_FACTORY);
+ if (feature)
+ return GST_ELEMENT_FACTORY (feature);
+
+ /* this isn't an error, for instance when you query if an element factory is
+ * present */
+ GST_LOG ("no such element factory \"%s\"", name);
+ return NULL;
+}
+
+static void
+gst_element_factory_cleanup (GstElementFactory * factory)
+{
+ GList *item;
+
+ if (factory->metadata) {
+ gst_structure_free ((GstStructure *) factory->metadata);
+ factory->metadata = NULL;
+ }
+ if (factory->type) {
+ factory->type = G_TYPE_INVALID;
+ }
+
+ for (item = factory->staticpadtemplates; item; item = item->next) {
+ GstStaticPadTemplate *templ = item->data;
+
+ gst_static_caps_cleanup (&templ->static_caps);
+ g_slice_free (GstStaticPadTemplate, templ);
+ }
+ g_list_free (factory->staticpadtemplates);
+ factory->staticpadtemplates = NULL;
+ factory->numpadtemplates = 0;
+ factory->uri_type = GST_URI_UNKNOWN;
+ if (factory->uri_protocols) {
+ g_strfreev (factory->uri_protocols);
+ factory->uri_protocols = NULL;
+ }
+
+ g_list_free (factory->interfaces);
+ factory->interfaces = NULL;
+}
+
+/**
+ * gst_element_register:
+ * @plugin: (allow-none): #GstPlugin to register the element with, or NULL for
+ * a static element (note that passing NULL only works in GStreamer 0.10.13
+ * and later)
+ * @name: name of elements of this type
+ * @rank: rank of element (higher rank means more importance when autoplugging)
+ * @type: GType of element to register
+ *
+ * Create a new elementfactory capable of instantiating objects of the
+ * @type and add the factory to @plugin.
+ *
+ * Returns: TRUE, if the registering succeeded, FALSE on error
+ */
+gboolean
+gst_element_register (GstPlugin * plugin, const gchar * name, guint rank,
+ GType type)
+{
+ GstPluginFeature *existing_feature;
+ GstRegistry *registry;
+ GstElementFactory *factory;
+ GType *interfaces;
+ guint n_interfaces, i;
+ GstElementClass *klass;
+ GList *item;
+
+ g_return_val_if_fail (name != NULL, FALSE);
+ g_return_val_if_fail (g_type_is_a (type, GST_TYPE_ELEMENT), FALSE);
+
+ registry = gst_registry_get_default ();
+
+ /* check if feature already exists, if it exists there is no need to update it
+ * when the registry is getting updated, outdated plugins and all their
+ * features are removed and readded.
+ */
+ existing_feature = gst_registry_lookup_feature (registry, name);
+ if (existing_feature) {
+ GST_DEBUG_OBJECT (registry, "update existing feature %p (%s)",
+ existing_feature, name);
+ factory = GST_ELEMENT_FACTORY_CAST (existing_feature);
+ factory->type = type;
+ existing_feature->loaded = TRUE;
+ g_type_set_qdata (type, _gst_elementclass_factory, factory);
+ gst_object_unref (existing_feature);
+ return TRUE;
+ }
+
+ factory =
+ GST_ELEMENT_FACTORY_CAST (g_object_newv (GST_TYPE_ELEMENT_FACTORY, 0,
+ NULL));
+ gst_plugin_feature_set_name (GST_PLUGIN_FEATURE_CAST (factory), name);
+ GST_LOG_OBJECT (factory, "Created new elementfactory for type %s",
+ g_type_name (type));
+
+ /* provide info needed during class structure setup */
+ g_type_set_qdata (type, _gst_elementclass_factory, factory);
+ klass = GST_ELEMENT_CLASS (g_type_class_ref (type));
+#if 0
+ /* FIXME */
+ if ((klass->details.longname == NULL) ||
+ (klass->details.klass == NULL) || (klass->details.author == NULL))
+ goto detailserror;
+#endif
+
+ factory->type = type;
+ factory->metadata = gst_structure_copy ((GstStructure *) klass->metadata);
+
+ for (item = klass->padtemplates; item; item = item->next) {
+ GstPadTemplate *templ = item->data;
+ GstStaticPadTemplate *newt;
+ gchar *caps_string = gst_caps_to_string (templ->caps);
+
+ newt = g_slice_new (GstStaticPadTemplate);
+ newt->name_template = g_intern_string (templ->name_template);
+ newt->direction = templ->direction;
+ newt->presence = templ->presence;
+ newt->static_caps.caps.mini_object.refcount = 0;
+ newt->static_caps.string = g_intern_string (caps_string);
+ factory->staticpadtemplates =
+ g_list_append (factory->staticpadtemplates, newt);
+
+ g_free (caps_string);
+ }
+ factory->numpadtemplates = klass->numpadtemplates;
+
+ /* special stuff for URI handling */
+ if (g_type_is_a (type, GST_TYPE_URI_HANDLER)) {
+ GstURIHandlerInterface *iface = (GstURIHandlerInterface *)
+ g_type_interface_peek (klass, GST_TYPE_URI_HANDLER);
+
+ if (!iface || !iface->get_type || !iface->get_protocols)
+ goto urierror;
+ if (iface->get_type)
+ factory->uri_type = iface->get_type (factory->type);
+ if (!GST_URI_TYPE_IS_VALID (factory->uri_type))
+ goto urierror;
+ if (iface->get_protocols)
+ factory->uri_protocols = iface->get_protocols (factory->type);
+ if (!factory->uri_protocols)
+ goto urierror;
+ }
+
+ interfaces = g_type_interfaces (type, &n_interfaces);
+ for (i = 0; i < n_interfaces; i++) {
+ __gst_element_factory_add_interface (factory, g_type_name (interfaces[i]));
+ }
+ g_free (interfaces);
+
+ if (plugin && plugin->desc.name) {
+ GST_PLUGIN_FEATURE_CAST (factory)->plugin_name = plugin->desc.name;
+ GST_PLUGIN_FEATURE_CAST (factory)->plugin = plugin;
+ g_object_add_weak_pointer ((GObject *) plugin,
+ (gpointer *) & GST_PLUGIN_FEATURE_CAST (factory)->plugin);
+ } else {
+ GST_PLUGIN_FEATURE_CAST (factory)->plugin_name = "NULL";
+ GST_PLUGIN_FEATURE_CAST (factory)->plugin = NULL;
+ }
+ gst_plugin_feature_set_rank (GST_PLUGIN_FEATURE_CAST (factory), rank);
+ GST_PLUGIN_FEATURE_CAST (factory)->loaded = TRUE;
+
+ gst_registry_add_feature (registry, GST_PLUGIN_FEATURE_CAST (factory));
+
+ return TRUE;
+
+ /* ERRORS */
+urierror:
+ {
+ GST_WARNING_OBJECT (factory, "error with uri handler!");
+ gst_element_factory_cleanup (factory);
+ return FALSE;
+ }
+
+#if 0
+detailserror:
+ {
+ GST_WARNING_OBJECT (factory,
+ "The GstElementDetails don't seem to have been set properly");
+ gst_element_factory_cleanup (factory);
+ return FALSE;
+ }
+#endif
+}
+
+/**
+ * gst_element_factory_create:
+ * @factory: factory to instantiate
+ * @name: (allow-none): name of new element, or NULL to automatically create
+ * a unique name
+ *
+ * Create a new element of the type defined by the given elementfactory.
+ * It will be given the name supplied, since all elements require a name as
+ * their first argument.
+ *
+ * Returns: (transfer full): new #GstElement or NULL if the element couldn't
+ * be created
+ */
+GstElement *
+gst_element_factory_create (GstElementFactory * factory, const gchar * name)
+{
+ GstElement *element;
+ GstElementClass *oclass;
+ GstElementFactory *newfactory;
+
+ g_return_val_if_fail (factory != NULL, NULL);
+
+ newfactory =
+ GST_ELEMENT_FACTORY (gst_plugin_feature_load (GST_PLUGIN_FEATURE
+ (factory)));
+
+ if (newfactory == NULL)
+ goto load_failed;
+
+ factory = newfactory;
+
+ if (name)
+ GST_INFO ("creating element \"%s\" named \"%s\"",
+ GST_OBJECT_NAME (factory), GST_STR_NULL (name));
+ else
+ GST_INFO ("creating element \"%s\"", GST_OBJECT_NAME (factory));
+
+ if (factory->type == 0)
+ goto no_type;
+
+ /* create an instance of the element, cast so we don't assert on NULL
+ * also set name as early as we can
+ */
+ if (name)
+ element =
+ GST_ELEMENT_CAST (g_object_new (factory->type, "name", name, NULL));
+ else
+ element = GST_ELEMENT_CAST (g_object_newv (factory->type, 0, NULL));
+ if (G_UNLIKELY (element == NULL))
+ goto no_element;
+
+ /* fill in the pointer to the factory in the element class. The
+ * class will not be unreffed currently.
+ * Be thread safe as there might be 2 threads creating the first instance of
+ * an element at the same moment
+ */
+ oclass = GST_ELEMENT_GET_CLASS (element);
+ if (!G_ATOMIC_POINTER_COMPARE_AND_EXCHANGE (&oclass->elementfactory, NULL,
+ factory))
+ gst_object_unref (factory);
+
+ GST_DEBUG ("created element \"%s\"", GST_OBJECT_NAME (factory));
+
+ return element;
+
+ /* ERRORS */
+load_failed:
+ {
+ GST_WARNING_OBJECT (factory,
+ "loading plugin containing feature %s returned NULL!", name);
+ return NULL;
+ }
+no_type:
+ {
+ GST_WARNING_OBJECT (factory, "factory has no type");
+ gst_object_unref (factory);
+ return NULL;
+ }
+no_element:
+ {
+ GST_WARNING_OBJECT (factory, "could not create element");
+ gst_object_unref (factory);
+ return NULL;
+ }
+}
+
+/**
+ * gst_element_factory_make:
+ * @factoryname: a named factory to instantiate
+ * @name: (allow-none): name of new element, or NULL to automatically create
+ * a unique name
+ *
+ * Create a new element of the type defined by the given element factory.
+ * If name is NULL, then the element will receive a guaranteed unique name,
+ * consisting of the element factory name and a number.
+ * If name is given, it will be given the name supplied.
+ *
+ * Returns: (transfer full): new #GstElement or NULL if unable to create element
+ */
+GstElement *
+gst_element_factory_make (const gchar * factoryname, const gchar * name)
+{
+ GstElementFactory *factory;
+ GstElement *element;
+
+ g_return_val_if_fail (factoryname != NULL, NULL);
+ g_return_val_if_fail (gst_is_initialized (), NULL);
+
+ GST_LOG ("gstelementfactory: make \"%s\" \"%s\"",
+ factoryname, GST_STR_NULL (name));
+
+ factory = gst_element_factory_find (factoryname);
+ if (factory == NULL)
+ goto no_factory;
+
+ GST_LOG_OBJECT (factory, "found factory %p", factory);
+ element = gst_element_factory_create (factory, name);
+ if (element == NULL)
+ goto create_failed;
+
+ gst_object_unref (factory);
+ return element;
+
+ /* ERRORS */
+no_factory:
+ {
+ GST_INFO ("no such element factory \"%s\"!", factoryname);
+ return NULL;
+ }
+create_failed:
+ {
+ GST_INFO_OBJECT (factory, "couldn't create instance!");
+ gst_object_unref (factory);
+ return NULL;
+ }
+}
+
+void
+__gst_element_factory_add_static_pad_template (GstElementFactory * factory,
+ GstStaticPadTemplate * templ)
+{
+ g_return_if_fail (factory != NULL);
+ g_return_if_fail (templ != NULL);
+
+ factory->staticpadtemplates =
+ g_list_append (factory->staticpadtemplates, templ);
+ factory->numpadtemplates++;
+}
+
+/**
+ * gst_element_factory_get_element_type:
+ * @factory: factory to get managed #GType from
+ *
+ * Get the #GType for elements managed by this factory. The type can
+ * only be retrieved if the element factory is loaded, which can be
+ * assured with gst_plugin_feature_load().
+ *
+ * Returns: the #GType for elements managed by this factory or 0 if
+ * the factory is not loaded.
+ */
+GType
+gst_element_factory_get_element_type (GstElementFactory * factory)
+{
+ g_return_val_if_fail (GST_IS_ELEMENT_FACTORY (factory), 0);
+
+ return factory->type;
+}
+
+const gchar *
+gst_element_factory_get_metadata (GstElementFactory * factory,
+ const gchar * key)
+{
+ return gst_structure_get_string ((GstStructure *) factory->metadata, key);
+}
+
+/**
+ * gst_element_factory_get_num_pad_templates:
+ * @factory: a #GstElementFactory
+ *
+ * Gets the number of pad_templates in this factory.
+ *
+ * Returns: the number of pad_templates
+ */
+guint
+gst_element_factory_get_num_pad_templates (GstElementFactory * factory)
+{
+ g_return_val_if_fail (GST_IS_ELEMENT_FACTORY (factory), 0);
+
+ return factory->numpadtemplates;
+}
+
+/**
+ * __gst_element_factory_add_interface:
+ * @elementfactory: The elementfactory to add the interface to
+ * @interfacename: Name of the interface
+ *
+ * Adds the given interfacename to the list of implemented interfaces of the
+ * element.
+ */
+void
+__gst_element_factory_add_interface (GstElementFactory * elementfactory,
+ const gchar * interfacename)
+{
+ g_return_if_fail (GST_IS_ELEMENT_FACTORY (elementfactory));
+ g_return_if_fail (interfacename != NULL);
+ g_return_if_fail (interfacename[0] != '\0'); /* no empty string */
+
+ elementfactory->interfaces =
+ g_list_prepend (elementfactory->interfaces,
+ (gpointer) g_intern_string (interfacename));
+}
+
+/**
+ * gst_element_factory_get_static_pad_templates:
+ * @factory: a #GstElementFactory
+ *
+ * Gets the #GList of #GstStaticPadTemplate for this factory.
+ *
+ * Returns: (transfer none) (element-type Gst.StaticPadTemplate): the
+ * static pad templates
+ */
+const GList *
+gst_element_factory_get_static_pad_templates (GstElementFactory * factory)
+{
+ g_return_val_if_fail (GST_IS_ELEMENT_FACTORY (factory), NULL);
+
+ return factory->staticpadtemplates;
+}
+
+/**
+ * gst_element_factory_get_uri_type:
+ * @factory: a #GstElementFactory
+ *
+ * Gets the type of URIs the element supports or #GST_URI_UNKNOWN if none.
+ *
+ * Returns: type of URIs this element supports
+ */
+gint
+gst_element_factory_get_uri_type (GstElementFactory * factory)
+{
+ g_return_val_if_fail (GST_IS_ELEMENT_FACTORY (factory), GST_URI_UNKNOWN);
+
+ return factory->uri_type;
+}
+
+/**
+ * gst_element_factory_get_uri_protocols:
+ * @factory: a #GstElementFactory
+ *
+ * Gets a NULL-terminated array of protocols this element supports or NULL if
+ * no protocols are supported. You may not change the contents of the returned
+ * array, as it is still owned by the element factory. Use g_strdupv() to
+ * make a copy of the protocol string array if you need to.
+ *
+ * Returns: (transfer none) (array zero-terminated=1): the supported protocols
+ * or NULL
+ */
+gchar **
+gst_element_factory_get_uri_protocols (GstElementFactory * factory)
+{
+ g_return_val_if_fail (GST_IS_ELEMENT_FACTORY (factory), NULL);
+
+ return factory->uri_protocols;
+}
+
+/**
+ * gst_element_factory_has_interface:
+ * @factory: a #GstElementFactory
+ * @interfacename: an interface name
+ *
+ * Check if @factory implements the interface with name @interfacename.
+ *
+ * Returns: #TRUE when @factory implement the interface.
+ *
+ * Since: 0.10.14
+ */
+gboolean
+gst_element_factory_has_interface (GstElementFactory * factory,
+ const gchar * interfacename)
+{
+ GList *walk;
+
+ g_return_val_if_fail (GST_IS_ELEMENT_FACTORY (factory), FALSE);
+
+ for (walk = factory->interfaces; walk; walk = g_list_next (walk)) {
+ gchar *iname = (gchar *) walk->data;
+
+ if (!strcmp (iname, interfacename))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+typedef struct
+{
+ GstElementFactoryListType type;
+ GstRank minrank;
+} FilterData;
+
+
+/**
+ * gst_element_factory_list_is_type:
+ * @factory: a #GstElementFactory
+ * @type: a #GstElementFactoryListType
+ *
+ * Check if @factory is of the given types.
+ *
+ * Returns: %TRUE if @factory is of @type.
+ *
+ * Since: 0.10.31
+ */
+gboolean
+gst_element_factory_list_is_type (GstElementFactory * factory,
+ GstElementFactoryListType type)
+{
+ gboolean res = FALSE;
+ const gchar *klass;
+
+ klass =
+ gst_element_factory_get_metadata (factory, GST_ELEMENT_METADATA_KLASS);
+
+ /* Filter by element type first, as soon as it matches
+ * one type, we skip all other tests */
+ if (!res && (type & GST_ELEMENT_FACTORY_TYPE_SINK))
+ res = (strstr (klass, "Sink") != NULL);
+
+ if (!res && (type & GST_ELEMENT_FACTORY_TYPE_SRC))
+ res = (strstr (klass, "Source") != NULL);
+
+ if (!res && (type & GST_ELEMENT_FACTORY_TYPE_DECODER))
+ res = (strstr (klass, "Decoder") != NULL);
+
+ if (!res && (type & GST_ELEMENT_FACTORY_TYPE_ENCODER))
+ res = (strstr (klass, "Encoder") != NULL);
+
+ if (!res && (type & GST_ELEMENT_FACTORY_TYPE_MUXER))
+ res = (strstr (klass, "Muxer") != NULL);
+
+ if (!res && (type & GST_ELEMENT_FACTORY_TYPE_DEMUXER))
+ res = (strstr (klass, "Demux") != NULL);
+
+ /* FIXME : We're actually parsing two Classes here... */
+ if (!res && (type & GST_ELEMENT_FACTORY_TYPE_PARSER))
+ res = ((strstr (klass, "Parser") != NULL)
+ && (strstr (klass, "Codec") != NULL));
+
+ if (!res && (type & GST_ELEMENT_FACTORY_TYPE_DEPAYLOADER))
+ res = (strstr (klass, "Depayloader") != NULL);
+
+ if (!res && (type & GST_ELEMENT_FACTORY_TYPE_PAYLOADER))
+ res = (strstr (klass, "Payloader") != NULL);
+
+ if (!res && (type & GST_ELEMENT_FACTORY_TYPE_FORMATTER))
+ res = (strstr (klass, "Formatter") != NULL);
+
+ /* Filter by media type now, we only test if it
+ * matched any of the types above. */
+ if (res
+ && (type & (GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO |
+ GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO |
+ GST_ELEMENT_FACTORY_TYPE_MEDIA_IMAGE)))
+ res = ((type & GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO)
+ && (strstr (klass, "Audio") != NULL))
+ || ((type & GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO)
+ && (strstr (klass, "Video") != NULL))
+ || ((type & GST_ELEMENT_FACTORY_TYPE_MEDIA_IMAGE)
+ && (strstr (klass, "Image") != NULL));
+
+ return res;
+}
+
+static gboolean
+element_filter (GstPluginFeature * feature, FilterData * data)
+{
+ gboolean res;
+
+ /* we only care about element factories */
+ if (G_UNLIKELY (!GST_IS_ELEMENT_FACTORY (feature)))
+ return FALSE;
+
+ res = (gst_plugin_feature_get_rank (feature) >= data->minrank) &&
+ gst_element_factory_list_is_type (GST_ELEMENT_FACTORY_CAST (feature),
+ data->type);
+
+ return res;
+}
+
+/**
+ * gst_element_factory_list_get_elements:
+ * @type: a #GstElementFactoryListType
+ * @minrank: Minimum rank
+ *
+ * Get a list of factories that match the given @type. Only elements
+ * with a rank greater or equal to @minrank will be returned.
+ * The list of factories is returned by decreasing rank.
+ *
+ * Returns: (transfer full) (element-type Gst.ElementFactory): a #GList of
+ * #GstElementFactory elements. Use gst_plugin_feature_list_free() after
+ * usage.
+ *
+ * Since: 0.10.31
+ */
+GList *
+gst_element_factory_list_get_elements (GstElementFactoryListType type,
+ GstRank minrank)
+{
+ GList *result;
+ FilterData data;
+
+ /* prepare type */
+ data.type = type;
+ data.minrank = minrank;
+
+ /* get the feature list using the filter */
+ result = gst_default_registry_feature_filter ((GstPluginFeatureFilter)
+ element_filter, FALSE, &data);
+
+ /* sort on rank and name */
+ result = g_list_sort (result, gst_plugin_feature_rank_compare_func);
+
+ return result;
+}
+
+/**
+ * gst_element_factory_list_filter:
+ * @list: (transfer none) (element-type Gst.ElementFactory): a #GList of
+ * #GstElementFactory to filter
+ * @caps: a #GstCaps
+ * @direction: a #GstPadDirection to filter on
+ * @subsetonly: whether to filter on caps subsets or not.
+ *
+ * Filter out all the elementfactories in @list that can handle @caps in
+ * the given direction.
+ *
+ * If @subsetonly is %TRUE, then only the elements whose pads templates
+ * are a complete superset of @caps will be returned. Else any element
+ * whose pad templates caps can intersect with @caps will be returned.
+ *
+ * Returns: (transfer full) (element-type Gst.ElementFactory): a #GList of
+ * #GstElementFactory elements that match the given requisits.
+ * Use #gst_plugin_feature_list_free after usage.
+ *
+ * Since: 0.10.31
+ */
+GList *
+gst_element_factory_list_filter (GList * list,
+ const GstCaps * caps, GstPadDirection direction, gboolean subsetonly)
+{
+ GList *result = NULL;
+
+ GST_DEBUG ("finding factories");
+
+ /* loop over all the factories */
+ for (; list; list = list->next) {
+ GstElementFactory *factory;
+ const GList *templates;
+ GList *walk;
+
+ factory = (GstElementFactory *) list->data;
+
+ GST_DEBUG ("Trying %s",
+ gst_plugin_feature_get_name ((GstPluginFeature *) factory));
+
+ /* get the templates from the element factory */
+ templates = gst_element_factory_get_static_pad_templates (factory);
+ for (walk = (GList *) templates; walk; walk = g_list_next (walk)) {
+ GstStaticPadTemplate *templ = walk->data;
+
+ /* we only care about the sink templates */
+ if (templ->direction == direction) {
+ GstCaps *tmpl_caps;
+
+ /* try to intersect the caps with the caps of the template */
+ tmpl_caps = gst_static_caps_get (&templ->static_caps);
+
+ /* FIXME, intersect is not the right method, we ideally want to check
+ * for a subset here */
+
+ /* check if the intersection is empty */
+ if ((subsetonly && gst_caps_is_subset (caps, tmpl_caps)) ||
+ (!subsetonly && gst_caps_can_intersect (caps, tmpl_caps))) {
+ /* non empty intersection, we can use this element */
+ result = g_list_prepend (result, gst_object_ref (factory));
+ gst_caps_unref (tmpl_caps);
+ break;
+ }
+ gst_caps_unref (tmpl_caps);
+ }
+ }
+ }
+ return g_list_reverse (result);
+}
diff --git a/gst/gstelementfactory.h b/gst/gstelementfactory.h
new file mode 100644
index 0000000..ee99594
--- /dev/null
+++ b/gst/gstelementfactory.h
@@ -0,0 +1,249 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2000,2004 Wim Taymans <wim@fluendo.com>
+ *
+ * gstelement.h: Header for GstElement
+ *
+ * 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_ELEMENT_FACTORY_H__
+#define __GST_ELEMENT_FACTORY_H__
+
+typedef struct _GstElementFactory GstElementFactory;
+typedef struct _GstElementFactoryClass GstElementFactoryClass;
+
+#include <gst/gstconfig.h>
+#include <gst/gstelement.h>
+#include <gst/gstobject.h>
+#include <gst/gstplugin.h>
+#include <gst/gstpluginfeature.h>
+#include <gst/gstpadtemplate.h>
+#include <gst/gstiterator.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_ELEMENT_FACTORY (gst_element_factory_get_type())
+#define GST_ELEMENT_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ELEMENT_FACTORY,\
+ GstElementFactory))
+#define GST_ELEMENT_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ELEMENT_FACTORY,\
+ GstElementFactoryClass))
+#define GST_IS_ELEMENT_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ELEMENT_FACTORY))
+#define GST_IS_ELEMENT_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ELEMENT_FACTORY))
+#define GST_ELEMENT_FACTORY_CAST(obj) ((GstElementFactory *)(obj))
+
+/**
+ * GstElementFactory:
+ *
+ * The opaque #GstElementFactory data structure.
+ */
+struct _GstElementFactory {
+ GstPluginFeature parent;
+
+ GType type; /* unique GType of element or 0 if not loaded */
+
+ gpointer metadata;
+
+ GList * staticpadtemplates; /* GstStaticPadTemplate list */
+ guint numpadtemplates;
+
+ /* URI interface stuff */
+ guint uri_type;
+ gchar ** uri_protocols;
+
+ GList * interfaces; /* interface type names this element implements */
+
+ /*< private >*/
+ gpointer _gst_reserved[GST_PADDING];
+};
+
+struct _GstElementFactoryClass {
+ GstPluginFeatureClass parent_class;
+
+ gpointer _gst_reserved[GST_PADDING];
+};
+
+GType gst_element_factory_get_type (void);
+
+GstElementFactory * gst_element_factory_find (const gchar *name);
+
+GType gst_element_factory_get_element_type (GstElementFactory *factory);
+
+const gchar * gst_element_factory_get_metadata (GstElementFactory *factory, const gchar *key);
+
+guint gst_element_factory_get_num_pad_templates (GstElementFactory *factory);
+const GList * gst_element_factory_get_static_pad_templates (GstElementFactory *factory);
+
+gint gst_element_factory_get_uri_type (GstElementFactory *factory);
+gchar ** gst_element_factory_get_uri_protocols (GstElementFactory *factory);
+
+gboolean gst_element_factory_has_interface (GstElementFactory *factory,
+ const gchar *interfacename);
+
+GstElement* gst_element_factory_create (GstElementFactory *factory,
+ const gchar *name);
+GstElement* gst_element_factory_make (const gchar *factoryname, const gchar *name);
+
+/* FIXME 0.11: move these two into our private headers */
+void __gst_element_factory_add_static_pad_template (GstElementFactory *elementfactory,
+ GstStaticPadTemplate *templ);
+void __gst_element_factory_add_interface (GstElementFactory *elementfactory,
+ const gchar *interfacename);
+gboolean gst_element_register (GstPlugin *plugin, const gchar *name,
+ guint rank, GType type);
+
+/* Factory list functions */
+
+/**
+ * GstFactoryListType:
+ * @GST_ELEMENT_FACTORY_TYPE_DECODER: Decoder elements
+ * @GST_ELEMENT_FACTORY_TYPE_ENCODER: Encoder elements
+ * @GST_ELEMENT_FACTORY_TYPE_SINK: Sink elements
+ * @GST_ELEMENT_FACTORY_TYPE_SRC: Source elements
+ * @GST_ELEMENT_FACTORY_TYPE_MUXER: Muxer elements
+ * @GST_ELEMENT_FACTORY_TYPE_DEMUXER: Demuxer elements
+ * @GST_ELEMENT_FACTORY_TYPE_PARSER: Parser elements
+ * @GST_ELEMENT_FACTORY_TYPE_PAYLOADER: Payloader elements
+ * @GST_ELEMENT_FACTORY_TYPE_DEPAYLOADER: Depayloader elements
+ * @GST_ELEMENT_FACTORY_TYPE_MAX_ELEMENTS: Private, do not use
+ * @GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO: Elements handling video media types
+ * @GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO: Elements handling audio media types
+ * @GST_ELEMENT_FACTORY_TYPE_MEDIA_IMAGE: Elements handling image media types
+ * @GST_ELEMENT_FACTORY_TYPE_MEDIA_SUBTITLE: Elements handling subtitle media types
+ * @GST_ELEMENT_FACTORY_TYPE_MEDIA_METADATA: Elements handling metadata media types
+ *
+ * The type of #GstElementFactory to filter.
+ *
+ * All @GstFactoryListType up to @GST_ELEMENT_FACTORY_TYPE_MAX_ELEMENTS are exclusive.
+ *
+ * If one or more of the MEDIA types are specified, then only elements
+ * matching the specified media types will be selected.
+ *
+ * Since: 0.10.31
+ */
+
+typedef guint64 GstElementFactoryListType;
+
+#define GST_ELEMENT_FACTORY_TYPE_DECODER (G_GUINT64_CONSTANT (1) << 0)
+#define GST_ELEMENT_FACTORY_TYPE_ENCODER (G_GUINT64_CONSTANT (1) << 1)
+#define GST_ELEMENT_FACTORY_TYPE_SINK (G_GUINT64_CONSTANT (1) << 2)
+#define GST_ELEMENT_FACTORY_TYPE_SRC (G_GUINT64_CONSTANT (1) << 3)
+#define GST_ELEMENT_FACTORY_TYPE_MUXER (G_GUINT64_CONSTANT (1) << 4)
+#define GST_ELEMENT_FACTORY_TYPE_DEMUXER (G_GUINT64_CONSTANT (1) << 5)
+#define GST_ELEMENT_FACTORY_TYPE_PARSER (G_GUINT64_CONSTANT (1) << 6)
+#define GST_ELEMENT_FACTORY_TYPE_PAYLOADER (G_GUINT64_CONSTANT (1) << 7)
+#define GST_ELEMENT_FACTORY_TYPE_DEPAYLOADER (G_GUINT64_CONSTANT (1) << 8)
+#define GST_ELEMENT_FACTORY_TYPE_FORMATTER (G_GUINT64_CONSTANT (1) << 9)
+
+#define GST_ELEMENT_FACTORY_TYPE_MAX_ELEMENTS (G_GUINT64_CONSTANT (1) << 48)
+
+#define GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO (G_GUINT64_CONSTANT (1) << 49)
+#define GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO (G_GUINT64_CONSTANT (1) << 50)
+#define GST_ELEMENT_FACTORY_TYPE_MEDIA_IMAGE (G_GUINT64_CONSTANT (1) << 51)
+#define GST_ELEMENT_FACTORY_TYPE_MEDIA_SUBTITLE (G_GUINT64_CONSTANT (1) << 52)
+#define GST_ELEMENT_FACTORY_TYPE_MEDIA_METADATA (G_GUINT64_CONSTANT (1) << 53)
+
+/**
+ * GST_ELEMENT_FACTORY_TYPE_ANY:
+ *
+ * Elements of any of the defined GST_ELEMENT_FACTORY_LIST types
+ *
+ * Since: 0.10.31
+ */
+#define GST_ELEMENT_FACTORY_TYPE_ANY ((G_GUINT64_CONSTANT (1) << 49) - 1)
+
+/**
+ * GST_ELEMENT_FACTORY_TYPE_MEDIA_ANY:
+ *
+ * Elements matching any of the defined GST_ELEMENT_FACTORY_TYPE_MEDIA types
+ *
+ * Note: Do not use this if you wish to not filter against any of the defined
+ * media types. If you wish to do this, simply don't specify any
+ * GST_ELEMENT_FACTORY_TYPE_MEDIA flag.
+ *
+ * Since: 0.10.31
+ */
+#define GST_ELEMENT_FACTORY_TYPE_MEDIA_ANY (~G_GUINT64_CONSTANT (0) << 48)
+
+/**
+ * GST_ELEMENT_FACTORY_TYPE_VIDEO_ENCODER:
+ *
+ * All encoders handling video or image media types
+ *
+ * Since: 0.10.31
+ */
+#define GST_ELEMENT_FACTORY_TYPE_VIDEO_ENCODER (GST_ELEMENT_FACTORY_TYPE_ENCODER | GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO | GST_ELEMENT_FACTORY_TYPE_MEDIA_IMAGE)
+
+/**
+ * GST_ELEMENT_FACTORY_TYPE_AUDIO_ENCODER:
+ *
+ * All encoders handling audio media types
+ *
+ * Since: 0.10.31
+ */
+#define GST_ELEMENT_FACTORY_TYPE_AUDIO_ENCODER (GST_ELEMENT_FACTORY_TYPE_ENCODER | GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO)
+
+/**
+ * GST_ELEMENT_FACTORY_TYPE_AUDIOVIDEO_SINKS:
+ *
+ * All sinks handling audio, video or image media types
+ *
+ * Since: 0.10.31
+ */
+#define GST_ELEMENT_FACTORY_TYPE_AUDIOVIDEO_SINKS (GST_ELEMENT_FACTORY_TYPE_SINK | GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO | GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO | GST_ELEMENT_FACTORY_TYPE_MEDIA_IMAGE)
+
+/**
+ * GST_ELEMENT_FACTORY_TYPE_DECODABLE:
+ *
+ * All elements used to 'decode' streams (decoders, demuxers, parsers, depayloaders)
+ *
+ * Since: 0.10.31
+ */
+#define GST_ELEMENT_FACTORY_TYPE_DECODABLE \
+ (GST_ELEMENT_FACTORY_TYPE_DECODER | GST_ELEMENT_FACTORY_TYPE_DEMUXER | GST_ELEMENT_FACTORY_TYPE_DEPAYLOADER | GST_ELEMENT_FACTORY_TYPE_PARSER)
+
+/* Element klass defines */
+#define GST_ELEMENT_FACTORY_KLASS_DECODER "Decoder"
+#define GST_ELEMENT_FACTORY_KLASS_ENCODER "Encoder"
+#define GST_ELEMENT_FACTORY_KLASS_SINK "Sink"
+#define GST_ELEMENT_FACTORY_KLASS_SRC "Source"
+#define GST_ELEMENT_FACTORY_KLASS_MUXER "Muxer"
+#define GST_ELEMENT_FACTORY_KLASS_DEMUXER "Demuxer"
+#define GST_ELEMENT_FACTORY_KLASS_PARSER "Parser"
+#define GST_ELEMENT_FACTORY_KLASS_PAYLOADER "Payloader"
+#define GST_ELEMENT_FACTORY_KLASS_DEPAYLOADER "Depayloader"
+#define GST_ELEMENT_FACTORY_KLASS_FORMATTER "Formatter"
+
+#define GST_ELEMENT_FACTORY_KLASS_MEDIA_VIDEO "Video"
+#define GST_ELEMENT_FACTORY_KLASS_MEDIA_AUDIO "Audio"
+#define GST_ELEMENT_FACTORY_KLASS_MEDIA_IMAGE "Image"
+#define GST_ELEMENT_FACTORY_KLASS_MEDIA_SUBTITLE "Subtitle"
+#define GST_ELEMENT_FACTORY_KLASS_MEDIA_METADATA "Metadata"
+
+gboolean gst_element_factory_list_is_type (GstElementFactory *factory,
+ GstElementFactoryListType type);
+
+GList * gst_element_factory_list_get_elements (GstElementFactoryListType type,
+ GstRank minrank);
+
+
+GList * gst_element_factory_list_filter (GList *list, const GstCaps *caps,
+ GstPadDirection direction,
+ gboolean subsetonly);
+G_END_DECLS
+
+#endif /* __GST_ELEMENT_FACTORY_H__ */
diff --git a/gst/gstelementmetadata.h b/gst/gstelementmetadata.h
new file mode 100644
index 0000000..6c331d9
--- /dev/null
+++ b/gst/gstelementmetadata.h
@@ -0,0 +1,77 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2000,2004 Wim Taymans <wim@fluendo.com>
+ *
+ * gstelementmetadata.h: Metadata for GstElement classes
+ *
+ * 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_ELEMENT_METADATA_H__
+#define __GST_ELEMENT_METADATA_H__
+
+G_BEGIN_DECLS
+
+/**
+ * GST_ELEMENT_METADATA_LONGNAME:
+ *
+ * The long English name of the element. E.g. "File Sink"
+ */
+#define GST_ELEMENT_METADATA_LONGNAME "long-name"
+/**
+ * GST_ELEMENT_METADATA_KLASS:
+ *
+ * String describing the type of element, as an unordered list
+ * separated with slashes ('/'). See draft-klass.txt of the design docs
+ * for more details and common types. E.g: "Sink/File"
+ */
+#define GST_ELEMENT_METADATA_KLASS "klass"
+
+/**
+ * GST_ELEMENT_METADATA_DESCRIPTION:
+ *
+ * Sentence describing the purpose of the element.
+ * E.g: "Write stream to a file"
+ */
+#define GST_ELEMENT_METADATA_DESCRIPTION "description"
+/**
+ * GST_ELEMENT_METADATA_AUTHOR:
+ *
+ * Name and contact details of the author(s). Use \n to separate
+ * multiple author details.
+ * E.g: "Joe Bloggs &lt;joe.blogs at foo.com&gt;"
+ */
+#define GST_ELEMENT_METADATA_AUTHOR "author"
+
+/**
+ * GST_ELEMENT_METADATA_DOC_URI:
+ *
+ * Set uri pointing to user documentation. Applications can use this to show
+ * help for e.g. effects to users.
+ */
+#define GST_ELEMENT_METADATA_DOC_URI "doc-uri"
+/**
+ * GST_ELEMENT_METADATA_ICON_NAME:
+ *
+ * Elements that bridge to certain other products can include an icon of that
+ * used product. Application can show the icon in menus/selectors to help
+ * identifying specific elements.
+ */
+#define GST_ELEMENT_METADATA_ICON_NAME "icon-name"
+
+G_END_DECLS
+
+#endif /* __GST_ELEMENT_METADATA_H__ */
diff --git a/gst/gsterror.c b/gst/gsterror.c
new file mode 100644
index 0000000..f3f2d22
--- /dev/null
+++ b/gst/gsterror.c
@@ -0,0 +1,331 @@
+/* GStreamer
+ * Copyright (C) <2003> David A. Schleef <ds@schleef.org>
+ *
+ * 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:gsterror
+ * @short_description: Categorized error messages
+ * @see_also: #GstMessage
+ *
+ * GStreamer elements can throw non-fatal warnings and fatal errors.
+ * Higher-level elements and applications can programatically filter
+ * the ones they are interested in or can recover from,
+ * and have a default handler handle the rest of them.
+ *
+ * The rest of this section will use the term <quote>error</quote>
+ * to mean both (non-fatal) warnings and (fatal) errors; they are treated
+ * similarly.
+ *
+ * Errors from elements are the combination of a #GError and a debug string.
+ * The #GError contains:
+ * - a domain type: CORE, LIBRARY, RESOURCE or STREAM
+ * - a code: an enum value specific to the domain
+ * - a translated, human-readable message
+ * - a non-translated additional debug string, which also contains
+ * - file and line information
+ *
+ * Elements do not have the context required to decide what to do with
+ * errors. As such, they should only inform about errors, and stop their
+ * processing. In short, an element doesn't know what it is being used for.
+ *
+ * It is the application or compound element using the given element that
+ * has more context about the use of the element. Errors can be received by
+ * listening to the #GstBus of the element/pipeline for #GstMessage objects with
+ * the type %GST_MESSAGE_ERROR or %GST_MESSAGE_WARNING. The thrown errors should
+ * be inspected, and filtered if appropriate.
+ *
+ * An application is expected to, by default, present the user with a
+ * dialog box (or an equivalent) showing the error message. The dialog
+ * should also allow a way to get at the additional debug information,
+ * so the user can provide bug reporting information.
+ *
+ * A compound element is expected to forward errors by default higher up
+ * the hierarchy; this is done by default in the same way as for other types
+ * of #GstMessage.
+ *
+ * When applications or compound elements trigger errors that they can
+ * recover from, they can filter out these errors and take appropriate action.
+ * For example, an application that gets an error from xvimagesink
+ * that indicates all XVideo ports are taken, the application can attempt
+ * to use another sink instead.
+ *
+ * Elements throw errors using the #GST_ELEMENT_ERROR convenience macro:
+ *
+ * <example>
+ * <title>Throwing an error</title>
+ * <programlisting>
+ * GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND,
+ * (_("No file name specified for reading.")), (NULL));
+ * </programlisting>
+ * </example>
+ *
+ * Things to keep in mind:
+ * <itemizedlist>
+ * <listitem><para>Don't go off inventing new error codes. The ones
+ * currently provided should be enough. If you find your type of error
+ * does not fit the current codes, you should use FAILED.</para></listitem>
+ * <listitem><para>Don't provide a message if the default one suffices.
+ * this keeps messages more uniform. Use (NULL) - not forgetting the
+ * parentheses.</para></listitem>
+ * <listitem><para>If you do supply a custom message, it should be
+ * marked for translation. The message should start with a capital
+ * and end with a period. The message should describe the error in short,
+ * in a human-readable form, and without any complex technical terms.
+ * A user interface will present this message as the first thing a user
+ * sees. Details, technical info, ... should go in the debug string.
+ * </para></listitem>
+ * <listitem><para>The debug string can be as you like. Again, use (NULL)
+ * if there's nothing to add - file and line number will still be
+ * passed. #GST_ERROR_SYSTEM can be used as a shortcut to give
+ * debug information on a system call error.</para></listitem>
+ * </itemizedlist>
+ *
+ * Last reviewed on 2006-09-15 (0.10.10)
+ */
+
+/* FIXME 0.11: the entire error system needs an overhaul - it's not very
+ * useful the way it is. Also, we need to be able to specify additional
+ * 'details' for errors (e.g. disk/file/resource error -> out-of-space; or
+ * put the url/filename/device name that caused the error somewhere)
+ * without having to add enums for every little thing.
+ *
+ * FIXME 0.11: get rid of GST_{CORE,LIBRARY,RESOURCE,STREAM}_ERROR_NUM_ERRORS.
+ * Maybe also replace _quark() functions with g_quark_from_static_string()?
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gst_private.h"
+#include <gst/gst.h>
+#include "gst-i18n-lib.h"
+
+#define QUARK_FUNC(string) \
+GQuark gst_ ## string ## _error_quark (void) { \
+ static GQuark quark; \
+ if (!quark) \
+ quark = g_quark_from_static_string ("gst-" # string "-error-quark"); \
+ return quark; }
+
+/* FIXME: Deprecate when we depend on GLib 2.26 */
+GType
+gst_g_error_get_type (void)
+{
+#if GLIB_CHECK_VERSION(2,25,2)
+ return g_error_get_type ();
+#else
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0))
+ type = g_boxed_type_register_static ("GstGError",
+ (GBoxedCopyFunc) g_error_copy, (GBoxedFreeFunc) g_error_free);
+ return type;
+#endif
+}
+
+#define FILE_A_BUG " Please file a bug at " PACKAGE_BUGREPORT "."
+
+static const gchar *
+gst_error_get_core_error (GstCoreError code)
+{
+ switch (code) {
+ case GST_CORE_ERROR_FAILED:
+ return _("GStreamer encountered a general core library error.");
+ case GST_CORE_ERROR_TOO_LAZY:
+ return _("GStreamer developers were too lazy to assign an error code "
+ "to this error." FILE_A_BUG);
+ case GST_CORE_ERROR_NOT_IMPLEMENTED:
+ return _("Internal GStreamer error: code not implemented." FILE_A_BUG);
+ case GST_CORE_ERROR_STATE_CHANGE:
+ return _("GStreamer error: state change failed and some element failed "
+ "to post a proper error message with the reason for the failure.");
+ case GST_CORE_ERROR_PAD:
+ return _("Internal GStreamer error: pad problem." FILE_A_BUG);
+ case GST_CORE_ERROR_THREAD:
+ return _("Internal GStreamer error: thread problem." FILE_A_BUG);
+ case GST_CORE_ERROR_NEGOTIATION:
+ return _("Internal GStreamer error: negotiation problem." FILE_A_BUG);
+ case GST_CORE_ERROR_EVENT:
+ return _("Internal GStreamer error: event problem." FILE_A_BUG);
+ case GST_CORE_ERROR_SEEK:
+ return _("Internal GStreamer error: seek problem." FILE_A_BUG);
+ case GST_CORE_ERROR_CAPS:
+ return _("Internal GStreamer error: caps problem." FILE_A_BUG);
+ case GST_CORE_ERROR_TAG:
+ return _("Internal GStreamer error: tag problem." FILE_A_BUG);
+ case GST_CORE_ERROR_MISSING_PLUGIN:
+ return _("Your GStreamer installation is missing a plug-in.");
+ case GST_CORE_ERROR_CLOCK:
+ return _("Internal GStreamer error: clock problem." FILE_A_BUG);
+ case GST_CORE_ERROR_DISABLED:
+ return _("This application is trying to use GStreamer functionality "
+ "that has been disabled.");
+ case GST_CORE_ERROR_NUM_ERRORS:
+ default:
+ break;
+ }
+ return NULL;
+}
+
+static const gchar *
+gst_error_get_library_error (GstLibraryError code)
+{
+ switch (code) {
+ case GST_LIBRARY_ERROR_FAILED:
+ return _("GStreamer encountered a general supporting library error.");
+ case GST_LIBRARY_ERROR_TOO_LAZY:
+ return _("GStreamer developers were too lazy to assign an error code "
+ "to this error." FILE_A_BUG);
+ case GST_LIBRARY_ERROR_INIT:
+ return _("Could not initialize supporting library.");
+ case GST_LIBRARY_ERROR_SHUTDOWN:
+ return _("Could not close supporting library.");
+ case GST_LIBRARY_ERROR_SETTINGS:
+ return _("Could not configure supporting library.");
+ case GST_LIBRARY_ERROR_ENCODE:
+ return _("Encoding error.");
+ case GST_LIBRARY_ERROR_NUM_ERRORS:
+ default:
+ break;
+ }
+ return NULL;
+}
+
+static const gchar *
+gst_error_get_resource_error (GstResourceError code)
+{
+ switch (code) {
+ case GST_RESOURCE_ERROR_FAILED:
+ return _("GStreamer encountered a general resource error.");
+ case GST_RESOURCE_ERROR_TOO_LAZY:
+ return _("GStreamer developers were too lazy to assign an error code "
+ "to this error." FILE_A_BUG);
+ case GST_RESOURCE_ERROR_NOT_FOUND:
+ return _("Resource not found.");
+ case GST_RESOURCE_ERROR_BUSY:
+ return _("Resource busy or not available.");
+ case GST_RESOURCE_ERROR_OPEN_READ:
+ return _("Could not open resource for reading.");
+ case GST_RESOURCE_ERROR_OPEN_WRITE:
+ return _("Could not open resource for writing.");
+ case GST_RESOURCE_ERROR_OPEN_READ_WRITE:
+ return _("Could not open resource for reading and writing.");
+ case GST_RESOURCE_ERROR_CLOSE:
+ return _("Could not close resource.");
+ case GST_RESOURCE_ERROR_READ:
+ return _("Could not read from resource.");
+ case GST_RESOURCE_ERROR_WRITE:
+ return _("Could not write to resource.");
+ case GST_RESOURCE_ERROR_SEEK:
+ return _("Could not perform seek on resource.");
+ case GST_RESOURCE_ERROR_SYNC:
+ return _("Could not synchronize on resource.");
+ case GST_RESOURCE_ERROR_SETTINGS:
+ return _("Could not get/set settings from/on resource.");
+ case GST_RESOURCE_ERROR_NO_SPACE_LEFT:
+ return _("No space left on the resource.");
+ case GST_RESOURCE_ERROR_NUM_ERRORS:
+ default:
+ break;
+ }
+ return NULL;
+}
+
+static const gchar *
+gst_error_get_stream_error (GstStreamError code)
+{
+ switch (code) {
+ case GST_STREAM_ERROR_FAILED:
+ return _("GStreamer encountered a general stream error.");
+ case GST_STREAM_ERROR_TOO_LAZY:
+ return _("GStreamer developers were too lazy to assign an error code "
+ "to this error." FILE_A_BUG);
+ case GST_STREAM_ERROR_NOT_IMPLEMENTED:
+ return _("Element doesn't implement handling of this stream. "
+ "Please file a bug.");
+ case GST_STREAM_ERROR_TYPE_NOT_FOUND:
+ return _("Could not determine type of stream.");
+ case GST_STREAM_ERROR_WRONG_TYPE:
+ return _("The stream is of a different type than handled by this "
+ "element.");
+ case GST_STREAM_ERROR_CODEC_NOT_FOUND:
+ return _("There is no codec present that can handle the stream's type.");
+ case GST_STREAM_ERROR_DECODE:
+ return _("Could not decode stream.");
+ case GST_STREAM_ERROR_ENCODE:
+ return _("Could not encode stream.");
+ case GST_STREAM_ERROR_DEMUX:
+ return _("Could not demultiplex stream.");
+ case GST_STREAM_ERROR_MUX:
+ return _("Could not multiplex stream.");
+ case GST_STREAM_ERROR_FORMAT:
+ return _("The stream is in the wrong format.");
+ case GST_STREAM_ERROR_DECRYPT:
+ return _("The stream is encrypted and decryption is not supported.");
+ case GST_STREAM_ERROR_DECRYPT_NOKEY:
+ return _("The stream is encrypted and can't be decrypted because no "
+ "suitable key has been supplied.");
+ case GST_STREAM_ERROR_NUM_ERRORS:
+ default:
+ break;
+ }
+
+ return NULL;
+}
+
+QUARK_FUNC (core);
+QUARK_FUNC (library);
+QUARK_FUNC (resource);
+QUARK_FUNC (stream);
+
+/**
+ * gst_error_get_message:
+ * @domain: the GStreamer error domain this error belongs to.
+ * @code: the error code belonging to the domain.
+ *
+ * Get a string describing the error message in the current locale.
+ *
+ * Returns: (transfer full): a newly allocated string describing
+ * the error message (in UTF-8 encoding)
+ */
+gchar *
+gst_error_get_message (GQuark domain, gint code)
+{
+ const gchar *message = NULL;
+
+ if (domain == GST_CORE_ERROR)
+ message = gst_error_get_core_error ((GstCoreError) code);
+ else if (domain == GST_LIBRARY_ERROR)
+ message = gst_error_get_library_error ((GstLibraryError) code);
+ else if (domain == GST_RESOURCE_ERROR)
+ message = gst_error_get_resource_error ((GstResourceError) code);
+ else if (domain == GST_STREAM_ERROR)
+ message = gst_error_get_stream_error ((GstStreamError) code);
+ else {
+ g_warning ("No error messages for domain %s", g_quark_to_string (domain));
+ return g_strdup_printf (_("No error message for domain %s."),
+ g_quark_to_string (domain));
+ }
+ if (message)
+ return g_strdup (message);
+ else
+ return
+ g_strdup_printf (_
+ ("No standard error message for domain %s and code %d."),
+ g_quark_to_string (domain), code);
+}
diff --git a/gst/gsterror.h b/gst/gsterror.h
new file mode 100644
index 0000000..d722ce0
--- /dev/null
+++ b/gst/gsterror.h
@@ -0,0 +1,255 @@
+/* GStreamer
+ * Copyright (C) 2004 Thomas Vander Stichele <thomas at apestaart dot org>
+ *
+ * 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_ERROR_H__
+#define __GST_ERROR_H__
+
+#include <glib.h>
+#include <glib-object.h>
+#include <errno.h>
+
+G_BEGIN_DECLS
+/*
+ * we define FIXME error domains:
+ * GST_CORE_ERROR
+ * GST_LIBRARY_ERROR
+ * GST_RESOURCE_ERROR
+ * GST_STREAM_ERROR
+ *
+ * Check GError API docs for rationale for naming.
+ */
+/**
+ * GstCoreError:
+ * @GST_CORE_ERROR_FAILED: a general error which doesn't fit in any other
+ * category. Make sure you add a custom message to the error call.
+ * @GST_CORE_ERROR_TOO_LAZY: do not use this except as a placeholder for
+ * deciding where to go while developing code.
+ * @GST_CORE_ERROR_NOT_IMPLEMENTED: use this when you do not want to implement
+ * this functionality yet.
+ * @GST_CORE_ERROR_STATE_CHANGE: used for state change errors.
+ * @GST_CORE_ERROR_PAD: used for pad-related errors.
+ * @GST_CORE_ERROR_THREAD: used for thread-related errors.
+ * @GST_CORE_ERROR_NEGOTIATION: used for negotiation-related errors.
+ * @GST_CORE_ERROR_EVENT: used for event-related errors.
+ * @GST_CORE_ERROR_SEEK: used for seek-related errors.
+ * @GST_CORE_ERROR_CAPS: used for caps-related errors.
+ * @GST_CORE_ERROR_TAG: used for negotiation-related errors.
+ * @GST_CORE_ERROR_MISSING_PLUGIN: used if a plugin is missing.
+ * @GST_CORE_ERROR_CLOCK: used for clock related errors.
+ * @GST_CORE_ERROR_DISABLED: used if functionality has been disabled at
+ * compile time (Since: 0.10.13).
+ * @GST_CORE_ERROR_NUM_ERRORS: the number of core error types.
+ *
+ * Core errors are errors inside the core GStreamer library.
+ */
+/* FIXME: should we divide in numerical blocks so we can easily add
+ for example PAD errors later ? */
+typedef enum
+{
+ GST_CORE_ERROR_FAILED = 1,
+ GST_CORE_ERROR_TOO_LAZY,
+ GST_CORE_ERROR_NOT_IMPLEMENTED,
+ GST_CORE_ERROR_STATE_CHANGE,
+ GST_CORE_ERROR_PAD,
+ GST_CORE_ERROR_THREAD,
+ GST_CORE_ERROR_NEGOTIATION,
+ GST_CORE_ERROR_EVENT,
+ GST_CORE_ERROR_SEEK,
+ GST_CORE_ERROR_CAPS,
+ GST_CORE_ERROR_TAG,
+ GST_CORE_ERROR_MISSING_PLUGIN,
+ GST_CORE_ERROR_CLOCK,
+ GST_CORE_ERROR_DISABLED,
+ GST_CORE_ERROR_NUM_ERRORS
+} GstCoreError;
+
+/**
+ * GstLibraryError:
+ * @GST_LIBRARY_ERROR_FAILED: a general error which doesn't fit in any other
+ * category. Make sure you add a custom message to the error call.
+ * @GST_LIBRARY_ERROR_TOO_LAZY: do not use this except as a placeholder for
+ * deciding where to go while developing code.
+ * @GST_LIBRARY_ERROR_INIT: used when the library could not be opened.
+ * @GST_LIBRARY_ERROR_SHUTDOWN: used when the library could not be closed.
+ * @GST_LIBRARY_ERROR_SETTINGS: used when the library doesn't accept settings.
+ * @GST_LIBRARY_ERROR_ENCODE: used when the library generated an encoding error.
+ * @GST_LIBRARY_ERROR_NUM_ERRORS: the number of library error types.
+ *
+ * Library errors are for errors from the library being used by elements
+ * (initializing, finalizing, settings, ...)
+ */
+typedef enum
+{
+ GST_LIBRARY_ERROR_FAILED = 1,
+ GST_LIBRARY_ERROR_TOO_LAZY,
+ GST_LIBRARY_ERROR_INIT,
+ GST_LIBRARY_ERROR_SHUTDOWN,
+ GST_LIBRARY_ERROR_SETTINGS,
+ GST_LIBRARY_ERROR_ENCODE,
+ GST_LIBRARY_ERROR_NUM_ERRORS
+} GstLibraryError;
+
+/**
+ * GstResourceError:
+ * @GST_RESOURCE_ERROR_FAILED: a general error which doesn't fit in any other
+ * category. Make sure you add a custom message to the error call.
+ * @GST_RESOURCE_ERROR_TOO_LAZY: do not use this except as a placeholder for
+ * deciding where to go while developing code.
+ * @GST_RESOURCE_ERROR_NOT_FOUND: used when the resource could not be found.
+ * @GST_RESOURCE_ERROR_BUSY: used when resource is busy.
+ * @GST_RESOURCE_ERROR_OPEN_READ: used when resource fails to open for reading.
+ * @GST_RESOURCE_ERROR_OPEN_WRITE: used when resource fails to open for writing.
+ * @GST_RESOURCE_ERROR_OPEN_READ_WRITE: used when resource cannot be opened for
+ * both reading and writing, or either (but unspecified which).
+ * @GST_RESOURCE_ERROR_CLOSE: used when the resource can't be closed.
+ * @GST_RESOURCE_ERROR_READ: used when the resource can't be read from.
+ * @GST_RESOURCE_ERROR_WRITE: used when the resource can't be written to.
+ * @GST_RESOURCE_ERROR_SEEK: used when a seek on the resource fails.
+ * @GST_RESOURCE_ERROR_SYNC: used when a synchronize on the resource fails.
+ * @GST_RESOURCE_ERROR_SETTINGS: used when settings can't be manipulated on.
+ * @GST_RESOURCE_ERROR_NO_SPACE_LEFT: used when the resource has no space left.
+ * @GST_RESOURCE_ERROR_NUM_ERRORS: the number of resource error types.
+ *
+ * Resource errors are for any resource used by an element:
+ * memory, files, network connections, process space, ...
+ * They're typically used by source and sink elements.
+ */
+typedef enum
+{
+ GST_RESOURCE_ERROR_FAILED = 1,
+ GST_RESOURCE_ERROR_TOO_LAZY,
+ GST_RESOURCE_ERROR_NOT_FOUND,
+ GST_RESOURCE_ERROR_BUSY,
+ GST_RESOURCE_ERROR_OPEN_READ,
+ GST_RESOURCE_ERROR_OPEN_WRITE,
+ GST_RESOURCE_ERROR_OPEN_READ_WRITE,
+ GST_RESOURCE_ERROR_CLOSE,
+ GST_RESOURCE_ERROR_READ,
+ GST_RESOURCE_ERROR_WRITE,
+ GST_RESOURCE_ERROR_SEEK,
+ GST_RESOURCE_ERROR_SYNC,
+ GST_RESOURCE_ERROR_SETTINGS,
+ GST_RESOURCE_ERROR_NO_SPACE_LEFT,
+ GST_RESOURCE_ERROR_NUM_ERRORS
+} GstResourceError;
+
+/**
+ * GstStreamError:
+ * @GST_STREAM_ERROR_FAILED: a general error which doesn't fit in any other
+ * category. Make sure you add a custom message to the error call.
+ * @GST_STREAM_ERROR_TOO_LAZY: do not use this except as a placeholder for
+ * deciding where to go while developing code.
+ * @GST_STREAM_ERROR_NOT_IMPLEMENTED: use this when you do not want to implement
+ * this functionality yet.
+ * @GST_STREAM_ERROR_TYPE_NOT_FOUND: used when the element doesn't know the
+ * stream's type.
+ * @GST_STREAM_ERROR_WRONG_TYPE: used when the element doesn't handle this type
+ * of stream.
+ * @GST_STREAM_ERROR_CODEC_NOT_FOUND: used when there's no codec to handle the
+ * stream's type.
+ * @GST_STREAM_ERROR_DECODE: used when decoding fails.
+ * @GST_STREAM_ERROR_ENCODE: used when encoding fails.
+ * @GST_STREAM_ERROR_DEMUX: used when demuxing fails.
+ * @GST_STREAM_ERROR_MUX: used when muxing fails.
+ * @GST_STREAM_ERROR_FORMAT: used when the stream is of the wrong format
+ * (for example, wrong caps).
+ * @GST_STREAM_ERROR_DECRYPT: used when the stream is encrypted and can't be
+ * decrypted because this is not supported by the element. (Since: 0.10.20)
+ * @GST_STREAM_ERROR_DECRYPT_NOKEY: used when the stream is encrypted and
+ * can't be decrypted because no suitable key is available. (Since: 0.10.20)
+ * @GST_STREAM_ERROR_NUM_ERRORS: the number of stream error types.
+ *
+ * Stream errors are for anything related to the stream being processed:
+ * format errors, media type errors, ...
+ * They're typically used by decoders, demuxers, converters, ...
+ */
+typedef enum
+{
+ GST_STREAM_ERROR_FAILED = 1,
+ GST_STREAM_ERROR_TOO_LAZY,
+ GST_STREAM_ERROR_NOT_IMPLEMENTED,
+ GST_STREAM_ERROR_TYPE_NOT_FOUND,
+ GST_STREAM_ERROR_WRONG_TYPE,
+ GST_STREAM_ERROR_CODEC_NOT_FOUND,
+ GST_STREAM_ERROR_DECODE,
+ GST_STREAM_ERROR_ENCODE,
+ GST_STREAM_ERROR_DEMUX,
+ GST_STREAM_ERROR_MUX,
+ GST_STREAM_ERROR_FORMAT,
+ GST_STREAM_ERROR_DECRYPT,
+ GST_STREAM_ERROR_DECRYPT_NOKEY,
+ GST_STREAM_ERROR_NUM_ERRORS
+} GstStreamError;
+
+#define GST_TYPE_G_ERROR (gst_g_error_get_type ())
+
+/**
+ * GST_LIBRARY_ERROR:
+ *
+ * Error domain for library loading. Errors in this domain will
+ * be from the #GstLibraryError enumeration.
+ * See #GError for information on error domains.
+ */
+#define GST_LIBRARY_ERROR gst_library_error_quark ()
+/**
+ * GST_RESOURCE_ERROR:
+ *
+ * Error domain for resource handling. Errors in this domain will
+ * be from the #GstResourceError enumeration.
+ * See #GError for information on error domains.
+ */
+#define GST_RESOURCE_ERROR gst_resource_error_quark ()
+/**
+ * GST_CORE_ERROR:
+ *
+ * Error domain for core system. Errors in this domain will
+ * be from the #GstCoreError enumeration.
+ * See #GError for information on error domains.
+ */
+#define GST_CORE_ERROR gst_core_error_quark ()
+/**
+ * GST_STREAM_ERROR:
+ *
+ * Error domain for media stream processing. Errors in this domain will
+ * be from the #GstStreamError enumeration.
+ * See #GError for information on error domains.
+ */
+#define GST_STREAM_ERROR gst_stream_error_quark ()
+
+/**
+ * GST_ERROR_SYSTEM:
+ *
+ * Builds a string using errno describing the previously failed system
+ * call. To be used as the debug argument in #GST_ELEMENT_ERROR.
+ */
+#define GST_ERROR_SYSTEM ("system error: %s", g_strerror (errno))
+
+/* Hide this compatibility type from introspection */
+#ifndef __GI_SCANNER__
+GType gst_g_error_get_type (void);
+#endif
+
+gchar *gst_error_get_message (GQuark domain, gint code);
+GQuark gst_stream_error_quark (void);
+GQuark gst_core_error_quark (void);
+GQuark gst_resource_error_quark (void);
+GQuark gst_library_error_quark (void);
+
+G_END_DECLS
+#endif /* __GST_ERROR_H__ */
diff --git a/gst/gstevent.c b/gst/gstevent.c
new file mode 100644
index 0000000..030b9d4
--- /dev/null
+++ b/gst/gstevent.c
@@ -0,0 +1,1311 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2000 Wim Taymans <wim.taymans@chello.be>
+ * 2005 Wim Taymans <wim@fluendo.com>
+ *
+ * gstevent.c: GstEvent subsystem
+ *
+ * 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:gstevent
+ * @short_description: Structure describing events that are passed up and down
+ * a pipeline
+ * @see_also: #GstPad, #GstElement
+ *
+ * The event class provides factory methods to construct events for sending
+ * and functions to query (parse) received events.
+ *
+ * Events are usually created with gst_event_new_*() which takes event-type
+ * specific parameters as arguments.
+ * To send an event application will usually use gst_element_send_event() and
+ * elements will use gst_pad_send_event() or gst_pad_push_event().
+ * The event should be unreffed with gst_event_unref() if it has not been sent.
+ *
+ * Events that have been received can be parsed with their respective
+ * gst_event_parse_*() functions. It is valid to pass %NULL for unwanted details.
+ *
+ * Events are passed between elements in parallel to the data stream. Some events
+ * are serialized with buffers, others are not. Some events only travel downstream,
+ * others only upstream. Some events can travel both upstream and downstream.
+ *
+ * The events are used to signal special conditions in the datastream such as
+ * EOS (end of stream) or the start of a new stream-segment.
+ * Events are also used to flush the pipeline of any pending data.
+ *
+ * Most of the event API is used inside plugins. Applications usually only
+ * construct and use seek events.
+ * To do that gst_event_new_seek() is used to create a seek event. It takes
+ * the needed parameters to specify seeking time and mode.
+ * <example>
+ * <title>performing a seek on a pipeline</title>
+ * <programlisting>
+ * GstEvent *event;
+ * gboolean result;
+ * ...
+ * // construct a seek event to play the media from second 2 to 5, flush
+ * // the pipeline to decrease latency.
+ * event = gst_event_new_seek (1.0,
+ * GST_FORMAT_TIME,
+ * GST_SEEK_FLAG_FLUSH,
+ * GST_SEEK_TYPE_SET, 2 * GST_SECOND,
+ * GST_SEEK_TYPE_SET, 5 * GST_SECOND);
+ * ...
+ * result = gst_element_send_event (pipeline, event);
+ * if (!result)
+ * g_warning ("seek failed");
+ * ...
+ * </programlisting>
+ * </example>
+ *
+ * Last reviewed on 2006-09-6 (0.10.10)
+ */
+
+
+#include "gst_private.h"
+#include <string.h> /* memcpy */
+
+#include "gstinfo.h"
+#include "gstevent.h"
+#include "gstenumtypes.h"
+#include "gstutils.h"
+#include "gstquark.h"
+
+GType _gst_event_type = 0;
+
+typedef struct
+{
+ GstEvent event;
+
+ GstStructure *structure;
+} GstEventImpl;
+
+#define GST_EVENT_STRUCTURE(e) (((GstEventImpl *)(e))->structure)
+
+typedef struct
+{
+ const gint type;
+ const gchar *name;
+ GQuark quark;
+} GstEventQuarks;
+
+static GstEventQuarks event_quarks[] = {
+ {GST_EVENT_UNKNOWN, "unknown", 0},
+ {GST_EVENT_FLUSH_START, "flush-start", 0},
+ {GST_EVENT_FLUSH_STOP, "flush-stop", 0},
+ {GST_EVENT_EOS, "eos", 0},
+ {GST_EVENT_CAPS, "caps", 0},
+ {GST_EVENT_SEGMENT, "segment", 0},
+ {GST_EVENT_TAG, "tag", 0},
+ {GST_EVENT_BUFFERSIZE, "buffersize", 0},
+ {GST_EVENT_SINK_MESSAGE, "sink-message", 0},
+ {GST_EVENT_QOS, "qos", 0},
+ {GST_EVENT_SEEK, "seek", 0},
+ {GST_EVENT_NAVIGATION, "navigation", 0},
+ {GST_EVENT_LATENCY, "latency", 0},
+ {GST_EVENT_STEP, "step", 0},
+ {GST_EVENT_RECONFIGURE, "reconfigure", 0},
+ {GST_EVENT_CUSTOM_UPSTREAM, "custom-upstream", 0},
+ {GST_EVENT_CUSTOM_DOWNSTREAM, "custom-downstream", 0},
+ {GST_EVENT_CUSTOM_DOWNSTREAM_OOB, "custom-downstream-oob", 0},
+ {GST_EVENT_CUSTOM_BOTH, "custom-both", 0},
+ {GST_EVENT_CUSTOM_BOTH_OOB, "custom-both-oob", 0},
+
+ {0, NULL, 0}
+};
+
+GST_DEFINE_MINI_OBJECT_TYPE (GstEvent, gst_event);
+
+void
+_priv_gst_event_initialize (void)
+{
+ gint i;
+
+ _gst_event_type = gst_event_get_type ();
+
+ g_type_class_ref (gst_seek_flags_get_type ());
+ g_type_class_ref (gst_seek_type_get_type ());
+
+ for (i = 0; event_quarks[i].name; i++) {
+ event_quarks[i].quark = g_quark_from_static_string (event_quarks[i].name);
+ }
+}
+
+/**
+ * gst_event_type_get_name:
+ * @type: the event type
+ *
+ * Get a printable name for the given event type. Do not modify or free.
+ *
+ * Returns: a reference to the static name of the event.
+ */
+const gchar *
+gst_event_type_get_name (GstEventType type)
+{
+ gint i;
+
+ for (i = 0; event_quarks[i].name; i++) {
+ if (type == event_quarks[i].type)
+ return event_quarks[i].name;
+ }
+ return "unknown";
+}
+
+/**
+ * gst_event_type_to_quark:
+ * @type: the event type
+ *
+ * Get the unique quark for the given event type.
+ *
+ * Returns: the quark associated with the event type
+ */
+GQuark
+gst_event_type_to_quark (GstEventType type)
+{
+ gint i;
+
+ for (i = 0; event_quarks[i].name; i++) {
+ if (type == event_quarks[i].type)
+ return event_quarks[i].quark;
+ }
+ return 0;
+}
+
+/**
+ * gst_event_type_get_flags:
+ * @type: a #GstEventType
+ *
+ * Gets the #GstEventTypeFlags associated with @type.
+ *
+ * Returns: a #GstEventTypeFlags.
+ */
+GstEventTypeFlags
+gst_event_type_get_flags (GstEventType type)
+{
+ GstEventTypeFlags ret;
+
+ ret = type & ((1 << GST_EVENT_STICKY_SHIFT) - 1);
+
+ return ret;
+}
+
+static void
+_gst_event_free (GstEvent * event)
+{
+ GstStructure *s;
+
+ g_return_if_fail (event != NULL);
+ g_return_if_fail (GST_IS_EVENT (event));
+
+ GST_CAT_LOG (GST_CAT_EVENT, "freeing event %p type %s", event,
+ GST_EVENT_TYPE_NAME (event));
+
+ s = GST_EVENT_STRUCTURE (event);
+
+ if (s) {
+ gst_structure_set_parent_refcount (s, NULL);
+ gst_structure_free (s);
+ }
+
+ g_slice_free1 (GST_MINI_OBJECT_SIZE (event), event);
+}
+
+static void gst_event_init (GstEventImpl * event, gsize size,
+ GstEventType type);
+
+static GstEvent *
+_gst_event_copy (GstEvent * event)
+{
+ GstEventImpl *copy;
+ GstStructure *s;
+
+ copy = g_slice_new0 (GstEventImpl);
+
+ gst_event_init (copy, sizeof (GstEventImpl), GST_EVENT_TYPE (event));
+
+ GST_EVENT_TIMESTAMP (copy) = GST_EVENT_TIMESTAMP (event);
+ GST_EVENT_SEQNUM (copy) = GST_EVENT_SEQNUM (event);
+
+ s = GST_EVENT_STRUCTURE (event);
+ if (s) {
+ GST_EVENT_STRUCTURE (copy) = gst_structure_copy (s);
+ gst_structure_set_parent_refcount (GST_EVENT_STRUCTURE (copy),
+ &copy->event.mini_object.refcount);
+ }
+ return GST_EVENT_CAST (copy);
+}
+
+static void
+gst_event_init (GstEventImpl * event, gsize size, GstEventType type)
+{
+ gst_mini_object_init (GST_MINI_OBJECT_CAST (event), _gst_event_type, size);
+
+ event->event.mini_object.copy = (GstMiniObjectCopyFunction) _gst_event_copy;
+ event->event.mini_object.free = (GstMiniObjectFreeFunction) _gst_event_free;
+
+ GST_EVENT_TYPE (event) = type;
+ GST_EVENT_TIMESTAMP (event) = GST_CLOCK_TIME_NONE;
+ GST_EVENT_SEQNUM (event) = gst_util_seqnum_next ();
+}
+
+static GstEvent *
+gst_event_new (GstEventType type)
+{
+ GstEventImpl *event;
+
+ event = g_slice_new0 (GstEventImpl);
+
+ GST_CAT_DEBUG (GST_CAT_EVENT, "creating new event %p %s %d", event,
+ gst_event_type_get_name (type), type);
+
+ gst_event_init (event, sizeof (GstEventImpl), type);
+
+ return GST_EVENT_CAST (event);
+}
+
+/**
+ * gst_event_new_custom:
+ * @type: The type of the new event
+ * @structure: (transfer full): the structure for the event. The event will
+ * take ownership of the structure.
+ *
+ * Create a new custom-typed event. This can be used for anything not
+ * handled by other event-specific functions to pass an event to another
+ * element.
+ *
+ * Make sure to allocate an event type with the #GST_EVENT_MAKE_TYPE macro,
+ * assigning a free number and filling in the correct direction and
+ * serialization flags.
+ *
+ * New custom events can also be created by subclassing the event type if
+ * needed.
+ *
+ * Returns: (transfer full): the new custom event.
+ */
+GstEvent *
+gst_event_new_custom (GstEventType type, GstStructure * structure)
+{
+ GstEvent *event;
+
+ /* structure must not have a parent */
+ event = gst_event_new (type);
+ if (structure) {
+ if (!gst_structure_set_parent_refcount (structure,
+ &event->mini_object.refcount))
+ goto had_parent;
+
+ GST_EVENT_STRUCTURE (event) = structure;
+ }
+ return event;
+
+ /* ERRORS */
+had_parent:
+ {
+ gst_event_unref (event);
+ g_warning ("structure is already owned by another object");
+ return NULL;
+ }
+}
+
+/**
+ * gst_event_get_structure:
+ * @event: The #GstEvent.
+ *
+ * Access the structure of the event.
+ *
+ * Returns: The structure of the event. The structure is still
+ * owned by the event, which means that you should not free it and
+ * that the pointer becomes invalid when you free the event.
+ *
+ * MT safe.
+ */
+const GstStructure *
+gst_event_get_structure (GstEvent * event)
+{
+ g_return_val_if_fail (GST_IS_EVENT (event), NULL);
+
+ return GST_EVENT_STRUCTURE (event);
+}
+
+/**
+ * gst_event_writable_structure:
+ * @event: The #GstEvent.
+ *
+ * Get a writable version of the structure.
+ *
+ * Returns: The structure of the event. The structure is still
+ * owned by the event, which means that you should not free it and
+ * that the pointer becomes invalid when you free the event.
+ * This function checks if @event is writable and will never return NULL.
+ *
+ * MT safe.
+ */
+GstStructure *
+gst_event_writable_structure (GstEvent * event)
+{
+ GstStructure *structure;
+
+ g_return_val_if_fail (GST_IS_EVENT (event), NULL);
+ g_return_val_if_fail (gst_event_is_writable (event), NULL);
+
+ structure = GST_EVENT_STRUCTURE (event);
+
+ if (structure == NULL) {
+ structure =
+ gst_structure_id_empty_new (gst_event_type_to_quark (GST_EVENT_TYPE
+ (event)));
+ gst_structure_set_parent_refcount (structure, &event->mini_object.refcount);
+ GST_EVENT_STRUCTURE (event) = structure;
+ }
+ return structure;
+}
+
+/**
+ * gst_event_has_name:
+ * @event: The #GstEvent.
+ * @name: name to check
+ *
+ * Checks if @event has the given @name. This function is usually used to
+ * check the name of a custom event.
+ *
+ * Returns: %TRUE if @name matches the name of the event structure.
+ *
+ * Since: 0.10.20
+ */
+gboolean
+gst_event_has_name (GstEvent * event, const gchar * name)
+{
+ g_return_val_if_fail (GST_IS_EVENT (event), FALSE);
+
+ if (GST_EVENT_STRUCTURE (event) == NULL)
+ return FALSE;
+
+ return gst_structure_has_name (GST_EVENT_STRUCTURE (event), name);
+}
+
+/**
+ * gst_event_get_seqnum:
+ * @event: A #GstEvent.
+ *
+ * Retrieve the sequence number of a event.
+ *
+ * Events have ever-incrementing sequence numbers, which may also be set
+ * explicitly via gst_event_set_seqnum(). Sequence numbers are typically used to
+ * indicate that a event corresponds to some other set of events or messages,
+ * for example an EOS event corresponding to a SEEK event. It is considered good
+ * practice to make this correspondence when possible, though it is not
+ * required.
+ *
+ * Note that events and messages share the same sequence number incrementor;
+ * two events or messages will never have the same sequence number unless
+ * that correspondence was made explicitly.
+ *
+ * Returns: The event's sequence number.
+ *
+ * MT safe.
+ *
+ * Since: 0.10.22
+ */
+guint32
+gst_event_get_seqnum (GstEvent * event)
+{
+ g_return_val_if_fail (GST_IS_EVENT (event), -1);
+
+ return GST_EVENT_SEQNUM (event);
+}
+
+/**
+ * gst_event_set_seqnum:
+ * @event: A #GstEvent.
+ * @seqnum: A sequence number.
+ *
+ * Set the sequence number of a event.
+ *
+ * This function might be called by the creator of a event to indicate that the
+ * event relates to other events or messages. See gst_event_get_seqnum() for
+ * more information.
+ *
+ * MT safe.
+ *
+ * Since: 0.10.22
+ */
+void
+gst_event_set_seqnum (GstEvent * event, guint32 seqnum)
+{
+ g_return_if_fail (GST_IS_EVENT (event));
+
+ GST_EVENT_SEQNUM (event) = seqnum;
+}
+
+/* FIXME 0.11: It would be nice to have flush events
+ * that don't reset the running time in the sinks
+ */
+
+/**
+ * gst_event_new_flush_start:
+ *
+ * Allocate a new flush start event. The flush start event can be sent
+ * upstream and downstream and travels out-of-bounds with the dataflow.
+ *
+ * It marks pads as being flushing and will make them return
+ * #GST_FLOW_WRONG_STATE when used for data flow with gst_pad_push(),
+ * gst_pad_chain(), gst_pad_alloc_buffer(), gst_pad_get_range() and
+ * gst_pad_pull_range(). Any event (except a #GST_EVENT_FLUSH_STOP) received
+ * on a flushing pad will return %FALSE immediately.
+ *
+ * Elements should unlock any blocking functions and exit their streaming
+ * functions as fast as possible when this event is received.
+ *
+ * This event is typically generated after a seek to flush out all queued data
+ * in the pipeline so that the new media is played as soon as possible.
+ *
+ * Returns: (transfer full): a new flush start event.
+ */
+GstEvent *
+gst_event_new_flush_start (void)
+{
+ return gst_event_new (GST_EVENT_FLUSH_START);
+}
+
+/**
+ * gst_event_new_flush_stop:
+ * @reset_time: if time should be reset
+ *
+ * Allocate a new flush stop event. The flush stop event can be sent
+ * upstream and downstream and travels serialized with the dataflow.
+ * It is typically sent after sending a FLUSH_START event to make the
+ * pads accept data again.
+ *
+ * Elements can process this event synchronized with the dataflow since
+ * the preceeding FLUSH_START event stopped the dataflow.
+ *
+ * This event is typically generated to complete a seek and to resume
+ * dataflow.
+ *
+ * Returns: (transfer full): a new flush stop event.
+ */
+GstEvent *
+gst_event_new_flush_stop (gboolean reset_time)
+{
+ GstEvent *event;
+
+ GST_CAT_INFO (GST_CAT_EVENT, "creating flush stop %d", reset_time);
+
+ event = gst_event_new_custom (GST_EVENT_FLUSH_STOP,
+ gst_structure_id_new (GST_QUARK (EVENT_FLUSH_STOP),
+ GST_QUARK (RESET_TIME), G_TYPE_BOOLEAN, reset_time, NULL));
+
+ return event;
+}
+
+/**
+ * gst_event_parse_flush_stop:
+ * @event: The event to parse
+ * @reset_time: (out): if time should be reset
+ *
+ * Parse the FLUSH_STOP event and retrieve the @reset_time member.
+ */
+void
+gst_event_parse_flush_stop (GstEvent * event, gboolean * reset_time)
+{
+ GstStructure *structure;
+
+ g_return_if_fail (GST_IS_EVENT (event));
+ g_return_if_fail (GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_STOP);
+
+ structure = GST_EVENT_STRUCTURE (event);
+ if (G_LIKELY (reset_time))
+ *reset_time =
+ g_value_get_boolean (gst_structure_id_get_value (structure,
+ GST_QUARK (RESET_TIME)));
+}
+
+/**
+ * gst_event_new_eos:
+ *
+ * Create a new EOS event. The eos event can only travel downstream
+ * synchronized with the buffer flow. Elements that receive the EOS
+ * event on a pad can return #GST_FLOW_UNEXPECTED as a #GstFlowReturn
+ * when data after the EOS event arrives.
+ *
+ * The EOS event will travel down to the sink elements in the pipeline
+ * which will then post the #GST_MESSAGE_EOS on the bus after they have
+ * finished playing any buffered data.
+ *
+ * When all sinks have posted an EOS message, an EOS message is
+ * forwarded to the application.
+ *
+ * The EOS event itself will not cause any state transitions of the pipeline.
+ *
+ * Returns: (transfer full): the new EOS event.
+ */
+GstEvent *
+gst_event_new_eos (void)
+{
+ return gst_event_new (GST_EVENT_EOS);
+}
+
+/**
+ * gst_event_new_caps:
+ * @caps: a #GstCaps
+ *
+ * Create a new CAPS event for @caps. The caps event can only travel downstream
+ * synchronized with the buffer flow and contains the format of the buffers
+ * that will follow after the event.
+ *
+ * Returns: (transfer full): the new CAPS event.
+ */
+GstEvent *
+gst_event_new_caps (GstCaps * caps)
+{
+ GstEvent *event;
+
+ g_return_val_if_fail (caps != NULL, NULL);
+ g_return_val_if_fail (gst_caps_is_fixed (caps), NULL);
+
+ GST_CAT_INFO (GST_CAT_EVENT, "creating caps event %" GST_PTR_FORMAT, caps);
+
+ event = gst_event_new_custom (GST_EVENT_CAPS,
+ gst_structure_id_new (GST_QUARK (EVENT_CAPS),
+ GST_QUARK (CAPS), GST_TYPE_CAPS, caps, NULL));
+
+ return event;
+}
+
+/**
+ * gst_event_parse_caps:
+ * @event: The event to parse
+ * @caps: (out): A pointer to the caps
+ *
+ * Get the caps from @event. The caps remains valid as long as @event remains
+ * valid.
+ */
+void
+gst_event_parse_caps (GstEvent * event, GstCaps ** caps)
+{
+ GstStructure *structure;
+
+ g_return_if_fail (GST_IS_EVENT (event));
+ g_return_if_fail (GST_EVENT_TYPE (event) == GST_EVENT_CAPS);
+
+ structure = GST_EVENT_STRUCTURE (event);
+ if (G_LIKELY (caps))
+ *caps =
+ g_value_get_boxed (gst_structure_id_get_value (structure,
+ GST_QUARK (CAPS)));
+}
+
+/**
+ * gst_event_new_segment:
+ * @segment: a #GstSegment
+ *
+ * Create a new SEGMENT event for @segment. The segment event can only travel
+ * downstream synchronized with the buffer flow and contains timing information
+ * and playback properties for the buffers that will follow.
+ *
+ * The newsegment event marks the range of buffers to be processed. All
+ * data not within the segment range is not to be processed. This can be
+ * used intelligently by plugins to apply more efficient methods of skipping
+ * unneeded data. The valid range is expressed with the @start and @stop
+ * values.
+ *
+ * The time value of the segment is used in conjunction with the start
+ * value to convert the buffer timestamps into the stream time. This is
+ * usually done in sinks to report the current stream_time.
+ * @time represents the stream_time of a buffer carrying a timestamp of
+ * @start. @time cannot be -1.
+ *
+ * @start cannot be -1, @stop can be -1. If there
+ * is a valid @stop given, it must be greater or equal the @start, including
+ * when the indicated playback @rate is < 0.
+ *
+ * The @applied_rate value provides information about any rate adjustment that
+ * has already been made to the timestamps and content on the buffers of the
+ * stream. (@rate * @applied_rate) should always equal the rate that has been
+ * requested for playback. For example, if an element has an input segment
+ * with intended playback @rate of 2.0 and applied_rate of 1.0, it can adjust
+ * incoming timestamps and buffer content by half and output a newsegment event
+ * with @rate of 1.0 and @applied_rate of 2.0
+ *
+ * After a newsegment event, the buffer stream time is calculated with:
+ *
+ * time + (TIMESTAMP(buf) - start) * ABS (rate * applied_rate)
+ *
+ * Returns: (transfer full): the new SEGMENT event.
+ */
+GstEvent *
+gst_event_new_segment (GstSegment * segment)
+{
+ GstEvent *event;
+
+ g_return_val_if_fail (segment != NULL, NULL);
+
+ GST_CAT_INFO (GST_CAT_EVENT, "creating segment event %" GST_SEGMENT_FORMAT,
+ segment);
+
+ event = gst_event_new_custom (GST_EVENT_SEGMENT,
+ gst_structure_id_new (GST_QUARK (EVENT_SEGMENT),
+ GST_QUARK (SEGMENT), GST_TYPE_SEGMENT, segment, NULL));
+
+ return event;
+}
+
+/**
+ * gst_event_parse_segment:
+ * @event: The event to parse
+ * @segment: (out) (transfer none): a pointer to a #GstSegment
+ *
+ * Parses a segment @event and stores the result in the given @segment location.
+ * @segment remains valid only until the @event is freed. Don't modify the segment
+ * and make a copy if you want to modify it or store it for later use.
+ */
+void
+gst_event_parse_segment (GstEvent * event, const GstSegment ** segment)
+{
+ GstStructure *structure;
+
+ g_return_if_fail (GST_IS_EVENT (event));
+ g_return_if_fail (GST_EVENT_TYPE (event) == GST_EVENT_SEGMENT);
+
+ if (segment) {
+ structure = GST_EVENT_STRUCTURE (event);
+ *segment = g_value_get_boxed (gst_structure_id_get_value (structure,
+ GST_QUARK (SEGMENT)));
+ }
+}
+
+/**
+ * gst_event_copy_segment:
+ * @event: The event to parse
+ * @segment: a pointer to a #GstSegment
+ *
+ * Parses a segment @event and copies the #GstSegment into the location
+ * given by @segment.
+ */
+void
+gst_event_copy_segment (GstEvent * event, GstSegment * segment)
+{
+ const GstSegment *src;
+
+ g_return_if_fail (GST_IS_EVENT (event));
+ g_return_if_fail (GST_EVENT_TYPE (event) == GST_EVENT_SEGMENT);
+
+ if (segment) {
+ gst_event_parse_segment (event, &src);
+ gst_segment_copy_into (src, segment);
+ }
+}
+
+/**
+ * gst_event_new_tag:
+ * @taglist: (transfer full): metadata list. The event will take ownership
+ * of the taglist.
+ *
+ * Generates a metadata tag event from the given @taglist.
+ *
+ * Returns: (transfer full): a new #GstEvent
+ */
+GstEvent *
+gst_event_new_tag (GstTagList * taglist)
+{
+ g_return_val_if_fail (taglist != NULL, NULL);
+
+ return gst_event_new_custom (GST_EVENT_TAG, (GstStructure *) taglist);
+}
+
+/**
+ * gst_event_parse_tag:
+ * @event: a tag event
+ * @taglist: (out) (transfer none): pointer to metadata list
+ *
+ * Parses a tag @event and stores the results in the given @taglist location.
+ * No reference to the taglist will be returned, it remains valid only until
+ * the @event is freed. Don't modify or free the taglist, make a copy if you
+ * want to modify it or store it for later use.
+ */
+void
+gst_event_parse_tag (GstEvent * event, GstTagList ** taglist)
+{
+ g_return_if_fail (GST_IS_EVENT (event));
+ g_return_if_fail (GST_EVENT_TYPE (event) == GST_EVENT_TAG);
+
+ if (taglist)
+ *taglist = (GstTagList *) GST_EVENT_STRUCTURE (event);
+}
+
+/* buffersize event */
+/**
+ * gst_event_new_buffer_size:
+ * @format: buffer format
+ * @minsize: minimum buffer size
+ * @maxsize: maximum buffer size
+ * @async: thread behavior
+ *
+ * Create a new buffersize event. The event is sent downstream and notifies
+ * elements that they should provide a buffer of the specified dimensions.
+ *
+ * When the @async flag is set, a thread boundary is preferred.
+ *
+ * Returns: (transfer full): a new #GstEvent
+ */
+GstEvent *
+gst_event_new_buffer_size (GstFormat format, gint64 minsize,
+ gint64 maxsize, gboolean async)
+{
+ GstEvent *event;
+ GstStructure *structure;
+
+ GST_CAT_INFO (GST_CAT_EVENT,
+ "creating buffersize format %s, minsize %" G_GINT64_FORMAT
+ ", maxsize %" G_GINT64_FORMAT ", async %d", gst_format_get_name (format),
+ minsize, maxsize, async);
+
+ structure = gst_structure_id_new (GST_QUARK (EVENT_BUFFER_SIZE),
+ GST_QUARK (FORMAT), GST_TYPE_FORMAT, format,
+ GST_QUARK (MINSIZE), G_TYPE_INT64, minsize,
+ GST_QUARK (MAXSIZE), G_TYPE_INT64, maxsize,
+ GST_QUARK (ASYNC), G_TYPE_BOOLEAN, async, NULL);
+ event = gst_event_new_custom (GST_EVENT_BUFFERSIZE, structure);
+
+ return event;
+}
+
+/**
+ * gst_event_parse_buffer_size:
+ * @event: The event to query
+ * @format: (out): A pointer to store the format in
+ * @minsize: (out): A pointer to store the minsize in
+ * @maxsize: (out): A pointer to store the maxsize in
+ * @async: (out): A pointer to store the async-flag in
+ *
+ * Get the format, minsize, maxsize and async-flag in the buffersize event.
+ */
+void
+gst_event_parse_buffer_size (GstEvent * event, GstFormat * format,
+ gint64 * minsize, gint64 * maxsize, gboolean * async)
+{
+ const GstStructure *structure;
+
+ g_return_if_fail (GST_IS_EVENT (event));
+ g_return_if_fail (GST_EVENT_TYPE (event) == GST_EVENT_BUFFERSIZE);
+
+ structure = GST_EVENT_STRUCTURE (event);
+ if (format)
+ *format = (GstFormat)
+ g_value_get_enum (gst_structure_id_get_value (structure,
+ GST_QUARK (FORMAT)));
+ if (minsize)
+ *minsize =
+ g_value_get_int64 (gst_structure_id_get_value (structure,
+ GST_QUARK (MINSIZE)));
+ if (maxsize)
+ *maxsize =
+ g_value_get_int64 (gst_structure_id_get_value (structure,
+ GST_QUARK (MAXSIZE)));
+ if (async)
+ *async =
+ g_value_get_boolean (gst_structure_id_get_value (structure,
+ GST_QUARK (ASYNC)));
+}
+
+/**
+ * gst_event_new_qos:
+ * @type: the QoS type
+ * @proportion: the proportion of the qos message
+ * @diff: The time difference of the last Clock sync
+ * @timestamp: The timestamp of the buffer
+ *
+ * Allocate a new qos event with the given values.
+ * The QOS event is generated in an element that wants an upstream
+ * element to either reduce or increase its rate because of
+ * high/low CPU load or other resource usage such as network performance or
+ * throttling. Typically sinks generate these events for each buffer
+ * they receive.
+ *
+ * @type indicates the reason for the QoS event. #GST_QOS_TYPE_OVERFLOW is
+ * used when a buffer arrived in time or when the sink cannot keep up with
+ * the upstream datarate. #GST_QOS_TYPE_UNDERFLOW is when the sink is not
+ * receiving buffers fast enough and thus has to drop late buffers.
+ * #GST_QOS_TYPE_THROTTLE is used when the datarate is artificially limited
+ * by the application, for example to reduce power consumption.
+ *
+ * @proportion indicates the real-time performance of the streaming in the
+ * element that generated the QoS event (usually the sink). The value is
+ * generally computed based on more long term statistics about the streams
+ * timestamps compared to the clock.
+ * A value < 1.0 indicates that the upstream element is producing data faster
+ * than real-time. A value > 1.0 indicates that the upstream element is not
+ * producing data fast enough. 1.0 is the ideal @proportion value. The
+ * proportion value can safely be used to lower or increase the quality of
+ * the element.
+ *
+ * @diff is the difference against the clock in running time of the last
+ * buffer that caused the element to generate the QOS event. A negative value
+ * means that the buffer with @timestamp arrived in time. A positive value
+ * indicates how late the buffer with @timestamp was. When throttling is
+ * enabled, @diff will be set to the requested throttling interval.
+ *
+ * @timestamp is the timestamp of the last buffer that cause the element
+ * to generate the QOS event. It is expressed in running time and thus an ever
+ * increasing value.
+ *
+ * The upstream element can use the @diff and @timestamp values to decide
+ * whether to process more buffers. For possitive @diff, all buffers with
+ * timestamp <= @timestamp + @diff will certainly arrive late in the sink
+ * as well. A (negative) @diff value so that @timestamp + @diff would yield a
+ * result smaller than 0 is not allowed.
+ *
+ * The application can use general event probes to intercept the QoS
+ * event and implement custom application specific QoS handling.
+ *
+ * Returns: (transfer full): a new QOS event.
+ */
+GstEvent *
+gst_event_new_qos (GstQOSType type, gdouble proportion,
+ GstClockTimeDiff diff, GstClockTime timestamp)
+{
+ GstEvent *event;
+ GstStructure *structure;
+
+ /* diff must be positive or timestamp + diff must be positive */
+ g_return_val_if_fail (diff >= 0 || -diff <= timestamp, NULL);
+
+ GST_CAT_INFO (GST_CAT_EVENT,
+ "creating qos type %d, proportion %lf, diff %" G_GINT64_FORMAT
+ ", timestamp %" GST_TIME_FORMAT, type, proportion,
+ diff, GST_TIME_ARGS (timestamp));
+
+ structure = gst_structure_id_new (GST_QUARK (EVENT_QOS),
+ GST_QUARK (TYPE), GST_TYPE_QOS_TYPE, type,
+ GST_QUARK (PROPORTION), G_TYPE_DOUBLE, proportion,
+ GST_QUARK (DIFF), G_TYPE_INT64, diff,
+ GST_QUARK (TIMESTAMP), G_TYPE_UINT64, timestamp, NULL);
+ event = gst_event_new_custom (GST_EVENT_QOS, structure);
+
+ return event;
+}
+
+/**
+ * gst_event_parse_qos:
+ * @event: The event to query
+ * @type: (out): A pointer to store the QoS type in
+ * @proportion: (out): A pointer to store the proportion in
+ * @diff: (out): A pointer to store the diff in
+ * @timestamp: (out): A pointer to store the timestamp in
+ *
+ * Get the type, proportion, diff and timestamp in the qos event. See
+ * gst_event_new_qos() for more information about the different QoS values.
+ */
+void
+gst_event_parse_qos (GstEvent * event, GstQOSType * type,
+ gdouble * proportion, GstClockTimeDiff * diff, GstClockTime * timestamp)
+{
+ const GstStructure *structure;
+
+ g_return_if_fail (GST_IS_EVENT (event));
+ g_return_if_fail (GST_EVENT_TYPE (event) == GST_EVENT_QOS);
+
+ structure = GST_EVENT_STRUCTURE (event);
+ if (type)
+ *type = (GstQOSType)
+ g_value_get_enum (gst_structure_id_get_value (structure,
+ GST_QUARK (TYPE)));
+ if (proportion)
+ *proportion =
+ g_value_get_double (gst_structure_id_get_value (structure,
+ GST_QUARK (PROPORTION)));
+ if (diff)
+ *diff =
+ g_value_get_int64 (gst_structure_id_get_value (structure,
+ GST_QUARK (DIFF)));
+ if (timestamp)
+ *timestamp =
+ g_value_get_uint64 (gst_structure_id_get_value (structure,
+ GST_QUARK (TIMESTAMP)));
+}
+
+/**
+ * gst_event_new_seek:
+ * @rate: The new playback rate
+ * @format: The format of the seek values
+ * @flags: The optional seek flags
+ * @start_type: The type and flags for the new start position
+ * @start: The value of the new start position
+ * @stop_type: The type and flags for the new stop position
+ * @stop: The value of the new stop position
+ *
+ * Allocate a new seek event with the given parameters.
+ *
+ * The seek event configures playback of the pipeline between @start to @stop
+ * at the speed given in @rate, also called a playback segment.
+ * The @start and @stop values are expressed in @format.
+ *
+ * A @rate of 1.0 means normal playback rate, 2.0 means double speed.
+ * Negatives values means backwards playback. A value of 0.0 for the
+ * rate is not allowed and should be accomplished instead by PAUSING the
+ * pipeline.
+ *
+ * A pipeline has a default playback segment configured with a start
+ * position of 0, a stop position of -1 and a rate of 1.0. The currently
+ * configured playback segment can be queried with #GST_QUERY_SEGMENT.
+ *
+ * @start_type and @stop_type specify how to adjust the currently configured
+ * start and stop fields in playback segment. Adjustments can be made relative
+ * or absolute to the last configured values. A type of #GST_SEEK_TYPE_NONE
+ * means that the position should not be updated.
+ *
+ * When the rate is positive and @start has been updated, playback will start
+ * from the newly configured start position.
+ *
+ * For negative rates, playback will start from the newly configured stop
+ * position (if any). If the stop position if updated, it must be different from
+ * -1 for negative rates.
+ *
+ * It is not possible to seek relative to the current playback position, to do
+ * this, PAUSE the pipeline, query the current playback position with
+ * #GST_QUERY_POSITION and update the playback segment current position with a
+ * #GST_SEEK_TYPE_SET to the desired position.
+ *
+ * Returns: (transfer full): a new seek event.
+ */
+GstEvent *
+gst_event_new_seek (gdouble rate, GstFormat format, GstSeekFlags flags,
+ GstSeekType start_type, gint64 start, GstSeekType stop_type, gint64 stop)
+{
+ GstEvent *event;
+ GstStructure *structure;
+
+ g_return_val_if_fail (rate != 0.0, NULL);
+
+ if (format == GST_FORMAT_TIME) {
+ GST_CAT_INFO (GST_CAT_EVENT,
+ "creating seek rate %lf, format TIME, flags %d, "
+ "start_type %d, start %" GST_TIME_FORMAT ", "
+ "stop_type %d, stop %" GST_TIME_FORMAT,
+ rate, flags, start_type, GST_TIME_ARGS (start),
+ stop_type, GST_TIME_ARGS (stop));
+ } else {
+ GST_CAT_INFO (GST_CAT_EVENT,
+ "creating seek rate %lf, format %s, flags %d, "
+ "start_type %d, start %" G_GINT64_FORMAT ", "
+ "stop_type %d, stop %" G_GINT64_FORMAT,
+ rate, gst_format_get_name (format), flags, start_type, start, stop_type,
+ stop);
+ }
+
+ structure = gst_structure_id_new (GST_QUARK (EVENT_SEEK),
+ GST_QUARK (RATE), G_TYPE_DOUBLE, rate,
+ GST_QUARK (FORMAT), GST_TYPE_FORMAT, format,
+ GST_QUARK (FLAGS), GST_TYPE_SEEK_FLAGS, flags,
+ GST_QUARK (CUR_TYPE), GST_TYPE_SEEK_TYPE, start_type,
+ GST_QUARK (CUR), G_TYPE_INT64, start,
+ GST_QUARK (STOP_TYPE), GST_TYPE_SEEK_TYPE, stop_type,
+ GST_QUARK (STOP), G_TYPE_INT64, stop, NULL);
+ event = gst_event_new_custom (GST_EVENT_SEEK, structure);
+
+ return event;
+}
+
+/**
+ * gst_event_parse_seek:
+ * @event: a seek event
+ * @rate: (out): result location for the rate
+ * @format: (out): result location for the stream format
+ * @flags: (out): result location for the #GstSeekFlags
+ * @start_type: (out): result location for the #GstSeekType of the start position
+ * @start: (out): result location for the start postion expressed in @format
+ * @stop_type: (out): result location for the #GstSeekType of the stop position
+ * @stop: (out): result location for the stop postion expressed in @format
+ *
+ * Parses a seek @event and stores the results in the given result locations.
+ */
+void
+gst_event_parse_seek (GstEvent * event, gdouble * rate,
+ GstFormat * format, GstSeekFlags * flags, GstSeekType * start_type,
+ gint64 * start, GstSeekType * stop_type, gint64 * stop)
+{
+ const GstStructure *structure;
+
+ g_return_if_fail (GST_IS_EVENT (event));
+ g_return_if_fail (GST_EVENT_TYPE (event) == GST_EVENT_SEEK);
+
+ structure = GST_EVENT_STRUCTURE (event);
+ if (rate)
+ *rate =
+ g_value_get_double (gst_structure_id_get_value (structure,
+ GST_QUARK (RATE)));
+ if (format)
+ *format = (GstFormat)
+ g_value_get_enum (gst_structure_id_get_value (structure,
+ GST_QUARK (FORMAT)));
+ if (flags)
+ *flags = (GstSeekFlags)
+ g_value_get_flags (gst_structure_id_get_value (structure,
+ GST_QUARK (FLAGS)));
+ if (start_type)
+ *start_type = (GstSeekType)
+ g_value_get_enum (gst_structure_id_get_value (structure,
+ GST_QUARK (CUR_TYPE)));
+ if (start)
+ *start =
+ g_value_get_int64 (gst_structure_id_get_value (structure,
+ GST_QUARK (CUR)));
+ if (stop_type)
+ *stop_type = (GstSeekType)
+ g_value_get_enum (gst_structure_id_get_value (structure,
+ GST_QUARK (STOP_TYPE)));
+ if (stop)
+ *stop =
+ g_value_get_int64 (gst_structure_id_get_value (structure,
+ GST_QUARK (STOP)));
+}
+
+/**
+ * gst_event_new_navigation:
+ * @structure: (transfer full): description of the event. The event will take
+ * ownership of the structure.
+ *
+ * Create a new navigation event from the given description.
+ *
+ * Returns: (transfer full): a new #GstEvent
+ */
+GstEvent *
+gst_event_new_navigation (GstStructure * structure)
+{
+ g_return_val_if_fail (structure != NULL, NULL);
+
+ return gst_event_new_custom (GST_EVENT_NAVIGATION, structure);
+}
+
+/**
+ * gst_event_new_latency:
+ * @latency: the new latency value
+ *
+ * Create a new latency event. The event is sent upstream from the sinks and
+ * notifies elements that they should add an additional @latency to the
+ * running time before synchronising against the clock.
+ *
+ * The latency is mostly used in live sinks and is always expressed in
+ * the time format.
+ *
+ * Returns: (transfer full): a new #GstEvent
+ *
+ * Since: 0.10.12
+ */
+GstEvent *
+gst_event_new_latency (GstClockTime latency)
+{
+ GstEvent *event;
+ GstStructure *structure;
+
+ GST_CAT_INFO (GST_CAT_EVENT,
+ "creating latency event %" GST_TIME_FORMAT, GST_TIME_ARGS (latency));
+
+ structure = gst_structure_id_new (GST_QUARK (EVENT_LATENCY),
+ GST_QUARK (LATENCY), G_TYPE_UINT64, latency, NULL);
+ event = gst_event_new_custom (GST_EVENT_LATENCY, structure);
+
+ return event;
+}
+
+/**
+ * gst_event_parse_latency:
+ * @event: The event to query
+ * @latency: (out): A pointer to store the latency in.
+ *
+ * Get the latency in the latency event.
+ *
+ * Since: 0.10.12
+ */
+void
+gst_event_parse_latency (GstEvent * event, GstClockTime * latency)
+{
+ g_return_if_fail (GST_IS_EVENT (event));
+ g_return_if_fail (GST_EVENT_TYPE (event) == GST_EVENT_LATENCY);
+
+ if (latency)
+ *latency =
+ g_value_get_uint64 (gst_structure_id_get_value (GST_EVENT_STRUCTURE
+ (event), GST_QUARK (LATENCY)));
+}
+
+/**
+ * gst_event_new_step:
+ * @format: the format of @amount
+ * @amount: the amount of data to step
+ * @rate: the step rate
+ * @flush: flushing steps
+ * @intermediate: intermediate steps
+ *
+ * Create a new step event. The purpose of the step event is to instruct a sink
+ * to skip @amount (expressed in @format) of media. It can be used to implement
+ * stepping through the video frame by frame or for doing fast trick modes.
+ *
+ * A rate of <= 0.0 is not allowed, pause the pipeline or reverse the playback
+ * direction of the pipeline to get the same effect.
+ *
+ * The @flush flag will clear any pending data in the pipeline before starting
+ * the step operation.
+ *
+ * The @intermediate flag instructs the pipeline that this step operation is
+ * part of a larger step operation.
+ *
+ * Returns: (transfer full): a new #GstEvent
+ *
+ * Since: 0.10.24
+ */
+GstEvent *
+gst_event_new_step (GstFormat format, guint64 amount, gdouble rate,
+ gboolean flush, gboolean intermediate)
+{
+ GstEvent *event;
+ GstStructure *structure;
+
+ g_return_val_if_fail (rate > 0.0, NULL);
+
+ GST_CAT_INFO (GST_CAT_EVENT, "creating step event");
+
+ structure = gst_structure_id_new (GST_QUARK (EVENT_STEP),
+ GST_QUARK (FORMAT), GST_TYPE_FORMAT, format,
+ GST_QUARK (AMOUNT), G_TYPE_UINT64, amount,
+ GST_QUARK (RATE), G_TYPE_DOUBLE, rate,
+ GST_QUARK (FLUSH), G_TYPE_BOOLEAN, flush,
+ GST_QUARK (INTERMEDIATE), G_TYPE_BOOLEAN, intermediate, NULL);
+ event = gst_event_new_custom (GST_EVENT_STEP, structure);
+
+ return event;
+}
+
+/**
+ * gst_event_parse_step:
+ * @event: The event to query
+ * @format: (out) (allow-none): a pointer to store the format in
+ * @amount: (out) (allow-none): a pointer to store the amount in
+ * @rate: (out) (allow-none): a pointer to store the rate in
+ * @flush: (out) (allow-none): a pointer to store the flush boolean in
+ * @intermediate: (out) (allow-none): a pointer to store the intermediate
+ * boolean in
+ *
+ * Parse the step event.
+ *
+ * Since: 0.10.24
+ */
+void
+gst_event_parse_step (GstEvent * event, GstFormat * format, guint64 * amount,
+ gdouble * rate, gboolean * flush, gboolean * intermediate)
+{
+ const GstStructure *structure;
+
+ g_return_if_fail (GST_IS_EVENT (event));
+ g_return_if_fail (GST_EVENT_TYPE (event) == GST_EVENT_STEP);
+
+ structure = GST_EVENT_STRUCTURE (event);
+ if (format)
+ *format =
+ (GstFormat) g_value_get_enum (gst_structure_id_get_value (structure,
+ GST_QUARK (FORMAT)));
+ if (amount)
+ *amount = g_value_get_uint64 (gst_structure_id_get_value (structure,
+ GST_QUARK (AMOUNT)));
+ if (rate)
+ *rate = g_value_get_double (gst_structure_id_get_value (structure,
+ GST_QUARK (RATE)));
+ if (flush)
+ *flush = g_value_get_boolean (gst_structure_id_get_value (structure,
+ GST_QUARK (FLUSH)));
+ if (intermediate)
+ *intermediate = g_value_get_boolean (gst_structure_id_get_value (structure,
+ GST_QUARK (INTERMEDIATE)));
+}
+
+/**
+ * gst_event_new_reconfigure:
+
+ * Create a new reconfigure event. The purpose of the reconfingure event is
+ * to travel upstream and make elements renegotiate their caps or reconfigure
+ * their buffer pools. This is useful when changing properties on elements
+ * or changing the topology of the pipeline.
+ *
+ * Returns: (transfer full): a new #GstEvent
+ *
+ * Since: 0.11.0
+ */
+GstEvent *
+gst_event_new_reconfigure (void)
+{
+ GstEvent *event;
+
+ GST_CAT_INFO (GST_CAT_EVENT, "creating reconfigure event");
+
+ event = gst_event_new_custom (GST_EVENT_RECONFIGURE, NULL);
+
+ return event;
+}
+
+/**
+ * gst_event_new_sink_message:
+ * @msg: (transfer none): the #GstMessage to be posted
+ *
+ * Create a new sink-message event. The purpose of the sink-message event is
+ * to instruct a sink to post the message contained in the event synchronized
+ * with the stream.
+ *
+ * Returns: (transfer full): a new #GstEvent
+ *
+ * Since: 0.10.26
+ */
+/* FIXME 0.11: take ownership of msg for consistency? */
+GstEvent *
+gst_event_new_sink_message (GstMessage * msg)
+{
+ GstEvent *event;
+ GstStructure *structure;
+
+ g_return_val_if_fail (msg != NULL, NULL);
+
+ GST_CAT_INFO (GST_CAT_EVENT, "creating sink-message event");
+
+ structure = gst_structure_id_new (GST_QUARK (EVENT_SINK_MESSAGE),
+ GST_QUARK (MESSAGE), GST_TYPE_MESSAGE, msg, NULL);
+ event = gst_event_new_custom (GST_EVENT_SINK_MESSAGE, structure);
+
+ return event;
+}
+
+/**
+ * gst_event_parse_sink_message:
+ * @event: The event to query
+ * @msg: (out) (transfer full): a pointer to store the #GstMessage in.
+ *
+ * Parse the sink-message event. Unref @msg after usage.
+ *
+ * Since: 0.10.26
+ */
+void
+gst_event_parse_sink_message (GstEvent * event, GstMessage ** msg)
+{
+ const GstStructure *structure;
+
+ g_return_if_fail (GST_IS_EVENT (event));
+ g_return_if_fail (GST_EVENT_TYPE (event) == GST_EVENT_SINK_MESSAGE);
+
+ structure = GST_EVENT_STRUCTURE (event);
+ if (msg)
+ *msg =
+ GST_MESSAGE (g_value_dup_boxed (gst_structure_id_get_value
+ (structure, GST_QUARK (MESSAGE))));
+}
diff --git a/gst/gstevent.h b/gst/gstevent.h
new file mode 100644
index 0000000..95013ba
--- /dev/null
+++ b/gst/gstevent.h
@@ -0,0 +1,497 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2000 Wim Taymans <wim.taymans@chello.be>
+ * 2005 Wim Taymans <wim@fluendo.com>
+ *
+ * gstevent.h: Header for GstEvent subsystem
+ *
+ * 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_EVENT_H__
+#define __GST_EVENT_H__
+
+typedef struct _GstEvent GstEvent;
+
+/**
+ * GstEventTypeFlags:
+ * @GST_EVENT_TYPE_UPSTREAM: Set if the event can travel upstream.
+ * @GST_EVENT_TYPE_DOWNSTREAM: Set if the event can travel downstream.
+ * @GST_EVENT_TYPE_SERIALIZED: Set if the event should be serialized with data
+ * flow.
+ * @GST_EVENT_TYPE_STICKY: Set if the event is sticky on the pads.
+ *
+ * #GstEventTypeFlags indicate the aspects of the different #GstEventType
+ * values. You can get the type flags of a #GstEventType with the
+ * gst_event_type_get_flags() function.
+ */
+typedef enum {
+ GST_EVENT_TYPE_UPSTREAM = 1 << 0,
+ GST_EVENT_TYPE_DOWNSTREAM = 1 << 1,
+ GST_EVENT_TYPE_SERIALIZED = 1 << 2,
+ GST_EVENT_TYPE_STICKY = 1 << 3
+} GstEventTypeFlags;
+
+/**
+ * GST_EVENT_TYPE_BOTH:
+ *
+ * The same thing as #GST_EVENT_TYPE_UPSTREAM | #GST_EVENT_TYPE_DOWNSTREAM.
+ */
+#define GST_EVENT_TYPE_BOTH \
+ (GST_EVENT_TYPE_UPSTREAM | GST_EVENT_TYPE_DOWNSTREAM)
+
+#define GST_EVENT_MAX_STICKY 16
+#define GST_EVENT_STICKY_SHIFT 8
+#define GST_EVENT_NUM_SHIFT (GST_EVENT_STICKY_SHIFT + 4)
+
+/**
+ * GST_EVENT_MAKE_TYPE:
+ * @num: the event number to create
+ * @idx: the index in the sticky array
+ * @flags: the event flags
+ *
+ * when making custom event types, use this macro with the num and
+ * the given flags
+ */
+#define GST_EVENT_MAKE_TYPE(num,idx,flags) \
+ (((num) << GST_EVENT_NUM_SHIFT) | ((idx) << GST_EVENT_STICKY_SHIFT) | (flags))
+
+#define FLAG(name) GST_EVENT_TYPE_##name
+
+#define GST_EVENT_STICKY_IDX_TYPE(type) (((type) >> GST_EVENT_STICKY_SHIFT) & 0xf)
+#define GST_EVENT_STICKY_IDX(ev) GST_EVENT_STICKY_IDX_TYPE(GST_EVENT_TYPE(ev))
+
+/**
+ * GstEventType:
+ * @GST_EVENT_UNKNOWN: unknown event.
+ * @GST_EVENT_FLUSH_START: Start a flush operation. This event clears all data
+ * from the pipeline and unblock all streaming threads.
+ * @GST_EVENT_FLUSH_STOP: Stop a flush operation. This event resets the
+ * running-time of the pipeline.
+ * @GST_EVENT_CAPS: #GstCaps event. Notify the pad of a new media type.
+ * @GST_EVENT_SEGMENT: A new media segment follows in the dataflow. The
+ * segment events contains information for clipping buffers and
+ * converting buffer timestamps to running-time and
+ * stream-time.
+ * @GST_EVENT_TAG: A new set of metadata tags has been found in the stream.
+ * @GST_EVENT_BUFFERSIZE: Notification of buffering requirements. Currently not
+ * used yet.
+ * @GST_EVENT_SINK_MESSAGE: An event that sinks turn into a message. Used to
+ * send messages that should be emitted in sync with
+ * rendering.
+ * Since: 0.10.26
+ * @GST_EVENT_EOS: End-Of-Stream. No more data is to be expected to follow
+ * without a SEGMENT event.
+ * @GST_EVENT_QOS: A quality message. Used to indicate to upstream elements
+ * that the downstream elements should adjust their processing
+ * rate.
+ * @GST_EVENT_SEEK: A request for a new playback position and rate.
+ * @GST_EVENT_NAVIGATION: Navigation events are usually used for communicating
+ * user requests, such as mouse or keyboard movements,
+ * to upstream elements.
+ * @GST_EVENT_LATENCY: Notification of new latency adjustment. Sinks will use
+ * the latency information to adjust their synchronisation.
+ * Since: 0.10.12
+ * @GST_EVENT_STEP: A request for stepping through the media. Sinks will usually
+ * execute the step operation. Since: 0.10.24
+ * @GST_EVENT_RECONFIGURE: A request for upstream renegotiating caps and reconfiguring.
+ * Since: 0.11.0
+ * @GST_EVENT_CUSTOM_UPSTREAM: Upstream custom event
+ * @GST_EVENT_CUSTOM_DOWNSTREAM: Downstream custom event that travels in the
+ * data flow.
+ * @GST_EVENT_CUSTOM_DOWNSTREAM_OOB: Custom out-of-band downstream event.
+ * @GST_EVENT_CUSTOM_BOTH: Custom upstream or downstream event.
+ * In-band when travelling downstream.
+ * @GST_EVENT_CUSTOM_BOTH_OOB: Custom upstream or downstream out-of-band event.
+ *
+ * #GstEventType lists the standard event types that can be sent in a pipeline.
+ *
+ * The custom event types can be used for private messages between elements
+ * that can't be expressed using normal
+ * GStreamer buffer passing semantics. Custom events carry an arbitrary
+ * #GstStructure.
+ * Specific custom events are distinguished by the name of the structure.
+ */
+/* NOTE: keep in sync with quark registration in gstevent.c */
+typedef enum {
+ GST_EVENT_UNKNOWN = GST_EVENT_MAKE_TYPE (0, 0, 0),
+ /* bidirectional events */
+ GST_EVENT_FLUSH_START = GST_EVENT_MAKE_TYPE (1, 0, FLAG(BOTH)),
+ GST_EVENT_FLUSH_STOP = GST_EVENT_MAKE_TYPE (2, 0, FLAG(BOTH) | FLAG(SERIALIZED)),
+ /* downstream serialized events */
+ GST_EVENT_CAPS = GST_EVENT_MAKE_TYPE (5, 1, FLAG(DOWNSTREAM) | FLAG(SERIALIZED) | FLAG(STICKY)),
+ GST_EVENT_SEGMENT = GST_EVENT_MAKE_TYPE (6, 2, FLAG(DOWNSTREAM) | FLAG(SERIALIZED) | FLAG(STICKY)),
+ GST_EVENT_TAG = GST_EVENT_MAKE_TYPE (7, 3, FLAG(DOWNSTREAM) | FLAG(SERIALIZED) | FLAG(STICKY)),
+ GST_EVENT_BUFFERSIZE = GST_EVENT_MAKE_TYPE (8, 4, FLAG(DOWNSTREAM) | FLAG(SERIALIZED) | FLAG(STICKY)),
+ GST_EVENT_SINK_MESSAGE = GST_EVENT_MAKE_TYPE (9, 5, FLAG(DOWNSTREAM) | FLAG(SERIALIZED) | FLAG(STICKY)),
+ GST_EVENT_EOS = GST_EVENT_MAKE_TYPE (10, 6, FLAG(DOWNSTREAM) | FLAG(SERIALIZED) | FLAG(STICKY)),
+
+ /* upstream events */
+ GST_EVENT_QOS = GST_EVENT_MAKE_TYPE (15, 0, FLAG(UPSTREAM)),
+ GST_EVENT_SEEK = GST_EVENT_MAKE_TYPE (16, 0, FLAG(UPSTREAM)),
+ GST_EVENT_NAVIGATION = GST_EVENT_MAKE_TYPE (17, 0, FLAG(UPSTREAM)),
+ GST_EVENT_LATENCY = GST_EVENT_MAKE_TYPE (18, 0, FLAG(UPSTREAM)),
+ GST_EVENT_STEP = GST_EVENT_MAKE_TYPE (19, 0, FLAG(UPSTREAM)),
+ GST_EVENT_RECONFIGURE = GST_EVENT_MAKE_TYPE (20, 0, FLAG(UPSTREAM)),
+
+ /* custom events start here */
+ GST_EVENT_CUSTOM_UPSTREAM = GST_EVENT_MAKE_TYPE (32, 0, FLAG(UPSTREAM)),
+ GST_EVENT_CUSTOM_DOWNSTREAM = GST_EVENT_MAKE_TYPE (32, 0, FLAG(DOWNSTREAM) | FLAG(SERIALIZED)),
+ GST_EVENT_CUSTOM_DOWNSTREAM_OOB = GST_EVENT_MAKE_TYPE (32, 0, FLAG(DOWNSTREAM)),
+ GST_EVENT_CUSTOM_BOTH = GST_EVENT_MAKE_TYPE (32, 0, FLAG(BOTH) | FLAG(SERIALIZED)),
+ GST_EVENT_CUSTOM_BOTH_OOB = GST_EVENT_MAKE_TYPE (32, 0, FLAG(BOTH))
+} GstEventType;
+#undef FLAG
+
+#include <gst/gstminiobject.h>
+#include <gst/gstformat.h>
+#include <gst/gstobject.h>
+#include <gst/gstclock.h>
+#include <gst/gststructure.h>
+#include <gst/gsttaglist.h>
+#include <gst/gstsegment.h>
+#include <gst/gstsegment.h>
+#include <gst/gstmessage.h>
+
+G_BEGIN_DECLS
+
+extern GType _gst_event_type;
+
+#define GST_TYPE_EVENT (_gst_event_type)
+#define GST_IS_EVENT(obj) (GST_IS_MINI_OBJECT_TYPE (obj, GST_TYPE_EVENT))
+#define GST_EVENT_CAST(obj) ((GstEvent *)(obj))
+#define GST_EVENT(obj) (GST_EVENT_CAST(obj))
+
+/**
+ * GST_EVENT_TRACE_NAME:
+ *
+ * The name used for memory allocation tracing
+ */
+#define GST_EVENT_TRACE_NAME "GstEvent"
+
+/**
+ * GST_EVENT_TYPE:
+ * @event: the event to query
+ *
+ * Get the #GstEventType of the event.
+ */
+#define GST_EVENT_TYPE(event) (GST_EVENT_CAST(event)->type)
+
+/**
+ * GST_EVENT_TYPE_NAME:
+ * @event: the event to query
+ *
+ * Get a constant string representation of the #GstEventType of the event.
+ */
+#define GST_EVENT_TYPE_NAME(event) (gst_event_type_get_name(GST_EVENT_TYPE(event)))
+
+/**
+ * GST_EVENT_TIMESTAMP:
+ * @event: the event to query
+ *
+ * Get the #GstClockTime timestamp of the event. This is the time when the event
+ * was created.
+ */
+#define GST_EVENT_TIMESTAMP(event) (GST_EVENT_CAST(event)->timestamp)
+
+/**
+ * GST_EVENT_SEQNUM:
+ * @event: the event to query
+ *
+ * The sequence number of @event.
+ */
+#define GST_EVENT_SEQNUM(event) (GST_EVENT_CAST(event)->seqnum)
+
+/**
+ * GST_EVENT_IS_UPSTREAM:
+ * @ev: the event to query
+ *
+ * Check if an event can travel upstream.
+ */
+#define GST_EVENT_IS_UPSTREAM(ev) !!(GST_EVENT_TYPE (ev) & GST_EVENT_TYPE_UPSTREAM)
+/**
+ * GST_EVENT_IS_DOWNSTREAM:
+ * @ev: the event to query
+ *
+ * Check if an event can travel downstream.
+ */
+#define GST_EVENT_IS_DOWNSTREAM(ev) !!(GST_EVENT_TYPE (ev) & GST_EVENT_TYPE_DOWNSTREAM)
+/**
+ * GST_EVENT_IS_SERIALIZED:
+ * @ev: the event to query
+ *
+ * Check if an event is serialized with the data stream.
+ */
+#define GST_EVENT_IS_SERIALIZED(ev) !!(GST_EVENT_TYPE (ev) & GST_EVENT_TYPE_SERIALIZED)
+/**
+ * GST_EVENT_IS_STICKY:
+ * @ev: the event to query
+ *
+ * Check if an event is sticky on the pads.
+ */
+#define GST_EVENT_IS_STICKY(ev) !!(GST_EVENT_TYPE (ev) & GST_EVENT_TYPE_STICKY)
+
+/**
+ * gst_event_is_writable:
+ * @ev: a #GstEvent
+ *
+ * Tests if you can safely write data into a event's structure or validly
+ * modify the seqnum and timestamp field.
+ */
+#define gst_event_is_writable(ev) gst_mini_object_is_writable (GST_MINI_OBJECT_CAST (ev))
+/**
+ * gst_event_make_writable:
+ * @ev: (transfer full): a #GstEvent
+ *
+ * Makes a writable event from the given event. If the source event is
+ * already writable, this will simply return the same event. A copy will
+ * otherwise be made using gst_event_copy().
+ *
+ * Returns: (transfer full): a writable event which may or may not be the
+ * same as @ev
+ */
+#define gst_event_make_writable(ev) GST_EVENT_CAST (gst_mini_object_make_writable (GST_MINI_OBJECT_CAST (ev)))
+/**
+ * gst_event_replace:
+ * @old_event: (inout) (transfer full): pointer to a pointer to a #GstEvent
+ * to be replaced.
+ * @new_event: (allow-none) (transfer none): pointer to a #GstEvent that will
+ * replace the event pointed to by @old_event.
+ *
+ * Modifies a pointer to a #GstEvent to point to a different #GstEvent. The
+ * modification is done atomically (so this is useful for ensuring thread safety
+ * in some cases), and the reference counts are updated appropriately (the old
+ * event is unreffed, the new one is reffed).
+ *
+ * Either @new_event or the #GstEvent pointed to by @old_event may be NULL.
+ *
+ * Returns: TRUE if @new_event was different from @old_event
+ */
+#define gst_event_replace(old_event,new_event) \
+ gst_mini_object_replace ((GstMiniObject **)(old_event), GST_MINI_OBJECT_CAST (new_event))
+/**
+ * gst_event_steal:
+ * @old_event: (inout) (transfer full): pointer to a pointer to a #GstEvent
+ * to be stolen.
+ *
+ * Atomically replace the #GstEvent pointed to by @old_event with NULL and
+ * return the original event.
+ *
+ * Returns: the #GstEvent that was in @old_event
+ */
+#define gst_event_steal(old_event) \
+ GST_EVENT_CAST (gst_mini_object_steal ((GstMiniObject **)(old_event)))
+/**
+ * gst_event_take:
+ * @old_event: (inout) (transfer full): pointer to a pointer to a #GstEvent
+ * to be stolen.
+ * @new_event: (allow-none) (transfer full): pointer to a #GstEvent that will
+ * replace the event pointed to by @old_event.
+ *
+ * Modifies a pointer to a #GstEvent to point to a different #GstEvent. This
+ * function is similar to gst_event_replace() except that it takes ownership of
+ * @new_event.
+ *
+ * Either @new_event or the #GstEvent pointed to by @old_event may be NULL.
+ *
+ * Returns: TRUE if @new_event was different from @old_event
+ */
+#define gst_event_take(old_event,new_event) \
+ gst_mini_object_take ((GstMiniObject **)(old_event), GST_MINI_OBJECT_CAST (new_event))
+
+/**
+ * GstQOSType:
+ * @GST_QOS_TYPE_OVERFLOW: The QoS event type that is produced when downstream
+ * elements are producing data too quickly and the element can't keep up
+ * processing the data. Upstream should reduce their processing rate. This
+ * type is also used when buffers arrive early or in time.
+ * @GST_QOS_TYPE_UNDERFLOW: The QoS event type that is produced when downstream
+ * elements are producing data too slowly and need to speed up their processing
+ * rate.
+ * @GST_QOS_TYPE_THROTTLE: The QoS event type that is produced when the
+ * application enabled throttling to limit the datarate.
+ *
+ * The different types of QoS events that can be given to the
+ * gst_event_new_qos() method.
+ *
+ * Since: 0.10.33
+ */
+typedef enum {
+ GST_QOS_TYPE_OVERFLOW = 0,
+ GST_QOS_TYPE_UNDERFLOW = 1,
+ GST_QOS_TYPE_THROTTLE = 2
+} GstQOSType;
+
+/**
+ * GstEvent:
+ * @mini_object: the parent structure
+ * @type: the #GstEventType of the event
+ * @timestamp: the timestamp of the event
+ * @seqnum: the sequence number of the event
+ *
+ * A #GstEvent.
+ */
+struct _GstEvent {
+ GstMiniObject mini_object;
+
+ /*< public >*/ /* with COW */
+ GstEventType type;
+ guint64 timestamp;
+ guint32 seqnum;
+};
+
+const gchar* gst_event_type_get_name (GstEventType type);
+GQuark gst_event_type_to_quark (GstEventType type);
+GstEventTypeFlags
+ gst_event_type_get_flags (GstEventType type);
+
+
+/* refcounting */
+/**
+ * gst_event_ref:
+ * @event: The event to refcount
+ *
+ * Increase the refcount of this event.
+ *
+ * Returns: (transfer full): @event (for convenience when doing assignments)
+ */
+#ifdef _FOOL_GTK_DOC_
+G_INLINE_FUNC GstEvent * gst_event_ref (GstEvent * event);
+#endif
+
+static inline GstEvent *
+gst_event_ref (GstEvent * event)
+{
+ return (GstEvent *) gst_mini_object_ref (GST_MINI_OBJECT_CAST (event));
+}
+
+/**
+ * gst_event_unref:
+ * @event: (transfer full): the event to refcount
+ *
+ * Decrease the refcount of an event, freeing it if the refcount reaches 0.
+ */
+#ifdef _FOOL_GTK_DOC_
+G_INLINE_FUNC void gst_event_unref (GstEvent * event);
+#endif
+
+static inline void
+gst_event_unref (GstEvent * event)
+{
+ gst_mini_object_unref (GST_MINI_OBJECT_CAST (event));
+}
+
+/* copy event */
+/**
+ * gst_event_copy:
+ * @event: The event to copy
+ *
+ * Copy the event using the event specific copy function.
+ *
+ * Returns: (transfer full): the new event
+ */
+#ifdef _FOOL_GTK_DOC_
+G_INLINE_FUNC GstEvent * gst_event_copy (const GstEvent * event);
+#endif
+
+static inline GstEvent *
+gst_event_copy (const GstEvent * event)
+{
+ return GST_EVENT_CAST (gst_mini_object_copy (GST_MINI_OBJECT_CONST_CAST (event)));
+}
+
+GType gst_event_get_type (void);
+
+/* custom event */
+GstEvent* gst_event_new_custom (GstEventType type, GstStructure *structure);
+
+const GstStructure *
+ gst_event_get_structure (GstEvent *event);
+GstStructure * gst_event_writable_structure (GstEvent *event);
+
+gboolean gst_event_has_name (GstEvent *event, const gchar *name);
+
+/* identifiers for events and messages */
+guint32 gst_event_get_seqnum (GstEvent *event);
+void gst_event_set_seqnum (GstEvent *event, guint32 seqnum);
+
+/* flush events */
+GstEvent * gst_event_new_flush_start (void);
+
+GstEvent * gst_event_new_flush_stop (gboolean reset_time);
+void gst_event_parse_flush_stop (GstEvent *event, gboolean *reset_time);
+
+/* EOS event */
+GstEvent * gst_event_new_eos (void);
+
+/* Caps events */
+GstEvent * gst_event_new_caps (GstCaps *caps);
+void gst_event_parse_caps (GstEvent *event, GstCaps **caps);
+
+/* segment event */
+GstEvent* gst_event_new_segment (GstSegment *segment);
+void gst_event_parse_segment (GstEvent *event, const GstSegment **segment);
+void gst_event_copy_segment (GstEvent *event, GstSegment *segment);
+
+/* tag event */
+GstEvent* gst_event_new_tag (GstTagList *taglist);
+void gst_event_parse_tag (GstEvent *event, GstTagList **taglist);
+
+/* buffer */
+GstEvent * gst_event_new_buffer_size (GstFormat format, gint64 minsize, gint64 maxsize,
+ gboolean async);
+void gst_event_parse_buffer_size (GstEvent *event, GstFormat *format, gint64 *minsize,
+ gint64 *maxsize, gboolean *async);
+
+/* sink message */
+GstEvent* gst_event_new_sink_message (GstMessage *msg);
+void gst_event_parse_sink_message (GstEvent *event, GstMessage **msg);
+
+/* QOS events */
+GstEvent* gst_event_new_qos (GstQOSType type, gdouble proportion,
+ GstClockTimeDiff diff, GstClockTime timestamp);
+void gst_event_parse_qos (GstEvent *event, GstQOSType *type,
+ gdouble *proportion, GstClockTimeDiff *diff,
+ GstClockTime *timestamp);
+/* seek event */
+GstEvent* gst_event_new_seek (gdouble rate, GstFormat format, GstSeekFlags flags,
+ GstSeekType start_type, gint64 start,
+ GstSeekType stop_type, gint64 stop);
+void gst_event_parse_seek (GstEvent *event, gdouble *rate, GstFormat *format,
+ GstSeekFlags *flags,
+ GstSeekType *start_type, gint64 *start,
+ GstSeekType *stop_type, gint64 *stop);
+
+/* navigation event */
+GstEvent* gst_event_new_navigation (GstStructure *structure);
+
+/* latency event */
+GstEvent* gst_event_new_latency (GstClockTime latency);
+void gst_event_parse_latency (GstEvent *event, GstClockTime *latency);
+
+/* step event */
+GstEvent* gst_event_new_step (GstFormat format, guint64 amount, gdouble rate,
+ gboolean flush, gboolean intermediate);
+void gst_event_parse_step (GstEvent *event, GstFormat *format, guint64 *amount,
+ gdouble *rate, gboolean *flush, gboolean *intermediate);
+
+/* renegotiate event */
+GstEvent* gst_event_new_reconfigure (void);
+
+G_END_DECLS
+
+#endif /* __GST_EVENT_H__ */
diff --git a/gst/gstfilter.c b/gst/gstfilter.c
new file mode 100644
index 0000000..7d6c055
--- /dev/null
+++ b/gst/gstfilter.c
@@ -0,0 +1,88 @@
+/* GStreamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ *
+ * 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:gstfilter
+ * @short_description: A utility function to filter GLists.
+ *
+ * <example>
+ * <title>Filtering a list</title>
+ * <programlisting>
+ * GList *node;
+ * GstObject *result = NULL;
+ *
+ * node = gst_filter_run (list, (GstFilterFunc) my_filter, TRUE, NULL);
+ * if (node) {
+ * result = GST_OBJECT (node->data);
+ * gst_object_ref (result);
+ * g_list_free (node);
+ * }
+ * </programlisting>
+ * </example>
+ */
+#include "gst_private.h"
+#include <gst/gstfilter.h>
+
+/**
+ * gst_filter_run:
+ * @list: a linked list
+ * @func: (scope call): the function to execute for each item
+ * @first: flag to stop execution after a successful item
+ * @user_data: (closure): user data
+ *
+ * Iterates over the elements in @list, calling @func with the
+ * list item data for each item. If @func returns TRUE, @data is
+ * prepended to the list of results returned. If @first is true,
+ * the search is halted after the first result is found.
+ *
+ * Since gst_filter_run() knows nothing about the type of @data, no
+ * reference will be taken (if @data refers to an object) and no copy of
+ * @data wil be made in any other way when prepending @data to the list of
+ * results.
+ *
+ * Returns: (transfer container): the list of results. Free with g_list_free()
+ * when no longer needed (the data contained in the list is a flat copy
+ * and does need to be unreferenced or freed).
+ */
+GList *
+gst_filter_run (const GList * list, GstFilterFunc func, gboolean first,
+ gpointer user_data)
+{
+ const GList *walk = list;
+ GList *result = NULL;
+
+ while (walk) {
+ gboolean res = TRUE;
+ gpointer data = walk->data;
+
+ walk = g_list_next (walk);
+
+ if (func)
+ res = func (data, user_data);
+
+ if (res) {
+ result = g_list_prepend (result, data);
+
+ if (first)
+ break;
+ }
+ }
+
+ return result;
+}
diff --git a/gst/gstfilter.h b/gst/gstfilter.h
new file mode 100644
index 0000000..1728f00
--- /dev/null
+++ b/gst/gstfilter.h
@@ -0,0 +1,44 @@
+/* GStreamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ *
+ * 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_FILTER_H__
+#define __GST_FILTER_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+/**
+ * GstFilterFunc:
+ * @obj: the object
+ * @user_data: filter data
+ *
+ * Function prototype for a filter callback that can be use in gst_filter_run().
+ * The function should apply its filtering to @obj. Additional data passed to
+ * gst_filter_run() are in @data.
+ *
+ * Returns: %TRUE for success.
+ */
+typedef gboolean (*GstFilterFunc) (gpointer obj, gpointer user_data);
+
+GList* gst_filter_run (const GList *list, GstFilterFunc func, gboolean first, gpointer user_data);
+
+G_END_DECLS
+
+#endif /* __GST_FILTER_H_ */
diff --git a/gst/gstformat.c b/gst/gstformat.c
new file mode 100644
index 0000000..24c3d0f
--- /dev/null
+++ b/gst/gstformat.c
@@ -0,0 +1,264 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2000 Wim Taymans <wim.taymans@chello.be>
+ * 2005 Wim Taymans <wim@fluendo.com>
+ *
+ * gstformat.c: GstFormat registration
+ *
+ * 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:gstformat
+ * @short_description: Dynamically register new data formats
+ * @see_also: #GstPad, #GstElement
+ *
+ * GstFormats functions are used to register a new format to the gstreamer
+ * core. Formats can be used to perform seeking or conversions/query
+ * operations.
+ */
+
+
+#include "gst_private.h"
+#include <string.h>
+#include "gstformat.h"
+#include "gstenumtypes.h"
+
+static GStaticMutex mutex = G_STATIC_MUTEX_INIT;
+static GList *_gst_formats = NULL;
+static GHashTable *_nick_to_format = NULL;
+static GHashTable *_format_to_nick = NULL;
+static guint32 _n_values = 1; /* we start from 1 because 0 reserved for UNDEFINED */
+
+static GstFormatDefinition standard_definitions[] = {
+ {GST_FORMAT_DEFAULT, "default", "Default format for the media type", 0},
+ {GST_FORMAT_BYTES, "bytes", "Bytes", 0},
+ {GST_FORMAT_TIME, "time", "Time", 0},
+ {GST_FORMAT_BUFFERS, "buffers", "Buffers", 0},
+ {GST_FORMAT_PERCENT, "percent", "Percent", 0},
+ {GST_FORMAT_UNDEFINED, NULL, NULL, 0}
+};
+
+void
+_priv_gst_format_initialize (void)
+{
+ GstFormatDefinition *standards = standard_definitions;
+
+ g_static_mutex_lock (&mutex);
+ if (_nick_to_format == NULL) {
+ _nick_to_format = g_hash_table_new (g_str_hash, g_str_equal);
+ _format_to_nick = g_hash_table_new (NULL, NULL);
+ }
+
+ while (standards->nick) {
+ standards->quark = g_quark_from_static_string (standards->nick);
+ g_hash_table_insert (_nick_to_format, (gpointer) standards->nick,
+ standards);
+ g_hash_table_insert (_format_to_nick, GINT_TO_POINTER (standards->value),
+ standards);
+
+ _gst_formats = g_list_append (_gst_formats, standards);
+ standards++;
+ _n_values++;
+ }
+ /* getting the type registers the enum */
+ g_type_class_ref (gst_format_get_type ());
+ g_static_mutex_unlock (&mutex);
+}
+
+/**
+ * gst_format_get_name:
+ * @format: a #GstFormat
+ *
+ * Get a printable name for the given format. Do not modify or free.
+ *
+ * Returns: a reference to the static name of the format or NULL if
+ * the format is unknown.
+ */
+const gchar *
+gst_format_get_name (GstFormat format)
+{
+ const GstFormatDefinition *def;
+ const gchar *result;
+
+ if ((def = gst_format_get_details (format)) != NULL)
+ result = def->nick;
+ else
+ result = NULL;
+
+ return result;
+}
+
+/**
+ * gst_format_to_quark:
+ * @format: a #GstFormat
+ *
+ * Get the unique quark for the given format.
+ *
+ * Returns: the quark associated with the format or 0 if the format
+ * is unknown.
+ */
+GQuark
+gst_format_to_quark (GstFormat format)
+{
+ const GstFormatDefinition *def;
+ GQuark result;
+
+ if ((def = gst_format_get_details (format)) != NULL)
+ result = def->quark;
+ else
+ result = 0;
+
+ return result;
+}
+
+/**
+ * gst_format_register:
+ * @nick: The nick of the new format
+ * @description: The description of the new format
+ *
+ * Create a new GstFormat based on the nick or return an
+ * already registered format with that nick.
+ *
+ * Returns: A new GstFormat or an already registered format
+ * with the same nick.
+ *
+ * MT safe.
+ */
+GstFormat
+gst_format_register (const gchar * nick, const gchar * description)
+{
+ GstFormatDefinition *format;
+ GstFormat query;
+
+ g_return_val_if_fail (nick != NULL, GST_FORMAT_UNDEFINED);
+ g_return_val_if_fail (description != NULL, GST_FORMAT_UNDEFINED);
+
+ query = gst_format_get_by_nick (nick);
+ if (query != GST_FORMAT_UNDEFINED)
+ return query;
+
+ g_static_mutex_lock (&mutex);
+ format = g_slice_new (GstFormatDefinition);
+ format->value = (GstFormat) _n_values;
+ format->nick = g_strdup (nick);
+ format->description = g_strdup (description);
+ format->quark = g_quark_from_static_string (format->nick);
+
+ g_hash_table_insert (_nick_to_format, (gpointer) format->nick, format);
+ g_hash_table_insert (_format_to_nick, GINT_TO_POINTER (format->value),
+ format);
+ _gst_formats = g_list_append (_gst_formats, format);
+ _n_values++;
+ g_static_mutex_unlock (&mutex);
+
+ return format->value;
+}
+
+/**
+ * gst_format_get_by_nick:
+ * @nick: The nick of the format
+ *
+ * Return the format registered with the given nick.
+ *
+ * Returns: The format with @nick or GST_FORMAT_UNDEFINED
+ * if the format was not registered.
+ */
+GstFormat
+gst_format_get_by_nick (const gchar * nick)
+{
+ GstFormatDefinition *format;
+
+ g_return_val_if_fail (nick != NULL, GST_FORMAT_UNDEFINED);
+
+ g_static_mutex_lock (&mutex);
+ format = g_hash_table_lookup (_nick_to_format, nick);
+ g_static_mutex_unlock (&mutex);
+
+ if (format != NULL)
+ return format->value;
+ else
+ return GST_FORMAT_UNDEFINED;
+}
+
+/**
+ * gst_formats_contains:
+ * @formats: The format array to search
+ * @format: the format to find
+ *
+ * See if the given format is inside the format array.
+ *
+ * Returns: TRUE if the format is found inside the array
+ */
+gboolean
+gst_formats_contains (const GstFormat * formats, GstFormat format)
+{
+ if (!formats)
+ return FALSE;
+
+ while (*formats) {
+ if (*formats == format)
+ return TRUE;
+
+ formats++;
+ }
+ return FALSE;
+}
+
+
+/**
+ * gst_format_get_details:
+ * @format: The format to get details of
+ *
+ * Get details about the given format.
+ *
+ * Returns: The #GstFormatDefinition for @format or NULL on failure.
+ *
+ * MT safe.
+ */
+const GstFormatDefinition *
+gst_format_get_details (GstFormat format)
+{
+ const GstFormatDefinition *result;
+
+ g_static_mutex_lock (&mutex);
+ result = g_hash_table_lookup (_format_to_nick, GINT_TO_POINTER (format));
+ g_static_mutex_unlock (&mutex);
+
+ return result;
+}
+
+/**
+ * gst_format_iterate_definitions:
+ *
+ * Iterate all the registered formats. The format definition is read
+ * only.
+ *
+ * Returns: (transfer full): a GstIterator of #GstFormatDefinition.
+ */
+GstIterator *
+gst_format_iterate_definitions (void)
+{
+ GstIterator *result;
+
+ g_static_mutex_lock (&mutex);
+ /* FIXME: register a boxed type for GstFormatDefinition */
+ result = gst_iterator_new_list (G_TYPE_POINTER,
+ g_static_mutex_get_mutex (&mutex), &_n_values, &_gst_formats, NULL, NULL);
+ g_static_mutex_unlock (&mutex);
+
+ return result;
+}
diff --git a/gst/gstformat.h b/gst/gstformat.h
new file mode 100644
index 0000000..9f8b362
--- /dev/null
+++ b/gst/gstformat.h
@@ -0,0 +1,113 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2000 Wim Taymans <wim.taymans@chello.be>
+ *
+ * gstformat.h: Header for GstFormat types used in queries and
+ * seeking.
+ *
+ * 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_FORMAT_H__
+#define __GST_FORMAT_H__
+
+#include <glib.h>
+
+#include <gst/gstiterator.h>
+
+G_BEGIN_DECLS
+
+/**
+ * GstFormat:
+ * @GST_FORMAT_UNDEFINED: undefined format
+ * @GST_FORMAT_DEFAULT: the default format of the pad/element. This can be
+ * samples for raw audio, frames/fields for raw video (some, but not all,
+ * elements support this; use @GST_FORMAT_TIME if you don't have a good
+ * reason to query for samples/frames)
+ * @GST_FORMAT_BYTES: bytes
+ * @GST_FORMAT_TIME: time in nanoseconds
+ * @GST_FORMAT_BUFFERS: buffers (few, if any, elements implement this as of
+ * May 2009)
+ * @GST_FORMAT_PERCENT: percentage of stream (few, if any, elements implement
+ * this as of May 2009)
+ *
+ * Standard predefined formats
+ */
+/* NOTE: don't forget to update the table in gstformat.c when changing
+ * this enum */
+typedef enum {
+ GST_FORMAT_UNDEFINED = 0, /* must be first in list */
+ GST_FORMAT_DEFAULT = 1,
+ GST_FORMAT_BYTES = 2,
+ GST_FORMAT_TIME = 3,
+ GST_FORMAT_BUFFERS = 4,
+ GST_FORMAT_PERCENT = 5
+} GstFormat;
+
+/* a percentage is always relative to 1000000 */
+/**
+ * GST_FORMAT_PERCENT_MAX:
+ *
+ * The PERCENT format is between 0 and this value
+ */
+#define GST_FORMAT_PERCENT_MAX G_GINT64_CONSTANT (1000000)
+/**
+ * GST_FORMAT_PERCENT_SCALE:
+ *
+ * The value used to scale down the reported PERCENT format value to
+ * its real value.
+ */
+#define GST_FORMAT_PERCENT_SCALE G_GINT64_CONSTANT (10000)
+
+typedef struct _GstFormatDefinition GstFormatDefinition;
+
+/**
+ * GstFormatDefinition:
+ * @value: The unique id of this format
+ * @nick: A short nick of the format
+ * @description: A longer description of the format
+ * @quark: A quark for the nick
+ *
+ * A format definition
+ */
+struct _GstFormatDefinition
+{
+ GstFormat value;
+ const gchar *nick;
+ const gchar *description;
+ GQuark quark;
+};
+
+const gchar* gst_format_get_name (GstFormat format);
+GQuark gst_format_to_quark (GstFormat format);
+
+/* register a new format */
+GstFormat gst_format_register (const gchar *nick,
+ const gchar *description);
+GstFormat gst_format_get_by_nick (const gchar *nick);
+
+/* check if a format is in an array of formats */
+gboolean gst_formats_contains (const GstFormat *formats, GstFormat format);
+
+/* query for format details */
+const GstFormatDefinition*
+ gst_format_get_details (GstFormat format);
+GstIterator* gst_format_iterate_definitions (void);
+
+G_END_DECLS
+
+#endif /* __GST_FORMAT_H__ */
diff --git a/gst/gstghostpad.c b/gst/gstghostpad.c
new file mode 100644
index 0000000..f739988
--- /dev/null
+++ b/gst/gstghostpad.c
@@ -0,0 +1,1312 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2000 Wim Taymans <wtay@chello.be>
+ * 2005 Andy Wingo <wingo@pobox.com>
+ * 2006 Edward Hervey <bilboed@bilboed.com>
+ *
+ * gstghostpad.c: Proxy pads
+ *
+ * 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:gstghostpad
+ * @short_description: Pseudo link pads
+ * @see_also: #GstPad
+ *
+ * GhostPads are useful when organizing pipelines with #GstBin like elements.
+ * The idea here is to create hierarchical element graphs. The bin element
+ * contains a sub-graph. Now one would like to treat the bin-element like any
+ * other #GstElement. This is where GhostPads come into play. A GhostPad acts as
+ * a proxy for another pad. Thus the bin can have sink and source ghost-pads
+ * that are associated with sink and source pads of the child elements.
+ *
+ * If the target pad is known at creation time, gst_ghost_pad_new() is the
+ * function to use to get a ghost-pad. Otherwise one can use gst_ghost_pad_new_no_target()
+ * to create the ghost-pad and use gst_ghost_pad_set_target() to establish the
+ * association later on.
+ *
+ * Note that GhostPads add overhead to the data processing of a pipeline.
+ *
+ * Last reviewed on 2005-11-18 (0.9.5)
+ */
+
+#include "gst_private.h"
+#include "gstinfo.h"
+
+#include "gstghostpad.h"
+#include "gst.h"
+
+#define GST_CAT_DEFAULT GST_CAT_PADS
+
+#define GST_PROXY_PAD_CAST(obj) ((GstProxyPad *)obj)
+#define GST_PROXY_PAD_PRIVATE(obj) (GST_PROXY_PAD_CAST (obj)->priv)
+#define GST_PROXY_PAD_TARGET(pad) (GST_PROXY_PAD_PRIVATE (pad)->target)
+#define GST_PROXY_PAD_INTERNAL(pad) (GST_PROXY_PAD_PRIVATE (pad)->internal)
+#define GST_PROXY_PAD_RETARGET(pad) (GST_PROXY_PAD_PRIVATE (pad)->retarget)
+#define GST_PROXY_GET_LOCK(pad) (GST_PROXY_PAD_PRIVATE (pad)->proxy_lock)
+#define GST_PROXY_LOCK(pad) (g_mutex_lock (GST_PROXY_GET_LOCK (pad)))
+#define GST_PROXY_UNLOCK(pad) (g_mutex_unlock (GST_PROXY_GET_LOCK (pad)))
+
+struct _GstProxyPadPrivate
+{
+ /* with PROXY_LOCK */
+ GMutex *proxy_lock;
+ GstPad *target;
+ GstPad *internal;
+ gboolean retarget;
+};
+
+G_DEFINE_TYPE (GstProxyPad, gst_proxy_pad, GST_TYPE_PAD);
+
+static GstPad *gst_proxy_pad_get_target (GstPad * pad);
+
+static void gst_proxy_pad_dispose (GObject * object);
+static void gst_proxy_pad_finalize (GObject * object);
+
+/**
+ * gst_proxy_pad_query_type_default:
+ * @pad: a #GstPad.
+ *
+ * Invoke the default query type handler of the proxy pad.
+ *
+ * Returns: (transfer none) (array zero-terminated=1): a zero-terminated array
+ * of #GstQueryType.
+ *
+ * Since: 0.10.35
+ */
+const GstQueryType *
+gst_proxy_pad_query_type_default (GstPad * pad)
+{
+ GstPad *target;
+ const GstQueryType *res = NULL;
+
+ g_return_val_if_fail (GST_IS_PROXY_PAD (pad), NULL);
+
+ if (!(target = gst_proxy_pad_get_target (pad)))
+ goto no_target;
+
+ res = gst_pad_get_query_types (target);
+ gst_object_unref (target);
+
+ return res;
+
+ /* ERRORS */
+no_target:
+ {
+ GST_DEBUG_OBJECT (pad, "no target pad");
+ return FALSE;
+ }
+}
+
+/**
+ * gst_proxy_pad_event_default:
+ * @pad: a #GstPad to push the event to.
+ * @event: (transfer full): the #GstEvent to send to the pad.
+ *
+ * Invoke the default event of the proxy pad.
+ *
+ * Returns: TRUE if the event was handled.
+ *
+ * Since: 0.10.35
+ */
+gboolean
+gst_proxy_pad_event_default (GstPad * pad, GstEvent * event)
+{
+ gboolean res;
+ GstPad *internal;
+
+ g_return_val_if_fail (GST_IS_PROXY_PAD (pad), FALSE);
+ g_return_val_if_fail (GST_IS_EVENT (event), FALSE);
+
+ internal = GST_PROXY_PAD_INTERNAL (pad);
+ res = gst_pad_push_event (internal, event);
+
+ return res;
+}
+
+/**
+ * gst_proxy_pad_query_default:
+ * @pad: a #GstPad to invoke the default query on.
+ * @query: (transfer none): the #GstQuery to perform.
+ *
+ * Invoke the default query function of the proxy pad.
+ *
+ * Returns: TRUE if the query could be performed.
+ *
+ * Since: 0.10.35
+ */
+gboolean
+gst_proxy_pad_query_default (GstPad * pad, GstQuery * query)
+{
+ gboolean res;
+ GstPad *target;
+
+ g_return_val_if_fail (GST_IS_PROXY_PAD (pad), FALSE);
+ g_return_val_if_fail (GST_IS_QUERY (query), FALSE);
+
+ if (!(target = gst_proxy_pad_get_target (pad)))
+ goto no_target;
+
+ res = gst_pad_query (target, query);
+ gst_object_unref (target);
+
+ return res;
+
+ /* ERRORS */
+no_target:
+ {
+ GST_DEBUG_OBJECT (pad, "no target pad");
+ return FALSE;
+ }
+}
+
+/**
+ * gst_proyx_pad_iterate_internal_links_default:
+ * @pad: the #GstPad to get the internal links of.
+ *
+ * Invoke the default iterate internal links function of the proxy pad.
+ *
+ * Returns: a #GstIterator of #GstPad, or NULL if @pad has no parent. Unref each
+ * returned pad with gst_object_unref().
+ *
+ * Since: 0.10.35
+ */
+GstIterator *
+gst_proxy_pad_iterate_internal_links_default (GstPad * pad)
+{
+ GstIterator *res = NULL;
+ GstPad *internal;
+ GValue v = { 0, };
+
+ g_return_val_if_fail (GST_IS_PROXY_PAD (pad), NULL);
+
+ internal = GST_PROXY_PAD_INTERNAL (pad);
+ g_value_init (&v, GST_TYPE_PAD);
+ g_value_set_object (&v, internal);
+ res = gst_iterator_new_single (GST_TYPE_PAD, &v);
+ g_value_unset (&v);
+ gst_object_unref (internal);
+
+ return res;
+}
+
+/**
+ * gst_proxy_pad_chain_default:
+ * @pad: a sink #GstPad, returns GST_FLOW_ERROR if not.
+ * @buffer: (transfer full): the #GstBuffer to send, return GST_FLOW_ERROR
+ * if not.
+ *
+ * Invoke the default chain function of the proxy pad.
+ *
+ * Returns: a #GstFlowReturn from the pad.
+ *
+ * Since: 0.10.35
+ */
+GstFlowReturn
+gst_proxy_pad_chain_default (GstPad * pad, GstBuffer * buffer)
+{
+ GstFlowReturn res;
+ GstPad *internal;
+
+ g_return_val_if_fail (GST_IS_PROXY_PAD (pad), GST_FLOW_ERROR);
+ g_return_val_if_fail (GST_IS_BUFFER (buffer), GST_FLOW_ERROR);
+
+ internal = GST_PROXY_PAD_INTERNAL (pad);
+ res = gst_pad_push (internal, buffer);
+
+ return res;
+}
+
+/**
+ * gst_proxy_pad_chain_list_default:
+ * @pad: a sink #GstPad, returns GST_FLOW_ERROR if not.
+ * @list: (transfer full): the #GstBufferList to send, return GST_FLOW_ERROR
+ * if not.
+ *
+ * Invoke the default chain list function of the proxy pad.
+ *
+ * Returns: a #GstFlowReturn from the pad.
+ *
+ * Since: 0.10.35
+ */
+GstFlowReturn
+gst_proxy_pad_chain_list_default (GstPad * pad, GstBufferList * list)
+{
+ GstFlowReturn res;
+ GstPad *internal;
+
+ g_return_val_if_fail (GST_IS_PROXY_PAD (pad), GST_FLOW_ERROR);
+ g_return_val_if_fail (GST_IS_BUFFER_LIST (list), GST_FLOW_ERROR);
+
+ internal = GST_PROXY_PAD_INTERNAL (pad);
+ res = gst_pad_push_list (internal, list);
+
+ return res;
+}
+
+/**
+ * gst_proxy_pad_get_range_default:
+ * @pad: a src #GstPad, returns #GST_FLOW_ERROR if not.
+ * @offset: The start offset of the buffer
+ * @size: The length of the buffer
+ * @buffer: (out callee-allocates): a pointer to hold the #GstBuffer,
+ * returns #GST_FLOW_ERROR if %NULL.
+ *
+ * Invoke the default getrange function of the proxy pad.
+ *
+ * Returns: a #GstFlowReturn from the pad.
+ *
+ * Since: 0.10.35
+ */
+GstFlowReturn
+gst_proxy_pad_getrange_default (GstPad * pad, guint64 offset, guint size,
+ GstBuffer ** buffer)
+{
+ GstFlowReturn res;
+ GstPad *internal;
+
+ g_return_val_if_fail (GST_IS_PROXY_PAD (pad), GST_FLOW_ERROR);
+ g_return_val_if_fail (buffer != NULL, GST_FLOW_ERROR);
+
+ internal = GST_PROXY_PAD_INTERNAL (pad);
+ res = gst_pad_pull_range (internal, offset, size, buffer);
+
+ return res;
+}
+
+/**
+ * gst_proxy_pad_getcaps_default:
+ * @pad: a #GstPad to get the capabilities of.
+ * @filter: a #GstCaps filter.
+ *
+ * Invoke the default getcaps function of the proxy pad.
+ *
+ * Returns: (transfer full): the caps of the pad with incremented ref-count
+ *
+ * Since: 0.10.35
+ */
+GstCaps *
+gst_proxy_pad_getcaps_default (GstPad * pad, GstCaps * filter)
+{
+ GstPad *target;
+ GstCaps *res;
+ GstPadTemplate *templ;
+
+ g_return_val_if_fail (GST_IS_PROXY_PAD (pad), NULL);
+
+ templ = GST_PAD_PAD_TEMPLATE (pad);
+ target = gst_proxy_pad_get_target (pad);
+ if (target) {
+ /* if we have a real target, proxy the call */
+ res = gst_pad_get_caps (target, filter);
+
+ GST_DEBUG_OBJECT (pad, "get caps of target %s:%s : %" GST_PTR_FORMAT,
+ GST_DEBUG_PAD_NAME (target), res);
+
+ gst_object_unref (target);
+
+ /* filter against the template */
+ if (templ && res) {
+ GstCaps *filt, *tmp;
+
+ filt = GST_PAD_TEMPLATE_CAPS (templ);
+ if (filt) {
+ tmp = gst_caps_intersect_full (res, filt, GST_CAPS_INTERSECT_FIRST);
+ gst_caps_unref (res);
+ res = tmp;
+ GST_DEBUG_OBJECT (pad,
+ "filtered against template gives %" GST_PTR_FORMAT, res);
+ }
+ }
+ } else {
+ /* else, if we have a template, use its caps. */
+ if (templ) {
+ res = GST_PAD_TEMPLATE_CAPS (templ);
+ GST_DEBUG_OBJECT (pad,
+ "using pad template %p with caps %p %" GST_PTR_FORMAT, templ, res,
+ res);
+ res = gst_caps_ref (res);
+
+ if (filter) {
+ GstCaps *intersection =
+ gst_caps_intersect_full (filter, res, GST_CAPS_INTERSECT_FIRST);
+
+ gst_caps_unref (res);
+ res = intersection;
+ }
+
+ goto done;
+ }
+
+ /* last resort, any caps */
+ GST_DEBUG_OBJECT (pad, "pad has no template, returning ANY");
+ res = gst_caps_new_any ();
+ }
+
+done:
+ return res;
+}
+
+/**
+ * gst_proxy_pad_acceptcaps_default:
+ * @pad: a #GstPad to check
+ * @caps: a #GstCaps to check on the pad
+ *
+ * Invoke the default acceptcaps function of the proxy pad.
+ *
+ * Returns: TRUE if the pad can accept the caps.
+ *
+ * Since: 0.10.35
+ */
+gboolean
+gst_proxy_pad_acceptcaps_default (GstPad * pad, GstCaps * caps)
+{
+ GstPad *target;
+ gboolean res;
+
+ g_return_val_if_fail (GST_IS_PROXY_PAD (pad), FALSE);
+ g_return_val_if_fail (caps == NULL || GST_IS_CAPS (caps), FALSE);
+
+ target = gst_proxy_pad_get_target (pad);
+ if (target) {
+ res = gst_pad_accept_caps (target, caps);
+ gst_object_unref (target);
+ } else {
+ GST_DEBUG_OBJECT (pad, "no target");
+ /* We don't have a target, we return TRUE and we assume that any future
+ * target will be able to deal with any configured caps. */
+ res = TRUE;
+ }
+
+ return res;
+}
+
+/**
+ * gst_proxy_pad_fixatecaps_default:
+ * @pad: a #GstPad to fixate
+ * @caps: the #GstCaps to fixate
+ *
+ * Invoke the default fixatecaps function of the proxy pad.
+ *
+ * Since: 0.10.35
+ */
+void
+gst_proxy_pad_fixatecaps_default (GstPad * pad, GstCaps * caps)
+{
+ GstPad *target;
+
+ g_return_if_fail (GST_IS_PROXY_PAD (pad));
+ g_return_if_fail (GST_IS_CAPS (caps));
+
+ if (!(target = gst_proxy_pad_get_target (pad)))
+ goto no_target;
+
+ gst_pad_fixate_caps (target, caps);
+ gst_object_unref (target);
+
+ return;
+
+ /* ERRORS */
+no_target:
+ {
+ GST_DEBUG_OBJECT (pad, "no target");
+ return;
+ }
+}
+
+static gboolean
+gst_proxy_pad_set_target_unlocked (GstPad * pad, GstPad * target)
+{
+ GstPad *oldtarget;
+
+ if (target) {
+ GST_LOG_OBJECT (pad, "setting target %s:%s", GST_DEBUG_PAD_NAME (target));
+
+ if (G_UNLIKELY (GST_PAD_DIRECTION (pad) != GST_PAD_DIRECTION (target)))
+ goto wrong_direction;
+ } else
+ GST_LOG_OBJECT (pad, "clearing target");
+
+ /* clear old target */
+ if ((oldtarget = GST_PROXY_PAD_TARGET (pad)))
+ gst_object_unref (oldtarget);
+
+ /* set and ref new target if any */
+ if (target)
+ GST_PROXY_PAD_TARGET (pad) = gst_object_ref (target);
+ else
+ GST_PROXY_PAD_TARGET (pad) = NULL;
+
+ return TRUE;
+
+ /* ERRORS */
+wrong_direction:
+ {
+ GST_ERROR_OBJECT (pad,
+ "target pad doesn't have the same direction as ourself");
+ return FALSE;
+ }
+}
+
+static gboolean
+gst_proxy_pad_set_target (GstPad * pad, GstPad * target)
+{
+ gboolean result;
+
+ GST_PROXY_LOCK (pad);
+ result = gst_proxy_pad_set_target_unlocked (pad, target);
+ GST_PROXY_UNLOCK (pad);
+
+ return result;
+}
+
+static GstPad *
+gst_proxy_pad_get_target (GstPad * pad)
+{
+ GstPad *target;
+
+ GST_PROXY_LOCK (pad);
+ target = GST_PROXY_PAD_TARGET (pad);
+ if (target)
+ gst_object_ref (target);
+ GST_PROXY_UNLOCK (pad);
+
+ return target;
+}
+
+/**
+ * gst_proxy_pad_get_internal:
+ * @pad: the #GstProxyPad
+ *
+ * Get the internal pad of @pad. Unref target pad after usage.
+ *
+ * The internal pad of a #GstGhostPad is the internally used
+ * pad of opposite direction, which is used to link to the target.
+ *
+ * Returns: (transfer full): the target #GstProxyPad, can be NULL.
+ * Unref target pad after usage.
+ *
+ * Since: 0.10.35
+ */
+GstProxyPad *
+gst_proxy_pad_get_internal (GstProxyPad * pad)
+{
+ GstPad *internal;
+
+ g_return_val_if_fail (GST_IS_PROXY_PAD (pad), NULL);
+
+ GST_PROXY_LOCK (pad);
+ internal = GST_PROXY_PAD_INTERNAL (pad);
+ if (internal)
+ gst_object_ref (internal);
+ GST_PROXY_UNLOCK (pad);
+
+ return GST_PROXY_PAD_CAST (internal);
+}
+
+/**
+ * gst_proxy_pad_unlink_default:
+ * @pad: a #GstPad to unlink
+ *
+ * Invoke the default unlink function of the proxy pad.
+ *
+ * Since: 0.10.35
+ */
+void
+gst_proxy_pad_unlink_default (GstPad * pad)
+{
+ GstPad *internal;
+
+ /* don't do anything if this unlink resulted from retargeting the pad
+ * controlled by the ghostpad. We only want to invalidate the target pad when
+ * the element suddenly unlinked with our internal pad. */
+ if (GST_PROXY_PAD_RETARGET (pad))
+ return;
+
+ internal = GST_PROXY_PAD_INTERNAL (pad);
+
+ GST_DEBUG_OBJECT (pad, "pad is unlinked");
+
+ gst_proxy_pad_set_target (internal, NULL);
+}
+
+static void
+gst_proxy_pad_class_init (GstProxyPadClass * klass)
+{
+ GObjectClass *gobject_class = (GObjectClass *) klass;
+
+ g_type_class_add_private (klass, sizeof (GstProxyPadPrivate));
+
+ gobject_class->dispose = gst_proxy_pad_dispose;
+ gobject_class->finalize = gst_proxy_pad_finalize;
+
+ /* Register common function pointer descriptions */
+ GST_DEBUG_REGISTER_FUNCPTR (gst_proxy_pad_query_type_default);
+ GST_DEBUG_REGISTER_FUNCPTR (gst_proxy_pad_event_default);
+ GST_DEBUG_REGISTER_FUNCPTR (gst_proxy_pad_query_default);
+ GST_DEBUG_REGISTER_FUNCPTR (gst_proxy_pad_iterate_internal_links_default);
+ GST_DEBUG_REGISTER_FUNCPTR (gst_proxy_pad_getcaps_default);
+ GST_DEBUG_REGISTER_FUNCPTR (gst_proxy_pad_acceptcaps_default);
+ GST_DEBUG_REGISTER_FUNCPTR (gst_proxy_pad_fixatecaps_default);
+ GST_DEBUG_REGISTER_FUNCPTR (gst_proxy_pad_unlink_default);
+ GST_DEBUG_REGISTER_FUNCPTR (gst_proxy_pad_chain_default);
+ GST_DEBUG_REGISTER_FUNCPTR (gst_proxy_pad_chain_list_default);
+ GST_DEBUG_REGISTER_FUNCPTR (gst_proxy_pad_getrange_default);
+}
+
+static void
+gst_proxy_pad_dispose (GObject * object)
+{
+ GstPad *pad = GST_PAD (object);
+ GstPad **target_p;
+
+ GST_PROXY_LOCK (pad);
+ /* remove and unref the target */
+ target_p = &GST_PROXY_PAD_TARGET (pad);
+ gst_object_replace ((GstObject **) target_p, NULL);
+ /* The internal is only cleared by GstGhostPad::dispose, since it is the
+ * parent of non-ghost GstProxyPad and owns the refcount on the internal.
+ */
+ GST_PROXY_UNLOCK (pad);
+
+ G_OBJECT_CLASS (gst_proxy_pad_parent_class)->dispose (object);
+}
+
+static void
+gst_proxy_pad_finalize (GObject * object)
+{
+ GstProxyPad *pad = GST_PROXY_PAD (object);
+
+ g_mutex_free (GST_PROXY_GET_LOCK (pad));
+ GST_PROXY_GET_LOCK (pad) = NULL;
+
+ G_OBJECT_CLASS (gst_proxy_pad_parent_class)->finalize (object);
+}
+
+static void
+gst_proxy_pad_init (GstProxyPad * ppad)
+{
+ GstPad *pad = (GstPad *) ppad;
+
+ GST_PROXY_PAD_PRIVATE (ppad) = G_TYPE_INSTANCE_GET_PRIVATE (ppad,
+ GST_TYPE_PROXY_PAD, GstProxyPadPrivate);
+ GST_PROXY_GET_LOCK (pad) = g_mutex_new ();
+
+ gst_pad_set_query_type_function (pad, gst_proxy_pad_query_type_default);
+ gst_pad_set_event_function (pad, gst_proxy_pad_event_default);
+ gst_pad_set_query_function (pad, gst_proxy_pad_query_default);
+ gst_pad_set_iterate_internal_links_function (pad,
+ gst_proxy_pad_iterate_internal_links_default);
+
+ gst_pad_set_getcaps_function (pad, gst_proxy_pad_getcaps_default);
+ gst_pad_set_acceptcaps_function (pad, gst_proxy_pad_acceptcaps_default);
+ gst_pad_set_fixatecaps_function (pad, gst_proxy_pad_fixatecaps_default);
+ gst_pad_set_unlink_function (pad, gst_proxy_pad_unlink_default);
+}
+
+
+/***********************************************************************
+ * Ghost pads, implemented as a pair of proxy pads (sort of)
+ */
+
+
+#define GST_GHOST_PAD_PRIVATE(obj) (GST_GHOST_PAD_CAST (obj)->priv)
+
+struct _GstGhostPadPrivate
+{
+ /* with PROXY_LOCK */
+ gboolean constructed;
+};
+
+G_DEFINE_TYPE (GstGhostPad, gst_ghost_pad, GST_TYPE_PROXY_PAD);
+
+static void gst_ghost_pad_dispose (GObject * object);
+
+/**
+ * gst_ghost_pad_internal_activate_push_default:
+ * @pad: the #GstPad to activate or deactivate.
+ * @active: whether the pad should be active or not.
+ *
+ * Invoke the default activate push function of a proxy pad that is
+ * owned by a ghost pad.
+ *
+ * Returns: %TRUE if the operation was successful.
+ *
+ * Since: 0.10.35
+ */
+gboolean
+gst_ghost_pad_internal_activate_push_default (GstPad * pad, gboolean active)
+{
+ gboolean ret;
+ GstPad *other;
+
+ g_return_val_if_fail (GST_IS_PROXY_PAD (pad), FALSE);
+
+ GST_LOG_OBJECT (pad, "%sactivate push on %s:%s, we're ok",
+ (active ? "" : "de"), GST_DEBUG_PAD_NAME (pad));
+
+ /* in both cases (SRC and SINK) we activate just the internal pad. The targets
+ * will be activated later (or already in case of a ghost sinkpad). */
+ other = GST_PROXY_PAD_INTERNAL (pad);
+ ret = gst_pad_activate_push (other, active);
+
+ return ret;
+}
+
+/**
+ * gst_ghost_pad_internal_activate_pull_default:
+ * @pad: the #GstPad to activate or deactivate.
+ * @active: whether the pad should be active or not.
+ *
+ * Invoke the default activate pull function of a proxy pad that is
+ * owned by a ghost pad.
+ *
+ * Returns: %TRUE if the operation was successful.
+ *
+ * Since: 0.10.35
+ */
+gboolean
+gst_ghost_pad_internal_activate_pull_default (GstPad * pad, gboolean active)
+{
+ gboolean ret;
+ GstPad *other;
+
+ g_return_val_if_fail (GST_IS_PROXY_PAD (pad), FALSE);
+
+ GST_LOG_OBJECT (pad, "%sactivate pull on %s:%s", (active ? "" : "de"),
+ GST_DEBUG_PAD_NAME (pad));
+
+ if (GST_PAD_DIRECTION (pad) == GST_PAD_SRC) {
+ /* we are activated in pull mode by our peer element, which is a sinkpad
+ * that wants to operate in pull mode. This activation has to propagate
+ * upstream through the pipeline. We call the internal activation function,
+ * which will trigger gst_ghost_pad_activate_pull_default, which propagates even
+ * further upstream */
+ GST_LOG_OBJECT (pad, "pad is src, activate internal");
+ other = GST_PROXY_PAD_INTERNAL (pad);
+ ret = gst_pad_activate_pull (other, active);
+ } else if (G_LIKELY ((other = gst_pad_get_peer (pad)))) {
+ /* We are SINK, the ghostpad is SRC, we propagate the activation upstream
+ * since we hold a pointer to the upstream peer. */
+ GST_LOG_OBJECT (pad, "activating peer");
+ ret = gst_pad_activate_pull (other, active);
+ gst_object_unref (other);
+ } else {
+ /* this is failure, we can't activate pull if there is no peer */
+ GST_LOG_OBJECT (pad, "not src and no peer, failing");
+ ret = FALSE;
+ }
+
+ return ret;
+}
+
+/**
+ * gst_ghost_pad_activate_push_default:
+ * @pad: the #GstPad to activate or deactivate.
+ * @active: whether the pad should be active or not.
+ *
+ * Invoke the default activate push function of a ghost pad.
+ *
+ * Returns: %TRUE if the operation was successful.
+ *
+ * Since: 0.10.35
+ */
+gboolean
+gst_ghost_pad_activate_push_default (GstPad * pad, gboolean active)
+{
+ gboolean ret;
+ GstPad *other;
+
+ g_return_val_if_fail (GST_IS_GHOST_PAD (pad), FALSE);
+
+ GST_LOG_OBJECT (pad, "%sactivate push on %s:%s, proxy internal",
+ (active ? "" : "de"), GST_DEBUG_PAD_NAME (pad));
+
+ /* just activate the internal pad */
+ other = GST_PROXY_PAD_INTERNAL (pad);
+ ret = gst_pad_activate_push (other, active);
+
+ return ret;
+}
+
+/**
+ * gst_ghost_pad_activate_pull_default:
+ * @pad: the #GstPad to activate or deactivate.
+ * @active: whether the pad should be active or not.
+ *
+ * Invoke the default activate pull function of a ghost pad.
+ *
+ * Returns: %TRUE if the operation was successful.
+ *
+ * Since: 0.10.35
+ */
+gboolean
+gst_ghost_pad_activate_pull_default (GstPad * pad, gboolean active)
+{
+ gboolean ret;
+ GstPad *other;
+
+ g_return_val_if_fail (GST_IS_GHOST_PAD (pad), FALSE);
+
+ GST_LOG_OBJECT (pad, "%sactivate pull on %s:%s", (active ? "" : "de"),
+ GST_DEBUG_PAD_NAME (pad));
+
+ if (GST_PAD_DIRECTION (pad) == GST_PAD_SRC) {
+ /* the ghostpad is SRC and activated in pull mode by its peer, call the
+ * activation function of the internal pad to propagate the activation
+ * upstream */
+ GST_LOG_OBJECT (pad, "pad is src, activate internal");
+ other = GST_PROXY_PAD_INTERNAL (pad);
+ ret = gst_pad_activate_pull (other, active);
+ } else if (G_LIKELY ((other = gst_pad_get_peer (pad)))) {
+ /* We are SINK and activated by the internal pad, propagate activation
+ * upstream because we hold a ref to the upstream peer */
+ GST_LOG_OBJECT (pad, "activating peer");
+ ret = gst_pad_activate_pull (other, active);
+ gst_object_unref (other);
+ } else {
+ /* no peer, we fail */
+ GST_LOG_OBJECT (pad, "pad not src and no peer, failing");
+ ret = FALSE;
+ }
+
+ return ret;
+}
+
+/**
+ * gst_ghost_pad_link_default:
+ * @pad: the #GstPad to link.
+ * @peer: the #GstPad peer
+ *
+ * Invoke the default link function of a ghost pad.
+ *
+ * Returns: #GstPadLinkReturn of the operation
+ *
+ * Since: 0.10.35
+ */
+GstPadLinkReturn
+gst_ghost_pad_link_default (GstPad * pad, GstPad * peer)
+{
+ GstPadLinkReturn ret;
+ GstPad *internal;
+
+ g_return_val_if_fail (GST_IS_GHOST_PAD (pad), GST_PAD_LINK_REFUSED);
+ g_return_val_if_fail (GST_IS_PAD (peer), GST_PAD_LINK_REFUSED);
+
+ GST_DEBUG_OBJECT (pad, "linking ghostpad");
+
+ internal = GST_PROXY_PAD_INTERNAL (pad);
+ if (!gst_proxy_pad_set_target (internal, peer))
+ goto target_failed;
+
+ ret = GST_PAD_LINK_OK;
+ /* if we are a source pad, we should call the peer link function
+ * if the peer has one, see design docs. */
+ if (GST_PAD_IS_SRC (pad)) {
+ if (GST_PAD_LINKFUNC (peer)) {
+ ret = GST_PAD_LINKFUNC (peer) (peer, pad);
+ if (ret != GST_PAD_LINK_OK)
+ goto link_failed;
+ }
+ }
+ return ret;
+
+ /* ERRORS */
+target_failed:
+ {
+ GST_DEBUG_OBJECT (pad, "setting target failed");
+ return GST_PAD_LINK_REFUSED;
+ }
+link_failed:
+ {
+ GST_DEBUG_OBJECT (pad, "linking failed");
+ /* clear target again */
+ gst_proxy_pad_set_target (internal, NULL);
+ return ret;
+ }
+}
+
+/**
+ * gst_ghost_pad_unlink_default:
+ * @pad: the #GstPad to link.
+ *
+ * Invoke the default unlink function of a ghost pad.
+ *
+ * Since: 0.10.35
+ */
+void
+gst_ghost_pad_unlink_default (GstPad * pad)
+{
+ GstPad *internal;
+
+ g_return_if_fail (GST_IS_GHOST_PAD (pad));
+
+ internal = GST_PROXY_PAD_INTERNAL (pad);
+
+ GST_DEBUG_OBJECT (pad, "unlinking ghostpad");
+
+ /* The target of the internal pad is no longer valid */
+ gst_proxy_pad_set_target (internal, NULL);
+}
+
+static void
+gst_ghost_pad_class_init (GstGhostPadClass * klass)
+{
+ GObjectClass *gobject_class = (GObjectClass *) klass;
+
+ g_type_class_add_private (klass, sizeof (GstGhostPadPrivate));
+
+ gobject_class->dispose = gst_ghost_pad_dispose;
+
+ GST_DEBUG_REGISTER_FUNCPTR (gst_ghost_pad_activate_pull_default);
+ GST_DEBUG_REGISTER_FUNCPTR (gst_ghost_pad_activate_push_default);
+ GST_DEBUG_REGISTER_FUNCPTR (gst_ghost_pad_link_default);
+}
+
+static void
+gst_ghost_pad_init (GstGhostPad * pad)
+{
+ GST_GHOST_PAD_PRIVATE (pad) = G_TYPE_INSTANCE_GET_PRIVATE (pad,
+ GST_TYPE_GHOST_PAD, GstGhostPadPrivate);
+
+ gst_pad_set_activatepull_function (GST_PAD_CAST (pad),
+ gst_ghost_pad_activate_pull_default);
+ gst_pad_set_activatepush_function (GST_PAD_CAST (pad),
+ gst_ghost_pad_activate_push_default);
+}
+
+static void
+gst_ghost_pad_dispose (GObject * object)
+{
+ GstPad *pad;
+ GstPad *internal;
+ GstPad *peer;
+
+ pad = GST_PAD (object);
+
+ GST_DEBUG_OBJECT (pad, "dispose");
+
+ gst_ghost_pad_set_target (GST_GHOST_PAD (pad), NULL);
+
+ /* Unlink here so that gst_pad_dispose doesn't. That would lead to a call to
+ * gst_ghost_pad_unlink_default when the ghost pad is in an inconsistent state */
+ peer = gst_pad_get_peer (pad);
+ if (peer) {
+ if (GST_PAD_IS_SRC (pad))
+ gst_pad_unlink (pad, peer);
+ else
+ gst_pad_unlink (peer, pad);
+
+ gst_object_unref (peer);
+ }
+
+ GST_PROXY_LOCK (pad);
+ internal = GST_PROXY_PAD_INTERNAL (pad);
+
+ gst_pad_set_activatepull_function (internal, NULL);
+ gst_pad_set_activatepush_function (internal, NULL);
+
+ /* disposes of the internal pad, since the ghostpad is the only possible object
+ * that has a refcount on the internal pad. */
+ gst_object_unparent (GST_OBJECT_CAST (internal));
+ GST_PROXY_PAD_INTERNAL (pad) = NULL;
+
+ GST_PROXY_UNLOCK (pad);
+
+ G_OBJECT_CLASS (gst_ghost_pad_parent_class)->dispose (object);
+}
+
+/**
+ * gst_ghost_pad_construct:
+ * @gpad: the newly allocated ghost pad
+ *
+ * Finish initialization of a newly allocated ghost pad.
+ *
+ * This function is most useful in language bindings and when subclassing
+ * #GstGhostPad; plugin and application developers normally will not call this
+ * function. Call this function directly after a call to g_object_new
+ * (GST_TYPE_GHOST_PAD, "direction", @dir, ..., NULL).
+ *
+ * Returns: %TRUE if the construction succeeds, %FALSE otherwise.
+ *
+ * Since: 0.10.22
+ */
+gboolean
+gst_ghost_pad_construct (GstGhostPad * gpad)
+{
+ GstPadDirection dir, otherdir;
+ GstPadTemplate *templ;
+ GstPad *pad, *internal;
+
+ g_return_val_if_fail (GST_IS_GHOST_PAD (gpad), FALSE);
+ g_return_val_if_fail (GST_GHOST_PAD_PRIVATE (gpad)->constructed == FALSE,
+ FALSE);
+
+ g_object_get (gpad, "direction", &dir, "template", &templ, NULL);
+
+ g_return_val_if_fail (dir != GST_PAD_UNKNOWN, FALSE);
+
+ pad = GST_PAD (gpad);
+
+ /* Set directional padfunctions for ghostpad */
+ if (dir == GST_PAD_SINK) {
+ gst_pad_set_chain_function (pad, gst_proxy_pad_chain_default);
+ gst_pad_set_chain_list_function (pad, gst_proxy_pad_chain_list_default);
+ } else {
+ gst_pad_set_getrange_function (pad, gst_proxy_pad_getrange_default);
+ }
+
+ /* link/unlink functions */
+ gst_pad_set_link_function (pad, gst_ghost_pad_link_default);
+ gst_pad_set_unlink_function (pad, gst_ghost_pad_unlink_default);
+
+ /* INTERNAL PAD, it always exists and is child of the ghostpad */
+ otherdir = (dir == GST_PAD_SRC) ? GST_PAD_SINK : GST_PAD_SRC;
+ if (templ) {
+ internal =
+ g_object_new (GST_TYPE_PROXY_PAD, "name", NULL,
+ "direction", otherdir, "template", templ, NULL);
+ /* release ref obtained via g_object_get */
+ gst_object_unref (templ);
+ } else {
+ internal =
+ g_object_new (GST_TYPE_PROXY_PAD, "name", NULL,
+ "direction", otherdir, NULL);
+ }
+ GST_PAD_UNSET_FLUSHING (internal);
+
+ /* Set directional padfunctions for internal pad */
+ if (dir == GST_PAD_SRC) {
+ gst_pad_set_chain_function (internal, gst_proxy_pad_chain_default);
+ gst_pad_set_chain_list_function (internal,
+ gst_proxy_pad_chain_list_default);
+ } else {
+ gst_pad_set_getrange_function (internal, gst_proxy_pad_getrange_default);
+ }
+
+ GST_PROXY_LOCK (pad);
+
+ /* now make the ghostpad a parent of the internal pad */
+ if (!gst_object_set_parent (GST_OBJECT_CAST (internal),
+ GST_OBJECT_CAST (pad)))
+ goto parent_failed;
+
+ /* The ghostpad is the parent of the internal pad and is the only object that
+ * can have a refcount on the internal pad.
+ * At this point, the GstGhostPad has a refcount of 1, and the internal pad has
+ * a refcount of 1.
+ * When the refcount of the GstGhostPad drops to 0, the ghostpad will dispose
+ * its refcount on the internal pad in the dispose method by un-parenting it.
+ * This is why we don't take extra refcounts in the assignments below
+ */
+ GST_PROXY_PAD_INTERNAL (pad) = internal;
+ GST_PROXY_PAD_INTERNAL (internal) = pad;
+
+ /* special activation functions for the internal pad */
+ gst_pad_set_activatepull_function (internal,
+ gst_ghost_pad_internal_activate_pull_default);
+ gst_pad_set_activatepush_function (internal,
+ gst_ghost_pad_internal_activate_push_default);
+
+ GST_PROXY_UNLOCK (pad);
+
+ GST_GHOST_PAD_PRIVATE (gpad)->constructed = TRUE;
+ return TRUE;
+
+ /* ERRORS */
+parent_failed:
+ {
+ GST_WARNING_OBJECT (gpad, "Could not set internal pad %s:%s",
+ GST_DEBUG_PAD_NAME (internal));
+ g_critical ("Could not set internal pad %s:%s",
+ GST_DEBUG_PAD_NAME (internal));
+ GST_PROXY_UNLOCK (pad);
+ gst_object_unref (internal);
+ return FALSE;
+ }
+}
+
+static GstPad *
+gst_ghost_pad_new_full (const gchar * name, GstPadDirection dir,
+ GstPadTemplate * templ)
+{
+ GstGhostPad *ret;
+
+ g_return_val_if_fail (dir != GST_PAD_UNKNOWN, NULL);
+
+ /* OBJECT CREATION */
+ if (templ) {
+ ret = g_object_new (GST_TYPE_GHOST_PAD, "name", name,
+ "direction", dir, "template", templ, NULL);
+ } else {
+ ret = g_object_new (GST_TYPE_GHOST_PAD, "name", name,
+ "direction", dir, NULL);
+ }
+
+ if (!gst_ghost_pad_construct (ret))
+ goto construct_failed;
+
+ return GST_PAD_CAST (ret);
+
+construct_failed:
+ /* already logged */
+ gst_object_unref (ret);
+ return NULL;
+}
+
+/**
+ * gst_ghost_pad_new_no_target:
+ * @name: (allow-none): the name of the new pad, or NULL to assign a default name.
+ * @dir: the direction of the ghostpad
+ *
+ * Create a new ghostpad without a target with the given direction.
+ * A target can be set on the ghostpad later with the
+ * gst_ghost_pad_set_target() function.
+ *
+ * The created ghostpad will not have a padtemplate.
+ *
+ * Returns: (transfer full): a new #GstPad, or NULL in case of an error.
+ */
+GstPad *
+gst_ghost_pad_new_no_target (const gchar * name, GstPadDirection dir)
+{
+ GstPad *ret;
+
+ g_return_val_if_fail (dir != GST_PAD_UNKNOWN, NULL);
+
+ GST_LOG ("name:%s, direction:%d", GST_STR_NULL (name), dir);
+
+ ret = gst_ghost_pad_new_full (name, dir, NULL);
+
+ return ret;
+}
+
+/**
+ * gst_ghost_pad_new:
+ * @name: (allow-none): the name of the new pad, or NULL to assign a default name
+ * @target: (transfer none): the pad to ghost.
+ *
+ * Create a new ghostpad with @target as the target. The direction will be taken
+ * from the target pad. @target must be unlinked.
+ *
+ * Will ref the target.
+ *
+ * Returns: (transfer full): a new #GstPad, or NULL in case of an error.
+ */
+GstPad *
+gst_ghost_pad_new (const gchar * name, GstPad * target)
+{
+ GstPad *ret;
+
+ g_return_val_if_fail (GST_IS_PAD (target), NULL);
+ g_return_val_if_fail (!gst_pad_is_linked (target), NULL);
+
+ GST_LOG ("name:%s, target:%s:%s", GST_STR_NULL (name),
+ GST_DEBUG_PAD_NAME (target));
+
+ if ((ret = gst_ghost_pad_new_no_target (name, GST_PAD_DIRECTION (target))))
+ if (!gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (ret), target))
+ goto set_target_failed;
+
+ return ret;
+
+ /* ERRORS */
+set_target_failed:
+ {
+ GST_WARNING_OBJECT (ret, "failed to set target %s:%s",
+ GST_DEBUG_PAD_NAME (target));
+ gst_object_unref (ret);
+ return NULL;
+ }
+}
+
+/**
+ * gst_ghost_pad_new_from_template:
+ * @name: (allow-none): the name of the new pad, or NULL to assign a default name.
+ * @target: (transfer none): the pad to ghost.
+ * @templ: (transfer none): the #GstPadTemplate to use on the ghostpad.
+ *
+ * Create a new ghostpad with @target as the target. The direction will be taken
+ * from the target pad. The template used on the ghostpad will be @template.
+ *
+ * Will ref the target.
+ *
+ * Returns: (transfer full): a new #GstPad, or NULL in case of an error.
+ *
+ * Since: 0.10.10
+ */
+
+GstPad *
+gst_ghost_pad_new_from_template (const gchar * name, GstPad * target,
+ GstPadTemplate * templ)
+{
+ GstPad *ret;
+
+ g_return_val_if_fail (GST_IS_PAD (target), NULL);
+ g_return_val_if_fail (!gst_pad_is_linked (target), NULL);
+ g_return_val_if_fail (templ != NULL, NULL);
+ g_return_val_if_fail (GST_PAD_TEMPLATE_DIRECTION (templ) ==
+ GST_PAD_DIRECTION (target), NULL);
+
+ GST_LOG ("name:%s, target:%s:%s, templ:%p", GST_STR_NULL (name),
+ GST_DEBUG_PAD_NAME (target), templ);
+
+ if ((ret = gst_ghost_pad_new_full (name, GST_PAD_DIRECTION (target), templ)))
+ if (!gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (ret), target))
+ goto set_target_failed;
+
+ return ret;
+
+ /* ERRORS */
+set_target_failed:
+ {
+ GST_WARNING_OBJECT (ret, "failed to set target %s:%s",
+ GST_DEBUG_PAD_NAME (target));
+ gst_object_unref (ret);
+ return NULL;
+ }
+}
+
+/**
+ * gst_ghost_pad_new_no_target_from_template:
+ * @name: (allow-none): the name of the new pad, or NULL to assign a default name
+ * @templ: (transfer none): the #GstPadTemplate to create the ghostpad from.
+ *
+ * Create a new ghostpad based on @templ, without setting a target. The
+ * direction will be taken from the @templ.
+ *
+ * Returns: (transfer full): a new #GstPad, or NULL in case of an error.
+ *
+ * Since: 0.10.10
+ */
+GstPad *
+gst_ghost_pad_new_no_target_from_template (const gchar * name,
+ GstPadTemplate * templ)
+{
+ GstPad *ret;
+
+ g_return_val_if_fail (templ != NULL, NULL);
+
+ ret =
+ gst_ghost_pad_new_full (name, GST_PAD_TEMPLATE_DIRECTION (templ), templ);
+
+ return ret;
+}
+
+/**
+ * gst_ghost_pad_get_target:
+ * @gpad: the #GstGhostPad
+ *
+ * Get the target pad of @gpad. Unref target pad after usage.
+ *
+ * Returns: (transfer full): the target #GstPad, can be NULL if the ghostpad
+ * has no target set. Unref target pad after usage.
+ */
+GstPad *
+gst_ghost_pad_get_target (GstGhostPad * gpad)
+{
+ GstPad *ret;
+
+ g_return_val_if_fail (GST_IS_GHOST_PAD (gpad), NULL);
+
+ ret = gst_proxy_pad_get_target (GST_PAD_CAST (gpad));
+
+ GST_DEBUG_OBJECT (gpad, "get target %s:%s", GST_DEBUG_PAD_NAME (ret));
+
+ return ret;
+}
+
+/**
+ * gst_ghost_pad_set_target:
+ * @gpad: the #GstGhostPad
+ * @newtarget: (transfer none) (allow-none): the new pad target
+ *
+ * Set the new target of the ghostpad @gpad. Any existing target
+ * is unlinked and links to the new target are established. if @newtarget is
+ * NULL the target will be cleared.
+ *
+ * Returns: (transfer full): TRUE if the new target could be set. This function
+ * can return FALSE when the internal pads could not be linked.
+ */
+gboolean
+gst_ghost_pad_set_target (GstGhostPad * gpad, GstPad * newtarget)
+{
+ GstPad *internal;
+ GstPad *oldtarget;
+ gboolean result;
+ GstPadLinkReturn lret;
+
+ g_return_val_if_fail (GST_IS_GHOST_PAD (gpad), FALSE);
+ g_return_val_if_fail (GST_PAD_CAST (gpad) != newtarget, FALSE);
+ g_return_val_if_fail (newtarget != GST_PROXY_PAD_INTERNAL (gpad), FALSE);
+
+ /* no need for locking, the internal pad's lifecycle is directly linked to the
+ * ghostpad's */
+ internal = GST_PROXY_PAD_INTERNAL (gpad);
+
+ if (newtarget)
+ GST_DEBUG_OBJECT (gpad, "set target %s:%s", GST_DEBUG_PAD_NAME (newtarget));
+ else
+ GST_DEBUG_OBJECT (gpad, "clearing target");
+
+ /* clear old target */
+ GST_PROXY_LOCK (gpad);
+ if ((oldtarget = GST_PROXY_PAD_TARGET (gpad))) {
+
+ GST_PROXY_PAD_RETARGET (internal) = TRUE;
+
+ /* unlink internal pad */
+ if (GST_PAD_IS_SRC (internal))
+ gst_pad_unlink (internal, oldtarget);
+ else
+ gst_pad_unlink (oldtarget, internal);
+
+ GST_PROXY_PAD_RETARGET (internal) = FALSE;
+ }
+
+ result = gst_proxy_pad_set_target_unlocked (GST_PAD_CAST (gpad), newtarget);
+ GST_PROXY_UNLOCK (gpad);
+
+ if (result && newtarget) {
+ /* and link to internal pad without any checks */
+ GST_DEBUG_OBJECT (gpad, "connecting internal pad to target");
+
+ if (GST_PAD_IS_SRC (internal))
+ lret =
+ gst_pad_link_full (internal, newtarget, GST_PAD_LINK_CHECK_NOTHING);
+ else
+ lret =
+ gst_pad_link_full (newtarget, internal, GST_PAD_LINK_CHECK_NOTHING);
+
+ if (lret != GST_PAD_LINK_OK)
+ goto link_failed;
+ }
+
+ return result;
+
+ /* ERRORS */
+link_failed:
+ {
+ GST_WARNING_OBJECT (gpad, "could not link internal and target, reason:%d",
+ lret);
+ /* and unset target again */
+ GST_PROXY_LOCK (gpad);
+ gst_proxy_pad_set_target_unlocked (GST_PAD_CAST (gpad), NULL);
+ GST_PROXY_UNLOCK (gpad);
+ return FALSE;
+ }
+}
diff --git a/gst/gstghostpad.h b/gst/gstghostpad.h
new file mode 100644
index 0000000..8eb010a
--- /dev/null
+++ b/gst/gstghostpad.h
@@ -0,0 +1,133 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2000 Wim Taymans <wim.taymans@chello.be>
+ * 2005 Andy Wingo <wingo@pobox.com>
+ *
+ * gstghostpad.h: Proxy pads
+ *
+ * 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_GHOST_PAD_H__
+#define __GST_GHOST_PAD_H__
+
+
+#include <gst/gstpad.h>
+
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_PROXY_PAD (gst_proxy_pad_get_type ())
+#define GST_IS_PROXY_PAD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_PROXY_PAD))
+#define GST_IS_PROXY_PAD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_PROXY_PAD))
+#define GST_PROXY_PAD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_PROXY_PAD, GstProxyPad))
+#define GST_PROXY_PAD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_PROXY_PAD, GstProxyPadClass))
+
+typedef struct _GstProxyPad GstProxyPad;
+typedef struct _GstProxyPadPrivate GstProxyPadPrivate;
+typedef struct _GstProxyPadClass GstProxyPadClass;
+
+struct _GstProxyPad
+{
+ GstPad pad;
+
+ /*< private >*/
+ GstProxyPadPrivate *priv;
+};
+
+struct _GstProxyPadClass
+{
+ GstPadClass parent_class;
+
+ /*< private >*/
+ gpointer _gst_reserved[1];
+};
+
+GType gst_proxy_pad_get_type (void);
+
+GstProxyPad* gst_proxy_pad_get_internal (GstProxyPad *pad);
+
+
+const GstQueryType* gst_proxy_pad_query_type_default (GstPad *pad);
+gboolean gst_proxy_pad_event_default (GstPad *pad, GstEvent *event);
+gboolean gst_proxy_pad_query_default (GstPad *pad, GstQuery *query);
+GstIterator* gst_proxy_pad_iterate_internal_links_default (GstPad *pad);
+GstFlowReturn gst_proxy_pad_chain_default (GstPad *pad, GstBuffer *buffer);
+GstFlowReturn gst_proxy_pad_chain_list_default (GstPad *pad, GstBufferList *list);
+GstFlowReturn gst_proxy_pad_getrange_default (GstPad *pad, guint64 offset, guint size, GstBuffer **buffer);
+GstCaps* gst_proxy_pad_getcaps_default (GstPad *pad, GstCaps * filter);
+gboolean gst_proxy_pad_acceptcaps_default (GstPad *pad, GstCaps *caps);
+void gst_proxy_pad_fixatecaps_default (GstPad *pad, GstCaps *caps);
+void gst_proxy_pad_unlink_default (GstPad * pad);
+
+#define GST_TYPE_GHOST_PAD (gst_ghost_pad_get_type ())
+#define GST_IS_GHOST_PAD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_GHOST_PAD))
+#define GST_IS_GHOST_PAD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_GHOST_PAD))
+#define GST_GHOST_PAD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_GHOST_PAD, GstGhostPad))
+#define GST_GHOST_PAD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_GHOST_PAD, GstGhostPadClass))
+#define GST_GHOST_PAD_CAST(obj) ((GstGhostPad*)(obj))
+
+/**
+ * GstGhostPad:
+ *
+ * Opaque #GstGhostPad structure.
+ */
+typedef struct _GstGhostPad GstGhostPad;
+typedef struct _GstGhostPadPrivate GstGhostPadPrivate;
+typedef struct _GstGhostPadClass GstGhostPadClass;
+
+struct _GstGhostPad
+{
+ GstProxyPad pad;
+
+ /*< private >*/
+ GstGhostPadPrivate *priv;
+};
+
+struct _GstGhostPadClass
+{
+ GstProxyPadClass parent_class;
+
+ /*< private >*/
+ gpointer _gst_reserved[GST_PADDING];
+};
+
+
+GType gst_ghost_pad_get_type (void);
+
+GstPad* gst_ghost_pad_new (const gchar *name, GstPad *target);
+GstPad* gst_ghost_pad_new_no_target (const gchar *name, GstPadDirection dir);
+
+GstPad* gst_ghost_pad_new_from_template (const gchar *name, GstPad * target, GstPadTemplate * templ);
+GstPad* gst_ghost_pad_new_no_target_from_template (const gchar *name, GstPadTemplate * templ);
+
+GstPad* gst_ghost_pad_get_target (GstGhostPad *gpad);
+gboolean gst_ghost_pad_set_target (GstGhostPad *gpad, GstPad *newtarget);
+
+gboolean gst_ghost_pad_construct (GstGhostPad *gpad);
+
+void gst_ghost_pad_unlink_default (GstPad * pad);
+GstPadLinkReturn gst_ghost_pad_link_default (GstPad * pad, GstPad * peer);
+gboolean gst_ghost_pad_activate_pull_default (GstPad * pad, gboolean active);
+gboolean gst_ghost_pad_activate_push_default (GstPad * pad, gboolean active);
+
+gboolean gst_ghost_pad_internal_activate_push_default (GstPad * pad, gboolean active);
+gboolean gst_ghost_pad_internal_activate_pull_default (GstPad * pad, gboolean active);
+
+G_END_DECLS
+
+#endif /* __GST_GHOST_PAD_H__ */
diff --git a/gst/gstindex.c b/gst/gstindex.c
new file mode 100644
index 0000000..d168eee
--- /dev/null
+++ b/gst/gstindex.c
@@ -0,0 +1,1009 @@
+/* GStreamer
+ * Copyright (C) 2001 RidgeRun (http://www.ridgerun.com/)
+ * Written by Erik Walthinsen <omega@ridgerun.com>
+ *
+ * gstindex.c: Index for mappings and other data
+ *
+ * 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:gstindex
+ * @short_description: Generate indexes on objects
+ * @see_also: #GstIndexFactory
+ *
+ * GstIndex is used to generate a stream index of one or more elements
+ * in a pipeline.
+ *
+ * Elements will overload the set_index and get_index virtual methods in
+ * #GstElement. When streaming data, the element will add index entries if it
+ * has an index set.
+ *
+ * Each element that adds to the index will do that using a writer_id. The
+ * writer_id is obtained from gst_index_get_writer_id().
+ *
+ * The application that wants to index the stream will create a new index object
+ * using gst_index_new() or gst_index_factory_make(). The index is assigned to a
+ * specific element, a bin or the whole pipeline. This will cause indexable
+ * elements to add entires to the index while playing.
+ */
+
+/* FIXME: complete gobject annotations */
+/* FIXME-0.11: cleanup API
+ * - no one seems to use GstIndexGroup, GstIndexCertainty
+ *
+ * - the API for application to use the index is mostly missing
+ * - apps need to get a list of writers
+ * - apps need to be able to iterate over each writers index entry collection
+ * - gst_index_get_assoc_entry() should pass ownership
+ * - the GstIndexEntry structure is large and contains repetitive information
+ * - we want to allow Indexers to implement a saner storage and create
+ * GstIndexEntries on demand (the app has to free them), might even make
+ * sense to ask the app to provide a ptr and fill it.
+ */
+
+#include "gst_private.h"
+
+#include "gstinfo.h"
+#include "gstindex.h"
+#include "gstindexfactory.h"
+#include "gstmarshal.h"
+#include "gstregistry.h"
+/* for constructing an entry name */
+#include "gstelement.h"
+#include "gstpad.h"
+#include "gstinfo.h"
+
+/* Index signals and args */
+enum
+{
+ ENTRY_ADDED,
+ LAST_SIGNAL
+};
+
+enum
+{
+ ARG_0,
+ ARG_RESOLVER
+ /* FILL ME */
+};
+
+GST_DEBUG_CATEGORY_STATIC (index_debug);
+#define GST_CAT_DEFAULT index_debug
+
+static void gst_index_finalize (GObject * object);
+
+static void gst_index_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+static void gst_index_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
+
+static GstIndexGroup *gst_index_group_new (guint groupnum);
+static void gst_index_group_free (GstIndexGroup * group);
+
+static gboolean gst_index_path_resolver (GstIndex * index, GstObject * writer,
+ gchar ** writer_string, gpointer data);
+static gboolean gst_index_gtype_resolver (GstIndex * index, GstObject * writer,
+ gchar ** writer_string, gpointer data);
+static void gst_index_add_entry (GstIndex * index, GstIndexEntry * entry);
+
+static GstObject *parent_class = NULL;
+static guint gst_index_signals[LAST_SIGNAL] = { 0 };
+
+typedef struct
+{
+ GstIndexResolverMethod method;
+ GstIndexResolver resolver;
+ gpointer user_data;
+}
+ResolverEntry;
+
+static const ResolverEntry resolvers[] = {
+ {GST_INDEX_RESOLVER_CUSTOM, NULL, NULL},
+ {GST_INDEX_RESOLVER_GTYPE, gst_index_gtype_resolver, NULL},
+ {GST_INDEX_RESOLVER_PATH, gst_index_path_resolver, NULL},
+};
+
+#define GST_TYPE_INDEX_RESOLVER (gst_index_resolver_get_type())
+static GType
+gst_index_resolver_get_type (void)
+{
+ static GType index_resolver_type = 0;
+ static const GEnumValue index_resolver[] = {
+ {GST_INDEX_RESOLVER_CUSTOM, "GST_INDEX_RESOLVER_CUSTOM", "custom"},
+ {GST_INDEX_RESOLVER_GTYPE, "GST_INDEX_RESOLVER_GTYPE", "gtype"},
+ {GST_INDEX_RESOLVER_PATH, "GST_INDEX_RESOLVER_PATH", "path"},
+ {0, NULL, NULL},
+ };
+
+ if (!index_resolver_type) {
+ index_resolver_type =
+ g_enum_register_static ("GstIndexResolver", index_resolver);
+ }
+ return index_resolver_type;
+}
+
+GType
+gst_index_entry_get_type (void)
+{
+ static GType index_entry_type = 0;
+
+ if (!index_entry_type) {
+ index_entry_type = g_boxed_type_register_static ("GstIndexEntry",
+ (GBoxedCopyFunc) gst_index_entry_copy,
+ (GBoxedFreeFunc) gst_index_entry_free);
+ }
+ return index_entry_type;
+}
+
+#define _do_init \
+{ \
+ GST_DEBUG_CATEGORY_INIT (index_debug, "GST_INDEX", GST_DEBUG_BOLD, \
+ "Generic indexing support"); \
+}
+
+G_DEFINE_TYPE_WITH_CODE (GstIndex, gst_index, GST_TYPE_OBJECT, _do_init);
+
+static void
+gst_index_class_init (GstIndexClass * klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ /**
+ * GstIndex::entry-added
+ * @gstindex: the object which received the signal.
+ * @arg1: The entry added to the index.
+ *
+ * Is emitted when a new entry is added to the index.
+ */
+ gst_index_signals[ENTRY_ADDED] =
+ g_signal_new ("entry-added", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GstIndexClass, entry_added), NULL, NULL,
+ gst_marshal_VOID__BOXED, G_TYPE_NONE, 1, GST_TYPE_INDEX_ENTRY);
+
+ gobject_class->set_property = gst_index_set_property;
+ gobject_class->get_property = gst_index_get_property;
+ gobject_class->finalize = gst_index_finalize;
+
+ g_object_class_install_property (gobject_class, ARG_RESOLVER,
+ g_param_spec_enum ("resolver", "Resolver",
+ "Select a predefined object to string mapper",
+ GST_TYPE_INDEX_RESOLVER, GST_INDEX_RESOLVER_PATH,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+}
+
+static void
+gst_index_init (GstIndex * index)
+{
+ index->curgroup = gst_index_group_new (0);
+ index->maxgroup = 0;
+ index->groups = g_list_prepend (NULL, index->curgroup);
+
+ index->writers = g_hash_table_new (NULL, NULL);
+ index->last_id = 0;
+
+ index->method = GST_INDEX_RESOLVER_PATH;
+ index->resolver = resolvers[index->method].resolver;
+ index->resolver_user_data = resolvers[index->method].user_data;
+
+ GST_OBJECT_FLAG_SET (index, GST_INDEX_WRITABLE);
+ GST_OBJECT_FLAG_SET (index, GST_INDEX_READABLE);
+
+ GST_DEBUG ("created new index");
+}
+
+static void
+gst_index_free_writer (gpointer key, gpointer value, gpointer user_data)
+{
+ GstIndexEntry *entry = (GstIndexEntry *) value;
+
+ if (entry) {
+ gst_index_entry_free (entry);
+ }
+}
+
+static void
+gst_index_finalize (GObject * object)
+{
+ GstIndex *index = GST_INDEX (object);
+
+ if (index->groups) {
+ g_list_foreach (index->groups, (GFunc) gst_index_group_free, NULL);
+ g_list_free (index->groups);
+ index->groups = NULL;
+ }
+
+ if (index->writers) {
+ g_hash_table_foreach (index->writers, gst_index_free_writer, NULL);
+ g_hash_table_destroy (index->writers);
+ index->writers = NULL;
+ }
+
+ if (index->filter_user_data && index->filter_user_data_destroy)
+ index->filter_user_data_destroy (index->filter_user_data);
+
+ if (index->resolver_user_data && index->resolver_user_data_destroy)
+ index->resolver_user_data_destroy (index->resolver_user_data);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gst_index_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstIndex *index;
+
+ index = GST_INDEX (object);
+
+ switch (prop_id) {
+ case ARG_RESOLVER:
+ index->method = (GstIndexResolverMethod) g_value_get_enum (value);
+ index->resolver = resolvers[index->method].resolver;
+ index->resolver_user_data = resolvers[index->method].user_data;
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_index_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GstIndex *index;
+
+ index = GST_INDEX (object);
+
+ switch (prop_id) {
+ case ARG_RESOLVER:
+ g_value_set_enum (value, index->method);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static GstIndexGroup *
+gst_index_group_new (guint groupnum)
+{
+ GstIndexGroup *indexgroup = g_slice_new (GstIndexGroup);
+
+ indexgroup->groupnum = groupnum;
+ indexgroup->entries = NULL;
+ indexgroup->certainty = GST_INDEX_UNKNOWN;
+ indexgroup->peergroup = -1;
+
+ GST_DEBUG ("created new index group %d", groupnum);
+
+ return indexgroup;
+}
+
+static void
+gst_index_group_free (GstIndexGroup * group)
+{
+ g_slice_free (GstIndexGroup, group);
+}
+
+/**
+ * gst_index_new:
+ *
+ * Create a new dummy index object. Use gst_element_set_index() to assign that
+ * to an element or pipeline. This index is not storing anything, but will
+ * still emit e.g. the #GstIndex::entry-added signal.
+ *
+ * Returns: (transfer full): a new index object
+ */
+GstIndex *
+gst_index_new (void)
+{
+ GstIndex *index;
+
+ index = g_object_newv (gst_index_get_type (), 0, NULL);
+
+ return index;
+}
+
+/**
+ * gst_index_commit:
+ * @index: the index to commit
+ * @id: the writer that commited the index
+ *
+ * Tell the index that the writer with the given id is done
+ * with this index and is not going to write any more entries
+ * to it.
+ */
+void
+gst_index_commit (GstIndex * index, gint id)
+{
+ GstIndexClass *iclass;
+
+ iclass = GST_INDEX_GET_CLASS (index);
+
+ if (iclass->commit)
+ iclass->commit (index, id);
+}
+
+
+/**
+ * gst_index_get_group:
+ * @index: the index to get the current group from
+ *
+ * Get the id of the current group.
+ *
+ * Returns: the id of the current group.
+ */
+gint
+gst_index_get_group (GstIndex * index)
+{
+ return index->curgroup->groupnum;
+}
+
+/**
+ * gst_index_new_group:
+ * @index: the index to create the new group in
+ *
+ * Create a new group for the given index. It will be
+ * set as the current group.
+ *
+ * Returns: the id of the newly created group.
+ */
+gint
+gst_index_new_group (GstIndex * index)
+{
+ index->curgroup = gst_index_group_new (++index->maxgroup);
+ index->groups = g_list_append (index->groups, index->curgroup);
+ GST_DEBUG ("created new group %d in index", index->maxgroup);
+ return index->maxgroup;
+}
+
+/**
+ * gst_index_set_group:
+ * @index: the index to set the new group in
+ * @groupnum: the groupnumber to set
+ *
+ * Set the current groupnumber to the given argument.
+ *
+ * Returns: TRUE if the operation succeeded, FALSE if the group
+ * did not exist.
+ */
+gboolean
+gst_index_set_group (GstIndex * index, gint groupnum)
+{
+ GList *list;
+ GstIndexGroup *indexgroup;
+
+ /* first check for null change */
+ if (groupnum == index->curgroup->groupnum)
+ return TRUE;
+
+ /* else search for the proper group */
+ list = index->groups;
+ while (list) {
+ indexgroup = (GstIndexGroup *) (list->data);
+ list = g_list_next (list);
+ if (indexgroup->groupnum == groupnum) {
+ index->curgroup = indexgroup;
+ GST_DEBUG ("switched to index group %d", indexgroup->groupnum);
+ return TRUE;
+ }
+ }
+
+ /* couldn't find the group in question */
+ GST_DEBUG ("couldn't find index group %d", groupnum);
+ return FALSE;
+}
+
+/**
+ * gst_index_set_certainty:
+ * @index: the index to set the certainty on
+ * @certainty: the certainty to set
+ *
+ * Set the certainty of the given index.
+ */
+void
+gst_index_set_certainty (GstIndex * index, GstIndexCertainty certainty)
+{
+ index->curgroup->certainty = certainty;
+}
+
+/**
+ * gst_index_get_certainty:
+ * @index: the index to get the certainty of
+ *
+ * Get the certainty of the given index.
+ *
+ * Returns: the certainty of the index.
+ */
+GstIndexCertainty
+gst_index_get_certainty (GstIndex * index)
+{
+ return index->curgroup->certainty;
+}
+
+/**
+ * gst_index_set_filter:
+ * @index: the index to register the filter on
+ * @filter: the filter to register
+ * @user_data: data passed to the filter function
+ *
+ * Lets the app register a custom filter function so that
+ * it can select what entries should be stored in the index.
+ */
+void
+gst_index_set_filter (GstIndex * index,
+ GstIndexFilter filter, gpointer user_data)
+{
+ g_return_if_fail (GST_IS_INDEX (index));
+
+ gst_index_set_filter_full (index, filter, user_data, NULL);
+}
+
+/**
+ * gst_index_set_filter_full:
+ * @index: the index to register the filter on
+ * @filter: the filter to register
+ * @user_data: data passed to the filter function
+ * @user_data_destroy: function to call when @user_data is unset
+ *
+ * Lets the app register a custom filter function so that
+ * it can select what entries should be stored in the index.
+ */
+void
+gst_index_set_filter_full (GstIndex * index,
+ GstIndexFilter filter, gpointer user_data, GDestroyNotify user_data_destroy)
+{
+ g_return_if_fail (GST_IS_INDEX (index));
+
+ if (index->filter_user_data && index->filter_user_data_destroy)
+ index->filter_user_data_destroy (index->filter_user_data);
+
+ index->filter = filter;
+ index->filter_user_data = user_data;
+ index->filter_user_data_destroy = user_data_destroy;
+}
+
+/**
+ * gst_index_set_resolver:
+ * @index: the index to register the resolver on
+ * @resolver: the resolver to register
+ * @user_data: data passed to the resolver function
+ *
+ * Lets the app register a custom function to map index
+ * ids to writer descriptions.
+ */
+void
+gst_index_set_resolver (GstIndex * index,
+ GstIndexResolver resolver, gpointer user_data)
+{
+ gst_index_set_resolver_full (index, resolver, user_data, NULL);
+}
+
+/**
+ * gst_index_set_resolver_full:
+ * @index: the index to register the resolver on
+ * @resolver: the resolver to register
+ * @user_data: data passed to the resolver function
+ * @user_data_destroy: destroy function for @user_data
+ *
+ * Lets the app register a custom function to map index
+ * ids to writer descriptions.
+ *
+ * Since: 0.10.18
+ */
+void
+gst_index_set_resolver_full (GstIndex * index, GstIndexResolver resolver,
+ gpointer user_data, GDestroyNotify user_data_destroy)
+{
+ g_return_if_fail (GST_IS_INDEX (index));
+
+ if (index->resolver_user_data && index->resolver_user_data_destroy)
+ index->resolver_user_data_destroy (index->resolver_user_data);
+
+ index->resolver = resolver;
+ index->resolver_user_data = user_data;
+ index->resolver_user_data_destroy = user_data_destroy;
+ index->method = GST_INDEX_RESOLVER_CUSTOM;
+}
+
+/**
+ * gst_index_entry_copy:
+ * @entry: the entry to copy
+ *
+ * Copies an entry and returns the result.
+ *
+ * Free-function: gst_index_entry_free
+ *
+ * Returns: (transfer full): a newly allocated #GstIndexEntry.
+ */
+GstIndexEntry *
+gst_index_entry_copy (GstIndexEntry * entry)
+{
+ GstIndexEntry *new_entry = g_slice_new (GstIndexEntry);
+
+ memcpy (new_entry, entry, sizeof (GstIndexEntry));
+ return new_entry;
+}
+
+/**
+ * gst_index_entry_free:
+ * @entry: (transfer full): the entry to free
+ *
+ * Free the memory used by the given entry.
+ */
+void
+gst_index_entry_free (GstIndexEntry * entry)
+{
+ switch (entry->type) {
+ case GST_INDEX_ENTRY_ID:
+ if (entry->data.id.description) {
+ g_free (entry->data.id.description);
+ entry->data.id.description = NULL;
+ }
+ break;
+ case GST_INDEX_ENTRY_ASSOCIATION:
+ if (entry->data.assoc.assocs) {
+ g_free (entry->data.assoc.assocs);
+ entry->data.assoc.assocs = NULL;
+ }
+ break;
+ case GST_INDEX_ENTRY_OBJECT:
+ break;
+ case GST_INDEX_ENTRY_FORMAT:
+ break;
+ }
+
+ g_slice_free (GstIndexEntry, entry);
+}
+
+/**
+ * gst_index_add_format:
+ * @index: the index to add the entry to
+ * @id: the id of the index writer
+ * @format: the format to add to the index
+ *
+ * Adds a format entry into the index. This function is
+ * used to map dynamic GstFormat ids to their original
+ * format key.
+ *
+ * Free-function: gst_index_entry_free
+ *
+ * Returns: (transfer full): a pointer to the newly added entry in the index.
+ */
+GstIndexEntry *
+gst_index_add_format (GstIndex * index, gint id, GstFormat format)
+{
+ GstIndexEntry *entry;
+ const GstFormatDefinition *def;
+
+ g_return_val_if_fail (GST_IS_INDEX (index), NULL);
+ g_return_val_if_fail (format != 0, NULL);
+
+ if (!GST_INDEX_IS_WRITABLE (index) || id == -1)
+ return NULL;
+
+ entry = g_slice_new (GstIndexEntry);
+ entry->type = GST_INDEX_ENTRY_FORMAT;
+ entry->id = id;
+ entry->data.format.format = format;
+
+ def = gst_format_get_details (format);
+ entry->data.format.key = def->nick;
+
+ gst_index_add_entry (index, entry);
+
+ return entry;
+}
+
+/**
+ * gst_index_add_id:
+ * @index: the index to add the entry to
+ * @id: the id of the index writer
+ * @description: the description of the index writer
+ *
+ * Add an id entry into the index.
+ *
+ * Returns: a pointer to the newly added entry in the index.
+ */
+GstIndexEntry *
+gst_index_add_id (GstIndex * index, gint id, gchar * description)
+{
+ GstIndexEntry *entry;
+
+ g_return_val_if_fail (GST_IS_INDEX (index), NULL);
+ g_return_val_if_fail (description != NULL, NULL);
+
+ if (!GST_INDEX_IS_WRITABLE (index) || id == -1)
+ return NULL;
+
+ entry = g_slice_new (GstIndexEntry);
+ entry->type = GST_INDEX_ENTRY_ID;
+ entry->id = id;
+ entry->data.id.description = description;
+
+ gst_index_add_entry (index, entry);
+
+ return entry;
+}
+
+static gboolean
+gst_index_path_resolver (GstIndex * index, GstObject * writer,
+ gchar ** writer_string, gpointer data)
+{
+ *writer_string = gst_object_get_path_string (writer);
+
+ return TRUE;
+}
+
+static gboolean
+gst_index_gtype_resolver (GstIndex * index, GstObject * writer,
+ gchar ** writer_string, gpointer data)
+{
+ g_return_val_if_fail (writer != NULL, FALSE);
+
+ if (GST_IS_PAD (writer)) {
+ GstObject *element = gst_object_get_parent (GST_OBJECT (writer));
+ gchar *name;
+
+ name = gst_object_get_name (writer);
+ if (element) {
+ *writer_string = g_strdup_printf ("%s.%s",
+ G_OBJECT_TYPE_NAME (element), name);
+ gst_object_unref (element);
+ } else {
+ *writer_string = name;
+ name = NULL;
+ }
+
+ g_free (name);
+
+ } else {
+ *writer_string = g_strdup (G_OBJECT_TYPE_NAME (writer));
+ }
+
+ return TRUE;
+}
+
+/**
+ * gst_index_get_writer_id:
+ * @index: the index to get a unique write id for
+ * @writer: the GstObject to allocate an id for
+ * @id: a pointer to a gint to hold the id
+ *
+ * Before entries can be added to the index, a writer
+ * should obtain a unique id. The methods to add new entries
+ * to the index require this id as an argument.
+ *
+ * The application can implement a custom function to map the writer object
+ * to a string. That string will be used to register or look up an id
+ * in the index.
+ *
+ * <note>
+ * The caller must not hold @writer's #GST_OBJECT_LOCK, as the default
+ * resolver may call functions that take the object lock as well, and
+ * the lock is not recursive.
+ * </note>
+ *
+ * Returns: TRUE if the writer would be mapped to an id.
+ */
+gboolean
+gst_index_get_writer_id (GstIndex * index, GstObject * writer, gint * id)
+{
+ gchar *writer_string = NULL;
+ GstIndexEntry *entry;
+ GstIndexClass *iclass;
+ gboolean success = FALSE;
+
+ g_return_val_if_fail (GST_IS_INDEX (index), FALSE);
+ g_return_val_if_fail (GST_IS_OBJECT (writer), FALSE);
+ g_return_val_if_fail (id, FALSE);
+
+ *id = -1;
+
+ /* first try to get a previously cached id */
+ entry = g_hash_table_lookup (index->writers, writer);
+ if (entry == NULL) {
+
+ iclass = GST_INDEX_GET_CLASS (index);
+
+ /* let the app make a string */
+ if (index->resolver) {
+ gboolean res;
+
+ res =
+ index->resolver (index, writer, &writer_string,
+ index->resolver_user_data);
+ if (!res)
+ return FALSE;
+ } else {
+ g_warning ("no resolver found");
+ return FALSE;
+ }
+
+ /* if the index has a resolver, make it map this string to an id */
+ if (iclass->get_writer_id) {
+ success = iclass->get_writer_id (index, id, writer_string);
+ }
+ /* if the index could not resolve, we allocate one ourselves */
+ if (!success) {
+ *id = ++index->last_id;
+ }
+
+ entry = gst_index_add_id (index, *id, writer_string);
+ if (!entry) {
+ /* index is probably not writable, make an entry anyway
+ * to keep it in our cache */
+ entry = g_slice_new (GstIndexEntry);
+ entry->type = GST_INDEX_ENTRY_ID;
+ entry->id = *id;
+ entry->data.id.description = writer_string;
+ }
+ g_hash_table_insert (index->writers, writer, entry);
+ } else {
+ *id = entry->id;
+ }
+
+ return TRUE;
+}
+
+static void
+gst_index_add_entry (GstIndex * index, GstIndexEntry * entry)
+{
+ GstIndexClass *iclass;
+
+ iclass = GST_INDEX_GET_CLASS (index);
+
+ if (iclass->add_entry) {
+ iclass->add_entry (index, entry);
+ }
+
+ g_signal_emit (index, gst_index_signals[ENTRY_ADDED], 0, entry);
+}
+
+/**
+ * gst_index_add_associationv:
+ * @index: the index to add the entry to
+ * @id: the id of the index writer
+ * @flags: optinal flags for this entry
+ * @n: number of associations
+ * @list: list of associations
+ *
+ * Associate given format/value pairs with each other.
+ *
+ * Returns: a pointer to the newly added entry in the index.
+ */
+GstIndexEntry *
+gst_index_add_associationv (GstIndex * index, gint id, GstAssocFlags flags,
+ gint n, const GstIndexAssociation * list)
+{
+ GstIndexEntry *entry;
+
+ g_return_val_if_fail (n > 0, NULL);
+ g_return_val_if_fail (list != NULL, NULL);
+ g_return_val_if_fail (GST_IS_INDEX (index), NULL);
+
+ if (!GST_INDEX_IS_WRITABLE (index) || id == -1)
+ return NULL;
+
+ entry = g_slice_new (GstIndexEntry);
+
+ entry->type = GST_INDEX_ENTRY_ASSOCIATION;
+ entry->id = id;
+ entry->data.assoc.flags = flags;
+ entry->data.assoc.assocs = g_memdup (list, sizeof (GstIndexAssociation) * n);
+ entry->data.assoc.nassocs = n;
+
+ gst_index_add_entry (index, entry);
+
+ return entry;
+}
+
+/**
+ * gst_index_add_association:
+ * @index: the index to add the entry to
+ * @id: the id of the index writer
+ * @flags: optinal flags for this entry
+ * @format: the format of the value
+ * @value: the value
+ * @...: other format/value pairs or 0 to end the list
+ *
+ * Associate given format/value pairs with each other.
+ * Be sure to pass gint64 values to this functions varargs,
+ * you might want to use a gint64 cast to be sure.
+ *
+ * Returns: a pointer to the newly added entry in the index.
+ */
+GstIndexEntry *
+gst_index_add_association (GstIndex * index, gint id, GstAssocFlags flags,
+ GstFormat format, gint64 value, ...)
+{
+ va_list args;
+ GstIndexEntry *entry;
+ GstIndexAssociation *list;
+ gint n_assocs = 0;
+ GstFormat cur_format;
+ GArray *array;
+
+ g_return_val_if_fail (GST_IS_INDEX (index), NULL);
+ g_return_val_if_fail (format != 0, NULL);
+
+ if (!GST_INDEX_IS_WRITABLE (index) || id == -1)
+ return NULL;
+
+ array = g_array_new (FALSE, FALSE, sizeof (GstIndexAssociation));
+
+ {
+ GstIndexAssociation a;
+
+ a.format = format;
+ a.value = value;
+ n_assocs = 1;
+ g_array_append_val (array, a);
+ }
+
+ va_start (args, value);
+
+ while ((cur_format = va_arg (args, GstFormat))) {
+ GstIndexAssociation a;
+
+ a.format = cur_format;
+ a.value = va_arg (args, gint64);
+ n_assocs++;
+ g_array_append_val (array, a);
+ }
+
+ va_end (args);
+
+ list = (GstIndexAssociation *) g_array_free (array, FALSE);
+
+ entry = gst_index_add_associationv (index, id, flags, n_assocs, list);
+ g_free (list);
+
+ return entry;
+}
+
+/**
+ * gst_index_add_object:
+ * @index: the index to add the object to
+ * @id: the id of the index writer
+ * @key: a key for the object
+ * @type: the GType of the object
+ * @object: a pointer to the object to add
+ *
+ * Add the given object to the index with the given key.
+ *
+ * This function is not yet implemented.
+ *
+ * Returns: a pointer to the newly added entry in the index.
+ */
+GstIndexEntry *
+gst_index_add_object (GstIndex * index, gint id, gchar * key,
+ GType type, gpointer object)
+{
+ if (!GST_INDEX_IS_WRITABLE (index) || id == -1)
+ return NULL;
+
+ return NULL;
+}
+
+static gint
+gst_index_compare_func (gconstpointer a, gconstpointer b, gpointer user_data)
+{
+ if (a < b)
+ return -1;
+ if (a > b)
+ return 1;
+ return 0;
+}
+
+/**
+ * gst_index_get_assoc_entry:
+ * @index: the index to search
+ * @id: the id of the index writer
+ * @method: The lookup method to use
+ * @flags: Flags for the entry
+ * @format: the format of the value
+ * @value: the value to find
+ *
+ * Finds the given format/value in the index
+ *
+ * Returns: the entry associated with the value or NULL if the
+ * value was not found.
+ */
+GstIndexEntry *
+gst_index_get_assoc_entry (GstIndex * index, gint id,
+ GstIndexLookupMethod method, GstAssocFlags flags,
+ GstFormat format, gint64 value)
+{
+ g_return_val_if_fail (GST_IS_INDEX (index), NULL);
+
+ if (id == -1)
+ return NULL;
+
+ return gst_index_get_assoc_entry_full (index, id, method, flags, format,
+ value, gst_index_compare_func, NULL);
+}
+
+/**
+ * gst_index_get_assoc_entry_full:
+ * @index: the index to search
+ * @id: the id of the index writer
+ * @method: The lookup method to use
+ * @flags: Flags for the entry
+ * @format: the format of the value
+ * @value: the value to find
+ * @func: the function used to compare entries
+ * @user_data: user data passed to the compare function
+ *
+ * Finds the given format/value in the index with the given
+ * compare function and user_data.
+ *
+ * Returns: the entry associated with the value or NULL if the
+ * value was not found.
+ */
+GstIndexEntry *
+gst_index_get_assoc_entry_full (GstIndex * index, gint id,
+ GstIndexLookupMethod method, GstAssocFlags flags,
+ GstFormat format, gint64 value, GCompareDataFunc func, gpointer user_data)
+{
+ GstIndexClass *iclass;
+
+ g_return_val_if_fail (GST_IS_INDEX (index), NULL);
+
+ if (id == -1)
+ return NULL;
+
+ iclass = GST_INDEX_GET_CLASS (index);
+
+ if (iclass->get_assoc_entry)
+ return iclass->get_assoc_entry (index, id, method, flags, format, value,
+ func, user_data);
+
+ return NULL;
+}
+
+/**
+ * gst_index_entry_assoc_map:
+ * @entry: the index to search
+ * @format: the format of the value the find
+ * @value: a pointer to store the value
+ *
+ * Gets alternative formats associated with the indexentry.
+ *
+ * Returns: TRUE if there was a value associated with the given
+ * format.
+ */
+gboolean
+gst_index_entry_assoc_map (GstIndexEntry * entry,
+ GstFormat format, gint64 * value)
+{
+ gint i;
+
+ g_return_val_if_fail (entry != NULL, FALSE);
+ g_return_val_if_fail (value != NULL, FALSE);
+
+ for (i = 0; i < GST_INDEX_NASSOCS (entry); i++) {
+ if (GST_INDEX_ASSOC_FORMAT (entry, i) == format) {
+ *value = GST_INDEX_ASSOC_VALUE (entry, i);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
diff --git a/gst/gstindex.h b/gst/gstindex.h
new file mode 100644
index 0000000..9447a38
--- /dev/null
+++ b/gst/gstindex.h
@@ -0,0 +1,424 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2000 Wim Taymans <wim.taymans@chello.be>
+ *
+ * gstindex.h: Header for GstIndex, base class to handle efficient
+ * storage or caching of seeking information.
+ *
+ * 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_INDEX_H__
+#define __GST_INDEX_H__
+
+#include <gst/gstobject.h>
+#include <gst/gstformat.h>
+#include <gst/gstpluginfeature.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_INDEX (gst_index_get_type ())
+#define GST_INDEX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_INDEX, GstIndex))
+#define GST_IS_INDEX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_INDEX))
+#define GST_INDEX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_INDEX, GstIndexClass))
+#define GST_IS_INDEX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_INDEX))
+#define GST_INDEX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_INDEX, GstIndexClass))
+
+#define GST_TYPE_INDEX_ENTRY (gst_index_entry_get_type())
+
+typedef struct _GstIndexEntry GstIndexEntry;
+typedef struct _GstIndexGroup GstIndexGroup;
+typedef struct _GstIndex GstIndex;
+typedef struct _GstIndexClass GstIndexClass;
+
+/**
+ * GstIndexCertainty:
+ * @GST_INDEX_UNKNOWN: accuracy is not known
+ * @GST_INDEX_CERTAIN: accuracy is perfect
+ * @GST_INDEX_FUZZY: accuracy is fuzzy
+ *
+ * The certainty of a group in the index.
+ */
+typedef enum {
+ GST_INDEX_UNKNOWN,
+ GST_INDEX_CERTAIN,
+ GST_INDEX_FUZZY
+} GstIndexCertainty;
+
+/**
+ * GstIndexEntryType:
+ * @GST_INDEX_ENTRY_ID: This entry is an id that maps an index id to its owner object
+ * @GST_INDEX_ENTRY_ASSOCIATION: This entry is an association between formats
+ * @GST_INDEX_ENTRY_OBJECT: An object
+ * @GST_INDEX_ENTRY_FORMAT: A format definition
+ *
+ * The different types of entries in the index.
+ */
+typedef enum {
+ GST_INDEX_ENTRY_ID,
+ GST_INDEX_ENTRY_ASSOCIATION,
+ GST_INDEX_ENTRY_OBJECT,
+ GST_INDEX_ENTRY_FORMAT
+} GstIndexEntryType;
+
+/**
+ * GstIndexLookupMethod:
+ * @GST_INDEX_LOOKUP_EXACT: There has to be an exact indexentry with the given format/value
+ * @GST_INDEX_LOOKUP_BEFORE: The exact entry or the one before it
+ * @GST_INDEX_LOOKUP_AFTER: The exact entry or the one after it
+ *
+ * Specify the method to find an index entry in the index.
+ */
+typedef enum {
+ GST_INDEX_LOOKUP_EXACT,
+ GST_INDEX_LOOKUP_BEFORE,
+ GST_INDEX_LOOKUP_AFTER
+} GstIndexLookupMethod;
+
+/**
+ * GST_INDEX_NASSOCS:
+ * @entry: The entry to query
+ *
+ * Get the number of associations in the entry.
+ */
+#define GST_INDEX_NASSOCS(entry) ((entry)->data.assoc.nassocs)
+
+/**
+ * GST_INDEX_ASSOC_FLAGS:
+ * @entry: The entry to query
+ *
+ * Get the flags for this entry.
+ */
+#define GST_INDEX_ASSOC_FLAGS(entry) ((entry)->data.assoc.flags)
+
+/**
+ * GST_INDEX_ASSOC_FORMAT:
+ * @entry: The entry to query
+ * @i: The format index
+ *
+ * Get the i-th format of the entry.
+ */
+#define GST_INDEX_ASSOC_FORMAT(entry,i) ((entry)->data.assoc.assocs[(i)].format)
+
+/**
+ * GST_INDEX_ASSOC_VALUE:
+ * @entry: The entry to query
+ * @i: The value index
+ *
+ * Get the i-th value of the entry.
+ */
+#define GST_INDEX_ASSOC_VALUE(entry,i) ((entry)->data.assoc.assocs[(i)].value)
+
+typedef struct _GstIndexAssociation GstIndexAssociation;
+
+/**
+ * GstIndexAssociation:
+ * @format: the format of the association
+ * @value: the value of the association
+ *
+ * An association in an entry.
+ */
+struct _GstIndexAssociation {
+ GstFormat format;
+ gint64 value;
+};
+
+/**
+ * GstAssocFlags:
+ * @GST_ASSOCIATION_FLAG_NONE: no extra flags
+ * @GST_ASSOCIATION_FLAG_KEY_UNIT: the entry marks a key unit, a key unit is one
+ * that marks a place where one can randomly seek to.
+ * @GST_ASSOCIATION_FLAG_DELTA_UNIT: the entry marks a delta unit, a delta unit
+ * is one that marks a place where one can relatively seek to.
+ * @GST_ASSOCIATION_FLAG_LAST: extra user defined flags should start here.
+ *
+ * Flags for an association entry.
+ */
+typedef enum {
+ GST_ASSOCIATION_FLAG_NONE = 0,
+ GST_ASSOCIATION_FLAG_KEY_UNIT = (1 << 0),
+ GST_ASSOCIATION_FLAG_DELTA_UNIT = (1 << 1),
+
+ /* new flags should start here */
+ GST_ASSOCIATION_FLAG_LAST = (1 << 8)
+} GstAssocFlags;
+
+/**
+ * GST_INDEX_FORMAT_FORMAT:
+ * @entry: The entry to query
+ *
+ * Get the format of the format entry
+ */
+#define GST_INDEX_FORMAT_FORMAT(entry) ((entry)->data.format.format)
+
+/**
+ * GST_INDEX_FORMAT_KEY:
+ * @entry: The entry to query
+ *
+ * Get the key of the format entry
+ */
+#define GST_INDEX_FORMAT_KEY(entry) ((entry)->data.format.key)
+
+/**
+ * GST_INDEX_ID_INVALID:
+ *
+ * Constant for an invalid index id
+ */
+#define GST_INDEX_ID_INVALID (-1)
+
+/**
+ * GST_INDEX_ID_DESCRIPTION:
+ * @entry: The entry to query
+ *
+ * Get the description of the id entry
+ */
+#define GST_INDEX_ID_DESCRIPTION(entry) ((entry)->data.id.description)
+
+/**
+ * GstIndexEntry:
+ *
+ * The basic element of an index.
+ */
+struct _GstIndexEntry {
+ /*< private >*/
+ GstIndexEntryType type;
+ gint id;
+
+ union {
+ struct {
+ gchar *description;
+ } id;
+ struct {
+ gint nassocs;
+ GstIndexAssociation
+ *assocs;
+ GstAssocFlags flags;
+ } assoc;
+ struct {
+ gchar *key;
+ GType type;
+ gpointer object;
+ } object;
+ struct {
+ GstFormat format;
+ const gchar *key;
+ } format;
+ } data;
+};
+
+/**
+ * GstIndexGroup:
+ *
+ * A group of related entries in an index.
+ */
+
+struct _GstIndexGroup {
+ /*< private >*/
+ /* unique ID of group in index */
+ gint groupnum;
+
+ /* list of entries */
+ GList *entries;
+
+ /* the certainty level of the group */
+ GstIndexCertainty certainty;
+
+ /* peer group that contains more certain entries */
+ gint peergroup;
+};
+
+/**
+ * GstIndexFilter:
+ * @index: The index being queried
+ * @entry: The entry to be added.
+ * @user_data: User data passed to the function.
+ *
+ * Function to filter out entries in the index.
+ *
+ * Returns: This function should return %TRUE if the entry is to be added
+ * to the index, %FALSE otherwise.
+ *
+ */
+typedef gboolean (*GstIndexFilter) (GstIndex *index,
+ GstIndexEntry *entry,
+ gpointer user_data);
+/**
+ * GstIndexResolverMethod:
+ * @GST_INDEX_RESOLVER_CUSTOM: Use a custom resolver
+ * @GST_INDEX_RESOLVER_GTYPE: Resolve based on the GType of the object
+ * @GST_INDEX_RESOLVER_PATH: Resolve on the path in graph
+ *
+ * The method used to resolve index writers
+ */
+typedef enum {
+ GST_INDEX_RESOLVER_CUSTOM,
+ GST_INDEX_RESOLVER_GTYPE,
+ GST_INDEX_RESOLVER_PATH
+} GstIndexResolverMethod;
+
+/**
+ * GstIndexResolver:
+ * @index: the index being queried.
+ * @writer: The object that wants to write
+ * @writer_string: A description of the writer.
+ * @user_data: user_data as registered
+ *
+ * Function to resolve ids to writer descriptions.
+ *
+ * Returns: %TRUE if an id could be assigned to the writer.
+ */
+typedef gboolean (*GstIndexResolver) (GstIndex *index,
+ GstObject *writer,
+ gchar **writer_string,
+ gpointer user_data);
+
+/**
+ * GstIndexFlags:
+ * @GST_INDEX_WRITABLE: The index is writable
+ * @GST_INDEX_READABLE: The index is readable
+ * @GST_INDEX_FLAG_LAST: First flag that can be used by subclasses
+ *
+ * Flags for this index
+ */
+typedef enum {
+ GST_INDEX_WRITABLE = (GST_OBJECT_FLAG_LAST << 0),
+ GST_INDEX_READABLE = (GST_OBJECT_FLAG_LAST << 1),
+
+ GST_INDEX_FLAG_LAST = (GST_OBJECT_FLAG_LAST << 8)
+} GstIndexFlags;
+
+/**
+ * GST_INDEX_IS_READABLE:
+ * @obj: The index to check
+ *
+ * Check if the index can be read from
+ */
+#define GST_INDEX_IS_READABLE(obj) (GST_OBJECT_FLAG_IS_SET (obj, GST_INDEX_READABLE))
+
+/**
+ * GST_INDEX_IS_WRITABLE:
+ * @obj: The index to check
+ *
+ * Check if the index can be written to
+ */
+#define GST_INDEX_IS_WRITABLE(obj) (GST_OBJECT_FLAG_IS_SET (obj, GST_INDEX_WRITABLE))
+
+/**
+ * GstIndex:
+ *
+ * Opaque #GstIndex structure.
+ */
+struct _GstIndex {
+ GstObject object;
+
+ /*< private >*/
+ GList *groups;
+ GstIndexGroup *curgroup;
+ gint maxgroup;
+
+ GstIndexResolverMethod method;
+ GstIndexResolver resolver;
+ gpointer resolver_user_data;
+ GDestroyNotify resolver_user_data_destroy;
+
+ GstIndexFilter filter;
+ gpointer filter_user_data;
+ GDestroyNotify filter_user_data_destroy;
+
+ GHashTable *writers;
+ gint last_id;
+
+ /*< private >*/
+ gpointer _gst_reserved[GST_PADDING];
+};
+
+struct _GstIndexClass {
+ GstObjectClass parent_class;
+
+ /*< protected >*/
+ gboolean (*get_writer_id) (GstIndex *index, gint *writer_id, gchar *writer_string);
+
+ void (*commit) (GstIndex *index, gint id);
+
+ /* abstract methods */
+ void (*add_entry) (GstIndex *index, GstIndexEntry *entry);
+
+ GstIndexEntry* (*get_assoc_entry) (GstIndex *index, gint id,
+ GstIndexLookupMethod method, GstAssocFlags flags,
+ GstFormat format, gint64 value,
+ GCompareDataFunc func,
+ gpointer user_data);
+ /* signals */
+ void (*entry_added) (GstIndex *index, GstIndexEntry *entry);
+
+ /*< private >*/
+ gpointer _gst_reserved[GST_PADDING];
+};
+
+GType gst_index_get_type (void);
+GstIndex* gst_index_new (void);
+void gst_index_commit (GstIndex *index, gint id);
+
+gint gst_index_get_group (GstIndex *index);
+gint gst_index_new_group (GstIndex *index);
+gboolean gst_index_set_group (GstIndex *index, gint groupnum);
+
+void gst_index_set_certainty (GstIndex *index,
+ GstIndexCertainty certainty);
+GstIndexCertainty gst_index_get_certainty (GstIndex *index);
+
+void gst_index_set_filter (GstIndex *index,
+ GstIndexFilter filter, gpointer user_data);
+void gst_index_set_filter_full (GstIndex *index,
+ GstIndexFilter filter, gpointer user_data,
+ GDestroyNotify user_data_destroy);
+void gst_index_set_resolver (GstIndex *index,
+ GstIndexResolver resolver, gpointer user_data);
+void gst_index_set_resolver_full (GstIndex *index, GstIndexResolver resolver,
+ gpointer user_data,
+ GDestroyNotify user_data_destroy);
+
+gboolean gst_index_get_writer_id (GstIndex *index, GstObject *writer, gint *id);
+
+GstIndexEntry* gst_index_add_format (GstIndex *index, gint id, GstFormat format);
+GstIndexEntry* gst_index_add_associationv (GstIndex * index, gint id, GstAssocFlags flags,
+ gint n, const GstIndexAssociation * list);
+GstIndexEntry* gst_index_add_association (GstIndex *index, gint id, GstAssocFlags flags,
+ GstFormat format, gint64 value, ...);
+GstIndexEntry* gst_index_add_object (GstIndex *index, gint id, gchar *key,
+ GType type, gpointer object);
+GstIndexEntry* gst_index_add_id (GstIndex *index, gint id,
+ gchar *description);
+
+GstIndexEntry* gst_index_get_assoc_entry (GstIndex *index, gint id,
+ GstIndexLookupMethod method, GstAssocFlags flags,
+ GstFormat format, gint64 value);
+GstIndexEntry* gst_index_get_assoc_entry_full (GstIndex *index, gint id,
+ GstIndexLookupMethod method, GstAssocFlags flags,
+ GstFormat format, gint64 value,
+ GCompareDataFunc func,
+ gpointer user_data);
+
+/* working with index entries */
+GType gst_index_entry_get_type (void);
+GstIndexEntry * gst_index_entry_copy (GstIndexEntry *entry);
+void gst_index_entry_free (GstIndexEntry *entry);
+gboolean gst_index_entry_assoc_map (GstIndexEntry *entry,
+ GstFormat format, gint64 *value);
+
+G_END_DECLS
+
+#endif /* __GST_INDEX_H__ */
diff --git a/gst/gstindexfactory.c b/gst/gstindexfactory.c
new file mode 100644
index 0000000..111a0ce
--- /dev/null
+++ b/gst/gstindexfactory.c
@@ -0,0 +1,214 @@
+/* GStreamer
+ * Copyright (C) 2001 RidgeRun (http://www.ridgerun.com/)
+ * Written by Erik Walthinsen <omega@ridgerun.com>
+ *
+ * gstindexfactory.c: Index for mappings and other data
+ *
+ * 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:gstindexfactory
+ * @short_description: Create GstIndexes from a factory
+ * @see_also: #GstIndex
+ *
+ * GstIndexFactory is used to dynamically create GstIndex implementations.
+ */
+
+
+#include "gst_private.h"
+
+#include "gstinfo.h"
+#include "gstindex.h"
+#include "gstindexfactory.h"
+#include "gstmarshal.h"
+#include "gstregistry.h"
+
+static void gst_index_factory_finalize (GObject * object);
+
+static GstPluginFeatureClass *factory_parent_class = NULL;
+
+/* static guint gst_index_factory_signals[LAST_SIGNAL] = { 0 }; */
+G_DEFINE_TYPE (GstIndexFactory, gst_index_factory, GST_TYPE_PLUGIN_FEATURE);
+
+static void
+gst_index_factory_class_init (GstIndexFactoryClass * klass)
+{
+ GObjectClass *gobject_class = (GObjectClass *) klass;
+
+ factory_parent_class = g_type_class_peek_parent (klass);
+
+ gobject_class->finalize = gst_index_factory_finalize;
+}
+
+static void
+gst_index_factory_init (GstIndexFactory * factory)
+{
+}
+
+static void
+gst_index_factory_finalize (GObject * object)
+{
+ GstIndexFactory *factory = GST_INDEX_FACTORY (object);
+
+ g_free (factory->longdesc);
+
+ G_OBJECT_CLASS (factory_parent_class)->finalize (object);
+
+}
+
+/**
+ * gst_index_factory_new:
+ * @name: name of indexfactory to create
+ * @longdesc: long description of indexfactory to create
+ * @type: the GType of the GstIndex element of this factory
+ *
+ * Create a new indexfactory with the given parameters
+ *
+ * Returns: (transfer full): a new #GstIndexFactory.
+ */
+GstIndexFactory *
+gst_index_factory_new (const gchar * name, const gchar * longdesc, GType type)
+{
+ GstIndexFactory *factory;
+
+ g_return_val_if_fail (name != NULL, NULL);
+
+ factory =
+ GST_INDEX_FACTORY (g_object_new (GST_TYPE_INDEX_FACTORY, "name", name,
+ NULL));
+
+ if (factory->longdesc)
+ g_free (factory->longdesc);
+ factory->longdesc = g_strdup (longdesc);
+ factory->type = type;
+
+ return factory;
+}
+
+/**
+ * gst_index_factory_destroy:
+ * @factory: factory to destroy
+ *
+ * Removes the index from the global list.
+ */
+void
+gst_index_factory_destroy (GstIndexFactory * factory)
+{
+ g_return_if_fail (factory != NULL);
+
+ /* we don't free the struct bacause someone might have a handle to it.. */
+ /* FIXME: gst_index_factory_destroy */
+}
+
+/**
+ * gst_index_factory_find:
+ * @name: name of indexfactory to find
+ *
+ * Search for an indexfactory of the given name.
+ *
+ * Returns: (transfer full): #GstIndexFactory if found, NULL otherwise
+ */
+GstIndexFactory *
+gst_index_factory_find (const gchar * name)
+{
+ GstPluginFeature *feature;
+
+ g_return_val_if_fail (name != NULL, NULL);
+
+ GST_DEBUG ("gstindex: find \"%s\"", name);
+
+ feature = gst_registry_find_feature (gst_registry_get_default (), name,
+ GST_TYPE_INDEX_FACTORY);
+ if (feature)
+ return GST_INDEX_FACTORY (feature);
+
+ return NULL;
+}
+
+/**
+ * gst_index_factory_create:
+ * @factory: the factory used to create the instance
+ *
+ * Create a new #GstIndex instance from the
+ * given indexfactory.
+ *
+ * Returns: (transfer full): a new #GstIndex instance.
+ */
+GstIndex *
+gst_index_factory_create (GstIndexFactory * factory)
+{
+ GstIndexFactory *newfactory;
+ GstIndex *new = NULL;
+
+ g_return_val_if_fail (factory != NULL, NULL);
+
+ newfactory =
+ GST_INDEX_FACTORY (gst_plugin_feature_load (GST_PLUGIN_FEATURE
+ (factory)));
+ if (newfactory == NULL)
+ return NULL;
+
+ new = GST_INDEX (g_object_newv (newfactory->type, 0, NULL));
+
+ gst_object_unref (newfactory);
+
+ return new;
+}
+
+/**
+ * gst_index_factory_make:
+ * @name: the name of the factory used to create the instance
+ *
+ * Create a new #GstIndex instance from the
+ * indexfactory with the given name.
+ *
+ * Returns: (transfer full): a new #GstIndex instance.
+ */
+GstIndex *
+gst_index_factory_make (const gchar * name)
+{
+ GstIndexFactory *factory;
+ GstIndex *index;
+
+ g_return_val_if_fail (name != NULL, NULL);
+
+ factory = gst_index_factory_find (name);
+
+ if (factory == NULL)
+ goto no_factory;
+
+ index = gst_index_factory_create (factory);
+
+ if (index == NULL)
+ goto create_failed;
+
+ gst_object_unref (factory);
+ return index;
+
+ /* ERRORS */
+no_factory:
+ {
+ GST_INFO ("no such index factory \"%s\"!", name);
+ return NULL;
+ }
+create_failed:
+ {
+ GST_INFO_OBJECT (factory, "couldn't create instance!");
+ gst_object_unref (factory);
+ return NULL;
+ }
+}
diff --git a/gst/gstindexfactory.h b/gst/gstindexfactory.h
new file mode 100644
index 0000000..f5e9ef3
--- /dev/null
+++ b/gst/gstindexfactory.h
@@ -0,0 +1,76 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2000 Wim Taymans <wim.taymans@chello.be>
+ *
+ * gstindexfactory.h: Header for GstIndexFactory, base class to handle efficient
+ * storage or caching of seeking information.
+ *
+ * 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_INDEX_FACTORY_H__
+#define __GST_INDEX_FACTORY_H__
+
+#include <gst/gstobject.h>
+#include <gst/gstformat.h>
+#include <gst/gstpluginfeature.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_INDEX_FACTORY (gst_index_factory_get_type())
+#define GST_INDEX_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_INDEX_FACTORY, GstIndexFactory))
+#define GST_IS_INDEX_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_INDEX_FACTORY))
+#define GST_INDEX_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_INDEX_FACTORY, GstIndexFactoryClass))
+#define GST_IS_INDEX_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_INDEX_FACTORY))
+#define GST_INDEX_FACTORY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_INDEX_FACTORY, GstIndexFactoryClass))
+
+typedef struct _GstIndexFactory GstIndexFactory;
+typedef struct _GstIndexFactoryClass GstIndexFactoryClass;
+
+/**
+ * GstIndexFactory:
+ *
+ * The GstIndexFactory object
+ */
+struct _GstIndexFactory {
+ GstPluginFeature feature;
+
+ gchar *longdesc; /* long description of the index (well, don't overdo it..) */
+ GType type; /* unique GType of the index */
+
+ gpointer _gst_reserved[GST_PADDING];
+};
+
+struct _GstIndexFactoryClass {
+ GstPluginFeatureClass parent;
+
+ gpointer _gst_reserved[GST_PADDING];
+};
+
+GType gst_index_factory_get_type (void);
+
+GstIndexFactory* gst_index_factory_new (const gchar *name,
+ const gchar *longdesc, GType type);
+void gst_index_factory_destroy (GstIndexFactory *factory);
+
+GstIndexFactory* gst_index_factory_find (const gchar *name);
+
+GstIndex* gst_index_factory_create (GstIndexFactory *factory);
+GstIndex* gst_index_factory_make (const gchar *name);
+
+G_END_DECLS
+
+#endif /* __GST_INDEX_FACTORY_H__ */
diff --git a/gst/gstinfo.c b/gst/gstinfo.c
new file mode 100644
index 0000000..1a62b83
--- /dev/null
+++ b/gst/gstinfo.c
@@ -0,0 +1,2003 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2000 Wim Taymans <wtay@chello.be>
+ * 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
+ * Copyright (C) 2008-2009 Tim-Philipp Müller <tim centricular net>
+ *
+ * gstinfo.c: debugging functions
+ *
+ * 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:gstinfo
+ * @short_description: Debugging and logging facilities
+ * @see_also: #gstreamer-gstconfig, #gstreamer-Gst for command line parameters
+ * and environment variables that affect the debugging output.
+ *
+ * GStreamer's debugging subsystem is an easy way to get information about what
+ * the application is doing. It is not meant for programming errors. Use GLib
+ * methods (g_warning and friends) for that.
+ *
+ * The debugging subsystem works only after GStreamer has been initialized
+ * - for example by calling gst_init().
+ *
+ * The debugging subsystem is used to log informational messages while the
+ * application runs. Each messages has some properties attached to it. Among
+ * these properties are the debugging category, the severity (called "level"
+ * here) and an optional #GObject it belongs to. Each of these messages is sent
+ * to all registered debugging handlers, which then handle the messages.
+ * GStreamer attaches a default handler on startup, which outputs requested
+ * messages to stderr.
+ *
+ * Messages are output by using shortcut macros like #GST_DEBUG,
+ * #GST_CAT_ERROR_OBJECT or similar. These all expand to calling gst_debug_log()
+ * with the right parameters.
+ * The only thing a developer will probably want to do is define his own
+ * categories. This is easily done with 3 lines. At the top of your code,
+ * declare
+ * the variables and set the default category.
+ * <informalexample>
+ * <programlisting>
+ * GST_DEBUG_CATEGORY_STATIC (my_category); // define category (statically)
+ * &hash;define GST_CAT_DEFAULT my_category // set as default
+ * </programlisting>
+ * </informalexample>
+ * After that you only need to initialize the category.
+ * <informalexample>
+ * <programlisting>
+ * GST_DEBUG_CATEGORY_INIT (my_category, "my category",
+ * 0, "This is my very own");
+ * </programlisting>
+ * </informalexample>
+ * Initialization must be done before the category is used first.
+ * Plugins do this
+ * in their plugin_init function, libraries and applications should do that
+ * during their initialization.
+ *
+ * The whole debugging subsystem can be disabled at build time with passing the
+ * --disable-gst-debug switch to configure. If this is done, every function,
+ * macro and even structs described in this file evaluate to default values or
+ * nothing at all.
+ * So don't take addresses of these functions or use other tricks.
+ * If you must do that for some reason, there is still an option.
+ * If the debugging
+ * subsystem was compiled out, #GST_DISABLE_GST_DEBUG is defined in
+ * &lt;gst/gst.h&gt;,
+ * so you can check that before doing your trick.
+ * Disabling the debugging subsystem will give you a slight (read: unnoticeable)
+ * speed increase and will reduce the size of your compiled code. The GStreamer
+ * library itself becomes around 10% smaller.
+ *
+ * Please note that there are naming conventions for the names of debugging
+ * categories. These are explained at GST_DEBUG_CATEGORY_INIT().
+ */
+
+#define GST_INFO_C
+#include "gst_private.h"
+#include "gstinfo.h"
+
+#undef gst_debug_remove_log_function
+#undef gst_debug_add_log_function
+
+#ifndef GST_DISABLE_GST_DEBUG
+
+#ifdef HAVE_DLFCN_H
+# include <dlfcn.h>
+#endif
+#ifdef HAVE_PRINTF_EXTENSION
+# include <printf.h>
+#endif
+#include <stdio.h> /* fprintf */
+#include <glib/gstdio.h>
+#include <errno.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h> /* getpid on UNIX */
+#endif
+#ifdef HAVE_PROCESS_H
+# include <process.h> /* getpid on win32 */
+#endif
+#include <string.h> /* G_VA_COPY */
+#ifdef G_OS_WIN32
+# define WIN32_LEAN_AND_MEAN /* prevents from including too many things */
+# include <windows.h> /* GetStdHandle, windows console */
+#endif
+
+#include "gst_private.h"
+#include "gstutils.h"
+#include "gstquark.h"
+#include "gstsegment.h"
+#ifdef HAVE_VALGRIND_VALGRIND_H
+# include <valgrind/valgrind.h>
+#endif
+#include <glib/gprintf.h> /* g_sprintf */
+
+#endif /* !GST_DISABLE_GST_DEBUG */
+
+/* we want these symbols exported even if debug is disabled, to maintain
+ * ABI compatibility. Unless GST_REMOVE_DISABLED is defined. */
+#if !defined(GST_DISABLE_GST_DEBUG) || !defined(GST_REMOVE_DISABLED)
+
+/* disabled by default, as soon as some threshold is set > NONE,
+ * it becomes enabled. */
+gboolean __gst_debug_enabled = FALSE;
+GstDebugLevel __gst_debug_min = GST_LEVEL_NONE;
+
+GstDebugCategory *GST_CAT_DEFAULT = NULL;
+
+GstDebugCategory *GST_CAT_GST_INIT = NULL;
+GstDebugCategory *GST_CAT_AUTOPLUG = NULL;
+GstDebugCategory *GST_CAT_AUTOPLUG_ATTEMPT = NULL;
+GstDebugCategory *GST_CAT_PARENTAGE = NULL;
+GstDebugCategory *GST_CAT_STATES = NULL;
+GstDebugCategory *GST_CAT_SCHEDULING = NULL;
+
+GstDebugCategory *GST_CAT_BUFFER = NULL;
+GstDebugCategory *GST_CAT_BUFFER_LIST = NULL;
+GstDebugCategory *GST_CAT_BUS = NULL;
+GstDebugCategory *GST_CAT_CAPS = NULL;
+GstDebugCategory *GST_CAT_CLOCK = NULL;
+GstDebugCategory *GST_CAT_ELEMENT_PADS = NULL;
+GstDebugCategory *GST_CAT_PADS = NULL;
+GstDebugCategory *GST_CAT_PERFORMANCE = NULL;
+GstDebugCategory *GST_CAT_PIPELINE = NULL;
+GstDebugCategory *GST_CAT_PLUGIN_LOADING = NULL;
+GstDebugCategory *GST_CAT_PLUGIN_INFO = NULL;
+GstDebugCategory *GST_CAT_PROPERTIES = NULL;
+GstDebugCategory *GST_CAT_TYPES = NULL;
+GstDebugCategory *GST_CAT_NEGOTIATION = NULL;
+GstDebugCategory *GST_CAT_REFCOUNTING = NULL;
+GstDebugCategory *GST_CAT_ERROR_SYSTEM = NULL;
+GstDebugCategory *GST_CAT_EVENT = NULL;
+GstDebugCategory *GST_CAT_MESSAGE = NULL;
+GstDebugCategory *GST_CAT_PARAMS = NULL;
+GstDebugCategory *GST_CAT_CALL_TRACE = NULL;
+GstDebugCategory *GST_CAT_SIGNAL = NULL;
+GstDebugCategory *GST_CAT_PROBE = NULL;
+GstDebugCategory *GST_CAT_REGISTRY = NULL;
+GstDebugCategory *GST_CAT_QOS = NULL;
+GstDebugCategory *_priv_GST_CAT_POLL = NULL;
+
+
+#endif /* !defined(GST_DISABLE_GST_DEBUG) || !defined(GST_REMOVE_DISABLED) */
+
+#ifndef GST_DISABLE_GST_DEBUG
+
+/* underscore is to prevent conflict with GST_CAT_DEBUG define */
+GST_DEBUG_CATEGORY_STATIC (_GST_CAT_DEBUG);
+
+/* time of initialization, so we get useful debugging output times
+ * FIXME: we use this in gstdebugutils.c, what about a function + macro to
+ * get the running time: GST_DEBUG_RUNNING_TIME
+ */
+GstClockTime _priv_gst_info_start_time;
+
+#if 0
+#if defined __sgi__
+#include <rld_interface.h>
+typedef struct DL_INFO
+{
+ const char *dli_fname;
+ void *dli_fbase;
+ const char *dli_sname;
+ void *dli_saddr;
+ int dli_version;
+ int dli_reserved1;
+ long dli_reserved[4];
+}
+Dl_info;
+
+#define _RLD_DLADDR 14
+int dladdr (void *address, Dl_info * dl);
+
+int
+dladdr (void *address, Dl_info * dl)
+{
+ void *v;
+
+ v = _rld_new_interface (_RLD_DLADDR, address, dl);
+ return (int) v;
+}
+#endif /* __sgi__ */
+#endif
+
+static void gst_debug_reset_threshold (gpointer category, gpointer unused);
+static void gst_debug_reset_all_thresholds (void);
+
+#ifdef HAVE_PRINTF_EXTENSION
+static int _gst_info_printf_extension_ptr (FILE * stream,
+ const struct printf_info *info, const void *const *args);
+static int _gst_info_printf_extension_segment (FILE * stream,
+ const struct printf_info *info, const void *const *args);
+#ifdef HAVE_REGISTER_PRINTF_SPECIFIER
+static int _gst_info_printf_extension_arginfo (const struct printf_info *info,
+ size_t n, int *argtypes, int *size);
+#else
+static int _gst_info_printf_extension_arginfo (const struct printf_info *info,
+ size_t n, int *argtypes);
+#endif
+#endif
+
+struct _GstDebugMessage
+{
+ gchar *message;
+ const gchar *format;
+ va_list arguments;
+};
+
+/* list of all name/level pairs from --gst-debug and GST_DEBUG */
+static GStaticMutex __level_name_mutex = G_STATIC_MUTEX_INIT;
+static GSList *__level_name = NULL;
+typedef struct
+{
+ GPatternSpec *pat;
+ GstDebugLevel level;
+}
+LevelNameEntry;
+
+/* list of all categories */
+static GStaticMutex __cat_mutex = G_STATIC_MUTEX_INIT;
+static GSList *__categories = NULL;
+
+/* all registered debug handlers */
+typedef struct
+{
+ GstLogFunction func;
+ gpointer user_data;
+}
+LogFuncEntry;
+static GStaticMutex __log_func_mutex = G_STATIC_MUTEX_INIT;
+static GSList *__log_functions = NULL;
+
+#define PRETTY_TAGS_DEFAULT TRUE
+static gboolean pretty_tags = PRETTY_TAGS_DEFAULT;
+
+static volatile gint G_GNUC_MAY_ALIAS __default_level = GST_LEVEL_DEFAULT;
+static volatile gint G_GNUC_MAY_ALIAS __use_color = 1;
+
+static FILE *log_file;
+
+/* FIXME: export this? */
+gboolean
+_priv_gst_in_valgrind (void)
+{
+ static enum
+ {
+ GST_VG_UNCHECKED,
+ GST_VG_NO_VALGRIND,
+ GST_VG_INSIDE
+ }
+ in_valgrind = GST_VG_UNCHECKED;
+
+ if (in_valgrind == GST_VG_UNCHECKED) {
+#ifdef HAVE_VALGRIND_VALGRIND_H
+ if (RUNNING_ON_VALGRIND) {
+ GST_CAT_INFO (GST_CAT_GST_INIT, "we're running inside valgrind");
+ printf ("GStreamer has detected that it is running inside valgrind.\n");
+ printf ("It might now take different code paths to ease debugging.\n");
+ printf ("Of course, this may also lead to different bugs.\n");
+ in_valgrind = GST_VG_INSIDE;
+ } else {
+ GST_CAT_LOG (GST_CAT_GST_INIT, "not doing extra valgrind stuff");
+ in_valgrind = GST_VG_NO_VALGRIND;
+ }
+#else
+ in_valgrind = GST_VG_NO_VALGRIND;
+#endif
+ g_assert (in_valgrind == GST_VG_NO_VALGRIND ||
+ in_valgrind == GST_VG_INSIDE);
+ }
+ return (in_valgrind == GST_VG_INSIDE);
+}
+
+/**
+ * _gst_debug_init:
+ *
+ * Initializes the debugging system.
+ * Normally you don't want to call this, because gst_init() does it for you.
+ */
+void
+_gst_debug_init (void)
+{
+ const gchar *env;
+
+ env = g_getenv ("GST_DEBUG_FILE");
+ if (env != NULL && *env != '\0') {
+ if (strcmp (env, "-") == 0) {
+ log_file = stdout;
+ } else {
+ log_file = g_fopen (env, "w");
+ if (log_file == NULL) {
+ g_printerr ("Could not open log file '%s' for writing: %s\n", env,
+ g_strerror (errno));
+ log_file = stderr;
+ }
+ }
+ } else {
+ log_file = stderr;
+ }
+
+ /* get time we started for debugging messages */
+ _priv_gst_info_start_time = gst_util_get_timestamp ();
+
+#ifdef HAVE_PRINTF_EXTENSION
+#ifdef HAVE_REGISTER_PRINTF_SPECIFIER
+ register_printf_specifier (GST_PTR_FORMAT[0], _gst_info_printf_extension_ptr,
+ _gst_info_printf_extension_arginfo);
+ register_printf_specifier (GST_SEGMENT_FORMAT[0],
+ _gst_info_printf_extension_segment, _gst_info_printf_extension_arginfo);
+#else
+ register_printf_function (GST_PTR_FORMAT[0], _gst_info_printf_extension_ptr,
+ _gst_info_printf_extension_arginfo);
+ register_printf_function (GST_SEGMENT_FORMAT[0],
+ _gst_info_printf_extension_segment, _gst_info_printf_extension_arginfo);
+#endif
+#endif
+
+ /* do NOT use a single debug function before this line has been run */
+ GST_CAT_DEFAULT = _gst_debug_category_new ("default",
+ GST_DEBUG_UNDERLINE, NULL);
+ _GST_CAT_DEBUG = _gst_debug_category_new ("GST_DEBUG",
+ GST_DEBUG_BOLD | GST_DEBUG_FG_YELLOW, "debugging subsystem");
+
+ gst_debug_add_log_function (gst_debug_log_default, NULL);
+
+ /* FIXME: add descriptions here */
+ GST_CAT_GST_INIT = _gst_debug_category_new ("GST_INIT",
+ GST_DEBUG_BOLD | GST_DEBUG_FG_RED, NULL);
+ GST_CAT_AUTOPLUG = _gst_debug_category_new ("GST_AUTOPLUG",
+ GST_DEBUG_BOLD | GST_DEBUG_FG_BLUE, NULL);
+ GST_CAT_AUTOPLUG_ATTEMPT = _gst_debug_category_new ("GST_AUTOPLUG_ATTEMPT",
+ GST_DEBUG_BOLD | GST_DEBUG_FG_CYAN | GST_DEBUG_BG_BLUE, NULL);
+ GST_CAT_PARENTAGE = _gst_debug_category_new ("GST_PARENTAGE",
+ GST_DEBUG_BOLD | GST_DEBUG_FG_WHITE | GST_DEBUG_BG_RED, NULL);
+ GST_CAT_STATES = _gst_debug_category_new ("GST_STATES",
+ GST_DEBUG_BOLD | GST_DEBUG_FG_RED, NULL);
+ GST_CAT_SCHEDULING = _gst_debug_category_new ("GST_SCHEDULING",
+ GST_DEBUG_BOLD | GST_DEBUG_FG_MAGENTA, NULL);
+ GST_CAT_BUFFER = _gst_debug_category_new ("GST_BUFFER",
+ GST_DEBUG_BOLD | GST_DEBUG_BG_GREEN, NULL);
+ GST_CAT_BUFFER_LIST = _gst_debug_category_new ("GST_BUFFER_LIST",
+ GST_DEBUG_BOLD | GST_DEBUG_BG_GREEN, NULL);
+ GST_CAT_BUS = _gst_debug_category_new ("GST_BUS", GST_DEBUG_BG_YELLOW, NULL);
+ GST_CAT_CAPS = _gst_debug_category_new ("GST_CAPS",
+ GST_DEBUG_BOLD | GST_DEBUG_FG_BLUE, NULL);
+ GST_CAT_CLOCK = _gst_debug_category_new ("GST_CLOCK",
+ GST_DEBUG_BOLD | GST_DEBUG_FG_YELLOW, NULL);
+ GST_CAT_ELEMENT_PADS = _gst_debug_category_new ("GST_ELEMENT_PADS",
+ GST_DEBUG_BOLD | GST_DEBUG_FG_WHITE | GST_DEBUG_BG_RED, NULL);
+ GST_CAT_PADS = _gst_debug_category_new ("GST_PADS",
+ GST_DEBUG_BOLD | GST_DEBUG_FG_RED | GST_DEBUG_BG_RED, NULL);
+ GST_CAT_PERFORMANCE = _gst_debug_category_new ("GST_PERFORMANCE",
+ GST_DEBUG_BOLD | GST_DEBUG_FG_WHITE | GST_DEBUG_BG_RED, NULL);
+ GST_CAT_PIPELINE = _gst_debug_category_new ("GST_PIPELINE",
+ GST_DEBUG_BOLD | GST_DEBUG_FG_WHITE | GST_DEBUG_BG_RED, NULL);
+ GST_CAT_PLUGIN_LOADING = _gst_debug_category_new ("GST_PLUGIN_LOADING",
+ GST_DEBUG_BOLD | GST_DEBUG_FG_CYAN, NULL);
+ GST_CAT_PLUGIN_INFO = _gst_debug_category_new ("GST_PLUGIN_INFO",
+ GST_DEBUG_BOLD | GST_DEBUG_FG_CYAN, NULL);
+ GST_CAT_PROPERTIES = _gst_debug_category_new ("GST_PROPERTIES",
+ GST_DEBUG_BOLD | GST_DEBUG_FG_WHITE | GST_DEBUG_BG_BLUE, NULL);
+ GST_CAT_TYPES = _gst_debug_category_new ("GST_TYPES",
+ GST_DEBUG_BOLD | GST_DEBUG_FG_WHITE | GST_DEBUG_BG_RED, NULL);
+ GST_CAT_NEGOTIATION = _gst_debug_category_new ("GST_NEGOTIATION",
+ GST_DEBUG_BOLD | GST_DEBUG_FG_BLUE, NULL);
+ GST_CAT_REFCOUNTING = _gst_debug_category_new ("GST_REFCOUNTING",
+ GST_DEBUG_BOLD | GST_DEBUG_FG_RED | GST_DEBUG_BG_BLUE, NULL);
+ GST_CAT_ERROR_SYSTEM = _gst_debug_category_new ("GST_ERROR_SYSTEM",
+ GST_DEBUG_BOLD | GST_DEBUG_FG_RED | GST_DEBUG_BG_WHITE, NULL);
+
+ GST_CAT_EVENT = _gst_debug_category_new ("GST_EVENT",
+ GST_DEBUG_BOLD | GST_DEBUG_FG_BLUE, NULL);
+ GST_CAT_MESSAGE = _gst_debug_category_new ("GST_MESSAGE",
+ GST_DEBUG_BOLD | GST_DEBUG_FG_WHITE | GST_DEBUG_BG_RED, NULL);
+ GST_CAT_PARAMS = _gst_debug_category_new ("GST_PARAMS",
+ GST_DEBUG_BOLD | GST_DEBUG_FG_BLACK | GST_DEBUG_BG_YELLOW, NULL);
+ GST_CAT_CALL_TRACE = _gst_debug_category_new ("GST_CALL_TRACE",
+ GST_DEBUG_BOLD, NULL);
+ GST_CAT_SIGNAL = _gst_debug_category_new ("GST_SIGNAL",
+ GST_DEBUG_BOLD | GST_DEBUG_FG_WHITE | GST_DEBUG_BG_RED, NULL);
+ GST_CAT_PROBE = _gst_debug_category_new ("GST_PROBE",
+ GST_DEBUG_BOLD | GST_DEBUG_FG_GREEN, "pad probes");
+ GST_CAT_REGISTRY = _gst_debug_category_new ("GST_REGISTRY", 0, "registry");
+ GST_CAT_QOS = _gst_debug_category_new ("GST_QOS", 0, "QoS");
+ _priv_GST_CAT_POLL = _gst_debug_category_new ("GST_POLL", 0, "poll");
+
+
+ /* print out the valgrind message if we're in valgrind */
+ _priv_gst_in_valgrind ();
+
+ env = g_getenv ("GST_DEBUG_OPTIONS");
+ if (env != NULL) {
+ if (strstr (env, "full_tags") || strstr (env, "full-tags"))
+ pretty_tags = FALSE;
+ else if (strstr (env, "pretty_tags") || strstr (env, "pretty-tags"))
+ pretty_tags = TRUE;
+ }
+}
+
+/* we can't do this further above, because we initialize the GST_CAT_DEFAULT struct */
+#define GST_CAT_DEFAULT _GST_CAT_DEBUG
+
+/**
+ * gst_debug_log:
+ * @category: category to log
+ * @level: level of the message is in
+ * @file: the file that emitted the message, usually the __FILE__ identifier
+ * @function: the function that emitted the message
+ * @line: the line from that the message was emitted, usually __LINE__
+ * @object: (transfer none) (allow-none): the object this message relates to,
+ * or NULL if none
+ * @format: a printf style format string
+ * @...: optional arguments for the format
+ *
+ * Logs the given message using the currently registered debugging handlers.
+ */
+void
+gst_debug_log (GstDebugCategory * category, GstDebugLevel level,
+ const gchar * file, const gchar * function, gint line,
+ GObject * object, const gchar * format, ...)
+{
+ va_list var_args;
+
+ va_start (var_args, format);
+ gst_debug_log_valist (category, level, file, function, line, object, format,
+ var_args);
+ va_end (var_args);
+}
+
+#ifdef _MSC_VER
+/* based on g_basename(), which we can't use because it was deprecated */
+static inline const gchar *
+gst_path_basename (const gchar * file_name)
+{
+ register const gchar *base;
+
+ base = strrchr (file_name, G_DIR_SEPARATOR);
+
+ {
+ const gchar *q = strrchr (file_name, '/');
+ if (base == NULL || (q != NULL && q > base))
+ base = q;
+ }
+
+ if (base)
+ return base + 1;
+
+ if (g_ascii_isalpha (file_name[0]) && file_name[1] == ':')
+ return file_name + 2;
+
+ return file_name;
+}
+#endif
+
+/**
+ * gst_debug_log_valist:
+ * @category: category to log
+ * @level: level of the message is in
+ * @file: the file that emitted the message, usually the __FILE__ identifier
+ * @function: the function that emitted the message
+ * @line: the line from that the message was emitted, usually __LINE__
+ * @object: (transfer none) (allow-none): the object this message relates to,
+ * or NULL if none
+ * @format: a printf style format string
+ * @args: optional arguments for the format
+ *
+ * Logs the given message using the currently registered debugging handlers.
+ */
+void
+gst_debug_log_valist (GstDebugCategory * category, GstDebugLevel level,
+ const gchar * file, const gchar * function, gint line,
+ GObject * object, const gchar * format, va_list args)
+{
+ GstDebugMessage message;
+ LogFuncEntry *entry;
+ GSList *handler;
+
+ g_return_if_fail (category != NULL);
+ g_return_if_fail (file != NULL);
+ g_return_if_fail (function != NULL);
+ g_return_if_fail (format != NULL);
+
+ /* The predefined macro __FILE__ is always the exact path given to the
+ * compiler with MSVC, which may or may not be the basename. We work
+ * around it at runtime to improve the readability. */
+#ifdef _MSC_VER
+ file = gst_path_basename (file);
+#endif
+
+ message.message = NULL;
+ message.format = format;
+ G_VA_COPY (message.arguments, args);
+
+ handler = __log_functions;
+ while (handler) {
+ entry = handler->data;
+ handler = g_slist_next (handler);
+ entry->func (category, level, file, function, line, object, &message,
+ entry->user_data);
+ }
+ g_free (message.message);
+ va_end (message.arguments);
+}
+
+/**
+ * gst_debug_message_get:
+ * @message: a debug message
+ *
+ * Gets the string representation of a #GstDebugMessage. This function is used
+ * in debug handlers to extract the message.
+ *
+ * Returns: the string representation of a #GstDebugMessage.
+ */
+const gchar *
+gst_debug_message_get (GstDebugMessage * message)
+{
+ if (message->message == NULL) {
+ message->message = g_strdup_vprintf (message->format, message->arguments);
+ }
+ return message->message;
+}
+
+#define MAX_BUFFER_DUMP_STRING_LEN 100
+
+/* structure_to_pretty_string:
+ * @structure: a #GstStructure
+ *
+ * Converts @structure to a human-readable string representation. Basically
+ * the same as gst_structure_to_string(), but if the structure contains large
+ * buffers such as images the hex representation of those buffers will be
+ * shortened so that the string remains readable.
+ *
+ * Returns: a newly-allocated string. g_free() when no longer needed.
+ */
+static gchar *
+structure_to_pretty_string (const GstStructure * s)
+{
+ gchar *str, *pos, *end;
+
+ str = gst_structure_to_string (s);
+ if (str == NULL)
+ return NULL;
+
+ pos = str;
+ while ((pos = strstr (pos, "(buffer)"))) {
+ guint count = 0;
+
+ pos += strlen ("(buffer)");
+ for (end = pos; *end != '\0' && *end != ';' && *end != ' '; ++end)
+ ++count;
+ if (count > MAX_BUFFER_DUMP_STRING_LEN) {
+ memcpy (pos + MAX_BUFFER_DUMP_STRING_LEN - 6, "..", 2);
+ memcpy (pos + MAX_BUFFER_DUMP_STRING_LEN - 4, pos + count - 4, 4);
+ g_memmove (pos + MAX_BUFFER_DUMP_STRING_LEN, pos + count,
+ strlen (pos + count) + 1);
+ pos += MAX_BUFFER_DUMP_STRING_LEN;
+ }
+ }
+
+ return str;
+}
+
+static inline gchar *
+gst_info_structure_to_string (const GstStructure * s)
+{
+ if (G_UNLIKELY (pretty_tags && s->name == GST_QUARK (TAGLIST)))
+ return structure_to_pretty_string (s);
+ else
+ return gst_structure_to_string (s);
+}
+
+static gchar *
+gst_debug_print_object (gpointer ptr)
+{
+ GObject *object = (GObject *) ptr;
+
+#ifdef unused
+ /* This is a cute trick to detect unmapped memory, but is unportable,
+ * slow, screws around with madvise, and not actually that useful. */
+ {
+ int ret;
+
+ ret = madvise ((void *) ((unsigned long) ptr & (~0xfff)), 4096, 0);
+ if (ret == -1 && errno == ENOMEM) {
+ buffer = g_strdup_printf ("%p (unmapped memory)", ptr);
+ }
+ }
+#endif
+
+ /* nicely printed object */
+ if (object == NULL) {
+ return g_strdup ("(NULL)");
+ }
+ if (*(GType *) ptr == GST_TYPE_CAPS) {
+ return gst_caps_to_string ((const GstCaps *) ptr);
+ }
+ if (*(GType *) ptr == GST_TYPE_STRUCTURE) {
+ return gst_info_structure_to_string ((const GstStructure *) ptr);
+ }
+#ifdef USE_POISONING
+ if (*(guint32 *) ptr == 0xffffffff) {
+ return g_strdup_printf ("<poisoned@%p>", ptr);
+ }
+#endif
+ if (GST_IS_PAD (object) && GST_OBJECT_NAME (object)) {
+ return g_strdup_printf ("<%s:%s>", GST_DEBUG_PAD_NAME (object));
+ }
+ if (GST_IS_OBJECT (object) && GST_OBJECT_NAME (object)) {
+ return g_strdup_printf ("<%s>", GST_OBJECT_NAME (object));
+ }
+ if (G_IS_OBJECT (object)) {
+ return g_strdup_printf ("<%s@%p>", G_OBJECT_TYPE_NAME (object), object);
+ }
+ if (GST_IS_MESSAGE (object)) {
+ GstMessage *msg = GST_MESSAGE_CAST (object);
+ gchar *s, *ret;
+ const GstStructure *structure;
+
+ structure = gst_message_get_structure (msg);
+
+ if (structure) {
+ s = gst_info_structure_to_string (structure);
+ } else {
+ s = g_strdup ("(NULL)");
+ }
+
+ ret = g_strdup_printf ("%s message from element '%s': %s",
+ GST_MESSAGE_TYPE_NAME (msg), (msg->src != NULL) ?
+ GST_ELEMENT_NAME (msg->src) : "(NULL)", s);
+ g_free (s);
+ return ret;
+ }
+ if (GST_IS_QUERY (object)) {
+ GstQuery *query = GST_QUERY_CAST (object);
+ const GstStructure *structure;
+
+ structure = gst_query_get_structure (query);
+
+ if (structure) {
+ return gst_info_structure_to_string (structure);
+ } else {
+ const gchar *query_type_name;
+
+ query_type_name = gst_query_type_get_name (query->type);
+ if (G_LIKELY (query_type_name != NULL)) {
+ return g_strdup_printf ("%s query", query_type_name);
+ } else {
+ return g_strdup_printf ("query of unknown type %d", query->type);
+ }
+ }
+ }
+ if (GST_IS_EVENT (object)) {
+ GstEvent *event = GST_EVENT_CAST (object);
+ gchar *s, *ret;
+ GstStructure *structure;
+
+ structure = (GstStructure *) gst_event_get_structure (event);
+ if (structure) {
+ s = gst_info_structure_to_string (structure);
+ } else {
+ s = g_strdup ("(NULL)");
+ }
+
+ ret = g_strdup_printf ("%s event at time %"
+ GST_TIME_FORMAT ": %s",
+ GST_EVENT_TYPE_NAME (event), GST_TIME_ARGS (event->timestamp), s);
+ g_free (s);
+ return ret;
+ }
+
+ return g_strdup_printf ("%p", ptr);
+}
+
+#ifdef HAVE_PRINTF_EXTENSION
+
+static gchar *
+gst_debug_print_segment (gpointer ptr)
+{
+ GstSegment *segment = (GstSegment *) ptr;
+
+ /* nicely printed segment */
+ if (segment == NULL) {
+ return g_strdup ("(NULL)");
+ }
+
+ switch (segment->format) {
+ case GST_FORMAT_UNDEFINED:{
+ return g_strdup_printf ("UNDEFINED segment");
+ }
+ case GST_FORMAT_TIME:{
+ return g_strdup_printf ("time segment start=%" GST_TIME_FORMAT
+ ", stop=%" GST_TIME_FORMAT ", rate=%f, applied_rate=%f"
+ ", flags=0x%02x, time=%" GST_TIME_FORMAT ", base=%" GST_TIME_FORMAT,
+ GST_TIME_ARGS (segment->start), GST_TIME_ARGS (segment->stop),
+ segment->rate, segment->applied_rate, (guint) segment->flags,
+ GST_TIME_ARGS (segment->time), GST_TIME_ARGS (segment->base));
+ }
+ default:{
+ const gchar *format_name;
+
+ format_name = gst_format_get_name (segment->format);
+ if (G_UNLIKELY (format_name == NULL))
+ format_name = "(UNKNOWN FORMAT)";
+ return g_strdup_printf ("%s segment start=%" G_GINT64_FORMAT
+ ", stop=%" G_GINT64_FORMAT ", rate=%f, applied_rate=%f"
+ ", flags=0x%02x, time=%" GST_TIME_FORMAT ", base=%" GST_TIME_FORMAT,
+ format_name, segment->start, segment->stop, segment->rate,
+ segment->applied_rate, (guint) segment->flags,
+ GST_TIME_ARGS (segment->time), GST_TIME_ARGS (segment->base));
+ }
+ }
+}
+
+#endif /* HAVE_PRINTF_EXTENSION */
+
+/**
+ * gst_debug_construct_term_color:
+ * @colorinfo: the color info
+ *
+ * Constructs a string that can be used for getting the desired color in color
+ * terminals.
+ * You need to free the string after use.
+ *
+ * Returns: (transfer full) (type gchar*): a string containing the color
+ * definition
+ */
+gchar *
+gst_debug_construct_term_color (guint colorinfo)
+{
+ GString *color;
+
+ color = g_string_new ("\033[00");
+
+ if (colorinfo & GST_DEBUG_BOLD) {
+ g_string_append_len (color, ";01", 3);
+ }
+ if (colorinfo & GST_DEBUG_UNDERLINE) {
+ g_string_append_len (color, ";04", 3);
+ }
+ if (colorinfo & GST_DEBUG_FG_MASK) {
+ g_string_append_printf (color, ";3%1d", colorinfo & GST_DEBUG_FG_MASK);
+ }
+ if (colorinfo & GST_DEBUG_BG_MASK) {
+ g_string_append_printf (color, ";4%1d",
+ (colorinfo & GST_DEBUG_BG_MASK) >> 4);
+ }
+ g_string_append_c (color, 'm');
+
+ return g_string_free (color, FALSE);
+}
+
+/**
+ * gst_debug_construct_win_color:
+ * @colorinfo: the color info
+ *
+ * Constructs an integer that can be used for getting the desired color in
+ * windows' terminals (cmd.exe). As there is no mean to underline, we simply
+ * ignore this attribute.
+ *
+ * This function returns 0 on non-windows machines.
+ *
+ * Returns: an integer containing the color definition
+ *
+ * Since: 0.10.23
+ */
+gint
+gst_debug_construct_win_color (guint colorinfo)
+{
+ gint color = 0;
+#ifdef G_OS_WIN32
+ static const guchar ansi_to_win_fg[8] = {
+ 0, /* black */
+ FOREGROUND_RED, /* red */
+ FOREGROUND_GREEN, /* green */
+ FOREGROUND_RED | FOREGROUND_GREEN, /* yellow */
+ FOREGROUND_BLUE, /* blue */
+ FOREGROUND_RED | FOREGROUND_BLUE, /* magenta */
+ FOREGROUND_GREEN | FOREGROUND_BLUE, /* cyan */
+ FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE /* white */
+ };
+ static const guchar ansi_to_win_bg[8] = {
+ 0,
+ BACKGROUND_RED,
+ BACKGROUND_GREEN,
+ BACKGROUND_RED | BACKGROUND_GREEN,
+ BACKGROUND_BLUE,
+ BACKGROUND_RED | BACKGROUND_BLUE,
+ BACKGROUND_GREEN | FOREGROUND_BLUE,
+ BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE
+ };
+
+ /* we draw black as white, as cmd.exe can only have black bg */
+ if (colorinfo == 0) {
+ return ansi_to_win_fg[7];
+ }
+
+ if (colorinfo & GST_DEBUG_BOLD) {
+ color |= FOREGROUND_INTENSITY;
+ }
+ if (colorinfo & GST_DEBUG_FG_MASK) {
+ color |= ansi_to_win_fg[colorinfo & GST_DEBUG_FG_MASK];
+ }
+ if (colorinfo & GST_DEBUG_BG_MASK) {
+ color |= ansi_to_win_bg[(colorinfo & GST_DEBUG_BG_MASK) >> 4];
+ }
+#endif
+ return color;
+}
+
+/* width of %p varies depending on actual value of pointer, which can make
+ * output unevenly aligned if multiple threads are involved, hence the %14p
+ * (should really be %18p, but %14p seems a good compromise between too many
+ * white spaces and likely unalignment on my system) */
+#if defined (GLIB_SIZEOF_VOID_P) && GLIB_SIZEOF_VOID_P == 8
+#define PTR_FMT "%14p"
+#else
+#define PTR_FMT "%10p"
+#endif
+#define PID_FMT "%5d"
+#define CAT_FMT "%20s %s:%d:%s:%s"
+
+#ifdef G_OS_WIN32
+static const guchar levelcolormap[GST_LEVEL_COUNT] = {
+ /* GST_LEVEL_NONE */
+ FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE,
+ /* GST_LEVEL_ERROR */
+ FOREGROUND_RED | FOREGROUND_INTENSITY,
+ /* GST_LEVEL_WARNING */
+ FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY,
+ /* GST_LEVEL_INFO */
+ FOREGROUND_GREEN | FOREGROUND_INTENSITY,
+ /* GST_LEVEL_DEBUG */
+ FOREGROUND_GREEN | FOREGROUND_BLUE,
+ /* GST_LEVEL_LOG */
+ FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE,
+ /* GST_LEVEL_FIXME */
+ FOREGROUND_RED | FOREGROUND_GREEN,
+ /* GST_LEVEL_TRACE */
+ FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE,
+ /* placeholder for log level 8 */
+ 0,
+ /* GST_LEVEL_MEMDUMP */
+ FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
+};
+
+static const guchar available_colors[] = {
+ FOREGROUND_RED, FOREGROUND_GREEN, FOREGROUND_RED | FOREGROUND_GREEN,
+ FOREGROUND_BLUE, FOREGROUND_RED | FOREGROUND_BLUE,
+ FOREGROUND_GREEN | FOREGROUND_BLUE,
+};
+#else
+static const gchar *levelcolormap[GST_LEVEL_COUNT] = {
+ "\033[37m", /* GST_LEVEL_NONE */
+ "\033[31;01m", /* GST_LEVEL_ERROR */
+ "\033[33;01m", /* GST_LEVEL_WARNING */
+ "\033[32;01m", /* GST_LEVEL_INFO */
+ "\033[36m", /* GST_LEVEL_DEBUG */
+ "\033[37m", /* GST_LEVEL_LOG */
+ "\033[33;01m", /* GST_LEVEL_FIXME */
+ "\033[37m", /* GST_LEVEL_TRACE */
+ "\033[37m", /* placeholder for log level 8 */
+ "\033[37m" /* GST_LEVEL_MEMDUMP */
+};
+#endif
+
+/**
+ * gst_debug_log_default:
+ * @category: category to log
+ * @level: level of the message
+ * @file: the file that emitted the message, usually the __FILE__ identifier
+ * @function: the function that emitted the message
+ * @line: the line from that the message was emitted, usually __LINE__
+ * @message: the actual message
+ * @object: (transfer none) (allow-none): the object this message relates to,
+ * or NULL if none
+ * @unused: an unused variable, reserved for some user_data.
+ *
+ * The default logging handler used by GStreamer. Logging functions get called
+ * whenever a macro like GST_DEBUG or similar is used. This function outputs the
+ * message and additional info to stderr (or the log file specified via the
+ * GST_DEBUG_FILE environment variable).
+ *
+ * You can add other handlers by using gst_debug_add_log_function().
+ * And you can remove this handler by calling
+ * gst_debug_remove_log_function(gst_debug_log_default);
+ */
+void
+gst_debug_log_default (GstDebugCategory * category, GstDebugLevel level,
+ const gchar * file, const gchar * function, gint line,
+ GObject * object, GstDebugMessage * message, gpointer unused)
+{
+ gint pid;
+ GstClockTime elapsed;
+ gchar *obj = NULL;
+ gboolean is_colored;
+
+ if (level > gst_debug_category_get_threshold (category))
+ return;
+
+ pid = getpid ();
+ is_colored = gst_debug_is_colored ();
+
+ if (object) {
+ obj = gst_debug_print_object (object);
+ } else {
+ obj = g_strdup ("");
+ }
+
+ elapsed = GST_CLOCK_DIFF (_priv_gst_info_start_time,
+ gst_util_get_timestamp ());
+
+ if (is_colored) {
+#ifndef G_OS_WIN32
+ /* colors, non-windows */
+ gchar *color = NULL;
+ const gchar *clear;
+ gchar pidcolor[10];
+ const gchar *levelcolor;
+
+ color = gst_debug_construct_term_color (gst_debug_category_get_color
+ (category));
+ clear = "\033[00m";
+ g_sprintf (pidcolor, "\033[3%1dm", pid % 6 + 31);
+ levelcolor = levelcolormap[level];
+
+#define PRINT_FMT " %s"PID_FMT"%s "PTR_FMT" %s%s%s %s"CAT_FMT"%s %s\n"
+ fprintf (log_file, "%" GST_TIME_FORMAT PRINT_FMT, GST_TIME_ARGS (elapsed),
+ pidcolor, pid, clear, g_thread_self (), levelcolor,
+ gst_debug_level_get_name (level), clear, color,
+ gst_debug_category_get_name (category), file, line, function, obj,
+ clear, gst_debug_message_get (message));
+ fflush (log_file);
+#undef PRINT_FMT
+ g_free (color);
+#else
+ /* colors, windows. We take a lock to keep colors and content together.
+ * Maybe there is a better way but for now this will do the right
+ * thing. */
+ static GStaticMutex win_print_mutex = G_STATIC_MUTEX_INIT;
+ const gint clear = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
+#define SET_COLOR(c) G_STMT_START { \
+ if (log_file == stderr) \
+ SetConsoleTextAttribute (GetStdHandle (STD_ERROR_HANDLE), (c)); \
+ } G_STMT_END
+ g_static_mutex_lock (&win_print_mutex);
+ /* timestamp */
+ fprintf (log_file, "%" GST_TIME_FORMAT " ", GST_TIME_ARGS (elapsed));
+ fflush (log_file);
+ /* pid */
+ SET_COLOR (available_colors[pid % G_N_ELEMENTS (available_colors)]);
+ fprintf (log_file, PID_FMT, pid);
+ fflush (log_file);
+ /* thread */
+ SET_COLOR (clear);
+ fprintf (log_file, " " PTR_FMT " ", g_thread_self ());
+ fflush (log_file);
+ /* level */
+ SET_COLOR (levelcolormap[level]);
+ fprintf (log_file, "%s ", gst_debug_level_get_name (level));
+ fflush (log_file);
+ /* category */
+ SET_COLOR (gst_debug_construct_win_color (gst_debug_category_get_color
+ (category)));
+ fprintf (log_file, CAT_FMT, gst_debug_category_get_name (category),
+ file, line, function, obj);
+ fflush (log_file);
+ /* message */
+ SET_COLOR (clear);
+ fprintf (log_file, " %s\n", gst_debug_message_get (message));
+ fflush (log_file);
+ g_static_mutex_unlock (&win_print_mutex);
+#endif
+ } else {
+ /* no color, all platforms */
+#define PRINT_FMT " "PID_FMT" "PTR_FMT" %s "CAT_FMT" %s\n"
+ fprintf (log_file, "%" GST_TIME_FORMAT PRINT_FMT, GST_TIME_ARGS (elapsed),
+ pid, g_thread_self (), gst_debug_level_get_name (level),
+ gst_debug_category_get_name (category), file, line, function, obj,
+ gst_debug_message_get (message));
+ fflush (log_file);
+#undef PRINT_FMT
+ }
+
+ g_free (obj);
+}
+
+/**
+ * gst_debug_level_get_name:
+ * @level: the level to get the name for
+ *
+ * Get the string representation of a debugging level
+ *
+ * Returns: the name
+ */
+const gchar *
+gst_debug_level_get_name (GstDebugLevel level)
+{
+ switch (level) {
+ case GST_LEVEL_NONE:
+ return "";
+ case GST_LEVEL_ERROR:
+ return "ERROR ";
+ case GST_LEVEL_WARNING:
+ return "WARN ";
+ case GST_LEVEL_INFO:
+ return "INFO ";
+ case GST_LEVEL_DEBUG:
+ return "DEBUG ";
+ case GST_LEVEL_LOG:
+ return "LOG ";
+ case GST_LEVEL_FIXME:
+ return "FIXME ";
+ case GST_LEVEL_TRACE:
+ return "TRACE ";
+ case GST_LEVEL_MEMDUMP:
+ return "MEMDUMP";
+ default:
+ g_warning ("invalid level specified for gst_debug_level_get_name");
+ return "";
+ }
+}
+
+/**
+ * gst_debug_add_log_function:
+ * @func: the function to use
+ * @data: (closure): user data
+ *
+ * Adds the logging function to the list of logging functions.
+ * Be sure to use #G_GNUC_NO_INSTRUMENT on that function, it is needed.
+ */
+void
+gst_debug_add_log_function (GstLogFunction func, gpointer data)
+{
+ LogFuncEntry *entry;
+ GSList *list;
+
+ if (func == NULL)
+ func = gst_debug_log_default;
+
+ entry = g_slice_new (LogFuncEntry);
+ entry->func = func;
+ entry->user_data = data;
+ /* FIXME: we leak the old list here - other threads might access it right now
+ * in gst_debug_logv. Another solution is to lock the mutex in gst_debug_logv,
+ * but that is waaay costly.
+ * It'd probably be clever to use some kind of RCU here, but I don't know
+ * anything about that.
+ */
+ g_static_mutex_lock (&__log_func_mutex);
+ list = g_slist_copy (__log_functions);
+ __log_functions = g_slist_prepend (list, entry);
+ g_static_mutex_unlock (&__log_func_mutex);
+
+ GST_DEBUG ("prepended log function %p (user data %p) to log functions",
+ func, data);
+}
+
+static gint
+gst_debug_compare_log_function_by_func (gconstpointer entry, gconstpointer func)
+{
+ gpointer entryfunc = (gpointer) (((LogFuncEntry *) entry)->func);
+
+ return (entryfunc < func) ? -1 : (entryfunc > func) ? 1 : 0;
+}
+
+static gint
+gst_debug_compare_log_function_by_data (gconstpointer entry, gconstpointer data)
+{
+ gpointer entrydata = ((LogFuncEntry *) entry)->user_data;
+
+ return (entrydata < data) ? -1 : (entrydata > data) ? 1 : 0;
+}
+
+static guint
+gst_debug_remove_with_compare_func (GCompareFunc func, gpointer data)
+{
+ GSList *found;
+ GSList *new;
+ guint removals = 0;
+
+ g_static_mutex_lock (&__log_func_mutex);
+ new = __log_functions;
+ while ((found = g_slist_find_custom (new, data, func))) {
+ if (new == __log_functions) {
+ /* make a copy when we have the first hit, so that we modify the copy and
+ * make that the new list later */
+ new = g_slist_copy (new);
+ continue;
+ }
+ g_slice_free (LogFuncEntry, found->data);
+ new = g_slist_delete_link (new, found);
+ removals++;
+ }
+ /* FIXME: We leak the old list here. See _add_log_function for why. */
+ __log_functions = new;
+ g_static_mutex_unlock (&__log_func_mutex);
+
+ return removals;
+}
+
+/**
+ * gst_debug_remove_log_function:
+ * @func: the log function to remove
+ *
+ * Removes all registered instances of the given logging functions.
+ *
+ * Returns: How many instances of the function were removed
+ */
+guint
+gst_debug_remove_log_function (GstLogFunction func)
+{
+ guint removals;
+
+ if (func == NULL)
+ func = gst_debug_log_default;
+
+ removals =
+ gst_debug_remove_with_compare_func
+ (gst_debug_compare_log_function_by_func, (gpointer) func);
+ GST_DEBUG ("removed log function %p %d times from log function list", func,
+ removals);
+
+ return removals;
+}
+
+/**
+ * gst_debug_remove_log_function_by_data:
+ * @data: user data of the log function to remove
+ *
+ * Removes all registered instances of log functions with the given user data.
+ *
+ * Returns: How many instances of the function were removed
+ */
+guint
+gst_debug_remove_log_function_by_data (gpointer data)
+{
+ guint removals;
+
+ removals =
+ gst_debug_remove_with_compare_func
+ (gst_debug_compare_log_function_by_data, data);
+ GST_DEBUG
+ ("removed %d log functions with user data %p from log function list",
+ removals, data);
+
+ return removals;
+}
+
+/**
+ * gst_debug_set_colored:
+ * @colored: Whether to use colored output or not
+ *
+ * Sets or unsets the use of coloured debugging output.
+ *
+ * This function may be called before gst_init().
+ */
+void
+gst_debug_set_colored (gboolean colored)
+{
+ g_atomic_int_set (&__use_color, (gint) colored);
+}
+
+/**
+ * gst_debug_is_colored:
+ *
+ * Checks if the debugging output should be colored.
+ *
+ * Returns: TRUE, if the debug output should be colored.
+ */
+gboolean
+gst_debug_is_colored (void)
+{
+ return (gboolean) g_atomic_int_get (&__use_color);
+}
+
+/**
+ * gst_debug_set_active:
+ * @active: Whether to use debugging output or not
+ *
+ * If activated, debugging messages are sent to the debugging
+ * handlers.
+ * It makes sense to deactivate it for speed issues.
+ * <note><para>This function is not threadsafe. It makes sense to only call it
+ * during initialization.</para></note>
+ */
+void
+gst_debug_set_active (gboolean active)
+{
+ __gst_debug_enabled = active;
+ if (active)
+ __gst_debug_min = GST_LEVEL_COUNT;
+ else
+ __gst_debug_min = GST_LEVEL_NONE;
+}
+
+/**
+ * gst_debug_is_active:
+ *
+ * Checks if debugging output is activated.
+ *
+ * Returns: TRUE, if debugging is activated
+ */
+gboolean
+gst_debug_is_active (void)
+{
+ return __gst_debug_enabled;
+}
+
+/**
+ * gst_debug_set_default_threshold:
+ * @level: level to set
+ *
+ * Sets the default threshold to the given level and updates all categories to
+ * use this threshold.
+ *
+ * This function may be called before gst_init().
+ */
+void
+gst_debug_set_default_threshold (GstDebugLevel level)
+{
+ g_atomic_int_set (&__default_level, level);
+ gst_debug_reset_all_thresholds ();
+}
+
+/**
+ * gst_debug_get_default_threshold:
+ *
+ * Returns the default threshold that is used for new categories.
+ *
+ * Returns: the default threshold level
+ */
+GstDebugLevel
+gst_debug_get_default_threshold (void)
+{
+ return (GstDebugLevel) g_atomic_int_get (&__default_level);
+}
+
+static void
+gst_debug_reset_threshold (gpointer category, gpointer unused)
+{
+ GstDebugCategory *cat = (GstDebugCategory *) category;
+ GSList *walk;
+
+ g_static_mutex_lock (&__level_name_mutex);
+ walk = __level_name;
+ while (walk) {
+ LevelNameEntry *entry = walk->data;
+
+ walk = g_slist_next (walk);
+ if (g_pattern_match_string (entry->pat, cat->name)) {
+ GST_LOG ("category %s matches pattern %p - gets set to level %d",
+ cat->name, entry->pat, entry->level);
+ gst_debug_category_set_threshold (cat, entry->level);
+ goto exit;
+ }
+ }
+ gst_debug_category_set_threshold (cat, gst_debug_get_default_threshold ());
+
+exit:
+ g_static_mutex_unlock (&__level_name_mutex);
+}
+
+static void
+gst_debug_reset_all_thresholds (void)
+{
+ g_static_mutex_lock (&__cat_mutex);
+ g_slist_foreach (__categories, gst_debug_reset_threshold, NULL);
+ g_static_mutex_unlock (&__cat_mutex);
+}
+
+static void
+for_each_threshold_by_entry (gpointer data, gpointer user_data)
+{
+ GstDebugCategory *cat = (GstDebugCategory *) data;
+ LevelNameEntry *entry = (LevelNameEntry *) user_data;
+
+ if (g_pattern_match_string (entry->pat, cat->name)) {
+ GST_LOG ("category %s matches pattern %p - gets set to level %d",
+ cat->name, entry->pat, entry->level);
+ gst_debug_category_set_threshold (cat, entry->level);
+ }
+}
+
+/**
+ * gst_debug_set_threshold_for_name:
+ * @name: name of the categories to set
+ * @level: level to set them to
+ *
+ * Sets all categories which match the given glob style pattern to the given
+ * level.
+ */
+void
+gst_debug_set_threshold_for_name (const gchar * name, GstDebugLevel level)
+{
+ GPatternSpec *pat;
+ LevelNameEntry *entry;
+
+ g_return_if_fail (name != NULL);
+
+ pat = g_pattern_spec_new (name);
+ entry = g_slice_new (LevelNameEntry);
+ entry->pat = pat;
+ entry->level = level;
+ g_static_mutex_lock (&__level_name_mutex);
+ __level_name = g_slist_prepend (__level_name, entry);
+ g_static_mutex_unlock (&__level_name_mutex);
+ g_static_mutex_lock (&__cat_mutex);
+ g_slist_foreach (__categories, for_each_threshold_by_entry, entry);
+ g_static_mutex_unlock (&__cat_mutex);
+}
+
+/**
+ * gst_debug_unset_threshold_for_name:
+ * @name: name of the categories to set
+ *
+ * Resets all categories with the given name back to the default level.
+ */
+void
+gst_debug_unset_threshold_for_name (const gchar * name)
+{
+ GSList *walk;
+ GPatternSpec *pat;
+
+ g_return_if_fail (name != NULL);
+
+ pat = g_pattern_spec_new (name);
+ g_static_mutex_lock (&__level_name_mutex);
+ walk = __level_name;
+ /* improve this if you want, it's mighty slow */
+ while (walk) {
+ LevelNameEntry *entry = walk->data;
+
+ if (g_pattern_spec_equal (entry->pat, pat)) {
+ __level_name = g_slist_remove_link (__level_name, walk);
+ g_pattern_spec_free (entry->pat);
+ g_slice_free (LevelNameEntry, entry);
+ g_slist_free_1 (walk);
+ walk = __level_name;
+ }
+ }
+ g_static_mutex_unlock (&__level_name_mutex);
+ g_pattern_spec_free (pat);
+ gst_debug_reset_all_thresholds ();
+}
+
+GstDebugCategory *
+_gst_debug_category_new (const gchar * name, guint color,
+ const gchar * description)
+{
+ GstDebugCategory *cat;
+
+ g_return_val_if_fail (name != NULL, NULL);
+
+ cat = g_slice_new (GstDebugCategory);
+ cat->name = g_strdup (name);
+ cat->color = color;
+ if (description != NULL) {
+ cat->description = g_strdup (description);
+ } else {
+ cat->description = g_strdup ("no description");
+ }
+ g_atomic_int_set (&cat->threshold, 0);
+ gst_debug_reset_threshold (cat, NULL);
+
+ /* add to category list */
+ g_static_mutex_lock (&__cat_mutex);
+ __categories = g_slist_prepend (__categories, cat);
+ g_static_mutex_unlock (&__cat_mutex);
+
+ return cat;
+}
+
+/**
+ * gst_debug_category_free:
+ * @category: #GstDebugCategory to free.
+ *
+ * Removes and frees the category and all associated resources.
+ */
+void
+gst_debug_category_free (GstDebugCategory * category)
+{
+ if (category == NULL)
+ return;
+
+ /* remove from category list */
+ g_static_mutex_lock (&__cat_mutex);
+ __categories = g_slist_remove (__categories, category);
+ g_static_mutex_unlock (&__cat_mutex);
+
+ g_free ((gpointer) category->name);
+ g_free ((gpointer) category->description);
+ g_slice_free (GstDebugCategory, category);
+}
+
+/**
+ * gst_debug_category_set_threshold:
+ * @category: a #GstDebugCategory to set threshold of.
+ * @level: the #GstDebugLevel threshold to set.
+ *
+ * Sets the threshold of the category to the given level. Debug information will
+ * only be output if the threshold is lower or equal to the level of the
+ * debugging message.
+ * <note><para>
+ * Do not use this function in production code, because other functions may
+ * change the threshold of categories as side effect. It is however a nice
+ * function to use when debugging (even from gdb).
+ * </para></note>
+ */
+void
+gst_debug_category_set_threshold (GstDebugCategory * category,
+ GstDebugLevel level)
+{
+ g_return_if_fail (category != NULL);
+
+ if (level > __gst_debug_min) {
+ __gst_debug_enabled = TRUE;
+ __gst_debug_min = level;
+ }
+
+ g_atomic_int_set (&category->threshold, level);
+}
+
+/**
+ * gst_debug_category_reset_threshold:
+ * @category: a #GstDebugCategory to reset threshold of.
+ *
+ * Resets the threshold of the category to the default level. Debug information
+ * will only be output if the threshold is lower or equal to the level of the
+ * debugging message.
+ * Use this function to set the threshold back to where it was after using
+ * gst_debug_category_set_threshold().
+ */
+void
+gst_debug_category_reset_threshold (GstDebugCategory * category)
+{
+ gst_debug_reset_threshold (category, NULL);
+}
+
+/**
+ * gst_debug_category_get_threshold:
+ * @category: a #GstDebugCategory to get threshold of.
+ *
+ * Returns the threshold of a #GstDebugCategory.
+ *
+ * Returns: the #GstDebugLevel that is used as threshold.
+ */
+GstDebugLevel
+gst_debug_category_get_threshold (GstDebugCategory * category)
+{
+ return (GstDebugLevel) g_atomic_int_get (&category->threshold);
+}
+
+/**
+ * gst_debug_category_get_name:
+ * @category: a #GstDebugCategory to get name of.
+ *
+ * Returns the name of a debug category.
+ *
+ * Returns: the name of the category.
+ */
+const gchar *
+gst_debug_category_get_name (GstDebugCategory * category)
+{
+ return category->name;
+}
+
+/**
+ * gst_debug_category_get_color:
+ * @category: a #GstDebugCategory to get the color of.
+ *
+ * Returns the color of a debug category used when printing output in this
+ * category.
+ *
+ * Returns: the color of the category.
+ */
+guint
+gst_debug_category_get_color (GstDebugCategory * category)
+{
+ return category->color;
+}
+
+/**
+ * gst_debug_category_get_description:
+ * @category: a #GstDebugCategory to get the description of.
+ *
+ * Returns the description of a debug category.
+ *
+ * Returns: the description of the category.
+ */
+const gchar *
+gst_debug_category_get_description (GstDebugCategory * category)
+{
+ return category->description;
+}
+
+/**
+ * gst_debug_get_all_categories:
+ *
+ * Returns a snapshot of a all categories that are currently in use . This list
+ * may change anytime.
+ * The caller has to free the list after use.
+ *
+ * Returns: (transfer container) (element-type Gst.DebugCategory): the list of
+ * debug categories
+ */
+GSList *
+gst_debug_get_all_categories (void)
+{
+ GSList *ret;
+
+ g_static_mutex_lock (&__cat_mutex);
+ ret = g_slist_copy (__categories);
+ g_static_mutex_unlock (&__cat_mutex);
+
+ return ret;
+}
+
+GstDebugCategory *
+_gst_debug_get_category (const gchar * name)
+{
+ GstDebugCategory *ret = NULL;
+ GSList *node;
+
+ for (node = __categories; node; node = g_slist_next (node)) {
+ ret = (GstDebugCategory *) node->data;
+ if (!strcmp (name, ret->name)) {
+ return ret;
+ }
+ }
+ return NULL;
+}
+
+/*** FUNCTION POINTERS ********************************************************/
+
+static GHashTable *__gst_function_pointers; /* NULL */
+static GStaticMutex __dbg_functions_mutex = G_STATIC_MUTEX_INIT;
+
+/* This function MUST NOT return NULL */
+const gchar *
+_gst_debug_nameof_funcptr (GstDebugFuncPtr func)
+{
+ gchar *ptrname;
+
+#ifdef HAVE_DLADDR
+ Dl_info dl_info;
+#endif
+
+ if (G_UNLIKELY (func == NULL))
+ return "(NULL)";
+
+ g_static_mutex_lock (&__dbg_functions_mutex);
+ if (G_LIKELY (__gst_function_pointers)) {
+ ptrname = g_hash_table_lookup (__gst_function_pointers, (gpointer) func);
+ g_static_mutex_unlock (&__dbg_functions_mutex);
+ if (G_LIKELY (ptrname))
+ return ptrname;
+ } else {
+ g_static_mutex_unlock (&__dbg_functions_mutex);
+ }
+ /* we need to create an entry in the hash table for this one so we don't leak
+ * the name */
+#ifdef HAVE_DLADDR
+ if (dladdr ((gpointer) func, &dl_info) && dl_info.dli_sname) {
+ gchar *name = g_strdup (dl_info.dli_sname);
+
+ _gst_debug_register_funcptr (func, name);
+ return name;
+ } else
+#endif
+ {
+ gchar *name = g_strdup_printf ("%p", (gpointer) func);
+
+ _gst_debug_register_funcptr (func, name);
+ return name;
+ }
+}
+
+void
+_gst_debug_register_funcptr (GstDebugFuncPtr func, const gchar * ptrname)
+{
+ gpointer ptr = (gpointer) func;
+
+ g_static_mutex_lock (&__dbg_functions_mutex);
+
+ if (!__gst_function_pointers)
+ __gst_function_pointers = g_hash_table_new (g_direct_hash, g_direct_equal);
+ if (!g_hash_table_lookup (__gst_function_pointers, ptr))
+ g_hash_table_insert (__gst_function_pointers, ptr, (gpointer) ptrname);
+
+ g_static_mutex_unlock (&__dbg_functions_mutex);
+}
+
+/*** PRINTF EXTENSIONS ********************************************************/
+
+#ifdef HAVE_PRINTF_EXTENSION
+static int
+_gst_info_printf_extension_ptr (FILE * stream, const struct printf_info *info,
+ const void *const *args)
+{
+ char *buffer;
+ int len;
+ void *ptr;
+
+ buffer = NULL;
+ ptr = *(void **) args[0];
+
+ buffer = gst_debug_print_object (ptr);
+ len = fprintf (stream, "%*s", (info->left ? -info->width : info->width),
+ buffer);
+
+ g_free (buffer);
+ return len;
+}
+
+static int
+_gst_info_printf_extension_segment (FILE * stream,
+ const struct printf_info *info, const void *const *args)
+{
+ char *buffer;
+ int len;
+ void *ptr;
+
+ buffer = NULL;
+ ptr = *(void **) args[0];
+
+ buffer = gst_debug_print_segment (ptr);
+ len = fprintf (stream, "%*s", (info->left ? -info->width : info->width),
+ buffer);
+
+ g_free (buffer);
+ return len;
+}
+
+#ifdef HAVE_REGISTER_PRINTF_SPECIFIER
+static int
+_gst_info_printf_extension_arginfo (const struct printf_info *info, size_t n,
+ int *argtypes, int *size)
+#else
+static int
+_gst_info_printf_extension_arginfo (const struct printf_info *info, size_t n,
+ int *argtypes)
+#endif
+{
+ if (n > 0) {
+ argtypes[0] = PA_POINTER;
+#ifdef HAVE_REGISTER_PRINTF_SPECIFIER
+ *size = sizeof (gpointer);
+#endif
+ }
+ return 1;
+}
+#endif /* HAVE_PRINTF_EXTENSION */
+
+static void
+gst_info_dump_mem_line (gchar * linebuf, gsize linebuf_size,
+ const guint8 * mem, gsize mem_offset, gsize mem_size)
+{
+ gchar hexstr[50], ascstr[18], digitstr[4];
+
+ if (mem_size > 16)
+ mem_size = 16;
+
+ hexstr[0] = '\0';
+ ascstr[0] = '\0';
+
+ if (mem != NULL) {
+ guint i = 0;
+
+ mem += mem_offset;
+ while (i < mem_size) {
+ ascstr[i] = (g_ascii_isprint (mem[i])) ? mem[i] : '.';
+ g_snprintf (digitstr, sizeof (digitstr), "%02x ", mem[i]);
+ g_strlcat (hexstr, digitstr, sizeof (hexstr));
+ ++i;
+ }
+ ascstr[i] = '\0';
+ }
+
+ g_snprintf (linebuf, linebuf_size, "%08x: %-48.48s %-16.16s",
+ (guint) mem_offset, hexstr, ascstr);
+}
+
+void
+_gst_debug_dump_mem (GstDebugCategory * cat, const gchar * file,
+ const gchar * func, gint line, GObject * obj, const gchar * msg,
+ const guint8 * data, guint length)
+{
+ guint off = 0;
+
+ gst_debug_log ((cat), GST_LEVEL_MEMDUMP, file, func, line, obj, "--------"
+ "-------------------------------------------------------------------");
+
+ if (msg != NULL && *msg != '\0') {
+ gst_debug_log ((cat), GST_LEVEL_MEMDUMP, file, func, line, obj, "%s", msg);
+ }
+
+ while (off < length) {
+ gchar buf[128];
+
+ /* gst_info_dump_mem_line will process 16 bytes at most */
+ gst_info_dump_mem_line (buf, sizeof (buf), data, off, length - off);
+ gst_debug_log (cat, GST_LEVEL_MEMDUMP, file, func, line, obj, "%s", buf);
+ off += 16;
+ }
+
+ gst_debug_log ((cat), GST_LEVEL_MEMDUMP, file, func, line, obj, "--------"
+ "-------------------------------------------------------------------");
+}
+
+#else /* !GST_DISABLE_GST_DEBUG */
+#ifndef GST_REMOVE_DISABLED
+
+GstDebugCategory *
+_gst_debug_category_new (const gchar * name, guint color,
+ const gchar * description)
+{
+ return NULL;
+}
+
+void
+_gst_debug_register_funcptr (GstDebugFuncPtr func, const gchar * ptrname)
+{
+}
+
+/* This function MUST NOT return NULL */
+const gchar *
+_gst_debug_nameof_funcptr (GstDebugFuncPtr func)
+{
+ return "(NULL)";
+}
+
+void
+gst_debug_log (GstDebugCategory * category, GstDebugLevel level,
+ const gchar * file, const gchar * function, gint line,
+ GObject * object, const gchar * format, ...)
+{
+}
+
+void
+gst_debug_log_valist (GstDebugCategory * category, GstDebugLevel level,
+ const gchar * file, const gchar * function, gint line,
+ GObject * object, const gchar * format, va_list args)
+{
+}
+
+const gchar *
+gst_debug_message_get (GstDebugMessage * message)
+{
+ return "";
+}
+
+void
+gst_debug_log_default (GstDebugCategory * category, GstDebugLevel level,
+ const gchar * file, const gchar * function, gint line,
+ GObject * object, GstDebugMessage * message, gpointer unused)
+{
+}
+
+const gchar *
+gst_debug_level_get_name (GstDebugLevel level)
+{
+ return "NONE";
+}
+
+void
+gst_debug_add_log_function (GstLogFunction func, gpointer data)
+{
+}
+
+guint
+gst_debug_remove_log_function (GstLogFunction func)
+{
+ return 0;
+}
+
+guint
+gst_debug_remove_log_function_by_data (gpointer data)
+{
+ return 0;
+}
+
+void
+gst_debug_set_active (gboolean active)
+{
+}
+
+gboolean
+gst_debug_is_active (void)
+{
+ return FALSE;
+}
+
+void
+gst_debug_set_colored (gboolean colored)
+{
+}
+
+gboolean
+gst_debug_is_colored (void)
+{
+ return FALSE;
+}
+
+void
+gst_debug_set_default_threshold (GstDebugLevel level)
+{
+}
+
+GstDebugLevel
+gst_debug_get_default_threshold (void)
+{
+ return GST_LEVEL_NONE;
+}
+
+void
+gst_debug_set_threshold_for_name (const gchar * name, GstDebugLevel level)
+{
+}
+
+void
+gst_debug_unset_threshold_for_name (const gchar * name)
+{
+}
+
+void
+gst_debug_category_free (GstDebugCategory * category)
+{
+}
+
+void
+gst_debug_category_set_threshold (GstDebugCategory * category,
+ GstDebugLevel level)
+{
+}
+
+void
+gst_debug_category_reset_threshold (GstDebugCategory * category)
+{
+}
+
+GstDebugLevel
+gst_debug_category_get_threshold (GstDebugCategory * category)
+{
+ return GST_LEVEL_NONE;
+}
+
+const gchar *
+gst_debug_category_get_name (GstDebugCategory * category)
+{
+ return "";
+}
+
+guint
+gst_debug_category_get_color (GstDebugCategory * category)
+{
+ return 0;
+}
+
+const gchar *
+gst_debug_category_get_description (GstDebugCategory * category)
+{
+ return "";
+}
+
+GSList *
+gst_debug_get_all_categories (void)
+{
+ return NULL;
+}
+
+GstDebugCategory *
+_gst_debug_get_category (const gchar * name)
+{
+ return NULL;
+}
+
+gchar *
+gst_debug_construct_term_color (guint colorinfo)
+{
+ return g_strdup ("00");
+}
+
+gint
+gst_debug_construct_win_color (guint colorinfo)
+{
+ return 0;
+}
+
+gboolean
+_priv_gst_in_valgrind (void)
+{
+ return FALSE;
+}
+
+void
+_gst_debug_dump_mem (GstDebugCategory * cat, const gchar * file,
+ const gchar * func, gint line, GObject * obj, const gchar * msg,
+ const guint8 * data, guint length)
+{
+}
+#endif /* GST_REMOVE_DISABLED */
+#endif /* GST_DISABLE_GST_DEBUG */
+
+
+#ifdef GST_ENABLE_FUNC_INSTRUMENTATION
+/* FIXME make this thread specific */
+static GSList *stack_trace = NULL;
+
+void
+__cyg_profile_func_enter (void *this_fn, void *call_site)
+ G_GNUC_NO_INSTRUMENT;
+ void __cyg_profile_func_enter (void *this_fn, void *call_site)
+{
+ gchar *name = _gst_debug_nameof_funcptr (this_fn);
+ gchar *site = _gst_debug_nameof_funcptr (call_site);
+
+ GST_CAT_DEBUG (GST_CAT_CALL_TRACE, "entering function %s from %s", name,
+ site);
+ stack_trace =
+ g_slist_prepend (stack_trace, g_strdup_printf ("%8p in %s from %p (%s)",
+ this_fn, name, call_site, site));
+
+ g_free (name);
+ g_free (site);
+}
+
+void
+__cyg_profile_func_exit (void *this_fn, void *call_site)
+ G_GNUC_NO_INSTRUMENT;
+ void __cyg_profile_func_exit (void *this_fn, void *call_site)
+{
+ gchar *name = _gst_debug_nameof_funcptr (this_fn);
+
+ GST_CAT_DEBUG (GST_CAT_CALL_TRACE, "leaving function %s", name);
+ g_free (stack_trace->data);
+ stack_trace = g_slist_delete_link (stack_trace, stack_trace);
+
+ g_free (name);
+}
+
+/**
+ * gst_debug_print_stack_trace:
+ *
+ * If GST_ENABLE_FUNC_INSTRUMENTATION is defined a stacktrace is available for
+ * gstreamer code, which can be printed with this function.
+ */
+void
+gst_debug_print_stack_trace (void)
+{
+ GSList *walk = stack_trace;
+ gint count = 0;
+
+ if (walk)
+ walk = g_slist_next (walk);
+
+ while (walk) {
+ gchar *name = (gchar *) walk->data;
+
+ g_print ("#%-2d %s\n", count++, name);
+
+ walk = g_slist_next (walk);
+ }
+}
+#else
+void
+gst_debug_print_stack_trace (void)
+{
+ /* nothing because it's compiled out */
+}
+
+#endif /* GST_ENABLE_FUNC_INSTRUMENTATION */
diff --git a/gst/gstinfo.h b/gst/gstinfo.h
new file mode 100644
index 0000000..f841cd7
--- /dev/null
+++ b/gst/gstinfo.h
@@ -0,0 +1,1549 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2000 Wim Taymans <wtay@chello.be>
+ * 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
+ *
+ * gstinfo.h: debugging functions
+ *
+ * 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 __GSTINFO_H__
+#define __GSTINFO_H__
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gst/gstconfig.h>
+
+G_BEGIN_DECLS
+
+/**
+ * GstDebugLevel:
+ * @GST_LEVEL_NONE: No debugging level specified or desired. Used to deactivate
+ * debugging output.
+ * @GST_LEVEL_ERROR: Error messages are to be used only when an error occured
+ * that stops the application from keeping working correctly.
+ * An examples is gst_element_error, which outputs a message with this priority.
+ * It does not mean that the application is terminating as with g_errror.
+ * @GST_LEVEL_WARNING: Warning messages are to inform about abnormal behaviour
+ * that could lead to problems or weird behaviour later on. An example of this
+ * would be clocking issues ("your computer is pretty slow") or broken input
+ * data ("Can't synchronize to stream.")
+ * @GST_LEVEL_INFO: Informational messages should be used to keep the developer
+ * updated about what is happening.
+ * Examples where this should be used are when a typefind function has
+ * successfully determined the type of the stream or when an mp3 plugin detects
+ * the format to be used. ("This file has mono sound.")
+ * @GST_LEVEL_DEBUG: Debugging messages should be used when something common
+ * happens that is not the expected default behavior, or something that's
+ * useful to know but doesn't happen all the time (ie. per loop iteration or
+ * buffer processed or event handled).
+ * An example would be notifications about state changes or receiving/sending
+ * of events.
+ * @GST_LEVEL_LOG: Log messages are messages that are very common but might be
+ * useful to know. As a rule of thumb a pipeline that is iterating as expected
+ * should never output anything else but LOG messages. Use this log level to
+ * log recurring information in chain functions and loop functions, for
+ * example.
+ * @GST_LEVEL_FIXME: Fixme messages are messages that indicate that something
+ * in the executed code path is not fully implemented or handled yet. Note
+ * that this does not replace proper error handling in any way, the purpose
+ * of this message is to make it easier to spot incomplete/unfinished pieces
+ * of code when reading the debug log. (Since: 0.10.23)
+ * @GST_LEVEL_TRACE: Tracing-related messages (Since: 0.10.30)
+ * Examples for this are referencing/dereferencing of objects.
+ * @GST_LEVEL_MEMDUMP: memory dump messages are used to log (small) chunks of
+ * data as memory dumps in the log. They will be displayed as hexdump with
+ * ASCII characters. (Since: 0.10.23)
+ * @GST_LEVEL_COUNT: The number of defined debugging levels.
+ *
+ * The level defines the importance of a debugging message. The more important a
+ * message is, the greater the probability that the debugging system outputs it.
+ */
+typedef enum {
+ GST_LEVEL_NONE = 0,
+ GST_LEVEL_ERROR,
+ GST_LEVEL_WARNING,
+ GST_LEVEL_INFO,
+ GST_LEVEL_DEBUG,
+ GST_LEVEL_LOG,
+ GST_LEVEL_FIXME = 6,
+ GST_LEVEL_TRACE = 7,
+ /* add more */
+ GST_LEVEL_MEMDUMP = 9,
+ /* add more */
+ GST_LEVEL_COUNT
+} GstDebugLevel;
+
+/**
+ * GST_LEVEL_DEFAULT:
+ *
+ * Defines the default debugging level to be used with GStreamer. It is normally
+ * set to #GST_LEVEL_NONE so nothing get printed.
+ * As it can be configured at compile time, developer builds may chose to
+ * override that though.
+ * You can use this as an argument to gst_debug_set_default_threshold() to
+ * reset the debugging output to default behaviour.
+ */
+#ifndef GST_LEVEL_DEFAULT
+#define GST_LEVEL_DEFAULT GST_LEVEL_NONE
+#endif
+
+/* defines for format (colors etc)
+ * don't change them around, it uses terminal layout
+ * Terminal color strings:
+ * 00=none 01=bold 04=underscore 05=blink 07=reverse 08=concealed
+ * Text color codes:
+ * 30=black 31=red 32=green 33=yellow 34=blue 35=magenta 36=cyan 37=white
+ * Background color codes:
+ * 40=black 41=red 42=green 43=yellow 44=blue 45=magenta 46=cyan 47=white
+ */
+/**
+ * GstDebugColorFlags:
+ * @GST_DEBUG_FG_BLACK: Use black as foreground color.
+ * @GST_DEBUG_FG_RED: Use red as foreground color.
+ * @GST_DEBUG_FG_GREEN: Use green as foreground color.
+ * @GST_DEBUG_FG_YELLOW: Use yellow as foreground color.
+ * @GST_DEBUG_FG_BLUE: Use blue as foreground color.
+ * @GST_DEBUG_FG_MAGENTA: Use magenta as foreground color.
+ * @GST_DEBUG_FG_CYAN: Use cyan as foreground color.
+ * @GST_DEBUG_FG_WHITE: Use white as foreground color.
+ * @GST_DEBUG_BG_BLACK: Use black as background color.
+ * @GST_DEBUG_BG_RED: Use red as background color.
+ * @GST_DEBUG_BG_GREEN: Use green as background color.
+ * @GST_DEBUG_BG_YELLOW: Use yellow as background color.
+ * @GST_DEBUG_BG_BLUE: Use blue as background color.
+ * @GST_DEBUG_BG_MAGENTA: Use magenta as background color.
+ * @GST_DEBUG_BG_CYAN: Use cyan as background color.
+ * @GST_DEBUG_BG_WHITE: Use white as background color.
+ * @GST_DEBUG_BOLD: Make the output bold.
+ * @GST_DEBUG_UNDERLINE: Underline the output.
+ *
+ * These are some terminal style flags you can use when creating your
+ * debugging categories to make them stand out in debugging output.
+ */
+typedef enum {
+ /* colors */
+ GST_DEBUG_FG_BLACK = 0x0000,
+ GST_DEBUG_FG_RED = 0x0001,
+ GST_DEBUG_FG_GREEN = 0x0002,
+ GST_DEBUG_FG_YELLOW = 0x0003,
+ GST_DEBUG_FG_BLUE = 0x0004,
+ GST_DEBUG_FG_MAGENTA = 0x0005,
+ GST_DEBUG_FG_CYAN = 0x0006,
+ GST_DEBUG_FG_WHITE = 0x0007,
+ /* background colors */
+ GST_DEBUG_BG_BLACK = 0x0000,
+ GST_DEBUG_BG_RED = 0x0010,
+ GST_DEBUG_BG_GREEN = 0x0020,
+ GST_DEBUG_BG_YELLOW = 0x0030,
+ GST_DEBUG_BG_BLUE = 0x0040,
+ GST_DEBUG_BG_MAGENTA = 0x0050,
+ GST_DEBUG_BG_CYAN = 0x0060,
+ GST_DEBUG_BG_WHITE = 0x0070,
+ /* other formats */
+ GST_DEBUG_BOLD = 0x0100,
+ GST_DEBUG_UNDERLINE = 0x0200
+} GstDebugColorFlags;
+
+#define GST_DEBUG_FG_MASK (0x000F)
+#define GST_DEBUG_BG_MASK (0x00F0)
+#define GST_DEBUG_FORMAT_MASK (0xFF00)
+
+typedef struct _GstDebugCategory GstDebugCategory;
+/**
+ * GstDebugCategory:
+ *
+ * This is the struct that describes the categories. Once initialized with
+ * #GST_DEBUG_CATEGORY_INIT, its values can't be changed anymore.
+ */
+struct _GstDebugCategory {
+ /*< private >*/
+ gint threshold;
+ guint color; /* see defines above */
+
+ const gchar * name;
+ const gchar * description;
+};
+
+/********** some convenience macros for debugging **********/
+
+/**
+ * GST_STR_NULL:
+ * @str: The string to check.
+ *
+ * Macro to use when a string must not be NULL, but may be NULL. If the string
+ * is NULL, "(NULL)" is printed instead.
+ * In GStreamer printf string arguments may not be NULL, because on some
+ * platforms (ie Solaris) the libc crashes in that case. This includes debugging
+ * strings.
+ */
+#define GST_STR_NULL(str) ((str) ? (str) : "(NULL)")
+
+/* FIXME, not MT safe */
+/**
+ * GST_DEBUG_PAD_NAME:
+ * @pad: The pad to debug.
+ *
+ * Evaluates to 2 strings, that describe the pad. Often used in debugging
+ * statements.
+ */
+#define GST_DEBUG_PAD_NAME(pad) \
+ (pad != NULL) ? \
+ ((GST_OBJECT_PARENT(pad) != NULL) ? \
+ GST_STR_NULL (GST_OBJECT_NAME (GST_OBJECT_PARENT(pad))) : \
+ "''" ) : "''", \
+ (pad != NULL) ? GST_STR_NULL (GST_OBJECT_NAME (pad)) : "''"
+
+/**
+ * GST_FUNCTION:
+ *
+ * This macro should evaluate to the name of the current function and be should
+ * be defined when configuring your project, as it is compiler dependant. If it
+ * is not defined, some default value is used. It is used to provide debugging
+ * output with the function name of the message.
+ *
+ * Note that this is different from G_STRFUNC as we do not want the full
+ * function signature in C++ code.
+ */
+#ifndef GST_FUNCTION
+#if defined (__GNUC__) || (defined (_MSC_VER) && _MSC_VER >= 1300)
+# define GST_FUNCTION ((const char*) (__FUNCTION__))
+#elif defined (__STDC__) && defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+# define GST_FUNCTION ((const char*) (__func__))
+#else
+# define GST_FUNCTION ((const char*) ("???"))
+#endif
+#endif /* ifndef GST_FUNCTION */
+
+
+typedef struct _GstDebugMessage GstDebugMessage;
+
+/**
+ * GstLogFunction:
+ * @category: a #GstDebugCategory
+ * @level: a #GstDebugLevel
+ * @file: file name
+ * @function: function name
+ * @line: line number
+ * @object: a #GObject
+ * @message: the message
+ * @data: user data for the log function
+ *
+ * Function prototype for a logging function that can be registered with
+ * gst_debug_add_log_function().
+ * Use G_GNUC_NO_INSTRUMENT on that function.
+ */
+typedef void (*GstLogFunction) (GstDebugCategory * category,
+ GstDebugLevel level,
+ const gchar * file,
+ const gchar * function,
+ gint line,
+ GObject * object,
+ GstDebugMessage * message,
+ gpointer data);
+
+
+/* FIXME 0.11: move this into private headers */
+void _gst_debug_init (void);
+
+
+#ifdef GST_USING_PRINTF_EXTENSION
+
+/* not using G_GNUC_PRINTF, since gcc will choke on GST_PTR_FORMAT being %P */
+void gst_debug_log (GstDebugCategory * category,
+ GstDebugLevel level,
+ const gchar * file,
+ const gchar * function,
+ gint line,
+ GObject * object,
+ const gchar * format,
+ ...) G_GNUC_NO_INSTRUMENT;
+
+#else /* GST_USING_PRINTF_EXTENSION */
+
+void gst_debug_log (GstDebugCategory * category,
+ GstDebugLevel level,
+ const gchar * file,
+ const gchar * function,
+ gint line,
+ GObject * object,
+ const gchar * format,
+ ...) G_GNUC_PRINTF (7, 8) G_GNUC_NO_INSTRUMENT;
+
+#endif /* GST_USING_PRINTF_EXTENSION */
+
+void gst_debug_log_valist (GstDebugCategory * category,
+ GstDebugLevel level,
+ const gchar * file,
+ const gchar * function,
+ gint line,
+ GObject * object,
+ const gchar * format,
+ va_list args) G_GNUC_NO_INSTRUMENT;
+
+/* do not use this function, use the GST_DEBUG_CATEGORY_INIT macro */
+GstDebugCategory *_gst_debug_category_new (const gchar * name,
+ guint color,
+ const gchar * description);
+/* do not use this function, use the GST_DEBUG_CATEGORY_GET macro */
+GstDebugCategory *_gst_debug_get_category (const gchar *name);
+
+
+/* do not use this function, use the GST_CAT_MEMDUMP_* macros */
+void _gst_debug_dump_mem (GstDebugCategory * cat, const gchar * file,
+ const gchar * func, gint line, GObject * obj, const gchar * msg,
+ const guint8 * data, guint length);
+
+/* we define this to avoid a compiler warning regarding a cast from a function
+ * pointer to a void pointer
+ * (see https://bugzilla.gnome.org/show_bug.cgi?id=309253)
+ */
+typedef void (* GstDebugFuncPtr) (void);
+
+/* do no use these functions, use the GST_DEBUG*_FUNCPTR macros */
+void _gst_debug_register_funcptr (GstDebugFuncPtr func,
+ const gchar * ptrname);
+const gchar *
+ _gst_debug_nameof_funcptr (GstDebugFuncPtr func) G_GNUC_NO_INSTRUMENT;
+
+
+const gchar * gst_debug_message_get (GstDebugMessage * message);
+
+void gst_debug_log_default (GstDebugCategory * category,
+ GstDebugLevel level,
+ const gchar * file,
+ const gchar * function,
+ gint line,
+ GObject * object,
+ GstDebugMessage * message,
+ gpointer unused) G_GNUC_NO_INSTRUMENT;
+
+const gchar * gst_debug_level_get_name (GstDebugLevel level);
+
+void gst_debug_add_log_function (GstLogFunction func,
+ gpointer data);
+
+guint gst_debug_remove_log_function (GstLogFunction func);
+guint gst_debug_remove_log_function_by_data (gpointer data);
+
+void gst_debug_set_active (gboolean active);
+gboolean gst_debug_is_active (void);
+
+void gst_debug_set_colored (gboolean colored);
+gboolean gst_debug_is_colored (void);
+
+void gst_debug_set_default_threshold (GstDebugLevel level);
+GstDebugLevel gst_debug_get_default_threshold (void);
+void gst_debug_set_threshold_for_name (const gchar * name,
+ GstDebugLevel level);
+void gst_debug_unset_threshold_for_name (const gchar * name);
+
+
+void gst_debug_category_free (GstDebugCategory * category);
+void gst_debug_category_set_threshold (GstDebugCategory * category,
+ GstDebugLevel level);
+void gst_debug_category_reset_threshold (GstDebugCategory * category);
+GstDebugLevel gst_debug_category_get_threshold (GstDebugCategory * category);
+const gchar * gst_debug_category_get_name (GstDebugCategory * category);
+guint gst_debug_category_get_color (GstDebugCategory * category);
+const gchar * gst_debug_category_get_description (GstDebugCategory * category);
+GSList * gst_debug_get_all_categories (void);
+
+
+gchar * gst_debug_construct_term_color (guint colorinfo);
+gint gst_debug_construct_win_color (guint colorinfo);
+
+
+#ifndef GST_DISABLE_GST_DEBUG
+
+#define gst_debug_add_log_function(func,data) \
+G_STMT_START{ \
+ if (func == gst_debug_log_default) { \
+ gst_debug_add_log_function(NULL,data); \
+ } else { \
+ gst_debug_add_log_function(func,data); \
+ } \
+}G_STMT_END
+
+#define gst_debug_remove_log_function(func) \
+ (func == gst_debug_log_default) ? \
+ gst_debug_remove_log_function(NULL) : \
+ gst_debug_remove_log_function(func)
+
+/**
+ * GST_DEBUG_CATEGORY:
+ * @cat: the category
+ *
+ * Defines a GstDebugCategory variable.
+ * This macro expands to nothing if debugging is disabled.
+ */
+#define GST_DEBUG_CATEGORY(cat) GstDebugCategory *cat = NULL
+/**
+ * GST_DEBUG_CATEGORY_EXTERN:
+ * @cat: the category
+ *
+ * Declares a GstDebugCategory variable as extern. Use in header files.
+ * This macro expands to nothing if debugging is disabled.
+ */
+#define GST_DEBUG_CATEGORY_EXTERN(cat) extern GstDebugCategory *cat
+
+/**
+ * GST_DEBUG_CATEGORY_STATIC:
+ * @cat: the category
+ *
+ * Defines a static GstDebugCategory variable.
+ * This macro expands to nothing if debugging is disabled.
+ */
+#define GST_DEBUG_CATEGORY_STATIC(cat) static GstDebugCategory *cat = NULL
+
+/**
+ * GST_DEBUG_CATEGORY_INIT:
+ * @cat: the category to initialize.
+ * @name: the name of the category.
+ * @color: the colors to use for a color representation or 0 for no color.
+ * @description: optional description of the category.
+ *
+ * Initializes a new #GstDebugCategory with the given properties and set to
+ * the default threshold.
+ *
+ * <note>
+ * <para>
+ * This macro expands to nothing if debugging is disabled.
+ * </para>
+ * <para>
+ * When naming your category, please follow the following conventions to ensure
+ * that the pattern matching for categories works as expected. It is not
+ * earth-shattering if you don't follow these conventions, but it would be nice
+ * for everyone.
+ * </para>
+ * <para>
+ * If you define a category for a plugin or a feature of it, name the category
+ * like the feature. So if you wanted to write a "filesrc" element, you would
+ * name the category "filesrc". Use lowercase letters only.
+ * If you define more than one category for the same element, append an
+ * underscore and an identifier to your categories, like this: "filesrc_cache"
+ * </para>
+ * <para>
+ * If you create a library or an application using debugging categories, use a
+ * common prefix followed by an underscore for all your categories. GStreamer
+ * uses the GST prefix so GStreamer categories look like "GST_STATES". Be sure
+ * to include uppercase letters.
+ * </para>
+ * </note>
+ */
+#define GST_DEBUG_CATEGORY_INIT(cat,name,color,description) G_STMT_START{\
+ if (cat == NULL) \
+ cat = _gst_debug_category_new (name,color,description); \
+}G_STMT_END
+
+/**
+ * GST_DEBUG_CATEGORY_GET:
+ * @cat: the category to initialize.
+ * @name: log category name
+ *
+ * Looks up an existing #GstDebugCategory by its @name and sets @cat. If the
+ * category is not found, but GST_CAT_DEFAULT is defined, that is assigned to
+ * @cat. Otherwise @cat will be NULL.
+ *
+ * |[
+ * GST_DEBUG_CATEGORY_STATIC (gst_myplugin_debug);
+ * #define GST_CAT_DEFAULT gst_myplugin_debug
+ * GST_DEBUG_CATEGORY_STATIC (GST_CAT_PERFORMANCE);
+ * ...
+ * GST_DEBUG_CATEGORY_INIT (gst_myplugin_debug, "myplugin", 0, "nice element");
+ * GST_DEBUG_CATEGORY_GET (GST_CAT_PERFORMANCE, "GST_PERFORMANCE");
+ * ]|
+ *
+ * Since: 0.10.24
+ */
+#ifdef GST_CAT_DEFAULT
+#define GST_DEBUG_CATEGORY_GET(cat,name) G_STMT_START{\
+ cat = _gst_debug_get_category (name); \
+ if (!cat) { \
+ cat = GST_CAT_DEFAULT; \
+ } \
+}G_STMT_END
+#else
+#define GST_DEBUG_CATEGORY_GET(cat,name) G_STMT_START{\
+ cat = _gst_debug_get_category (name); \
+}G_STMT_END
+#endif
+
+/**
+ * GST_CAT_DEFAULT:
+ *
+ * Default gstreamer core debug log category. Please define your own.
+ */
+GST_EXPORT GstDebugCategory * GST_CAT_DEFAULT;
+/* this symbol may not be used */
+extern gboolean __gst_debug_enabled;
+
+/* since 0.10.7, the min debug level, used for quickly discarding debug
+ * messages that fall under the threshold. */
+GST_EXPORT GstDebugLevel __gst_debug_min;
+
+/**
+ * GST_CAT_LEVEL_LOG:
+ * @cat: category to use
+ * @level: the severity of the message
+ * @object: the #GObject the message belongs to or NULL if none
+ * @...: A printf-style message to output
+ *
+ * Outputs a debugging message. This is the most general macro for outputting
+ * debugging messages. You will probably want to use one of the ones described
+ * below.
+ */
+#ifdef G_HAVE_ISO_VARARGS
+#define GST_CAT_LEVEL_LOG(cat,level,object,...) G_STMT_START{ \
+ if (G_UNLIKELY (level <= __gst_debug_min)) { \
+ gst_debug_log ((cat), (level), __FILE__, GST_FUNCTION, __LINE__, \
+ (GObject *) (object), __VA_ARGS__); \
+ } \
+}G_STMT_END
+#else /* G_HAVE_GNUC_VARARGS */
+#ifdef G_HAVE_GNUC_VARARGS
+#define GST_CAT_LEVEL_LOG(cat,level,object,args...) G_STMT_START{ \
+ if (G_UNLIKELY (level <= __gst_debug_min)) { \
+ gst_debug_log ((cat), (level), __FILE__, GST_FUNCTION, __LINE__, \
+ (GObject *) (object), ##args ); \
+ } \
+}G_STMT_END
+#else /* no variadic macros, use inline */
+static inline void
+GST_CAT_LEVEL_LOG_valist (GstDebugCategory * cat,
+ GstDebugLevel level, gpointer object, const char *format, va_list varargs)
+{
+ if (G_UNLIKELY (level <= __gst_debug_min)) {
+ gst_debug_log_valist (cat, level, "", "", 0, (GObject *) object, format,
+ varargs);
+ }
+}
+
+static inline void
+GST_CAT_LEVEL_LOG (GstDebugCategory * cat, GstDebugLevel level,
+ gpointer object, const char *format, ...)
+{
+ va_list varargs;
+
+ va_start (varargs, format);
+ GST_CAT_LEVEL_LOG_valist (cat, level, object, format, varargs);
+ va_end (varargs);
+}
+#endif
+#endif /* G_HAVE_ISO_VARARGS */
+
+/* This one doesn't have varargs in the macro, so it's different than all the
+ * other macros and hence in a separate block right here. Docs chunks are
+ * with the other doc chunks below though. */
+#define __GST_CAT_MEMDUMP_LOG(cat,object,msg,data,length) G_STMT_START{ \
+ if (G_UNLIKELY (GST_LEVEL_MEMDUMP <= __gst_debug_min)) { \
+ _gst_debug_dump_mem ((cat), __FILE__, GST_FUNCTION, __LINE__, \
+ (GObject *) (object), (msg), (data), (length)); \
+ } \
+}G_STMT_END
+
+#define GST_CAT_MEMDUMP_OBJECT(cat,obj,msg,data,length) \
+ __GST_CAT_MEMDUMP_LOG(cat,obj,msg,data,length)
+#define GST_CAT_MEMDUMP(cat,msg,data,length) \
+ __GST_CAT_MEMDUMP_LOG(cat,NULL,msg,data,length)
+#define GST_MEMDUMP_OBJECT(obj,msg,data,length) \
+ __GST_CAT_MEMDUMP_LOG(GST_CAT_DEFAULT,obj,msg,data,length)
+#define GST_MEMDUMP(msg,data,length) \
+ __GST_CAT_MEMDUMP_LOG(GST_CAT_DEFAULT,NULL,msg,data,length)
+
+/**
+ * GST_CAT_ERROR_OBJECT:
+ * @cat: category to use
+ * @obj: the #GObject the message belongs to
+ * @...: printf-style message to output
+ *
+ * Output an error message belonging to the given object in the given category.
+ */
+/**
+ * GST_CAT_WARNING_OBJECT:
+ * @cat: category to use
+ * @obj: the #GObject the message belongs to
+ * @...: printf-style message to output
+ *
+ * Output a warning message belonging to the given object in the given category.
+ */
+/**
+ * GST_CAT_INFO_OBJECT:
+ * @cat: category to use
+ * @obj: the #GObject the message belongs to
+ * @...: printf-style message to output
+ *
+ * Output an informational message belonging to the given object in the given
+ * category.
+ */
+/**
+ * GST_CAT_DEBUG_OBJECT:
+ * @cat: category to use
+ * @obj: the #GObject the message belongs to
+ * @...: printf-style message to output
+ *
+ * Output an debugging message belonging to the given object in the given category.
+ */
+/**
+ * GST_CAT_LOG_OBJECT:
+ * @cat: category to use
+ * @obj: the #GObject the message belongs to
+ * @...: printf-style message to output
+ *
+ * Output an logging message belonging to the given object in the given category.
+ */
+/**
+ * GST_CAT_FIXME_OBJECT:
+ * @cat: category to use
+ * @obj: the #GObject the message belongs to
+ * @...: printf-style message to output
+ *
+ * Output a fixme message belonging to the given object in the given category.
+ *
+ * Since: 0.10.23
+ */
+/**
+ * GST_CAT_TRACE_OBJECT:
+ * @cat: category to use
+ * @obj: the #GObject the message belongs to
+ * @...: printf-style message to output
+ *
+ * Output a tracing message belonging to the given object in the given
+ * category.
+ *
+ * Since: 0.10.30
+ */
+/**
+ * GST_CAT_MEMDUMP_OBJECT:
+ * @cat: category to use
+ * @obj: the #GObject the message belongs to
+ * @msg: message string to log with the data
+ * @data: pointer to the data to output
+ * @length: length of the data to output
+ *
+ * Output a hexdump of @data relating to the given object in the given
+ * category.
+ *
+ * Since: 0.10.23
+ */
+
+
+/**
+ * GST_CAT_ERROR:
+ * @cat: category to use
+ * @...: printf-style message to output
+ *
+ * Output an error message in the given category.
+ */
+/**
+ * GST_CAT_WARNING:
+ * @cat: category to use
+ * @...: printf-style message to output
+ *
+ * Output an warning message in the given category.
+ */
+/**
+ * GST_CAT_INFO:
+ * @cat: category to use
+ * @...: printf-style message to output
+ *
+ * Output an informational message in the given category.
+ */
+/**
+ * GST_CAT_DEBUG:
+ * @cat: category to use
+ * @...: printf-style message to output
+ *
+ * Output an debugging message in the given category.
+ */
+/**
+ * GST_CAT_LOG:
+ * @cat: category to use
+ * @...: printf-style message to output
+ *
+ * Output an logging message in the given category.
+ */
+/**
+ * GST_CAT_FIXME:
+ * @cat: category to use
+ * @...: printf-style message to output
+ *
+ * Output an fixme message in the given category.
+ *
+ * Since: 0.10.23
+ */
+/**
+ * GST_CAT_TRACE:
+ * @cat: category to use
+ * @...: printf-style message to output
+ *
+ * Output a tracing message in the given category.
+ *
+ * Since: 0.10.30
+ */
+/**
+ * GST_CAT_MEMDUMP:
+ * @cat: category to use
+ * @msg: message string to log with the data
+ * @data: pointer to the data to output
+ * @length: length of the data to output
+ *
+ * Output a hexdump of @data in the given category.
+ *
+ * Since: 0.10.23
+ */
+
+
+/**
+ * GST_ERROR_OBJECT:
+ * @obj: the #GObject the message belongs to
+ * @...: printf-style message to output
+ *
+ * Output an error message belonging to the given object in the default category.
+ */
+/**
+ * GST_WARNING_OBJECT:
+ * @obj: the #GObject the message belongs to
+ * @...: printf-style message to output
+ *
+ * Output a warning message belonging to the given object in the default category.
+ */
+/**
+ * GST_INFO_OBJECT:
+ * @obj: the #GObject the message belongs to
+ * @...: printf-style message to output
+ *
+ * Output an informational message belonging to the given object in the default
+ * category.
+ */
+/**
+ * GST_DEBUG_OBJECT:
+ * @obj: the #GObject the message belongs to
+ * @...: printf-style message to output
+ *
+ * Output a debugging message belonging to the given object in the default
+ * category.
+ */
+/**
+ * GST_LOG_OBJECT:
+ * @obj: the #GObject the message belongs to
+ * @...: printf-style message to output
+ *
+ * Output a logging message belonging to the given object in the default category.
+ */
+/**
+ * GST_FIXME_OBJECT:
+ * @obj: the #GObject the message belongs to
+ * @...: printf-style message to output
+ *
+ * Output a fixme message belonging to the given object in the default category.
+ *
+ * Since: 0.10.23
+ */
+/**
+ * GST_TRACE_OBJECT:
+ * @obj: the #GObject the message belongs to
+ * @...: printf-style message to output
+ *
+ * Output a tracing message belonging to the given object in the default category.
+ *
+ * Since: 0.10.30
+ */
+/**
+ * GST_MEMDUMP_OBJECT:
+ * @obj: the #GObject the message belongs to
+ * @msg: message string to log with the data
+ * @data: pointer to the data to output
+ * @length: length of the data to output
+ *
+ * Output a logging message belonging to the given object in the default category.
+ *
+ * Since: 0.10.23
+ */
+
+
+/**
+ * GST_ERROR:
+ * @...: printf-style message to output
+ *
+ * Output an error message in the default category.
+ */
+/**
+ * GST_WARNING:
+ * @...: printf-style message to output
+ *
+ * Output a warning message in the default category.
+ */
+/**
+ * GST_INFO:
+ * @...: printf-style message to output
+ *
+ * Output an informational message in the default category.
+ */
+/**
+ * GST_DEBUG:
+ * @...: printf-style message to output
+ *
+ * Output a debugging message in the default category.
+ */
+/**
+ * GST_LOG:
+ * @...: printf-style message to output
+ *
+ * Output a logging message in the default category.
+ */
+/**
+ * GST_FIXME:
+ * @...: printf-style message to output
+ *
+ * Output a fixme message in the default category.
+ *
+ * Since: 0.10.23
+ */
+/**
+ * GST_TRACE:
+ * @...: printf-style message to output
+ *
+ * Output a tracing message in the default category.
+ *
+ * Since: 0.10.30
+ */
+/**
+ * GST_MEMDUMP:
+ * @msg: message string to log with the data
+ * @data: pointer to the data to output
+ * @length: length of the data to output
+ *
+ * Output a hexdump of @data.
+ *
+ * Since: 0.10.23
+ */
+
+#ifdef G_HAVE_ISO_VARARGS
+
+#define GST_CAT_ERROR_OBJECT(cat,obj,...) GST_CAT_LEVEL_LOG (cat, GST_LEVEL_ERROR, obj, __VA_ARGS__)
+#define GST_CAT_WARNING_OBJECT(cat,obj,...) GST_CAT_LEVEL_LOG (cat, GST_LEVEL_WARNING, obj, __VA_ARGS__)
+#define GST_CAT_INFO_OBJECT(cat,obj,...) GST_CAT_LEVEL_LOG (cat, GST_LEVEL_INFO, obj, __VA_ARGS__)
+#define GST_CAT_DEBUG_OBJECT(cat,obj,...) GST_CAT_LEVEL_LOG (cat, GST_LEVEL_DEBUG, obj, __VA_ARGS__)
+#define GST_CAT_LOG_OBJECT(cat,obj,...) GST_CAT_LEVEL_LOG (cat, GST_LEVEL_LOG, obj, __VA_ARGS__)
+#define GST_CAT_FIXME_OBJECT(cat,obj,...) GST_CAT_LEVEL_LOG (cat, GST_LEVEL_FIXME, obj, __VA_ARGS__)
+#define GST_CAT_TRACE_OBJECT(cat,obj,...) GST_CAT_LEVEL_LOG (cat, GST_LEVEL_TRACE, obj, __VA_ARGS__)
+
+#define GST_CAT_ERROR(cat,...) GST_CAT_LEVEL_LOG (cat, GST_LEVEL_ERROR, NULL, __VA_ARGS__)
+#define GST_CAT_WARNING(cat,...) GST_CAT_LEVEL_LOG (cat, GST_LEVEL_WARNING, NULL, __VA_ARGS__)
+#define GST_CAT_INFO(cat,...) GST_CAT_LEVEL_LOG (cat, GST_LEVEL_INFO, NULL, __VA_ARGS__)
+#define GST_CAT_DEBUG(cat,...) GST_CAT_LEVEL_LOG (cat, GST_LEVEL_DEBUG, NULL, __VA_ARGS__)
+#define GST_CAT_LOG(cat,...) GST_CAT_LEVEL_LOG (cat, GST_LEVEL_LOG, NULL, __VA_ARGS__)
+#define GST_CAT_FIXME(cat,...) GST_CAT_LEVEL_LOG (cat, GST_LEVEL_FIXME, NULL, __VA_ARGS__)
+#define GST_CAT_TRACE(cat,...) GST_CAT_LEVEL_LOG (cat, GST_LEVEL_TRACE, NULL, __VA_ARGS__)
+
+#define GST_ERROR_OBJECT(obj,...) GST_CAT_LEVEL_LOG (GST_CAT_DEFAULT, GST_LEVEL_ERROR, obj, __VA_ARGS__)
+#define GST_WARNING_OBJECT(obj,...) GST_CAT_LEVEL_LOG (GST_CAT_DEFAULT, GST_LEVEL_WARNING, obj, __VA_ARGS__)
+#define GST_INFO_OBJECT(obj,...) GST_CAT_LEVEL_LOG (GST_CAT_DEFAULT, GST_LEVEL_INFO, obj, __VA_ARGS__)
+#define GST_DEBUG_OBJECT(obj,...) GST_CAT_LEVEL_LOG (GST_CAT_DEFAULT, GST_LEVEL_DEBUG, obj, __VA_ARGS__)
+#define GST_LOG_OBJECT(obj,...) GST_CAT_LEVEL_LOG (GST_CAT_DEFAULT, GST_LEVEL_LOG, obj, __VA_ARGS__)
+#define GST_FIXME_OBJECT(obj,...) GST_CAT_LEVEL_LOG (GST_CAT_DEFAULT, GST_LEVEL_FIXME, obj, __VA_ARGS__)
+#define GST_TRACE_OBJECT(obj,...) GST_CAT_LEVEL_LOG (GST_CAT_DEFAULT, GST_LEVEL_TRACE, obj, __VA_ARGS__)
+
+#define GST_ERROR(...) GST_CAT_LEVEL_LOG (GST_CAT_DEFAULT, GST_LEVEL_ERROR, NULL, __VA_ARGS__)
+#define GST_WARNING(...) GST_CAT_LEVEL_LOG (GST_CAT_DEFAULT, GST_LEVEL_WARNING, NULL, __VA_ARGS__)
+#define GST_INFO(...) GST_CAT_LEVEL_LOG (GST_CAT_DEFAULT, GST_LEVEL_INFO, NULL, __VA_ARGS__)
+#define GST_DEBUG(...) GST_CAT_LEVEL_LOG (GST_CAT_DEFAULT, GST_LEVEL_DEBUG, NULL, __VA_ARGS__)
+#define GST_LOG(...) GST_CAT_LEVEL_LOG (GST_CAT_DEFAULT, GST_LEVEL_LOG, NULL, __VA_ARGS__)
+#define GST_FIXME(...) GST_CAT_LEVEL_LOG (GST_CAT_DEFAULT, GST_LEVEL_FIXME, NULL, __VA_ARGS__)
+#define GST_TRACE(...) GST_CAT_LEVEL_LOG (GST_CAT_DEFAULT, GST_LEVEL_TRACE, NULL, __VA_ARGS__)
+
+#else
+#ifdef G_HAVE_GNUC_VARARGS
+
+#define GST_CAT_ERROR_OBJECT(cat,obj,args...) GST_CAT_LEVEL_LOG (cat, GST_LEVEL_ERROR, obj, ##args )
+#define GST_CAT_WARNING_OBJECT(cat,obj,args...) GST_CAT_LEVEL_LOG (cat, GST_LEVEL_WARNING, obj, ##args )
+#define GST_CAT_INFO_OBJECT(cat,obj,args...) GST_CAT_LEVEL_LOG (cat, GST_LEVEL_INFO, obj, ##args )
+#define GST_CAT_DEBUG_OBJECT(cat,obj,args...) GST_CAT_LEVEL_LOG (cat, GST_LEVEL_DEBUG, obj, ##args )
+#define GST_CAT_LOG_OBJECT(cat,obj,args...) GST_CAT_LEVEL_LOG (cat, GST_LEVEL_LOG, obj, ##args )
+#define GST_CAT_FIXME_OBJECT(cat,obj,args...) GST_CAT_LEVEL_LOG (cat, GST_LEVEL_FIXME, obj, ##args )
+#define GST_CAT_TRACE_OBJECT(cat,obj,args...) GST_CAT_LEVEL_LOG (cat, GST_LEVEL_TRACE, obj, ##args )
+
+#define GST_CAT_ERROR(cat,args...) GST_CAT_LEVEL_LOG (cat, GST_LEVEL_ERROR, NULL, ##args )
+#define GST_CAT_WARNING(cat,args...) GST_CAT_LEVEL_LOG (cat, GST_LEVEL_WARNING, NULL, ##args )
+#define GST_CAT_INFO(cat,args...) GST_CAT_LEVEL_LOG (cat, GST_LEVEL_INFO, NULL, ##args )
+#define GST_CAT_DEBUG(cat,args...) GST_CAT_LEVEL_LOG (cat, GST_LEVEL_DEBUG, NULL, ##args )
+#define GST_CAT_LOG(cat,args...) GST_CAT_LEVEL_LOG (cat, GST_LEVEL_LOG, NULL, ##args )
+#define GST_CAT_FIXME(cat,args...) GST_CAT_LEVEL_LOG (cat, GST_LEVEL_FIXME, NULL, ##args )
+#define GST_CAT_TRACE(cat,args...) GST_CAT_LEVEL_LOG (cat, GST_LEVEL_TRACE, NULL, ##args )
+
+#define GST_ERROR_OBJECT(obj,args...) GST_CAT_LEVEL_LOG (GST_CAT_DEFAULT, GST_LEVEL_ERROR, obj, ##args )
+#define GST_WARNING_OBJECT(obj,args...) GST_CAT_LEVEL_LOG (GST_CAT_DEFAULT, GST_LEVEL_WARNING, obj, ##args )
+#define GST_INFO_OBJECT(obj,args...) GST_CAT_LEVEL_LOG (GST_CAT_DEFAULT, GST_LEVEL_INFO, obj, ##args )
+#define GST_DEBUG_OBJECT(obj,args...) GST_CAT_LEVEL_LOG (GST_CAT_DEFAULT, GST_LEVEL_DEBUG, obj, ##args )
+#define GST_LOG_OBJECT(obj,args...) GST_CAT_LEVEL_LOG (GST_CAT_DEFAULT, GST_LEVEL_LOG, obj, ##args )
+#define GST_FIXME_OBJECT(obj,args...) GST_CAT_LEVEL_LOG (GST_CAT_DEFAULT, GST_LEVEL_FIXME, obj, ##args )
+#define GST_TRACE_OBJECT(obj,args...) GST_CAT_LEVEL_LOG (GST_CAT_DEFAULT, GST_LEVEL_TRACE, obj, ##args )
+
+#define GST_ERROR(args...) GST_CAT_LEVEL_LOG (GST_CAT_DEFAULT, GST_LEVEL_ERROR, NULL, ##args )
+#define GST_WARNING(args...) GST_CAT_LEVEL_LOG (GST_CAT_DEFAULT, GST_LEVEL_WARNING, NULL, ##args )
+#define GST_INFO(args...) GST_CAT_LEVEL_LOG (GST_CAT_DEFAULT, GST_LEVEL_INFO, NULL, ##args )
+#define GST_DEBUG(args...) GST_CAT_LEVEL_LOG (GST_CAT_DEFAULT, GST_LEVEL_DEBUG, NULL, ##args )
+#define GST_LOG(args...) GST_CAT_LEVEL_LOG (GST_CAT_DEFAULT, GST_LEVEL_LOG, NULL, ##args )
+#define GST_FIXME(args...) GST_CAT_LEVEL_LOG (GST_CAT_DEFAULT, GST_LEVEL_FIXME, NULL, ##args )
+#define GST_TRACE(args...) GST_CAT_LEVEL_LOG (GST_CAT_DEFAULT, GST_LEVEL_TRACE, NULL, ##args )
+
+#else
+/* no variadic macros, use inline */
+static inline void
+GST_CAT_ERROR_OBJECT (GstDebugCategory * cat, gpointer obj, const char *format,
+ ...)
+{
+ va_list varargs;
+
+ va_start (varargs, format);
+ GST_CAT_LEVEL_LOG_valist (cat, GST_LEVEL_ERROR, obj, format, varargs);
+ va_end (varargs);
+}
+
+static inline void
+GST_CAT_WARNING_OBJECT (GstDebugCategory * cat, gpointer obj,
+ const char *format, ...)
+{
+ va_list varargs;
+
+ va_start (varargs, format);
+ GST_CAT_LEVEL_LOG_valist (cat, GST_LEVEL_WARNING, obj, format, varargs);
+ va_end (varargs);
+}
+
+static inline void
+GST_CAT_INFO_OBJECT (GstDebugCategory * cat, gpointer obj, const char *format,
+ ...)
+{
+ va_list varargs;
+
+ va_start (varargs, format);
+ GST_CAT_LEVEL_LOG_valist (cat, GST_LEVEL_INFO, obj, format, varargs);
+ va_end (varargs);
+}
+
+static inline void
+GST_CAT_DEBUG_OBJECT (GstDebugCategory * cat, gpointer obj, const char *format,
+ ...)
+{
+ va_list varargs;
+
+ va_start (varargs, format);
+ GST_CAT_LEVEL_LOG_valist (cat, GST_LEVEL_DEBUG, obj, format, varargs);
+ va_end (varargs);
+}
+
+static inline void
+GST_CAT_LOG_OBJECT (GstDebugCategory * cat, gpointer obj, const char *format,
+ ...)
+{
+ va_list varargs;
+
+ va_start (varargs, format);
+ GST_CAT_LEVEL_LOG_valist (cat, GST_LEVEL_LOG, obj, format, varargs);
+ va_end (varargs);
+}
+
+static inline void
+GST_CAT_FIXME_OBJECT (GstDebugCategory * cat, gpointer obj, const char *format,
+ ...)
+{
+ va_list varargs;
+
+ va_start (varargs, format);
+ GST_CAT_LEVEL_LOG_valist (cat, GST_LEVEL_FIXME, obj, format, varargs);
+ va_end (varargs);
+}
+
+static inline void
+GST_CAT_TRACE_OBJECT (GstDebugCategory * cat, gpointer obj, const char *format,
+ ...)
+{
+ va_list varargs;
+
+ va_start (varargs, format);
+ GST_CAT_LEVEL_LOG_valist (cat, GST_LEVEL_TRACE, obj, format, varargs);
+ va_end (varargs);
+}
+
+static inline void
+GST_CAT_ERROR (GstDebugCategory * cat, const char *format, ...)
+{
+ va_list varargs;
+
+ va_start (varargs, format);
+ GST_CAT_LEVEL_LOG_valist (cat, GST_LEVEL_ERROR, NULL, format, varargs);
+ va_end (varargs);
+}
+
+static inline void
+GST_CAT_WARNING (GstDebugCategory * cat, const char *format, ...)
+{
+ va_list varargs;
+
+ va_start (varargs, format);
+ GST_CAT_LEVEL_LOG_valist (cat, GST_LEVEL_WARNING, NULL, format, varargs);
+ va_end (varargs);
+}
+
+static inline void
+GST_CAT_INFO (GstDebugCategory * cat, const char *format, ...)
+{
+ va_list varargs;
+
+ va_start (varargs, format);
+ GST_CAT_LEVEL_LOG_valist (cat, GST_LEVEL_INFO, NULL, format, varargs);
+ va_end (varargs);
+}
+
+static inline void
+GST_CAT_DEBUG (GstDebugCategory * cat, const char *format, ...)
+{
+ va_list varargs;
+
+ va_start (varargs, format);
+ GST_CAT_LEVEL_LOG_valist (cat, GST_LEVEL_DEBUG, NULL, format, varargs);
+ va_end (varargs);
+}
+
+static inline void
+GST_CAT_LOG (GstDebugCategory * cat, const char *format, ...)
+{
+ va_list varargs;
+
+ va_start (varargs, format);
+ GST_CAT_LEVEL_LOG_valist (cat, GST_LEVEL_LOG, NULL, format, varargs);
+ va_end (varargs);
+}
+
+static inline void
+GST_CAT_FIXME (GstDebugCategory * cat, const char *format, ...)
+{
+ va_list varargs;
+
+ va_start (varargs, format);
+ GST_CAT_LEVEL_LOG_valist (cat, GST_LEVEL_FIXME, NULL, format, varargs);
+ va_end (varargs);
+}
+
+static inline void
+GST_CAT_TRACE (GstDebugCategory * cat, const char *format, ...)
+{
+ va_list varargs;
+
+ va_start (varargs, format);
+ GST_CAT_LEVEL_LOG_valist (cat, GST_LEVEL_TRACE, NULL, format, varargs);
+ va_end (varargs);
+}
+
+static inline void
+GST_ERROR_OBJECT (gpointer obj, const char *format, ...)
+{
+ va_list varargs;
+
+ va_start (varargs, format);
+ GST_CAT_LEVEL_LOG_valist (GST_CAT_DEFAULT, GST_LEVEL_ERROR, obj, format,
+ varargs);
+ va_end (varargs);
+}
+
+static inline void
+GST_WARNING_OBJECT (gpointer obj, const char *format, ...)
+{
+ va_list varargs;
+
+ va_start (varargs, format);
+ GST_CAT_LEVEL_LOG_valist (GST_CAT_DEFAULT, GST_LEVEL_WARNING, obj, format,
+ varargs);
+ va_end (varargs);
+}
+
+static inline void
+GST_INFO_OBJECT (gpointer obj, const char *format, ...)
+{
+ va_list varargs;
+
+ va_start (varargs, format);
+ GST_CAT_LEVEL_LOG_valist (GST_CAT_DEFAULT, GST_LEVEL_INFO, obj, format,
+ varargs);
+ va_end (varargs);
+}
+
+static inline void
+GST_DEBUG_OBJECT (gpointer obj, const char *format, ...)
+{
+ va_list varargs;
+
+ va_start (varargs, format);
+ GST_CAT_LEVEL_LOG_valist (GST_CAT_DEFAULT, GST_LEVEL_DEBUG, obj, format,
+ varargs);
+ va_end (varargs);
+}
+
+static inline void
+GST_LOG_OBJECT (gpointer obj, const char *format, ...)
+{
+ va_list varargs;
+
+ va_start (varargs, format);
+ GST_CAT_LEVEL_LOG_valist (GST_CAT_DEFAULT, GST_LEVEL_LOG, obj, format,
+ varargs);
+ va_end (varargs);
+}
+
+static inline void
+GST_FIXME_OBJECT (gpointer obj, const char *format, ...)
+{
+ va_list varargs;
+
+ va_start (varargs, format);
+ GST_CAT_LEVEL_LOG_valist (GST_CAT_DEFAULT, GST_LEVEL_FIXME, obj, format,
+ varargs);
+ va_end (varargs);
+}
+
+static inline void
+GST_TRACE_OBJECT (gpointer obj, const char *format, ...)
+{
+ va_list varargs;
+
+ va_start (varargs, format);
+ GST_CAT_LEVEL_LOG_valist (GST_CAT_DEFAULT, GST_LEVEL_TRACE, obj, format,
+ varargs);
+ va_end (varargs);
+}
+
+static inline void
+GST_ERROR (const char *format, ...)
+{
+ va_list varargs;
+
+ va_start (varargs, format);
+ GST_CAT_LEVEL_LOG_valist (GST_CAT_DEFAULT, GST_LEVEL_ERROR, NULL, format,
+ varargs);
+ va_end (varargs);
+}
+
+static inline void
+GST_WARNING (const char *format, ...)
+{
+ va_list varargs;
+
+ va_start (varargs, format);
+ GST_CAT_LEVEL_LOG_valist (GST_CAT_DEFAULT, GST_LEVEL_WARNING, NULL, format,
+ varargs);
+ va_end (varargs);
+}
+
+static inline void
+GST_INFO (const char *format, ...)
+{
+ va_list varargs;
+
+ va_start (varargs, format);
+ GST_CAT_LEVEL_LOG_valist (GST_CAT_DEFAULT, GST_LEVEL_INFO, NULL, format,
+ varargs);
+ va_end (varargs);
+}
+
+static inline void
+GST_DEBUG (const char *format, ...)
+{
+ va_list varargs;
+
+ va_start (varargs, format);
+ GST_CAT_LEVEL_LOG_valist (GST_CAT_DEFAULT, GST_LEVEL_DEBUG, NULL, format,
+ varargs);
+ va_end (varargs);
+}
+
+static inline void
+GST_LOG (const char *format, ...)
+{
+ va_list varargs;
+
+ va_start (varargs, format);
+ GST_CAT_LEVEL_LOG_valist (GST_CAT_DEFAULT, GST_LEVEL_LOG, NULL,
+ format, varargs);
+ va_end (varargs);
+}
+
+static inline void
+GST_FIXME (const char *format, ...)
+{
+ va_list varargs;
+
+ va_start (varargs, format);
+ GST_CAT_LEVEL_LOG_valist (GST_CAT_DEFAULT, GST_LEVEL_FIXME, NULL, format,
+ varargs);
+ va_end (varargs);
+}
+
+static inline void
+GST_TRACE (const char *format, ...)
+{
+ va_list varargs;
+
+ va_start (varargs, format);
+ GST_CAT_LEVEL_LOG_valist (GST_CAT_DEFAULT, GST_LEVEL_TRACE, NULL, format,
+ varargs);
+ va_end (varargs);
+}
+#endif
+#endif
+
+
+/********** function pointer stuff **********/
+
+/**
+ * GST_DEBUG_REGISTER_FUNCPTR:
+ * @ptr: pointer to the function to register
+ *
+ * Register a pointer to a function with its name, so it can later be used by
+ * GST_DEBUG_FUNCPTR_NAME().
+ *
+ * Use this variant of #GST_DEBUG_FUNCPTR if you do not need to use @ptr.
+ *
+ * Since: 0.10.26
+ */
+#define GST_DEBUG_REGISTER_FUNCPTR(ptr) \
+ _gst_debug_register_funcptr((GstDebugFuncPtr)(ptr), #ptr)
+/**
+ * GST_DEBUG_FUNCPTR:
+ * @ptr: pointer to the function to register
+ *
+ * Register a pointer to a function with its name, so it can later be used by
+ * GST_DEBUG_FUNCPTR_NAME().
+ *
+ * Returns: the value passed to @ptr.
+ */
+#define GST_DEBUG_FUNCPTR(ptr) \
+ (_gst_debug_register_funcptr((GstDebugFuncPtr)(ptr), #ptr) , ptr)
+
+/**
+ * GST_DEBUG_FUNCPTR_NAME:
+ * @ptr: address of the function of which to look up the name
+ *
+ * Retrieves the name of the function, if it was previously registered with
+ * GST_DEBUG_FUNCPTR(). If not, it returns a description of the pointer.
+ *
+ * This macro returns a constant string which must not be modified or
+ * freed by the caller.
+ */
+#define GST_DEBUG_FUNCPTR_NAME(ptr) \
+ _gst_debug_nameof_funcptr((GstDebugFuncPtr)ptr)
+
+
+#else /* GST_DISABLE_GST_DEBUG */
+
+
+#ifndef GST_INFO_C
+
+#if defined(__GNUC__) && __GNUC__ >= 3
+# pragma GCC poison gst_debug_log
+# pragma GCC poison gst_debug_log_valist
+# pragma GCC poison _gst_debug_category_new
+#endif
+
+#define __gst_debug_min GST_LEVEL_NONE
+
+#define _gst_debug_init() G_STMT_START{ }G_STMT_END
+
+#define gst_debug_set_default_threshold(level) G_STMT_START{ }G_STMT_END
+#define gst_debug_get_default_threshold() (GST_LEVEL_NONE)
+
+#define gst_debug_level_get_name(level) ("NONE")
+#define gst_debug_message_get(message) ("")
+#define gst_debug_add_log_function(func,data) G_STMT_START{ }G_STMT_END
+#define gst_debug_set_active(active) G_STMT_START{ }G_STMT_END
+#define gst_debug_is_active() (FALSE)
+#define gst_debug_set_colored(colored) G_STMT_START{ }G_STMT_END
+#define gst_debug_is_colored() (FALSE)
+#define gst_debug_set_default_threshold(level) G_STMT_START{ }G_STMT_END
+#define gst_debug_get_default_threshold() (GST_LEVEL_NONE)
+#define gst_debug_set_threshold_for_name(name,level) G_STMT_START{ }G_STMT_END
+#define gst_debug_unset_threshold_for_name(name) G_STMT_START{ }G_STMT_END
+
+/* we are using dummy function prototypes here to eat ';' as these macros are
+ * used outside of functions */
+#define GST_DEBUG_CATEGORY(var) void _gst_debug_dummy_##var (void)
+#define GST_DEBUG_CATEGORY_EXTERN(var) void _gst_debug_dummy_extern_##var (void)
+#define GST_DEBUG_CATEGORY_STATIC(var) void _gst_debug_dummy_static_##var (void)
+
+#define GST_DEBUG_CATEGORY_INIT(var,name,color,desc) G_STMT_START{ }G_STMT_END
+#define GST_DEBUG_CATEGORY_GET(var,name) G_STMT_START{ }G_STMT_END
+#define gst_debug_category_free(category) G_STMT_START{ }G_STMT_END
+#define gst_debug_category_set_threshold(category,level) G_STMT_START{ }G_STMT_END
+#define gst_debug_category_reset_threshold(category) G_STMT_START{ }G_STMT_END
+#define gst_debug_category_get_threshold(category) (GST_LEVEL_NONE)
+#define gst_debug_category_get_name(cat) ("")
+#define gst_debug_category_get_color(cat) (0)
+#define gst_debug_category_get_description(cat) ("")
+#define gst_debug_get_all_categories() (NULL)
+#define gst_debug_construct_term_color(colorinfo) (g_strdup ("00"))
+#define gst_debug_construct_win_color(colorinfo) (0)
+
+#endif /* !GST_INFO_C */
+
+#ifdef G_HAVE_ISO_VARARGS
+
+#define GST_CAT_LEVEL_LOG(cat,level,...) G_STMT_START{ }G_STMT_END
+
+#define GST_CAT_ERROR_OBJECT(...) G_STMT_START{ }G_STMT_END
+#define GST_CAT_WARNING_OBJECT(...) G_STMT_START{ }G_STMT_END
+#define GST_CAT_INFO_OBJECT(...) G_STMT_START{ }G_STMT_END
+#define GST_CAT_DEBUG_OBJECT(...) G_STMT_START{ }G_STMT_END
+#define GST_CAT_LOG_OBJECT(...) G_STMT_START{ }G_STMT_END
+#define GST_CAT_FIXME_OBJECT(...) G_STMT_START{ }G_STMT_END
+#define GST_CAT_TRACE_OBJECT(...) G_STMT_START{ }G_STMT_END
+
+#define GST_CAT_ERROR(...) G_STMT_START{ }G_STMT_END
+#define GST_CAT_WARNING(...) G_STMT_START{ }G_STMT_END
+#define GST_CAT_INFO(...) G_STMT_START{ }G_STMT_END
+#define GST_CAT_DEBUG(...) G_STMT_START{ }G_STMT_END
+#define GST_CAT_LOG(...) G_STMT_START{ }G_STMT_END
+#define GST_CAT_FIXME(...) G_STMT_START{ }G_STMT_END
+#define GST_CAT_TRACE(...) G_STMT_START{ }G_STMT_END
+
+#define GST_ERROR_OBJECT(...) G_STMT_START{ }G_STMT_END
+#define GST_WARNING_OBJECT(...) G_STMT_START{ }G_STMT_END
+#define GST_INFO_OBJECT(...) G_STMT_START{ }G_STMT_END
+#define GST_DEBUG_OBJECT(...) G_STMT_START{ }G_STMT_END
+#define GST_LOG_OBJECT(...) G_STMT_START{ }G_STMT_END
+#define GST_FIXME_OBJECT(...) G_STMT_START{ }G_STMT_END
+#define GST_TRACE_OBJECT(...) G_STMT_START{ }G_STMT_END
+
+#define GST_ERROR(...) G_STMT_START{ }G_STMT_END
+#define GST_WARNING(...) G_STMT_START{ }G_STMT_END
+#define GST_INFO(...) G_STMT_START{ }G_STMT_END
+#define GST_DEBUG(...) G_STMT_START{ }G_STMT_END
+#define GST_LOG(...) G_STMT_START{ }G_STMT_END
+#define GST_FIXME(...) G_STMT_START{ }G_STMT_END
+#define GST_TRACE(...) G_STMT_START{ }G_STMT_END
+
+#else /* !G_HAVE_ISO_VARARGS */
+#ifdef G_HAVE_GNUC_VARARGS
+
+#define GST_CAT_LEVEL_LOG(cat,level,args...) G_STMT_START{ }G_STMT_END
+
+#define GST_CAT_ERROR_OBJECT(args...) G_STMT_START{ }G_STMT_END
+#define GST_CAT_WARNING_OBJECT(args...) G_STMT_START{ }G_STMT_END
+#define GST_CAT_INFO_OBJECT(args...) G_STMT_START{ }G_STMT_END
+#define GST_CAT_DEBUG_OBJECT(args...) G_STMT_START{ }G_STMT_END
+#define GST_CAT_LOG_OBJECT(args...) G_STMT_START{ }G_STMT_END
+#define GST_CAT_FIXME_OBJECT(args...) G_STMT_START{ }G_STMT_END
+#define GST_CAT_TRACE_OBJECT(args...) G_STMT_START{ }G_STMT_END
+
+#define GST_CAT_ERROR(args...) G_STMT_START{ }G_STMT_END
+#define GST_CAT_WARNING(args...) G_STMT_START{ }G_STMT_END
+#define GST_CAT_INFO(args...) G_STMT_START{ }G_STMT_END
+#define GST_CAT_DEBUG(args...) G_STMT_START{ }G_STMT_END
+#define GST_CAT_LOG(args...) G_STMT_START{ }G_STMT_END
+#define GST_CAT_FIXME(args...) G_STMT_START{ }G_STMT_END
+#define GST_CAT_TRACE(args...) G_STMT_START{ }G_STMT_END
+
+#define GST_ERROR_OBJECT(args...) G_STMT_START{ }G_STMT_END
+#define GST_WARNING_OBJECT(args...) G_STMT_START{ }G_STMT_END
+#define GST_INFO_OBJECT(args...) G_STMT_START{ }G_STMT_END
+#define GST_DEBUG_OBJECT(args...) G_STMT_START{ }G_STMT_END
+#define GST_LOG_OBJECT(args...) G_STMT_START{ }G_STMT_END
+#define GST_FIXME_OBJECT(args...) G_STMT_START{ }G_STMT_END
+#define GST_TRACE_OBJECT(args...) G_STMT_START{ }G_STMT_END
+
+#define GST_ERROR(args...) G_STMT_START{ }G_STMT_END
+#define GST_WARNING(args...) G_STMT_START{ }G_STMT_END
+#define GST_INFO(args...) G_STMT_START{ }G_STMT_END
+#define GST_DEBUG(args...) G_STMT_START{ }G_STMT_END
+#define GST_LOG(args...) G_STMT_START{ }G_STMT_END
+#define GST_FIXME(args...) G_STMT_START{ }G_STMT_END
+#define GST_TRACE(args...) G_STMT_START{ }G_STMT_END
+
+#else /* !G_HAVE_GNUC_VARARGS */
+static inline void
+GST_CAT_LEVEL_LOG_valist (GstDebugCategory * cat,
+ GstDebugLevel level, gpointer object, const char *format, va_list varargs)
+{
+}
+
+static inline void
+GST_CAT_ERROR_OBJECT (GstDebugCategory * cat, gpointer obj, const char *format,
+ ...)
+{
+}
+
+static inline void
+GST_CAT_WARNING_OBJECT (GstDebugCategory * cat, gpointer obj,
+ const char *format, ...)
+{
+}
+
+static inline void
+GST_CAT_INFO_OBJECT (GstDebugCategory * cat, gpointer obj, const char *format,
+ ...)
+{
+}
+
+static inline void
+GST_CAT_DEBUG_OBJECT (GstDebugCategory * cat, gpointer obj, const char *format,
+ ...)
+{
+}
+
+static inline void
+GST_CAT_LOG_OBJECT (GstDebugCategory * cat, gpointer obj, const char *format,
+ ...)
+{
+}
+
+static inline void
+GST_CAT_FIXME_OBJECT (GstDebugCategory * cat, gpointer obj, const char *format,
+ ...)
+{
+}
+
+static inline void
+GST_CAT_TRACE_OBJECT (GstDebugCategory * cat, gpointer obj, const char *format,
+ ...)
+{
+}
+
+static inline void
+GST_CAT_ERROR (GstDebugCategory * cat, const char *format, ...)
+{
+}
+
+static inline void
+GST_CAT_WARNING (GstDebugCategory * cat, const char *format, ...)
+{
+}
+
+static inline void
+GST_CAT_INFO (GstDebugCategory * cat, const char *format, ...)
+{
+}
+
+static inline void
+GST_CAT_DEBUG (GstDebugCategory * cat, const char *format, ...)
+{
+}
+
+static inline void
+GST_CAT_LOG (GstDebugCategory * cat, const char *format, ...)
+{
+}
+
+static inline void
+GST_CAT_FIXME (GstDebugCategory * cat, const char *format, ...)
+{
+}
+
+static inline void
+GST_CAT_TRACE (GstDebugCategory * cat, const char *format, ...)
+{
+}
+
+static inline void
+GST_ERROR_OBJECT (gpointer obj, const char *format, ...)
+{
+}
+
+static inline void
+GST_WARNING_OBJECT (gpointer obj, const char *format, ...)
+{
+}
+
+static inline void
+GST_INFO_OBJECT (gpointer obj, const char *format, ...)
+{
+}
+
+static inline void
+GST_DEBUG_OBJECT (gpointer obj, const char *format, ...)
+{
+}
+
+static inline void
+GST_LOG_OBJECT (gpointer obj, const char *format, ...)
+{
+}
+
+static inline void
+GST_FIXME_OBJECT (gpointer obj, const char *format, ...)
+{
+}
+
+static inline void
+GST_TRACE_OBJECT (gpointer obj, const char *format, ...)
+{
+}
+
+static inline void
+GST_ERROR (const char *format, ...)
+{
+}
+
+static inline void
+GST_WARNING (const char *format, ...)
+{
+}
+
+static inline void
+GST_INFO (const char *format, ...)
+{
+}
+
+static inline void
+GST_DEBUG (const char *format, ...)
+{
+}
+
+static inline void
+GST_LOG (const char *format, ...)
+{
+}
+
+static inline void
+GST_FIXME (const char *format, ...)
+{
+}
+
+static inline void
+GST_TRACE (const char *format, ...)
+{
+}
+
+#endif /* G_HAVE_GNUC_VARARGS */
+#endif /* G_HAVE_ISO_VARARGS */
+
+#define GST_DEBUG_REGISTER_FUNCPTR(ptr) G_STMT_START{ }G_STMT_END
+#define GST_DEBUG_FUNCPTR(ptr) (ptr)
+#define GST_DEBUG_FUNCPTR_NAME(ptr) (g_strdup_printf ("%p", ptr))
+
+#define GST_CAT_MEMDUMP_OBJECT(cat,obj,msg,data,length) G_STMT_START{ }G_STMT_END
+#define GST_CAT_MEMDUMP(cat,msg,data,length) G_STMT_START{ }G_STMT_END
+#define GST_MEMDUMP_OBJECT(obj,msg,data,length) G_STMT_START{ }G_STMT_END
+#define GST_MEMDUMP(msg,data,length) G_STMT_START{ }G_STMT_END
+
+#endif /* GST_DISABLE_GST_DEBUG */
+
+
+void gst_debug_print_stack_trace (void);
+
+G_END_DECLS
+
+#endif /* __GSTINFO_H__ */
diff --git a/gst/gstiterator.c b/gst/gstiterator.c
new file mode 100644
index 0000000..56675b4
--- /dev/null
+++ b/gst/gstiterator.c
@@ -0,0 +1,830 @@
+/* GStreamer
+ * Copyright (C) 2004 Wim Taymans <wim@fluendo.com>
+ * Copyright (C) 2011 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+ *
+ * gstiterator.h: Base class for iterating datastructures.
+ *
+ * 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:gstiterator
+ * @short_description: Object to retrieve multiple elements in a threadsafe
+ * way.
+ * @see_also: #GstElement, #GstBin
+ *
+ * A GstIterator is used to retrieve multiple objects from another object in
+ * a threadsafe way.
+ *
+ * Various GStreamer objects provide access to their internal structures using
+ * an iterator.
+ *
+ * In general, whenever calling a GstIterator function results in your code
+ * receiving a refcounted object, the refcount for that object will have been
+ * increased. Your code is responsible for unrefing that object after use.
+ *
+ * The basic use pattern of an iterator is as follows:
+ *
+ * <example>
+ * <title>Using an iterator</title>
+ * <programlisting>
+ * it = _get_iterator(object);
+ * done = FALSE;
+ * while (!done) {
+ * switch (gst_iterator_next (it, &amp;item)) {
+ * case GST_ITERATOR_OK:
+ * ... use/change item here...
+ * g_value_reset (&amp;item);
+ * break;
+ * case GST_ITERATOR_RESYNC:
+ * ...rollback changes to items...
+ * gst_iterator_resync (it);
+ * break;
+ * case GST_ITERATOR_ERROR:
+ * ...wrong parameters were given...
+ * done = TRUE;
+ * break;
+ * case GST_ITERATOR_DONE:
+ * done = TRUE;
+ * break;
+ * }
+ * }
+ * g_value_unset (&amp;item);
+ * gst_iterator_free (it);
+ * </programlisting>
+ * </example>
+ *
+ * Last reviewed on 2009-06-16 (0.10.24)
+ */
+
+#include "gst_private.h"
+#include <gst/gstiterator.h>
+
+GstIterator *
+gst_iterator_copy (const GstIterator * it)
+{
+ GstIterator *copy;
+
+ copy = g_slice_copy (it->size, it);
+ if (it->copy)
+ it->copy (it, copy);
+
+ return copy;
+}
+
+GType
+gst_iterator_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0))
+ type = g_boxed_type_register_static ("GstIterator",
+ (GBoxedCopyFunc) gst_iterator_copy, (GBoxedFreeFunc) gst_iterator_free);
+ return type;
+}
+
+static void
+gst_iterator_init (GstIterator * it,
+ guint size,
+ GType type,
+ GMutex * lock,
+ guint32 * master_cookie,
+ GstIteratorCopyFunction copy,
+ GstIteratorNextFunction next,
+ GstIteratorItemFunction item,
+ GstIteratorResyncFunction resync, GstIteratorFreeFunction free)
+{
+ it->size = size;
+ it->type = type;
+ it->lock = lock;
+ it->master_cookie = master_cookie;
+ it->cookie = *master_cookie;
+ it->copy = copy;
+ it->next = next;
+ it->item = item;
+ it->resync = resync;
+ it->free = free;
+ it->pushed = NULL;
+}
+
+/**
+ * gst_iterator_new:
+ * @size: the size of the iterator structure
+ * @type: #GType of children
+ * @lock: pointer to a #GMutex.
+ * @master_cookie: pointer to a guint32 that is changed when the items in the
+ * iterator changed.
+ * @copy: copy function
+ * @next: function to get next item
+ * @item: function to call on each item retrieved
+ * @resync: function to resync the iterator
+ * @free: function to free the iterator
+ *
+ * Create a new iterator. This function is mainly used for objects
+ * implementing the next/resync/free function to iterate a data structure.
+ *
+ * For each item retrieved, the @item function is called with the lock
+ * held. The @free function is called when the iterator is freed.
+ *
+ * Returns: the new #GstIterator.
+ *
+ * MT safe.
+ */
+GstIterator *
+gst_iterator_new (guint size,
+ GType type,
+ GMutex * lock,
+ guint32 * master_cookie,
+ GstIteratorCopyFunction copy,
+ GstIteratorNextFunction next,
+ GstIteratorItemFunction item,
+ GstIteratorResyncFunction resync, GstIteratorFreeFunction free)
+{
+ GstIterator *result;
+
+ g_return_val_if_fail (size >= sizeof (GstIterator), NULL);
+ g_return_val_if_fail (g_type_qname (type) != 0, NULL);
+ g_return_val_if_fail (master_cookie != NULL, NULL);
+ g_return_val_if_fail (next != NULL, NULL);
+ g_return_val_if_fail (resync != NULL, NULL);
+ g_return_val_if_fail (free != NULL, NULL);
+
+ result = g_slice_alloc0 (size);
+ gst_iterator_init (result, size, type, lock, master_cookie, copy, next, item,
+ resync, free);
+
+ return result;
+}
+
+/*
+ * list iterator
+ */
+typedef struct _GstListIterator
+{
+ GstIterator iterator;
+ GObject *owner;
+ GList **orig;
+ GList *list; /* pointer in list */
+
+ void (*set_value) (GValue * value, gpointer item);
+} GstListIterator;
+
+static void
+gst_list_iterator_copy (const GstListIterator * it, GstListIterator * copy)
+{
+ if (copy->owner)
+ g_object_ref (copy->owner);
+}
+
+static GstIteratorResult
+gst_list_iterator_next (GstListIterator * it, GValue * elem)
+{
+ gpointer data;
+
+ if (it->list == NULL)
+ return GST_ITERATOR_DONE;
+
+ data = it->list->data;
+ it->list = g_list_next (it->list);
+
+ it->set_value (elem, data);
+
+ return GST_ITERATOR_OK;
+}
+
+static void
+gst_list_iterator_resync (GstListIterator * it)
+{
+ it->list = *it->orig;
+}
+
+static void
+gst_list_iterator_free (GstListIterator * it)
+{
+ if (it->owner)
+ g_object_unref (it->owner);
+}
+
+/**
+ * gst_iterator_new_list:
+ * @type: #GType of elements
+ * @lock: pointer to a #GMutex protecting the list.
+ * @master_cookie: pointer to a guint32 that is incremented when the list
+ * is changed.
+ * @list: pointer to the list
+ * @owner: object owning the list
+ * @item: function to call on each item retrieved
+ *
+ * Create a new iterator designed for iterating @list.
+ *
+ * The list you iterate is usually part of a data structure @owner and is
+ * protected with @lock.
+ *
+ * The iterator will use @lock to retrieve the next item of the list and it
+ * will then call the @item function before releasing @lock again.
+ *
+ * When a concurrent update to the list is performed, usually by @owner while
+ * holding @lock, @master_cookie will be updated. The iterator implementation
+ * will notice the update of the cookie and will return %GST_ITERATOR_RESYNC to
+ * the user of the iterator in the next call to gst_iterator_next().
+ *
+ * Returns: the new #GstIterator for @list.
+ *
+ * MT safe.
+ */
+GstIterator *
+gst_iterator_new_list (GType type,
+ GMutex * lock, guint32 * master_cookie, GList ** list, GObject * owner,
+ GstIteratorItemFunction item)
+{
+ GstListIterator *result;
+ gpointer set_value;
+
+ if (g_type_is_a (type, G_TYPE_OBJECT)) {
+ set_value = g_value_set_object;
+ } else if (g_type_is_a (type, G_TYPE_BOXED)) {
+ set_value = g_value_set_boxed;
+ } else if (g_type_is_a (type, G_TYPE_POINTER)) {
+ set_value = g_value_set_pointer;
+ } else if (g_type_is_a (type, G_TYPE_STRING)) {
+ set_value = g_value_set_string;
+ } else {
+ g_critical ("List iterators can only be created for lists containing "
+ "instances of GObject, boxed types, pointer types and strings");
+ return NULL;
+ }
+
+ /* no need to lock, nothing can change here */
+ result = (GstListIterator *) gst_iterator_new (sizeof (GstListIterator),
+ type,
+ lock,
+ master_cookie,
+ (GstIteratorCopyFunction) gst_list_iterator_copy,
+ (GstIteratorNextFunction) gst_list_iterator_next,
+ (GstIteratorItemFunction) item,
+ (GstIteratorResyncFunction) gst_list_iterator_resync,
+ (GstIteratorFreeFunction) gst_list_iterator_free);
+
+ result->owner = owner ? g_object_ref (owner) : NULL;
+ result->orig = list;
+ result->list = *list;
+ result->set_value = set_value;
+
+ return GST_ITERATOR (result);
+}
+
+static void
+gst_iterator_pop (GstIterator * it)
+{
+ if (it->pushed) {
+ gst_iterator_free (it->pushed);
+ it->pushed = NULL;
+ }
+}
+
+/**
+ * gst_iterator_next:
+ * @it: The #GstIterator to iterate
+ * @elem: (out caller-allocates): pointer to hold next element
+ *
+ * Get the next item from the iterator in @elem.
+ *
+ * Only when this function returns %GST_ITERATOR_OK, @elem will contain a valid
+ * value. @elem must have been initialized to the type of the iterator or
+ * initialized to zeroes with g_value_unset(). The caller is responsible for
+ * unsetting or resetting @elem with g_value_unset() or g_value_reset()
+ * after usage.
+ *
+ * When this function returns %GST_ITERATOR_DONE, no more elements can be
+ * retrieved from @it.
+ *
+ * A return value of %GST_ITERATOR_RESYNC indicates that the element list was
+ * concurrently updated. The user of @it should call gst_iterator_resync() to
+ * get the newly updated list.
+ *
+ * A return value of %GST_ITERATOR_ERROR indicates an unrecoverable fatal error.
+ *
+ * Returns: The result of the iteration. Unset @elem after usage.
+ *
+ * MT safe.
+ */
+GstIteratorResult
+gst_iterator_next (GstIterator * it, GValue * elem)
+{
+ GstIteratorResult result;
+
+ g_return_val_if_fail (it != NULL, GST_ITERATOR_ERROR);
+ g_return_val_if_fail (elem != NULL, GST_ITERATOR_ERROR);
+ g_return_val_if_fail (G_VALUE_TYPE (elem) == G_TYPE_INVALID
+ || G_VALUE_HOLDS (elem, it->type), GST_ITERATOR_ERROR);
+
+ if (G_VALUE_TYPE (elem) == G_TYPE_INVALID)
+ g_value_init (elem, it->type);
+
+restart:
+ if (it->pushed) {
+ result = gst_iterator_next (it->pushed, elem);
+ if (result == GST_ITERATOR_DONE) {
+ /* we are done with this iterator, pop it and
+ * fallthrough iterating the main iterator again. */
+ gst_iterator_pop (it);
+ } else {
+ return result;
+ }
+ }
+
+ if (G_LIKELY (it->lock))
+ g_mutex_lock (it->lock);
+
+ if (G_UNLIKELY (*it->master_cookie != it->cookie)) {
+ result = GST_ITERATOR_RESYNC;
+ goto done;
+ }
+
+ result = it->next (it, elem);
+ if (result == GST_ITERATOR_OK && it->item) {
+ GstIteratorItem itemres;
+
+ itemres = it->item (it, elem);
+ switch (itemres) {
+ case GST_ITERATOR_ITEM_SKIP:
+ if (G_LIKELY (it->lock))
+ g_mutex_unlock (it->lock);
+ g_value_reset (elem);
+ goto restart;
+ case GST_ITERATOR_ITEM_END:
+ result = GST_ITERATOR_DONE;
+ g_value_reset (elem);
+ break;
+ case GST_ITERATOR_ITEM_PASS:
+ break;
+ }
+ }
+
+done:
+ if (G_LIKELY (it->lock))
+ g_mutex_unlock (it->lock);
+
+ return result;
+}
+
+/**
+ * gst_iterator_resync:
+ * @it: The #GstIterator to resync
+ *
+ * Resync the iterator. this function is mostly called
+ * after gst_iterator_next() returned %GST_ITERATOR_RESYNC.
+ *
+ * When an iterator was pushed on @it, it will automatically be popped again
+ * with this function.
+ *
+ * MT safe.
+ */
+void
+gst_iterator_resync (GstIterator * it)
+{
+ g_return_if_fail (it != NULL);
+
+ gst_iterator_pop (it);
+
+ if (G_LIKELY (it->lock))
+ g_mutex_lock (it->lock);
+ it->resync (it);
+ it->cookie = *it->master_cookie;
+ if (G_LIKELY (it->lock))
+ g_mutex_unlock (it->lock);
+}
+
+/**
+ * gst_iterator_free:
+ * @it: The #GstIterator to free
+ *
+ * Free the iterator.
+ *
+ * MT safe.
+ */
+void
+gst_iterator_free (GstIterator * it)
+{
+ g_return_if_fail (it != NULL);
+
+ gst_iterator_pop (it);
+
+ it->free (it);
+
+ g_slice_free1 (it->size, it);
+}
+
+/**
+ * gst_iterator_push:
+ * @it: The #GstIterator to use
+ * @other: The #GstIterator to push
+ *
+ * Pushes @other iterator onto @it. All calls performed on @it are
+ * forwarded to @other. If @other returns %GST_ITERATOR_DONE, it is
+ * popped again and calls are handled by @it again.
+ *
+ * This function is mainly used by objects implementing the iterator
+ * next function to recurse into substructures.
+ *
+ * When gst_iterator_resync() is called on @it, @other will automatically be
+ * popped.
+ *
+ * MT safe.
+ */
+void
+gst_iterator_push (GstIterator * it, GstIterator * other)
+{
+ g_return_if_fail (it != NULL);
+ g_return_if_fail (other != NULL);
+
+ it->pushed = other;
+}
+
+typedef struct _GstIteratorFilter
+{
+ GstIterator iterator;
+ GstIterator *slave;
+
+ GCompareFunc func;
+ GValue user_data;
+ gboolean have_user_data;
+} GstIteratorFilter;
+
+static GstIteratorResult
+filter_next (GstIteratorFilter * it, GValue * elem)
+{
+ GstIteratorResult result = GST_ITERATOR_ERROR;
+ gboolean done = FALSE;
+ GValue item = { 0, };
+
+ while (G_LIKELY (!done)) {
+ result = gst_iterator_next (it->slave, &item);
+ switch (result) {
+ case GST_ITERATOR_OK:
+ if (G_LIKELY (GST_ITERATOR (it)->lock))
+ g_mutex_unlock (GST_ITERATOR (it)->lock);
+ if (it->func (&item, &it->user_data) == 0) {
+ g_value_copy (&item, elem);
+ done = TRUE;
+ }
+ g_value_reset (&item);
+ if (G_LIKELY (GST_ITERATOR (it)->lock))
+ g_mutex_lock (GST_ITERATOR (it)->lock);
+ break;
+ case GST_ITERATOR_RESYNC:
+ case GST_ITERATOR_DONE:
+ done = TRUE;
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+ }
+ g_value_unset (&item);
+ return result;
+}
+
+static void
+filter_copy (const GstIteratorFilter * it, GstIteratorFilter * copy)
+{
+ copy->slave = gst_iterator_copy (it->slave);
+
+ if (it->have_user_data) {
+ memset (&copy->user_data, 0, sizeof (copy->user_data));
+ g_value_init (&copy->user_data, G_VALUE_TYPE (&it->user_data));
+ g_value_copy (&it->user_data, &copy->user_data);
+ }
+}
+
+static void
+filter_resync (GstIteratorFilter * it)
+{
+ gst_iterator_resync (it->slave);
+}
+
+static void
+filter_free (GstIteratorFilter * it)
+{
+ if (it->have_user_data)
+ g_value_unset (&it->user_data);
+ gst_iterator_free (it->slave);
+}
+
+/**
+ * gst_iterator_filter:
+ * @it: The #GstIterator to filter
+ * @func: (scope call): the compare function to select elements
+ * @user_data: (closure): user data passed to the compare function
+ *
+ * Create a new iterator from an existing iterator. The new iterator
+ * will only return those elements that match the given compare function @func.
+ * The first parameter that is passed to @func is the #GValue of the current
+ * iterator element and the second parameter is @user_data. @func should
+ * return 0 for elements that should be included in the filtered iterator.
+ *
+ * When this iterator is freed, @it will also be freed.
+ *
+ * Returns: (transfer full): a new #GstIterator.
+ *
+ * MT safe.
+ */
+GstIterator *
+gst_iterator_filter (GstIterator * it, GCompareFunc func,
+ const GValue * user_data)
+{
+ GstIteratorFilter *result;
+
+ g_return_val_if_fail (it != NULL, NULL);
+ g_return_val_if_fail (func != NULL, NULL);
+
+ result = (GstIteratorFilter *) gst_iterator_new (sizeof (GstIteratorFilter),
+ it->type, it->lock, it->master_cookie,
+ (GstIteratorCopyFunction) filter_copy,
+ (GstIteratorNextFunction) filter_next,
+ (GstIteratorItemFunction) NULL,
+ (GstIteratorResyncFunction) filter_resync,
+ (GstIteratorFreeFunction) filter_free);
+
+ it->lock = NULL;
+ result->func = func;
+ if (user_data) {
+ g_value_init (&result->user_data, G_VALUE_TYPE (user_data));
+ g_value_copy (user_data, &result->user_data);
+ result->have_user_data = TRUE;
+ } else {
+ result->have_user_data = FALSE;
+ }
+ result->slave = it;
+
+ return GST_ITERATOR (result);
+}
+
+/**
+ * gst_iterator_fold:
+ * @it: The #GstIterator to fold over
+ * @func: (scope call): the fold function
+ * @ret: the seed value passed to the fold function
+ * @user_data: (closure): user data passed to the fold function
+ *
+ * Folds @func over the elements of @iter. That is to say, @func will be called
+ * as @func (object, @ret, @user_data) for each object in @it. The normal use
+ * of this procedure is to accumulate the results of operating on the objects in
+ * @ret.
+ *
+ * This procedure can be used (and is used internally) to implement the
+ * gst_iterator_foreach() and gst_iterator_find_custom() operations.
+ *
+ * The fold will proceed as long as @func returns TRUE. When the iterator has no
+ * more arguments, %GST_ITERATOR_DONE will be returned. If @func returns FALSE,
+ * the fold will stop, and %GST_ITERATOR_OK will be returned. Errors or resyncs
+ * will cause fold to return %GST_ITERATOR_ERROR or %GST_ITERATOR_RESYNC as
+ * appropriate.
+ *
+ * The iterator will not be freed.
+ *
+ * Returns: A #GstIteratorResult, as described above.
+ *
+ * MT safe.
+ */
+GstIteratorResult
+gst_iterator_fold (GstIterator * it, GstIteratorFoldFunction func,
+ GValue * ret, gpointer user_data)
+{
+ GValue item = { 0, };
+ GstIteratorResult result;
+
+ while (1) {
+ result = gst_iterator_next (it, &item);
+ switch (result) {
+ case GST_ITERATOR_OK:
+ if (!func (&item, ret, user_data))
+ goto fold_done;
+
+ g_value_reset (&item);
+ break;
+ case GST_ITERATOR_RESYNC:
+ case GST_ITERATOR_ERROR:
+ goto fold_done;
+ case GST_ITERATOR_DONE:
+ goto fold_done;
+ }
+ }
+
+fold_done:
+ g_value_unset (&item);
+
+ return result;
+}
+
+typedef struct
+{
+ GstIteratorForeachFunction func;
+ gpointer user_data;
+} ForeachFoldData;
+
+static gboolean
+foreach_fold_func (const GValue * item, GValue * unused, ForeachFoldData * data)
+{
+ data->func (item, data->user_data);
+ return TRUE;
+}
+
+/**
+ * gst_iterator_foreach:
+ * @it: The #GstIterator to iterate
+ * @func: (scope call): the function to call for each element.
+ * @user_data: (closure): user data passed to the function
+ *
+ * Iterate over all element of @it and call the given function @func for
+ * each element.
+ *
+ * Returns: the result call to gst_iterator_fold(). The iterator will not be
+ * freed.
+ *
+ * MT safe.
+ */
+GstIteratorResult
+gst_iterator_foreach (GstIterator * it, GstIteratorForeachFunction func,
+ gpointer user_data)
+{
+ ForeachFoldData data;
+
+ data.func = func;
+ data.user_data = user_data;
+
+ return gst_iterator_fold (it, (GstIteratorFoldFunction) foreach_fold_func,
+ NULL, &data);
+}
+
+typedef struct
+{
+ GCompareFunc func;
+ gpointer user_data;
+ gboolean found;
+} FindCustomFoldData;
+
+static gboolean
+find_custom_fold_func (const GValue * item, GValue * ret,
+ FindCustomFoldData * data)
+{
+ if (data->func (item, data->user_data) == 0) {
+ data->found = TRUE;
+ g_value_copy (item, ret);
+ return FALSE;
+ } else {
+ return TRUE;
+ }
+}
+
+/**
+ * gst_iterator_find_custom:
+ * @it: The #GstIterator to iterate
+ * @func: (scope call): the compare function to use
+ * @elem: (out): pointer to a #GValue where to store the result
+ * @user_data: (closure): user data passed to the compare function
+ *
+ * Find the first element in @it that matches the compare function @func.
+ * @func should return 0 when the element is found. The first parameter
+ * to @func will be the current element of the iterator and the
+ * second parameter will be @user_data.
+ * The result will be stored in @elem if a result is found.
+ *
+ * The iterator will not be freed.
+ *
+ * This function will return FALSE if an error happened to the iterator
+ * or if the element wasn't found.
+ *
+ * Returns: Returns TRUE if the element was found, else FALSE.
+ *
+ * MT safe.
+ */
+gboolean
+gst_iterator_find_custom (GstIterator * it, GCompareFunc func,
+ GValue * elem, gpointer user_data)
+{
+ GstIteratorResult res;
+ FindCustomFoldData data;
+
+ g_return_val_if_fail (G_VALUE_TYPE (elem) == G_TYPE_INVALID
+ || G_VALUE_HOLDS (elem, it->type), GST_ITERATOR_ERROR);
+
+ if (G_VALUE_TYPE (elem) == G_TYPE_INVALID)
+ g_value_init (elem, it->type);
+
+ data.func = func;
+ data.user_data = user_data;
+ data.found = FALSE;
+
+ do {
+ res =
+ gst_iterator_fold (it, (GstIteratorFoldFunction) find_custom_fold_func,
+ elem, &data);
+ if (res == GST_ITERATOR_RESYNC)
+ gst_iterator_resync (it);
+ } while (res == GST_ITERATOR_RESYNC);
+
+ if (!data.found)
+ g_value_unset (elem);
+
+ return data.found;
+}
+
+typedef struct
+{
+ GstIterator parent;
+ GValue object;
+ gboolean visited;
+ gboolean empty;
+} GstSingleObjectIterator;
+
+static guint32 _single_object_dummy_cookie = 0;
+
+static void
+gst_single_object_iterator_copy (const GstSingleObjectIterator * it,
+ GstSingleObjectIterator * copy)
+{
+ if (!it->empty) {
+ memset (&copy->object, 0, sizeof (copy->object));
+ g_value_init (&copy->object, it->parent.type);
+ g_value_copy (&it->object, &copy->object);
+ }
+}
+
+static GstIteratorResult
+gst_single_object_iterator_next (GstSingleObjectIterator * it, GValue * result)
+{
+ if (it->visited || it->empty)
+ return GST_ITERATOR_DONE;
+
+ g_value_copy (&it->object, result);
+ it->visited = TRUE;
+
+ return GST_ITERATOR_OK;
+}
+
+static void
+gst_single_object_iterator_resync (GstSingleObjectIterator * it)
+{
+ it->visited = FALSE;
+}
+
+static void
+gst_single_object_iterator_free (GstSingleObjectIterator * it)
+{
+ if (!it->empty)
+ g_value_unset (&it->object);
+}
+
+/**
+ * gst_iterator_new_single:
+ * @type: #GType of the passed object
+ * @object: object that this iterator should return
+ *
+ * This #GstIterator is a convenient iterator for the common
+ * case where a #GstIterator needs to be returned but only
+ * a single object has to be considered. This happens often
+ * for the #GstPadIterIntLinkFunction.
+ *
+ * Returns: the new #GstIterator for @object.
+ *
+ * Since: 0.10.25
+ */
+GstIterator *
+gst_iterator_new_single (GType type, const GValue * object)
+{
+ GstSingleObjectIterator *result;
+
+ result = (GstSingleObjectIterator *)
+ gst_iterator_new (sizeof (GstSingleObjectIterator),
+ type, NULL, &_single_object_dummy_cookie,
+ (GstIteratorCopyFunction) gst_single_object_iterator_copy,
+ (GstIteratorNextFunction) gst_single_object_iterator_next,
+ (GstIteratorItemFunction) NULL,
+ (GstIteratorResyncFunction) gst_single_object_iterator_resync,
+ (GstIteratorFreeFunction) gst_single_object_iterator_free);
+
+ if (object) {
+ g_value_init (&result->object, type);
+ g_value_copy (object, &result->object);
+ result->empty = FALSE;
+ } else {
+ result->empty = TRUE;
+ }
+ result->visited = FALSE;
+
+ return GST_ITERATOR (result);
+}
diff --git a/gst/gstiterator.h b/gst/gstiterator.h
new file mode 100644
index 0000000..3443402
--- /dev/null
+++ b/gst/gstiterator.h
@@ -0,0 +1,273 @@
+/* GStreamer
+ * Copyright (C) 2004 Wim Taymans <wim@fluendo.com>
+ * Copyright (C) 2011 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+ *
+ * gstiterator.h: Header for GstIterator
+ *
+ * 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_ITERATOR_H__
+#define __GST_ITERATOR_H__
+
+#include <glib-object.h> /* for GValue in the fold */
+#include <gst/gstconfig.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_ITERATOR (gst_iterator_get_type ())
+
+/**
+ * GstIteratorResult:
+ * @GST_ITERATOR_DONE: No more items in the iterator
+ * @GST_ITERATOR_OK: An item was retrieved
+ * @GST_ITERATOR_RESYNC: Datastructure changed while iterating
+ * @GST_ITERATOR_ERROR: An error happened
+ *
+ * The result of gst_iterator_next().
+ */
+typedef enum {
+ GST_ITERATOR_DONE = 0,
+ GST_ITERATOR_OK = 1,
+ GST_ITERATOR_RESYNC = 2,
+ GST_ITERATOR_ERROR = 3
+} GstIteratorResult;
+
+typedef struct _GstIterator GstIterator;
+
+/**
+ * GstIteratorItem:
+ * @GST_ITERATOR_ITEM_SKIP: Skip this item
+ * @GST_ITERATOR_ITEM_PASS: Return item
+ * @GST_ITERATOR_ITEM_END: Stop after this item.
+ *
+ * The result of a #GstIteratorItemFunction.
+ */
+typedef enum {
+ GST_ITERATOR_ITEM_SKIP = 0,
+ GST_ITERATOR_ITEM_PASS = 1,
+ GST_ITERATOR_ITEM_END = 2
+} GstIteratorItem;
+
+/**
+ * GstIteratorCopyFunction:
+ * @it: The original iterator
+ * @copy: The copied iterator
+ *
+ * This function will be called when creating a copy of @it and should
+ * create a copy of all custom iterator fields or increase their
+ * reference counts.
+ */
+typedef void (*GstIteratorCopyFunction) (const GstIterator *it, GstIterator *copy);
+
+/**
+ * GstIteratorItemFunction:
+ * @it: the iterator
+ * @item: the item being retrieved.
+ *
+ * The function that will be called after the next item of the iterator
+ * has been retrieved. This function can be used to skip items or stop
+ * the iterator.
+ *
+ * The function will be called with the iterator lock held.
+ *
+ * Returns: the result of the operation.
+ */
+typedef GstIteratorItem (*GstIteratorItemFunction) (GstIterator *it, const GValue * item);
+
+/**
+ * GstIteratorNextFunction:
+ * @it: the iterator
+ * @result: a pointer to hold the next item
+ *
+ * The function that will be called when the next element of the iterator
+ * should be retrieved.
+ *
+ * Implementors of a #GstIterator should implement this
+ * function and pass it to the constructor of the custom iterator.
+ * The function will be called with the iterator lock held.
+ *
+ * Returns: the result of the operation.
+ */
+typedef GstIteratorResult (*GstIteratorNextFunction) (GstIterator *it, GValue *result);
+/**
+ * GstIteratorResyncFunction:
+ * @it: the iterator
+ *
+ * This function will be called whenever a concurrent update happened
+ * to the iterated datastructure. The implementor of the iterator should
+ * restart the iterator from the beginning and clean up any state it might
+ * have.
+ *
+ * Implementors of a #GstIterator should implement this
+ * function and pass it to the constructor of the custom iterator.
+ * The function will be called with the iterator lock held.
+ */
+typedef void (*GstIteratorResyncFunction) (GstIterator *it);
+/**
+ * GstIteratorFreeFunction:
+ * @it: the iterator
+ *
+ * This function will be called when the iterator is freed.
+ *
+ * Implementors of a #GstIterator should implement this
+ * function and pass it to the constructor of the custom iterator.
+ * The function will be called with the iterator lock held.
+ */
+typedef void (*GstIteratorFreeFunction) (GstIterator *it);
+
+/**
+ * GstIteratorForeachFunction:
+ * @item: The item
+ * @user_data: User data
+ *
+ * A function that is called by gst_iterator_foreach() for every element.
+ */
+typedef void (*GstIteratorForeachFunction) (const GValue * item, gpointer user_data);
+
+/**
+ * GstIteratorFoldFunction:
+ * @item: the item to fold
+ * @ret: a #GValue collecting the result
+ * @user_data: data passed to gst_iterator_fold()
+ *
+ * A function to be passed to gst_iterator_fold().
+ *
+ * Returns: TRUE if the fold should continue, FALSE if it should stop.
+ */
+typedef gboolean (*GstIteratorFoldFunction) (const GValue * item, GValue * ret, gpointer user_data);
+
+/**
+ * GST_ITERATOR:
+ * @it: the #GstIterator value
+ *
+ * Macro to cast to a #GstIterator
+ */
+#define GST_ITERATOR(it) ((GstIterator*)(it))
+/**
+ * GST_ITERATOR_LOCK:
+ * @it: the #GstIterator to get the lock of
+ *
+ * Macro to get the lock protecting the datastructure being iterated.
+ */
+#define GST_ITERATOR_LOCK(it) (GST_ITERATOR(it)->lock)
+/**
+ * GST_ITERATOR_COOKIE:
+ * @it: the #GstIterator to get the cookie of
+ *
+ * Macro to get the cookie of a #GstIterator. The cookie of the
+ * iterator is the value of the master cookie when the iterator
+ * was created.
+ * Whenever the iterator is iterated, the value is compared to the
+ * value of the master cookie. If they are different, a concurrent
+ * modification happened to the iterator and a resync is needed.
+ */
+#define GST_ITERATOR_COOKIE(it) (GST_ITERATOR(it)->cookie)
+/**
+ * GST_ITERATOR_ORIG_COOKIE:
+ * @it: the #GstIterator to get the master cookie of
+ *
+ * Macro to get a pointer to where the master cookie is stored. The
+ * master cookie protects the structure being iterated and gets updated
+ * whenever the datastructure changes.
+ */
+#define GST_ITERATOR_ORIG_COOKIE(it) (GST_ITERATOR(it)->master_cookie)
+
+/**
+ * GstIterator:
+ * @copy: The function to copy the iterator
+ * @next: The function to get the next item in the iterator
+ * @item: The function to be called for each item retrieved
+ * @resync: The function to call when a resync is needed.
+ * @free: The function to call when the iterator is freed
+ * @pushed: The iterator that is currently pushed with gst_iterator_push()
+ * @type: The type of the object that this iterator will return
+ * @lock: The lock protecting the data structure and the cookie.
+ * @cookie: The cookie; the value of the master_cookie when this iterator was
+ * created.
+ * @master_cookie: A pointer to the master cookie.
+ * @size: the size of the iterator
+ *
+ * #GstIterator base structure. The values of this structure are
+ * protected for subclasses, use the methods to use the #GstIterator.
+ */
+struct _GstIterator {
+ /*< protected >*/
+ GstIteratorCopyFunction copy;
+ GstIteratorNextFunction next;
+ GstIteratorItemFunction item;
+ GstIteratorResyncFunction resync;
+ GstIteratorFreeFunction free;
+
+ GstIterator *pushed; /* pushed iterator */
+
+ GType type;
+ GMutex *lock;
+ guint32 cookie; /* cookie of the iterator */
+ guint32 *master_cookie; /* pointer to guint32 holding the cookie when this
+ iterator was created */
+ guint size;
+
+ /*< private >*/
+ gpointer _gst_reserved[GST_PADDING];
+};
+
+GType gst_iterator_get_type (void);
+
+/* creating iterators */
+GstIterator* gst_iterator_new (guint size,
+ GType type,
+ GMutex *lock,
+ guint32 *master_cookie,
+ GstIteratorCopyFunction copy,
+ GstIteratorNextFunction next,
+ GstIteratorItemFunction item,
+ GstIteratorResyncFunction resync,
+ GstIteratorFreeFunction free);
+
+GstIterator* gst_iterator_new_list (GType type,
+ GMutex *lock,
+ guint32 *master_cookie,
+ GList **list,
+ GObject * owner,
+ GstIteratorItemFunction item);
+
+GstIterator* gst_iterator_new_single (GType type,
+ const GValue * object);
+
+GstIterator* gst_iterator_copy (const GstIterator *it);
+
+/* using iterators */
+GstIteratorResult gst_iterator_next (GstIterator *it, GValue * elem);
+void gst_iterator_resync (GstIterator *it);
+void gst_iterator_free (GstIterator *it);
+
+void gst_iterator_push (GstIterator *it, GstIterator *other);
+
+/* higher-order functions that operate on iterators */
+GstIterator* gst_iterator_filter (GstIterator *it, GCompareFunc func,
+ const GValue * user_data);
+GstIteratorResult gst_iterator_fold (GstIterator *it,
+ GstIteratorFoldFunction func,
+ GValue *ret, gpointer user_data);
+GstIteratorResult gst_iterator_foreach (GstIterator *it,
+ GstIteratorForeachFunction func, gpointer user_data);
+gboolean gst_iterator_find_custom (GstIterator *it, GCompareFunc func,
+ GValue *elem, gpointer user_data);
+
+G_END_DECLS
+
+#endif /* __GST_ITERATOR_H__ */
diff --git a/gst/gstmacros.h b/gst/gstmacros.h
new file mode 100644
index 0000000..e38af53
--- /dev/null
+++ b/gst/gstmacros.h
@@ -0,0 +1,54 @@
+/* GStreamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ *
+ * 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_MACROS_H__
+#define __GST_MACROS_H__
+
+G_BEGIN_DECLS
+
+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4)
+# define GST_GNUC_CONSTRUCTOR \
+ __attribute__ ((constructor))
+#else /* !__GNUC__ */
+# define GST_GNUC_CONSTRUCTOR
+#endif /* !__GNUC__ */
+
+#if defined (__GNUC__) && !defined (GST_IMPLEMENT_INLINES)
+# define GST_INLINE_FUNC extern __inline__
+# define GST_CAN_INLINE 1
+#elif defined(_MSC_VER)
+# define GST_INLINE_FUNC extern __inline
+# define GST_CAN_INLINE 1
+#else
+# define GST_INLINE_FUNC extern
+# undef GST_CAN_INLINE
+#endif
+
+#if (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L) && !defined(restrict)
+# if defined(__GNUC__) && __GNUC__ >= 4
+# define restrict __restrict__
+# else
+# define restrict
+# endif
+#endif
+
+G_END_DECLS
+
+#endif /* __GST_MACROS_H__ */
+
+
diff --git a/gst/gstmarshal.list b/gst/gstmarshal.list
new file mode 100644
index 0000000..b9fc164
--- /dev/null
+++ b/gst/gstmarshal.list
@@ -0,0 +1,25 @@
+VOID:VOID
+VOID:BOOLEAN
+VOID:INT
+VOID:STRING
+VOID:BOXED
+VOID:BOXED,OBJECT
+VOID:POINTER
+VOID:POINTER,OBJECT
+VOID:OBJECT
+VOID:OBJECT,OBJECT
+VOID:OBJECT,PARAM
+VOID:OBJECT,POINTER
+VOID:OBJECT,BOXED
+VOID:OBJECT,BOXED,STRING
+VOID:OBJECT,OBJECT,STRING
+VOID:OBJECT,STRING
+VOID:INT,INT
+VOID:INT64
+VOID:UINT,BOXED
+VOID:UINT,POINTER
+BOOLEAN:VOID
+BOOLEAN:POINTER
+BOOLEAN:BOXED
+POINTER:POINTER
+BOXED:BOXED
diff --git a/gst/gstmemory.c b/gst/gstmemory.c
new file mode 100644
index 0000000..ebaefc3
--- /dev/null
+++ b/gst/gstmemory.c
@@ -0,0 +1,677 @@
+/* GStreamer
+ * Copyright (C) 2011 Wim Taymans <wim.taymans@gmail.be>
+ *
+ * gstmemory.c: memory block handling
+ *
+ * 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:gstmemory
+ * @short_description: refcounted wrapper for memory blocks
+ * @see_also: #GstBuffer
+ *
+ * GstMemory is a lightweight refcounted object that wraps a region of memory.
+ * They are typically used to manage the data of a #GstBuffer.
+ *
+ * Memory is usually created by allocators with a gst_allocator_alloc()
+ * method call. When NULL is used as the allocator, the default allocator will
+ * be used.
+ *
+ * New allocators can be registered with gst_allocator_register().
+ * Allocators are identified by name and can be retrieved with
+ * gst_allocator_find().
+ *
+ * New memory can be created with gst_memory_new_wrapped() that wraps the memory
+ * allocated elsewhere.
+ *
+ * Refcounting of the memory block is performed with gst_memory_ref() and
+ * gst_memory_unref().
+ *
+ * The size of the memory can be retrieved and changed with
+ * gst_memory_get_sizes() and gst_memory_resize() respectively.
+ *
+ * Getting access to the data of the memory is performed with gst_memory_map().
+ * After the memory access is completed, gst_memory_unmap() should be called.
+ *
+ * Memory can be copied with gst_memory_copy(), which will returnn a writable
+ * copy. gst_memory_share() will create a new memory block that shares the
+ * memory with an existing memory block at a custom offset and with a custom
+ * size.
+ *
+ * Memory can be efficiently merged when gst_memory_is_span() returns TRUE.
+ *
+ * Last reviewed on 2011-06-08 (0.11.0)
+ */
+
+#include "config.h"
+#include "gst_private.h"
+#include "gstmemory.h"
+
+
+/* buffer alignment in bytes - 1
+ * an alignment of 7 would be the same as malloc() guarantees
+ */
+#if defined(MEMORY_ALIGNMENT_MALLOC)
+size_t gst_memory_alignment = 7;
+#elif defined(MEMORY_ALIGNMENT_PAGESIZE)
+/* we fill this in in the _init method */
+size_t gst_memory_alignment = 0;
+#elif defined(MEMORY_ALIGNMENT)
+size_t gst_memory_alignment = MEMORY_ALIGNMENT - 1;
+#else
+#error "No memory alignment configured"
+size_t gst_memory_alignment = 0;
+#endif
+
+struct _GstAllocator
+{
+ GQuark name;
+
+ GstMemoryInfo info;
+};
+
+/* default memory implementation */
+typedef struct
+{
+ GstMemory mem;
+ gsize slice_size;
+ guint8 *data;
+ GFreeFunc free_func;
+ gsize maxsize;
+ gsize offset;
+ gsize size;
+} GstMemoryDefault;
+
+/* the default allocator */
+static const GstAllocator *_default_allocator;
+
+/* our predefined allocators */
+static const GstAllocator *_default_mem_impl;
+
+/* initialize the fields */
+static void
+_default_mem_init (GstMemoryDefault * mem, GstMemoryFlags flags,
+ GstMemory * parent, gsize slice_size, gpointer data,
+ GFreeFunc free_func, gsize maxsize, gsize offset, gsize size)
+{
+ mem->mem.allocator = _default_mem_impl;
+ mem->mem.flags = flags;
+ mem->mem.refcount = 1;
+ mem->mem.parent = parent ? gst_memory_ref (parent) : NULL;
+ mem->slice_size = slice_size;
+ mem->data = data;
+ mem->free_func = free_func;
+ mem->maxsize = maxsize;
+ mem->offset = offset;
+ mem->size = size;
+}
+
+/* create a new memory block that manages the given memory */
+static GstMemoryDefault *
+_default_mem_new (GstMemoryFlags flags, GstMemory * parent, gpointer data,
+ GFreeFunc free_func, gsize maxsize, gsize offset, gsize size)
+{
+ GstMemoryDefault *mem;
+ gsize slice_size;
+
+ slice_size = sizeof (GstMemoryDefault);
+
+ mem = g_slice_alloc (slice_size);
+ _default_mem_init (mem, flags, parent, slice_size,
+ data, free_func, maxsize, offset, size);
+
+ return mem;
+}
+
+/* allocate the memory and structure in one block */
+static GstMemoryDefault *
+_default_mem_new_block (gsize maxsize, gsize align, gsize offset, gsize size)
+{
+ GstMemoryDefault *mem;
+ gsize aoffset, slice_size;
+ guint8 *data;
+
+ /* ensure configured alignment */
+ align |= gst_memory_alignment;
+ /* allocate more to compensate for alignment */
+ maxsize += align;
+ /* alloc header and data in one block */
+ slice_size = sizeof (GstMemoryDefault) + maxsize;
+
+ mem = g_slice_alloc (slice_size);
+ if (mem == NULL)
+ return NULL;
+
+ data = (guint8 *) mem + sizeof (GstMemoryDefault);
+
+ if ((aoffset = ((guintptr) data & align)))
+ aoffset = (align + 1) - aoffset;
+
+ _default_mem_init (mem, 0, NULL, slice_size, data, NULL, maxsize,
+ aoffset + offset, size);
+
+ return mem;
+}
+
+static GstMemory *
+_default_mem_alloc (const GstAllocator * allocator, gsize maxsize, gsize align)
+{
+ return (GstMemory *) _default_mem_new_block (maxsize, align, 0, maxsize);
+}
+
+static gsize
+_default_mem_get_sizes (GstMemoryDefault * mem, gsize * offset, gsize * maxsize)
+{
+ if (offset)
+ *offset = mem->offset;
+ if (maxsize)
+ *maxsize = mem->maxsize;
+
+ return mem->size;
+}
+
+static void
+_default_mem_resize (GstMemoryDefault * mem, gssize offset, gsize size)
+{
+ g_return_if_fail (size + mem->offset + offset <= mem->maxsize);
+
+ mem->offset += offset;
+ mem->size = size;
+}
+
+static gpointer
+_default_mem_map (GstMemoryDefault * mem, gsize * size, gsize * maxsize,
+ GstMapFlags flags)
+{
+ if (size)
+ *size = mem->size;
+ if (maxsize)
+ *maxsize = mem->maxsize;
+
+ return mem->data + mem->offset;
+}
+
+static gboolean
+_default_mem_unmap (GstMemoryDefault * mem, gpointer data, gsize size)
+{
+ if (size != -1)
+ mem->size = size;
+ return TRUE;
+}
+
+static void
+_default_mem_free (GstMemoryDefault * mem)
+{
+ if (mem->mem.parent)
+ gst_memory_unref (mem->mem.parent);
+
+ if (mem->free_func)
+ mem->free_func (mem->data);
+
+ g_slice_free1 (mem->slice_size, mem);
+}
+
+static GstMemoryDefault *
+_default_mem_copy (GstMemoryDefault * mem, gssize offset, gsize size)
+{
+ GstMemoryDefault *copy;
+
+ if (size == -1)
+ size = mem->size > offset ? mem->size - offset : 0;
+
+ copy = _default_mem_new_block (mem->maxsize, 0, mem->offset + offset, size);
+ memcpy (copy->data, mem->data, mem->maxsize);
+
+ return copy;
+}
+
+static GstMemoryDefault *
+_default_mem_share (GstMemoryDefault * mem, gssize offset, gsize size)
+{
+ GstMemoryDefault *sub;
+ GstMemory *parent;
+
+ /* find the real parent */
+ if ((parent = mem->mem.parent) == NULL)
+ parent = (GstMemory *) mem;
+
+ if (size == -1)
+ size = mem->size - offset;
+
+ sub = _default_mem_new (parent->flags, parent, mem->data, NULL, mem->maxsize,
+ mem->offset + offset, size);
+
+ return sub;
+}
+
+static gboolean
+_default_mem_is_span (GstMemoryDefault * mem1, GstMemoryDefault * mem2,
+ gsize * offset)
+{
+
+ if (offset) {
+ GstMemoryDefault *parent;
+
+ parent = (GstMemoryDefault *) mem1->mem.parent;
+
+ *offset = mem1->offset - parent->offset;
+ }
+
+ /* and memory is contiguous */
+ return mem1->data + mem1->offset + mem1->size == mem2->data + mem2->offset;
+}
+
+static GstMemory *
+_fallback_copy (GstMemory * mem, gssize offset, gsize size)
+{
+ GstMemory *copy;
+ guint8 *data, *dest;
+ gsize msize;
+
+ data = gst_memory_map (mem, &msize, NULL, GST_MAP_READ);
+ if (size == -1)
+ size = msize > offset ? msize - offset : 0;
+ /* use the same allocator as the memory we copy, FIXME, alignment? */
+ copy = gst_allocator_alloc (mem->allocator, size, 0);
+ dest = gst_memory_map (copy, NULL, NULL, GST_MAP_WRITE);
+ memcpy (dest, data + offset, size);
+ gst_memory_unmap (copy, dest, size);
+
+ gst_memory_unmap (mem, data, msize);
+
+ return (GstMemory *) copy;
+}
+
+static gboolean
+_fallback_is_span (GstMemory * mem1, GstMemory * mem2, gsize * offset)
+{
+ return FALSE;
+}
+
+static GStaticRWLock lock = G_STATIC_RW_LOCK_INIT;
+static GHashTable *allocators;
+
+void
+_priv_gst_memory_initialize (void)
+{
+ static const GstMemoryInfo _mem_info = {
+ (GstMemoryAllocFunction) _default_mem_alloc,
+ (GstMemoryGetSizesFunction) _default_mem_get_sizes,
+ (GstMemoryResizeFunction) _default_mem_resize,
+ (GstMemoryMapFunction) _default_mem_map,
+ (GstMemoryUnmapFunction) _default_mem_unmap,
+ (GstMemoryFreeFunction) _default_mem_free,
+ (GstMemoryCopyFunction) _default_mem_copy,
+ (GstMemoryShareFunction) _default_mem_share,
+ (GstMemoryIsSpanFunction) _default_mem_is_span,
+ NULL
+ };
+
+ allocators = g_hash_table_new (g_str_hash, g_str_equal);
+
+#ifdef HAVE_GETPAGESIZE
+#ifdef MEMORY_ALIGNMENT_PAGESIZE
+ gst_memory_alignment = getpagesize () - 1;
+#endif
+#endif
+
+ GST_DEBUG ("memory alignment: %" G_GSIZE_FORMAT, gst_memory_alignment);
+
+ _default_mem_impl = gst_allocator_register (GST_ALLOCATOR_SYSMEM, &_mem_info);
+
+ _default_allocator = _default_mem_impl;
+}
+
+/**
+ * gst_memory_new_wrapped:
+ * @flags: #GstMemoryFlags
+ * @data: data to wrap
+ * @free_func: function to free @data
+ * @maxsize: allocated size of @data
+ * @offset: offset in @data
+ * @size: size of valid data
+ *
+ * Allocate a new memory block that wraps the given @data.
+ *
+ * Returns: a new #GstMemory.
+ */
+GstMemory *
+gst_memory_new_wrapped (GstMemoryFlags flags, gpointer data,
+ GFreeFunc free_func, gsize maxsize, gsize offset, gsize size)
+{
+ GstMemoryDefault *mem;
+
+ g_return_val_if_fail (data != NULL, NULL);
+ g_return_val_if_fail (offset + size <= maxsize, NULL);
+
+ mem = _default_mem_new (flags, NULL, data, free_func, maxsize, offset, size);
+
+ return (GstMemory *) mem;
+}
+
+/**
+ * gst_memory_ref:
+ * @mem: a #GstMemory
+ *
+ * Increases the refcount of @mem.
+ *
+ * Returns: @mem with increased refcount
+ */
+GstMemory *
+gst_memory_ref (GstMemory * mem)
+{
+ g_return_val_if_fail (mem != NULL, NULL);
+
+ g_atomic_int_inc (&mem->refcount);
+
+ return mem;
+}
+
+/**
+ * gst_memory_unref:
+ * @mem: a #GstMemory
+ *
+ * Decreases the refcount of @mem. When the refcount reaches 0, the free
+ * function of @mem will be called.
+ */
+void
+gst_memory_unref (GstMemory * mem)
+{
+ g_return_if_fail (mem != NULL);
+ g_return_if_fail (mem->allocator != NULL);
+
+ if (g_atomic_int_dec_and_test (&mem->refcount))
+ mem->allocator->info.free (mem);
+}
+
+/**
+ * gst_memory_get_sizes:
+ * @mem: a #GstMemory
+ * @offset: pointer to offset
+ * @maxsize: pointer to maxsize
+ *
+ * Get the current @size, @offset and @maxsize of @mem.
+ *
+ * Returns: the current sizes of @mem
+ */
+gsize
+gst_memory_get_sizes (GstMemory * mem, gsize * offset, gsize * maxsize)
+{
+ g_return_val_if_fail (mem != NULL, 0);
+
+ return mem->allocator->info.get_sizes (mem, offset, maxsize);
+}
+
+/**
+ * gst_memory_resize:
+ * @mem: a #GstMemory
+ * @offset: a new offset
+ * @size: a new size
+ *
+ * Resize the memory region. @mem should be writable and offset + size should be
+ * less than the maxsize of @mem.
+ */
+void
+gst_memory_resize (GstMemory * mem, gssize offset, gsize size)
+{
+ g_return_if_fail (mem != NULL);
+ g_return_if_fail (GST_MEMORY_IS_WRITABLE (mem));
+
+ mem->allocator->info.resize (mem, offset, size);
+}
+
+/**
+ * gst_memory_map:
+ * @mem: a #GstMemory
+ * @size: pointer for size
+ * @maxsize: pointer for maxsize
+ * @flags: mapping flags
+ *
+ * Get a pointer to the memory of @mem that can be accessed according to @flags.
+ *
+ * @size and @maxsize will contain the size of the memory and the maximum
+ * allocated memory of @mem respectively. They can be set to NULL.
+ *
+ * Returns: a pointer to the memory of @mem.
+ */
+gpointer
+gst_memory_map (GstMemory * mem, gsize * size, gsize * maxsize,
+ GstMapFlags flags)
+{
+ g_return_val_if_fail (mem != NULL, NULL);
+ g_return_val_if_fail (!(flags & GST_MAP_WRITE) ||
+ GST_MEMORY_IS_WRITABLE (mem), NULL);
+
+ return mem->allocator->info.map (mem, size, maxsize, flags);
+}
+
+/**
+ * gst_memory_unmap:
+ * @mem: a #GstMemory
+ * @data: data to unmap
+ * @size: new size of @mem
+ *
+ * Release the memory pointer obtained with gst_memory_map() and set the size of
+ * the memory to @size. @size can be set to -1 when the size should not be
+ * updated.
+ *
+ * Returns: TRUE when the memory was release successfully.
+ */
+gboolean
+gst_memory_unmap (GstMemory * mem, gpointer data, gsize size)
+{
+ g_return_val_if_fail (mem != NULL, FALSE);
+
+ return mem->allocator->info.unmap (mem, data, size);
+}
+
+/**
+ * gst_memory_copy:
+ * @mem: a #GstMemory
+ * @offset: an offset to copy
+ * @size: size to copy
+ *
+ * Return a copy of @size bytes from @mem starting from @offset. This copy is
+ * guaranteed to be writable. @size can be set to -1 to return a copy all bytes
+ * from @offset.
+ *
+ * Returns: a new #GstMemory.
+ */
+GstMemory *
+gst_memory_copy (GstMemory * mem, gssize offset, gsize size)
+{
+ g_return_val_if_fail (mem != NULL, NULL);
+
+ return mem->allocator->info.copy (mem, offset, size);
+}
+
+/**
+ * gst_memory_share:
+ * @mem: a #GstMemory
+ * @offset: an offset to share
+ * @size: size to share
+ *
+ * Return a shared copy of @size bytes from @mem starting from @offset. No memory
+ * copy is performed and the memory region is simply shared. The result is
+ * guaranteed to be not-writable. @size can be set to -1 to return a share all bytes
+ * from @offset.
+ *
+ * Returns: a new #GstMemory.
+ */
+GstMemory *
+gst_memory_share (GstMemory * mem, gssize offset, gsize size)
+{
+ g_return_val_if_fail (mem != NULL, NULL);
+
+ return mem->allocator->info.share (mem, offset, size);
+}
+
+/**
+ * gst_memory_is_span:
+ * @mem1: a #GstMemory
+ * @mem2: a #GstMemory
+ * @offset: a pointer to a result offset
+ *
+ * Check if @mem1 and mem2 share the memory with a common parent memory object
+ * and that the memory is contiguous.
+ *
+ * If this is the case, the memory of @mem1 and @mem2 can be merged
+ * efficiently by performing gst_memory_share() on the parent object from
+ * the returned @offset.
+ *
+ * Returns: %TRUE if the memory is contiguous and of a common parent.
+ */
+gboolean
+gst_memory_is_span (GstMemory * mem1, GstMemory * mem2, gsize * offset)
+{
+ g_return_val_if_fail (mem1 != NULL, FALSE);
+ g_return_val_if_fail (mem2 != NULL, FALSE);
+
+ /* need to have the same allocators */
+ if (mem1->allocator != mem2->allocator)
+ return FALSE;
+
+ /* need to have the same parent */
+ if (mem1->parent == NULL || mem1->parent != mem2->parent)
+ return FALSE;
+
+ /* and memory is contiguous */
+ if (!mem1->allocator->info.is_span (mem1, mem2, offset))
+ return FALSE;
+
+ return TRUE;
+}
+
+/**
+ * gst_allocator_register:
+ * @name: the name of the allocator
+ * @info: #GstMemoryInfo
+ *
+ * Registers the memory allocator with @name and implementation functions
+ * @info.
+ *
+ * All functions in @info are mandatory exept the copy and is_span
+ * functions, which will have a default implementation when left NULL.
+ *
+ * The user_data field in @info will be passed to all calls of the alloc
+ * function.
+ *
+ * Returns: a new #GstAllocator.
+ */
+const GstAllocator *
+gst_allocator_register (const gchar * name, const GstMemoryInfo * info)
+{
+ GstAllocator *allocator;
+
+#define INSTALL_FALLBACK(_t) \
+ if (allocator->info._t == NULL) allocator->info._t = _fallback_ ##_t;
+
+ g_return_val_if_fail (name != NULL, NULL);
+ g_return_val_if_fail (info != NULL, NULL);
+ g_return_val_if_fail (info->alloc != NULL, NULL);
+ g_return_val_if_fail (info->get_sizes != NULL, NULL);
+ g_return_val_if_fail (info->resize != NULL, NULL);
+ g_return_val_if_fail (info->map != NULL, NULL);
+ g_return_val_if_fail (info->unmap != NULL, NULL);
+ g_return_val_if_fail (info->free != NULL, NULL);
+ g_return_val_if_fail (info->share != NULL, NULL);
+
+ allocator = g_slice_new (GstAllocator);
+ allocator->name = g_quark_from_string (name);
+ allocator->info = *info;
+ INSTALL_FALLBACK (copy);
+ INSTALL_FALLBACK (is_span);
+#undef INSTALL_FALLBACK
+
+ GST_DEBUG ("registering allocator \"%s\"", name);
+
+ g_static_rw_lock_writer_lock (&lock);
+ g_hash_table_insert (allocators, (gpointer) name, (gpointer) allocator);
+ g_static_rw_lock_writer_unlock (&lock);
+
+ return allocator;
+}
+
+/**
+ * gst_allocator_find:
+ * @name: the name of the allocator
+ *
+ * Find a previously registered allocator with @name. When @name is NULL, the
+ * default allocator will be returned.
+ *
+ * Returns: a #GstAllocator or NULL when the allocator with @name was not
+ * registered.
+ */
+const GstAllocator *
+gst_allocator_find (const gchar * name)
+{
+ const GstAllocator *allocator;
+
+ g_static_rw_lock_reader_lock (&lock);
+ if (name) {
+ allocator = g_hash_table_lookup (allocators, (gconstpointer) name);
+ } else {
+ allocator = _default_allocator;
+ }
+ g_static_rw_lock_reader_unlock (&lock);
+
+ return allocator;
+}
+
+/**
+ * gst_allocator_set_default:
+ * @allocator: a #GstAllocator
+ *
+ * Set the default allocator.
+ */
+void
+gst_allocator_set_default (const GstAllocator * allocator)
+{
+ g_return_if_fail (allocator != NULL);
+
+ g_static_rw_lock_writer_lock (&lock);
+ _default_allocator = allocator;
+ g_static_rw_lock_writer_unlock (&lock);
+}
+
+/**
+ * gst_allocator_alloc:
+ * @allocator: a #GstAllocator to use
+ * @maxsize: allocated size of @data
+ * @align: alignment for the data
+ *
+ * Use @allocator to allocate a new memory block with memory that is at least
+ * @maxsize big and has the given alignment.
+ *
+ * When @allocator is NULL, the default allocator will be used.
+ *
+ * @align is given as a bitmask so that @align + 1 equals the amount of bytes to
+ * align to. For example, to align to 8 bytes, use an alignment of 7.
+ *
+ * Returns: a new #GstMemory.
+ */
+GstMemory *
+gst_allocator_alloc (const GstAllocator * allocator, gsize maxsize, gsize align)
+{
+ g_return_val_if_fail (((align + 1) & align) == 0, NULL);
+
+ if (allocator == NULL)
+ allocator = _default_allocator;
+
+ return allocator->info.alloc (allocator, maxsize, align,
+ allocator->info.user_data);
+}
diff --git a/gst/gstmemory.h b/gst/gstmemory.h
new file mode 100644
index 0000000..c1f8e67
--- /dev/null
+++ b/gst/gstmemory.h
@@ -0,0 +1,302 @@
+/* GStreamer
+ * Copyright (C) 2009 Wim Taymans <wim.taymans@gmail.be>
+ *
+ * gstmemory.h: Header for memory blocks
+ *
+ * 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_MEMORY_H__
+#define __GST_MEMORY_H__
+
+#include <gst/gstconfig.h>
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GstMemory GstMemory;
+typedef struct _GstMemoryInfo GstMemoryInfo;
+typedef struct _GstAllocator GstAllocator;
+
+extern gsize gst_memory_alignment;
+
+/**
+ * GstMemoryFlags:
+ * @GST_MEMORY_FLAG_READONLY: memory is readonly. It is not allowed to map the
+ * memory with #GST_MAP_WRITE.
+ * @GST_MEMORY_FLAG_NO_SHARE: memory must not be shared. Copies will have to be
+ * made when this memory needs to be shared between buffers.
+ * @GST_MEMORY_FLAG_LAST: first flag that can be used for custom purposes
+ *
+ * Flags for wrapped memory.
+ */
+typedef enum {
+ GST_MEMORY_FLAG_READONLY = (1 << 0),
+ GST_MEMORY_FLAG_NO_SHARE = (1 << 1),
+
+ GST_MEMORY_FLAG_LAST = (1 << 24)
+} GstMemoryFlags;
+
+/**
+ * GST_MEMORY_IS_WRITABLE:
+ * @mem: a #GstMemory
+ *
+ * Check if @mem is writable.
+ */
+#define GST_MEMORY_IS_WRITABLE(mem) (((mem)->refcount == 1) && \
+ (((mem)->parent == NULL) || ((mem)->parent->refcount == 1)) && \
+ (((mem)->flags & GST_MEMORY_FLAG_READONLY) == 0))
+
+/**
+ * GstMemory:
+ * @allocator: pointer to the #GstAllocator
+ * @flags: memory flags
+ * @refcount: refcount
+ * @parent: parent memory block
+ *
+ * Base structure for memory implementations. Custom memory will put this structure
+ * as the first member of their structure.
+ */
+struct _GstMemory {
+ const GstAllocator *allocator;
+
+ GstMemoryFlags flags;
+ gint refcount;
+ GstMemory *parent;
+};
+
+/**
+ * GstMapFlags:
+ * @GST_MAP_READ: map for read access
+ * @GST_MAP_WRITE: map for write access
+ *
+ * Flags used when mapping memory
+ */
+typedef enum {
+ GST_MAP_READ = (1 << 0),
+ GST_MAP_WRITE = (1 << 1),
+} GstMapFlags;
+
+/**
+ * GST_MAP_READWRITE:
+ *
+ * Map for readwrite access
+ */
+#define GST_MAP_READWRITE (GST_MAP_READ | GST_MAP_WRITE)
+
+/**
+ * GST_ALLOCATOR_SYSMEM:
+ *
+ * The allocator name for the default system memory allocator
+ */
+#define GST_ALLOCATOR_SYSMEM "SystemMemory"
+
+/**
+ * GstMemoryAllocFunction:
+ * @allocator: a #GstAllocator
+ * @maxsize: the maxsize
+ * @align: the alignment
+ * @user_data: user data
+ *
+ * Allocate a new #GstMemory from @allocator that can hold at least @maxsize bytes
+ * and is aligned to (@align + 1) bytes.
+ *
+ * @user_data is the data that was used when registering @allocator.
+ *
+ * Returns: a newly allocated #GstMemory. Free with gst_memory_unref()
+ */
+typedef GstMemory * (*GstMemoryAllocFunction) (const GstAllocator *allocator,
+ gsize maxsize, gsize align,
+ gpointer user_data);
+
+/**
+ * GstMemoryGetSizesFunction:
+ * @mem: a #GstMemory
+ * @offset: result pointer for offset
+ * @maxsize: result pointer for maxsize
+ *
+ * Retrieve the size, offset and maxsize of @mem.
+ *
+ * Returns: the size of @mem, the offset and the maximum allocated size in @maxsize.
+ */
+typedef gsize (*GstMemoryGetSizesFunction) (GstMemory *mem, gsize *offset, gsize *maxsize);
+
+/**
+ * GstMemoryResizeFunction:
+ * @mem: a #GstMemory
+ * @offset: the offset adjustement
+ * @size: the new size
+ *
+ * Adjust the size and offset of @mem. @offset bytes will be adjusted from the
+ * current first byte in @mem as retrieved with gst_memory_map() and the new
+ * size will be set to @size.
+ *
+ * @size can be set to -1, which will only adjust the offset.
+ */
+typedef void (*GstMemoryResizeFunction) (GstMemory *mem, gssize offset, gsize size);
+
+/**
+ * GstMemoryMapFunction:
+ * @mem: a #GstMemory
+ * @size: pointer for the size
+ * @maxsize: pointer for the maxsize
+ * @flags: access mode for the memory
+ *
+ * Get the memory of @mem that can be accessed according to the mode specified
+ * in @flags. @size and @maxsize will respectively contain the current amount of
+ * valid bytes in the returned memory and the maximum allocated memory.
+ * @size and @maxsize can optionally be set to NULL.
+ *
+ * Returns: a pointer to memory. @size bytes are currently used from the
+ * returned pointer and @maxsize bytes can potentially be used.
+ */
+typedef gpointer (*GstMemoryMapFunction) (GstMemory *mem, gsize *size, gsize *maxsize,
+ GstMapFlags flags);
+
+/**
+ * GstMemoryUnmapFunction:
+ * @mem: a #GstMemory
+ * @data: the data pointer
+ * @size: the new size
+ *
+ * Return the pointer previously retrieved with gst_memory_map() and adjust the
+ * size of the memory with @size. @size can optionally be set to -1 to not
+ * modify the size.
+ *
+ * Returns: %TRUE on success.
+ */
+typedef gboolean (*GstMemoryUnmapFunction) (GstMemory *mem, gpointer data, gsize size);
+
+/**
+ * GstMemoryFreeFunction:
+ * @mem: a #GstMemory
+ *
+ * Free the memory used by @mem. This function is usually called when the
+ * refcount of the @mem has reached 0.
+ */
+typedef void (*GstMemoryFreeFunction) (GstMemory *mem);
+
+/**
+ * GstMemoryCopyFunction:
+ * @mem: a #GstMemory
+ * @offset: an offset
+ * @size: a size
+ *
+ * Copy @size bytes from @mem starting at @offset and return them wrapped in a
+ * new GstMemory object.
+ * If @size is set to -1, all bytes starting at @offset are copied.
+ *
+ * Returns: a new #GstMemory object wrapping a copy of the requested region in
+ * @mem.
+ */
+typedef GstMemory * (*GstMemoryCopyFunction) (GstMemory *mem, gssize offset, gsize size);
+
+/**
+ * GstMemoryShareFunction:
+ * @mem: a #GstMemory
+ * @offset: an offset
+ * @size: a size
+ *
+ * Share @size bytes from @mem starting at @offset and return them wrapped in a
+ * new GstMemory object. If @size is set to -1, all bytes starting at @offset are
+ * shared. This function does not make a copy of the bytes in @mem.
+ *
+ * Returns: a new #GstMemory object sharing the requested region in @mem.
+ */
+typedef GstMemory * (*GstMemoryShareFunction) (GstMemory *mem, gssize offset, gsize size);
+
+/**
+ * GstMemoryIsSpanFunction:
+ * @mem1: a #GstMemory
+ * @mem2: a #GstMemory
+ * @offset: a result offset
+ *
+ * Check if @mem1 and @mem2 occupy contiguous memory and return the offset of
+ * @mem1 in the parent buffer in @offset.
+ *
+ * Returns: %TRUE if @mem1 and @mem2 are in contiguous memory.
+ */
+typedef gboolean (*GstMemoryIsSpanFunction) (GstMemory *mem1, GstMemory *mem2, gsize *offset);
+
+/**
+ * GstMemoryInfo:
+ * @alloc: the implementation of the GstMemoryAllocFunction
+ * @get_sizes: the implementation of the GstMemoryGetSizesFunction
+ * @resize: the implementation of the GstMemoryResizeFunction
+ * @map: the implementation of the GstMemoryMapFunction
+ * @unmap: the implementation of the GstMemoryUnmapFunction
+ * @free: the implementation of the GstMemoryFreeFunction
+ * @copy: the implementation of the GstMemoryCopyFunction
+ * @share: the implementation of the GstMemoryShareFunction
+ * @is_span: the implementation of the GstMemoryIsSpanFunction
+ * @user_data: generic user data for the allocator
+ *
+ * The #GstMemoryInfo is used to register new memory allocators and contain
+ * the implementations for various memory operations.
+ */
+struct _GstMemoryInfo {
+ GstMemoryAllocFunction alloc;
+ GstMemoryGetSizesFunction get_sizes;
+ GstMemoryResizeFunction resize;
+ GstMemoryMapFunction map;
+ GstMemoryUnmapFunction unmap;
+ GstMemoryFreeFunction free;
+
+ GstMemoryCopyFunction copy;
+ GstMemoryShareFunction share;
+ GstMemoryIsSpanFunction is_span;
+
+ gpointer user_data;
+};
+
+/* allocators */
+const GstAllocator * gst_allocator_register (const gchar *name, const GstMemoryInfo *info);
+const GstAllocator * gst_allocator_find (const gchar *name);
+
+void gst_allocator_set_default (const GstAllocator * allocator);
+
+/* allocating memory blocks */
+GstMemory * gst_allocator_alloc (const GstAllocator * allocator,
+ gsize maxsize, gsize align);
+
+GstMemory * gst_memory_new_wrapped (GstMemoryFlags flags, gpointer data, GFreeFunc free_func,
+ gsize maxsize, gsize offset, gsize size);
+
+/* refcounting */
+GstMemory * gst_memory_ref (GstMemory *mem);
+void gst_memory_unref (GstMemory *mem);
+
+/* getting/setting memory properties */
+gsize gst_memory_get_sizes (GstMemory *mem, gsize *offset, gsize *maxsize);
+void gst_memory_resize (GstMemory *mem, gssize offset, gsize size);
+
+/* retrieving data */
+gpointer gst_memory_map (GstMemory *mem, gsize *size, gsize *maxsize,
+ GstMapFlags flags);
+gboolean gst_memory_unmap (GstMemory *mem, gpointer data, gsize size);
+
+/* copy and subregions */
+GstMemory * gst_memory_copy (GstMemory *mem, gssize offset, gsize size);
+GstMemory * gst_memory_share (GstMemory *mem, gssize offset, gsize size);
+
+/* span memory */
+gboolean gst_memory_is_span (GstMemory *mem1, GstMemory *mem2, gsize *offset);
+
+G_END_DECLS
+
+#endif /* __GST_MEMORY_H__ */
diff --git a/gst/gstmessage.c b/gst/gstmessage.c
new file mode 100644
index 0000000..41e7ccc
--- /dev/null
+++ b/gst/gstmessage.c
@@ -0,0 +1,2158 @@
+/* GStreamer
+ * Copyright (C) 2004 Wim Taymans <wim@fluendo.com>
+ *
+ * gstmessage.c: GstMessage subsystem
+ *
+ * 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:gstmessage
+ * @short_description: Lightweight objects to signal the application of
+ * pipeline events
+ * @see_also: #GstBus, #GstMiniObject, #GstElement
+ *
+ * Messages are implemented as a subclass of #GstMiniObject with a generic
+ * #GstStructure as the content. This allows for writing custom messages without
+ * requiring an API change while allowing a wide range of different types
+ * of messages.
+ *
+ * Messages are posted by objects in the pipeline and are passed to the
+ * application using the #GstBus.
+
+ * The basic use pattern of posting a message on a #GstBus is as follows:
+ *
+ * <example>
+ * <title>Posting a #GstMessage</title>
+ * <programlisting>
+ * gst_bus_post (bus, gst_message_new_eos());
+ * </programlisting>
+ * </example>
+ *
+ * A #GstElement usually posts messages on the bus provided by the parent
+ * container using gst_element_post_message().
+ *
+ * Last reviewed on 2005-11-09 (0.9.4)
+ */
+
+
+#include "gst_private.h"
+#include <string.h> /* memcpy */
+#include "gsterror.h"
+#include "gstenumtypes.h"
+#include "gstinfo.h"
+#include "gstmessage.h"
+#include "gsttaglist.h"
+#include "gstutils.h"
+#include "gstquark.h"
+
+
+typedef struct
+{
+ GstMessage message;
+
+ GstStructure *structure;
+} GstMessageImpl;
+
+#define GST_MESSAGE_STRUCTURE(m) (((GstMessageImpl *)(m))->structure)
+
+typedef struct
+{
+ const gint type;
+ const gchar *name;
+ GQuark quark;
+} GstMessageQuarks;
+
+static GstMessageQuarks message_quarks[] = {
+ {GST_MESSAGE_UNKNOWN, "unknown", 0},
+ {GST_MESSAGE_EOS, "eos", 0},
+ {GST_MESSAGE_ERROR, "error", 0},
+ {GST_MESSAGE_WARNING, "warning", 0},
+ {GST_MESSAGE_INFO, "info", 0},
+ {GST_MESSAGE_TAG, "tag", 0},
+ {GST_MESSAGE_BUFFERING, "buffering", 0},
+ {GST_MESSAGE_STATE_CHANGED, "state-changed", 0},
+ {GST_MESSAGE_STATE_DIRTY, "state-dirty", 0},
+ {GST_MESSAGE_STEP_DONE, "step-done", 0},
+ {GST_MESSAGE_CLOCK_PROVIDE, "clock-provide", 0},
+ {GST_MESSAGE_CLOCK_LOST, "clock-lost", 0},
+ {GST_MESSAGE_NEW_CLOCK, "new-clock", 0},
+ {GST_MESSAGE_STRUCTURE_CHANGE, "structure-change", 0},
+ {GST_MESSAGE_STREAM_STATUS, "stream-status", 0},
+ {GST_MESSAGE_APPLICATION, "application", 0},
+ {GST_MESSAGE_ELEMENT, "element", 0},
+ {GST_MESSAGE_SEGMENT_START, "segment-start", 0},
+ {GST_MESSAGE_SEGMENT_DONE, "segment-done", 0},
+ {GST_MESSAGE_DURATION, "duration", 0},
+ {GST_MESSAGE_LATENCY, "latency", 0},
+ {GST_MESSAGE_ASYNC_START, "async-start", 0},
+ {GST_MESSAGE_ASYNC_DONE, "async-done", 0},
+ {GST_MESSAGE_REQUEST_STATE, "request-state", 0},
+ {GST_MESSAGE_STEP_START, "step-start", 0},
+ {GST_MESSAGE_QOS, "qos", 0},
+ {GST_MESSAGE_PROGRESS, "progress", 0},
+ {0, NULL, 0}
+};
+
+static GType _gst_message_type = 0;
+GST_DEFINE_MINI_OBJECT_TYPE (GstMessage, gst_message);
+
+void
+_priv_gst_message_initialize (void)
+{
+ gint i;
+
+ GST_CAT_INFO (GST_CAT_GST_INIT, "init messages");
+
+ /* the GstMiniObject types need to be class_ref'd once before it can be
+ * done from multiple threads;
+ * see http://bugzilla.gnome.org/show_bug.cgi?id=304551 */
+ gst_message_get_type ();
+
+ for (i = 0; message_quarks[i].name; i++) {
+ message_quarks[i].quark =
+ g_quark_from_static_string (message_quarks[i].name);
+ }
+
+ _gst_message_type = gst_message_get_type ();
+}
+
+/**
+ * gst_message_type_get_name:
+ * @type: the message type
+ *
+ * Get a printable name for the given message type. Do not modify or free.
+ *
+ * Returns: a reference to the static name of the message.
+ */
+const gchar *
+gst_message_type_get_name (GstMessageType type)
+{
+ gint i;
+
+ for (i = 0; message_quarks[i].name; i++) {
+ if (type == message_quarks[i].type)
+ return message_quarks[i].name;
+ }
+ return "unknown";
+}
+
+/**
+ * gst_message_type_to_quark:
+ * @type: the message type
+ *
+ * Get the unique quark for the given message type.
+ *
+ * Returns: the quark associated with the message type
+ */
+GQuark
+gst_message_type_to_quark (GstMessageType type)
+{
+ gint i;
+
+ for (i = 0; message_quarks[i].name; i++) {
+ if (type == message_quarks[i].type)
+ return message_quarks[i].quark;
+ }
+ return 0;
+}
+
+static void
+_gst_message_free (GstMessage * message)
+{
+ GstStructure *structure;
+
+ g_return_if_fail (message != NULL);
+
+ GST_CAT_LOG (GST_CAT_MESSAGE, "finalize message %p", message);
+
+ if (GST_MESSAGE_SRC (message)) {
+ gst_object_unref (GST_MESSAGE_SRC (message));
+ GST_MESSAGE_SRC (message) = NULL;
+ }
+
+ if (message->lock) {
+ GST_MESSAGE_LOCK (message);
+ GST_MESSAGE_SIGNAL (message);
+ GST_MESSAGE_UNLOCK (message);
+ }
+
+ structure = GST_MESSAGE_STRUCTURE (message);
+ if (structure) {
+ gst_structure_set_parent_refcount (structure, NULL);
+ gst_structure_free (structure);
+ }
+
+ g_slice_free1 (GST_MINI_OBJECT_SIZE (message), message);
+}
+
+static GstMessage *
+_gst_message_copy (GstMessage * message)
+{
+ GstMessageImpl *copy;
+ GstStructure *structure;
+
+ GST_CAT_LOG (GST_CAT_MESSAGE, "copy message %p", message);
+
+ copy = g_slice_new0 (GstMessageImpl);
+
+ gst_mini_object_init (GST_MINI_OBJECT_CAST (copy),
+ _gst_message_type, sizeof (GstMessageImpl));
+
+ copy->message.mini_object.copy =
+ (GstMiniObjectCopyFunction) _gst_message_copy;
+ copy->message.mini_object.free =
+ (GstMiniObjectFreeFunction) _gst_message_free;
+
+ GST_MESSAGE_TYPE (copy) = GST_MESSAGE_TYPE (message);
+ GST_MESSAGE_TIMESTAMP (copy) = GST_MESSAGE_TIMESTAMP (message);
+ GST_MESSAGE_SEQNUM (copy) = GST_MESSAGE_SEQNUM (message);
+ if (GST_MESSAGE_SRC (message)) {
+ GST_MESSAGE_SRC (copy) = gst_object_ref (GST_MESSAGE_SRC (message));
+ }
+
+ GST_MESSAGE_GET_LOCK (copy) = GST_MESSAGE_GET_LOCK (message);
+ GST_MESSAGE_COND (copy) = GST_MESSAGE_COND (message);
+
+ structure = GST_MESSAGE_STRUCTURE (message);
+ if (structure) {
+ copy->structure = gst_structure_copy (structure);
+ gst_structure_set_parent_refcount (copy->structure,
+ &copy->message.mini_object.refcount);
+ }
+
+ return GST_MESSAGE_CAST (copy);
+}
+
+/**
+ * gst_message_new_custom:
+ * @type: The #GstMessageType to distinguish messages
+ * @src: The object originating the message.
+ * @structure: (transfer full): the structure for the message. The message
+ * will take ownership of the structure.
+ *
+ * Create a new custom-typed message. This can be used for anything not
+ * handled by other message-specific functions to pass a message to the
+ * app. The structure field can be NULL.
+ *
+ * Returns: (transfer full): The new message.
+ *
+ * MT safe.
+ */
+GstMessage *
+gst_message_new_custom (GstMessageType type, GstObject * src,
+ GstStructure * structure)
+{
+ GstMessageImpl *message;
+
+ message = g_slice_new0 (GstMessageImpl);
+
+ gst_mini_object_init (GST_MINI_OBJECT_CAST (message),
+ _gst_message_type, sizeof (GstMessageImpl));
+
+ message->message.mini_object.copy =
+ (GstMiniObjectCopyFunction) _gst_message_copy;
+ message->message.mini_object.free =
+ (GstMiniObjectFreeFunction) _gst_message_free;
+
+ GST_CAT_LOG (GST_CAT_MESSAGE, "source %s: creating new message %p %s",
+ (src ? GST_OBJECT_NAME (src) : "NULL"), message,
+ gst_message_type_get_name (type));
+
+ GST_MESSAGE_TYPE (message) = type;
+ if (src)
+ gst_object_ref (src);
+ GST_MESSAGE_SRC (message) = src;
+ GST_MESSAGE_TIMESTAMP (message) = GST_CLOCK_TIME_NONE;
+ GST_MESSAGE_SEQNUM (message) = gst_util_seqnum_next ();
+
+ if (structure) {
+ gst_structure_set_parent_refcount (structure,
+ &message->message.mini_object.refcount);
+ }
+ message->structure = structure;
+
+ return GST_MESSAGE_CAST (message);
+}
+
+/**
+ * gst_message_get_seqnum:
+ * @message: A #GstMessage.
+ *
+ * Retrieve the sequence number of a message.
+ *
+ * Messages have ever-incrementing sequence numbers, which may also be set
+ * explicitly via gst_message_set_seqnum(). Sequence numbers are typically used
+ * to indicate that a message corresponds to some other set of messages or
+ * events, for example a SEGMENT_DONE message corresponding to a SEEK event. It
+ * is considered good practice to make this correspondence when possible, though
+ * it is not required.
+ *
+ * Note that events and messages share the same sequence number incrementor;
+ * two events or messages will never have the same sequence number unless
+ * that correspondence was made explicitly.
+ *
+ * Returns: The message's sequence number.
+ *
+ * MT safe.
+ *
+ * Since: 0.10.22
+ */
+guint32
+gst_message_get_seqnum (GstMessage * message)
+{
+ g_return_val_if_fail (GST_IS_MESSAGE (message), -1);
+
+ return GST_MESSAGE_SEQNUM (message);
+}
+
+/**
+ * gst_message_set_seqnum:
+ * @message: A #GstMessage.
+ * @seqnum: A sequence number.
+ *
+ * Set the sequence number of a message.
+ *
+ * This function might be called by the creator of a message to indicate that
+ * the message relates to other messages or events. See gst_message_get_seqnum()
+ * for more information.
+ *
+ * MT safe.
+ *
+ * Since: 0.10.22
+ */
+void
+gst_message_set_seqnum (GstMessage * message, guint32 seqnum)
+{
+ g_return_if_fail (GST_IS_MESSAGE (message));
+
+ GST_MESSAGE_SEQNUM (message) = seqnum;
+}
+
+/**
+ * gst_message_new_eos:
+ * @src: (transfer none): The object originating the message.
+ *
+ * Create a new eos message. This message is generated and posted in
+ * the sink elements of a GstBin. The bin will only forward the EOS
+ * message to the application if all sinks have posted an EOS message.
+ *
+ * Returns: (transfer full): The new eos message.
+ *
+ * MT safe.
+ */
+GstMessage *
+gst_message_new_eos (GstObject * src)
+{
+ GstMessage *message;
+
+ message = gst_message_new_custom (GST_MESSAGE_EOS, src, NULL);
+
+ return message;
+}
+
+/**
+ * gst_message_new_error:
+ * @src: (transfer none): The object originating the message.
+ * @error: (transfer none): The GError for this message.
+ * @debug: A debugging string.
+ *
+ * Create a new error message. The message will copy @error and
+ * @debug. This message is posted by element when a fatal event
+ * occured. The pipeline will probably (partially) stop. The application
+ * receiving this message should stop the pipeline.
+ *
+ * Returns: (transfer full): the new error message.
+ *
+ * MT safe.
+ */
+GstMessage *
+gst_message_new_error (GstObject * src, GError * error, const gchar * debug)
+{
+ GstMessage *message;
+ GstStructure *structure;
+
+ structure = gst_structure_id_new (GST_QUARK (MESSAGE_ERROR),
+ GST_QUARK (GERROR), GST_TYPE_G_ERROR, error,
+ GST_QUARK (DEBUG), G_TYPE_STRING, debug, NULL);
+ message = gst_message_new_custom (GST_MESSAGE_ERROR, src, structure);
+
+ return message;
+}
+
+/**
+ * gst_message_new_warning:
+ * @src: (transfer none): The object originating the message.
+ * @error: (transfer none): The GError for this message.
+ * @debug: A debugging string.
+ *
+ * Create a new warning message. The message will make copies of @error and
+ * @debug.
+ *
+ * Returns: (transfer full): The new warning message.
+ *
+ * MT safe.
+ */
+GstMessage *
+gst_message_new_warning (GstObject * src, GError * error, const gchar * debug)
+{
+ GstMessage *message;
+ GstStructure *structure;
+
+ structure = gst_structure_id_new (GST_QUARK (MESSAGE_WARNING),
+ GST_QUARK (GERROR), GST_TYPE_G_ERROR, error,
+ GST_QUARK (DEBUG), G_TYPE_STRING, debug, NULL);
+ message = gst_message_new_custom (GST_MESSAGE_WARNING, src, structure);
+
+ return message;
+}
+
+/**
+ * gst_message_new_info:
+ * @src: (transfer none): The object originating the message.
+ * @error: (transfer none): The GError for this message.
+ * @debug: A debugging string.
+ *
+ * Create a new info message. The message will make copies of @error and
+ * @debug.
+ *
+ * MT safe.
+ *
+ * Returns: (transfer full): the new info message.
+ *
+ * Since: 0.10.12
+ */
+GstMessage *
+gst_message_new_info (GstObject * src, GError * error, const gchar * debug)
+{
+ GstMessage *message;
+ GstStructure *structure;
+
+ structure = gst_structure_id_new (GST_QUARK (MESSAGE_INFO),
+ GST_QUARK (GERROR), GST_TYPE_G_ERROR, error,
+ GST_QUARK (DEBUG), G_TYPE_STRING, debug, NULL);
+ message = gst_message_new_custom (GST_MESSAGE_INFO, src, structure);
+
+ return message;
+}
+
+/**
+ * gst_message_new_tag:
+ * @src: (transfer none): The object originating the message.
+ * @tag_list: (transfer full): the tag list for the message.
+ *
+ * Create a new tag message. The message will take ownership of the tag list.
+ * The message is posted by elements that discovered a new taglist.
+ *
+ * Returns: (transfer full): the new tag message.
+ *
+ * MT safe.
+ */
+GstMessage *
+gst_message_new_tag (GstObject * src, GstTagList * tag_list)
+{
+ GstMessage *message;
+
+ g_return_val_if_fail (GST_IS_STRUCTURE (tag_list), NULL);
+
+ message =
+ gst_message_new_custom (GST_MESSAGE_TAG, src, (GstStructure *) tag_list);
+
+ return message;
+}
+
+/**
+ * gst_message_new_buffering:
+ * @src: (transfer none): The object originating the message.
+ * @percent: The buffering percent
+ *
+ * Create a new buffering message. This message can be posted by an element that
+ * needs to buffer data before it can continue processing. @percent should be a
+ * value between 0 and 100. A value of 100 means that the buffering completed.
+ *
+ * When @percent is < 100 the application should PAUSE a PLAYING pipeline. When
+ * @percent is 100, the application can set the pipeline (back) to PLAYING.
+ * The application must be prepared to receive BUFFERING messages in the
+ * PREROLLING state and may only set the pipeline to PLAYING after receiving a
+ * message with @percent set to 100, which can happen after the pipeline
+ * completed prerolling.
+ *
+ * MT safe.
+ *
+ * Returns: (transfer full): The new buffering message.
+ *
+ * Since: 0.10.11
+ */
+GstMessage *
+gst_message_new_buffering (GstObject * src, gint percent)
+{
+ GstMessage *message;
+ GstStructure *structure;
+
+ g_return_val_if_fail (percent >= 0 && percent <= 100, NULL);
+
+ structure = gst_structure_id_new (GST_QUARK (MESSAGE_BUFFERING),
+ GST_QUARK (BUFFER_PERCENT), G_TYPE_INT, percent,
+ GST_QUARK (BUFFERING_MODE), GST_TYPE_BUFFERING_MODE, GST_BUFFERING_STREAM,
+ GST_QUARK (AVG_IN_RATE), G_TYPE_INT, -1,
+ GST_QUARK (AVG_OUT_RATE), G_TYPE_INT, -1,
+ GST_QUARK (BUFFERING_LEFT), G_TYPE_INT64, G_GINT64_CONSTANT (-1),
+ GST_QUARK (ESTIMATED_TOTAL), G_TYPE_INT64, G_GINT64_CONSTANT (-1), NULL);
+ message = gst_message_new_custom (GST_MESSAGE_BUFFERING, src, structure);
+
+ return message;
+}
+
+/**
+ * gst_message_new_state_changed:
+ * @src: (transfer none): the object originating the message
+ * @oldstate: the previous state
+ * @newstate: the new (current) state
+ * @pending: the pending (target) state
+ *
+ * Create a state change message. This message is posted whenever an element
+ * changed its state.
+ *
+ * Returns: (transfer full): the new state change message.
+ *
+ * MT safe.
+ */
+GstMessage *
+gst_message_new_state_changed (GstObject * src,
+ GstState oldstate, GstState newstate, GstState pending)
+{
+ GstMessage *message;
+ GstStructure *structure;
+
+ structure = gst_structure_id_new (GST_QUARK (MESSAGE_STATE),
+ GST_QUARK (OLD_STATE), GST_TYPE_STATE, (gint) oldstate,
+ GST_QUARK (NEW_STATE), GST_TYPE_STATE, (gint) newstate,
+ GST_QUARK (PENDING_STATE), GST_TYPE_STATE, (gint) pending, NULL);
+ message = gst_message_new_custom (GST_MESSAGE_STATE_CHANGED, src, structure);
+
+ return message;
+}
+
+/**
+ * gst_message_new_state_dirty:
+ * @src: (transfer none): the object originating the message
+ *
+ * Create a state dirty message. This message is posted whenever an element
+ * changed its state asynchronously and is used internally to update the
+ * states of container objects.
+ *
+ * Returns: (transfer full): the new state dirty message.
+ *
+ * MT safe.
+ */
+GstMessage *
+gst_message_new_state_dirty (GstObject * src)
+{
+ GstMessage *message;
+
+ message = gst_message_new_custom (GST_MESSAGE_STATE_DIRTY, src, NULL);
+
+ return message;
+}
+
+/**
+ * gst_message_new_clock_provide:
+ * @src: (transfer none): the object originating the message.
+ * @clock: (transfer none): the clock it provides
+ * @ready: TRUE if the sender can provide a clock
+ *
+ * Create a clock provide message. This message is posted whenever an
+ * element is ready to provide a clock or lost its ability to provide
+ * a clock (maybe because it paused or became EOS).
+ *
+ * This message is mainly used internally to manage the clock
+ * selection.
+ *
+ * Returns: (transfer full): the new provide clock message.
+ *
+ * MT safe.
+ */
+GstMessage *
+gst_message_new_clock_provide (GstObject * src, GstClock * clock,
+ gboolean ready)
+{
+ GstMessage *message;
+ GstStructure *structure;
+
+ structure = gst_structure_id_new (GST_QUARK (MESSAGE_CLOCK_PROVIDE),
+ GST_QUARK (CLOCK), GST_TYPE_CLOCK, clock,
+ GST_QUARK (READY), G_TYPE_BOOLEAN, ready, NULL);
+ message = gst_message_new_custom (GST_MESSAGE_CLOCK_PROVIDE, src, structure);
+
+ return message;
+}
+
+/**
+ * gst_message_new_clock_lost:
+ * @src: (transfer none): the object originating the message.
+ * @clock: (transfer none): the clock that was lost
+ *
+ * Create a clock lost message. This message is posted whenever the
+ * clock is not valid anymore.
+ *
+ * If this message is posted by the pipeline, the pipeline will
+ * select a new clock again when it goes to PLAYING. It might therefore
+ * be needed to set the pipeline to PAUSED and PLAYING again.
+ *
+ * Returns: (transfer full): The new clock lost message.
+ *
+ * MT safe.
+ */
+GstMessage *
+gst_message_new_clock_lost (GstObject * src, GstClock * clock)
+{
+ GstMessage *message;
+ GstStructure *structure;
+
+ structure = gst_structure_id_new (GST_QUARK (MESSAGE_CLOCK_LOST),
+ GST_QUARK (CLOCK), GST_TYPE_CLOCK, clock, NULL);
+ message = gst_message_new_custom (GST_MESSAGE_CLOCK_LOST, src, structure);
+
+ return message;
+}
+
+/**
+ * gst_message_new_new_clock:
+ * @src: (transfer none): The object originating the message.
+ * @clock: (transfer none): the new selected clock
+ *
+ * Create a new clock message. This message is posted whenever the
+ * pipeline selectes a new clock for the pipeline.
+ *
+ * Returns: (transfer full): The new new clock message.
+ *
+ * MT safe.
+ */
+GstMessage *
+gst_message_new_new_clock (GstObject * src, GstClock * clock)
+{
+ GstMessage *message;
+ GstStructure *structure;
+
+ structure = gst_structure_id_new (GST_QUARK (MESSAGE_NEW_CLOCK),
+ GST_QUARK (CLOCK), GST_TYPE_CLOCK, clock, NULL);
+ message = gst_message_new_custom (GST_MESSAGE_NEW_CLOCK, src, structure);
+
+ return message;
+}
+
+/**
+ * gst_message_new_structure_change:
+ * @src: (transfer none): The object originating the message.
+ * @type: The change type.
+ * @owner: (transfer none): The owner element of @src.
+ * @busy: Whether the structure change is busy.
+ *
+ * Create a new structure change message. This message is posted when the
+ * structure of a pipeline is in the process of being changed, for example
+ * when pads are linked or unlinked.
+ *
+ * @src should be the sinkpad that unlinked or linked.
+ *
+ * Returns: (transfer full): the new structure change message.
+ *
+ * MT safe.
+ *
+ * Since: 0.10.22.
+ */
+GstMessage *
+gst_message_new_structure_change (GstObject * src, GstStructureChangeType type,
+ GstElement * owner, gboolean busy)
+{
+ GstMessage *message;
+ GstStructure *structure;
+
+ g_return_val_if_fail (GST_IS_PAD (src), NULL);
+ /* g_return_val_if_fail (GST_PAD_DIRECTION (src) == GST_PAD_SINK, NULL); */
+ g_return_val_if_fail (GST_IS_ELEMENT (owner), NULL);
+
+ structure = gst_structure_id_new (GST_QUARK (MESSAGE_STRUCTURE_CHANGE),
+ GST_QUARK (TYPE), GST_TYPE_STRUCTURE_CHANGE_TYPE, type,
+ GST_QUARK (OWNER), GST_TYPE_ELEMENT, owner,
+ GST_QUARK (BUSY), G_TYPE_BOOLEAN, busy, NULL);
+
+ message = gst_message_new_custom (GST_MESSAGE_STRUCTURE_CHANGE, src,
+ structure);
+
+ return message;
+}
+
+/**
+ * gst_message_new_segment_start:
+ * @src: (transfer none): The object originating the message.
+ * @format: The format of the position being played
+ * @position: The position of the segment being played
+ *
+ * Create a new segment message. This message is posted by elements that
+ * start playback of a segment as a result of a segment seek. This message
+ * is not received by the application but is used for maintenance reasons in
+ * container elements.
+ *
+ * Returns: (transfer full): the new segment start message.
+ *
+ * MT safe.
+ */
+GstMessage *
+gst_message_new_segment_start (GstObject * src, GstFormat format,
+ gint64 position)
+{
+ GstMessage *message;
+ GstStructure *structure;
+
+ structure = gst_structure_id_new (GST_QUARK (MESSAGE_SEGMENT_START),
+ GST_QUARK (FORMAT), GST_TYPE_FORMAT, format,
+ GST_QUARK (POSITION), G_TYPE_INT64, position, NULL);
+ message = gst_message_new_custom (GST_MESSAGE_SEGMENT_START, src, structure);
+
+ return message;
+}
+
+/**
+ * gst_message_new_segment_done:
+ * @src: (transfer none): the object originating the message.
+ * @format: The format of the position being done
+ * @position: The position of the segment being done
+ *
+ * Create a new segment done message. This message is posted by elements that
+ * finish playback of a segment as a result of a segment seek. This message
+ * is received by the application after all elements that posted a segment_start
+ * have posted the segment_done.
+ *
+ * Returns: (transfer full): the new segment done message.
+ *
+ * MT safe.
+ */
+GstMessage *
+gst_message_new_segment_done (GstObject * src, GstFormat format,
+ gint64 position)
+{
+ GstMessage *message;
+ GstStructure *structure;
+
+ structure = gst_structure_id_new (GST_QUARK (MESSAGE_SEGMENT_DONE),
+ GST_QUARK (FORMAT), GST_TYPE_FORMAT, format,
+ GST_QUARK (POSITION), G_TYPE_INT64, position, NULL);
+ message = gst_message_new_custom (GST_MESSAGE_SEGMENT_DONE, src, structure);
+
+ return message;
+}
+
+/**
+ * gst_message_new_application:
+ * @src: (transfer none): the object originating the message.
+ * @structure: (transfer full): the structure for the message. The message
+ * will take ownership of the structure.
+ *
+ * Create a new application-typed message. GStreamer will never create these
+ * messages; they are a gift from us to you. Enjoy.
+ *
+ * Returns: (transfer full): The new application message.
+ *
+ * MT safe.
+ */
+GstMessage *
+gst_message_new_application (GstObject * src, GstStructure * structure)
+{
+ return gst_message_new_custom (GST_MESSAGE_APPLICATION, src, structure);
+}
+
+/**
+ * gst_message_new_element:
+ * @src: (transfer none): The object originating the message.
+ * @structure: (transfer full): The structure for the message. The message
+ * will take ownership of the structure.
+ *
+ * Create a new element-specific message. This is meant as a generic way of
+ * allowing one-way communication from an element to an application, for example
+ * "the firewire cable was unplugged". The format of the message should be
+ * documented in the element's documentation. The structure field can be NULL.
+ *
+ * Returns: (transfer full): The new element message.
+ *
+ * MT safe.
+ */
+GstMessage *
+gst_message_new_element (GstObject * src, GstStructure * structure)
+{
+ return gst_message_new_custom (GST_MESSAGE_ELEMENT, src, structure);
+}
+
+/**
+ * gst_message_new_duration:
+ * @src: (transfer none): The object originating the message.
+ * @format: The format of the duration
+ * @duration: The new duration
+ *
+ * Create a new duration message. This message is posted by elements that
+ * know the duration of a stream in a specific format. This message
+ * is received by bins and is used to calculate the total duration of a
+ * pipeline. Elements may post a duration message with a duration of
+ * GST_CLOCK_TIME_NONE to indicate that the duration has changed and the
+ * cached duration should be discarded. The new duration can then be
+ * retrieved via a query.
+ *
+ * Returns: (transfer full): The new duration message.
+ *
+ * MT safe.
+ */
+GstMessage *
+gst_message_new_duration (GstObject * src, GstFormat format, gint64 duration)
+{
+ GstMessage *message;
+ GstStructure *structure;
+
+ structure = gst_structure_id_new (GST_QUARK (MESSAGE_DURATION),
+ GST_QUARK (FORMAT), GST_TYPE_FORMAT, format,
+ GST_QUARK (DURATION), G_TYPE_INT64, duration, NULL);
+ message = gst_message_new_custom (GST_MESSAGE_DURATION, src, structure);
+
+ return message;
+}
+
+/**
+ * gst_message_new_async_start:
+ * @src: (transfer none): The object originating the message.
+ *
+ * This message is posted by elements when they start an ASYNC state change.
+ *
+ * Returns: (transfer full): The new async_start message.
+ *
+ * MT safe.
+ */
+GstMessage *
+gst_message_new_async_start (GstObject * src)
+{
+ GstMessage *message;
+
+ message = gst_message_new_custom (GST_MESSAGE_ASYNC_START, src, NULL);
+
+ return message;
+}
+
+/**
+ * gst_message_new_async_done:
+ * @src: (transfer none): The object originating the message.
+ * @reset_time: if the running_time should be reset
+ *
+ * The message is posted when elements completed an ASYNC state change.
+ * @reset_time is set to TRUE when the element requests a new running_time
+ * before going to PLAYING.
+ *
+ * Returns: (transfer full): The new async_done message.
+ *
+ * MT safe.
+ */
+GstMessage *
+gst_message_new_async_done (GstObject * src, gboolean reset_time)
+{
+ GstMessage *message;
+ GstStructure *structure;
+
+ structure = gst_structure_id_new (GST_QUARK (MESSAGE_ASYNC_DONE),
+ GST_QUARK (RESET_TIME), G_TYPE_BOOLEAN, reset_time, NULL);
+ message = gst_message_new_custom (GST_MESSAGE_ASYNC_DONE, src, structure);
+
+ return message;
+}
+
+/**
+ * gst_message_new_latency:
+ * @src: (transfer none): The object originating the message.
+ *
+ * This message can be posted by elements when their latency requirements have
+ * changed.
+ *
+ * Returns: (transfer full): The new latency message.
+ *
+ * MT safe.
+ *
+ * Since: 0.10.12
+ */
+GstMessage *
+gst_message_new_latency (GstObject * src)
+{
+ GstMessage *message;
+
+ message = gst_message_new_custom (GST_MESSAGE_LATENCY, src, NULL);
+
+ return message;
+}
+
+/**
+ * gst_message_new_request_state:
+ * @src: (transfer none): the object originating the message.
+ * @state: The new requested state
+ *
+ * This message can be posted by elements when they want to have their state
+ * changed. A typical use case would be an audio server that wants to pause the
+ * pipeline because a higher priority stream is being played.
+ *
+ * Returns: (transfer full): the new requst state message.
+ *
+ * MT safe.
+ *
+ * Since: 0.10.23
+ */
+GstMessage *
+gst_message_new_request_state (GstObject * src, GstState state)
+{
+ GstMessage *message;
+ GstStructure *structure;
+
+ structure = gst_structure_id_new (GST_QUARK (MESSAGE_REQUEST_STATE),
+ GST_QUARK (NEW_STATE), GST_TYPE_STATE, (gint) state, NULL);
+ message = gst_message_new_custom (GST_MESSAGE_REQUEST_STATE, src, structure);
+
+ return message;
+}
+
+/**
+ * gst_message_get_structure:
+ * @message: The #GstMessage.
+ *
+ * Access the structure of the message.
+ *
+ * Returns: (transfer none): The structure of the message. The structure is
+ * still owned by the message, which means that you should not free it and
+ * that the pointer becomes invalid when you free the message.
+ *
+ * MT safe.
+ */
+const GstStructure *
+gst_message_get_structure (GstMessage * message)
+{
+ g_return_val_if_fail (GST_IS_MESSAGE (message), NULL);
+
+ return GST_MESSAGE_STRUCTURE (message);
+}
+
+/**
+ * gst_message_has_name:
+ * @message: The #GstMessage.
+ * @name: name to check
+ *
+ * Checks if @message has the given @name. This function is usually used to
+ * check the name of a custom message.
+ *
+ * Returns: %TRUE if @name matches the name of the message structure.
+ *
+ * Since: 0.10.20
+ */
+gboolean
+gst_message_has_name (GstMessage * message, const gchar * name)
+{
+ GstStructure *structure;
+
+ g_return_val_if_fail (GST_IS_MESSAGE (message), FALSE);
+
+ structure = GST_MESSAGE_STRUCTURE (message);
+ if (structure == NULL)
+ return FALSE;
+
+ return gst_structure_has_name (structure, name);
+}
+
+/**
+ * gst_message_parse_tag:
+ * @message: A valid #GstMessage of type GST_MESSAGE_TAG.
+ * @tag_list: (out callee-allocates): return location for the tag-list.
+ *
+ * Extracts the tag list from the GstMessage. The tag list returned in the
+ * output argument is a copy; the caller must free it when done.
+ *
+ * Typical usage of this function might be:
+ * |[
+ * ...
+ * switch (GST_MESSAGE_TYPE (msg)) {
+ * case GST_MESSAGE_TAG: {
+ * GstTagList *tags = NULL;
+ *
+ * gst_message_parse_tag (msg, &amp;tags);
+ * g_print ("Got tags from element %s\n", GST_OBJECT_NAME (msg->src));
+ * handle_tags (tags);
+ * gst_tag_list_free (tags);
+ * break;
+ * }
+ * ...
+ * }
+ * ...
+ * ]|
+ *
+ * MT safe.
+ */
+void
+gst_message_parse_tag (GstMessage * message, GstTagList ** tag_list)
+{
+ GstStructure *ret;
+
+ g_return_if_fail (GST_IS_MESSAGE (message));
+ g_return_if_fail (GST_MESSAGE_TYPE (message) == GST_MESSAGE_TAG);
+ g_return_if_fail (tag_list != NULL);
+
+ ret = gst_structure_copy (GST_MESSAGE_STRUCTURE (message));
+ gst_structure_remove_field (ret, "source-pad");
+
+ *tag_list = (GstTagList *) ret;
+}
+
+/**
+ * gst_message_parse_buffering:
+ * @message: A valid #GstMessage of type GST_MESSAGE_BUFFERING.
+ * @percent: (out) (allow-none): Return location for the percent.
+ *
+ * Extracts the buffering percent from the GstMessage. see also
+ * gst_message_new_buffering().
+ *
+ * MT safe.
+ *
+ * Since: 0.10.11
+ */
+void
+gst_message_parse_buffering (GstMessage * message, gint * percent)
+{
+ g_return_if_fail (GST_IS_MESSAGE (message));
+ g_return_if_fail (GST_MESSAGE_TYPE (message) == GST_MESSAGE_BUFFERING);
+
+ if (percent)
+ *percent =
+ g_value_get_int (gst_structure_id_get_value (GST_MESSAGE_STRUCTURE
+ (message), GST_QUARK (BUFFER_PERCENT)));
+}
+
+/**
+ * gst_message_set_buffering_stats:
+ * @message: A valid #GstMessage of type GST_MESSAGE_BUFFERING.
+ * @mode: a buffering mode
+ * @avg_in: the average input rate
+ * @avg_out: the average output rate
+ * @buffering_left: amount of buffering time left in milliseconds
+ *
+ * Configures the buffering stats values in @message.
+ *
+ * Since: 0.10.20
+ */
+void
+gst_message_set_buffering_stats (GstMessage * message, GstBufferingMode mode,
+ gint avg_in, gint avg_out, gint64 buffering_left)
+{
+ g_return_if_fail (GST_MESSAGE_TYPE (message) == GST_MESSAGE_BUFFERING);
+
+ gst_structure_id_set (GST_MESSAGE_STRUCTURE (message),
+ GST_QUARK (BUFFERING_MODE), GST_TYPE_BUFFERING_MODE, mode,
+ GST_QUARK (AVG_IN_RATE), G_TYPE_INT, avg_in,
+ GST_QUARK (AVG_OUT_RATE), G_TYPE_INT, avg_out,
+ GST_QUARK (BUFFERING_LEFT), G_TYPE_INT64, buffering_left, NULL);
+}
+
+/**
+ * gst_message_parse_buffering_stats:
+ * @message: A valid #GstMessage of type GST_MESSAGE_BUFFERING.
+ * @mode: (out) (allow-none): a buffering mode, or NULL
+ * @avg_in: (out) (allow-none): the average input rate, or NULL
+ * @avg_out: (out) (allow-none): the average output rate, or NULL
+ * @buffering_left: (out) (allow-none): amount of buffering time left in
+ * milliseconds, or NULL
+ *
+ * Extracts the buffering stats values from @message.
+ *
+ * Since: 0.10.20
+ */
+void
+gst_message_parse_buffering_stats (GstMessage * message,
+ GstBufferingMode * mode, gint * avg_in, gint * avg_out,
+ gint64 * buffering_left)
+{
+ GstStructure *structure;
+
+ g_return_if_fail (GST_MESSAGE_TYPE (message) == GST_MESSAGE_BUFFERING);
+
+ structure = GST_MESSAGE_STRUCTURE (message);
+ if (mode)
+ *mode = (GstBufferingMode)
+ g_value_get_enum (gst_structure_id_get_value (structure,
+ GST_QUARK (BUFFERING_MODE)));
+ if (avg_in)
+ *avg_in = g_value_get_int (gst_structure_id_get_value (structure,
+ GST_QUARK (AVG_IN_RATE)));
+ if (avg_out)
+ *avg_out = g_value_get_int (gst_structure_id_get_value (structure,
+ GST_QUARK (AVG_OUT_RATE)));
+ if (buffering_left)
+ *buffering_left =
+ g_value_get_int64 (gst_structure_id_get_value (structure,
+ GST_QUARK (BUFFERING_LEFT)));
+}
+
+/**
+ * gst_message_parse_state_changed:
+ * @message: a valid #GstMessage of type GST_MESSAGE_STATE_CHANGED
+ * @oldstate: (out) (allow-none): the previous state, or NULL
+ * @newstate: (out) (allow-none): the new (current) state, or NULL
+ * @pending: (out) (allow-none): the pending (target) state, or NULL
+ *
+ * Extracts the old and new states from the GstMessage.
+ *
+ * Typical usage of this function might be:
+ * |[
+ * ...
+ * switch (GST_MESSAGE_TYPE (msg)) {
+ * case GST_MESSAGE_STATE_CHANGED: {
+ * GstState old_state, new_state;
+ *
+ * gst_message_parse_state_changed (msg, &amp;old_state, &amp;new_state, NULL);
+ * g_print ("Element %s changed state from %s to %s.\n",
+ * GST_OBJECT_NAME (msg->src),
+ * gst_element_state_get_name (old_state),
+ * gst_element_state_get_name (new_state));
+ * break;
+ * }
+ * ...
+ * }
+ * ...
+ * ]|
+ *
+ * MT safe.
+ */
+void
+gst_message_parse_state_changed (GstMessage * message,
+ GstState * oldstate, GstState * newstate, GstState * pending)
+{
+ GstStructure *structure;
+
+ g_return_if_fail (GST_IS_MESSAGE (message));
+ g_return_if_fail (GST_MESSAGE_TYPE (message) == GST_MESSAGE_STATE_CHANGED);
+
+ structure = GST_MESSAGE_STRUCTURE (message);
+ if (oldstate)
+ *oldstate = (GstState)
+ g_value_get_enum (gst_structure_id_get_value (structure,
+ GST_QUARK (OLD_STATE)));
+ if (newstate)
+ *newstate = (GstState)
+ g_value_get_enum (gst_structure_id_get_value (structure,
+ GST_QUARK (NEW_STATE)));
+ if (pending)
+ *pending = (GstState)
+ g_value_get_enum (gst_structure_id_get_value (structure,
+ GST_QUARK (PENDING_STATE)));
+}
+
+/**
+ * gst_message_parse_clock_provide:
+ * @message: A valid #GstMessage of type GST_MESSAGE_CLOCK_PROVIDE.
+ * @clock: (out) (allow-none) (transfer none): a pointer to hold a clock
+ * object, or NULL
+ * @ready: (out) (allow-none): a pointer to hold the ready flag, or NULL
+ *
+ * Extracts the clock and ready flag from the GstMessage.
+ * The clock object returned remains valid until the message is freed.
+ *
+ * MT safe.
+ */
+void
+gst_message_parse_clock_provide (GstMessage * message, GstClock ** clock,
+ gboolean * ready)
+{
+ const GValue *clock_gvalue;
+ GstStructure *structure;
+
+ g_return_if_fail (GST_IS_MESSAGE (message));
+ g_return_if_fail (GST_MESSAGE_TYPE (message) == GST_MESSAGE_CLOCK_PROVIDE);
+
+ structure = GST_MESSAGE_STRUCTURE (message);
+ clock_gvalue = gst_structure_id_get_value (structure, GST_QUARK (CLOCK));
+ g_return_if_fail (clock_gvalue != NULL);
+ g_return_if_fail (G_VALUE_TYPE (clock_gvalue) == GST_TYPE_CLOCK);
+
+ if (ready)
+ *ready =
+ g_value_get_boolean (gst_structure_id_get_value (structure,
+ GST_QUARK (READY)));
+ if (clock)
+ *clock = (GstClock *) g_value_get_object (clock_gvalue);
+}
+
+/**
+ * gst_message_parse_clock_lost:
+ * @message: A valid #GstMessage of type GST_MESSAGE_CLOCK_LOST.
+ * @clock: (out) (allow-none) (transfer none): a pointer to hold the lost clock
+ *
+ * Extracts the lost clock from the GstMessage.
+ * The clock object returned remains valid until the message is freed.
+ *
+ * MT safe.
+ */
+void
+gst_message_parse_clock_lost (GstMessage * message, GstClock ** clock)
+{
+ const GValue *clock_gvalue;
+ GstStructure *structure;
+
+ g_return_if_fail (GST_IS_MESSAGE (message));
+ g_return_if_fail (GST_MESSAGE_TYPE (message) == GST_MESSAGE_CLOCK_LOST);
+
+ structure = GST_MESSAGE_STRUCTURE (message);
+ clock_gvalue = gst_structure_id_get_value (structure, GST_QUARK (CLOCK));
+ g_return_if_fail (clock_gvalue != NULL);
+ g_return_if_fail (G_VALUE_TYPE (clock_gvalue) == GST_TYPE_CLOCK);
+
+ if (clock)
+ *clock = (GstClock *) g_value_get_object (clock_gvalue);
+}
+
+/**
+ * gst_message_parse_new_clock:
+ * @message: A valid #GstMessage of type GST_MESSAGE_NEW_CLOCK.
+ * @clock: (out) (allow-none) (transfer none): a pointer to hold the selected
+ * new clock
+ *
+ * Extracts the new clock from the GstMessage.
+ * The clock object returned remains valid until the message is freed.
+ *
+ * MT safe.
+ */
+void
+gst_message_parse_new_clock (GstMessage * message, GstClock ** clock)
+{
+ const GValue *clock_gvalue;
+ GstStructure *structure;
+
+ g_return_if_fail (GST_IS_MESSAGE (message));
+ g_return_if_fail (GST_MESSAGE_TYPE (message) == GST_MESSAGE_NEW_CLOCK);
+
+ structure = GST_MESSAGE_STRUCTURE (message);
+ clock_gvalue = gst_structure_id_get_value (structure, GST_QUARK (CLOCK));
+ g_return_if_fail (clock_gvalue != NULL);
+ g_return_if_fail (G_VALUE_TYPE (clock_gvalue) == GST_TYPE_CLOCK);
+
+ if (clock)
+ *clock = (GstClock *) g_value_get_object (clock_gvalue);
+}
+
+/**
+ * gst_message_parse_structure_change:
+ * @message: A valid #GstMessage of type GST_MESSAGE_STRUCTURE_CHANGE.
+ * @type: (out): A pointer to hold the change type
+ * @owner: (out) (allow-none) (transfer none): The owner element of the
+ * message source
+ * @busy: (out) (allow-none): a pointer to hold whether the change is in
+ * progress or has been completed
+ *
+ * Extracts the change type and completion status from the GstMessage.
+ *
+ * MT safe.
+ *
+ * Since: 0.10.22
+ */
+void
+gst_message_parse_structure_change (GstMessage * message,
+ GstStructureChangeType * type, GstElement ** owner, gboolean * busy)
+{
+ const GValue *owner_gvalue;
+ GstStructure *structure;
+
+ g_return_if_fail (GST_IS_MESSAGE (message));
+ g_return_if_fail (GST_MESSAGE_TYPE (message) == GST_MESSAGE_STRUCTURE_CHANGE);
+
+ structure = GST_MESSAGE_STRUCTURE (message);
+ owner_gvalue = gst_structure_id_get_value (structure, GST_QUARK (OWNER));
+ g_return_if_fail (owner_gvalue != NULL);
+ g_return_if_fail (G_VALUE_TYPE (owner_gvalue) == GST_TYPE_ELEMENT);
+
+ if (type)
+ *type = (GstStructureChangeType)
+ g_value_get_enum (gst_structure_id_get_value (structure,
+ GST_QUARK (TYPE)));
+ if (owner)
+ *owner = (GstElement *) g_value_get_object (owner_gvalue);
+ if (busy)
+ *busy =
+ g_value_get_boolean (gst_structure_id_get_value (structure,
+ GST_QUARK (BUSY)));
+}
+
+/**
+ * gst_message_parse_error:
+ * @message: A valid #GstMessage of type GST_MESSAGE_ERROR.
+ * @gerror: (out) (allow-none) (transfer full): location for the GError
+ * @debug: (out) (allow-none) (transfer full): location for the debug message,
+ * or NULL
+ *
+ * Extracts the GError and debug string from the GstMessage. The values returned
+ * in the output arguments are copies; the caller must free them when done.
+ *
+ * Typical usage of this function might be:
+ * |[
+ * ...
+ * switch (GST_MESSAGE_TYPE (msg)) {
+ * case GST_MESSAGE_ERROR: {
+ * GError *err = NULL;
+ * gchar *dbg_info = NULL;
+ *
+ * gst_message_parse_error (msg, &amp;err, &amp;dbg_info);
+ * g_printerr ("ERROR from element %s: %s\n",
+ * GST_OBJECT_NAME (msg->src), err->message);
+ * g_printerr ("Debugging info: %s\n", (dbg_info) ? dbg_info : "none");
+ * g_error_free (err);
+ * g_free (dbg_info);
+ * break;
+ * }
+ * ...
+ * }
+ * ...
+ * ]|
+ *
+ * MT safe.
+ */
+void
+gst_message_parse_error (GstMessage * message, GError ** gerror, gchar ** debug)
+{
+ const GValue *error_gvalue;
+ GError *error_val;
+ GstStructure *structure;
+
+ g_return_if_fail (GST_IS_MESSAGE (message));
+ g_return_if_fail (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ERROR);
+
+ structure = GST_MESSAGE_STRUCTURE (message);
+ error_gvalue = gst_structure_id_get_value (structure, GST_QUARK (GERROR));
+ g_return_if_fail (error_gvalue != NULL);
+ g_return_if_fail (G_VALUE_TYPE (error_gvalue) == GST_TYPE_G_ERROR);
+
+ error_val = (GError *) g_value_get_boxed (error_gvalue);
+ if (error_val)
+ *gerror = g_error_copy (error_val);
+ else
+ *gerror = NULL;
+
+ if (debug)
+ *debug =
+ g_value_dup_string (gst_structure_id_get_value (structure,
+ GST_QUARK (DEBUG)));
+}
+
+/**
+ * gst_message_parse_warning:
+ * @message: A valid #GstMessage of type GST_MESSAGE_WARNING.
+ * @gerror: (out) (allow-none) (transfer full): location for the GError
+ * @debug: (out) (allow-none) (transfer full): location for the debug message,
+ * or NULL
+ *
+ * Extracts the GError and debug string from the GstMessage. The values returned
+ * in the output arguments are copies; the caller must free them when done.
+ *
+ * MT safe.
+ */
+void
+gst_message_parse_warning (GstMessage * message, GError ** gerror,
+ gchar ** debug)
+{
+ const GValue *error_gvalue;
+ GError *error_val;
+ GstStructure *structure;
+
+ g_return_if_fail (GST_IS_MESSAGE (message));
+ g_return_if_fail (GST_MESSAGE_TYPE (message) == GST_MESSAGE_WARNING);
+
+ structure = GST_MESSAGE_STRUCTURE (message);
+ error_gvalue = gst_structure_id_get_value (structure, GST_QUARK (GERROR));
+ g_return_if_fail (error_gvalue != NULL);
+ g_return_if_fail (G_VALUE_TYPE (error_gvalue) == GST_TYPE_G_ERROR);
+
+ error_val = (GError *) g_value_get_boxed (error_gvalue);
+ if (error_val)
+ *gerror = g_error_copy (error_val);
+ else
+ *gerror = NULL;
+
+ if (debug)
+ *debug =
+ g_value_dup_string (gst_structure_id_get_value (structure,
+ GST_QUARK (DEBUG)));
+}
+
+/**
+ * gst_message_parse_info:
+ * @message: A valid #GstMessage of type GST_MESSAGE_INFO.
+ * @gerror: (out) (allow-none) (transfer full): location for the GError
+ * @debug: (out) (allow-none) (transfer full): location for the debug message,
+ * or NULL
+ *
+ * Extracts the GError and debug string from the GstMessage. The values returned
+ * in the output arguments are copies; the caller must free them when done.
+ *
+ * MT safe.
+ *
+ * Since: 0.10.12
+ */
+void
+gst_message_parse_info (GstMessage * message, GError ** gerror, gchar ** debug)
+{
+ const GValue *error_gvalue;
+ GError *error_val;
+ GstStructure *structure;
+
+ g_return_if_fail (GST_IS_MESSAGE (message));
+ g_return_if_fail (GST_MESSAGE_TYPE (message) == GST_MESSAGE_INFO);
+
+ structure = GST_MESSAGE_STRUCTURE (message);
+ error_gvalue = gst_structure_id_get_value (structure, GST_QUARK (GERROR));
+ g_return_if_fail (error_gvalue != NULL);
+ g_return_if_fail (G_VALUE_TYPE (error_gvalue) == GST_TYPE_G_ERROR);
+
+ error_val = (GError *) g_value_get_boxed (error_gvalue);
+ if (error_val)
+ *gerror = g_error_copy (error_val);
+ else
+ *gerror = NULL;
+
+ if (debug)
+ *debug =
+ g_value_dup_string (gst_structure_id_get_value (structure,
+ GST_QUARK (DEBUG)));
+}
+
+/**
+ * gst_message_parse_segment_start:
+ * @message: A valid #GstMessage of type GST_MESSAGE_SEGMENT_START.
+ * @format: (out): Result location for the format, or NULL
+ * @position: (out): Result location for the position, or NULL
+ *
+ * Extracts the position and format from the segment start message.
+ *
+ * MT safe.
+ */
+void
+gst_message_parse_segment_start (GstMessage * message, GstFormat * format,
+ gint64 * position)
+{
+ GstStructure *structure;
+
+ g_return_if_fail (GST_IS_MESSAGE (message));
+ g_return_if_fail (GST_MESSAGE_TYPE (message) == GST_MESSAGE_SEGMENT_START);
+
+ structure = GST_MESSAGE_STRUCTURE (message);
+ if (format)
+ *format = (GstFormat)
+ g_value_get_enum (gst_structure_id_get_value (structure,
+ GST_QUARK (FORMAT)));
+ if (position)
+ *position =
+ g_value_get_int64 (gst_structure_id_get_value (structure,
+ GST_QUARK (POSITION)));
+}
+
+/**
+ * gst_message_parse_segment_done:
+ * @message: A valid #GstMessage of type GST_MESSAGE_SEGMENT_DONE.
+ * @format: (out): Result location for the format, or NULL
+ * @position: (out): Result location for the position, or NULL
+ *
+ * Extracts the position and format from the segment start message.
+ *
+ * MT safe.
+ */
+void
+gst_message_parse_segment_done (GstMessage * message, GstFormat * format,
+ gint64 * position)
+{
+ GstStructure *structure;
+
+ g_return_if_fail (GST_IS_MESSAGE (message));
+ g_return_if_fail (GST_MESSAGE_TYPE (message) == GST_MESSAGE_SEGMENT_DONE);
+
+ structure = GST_MESSAGE_STRUCTURE (message);
+ if (format)
+ *format = (GstFormat)
+ g_value_get_enum (gst_structure_id_get_value (structure,
+ GST_QUARK (FORMAT)));
+ if (position)
+ *position =
+ g_value_get_int64 (gst_structure_id_get_value (structure,
+ GST_QUARK (POSITION)));
+}
+
+/**
+ * gst_message_parse_duration:
+ * @message: A valid #GstMessage of type GST_MESSAGE_DURATION.
+ * @format: (out): Result location for the format, or NULL
+ * @duration: (out): Result location for the duration, or NULL
+ *
+ * Extracts the duration and format from the duration message. The duration
+ * might be GST_CLOCK_TIME_NONE, which indicates that the duration has
+ * changed. Applications should always use a query to retrieve the duration
+ * of a pipeline.
+ *
+ * MT safe.
+ */
+void
+gst_message_parse_duration (GstMessage * message, GstFormat * format,
+ gint64 * duration)
+{
+ GstStructure *structure;
+
+ g_return_if_fail (GST_IS_MESSAGE (message));
+ g_return_if_fail (GST_MESSAGE_TYPE (message) == GST_MESSAGE_DURATION);
+
+ structure = GST_MESSAGE_STRUCTURE (message);
+ if (format)
+ *format = (GstFormat)
+ g_value_get_enum (gst_structure_id_get_value (structure,
+ GST_QUARK (FORMAT)));
+ if (duration)
+ *duration =
+ g_value_get_int64 (gst_structure_id_get_value (structure,
+ GST_QUARK (DURATION)));
+}
+
+/**
+ * gst_message_parse_async_done:
+ * @message: A valid #GstMessage of type GST_MESSAGE_ASYNC_DONE.
+ * @reset_time: (out): Result location for the reset_time or NULL
+ *
+ * Extract the reset_time from the async_done message.
+ *
+ * MT safe.
+ */
+void
+gst_message_parse_async_done (GstMessage * message, gboolean * reset_time)
+{
+ GstStructure *structure;
+
+ g_return_if_fail (GST_IS_MESSAGE (message));
+ g_return_if_fail (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ASYNC_DONE);
+
+ structure = GST_MESSAGE_STRUCTURE (message);
+ if (reset_time)
+ *reset_time =
+ g_value_get_boolean (gst_structure_id_get_value (structure,
+ GST_QUARK (RESET_TIME)));
+}
+
+/**
+ * gst_message_parse_request_state:
+ * @message: A valid #GstMessage of type GST_MESSAGE_REQUEST_STATE.
+ * @state: (out): Result location for the requested state or NULL
+ *
+ * Extract the requested state from the request_state message.
+ *
+ * MT safe.
+ *
+ * Since: 0.10.23
+ */
+void
+gst_message_parse_request_state (GstMessage * message, GstState * state)
+{
+ GstStructure *structure;
+
+ g_return_if_fail (GST_IS_MESSAGE (message));
+ g_return_if_fail (GST_MESSAGE_TYPE (message) == GST_MESSAGE_REQUEST_STATE);
+
+ structure = GST_MESSAGE_STRUCTURE (message);
+ if (state)
+ *state = (GstState)
+ g_value_get_enum (gst_structure_id_get_value (structure,
+ GST_QUARK (NEW_STATE)));
+}
+
+/**
+ * gst_message_new_stream_status:
+ * @src: The object originating the message.
+ * @type: The stream status type.
+ * @owner: (transfer none): the owner element of @src.
+ *
+ * Create a new stream status message. This message is posted when a streaming
+ * thread is created/destroyed or when the state changed.
+ *
+ * Returns: (transfer full): the new stream status message.
+ *
+ * MT safe.
+ *
+ * Since: 0.10.24.
+ */
+GstMessage *
+gst_message_new_stream_status (GstObject * src, GstStreamStatusType type,
+ GstElement * owner)
+{
+ GstMessage *message;
+ GstStructure *structure;
+
+ structure = gst_structure_id_new (GST_QUARK (MESSAGE_STREAM_STATUS),
+ GST_QUARK (TYPE), GST_TYPE_STREAM_STATUS_TYPE, (gint) type,
+ GST_QUARK (OWNER), GST_TYPE_ELEMENT, owner, NULL);
+ message = gst_message_new_custom (GST_MESSAGE_STREAM_STATUS, src, structure);
+
+ return message;
+}
+
+/**
+ * gst_message_parse_stream_status:
+ * @message: A valid #GstMessage of type GST_MESSAGE_STREAM_STATUS.
+ * @type: (out): A pointer to hold the status type
+ * @owner: (out) (transfer none): The owner element of the message source
+ *
+ * Extracts the stream status type and owner the GstMessage. The returned
+ * owner remains valid for as long as the reference to @message is valid and
+ * should thus not be unreffed.
+ *
+ * MT safe.
+ *
+ * Since: 0.10.24.
+ */
+void
+gst_message_parse_stream_status (GstMessage * message,
+ GstStreamStatusType * type, GstElement ** owner)
+{
+ const GValue *owner_gvalue;
+ GstStructure *structure;
+
+ g_return_if_fail (GST_IS_MESSAGE (message));
+ g_return_if_fail (GST_MESSAGE_TYPE (message) == GST_MESSAGE_STREAM_STATUS);
+
+ structure = GST_MESSAGE_STRUCTURE (message);
+ owner_gvalue = gst_structure_id_get_value (structure, GST_QUARK (OWNER));
+ g_return_if_fail (owner_gvalue != NULL);
+
+ if (type)
+ *type = (GstStreamStatusType)
+ g_value_get_enum (gst_structure_id_get_value (structure,
+ GST_QUARK (TYPE)));
+ if (owner)
+ *owner = (GstElement *) g_value_get_object (owner_gvalue);
+}
+
+/**
+ * gst_message_set_stream_status_object:
+ * @message: A valid #GstMessage of type GST_MESSAGE_STREAM_STATUS.
+ * @object: the object controlling the streaming
+ *
+ * Configures the object handling the streaming thread. This is usually a
+ * GstTask object but other objects might be added in the future.
+ *
+ * Since: 0.10.24
+ */
+void
+gst_message_set_stream_status_object (GstMessage * message,
+ const GValue * object)
+{
+ GstStructure *structure;
+
+ g_return_if_fail (GST_IS_MESSAGE (message));
+ g_return_if_fail (GST_MESSAGE_TYPE (message) == GST_MESSAGE_STREAM_STATUS);
+
+ structure = GST_MESSAGE_STRUCTURE (message);
+ gst_structure_id_set_value (structure, GST_QUARK (OBJECT), object);
+}
+
+/**
+ * gst_message_get_stream_status_object:
+ * @message: A valid #GstMessage of type GST_MESSAGE_STREAM_STATUS.
+ *
+ * Extracts the object managing the streaming thread from @message.
+ *
+ * Returns: a GValue containing the object that manages the streaming thread.
+ * This object is usually of type GstTask but other types can be added in the
+ * future. The object remains valid as long as @message is valid.
+ *
+ * Since: 0.10.24
+ */
+const GValue *
+gst_message_get_stream_status_object (GstMessage * message)
+{
+ const GValue *result;
+ GstStructure *structure;
+
+ g_return_val_if_fail (GST_IS_MESSAGE (message), NULL);
+ g_return_val_if_fail (GST_MESSAGE_TYPE (message) == GST_MESSAGE_STREAM_STATUS,
+ NULL);
+
+ structure = GST_MESSAGE_STRUCTURE (message);
+ result = gst_structure_id_get_value (structure, GST_QUARK (OBJECT));
+
+ return result;
+}
+
+/**
+ * gst_message_new_step_done:
+ * @src: The object originating the message.
+ * @format: the format of @amount
+ * @amount: the amount of stepped data
+ * @rate: the rate of the stepped amount
+ * @flush: is this an flushing step
+ * @intermediate: is this an intermediate step
+ * @duration: the duration of the data
+ * @eos: the step caused EOS
+ *
+ * This message is posted by elements when they complete a part, when @intermediate set
+ * to TRUE, or a complete step operation.
+ *
+ * @duration will contain the amount of time (in GST_FORMAT_TIME) of the stepped
+ * @amount of media in format @format.
+ *
+ * Returns: (transfer full): the new step_done message.
+ *
+ * MT safe.
+ *
+ * Since: 0.10.24
+ */
+GstMessage *
+gst_message_new_step_done (GstObject * src, GstFormat format, guint64 amount,
+ gdouble rate, gboolean flush, gboolean intermediate, guint64 duration,
+ gboolean eos)
+{
+ GstMessage *message;
+ GstStructure *structure;
+
+ structure = gst_structure_id_new (GST_QUARK (MESSAGE_STEP_DONE),
+ GST_QUARK (FORMAT), GST_TYPE_FORMAT, format,
+ GST_QUARK (AMOUNT), G_TYPE_UINT64, amount,
+ GST_QUARK (RATE), G_TYPE_DOUBLE, rate,
+ GST_QUARK (FLUSH), G_TYPE_BOOLEAN, flush,
+ GST_QUARK (INTERMEDIATE), G_TYPE_BOOLEAN, intermediate,
+ GST_QUARK (DURATION), G_TYPE_UINT64, duration,
+ GST_QUARK (EOS), G_TYPE_BOOLEAN, eos, NULL);
+ message = gst_message_new_custom (GST_MESSAGE_STEP_DONE, src, structure);
+
+ return message;
+}
+
+/**
+ * gst_message_parse_step_done:
+ * @message: A valid #GstMessage of type GST_MESSAGE_STEP_DONE.
+ * @format: (out) (allow-none): result location for the format
+ * @amount: (out) (allow-none): result location for the amount
+ * @rate: (out) (allow-none): result location for the rate
+ * @flush: (out) (allow-none): result location for the flush flag
+ * @intermediate: (out) (allow-none): result location for the intermediate flag
+ * @duration: (out) (allow-none): result location for the duration
+ * @eos: (out) (allow-none): result location for the EOS flag
+ *
+ * Extract the values the step_done message.
+ *
+ * MT safe.
+ *
+ * Since: 0.10.24
+ */
+void
+gst_message_parse_step_done (GstMessage * message, GstFormat * format,
+ guint64 * amount, gdouble * rate, gboolean * flush, gboolean * intermediate,
+ guint64 * duration, gboolean * eos)
+{
+ GstStructure *structure;
+
+ g_return_if_fail (GST_IS_MESSAGE (message));
+ g_return_if_fail (GST_MESSAGE_TYPE (message) == GST_MESSAGE_STEP_DONE);
+
+ structure = GST_MESSAGE_STRUCTURE (message);
+ gst_structure_id_get (structure,
+ GST_QUARK (FORMAT), GST_TYPE_FORMAT, format,
+ GST_QUARK (AMOUNT), G_TYPE_UINT64, amount,
+ GST_QUARK (RATE), G_TYPE_DOUBLE, rate,
+ GST_QUARK (FLUSH), G_TYPE_BOOLEAN, flush,
+ GST_QUARK (INTERMEDIATE), G_TYPE_BOOLEAN, intermediate,
+ GST_QUARK (DURATION), G_TYPE_UINT64, duration,
+ GST_QUARK (EOS), G_TYPE_BOOLEAN, eos, NULL);
+}
+
+/**
+ * gst_message_new_step_start:
+ * @src: The object originating the message.
+ * @active: if the step is active or queued
+ * @format: the format of @amount
+ * @amount: the amount of stepped data
+ * @rate: the rate of the stepped amount
+ * @flush: is this an flushing step
+ * @intermediate: is this an intermediate step
+ *
+ * This message is posted by elements when they accept or activate a new step
+ * event for @amount in @format.
+ *
+ * @active is set to FALSE when the element accepted the new step event and has
+ * queued it for execution in the streaming threads.
+ *
+ * @active is set to TRUE when the element has activated the step operation and
+ * is now ready to start executing the step in the streaming thread. After this
+ * message is emited, the application can queue a new step operation in the
+ * element.
+ *
+ * Returns: (transfer full): The new step_start message.
+ *
+ * MT safe.
+ *
+ * Since: 0.10.24
+ */
+GstMessage *
+gst_message_new_step_start (GstObject * src, gboolean active, GstFormat format,
+ guint64 amount, gdouble rate, gboolean flush, gboolean intermediate)
+{
+ GstMessage *message;
+ GstStructure *structure;
+
+ structure = gst_structure_id_new (GST_QUARK (MESSAGE_STEP_START),
+ GST_QUARK (ACTIVE), G_TYPE_BOOLEAN, active,
+ GST_QUARK (FORMAT), GST_TYPE_FORMAT, format,
+ GST_QUARK (AMOUNT), G_TYPE_UINT64, amount,
+ GST_QUARK (RATE), G_TYPE_DOUBLE, rate,
+ GST_QUARK (FLUSH), G_TYPE_BOOLEAN, flush,
+ GST_QUARK (INTERMEDIATE), G_TYPE_BOOLEAN, intermediate, NULL);
+ message = gst_message_new_custom (GST_MESSAGE_STEP_START, src, structure);
+
+ return message;
+}
+
+/**
+ * gst_message_parse_step_start:
+ * @message: A valid #GstMessage of type GST_MESSAGE_STEP_DONE.
+ * @active: (out) (allow-none): result location for the active flag
+ * @format: (out) (allow-none): result location for the format
+ * @amount: (out) (allow-none): result location for the amount
+ * @rate: (out) (allow-none): result location for the rate
+ * @flush: (out) (allow-none): result location for the flush flag
+ * @intermediate: (out) (allow-none): result location for the intermediate flag
+ *
+ * Extract the values from step_start message.
+ *
+ * MT safe.
+ *
+ * Since: 0.10.24
+ */
+void
+gst_message_parse_step_start (GstMessage * message, gboolean * active,
+ GstFormat * format, guint64 * amount, gdouble * rate, gboolean * flush,
+ gboolean * intermediate)
+{
+ GstStructure *structure;
+
+ g_return_if_fail (GST_IS_MESSAGE (message));
+ g_return_if_fail (GST_MESSAGE_TYPE (message) == GST_MESSAGE_STEP_START);
+
+ structure = GST_MESSAGE_STRUCTURE (message);
+ gst_structure_id_get (structure,
+ GST_QUARK (ACTIVE), G_TYPE_BOOLEAN, active,
+ GST_QUARK (FORMAT), GST_TYPE_FORMAT, format,
+ GST_QUARK (AMOUNT), G_TYPE_UINT64, amount,
+ GST_QUARK (RATE), G_TYPE_DOUBLE, rate,
+ GST_QUARK (FLUSH), G_TYPE_BOOLEAN, flush,
+ GST_QUARK (INTERMEDIATE), G_TYPE_BOOLEAN, intermediate, NULL);
+}
+
+/**
+ * gst_message_new_qos:
+ * @src: The object originating the message.
+ * @live: if the message was generated by a live element
+ * @running_time: the running time of the buffer that generated the message
+ * @stream_time: the stream time of the buffer that generated the message
+ * @timestamp: the timestamps of the buffer that generated the message
+ * @duration: the duration of the buffer that generated the message
+ *
+ * A QOS message is posted on the bus whenever an element decides to drop a
+ * buffer because of QoS reasons or whenever it changes its processing strategy
+ * because of QoS reasons (quality adjustments such as processing at lower
+ * accuracy).
+ *
+ * This message can be posted by an element that performs synchronisation against the
+ * clock (live) or it could be dropped by an element that performs QoS because of QOS
+ * events received from a downstream element (!live).
+ *
+ * @running_time, @stream_time, @timestamp, @duration should be set to the
+ * respective running-time, stream-time, timestamp and duration of the (dropped)
+ * buffer that generated the QoS event. Values can be left to
+ * GST_CLOCK_TIME_NONE when unknown.
+ *
+ * Returns: (transfer full): The new qos message.
+ *
+ * MT safe.
+ *
+ * Since: 0.10.29
+ */
+GstMessage *
+gst_message_new_qos (GstObject * src, gboolean live, guint64 running_time,
+ guint64 stream_time, guint64 timestamp, guint64 duration)
+{
+ GstMessage *message;
+ GstStructure *structure;
+
+ structure = gst_structure_id_new (GST_QUARK (MESSAGE_QOS),
+ GST_QUARK (LIVE), G_TYPE_BOOLEAN, live,
+ GST_QUARK (RUNNING_TIME), G_TYPE_UINT64, running_time,
+ GST_QUARK (STREAM_TIME), G_TYPE_UINT64, stream_time,
+ GST_QUARK (TIMESTAMP), G_TYPE_UINT64, timestamp,
+ GST_QUARK (DURATION), G_TYPE_UINT64, duration,
+ GST_QUARK (JITTER), G_TYPE_INT64, (gint64) 0,
+ GST_QUARK (PROPORTION), G_TYPE_DOUBLE, (gdouble) 1.0,
+ GST_QUARK (QUALITY), G_TYPE_INT, (gint) 1000000,
+ GST_QUARK (FORMAT), GST_TYPE_FORMAT, GST_FORMAT_UNDEFINED,
+ GST_QUARK (PROCESSED), G_TYPE_UINT64, (guint64) - 1,
+ GST_QUARK (DROPPED), G_TYPE_UINT64, (guint64) - 1, NULL);
+ message = gst_message_new_custom (GST_MESSAGE_QOS, src, structure);
+
+ return message;
+}
+
+/**
+ * gst_message_set_qos_values:
+ * @message: A valid #GstMessage of type GST_MESSAGE_QOS.
+ * @jitter: The difference of the running-time against the deadline.
+ * @proportion: Long term prediction of the ideal rate relative to normal rate
+ * to get optimal quality.
+ * @quality: An element dependent integer value that specifies the current
+ * quality level of the element. The default maximum quality is 1000000.
+ *
+ * Set the QoS values that have been calculated/analysed from the QoS data
+ *
+ * MT safe.
+ *
+ * Since: 0.10.29
+ */
+void
+gst_message_set_qos_values (GstMessage * message, gint64 jitter,
+ gdouble proportion, gint quality)
+{
+ GstStructure *structure;
+
+ g_return_if_fail (GST_IS_MESSAGE (message));
+ g_return_if_fail (GST_MESSAGE_TYPE (message) == GST_MESSAGE_QOS);
+
+ structure = GST_MESSAGE_STRUCTURE (message);
+ gst_structure_id_set (structure,
+ GST_QUARK (JITTER), G_TYPE_INT64, jitter,
+ GST_QUARK (PROPORTION), G_TYPE_DOUBLE, proportion,
+ GST_QUARK (QUALITY), G_TYPE_INT, quality, NULL);
+}
+
+/**
+ * gst_message_set_qos_stats:
+ * @message: A valid #GstMessage of type GST_MESSAGE_QOS.
+ * @format: Units of the 'processed' and 'dropped' fields. Video sinks and video
+ * filters will use GST_FORMAT_BUFFERS (frames). Audio sinks and audio filters
+ * will likely use GST_FORMAT_DEFAULT (samples).
+ * @processed: Total number of units correctly processed since the last state
+ * change to READY or a flushing operation.
+ * @dropped: Total number of units dropped since the last state change to READY
+ * or a flushing operation.
+ *
+ * Set the QoS stats representing the history of the current continuous pipeline
+ * playback period.
+ *
+ * When @format is @GST_FORMAT_UNDEFINED both @dropped and @processed are
+ * invalid. Values of -1 for either @processed or @dropped mean unknown values.
+ *
+ * MT safe.
+ *
+ * Since: 0.10.29
+ */
+void
+gst_message_set_qos_stats (GstMessage * message, GstFormat format,
+ guint64 processed, guint64 dropped)
+{
+ GstStructure *structure;
+
+ g_return_if_fail (GST_IS_MESSAGE (message));
+ g_return_if_fail (GST_MESSAGE_TYPE (message) == GST_MESSAGE_QOS);
+
+ structure = GST_MESSAGE_STRUCTURE (message);
+ gst_structure_id_set (structure,
+ GST_QUARK (FORMAT), GST_TYPE_FORMAT, format,
+ GST_QUARK (PROCESSED), G_TYPE_UINT64, processed,
+ GST_QUARK (DROPPED), G_TYPE_UINT64, dropped, NULL);
+}
+
+/**
+ * gst_message_parse_qos:
+ * @message: A valid #GstMessage of type GST_MESSAGE_QOS.
+ * @live: (out) (allow-none): if the message was generated by a live element
+ * @running_time: (out) (allow-none): the running time of the buffer that
+ * generated the message
+ * @stream_time: (out) (allow-none): the stream time of the buffer that
+ * generated the message
+ * @timestamp: (out) (allow-none): the timestamps of the buffer that
+ * generated the message
+ * @duration: (out) (allow-none): the duration of the buffer that
+ * generated the message
+ *
+ * Extract the timestamps and live status from the QoS message.
+ *
+ * The returned values give the running_time, stream_time, timestamp and
+ * duration of the dropped buffer. Values of GST_CLOCK_TIME_NONE mean unknown
+ * values.
+ *
+ * MT safe.
+ *
+ * Since: 0.10.29
+ */
+void
+gst_message_parse_qos (GstMessage * message, gboolean * live,
+ guint64 * running_time, guint64 * stream_time, guint64 * timestamp,
+ guint64 * duration)
+{
+ GstStructure *structure;
+
+ g_return_if_fail (GST_IS_MESSAGE (message));
+ g_return_if_fail (GST_MESSAGE_TYPE (message) == GST_MESSAGE_QOS);
+
+ structure = GST_MESSAGE_STRUCTURE (message);
+ gst_structure_id_get (structure,
+ GST_QUARK (LIVE), G_TYPE_BOOLEAN, live,
+ GST_QUARK (RUNNING_TIME), G_TYPE_UINT64, running_time,
+ GST_QUARK (STREAM_TIME), G_TYPE_UINT64, stream_time,
+ GST_QUARK (TIMESTAMP), G_TYPE_UINT64, timestamp,
+ GST_QUARK (DURATION), G_TYPE_UINT64, duration, NULL);
+}
+
+/**
+ * gst_message_parse_qos_values:
+ * @message: A valid #GstMessage of type GST_MESSAGE_QOS.
+ * @jitter: (out) (allow-none): The difference of the running-time against
+ * the deadline.
+ * @proportion: (out) (allow-none): Long term prediction of the ideal rate
+ * relative to normal rate to get optimal quality.
+ * @quality: (out) (allow-none): An element dependent integer value that
+ * specifies the current quality level of the element. The default
+ * maximum quality is 1000000.
+ *
+ * Extract the QoS values that have been calculated/analysed from the QoS data
+ *
+ * MT safe.
+ *
+ * Since: 0.10.29
+ */
+void
+gst_message_parse_qos_values (GstMessage * message, gint64 * jitter,
+ gdouble * proportion, gint * quality)
+{
+ GstStructure *structure;
+
+ g_return_if_fail (GST_IS_MESSAGE (message));
+ g_return_if_fail (GST_MESSAGE_TYPE (message) == GST_MESSAGE_QOS);
+
+ structure = GST_MESSAGE_STRUCTURE (message);
+ gst_structure_id_get (structure,
+ GST_QUARK (JITTER), G_TYPE_INT64, jitter,
+ GST_QUARK (PROPORTION), G_TYPE_DOUBLE, proportion,
+ GST_QUARK (QUALITY), G_TYPE_INT, quality, NULL);
+}
+
+/**
+ * gst_message_parse_qos_stats:
+ * @message: A valid #GstMessage of type GST_MESSAGE_QOS.
+ * @format: (out) (allow-none): Units of the 'processed' and 'dropped' fields.
+ * Video sinks and video filters will use GST_FORMAT_BUFFERS (frames).
+ * Audio sinks and audio filters will likely use GST_FORMAT_DEFAULT
+ * (samples).
+ * @processed: (out) (allow-none): Total number of units correctly processed
+ * since the last state change to READY or a flushing operation.
+ * @dropped: (out) (allow-none): Total number of units dropped since the last
+ * state change to READY or a flushing operation.
+ *
+ * Extract the QoS stats representing the history of the current continuous
+ * pipeline playback period.
+ *
+ * When @format is @GST_FORMAT_UNDEFINED both @dropped and @processed are
+ * invalid. Values of -1 for either @processed or @dropped mean unknown values.
+ *
+ * MT safe.
+ *
+ * Since: 0.10.29
+ */
+void
+gst_message_parse_qos_stats (GstMessage * message, GstFormat * format,
+ guint64 * processed, guint64 * dropped)
+{
+ GstStructure *structure;
+
+ g_return_if_fail (GST_IS_MESSAGE (message));
+ g_return_if_fail (GST_MESSAGE_TYPE (message) == GST_MESSAGE_QOS);
+
+ structure = GST_MESSAGE_STRUCTURE (message);
+ gst_structure_id_get (structure,
+ GST_QUARK (FORMAT), GST_TYPE_FORMAT, format,
+ GST_QUARK (PROCESSED), G_TYPE_UINT64, processed,
+ GST_QUARK (DROPPED), G_TYPE_UINT64, dropped, NULL);
+}
+
+/**
+ * gst_message_new_progress:
+ * @src: The object originating the message.
+ * @type: a #GstProgressType
+ * @code: a progress code
+ * @text: free, user visible text describing the progress
+ *
+ * Progress messages are posted by elements when they use an asynchronous task
+ * to perform actions triggered by a state change.
+ *
+ * @code contains a well defined string describing the action.
+ * @test should contain a user visible string detailing the current action.
+ *
+ * Returns: (transfer full): The new qos message.
+ *
+ * Since: 0.10.33
+ */
+GstMessage *
+gst_message_new_progress (GstObject * src, GstProgressType type,
+ const gchar * code, const gchar * text)
+{
+ GstMessage *message;
+ GstStructure *structure;
+ gint percent = 100, timeout = -1;
+
+ g_return_val_if_fail (code != NULL, NULL);
+ g_return_val_if_fail (text != NULL, NULL);
+
+ if (type == GST_PROGRESS_TYPE_START || type == GST_PROGRESS_TYPE_CONTINUE)
+ percent = 0;
+
+ structure = gst_structure_id_new (GST_QUARK (MESSAGE_PROGRESS),
+ GST_QUARK (TYPE), GST_TYPE_PROGRESS_TYPE, type,
+ GST_QUARK (CODE), G_TYPE_STRING, code,
+ GST_QUARK (TEXT), G_TYPE_STRING, text,
+ GST_QUARK (PERCENT), G_TYPE_INT, percent,
+ GST_QUARK (TIMEOUT), G_TYPE_INT, timeout, NULL);
+ message = gst_message_new_custom (GST_MESSAGE_PROGRESS, src, structure);
+
+ return message;
+}
+
+/**
+ * gst_message_parse_progress:
+ * @message: A valid #GstMessage of type GST_MESSAGE_PROGRESS.
+ * @type: (out) (allow-none): location for the type
+ * @code: (out) (allow-none) (transfer full): location for the code
+ * @text: (out) (allow-none) (transfer full): location for the text
+ *
+ * Parses the progress @type, @code and @text.
+ *
+ * Since: 0.10.33
+ */
+void
+gst_message_parse_progress (GstMessage * message, GstProgressType * type,
+ gchar ** code, gchar ** text)
+{
+ GstStructure *structure;
+
+ g_return_if_fail (GST_IS_MESSAGE (message));
+ g_return_if_fail (GST_MESSAGE_TYPE (message) == GST_MESSAGE_PROGRESS);
+
+ structure = GST_MESSAGE_STRUCTURE (message);
+ gst_structure_id_get (structure,
+ GST_QUARK (TYPE), GST_TYPE_PROGRESS_TYPE, type,
+ GST_QUARK (CODE), G_TYPE_STRING, code,
+ GST_QUARK (TEXT), G_TYPE_STRING, text, NULL);
+}
diff --git a/gst/gstmessage.h b/gst/gstmessage.h
new file mode 100644
index 0000000..050be67
--- /dev/null
+++ b/gst/gstmessage.h
@@ -0,0 +1,549 @@
+/* GStreamer
+ * Copyright (C) 2004 Wim Taymans <wim@fluendo.com>
+ *
+ * gstmessage.h: Header for GstMessage subsystem
+ *
+ * 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_MESSAGE_H__
+#define __GST_MESSAGE_H__
+
+G_BEGIN_DECLS
+
+typedef struct _GstMessage GstMessage;
+
+/**
+ * GstMessageType:
+ * @GST_MESSAGE_UNKNOWN: an undefined message
+ * @GST_MESSAGE_EOS: end-of-stream reached in a pipeline. The application will
+ * only receive this message in the PLAYING state and every time it sets a
+ * pipeline to PLAYING that is in the EOS state. The application can perform a
+ * flushing seek in the pipeline, which will undo the EOS state again.
+ * @GST_MESSAGE_ERROR: an error occured. When the application receives an error
+ * message it should stop playback of the pipeline and not assume that more
+ * data will be played.
+ * @GST_MESSAGE_WARNING: a warning occured.
+ * @GST_MESSAGE_INFO: an info message occured
+ * @GST_MESSAGE_TAG: a tag was found.
+ * @GST_MESSAGE_BUFFERING: the pipeline is buffering. When the application
+ * receives a buffering message in the PLAYING state for a non-live pipeline it
+ * must PAUSE the pipeline until the buffering completes, when the percentage
+ * field in the message is 100%. For live pipelines, no action must be
+ * performed and the buffering percentage can be used to inform the user about
+ * the progress.
+ * @GST_MESSAGE_STATE_CHANGED: a state change happened
+ * @GST_MESSAGE_STATE_DIRTY: an element changed state in a streaming thread.
+ * This message is deprecated.
+ * @GST_MESSAGE_STEP_DONE: a stepping operation finished.
+ * @GST_MESSAGE_CLOCK_PROVIDE: an element notifies its capability of providing
+ * a clock. This message is used internally and
+ * never forwarded to the application.
+ * @GST_MESSAGE_CLOCK_LOST: The current clock as selected by the pipeline became
+ * unusable. The pipeline will select a new clock on
+ * the next PLAYING state change. The application
+ * should set the pipeline to PAUSED and back to
+ * PLAYING when this message is received.
+ * @GST_MESSAGE_NEW_CLOCK: a new clock was selected in the pipeline.
+ * @GST_MESSAGE_STRUCTURE_CHANGE: the structure of the pipeline changed. This
+ * message is used internally and never forwarded to the application.
+ * @GST_MESSAGE_STREAM_STATUS: status about a stream, emitted when it starts,
+ * stops, errors, etc..
+ * @GST_MESSAGE_APPLICATION: message posted by the application, possibly
+ * via an application-specific element.
+ * @GST_MESSAGE_ELEMENT: element-specific message, see the specific element's
+ * documentation
+ * @GST_MESSAGE_SEGMENT_START: pipeline started playback of a segment. This
+ * message is used internally and never forwarded to the application.
+ * @GST_MESSAGE_SEGMENT_DONE: pipeline completed playback of a segment. This
+ * message is forwarded to the application after all elements that posted
+ * @GST_MESSAGE_SEGMENT_START posted a GST_MESSAGE_SEGMENT_DONE message.
+ * @GST_MESSAGE_DURATION: The duration of a pipeline changed. The application
+ * can get the new duration with a duration query.
+ * @GST_MESSAGE_ASYNC_START: Posted by elements when they start an ASYNC
+ * #GstStateChange. This message is not forwarded to the application but is used
+ * internally. Since: 0.10.13.
+ * @GST_MESSAGE_ASYNC_DONE: Posted by elements when they complete an ASYNC
+ * #GstStateChange. The application will only receive this message from the toplevel
+ * pipeline. Since: 0.10.13
+ * @GST_MESSAGE_LATENCY: Posted by elements when their latency changes. The
+ * application should recalculate and distribute a new latency. Since: 0.10.12
+ * @GST_MESSAGE_REQUEST_STATE: Posted by elements when they want the pipeline to
+ * change state. This message is a suggestion to the application which can
+ * decide to perform the state change on (part of) the pipeline. Since: 0.10.23.
+ * @GST_MESSAGE_STEP_START: A stepping operation was started. Since: 0.10.24
+ * @GST_MESSAGE_QOS: A buffer was dropped or an element changed its processing
+ * strategy for Quality of Service reasons. Since: 0.10.29
+ * @GST_MESSAGE_PROGRESS: A progress message. Since: 0.10.33
+ * @GST_MESSAGE_ANY: mask for all of the above messages.
+ *
+ * The different message types that are available.
+ */
+/* NOTE: keep in sync with quark registration in gstmessage.c
+ * NOTE: keep GST_MESSAGE_ANY a valid gint to avoid compiler warnings.
+ */
+typedef enum
+{
+ GST_MESSAGE_UNKNOWN = 0,
+ GST_MESSAGE_EOS = (1 << 0),
+ GST_MESSAGE_ERROR = (1 << 1),
+ GST_MESSAGE_WARNING = (1 << 2),
+ GST_MESSAGE_INFO = (1 << 3),
+ GST_MESSAGE_TAG = (1 << 4),
+ GST_MESSAGE_BUFFERING = (1 << 5),
+ GST_MESSAGE_STATE_CHANGED = (1 << 6),
+ GST_MESSAGE_STATE_DIRTY = (1 << 7),
+ GST_MESSAGE_STEP_DONE = (1 << 8),
+ GST_MESSAGE_CLOCK_PROVIDE = (1 << 9),
+ GST_MESSAGE_CLOCK_LOST = (1 << 10),
+ GST_MESSAGE_NEW_CLOCK = (1 << 11),
+ GST_MESSAGE_STRUCTURE_CHANGE = (1 << 12),
+ GST_MESSAGE_STREAM_STATUS = (1 << 13),
+ GST_MESSAGE_APPLICATION = (1 << 14),
+ GST_MESSAGE_ELEMENT = (1 << 15),
+ GST_MESSAGE_SEGMENT_START = (1 << 16),
+ GST_MESSAGE_SEGMENT_DONE = (1 << 17),
+ GST_MESSAGE_DURATION = (1 << 18),
+ GST_MESSAGE_LATENCY = (1 << 19),
+ GST_MESSAGE_ASYNC_START = (1 << 20),
+ GST_MESSAGE_ASYNC_DONE = (1 << 21),
+ GST_MESSAGE_REQUEST_STATE = (1 << 22),
+ GST_MESSAGE_STEP_START = (1 << 23),
+ GST_MESSAGE_QOS = (1 << 24),
+ GST_MESSAGE_PROGRESS = (1 << 25),
+ GST_MESSAGE_ANY = ~0
+} GstMessageType;
+
+#include <gst/gstminiobject.h>
+#include <gst/gstobject.h>
+#include <gst/gstelement.h>
+#include <gst/gsttaglist.h>
+#include <gst/gststructure.h>
+#include <gst/gstquery.h>
+
+/**
+ * GST_MESSAGE_TRACE_NAME:
+ *
+ * The name used for memory allocation tracing
+ */
+#define GST_MESSAGE_TRACE_NAME "GstMessage"
+
+#define GST_TYPE_MESSAGE (gst_message_get_type())
+#define GST_IS_MESSAGE(obj) (GST_IS_MINI_OBJECT_TYPE (obj, GST_TYPE_MESSAGE))
+#define GST_MESSAGE_CAST(obj) ((GstMessage*)(obj))
+#define GST_MESSAGE(obj) (GST_MESSAGE_CAST(obj))
+
+/* the lock is used to handle the synchronous handling of messages,
+ * the emiting thread is block until the handling thread processed
+ * the message using this mutex/cond pair */
+#define GST_MESSAGE_GET_LOCK(message) (GST_MESSAGE_CAST(message)->lock)
+#define GST_MESSAGE_LOCK(message) g_mutex_lock(GST_MESSAGE_GET_LOCK(message))
+#define GST_MESSAGE_UNLOCK(message) g_mutex_unlock(GST_MESSAGE_GET_LOCK(message))
+#define GST_MESSAGE_COND(message) (GST_MESSAGE_CAST(message)->cond)
+#define GST_MESSAGE_WAIT(message) g_cond_wait(GST_MESSAGE_COND(message),GST_MESSAGE_GET_LOCK(message))
+#define GST_MESSAGE_SIGNAL(message) g_cond_signal(GST_MESSAGE_COND(message))
+
+/**
+ * GST_MESSAGE_TYPE:
+ * @message: a #GstMessage
+ *
+ * Get the #GstMessageType of @message.
+ */
+#define GST_MESSAGE_TYPE(message) (GST_MESSAGE_CAST(message)->type)
+/**
+ * GST_MESSAGE_TYPE_NAME:
+ * @message: a #GstMessage
+ *
+ * Get a constant string representation of the #GstMessageType of @message.
+ *
+ * Since: 0.10.4
+ */
+#define GST_MESSAGE_TYPE_NAME(message) gst_message_type_get_name(GST_MESSAGE_TYPE(message))
+/**
+ * GST_MESSAGE_TIMESTAMP:
+ * @message: a #GstMessage
+ *
+ * Get the timestamp of @message. This is the timestamp when the message
+ * was created.
+ */
+#define GST_MESSAGE_TIMESTAMP(message) (GST_MESSAGE_CAST(message)->timestamp)
+/**
+ * GST_MESSAGE_SRC:
+ * @message: a #GstMessage
+ *
+ * Get the object that posted @message.
+ */
+#define GST_MESSAGE_SRC(message) (GST_MESSAGE_CAST(message)->src)
+
+/**
+ * GST_MESSAGE_SEQNUM:
+ * @message: a #GstMessage
+ *
+ * Get the sequence number of @message.
+ */
+#define GST_MESSAGE_SEQNUM(message) (GST_MESSAGE_CAST(message)->seqnum)
+
+/**
+ * GST_MESSAGE_SRC_NAME:
+ * @message: a #GstMessage
+ *
+ * Get the name of the object that posted @message. Returns "(NULL)" if
+ * the message has no source object set.
+ *
+ * Since: 0.10.24
+ */
+#define GST_MESSAGE_SRC_NAME(message) (GST_MESSAGE_SRC(message) ? \
+ GST_OBJECT_NAME (GST_MESSAGE_SRC(message)) : "(NULL)")
+
+/**
+ * GstStructureChangeType:
+ * @GST_STRUCTURE_CHANGE_TYPE_PAD_LINK: Pad linking is starting or done.
+ * @GST_STRUCTURE_CHANGE_TYPE_PAD_UNLINK: Pad unlinking is starting or done.
+ *
+ * The type of a %GST_MESSAGE_STRUCTURE_CHANGE.
+ *
+ * Since: 0.10.22
+ */
+typedef enum {
+ GST_STRUCTURE_CHANGE_TYPE_PAD_LINK = 0,
+ GST_STRUCTURE_CHANGE_TYPE_PAD_UNLINK = 1
+} GstStructureChangeType;
+
+/**
+ * GstStreamStatusType:
+ * @GST_STREAM_STATUS_TYPE_CREATE: A new thread need to be created.
+ * @GST_STREAM_STATUS_TYPE_ENTER: a thread entered its loop function
+ * @GST_STREAM_STATUS_TYPE_LEAVE: a thread left its loop function
+ * @GST_STREAM_STATUS_TYPE_DESTROY: a thread is destroyed
+ * @GST_STREAM_STATUS_TYPE_START: a thread is started
+ * @GST_STREAM_STATUS_TYPE_PAUSE: a thread is paused
+ * @GST_STREAM_STATUS_TYPE_STOP: a thread is stopped
+ *
+ * The type of a %GST_MESSAGE_STREAM_STATUS. The stream status messages inform the
+ * application of new streaming threads and their status.
+ *
+ * Since: 0.10.24
+ */
+typedef enum {
+ GST_STREAM_STATUS_TYPE_CREATE = 0,
+ GST_STREAM_STATUS_TYPE_ENTER = 1,
+ GST_STREAM_STATUS_TYPE_LEAVE = 2,
+ GST_STREAM_STATUS_TYPE_DESTROY = 3,
+
+ GST_STREAM_STATUS_TYPE_START = 8,
+ GST_STREAM_STATUS_TYPE_PAUSE = 9,
+ GST_STREAM_STATUS_TYPE_STOP = 10
+} GstStreamStatusType;
+
+/**
+ * GstProgressType:
+ * @GST_PROGRESS_TYPE_START: A new task started.
+ * @GST_PROGRESS_TYPE_CONTINUE: A task completed and a new one continues.
+ * @GST_PROGRESS_TYPE_COMPLETE: A task completed.
+ * @GST_PROGRESS_TYPE_CANCELED: A task was canceled.
+ * @GST_PROGRESS_TYPE_ERROR: A task caused an error. An error message is also
+ * posted on the bus.
+ *
+ * The type of a %GST_MESSAGE_PROGRESS. The progress messages inform the
+ * application of the status of assynchronous tasks.
+ *
+ * Since: 0.10.33
+ */
+typedef enum {
+ GST_PROGRESS_TYPE_START = 0,
+ GST_PROGRESS_TYPE_CONTINUE = 1,
+ GST_PROGRESS_TYPE_COMPLETE = 2,
+ GST_PROGRESS_TYPE_CANCELED = 3,
+ GST_PROGRESS_TYPE_ERROR = 4,
+} GstProgressType;
+
+/**
+ * GstMessage:
+ * @mini_object: the parent structure
+ * @type: the #GstMessageType of the message
+ * @timestamp: the timestamp of the message
+ * @src: the src of the message
+ * @seqnum: the sequence number of the message
+ *
+ * A #GstMessage.
+ */
+struct _GstMessage
+{
+ GstMiniObject mini_object;
+
+ /*< public > *//* with COW */
+ GstMessageType type;
+ guint64 timestamp;
+ GstObject *src;
+ guint32 seqnum;
+
+ /*< private >*//* with MESSAGE_LOCK */
+ GMutex *lock; /* lock and cond for async delivery */
+ GCond *cond;
+};
+
+GType gst_message_get_type (void);
+
+const gchar* gst_message_type_get_name (GstMessageType type);
+GQuark gst_message_type_to_quark (GstMessageType type);
+
+/* refcounting */
+/**
+ * gst_message_ref:
+ * @msg: the message to ref
+ *
+ * Convenience macro to increase the reference count of the message.
+ *
+ * Returns: @msg (for convenience when doing assignments)
+ */
+#ifdef _FOOL_GTK_DOC_
+G_INLINE_FUNC GstMessage * gst_message_ref (GstMessage * msg);
+#endif
+
+static inline GstMessage *
+gst_message_ref (GstMessage * msg)
+{
+ return (GstMessage *) gst_mini_object_ref (GST_MINI_OBJECT_CAST (msg));
+}
+
+/**
+ * gst_message_unref:
+ * @msg: the message to unref
+ *
+ * Convenience macro to decrease the reference count of the message, possibly
+ * freeing it.
+ */
+#ifdef _FOOL_GTK_DOC_
+G_INLINE_FUNC void gst_message_unref (GstMessage * msg);
+#endif
+
+static inline void
+gst_message_unref (GstMessage * msg)
+{
+ gst_mini_object_unref (GST_MINI_OBJECT_CAST (msg));
+}
+
+/* copy message */
+/**
+ * gst_message_copy:
+ * @msg: the message to copy
+ *
+ * Creates a copy of the message. Returns a copy of the message.
+ *
+ * Returns: (transfer full): a new copy of @msg.
+ *
+ * MT safe
+ */
+#ifdef _FOOL_GTK_DOC_
+G_INLINE_FUNC GstMessage * gst_message_copy (const GstMessage * msg);
+#endif
+
+static inline GstMessage *
+gst_message_copy (const GstMessage * msg)
+{
+ return GST_MESSAGE_CAST (gst_mini_object_copy (GST_MINI_OBJECT_CONST_CAST (msg)));
+}
+
+/**
+ * gst_message_is_writable:
+ * @msg: a #GstMessage
+ *
+ * Tests if you can safely write into a message's structure or validly
+ * modify the seqnum and timestamp fields.
+ */
+#define gst_message_is_writable(msg) gst_mini_object_is_writable (GST_MINI_OBJECT_CAST (msg))
+/**
+ * gst_message_make_writable:
+ * @msg: (transfer full): the message to make writable
+ *
+ * Checks if a message is writable. If not, a writable copy is made and
+ * returned.
+ *
+ * Returns: (transfer full): a message (possibly a duplicate) that is writable.
+ *
+ * MT safe
+ */
+#define gst_message_make_writable(msg) GST_MESSAGE_CAST (gst_mini_object_make_writable (GST_MINI_OBJECT_CAST (msg)))
+/**
+ * gst_message_replace:
+ * @old_message: (inout) (transfer full): pointer to a pointer to a #GstMessage
+ * to be replaced.
+ * @new_message: (allow-none) (transfer none): pointer to a #GstMessage that will
+ * replace the message pointed to by @old_message.
+ *
+ * Modifies a pointer to a #GstMessage to point to a different #GstMessage. The
+ * modification is done atomically (so this is useful for ensuring thread safety
+ * in some cases), and the reference counts are updated appropriately (the old
+ * message is unreffed, the new one is reffed).
+ *
+ * Either @new_message or the #GstMessage pointed to by @old_message may be NULL.
+ */
+#define gst_message_replace(old_message,new_message) \
+ gst_mini_object_replace ((GstMiniObject **)(old_message), GST_MINI_OBJECT_CAST (new_message))
+
+
+/* custom messages */
+GstMessage * gst_message_new_custom (GstMessageType type,
+ GstObject * src,
+ GstStructure * structure);
+const GstStructure *
+ gst_message_get_structure (GstMessage *message);
+
+gboolean gst_message_has_name (GstMessage *message, const gchar *name);
+
+/* identifiers for events and messages */
+guint32 gst_message_get_seqnum (GstMessage *message);
+void gst_message_set_seqnum (GstMessage *message, guint32 seqnum);
+
+/* EOS */
+GstMessage * gst_message_new_eos (GstObject * src);
+
+/* ERROR */
+
+GstMessage * gst_message_new_error (GstObject * src, GError * error, const gchar * debug);
+void gst_message_parse_error (GstMessage *message, GError **gerror, gchar **debug);
+
+/* WARNING */
+GstMessage * gst_message_new_warning (GstObject * src, GError * error, const gchar * debug);
+void gst_message_parse_warning (GstMessage *message, GError **gerror, gchar **debug);
+
+/* INFO */
+GstMessage * gst_message_new_info (GstObject * src, GError * error, const gchar * debug);
+void gst_message_parse_info (GstMessage *message, GError **gerror, gchar **debug);
+
+/* TAG */
+GstMessage * gst_message_new_tag (GstObject * src, GstTagList * tag_list);
+void gst_message_parse_tag (GstMessage *message, GstTagList **tag_list);
+
+/* BUFFERING */
+GstMessage * gst_message_new_buffering (GstObject * src, gint percent);
+void gst_message_parse_buffering (GstMessage *message, gint *percent);
+void gst_message_set_buffering_stats (GstMessage *message, GstBufferingMode mode,
+ gint avg_in, gint avg_out,
+ gint64 buffering_left);
+void gst_message_parse_buffering_stats (GstMessage *message, GstBufferingMode *mode,
+ gint *avg_in, gint *avg_out,
+ gint64 *buffering_left);
+
+/* STATE_CHANGED */
+GstMessage * gst_message_new_state_changed (GstObject * src, GstState oldstate,
+ GstState newstate, GstState pending);
+void gst_message_parse_state_changed (GstMessage *message, GstState *oldstate,
+ GstState *newstate, GstState *pending);
+
+/* STATE_DIRTY */
+GstMessage * gst_message_new_state_dirty (GstObject * src);
+
+/* STEP_DONE */
+GstMessage * gst_message_new_step_done (GstObject * src, GstFormat format, guint64 amount,
+ gdouble rate, gboolean flush, gboolean intermediate,
+ guint64 duration, gboolean eos);
+void gst_message_parse_step_done (GstMessage * message, GstFormat *format, guint64 *amount,
+ gdouble *rate, gboolean *flush, gboolean *intermediate,
+ guint64 *duration, gboolean *eos);
+/* CLOCK_PROVIDE */
+GstMessage * gst_message_new_clock_provide (GstObject * src, GstClock *clock, gboolean ready);
+void gst_message_parse_clock_provide (GstMessage *message, GstClock **clock,
+ gboolean *ready);
+
+/* CLOCK_LOST */
+GstMessage * gst_message_new_clock_lost (GstObject * src, GstClock *clock);
+void gst_message_parse_clock_lost (GstMessage *message, GstClock **clock);
+
+/* NEW_CLOCK */
+GstMessage * gst_message_new_new_clock (GstObject * src, GstClock *clock);
+void gst_message_parse_new_clock (GstMessage *message, GstClock **clock);
+
+/* APPLICATION */
+GstMessage * gst_message_new_application (GstObject * src, GstStructure * structure);
+
+/* ELEMENT */
+GstMessage * gst_message_new_element (GstObject * src, GstStructure * structure);
+
+/* SEGMENT_START */
+GstMessage * gst_message_new_segment_start (GstObject * src, GstFormat format, gint64 position);
+void gst_message_parse_segment_start (GstMessage *message, GstFormat *format,
+ gint64 *position);
+
+/* SEGMENT_DONE */
+GstMessage * gst_message_new_segment_done (GstObject * src, GstFormat format, gint64 position);
+void gst_message_parse_segment_done (GstMessage *message, GstFormat *format,
+ gint64 *position);
+
+/* DURATION */
+GstMessage * gst_message_new_duration (GstObject * src, GstFormat format, gint64 duration);
+void gst_message_parse_duration (GstMessage *message, GstFormat *format,
+ gint64 *duration);
+
+/* LATENCY */
+GstMessage * gst_message_new_latency (GstObject * src);
+
+/* ASYNC_START */
+GstMessage * gst_message_new_async_start (GstObject * src);
+
+/* ASYNC_DONE */
+GstMessage * gst_message_new_async_done (GstObject * src, gboolean reset_time);
+void gst_message_parse_async_done (GstMessage *message, gboolean *reset_time);
+
+/* STRUCTURE CHANGE */
+GstMessage * gst_message_new_structure_change (GstObject * src, GstStructureChangeType type,
+ GstElement *owner, gboolean busy);
+void gst_message_parse_structure_change (GstMessage *message, GstStructureChangeType *type,
+ GstElement **owner, gboolean *busy);
+
+/* STREAM STATUS */
+GstMessage * gst_message_new_stream_status (GstObject * src, GstStreamStatusType type,
+ GstElement *owner);
+void gst_message_parse_stream_status (GstMessage *message, GstStreamStatusType *type,
+ GstElement **owner);
+void gst_message_set_stream_status_object (GstMessage *message, const GValue *object);
+const GValue * gst_message_get_stream_status_object (GstMessage *message);
+
+/* REQUEST_STATE */
+GstMessage * gst_message_new_request_state (GstObject * src, GstState state);
+void gst_message_parse_request_state (GstMessage * message, GstState *state);
+
+/* STEP_START */
+GstMessage * gst_message_new_step_start (GstObject * src, gboolean active, GstFormat format,
+ guint64 amount, gdouble rate, gboolean flush,
+ gboolean intermediate);
+void gst_message_parse_step_start (GstMessage * message, gboolean *active, GstFormat *format,
+ guint64 *amount, gdouble *rate, gboolean *flush,
+ gboolean *intermediate);
+
+/* QOS */
+GstMessage * gst_message_new_qos (GstObject * src, gboolean live, guint64 running_time,
+ guint64 stream_time, guint64 timestamp, guint64 duration);
+void gst_message_set_qos_values (GstMessage * message, gint64 jitter, gdouble proportion,
+ gint quality);
+void gst_message_set_qos_stats (GstMessage * message, GstFormat format, guint64 processed,
+ guint64 dropped);
+void gst_message_parse_qos (GstMessage * message, gboolean * live, guint64 * running_time,
+ guint64 * stream_time, guint64 * timestamp, guint64 * duration);
+void gst_message_parse_qos_values (GstMessage * message, gint64 * jitter, gdouble * proportion,
+ gint * quality);
+void gst_message_parse_qos_stats (GstMessage * message, GstFormat * format, guint64 * processed,
+ guint64 * dropped);
+/* PROGRESS */
+GstMessage * gst_message_new_progress (GstObject * src, GstProgressType type, const gchar *code,
+ const gchar *text);
+void gst_message_parse_progress (GstMessage * message, GstProgressType * type, gchar ** code,
+ gchar ** text);
+
+
+G_END_DECLS
+
+#endif /* __GST_MESSAGE_H__ */
diff --git a/gst/gstmeta.c b/gst/gstmeta.c
new file mode 100644
index 0000000..078aef1
--- /dev/null
+++ b/gst/gstmeta.c
@@ -0,0 +1,153 @@
+/* GStreamer
+ * Copyright (C) 2011 Wim Taymans <wim.taymans@gmail.com>
+ *
+ * gstmeta.c: metadata operations
+ *
+ * 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:gstmeta
+ * @short_description: Buffer metadata
+ *
+ * Last reviewed on December 17th, 2009 (0.10.26)
+ */
+#include "gst_private.h"
+
+#include "gstbuffer.h"
+#include "gstmeta.h"
+#include "gstinfo.h"
+#include "gstutils.h"
+
+static GHashTable *metainfo = NULL;
+static GStaticRWLock lock = G_STATIC_RW_LOCK_INIT;
+
+void
+_priv_gst_meta_initialize (void)
+{
+ metainfo = g_hash_table_new (g_str_hash, g_str_equal);
+}
+
+/**
+ * gst_meta_register_info:
+ * @info: a #GstMetaInfo
+ *
+ * Register a #GstMetaInfo. The same @info can be retrieved later with
+ * gst_meta_get_info() by using @impl as the key.
+ *
+ * Returns: a #GstMetaInfo that can be used to access metadata.
+ */
+
+const GstMetaInfo *
+gst_meta_register (const gchar * api, const gchar * impl, gsize size,
+ GstMetaInitFunction init_func, GstMetaFreeFunction free_func,
+ GstMetaCopyFunction copy_func, GstMetaTransformFunction transform_func)
+{
+ GstMetaInfo *info;
+
+ g_return_val_if_fail (api != NULL, NULL);
+ g_return_val_if_fail (impl != NULL, NULL);
+ g_return_val_if_fail (size != 0, NULL);
+
+ info = g_slice_new (GstMetaInfo);
+ info->api = g_quark_from_string (api);
+ info->type = g_pointer_type_register_static (impl);
+ info->size = size;
+ info->init_func = init_func;
+ info->free_func = free_func;
+ info->copy_func = copy_func;
+ info->transform_func = transform_func;
+
+ GST_DEBUG ("register \"%s\" implementing \"%s\" of size %" G_GSIZE_FORMAT,
+ api, impl, size);
+
+ g_static_rw_lock_writer_lock (&lock);
+ g_hash_table_insert (metainfo, (gpointer) impl, (gpointer) info);
+ g_static_rw_lock_writer_unlock (&lock);
+
+ return info;
+}
+
+/**
+ * gst_meta_get_info:
+ * @impl: the name
+ *
+ * Lookup a previously registered meta info structure by its implementor name
+ * @impl.
+ *
+ * Returns: a #GstMetaInfo with @impl or #NULL when no such metainfo
+ * exists.
+ */
+const GstMetaInfo *
+gst_meta_get_info (const gchar * impl)
+{
+ GstMetaInfo *info;
+
+ g_return_val_if_fail (impl != NULL, NULL);
+
+ g_static_rw_lock_reader_lock (&lock);
+ info = g_hash_table_lookup (metainfo, impl);
+ g_static_rw_lock_reader_unlock (&lock);
+
+ return info;
+}
+
+/* Timing metadata */
+static void
+meta_timing_copy (GstBuffer * copybuf, GstMetaTiming * meta,
+ GstBuffer * buffer, gsize offset, gsize size)
+{
+ GstMetaTiming *timing;
+
+ GST_DEBUG ("trans called from buffer %p to %p, meta %p,"
+ "offset %" G_GSIZE_FORMAT ", size %" G_GSIZE_FORMAT, buffer,
+ copybuf, meta, offset, size);
+
+ timing = gst_buffer_add_meta_timing (copybuf);
+ if (offset == 0) {
+ /* same offset, copy timestamps */
+ timing->pts = meta->pts;
+ timing->dts = meta->dts;
+ if (size == gst_buffer_get_size (buffer)) {
+ /* same size, copy duration */
+ timing->duration = meta->duration;
+ } else {
+ /* else clear */
+ timing->duration = GST_CLOCK_TIME_NONE;
+ }
+ } else {
+ timing->pts = -1;
+ timing->dts = -1;
+ timing->duration = -1;
+ }
+ timing->clock_rate = meta->clock_rate;
+}
+
+const GstMetaInfo *
+gst_meta_timing_get_info (void)
+{
+ static const GstMetaInfo *meta_info = NULL;
+
+ if (meta_info == NULL) {
+ meta_info = gst_meta_register ("GstMetaTiming", "GstMetaTiming",
+ sizeof (GstMetaTiming),
+ (GstMetaInitFunction) NULL,
+ (GstMetaFreeFunction) NULL,
+ (GstMetaCopyFunction) meta_timing_copy,
+ (GstMetaTransformFunction) NULL);
+ }
+ return meta_info;
+}
diff --git a/gst/gstmeta.h b/gst/gstmeta.h
new file mode 100644
index 0000000..90d7fc2
--- /dev/null
+++ b/gst/gstmeta.h
@@ -0,0 +1,151 @@
+/* GStreamer
+ * Copyright (C) 2009 Wim Taymans <wim.taymans@gmail.be>
+ *
+ * gstmeta.h: Header for Metadata structures
+ *
+ * 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_META_H__
+#define __GST_META_H__
+
+G_BEGIN_DECLS
+
+typedef struct _GstMeta GstMeta;
+typedef struct _GstMetaInfo GstMetaInfo;
+
+/**
+ * GstMeta:
+ * @info: pointer to the #GstMetaInfo
+ *
+ * Base structure for metadata. Custom metadata will put this structure
+ * as the first member of their structure.
+ */
+struct _GstMeta {
+ const GstMetaInfo *info;
+};
+
+/**
+ * GST_META_TRACE_NAME:
+ *
+ * The name used for tracing memory allocations.
+ */
+#define GST_META_TRACE_NAME "GstMeta"
+
+/**
+ * GstMetaInitFunction:
+ * @meta: a #GstMeta
+ * @params: parameters passed to the init function
+ * @buffer: a #GstBuffer
+ *
+ * Function called when @meta is initialized in @buffer.
+ */
+typedef gboolean (*GstMetaInitFunction) (GstMeta *meta, gpointer params, GstBuffer *buffer);
+
+/**
+ * GstMetaFreeFunction:
+ * @meta: a #GstMeta
+ * @buffer: a #GstBuffer
+ *
+ * Function called when @meta is freed in @buffer.
+ */
+typedef void (*GstMetaFreeFunction) (GstMeta *meta, GstBuffer *buffer);
+
+typedef void (*GstMetaCopyFunction) (GstBuffer *dest, GstMeta *meta,
+ GstBuffer *buffer, gsize offset, gsize size);
+/**
+ * GstMetaTransformFunction:
+ * @transbuf: a #GstBuffer
+ * @meta: a #GstMeta
+ * @buffer: a #GstBuffer
+ * @data: transform specific data.
+ *
+ * Function called for each @meta in @buffer as a result of performing a
+ * transformation on @transbuf. Additional type specific transform data
+ * is passed to the function.
+ *
+ * Implementations should check the type of the transform @data and parse
+ * additional type specific field that should be used to perform the transform.
+ */
+typedef void (*GstMetaTransformFunction) (GstBuffer *transbuf, GstMeta *meta,
+ GstBuffer *buffer, gpointer data);
+
+/**
+ * GstMetaInfo:
+ * @api: tag indentifying the metadata structure and api
+ * @type: type indentifying the implementor of the api
+ * @size: size of the metadata
+ * @init_func: function for initializing the metadata
+ * @free_func: function for freeing the metadata
+ * @copy_func: function for copying the metadata
+ * @transform_func: function for transforming the metadata
+ *
+ * The #GstMetaInfo provides information about a specific metadata
+ * structure.
+ */
+struct _GstMetaInfo {
+ GQuark api;
+ GType type;
+ gsize size;
+
+ GstMetaInitFunction init_func;
+ GstMetaFreeFunction free_func;
+ GstMetaCopyFunction copy_func;
+ GstMetaTransformFunction transform_func;
+};
+
+const GstMetaInfo * gst_meta_register (const gchar *api, const gchar *impl,
+ gsize size,
+ GstMetaInitFunction init_func,
+ GstMetaFreeFunction free_func,
+ GstMetaCopyFunction copy_func,
+ GstMetaTransformFunction transform_func);
+const GstMetaInfo * gst_meta_get_info (const gchar * impl);
+
+/* default metadata */
+
+/* timing metadata */
+typedef struct _GstMetaTiming GstMetaTiming;
+
+const GstMetaInfo *gst_meta_timing_get_info(void);
+#define GST_META_TIMING_INFO (gst_meta_timing_get_info())
+
+/**
+ * GstMetaTiming:
+ * @meta: parent metadata
+ * @dts: the decoding timestamp
+ * @pts: the presentation timestamp
+ * @duration: the duration
+ * @clock_rate: the clock rate of the dts, pts and duration values
+ *
+ * Extra timing metadata
+ */
+struct _GstMetaTiming {
+ GstMeta meta; /* common meta header */
+
+ GstClockTime dts; /* decoding timestamp */
+ GstClockTime pts; /* presentation timestamp */
+ GstClockTime duration; /* duration of the data */
+ GstClockTime clock_rate; /* clock rate for the above values */
+};
+
+#define gst_buffer_get_meta_timing(b) ((GstMetaTiming*)gst_buffer_get_meta((b),GST_META_TIMING_INFO))
+#define gst_buffer_add_meta_timing(b) ((GstMetaTiming*)gst_buffer_add_meta((b),GST_META_TIMING_INFO,NULL))
+
+G_END_DECLS
+
+#endif /* __GST_META_H__ */
diff --git a/gst/gstminiobject.c b/gst/gstminiobject.c
new file mode 100644
index 0000000..78749dd
--- /dev/null
+++ b/gst/gstminiobject.c
@@ -0,0 +1,443 @@
+/* GStreamer
+ * Copyright (C) 2005 David Schleef <ds@schleef.org>
+ *
+ * gstminiobject.h: Header for GstMiniObject
+ *
+ * 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:gstminiobject
+ * @short_description: Lightweight base class for the GStreamer object hierarchy
+ *
+ * #GstMiniObject is a baseclass like #GObject, but has been stripped down of
+ * features to be fast and small.
+ * It offers sub-classing and ref-counting in the same way as #GObject does.
+ * It has no properties and no signal-support though.
+ *
+ * Last reviewed on 2005-11-23 (0.9.5)
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gst/gst_private.h"
+#include "gst/gstminiobject.h"
+#include "gst/gstinfo.h"
+#include <gobject/gvaluecollector.h>
+
+#define GST_DISABLE_TRACE
+
+#ifndef GST_DISABLE_TRACE
+#include "gsttrace.h"
+static GstAllocTrace *_gst_mini_object_trace;
+#endif
+
+/* Mutex used for weak referencing */
+G_LOCK_DEFINE_STATIC (weak_refs_mutex);
+
+/**
+ * gst_mini_object_init:
+ * @mini_object: a #GstMiniObject
+ * @type: the #GType of the mini-object to create
+ * @size: the size of the data
+ *
+ * Initializes a mini-object with the desired type and size.
+ *
+ * MT safe
+ *
+ * Returns: (transfer full): the new mini-object.
+ */
+void
+gst_mini_object_init (GstMiniObject * mini_object, GType type, gsize size)
+{
+ mini_object->type = type;
+ mini_object->refcount = 1;
+ mini_object->flags = 0;
+ mini_object->size = size;
+ mini_object->n_weak_refs = 0;
+ mini_object->weak_refs = NULL;
+}
+
+/**
+ * gst_mini_object_copy:
+ * @mini_object: the mini-object to copy
+ *
+ * Creates a copy of the mini-object.
+ *
+ * MT safe
+ *
+ * Returns: (transfer full): the new mini-object.
+ */
+GstMiniObject *
+gst_mini_object_copy (const GstMiniObject * mini_object)
+{
+ GstMiniObject *copy;
+
+ g_return_val_if_fail (mini_object != NULL, NULL);
+
+ if (mini_object->copy)
+ copy = mini_object->copy (mini_object);
+ else
+ copy = NULL;
+
+ return copy;
+}
+
+/**
+ * gst_mini_object_is_writable:
+ * @mini_object: the mini-object to check
+ *
+ * Checks if a mini-object is writable. A mini-object is writable
+ * if the reference count is one and the #GST_MINI_OBJECT_FLAG_READONLY
+ * flag is not set. Modification of a mini-object should only be
+ * done after verifying that it is writable.
+ *
+ * MT safe
+ *
+ * Returns: TRUE if the object is writable.
+ */
+gboolean
+gst_mini_object_is_writable (const GstMiniObject * mini_object)
+{
+ g_return_val_if_fail (mini_object != NULL, FALSE);
+
+ return (GST_MINI_OBJECT_REFCOUNT_VALUE (mini_object) == 1);
+}
+
+/**
+ * gst_mini_object_make_writable:
+ * @mini_object: (transfer full): the mini-object to make writable
+ *
+ * Checks if a mini-object is writable. If not, a writable copy is made and
+ * returned. This gives away the reference to the original mini object,
+ * and returns a reference to the new object.
+ *
+ * MT safe
+ *
+ * Returns: (transfer full): a mini-object (possibly the same pointer) that
+ * is writable.
+ */
+GstMiniObject *
+gst_mini_object_make_writable (GstMiniObject * mini_object)
+{
+ GstMiniObject *ret;
+
+ g_return_val_if_fail (mini_object != NULL, NULL);
+
+ if (gst_mini_object_is_writable (mini_object)) {
+ ret = mini_object;
+ } else {
+ ret = gst_mini_object_copy (mini_object);
+ GST_CAT_DEBUG (GST_CAT_PERFORMANCE, "copy %s miniobject %p -> %p",
+ g_type_name (GST_MINI_OBJECT_TYPE (mini_object)), mini_object, ret);
+ gst_mini_object_unref (mini_object);
+ }
+
+ return ret;
+}
+
+/**
+ * gst_mini_object_ref:
+ * @mini_object: the mini-object
+ *
+ * Increase the reference count of the mini-object.
+ *
+ * Note that the refcount affects the writeability
+ * of @mini-object, see gst_mini_object_is_writable(). It is
+ * important to note that keeping additional references to
+ * GstMiniObject instances can potentially increase the number
+ * of memcpy operations in a pipeline, especially if the miniobject
+ * is a #GstBuffer.
+ *
+ * Returns: (transfer full): the mini-object.
+ */
+GstMiniObject *
+gst_mini_object_ref (GstMiniObject * mini_object)
+{
+ g_return_val_if_fail (mini_object != NULL, NULL);
+ /* we can't assert that the refcount > 0 since the _free functions
+ * increments the refcount from 0 to 1 again to allow resurecting
+ * the object
+ g_return_val_if_fail (mini_object->refcount > 0, NULL);
+ */
+
+ GST_CAT_TRACE (GST_CAT_REFCOUNTING, "%p ref %d->%d", mini_object,
+ GST_MINI_OBJECT_REFCOUNT_VALUE (mini_object),
+ GST_MINI_OBJECT_REFCOUNT_VALUE (mini_object) + 1);
+
+ g_atomic_int_inc (&mini_object->refcount);
+
+ return mini_object;
+}
+
+static void
+weak_refs_notify (GstMiniObject * obj)
+{
+ guint i;
+
+ for (i = 0; i < obj->n_weak_refs; i++)
+ obj->weak_refs[i].notify (obj->weak_refs[i].data, obj);
+ g_free (obj->weak_refs);
+}
+
+/**
+ * gst_mini_object_unref:
+ * @mini_object: the mini-object
+ *
+ * Decreases the reference count of the mini-object, possibly freeing
+ * the mini-object.
+ */
+void
+gst_mini_object_unref (GstMiniObject * mini_object)
+{
+ g_return_if_fail (mini_object != NULL);
+ g_return_if_fail (mini_object->refcount > 0);
+
+ GST_CAT_TRACE (GST_CAT_REFCOUNTING, "%p unref %d->%d",
+ mini_object,
+ GST_MINI_OBJECT_REFCOUNT_VALUE (mini_object),
+ GST_MINI_OBJECT_REFCOUNT_VALUE (mini_object) - 1);
+
+ if (G_UNLIKELY (g_atomic_int_dec_and_test (&mini_object->refcount))) {
+ gboolean do_free;
+
+ if (mini_object->dispose)
+ do_free = mini_object->dispose (mini_object);
+ else
+ do_free = TRUE;
+
+ /* if the subclass recycled the object (and returned FALSE) we don't
+ * want to free the instance anymore */
+ if (G_LIKELY (do_free)) {
+ /* The weak reference stack is freed in the notification function */
+ if (mini_object->n_weak_refs)
+ weak_refs_notify (mini_object);
+
+#ifndef GST_DISABLE_TRACE
+ gst_alloc_trace_free (_gst_mini_object_trace, mini_object);
+#endif
+ if (mini_object->free)
+ mini_object->free (mini_object);
+ }
+ }
+}
+
+/**
+ * gst_mini_object_replace:
+ * @olddata: (inout) (transfer full): pointer to a pointer to a mini-object to
+ * be replaced
+ * @newdata: pointer to new mini-object
+ *
+ * Atomically modifies a pointer to point to a new mini-object.
+ * The reference count of @olddata is decreased and the reference count of
+ * @newdata is increased.
+ *
+ * Either @newdata and the value pointed to by @olddata may be NULL.
+ *
+ * Returns: TRUE if @newdata was different from @olddata
+ */
+gboolean
+gst_mini_object_replace (GstMiniObject ** olddata, GstMiniObject * newdata)
+{
+ GstMiniObject *olddata_val;
+
+ g_return_val_if_fail (olddata != NULL, FALSE);
+
+ GST_CAT_TRACE (GST_CAT_REFCOUNTING, "replace %p (%d) with %p (%d)",
+ *olddata, *olddata ? (*olddata)->refcount : 0,
+ newdata, newdata ? newdata->refcount : 0);
+
+ olddata_val = g_atomic_pointer_get ((gpointer *) olddata);
+
+ if (G_UNLIKELY (olddata_val == newdata))
+ return FALSE;
+
+ if (newdata)
+ gst_mini_object_ref (newdata);
+
+ while (G_UNLIKELY (!g_atomic_pointer_compare_and_exchange ((gpointer *)
+ olddata, olddata_val, newdata))) {
+ olddata_val = g_atomic_pointer_get ((gpointer *) olddata);
+ if (G_UNLIKELY (olddata_val == newdata))
+ break;
+ }
+
+ if (olddata_val)
+ gst_mini_object_unref (olddata_val);
+
+ return olddata_val != newdata;
+}
+
+/**
+ * gst_mini_object_steal:
+ * @olddata: (inout) (transfer full): pointer to a pointer to a mini-object to
+ * be stolen
+ *
+ * Replace the current #GstMiniObject pointer to by @olddata with NULL and
+ * return the old value.
+ *
+ * Returns: the #GstMiniObject at @oldata
+ */
+GstMiniObject *
+gst_mini_object_steal (GstMiniObject ** olddata)
+{
+ GstMiniObject *olddata_val;
+
+ g_return_val_if_fail (olddata != NULL, NULL);
+
+ GST_CAT_TRACE (GST_CAT_REFCOUNTING, "steal %p (%d)",
+ *olddata, *olddata ? (*olddata)->refcount : 0);
+
+ do {
+ olddata_val = g_atomic_pointer_get ((gpointer *) olddata);
+ if (olddata_val == NULL)
+ break;
+ } while (G_UNLIKELY (!g_atomic_pointer_compare_and_exchange ((gpointer *)
+ olddata, olddata_val, NULL)));
+
+ return olddata_val;
+}
+
+/**
+ * gst_mini_object_take:
+ * @olddata: (inout) (transfer full): pointer to a pointer to a mini-object to
+ * be replaced
+ * @newdata: pointer to new mini-object
+ *
+ * Modifies a pointer to point to a new mini-object. The modification
+ * is done atomically. This version is similar to gst_mini_object_replace()
+ * except that it does not increase the refcount of @newdata and thus
+ * takes ownership of @newdata.
+ *
+ * Either @newdata and the value pointed to by @olddata may be NULL.
+ *
+ * Returns: TRUE if @newdata was different from @olddata
+ */
+gboolean
+gst_mini_object_take (GstMiniObject ** olddata, GstMiniObject * newdata)
+{
+ GstMiniObject *olddata_val;
+
+ g_return_val_if_fail (olddata != NULL, FALSE);
+
+ GST_CAT_TRACE (GST_CAT_REFCOUNTING, "take %p (%d) with %p (%d)",
+ *olddata, *olddata ? (*olddata)->refcount : 0,
+ newdata, newdata ? newdata->refcount : 0);
+
+ do {
+ olddata_val = g_atomic_pointer_get ((gpointer *) olddata);
+ if (G_UNLIKELY (olddata_val == newdata))
+ break;
+ } while (G_UNLIKELY (!g_atomic_pointer_compare_and_exchange ((gpointer *)
+ olddata, olddata_val, newdata)));
+
+ if (olddata_val)
+ gst_mini_object_unref (olddata_val);
+
+ return olddata_val != newdata;
+}
+
+/**
+ * gst_mini_object_weak_ref: (skip)
+ * @object: #GstMiniObject to reference weakly
+ * @notify: callback to invoke before the mini object is freed
+ * @data: extra data to pass to notify
+ *
+ * Adds a weak reference callback to a mini object. Weak references are
+ * used for notification when a mini object is finalized. They are called
+ * "weak references" because they allow you to safely hold a pointer
+ * to the mini object without calling gst_mini_object_ref()
+ * (gst_mini_object_ref() adds a strong reference, that is, forces the object
+ * to stay alive).
+ *
+ * Since: 0.10.35
+ */
+void
+gst_mini_object_weak_ref (GstMiniObject * object,
+ GstMiniObjectWeakNotify notify, gpointer data)
+{
+ guint i;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (notify != NULL);
+ g_return_if_fail (GST_MINI_OBJECT_REFCOUNT_VALUE (object) >= 1);
+
+ G_LOCK (weak_refs_mutex);
+
+ if (object->n_weak_refs) {
+ /* Don't add the weak reference if it already exists. */
+ for (i = 0; i < object->n_weak_refs; i++) {
+ if (object->weak_refs[i].notify == notify &&
+ object->weak_refs[i].data == data) {
+ g_warning ("%s: Attempt to re-add existing weak ref %p(%p) failed.",
+ G_STRFUNC, notify, data);
+ goto found;
+ }
+ }
+
+ i = object->n_weak_refs++;
+ object->weak_refs =
+ g_realloc (object->weak_refs, sizeof (object->weak_refs[0]) * i);
+ } else {
+ object->weak_refs = g_malloc0 (sizeof (object->weak_refs[0]));
+ object->n_weak_refs = 1;
+ i = 0;
+ }
+ object->weak_refs[i].notify = notify;
+ object->weak_refs[i].data = data;
+found:
+ G_UNLOCK (weak_refs_mutex);
+}
+
+/**
+ * gst_mini_object_weak_unref: (skip)
+ * @object: #GstMiniObject to remove a weak reference from
+ * @notify: callback to search for
+ * @data: data to search for
+ *
+ * Removes a weak reference callback to a mini object.
+ *
+ * Since: 0.10.35
+ */
+void
+gst_mini_object_weak_unref (GstMiniObject * object,
+ GstMiniObjectWeakNotify notify, gpointer data)
+{
+ gboolean found_one = FALSE;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (notify != NULL);
+
+ G_LOCK (weak_refs_mutex);
+
+ if (object->n_weak_refs) {
+ guint i;
+
+ for (i = 0; i < object->n_weak_refs; i++)
+ if (object->weak_refs[i].notify == notify &&
+ object->weak_refs[i].data == data) {
+ found_one = TRUE;
+ object->n_weak_refs -= 1;
+ if (i != object->n_weak_refs)
+ object->weak_refs[i] = object->weak_refs[object->n_weak_refs];
+
+ break;
+ }
+ }
+ G_UNLOCK (weak_refs_mutex);
+ if (!found_one)
+ g_warning ("%s: couldn't find weak ref %p(%p)", G_STRFUNC, notify, data);
+}
diff --git a/gst/gstminiobject.h b/gst/gstminiobject.h
new file mode 100644
index 0000000..65b4dfb
--- /dev/null
+++ b/gst/gstminiobject.h
@@ -0,0 +1,228 @@
+/* GStreamer
+ * Copyright (C) 2005 David Schleef <ds@schleef.org>
+ *
+ * gstminiobject.h: Header for GstMiniObject
+ *
+ * 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_MINI_OBJECT_H__
+#define __GST_MINI_OBJECT_H__
+
+#include <gst/gstconfig.h>
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GST_IS_MINI_OBJECT_TYPE(obj,type) ((obj) && GST_MINI_OBJECT_TYPE(obj) == (type))
+#define GST_MINI_OBJECT_CAST(obj) ((GstMiniObject*)(obj))
+#define GST_MINI_OBJECT_CONST_CAST(obj) ((const GstMiniObject*)(obj))
+#define GST_MINI_OBJECT(obj) (GST_MINI_OBJECT_CAST(obj))
+
+typedef struct _GstMiniObject GstMiniObject;
+
+/**
+ * GstMiniObjectCopyFunction:
+ * @obj: MiniObject to copy
+ *
+ * Function prototype for methods to create copies of instances.
+ *
+ * Returns: reference to cloned instance.
+ */
+typedef GstMiniObject * (*GstMiniObjectCopyFunction) (const GstMiniObject *obj);
+/**
+ * GstMiniObjectDisposeFunction:
+ * @obj: MiniObject to dispose
+ *
+ * Function prototype for when a miniobject has lost its last refcount.
+ * Implementation of the mini object are allowed to revive the
+ * passed object by doing a gst_mini_object_ref(). If the object is not
+ * revived after the dispose function, the function should return %TRUE
+ * and the memory associated with the object is freed.
+ *
+ * Returns: %TRUE if the object should be cleaned up.
+ */
+typedef gboolean (*GstMiniObjectDisposeFunction) (GstMiniObject *obj);
+/**
+ * GstMiniObjectFreeFunction:
+ * @obj: MiniObject to free
+ *
+ * Virtual function prototype for methods to free ressources used by
+ * mini-objects.
+ */
+typedef void (*GstMiniObjectFreeFunction) (GstMiniObject *obj);
+
+ /**
+ * GstMiniObjectWeakNotify:
+ * @data: data that was provided when the weak reference was established
+ * @where_the_mini_object_was: the mini object being finalized
+ *
+ * A #GstMiniObjectWeakNotify function can be added to a mini object as a
+ * callback that gets triggered when the mini object is finalized. Since the
+ * mini object is already being finalized when the #GstMiniObjectWeakNotify is
+ * called, there's not much you could do with the object, apart from e.g. using
+ * its adress as hash-index or the like.
+ *
+ * Since: 0.10.35
+ */
+typedef void (*GstMiniObjectWeakNotify) (gpointer data,
+ GstMiniObject * where_the_mini_object_was);
+
+/**
+ * GST_MINI_OBJECT_FLAGS:
+ * @obj: MiniObject to return flags for.
+ *
+ * This macro returns the entire set of flags for the mini-object.
+ */
+#define GST_MINI_OBJECT_TYPE(obj) (GST_MINI_OBJECT_CAST(obj)->type)
+/**
+ * GST_MINI_OBJECT_FLAGS:
+ * @obj: MiniObject to return flags for.
+ *
+ * This macro returns the entire set of flags for the mini-object.
+ */
+#define GST_MINI_OBJECT_FLAGS(obj) (GST_MINI_OBJECT_CAST(obj)->flags)
+/**
+ * GST_MINI_OBJECT_FLAG_IS_SET:
+ * @obj: MiniObject to check for flags.
+ * @flag: Flag to check for
+ *
+ * This macro checks to see if the given flag is set.
+ */
+#define GST_MINI_OBJECT_FLAG_IS_SET(obj,flag) !!(GST_MINI_OBJECT_FLAGS (obj) & (flag))
+/**
+ * GST_MINI_OBJECT_FLAG_SET:
+ * @obj: MiniObject to set flag in.
+ * @flag: Flag to set, can by any number of bits in guint32.
+ *
+ * This macro sets the given bits.
+ */
+#define GST_MINI_OBJECT_FLAG_SET(obj,flag) (GST_MINI_OBJECT_FLAGS (obj) |= (flag))
+/**
+ * GST_MINI_OBJECT_FLAG_UNSET:
+ * @obj: MiniObject to unset flag in.
+ * @flag: Flag to set, must be a single bit in guint32.
+ *
+ * This macro usets the given bits.
+ */
+#define GST_MINI_OBJECT_FLAG_UNSET(obj,flag) (GST_MINI_OBJECT_FLAGS (obj) &= ~(flag))
+
+/**
+ * GstMiniObjectFlags:
+ * @GST_MINI_OBJECT_FLAG_LAST: first flag that can be used by subclasses.
+ *
+ * Flags for the mini object
+ */
+typedef enum
+{
+ /* padding */
+ GST_MINI_OBJECT_FLAG_LAST = (1<<4)
+} GstMiniObjectFlags;
+
+/**
+ * GST_MINI_OBJECT_REFCOUNT:
+ * @obj: a #GstMiniObject
+ *
+ * Get access to the reference count field of the mini-object.
+ */
+#define GST_MINI_OBJECT_REFCOUNT(obj) ((GST_MINI_OBJECT_CAST(obj))->refcount)
+/**
+ * GST_MINI_OBJECT_REFCOUNT_VALUE:
+ * @obj: a #GstMiniObject
+ *
+ * Get the reference count value of the mini-object.
+ */
+#define GST_MINI_OBJECT_REFCOUNT_VALUE(obj) (g_atomic_int_get (&(GST_MINI_OBJECT_CAST(obj))->refcount))
+
+/**
+ * GST_MINI_OBJECT_SIZE:
+ * @obj: a #GstMiniObject
+ *
+ * Get the allocated size of @obj.
+ */
+#define GST_MINI_OBJECT_SIZE(obj) ((GST_MINI_OBJECT_CAST(obj))->size)
+
+/**
+ * GstMiniObject:
+ * @type: the GType of the object
+ * @refcount: atomic refcount
+ * @flags: extra flags.
+ * @size: the size of the structure
+ * @copy: a copy function
+ * @dispose: a dispose function
+ * @free: the free function
+ *
+ * Base class for refcounted lightweight objects.
+ * Ref Func: gst_mini_object_ref
+ * Unref Func: gst_mini_object_unref
+ * Set Value Func: g_value_set_boxed
+ * Get Value Func: g_value_get_boxed
+ */
+struct _GstMiniObject {
+ GType type;
+
+ /*< public >*/ /* with COW */
+ gint refcount;
+ guint flags;
+ gsize size;
+
+ GstMiniObjectCopyFunction copy;
+ GstMiniObjectDisposeFunction dispose;
+ GstMiniObjectFreeFunction free;
+
+ /* < private > */
+ /* Used to keep track of weak ref notifies */
+ guint n_weak_refs;
+ struct
+ {
+ GstMiniObjectWeakNotify notify;
+ gpointer data;
+ } *weak_refs;
+};
+
+void gst_mini_object_init (GstMiniObject *mini_object,
+ GType type, gsize size);
+
+GstMiniObject * gst_mini_object_copy (const GstMiniObject *mini_object);
+gboolean gst_mini_object_is_writable (const GstMiniObject *mini_object);
+GstMiniObject * gst_mini_object_make_writable (GstMiniObject *mini_object);
+
+/* refcounting */
+GstMiniObject * gst_mini_object_ref (GstMiniObject *mini_object);
+void gst_mini_object_unref (GstMiniObject *mini_object);
+
+void gst_mini_object_weak_ref (GstMiniObject *object,
+ GstMiniObjectWeakNotify notify,
+ gpointer data);
+void gst_mini_object_weak_unref (GstMiniObject *object,
+ GstMiniObjectWeakNotify notify,
+ gpointer data);
+
+gboolean gst_mini_object_replace (GstMiniObject **olddata, GstMiniObject *newdata);
+gboolean gst_mini_object_take (GstMiniObject **olddata, GstMiniObject *newdata);
+GstMiniObject * gst_mini_object_steal (GstMiniObject **olddata);
+
+#define GST_DEFINE_MINI_OBJECT_TYPE(TypeName,type_name) \
+ G_DEFINE_BOXED_TYPE(TypeName,type_name, \
+ (GBoxedCopyFunc) gst_mini_object_ref, \
+ (GBoxedFreeFunc)gst_mini_object_unref)
+
+G_END_DECLS
+
+#endif
+
diff --git a/gst/gstobject.c b/gst/gstobject.c
new file mode 100644
index 0000000..50c4c25
--- /dev/null
+++ b/gst/gstobject.c
@@ -0,0 +1,952 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2000 Wim Taymans <wtay@chello.be>
+ * 2005 Wim Taymans <wim@fluendo.com>
+ *
+ * gstobject.c: Fundamental class used for all of GStreamer
+ *
+ * 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:gstobject
+ * @short_description: Base class for the GStreamer object hierarchy
+ *
+ * #GstObject provides a root for the object hierarchy tree filed in by the
+ * GStreamer library. It is currently a thin wrapper on top of
+ * #GObject. It is an abstract class that is not very usable on its own.
+ *
+ * #GstObject gives us basic refcounting, parenting functionality and locking.
+ * Most of the function are just extended for special GStreamer needs and can be
+ * found under the same name in the base class of #GstObject which is #GObject
+ * (e.g. g_object_ref() becomes gst_object_ref()).
+ *
+ * The most interesting difference between #GstObject and #GObject is the
+ * "floating" reference count. A #GObject is created with a reference count of
+ * 1, owned by the creator of the #GObject. (The owner of a reference is the
+ * code section that has the right to call gst_object_unref() in order to
+ * remove that reference.) A #GstObject is created with a reference count of 1
+ * also, but it isn't owned by anyone; Instead, the initial reference count
+ * of a #GstObject is "floating". The floating reference can be removed by
+ * anyone at any time, by calling gst_object_sink(). gst_object_sink() does
+ * nothing if an object is already sunk (has no floating reference).
+ *
+ * When you add a #GstElement to its parent container, the parent container will
+ * do this:
+ * <informalexample>
+ * <programlisting>
+ * gst_object_ref (GST_OBJECT (child_element));
+ * gst_object_sink (GST_OBJECT (child_element));
+ * </programlisting>
+ * </informalexample>
+ * This means that the container now owns a reference to the child element
+ * (since it called gst_object_ref()), and the child element has no floating
+ * reference.
+ *
+ * The purpose of the floating reference is to keep the child element alive
+ * until you add it to a parent container, which then manages the lifetime of
+ * the object itself:
+ * <informalexample>
+ * <programlisting>
+ * element = gst_element_factory_make (factoryname, name);
+ * // element has one floating reference to keep it alive
+ * gst_bin_add (GST_BIN (bin), element);
+ * // element has one non-floating reference owned by the container
+ * </programlisting>
+ * </informalexample>
+ *
+ * Another effect of this is, that calling gst_object_unref() on a bin object,
+ * will also destoy all the #GstElement objects in it. The same is true for
+ * calling gst_bin_remove().
+ *
+ * Special care has to be taken for all methods that gst_object_sink() an object
+ * since if the caller of those functions had a floating reference to the object,
+ * the object reference is now invalid.
+ *
+ * In contrast to #GObject instances, #GstObject adds a name property. The functions
+ * gst_object_set_name() and gst_object_get_name() are used to set/get the name
+ * of the object.
+ *
+ * Last reviewed on 2005-11-09 (0.9.4)
+ */
+
+#include "gst_private.h"
+#include "glib-compat-private.h"
+
+#include "gstobject.h"
+#include "gstmarshal.h"
+#include "gstinfo.h"
+#include "gstutils.h"
+
+#ifndef GST_DISABLE_TRACE
+#include "gsttrace.h"
+static GstAllocTrace *_gst_object_trace;
+#endif
+
+#define DEBUG_REFCOUNT
+
+/* Object signals and args */
+enum
+{
+ DEEP_NOTIFY,
+ LAST_SIGNAL
+};
+
+enum
+{
+ PROP_0,
+ PROP_NAME,
+ PROP_PARENT,
+ PROP_LAST
+};
+
+enum
+{
+ SO_OBJECT_LOADED,
+ SO_LAST_SIGNAL
+};
+
+/* maps type name quark => count */
+static GData *object_name_counts = NULL;
+
+G_LOCK_DEFINE_STATIC (object_name_mutex);
+
+static void gst_object_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+static void gst_object_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
+
+static void gst_object_dispatch_properties_changed (GObject * object,
+ guint n_pspecs, GParamSpec ** pspecs);
+
+static void gst_object_dispose (GObject * object);
+static void gst_object_finalize (GObject * object);
+
+static gboolean gst_object_set_name_default (GstObject * object);
+
+static guint gst_object_signals[LAST_SIGNAL] = { 0 };
+
+static GParamSpec *properties[PROP_LAST];
+
+G_DEFINE_ABSTRACT_TYPE (GstObject, gst_object, G_TYPE_INITIALLY_UNOWNED);
+
+static void
+gst_object_class_init (GstObjectClass * klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+#ifndef GST_DISABLE_TRACE
+ _gst_object_trace = gst_alloc_trace_register (g_type_name (GST_TYPE_OBJECT));
+#endif
+
+ gobject_class->set_property = gst_object_set_property;
+ gobject_class->get_property = gst_object_get_property;
+
+ properties[PROP_NAME] =
+ g_param_spec_string ("name", "Name", "The name of the object", NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (gobject_class, PROP_NAME,
+ properties[PROP_NAME]);
+
+ properties[PROP_PARENT] =
+ g_param_spec_object ("parent", "Parent", "The parent of the object",
+ GST_TYPE_OBJECT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (gobject_class, PROP_PARENT,
+ properties[PROP_PARENT]);
+
+ /**
+ * GstObject::deep-notify:
+ * @gstobject: a #GstObject
+ * @prop_object: the object that originated the signal
+ * @prop: the property that changed
+ *
+ * The deep notify signal is used to be notified of property changes. It is
+ * typically attached to the toplevel bin to receive notifications from all
+ * the elements contained in that bin.
+ */
+ gst_object_signals[DEEP_NOTIFY] =
+ g_signal_new ("deep-notify", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE | G_SIGNAL_DETAILED |
+ G_SIGNAL_NO_HOOKS, G_STRUCT_OFFSET (GstObjectClass, deep_notify), NULL,
+ NULL, gst_marshal_VOID__OBJECT_PARAM, G_TYPE_NONE, 2, GST_TYPE_OBJECT,
+ G_TYPE_PARAM);
+
+ klass->path_string_separator = "/";
+
+ /* see the comments at gst_object_dispatch_properties_changed */
+ gobject_class->dispatch_properties_changed
+ = GST_DEBUG_FUNCPTR (gst_object_dispatch_properties_changed);
+
+ gobject_class->dispose = gst_object_dispose;
+ gobject_class->finalize = gst_object_finalize;
+}
+
+static void
+gst_object_init (GstObject * object)
+{
+ object->lock = g_mutex_new ();
+ object->parent = NULL;
+ object->name = NULL;
+ GST_CAT_TRACE_OBJECT (GST_CAT_REFCOUNTING, object, "%p new", object);
+
+#ifndef GST_DISABLE_TRACE
+ gst_alloc_trace_new (_gst_object_trace, object);
+#endif
+
+ object->flags = 0;
+}
+
+/**
+ * gst_object_ref:
+ * @object: a #GstObject to reference
+ *
+ * Increments the reference count on @object. This function
+ * does not take the lock on @object because it relies on
+ * atomic refcounting.
+ *
+ * This object returns the input parameter to ease writing
+ * constructs like :
+ * result = gst_object_ref (object->parent);
+ *
+ * Returns: (transfer full): A pointer to @object
+ */
+gpointer
+gst_object_ref (gpointer object)
+{
+ g_return_val_if_fail (object != NULL, NULL);
+
+#ifdef DEBUG_REFCOUNT
+ GST_CAT_TRACE_OBJECT (GST_CAT_REFCOUNTING, object, "%p ref %d->%d", object,
+ ((GObject *) object)->ref_count, ((GObject *) object)->ref_count + 1);
+#endif
+ g_object_ref (object);
+
+ return object;
+}
+
+/**
+ * gst_object_unref:
+ * @object: a #GstObject to unreference
+ *
+ * Decrements the reference count on @object. If reference count hits
+ * zero, destroy @object. This function does not take the lock
+ * on @object as it relies on atomic refcounting.
+ *
+ * The unref method should never be called with the LOCK held since
+ * this might deadlock the dispose function.
+ */
+void
+gst_object_unref (gpointer object)
+{
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (((GObject *) object)->ref_count > 0);
+
+#ifdef DEBUG_REFCOUNT
+ GST_CAT_TRACE_OBJECT (GST_CAT_REFCOUNTING, object, "%p unref %d->%d", object,
+ ((GObject *) object)->ref_count, ((GObject *) object)->ref_count - 1);
+#endif
+ g_object_unref (object);
+}
+
+/**
+ * gst_object_ref_sink:
+ * @object: a #GstObject to sink
+ *
+ * Increase the reference count of @object, and possibly remove the floating
+ * reference, if @object has a floating reference.
+ *
+ * In other words, if the object is floating, then this call "assumes ownership"
+ * of the floating reference, converting it to a normal reference by clearing
+ * the floating flag while leaving the reference count unchanged. If the object
+ * is not floating, then this call adds a new normal reference increasing the
+ * reference count by one.
+ */
+gpointer
+gst_object_ref_sink (gpointer object)
+{
+ g_return_val_if_fail (object != NULL, NULL);
+
+#ifdef DEBUG_REFCOUNT
+ GST_CAT_TRACE_OBJECT (GST_CAT_REFCOUNTING, object, "%p ref_sink %d->%d",
+ object, ((GObject *) object)->ref_count,
+ ((GObject *) object)->ref_count + 1);
+#endif
+ return g_object_ref_sink (object);
+}
+
+/**
+ * gst_object_replace:
+ * @oldobj: (inout) (transfer full): pointer to a place of a #GstObject to
+ * replace
+ * @newobj: (transfer none): a new #GstObject
+ *
+ * Atomically modifies a pointer to point to a new object.
+ * The reference count of @oldobj is decreased and the reference count of
+ * @newobj is increased.
+ *
+ * Either @newobj and the value pointed to by @oldobj may be NULL.
+ *
+ * Returns: TRUE if @newobj was different from @oldobj
+ */
+gboolean
+gst_object_replace (GstObject ** oldobj, GstObject * newobj)
+{
+ GstObject *oldptr;
+
+ g_return_val_if_fail (oldobj != NULL, FALSE);
+
+#ifdef DEBUG_REFCOUNT
+ GST_CAT_TRACE (GST_CAT_REFCOUNTING, "replace %p %s (%d) with %p %s (%d)",
+ *oldobj, *oldobj ? GST_STR_NULL (GST_OBJECT_NAME (*oldobj)) : "(NONE)",
+ *oldobj ? G_OBJECT (*oldobj)->ref_count : 0,
+ newobj, newobj ? GST_STR_NULL (GST_OBJECT_NAME (newobj)) : "(NONE)",
+ newobj ? G_OBJECT (newobj)->ref_count : 0);
+#endif
+
+ oldptr = g_atomic_pointer_get ((gpointer *) oldobj);
+
+ if (G_UNLIKELY (oldptr == newobj))
+ return FALSE;
+
+ if (newobj)
+ g_object_ref (newobj);
+
+ while (G_UNLIKELY (!g_atomic_pointer_compare_and_exchange ((gpointer *)
+ oldobj, oldptr, newobj))) {
+ oldptr = g_atomic_pointer_get ((gpointer *) oldobj);
+ if (G_UNLIKELY (oldptr == newobj))
+ break;
+ }
+
+ if (oldptr)
+ g_object_unref (oldptr);
+
+ return oldptr != newobj;
+}
+
+/* dispose is called when the object has to release all links
+ * to other objects */
+static void
+gst_object_dispose (GObject * object)
+{
+ GstObject *parent;
+
+ GST_CAT_TRACE_OBJECT (GST_CAT_REFCOUNTING, object, "dispose");
+
+ GST_OBJECT_LOCK (object);
+ if ((parent = GST_OBJECT_PARENT (object)))
+ goto have_parent;
+ GST_OBJECT_PARENT (object) = NULL;
+ GST_OBJECT_UNLOCK (object);
+
+ ((GObjectClass *) gst_object_parent_class)->dispose (object);
+
+ return;
+
+ /* ERRORS */
+have_parent:
+ {
+ g_critical ("\nTrying to dispose object \"%s\", but it still has a "
+ "parent \"%s\".\nYou need to let the parent manage the "
+ "object instead of unreffing the object directly.\n",
+ GST_OBJECT_NAME (object), GST_OBJECT_NAME (parent));
+ GST_OBJECT_UNLOCK (object);
+ /* ref the object again to revive it in this error case */
+ gst_object_ref (object);
+ return;
+ }
+}
+
+/* finalize is called when the object has to free its resources */
+static void
+gst_object_finalize (GObject * object)
+{
+ GstObject *gstobject = GST_OBJECT_CAST (object);
+
+ GST_CAT_TRACE_OBJECT (GST_CAT_REFCOUNTING, object, "finalize");
+
+ g_signal_handlers_destroy (object);
+
+ g_free (gstobject->name);
+ g_mutex_free (gstobject->lock);
+
+#ifndef GST_DISABLE_TRACE
+ gst_alloc_trace_free (_gst_object_trace, object);
+#endif
+
+ ((GObjectClass *) gst_object_parent_class)->finalize (object);
+}
+
+/* Changing a GObject property of a GstObject will result in "deep-notify"
+ * signals being emitted by the object itself, as well as in each parent
+ * object. This is so that an application can connect a listener to the
+ * top-level bin to catch property-change notifications for all contained
+ * elements.
+ *
+ * MT safe.
+ */
+static void
+gst_object_dispatch_properties_changed (GObject * object,
+ guint n_pspecs, GParamSpec ** pspecs)
+{
+ GstObject *gst_object, *parent, *old_parent;
+ guint i;
+#ifndef GST_DISABLE_GST_DEBUG
+ gchar *name = NULL;
+ const gchar *debug_name;
+#endif
+
+ /* do the standard dispatching */
+ ((GObjectClass *)
+ gst_object_parent_class)->dispatch_properties_changed (object, n_pspecs,
+ pspecs);
+
+ gst_object = GST_OBJECT_CAST (object);
+#ifndef GST_DISABLE_GST_DEBUG
+ if (G_UNLIKELY (__gst_debug_min >= GST_LEVEL_LOG)) {
+ name = gst_object_get_name (gst_object);
+ debug_name = GST_STR_NULL (name);
+ } else
+ debug_name = "";
+#endif
+
+ /* now let the parent dispatch those, too */
+ parent = gst_object_get_parent (gst_object);
+ while (parent) {
+ for (i = 0; i < n_pspecs; i++) {
+ GST_CAT_LOG_OBJECT (GST_CAT_PROPERTIES, parent,
+ "deep notification from %s (%s)", debug_name, pspecs[i]->name);
+
+ g_signal_emit (parent, gst_object_signals[DEEP_NOTIFY],
+ g_quark_from_string (pspecs[i]->name), gst_object, pspecs[i]);
+ }
+
+ old_parent = parent;
+ parent = gst_object_get_parent (old_parent);
+ gst_object_unref (old_parent);
+ }
+#ifndef GST_DISABLE_GST_DEBUG
+ g_free (name);
+#endif
+}
+
+/**
+ * gst_object_default_deep_notify:
+ * @object: the #GObject that signalled the notify.
+ * @orig: a #GstObject that initiated the notify.
+ * @pspec: a #GParamSpec of the property.
+ * @excluded_props: (array zero-terminated=1) (element-type gchar*)
+ * (allow-none):a set of user-specified properties to exclude or
+ * NULL to show all changes.
+ *
+ * A default deep_notify signal callback for an object. The user data
+ * should contain a pointer to an array of strings that should be excluded
+ * from the notify. The default handler will print the new value of the property
+ * using g_print.
+ *
+ * MT safe. This function grabs and releases @object's LOCK for getting its
+ * path string.
+ */
+void
+gst_object_default_deep_notify (GObject * object, GstObject * orig,
+ GParamSpec * pspec, gchar ** excluded_props)
+{
+ GValue value = { 0, }; /* the important thing is that value.type = 0 */
+ gchar *str = NULL;
+ gchar *name = NULL;
+
+ if (pspec->flags & G_PARAM_READABLE) {
+ /* let's not print these out for excluded properties... */
+ while (excluded_props != NULL && *excluded_props != NULL) {
+ if (strcmp (pspec->name, *excluded_props) == 0)
+ return;
+ excluded_props++;
+ }
+ g_value_init (&value, pspec->value_type);
+ g_object_get_property (G_OBJECT (orig), pspec->name, &value);
+
+ /* FIXME: handle flags */
+ if (G_IS_PARAM_SPEC_ENUM (pspec)) {
+ GEnumValue *enum_value;
+ GEnumClass *klass = G_ENUM_CLASS (g_type_class_ref (pspec->value_type));
+
+ enum_value = g_enum_get_value (klass, g_value_get_enum (&value));
+
+ str = g_strdup_printf ("%s (%d)", enum_value->value_nick,
+ enum_value->value);
+ g_type_class_unref (klass);
+ } else {
+ str = g_strdup_value_contents (&value);
+ }
+ name = gst_object_get_path_string (orig);
+ g_print ("%s: %s = %s\n", name, pspec->name, str);
+ g_free (name);
+ g_free (str);
+ g_value_unset (&value);
+ } else {
+ name = gst_object_get_path_string (orig);
+ g_warning ("Parameter %s not readable in %s.", pspec->name, name);
+ g_free (name);
+ }
+}
+
+static gboolean
+gst_object_set_name_default (GstObject * object)
+{
+ const gchar *type_name;
+ gint count;
+ gchar *name;
+ GQuark q;
+ guint i, l;
+
+ /* to ensure guaranteed uniqueness across threads, only one thread
+ * may ever assign a name */
+ G_LOCK (object_name_mutex);
+
+ if (!object_name_counts) {
+ g_datalist_init (&object_name_counts);
+ }
+
+ q = g_type_qname (G_OBJECT_TYPE (object));
+ count = GPOINTER_TO_INT (g_datalist_id_get_data (&object_name_counts, q));
+ g_datalist_id_set_data (&object_name_counts, q, GINT_TO_POINTER (count + 1));
+
+ G_UNLOCK (object_name_mutex);
+
+ /* GstFooSink -> foosink<N> */
+ type_name = g_quark_to_string (q);
+ if (strncmp (type_name, "Gst", 3) == 0)
+ type_name += 3;
+ name = g_strdup_printf ("%s%d", type_name, count);
+ l = strlen (name);
+ for (i = 0; i < l; i++)
+ name[i] = g_ascii_tolower (name[i]);
+
+ GST_OBJECT_LOCK (object);
+ if (G_UNLIKELY (object->parent != NULL))
+ goto had_parent;
+
+ g_free (object->name);
+ object->name = name;
+
+ GST_OBJECT_UNLOCK (object);
+
+ return TRUE;
+
+had_parent:
+ {
+ g_free (name);
+ GST_WARNING ("parented objects can't be renamed");
+ GST_OBJECT_UNLOCK (object);
+ return FALSE;
+ }
+}
+
+/**
+ * gst_object_set_name:
+ * @object: a #GstObject
+ * @name: new name of object
+ *
+ * Sets the name of @object, or gives @object a guaranteed unique
+ * name (if @name is NULL).
+ * This function makes a copy of the provided name, so the caller
+ * retains ownership of the name it sent.
+ *
+ * Returns: TRUE if the name could be set. Since Objects that have
+ * a parent cannot be renamed, this function returns FALSE in those
+ * cases.
+ *
+ * MT safe. This function grabs and releases @object's LOCK.
+ */
+gboolean
+gst_object_set_name (GstObject * object, const gchar * name)
+{
+ gboolean result;
+
+ g_return_val_if_fail (GST_IS_OBJECT (object), FALSE);
+
+ GST_OBJECT_LOCK (object);
+
+ /* parented objects cannot be renamed */
+ if (G_UNLIKELY (object->parent != NULL))
+ goto had_parent;
+
+ if (name != NULL) {
+ g_free (object->name);
+ object->name = g_strdup (name);
+ GST_OBJECT_UNLOCK (object);
+ result = TRUE;
+ } else {
+ GST_OBJECT_UNLOCK (object);
+ result = gst_object_set_name_default (object);
+ }
+ /* FIXME-0.11: this misses a g_object_notify (object, "name"); unless called
+ * from gst_object_set_property.
+ * Ideally remove such custom setters (or make it static).
+ */
+ return result;
+
+ /* error */
+had_parent:
+ {
+ GST_WARNING ("parented objects can't be renamed");
+ GST_OBJECT_UNLOCK (object);
+ return FALSE;
+ }
+}
+
+/**
+ * gst_object_get_name:
+ * @object: a #GstObject
+ *
+ * Returns a copy of the name of @object.
+ * Caller should g_free() the return value after usage.
+ * For a nameless object, this returns NULL, which you can safely g_free()
+ * as well.
+ *
+ * Free-function: g_free
+ *
+ * Returns: (transfer full): the name of @object. g_free() after usage.
+ *
+ * MT safe. This function grabs and releases @object's LOCK.
+ */
+gchar *
+gst_object_get_name (GstObject * object)
+{
+ gchar *result = NULL;
+
+ g_return_val_if_fail (GST_IS_OBJECT (object), NULL);
+
+ GST_OBJECT_LOCK (object);
+ result = g_strdup (object->name);
+ GST_OBJECT_UNLOCK (object);
+
+ return result;
+}
+
+/**
+ * gst_object_set_parent:
+ * @object: a #GstObject
+ * @parent: new parent of object
+ *
+ * Sets the parent of @object to @parent. The object's reference count will
+ * be incremented, and any floating reference will be removed (see gst_object_ref_sink()).
+ *
+ * Returns: TRUE if @parent could be set or FALSE when @object
+ * already had a parent or @object and @parent are the same.
+ *
+ * MT safe. Grabs and releases @object's LOCK.
+ */
+gboolean
+gst_object_set_parent (GstObject * object, GstObject * parent)
+{
+ g_return_val_if_fail (GST_IS_OBJECT (object), FALSE);
+ g_return_val_if_fail (GST_IS_OBJECT (parent), FALSE);
+ g_return_val_if_fail (object != parent, FALSE);
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_REFCOUNTING, object,
+ "set parent (ref and sink)");
+
+ GST_OBJECT_LOCK (object);
+ if (G_UNLIKELY (object->parent != NULL))
+ goto had_parent;
+
+ object->parent = parent;
+ g_object_ref_sink (object);
+ GST_OBJECT_UNLOCK (object);
+
+ /* FIXME, this does not work, the deep notify takes the lock from the parent
+ * object and deadlocks when the parent holds its lock when calling this
+ * function (like _element_add_pad()) */
+ /* g_object_notify_by_pspec ((GObject *)object, properties[PROP_PARENT]); */
+
+ return TRUE;
+
+ /* ERROR handling */
+had_parent:
+ {
+ GST_CAT_DEBUG_OBJECT (GST_CAT_REFCOUNTING, object,
+ "set parent failed, object already had a parent");
+ GST_OBJECT_UNLOCK (object);
+ return FALSE;
+ }
+}
+
+/**
+ * gst_object_get_parent:
+ * @object: a #GstObject
+ *
+ * Returns the parent of @object. This function increases the refcount
+ * of the parent object so you should gst_object_unref() it after usage.
+ *
+ * Returns: (transfer full): parent of @object, this can be NULL if @object
+ * has no parent. unref after usage.
+ *
+ * MT safe. Grabs and releases @object's LOCK.
+ */
+GstObject *
+gst_object_get_parent (GstObject * object)
+{
+ GstObject *result = NULL;
+
+ g_return_val_if_fail (GST_IS_OBJECT (object), NULL);
+
+ GST_OBJECT_LOCK (object);
+ result = object->parent;
+ if (G_LIKELY (result))
+ gst_object_ref (result);
+ GST_OBJECT_UNLOCK (object);
+
+ return result;
+}
+
+/**
+ * gst_object_unparent:
+ * @object: a #GstObject to unparent
+ *
+ * Clear the parent of @object, removing the associated reference.
+ * This function decreases the refcount of @object.
+ *
+ * MT safe. Grabs and releases @object's lock.
+ */
+void
+gst_object_unparent (GstObject * object)
+{
+ GstObject *parent;
+
+ g_return_if_fail (GST_IS_OBJECT (object));
+
+ GST_OBJECT_LOCK (object);
+ parent = object->parent;
+
+ if (G_LIKELY (parent != NULL)) {
+ GST_CAT_TRACE_OBJECT (GST_CAT_REFCOUNTING, object, "unparent");
+ object->parent = NULL;
+ GST_OBJECT_UNLOCK (object);
+
+ /* g_object_notify_by_pspec ((GObject *)object, properties[PROP_PARENT]); */
+
+ gst_object_unref (object);
+ } else {
+ GST_OBJECT_UNLOCK (object);
+ }
+}
+
+/**
+ * gst_object_has_ancestor:
+ * @object: a #GstObject to check
+ * @ancestor: a #GstObject to check as ancestor
+ *
+ * Check if @object has an ancestor @ancestor somewhere up in
+ * the hierarchy. One can e.g. check if a #GstElement is inside a #GstPipeline.
+ *
+ * Returns: TRUE if @ancestor is an ancestor of @object.
+ *
+ * MT safe. Grabs and releases @object's locks.
+ */
+gboolean
+gst_object_has_ancestor (GstObject * object, GstObject * ancestor)
+{
+ GstObject *parent, *tmp;
+
+ if (!ancestor || !object)
+ return FALSE;
+
+ parent = gst_object_ref (object);
+ do {
+ if (parent == ancestor) {
+ gst_object_unref (parent);
+ return TRUE;
+ }
+
+ tmp = gst_object_get_parent (parent);
+ gst_object_unref (parent);
+ parent = tmp;
+ } while (parent);
+
+ return FALSE;
+}
+
+/**
+ * gst_object_check_uniqueness:
+ * @list: (transfer none) (element-type Gst.Object): a list of #GstObject to
+ * check through
+ * @name: the name to search for
+ *
+ * Checks to see if there is any object named @name in @list. This function
+ * does not do any locking of any kind. You might want to protect the
+ * provided list with the lock of the owner of the list. This function
+ * will lock each #GstObject in the list to compare the name, so be
+ * carefull when passing a list with a locked object.
+ *
+ * Returns: TRUE if a #GstObject named @name does not appear in @list,
+ * FALSE if it does.
+ *
+ * MT safe. Grabs and releases the LOCK of each object in the list.
+ */
+gboolean
+gst_object_check_uniqueness (GList * list, const gchar * name)
+{
+ gboolean result = TRUE;
+
+ g_return_val_if_fail (name != NULL, FALSE);
+
+ for (; list; list = g_list_next (list)) {
+ GstObject *child;
+ gboolean eq;
+
+ child = GST_OBJECT_CAST (list->data);
+
+ GST_OBJECT_LOCK (child);
+ eq = strcmp (GST_OBJECT_NAME (child), name) == 0;
+ GST_OBJECT_UNLOCK (child);
+
+ if (G_UNLIKELY (eq)) {
+ result = FALSE;
+ break;
+ }
+ }
+ return result;
+}
+
+
+static void
+gst_object_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstObject *gstobject;
+
+ gstobject = GST_OBJECT_CAST (object);
+
+ switch (prop_id) {
+ case PROP_NAME:
+ gst_object_set_name (gstobject, g_value_get_string (value));
+ break;
+ case PROP_PARENT:
+ gst_object_set_parent (gstobject, g_value_get_object (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_object_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GstObject *gstobject;
+
+ gstobject = GST_OBJECT_CAST (object);
+
+ switch (prop_id) {
+ case PROP_NAME:
+ g_value_take_string (value, gst_object_get_name (gstobject));
+ break;
+ case PROP_PARENT:
+ g_value_take_object (value, gst_object_get_parent (gstobject));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+/**
+ * gst_object_get_path_string:
+ * @object: a #GstObject
+ *
+ * Generates a string describing the path of @object in
+ * the object hierarchy. Only useful (or used) for debugging.
+ *
+ * Free-function: g_free
+ *
+ * Returns: (transfer full): a string describing the path of @object. You must
+ * g_free() the string after usage.
+ *
+ * MT safe. Grabs and releases the #GstObject's LOCK for all objects
+ * in the hierarchy.
+ */
+gchar *
+gst_object_get_path_string (GstObject * object)
+{
+ GSList *parentage;
+ GSList *parents;
+ void *parent;
+ gchar *prevpath, *path;
+ const gchar *typename;
+ gchar *component;
+ const gchar *separator;
+
+ /* ref object before adding to list */
+ gst_object_ref (object);
+ parentage = g_slist_prepend (NULL, object);
+
+ path = g_strdup ("");
+
+ /* first walk the object hierarchy to build a list of the parents,
+ * be carefull here with refcounting. */
+ do {
+ if (GST_IS_OBJECT (object)) {
+ parent = gst_object_get_parent (object);
+ /* add parents to list, refcount remains increased while
+ * we handle the object */
+ if (parent)
+ parentage = g_slist_prepend (parentage, parent);
+ } else {
+ break;
+ }
+ object = parent;
+ } while (object != NULL);
+
+ /* then walk the parent list and print them out. we need to
+ * decrease the refcounting on each element after we handled
+ * it. */
+ for (parents = parentage; parents; parents = g_slist_next (parents)) {
+ if (G_IS_OBJECT (parents->data)) {
+ typename = G_OBJECT_TYPE_NAME (parents->data);
+ } else {
+ typename = NULL;
+ }
+ if (GST_IS_OBJECT (parents->data)) {
+ GstObject *item = GST_OBJECT_CAST (parents->data);
+ GstObjectClass *oclass = GST_OBJECT_GET_CLASS (item);
+ gchar *objname = gst_object_get_name (item);
+
+ component = g_strdup_printf ("%s:%s", typename, objname);
+ separator = oclass->path_string_separator;
+ /* and unref now */
+ gst_object_unref (item);
+ g_free (objname);
+ } else {
+ if (typename) {
+ component = g_strdup_printf ("%s:%p", typename, parents->data);
+ } else {
+ component = g_strdup_printf ("%p", parents->data);
+ }
+ separator = "/";
+ }
+
+ prevpath = path;
+ path = g_strjoin (separator, prevpath, component, NULL);
+ g_free (prevpath);
+ g_free (component);
+ }
+
+ g_slist_free (parentage);
+
+ return path;
+}
diff --git a/gst/gstobject.h b/gst/gstobject.h
new file mode 100644
index 0000000..7c9ac04
--- /dev/null
+++ b/gst/gstobject.h
@@ -0,0 +1,234 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2000 Wim Taymans <wtay@chello.be>
+ * 2005 Wim Taymans <wim@fluendo.com>
+ *
+ * gstobject.h: Header for base GstObject
+ *
+ * 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_OBJECT_H__
+#define __GST_OBJECT_H__
+
+#include <gst/gstconfig.h>
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_OBJECT (gst_object_get_type ())
+#define GST_IS_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_OBJECT))
+#define GST_IS_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_OBJECT))
+#define GST_OBJECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_OBJECT, GstObjectClass))
+#define GST_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_OBJECT, GstObject))
+#define GST_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_OBJECT, GstObjectClass))
+#define GST_OBJECT_CAST(obj) ((GstObject*)(obj))
+#define GST_OBJECT_CLASS_CAST(klass) ((GstObjectClass*)(klass))
+
+/**
+ * GstObjectFlags:
+ * @GST_OBJECT_FLAG_LAST: subclasses can add additional flags starting from this flag
+ *
+ * The standard flags that an gstobject may have.
+ */
+typedef enum
+{
+ /* padding */
+ GST_OBJECT_FLAG_LAST = (1<<4)
+} GstObjectFlags;
+
+/**
+ * GST_OBJECT_REFCOUNT:
+ * @obj: a #GstObject
+ *
+ * Get access to the reference count field of the object.
+ */
+#define GST_OBJECT_REFCOUNT(obj) (((GObject*)(obj))->ref_count)
+/**
+ * GST_OBJECT_REFCOUNT_VALUE:
+ * @obj: a #GstObject
+ *
+ * Get the reference count value of the object.
+ */
+#define GST_OBJECT_REFCOUNT_VALUE(obj) g_atomic_int_get ((gint *) &GST_OBJECT_REFCOUNT(obj))
+
+/* we do a GST_OBJECT_CAST to avoid type checking, better call these
+ * function with a valid object! */
+
+/**
+ * GST_OBJECT_GET_LOCK:
+ * @obj: a #GstObject
+ *
+ * Acquire a reference to the mutex of this object.
+ */
+#define GST_OBJECT_GET_LOCK(obj) (GST_OBJECT_CAST(obj)->lock)
+/**
+ * GST_OBJECT_LOCK:
+ * @obj: a #GstObject to lock
+ *
+ * This macro will obtain a lock on the object, making serialization possible.
+ * It blocks until the lock can be obtained.
+ */
+#define GST_OBJECT_LOCK(obj) g_mutex_lock(GST_OBJECT_GET_LOCK(obj))
+/**
+ * GST_OBJECT_TRYLOCK:
+ * @obj: a #GstObject.
+ *
+ * This macro will try to obtain a lock on the object, but will return with
+ * FALSE if it can't get it immediately.
+ */
+#define GST_OBJECT_TRYLOCK(obj) g_mutex_trylock(GST_OBJECT_GET_LOCK(obj))
+/**
+ * GST_OBJECT_UNLOCK:
+ * @obj: a #GstObject to unlock.
+ *
+ * This macro releases a lock on the object.
+ */
+#define GST_OBJECT_UNLOCK(obj) g_mutex_unlock(GST_OBJECT_GET_LOCK(obj))
+
+
+/**
+ * GST_OBJECT_NAME:
+ * @obj: a #GstObject
+ *
+ * Get the name of this object
+ */
+#define GST_OBJECT_NAME(obj) (GST_OBJECT_CAST(obj)->name)
+/**
+ * GST_OBJECT_PARENT:
+ * @obj: a #GstObject
+ *
+ * Get the parent of this object
+ */
+#define GST_OBJECT_PARENT(obj) (GST_OBJECT_CAST(obj)->parent)
+
+
+/**
+ * GST_OBJECT_FLAGS:
+ * @obj: a #GstObject
+ *
+ * This macro returns the entire set of flags for the object.
+ */
+#define GST_OBJECT_FLAGS(obj) (GST_OBJECT_CAST (obj)->flags)
+/**
+ * GST_OBJECT_FLAG_IS_SET:
+ * @obj: a #GstObject
+ * @flag: Flag to check for
+ *
+ * This macro checks to see if the given flag is set.
+ */
+#define GST_OBJECT_FLAG_IS_SET(obj,flag) ((GST_OBJECT_FLAGS (obj) & (flag)) == (flag))
+/**
+ * GST_OBJECT_FLAG_SET:
+ * @obj: a #GstObject
+ * @flag: Flag to set
+ *
+ * This macro sets the given bits.
+ */
+#define GST_OBJECT_FLAG_SET(obj,flag) (GST_OBJECT_FLAGS (obj) |= (flag))
+/**
+ * GST_OBJECT_FLAG_UNSET:
+ * @obj: a #GstObject
+ * @flag: Flag to set
+ *
+ * This macro usets the given bits.
+ */
+#define GST_OBJECT_FLAG_UNSET(obj,flag) (GST_OBJECT_FLAGS (obj) &= ~(flag))
+
+
+typedef struct _GstObject GstObject;
+typedef struct _GstObjectClass GstObjectClass;
+
+/**
+ * GstObject:
+ * @lock: object LOCK
+ * @name: The name of the object
+ * @parent: this object's parent, weak ref
+ * @flags: flags for this object
+ *
+ * GStreamer base object class.
+ */
+struct _GstObject {
+ GInitiallyUnowned object;
+
+ /*< public >*/ /* with LOCK */
+ GMutex *lock; /* object LOCK */
+ gchar *name; /* object name */
+ GstObject *parent; /* this object's parent, weak ref */
+ guint32 flags;
+
+ /*< private >*/
+ gpointer _gst_reserved;
+};
+
+/**
+ * GstObjectClass:
+ * @parent_class: parent
+ * @path_string_separator: separator used by gst_object_get_path_string()
+ * @deep_notify: default signal handler
+ *
+ * GStreamer base object class.
+ */
+struct _GstObjectClass {
+ GInitiallyUnownedClass parent_class;
+
+ const gchar *path_string_separator;
+
+ /* signals */
+ void (*deep_notify) (GstObject * object, GstObject * orig, GParamSpec * pspec);
+
+ /*< public >*/
+ /* virtual methods for subclasses */
+
+ /*< private >*/
+ gpointer _gst_reserved[GST_PADDING];
+};
+
+/* normal GObject stuff */
+GType gst_object_get_type (void);
+
+/* name routines */
+gboolean gst_object_set_name (GstObject *object, const gchar *name);
+gchar* gst_object_get_name (GstObject *object);
+
+/* parentage routines */
+gboolean gst_object_set_parent (GstObject *object, GstObject *parent);
+GstObject* gst_object_get_parent (GstObject *object);
+void gst_object_unparent (GstObject *object);
+gboolean gst_object_has_ancestor (GstObject *object, GstObject *ancestor);
+
+void gst_object_default_deep_notify (GObject *object, GstObject *orig,
+ GParamSpec *pspec, gchar **excluded_props);
+
+/* refcounting + life cycle */
+gpointer gst_object_ref (gpointer object);
+void gst_object_unref (gpointer object);
+gpointer gst_object_ref_sink (gpointer object);
+
+/* replace object pointer */
+gboolean gst_object_replace (GstObject **oldobj, GstObject *newobj);
+
+/* printing out the 'path' of the object */
+gchar * gst_object_get_path_string (GstObject *object);
+
+/* misc utils */
+gboolean gst_object_check_uniqueness (GList *list, const gchar *name);
+
+G_END_DECLS
+
+#endif /* __GST_OBJECT_H__ */
+
diff --git a/gst/gstpad.c b/gst/gstpad.c
new file mode 100644
index 0000000..75732dc
--- /dev/null
+++ b/gst/gstpad.c
@@ -0,0 +1,4934 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2000 Wim Taymans <wtay@chello.be>
+ *
+ * gstpad.c: Pads for linking elements together
+ *
+ * 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:gstpad
+ * @short_description: Object contained by elements that allows links to
+ * other elements
+ * @see_also: #GstPadTemplate, #GstElement, #GstEvent
+ *
+ * A #GstElement is linked to other elements via "pads", which are extremely
+ * light-weight generic link points.
+ * After two pads are retrieved from an element with gst_element_get_pad(),
+ * the pads can be link with gst_pad_link(). (For quick links,
+ * you can also use gst_element_link(), which will make the obvious
+ * link for you if it's straightforward.)
+ *
+ * Pads are typically created from a #GstPadTemplate with
+ * gst_pad_new_from_template().
+ *
+ * Pads have #GstCaps attached to it to describe the media type they are
+ * capable of dealing with. gst_pad_get_caps() and gst_pad_set_caps() are
+ * used to manipulate the caps of the pads.
+ * Pads created from a pad template cannot set capabilities that are
+ * incompatible with the pad template capabilities.
+ *
+ * Pads without pad templates can be created with gst_pad_new(),
+ * which takes a direction and a name as an argument. If the name is NULL,
+ * then a guaranteed unique name will be assigned to it.
+ *
+ * gst_pad_get_parent() will retrieve the #GstElement that owns the pad.
+ *
+ * A #GstElement creating a pad will typically use the various
+ * gst_pad_set_*_function() calls to register callbacks for various events
+ * on the pads.
+ *
+ * GstElements will use gst_pad_push() and gst_pad_pull_range() to push out
+ * or pull in a buffer.
+ *
+ * To send a #GstEvent on a pad, use gst_pad_send_event() and
+ * gst_pad_push_event().
+ *
+ * Last reviewed on 2006-07-06 (0.10.9)
+ */
+
+#include "gst_private.h"
+
+#include "gstpad.h"
+#include "gstpadtemplate.h"
+#include "gstenumtypes.h"
+#include "gstmarshal.h"
+#include "gstutils.h"
+#include "gstinfo.h"
+#include "gsterror.h"
+#include "gstvalue.h"
+#include "glib-compat-private.h"
+
+GST_DEBUG_CATEGORY_STATIC (debug_dataflow);
+#define GST_CAT_DEFAULT GST_CAT_PADS
+
+/* Pad signals and args */
+enum
+{
+ PAD_LINKED,
+ PAD_UNLINKED,
+ /* FILL ME */
+ LAST_SIGNAL
+};
+
+enum
+{
+ PAD_PROP_0,
+ PAD_PROP_CAPS,
+ PAD_PROP_DIRECTION,
+ PAD_PROP_TEMPLATE,
+ /* FILL ME */
+};
+
+#define GST_PAD_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_PAD, GstPadPrivate))
+
+/* we have a pending and an active event on the pad. On source pads only the
+ * active event is used. On sinkpads, events are copied to the pending entry and
+ * moved to the active event when the eventfunc returned TRUE. */
+typedef struct
+{
+ GstEvent *pending;
+ GstEvent *event;
+} PadEvent;
+
+struct _GstPadPrivate
+{
+ PadEvent events[GST_EVENT_MAX_STICKY];
+
+ gint using;
+ gint probe_cookie;
+};
+
+typedef struct
+{
+ GHook hook;
+ guint cookie;
+} GstProbe;
+
+#define PROBE_COOKIE(h) (((GstProbe *)(h))->cookie)
+
+typedef struct
+{
+ GstPad *pad;
+ GstProbeType mask;
+ gpointer type_data;
+ GstProbeReturn ret;
+ gboolean pass;
+ guint cookie;
+} ProbeMarshall;
+
+static void gst_pad_dispose (GObject * object);
+static void gst_pad_finalize (GObject * object);
+static void gst_pad_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+static void gst_pad_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
+
+static GstCaps *gst_pad_get_caps_unlocked (GstPad * pad, GstCaps * filter);
+static void gst_pad_set_pad_template (GstPad * pad, GstPadTemplate * templ);
+static gboolean gst_pad_activate_default (GstPad * pad);
+static gboolean gst_pad_acceptcaps_default (GstPad * pad, GstCaps * caps);
+static void gst_pad_fixate_caps_default (GstPad * pad, GstCaps * caps);
+static GstFlowReturn gst_pad_chain_list_default (GstPad * pad,
+ GstBufferList * list);
+
+static GstObjectClass *parent_class = NULL;
+static guint gst_pad_signals[LAST_SIGNAL] = { 0 };
+
+static GParamSpec *pspec_caps = NULL;
+
+/* quarks for probe signals */
+static GQuark buffer_quark;
+static GQuark buffer_list_quark;
+static GQuark event_quark;
+
+typedef struct
+{
+ const gint ret;
+ const gchar *name;
+ GQuark quark;
+} GstFlowQuarks;
+
+static GstFlowQuarks flow_quarks[] = {
+ {GST_FLOW_CUSTOM_SUCCESS, "custom-success", 0},
+ {GST_FLOW_RESEND, "resend", 0},
+ {GST_FLOW_OK, "ok", 0},
+ {GST_FLOW_NOT_LINKED, "not-linked", 0},
+ {GST_FLOW_WRONG_STATE, "wrong-state", 0},
+ {GST_FLOW_UNEXPECTED, "unexpected", 0},
+ {GST_FLOW_NOT_NEGOTIATED, "not-negotiated", 0},
+ {GST_FLOW_ERROR, "error", 0},
+ {GST_FLOW_NOT_SUPPORTED, "not-supported", 0},
+ {GST_FLOW_CUSTOM_ERROR, "custom-error", 0}
+};
+
+/**
+ * gst_flow_get_name:
+ * @ret: a #GstFlowReturn to get the name of.
+ *
+ * Gets a string representing the given flow return.
+ *
+ * Returns: a static string with the name of the flow return.
+ */
+const gchar *
+gst_flow_get_name (GstFlowReturn ret)
+{
+ gint i;
+
+ ret = CLAMP (ret, GST_FLOW_CUSTOM_ERROR, GST_FLOW_CUSTOM_SUCCESS);
+
+ for (i = 0; i < G_N_ELEMENTS (flow_quarks); i++) {
+ if (ret == flow_quarks[i].ret)
+ return flow_quarks[i].name;
+ }
+ return "unknown";
+}
+
+/**
+ * gst_flow_to_quark:
+ * @ret: a #GstFlowReturn to get the quark of.
+ *
+ * Get the unique quark for the given GstFlowReturn.
+ *
+ * Returns: the quark associated with the flow return or 0 if an
+ * invalid return was specified.
+ */
+GQuark
+gst_flow_to_quark (GstFlowReturn ret)
+{
+ gint i;
+
+ ret = CLAMP (ret, GST_FLOW_CUSTOM_ERROR, GST_FLOW_CUSTOM_SUCCESS);
+
+ for (i = 0; i < G_N_ELEMENTS (flow_quarks); i++) {
+ if (ret == flow_quarks[i].ret)
+ return flow_quarks[i].quark;
+ }
+ return 0;
+}
+
+#define _do_init \
+{ \
+ gint i; \
+ \
+ buffer_quark = g_quark_from_static_string ("buffer"); \
+ buffer_list_quark = g_quark_from_static_string ("bufferlist"); \
+ event_quark = g_quark_from_static_string ("event"); \
+ \
+ for (i = 0; i < G_N_ELEMENTS (flow_quarks); i++) { \
+ flow_quarks[i].quark = g_quark_from_static_string (flow_quarks[i].name); \
+ } \
+ \
+ GST_DEBUG_CATEGORY_INIT (debug_dataflow, "GST_DATAFLOW", \
+ GST_DEBUG_BOLD | GST_DEBUG_FG_GREEN, "dataflow inside pads"); \
+}
+
+G_DEFINE_TYPE_WITH_CODE (GstPad, gst_pad, GST_TYPE_OBJECT, _do_init);
+
+static void
+gst_pad_class_init (GstPadClass * klass)
+{
+ GObjectClass *gobject_class;
+ GstObjectClass *gstobject_class;
+
+ gobject_class = G_OBJECT_CLASS (klass);
+ gstobject_class = GST_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (GstPadPrivate));
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ gobject_class->dispose = gst_pad_dispose;
+ gobject_class->finalize = gst_pad_finalize;
+ gobject_class->set_property = gst_pad_set_property;
+ gobject_class->get_property = gst_pad_get_property;
+
+ /**
+ * GstPad::linked:
+ * @pad: the pad that emitted the signal
+ * @peer: the peer pad that has been connected
+ *
+ * Signals that a pad has been linked to the peer pad.
+ */
+ gst_pad_signals[PAD_LINKED] =
+ g_signal_new ("linked", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GstPadClass, linked), NULL, NULL,
+ gst_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GST_TYPE_PAD);
+ /**
+ * GstPad::unlinked:
+ * @pad: the pad that emitted the signal
+ * @peer: the peer pad that has been disconnected
+ *
+ * Signals that a pad has been unlinked from the peer pad.
+ */
+ gst_pad_signals[PAD_UNLINKED] =
+ g_signal_new ("unlinked", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GstPadClass, unlinked), NULL, NULL,
+ gst_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GST_TYPE_PAD);
+
+ pspec_caps = g_param_spec_boxed ("caps", "Caps",
+ "The capabilities of the pad", GST_TYPE_CAPS,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (gobject_class, PAD_PROP_CAPS, pspec_caps);
+
+ g_object_class_install_property (gobject_class, PAD_PROP_DIRECTION,
+ g_param_spec_enum ("direction", "Direction", "The direction of the pad",
+ GST_TYPE_PAD_DIRECTION, GST_PAD_UNKNOWN,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+ /* FIXME, Make G_PARAM_CONSTRUCT_ONLY when we fix ghostpads. */
+ g_object_class_install_property (gobject_class, PAD_PROP_TEMPLATE,
+ g_param_spec_object ("template", "Template",
+ "The GstPadTemplate of this pad", GST_TYPE_PAD_TEMPLATE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ gstobject_class->path_string_separator = ".";
+
+ /* Register common function pointer descriptions */
+ GST_DEBUG_REGISTER_FUNCPTR (gst_pad_activate_default);
+ GST_DEBUG_REGISTER_FUNCPTR (gst_pad_event_default);
+ GST_DEBUG_REGISTER_FUNCPTR (gst_pad_get_query_types_default);
+ GST_DEBUG_REGISTER_FUNCPTR (gst_pad_query_default);
+ GST_DEBUG_REGISTER_FUNCPTR (gst_pad_iterate_internal_links_default);
+ GST_DEBUG_REGISTER_FUNCPTR (gst_pad_acceptcaps_default);
+ GST_DEBUG_REGISTER_FUNCPTR (gst_pad_chain_list_default);
+ GST_DEBUG_REGISTER_FUNCPTR (gst_pad_fixate_caps_default);
+}
+
+static void
+gst_pad_init (GstPad * pad)
+{
+ pad->priv = GST_PAD_GET_PRIVATE (pad);
+
+ GST_PAD_DIRECTION (pad) = GST_PAD_UNKNOWN;
+
+ GST_PAD_ACTIVATEFUNC (pad) = gst_pad_activate_default;
+ GST_PAD_EVENTFUNC (pad) = gst_pad_event_default;
+ GST_PAD_QUERYTYPEFUNC (pad) = gst_pad_get_query_types_default;
+ GST_PAD_QUERYFUNC (pad) = gst_pad_query_default;
+ GST_PAD_ITERINTLINKFUNC (pad) = gst_pad_iterate_internal_links_default;
+ GST_PAD_ACCEPTCAPSFUNC (pad) = gst_pad_acceptcaps_default;
+ GST_PAD_FIXATECAPSFUNC (pad) = gst_pad_fixate_caps_default;
+ GST_PAD_CHAINLISTFUNC (pad) = gst_pad_chain_list_default;
+
+ GST_PAD_SET_FLUSHING (pad);
+
+ g_static_rec_mutex_init (&pad->stream_rec_lock);
+
+ pad->block_cond = g_cond_new ();
+
+ g_hook_list_init (&pad->probes, sizeof (GstProbe));
+}
+
+/* called when setting the pad inactive. It removes all sticky events from
+ * the pad */
+static void
+clear_events (PadEvent events[])
+{
+ guint i;
+
+ for (i = 0; i < GST_EVENT_MAX_STICKY; i++) {
+ gst_event_replace (&events[i].event, NULL);
+ gst_event_replace (&events[i].pending, NULL);
+ }
+}
+
+/* The sticky event with @idx from the srcpad is copied to the
+ * pending event on the sinkpad (when different).
+ * This function applies the pad offsets in case of segment events.
+ * This will make sure that we send the event to the sinkpad event
+ * function when the next buffer of event arrives.
+ * Should be called with the OBJECT lock of both pads.
+ * This function returns TRUE when there is a pending event on the
+ * sinkpad */
+static gboolean
+replace_event (GstPad * srcpad, GstPad * sinkpad, guint idx)
+{
+ PadEvent *srcev, *sinkev;
+ GstEvent *event;
+ gboolean pending = FALSE;
+
+ srcev = &srcpad->priv->events[idx];
+
+ if ((event = srcev->event)) {
+ sinkev = &sinkpad->priv->events[idx];
+
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_SEGMENT:
+ {
+ GstSegment segment;
+ gint64 offset;
+
+ offset = srcpad->offset + sinkpad->offset;
+ if (offset != 0) {
+ gst_event_copy_segment (event, &segment);
+ /* adjust the base time. FIXME, check negative times, try to tweak the
+ * start to do clipping on negative times */
+ segment.base += offset;
+ /* make a new event from the updated segment */
+ event = gst_event_new_segment (&segment);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ if (sinkev->event != event) {
+ /* put in the pending entry when different */
+ gst_event_replace (&sinkev->pending, event);
+ pending = TRUE;
+ }
+ }
+ return pending;
+}
+
+
+static void
+prepare_event_update (GstPad * srcpad, GstPad * sinkpad)
+{
+ gboolean pending;
+ gint i;
+
+ /* make sure we push the events from the source to this new peer, for this we
+ * copy the events on the sinkpad and mark EVENTS_PENDING */
+ pending = FALSE;
+ for (i = 0; i < GST_EVENT_MAX_STICKY; i++)
+ pending |= replace_event (srcpad, sinkpad, i);
+
+ /* we had some new pending events, set our flag */
+ if (pending)
+ GST_OBJECT_FLAG_SET (sinkpad, GST_PAD_NEED_EVENTS);
+}
+
+/* should be called with the OBJECT_LOCK */
+static GstCaps *
+get_pad_caps (GstPad * pad)
+{
+ GstCaps *caps = NULL;
+ GstEvent *event;
+ guint idx;
+
+ idx = GST_EVENT_STICKY_IDX_TYPE (GST_EVENT_CAPS);
+ /* we can only use the caps when we have successfully send the caps
+ * event to the event function and is thus in the active entry */
+ if ((event = pad->priv->events[idx].event))
+ gst_event_parse_caps (event, &caps);
+
+ return caps;
+}
+
+static void
+gst_pad_dispose (GObject * object)
+{
+ GstPad *pad = GST_PAD_CAST (object);
+ GstPad *peer;
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_REFCOUNTING, pad, "dispose");
+
+ /* unlink the peer pad */
+ if ((peer = gst_pad_get_peer (pad))) {
+ /* window for MT unsafeness, someone else could unlink here
+ * and then we call unlink with wrong pads. The unlink
+ * function would catch this and safely return failed. */
+ if (GST_PAD_IS_SRC (pad))
+ gst_pad_unlink (pad, peer);
+ else
+ gst_pad_unlink (peer, pad);
+
+ gst_object_unref (peer);
+ }
+
+ gst_pad_set_pad_template (pad, NULL);
+
+ clear_events (pad->priv->events);
+
+ g_hook_list_clear (&pad->probes);
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+gst_pad_finalize (GObject * object)
+{
+ GstPad *pad = GST_PAD_CAST (object);
+ GstTask *task;
+
+ /* in case the task is still around, clean it up */
+ if ((task = GST_PAD_TASK (pad))) {
+ gst_task_join (task);
+ GST_PAD_TASK (pad) = NULL;
+ gst_object_unref (task);
+ }
+
+ g_static_rec_mutex_free (&pad->stream_rec_lock);
+ g_cond_free (pad->block_cond);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gst_pad_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ g_return_if_fail (GST_IS_PAD (object));
+
+ switch (prop_id) {
+ case PAD_PROP_DIRECTION:
+ GST_PAD_DIRECTION (object) = (GstPadDirection) g_value_get_enum (value);
+ break;
+ case PAD_PROP_TEMPLATE:
+ gst_pad_set_pad_template (GST_PAD_CAST (object),
+ (GstPadTemplate *) g_value_get_object (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_pad_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ g_return_if_fail (GST_IS_PAD (object));
+
+ switch (prop_id) {
+ case PAD_PROP_CAPS:
+ GST_OBJECT_LOCK (object);
+ g_value_set_boxed (value, get_pad_caps (GST_PAD_CAST (object)));
+ GST_OBJECT_UNLOCK (object);
+ break;
+ case PAD_PROP_DIRECTION:
+ g_value_set_enum (value, GST_PAD_DIRECTION (object));
+ break;
+ case PAD_PROP_TEMPLATE:
+ g_value_set_object (value, GST_PAD_PAD_TEMPLATE (object));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+/**
+ * gst_pad_new:
+ * @name: the name of the new pad.
+ * @direction: the #GstPadDirection of the pad.
+ *
+ * Creates a new pad with the given name in the given direction.
+ * If name is NULL, a guaranteed unique name (across all pads)
+ * will be assigned.
+ * This function makes a copy of the name so you can safely free the name.
+ *
+ * Returns: (transfer full): a new #GstPad, or NULL in case of an error.
+ *
+ * MT safe.
+ */
+GstPad *
+gst_pad_new (const gchar * name, GstPadDirection direction)
+{
+ return g_object_new (GST_TYPE_PAD,
+ "name", name, "direction", direction, NULL);
+}
+
+/**
+ * gst_pad_new_from_template:
+ * @templ: the pad template to use
+ * @name: the name of the element
+ *
+ * Creates a new pad with the given name from the given template.
+ * If name is NULL, a guaranteed unique name (across all pads)
+ * will be assigned.
+ * This function makes a copy of the name so you can safely free the name.
+ *
+ * Returns: (transfer full): a new #GstPad, or NULL in case of an error.
+ */
+GstPad *
+gst_pad_new_from_template (GstPadTemplate * templ, const gchar * name)
+{
+ g_return_val_if_fail (GST_IS_PAD_TEMPLATE (templ), NULL);
+
+ return g_object_new (GST_TYPE_PAD,
+ "name", name, "direction", templ->direction, "template", templ, NULL);
+}
+
+/**
+ * gst_pad_new_from_static_template:
+ * @templ: the #GstStaticPadTemplate to use
+ * @name: the name of the element
+ *
+ * Creates a new pad with the given name from the given static template.
+ * If name is NULL, a guaranteed unique name (across all pads)
+ * will be assigned.
+ * This function makes a copy of the name so you can safely free the name.
+ *
+ * Returns: (transfer full): a new #GstPad, or NULL in case of an error.
+ */
+GstPad *
+gst_pad_new_from_static_template (GstStaticPadTemplate * templ,
+ const gchar * name)
+{
+ GstPad *pad;
+ GstPadTemplate *template;
+
+ template = gst_static_pad_template_get (templ);
+ pad = gst_pad_new_from_template (template, name);
+ gst_object_unref (template);
+ return pad;
+}
+
+/**
+ * gst_pad_get_direction:
+ * @pad: a #GstPad to get the direction of.
+ *
+ * Gets the direction of the pad. The direction of the pad is
+ * decided at construction time so this function does not take
+ * the LOCK.
+ *
+ * Returns: the #GstPadDirection of the pad.
+ *
+ * MT safe.
+ */
+GstPadDirection
+gst_pad_get_direction (GstPad * pad)
+{
+ GstPadDirection result;
+
+ /* PAD_UNKNOWN is a little silly but we need some sort of
+ * error return value */
+ g_return_val_if_fail (GST_IS_PAD (pad), GST_PAD_UNKNOWN);
+
+ result = GST_PAD_DIRECTION (pad);
+
+ return result;
+}
+
+static gboolean
+gst_pad_activate_default (GstPad * pad)
+{
+ return gst_pad_activate_push (pad, TRUE);
+}
+
+static void
+pre_activate (GstPad * pad, GstActivateMode new_mode)
+{
+ switch (new_mode) {
+ case GST_ACTIVATE_PUSH:
+ case GST_ACTIVATE_PULL:
+ GST_OBJECT_LOCK (pad);
+ GST_DEBUG_OBJECT (pad, "setting ACTIVATE_MODE %d, unset flushing",
+ new_mode);
+ GST_PAD_UNSET_FLUSHING (pad);
+ GST_PAD_ACTIVATE_MODE (pad) = new_mode;
+ GST_OBJECT_UNLOCK (pad);
+ break;
+ case GST_ACTIVATE_NONE:
+ GST_OBJECT_LOCK (pad);
+ GST_DEBUG_OBJECT (pad, "setting ACTIVATE_MODE NONE, set flushing");
+ GST_PAD_SET_FLUSHING (pad);
+ GST_PAD_ACTIVATE_MODE (pad) = new_mode;
+ /* unlock blocked pads so element can resume and stop */
+ GST_PAD_BLOCK_BROADCAST (pad);
+ GST_OBJECT_UNLOCK (pad);
+ break;
+ }
+}
+
+static void
+post_activate (GstPad * pad, GstActivateMode new_mode)
+{
+ switch (new_mode) {
+ case GST_ACTIVATE_PUSH:
+ case GST_ACTIVATE_PULL:
+ /* nop */
+ break;
+ case GST_ACTIVATE_NONE:
+ /* ensures that streaming stops */
+ GST_PAD_STREAM_LOCK (pad);
+ GST_DEBUG_OBJECT (pad, "stopped streaming");
+ GST_OBJECT_LOCK (pad);
+ clear_events (pad->priv->events);
+ GST_OBJECT_UNLOCK (pad);
+ GST_PAD_STREAM_UNLOCK (pad);
+ break;
+ }
+}
+
+/**
+ * gst_pad_set_active:
+ * @pad: the #GstPad to activate or deactivate.
+ * @active: whether or not the pad should be active.
+ *
+ * Activates or deactivates the given pad.
+ * Normally called from within core state change functions.
+ *
+ * If @active, makes sure the pad is active. If it is already active, either in
+ * push or pull mode, just return. Otherwise dispatches to the pad's activate
+ * function to perform the actual activation.
+ *
+ * If not @active, checks the pad's current mode and calls
+ * gst_pad_activate_push() or gst_pad_activate_pull(), as appropriate, with a
+ * FALSE argument.
+ *
+ * Returns: #TRUE if the operation was successful.
+ *
+ * MT safe.
+ */
+gboolean
+gst_pad_set_active (GstPad * pad, gboolean active)
+{
+ GstActivateMode old;
+ gboolean ret = FALSE;
+
+ g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
+
+ GST_OBJECT_LOCK (pad);
+ old = GST_PAD_ACTIVATE_MODE (pad);
+ GST_OBJECT_UNLOCK (pad);
+
+ if (active) {
+ switch (old) {
+ case GST_ACTIVATE_PUSH:
+ GST_DEBUG_OBJECT (pad, "activating pad from push");
+ ret = TRUE;
+ break;
+ case GST_ACTIVATE_PULL:
+ GST_DEBUG_OBJECT (pad, "activating pad from pull");
+ ret = TRUE;
+ break;
+ case GST_ACTIVATE_NONE:
+ GST_DEBUG_OBJECT (pad, "activating pad from none");
+ ret = (GST_PAD_ACTIVATEFUNC (pad)) (pad);
+ break;
+ default:
+ GST_DEBUG_OBJECT (pad, "unknown activation mode!");
+ break;
+ }
+ } else {
+ switch (old) {
+ case GST_ACTIVATE_PUSH:
+ GST_DEBUG_OBJECT (pad, "deactivating pad from push");
+ ret = gst_pad_activate_push (pad, FALSE);
+ break;
+ case GST_ACTIVATE_PULL:
+ GST_DEBUG_OBJECT (pad, "deactivating pad from pull");
+ ret = gst_pad_activate_pull (pad, FALSE);
+ break;
+ case GST_ACTIVATE_NONE:
+ GST_DEBUG_OBJECT (pad, "deactivating pad from none");
+ ret = TRUE;
+ break;
+ default:
+ GST_DEBUG_OBJECT (pad, "unknown activation mode!");
+ break;
+ }
+ }
+
+ if (!ret) {
+ GST_OBJECT_LOCK (pad);
+ if (!active) {
+ g_critical ("Failed to deactivate pad %s:%s, very bad",
+ GST_DEBUG_PAD_NAME (pad));
+ } else {
+ GST_WARNING_OBJECT (pad, "Failed to activate pad");
+ }
+ GST_OBJECT_UNLOCK (pad);
+ } else {
+ if (!active) {
+ GST_OBJECT_LOCK (pad);
+ GST_OBJECT_FLAG_UNSET (pad, GST_PAD_NEED_RECONFIGURE);
+ GST_OBJECT_UNLOCK (pad);
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * gst_pad_activate_pull:
+ * @pad: the #GstPad to activate or deactivate.
+ * @active: whether or not the pad should be active.
+ *
+ * Activates or deactivates the given pad in pull mode via dispatching to the
+ * pad's activatepullfunc. For use from within pad activation functions only.
+ * When called on sink pads, will first proxy the call to the peer pad, which
+ * is expected to activate its internally linked pads from within its
+ * activate_pull function.
+ *
+ * If you don't know what this is, you probably don't want to call it.
+ *
+ * Returns: TRUE if the operation was successful.
+ *
+ * MT safe.
+ */
+gboolean
+gst_pad_activate_pull (GstPad * pad, gboolean active)
+{
+ GstActivateMode old, new;
+ GstPad *peer;
+
+ g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
+
+ GST_OBJECT_LOCK (pad);
+ old = GST_PAD_ACTIVATE_MODE (pad);
+ GST_OBJECT_UNLOCK (pad);
+
+ if (active) {
+ switch (old) {
+ case GST_ACTIVATE_PULL:
+ GST_DEBUG_OBJECT (pad, "activating pad from pull, was ok");
+ goto was_ok;
+ case GST_ACTIVATE_PUSH:
+ GST_DEBUG_OBJECT (pad,
+ "activating pad from push, deactivate push first");
+ /* pad was activate in the wrong direction, deactivate it
+ * and reactivate it in pull mode */
+ if (G_UNLIKELY (!gst_pad_activate_push (pad, FALSE)))
+ goto deactivate_failed;
+ /* fallthrough, pad is deactivated now. */
+ case GST_ACTIVATE_NONE:
+ GST_DEBUG_OBJECT (pad, "activating pad from none");
+ break;
+ }
+ } else {
+ switch (old) {
+ case GST_ACTIVATE_NONE:
+ GST_DEBUG_OBJECT (pad, "deactivating pad from none, was ok");
+ goto was_ok;
+ case GST_ACTIVATE_PUSH:
+ GST_DEBUG_OBJECT (pad, "deactivating pad from push, weird");
+ /* pad was activated in the other direction, deactivate it
+ * in push mode, this should not happen... */
+ if (G_UNLIKELY (!gst_pad_activate_push (pad, FALSE)))
+ goto deactivate_failed;
+ /* everything is fine now */
+ goto was_ok;
+ case GST_ACTIVATE_PULL:
+ GST_DEBUG_OBJECT (pad, "deactivating pad from pull");
+ break;
+ }
+ }
+
+ if (gst_pad_get_direction (pad) == GST_PAD_SINK) {
+ if ((peer = gst_pad_get_peer (pad))) {
+ GST_DEBUG_OBJECT (pad, "calling peer");
+ if (G_UNLIKELY (!gst_pad_activate_pull (peer, active)))
+ goto peer_failed;
+ gst_object_unref (peer);
+ } else {
+ /* there is no peer, this is only fatal when we activate. When we
+ * deactivate, we must assume the application has unlinked the peer and
+ * will deactivate it eventually. */
+ if (active)
+ goto not_linked;
+ else
+ GST_DEBUG_OBJECT (pad, "deactivating unlinked pad");
+ }
+ } else {
+ if (G_UNLIKELY (GST_PAD_GETRANGEFUNC (pad) == NULL))
+ goto failure; /* Can't activate pull on a src without a
+ getrange function */
+ }
+
+ new = active ? GST_ACTIVATE_PULL : GST_ACTIVATE_NONE;
+ pre_activate (pad, new);
+
+ if (GST_PAD_ACTIVATEPULLFUNC (pad)) {
+ if (G_UNLIKELY (!GST_PAD_ACTIVATEPULLFUNC (pad) (pad, active)))
+ goto failure;
+ } else {
+ /* can happen for sinks of passthrough elements */
+ }
+
+ post_activate (pad, new);
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "%s in pull mode",
+ active ? "activated" : "deactivated");
+
+ return TRUE;
+
+was_ok:
+ {
+ GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "already %s in pull mode",
+ active ? "activated" : "deactivated");
+ return TRUE;
+ }
+deactivate_failed:
+ {
+ GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad,
+ "failed to %s in switch to pull from mode %d",
+ (active ? "activate" : "deactivate"), old);
+ return FALSE;
+ }
+peer_failed:
+ {
+ GST_OBJECT_LOCK (peer);
+ GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad,
+ "activate_pull on peer (%s:%s) failed", GST_DEBUG_PAD_NAME (peer));
+ GST_OBJECT_UNLOCK (peer);
+ gst_object_unref (peer);
+ return FALSE;
+ }
+not_linked:
+ {
+ GST_CAT_INFO_OBJECT (GST_CAT_PADS, pad, "can't activate unlinked sink "
+ "pad in pull mode");
+ return FALSE;
+ }
+failure:
+ {
+ GST_OBJECT_LOCK (pad);
+ GST_CAT_INFO_OBJECT (GST_CAT_PADS, pad, "failed to %s in pull mode",
+ active ? "activate" : "deactivate");
+ GST_PAD_SET_FLUSHING (pad);
+ GST_PAD_ACTIVATE_MODE (pad) = old;
+ GST_OBJECT_UNLOCK (pad);
+ return FALSE;
+ }
+}
+
+/**
+ * gst_pad_activate_push:
+ * @pad: the #GstPad to activate or deactivate.
+ * @active: whether the pad should be active or not.
+ *
+ * Activates or deactivates the given pad in push mode via dispatching to the
+ * pad's activatepushfunc. For use from within pad activation functions only.
+ *
+ * If you don't know what this is, you probably don't want to call it.
+ *
+ * Returns: %TRUE if the operation was successful.
+ *
+ * MT safe.
+ */
+gboolean
+gst_pad_activate_push (GstPad * pad, gboolean active)
+{
+ GstActivateMode old, new;
+
+ g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
+ GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "trying to set %s in push mode",
+ active ? "activated" : "deactivated");
+
+ GST_OBJECT_LOCK (pad);
+ old = GST_PAD_ACTIVATE_MODE (pad);
+ GST_OBJECT_UNLOCK (pad);
+
+ if (active) {
+ switch (old) {
+ case GST_ACTIVATE_PUSH:
+ GST_DEBUG_OBJECT (pad, "activating pad from push, was ok");
+ goto was_ok;
+ case GST_ACTIVATE_PULL:
+ GST_DEBUG_OBJECT (pad,
+ "activating pad from push, deactivating pull first");
+ /* pad was activate in the wrong direction, deactivate it
+ * an reactivate it in push mode */
+ if (G_UNLIKELY (!gst_pad_activate_pull (pad, FALSE)))
+ goto deactivate_failed;
+ /* fallthrough, pad is deactivated now. */
+ case GST_ACTIVATE_NONE:
+ GST_DEBUG_OBJECT (pad, "activating pad from none");
+ break;
+ }
+ } else {
+ switch (old) {
+ case GST_ACTIVATE_NONE:
+ GST_DEBUG_OBJECT (pad, "deactivating pad from none, was ok");
+ goto was_ok;
+ case GST_ACTIVATE_PULL:
+ GST_DEBUG_OBJECT (pad, "deactivating pad from pull, weird");
+ /* pad was activated in the other direction, deactivate it
+ * in pull mode, this should not happen... */
+ if (G_UNLIKELY (!gst_pad_activate_pull (pad, FALSE)))
+ goto deactivate_failed;
+ /* everything is fine now */
+ goto was_ok;
+ case GST_ACTIVATE_PUSH:
+ GST_DEBUG_OBJECT (pad, "deactivating pad from push");
+ break;
+ }
+ }
+
+ new = active ? GST_ACTIVATE_PUSH : GST_ACTIVATE_NONE;
+ pre_activate (pad, new);
+
+ if (GST_PAD_ACTIVATEPUSHFUNC (pad)) {
+ if (G_UNLIKELY (!GST_PAD_ACTIVATEPUSHFUNC (pad) (pad, active))) {
+ goto failure;
+ }
+ } else {
+ /* quite ok, element relies on state change func to prepare itself */
+ }
+
+ post_activate (pad, new);
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "%s in push mode",
+ active ? "activated" : "deactivated");
+ return TRUE;
+
+was_ok:
+ {
+ GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "already %s in push mode",
+ active ? "activated" : "deactivated");
+ return TRUE;
+ }
+deactivate_failed:
+ {
+ GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad,
+ "failed to %s in switch to push from mode %d",
+ (active ? "activate" : "deactivate"), old);
+ return FALSE;
+ }
+failure:
+ {
+ GST_OBJECT_LOCK (pad);
+ GST_CAT_INFO_OBJECT (GST_CAT_PADS, pad, "failed to %s in push mode",
+ active ? "activate" : "deactivate");
+ GST_PAD_SET_FLUSHING (pad);
+ GST_PAD_ACTIVATE_MODE (pad) = old;
+ GST_OBJECT_UNLOCK (pad);
+ return FALSE;
+ }
+}
+
+/**
+ * gst_pad_is_active:
+ * @pad: the #GstPad to query
+ *
+ * Query if a pad is active
+ *
+ * Returns: TRUE if the pad is active.
+ *
+ * MT safe.
+ */
+gboolean
+gst_pad_is_active (GstPad * pad)
+{
+ gboolean result = FALSE;
+
+ g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
+
+ GST_OBJECT_LOCK (pad);
+ result = GST_PAD_IS_ACTIVE (pad);
+ GST_OBJECT_UNLOCK (pad);
+
+ return result;
+}
+
+/**
+ * gst_pad_add_probe:
+ * @pad: the #GstPad to add the probe to
+ * @mask: the probe mask
+ * @callback: #GstPadProbeCallback that will be called with notifications of
+ * the pad state
+ * @user_data: (closure): user data passed to the callback
+ * @destroy_data: #GDestroyNotify for user_data
+ *
+ * Be notified of different states of pads. The provided callback is called for
+ * every state that matches @mask.
+ *
+ * <note>
+ * Pad probe handlers are only called for source pads in push mode
+ * and sink pads in pull mode.
+ * </note>
+ *
+ * Returns: an id or 0 on error. The id can be used to remove the probe with
+ * gst_pad_remove_probe().
+ *
+ * MT safe.
+ */
+gulong
+gst_pad_add_probe (GstPad * pad, GstProbeType mask,
+ GstPadProbeCallback callback, gpointer user_data,
+ GDestroyNotify destroy_data)
+{
+ GHook *hook;
+ gulong res;
+
+ g_return_val_if_fail (GST_IS_PAD (pad), 0);
+ g_return_val_if_fail (mask != 0, 0);
+
+ GST_OBJECT_LOCK (pad);
+ /* make a new probe */
+ hook = g_hook_alloc (&pad->probes);
+
+ GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "adding probe for mask 0x%08x",
+ mask);
+
+ /* when no contraints are given for the types, assume all types are
+ * acceptable */
+ if ((mask & GST_PROBE_TYPE_DATA) == 0)
+ mask |= GST_PROBE_TYPE_DATA;
+ if ((mask & GST_PROBE_TYPE_SCHEDULING) == 0)
+ mask |= GST_PROBE_TYPE_SCHEDULING;
+
+ /* store our flags and other fields */
+ hook->flags |= (mask << G_HOOK_FLAG_USER_SHIFT);
+ hook->func = callback;
+ hook->data = user_data;
+ hook->destroy = destroy_data;
+ PROBE_COOKIE (hook) = 0;
+
+ /* incremenent cookie so that the new hook get's called */
+ pad->priv->probe_cookie++;
+
+ /* add the probe */
+ g_hook_prepend (&pad->probes, hook);
+ pad->num_probes++;
+
+ /* get the id of the hook, we return this and it can be used to remove the
+ * probe later */
+ res = hook->hook_id;
+
+ GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "got probe id %lu", res);
+
+ if (mask & GST_PROBE_TYPE_BLOCKING) {
+ /* we have a block probe */
+ pad->num_blocked++;
+ GST_OBJECT_FLAG_SET (pad, GST_PAD_BLOCKED);
+ GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "added blocking probe, "
+ "now %d blocking probes", pad->num_blocked);
+ }
+
+ /* call the callback if we need to be called for idle callbacks */
+ if ((mask & GST_PROBE_TYPE_IDLE) && (callback != NULL)) {
+ if (pad->priv->using > 0) {
+ /* the pad is in use, we can't signal the idle callback yet. Since we set the
+ * flag above, the last thread to leave the push will do the callback. New
+ * threads going into the push will block. */
+ GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
+ "pad is in use, delay idle callback");
+ GST_OBJECT_UNLOCK (pad);
+ } else {
+ /* the pad is idle now, we can signal the idle callback now */
+ GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
+ "pad is idle, trigger idle callback");
+ GST_OBJECT_UNLOCK (pad);
+
+ callback (pad, GST_PROBE_TYPE_IDLE, NULL, user_data);
+ }
+ } else {
+ GST_OBJECT_UNLOCK (pad);
+ }
+ return res;
+}
+
+static void
+cleanup_hook (GstPad * pad, GHook * hook)
+{
+ GstProbeType type;
+
+ type = (hook->flags) >> G_HOOK_FLAG_USER_SHIFT;
+
+ if (type & GST_PROBE_TYPE_BLOCKING) {
+ /* unblock when we remove the last blocking probe */
+ pad->num_blocked--;
+ GST_DEBUG_OBJECT (pad, "remove blocking probe, now %d left",
+ pad->num_blocked);
+ if (pad->num_blocked == 0) {
+ GST_DEBUG_OBJECT (pad, "last blocking probe removed, unblocking");
+ GST_OBJECT_FLAG_UNSET (pad, GST_PAD_BLOCKED);
+ GST_PAD_BLOCK_BROADCAST (pad);
+ }
+ }
+ g_hook_destroy_link (&pad->probes, hook);
+ pad->num_probes--;
+}
+
+/**
+ * gst_pad_remove_probe:
+ * @pad: the #GstPad with the probe
+ * @id: the probe id to remove
+ *
+ * Remove the probe with @id from @pad.
+ *
+ * MT safe.
+ */
+void
+gst_pad_remove_probe (GstPad * pad, gulong id)
+{
+ GHook *hook;
+
+ g_return_if_fail (GST_IS_PAD (pad));
+
+ GST_OBJECT_LOCK (pad);
+
+ hook = g_hook_get (&pad->probes, id);
+ if (hook == NULL)
+ goto not_found;
+
+ GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "removing hook %ld",
+ hook->hook_id);
+ cleanup_hook (pad, hook);
+ GST_OBJECT_UNLOCK (pad);
+
+ return;
+
+not_found:
+ {
+ GST_OBJECT_UNLOCK (pad);
+ g_warning ("%s: pad `%p' has no probe with id `%lu'", G_STRLOC, pad, id);
+ return;
+ }
+}
+
+/**
+ * gst_pad_is_blocked:
+ * @pad: the #GstPad to query
+ *
+ * Checks if the pad is blocked or not. This function returns the
+ * last requested state of the pad. It is not certain that the pad
+ * is actually blocking at this point (see gst_pad_is_blocking()).
+ *
+ * Returns: TRUE if the pad is blocked.
+ *
+ * MT safe.
+ */
+gboolean
+gst_pad_is_blocked (GstPad * pad)
+{
+ gboolean result = FALSE;
+
+ g_return_val_if_fail (GST_IS_PAD (pad), result);
+
+ GST_OBJECT_LOCK (pad);
+ result = GST_OBJECT_FLAG_IS_SET (pad, GST_PAD_BLOCKED);
+ GST_OBJECT_UNLOCK (pad);
+
+ return result;
+}
+
+/**
+ * gst_pad_is_blocking:
+ * @pad: the #GstPad to query
+ *
+ * Checks if the pad is blocking or not. This is a guaranteed state
+ * of whether the pad is actually blocking on a #GstBuffer or a #GstEvent.
+ *
+ * Returns: TRUE if the pad is blocking.
+ *
+ * MT safe.
+ *
+ * Since: 0.10.11
+ */
+gboolean
+gst_pad_is_blocking (GstPad * pad)
+{
+ gboolean result = FALSE;
+
+ g_return_val_if_fail (GST_IS_PAD (pad), result);
+
+ GST_OBJECT_LOCK (pad);
+ /* the blocking flag is only valid if the pad is not flushing */
+ result = GST_OBJECT_FLAG_IS_SET (pad, GST_PAD_BLOCKING) &&
+ !GST_OBJECT_FLAG_IS_SET (pad, GST_PAD_FLUSHING);
+ GST_OBJECT_UNLOCK (pad);
+
+ return result;
+}
+
+/**
+ * gst_pad_check_reconfigure:
+ * @pad: the #GstPad to check
+ *
+ * Check and clear the #GST_PAD_NEED_RECONFIGURE flag on @pad and return %TRUE
+ * if the flag was set.
+ *
+ * Returns: %TRUE is the GST_PAD_NEED_RECONFIGURE flag was set on @pad.
+ */
+gboolean
+gst_pad_check_reconfigure (GstPad * pad)
+{
+ gboolean reconfigure;
+
+ g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
+
+ GST_OBJECT_LOCK (pad);
+ reconfigure = GST_PAD_NEEDS_RECONFIGURE (pad);
+ GST_OBJECT_FLAG_UNSET (pad, GST_PAD_NEED_RECONFIGURE);
+ GST_OBJECT_UNLOCK (pad);
+
+ return reconfigure;
+}
+
+/**
+ * gst_pad_mark_reconfigure:
+ * @pad: the #GstPad to mark
+ *
+ * Mark a pad for needing reconfiguration. The next call to
+ * gst_pad_check_reconfigure() will return %TRUE after this call.
+ */
+void
+gst_pad_mark_reconfigure (GstPad * pad)
+{
+ g_return_if_fail (GST_IS_PAD (pad));
+
+ GST_OBJECT_LOCK (pad);
+ GST_OBJECT_FLAG_SET (pad, GST_PAD_NEED_RECONFIGURE);
+ GST_OBJECT_UNLOCK (pad);
+}
+
+/**
+ * gst_pad_set_activate_function:
+ * @pad: a #GstPad.
+ * @activate: the #GstPadActivateFunction to set.
+ *
+ * Sets the given activate function for @pad. The activate function will
+ * dispatch to gst_pad_activate_push() or gst_pad_activate_pull() to perform
+ * the actual activation. Only makes sense to set on sink pads.
+ *
+ * Call this function if your sink pad can start a pull-based task.
+ */
+void
+gst_pad_set_activate_function (GstPad * pad, GstPadActivateFunction activate)
+{
+ g_return_if_fail (GST_IS_PAD (pad));
+
+ GST_PAD_ACTIVATEFUNC (pad) = activate;
+ GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "activatefunc set to %s",
+ GST_DEBUG_FUNCPTR_NAME (activate));
+}
+
+/**
+ * gst_pad_set_activatepull_function:
+ * @pad: a #GstPad.
+ * @activatepull: the #GstPadActivateModeFunction to set.
+ *
+ * Sets the given activate_pull function for the pad. An activate_pull function
+ * prepares the element and any upstream connections for pulling. See XXX
+ * part-activation.txt for details.
+ */
+void
+gst_pad_set_activatepull_function (GstPad * pad,
+ GstPadActivateModeFunction activatepull)
+{
+ g_return_if_fail (GST_IS_PAD (pad));
+
+ GST_PAD_ACTIVATEPULLFUNC (pad) = activatepull;
+ GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "activatepullfunc set to %s",
+ GST_DEBUG_FUNCPTR_NAME (activatepull));
+}
+
+/**
+ * gst_pad_set_activatepush_function:
+ * @pad: a #GstPad.
+ * @activatepush: the #GstPadActivateModeFunction to set.
+ *
+ * Sets the given activate_push function for the pad. An activate_push function
+ * prepares the element for pushing. See XXX part-activation.txt for details.
+ */
+void
+gst_pad_set_activatepush_function (GstPad * pad,
+ GstPadActivateModeFunction activatepush)
+{
+ g_return_if_fail (GST_IS_PAD (pad));
+
+ GST_PAD_ACTIVATEPUSHFUNC (pad) = activatepush;
+ GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "activatepushfunc set to %s",
+ GST_DEBUG_FUNCPTR_NAME (activatepush));
+}
+
+/**
+ * gst_pad_set_chain_function:
+ * @pad: a sink #GstPad.
+ * @chain: the #GstPadChainFunction to set.
+ *
+ * Sets the given chain function for the pad. The chain function is called to
+ * process a #GstBuffer input buffer. see #GstPadChainFunction for more details.
+ */
+void
+gst_pad_set_chain_function (GstPad * pad, GstPadChainFunction chain)
+{
+ g_return_if_fail (GST_IS_PAD (pad));
+ g_return_if_fail (GST_PAD_IS_SINK (pad));
+
+ GST_PAD_CHAINFUNC (pad) = chain;
+ GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "chainfunc set to %s",
+ GST_DEBUG_FUNCPTR_NAME (chain));
+}
+
+/**
+ * gst_pad_set_chain_list_function:
+ * @pad: a sink #GstPad.
+ * @chainlist: the #GstPadChainListFunction to set.
+ *
+ * Sets the given chain list function for the pad. The chainlist function is
+ * called to process a #GstBufferList input buffer list. See
+ * #GstPadChainListFunction for more details.
+ *
+ * Since: 0.10.24
+ */
+void
+gst_pad_set_chain_list_function (GstPad * pad,
+ GstPadChainListFunction chainlist)
+{
+ g_return_if_fail (GST_IS_PAD (pad));
+ g_return_if_fail (GST_PAD_IS_SINK (pad));
+
+ GST_PAD_CHAINLISTFUNC (pad) = chainlist;
+ GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "chainlistfunc set to %s",
+ GST_DEBUG_FUNCPTR_NAME (chainlist));
+}
+
+/**
+ * gst_pad_set_getrange_function:
+ * @pad: a source #GstPad.
+ * @get: the #GstPadGetRangeFunction to set.
+ *
+ * Sets the given getrange function for the pad. The getrange function is
+ * called to produce a new #GstBuffer to start the processing pipeline. see
+ * #GstPadGetRangeFunction for a description of the getrange function.
+ */
+void
+gst_pad_set_getrange_function (GstPad * pad, GstPadGetRangeFunction get)
+{
+ g_return_if_fail (GST_IS_PAD (pad));
+ g_return_if_fail (GST_PAD_IS_SRC (pad));
+
+ GST_PAD_GETRANGEFUNC (pad) = get;
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "getrangefunc set to %s",
+ GST_DEBUG_FUNCPTR_NAME (get));
+}
+
+/**
+ * gst_pad_set_event_function:
+ * @pad: a #GstPad of either direction.
+ * @event: the #GstPadEventFunction to set.
+ *
+ * Sets the given event handler for the pad.
+ */
+void
+gst_pad_set_event_function (GstPad * pad, GstPadEventFunction event)
+{
+ g_return_if_fail (GST_IS_PAD (pad));
+
+ GST_PAD_EVENTFUNC (pad) = event;
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "eventfunc for set to %s",
+ GST_DEBUG_FUNCPTR_NAME (event));
+}
+
+/**
+ * gst_pad_set_query_function:
+ * @pad: a #GstPad of either direction.
+ * @query: the #GstPadQueryFunction to set.
+ *
+ * Set the given query function for the pad.
+ */
+void
+gst_pad_set_query_function (GstPad * pad, GstPadQueryFunction query)
+{
+ g_return_if_fail (GST_IS_PAD (pad));
+
+ GST_PAD_QUERYFUNC (pad) = query;
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "queryfunc set to %s",
+ GST_DEBUG_FUNCPTR_NAME (query));
+}
+
+/**
+ * gst_pad_set_query_type_function:
+ * @pad: a #GstPad of either direction.
+ * @type_func: the #GstPadQueryTypeFunction to set.
+ *
+ * Set the given query type function for the pad.
+ */
+void
+gst_pad_set_query_type_function (GstPad * pad,
+ GstPadQueryTypeFunction type_func)
+{
+ g_return_if_fail (GST_IS_PAD (pad));
+
+ GST_PAD_QUERYTYPEFUNC (pad) = type_func;
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "querytypefunc set to %s",
+ GST_DEBUG_FUNCPTR_NAME (type_func));
+}
+
+/**
+ * gst_pad_get_query_types:
+ * @pad: a #GstPad.
+ *
+ * Get an array of supported queries that can be performed
+ * on this pad.
+ *
+ * Returns: (transfer none) (array zero-terminated=1): a zero-terminated array
+ * of #GstQueryType.
+ */
+const GstQueryType *
+gst_pad_get_query_types (GstPad * pad)
+{
+ GstPadQueryTypeFunction func;
+
+ g_return_val_if_fail (GST_IS_PAD (pad), NULL);
+
+ if (G_UNLIKELY ((func = GST_PAD_QUERYTYPEFUNC (pad)) == NULL))
+ goto no_func;
+
+ return func (pad);
+
+no_func:
+ {
+ return NULL;
+ }
+}
+
+static gboolean
+gst_pad_get_query_types_dispatcher (GstPad * pad, const GstQueryType ** data)
+{
+ GstPad *peer;
+
+ if ((peer = gst_pad_get_peer (pad))) {
+ *data = gst_pad_get_query_types (peer);
+ gst_object_unref (peer);
+ }
+ return TRUE;
+}
+
+/**
+ * gst_pad_get_query_types_default:
+ * @pad: a #GstPad.
+ *
+ * Invoke the default query types function on the pad. This function will get
+ * the supported query type from the peer of an internally linked pad of @pad.
+ *
+ * Returns: (transfer none) (array zero-terminated=1): a zero-terminated array
+ * of #GstQueryType, or NULL if none of the internally-linked pads has a
+ * query types function.
+ */
+const GstQueryType *
+gst_pad_get_query_types_default (GstPad * pad)
+{
+ GstQueryType *result = NULL;
+
+ g_return_val_if_fail (GST_IS_PAD (pad), NULL);
+
+ gst_pad_forward (pad, (GstPadForwardFunction)
+ gst_pad_get_query_types_dispatcher, &result);
+
+ return result;
+}
+
+/**
+ * gst_pad_set_iterate_internal_links_function:
+ * @pad: a #GstPad of either direction.
+ * @iterintlink: the #GstPadIterIntLinkFunction to set.
+ *
+ * Sets the given internal link iterator function for the pad.
+ *
+ * Since: 0.10.21
+ */
+void
+gst_pad_set_iterate_internal_links_function (GstPad * pad,
+ GstPadIterIntLinkFunction iterintlink)
+{
+ g_return_if_fail (GST_IS_PAD (pad));
+
+ GST_PAD_ITERINTLINKFUNC (pad) = iterintlink;
+ GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "internal link iterator set to %s",
+ GST_DEBUG_FUNCPTR_NAME (iterintlink));
+}
+
+/**
+ * gst_pad_set_link_function:
+ * @pad: a #GstPad.
+ * @link: the #GstPadLinkFunction to set.
+ *
+ * Sets the given link function for the pad. It will be called when
+ * the pad is linked with another pad.
+ *
+ * The return value #GST_PAD_LINK_OK should be used when the connection can be
+ * made.
+ *
+ * The return value #GST_PAD_LINK_REFUSED should be used when the connection
+ * cannot be made for some reason.
+ *
+ * If @link is installed on a source pad, it should call the #GstPadLinkFunction
+ * of the peer sink pad, if present.
+ */
+void
+gst_pad_set_link_function (GstPad * pad, GstPadLinkFunction link)
+{
+ g_return_if_fail (GST_IS_PAD (pad));
+
+ GST_PAD_LINKFUNC (pad) = link;
+ GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "linkfunc set to %s",
+ GST_DEBUG_FUNCPTR_NAME (link));
+}
+
+/**
+ * gst_pad_set_unlink_function:
+ * @pad: a #GstPad.
+ * @unlink: the #GstPadUnlinkFunction to set.
+ *
+ * Sets the given unlink function for the pad. It will be called
+ * when the pad is unlinked.
+ */
+void
+gst_pad_set_unlink_function (GstPad * pad, GstPadUnlinkFunction unlink)
+{
+ g_return_if_fail (GST_IS_PAD (pad));
+
+ GST_PAD_UNLINKFUNC (pad) = unlink;
+ GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "unlinkfunc set to %s",
+ GST_DEBUG_FUNCPTR_NAME (unlink));
+}
+
+/**
+ * gst_pad_set_getcaps_function:
+ * @pad: a #GstPad.
+ * @getcaps: the #GstPadGetCapsFunction to set.
+ *
+ * Sets the given getcaps function for the pad. @getcaps should return the
+ * allowable caps for a pad in the context of the element's state, its link to
+ * other elements, and the devices or files it has opened. These caps must be a
+ * subset of the pad template caps. In the NULL state with no links, @getcaps
+ * should ideally return the same caps as the pad template. In rare
+ * circumstances, an object property can affect the caps returned by @getcaps,
+ * but this is discouraged.
+ *
+ * You do not need to call this function if @pad's allowed caps are always the
+ * same as the pad template caps. This can only be true if the padtemplate
+ * has fixed simple caps.
+ *
+ * For most filters, the caps returned by @getcaps is directly affected by the
+ * allowed caps on other pads. For demuxers and decoders, the caps returned by
+ * the srcpad's getcaps function is directly related to the stream data. Again,
+ * @getcaps should return the most specific caps it reasonably can, since this
+ * helps with autoplugging.
+ *
+ * Note that the return value from @getcaps is owned by the caller, so the
+ * caller should unref the caps after usage.
+ */
+void
+gst_pad_set_getcaps_function (GstPad * pad, GstPadGetCapsFunction getcaps)
+{
+ g_return_if_fail (GST_IS_PAD (pad));
+
+ GST_PAD_GETCAPSFUNC (pad) = getcaps;
+ GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "getcapsfunc set to %s",
+ GST_DEBUG_FUNCPTR_NAME (getcaps));
+}
+
+/**
+ * gst_pad_set_acceptcaps_function:
+ * @pad: a #GstPad.
+ * @acceptcaps: the #GstPadAcceptCapsFunction to set.
+ *
+ * Sets the given acceptcaps function for the pad. The acceptcaps function
+ * will be called to check if the pad can accept the given caps. Setting the
+ * acceptcaps function to NULL restores the default behaviour of allowing
+ * any caps that matches the caps from gst_pad_get_caps().
+ */
+void
+gst_pad_set_acceptcaps_function (GstPad * pad,
+ GstPadAcceptCapsFunction acceptcaps)
+{
+ g_return_if_fail (GST_IS_PAD (pad));
+
+ GST_PAD_ACCEPTCAPSFUNC (pad) = acceptcaps;
+ GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "acceptcapsfunc set to %s",
+ GST_DEBUG_FUNCPTR_NAME (acceptcaps));
+}
+
+/**
+ * gst_pad_set_fixatecaps_function:
+ * @pad: a #GstPad.
+ * @fixatecaps: the #GstPadFixateCapsFunction to set.
+ *
+ * Sets the given fixatecaps function for the pad. The fixatecaps function
+ * will be called whenever the default values for a GstCaps needs to be
+ * filled in.
+ */
+void
+gst_pad_set_fixatecaps_function (GstPad * pad,
+ GstPadFixateCapsFunction fixatecaps)
+{
+ g_return_if_fail (GST_IS_PAD (pad));
+
+ GST_PAD_FIXATECAPSFUNC (pad) = fixatecaps;
+ GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "fixatecapsfunc set to %s",
+ GST_DEBUG_FUNCPTR_NAME (fixatecaps));
+}
+
+/**
+ * gst_pad_unlink:
+ * @srcpad: the source #GstPad to unlink.
+ * @sinkpad: the sink #GstPad to unlink.
+ *
+ * Unlinks the source pad from the sink pad. Will emit the #GstPad::unlinked
+ * signal on both pads.
+ *
+ * Returns: TRUE if the pads were unlinked. This function returns FALSE if
+ * the pads were not linked together.
+ *
+ * MT safe.
+ */
+gboolean
+gst_pad_unlink (GstPad * srcpad, GstPad * sinkpad)
+{
+ gboolean result = FALSE;
+ GstElement *parent = NULL;
+ gint i;
+
+ g_return_val_if_fail (GST_IS_PAD (srcpad), FALSE);
+ g_return_val_if_fail (GST_PAD_IS_SRC (srcpad), FALSE);
+ g_return_val_if_fail (GST_IS_PAD (sinkpad), FALSE);
+ g_return_val_if_fail (GST_PAD_IS_SINK (sinkpad), FALSE);
+
+ GST_CAT_INFO (GST_CAT_ELEMENT_PADS, "unlinking %s:%s(%p) and %s:%s(%p)",
+ GST_DEBUG_PAD_NAME (srcpad), srcpad,
+ GST_DEBUG_PAD_NAME (sinkpad), sinkpad);
+
+ /* We need to notify the parent before taking any pad locks as the bin in
+ * question might be waiting for a lock on the pad while holding its lock
+ * that our message will try to take. */
+ if ((parent = GST_ELEMENT_CAST (gst_pad_get_parent (srcpad)))) {
+ if (GST_IS_ELEMENT (parent)) {
+ gst_element_post_message (parent,
+ gst_message_new_structure_change (GST_OBJECT_CAST (sinkpad),
+ GST_STRUCTURE_CHANGE_TYPE_PAD_UNLINK, parent, TRUE));
+ } else {
+ gst_object_unref (parent);
+ parent = NULL;
+ }
+ }
+
+ GST_OBJECT_LOCK (srcpad);
+ GST_OBJECT_LOCK (sinkpad);
+
+ if (G_UNLIKELY (GST_PAD_PEER (srcpad) != sinkpad))
+ goto not_linked_together;
+
+ if (GST_PAD_UNLINKFUNC (srcpad)) {
+ GST_PAD_UNLINKFUNC (srcpad) (srcpad);
+ }
+ if (GST_PAD_UNLINKFUNC (sinkpad)) {
+ GST_PAD_UNLINKFUNC (sinkpad) (sinkpad);
+ }
+
+ /* first clear peers */
+ GST_PAD_PEER (srcpad) = NULL;
+ GST_PAD_PEER (sinkpad) = NULL;
+
+ /* clear pending caps if any */
+ for (i = 0; i < GST_EVENT_MAX_STICKY; i++)
+ gst_event_replace (&sinkpad->priv->events[i].pending, NULL);
+
+ GST_OBJECT_UNLOCK (sinkpad);
+ GST_OBJECT_UNLOCK (srcpad);
+
+ /* fire off a signal to each of the pads telling them
+ * that they've been unlinked */
+ g_signal_emit (srcpad, gst_pad_signals[PAD_UNLINKED], 0, sinkpad);
+ g_signal_emit (sinkpad, gst_pad_signals[PAD_UNLINKED], 0, srcpad);
+
+ GST_CAT_INFO (GST_CAT_ELEMENT_PADS, "unlinked %s:%s and %s:%s",
+ GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad));
+
+ result = TRUE;
+
+done:
+ if (parent != NULL) {
+ gst_element_post_message (parent,
+ gst_message_new_structure_change (GST_OBJECT_CAST (sinkpad),
+ GST_STRUCTURE_CHANGE_TYPE_PAD_UNLINK, parent, FALSE));
+ gst_object_unref (parent);
+ }
+ return result;
+
+ /* ERRORS */
+not_linked_together:
+ {
+ /* we do not emit a warning in this case because unlinking cannot
+ * be made MT safe.*/
+ GST_OBJECT_UNLOCK (sinkpad);
+ GST_OBJECT_UNLOCK (srcpad);
+ goto done;
+ }
+}
+
+/**
+ * gst_pad_is_linked:
+ * @pad: pad to check
+ *
+ * Checks if a @pad is linked to another pad or not.
+ *
+ * Returns: TRUE if the pad is linked, FALSE otherwise.
+ *
+ * MT safe.
+ */
+gboolean
+gst_pad_is_linked (GstPad * pad)
+{
+ gboolean result;
+
+ g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
+
+ GST_OBJECT_LOCK (pad);
+ result = (GST_PAD_PEER (pad) != NULL);
+ GST_OBJECT_UNLOCK (pad);
+
+ return result;
+}
+
+/* get the caps from both pads and see if the intersection
+ * is not empty.
+ *
+ * This function should be called with the pad LOCK on both
+ * pads
+ */
+static gboolean
+gst_pad_link_check_compatible_unlocked (GstPad * src, GstPad * sink,
+ GstPadLinkCheck flags)
+{
+ GstCaps *srccaps = NULL;
+ GstCaps *sinkcaps = NULL;
+ gboolean compatible = FALSE;
+
+ if (!(flags & (GST_PAD_LINK_CHECK_CAPS | GST_PAD_LINK_CHECK_TEMPLATE_CAPS)))
+ return TRUE;
+
+ /* Doing the expensive caps checking takes priority over only checking the template caps */
+ if (flags & GST_PAD_LINK_CHECK_CAPS) {
+ srccaps = gst_pad_get_caps_unlocked (src, NULL);
+ sinkcaps = gst_pad_get_caps_unlocked (sink, NULL);
+ } else {
+ /* If one of the two pads doesn't have a template, consider the intersection
+ * as valid.*/
+ if (G_UNLIKELY ((GST_PAD_PAD_TEMPLATE (src) == NULL)
+ || (GST_PAD_PAD_TEMPLATE (sink) == NULL))) {
+ compatible = TRUE;
+ goto done;
+ }
+ srccaps = gst_caps_ref (GST_PAD_TEMPLATE_CAPS (GST_PAD_PAD_TEMPLATE (src)));
+ sinkcaps =
+ gst_caps_ref (GST_PAD_TEMPLATE_CAPS (GST_PAD_PAD_TEMPLATE (sink)));
+ }
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, src, "src caps %" GST_PTR_FORMAT,
+ srccaps);
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, sink, "sink caps %" GST_PTR_FORMAT,
+ sinkcaps);
+
+ /* if we have caps on both pads we can check the intersection. If one
+ * of the caps is NULL, we return TRUE. */
+ if (G_UNLIKELY (srccaps == NULL || sinkcaps == NULL)) {
+ if (srccaps)
+ gst_caps_unref (srccaps);
+ if (sinkcaps)
+ gst_caps_unref (sinkcaps);
+ goto done;
+ }
+
+ compatible = gst_caps_can_intersect (srccaps, sinkcaps);
+ gst_caps_unref (srccaps);
+ gst_caps_unref (sinkcaps);
+
+done:
+ GST_CAT_DEBUG (GST_CAT_CAPS, "caps are %scompatible",
+ (compatible ? "" : "not"));
+
+ return compatible;
+}
+
+/* check if the grandparents of both pads are the same.
+ * This check is required so that we don't try to link
+ * pads from elements in different bins without ghostpads.
+ *
+ * The LOCK should be held on both pads
+ */
+static gboolean
+gst_pad_link_check_hierarchy (GstPad * src, GstPad * sink)
+{
+ GstObject *psrc, *psink;
+
+ psrc = GST_OBJECT_PARENT (src);
+ psink = GST_OBJECT_PARENT (sink);
+
+ /* if one of the pads has no parent, we allow the link */
+ if (G_UNLIKELY (psrc == NULL || psink == NULL))
+ goto no_parent;
+
+ /* only care about parents that are elements */
+ if (G_UNLIKELY (!GST_IS_ELEMENT (psrc) || !GST_IS_ELEMENT (psink)))
+ goto no_element_parent;
+
+ /* if the parents are the same, we have a loop */
+ if (G_UNLIKELY (psrc == psink))
+ goto same_parents;
+
+ /* if they both have a parent, we check the grandparents. We can not lock
+ * the parent because we hold on the child (pad) and the locking order is
+ * parent >> child. */
+ psrc = GST_OBJECT_PARENT (psrc);
+ psink = GST_OBJECT_PARENT (psink);
+
+ /* if they have grandparents but they are not the same */
+ if (G_UNLIKELY (psrc != psink))
+ goto wrong_grandparents;
+
+ return TRUE;
+
+ /* ERRORS */
+no_parent:
+ {
+ GST_CAT_DEBUG (GST_CAT_CAPS,
+ "one of the pads has no parent %" GST_PTR_FORMAT " and %"
+ GST_PTR_FORMAT, psrc, psink);
+ return TRUE;
+ }
+no_element_parent:
+ {
+ GST_CAT_DEBUG (GST_CAT_CAPS,
+ "one of the pads has no element parent %" GST_PTR_FORMAT " and %"
+ GST_PTR_FORMAT, psrc, psink);
+ return TRUE;
+ }
+same_parents:
+ {
+ GST_CAT_DEBUG (GST_CAT_CAPS, "pads have same parent %" GST_PTR_FORMAT,
+ psrc);
+ return FALSE;
+ }
+wrong_grandparents:
+ {
+ GST_CAT_DEBUG (GST_CAT_CAPS,
+ "pads have different grandparents %" GST_PTR_FORMAT " and %"
+ GST_PTR_FORMAT, psrc, psink);
+ return FALSE;
+ }
+}
+
+/* FIXME leftover from an attempt at refactoring... */
+/* call with the two pads unlocked, when this function returns GST_PAD_LINK_OK,
+ * the two pads will be locked in the srcpad, sinkpad order. */
+static GstPadLinkReturn
+gst_pad_link_prepare (GstPad * srcpad, GstPad * sinkpad, GstPadLinkCheck flags)
+{
+ GST_CAT_INFO (GST_CAT_PADS, "trying to link %s:%s and %s:%s",
+ GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad));
+
+ GST_OBJECT_LOCK (srcpad);
+
+ if (G_UNLIKELY (GST_PAD_PEER (srcpad) != NULL))
+ goto src_was_linked;
+
+ GST_OBJECT_LOCK (sinkpad);
+
+ if (G_UNLIKELY (GST_PAD_PEER (sinkpad) != NULL))
+ goto sink_was_linked;
+
+ /* check hierarchy, pads can only be linked if the grandparents
+ * are the same. */
+ if ((flags & GST_PAD_LINK_CHECK_HIERARCHY)
+ && !gst_pad_link_check_hierarchy (srcpad, sinkpad))
+ goto wrong_hierarchy;
+
+ /* check pad caps for non-empty intersection */
+ if (!gst_pad_link_check_compatible_unlocked (srcpad, sinkpad, flags))
+ goto no_format;
+
+ /* FIXME check pad scheduling for non-empty intersection */
+
+ return GST_PAD_LINK_OK;
+
+src_was_linked:
+ {
+ GST_CAT_INFO (GST_CAT_PADS, "src %s:%s was already linked to %s:%s",
+ GST_DEBUG_PAD_NAME (srcpad),
+ GST_DEBUG_PAD_NAME (GST_PAD_PEER (srcpad)));
+ /* we do not emit a warning in this case because unlinking cannot
+ * be made MT safe.*/
+ GST_OBJECT_UNLOCK (srcpad);
+ return GST_PAD_LINK_WAS_LINKED;
+ }
+sink_was_linked:
+ {
+ GST_CAT_INFO (GST_CAT_PADS, "sink %s:%s was already linked to %s:%s",
+ GST_DEBUG_PAD_NAME (sinkpad),
+ GST_DEBUG_PAD_NAME (GST_PAD_PEER (sinkpad)));
+ /* we do not emit a warning in this case because unlinking cannot
+ * be made MT safe.*/
+ GST_OBJECT_UNLOCK (sinkpad);
+ GST_OBJECT_UNLOCK (srcpad);
+ return GST_PAD_LINK_WAS_LINKED;
+ }
+wrong_hierarchy:
+ {
+ GST_CAT_INFO (GST_CAT_PADS, "pads have wrong hierarchy");
+ GST_OBJECT_UNLOCK (sinkpad);
+ GST_OBJECT_UNLOCK (srcpad);
+ return GST_PAD_LINK_WRONG_HIERARCHY;
+ }
+no_format:
+ {
+ GST_CAT_INFO (GST_CAT_PADS, "caps are incompatible");
+ GST_OBJECT_UNLOCK (sinkpad);
+ GST_OBJECT_UNLOCK (srcpad);
+ return GST_PAD_LINK_NOFORMAT;
+ }
+}
+
+/**
+ * gst_pad_can_link:
+ * @srcpad: the source #GstPad.
+ * @sinkpad: the sink #GstPad.
+ *
+ * Checks if the source pad and the sink pad are compatible so they can be
+ * linked.
+ *
+ * Returns: TRUE if the pads can be linked.
+ */
+gboolean
+gst_pad_can_link (GstPad * srcpad, GstPad * sinkpad)
+{
+ GstPadLinkReturn result;
+
+ /* generic checks */
+ g_return_val_if_fail (GST_IS_PAD (srcpad), FALSE);
+ g_return_val_if_fail (GST_IS_PAD (sinkpad), FALSE);
+
+ GST_CAT_INFO (GST_CAT_PADS, "check if %s:%s can link with %s:%s",
+ GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad));
+
+ /* gst_pad_link_prepare does everything for us, we only release the locks
+ * on the pads that it gets us. If this function returns !OK the locks are not
+ * taken anymore. */
+ result = gst_pad_link_prepare (srcpad, sinkpad, GST_PAD_LINK_CHECK_DEFAULT);
+ if (result != GST_PAD_LINK_OK)
+ goto done;
+
+ GST_OBJECT_UNLOCK (srcpad);
+ GST_OBJECT_UNLOCK (sinkpad);
+
+done:
+ return result == GST_PAD_LINK_OK;
+}
+
+/**
+ * gst_pad_link_full:
+ * @srcpad: the source #GstPad to link.
+ * @sinkpad: the sink #GstPad to link.
+ * @flags: the checks to validate when linking
+ *
+ * Links the source pad and the sink pad.
+ *
+ * This variant of #gst_pad_link provides a more granular control on the
+ * checks being done when linking. While providing some considerable speedups
+ * the caller of this method must be aware that wrong usage of those flags
+ * can cause severe issues. Refer to the documentation of #GstPadLinkCheck
+ * for more information.
+ *
+ * MT Safe.
+ *
+ * Returns: A result code indicating if the connection worked or
+ * what went wrong.
+ *
+ * Since: 0.10.30
+ */
+GstPadLinkReturn
+gst_pad_link_full (GstPad * srcpad, GstPad * sinkpad, GstPadLinkCheck flags)
+{
+ GstPadLinkReturn result;
+ GstElement *parent;
+ GstPadLinkFunction srcfunc, sinkfunc;
+
+ g_return_val_if_fail (GST_IS_PAD (srcpad), GST_PAD_LINK_REFUSED);
+ g_return_val_if_fail (GST_PAD_IS_SRC (srcpad), GST_PAD_LINK_WRONG_DIRECTION);
+ g_return_val_if_fail (GST_IS_PAD (sinkpad), GST_PAD_LINK_REFUSED);
+ g_return_val_if_fail (GST_PAD_IS_SINK (sinkpad),
+ GST_PAD_LINK_WRONG_DIRECTION);
+
+ /* Notify the parent early. See gst_pad_unlink for details. */
+ if (G_LIKELY ((parent = GST_ELEMENT_CAST (gst_pad_get_parent (srcpad))))) {
+ if (G_LIKELY (GST_IS_ELEMENT (parent))) {
+ gst_element_post_message (parent,
+ gst_message_new_structure_change (GST_OBJECT_CAST (sinkpad),
+ GST_STRUCTURE_CHANGE_TYPE_PAD_LINK, parent, TRUE));
+ } else {
+ gst_object_unref (parent);
+ parent = NULL;
+ }
+ }
+
+ /* prepare will also lock the two pads */
+ result = gst_pad_link_prepare (srcpad, sinkpad, flags);
+
+ if (G_UNLIKELY (result != GST_PAD_LINK_OK))
+ goto done;
+
+ /* must set peers before calling the link function */
+ GST_PAD_PEER (srcpad) = sinkpad;
+ GST_PAD_PEER (sinkpad) = srcpad;
+
+ /* make sure we update events */
+ prepare_event_update (srcpad, sinkpad);
+
+ /* get the link functions */
+ srcfunc = GST_PAD_LINKFUNC (srcpad);
+ sinkfunc = GST_PAD_LINKFUNC (sinkpad);
+
+ if (G_UNLIKELY (srcfunc || sinkfunc)) {
+ /* custom link functions, execute them */
+ GST_OBJECT_UNLOCK (sinkpad);
+ GST_OBJECT_UNLOCK (srcpad);
+
+ if (srcfunc) {
+ /* this one will call the peer link function */
+ result = srcfunc (srcpad, sinkpad);
+ } else if (sinkfunc) {
+ /* if no source link function, we need to call the sink link
+ * function ourselves. */
+ result = sinkfunc (sinkpad, srcpad);
+ }
+
+ GST_OBJECT_LOCK (srcpad);
+ GST_OBJECT_LOCK (sinkpad);
+
+ /* we released the lock, check if the same pads are linked still */
+ if (GST_PAD_PEER (srcpad) != sinkpad || GST_PAD_PEER (sinkpad) != srcpad)
+ goto concurrent_link;
+
+ if (G_UNLIKELY (result != GST_PAD_LINK_OK))
+ goto link_failed;
+ }
+ GST_OBJECT_UNLOCK (sinkpad);
+ GST_OBJECT_UNLOCK (srcpad);
+
+ /* fire off a signal to each of the pads telling them
+ * that they've been linked */
+ g_signal_emit (srcpad, gst_pad_signals[PAD_LINKED], 0, sinkpad);
+ g_signal_emit (sinkpad, gst_pad_signals[PAD_LINKED], 0, srcpad);
+
+ GST_CAT_INFO (GST_CAT_PADS, "linked %s:%s and %s:%s, successful",
+ GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad));
+
+ gst_pad_send_event (srcpad, gst_event_new_reconfigure ());
+
+done:
+ if (G_LIKELY (parent)) {
+ gst_element_post_message (parent,
+ gst_message_new_structure_change (GST_OBJECT_CAST (sinkpad),
+ GST_STRUCTURE_CHANGE_TYPE_PAD_LINK, parent, FALSE));
+ gst_object_unref (parent);
+ }
+
+ return result;
+
+ /* ERRORS */
+concurrent_link:
+ {
+ GST_CAT_INFO (GST_CAT_PADS, "concurrent link between %s:%s and %s:%s",
+ GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad));
+ GST_OBJECT_UNLOCK (sinkpad);
+ GST_OBJECT_UNLOCK (srcpad);
+
+ /* The other link operation succeeded first */
+ result = GST_PAD_LINK_WAS_LINKED;
+ goto done;
+ }
+link_failed:
+ {
+ GST_CAT_INFO (GST_CAT_PADS, "link between %s:%s and %s:%s failed",
+ GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad));
+
+ GST_PAD_PEER (srcpad) = NULL;
+ GST_PAD_PEER (sinkpad) = NULL;
+
+ GST_OBJECT_UNLOCK (sinkpad);
+ GST_OBJECT_UNLOCK (srcpad);
+
+ goto done;
+ }
+}
+
+/**
+ * gst_pad_link:
+ * @srcpad: the source #GstPad to link.
+ * @sinkpad: the sink #GstPad to link.
+ *
+ * Links the source pad and the sink pad.
+ *
+ * Returns: A result code indicating if the connection worked or
+ * what went wrong.
+ *
+ * MT Safe.
+ */
+GstPadLinkReturn
+gst_pad_link (GstPad * srcpad, GstPad * sinkpad)
+{
+ return gst_pad_link_full (srcpad, sinkpad, GST_PAD_LINK_CHECK_DEFAULT);
+}
+
+static void
+gst_pad_set_pad_template (GstPad * pad, GstPadTemplate * templ)
+{
+ GstPadTemplate **template_p;
+
+ /* this function would need checks if it weren't static */
+
+ GST_OBJECT_LOCK (pad);
+ template_p = &pad->padtemplate;
+ gst_object_replace ((GstObject **) template_p, (GstObject *) templ);
+ GST_OBJECT_UNLOCK (pad);
+
+ if (templ)
+ gst_pad_template_pad_created (templ, pad);
+}
+
+/**
+ * gst_pad_get_pad_template:
+ * @pad: a #GstPad.
+ *
+ * Gets the template for @pad.
+ *
+ * Returns: (transfer full): the #GstPadTemplate from which this pad was
+ * instantiated, or %NULL if this pad has no template. Unref after
+ * usage.
+ */
+GstPadTemplate *
+gst_pad_get_pad_template (GstPad * pad)
+{
+ GstPadTemplate *templ;
+
+ g_return_val_if_fail (GST_IS_PAD (pad), NULL);
+
+ templ = GST_PAD_PAD_TEMPLATE (pad);
+
+ return (templ ? gst_object_ref (templ) : NULL);
+}
+
+static GstCaps *
+caps_with_getcaps (GstPad * pad, GstCaps * filter)
+{
+ GstCaps *result;
+
+ if (GST_PAD_GETCAPSFUNC (pad) == NULL)
+ return NULL;
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad,
+ "dispatching to pad getcaps function with "
+ "filter %" GST_PTR_FORMAT, filter);
+
+ GST_OBJECT_FLAG_SET (pad, GST_PAD_IN_GETCAPS);
+ GST_OBJECT_UNLOCK (pad);
+ result = GST_PAD_GETCAPSFUNC (pad) (pad, filter);
+ GST_OBJECT_LOCK (pad);
+ GST_OBJECT_FLAG_UNSET (pad, GST_PAD_IN_GETCAPS);
+
+ if (G_UNLIKELY (result == NULL))
+ goto null_caps;
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad,
+ "pad getcaps returned %" GST_PTR_FORMAT, result);
+
+#ifndef G_DISABLE_ASSERT
+ /* check that the returned caps are a real subset of the template caps */
+ if (GST_PAD_PAD_TEMPLATE (pad)) {
+ const GstCaps *templ_caps =
+ GST_PAD_TEMPLATE_CAPS (GST_PAD_PAD_TEMPLATE (pad));
+ if (!gst_caps_is_subset (result, templ_caps)) {
+ GstCaps *temp;
+
+ GST_CAT_ERROR_OBJECT (GST_CAT_CAPS, pad,
+ "pad returned caps %" GST_PTR_FORMAT
+ " which are not a real subset of its template caps %"
+ GST_PTR_FORMAT, result, templ_caps);
+ g_warning
+ ("pad %s:%s returned caps which are not a real "
+ "subset of its template caps", GST_DEBUG_PAD_NAME (pad));
+ temp = gst_caps_intersect (templ_caps, result);
+ gst_caps_unref (result);
+ result = temp;
+ }
+ }
+ if (filter) {
+ if (!gst_caps_is_subset (result, filter)) {
+ GstCaps *temp;
+
+ GST_CAT_ERROR_OBJECT (GST_CAT_CAPS, pad,
+ "pad returned caps %" GST_PTR_FORMAT
+ " which are not a real subset of the filter caps %"
+ GST_PTR_FORMAT, result, filter);
+ g_warning ("pad %s:%s returned caps which are not a real "
+ "subset of the filter caps", GST_DEBUG_PAD_NAME (pad));
+ /* FIXME: Order? But shouldn't happen anyway... */
+ temp = gst_caps_intersect_full (filter, result, GST_CAPS_INTERSECT_FIRST);
+ gst_caps_unref (result);
+ result = temp;
+ }
+ }
+#endif
+
+ return result;
+
+ /* ERRORS */
+null_caps:
+ {
+ g_critical ("pad %s:%s returned NULL caps from getcaps function",
+ GST_DEBUG_PAD_NAME (pad));
+ return NULL;
+ }
+}
+
+/* should be called with the pad LOCK held */
+/* refs the caps, so caller is responsible for getting it unreffed */
+static GstCaps *
+gst_pad_get_caps_unlocked (GstPad * pad, GstCaps * filter)
+{
+ GstCaps *result = NULL;
+ GstPadTemplate *templ;
+ gboolean fixed_caps;
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad, "get pad caps");
+
+ fixed_caps = GST_PAD_IS_FIXED_CAPS (pad);
+
+ if (fixed_caps) {
+ /* fixed caps, try the negotiated caps first */
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad, "fixed pad caps: trying pad caps");
+ if ((result = get_pad_caps (pad)))
+ goto filter_done;
+ }
+
+ /* try the getcaps function next */
+ if ((result = caps_with_getcaps (pad, filter)))
+ goto done;
+
+ if ((templ = GST_PAD_PAD_TEMPLATE (pad))) {
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad, "trying pad template caps");
+ if ((result = GST_PAD_TEMPLATE_CAPS (templ)))
+ goto filter_done;
+ }
+
+ if (!fixed_caps) {
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad,
+ "non-fixed pad caps: trying pad caps");
+ /* non fixed caps, try the negotiated caps */
+ if ((result = get_pad_caps (pad)))
+ goto filter_done;
+ }
+
+ /* this almost never happens */
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad, "pad has no caps");
+ result = gst_caps_new_empty ();
+ goto done;
+
+filter_done:
+ /* run the filter on the result */
+ if (filter) {
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad,
+ "using caps %p %" GST_PTR_FORMAT " with filter %p %"
+ GST_PTR_FORMAT, result, result, filter, filter);
+ result = gst_caps_intersect_full (filter, result, GST_CAPS_INTERSECT_FIRST);
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad, "result %p %" GST_PTR_FORMAT,
+ result, result);
+ } else {
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad,
+ "using caps %p %" GST_PTR_FORMAT, result, result);
+ result = gst_caps_ref (result);
+ }
+done:
+ return result;
+}
+
+/**
+ * gst_pad_has_current_caps:
+ * @pad: a #GstPad to check
+ *
+ * Check if @pad has caps set on it with a #GST_EVENT_CAPS event.
+ *
+ * Returns: TRUE when @pad has caps associated with it.
+ */
+gboolean
+gst_pad_has_current_caps (GstPad * pad)
+{
+ gboolean result;
+ GstCaps *caps;
+
+ g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
+
+ GST_OBJECT_LOCK (pad);
+ caps = get_pad_caps (pad);
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad,
+ "check current pad caps %" GST_PTR_FORMAT, caps);
+ result = (caps != NULL);
+ GST_OBJECT_UNLOCK (pad);
+
+ return result;
+}
+
+/**
+ * gst_pad_get_current_caps:
+ * @pad: a #GstPad to get the current capabilities of.
+ *
+ * Gets the capabilities currently configured on @pad with the last
+ * #GST_EVENT_CAPS event.
+ *
+ * Returns: the current caps of the pad with incremented ref-count.
+ */
+GstCaps *
+gst_pad_get_current_caps (GstPad * pad)
+{
+ GstCaps *result;
+
+ g_return_val_if_fail (GST_IS_PAD (pad), NULL);
+
+ GST_OBJECT_LOCK (pad);
+ if ((result = get_pad_caps (pad)))
+ gst_caps_ref (result);
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad,
+ "get current pad caps %" GST_PTR_FORMAT, result);
+ GST_OBJECT_UNLOCK (pad);
+
+ return result;
+}
+
+/**
+ * gst_pad_get_caps:
+ * @pad: a #GstPad to get the capabilities of.
+ * @filter: suggested #GstCaps.
+ *
+ * Gets the capabilities this pad can produce or consume.
+ * Note that this method doesn't necessarily return the caps set by
+ * gst_pad_set_caps() - use gst_pad_get_current_caps() for that instead.
+ * gst_pad_get_caps returns all possible caps a pad can operate with, using
+ * the pad's get_caps function;
+ * this returns the pad template caps if not explicitly set.
+ *
+ * When called on sinkpads @filter contains the caps that
+ * upstream could produce in the order preferred by upstream. When
+ * called on srcpads @filter contains the caps accepted by
+ * downstream in the preffered order. @filter might be %NULL but
+ * if it is not %NULL the returned caps will be a subset of @filter.
+ *
+ * Note that this function does not return writable #GstCaps, use
+ * gst_caps_make_writable() before modifying the caps.
+ *
+ * Returns: the caps of the pad with incremented ref-count.
+ */
+GstCaps *
+gst_pad_get_caps (GstPad * pad, GstCaps * filter)
+{
+ GstCaps *result = NULL;
+
+ g_return_val_if_fail (GST_IS_PAD (pad), NULL);
+ g_return_val_if_fail (filter == NULL || GST_IS_CAPS (filter), NULL);
+
+ GST_OBJECT_LOCK (pad);
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad, "get pad caps");
+
+ result = gst_pad_get_caps_unlocked (pad, filter);
+
+ GST_OBJECT_UNLOCK (pad);
+
+ return result;
+}
+
+
+/**
+ * gst_pad_peer_get_caps:
+ * @pad: a #GstPad to get the capabilities of.
+ * @filter: a #GstCaps filter.
+ *
+ * Gets the capabilities of the peer connected to this pad. Similar to
+ * gst_pad_get_caps().
+ *
+ * When called on srcpads @filter contains the caps that
+ * upstream could produce in the order preferred by upstream. When
+ * called on sinkpads @filter contains the caps accepted by
+ * downstream in the preffered order. @filter might be %NULL but
+ * if it is not %NULL the returned caps will be a subset of @filter.
+ *
+ * Returns: the caps of the peer pad with incremented ref-count. This function
+ * returns %NULL when there is no peer pad.
+ */
+GstCaps *
+gst_pad_peer_get_caps (GstPad * pad, GstCaps * filter)
+{
+ GstPad *peerpad;
+ GstCaps *result = NULL;
+
+ g_return_val_if_fail (GST_IS_PAD (pad), NULL);
+ g_return_val_if_fail (filter == NULL || GST_IS_CAPS (filter), NULL);
+
+ GST_OBJECT_LOCK (pad);
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad, "get peer caps");
+
+ peerpad = GST_PAD_PEER (pad);
+ if (G_UNLIKELY (peerpad == NULL))
+ goto no_peer;
+
+ gst_object_ref (peerpad);
+ GST_OBJECT_UNLOCK (pad);
+
+ result = gst_pad_get_caps (peerpad, filter);
+
+ gst_object_unref (peerpad);
+
+ return result;
+
+no_peer:
+ {
+ GST_OBJECT_UNLOCK (pad);
+ return NULL;
+ }
+}
+
+static void
+gst_pad_fixate_caps_default (GstPad * pad, GstCaps * caps)
+{
+ /* default fixation */
+ gst_caps_fixate (caps);
+}
+
+/**
+ * gst_pad_fixate_caps:
+ * @pad: a #GstPad to fixate
+ * @caps: the #GstCaps to fixate
+ *
+ * Fixate a caps on the given pad. Modifies the caps in place, so you should
+ * make sure that the caps are actually writable (see gst_caps_make_writable()).
+ */
+void
+gst_pad_fixate_caps (GstPad * pad, GstCaps * caps)
+{
+ GstPadFixateCapsFunction fixatefunc;
+
+ g_return_if_fail (GST_IS_PAD (pad));
+ g_return_if_fail (caps != NULL);
+ g_return_if_fail (!gst_caps_is_empty (caps));
+ g_return_if_fail (!gst_caps_is_any (caps));
+
+ if (gst_caps_is_fixed (caps) || gst_caps_is_any (caps))
+ return;
+
+ g_return_if_fail (gst_caps_is_writable (caps));
+
+ if (G_LIKELY ((fixatefunc = GST_PAD_FIXATECAPSFUNC (pad))))
+ fixatefunc (pad, caps);
+}
+
+/* Default accept caps implementation just checks against
+ * against the allowed caps for the pad */
+static gboolean
+gst_pad_acceptcaps_default (GstPad * pad, GstCaps * caps)
+{
+ /* get the caps and see if it intersects to something not empty */
+ GstCaps *allowed;
+ gboolean result = FALSE;
+
+ GST_DEBUG_OBJECT (pad, "caps %" GST_PTR_FORMAT, caps);
+
+ allowed = gst_pad_get_caps (pad, NULL);
+ if (!allowed)
+ goto nothing_allowed;
+
+ GST_DEBUG_OBJECT (pad, "allowed caps %" GST_PTR_FORMAT, allowed);
+
+ result = gst_caps_is_subset (caps, allowed);
+
+ gst_caps_unref (allowed);
+
+ return result;
+
+ /* ERRORS */
+nothing_allowed:
+ {
+ GST_DEBUG_OBJECT (pad, "no caps allowed on the pad");
+ return FALSE;
+ }
+}
+
+/**
+ * gst_pad_accept_caps:
+ * @pad: a #GstPad to check
+ * @caps: a #GstCaps to check on the pad
+ *
+ * Check if the given pad accepts the caps.
+ *
+ * Returns: TRUE if the pad can accept the caps.
+ */
+gboolean
+gst_pad_accept_caps (GstPad * pad, GstCaps * caps)
+{
+ gboolean result;
+ GstPadAcceptCapsFunction acceptfunc;
+#if 0
+ GstCaps *existing = NULL;
+#endif
+
+ g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
+
+ /* any pad can be unnegotiated */
+ if (caps == NULL)
+ return TRUE;
+
+ /* lock for checking the existing caps */
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad, "accept caps of %p", caps);
+#if 0
+ GST_OBJECT_LOCK (pad);
+ /* The current caps on a pad are trivially acceptable */
+ if (G_LIKELY ((existing = GST_PAD_CAPS (pad)))) {
+ if (caps == existing || gst_caps_is_equal (caps, existing))
+ goto is_same_caps;
+ }
+ GST_OBJECT_UNLOCK (pad);
+#endif
+ acceptfunc = GST_PAD_ACCEPTCAPSFUNC (pad);
+
+ /* Only null if the element explicitly unset it */
+ if (G_UNLIKELY (acceptfunc == NULL))
+ goto no_func;
+
+ /* we can call the function */
+ result = acceptfunc (pad, caps);
+ GST_DEBUG_OBJECT (pad, "acceptfunc returned %d", result);
+
+ return result;
+
+#if 0
+is_same_caps:
+ {
+ GST_DEBUG_OBJECT (pad, "pad had same caps");
+ GST_OBJECT_UNLOCK (pad);
+ return TRUE;
+ }
+#endif
+no_func:
+ {
+ GST_DEBUG_OBJECT (pad, "no acceptcaps function");
+ return FALSE;
+ }
+}
+
+/**
+ * gst_pad_peer_accept_caps:
+ * @pad: a #GstPad to check the peer of
+ * @caps: a #GstCaps to check on the pad
+ *
+ * Check if the peer of @pad accepts @caps. If @pad has no peer, this function
+ * returns TRUE.
+ *
+ * Returns: TRUE if the peer of @pad can accept the caps or @pad has no peer.
+ */
+gboolean
+gst_pad_peer_accept_caps (GstPad * pad, GstCaps * caps)
+{
+ GstPad *peerpad;
+ gboolean result;
+
+ g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
+
+ GST_OBJECT_LOCK (pad);
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad, "peer accept caps of (%p)", pad);
+
+ peerpad = GST_PAD_PEER (pad);
+ if (G_UNLIKELY (peerpad == NULL))
+ goto no_peer;
+
+ gst_object_ref (peerpad);
+ /* release lock before calling external methods but keep ref to pad */
+ GST_OBJECT_UNLOCK (pad);
+
+ result = gst_pad_accept_caps (peerpad, caps);
+
+ gst_object_unref (peerpad);
+
+ return result;
+
+no_peer:
+ {
+ GST_OBJECT_UNLOCK (pad);
+ return TRUE;
+ }
+}
+
+/**
+ * gst_pad_set_caps:
+ * @pad: a #GstPad to set the capabilities of.
+ * @caps: (transfer none): a #GstCaps to set.
+ *
+ * Sets the capabilities of this pad. The caps must be fixed. Any previous
+ * caps on the pad will be unreffed. This function refs the caps so you should
+ * unref if as soon as you don't need it anymore.
+ * It is possible to set NULL caps, which will make the pad unnegotiated
+ * again.
+ *
+ * Returns: TRUE if the caps could be set. FALSE if the caps were not fixed
+ * or bad parameters were provided to this function.
+ *
+ * MT safe.
+ */
+gboolean
+gst_pad_set_caps (GstPad * pad, GstCaps * caps)
+{
+ GstEvent *event;
+ gboolean res = TRUE;
+
+ g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
+ g_return_val_if_fail (caps != NULL && gst_caps_is_fixed (caps), FALSE);
+
+ event = gst_event_new_caps (caps);
+
+ if (GST_PAD_IS_SRC (pad))
+ res = gst_pad_push_event (pad, event);
+ else
+ res = gst_pad_send_event (pad, event);
+
+ return res;
+}
+
+static gboolean
+do_event_function (GstPad * pad, GstEvent * event,
+ GstPadEventFunction eventfunc, gboolean * caps_notify)
+{
+ gboolean result = TRUE, call_event = TRUE;
+ GstCaps *caps, *old, *templ;
+
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_CAPS:
+ {
+ /* backwards compatibility mode for caps */
+ gst_event_parse_caps (event, &caps);
+
+ /* See if pad accepts the caps */
+ templ = gst_pad_get_pad_template_caps (pad);
+ if (!gst_caps_is_subset (caps, templ))
+ goto not_accepted;
+
+ /* check if it changed */
+ if ((old = gst_pad_get_current_caps (pad))) {
+ call_event = !gst_caps_is_equal (caps, old);
+ gst_caps_unref (old);
+ }
+ if (call_event)
+ *caps_notify = TRUE;
+ gst_caps_unref (templ);
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (call_event) {
+ GST_DEBUG_OBJECT (pad, "calling event function with event %p", event);
+ result = eventfunc (pad, event);
+ } else {
+ gst_event_unref (event);
+ }
+ return result;
+
+ /* ERRORS */
+not_accepted:
+ {
+ gst_caps_unref (templ);
+ gst_event_unref (event);
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad,
+ "caps %" GST_PTR_FORMAT " not accepted", caps);
+ return FALSE;
+ }
+}
+
+/* function to send all pending events on the sinkpad to the event
+ * function and collect the results. This function should be called with
+ * the object lock. The object lock might be released by this function.
+ */
+static GstFlowReturn
+gst_pad_update_events (GstPad * pad)
+{
+ GstFlowReturn ret = GST_FLOW_OK;
+ guint i;
+ GstPadEventFunction eventfunc;
+ GstEvent *event;
+ gboolean caps_notify = FALSE;
+
+ if (G_UNLIKELY ((eventfunc = GST_PAD_EVENTFUNC (pad)) == NULL))
+ goto no_function;
+
+ for (i = 0; i < GST_EVENT_MAX_STICKY; i++) {
+ gboolean res;
+ PadEvent *ev;
+
+ ev = &pad->priv->events[i];
+
+ /* skip without pending event */
+ if ((event = gst_event_steal (&ev->pending)) == NULL)
+ continue;
+
+ gst_event_ref (event);
+ GST_OBJECT_UNLOCK (pad);
+
+ res = do_event_function (pad, event, eventfunc, &caps_notify);
+
+ /* things could have changed while we release the lock, check if we still
+ * are handling the same event, if we don't something changed and we have
+ * to try again. FIXME. we need a cookie here. FIXME, we also want to remove
+ * that lock eventually and then do the retry elsewhere. */
+
+ if (res) {
+ /* make the event active */
+ gst_event_take (&ev->event, event);
+
+ /* notify caps change when needed */
+ if (caps_notify) {
+ g_object_notify_by_pspec ((GObject *) pad, pspec_caps);
+ caps_notify = FALSE;
+ }
+ } else {
+ gst_event_unref (event);
+ ret = GST_FLOW_ERROR;
+ }
+ GST_OBJECT_LOCK (pad);
+ }
+ /* when we get here all events were successfully updated. */
+ return ret;
+
+ /* ERRORS */
+no_function:
+ {
+ g_warning ("pad %s:%s has no event handler, file a bug.",
+ GST_DEBUG_PAD_NAME (pad));
+ return GST_FLOW_NOT_SUPPORTED;
+ }
+}
+
+/**
+ * gst_pad_get_pad_template_caps:
+ * @pad: a #GstPad to get the template capabilities from.
+ *
+ * Gets the capabilities for @pad's template.
+ *
+ * Returns: (transfer full): the #GstCaps of this pad template.
+ * Unref after usage.
+ */
+GstCaps *
+gst_pad_get_pad_template_caps (GstPad * pad)
+{
+ static GstStaticCaps anycaps = GST_STATIC_CAPS ("ANY");
+
+ g_return_val_if_fail (GST_IS_PAD (pad), NULL);
+
+ if (GST_PAD_PAD_TEMPLATE (pad))
+ return gst_pad_template_get_caps (GST_PAD_PAD_TEMPLATE (pad));
+
+ return gst_static_caps_get (&anycaps);
+}
+
+/**
+ * gst_pad_get_peer:
+ * @pad: a #GstPad to get the peer of.
+ *
+ * Gets the peer of @pad. This function refs the peer pad so
+ * you need to unref it after use.
+ *
+ * Returns: (transfer full): the peer #GstPad. Unref after usage.
+ *
+ * MT safe.
+ */
+GstPad *
+gst_pad_get_peer (GstPad * pad)
+{
+ GstPad *result;
+
+ g_return_val_if_fail (GST_IS_PAD (pad), NULL);
+
+ GST_OBJECT_LOCK (pad);
+ result = GST_PAD_PEER (pad);
+ if (result)
+ gst_object_ref (result);
+ GST_OBJECT_UNLOCK (pad);
+
+ return result;
+}
+
+/**
+ * gst_pad_get_allowed_caps:
+ * @pad: a #GstPad.
+ *
+ * Gets the capabilities of the allowed media types that can flow through
+ * @pad and its peer.
+ *
+ * The allowed capabilities is calculated as the intersection of the results of
+ * calling gst_pad_get_caps() on @pad and its peer. The caller owns a reference
+ * on the resulting caps.
+ *
+ * Returns: (transfer full): the allowed #GstCaps of the pad link. Unref the
+ * caps when you no longer need it. This function returns NULL when @pad
+ * has no peer.
+ *
+ * MT safe.
+ */
+GstCaps *
+gst_pad_get_allowed_caps (GstPad * pad)
+{
+ GstCaps *mycaps;
+ GstCaps *caps;
+ GstCaps *peercaps;
+ GstPad *peer;
+
+ g_return_val_if_fail (GST_IS_PAD (pad), NULL);
+
+ GST_OBJECT_LOCK (pad);
+ peer = GST_PAD_PEER (pad);
+ if (G_UNLIKELY (peer == NULL))
+ goto no_peer;
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_PROPERTIES, pad, "getting allowed caps");
+
+ gst_object_ref (peer);
+ GST_OBJECT_UNLOCK (pad);
+ mycaps = gst_pad_get_caps (pad, NULL);
+
+ peercaps = gst_pad_get_caps (peer, NULL);
+ gst_object_unref (peer);
+
+ caps = gst_caps_intersect (mycaps, peercaps);
+ gst_caps_unref (peercaps);
+ gst_caps_unref (mycaps);
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad, "allowed caps %" GST_PTR_FORMAT,
+ caps);
+
+ return caps;
+
+no_peer:
+ {
+ GST_CAT_DEBUG_OBJECT (GST_CAT_PROPERTIES, pad, "no peer");
+ GST_OBJECT_UNLOCK (pad);
+
+ return NULL;
+ }
+}
+
+/**
+ * gst_pad_iterate_internal_links_default:
+ * @pad: the #GstPad to get the internal links of.
+ *
+ * Iterate the list of pads to which the given pad is linked to inside of
+ * the parent element.
+ * This is the default handler, and thus returns an iterator of all of the
+ * pads inside the parent element with opposite direction.
+ *
+ * The caller must free this iterator after use with gst_iterator_free().
+ *
+ * Returns: a #GstIterator of #GstPad, or NULL if @pad has no parent. Unref each
+ * returned pad with gst_object_unref().
+ *
+ * Since: 0.10.21
+ */
+GstIterator *
+gst_pad_iterate_internal_links_default (GstPad * pad)
+{
+ GstIterator *res;
+ GList **padlist;
+ guint32 *cookie;
+ GMutex *lock;
+ gpointer owner;
+
+ g_return_val_if_fail (GST_IS_PAD (pad), NULL);
+
+ {
+ GstElement *parent;
+
+ GST_OBJECT_LOCK (pad);
+ parent = GST_PAD_PARENT (pad);
+ if (!parent || !GST_IS_ELEMENT (parent))
+ goto no_parent;
+
+ gst_object_ref (parent);
+ GST_OBJECT_UNLOCK (pad);
+
+ if (pad->direction == GST_PAD_SRC)
+ padlist = &parent->sinkpads;
+ else
+ padlist = &parent->srcpads;
+
+ GST_DEBUG_OBJECT (pad, "Making iterator");
+
+ cookie = &parent->pads_cookie;
+ owner = parent;
+ lock = GST_OBJECT_GET_LOCK (parent);
+ }
+
+ res = gst_iterator_new_list (GST_TYPE_PAD,
+ lock, cookie, padlist, (GObject *) owner, NULL);
+
+ gst_object_unref (owner);
+
+ return res;
+
+ /* ERRORS */
+no_parent:
+ {
+ GST_OBJECT_UNLOCK (pad);
+ GST_DEBUG_OBJECT (pad, "no parent element");
+ return NULL;
+ }
+}
+
+/**
+ * gst_pad_iterate_internal_links:
+ * @pad: the GstPad to get the internal links of.
+ *
+ * Gets an iterator for the pads to which the given pad is linked to inside
+ * of the parent element.
+ *
+ * Each #GstPad element yielded by the iterator will have its refcount increased,
+ * so unref after use.
+ *
+ * Free-function: gst_iterator_free
+ *
+ * Returns: (transfer full): a new #GstIterator of #GstPad or %NULL when the
+ * pad does not have an iterator function configured. Use
+ * gst_iterator_free() after usage.
+ *
+ * Since: 0.10.21
+ */
+GstIterator *
+gst_pad_iterate_internal_links (GstPad * pad)
+{
+ GstIterator *res = NULL;
+
+ g_return_val_if_fail (GST_IS_PAD (pad), NULL);
+
+ if (GST_PAD_ITERINTLINKFUNC (pad))
+ res = GST_PAD_ITERINTLINKFUNC (pad) (pad);
+
+ return res;
+}
+
+/**
+ * gst_pad_forward:
+ * @pad: a #GstPad
+ * @forward: a #GstPadForwardFunction
+ * @user_data: user data passed to @forward
+ *
+ * Calls @forward for all internally linked pads of @pad. This function deals with
+ * dynamically changing internal pads and will make sure that the @forward
+ * function is only called once for each pad.
+ *
+ * When @forward returns TRUE, no further pads will be processed.
+ *
+ * Returns: TRUE if one of the dispatcher functions returned TRUE.
+ */
+gboolean
+gst_pad_forward (GstPad * pad, GstPadForwardFunction forward,
+ gpointer user_data)
+{
+ gboolean result = FALSE;
+ GstIterator *iter;
+ gboolean done = FALSE;
+ GValue item = { 0, };
+ GList *pushed_pads = NULL;
+
+ iter = gst_pad_iterate_internal_links (pad);
+
+ if (!iter)
+ goto no_iter;
+
+ while (!done) {
+ switch (gst_iterator_next (iter, &item)) {
+ case GST_ITERATOR_OK:
+ {
+ GstPad *intpad;
+
+ intpad = g_value_get_object (&item);
+
+ /* if already pushed, skip. FIXME, find something faster to tag pads */
+ if (g_list_find (pushed_pads, intpad)) {
+ g_value_reset (&item);
+ break;
+ }
+
+ GST_LOG_OBJECT (pad, "calling forward function on pad %s:%s",
+ GST_DEBUG_PAD_NAME (intpad));
+ done = result = forward (intpad, user_data);
+
+ pushed_pads = g_list_prepend (pushed_pads, intpad);
+
+ g_value_reset (&item);
+ break;
+ }
+ case GST_ITERATOR_RESYNC:
+ /* We don't reset the result here because we don't push the event
+ * again on pads that got the event already and because we need
+ * to consider the result of the previous pushes */
+ gst_iterator_resync (iter);
+ break;
+ case GST_ITERATOR_ERROR:
+ GST_ERROR_OBJECT (pad, "Could not iterate over internally linked pads");
+ done = TRUE;
+ break;
+ case GST_ITERATOR_DONE:
+ done = TRUE;
+ break;
+ }
+ }
+ g_value_unset (&item);
+ gst_iterator_free (iter);
+
+ g_list_free (pushed_pads);
+
+no_iter:
+ return result;
+}
+
+typedef struct
+{
+ GstEvent *event;
+ gboolean result;
+ gboolean dispatched;
+} EventData;
+
+static gboolean
+event_forward_func (GstPad * pad, EventData * data)
+{
+ /* for each pad we send to, we should ref the event; it's up
+ * to downstream to unref again when handled. */
+ GST_LOG_OBJECT (pad, "Reffing and pushing event %p (%s) to %s:%s",
+ data->event, GST_EVENT_TYPE_NAME (data->event), GST_DEBUG_PAD_NAME (pad));
+
+ data->result |= gst_pad_push_event (pad, gst_event_ref (data->event));
+
+ data->dispatched = TRUE;
+
+ /* don't stop */
+ return FALSE;
+}
+
+/**
+ * gst_pad_event_default:
+ * @pad: a #GstPad to call the default event handler on.
+ * @event: (transfer full): the #GstEvent to handle.
+ *
+ * Invokes the default event handler for the given pad.
+ *
+ * The EOS event will pause the task associated with @pad before it is forwarded
+ * to all internally linked pads,
+ *
+ * The CAPS event will never be forwarded.
+ *
+ * The the event is sent to all pads internally linked to @pad. This function
+ * takes ownership of @event.
+ *
+ * Returns: TRUE if the event was sent successfully.
+ */
+gboolean
+gst_pad_event_default (GstPad * pad, GstEvent * event)
+{
+ gboolean result;
+ EventData data;
+
+ g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ GST_LOG_OBJECT (pad, "default event handler");
+
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_EOS:
+ {
+ GST_DEBUG_OBJECT (pad, "pausing task because of eos");
+ gst_pad_pause_task (pad);
+ break;
+ }
+ default:
+ break;
+ }
+
+ data.event = event;
+ data.dispatched = FALSE;
+ data.result = FALSE;
+
+ gst_pad_forward (pad, (GstPadForwardFunction) event_forward_func, &data);
+
+ /* for sinkpads without a parent element or without internal links, nothing
+ * will be dispatched but we still want to return TRUE. */
+ if (data.dispatched)
+ result = data.result;
+ else
+ result = TRUE;
+
+ gst_event_unref (event);
+
+ return result;
+}
+
+/**
+ * gst_pad_query:
+ * @pad: a #GstPad to invoke the default query on.
+ * @query: (transfer none): the #GstQuery to perform.
+ *
+ * Dispatches a query to a pad. The query should have been allocated by the
+ * caller via one of the type-specific allocation functions. The element that
+ * the pad belongs to is responsible for filling the query with an appropriate
+ * response, which should then be parsed with a type-specific query parsing
+ * function.
+ *
+ * Again, the caller is responsible for both the allocation and deallocation of
+ * the query structure.
+ *
+ * Please also note that some queries might need a running pipeline to work.
+ *
+ * Returns: TRUE if the query could be performed.
+ */
+gboolean
+gst_pad_query (GstPad * pad, GstQuery * query)
+{
+ gboolean res;
+ GstPadQueryFunction func;
+
+ g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
+ g_return_val_if_fail (GST_IS_QUERY (query), FALSE);
+
+ GST_DEBUG_OBJECT (pad, "sending query %p (%s)", query,
+ GST_QUERY_TYPE_NAME (query));
+
+ if ((func = GST_PAD_QUERYFUNC (pad)) == NULL)
+ goto no_func;
+
+ res = func (pad, query);
+
+ GST_DEBUG_OBJECT (pad, "sent query %p (%s), result %d", query,
+ GST_QUERY_TYPE_NAME (query), res);
+
+ return res;
+
+no_func:
+ {
+ GST_DEBUG_OBJECT (pad, "had no query function");
+ return FALSE;
+ }
+}
+
+/**
+ * gst_pad_peer_query:
+ * @pad: a #GstPad to invoke the peer query on.
+ * @query: (transfer none): the #GstQuery to perform.
+ *
+ * Performs gst_pad_query() on the peer of @pad.
+ *
+ * The caller is responsible for both the allocation and deallocation of
+ * the query structure.
+ *
+ * Returns: TRUE if the query could be performed. This function returns %FALSE
+ * if @pad has no peer.
+ *
+ * Since: 0.10.15
+ */
+gboolean
+gst_pad_peer_query (GstPad * pad, GstQuery * query)
+{
+ GstPad *peerpad;
+ gboolean result;
+
+ g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
+ g_return_val_if_fail (GST_IS_QUERY (query), FALSE);
+
+ GST_OBJECT_LOCK (pad);
+
+ GST_DEBUG_OBJECT (pad, "peer query %p (%s)", query,
+ GST_QUERY_TYPE_NAME (query));
+
+ peerpad = GST_PAD_PEER (pad);
+ if (G_UNLIKELY (peerpad == NULL))
+ goto no_peer;
+
+ gst_object_ref (peerpad);
+ GST_OBJECT_UNLOCK (pad);
+
+ result = gst_pad_query (peerpad, query);
+
+ gst_object_unref (peerpad);
+
+ return result;
+
+ /* ERRORS */
+no_peer:
+ {
+ GST_WARNING_OBJECT (pad, "pad has no peer");
+ GST_OBJECT_UNLOCK (pad);
+ return FALSE;
+ }
+}
+
+/**
+ * gst_pad_query_default:
+ * @pad: a #GstPad to call the default query handler on.
+ * @query: (transfer none): the #GstQuery to handle.
+ *
+ * Invokes the default query handler for the given pad.
+ * The query is sent to all pads internally linked to @pad. Note that
+ * if there are many possible sink pads that are internally linked to
+ * @pad, only one will be sent the query.
+ * Multi-sinkpad elements should implement custom query handlers.
+ *
+ * Returns: TRUE if the query was performed successfully.
+ */
+gboolean
+gst_pad_query_default (GstPad * pad, GstQuery * query)
+{
+ gboolean forward = TRUE, ret = FALSE;
+
+ switch (GST_QUERY_TYPE (query)) {
+ case GST_QUERY_SCHEDULING:
+ forward = FALSE;
+ break;
+ case GST_QUERY_POSITION:
+ case GST_QUERY_SEEKING:
+ case GST_QUERY_FORMATS:
+ case GST_QUERY_LATENCY:
+ case GST_QUERY_JITTER:
+ case GST_QUERY_RATE:
+ case GST_QUERY_CONVERT:
+ case GST_QUERY_ALLOCATION:
+ default:
+ break;
+ }
+
+ if (forward) {
+ ret = gst_pad_forward
+ (pad, (GstPadForwardFunction) gst_pad_peer_query, query);
+ }
+ return ret;
+}
+
+static void
+probe_hook_marshal (GHook * hook, ProbeMarshall * data)
+{
+ GstPad *pad = data->pad;
+ GstProbeType flags;
+ GstPadProbeCallback callback;
+ GstProbeReturn ret;
+
+ /* if we have called this callback, do nothing */
+ if (PROBE_COOKIE (hook) == data->cookie)
+ return;
+
+ PROBE_COOKIE (hook) = data->cookie;
+
+ flags = hook->flags >> G_HOOK_FLAG_USER_SHIFT;
+
+ /* one of the data types */
+ if ((flags & GST_PROBE_TYPE_DATA & data->mask) == 0)
+ return;
+ /* one of the scheduling types */
+ if ((flags & GST_PROBE_TYPE_SCHEDULING & data->mask) == 0)
+ return;
+ /* all of the blocking types must match */
+ if ((flags & GST_PROBE_TYPE_BLOCKING) !=
+ (data->mask & GST_PROBE_TYPE_BLOCKING))
+ return;
+
+ GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
+ "hook %lu with flags 0x%08x matches", hook->hook_id, flags);
+
+ callback = (GstPadProbeCallback) hook->func;
+ if (callback == NULL)
+ return;
+
+ GST_OBJECT_UNLOCK (pad);
+
+ ret = callback (pad, data->mask, data->type_data, hook->data);
+
+ GST_OBJECT_LOCK (pad);
+
+ switch (ret) {
+ case GST_PROBE_REMOVE:
+ /* remove the probe */
+ GST_DEBUG_OBJECT (pad, "asked to remove hook");
+ cleanup_hook (pad, hook);
+ break;
+ case GST_PROBE_DROP:
+ /* need to drop the data, make sure other probes don't get called
+ * anymore */
+ GST_DEBUG_OBJECT (pad, "asked to drop item");
+ data->mask = GST_PROBE_TYPE_INVALID;
+ data->ret = GST_PROBE_DROP;
+ break;
+ case GST_PROBE_PASS:
+ /* inform the pad block to let things pass */
+ GST_DEBUG_OBJECT (pad, "asked to pass item");
+ data->pass = TRUE;
+ break;
+ default:
+ GST_DEBUG_OBJECT (pad, "probe returned %d", ret);
+ break;
+ }
+}
+
+#define PROBE(pad,mask,data,label) \
+ G_STMT_START { \
+ if (G_UNLIKELY (pad->num_probes)) { \
+ ret = do_probe_callbacks (pad, mask, data); \
+ if (G_UNLIKELY (ret != GST_FLOW_OK)) \
+ goto label; \
+ } \
+ } G_STMT_END
+
+static GstFlowReturn
+do_probe_callbacks (GstPad * pad, GstProbeType mask, gpointer type_data)
+{
+ ProbeMarshall data;
+ guint cookie;
+
+ data.pad = pad;
+ data.mask = mask;
+ data.type_data = type_data;
+ data.ret = GST_PROBE_OK;
+ data.pass = FALSE;
+ data.cookie = pad->priv->probe_cookie++;
+
+again:
+ cookie = pad->priv->probe_cookie;
+
+ g_hook_list_marshal (&pad->probes, FALSE,
+ (GHookMarshaller) probe_hook_marshal, &data);
+
+ /* if the list changed, call the new callbacks (they will not have their
+ * cookie set to data.cookie */
+ if (cookie != pad->priv->probe_cookie) {
+ GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
+ "probe list changed, restarting");
+ goto again;
+ }
+
+ if (data.ret == GST_PROBE_DROP)
+ goto dropped;
+
+ if (data.pass)
+ goto passed;
+
+ if (mask & GST_PROBE_TYPE_BLOCK) {
+ while (GST_PAD_IS_BLOCKED (pad)) {
+ GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
+ "we are blocked %d times", pad->num_blocked);
+
+ /* we might have released the lock */
+ if (G_UNLIKELY (GST_PAD_IS_FLUSHING (pad)))
+ goto flushing;
+
+ /* now we block the streaming thread. It can be unlocked when we
+ * deactivate the pad (which will also set the FLUSHING flag) or
+ * when the pad is unblocked. A flushing event will also unblock
+ * the pad after setting the FLUSHING flag. */
+ GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
+ "Waiting to be unblocked or set flushing");
+ GST_OBJECT_FLAG_SET (pad, GST_PAD_BLOCKING);
+ GST_PAD_BLOCK_WAIT (pad);
+ GST_OBJECT_FLAG_UNSET (pad, GST_PAD_BLOCKING);
+ GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "We got unblocked");
+
+ if (G_UNLIKELY (GST_PAD_IS_FLUSHING (pad)))
+ goto flushing;
+ }
+ }
+
+ return GST_FLOW_OK;
+
+ /* ERRORS */
+flushing:
+ {
+ GST_DEBUG_OBJECT (pad, "pad is flushing");
+ return GST_FLOW_WRONG_STATE;
+ }
+dropped:
+ {
+ GST_DEBUG_OBJECT (pad, "data is dropped");
+ return GST_FLOW_CUSTOM_SUCCESS;
+ }
+passed:
+ {
+ GST_DEBUG_OBJECT (pad, "data is passed");
+ return GST_FLOW_OK;
+ }
+}
+
+/* pad offsets */
+
+/**
+ * gst_pad_get_offset:
+ * @pad: a #GstPad
+ *
+ * Get the offset applied to the running time of @pad. @pad has to be a source
+ * pad.
+ *
+ * Returns: the offset.
+ */
+gint64
+gst_pad_get_offset (GstPad * pad)
+{
+ gint64 result;
+
+ g_return_val_if_fail (GST_IS_PAD (pad), 0);
+
+ GST_OBJECT_LOCK (pad);
+ result = pad->offset;
+ GST_OBJECT_UNLOCK (pad);
+
+ return result;
+}
+
+/**
+ * gst_pad_set_offset:
+ * @pad: a #GstPad
+ * @offset: the offset
+ *
+ * Set the offset that will be applied to the running time of @pad.
+ */
+void
+gst_pad_set_offset (GstPad * pad, gint64 offset)
+{
+ guint idx;
+ GstPad *peer;
+ GstPad *tmp = NULL;
+
+ g_return_if_fail (GST_IS_PAD (pad));
+
+ GST_OBJECT_LOCK (pad);
+ /* if nothing changed, do nothing */
+ if (pad->offset == offset)
+ goto done;
+
+ pad->offset = offset;
+
+ /* if no peer, we just updated the offset */
+ if ((peer = GST_PAD_PEER (pad)) == NULL)
+ goto done;
+
+ /* switch pads around when dealing with a sinkpad */
+ if (GST_PAD_IS_SINK (pad)) {
+ /* ref the peer so it doesn't go away when we release the lock */
+ tmp = gst_object_ref (peer);
+ /* make sure we get the peer (the srcpad) */
+ GST_OBJECT_UNLOCK (pad);
+
+ /* swap pads */
+ peer = pad;
+ pad = tmp;
+
+ GST_OBJECT_LOCK (pad);
+ /* check if the pad didn't get relinked */
+ if (GST_PAD_PEER (pad) != peer)
+ goto done;
+
+ /* we can release the ref now */
+ gst_object_unref (peer);
+ }
+
+ /* the index of the segment event in the array */
+ idx = GST_EVENT_STICKY_IDX_TYPE (GST_EVENT_SEGMENT);
+
+ /* lock order is srcpad >> sinkpad */
+ GST_OBJECT_LOCK (peer);
+ /* take the current segment event, adjust it and then place
+ * it on the sinkpad. events on the srcpad are always active. */
+ if (replace_event (pad, peer, idx))
+ GST_OBJECT_FLAG_SET (peer, GST_PAD_NEED_EVENTS);
+
+ GST_OBJECT_UNLOCK (peer);
+
+done:
+ GST_OBJECT_UNLOCK (pad);
+}
+
+
+/**********************************************************************
+ * Data passing functions
+ */
+
+/* this is the chain function that does not perform the additional argument
+ * checking for that little extra speed.
+ */
+static inline GstFlowReturn
+gst_pad_chain_data_unchecked (GstPad * pad, GstProbeType type, void *data)
+{
+ GstFlowReturn ret;
+ gboolean needs_events;
+
+ GST_PAD_STREAM_LOCK (pad);
+
+ GST_OBJECT_LOCK (pad);
+ if (G_UNLIKELY (GST_PAD_IS_FLUSHING (pad)))
+ goto flushing;
+
+ needs_events = GST_PAD_NEEDS_EVENTS (pad);
+ if (G_UNLIKELY (needs_events)) {
+ GST_OBJECT_FLAG_UNSET (pad, GST_PAD_NEED_EVENTS);
+
+ GST_DEBUG_OBJECT (pad, "need to update all events");
+ ret = gst_pad_update_events (pad);
+ if (G_UNLIKELY (ret != GST_FLOW_OK))
+ goto events_error;
+ }
+
+ PROBE (pad, GST_PROBE_TYPE_PUSH | type, data, probe_stopped);
+
+ GST_OBJECT_UNLOCK (pad);
+
+ /* NOTE: we read the chainfunc unlocked.
+ * we cannot hold the lock for the pad so we might send
+ * the data to the wrong function. This is not really a
+ * problem since functions are assigned at creation time
+ * and don't change that often... */
+ if (G_LIKELY (type & GST_PROBE_TYPE_BUFFER)) {
+ GstPadChainFunction chainfunc;
+
+ if (G_UNLIKELY ((chainfunc = GST_PAD_CHAINFUNC (pad)) == NULL))
+ goto no_function;
+
+ GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
+ "calling chainfunction &%s with buffer %p, ts %" GST_TIME_FORMAT,
+ GST_DEBUG_FUNCPTR_NAME (chainfunc),
+ GST_BUFFER (data), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (data)));
+
+ ret = chainfunc (pad, GST_BUFFER_CAST (data));
+
+ GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
+ "called chainfunction &%s with buffer %p, returned %s",
+ GST_DEBUG_FUNCPTR_NAME (chainfunc), data, gst_flow_get_name (ret));
+ } else {
+ GstPadChainListFunction chainlistfunc;
+
+ if (G_UNLIKELY ((chainlistfunc = GST_PAD_CHAINLISTFUNC (pad)) == NULL))
+ goto no_function;
+
+ GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
+ "calling chainlistfunction &%s",
+ GST_DEBUG_FUNCPTR_NAME (chainlistfunc));
+
+ ret = chainlistfunc (pad, GST_BUFFER_LIST_CAST (data));
+
+ GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
+ "called chainlistfunction &%s, returned %s",
+ GST_DEBUG_FUNCPTR_NAME (chainlistfunc), gst_flow_get_name (ret));
+ }
+
+ GST_PAD_STREAM_UNLOCK (pad);
+
+ return ret;
+
+ /* ERRORS */
+flushing:
+ {
+ GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
+ "chaining, but pad was flushing");
+ GST_OBJECT_UNLOCK (pad);
+ GST_PAD_STREAM_UNLOCK (pad);
+ return GST_FLOW_WRONG_STATE;
+ }
+events_error:
+ {
+ GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "events were not accepted");
+ GST_OBJECT_UNLOCK (pad);
+ GST_PAD_STREAM_UNLOCK (pad);
+ return ret;
+ }
+probe_stopped:
+ {
+ GST_OBJECT_UNLOCK (pad);
+ GST_PAD_STREAM_UNLOCK (pad);
+ gst_mini_object_unref (GST_MINI_OBJECT_CAST (data));
+
+ switch (ret) {
+ case GST_FLOW_CUSTOM_SUCCESS:
+ GST_DEBUG_OBJECT (pad, "dropped buffer");
+ ret = GST_FLOW_OK;
+ break;
+ default:
+ GST_DEBUG_OBJECT (pad, "en error occured %s", gst_flow_get_name (ret));
+ break;
+ }
+ return ret;
+ }
+no_function:
+ {
+ gst_mini_object_unref (GST_MINI_OBJECT_CAST (data));
+ GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
+ "pushing, but not chainhandler");
+ GST_ELEMENT_ERROR (GST_PAD_PARENT (pad), CORE, PAD, (NULL),
+ ("push on pad %s:%s but it has no chainfunction",
+ GST_DEBUG_PAD_NAME (pad)));
+ GST_PAD_STREAM_UNLOCK (pad);
+ return GST_FLOW_NOT_SUPPORTED;
+ }
+}
+
+/**
+ * gst_pad_chain:
+ * @pad: a sink #GstPad, returns GST_FLOW_ERROR if not.
+ * @buffer: (transfer full): the #GstBuffer to send, return GST_FLOW_ERROR
+ * if not.
+ *
+ * Chain a buffer to @pad.
+ *
+ * The function returns #GST_FLOW_WRONG_STATE if the pad was flushing.
+ *
+ * If the buffer type is not acceptable for @pad (as negotiated with a
+ * preceeding GST_EVENT_CAPS event), this function returns
+ * #GST_FLOW_NOT_NEGOTIATED.
+ *
+ * The function proceeds calling the chain function installed on @pad (see
+ * gst_pad_set_chain_function()) and the return value of that function is
+ * returned to the caller. #GST_FLOW_NOT_SUPPORTED is returned if @pad has no
+ * chain function.
+ *
+ * In all cases, success or failure, the caller loses its reference to @buffer
+ * after calling this function.
+ *
+ * Returns: a #GstFlowReturn from the pad.
+ *
+ * MT safe.
+ */
+GstFlowReturn
+gst_pad_chain (GstPad * pad, GstBuffer * buffer)
+{
+ g_return_val_if_fail (GST_IS_PAD (pad), GST_FLOW_ERROR);
+ g_return_val_if_fail (GST_PAD_IS_SINK (pad), GST_FLOW_ERROR);
+ g_return_val_if_fail (GST_IS_BUFFER (buffer), GST_FLOW_ERROR);
+
+ return gst_pad_chain_data_unchecked (pad, GST_PROBE_TYPE_BUFFER, buffer);
+}
+
+static GstFlowReturn
+gst_pad_chain_list_default (GstPad * pad, GstBufferList * list)
+{
+ guint i, len;
+ GstBuffer *buffer;
+ GstFlowReturn ret;
+
+ GST_INFO_OBJECT (pad, "chaining each group in list as a merged buffer");
+
+ len = gst_buffer_list_len (list);
+
+ ret = GST_FLOW_OK;
+ for (i = 0; i < len; i++) {
+ buffer = gst_buffer_list_get (list, i);
+ ret =
+ gst_pad_chain_data_unchecked (pad, GST_PROBE_TYPE_BUFFER,
+ gst_buffer_ref (buffer));
+ if (ret != GST_FLOW_OK)
+ break;
+ }
+ gst_buffer_list_unref (list);
+
+ return ret;
+}
+
+/**
+ * gst_pad_chain_list:
+ * @pad: a sink #GstPad, returns GST_FLOW_ERROR if not.
+ * @list: (transfer full): the #GstBufferList to send, return GST_FLOW_ERROR
+ * if not.
+ *
+ * Chain a bufferlist to @pad.
+ *
+ * The function returns #GST_FLOW_WRONG_STATE if the pad was flushing.
+ *
+ * If @pad was not negotiated properly with a CAPS event, this function
+ * returns #GST_FLOW_NOT_NEGOTIATED.
+ *
+ * The function proceeds calling the chainlist function installed on @pad (see
+ * gst_pad_set_chain_list_function()) and the return value of that function is
+ * returned to the caller. #GST_FLOW_NOT_SUPPORTED is returned if @pad has no
+ * chainlist function.
+ *
+ * In all cases, success or failure, the caller loses its reference to @list
+ * after calling this function.
+ *
+ * MT safe.
+ *
+ * Returns: a #GstFlowReturn from the pad.
+ *
+ * Since: 0.10.24
+ */
+GstFlowReturn
+gst_pad_chain_list (GstPad * pad, GstBufferList * list)
+{
+ g_return_val_if_fail (GST_IS_PAD (pad), GST_FLOW_ERROR);
+ g_return_val_if_fail (GST_PAD_IS_SINK (pad), GST_FLOW_ERROR);
+ g_return_val_if_fail (GST_IS_BUFFER_LIST (list), GST_FLOW_ERROR);
+
+ return gst_pad_chain_data_unchecked (pad, GST_PROBE_TYPE_BUFFER_LIST, list);
+}
+
+static GstFlowReturn
+gst_pad_push_data (GstPad * pad, GstProbeType type, void *data)
+{
+ GstPad *peer;
+ GstFlowReturn ret;
+
+ GST_OBJECT_LOCK (pad);
+ if (G_UNLIKELY (GST_PAD_IS_FLUSHING (pad)))
+ goto flushing;
+
+ type |= GST_PROBE_TYPE_PUSH;
+
+ /* do block probes */
+ PROBE (pad, type | GST_PROBE_TYPE_BLOCK, data, probe_stopped);
+
+ /* do post-blocking probes */
+ PROBE (pad, type, data, probe_stopped);
+
+ if (G_UNLIKELY ((peer = GST_PAD_PEER (pad)) == NULL))
+ goto not_linked;
+
+ /* take ref to peer pad before releasing the lock */
+ gst_object_ref (peer);
+ pad->priv->using++;
+ GST_OBJECT_UNLOCK (pad);
+
+ ret = gst_pad_chain_data_unchecked (peer, type, data);
+
+ gst_object_unref (peer);
+
+ GST_OBJECT_LOCK (pad);
+ pad->priv->using--;
+ if (pad->priv->using == 0) {
+ /* pad is not active anymore, trigger idle callbacks */
+ PROBE (pad, GST_PROBE_TYPE_PUSH | GST_PROBE_TYPE_IDLE, NULL, probe_stopped);
+ }
+ GST_OBJECT_UNLOCK (pad);
+
+ return ret;
+
+ /* ERROR recovery here */
+ /* ERRORS */
+flushing:
+ {
+ GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
+ "pushing, but pad was flushing");
+ GST_OBJECT_UNLOCK (pad);
+ gst_mini_object_unref (GST_MINI_OBJECT_CAST (data));
+ return GST_FLOW_WRONG_STATE;
+ }
+probe_stopped:
+ {
+ GST_OBJECT_UNLOCK (pad);
+ gst_mini_object_unref (GST_MINI_OBJECT_CAST (data));
+
+ switch (ret) {
+ case GST_FLOW_CUSTOM_SUCCESS:
+ GST_DEBUG_OBJECT (pad, "dropped buffer");
+ ret = GST_FLOW_OK;
+ break;
+ default:
+ GST_DEBUG_OBJECT (pad, "en error occured %s", gst_flow_get_name (ret));
+ break;
+ }
+ return ret;
+ }
+not_linked:
+ {
+ GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
+ "pushing, but it was not linked");
+ GST_OBJECT_UNLOCK (pad);
+ gst_mini_object_unref (GST_MINI_OBJECT_CAST (data));
+ return GST_FLOW_NOT_LINKED;
+ }
+}
+
+/**
+ * gst_pad_push:
+ * @pad: a source #GstPad, returns #GST_FLOW_ERROR if not.
+ * @buffer: (transfer full): the #GstBuffer to push returns GST_FLOW_ERROR
+ * if not.
+ *
+ * Pushes a buffer to the peer of @pad.
+ *
+ * This function will call installed block probes before triggering any
+ * installed data probes.
+ *
+ * The function proceeds calling gst_pad_chain() on the peer pad and returns
+ * the value from that function. If @pad has no peer, #GST_FLOW_NOT_LINKED will
+ * be returned.
+ *
+ * In all cases, success or failure, the caller loses its reference to @buffer
+ * after calling this function.
+ *
+ * Returns: a #GstFlowReturn from the peer pad.
+ *
+ * MT safe.
+ */
+GstFlowReturn
+gst_pad_push (GstPad * pad, GstBuffer * buffer)
+{
+ g_return_val_if_fail (GST_IS_PAD (pad), GST_FLOW_ERROR);
+ g_return_val_if_fail (GST_PAD_IS_SRC (pad), GST_FLOW_ERROR);
+ g_return_val_if_fail (GST_IS_BUFFER (buffer), GST_FLOW_ERROR);
+
+ return gst_pad_push_data (pad, GST_PROBE_TYPE_BUFFER, buffer);
+}
+
+/**
+ * gst_pad_push_list:
+ * @pad: a source #GstPad, returns #GST_FLOW_ERROR if not.
+ * @list: (transfer full): the #GstBufferList to push returns GST_FLOW_ERROR
+ * if not.
+ *
+ * Pushes a buffer list to the peer of @pad.
+ *
+ * This function will call installed block probes before triggering any
+ * installed data probes.
+ *
+ * The function proceeds calling the chain function on the peer pad and returns
+ * the value from that function. If @pad has no peer, #GST_FLOW_NOT_LINKED will
+ * be returned. If the peer pad does not have any installed chainlist function
+ * every group buffer of the list will be merged into a normal #GstBuffer and
+ * chained via gst_pad_chain().
+ *
+ * In all cases, success or failure, the caller loses its reference to @list
+ * after calling this function.
+ *
+ * Returns: a #GstFlowReturn from the peer pad.
+ *
+ * MT safe.
+ *
+ * Since: 0.10.24
+ */
+GstFlowReturn
+gst_pad_push_list (GstPad * pad, GstBufferList * list)
+{
+ g_return_val_if_fail (GST_IS_PAD (pad), GST_FLOW_ERROR);
+ g_return_val_if_fail (GST_PAD_IS_SRC (pad), GST_FLOW_ERROR);
+ g_return_val_if_fail (GST_IS_BUFFER_LIST (list), GST_FLOW_ERROR);
+
+ return gst_pad_push_data (pad, GST_PROBE_TYPE_BUFFER_LIST, list);
+}
+
+static GstFlowReturn
+gst_pad_get_range_unchecked (GstPad * pad, guint64 offset, guint size,
+ GstBuffer ** buffer)
+{
+ GstFlowReturn ret;
+ GstPadGetRangeFunction getrangefunc;
+
+ GST_PAD_STREAM_LOCK (pad);
+
+ GST_OBJECT_LOCK (pad);
+ if (G_UNLIKELY (GST_PAD_IS_FLUSHING (pad)))
+ goto flushing;
+ GST_OBJECT_UNLOCK (pad);
+
+ if (G_UNLIKELY ((getrangefunc = GST_PAD_GETRANGEFUNC (pad)) == NULL))
+ goto no_function;
+
+ GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
+ "calling getrangefunc %s, offset %"
+ G_GUINT64_FORMAT ", size %u",
+ GST_DEBUG_FUNCPTR_NAME (getrangefunc), offset, size);
+
+ ret = getrangefunc (pad, offset, size, buffer);
+
+ if (G_UNLIKELY (ret != GST_FLOW_OK))
+ goto get_range_failed;
+
+ /* can only fire the signal if we have a valid buffer */
+ GST_OBJECT_LOCK (pad);
+ PROBE (pad, GST_PROBE_TYPE_PULL | GST_PROBE_TYPE_BUFFER, *buffer,
+ probe_stopped);
+ GST_OBJECT_UNLOCK (pad);
+
+ GST_PAD_STREAM_UNLOCK (pad);
+
+ return ret;
+
+ /* ERRORS */
+flushing:
+ {
+ GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
+ "getrange, but pad was flushing");
+ GST_OBJECT_UNLOCK (pad);
+ GST_PAD_STREAM_UNLOCK (pad);
+ return GST_FLOW_WRONG_STATE;
+ }
+no_function:
+ {
+ GST_ELEMENT_ERROR (GST_PAD_PARENT (pad), CORE, PAD, (NULL),
+ ("getrange on pad %s:%s but it has no getrangefunction",
+ GST_DEBUG_PAD_NAME (pad)));
+ GST_PAD_STREAM_UNLOCK (pad);
+ return GST_FLOW_NOT_SUPPORTED;
+ }
+probe_stopped:
+ {
+ GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
+ "probe returned %s", gst_flow_get_name (ret));
+ GST_OBJECT_UNLOCK (pad);
+ GST_PAD_STREAM_UNLOCK (pad);
+ gst_buffer_unref (*buffer);
+ *buffer = NULL;
+ return ret;
+ }
+get_range_failed:
+ {
+ GST_PAD_STREAM_UNLOCK (pad);
+ *buffer = NULL;
+ GST_CAT_LEVEL_LOG (GST_CAT_SCHEDULING,
+ (ret >= GST_FLOW_UNEXPECTED) ? GST_LEVEL_INFO : GST_LEVEL_WARNING,
+ pad, "getrange failed, flow: %s", gst_flow_get_name (ret));
+ return ret;
+ }
+}
+
+/**
+ * gst_pad_get_range:
+ * @pad: a src #GstPad, returns #GST_FLOW_ERROR if not.
+ * @offset: The start offset of the buffer
+ * @size: The length of the buffer
+ * @buffer: (out callee-allocates): a pointer to hold the #GstBuffer,
+ * returns #GST_FLOW_ERROR if %NULL.
+ *
+ * When @pad is flushing this function returns #GST_FLOW_WRONG_STATE
+ * immediately and @buffer is %NULL.
+ *
+ * Calls the getrange function of @pad, see #GstPadGetRangeFunction for a
+ * description of a getrange function. If @pad has no getrange function
+ * installed (see gst_pad_set_getrange_function()) this function returns
+ * #GST_FLOW_NOT_SUPPORTED.
+ *
+ * This is a lowlevel function. Usualy gst_pad_pull_range() is used.
+ *
+ * Returns: a #GstFlowReturn from the pad.
+ *
+ * MT safe.
+ */
+GstFlowReturn
+gst_pad_get_range (GstPad * pad, guint64 offset, guint size,
+ GstBuffer ** buffer)
+{
+ g_return_val_if_fail (GST_IS_PAD (pad), GST_FLOW_ERROR);
+ g_return_val_if_fail (GST_PAD_IS_SRC (pad), GST_FLOW_ERROR);
+ g_return_val_if_fail (buffer != NULL, GST_FLOW_ERROR);
+
+ return gst_pad_get_range_unchecked (pad, offset, size, buffer);
+}
+
+/**
+ * gst_pad_pull_range:
+ * @pad: a sink #GstPad, returns GST_FLOW_ERROR if not.
+ * @offset: The start offset of the buffer
+ * @size: The length of the buffer
+ * @buffer: (out callee-allocates): a pointer to hold the #GstBuffer, returns
+ * GST_FLOW_ERROR if %NULL.
+ *
+ * Pulls a @buffer from the peer pad.
+ *
+ * This function will first trigger the pad block signal if it was
+ * installed.
+ *
+ * When @pad is not linked #GST_FLOW_NOT_LINKED is returned else this
+ * function returns the result of gst_pad_get_range() on the peer pad.
+ * See gst_pad_get_range() for a list of return values and for the
+ * semantics of the arguments of this function.
+ *
+ * Returns: a #GstFlowReturn from the peer pad.
+ * When this function returns #GST_FLOW_OK, @buffer will contain a valid
+ * #GstBuffer that should be freed with gst_buffer_unref() after usage.
+ * @buffer may not be used or freed when any other return value than
+ * #GST_FLOW_OK is returned.
+ *
+ * MT safe.
+ */
+GstFlowReturn
+gst_pad_pull_range (GstPad * pad, guint64 offset, guint size,
+ GstBuffer ** buffer)
+{
+ GstPad *peer;
+ GstFlowReturn ret;
+ gboolean needs_events;
+
+ g_return_val_if_fail (GST_IS_PAD (pad), GST_FLOW_ERROR);
+ g_return_val_if_fail (GST_PAD_IS_SINK (pad), GST_FLOW_ERROR);
+ g_return_val_if_fail (buffer != NULL, GST_FLOW_ERROR);
+
+ GST_OBJECT_LOCK (pad);
+ if (G_UNLIKELY (GST_PAD_IS_FLUSHING (pad)))
+ goto flushing;
+
+ PROBE (pad, GST_PROBE_TYPE_PULL | GST_PROBE_TYPE_BLOCK, NULL,
+ pre_probe_stopped);
+
+ if (G_UNLIKELY ((peer = GST_PAD_PEER (pad)) == NULL))
+ goto not_linked;
+
+ gst_object_ref (peer);
+ pad->priv->using++;
+ GST_OBJECT_UNLOCK (pad);
+
+ ret = gst_pad_get_range_unchecked (peer, offset, size, buffer);
+
+ gst_object_unref (peer);
+
+ GST_OBJECT_LOCK (pad);
+ pad->priv->using--;
+ if (pad->priv->using == 0) {
+ /* pad is not active anymore, trigger idle callbacks */
+ PROBE (pad, GST_PROBE_TYPE_PULL | GST_PROBE_TYPE_IDLE, NULL,
+ post_probe_stopped);
+ }
+
+ if (G_UNLIKELY (ret != GST_FLOW_OK))
+ goto pull_range_failed;
+
+ PROBE (pad, GST_PROBE_TYPE_PULL | GST_PROBE_TYPE_BUFFER, buffer,
+ post_probe_stopped);
+
+ needs_events = GST_PAD_NEEDS_EVENTS (pad);
+ if (G_UNLIKELY (needs_events)) {
+ GST_OBJECT_FLAG_UNSET (pad, GST_PAD_NEED_EVENTS);
+
+ GST_DEBUG_OBJECT (pad, "we need to update the events");
+ ret = gst_pad_update_events (pad);
+ if (G_UNLIKELY (ret != GST_FLOW_OK))
+ goto events_error;
+ }
+ GST_OBJECT_UNLOCK (pad);
+
+ return ret;
+
+ /* ERROR recovery here */
+flushing:
+ {
+ GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
+ "pullrange, but pad was flushing");
+ GST_OBJECT_UNLOCK (pad);
+ return GST_FLOW_WRONG_STATE;
+ }
+pre_probe_stopped:
+ {
+ GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "pre probe returned %s",
+ gst_flow_get_name (ret));
+ GST_OBJECT_UNLOCK (pad);
+ return ret;
+ }
+not_linked:
+ {
+ GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
+ "pulling range, but it was not linked");
+ GST_OBJECT_UNLOCK (pad);
+ return GST_FLOW_NOT_LINKED;
+ }
+pull_range_failed:
+ {
+ *buffer = NULL;
+ GST_OBJECT_UNLOCK (pad);
+ GST_CAT_LEVEL_LOG (GST_CAT_SCHEDULING,
+ (ret >= GST_FLOW_UNEXPECTED) ? GST_LEVEL_INFO : GST_LEVEL_WARNING,
+ pad, "pullrange failed, flow: %s", gst_flow_get_name (ret));
+ return ret;
+ }
+post_probe_stopped:
+ {
+ GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
+ "post probe returned %s", gst_flow_get_name (ret));
+ GST_OBJECT_UNLOCK (pad);
+ if (ret == GST_FLOW_OK)
+ gst_buffer_unref (*buffer);
+ *buffer = NULL;
+ return ret;
+ }
+events_error:
+ {
+ GST_OBJECT_UNLOCK (pad);
+ gst_buffer_unref (*buffer);
+ *buffer = NULL;
+ GST_CAT_WARNING_OBJECT (GST_CAT_SCHEDULING, pad,
+ "pullrange returned events that were not accepted");
+ return ret;
+ }
+}
+
+/**
+ * gst_pad_push_event:
+ * @pad: a #GstPad to push the event to.
+ * @event: (transfer full): the #GstEvent to send to the pad.
+ *
+ * Sends the event to the peer of the given pad. This function is
+ * mainly used by elements to send events to their peer
+ * elements.
+ *
+ * This function takes owership of the provided event so you should
+ * gst_event_ref() it if you want to reuse the event after this call.
+ *
+ * Returns: TRUE if the event was handled.
+ *
+ * MT safe.
+ */
+gboolean
+gst_pad_push_event (GstPad * pad, GstEvent * event)
+{
+ GstFlowReturn ret;
+ GstPad *peerpad;
+ gboolean result;
+ gboolean stored = FALSE;
+
+ g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+ g_return_val_if_fail (GST_IS_EVENT (event), FALSE);
+
+ GST_OBJECT_LOCK (pad);
+
+ peerpad = GST_PAD_PEER (pad);
+
+ /* Two checks to be made:
+ * . (un)set the FLUSHING flag for flushing events,
+ * . handle pad blocking */
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_FLUSH_START:
+ GST_PAD_SET_FLUSHING (pad);
+
+ if (G_UNLIKELY (GST_PAD_IS_BLOCKED (pad))) {
+ /* flush start will have set the FLUSHING flag and will then
+ * unlock all threads doing a GCond wait on the blocking pad. This
+ * will typically unblock the STREAMING thread blocked on a pad. */
+ GST_LOG_OBJECT (pad, "Pad is blocked, not forwarding flush-start, "
+ "doing block signal.");
+ GST_PAD_BLOCK_BROADCAST (pad);
+ goto flushed;
+ }
+ break;
+ case GST_EVENT_FLUSH_STOP:
+ GST_PAD_UNSET_FLUSHING (pad);
+ if (G_UNLIKELY (GST_PAD_IS_BLOCKED (pad))) {
+ GST_LOG_OBJECT (pad, "Pad is blocked, not forwarding flush-stop");
+ goto flushed;
+ }
+ break;
+ default:
+ {
+ /* store the event on the pad, but only on srcpads */
+ if (GST_PAD_IS_SRC (pad) && GST_EVENT_IS_STICKY (event)) {
+ guint idx;
+
+ idx = GST_EVENT_STICKY_IDX (event);
+ GST_LOG_OBJECT (pad, "storing sticky event %s at index %u",
+ GST_EVENT_TYPE_NAME (event), idx);
+
+ /* srcpad sticky events always become active immediately */
+ gst_event_replace (&pad->priv->events[idx].event, event);
+
+ stored = TRUE;
+ }
+
+ /* backwards compatibility mode for caps */
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_CAPS:
+ {
+ GST_OBJECT_UNLOCK (pad);
+
+ g_object_notify_by_pspec ((GObject *) pad, pspec_caps);
+
+ GST_OBJECT_LOCK (pad);
+ /* the peerpad might have changed. Things we checked above could not
+ * have changed. */
+ peerpad = GST_PAD_PEER (pad);
+ break;
+ }
+ case GST_EVENT_SEGMENT:
+ {
+ gint64 offset;
+
+ offset = pad->offset;
+ /* check if we need to adjust the segment */
+ if (offset != 0 && (peerpad != NULL)) {
+ GstSegment segment;
+
+ /* copy segment values */
+ gst_event_copy_segment (event, &segment);
+ gst_event_unref (event);
+
+ /* adjust and make a new event with the offset applied */
+ segment.base += offset;
+ event = gst_event_new_segment (&segment);
+ }
+ break;
+ }
+ case GST_EVENT_RECONFIGURE:
+ if (GST_PAD_IS_SINK (pad))
+ GST_OBJECT_FLAG_SET (pad, GST_PAD_NEED_RECONFIGURE);
+ break;
+ default:
+ break;
+ }
+
+ if (G_UNLIKELY (GST_PAD_IS_FLUSHING (pad)))
+ goto flushed;
+
+ PROBE (pad, GST_PROBE_TYPE_PUSH | GST_PROBE_TYPE_EVENT
+ | GST_PROBE_TYPE_BLOCK, event, probe_stopped);
+
+ break;
+ }
+ }
+
+ /* send probes after modifying the events above */
+ PROBE (pad, GST_PROBE_TYPE_PUSH | GST_PROBE_TYPE_EVENT, event, probe_stopped);
+
+ /* now check the peer pad */
+ if (peerpad == NULL)
+ goto not_linked;
+
+ gst_object_ref (peerpad);
+ pad->priv->using++;
+ GST_OBJECT_UNLOCK (pad);
+
+ GST_LOG_OBJECT (pad, "sending event %p (%s) to peerpad %" GST_PTR_FORMAT,
+ event, GST_EVENT_TYPE_NAME (event), peerpad);
+
+ result = gst_pad_send_event (peerpad, event);
+
+ /* Note: we gave away ownership of the event at this point but we can still
+ * print the old pointer */
+ GST_LOG_OBJECT (pad,
+ "sent event %p to peerpad %" GST_PTR_FORMAT ", result %d", event, peerpad,
+ result);
+
+ gst_object_unref (peerpad);
+
+ GST_OBJECT_LOCK (pad);
+ pad->priv->using--;
+ if (pad->priv->using == 0) {
+ /* pad is not active anymore, trigger idle callbacks */
+ PROBE (pad, GST_PROBE_TYPE_PUSH | GST_PROBE_TYPE_IDLE, NULL, probe_stopped);
+ }
+ GST_OBJECT_UNLOCK (pad);
+
+ return result | stored;
+
+ /* ERROR handling */
+flushed:
+ {
+ GST_DEBUG_OBJECT (pad, "We're flushing");
+ GST_OBJECT_UNLOCK (pad);
+ gst_event_unref (event);
+ return stored;
+ }
+probe_stopped:
+ {
+ GST_DEBUG_OBJECT (pad, "Probe returned %s", gst_flow_get_name (ret));
+ GST_OBJECT_UNLOCK (pad);
+ gst_event_unref (event);
+ return stored;
+ }
+not_linked:
+ {
+ GST_DEBUG_OBJECT (pad, "Dropping event because pad is not linked");
+ GST_OBJECT_UNLOCK (pad);
+ gst_event_unref (event);
+ return stored;
+ }
+}
+
+/**
+ * gst_pad_send_event:
+ * @pad: a #GstPad to send the event to.
+ * @event: (transfer full): the #GstEvent to send to the pad.
+ *
+ * Sends the event to the pad. This function can be used
+ * by applications to send events in the pipeline.
+ *
+ * If @pad is a source pad, @event should be an upstream event. If @pad is a
+ * sink pad, @event should be a downstream event. For example, you would not
+ * send a #GST_EVENT_EOS on a src pad; EOS events only propagate downstream.
+ * Furthermore, some downstream events have to be serialized with data flow,
+ * like EOS, while some can travel out-of-band, like #GST_EVENT_FLUSH_START. If
+ * the event needs to be serialized with data flow, this function will take the
+ * pad's stream lock while calling its event function.
+ *
+ * To find out whether an event type is upstream, downstream, or downstream and
+ * serialized, see #GstEventTypeFlags, gst_event_type_get_flags(),
+ * #GST_EVENT_IS_UPSTREAM, #GST_EVENT_IS_DOWNSTREAM, and
+ * #GST_EVENT_IS_SERIALIZED. Note that in practice that an application or
+ * plugin doesn't need to bother itself with this information; the core handles
+ * all necessary locks and checks.
+ *
+ * This function takes owership of the provided event so you should
+ * gst_event_ref() it if you want to reuse the event after this call.
+ *
+ * Returns: TRUE if the event was handled.
+ */
+gboolean
+gst_pad_send_event (GstPad * pad, GstEvent * event)
+{
+ GstFlowReturn ret;
+ gboolean result = FALSE;
+ gboolean serialized, need_unlock = FALSE, needs_events, sticky;
+
+ g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ GST_OBJECT_LOCK (pad);
+ if (GST_PAD_IS_SINK (pad)) {
+ if (G_UNLIKELY (!GST_EVENT_IS_DOWNSTREAM (event)))
+ goto wrong_direction;
+ serialized = GST_EVENT_IS_SERIALIZED (event);
+ sticky = GST_EVENT_IS_STICKY (event);
+ } else if (GST_PAD_IS_SRC (pad)) {
+ if (G_UNLIKELY (!GST_EVENT_IS_UPSTREAM (event)))
+ goto wrong_direction;
+ /* events on srcpad never are serialized and sticky */
+ serialized = sticky = FALSE;
+ } else
+ goto unknown_direction;
+
+ /* get the flag first, we clear it when we have a FLUSH or a non-serialized
+ * event. */
+ needs_events = GST_PAD_NEEDS_EVENTS (pad);
+
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_FLUSH_START:
+ GST_CAT_DEBUG_OBJECT (GST_CAT_EVENT, pad,
+ "have event type %d (FLUSH_START)", GST_EVENT_TYPE (event));
+
+ /* can't even accept a flush begin event when flushing */
+ if (GST_PAD_IS_FLUSHING (pad))
+ goto flushing;
+
+ GST_PAD_SET_FLUSHING (pad);
+ GST_CAT_DEBUG_OBJECT (GST_CAT_EVENT, pad, "set flush flag");
+ needs_events = FALSE;
+ break;
+ case GST_EVENT_FLUSH_STOP:
+ if (G_LIKELY (GST_PAD_ACTIVATE_MODE (pad) != GST_ACTIVATE_NONE)) {
+ GST_PAD_UNSET_FLUSHING (pad);
+ GST_CAT_DEBUG_OBJECT (GST_CAT_EVENT, pad, "cleared flush flag");
+ }
+ GST_OBJECT_UNLOCK (pad);
+ /* grab stream lock */
+ GST_PAD_STREAM_LOCK (pad);
+ need_unlock = TRUE;
+ GST_OBJECT_LOCK (pad);
+ needs_events = FALSE;
+ break;
+ case GST_EVENT_RECONFIGURE:
+ if (GST_PAD_IS_SRC (pad))
+ GST_OBJECT_FLAG_SET (pad, GST_PAD_NEED_RECONFIGURE);
+ default:
+ GST_CAT_DEBUG_OBJECT (GST_CAT_EVENT, pad, "have event type %s",
+ GST_EVENT_TYPE_NAME (event));
+
+ if (serialized) {
+ /* lock order: STREAM_LOCK, LOCK, recheck flushing. */
+ GST_OBJECT_UNLOCK (pad);
+ GST_PAD_STREAM_LOCK (pad);
+ need_unlock = TRUE;
+ GST_OBJECT_LOCK (pad);
+ } else {
+ /* don't forward events on non-serialized events */
+ needs_events = FALSE;
+ }
+
+ /* store the event on the pad, but only on srcpads. We need to store the
+ * event before checking the flushing flag. */
+ if (sticky) {
+ guint idx;
+ PadEvent *ev;
+
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_SEGMENT:
+ if (pad->offset != 0) {
+ GstSegment segment;
+
+ /* copy segment values */
+ gst_event_copy_segment (event, &segment);
+ gst_event_unref (event);
+
+ /* adjust and make a new event with the offset applied */
+ segment.base += pad->offset;
+ event = gst_event_new_segment (&segment);
+ }
+ break;
+ default:
+ break;
+ }
+
+ idx = GST_EVENT_STICKY_IDX (event);
+ ev = &pad->priv->events[idx];
+
+ if (ev->event != event) {
+ GST_LOG_OBJECT (pad, "storing sticky event %s at index %u",
+ GST_EVENT_TYPE_NAME (event), idx);
+ gst_event_replace (&ev->pending, event);
+ /* set the flag so that we update the events next time. We would
+ * usually update below but we might be flushing too. */
+ GST_OBJECT_FLAG_SET (pad, GST_PAD_NEED_EVENTS);
+ needs_events = TRUE;
+ }
+ }
+ /* now do the probe */
+ if (G_UNLIKELY (GST_PAD_IS_FLUSHING (pad)))
+ goto flushing;
+
+ PROBE (pad, GST_PROBE_TYPE_PUSH | GST_PROBE_TYPE_EVENT, event,
+ probe_stopped);
+
+ break;
+ }
+
+ if (G_UNLIKELY (needs_events)) {
+ GstFlowReturn ret;
+
+ GST_OBJECT_FLAG_UNSET (pad, GST_PAD_NEED_EVENTS);
+
+ GST_DEBUG_OBJECT (pad, "need to update all events");
+ ret = gst_pad_update_events (pad);
+ if (ret != GST_FLOW_OK)
+ goto update_failed;
+ GST_OBJECT_UNLOCK (pad);
+
+ gst_event_unref (event);
+
+ result = TRUE;
+ }
+
+ /* ensure to pass on event;
+ * note that a sticky event has already been updated above */
+ if (G_LIKELY (!needs_events || !sticky)) {
+ GstPadEventFunction eventfunc;
+
+ if (G_UNLIKELY ((eventfunc = GST_PAD_EVENTFUNC (pad)) == NULL))
+ goto no_function;
+
+ GST_OBJECT_UNLOCK (pad);
+
+ result = eventfunc (pad, event);
+ }
+
+ if (need_unlock)
+ GST_PAD_STREAM_UNLOCK (pad);
+
+ GST_DEBUG_OBJECT (pad, "sent event, result %d", result);
+
+ return result;
+
+ /* ERROR handling */
+wrong_direction:
+ {
+ g_warning ("pad %s:%s sending %s event in wrong direction",
+ GST_DEBUG_PAD_NAME (pad), GST_EVENT_TYPE_NAME (event));
+ GST_OBJECT_UNLOCK (pad);
+ gst_event_unref (event);
+ return FALSE;
+ }
+unknown_direction:
+ {
+ g_warning ("pad %s:%s has invalid direction", GST_DEBUG_PAD_NAME (pad));
+ GST_OBJECT_UNLOCK (pad);
+ gst_event_unref (event);
+ return FALSE;
+ }
+no_function:
+ {
+ g_warning ("pad %s:%s has no event handler, file a bug.",
+ GST_DEBUG_PAD_NAME (pad));
+ GST_OBJECT_UNLOCK (pad);
+ if (need_unlock)
+ GST_PAD_STREAM_UNLOCK (pad);
+ gst_event_unref (event);
+ return FALSE;
+ }
+flushing:
+ {
+ GST_OBJECT_UNLOCK (pad);
+ if (need_unlock)
+ GST_PAD_STREAM_UNLOCK (pad);
+ GST_CAT_INFO_OBJECT (GST_CAT_EVENT, pad,
+ "Received event on flushing pad. Discarding");
+ gst_event_unref (event);
+ return FALSE;
+ }
+probe_stopped:
+ {
+ GST_DEBUG_OBJECT (pad, "probe returned %s", gst_flow_get_name (ret));
+ GST_OBJECT_UNLOCK (pad);
+ if (need_unlock)
+ GST_PAD_STREAM_UNLOCK (pad);
+ gst_event_unref (event);
+ return FALSE;
+ }
+update_failed:
+ {
+ GST_OBJECT_UNLOCK (pad);
+ if (need_unlock)
+ GST_PAD_STREAM_UNLOCK (pad);
+ GST_CAT_INFO_OBJECT (GST_CAT_EVENT, pad, "Update events failed");
+ gst_event_unref (event);
+ return FALSE;
+ }
+}
+
+/**
+ * gst_pad_set_element_private:
+ * @pad: the #GstPad to set the private data of.
+ * @priv: The private data to attach to the pad.
+ *
+ * Set the given private data gpointer on the pad.
+ * This function can only be used by the element that owns the pad.
+ * No locking is performed in this function.
+ */
+void
+gst_pad_set_element_private (GstPad * pad, gpointer priv)
+{
+ pad->element_private = priv;
+}
+
+/**
+ * gst_pad_get_element_private:
+ * @pad: the #GstPad to get the private data of.
+ *
+ * Gets the private data of a pad.
+ * No locking is performed in this function.
+ *
+ * Returns: (transfer none): a #gpointer to the private data.
+ */
+gpointer
+gst_pad_get_element_private (GstPad * pad)
+{
+ return pad->element_private;
+}
+
+/**
+ * gst_pad_get_sticky_event:
+ * @pad: the #GstPad to get the event from.
+ * @event_type: the #GstEventType that should be retrieved.
+ *
+ * Returns a new reference of the sticky event of type @event_type
+ * from the event.
+ *
+ * Returns: (transfer full): a #GstEvent of type @event_type. Unref after usage.
+ */
+GstEvent *
+gst_pad_get_sticky_event (GstPad * pad, GstEventType event_type)
+{
+ GstEvent *event = NULL;
+ guint idx;
+
+ g_return_val_if_fail (GST_IS_PAD (pad), NULL);
+ g_return_val_if_fail ((event_type & GST_EVENT_TYPE_STICKY) != 0, NULL);
+
+ idx = GST_EVENT_STICKY_IDX_TYPE (event_type);
+
+ GST_OBJECT_LOCK (pad);
+ if ((event = pad->priv->events[idx].event)) {
+ gst_event_ref (event);
+ }
+ GST_OBJECT_UNLOCK (pad);
+
+ return event;
+}
+
+/**
+ * gst_pad_sticky_events_foreach:
+ * @pad: the #GstPad that should be used for iteration.
+ * @foreach_func: (scope call): the #GstPadStickyEventsForeachFunction that should be called for every event.
+ * @user_data: (closure): the optional user data.
+ *
+ * Iterates all active sticky events on @pad and calls @foreach_func for every
+ * event. If @foreach_func returns something else than GST_FLOW_OK the iteration
+ * is immediately stopped.
+ *
+ * Returns: GST_FLOW_OK if iteration was successful
+ */
+GstFlowReturn
+gst_pad_sticky_events_foreach (GstPad * pad,
+ GstPadStickyEventsForeachFunction foreach_func, gpointer user_data)
+{
+ GstFlowReturn ret = GST_FLOW_OK;
+ guint i;
+ GstEvent *event;
+
+ g_return_val_if_fail (GST_IS_PAD (pad), GST_FLOW_ERROR);
+ g_return_val_if_fail (foreach_func != NULL, GST_FLOW_ERROR);
+
+ GST_OBJECT_LOCK (pad);
+
+restart:
+ for (i = 0; i < GST_EVENT_MAX_STICKY; i++) {
+ gboolean res;
+ PadEvent *ev;
+
+ ev = &pad->priv->events[i];
+
+ /* skip without active event */
+ if ((event = ev->event) == NULL)
+ continue;
+
+ gst_event_ref (event);
+ GST_OBJECT_UNLOCK (pad);
+
+ res = foreach_func (pad, event, user_data);
+
+ GST_OBJECT_LOCK (pad);
+ gst_event_unref (event);
+
+ if (res != GST_FLOW_OK) {
+ ret = res;
+ break;
+ }
+
+ /* things could have changed while we release the lock, check if we still
+ * are handling the same event, if we don't something changed and we have
+ * to try again. FIXME. we need a cookie here. */
+ if (event != ev->event) {
+ GST_DEBUG_OBJECT (pad, "events changed, restarting");
+ goto restart;
+ }
+ }
+ GST_OBJECT_UNLOCK (pad);
+
+ return ret;
+}
+
+static void
+do_stream_status (GstPad * pad, GstStreamStatusType type,
+ GThread * thread, GstTask * task)
+{
+ GstElement *parent;
+
+ GST_DEBUG_OBJECT (pad, "doing stream-status %d", type);
+
+ if ((parent = GST_ELEMENT_CAST (gst_pad_get_parent (pad)))) {
+ if (GST_IS_ELEMENT (parent)) {
+ GstMessage *message;
+ GValue value = { 0 };
+
+ if (type == GST_STREAM_STATUS_TYPE_ENTER) {
+ gchar *tname, *ename, *pname;
+
+ /* create a good task name */
+ ename = gst_element_get_name (parent);
+ pname = gst_pad_get_name (pad);
+ tname = g_strdup_printf ("%s:%s", ename, pname);
+ g_free (ename);
+ g_free (pname);
+
+ gst_object_set_name (GST_OBJECT_CAST (task), tname);
+ g_free (tname);
+ }
+
+ message = gst_message_new_stream_status (GST_OBJECT_CAST (pad),
+ type, parent);
+
+ g_value_init (&value, GST_TYPE_TASK);
+ g_value_set_object (&value, task);
+ gst_message_set_stream_status_object (message, &value);
+ g_value_unset (&value);
+
+ GST_DEBUG_OBJECT (pad, "posting stream-status %d", type);
+ gst_element_post_message (parent, message);
+ }
+ gst_object_unref (parent);
+ }
+}
+
+static void
+pad_enter_thread (GstTask * task, GThread * thread, gpointer user_data)
+{
+ do_stream_status (GST_PAD_CAST (user_data), GST_STREAM_STATUS_TYPE_ENTER,
+ thread, task);
+}
+
+static void
+pad_leave_thread (GstTask * task, GThread * thread, gpointer user_data)
+{
+ do_stream_status (GST_PAD_CAST (user_data), GST_STREAM_STATUS_TYPE_LEAVE,
+ thread, task);
+}
+
+static GstTaskThreadCallbacks thr_callbacks = {
+ pad_enter_thread,
+ pad_leave_thread,
+};
+
+/**
+ * gst_pad_start_task:
+ * @pad: the #GstPad to start the task of
+ * @func: the task function to call
+ * @data: data passed to the task function
+ *
+ * Starts a task that repeatedly calls @func with @data. This function
+ * is mostly used in pad activation functions to start the dataflow.
+ * The #GST_PAD_STREAM_LOCK of @pad will automatically be acquired
+ * before @func is called.
+ *
+ * Returns: a %TRUE if the task could be started.
+ */
+gboolean
+gst_pad_start_task (GstPad * pad, GstTaskFunction func, gpointer data)
+{
+ GstTask *task;
+ gboolean res;
+
+ g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
+ g_return_val_if_fail (func != NULL, FALSE);
+
+ GST_DEBUG_OBJECT (pad, "start task");
+
+ GST_OBJECT_LOCK (pad);
+ task = GST_PAD_TASK (pad);
+ if (task == NULL) {
+ task = gst_task_create (func, data);
+ gst_task_set_lock (task, GST_PAD_GET_STREAM_LOCK (pad));
+ gst_task_set_thread_callbacks (task, &thr_callbacks, pad, NULL);
+ GST_DEBUG_OBJECT (pad, "created task");
+ GST_PAD_TASK (pad) = task;
+ gst_object_ref (task);
+ /* release lock to post the message */
+ GST_OBJECT_UNLOCK (pad);
+
+ do_stream_status (pad, GST_STREAM_STATUS_TYPE_CREATE, NULL, task);
+
+ gst_object_unref (task);
+
+ GST_OBJECT_LOCK (pad);
+ /* nobody else is supposed to have changed the pad now */
+ if (GST_PAD_TASK (pad) != task)
+ goto concurrent_stop;
+ }
+ res = gst_task_set_state (task, GST_TASK_STARTED);
+ GST_OBJECT_UNLOCK (pad);
+
+ return res;
+
+ /* ERRORS */
+concurrent_stop:
+ {
+ GST_OBJECT_UNLOCK (pad);
+ return TRUE;
+ }
+}
+
+/**
+ * gst_pad_pause_task:
+ * @pad: the #GstPad to pause the task of
+ *
+ * Pause the task of @pad. This function will also wait until the
+ * function executed by the task is finished if this function is not
+ * called from the task function.
+ *
+ * Returns: a TRUE if the task could be paused or FALSE when the pad
+ * has no task.
+ */
+gboolean
+gst_pad_pause_task (GstPad * pad)
+{
+ GstTask *task;
+ gboolean res;
+
+ g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
+
+ GST_DEBUG_OBJECT (pad, "pause task");
+
+ GST_OBJECT_LOCK (pad);
+ task = GST_PAD_TASK (pad);
+ if (task == NULL)
+ goto no_task;
+ res = gst_task_set_state (task, GST_TASK_PAUSED);
+ GST_OBJECT_UNLOCK (pad);
+
+ /* wait for task function to finish, this lock is recursive so it does nothing
+ * when the pause is called from the task itself */
+ GST_PAD_STREAM_LOCK (pad);
+ GST_PAD_STREAM_UNLOCK (pad);
+
+ return res;
+
+no_task:
+ {
+ GST_DEBUG_OBJECT (pad, "pad has no task");
+ GST_OBJECT_UNLOCK (pad);
+ return FALSE;
+ }
+}
+
+/**
+ * gst_pad_stop_task:
+ * @pad: the #GstPad to stop the task of
+ *
+ * Stop the task of @pad. This function will also make sure that the
+ * function executed by the task will effectively stop if not called
+ * from the GstTaskFunction.
+ *
+ * This function will deadlock if called from the GstTaskFunction of
+ * the task. Use gst_task_pause() instead.
+ *
+ * Regardless of whether the pad has a task, the stream lock is acquired and
+ * released so as to ensure that streaming through this pad has finished.
+ *
+ * Returns: a TRUE if the task could be stopped or FALSE on error.
+ */
+gboolean
+gst_pad_stop_task (GstPad * pad)
+{
+ GstTask *task;
+ gboolean res;
+
+ g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
+
+ GST_DEBUG_OBJECT (pad, "stop task");
+
+ GST_OBJECT_LOCK (pad);
+ task = GST_PAD_TASK (pad);
+ if (task == NULL)
+ goto no_task;
+ GST_PAD_TASK (pad) = NULL;
+ res = gst_task_set_state (task, GST_TASK_STOPPED);
+ GST_OBJECT_UNLOCK (pad);
+
+ GST_PAD_STREAM_LOCK (pad);
+ GST_PAD_STREAM_UNLOCK (pad);
+
+ if (!gst_task_join (task))
+ goto join_failed;
+
+ gst_object_unref (task);
+
+ return res;
+
+no_task:
+ {
+ GST_DEBUG_OBJECT (pad, "no task");
+ GST_OBJECT_UNLOCK (pad);
+
+ GST_PAD_STREAM_LOCK (pad);
+ GST_PAD_STREAM_UNLOCK (pad);
+
+ /* this is not an error */
+ return TRUE;
+ }
+join_failed:
+ {
+ /* this is bad, possibly the application tried to join the task from
+ * the task's thread. We install the task again so that it will be stopped
+ * again from the right thread next time hopefully. */
+ GST_OBJECT_LOCK (pad);
+ GST_DEBUG_OBJECT (pad, "join failed");
+ /* we can only install this task if there was no other task */
+ if (GST_PAD_TASK (pad) == NULL)
+ GST_PAD_TASK (pad) = task;
+ GST_OBJECT_UNLOCK (pad);
+
+ return FALSE;
+ }
+}
diff --git a/gst/gstpad.h b/gst/gstpad.h
new file mode 100644
index 0000000..d9ed2c5
--- /dev/null
+++ b/gst/gstpad.h
@@ -0,0 +1,927 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2000 Wim Taymans <wim.taymans@chello.be>
+ *
+ * gstpad.h: Header for GstPad object
+ *
+ * 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_PAD_H__
+#define __GST_PAD_H__
+
+#include <gst/gstconfig.h>
+
+typedef struct _GstPad GstPad;
+typedef struct _GstPadPrivate GstPadPrivate;
+typedef struct _GstPadClass GstPadClass;
+
+/**
+ * GstPadDirection:
+ * @GST_PAD_UNKNOWN: direction is unknown.
+ * @GST_PAD_SRC: the pad is a source pad.
+ * @GST_PAD_SINK: the pad is a sink pad.
+ *
+ * The direction of a pad.
+ */
+typedef enum {
+ GST_PAD_UNKNOWN,
+ GST_PAD_SRC,
+ GST_PAD_SINK
+} GstPadDirection;
+
+#include <gst/gstobject.h>
+#include <gst/gstbuffer.h>
+#include <gst/gstbufferlist.h>
+#include <gst/gstcaps.h>
+#include <gst/gstpadtemplate.h>
+#include <gst/gstevent.h>
+#include <gst/gstquery.h>
+#include <gst/gsttask.h>
+
+G_BEGIN_DECLS
+
+/*
+ * Pad base class
+ */
+#define GST_TYPE_PAD (gst_pad_get_type ())
+#define GST_IS_PAD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_PAD))
+#define GST_IS_PAD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_PAD))
+#define GST_PAD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_PAD, GstPad))
+#define GST_PAD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_PAD, GstPadClass))
+#define GST_PAD_CAST(obj) ((GstPad*)(obj))
+
+
+
+/**
+ * GstPadLinkReturn:
+ * @GST_PAD_LINK_OK : link succeeded
+ * @GST_PAD_LINK_WRONG_HIERARCHY: pads have no common grandparent
+ * @GST_PAD_LINK_WAS_LINKED : pad was already linked
+ * @GST_PAD_LINK_WRONG_DIRECTION: pads have wrong direction
+ * @GST_PAD_LINK_NOFORMAT : pads do not have common format
+ * @GST_PAD_LINK_NOSCHED : pads cannot cooperate in scheduling
+ * @GST_PAD_LINK_REFUSED : refused for some reason
+ *
+ * Result values from gst_pad_link and friends.
+ */
+typedef enum {
+ GST_PAD_LINK_OK = 0,
+ GST_PAD_LINK_WRONG_HIERARCHY = -1,
+ GST_PAD_LINK_WAS_LINKED = -2,
+ GST_PAD_LINK_WRONG_DIRECTION = -3,
+ GST_PAD_LINK_NOFORMAT = -4,
+ GST_PAD_LINK_NOSCHED = -5,
+ GST_PAD_LINK_REFUSED = -6
+} GstPadLinkReturn;
+
+/**
+ * GST_PAD_LINK_FAILED:
+ * @ret: the #GstPadLinkReturn value
+ *
+ * Macro to test if the given #GstPadLinkReturn value indicates a failed
+ * link step.
+ */
+#define GST_PAD_LINK_FAILED(ret) ((ret) < GST_PAD_LINK_OK)
+
+/**
+ * GST_PAD_LINK_SUCCESSFUL:
+ * @ret: the #GstPadLinkReturn value
+ *
+ * Macro to test if the given #GstPadLinkReturn value indicates a successful
+ * link step.
+ */
+#define GST_PAD_LINK_SUCCESSFUL(ret) ((ret) >= GST_PAD_LINK_OK)
+
+/**
+ * GstFlowReturn:
+ * @GST_FLOW_RESEND: Resend buffer, possibly with new caps (not
+ * sent yet) (unused/unimplemented).
+ * @GST_FLOW_OK: Data passing was ok.
+ * @GST_FLOW_NOT_LINKED: Pad is not linked.
+ * @GST_FLOW_WRONG_STATE: Pad is in wrong state.
+ * @GST_FLOW_UNEXPECTED: Did not expect anything, like after EOS.
+ * @GST_FLOW_NOT_NEGOTIATED: Pad is not negotiated.
+ * @GST_FLOW_ERROR: Some (fatal) error occured. Element generating
+ * this error should post an error message with more
+ * details.
+ * @GST_FLOW_NOT_SUPPORTED: This operation is not supported.
+ * @GST_FLOW_CUSTOM_SUCCESS: Elements can use values starting from
+ * this (and higher) to define custom success
+ * codes. Since 0.10.7.
+ * @GST_FLOW_CUSTOM_SUCCESS_1: Pre-defined custom success code (define your
+ * custom success code to this to avoid compiler
+ * warnings). Since 0.10.29.
+ * @GST_FLOW_CUSTOM_SUCCESS_2: Pre-defined custom success code. Since 0.10.29.
+ * @GST_FLOW_CUSTOM_ERROR: Elements can use values starting from
+ * this (and lower) to define custom error codes.
+ * Since 0.10.7.
+ * @GST_FLOW_CUSTOM_ERROR_1: Pre-defined custom error code (define your
+ * custom error code to this to avoid compiler
+ * warnings). Since 0.10.29.
+ * @GST_FLOW_CUSTOM_ERROR_2: Pre-defined custom error code. Since 0.10.29.
+ *
+ * The result of passing data to a pad.
+ *
+ * Note that the custom return values should not be exposed outside of the
+ * element scope and are available since 0.10.7.
+ */
+/* FIXME 0.11: remove custom flow returns */
+typedef enum {
+ /* custom success starts here */
+ GST_FLOW_CUSTOM_SUCCESS_2 = 102,
+ GST_FLOW_CUSTOM_SUCCESS_1 = 101,
+ GST_FLOW_CUSTOM_SUCCESS = 100,
+
+ /* core predefined */
+ GST_FLOW_RESEND = 1,
+ GST_FLOW_OK = 0,
+ /* expected failures */
+ GST_FLOW_NOT_LINKED = -1,
+ GST_FLOW_WRONG_STATE = -2,
+ /* error cases */
+ GST_FLOW_UNEXPECTED = -3,
+ GST_FLOW_NOT_NEGOTIATED = -4,
+ GST_FLOW_ERROR = -5,
+ GST_FLOW_NOT_SUPPORTED = -6,
+
+ /* custom error starts here */
+ GST_FLOW_CUSTOM_ERROR = -100,
+ GST_FLOW_CUSTOM_ERROR_1 = -101,
+ GST_FLOW_CUSTOM_ERROR_2 = -102
+} GstFlowReturn;
+
+const gchar* gst_flow_get_name (GstFlowReturn ret);
+GQuark gst_flow_to_quark (GstFlowReturn ret);
+
+/**
+ * GstPadLinkCheck:
+ * @GST_PAD_LINK_CHECK_NOTHING: Don't check hierarchy or caps compatibility.
+ * @GST_PAD_LINK_CHECK_HIERARCHY: Check the pads have same parents/grandparents.
+ * Could be omitted if it is already known that the two elements that own the
+ * pads are in the same bin.
+ * @GST_PAD_LINK_CHECK_TEMPLATE_CAPS: Check if the pads are compatible by using
+ * their template caps. This is much faster than @GST_PAD_LINK_CHECK_CAPS, but
+ * would be unsafe e.g. if one pad has %GST_CAPS_ANY.
+ * @GST_PAD_LINK_CHECK_CAPS: Check if the pads are compatible by comparing the
+ * caps returned by gst_pad_get_caps().
+ *
+ * The amount of checking to be done when linking pads. @GST_PAD_LINK_CHECK_CAPS
+ * and @GST_PAD_LINK_CHECK_TEMPLATE_CAPS are mutually exclusive. If both are
+ * specified, expensive but safe @GST_PAD_LINK_CHECK_CAPS are performed.
+ *
+ * <warning><para>
+ * Only disable some of the checks if you are 100% certain you know the link
+ * will not fail because of hierarchy/caps compatibility failures. If uncertain,
+ * use the default checks (%GST_PAD_LINK_CHECK_DEFAULT) or the regular methods
+ * for linking the pads.
+ * </para></warning>
+ *
+ * Since: 0.10.30
+ */
+
+typedef enum {
+ GST_PAD_LINK_CHECK_NOTHING = 0,
+ GST_PAD_LINK_CHECK_HIERARCHY = 1 << 0,
+ GST_PAD_LINK_CHECK_TEMPLATE_CAPS = 1 << 1,
+ GST_PAD_LINK_CHECK_CAPS = 1 << 2
+} GstPadLinkCheck;
+
+/**
+ * GST_PAD_LINK_CHECK_DEFAULT:
+ *
+ * The default checks done when linking pads (i.e. the ones used by
+ * gst_pad_link()).
+ *
+ * Since: 0.10.30
+ */
+#define GST_PAD_LINK_CHECK_DEFAULT ((GstPadLinkCheck) (GST_PAD_LINK_CHECK_HIERARCHY | GST_PAD_LINK_CHECK_CAPS))
+
+/**
+ * GstActivateMode:
+ * @GST_ACTIVATE_NONE: Pad will not handle dataflow
+ * @GST_ACTIVATE_PUSH: Pad handles dataflow in downstream push mode
+ * @GST_ACTIVATE_PULL: Pad handles dataflow in upstream pull mode
+ *
+ * The status of a GstPad. After activating a pad, which usually happens when the
+ * parent element goes from READY to PAUSED, the GstActivateMode defines if the
+ * pad operates in push or pull mode.
+ */
+typedef enum {
+ GST_ACTIVATE_NONE,
+ GST_ACTIVATE_PUSH,
+ GST_ACTIVATE_PULL
+} GstActivateMode;
+
+/* pad states */
+/**
+ * GstPadActivateFunction:
+ * @pad: a #GstPad
+ *
+ * This function is called when the pad is activated during the element
+ * READY to PAUSED state change. By default this function will call the
+ * activate function that puts the pad in push mode but elements can
+ * override this function to activate the pad in pull mode if they wish.
+ *
+ * Returns: TRUE if the pad could be activated.
+ */
+typedef gboolean (*GstPadActivateFunction) (GstPad *pad);
+/**
+ * GstPadActivateModeFunction:
+ * @pad: a #GstPad
+ * @active: activate or deactivate the pad.
+ *
+ * The prototype of the push and pull activate functions.
+ *
+ * Returns: TRUE if the pad could be activated or deactivated.
+ */
+typedef gboolean (*GstPadActivateModeFunction) (GstPad *pad, gboolean active);
+
+
+/* data passing */
+/**
+ * GstPadChainFunction:
+ * @pad: the sink #GstPad that performed the chain.
+ * @buffer: the #GstBuffer that is chained, not %NULL.
+ *
+ * A function that will be called on sinkpads when chaining buffers.
+ * The function typically processes the data contained in the buffer and
+ * either consumes the data or passes it on to the internally linked pad(s).
+ *
+ * The implementer of this function receives a refcount to @buffer and should
+ * gst_buffer_unref() when the buffer is no longer needed.
+ *
+ * When a chain function detects an error in the data stream, it must post an
+ * error on the bus and return an appropriate #GstFlowReturn value.
+ *
+ * Returns: #GST_FLOW_OK for success
+ */
+typedef GstFlowReturn (*GstPadChainFunction) (GstPad *pad, GstBuffer *buffer);
+
+/**
+ * GstPadChainListFunction:
+ * @pad: the sink #GstPad that performed the chain.
+ * @list: the #GstBufferList that is chained, not %NULL.
+ *
+ * A function that will be called on sinkpads when chaining buffer lists.
+ * The function typically processes the data contained in the buffer list and
+ * either consumes the data or passes it on to the internally linked pad(s).
+ *
+ * The implementer of this function receives a refcount to @list and
+ * should gst_buffer_list_unref() when the list is no longer needed.
+ *
+ * When a chainlist function detects an error in the data stream, it must
+ * post an error on the bus and return an appropriate #GstFlowReturn value.
+ *
+ * Returns: #GST_FLOW_OK for success
+ */
+typedef GstFlowReturn (*GstPadChainListFunction) (GstPad *pad, GstBufferList *list);
+
+/**
+ * GstPadGetRangeFunction:
+ * @pad: the src #GstPad to perform the getrange on.
+ * @offset: the offset of the range
+ * @length: the length of the range
+ * @buffer: a memory location to hold the result buffer, cannot be NULL.
+ *
+ * This function will be called on source pads when a peer element
+ * request a buffer at the specified @offset and @length. If this function
+ * returns #GST_FLOW_OK, the result buffer will be stored in @buffer. The
+ * contents of @buffer is invalid for any other return value.
+ *
+ * This function is installed on a source pad with
+ * gst_pad_set_getrange_function() and can only be called on source pads after
+ * they are successfully activated with gst_pad_activate_pull().
+ *
+ * @offset and @length are always given in byte units. @offset must normally be a value
+ * between 0 and the length in bytes of the data available on @pad. The
+ * length (duration in bytes) can be retrieved with a #GST_QUERY_DURATION or with a
+ * #GST_QUERY_SEEKING.
+ *
+ * Any @offset larger or equal than the length will make the function return
+ * #GST_FLOW_UNEXPECTED, which corresponds to EOS. In this case @buffer does not
+ * contain a valid buffer.
+ *
+ * The buffer size of @buffer will only be smaller than @length when @offset is
+ * near the end of the stream. In all other cases, the size of @buffer must be
+ * exactly the requested size.
+ *
+ * It is allowed to call this function with a 0 @length and valid @offset, in
+ * which case @buffer will contain a 0-sized buffer and the function returns
+ * #GST_FLOW_OK.
+ *
+ * When this function is called with a -1 @offset, the sequentially next buffer
+ * of length @length in the stream is returned.
+ *
+ * When this function is called with a -1 @length, a buffer with a default
+ * optimal length is returned in @buffer. The length might depend on the value
+ * of @offset.
+ *
+ * Returns: #GST_FLOW_OK for success and a valid buffer in @buffer. Any other
+ * return value leaves @buffer undefined.
+ */
+typedef GstFlowReturn (*GstPadGetRangeFunction) (GstPad *pad, guint64 offset,
+ guint length, GstBuffer **buffer);
+
+/**
+ * GstPadEventFunction:
+ * @pad: the #GstPad to handle the event.
+ * @event: the #GstEvent to handle.
+ *
+ * Function signature to handle an event for the pad.
+ *
+ * Returns: TRUE if the pad could handle the event.
+ */
+typedef gboolean (*GstPadEventFunction) (GstPad *pad, GstEvent *event);
+
+
+/* internal links */
+/**
+ * GstPadIterIntLinkFunction:
+ * @pad: The #GstPad to query.
+ *
+ * The signature of the internal pad link iterator function.
+ *
+ * Returns: a new #GstIterator that will iterate over all pads that are
+ * linked to the given pad on the inside of the parent element.
+ *
+ * the caller must call gst_iterator_free() after usage.
+ *
+ * Since 0.10.21
+ */
+typedef GstIterator* (*GstPadIterIntLinkFunction) (GstPad *pad);
+
+/* generic query function */
+/**
+ * GstPadQueryTypeFunction:
+ * @pad: a #GstPad to query
+ *
+ * The signature of the query types function.
+ *
+ * Returns: a constant array of query types
+ */
+typedef const GstQueryType* (*GstPadQueryTypeFunction) (GstPad *pad);
+
+/**
+ * GstPadQueryFunction:
+ * @pad: the #GstPad to query.
+ * @query: the #GstQuery object to execute
+ *
+ * The signature of the query function.
+ *
+ * Returns: TRUE if the query could be performed.
+ */
+typedef gboolean (*GstPadQueryFunction) (GstPad *pad, GstQuery *query);
+
+
+/* linking */
+/**
+ * GstPadLinkFunction
+ * @pad: the #GstPad that is linked.
+ * @peer: the peer #GstPad of the link
+ *
+ * Function signature to handle a new link on the pad.
+ *
+ * Returns: the result of the link with the specified peer.
+ */
+typedef GstPadLinkReturn (*GstPadLinkFunction) (GstPad *pad, GstPad *peer);
+/**
+ * GstPadUnlinkFunction
+ * @pad: the #GstPad that is linked.
+ *
+ * Function signature to handle a unlinking the pad prom its peer.
+ */
+typedef void (*GstPadUnlinkFunction) (GstPad *pad);
+
+
+/* caps nego */
+/**
+ * GstPadGetCapsFunction:
+ * @pad: the #GstPad to get the capabilities of.
+ * @filter: filter #GstCaps.
+ *
+ * When called on sinkpads @filter contains the caps that
+ * upstream could produce in the order preferred by upstream. When
+ * called on srcpads @filter contains the caps accepted by
+ * downstream in the preffered order. @filter might be %NULL but if
+ * it is not %NULL only a subset of @filter must be returned.
+ *
+ * Returns a copy of the capabilities of the specified pad. By default this
+ * function will return the pad template capabilities, but can optionally
+ * be overridden by elements.
+ *
+ * Returns: a newly allocated copy #GstCaps of the pad.
+ */
+typedef GstCaps* (*GstPadGetCapsFunction) (GstPad *pad, GstCaps *filter);
+
+/**
+ * GstPadAcceptCapsFunction:
+ * @pad: the #GstPad to check
+ * @caps: the #GstCaps to check
+ *
+ * Check if @pad can accept @caps. By default this function will see if @caps
+ * intersect with the result from gst_pad_get_caps() by can be overridden to
+ * perform extra checks.
+ *
+ * Returns: TRUE if the caps can be accepted by the pad.
+ */
+typedef gboolean (*GstPadAcceptCapsFunction) (GstPad *pad, GstCaps *caps);
+/**
+ * GstPadFixateCapsFunction:
+ * @pad: a #GstPad
+ * @caps: the #GstCaps to fixate
+ *
+ * Given possibly unfixed caps @caps, let @pad use its default preferred
+ * format to make a fixed caps. @caps should be writable. By default this
+ * function will pick the first value of any ranges or lists in the caps but
+ * elements can override this function to perform other behaviour.
+ */
+typedef void (*GstPadFixateCapsFunction) (GstPad *pad, GstCaps *caps);
+
+/* misc */
+/**
+ * GstPadForwardFunction:
+ * @pad: the #GstPad that is forwarded.
+ * @user_data: the gpointer to optional user data.
+ *
+ * A forward function is called for all internally linked pads, see
+ * gst_pad_forward().
+ *
+ * Returns: TRUE if the dispatching procedure has to be stopped.
+ */
+typedef gboolean (*GstPadForwardFunction) (GstPad *pad, gpointer user_data);
+
+/**
+ * GstProbeType:
+ * @GST_PROBE_TYPE_INVALID: invalid probe type
+ * @GST_PROBE_TYPE_IDLE: probe idle pads and block
+ * @GST_PROBE_TYPE_BLOCK: probe and block pads
+ * @GST_PROBE_TYPE_BUFFER: probe buffers
+ * @GST_PROBE_TYPE_BUFFER_LIST: probe buffer lists
+ * @GST_PROBE_TYPE_EVENT: probe events
+ * @GST_PROBE_TYPE_PUSH: probe push
+ * @GST_PROBE_TYPE_PULL: probe pull
+ *
+ * The different probing types that can occur. When either one of
+ * @GST_PROBE_TYPE_IDLE or @GST_PROBE_TYPE_BLOCK is used, the probe will be a
+ * blocking probe.
+ */
+typedef enum
+{
+ GST_PROBE_TYPE_INVALID = 0,
+ /* flags to control blocking */
+ GST_PROBE_TYPE_IDLE = (1 << 0),
+ GST_PROBE_TYPE_BLOCK = (1 << 1),
+ /* flags to select datatypes */
+ GST_PROBE_TYPE_BUFFER = (1 << 2),
+ GST_PROBE_TYPE_BUFFER_LIST = (1 << 3),
+ GST_PROBE_TYPE_EVENT = (1 << 4),
+ /* flags to select scheduling mode */
+ GST_PROBE_TYPE_PUSH = (1 << 5),
+ GST_PROBE_TYPE_PULL = (1 << 6),
+} GstProbeType;
+
+#define GST_PROBE_TYPE_BLOCKING (GST_PROBE_TYPE_IDLE | GST_PROBE_TYPE_BLOCK)
+#define GST_PROBE_TYPE_DATA (GST_PROBE_TYPE_BUFFER | GST_PROBE_TYPE_EVENT | \
+ GST_PROBE_TYPE_BUFFER_LIST)
+#define GST_PROBE_TYPE_SCHEDULING (GST_PROBE_TYPE_PUSH | GST_PROBE_TYPE_PULL)
+
+/**
+ * GstProbeReturn:
+ * @GST_PROBE_OK: normal probe return value
+ * @GST_PROBE_DROP: drop data in data probes
+ * @GST_PROBE_REMOVE: remove probe
+ * @GST_PROBE_PASS: pass the data item in the block probe and block on
+ * the next item
+ *
+ * Different return values for the #GstPadProbeCallback.
+ */
+typedef enum
+{
+ GST_PROBE_DROP,
+ GST_PROBE_OK,
+ GST_PROBE_REMOVE,
+ GST_PROBE_PASS,
+} GstProbeReturn;
+
+/**
+ * GstPadProbeCallback
+ * @pad: the #GstPad that is blocked
+ * @type: the current probe type
+ * @type_data: type specific data
+ * @user_data: the gpointer to optional user data.
+ *
+ * Callback used by gst_pad_add_probe(). Gets called to notify about the current
+ * blocking type.
+ */
+typedef GstProbeReturn (*GstPadProbeCallback) (GstPad *pad, GstProbeType type,
+ gpointer type_data, gpointer user_data);
+
+/**
+ * GstPadStickyEventsForeachFunction:
+ * @pad: the #GstPad.
+ * @event: the sticky #GstEvent.
+ * @user_data: the #gpointer to optional user data.
+ *
+ * Callback used by gst_pad_sticky_events_foreach().
+ *
+ * Returns: GST_FLOW_OK if the iteration should continue
+ */
+typedef GstFlowReturn (*GstPadStickyEventsForeachFunction) (GstPad *pad, GstEvent *event, gpointer user_data);
+
+/**
+ * GstPadFlags:
+ * @GST_PAD_BLOCKED: is dataflow on a pad blocked
+ * @GST_PAD_FLUSHING: is pad refusing buffers
+ * @GST_PAD_IN_GETCAPS: GstPadGetCapsFunction() is running now
+ * @GST_PAD_BLOCKING: is pad currently blocking on a buffer or event
+ * @GST_PAD_NEED_RECONFIGURE: the pad should be reconfigured/renegotiated.
+ * The flag has to be unset manually after
+ * reconfiguration happened.
+ * Since: 0.10.34.
+ * @GST_PAD_NEED_EVENTS: the pad has pending events
+ * @GST_PAD_FIXED_CAPS: the pad is using fixed caps this means that once the
+ * caps are set on the pad, the getcaps function only
+ * returns those caps.
+ * @GST_PAD_FLAG_LAST: offset to define more flags
+ *
+ * Pad state flags
+ */
+typedef enum {
+ GST_PAD_BLOCKED = (GST_OBJECT_FLAG_LAST << 0),
+ GST_PAD_FLUSHING = (GST_OBJECT_FLAG_LAST << 1),
+ GST_PAD_IN_GETCAPS = (GST_OBJECT_FLAG_LAST << 2),
+ GST_PAD_BLOCKING = (GST_OBJECT_FLAG_LAST << 4),
+ GST_PAD_NEED_RECONFIGURE = (GST_OBJECT_FLAG_LAST << 5),
+ GST_PAD_NEED_EVENTS = (GST_OBJECT_FLAG_LAST << 6),
+ GST_PAD_FIXED_CAPS = (GST_OBJECT_FLAG_LAST << 7),
+ /* padding */
+ GST_PAD_FLAG_LAST = (GST_OBJECT_FLAG_LAST << 16)
+} GstPadFlags;
+
+/**
+ * GstPad:
+ * @element_private: private data owned by the parent element
+ * @padtemplate: padtemplate for this pad
+ * @direction: the direction of the pad, cannot change after creating
+ * the pad.
+ * @stream_rec_lock: recursive stream lock of the pad, used to protect
+ * the data used in streaming.
+ * @task: task for this pad if the pad is actively driving dataflow.
+ * @block_cond: conditional to signal pad block
+ * @probes: installed probes
+ * @getcapsfunc: function to get caps of the pad
+ * @acceptcapsfunc: function to check if pad can accept caps
+ * @fixatecapsfunc: function to fixate caps
+ * @mode: current activation mode of the pad
+ * @activatefunc: pad activation function
+ * @activatepushfunc: function to activate/deactivate pad in push mode
+ * @activatepullfunc: function to activate/deactivate pad in pull mode
+ * @peer: the pad this pad is linked to
+ * @linkfunc: function called when pad is linked
+ * @unlinkfunc: function called when pad is unlinked
+ * @chainfunc: function to chain buffer to pad
+ * @chainlistfunc: function to chain a list of buffers to pad
+ * @getrangefunc: function to get a range of data from a pad
+ * @eventfunc: function to send an event to a pad
+ * @offset: the pad offset
+ * @querytypefunc: get list of supported queries
+ * @queryfunc: perform a query on the pad
+ * @iterintlinkfunc: get the internal links iterator of this pad
+ *
+ * The #GstPad structure. Use the functions to update the variables.
+ */
+struct _GstPad {
+ GstObject object;
+
+ /*< public >*/
+ gpointer element_private;
+
+ GstPadTemplate *padtemplate;
+
+ GstPadDirection direction;
+
+ /*< public >*/ /* with STREAM_LOCK */
+ /* streaming rec_lock */
+ GStaticRecMutex stream_rec_lock;
+ GstTask *task;
+
+ /*< public >*/ /* with LOCK */
+ /* block cond, mutex is from the object */
+ GCond *block_cond;
+ GHookList probes;
+
+ /* the pad capabilities */
+ GstPadGetCapsFunction getcapsfunc;
+ GstPadAcceptCapsFunction acceptcapsfunc;
+ GstPadFixateCapsFunction fixatecapsfunc;
+
+ GstActivateMode mode;
+ GstPadActivateFunction activatefunc;
+ GstPadActivateModeFunction activatepushfunc;
+ GstPadActivateModeFunction activatepullfunc;
+
+ /* pad link */
+ GstPad *peer;
+ GstPadLinkFunction linkfunc;
+ GstPadUnlinkFunction unlinkfunc;
+
+ /* data transport functions */
+ GstPadChainFunction chainfunc;
+ GstPadChainListFunction chainlistfunc;
+ GstPadGetRangeFunction getrangefunc;
+ GstPadEventFunction eventfunc;
+
+ /* pad offset */
+ gint64 offset;
+
+ /* generic query method */
+ GstPadQueryTypeFunction querytypefunc;
+ GstPadQueryFunction queryfunc;
+
+ /* internal links */
+ GstPadIterIntLinkFunction iterintlinkfunc;
+
+ /*< private >*/
+ /* counts number of probes attached. */
+ gint num_probes;
+ gint num_blocked;
+
+ GstPadPrivate *priv;
+
+ gpointer _gst_reserved[GST_PADDING];
+};
+
+struct _GstPadClass {
+ GstObjectClass parent_class;
+
+ /* signal callbacks */
+ void (*linked) (GstPad *pad, GstPad *peer);
+ void (*unlinked) (GstPad *pad, GstPad *peer);
+
+ /*< private >*/
+ gpointer _gst_reserved[GST_PADDING];
+};
+
+
+/***** helper macros *****/
+/* GstPad */
+#define GST_PAD_NAME(pad) (GST_OBJECT_NAME(pad))
+#define GST_PAD_PARENT(pad) (GST_ELEMENT_CAST(GST_OBJECT_PARENT(pad)))
+#define GST_PAD_ELEMENT_PRIVATE(pad) (GST_PAD_CAST(pad)->element_private)
+#define GST_PAD_PAD_TEMPLATE(pad) (GST_PAD_CAST(pad)->padtemplate)
+#define GST_PAD_DIRECTION(pad) (GST_PAD_CAST(pad)->direction)
+#define GST_PAD_TASK(pad) (GST_PAD_CAST(pad)->task)
+#define GST_PAD_ACTIVATE_MODE(pad) (GST_PAD_CAST(pad)->mode)
+
+#define GST_PAD_ACTIVATEFUNC(pad) (GST_PAD_CAST(pad)->activatefunc)
+#define GST_PAD_ACTIVATEPUSHFUNC(pad) (GST_PAD_CAST(pad)->activatepushfunc)
+#define GST_PAD_ACTIVATEPULLFUNC(pad) (GST_PAD_CAST(pad)->activatepullfunc)
+#define GST_PAD_CHAINFUNC(pad) (GST_PAD_CAST(pad)->chainfunc)
+#define GST_PAD_CHAINLISTFUNC(pad) (GST_PAD_CAST(pad)->chainlistfunc)
+#define GST_PAD_GETRANGEFUNC(pad) (GST_PAD_CAST(pad)->getrangefunc)
+#define GST_PAD_EVENTFUNC(pad) (GST_PAD_CAST(pad)->eventfunc)
+#define GST_PAD_QUERYTYPEFUNC(pad) (GST_PAD_CAST(pad)->querytypefunc)
+#define GST_PAD_QUERYFUNC(pad) (GST_PAD_CAST(pad)->queryfunc)
+#define GST_PAD_ITERINTLINKFUNC(pad) (GST_PAD_CAST(pad)->iterintlinkfunc)
+
+#define GST_PAD_PEER(pad) (GST_PAD_CAST(pad)->peer)
+#define GST_PAD_LINKFUNC(pad) (GST_PAD_CAST(pad)->linkfunc)
+#define GST_PAD_UNLINKFUNC(pad) (GST_PAD_CAST(pad)->unlinkfunc)
+
+#define GST_PAD_GETCAPSFUNC(pad) (GST_PAD_CAST(pad)->getcapsfunc)
+#define GST_PAD_ACCEPTCAPSFUNC(pad) (GST_PAD_CAST(pad)->acceptcapsfunc)
+#define GST_PAD_FIXATECAPSFUNC(pad) (GST_PAD_CAST(pad)->fixatecapsfunc)
+
+#define GST_PAD_IS_SRC(pad) (GST_PAD_DIRECTION(pad) == GST_PAD_SRC)
+#define GST_PAD_IS_SINK(pad) (GST_PAD_DIRECTION(pad) == GST_PAD_SINK)
+
+#define GST_PAD_IS_LINKED(pad) (GST_PAD_PEER(pad) != NULL)
+
+#define GST_PAD_IS_ACTIVE(pad) (GST_PAD_ACTIVATE_MODE(pad) != GST_ACTIVATE_NONE)
+
+#define GST_PAD_IS_BLOCKED(pad) (GST_OBJECT_FLAG_IS_SET (pad, GST_PAD_BLOCKED))
+#define GST_PAD_IS_BLOCKING(pad) (GST_OBJECT_FLAG_IS_SET (pad, GST_PAD_BLOCKING))
+#define GST_PAD_IS_FLUSHING(pad) (GST_OBJECT_FLAG_IS_SET (pad, GST_PAD_FLUSHING))
+#define GST_PAD_IS_IN_GETCAPS(pad) (GST_OBJECT_FLAG_IS_SET (pad, GST_PAD_IN_GETCAPS))
+#define GST_PAD_NEEDS_RECONFIGURE(pad) (GST_OBJECT_FLAG_IS_SET (pad, GST_PAD_NEED_RECONFIGURE))
+#define GST_PAD_NEEDS_EVENTS(pad) (GST_OBJECT_FLAG_IS_SET (pad, GST_PAD_NEED_EVENTS))
+#define GST_PAD_IS_FIXED_CAPS(pad) (GST_OBJECT_FLAG_IS_SET (pad, GST_PAD_FIXED_CAPS))
+
+#define GST_PAD_SET_FLUSHING(pad) (GST_OBJECT_FLAG_SET (pad, GST_PAD_FLUSHING))
+#define GST_PAD_UNSET_FLUSHING(pad) (GST_OBJECT_FLAG_UNSET (pad, GST_PAD_FLUSHING))
+
+/**
+ * GST_PAD_GET_STREAM_LOCK:
+ * @pad: a #GstPad
+ *
+ * Get the stream lock of @pad. The stream lock is protecting the
+ * resources used in the data processing functions of @pad.
+ */
+#define GST_PAD_GET_STREAM_LOCK(pad) (&(GST_PAD_CAST(pad)->stream_rec_lock))
+/**
+ * GST_PAD_STREAM_LOCK:
+ * @pad: a #GstPad
+ *
+ * Lock the stream lock of @pad.
+ */
+#define GST_PAD_STREAM_LOCK(pad) (g_static_rec_mutex_lock(GST_PAD_GET_STREAM_LOCK(pad)))
+/**
+ * GST_PAD_STREAM_LOCK_FULL:
+ * @pad: a #GstPad
+ * @t: the number of times to recursively lock
+ *
+ * Lock the stream lock of @pad @t times.
+ */
+#define GST_PAD_STREAM_LOCK_FULL(pad,t) (g_static_rec_mutex_lock_full(GST_PAD_GET_STREAM_LOCK(pad), t))
+/**
+ * GST_PAD_STREAM_TRYLOCK:
+ * @pad: a #GstPad
+ *
+ * Try to Lock the stream lock of the pad, return TRUE if the lock could be
+ * taken.
+ */
+#define GST_PAD_STREAM_TRYLOCK(pad) (g_static_rec_mutex_trylock(GST_PAD_GET_STREAM_LOCK(pad)))
+/**
+ * GST_PAD_STREAM_UNLOCK:
+ * @pad: a #GstPad
+ *
+ * Unlock the stream lock of @pad.
+ */
+#define GST_PAD_STREAM_UNLOCK(pad) (g_static_rec_mutex_unlock(GST_PAD_GET_STREAM_LOCK(pad)))
+/**
+ * GST_PAD_STREAM_UNLOCK_FULL:
+ * @pad: a #GstPad
+ *
+ * Fully unlock the recursive stream lock of @pad, return the number of times
+ * @pad was locked.
+ */
+#define GST_PAD_STREAM_UNLOCK_FULL(pad) (g_static_rec_mutex_unlock_full(GST_PAD_GET_STREAM_LOCK(pad)))
+
+#define GST_PAD_BLOCK_GET_COND(pad) (GST_PAD_CAST(pad)->block_cond)
+#define GST_PAD_BLOCK_WAIT(pad) (g_cond_wait(GST_PAD_BLOCK_GET_COND (pad), GST_OBJECT_GET_LOCK (pad)))
+#define GST_PAD_BLOCK_SIGNAL(pad) (g_cond_signal(GST_PAD_BLOCK_GET_COND (pad)))
+#define GST_PAD_BLOCK_BROADCAST(pad) (g_cond_broadcast(GST_PAD_BLOCK_GET_COND (pad)))
+
+GType gst_pad_get_type (void);
+
+/* creating pads */
+GstPad* gst_pad_new (const gchar *name, GstPadDirection direction);
+GstPad* gst_pad_new_from_template (GstPadTemplate *templ, const gchar *name);
+GstPad* gst_pad_new_from_static_template (GstStaticPadTemplate *templ, const gchar *name);
+
+
+/**
+ * gst_pad_get_name:
+ * @pad: the pad to get the name from
+ *
+ * Get a copy of the name of the pad. g_free() after usage.
+ *
+ * MT safe.
+ */
+#define gst_pad_get_name(pad) gst_object_get_name (GST_OBJECT_CAST (pad))
+/**
+ * gst_pad_get_parent:
+ * @pad: the pad to get the parent of
+ *
+ * Get the parent of @pad. This function increases the refcount
+ * of the parent object so you should gst_object_unref() it after usage.
+ * Can return NULL if the pad did not have a parent.
+ *
+ * MT safe.
+ */
+#define gst_pad_get_parent(pad) gst_object_get_parent (GST_OBJECT_CAST (pad))
+
+GstPadDirection gst_pad_get_direction (GstPad *pad);
+
+gboolean gst_pad_set_active (GstPad *pad, gboolean active);
+gboolean gst_pad_is_active (GstPad *pad);
+gboolean gst_pad_activate_pull (GstPad *pad, gboolean active);
+gboolean gst_pad_activate_push (GstPad *pad, gboolean active);
+
+gulong gst_pad_add_probe (GstPad *pad,
+ GstProbeType mask,
+ GstPadProbeCallback callback,
+ gpointer user_data,
+ GDestroyNotify destroy_data);
+void gst_pad_remove_probe (GstPad *pad, gulong id);
+
+gboolean gst_pad_is_blocked (GstPad *pad);
+gboolean gst_pad_is_blocking (GstPad *pad);
+
+void gst_pad_mark_reconfigure (GstPad *pad);
+gboolean gst_pad_check_reconfigure (GstPad *pad);
+
+void gst_pad_set_element_private (GstPad *pad, gpointer priv);
+gpointer gst_pad_get_element_private (GstPad *pad);
+
+GstPadTemplate* gst_pad_get_pad_template (GstPad *pad);
+
+GstEvent* gst_pad_get_sticky_event (GstPad *pad, GstEventType event_type);
+GstFlowReturn gst_pad_sticky_events_foreach (GstPad *pad, GstPadStickyEventsForeachFunction foreach_func, gpointer user_data);
+
+/* data passing setup functions */
+void gst_pad_set_activate_function (GstPad *pad, GstPadActivateFunction activate);
+void gst_pad_set_activatepull_function (GstPad *pad, GstPadActivateModeFunction activatepull);
+void gst_pad_set_activatepush_function (GstPad *pad, GstPadActivateModeFunction activatepush);
+void gst_pad_set_chain_function (GstPad *pad, GstPadChainFunction chain);
+void gst_pad_set_chain_list_function (GstPad *pad, GstPadChainListFunction chainlist);
+void gst_pad_set_getrange_function (GstPad *pad, GstPadGetRangeFunction get);
+void gst_pad_set_event_function (GstPad *pad, GstPadEventFunction event);
+
+/* pad links */
+void gst_pad_set_link_function (GstPad *pad, GstPadLinkFunction link);
+void gst_pad_set_unlink_function (GstPad *pad, GstPadUnlinkFunction unlink);
+
+gboolean gst_pad_can_link (GstPad *srcpad, GstPad *sinkpad);
+GstPadLinkReturn gst_pad_link (GstPad *srcpad, GstPad *sinkpad);
+GstPadLinkReturn gst_pad_link_full (GstPad *srcpad, GstPad *sinkpad, GstPadLinkCheck flags);
+gboolean gst_pad_unlink (GstPad *srcpad, GstPad *sinkpad);
+gboolean gst_pad_is_linked (GstPad *pad);
+
+GstPad* gst_pad_get_peer (GstPad *pad);
+
+/* capsnego functions */
+void gst_pad_set_getcaps_function (GstPad *pad, GstPadGetCapsFunction getcaps);
+void gst_pad_set_acceptcaps_function (GstPad *pad, GstPadAcceptCapsFunction acceptcaps);
+void gst_pad_set_fixatecaps_function (GstPad *pad, GstPadFixateCapsFunction fixatecaps);
+
+GstCaps* gst_pad_get_pad_template_caps (GstPad *pad);
+
+/* capsnego function for linked/unlinked pads */
+GstCaps * gst_pad_get_current_caps (GstPad * pad);
+gboolean gst_pad_has_current_caps (GstPad * pad);
+GstCaps * gst_pad_get_caps (GstPad * pad, GstCaps *filter);
+void gst_pad_fixate_caps (GstPad * pad, GstCaps *caps);
+gboolean gst_pad_accept_caps (GstPad * pad, GstCaps *caps);
+gboolean gst_pad_set_caps (GstPad * pad, GstCaps *caps);
+
+GstCaps * gst_pad_peer_get_caps (GstPad * pad, GstCaps *filter);
+gboolean gst_pad_peer_accept_caps (GstPad * pad, GstCaps *caps);
+
+/* capsnego for linked pads */
+GstCaps * gst_pad_get_allowed_caps (GstPad * pad);
+
+/* pad offsets */
+gint64 gst_pad_get_offset (GstPad *pad);
+void gst_pad_set_offset (GstPad *pad, gint64 offset);
+
+/* data passing functions to peer */
+GstFlowReturn gst_pad_push (GstPad *pad, GstBuffer *buffer);
+GstFlowReturn gst_pad_push_list (GstPad *pad, GstBufferList *list);
+GstFlowReturn gst_pad_pull_range (GstPad *pad, guint64 offset, guint size,
+ GstBuffer **buffer);
+gboolean gst_pad_push_event (GstPad *pad, GstEvent *event);
+gboolean gst_pad_event_default (GstPad *pad, GstEvent *event);
+
+/* data passing functions on pad */
+GstFlowReturn gst_pad_chain (GstPad *pad, GstBuffer *buffer);
+GstFlowReturn gst_pad_chain_list (GstPad *pad, GstBufferList *list);
+GstFlowReturn gst_pad_get_range (GstPad *pad, guint64 offset, guint size,
+ GstBuffer **buffer);
+gboolean gst_pad_send_event (GstPad *pad, GstEvent *event);
+
+/* pad tasks */
+gboolean gst_pad_start_task (GstPad *pad, GstTaskFunction func,
+ gpointer data);
+gboolean gst_pad_pause_task (GstPad *pad);
+gboolean gst_pad_stop_task (GstPad *pad);
+
+/* internal links */
+void gst_pad_set_iterate_internal_links_function (GstPad * pad,
+ GstPadIterIntLinkFunction iterintlink);
+GstIterator * gst_pad_iterate_internal_links (GstPad * pad);
+GstIterator * gst_pad_iterate_internal_links_default (GstPad * pad);
+
+
+/* generic query function */
+void gst_pad_set_query_type_function (GstPad *pad, GstPadQueryTypeFunction type_func);
+const GstQueryType* gst_pad_get_query_types (GstPad *pad);
+const GstQueryType* gst_pad_get_query_types_default (GstPad *pad);
+
+gboolean gst_pad_query (GstPad *pad, GstQuery *query);
+gboolean gst_pad_peer_query (GstPad *pad, GstQuery *query);
+void gst_pad_set_query_function (GstPad *pad, GstPadQueryFunction query);
+gboolean gst_pad_query_default (GstPad *pad, GstQuery *query);
+
+/* misc helper functions */
+gboolean gst_pad_forward (GstPad *pad, GstPadForwardFunction forward,
+ gpointer user_data);
+
+G_END_DECLS
+
+#endif /* __GST_PAD_H__ */
diff --git a/gst/gstpadtemplate.c b/gst/gstpadtemplate.c
new file mode 100644
index 0000000..bf8d576
--- /dev/null
+++ b/gst/gstpadtemplate.c
@@ -0,0 +1,456 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2000 Wim Taymans <wtay@chello.be>
+ *
+ * gstpadtemplate.c: Templates for pad creation
+ *
+ * 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:gstpadtemplate
+ * @short_description: Describe the media type of a pad.
+ * @see_also: #GstPad, #GstElementFactory
+ *
+ * Padtemplates describe the possible media types a pad or an elementfactory can
+ * handle. This allows for both inspection of handled types before loading the
+ * element plugin as well as identifying pads on elements that are not yet
+ * created (request or sometimes pads).
+ *
+ * Pad and PadTemplates have #GstCaps attached to it to describe the media type
+ * they are capable of dealing with. gst_pad_template_get_caps() or
+ * GST_PAD_TEMPLATE_CAPS() are used to get the caps of a padtemplate. It's not
+ * possible to modify the caps of a padtemplate after creation.
+ *
+ * PadTemplates have a #GstPadPresence property which identifies the lifetime
+ * of the pad and that can be retrieved with GST_PAD_TEMPLATE_PRESENCE(). Also
+ * the direction of the pad can be retrieved from the #GstPadTemplate with
+ * GST_PAD_TEMPLATE_DIRECTION().
+ *
+ * The GST_PAD_TEMPLATE_NAME_TEMPLATE () is important for GST_PAD_REQUEST pads
+ * because it has to be used as the name in the gst_element_get_request_pad()
+ * call to instantiate a pad from this template.
+ *
+ * Padtemplates can be created with gst_pad_template_new() or with
+ * gst_static_pad_template_get (), which creates a #GstPadTemplate from a
+ * #GstStaticPadTemplate that can be filled with the
+ * convenient GST_STATIC_PAD_TEMPLATE() macro.
+ *
+ * A padtemplate can be used to create a pad (see gst_pad_new_from_template()
+ * or gst_pad_new_from_static_template ()) or to add to an element class
+ * (see gst_element_class_add_pad_template ()).
+ *
+ * The following code example shows the code to create a pad from a padtemplate.
+ * <example>
+ * <title>Create a pad from a padtemplate</title>
+ * <programlisting>
+ * GstStaticPadTemplate my_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
+ * "audio/x-raw-int, "
+ * "channels = (int) [ 1, 6 ]"
+ * )
+ * );
+ * void
+ * my_method (void)
+ * {
+ * GstPad *pad;
+ * pad = gst_pad_new_from_static_template (&amp;my_template, "sink");
+ * ...
+ * }
+ * </programlisting>
+ * </example>
+ *
+ * The following example shows you how to add the padtemplate to an
+ * element class, this is usually done in the class_init of the class:
+ * <informalexample>
+ * <programlisting>
+ * static void
+ * my_element_class_init (GstMyElementClass *klass)
+ * {
+ * GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
+ *
+ * gst_element_class_add_pad_template (gstelement_class,
+ * gst_static_pad_template_get (&amp;my_template));
+ * }
+ * </programlisting>
+ * </informalexample>
+ *
+ * Last reviewed on 2006-02-14 (0.10.3)
+ */
+
+#include "gst_private.h"
+
+#include "gstpad.h"
+#include "gstpadtemplate.h"
+#include "gstenumtypes.h"
+#include "gstmarshal.h"
+#include "gstutils.h"
+#include "gstinfo.h"
+#include "gsterror.h"
+#include "gstvalue.h"
+
+#define GST_CAT_DEFAULT GST_CAT_PADS
+
+enum
+{
+ PROP_NAME_TEMPLATE = 1,
+ PROP_DIRECTION,
+ PROP_PRESENCE,
+ PROP_CAPS
+};
+
+enum
+{
+ TEMPL_PAD_CREATED,
+ /* FILL ME */
+ LAST_SIGNAL
+};
+
+static GstObject *parent_class = NULL;
+static guint gst_pad_template_signals[LAST_SIGNAL] = { 0 };
+
+static void gst_pad_template_dispose (GObject * object);
+static void gst_pad_template_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+static void gst_pad_template_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
+
+G_DEFINE_TYPE (GstPadTemplate, gst_pad_template, GST_TYPE_OBJECT);
+
+static void
+gst_pad_template_class_init (GstPadTemplateClass * klass)
+{
+ GObjectClass *gobject_class;
+ GstObjectClass *gstobject_class;
+
+ gobject_class = (GObjectClass *) klass;
+ gstobject_class = (GstObjectClass *) klass;
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ /**
+ * GstPadTemplate::pad-created:
+ * @pad_template: the object which received the signal.
+ * @pad: the pad that was created.
+ *
+ * This signal is fired when an element creates a pad from this template.
+ */
+ gst_pad_template_signals[TEMPL_PAD_CREATED] =
+ g_signal_new ("pad-created", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GstPadTemplateClass, pad_created),
+ NULL, NULL, gst_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GST_TYPE_PAD);
+
+ gobject_class->dispose = gst_pad_template_dispose;
+
+ gobject_class->get_property = gst_pad_template_get_property;
+ gobject_class->set_property = gst_pad_template_set_property;
+
+ /**
+ * GstPadTemplate:name-template
+ *
+ * The name template of the pad template.
+ *
+ * Since: 0.10.21
+ */
+ g_object_class_install_property (gobject_class, PROP_NAME_TEMPLATE,
+ g_param_spec_string ("name-template", "Name template",
+ "The name template of the pad template", NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GstPadTemplate:direction
+ *
+ * The direction of the pad described by the pad template.
+ *
+ * Since: 0.10.21
+ */
+ g_object_class_install_property (gobject_class, PROP_DIRECTION,
+ g_param_spec_enum ("direction", "Direction",
+ "The direction of the pad described by the pad template",
+ GST_TYPE_PAD_DIRECTION, GST_PAD_UNKNOWN,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GstPadTemplate:presence
+ *
+ * When the pad described by the pad template will become available.
+ *
+ * Since: 0.10.21
+ */
+ g_object_class_install_property (gobject_class, PROP_PRESENCE,
+ g_param_spec_enum ("presence", "Presence",
+ "When the pad described by the pad template will become available",
+ GST_TYPE_PAD_PRESENCE, GST_PAD_ALWAYS,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GstPadTemplate:caps
+ *
+ * The capabilities of the pad described by the pad template.
+ *
+ * Since: 0.10.21
+ */
+ g_object_class_install_property (gobject_class, PROP_CAPS,
+ g_param_spec_boxed ("caps", "Caps",
+ "The capabilities of the pad described by the pad template",
+ GST_TYPE_CAPS,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+ gstobject_class->path_string_separator = "*";
+}
+
+static void
+gst_pad_template_init (GstPadTemplate * templ)
+{
+}
+
+static void
+gst_pad_template_dispose (GObject * object)
+{
+ GstPadTemplate *templ = GST_PAD_TEMPLATE (object);
+
+ g_free (GST_PAD_TEMPLATE_NAME_TEMPLATE (templ));
+ if (GST_PAD_TEMPLATE_CAPS (templ)) {
+ gst_caps_unref (GST_PAD_TEMPLATE_CAPS (templ));
+ }
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+/* ALWAYS padtemplates cannot have conversion specifications (like src_%d),
+ * since it doesn't make sense.
+ * SOMETIMES padtemplates can do whatever they want, they are provided by the
+ * element.
+ * REQUEST padtemplates can be reverse-parsed (the user asks for 'sink1', the
+ * 'sink%d' template is automatically selected), so we need to restrict their
+ * naming.
+ */
+static gboolean
+name_is_valid (const gchar * name, GstPadPresence presence)
+{
+ const gchar *str;
+
+ if (presence == GST_PAD_ALWAYS) {
+ if (strchr (name, '%')) {
+ g_warning ("invalid name template %s: conversion specifications are not"
+ " allowed for GST_PAD_ALWAYS padtemplates", name);
+ return FALSE;
+ }
+ } else if (presence == GST_PAD_REQUEST) {
+ if ((str = strchr (name, '%')) && strchr (str + 1, '%')) {
+ g_warning ("invalid name template %s: only one conversion specification"
+ " allowed in GST_PAD_REQUEST padtemplate", name);
+ return FALSE;
+ }
+ if (str && (*(str + 1) != 's' && *(str + 1) != 'd' && *(str + 1) != 'u')) {
+ g_warning ("invalid name template %s: conversion specification must be of"
+ " type '%%d', '%%u' or '%%s' for GST_PAD_REQUEST padtemplate", name);
+ return FALSE;
+ }
+ if (str && (*(str + 2) != '\0')) {
+ g_warning ("invalid name template %s: conversion specification must"
+ " appear at the end of the GST_PAD_REQUEST padtemplate name", name);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+GType
+gst_static_pad_template_get_type (void)
+{
+ static GType staticpadtemplate_type = 0;
+
+ if (G_UNLIKELY (staticpadtemplate_type == 0)) {
+ staticpadtemplate_type =
+ g_pointer_type_register_static ("GstStaticPadTemplate");
+ }
+ return staticpadtemplate_type;
+}
+
+/**
+ * gst_static_pad_template_get:
+ * @pad_template: the static pad template
+ *
+ * Converts a #GstStaticPadTemplate into a #GstPadTemplate.
+ *
+ * Returns: (transfer full): a new #GstPadTemplate.
+ */
+/* FIXME0.11: rename to gst_pad_template_new_from_static_pad_template() */
+GstPadTemplate *
+gst_static_pad_template_get (GstStaticPadTemplate * pad_template)
+{
+ GstPadTemplate *new;
+ GstCaps *caps;
+
+ if (!name_is_valid (pad_template->name_template, pad_template->presence))
+ return NULL;
+
+ caps = gst_static_caps_get (&pad_template->static_caps);
+
+ new = g_object_new (gst_pad_template_get_type (),
+ "name", pad_template->name_template,
+ "name-template", pad_template->name_template,
+ "direction", pad_template->direction,
+ "presence", pad_template->presence, "caps", caps, NULL);
+
+ gst_caps_unref (caps);
+
+ return new;
+}
+
+/**
+ * gst_pad_template_new:
+ * @name_template: the name template.
+ * @direction: the #GstPadDirection of the template.
+ * @presence: the #GstPadPresence of the pad.
+ * @caps: a #GstCaps set for the template.
+ *
+ * Creates a new pad template with a name according to the given template
+ * and with the given arguments.
+ *
+ * Returns: (transfer full): a new #GstPadTemplate.
+ */
+GstPadTemplate *
+gst_pad_template_new (const gchar * name_template,
+ GstPadDirection direction, GstPadPresence presence, GstCaps * caps)
+{
+ GstPadTemplate *new;
+
+ g_return_val_if_fail (name_template != NULL, NULL);
+ g_return_val_if_fail (caps != NULL, NULL);
+ g_return_val_if_fail (direction == GST_PAD_SRC
+ || direction == GST_PAD_SINK, NULL);
+ g_return_val_if_fail (presence == GST_PAD_ALWAYS
+ || presence == GST_PAD_SOMETIMES || presence == GST_PAD_REQUEST, NULL);
+
+ if (!name_is_valid (name_template, presence)) {
+ return NULL;
+ }
+
+ new = g_object_new (gst_pad_template_get_type (),
+ "name", name_template, "name-template", name_template,
+ "direction", direction, "presence", presence, "caps", caps, NULL);
+
+ return new;
+}
+
+/**
+ * gst_static_pad_template_get_caps:
+ * @templ: a #GstStaticPadTemplate to get capabilities of.
+ *
+ * Gets the capabilities of the static pad template.
+ *
+ * Returns: (transfer full): the #GstCaps of the static pad template.
+ * Unref after usage. Since the core holds an additional
+ * ref to the returned caps, use gst_caps_make_writable()
+ * on the returned caps to modify it.
+ */
+GstCaps *
+gst_static_pad_template_get_caps (GstStaticPadTemplate * templ)
+{
+ g_return_val_if_fail (templ, NULL);
+
+ return (GstCaps *) gst_static_caps_get (&templ->static_caps);
+}
+
+/**
+ * gst_pad_template_get_caps:
+ * @templ: a #GstPadTemplate to get capabilities of.
+ *
+ * Gets the capabilities of the pad template.
+ *
+ * Returns: (transfer full): the #GstCaps of the pad template.
+ * Unref after usage.
+ */
+GstCaps *
+gst_pad_template_get_caps (GstPadTemplate * templ)
+{
+ GstCaps *caps;
+ g_return_val_if_fail (GST_IS_PAD_TEMPLATE (templ), NULL);
+
+ caps = GST_PAD_TEMPLATE_CAPS (templ);
+
+ return (caps ? gst_caps_ref (caps) : NULL);
+}
+
+/**
+ * gst_pad_template_pad_created:
+ * @templ: a #GstPadTemplate that has been created
+ * @pad: the #GstPad that created it
+ *
+ * Emit the pad-created signal for this template when created by this pad.
+ */
+void
+gst_pad_template_pad_created (GstPadTemplate * templ, GstPad * pad)
+{
+ g_signal_emit (templ, gst_pad_template_signals[TEMPL_PAD_CREATED], 0, pad);
+}
+
+static void
+gst_pad_template_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ /* these properties are all construct-only */
+ switch (prop_id) {
+ case PROP_NAME_TEMPLATE:
+ GST_PAD_TEMPLATE_NAME_TEMPLATE (object) = g_value_dup_string (value);
+ break;
+ case PROP_DIRECTION:
+ GST_PAD_TEMPLATE_DIRECTION (object) =
+ (GstPadDirection) g_value_get_enum (value);
+ break;
+ case PROP_PRESENCE:
+ GST_PAD_TEMPLATE_PRESENCE (object) =
+ (GstPadPresence) g_value_get_enum (value);
+ break;
+ case PROP_CAPS:
+ GST_PAD_TEMPLATE_CAPS (object) = g_value_dup_boxed (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_pad_template_get_property (GObject * object, guint prop_id, GValue * value,
+ GParamSpec * pspec)
+{
+ /* these properties are all construct-only */
+ switch (prop_id) {
+ case PROP_NAME_TEMPLATE:
+ g_value_set_string (value, GST_PAD_TEMPLATE_NAME_TEMPLATE (object));
+ break;
+ case PROP_DIRECTION:
+ g_value_set_enum (value, GST_PAD_TEMPLATE_DIRECTION (object));
+ break;
+ case PROP_PRESENCE:
+ g_value_set_enum (value, GST_PAD_TEMPLATE_PRESENCE (object));
+ break;
+ case PROP_CAPS:
+ g_value_set_boxed (value, GST_PAD_TEMPLATE_CAPS (object));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
diff --git a/gst/gstpadtemplate.h b/gst/gstpadtemplate.h
new file mode 100644
index 0000000..bc2bcee
--- /dev/null
+++ b/gst/gstpadtemplate.h
@@ -0,0 +1,192 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2000 Wim Taymans <wim.taymans@chello.be>
+ *
+ * gstpadtemplate.h: Header for GstPadTemplate object
+ *
+ * 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_PAD_TEMPLATE_H__
+#define __GST_PAD_TEMPLATE_H__
+
+#include <gst/gstconfig.h>
+
+typedef struct _GstPadTemplate GstPadTemplate;
+typedef struct _GstPadTemplateClass GstPadTemplateClass;
+typedef struct _GstStaticPadTemplate GstStaticPadTemplate;
+
+#include <gst/gstobject.h>
+#include <gst/gstbuffer.h>
+#include <gst/gstcaps.h>
+#include <gst/gstevent.h>
+#include <gst/gstquery.h>
+#include <gst/gsttask.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_STATIC_PAD_TEMPLATE (gst_static_pad_template_get_type ())
+
+#define GST_TYPE_PAD_TEMPLATE (gst_pad_template_get_type ())
+#define GST_PAD_TEMPLATE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_PAD_TEMPLATE,GstPadTemplate))
+#define GST_PAD_TEMPLATE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_PAD_TEMPLATE,GstPadTemplateClass))
+#define GST_IS_PAD_TEMPLATE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_PAD_TEMPLATE))
+#define GST_IS_PAD_TEMPLATE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_PAD_TEMPLATE))
+
+/**
+ * GstPadPresence:
+ * @GST_PAD_ALWAYS: the pad is always available
+ * @GST_PAD_SOMETIMES: the pad will become available depending on the media stream
+ * @GST_PAD_REQUEST: the pad is only available on request with
+ * gst_element_get_request_pad().
+ *
+ * Indicates when this pad will become available.
+ */
+typedef enum {
+ GST_PAD_ALWAYS,
+ GST_PAD_SOMETIMES,
+ GST_PAD_REQUEST
+} GstPadPresence;
+
+/**
+ * GST_PAD_TEMPLATE_NAME_TEMPLATE:
+ * @templ: the template to query
+ *
+ * Get the nametemplate of the padtemplate.
+ */
+#define GST_PAD_TEMPLATE_NAME_TEMPLATE(templ) (((GstPadTemplate *)(templ))->name_template)
+
+/**
+ * GST_PAD_TEMPLATE_DIRECTION:
+ * @templ: the template to query
+ *
+ * Get the #GstPadDirection of the padtemplate.
+ */
+#define GST_PAD_TEMPLATE_DIRECTION(templ) (((GstPadTemplate *)(templ))->direction)
+
+/**
+ * GST_PAD_TEMPLATE_PRESENCE:
+ * @templ: the template to query
+ *
+ * Get the #GstPadPresence of the padtemplate.
+ */
+#define GST_PAD_TEMPLATE_PRESENCE(templ) (((GstPadTemplate *)(templ))->presence)
+
+/**
+ * GST_PAD_TEMPLATE_CAPS:
+ * @templ: the template to query
+ *
+ * Get a handle to the padtemplate #GstCaps
+ */
+#define GST_PAD_TEMPLATE_CAPS(templ) (((GstPadTemplate *)(templ))->caps)
+
+/**
+ * GstPadTemplateFlags:
+ * @GST_PAD_TEMPLATE_FLAG_LAST: first flag that can be used by subclasses.
+ *
+ * Flags for the padtemplate
+ */
+typedef enum {
+ /* padding */
+ GST_PAD_TEMPLATE_FLAG_LAST = (GST_OBJECT_FLAG_LAST << 4)
+} GstPadTemplateFlags;
+
+/**
+ * GST_PAD_TEMPLATE_IS_FIXED:
+ * @templ: the template to query
+ *
+ * Check if the properties of the padtemplate are fixed
+ */
+#define GST_PAD_TEMPLATE_IS_FIXED(templ) (GST_OBJECT_FLAG_IS_SET(templ, GST_PAD_TEMPLATE_FIXED))
+
+/**
+ * GstPadTemplate:
+ *
+ * The padtemplate object.
+ */
+struct _GstPadTemplate {
+ GstObject object;
+
+ gchar *name_template;
+ GstPadDirection direction;
+ GstPadPresence presence;
+ GstCaps *caps;
+
+ gpointer _gst_reserved[GST_PADDING];
+};
+
+struct _GstPadTemplateClass {
+ GstObjectClass parent_class;
+
+ /* signal callbacks */
+ void (*pad_created) (GstPadTemplate *templ, GstPad *pad);
+
+ gpointer _gst_reserved[GST_PADDING];
+};
+
+/**
+ * GstStaticPadTemplate:
+ * @name_template: the name of the template
+ * @direction: the direction of the template
+ * @presence: the presence of the template
+ * @static_caps: the caps of the template.
+ *
+ * Structure describing the #GstStaticPadTemplate.
+ */
+struct _GstStaticPadTemplate {
+ const gchar *name_template;
+ GstPadDirection direction;
+ GstPadPresence presence;
+ GstStaticCaps static_caps;
+};
+
+/**
+ * GST_STATIC_PAD_TEMPLATE:
+ * @padname: the name template of the pad
+ * @dir: the GstPadDirection of the pad
+ * @pres: the GstPadPresence of the pad
+ * @caps: the GstStaticCaps of the pad
+ *
+ * Convenience macro to fill the values of a GstStaticPadTemplate
+ * structure.
+ */
+#define GST_STATIC_PAD_TEMPLATE(padname, dir, pres, caps) \
+{ \
+ /* name_template */ padname, \
+ /* direction */ dir, \
+ /* presence */ pres, \
+ /* caps */ caps \
+}
+
+/* templates and factories */
+GType gst_pad_template_get_type (void);
+GType gst_static_pad_template_get_type (void);
+
+GstPadTemplate* gst_pad_template_new (const gchar *name_template,
+ GstPadDirection direction, GstPadPresence presence,
+ GstCaps *caps);
+
+GstPadTemplate * gst_static_pad_template_get (GstStaticPadTemplate *pad_template);
+GstCaps* gst_static_pad_template_get_caps (GstStaticPadTemplate *templ);
+GstCaps* gst_pad_template_get_caps (GstPadTemplate *templ);
+
+void gst_pad_template_pad_created (GstPadTemplate * templ, GstPad * pad);
+
+G_END_DECLS
+
+#endif /* __GST_PAD_TEMPLATE_H__ */
+
diff --git a/gst/gstparamspecs.c b/gst/gstparamspecs.c
new file mode 100644
index 0000000..e75a944
--- /dev/null
+++ b/gst/gstparamspecs.c
@@ -0,0 +1,207 @@
+/* GStreamer - GParamSpecs for some of our types
+ * Copyright (C) 2007 Tim-Philipp Müller <tim centricular 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.
+ */
+/**
+ * SECTION:gstparamspec
+ * @short_description: GParamSpec implementations specific
+ * to GStreamer
+ *
+ * GParamSpec implementations specific to GStreamer.
+ *
+ * Last reviewed on 2008-03-11 (0.10.18)
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gst_private.h"
+#include "glib-compat-private.h"
+#include "gstparamspecs.h"
+
+/* --- GstParamSpecFraction --- */
+
+static void
+_gst_param_fraction_init (GParamSpec * pspec)
+{
+ GstParamSpecFraction *fspec = GST_PARAM_SPEC_FRACTION (pspec);
+
+ fspec->min_num = 0;
+ fspec->min_den = 1;
+ fspec->max_num = G_MAXINT;
+ fspec->max_den = 1;
+ fspec->def_num = 1;
+ fspec->def_den = 1;
+}
+
+static void
+_gst_param_fraction_set_default (GParamSpec * pspec, GValue * value)
+{
+ value->data[0].v_int = GST_PARAM_SPEC_FRACTION (pspec)->def_num;
+ value->data[1].v_int = GST_PARAM_SPEC_FRACTION (pspec)->def_den;
+}
+
+static gboolean
+_gst_param_fraction_validate (GParamSpec * pspec, GValue * value)
+{
+ GstParamSpecFraction *fspec = GST_PARAM_SPEC_FRACTION (pspec);
+ gboolean within_range = FALSE;
+ GValue f_this = { 0, };
+ GValue f_min = { 0, };
+ GValue f_max = { 0, };
+ gint res;
+
+ g_value_init (&f_this, GST_TYPE_FRACTION);
+ gst_value_set_fraction (&f_this, value->data[0].v_int, value->data[1].v_int);
+
+ g_value_init (&f_min, GST_TYPE_FRACTION);
+ gst_value_set_fraction (&f_min, fspec->min_num, fspec->min_den);
+
+ g_value_init (&f_max, GST_TYPE_FRACTION);
+ gst_value_set_fraction (&f_max, fspec->max_num, fspec->max_den);
+
+ res = gst_value_compare (&f_min, &f_this);
+#ifndef GST_DISABLE_GST_DEBUG
+ GST_LOG ("comparing %d/%d to %d/%d, result = %d", fspec->min_num,
+ fspec->min_den, value->data[0].v_int, value->data[1].v_int, res);
+#endif
+ if (res != GST_VALUE_LESS_THAN && res != GST_VALUE_EQUAL)
+ goto out;
+
+#ifndef GST_DISABLE_GST_DEBUG
+ GST_LOG ("comparing %d/%d to %d/%d, result = %d", value->data[0].v_int,
+ value->data[1].v_int, fspec->max_num, fspec->max_den, res);
+#endif
+ res = gst_value_compare (&f_this, &f_max);
+ if (res != GST_VALUE_LESS_THAN && res != GST_VALUE_EQUAL)
+ goto out;
+
+ within_range = TRUE;
+
+out:
+
+ g_value_unset (&f_min);
+ g_value_unset (&f_max);
+ g_value_unset (&f_this);
+
+#ifndef GST_DISABLE_GST_DEBUG
+ GST_LOG ("%swithin range", (within_range) ? "" : "not ");
+#endif
+
+ /* return FALSE if everything ok, otherwise TRUE */
+ return !within_range;
+}
+
+static gint
+_gst_param_fraction_values_cmp (GParamSpec * pspec, const GValue * value1,
+ const GValue * value2)
+{
+ gint res;
+
+ res = gst_value_compare (value1, value2);
+
+ g_assert (res != GST_VALUE_UNORDERED);
+
+ /* GST_VALUE_LESS_THAN is -1, EQUAL is 0, and GREATER_THAN is 1 */
+ return res;
+}
+
+GType
+gst_param_spec_fraction_get_type (void)
+{
+ static GType type; /* 0 */
+
+ /* register GST_TYPE_PARAM_FRACTION */
+ if (type == 0) {
+ static GParamSpecTypeInfo pspec_info = {
+ sizeof (GstParamSpecFraction), /* instance_size */
+ 0, /* n_preallocs */
+ _gst_param_fraction_init, /* instance_init */
+ G_TYPE_INVALID, /* value_type */
+ NULL, /* finalize */
+ _gst_param_fraction_set_default, /* value_set_default */
+ _gst_param_fraction_validate, /* value_validate */
+ _gst_param_fraction_values_cmp, /* values_cmp */
+ };
+ pspec_info.value_type = GST_TYPE_FRACTION;
+ type = g_param_type_register_static ("GstParamFraction", &pspec_info);
+ }
+ return type;
+}
+
+/**
+ * gst_param_spec_fraction:
+ * @name: canonical name of the property specified
+ * @nick: nick name for the property specified
+ * @blurb: description of the property specified
+ * @min_num: minimum value (fraction numerator)
+ * @min_denom: minimum value (fraction denominator)
+ * @max_num: maximum value (fraction numerator)
+ * @max_denom: maximum value (fraction denominator)
+ * @default_num: default value (fraction numerator)
+ * @default_denom: default value (fraction denominator)
+ * @flags: flags for the property specified
+ *
+ * This function creates a fraction GParamSpec for use by objects/elements
+ * that want to expose properties of fraction type. This function is typically
+ * used in connection with g_object_class_install_property() in a GObjects's
+ * instance_init function.
+ *
+ * Returns: a newly created parameter specification
+ *
+ * Since: 0.10.14
+ */
+GParamSpec *
+gst_param_spec_fraction (const gchar * name, const gchar * nick,
+ const gchar * blurb, gint min_num, gint min_denom, gint max_num,
+ gint max_denom, gint default_num, gint default_denom, GParamFlags flags)
+{
+ GstParamSpecFraction *fspec;
+ GParamSpec *pspec;
+ GValue default_val = { 0, };
+
+ fspec =
+ g_param_spec_internal (GST_TYPE_PARAM_FRACTION, name, nick, blurb, flags);
+
+ fspec->min_num = min_num;
+ fspec->min_den = min_denom;
+ fspec->max_num = max_num;
+ fspec->max_den = max_denom;
+ fspec->def_num = default_num;
+ fspec->def_den = default_denom;
+
+ pspec = G_PARAM_SPEC (fspec);
+
+ /* check that min <= default <= max */
+ g_value_init (&default_val, GST_TYPE_FRACTION);
+ gst_value_set_fraction (&default_val, default_num, default_denom);
+ /* validate returns TRUE if the validation fails */
+ if (_gst_param_fraction_validate (pspec, &default_val)) {
+ g_critical ("GstParamSpec of type 'fraction' for property '%s' has a "
+ "default value of %d/%d, which is not within the allowed range of "
+ "%d/%d to %d/%d", name, default_num, default_denom, min_num,
+ min_denom, max_num, max_denom);
+ g_param_spec_ref (pspec);
+ g_param_spec_sink (pspec);
+ g_param_spec_unref (pspec);
+ pspec = NULL;
+ }
+ g_value_unset (&default_val);
+
+ return pspec;
+}
diff --git a/gst/gstparamspecs.h b/gst/gstparamspecs.h
new file mode 100644
index 0000000..c2cdf6f
--- /dev/null
+++ b/gst/gstparamspecs.h
@@ -0,0 +1,128 @@
+/* GStreamer - GParamSpecs for some of our types
+ * Copyright (C) 2007 Tim-Philipp Müller <tim centricular 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.
+ */
+
+#ifndef __GST_PARAMSPECS_H__
+#define __GST_PARAMSPECS_H__
+
+#include <gst/gstvalue.h>
+
+G_BEGIN_DECLS
+
+/* --- paramspec flags */
+
+/**
+ * GST_PARAM_CONTROLLABLE:
+ *
+ * Use this flag on GObject properties to signal they can make sense to be.
+ * controlled over time. This hint is used by the GstController.
+ */
+#define GST_PARAM_CONTROLLABLE (1 << (G_PARAM_USER_SHIFT + 1))
+
+/**
+ * GST_PARAM_MUTABLE_READY:
+ *
+ * Use this flag on GObject properties of GstElements to indicate that
+ * they can be changed when the element is in the READY or lower state.
+ *
+ * Since: 0.10.23
+ */
+#define GST_PARAM_MUTABLE_READY (1 << (G_PARAM_USER_SHIFT + 2))
+
+/**
+ * GST_PARAM_MUTABLE_PAUSED:
+ *
+ * Use this flag on GObject properties of GstElements to indicate that
+ * they can be changed when the element is in the PAUSED or lower state.
+ * This flag implies GST_PARAM_MUTABLE_READY.
+ *
+ * Since: 0.10.23
+ */
+#define GST_PARAM_MUTABLE_PAUSED (1 << (G_PARAM_USER_SHIFT + 3))
+
+/**
+ * GST_PARAM_MUTABLE_PLAYING:
+ *
+ * Use this flag on GObject properties of GstElements to indicate that
+ * they can be changed when the element is in the PLAYING or lower state.
+ * This flag implies GST_PARAM_MUTABLE_PAUSED.
+ *
+ * Since: 0.10.23
+ */
+#define GST_PARAM_MUTABLE_PLAYING (1 << (G_PARAM_USER_SHIFT + 4))
+
+/**
+ * GST_PARAM_USER_SHIFT:
+ *
+ * Bits based on GST_PARAM_USER_SHIFT can be used by 3rd party applications.
+ */
+#define GST_PARAM_USER_SHIFT (1 << (G_PARAM_USER_SHIFT + 8))
+
+
+/* --- type macros --- */
+
+#define GST_TYPE_PARAM_FRACTION (gst_param_spec_fraction_get_type ())
+#define GST_IS_PARAM_SPEC_FRACTION(pspec) (G_TYPE_CHECK_INSTANCE_TYPE ((pspec), GST_TYPE_PARAM_FRACTION))
+#define GST_PARAM_SPEC_FRACTION(pspec) (G_TYPE_CHECK_INSTANCE_CAST ((pspec), GST_TYPE_PARAM_FRACTION, GstParamSpecFraction))
+
+
+/* --- get_type functions --- */
+
+GType gst_param_spec_fraction_get_type (void);
+
+
+/* --- typedefs & structures --- */
+
+typedef struct _GstParamSpecFraction GstParamSpecFraction;
+
+/**
+ * GstParamSpecFraction:
+ * @parent_instance: super class
+ * @min_num: minimal numerator
+ * @min_den: minimal denominator
+ * @max_num: maximal numerator
+ * @max_den: maximal denominator
+ * @def_num: default numerator
+ * @def_den: default denominator
+ *
+ * A GParamSpec derived structure that contains the meta data for fractional
+ * properties.
+ */
+struct _GstParamSpecFraction {
+ GParamSpec parent_instance;
+
+ gint min_num, min_den;
+ gint max_num, max_den;
+ gint def_num, def_den;
+};
+
+
+/* --- GParamSpec prototypes --- */
+
+GParamSpec * gst_param_spec_fraction (const gchar * name,
+ const gchar * nick,
+ const gchar * blurb,
+ gint min_num, gint min_denom,
+ gint max_num, gint max_denom,
+ gint default_num, gint default_denom,
+ GParamFlags flags);
+
+G_END_DECLS
+
+#endif /* __GST_PARAMSPECS_H__ */
+
diff --git a/gst/gstparse.c b/gst/gstparse.c
new file mode 100644
index 0000000..f60d758
--- /dev/null
+++ b/gst/gstparse.c
@@ -0,0 +1,360 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2000 Wim Taymans <wtay@chello.be>
+ * 2002 Andy Wingo <wingo@pobox.com>
+ * 2008 Tim-Philipp Müller <tim centricular net>
+ *
+ * gstparse.c: get a pipeline from a text pipeline description
+ *
+ * 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:gstparse
+ * @short_description: Get a pipeline from a text pipeline description
+ *
+ * These function allow to create a pipeline based on the syntax used in the
+ * gst-launch utility (see man-page for syntax documentation).
+ *
+ * Please note that these functions take several measures to create
+ * somewhat dynamic pipelines. Due to that such pipelines are not always
+ * reusable (set the state to NULL and back to PLAYING).
+ */
+
+#include "gst_private.h"
+#include <string.h>
+
+#include "gstparse.h"
+#include "gsterror.h"
+#include "gstinfo.h"
+#ifndef GST_DISABLE_PARSE
+#include "parse/types.h"
+#endif
+
+static void
+_prepend_missing_element (gchar * element, GList ** list)
+{
+ *list = g_list_prepend (*list, g_strdup (element));
+}
+
+static GstParseContext *
+gst_parse_context_copy (const GstParseContext * context)
+{
+ GstParseContext *ret = NULL;
+#ifndef GST_DISABLE_PARSE
+
+ ret = gst_parse_context_new ();
+ if (context) {
+ g_list_foreach (context->missing_elements, (GFunc) _prepend_missing_element,
+ &ret->missing_elements);
+ ret->missing_elements = g_list_reverse (ret->missing_elements);
+ }
+#endif
+ return ret;
+}
+
+GType
+gst_parse_context_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ type = g_boxed_type_register_static ("GstParseContext",
+ (GBoxedCopyFunc) gst_parse_context_copy,
+ (GBoxedFreeFunc) gst_parse_context_free);
+ }
+
+ return type;
+}
+
+/**
+ * gst_parse_error_quark:
+ *
+ * Get the error quark used by the parsing subsystem.
+ *
+ * Returns: the quark of the parse errors.
+ */
+GQuark
+gst_parse_error_quark (void)
+{
+ static GQuark quark = 0;
+
+ if (!quark)
+ quark = g_quark_from_static_string ("gst_parse_error");
+ return quark;
+}
+
+
+/**
+ * gst_parse_context_new:
+ *
+ * Allocates a parse context for use with gst_parse_launch_full() or
+ * gst_parse_launchv_full().
+ *
+ * Free-function: gst_parse_context_free
+ *
+ * Returns: (transfer full): a newly-allocated parse context. Free with
+ * gst_parse_context_free() when no longer needed.
+ *
+ * Since: 0.10.20
+ */
+GstParseContext *
+gst_parse_context_new (void)
+{
+#ifndef GST_DISABLE_PARSE
+ GstParseContext *ctx;
+
+ ctx = g_slice_new (GstParseContext);
+ ctx->missing_elements = NULL;
+
+ return ctx;
+#else
+ return NULL;
+#endif
+}
+
+/**
+ * gst_parse_context_free:
+ * @context: (transfer full): a #GstParseContext
+ *
+ * Frees a parse context previously allocated with gst_parse_context_new().
+ *
+ * Since: 0.10.20
+ */
+void
+gst_parse_context_free (GstParseContext * context)
+{
+#ifndef GST_DISABLE_PARSE
+ if (context) {
+ g_list_foreach (context->missing_elements, (GFunc) g_free, NULL);
+ g_list_free (context->missing_elements);
+ g_slice_free (GstParseContext, context);
+ }
+#endif
+}
+
+/**
+ * gst_parse_context_get_missing_elements:
+ * @context: a #GstParseContext
+ *
+ * Retrieve missing elements from a previous run of gst_parse_launch_full()
+ * or gst_parse_launchv_full(). Will only return results if an error code
+ * of %GST_PARSE_ERROR_NO_SUCH_ELEMENT was returned.
+ *
+ * Returns: (transfer full) (array zero-terminated=1) (element-type gchar*): a
+ * NULL-terminated array of element factory name strings of missing
+ * elements. Free with g_strfreev() when no longer needed.
+ *
+ * Since: 0.10.20
+ */
+gchar **
+gst_parse_context_get_missing_elements (GstParseContext * context)
+{
+#ifndef GST_DISABLE_PARSE
+ gchar **arr;
+ GList *l;
+ guint len, i;
+
+ g_return_val_if_fail (context != NULL, NULL);
+
+ len = g_list_length (context->missing_elements);
+
+ if (G_UNLIKELY (len == 0))
+ return NULL;
+
+ arr = g_new (gchar *, len + 1);
+
+ for (i = 0, l = context->missing_elements; l != NULL; l = l->next, ++i)
+ arr[i] = g_strdup (l->data);
+
+ arr[i] = NULL;
+
+ return arr;
+#else
+ return NULL;
+#endif
+}
+
+#ifndef GST_DISABLE_PARSE
+static gchar *
+_gst_parse_escape (const gchar * str)
+{
+ GString *gstr = NULL;
+
+ g_return_val_if_fail (str != NULL, NULL);
+
+ gstr = g_string_sized_new (strlen (str));
+
+ while (*str) {
+ if (*str == ' ')
+ g_string_append_c (gstr, '\\');
+ g_string_append_c (gstr, *str);
+ str++;
+ }
+
+ return g_string_free (gstr, FALSE);
+}
+#endif /* !GST_DISABLE_PARSE */
+
+/**
+ * gst_parse_launchv:
+ * @argv: (in) (array zero-terminated=1): null-terminated array of arguments
+ * @error: pointer to a #GError
+ *
+ * Create a new element based on command line syntax.
+ * @error will contain an error message if an erroneuos pipeline is specified.
+ * An error does not mean that the pipeline could not be constructed.
+ *
+ * Returns: (transfer full): a new element on success and %NULL on failure.
+ */
+GstElement *
+gst_parse_launchv (const gchar ** argv, GError ** error)
+{
+ return gst_parse_launchv_full (argv, NULL, GST_PARSE_FLAG_NONE, error);
+}
+
+/**
+ * gst_parse_launchv_full:
+ * @argv: (in) (array zero-terminated=1): null-terminated array of arguments
+ * @context: (allow-none): a parse context allocated with
+ * gst_parse_context_new(), or %NULL
+ * @flags: parsing options, or #GST_PARSE_FLAG_NONE
+ * @error: pointer to a #GError (which must be initialised to %NULL)
+ *
+ * Create a new element based on command line syntax.
+ * @error will contain an error message if an erroneous pipeline is specified.
+ * An error does not mean that the pipeline could not be constructed.
+ *
+ * Returns: (transfer full): a new element on success; on failure, either %NULL
+ * or a partially-constructed bin or element will be returned and @error will
+ * be set (unless you passed #GST_PARSE_FLAG_FATAL_ERRORS in @flags, then
+ * %NULL will always be returned on failure)
+ *
+ * Since: 0.10.20
+ */
+GstElement *
+gst_parse_launchv_full (const gchar ** argv, GstParseContext * context,
+ GstParseFlags flags, GError ** error)
+{
+#ifndef GST_DISABLE_PARSE
+ GstElement *element;
+ GString *str;
+ const gchar **argvp, *arg;
+ gchar *tmp;
+
+ g_return_val_if_fail (argv != NULL, NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ /* let's give it a nice size. */
+ str = g_string_sized_new (1024);
+
+ argvp = argv;
+ while (*argvp) {
+ arg = *argvp;
+ tmp = _gst_parse_escape (arg);
+ g_string_append (str, tmp);
+ g_free (tmp);
+ g_string_append_c (str, ' ');
+ argvp++;
+ }
+
+ element = gst_parse_launch_full (str->str, context, flags, error);
+
+ g_string_free (str, TRUE);
+
+ return element;
+#else
+ /* gst_parse_launch_full() will set a GST_CORE_ERROR_DISABLED error for us */
+ return gst_parse_launch_full ("", NULL, 0, error);
+#endif
+}
+
+/**
+ * gst_parse_launch:
+ * @pipeline_description: the command line describing the pipeline
+ * @error: the error message in case of an erroneous pipeline.
+ *
+ * Create a new pipeline based on command line syntax.
+ * Please note that you might get a return value that is not %NULL even though
+ * the @error is set. In this case there was a recoverable parsing error and you
+ * can try to play the pipeline.
+ *
+ * Returns: (transfer full): a new element on success, %NULL on failure. If
+ * more than one toplevel element is specified by the @pipeline_description,
+ * all elements are put into a #GstPipeline, which than is returned.
+ */
+GstElement *
+gst_parse_launch (const gchar * pipeline_description, GError ** error)
+{
+ return gst_parse_launch_full (pipeline_description, NULL, GST_PARSE_FLAG_NONE,
+ error);
+}
+
+/**
+ * gst_parse_launch_full:
+ * @pipeline_description: the command line describing the pipeline
+ * @context: (allow-none): a parse context allocated with
+ * gst_parse_context_new(), or %NULL
+ * @flags: parsing options, or #GST_PARSE_FLAG_NONE
+ * @error: the error message in case of an erroneous pipeline.
+ *
+ * Create a new pipeline based on command line syntax.
+ * Please note that you might get a return value that is not %NULL even though
+ * the @error is set. In this case there was a recoverable parsing error and you
+ * can try to play the pipeline.
+ *
+ * Returns: (transfer full): a new element on success, %NULL on failure. If
+ * more than one toplevel element is specified by the @pipeline_description,
+ * all elements are put into a #GstPipeline, which then is returned.
+ *
+ * Since: 0.10.20
+ */
+GstElement *
+gst_parse_launch_full (const gchar * pipeline_description,
+ GstParseContext * context, GstParseFlags flags, GError ** error)
+{
+#ifndef GST_DISABLE_PARSE
+ GstElement *element;
+
+ g_return_val_if_fail (pipeline_description != NULL, NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ GST_CAT_INFO (GST_CAT_PIPELINE, "parsing pipeline description '%s'",
+ pipeline_description);
+
+ element = _gst_parse_launch (pipeline_description, error, context, flags);
+
+ /* don't return partially constructed pipeline if FATAL_ERRORS was given */
+ if (G_UNLIKELY (error != NULL && *error != NULL && element != NULL)) {
+ if ((flags & GST_PARSE_FLAG_FATAL_ERRORS)) {
+ gst_object_unref (element);
+ element = NULL;
+ }
+ }
+
+ return element;
+#else
+ gchar *msg;
+
+ GST_WARNING ("Disabled API called");
+
+ msg = gst_error_get_message (GST_CORE_ERROR, GST_CORE_ERROR_DISABLED);
+ g_set_error (error, GST_CORE_ERROR, GST_CORE_ERROR_DISABLED, "%s", msg);
+ g_free (msg);
+
+ return NULL;
+#endif
+}
diff --git a/gst/gstparse.h b/gst/gstparse.h
new file mode 100644
index 0000000..a0f27b3
--- /dev/null
+++ b/gst/gstparse.h
@@ -0,0 +1,119 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2000 Wim Taymans <wtay@chello.be>
+ *
+ * gstparse.h: get a pipeline from a text pipeline description
+ *
+ * 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_PARSE_H__
+#define __GST_PARSE_H__
+
+#include <gst/gstelement.h>
+
+G_BEGIN_DECLS
+
+GQuark gst_parse_error_quark (void);
+/**
+ * GST_PARSE_ERROR:
+ *
+ * Get access to the error quark of the parse subsystem.
+ */
+#define GST_PARSE_ERROR gst_parse_error_quark ()
+
+/**
+ * GstParseError:
+ * @GST_PARSE_ERROR_SYNTAX: A syntax error occured.
+ * @GST_PARSE_ERROR_NO_SUCH_ELEMENT: The description contained an unknown element
+ * @GST_PARSE_ERROR_NO_SUCH_PROPERTY: An element did not have a specified property
+ * @GST_PARSE_ERROR_LINK: There was an error linking two pads.
+ * @GST_PARSE_ERROR_COULD_NOT_SET_PROPERTY: There was an error setting a property
+ * @GST_PARSE_ERROR_EMPTY_BIN: An empty bin was specified.
+ * @GST_PARSE_ERROR_EMPTY: An empty description was specified
+ *
+ * The different parsing errors that can occur.
+ */
+typedef enum
+{
+ GST_PARSE_ERROR_SYNTAX,
+ GST_PARSE_ERROR_NO_SUCH_ELEMENT,
+ GST_PARSE_ERROR_NO_SUCH_PROPERTY,
+ GST_PARSE_ERROR_LINK,
+ GST_PARSE_ERROR_COULD_NOT_SET_PROPERTY,
+ GST_PARSE_ERROR_EMPTY_BIN,
+ GST_PARSE_ERROR_EMPTY
+} GstParseError;
+
+/**
+ * GstParseFlags:
+ * @GST_PARSE_FLAG_NONE: Do not use any special parsing options.
+ * @GST_PARSE_FLAG_FATAL_ERRORS: Always return NULL when an error occurs
+ * (default behaviour is to return partially constructed bins or elements
+ * in some cases)
+ *
+ * Parsing options.
+ *
+ * Since: 0.10.20
+ */
+typedef enum
+{
+ GST_PARSE_FLAG_NONE = 0,
+ GST_PARSE_FLAG_FATAL_ERRORS = (1 << 0)
+} GstParseFlags;
+
+#define GST_TYPE_PARSE_CONTEXT (gst_parse_context_get_type())
+
+/**
+ * GstParseContext:
+ *
+ * Opaque structure.
+ *
+ * Since: 0.10.20
+ */
+typedef struct _GstParseContext GstParseContext;
+
+/* create, process and free a parse context */
+
+GType gst_parse_context_get_type (void);
+GstParseContext * gst_parse_context_new (void);
+
+gchar ** gst_parse_context_get_missing_elements (GstParseContext * context);
+
+void gst_parse_context_free (GstParseContext * context);
+
+
+/* parse functions */
+
+GstElement * gst_parse_launch (const gchar * pipeline_description,
+ GError ** error);
+
+GstElement * gst_parse_launchv (const gchar ** argv,
+ GError ** error);
+
+GstElement * gst_parse_launch_full (const gchar * pipeline_description,
+ GstParseContext * context,
+ GstParseFlags flags,
+ GError ** error);
+
+GstElement * gst_parse_launchv_full (const gchar ** argv,
+ GstParseContext * context,
+ GstParseFlags flags,
+ GError ** error);
+
+G_END_DECLS
+
+#endif /* __GST_PARSE_H__ */
diff --git a/gst/gstpipeline.c b/gst/gstpipeline.c
new file mode 100644
index 0000000..4efc029
--- /dev/null
+++ b/gst/gstpipeline.c
@@ -0,0 +1,855 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2004,2005 Wim Taymans <wim@fluendo.com>
+ *
+ * gstpipeline.c: Overall pipeline management element
+ *
+ * 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:gstpipeline
+ * @short_description: Top-level bin with clocking and bus management
+ functionality.
+ * @see_also: #GstElement, #GstBin, #GstClock, #GstBus
+ *
+ * A #GstPipeline is a special #GstBin used as the toplevel container for
+ * the filter graph. The #GstPipeline will manage the selection and
+ * distribution of a global #GstClock as well as provide a #GstBus to the
+ * application. It will also implement a default behaviour for managing
+ * seek events (see gst_element_seek()).
+ *
+ * gst_pipeline_new() is used to create a pipeline. when you are done with
+ * the pipeline, use gst_object_unref() to free its resources including all
+ * added #GstElement objects (if not otherwise referenced).
+ *
+ * Elements are added and removed from the pipeline using the #GstBin
+ * methods like gst_bin_add() and gst_bin_remove() (see #GstBin).
+ *
+ * Before changing the state of the #GstPipeline (see #GstElement) a #GstBus
+ * can be retrieved with gst_pipeline_get_bus(). This bus can then be
+ * used to receive #GstMessage from the elements in the pipeline.
+ *
+ * By default, a #GstPipeline will automatically flush the pending #GstBus
+ * messages when going to the NULL state to ensure that no circular
+ * references exist when no messages are read from the #GstBus. This
+ * behaviour can be changed with gst_pipeline_set_auto_flush_bus().
+ *
+ * When the #GstPipeline performs the PAUSED to PLAYING state change it will
+ * select a clock for the elements. The clock selection algorithm will by
+ * default select a clock provided by an element that is most upstream
+ * (closest to the source). For live pipelines (ones that return
+ * #GST_STATE_CHANGE_NO_PREROLL from the gst_element_set_state() call) this
+ * will select the clock provided by the live source. For normal pipelines
+ * this will select a clock provided by the sinks (most likely the audio
+ * sink). If no element provides a clock, a default #GstSystemClock is used.
+ *
+ * The clock selection can be controlled with the gst_pipeline_use_clock()
+ * method, which will enforce a given clock on the pipeline. With
+ * gst_pipeline_auto_clock() the default clock selection algorithm can be
+ * restored.
+ *
+ * A #GstPipeline maintains a running time for the elements. The running
+ * time is defined as the difference between the current clock time and
+ * the base time. When the pipeline goes to READY or a flushing seek is
+ * performed on it, the running time is reset to 0. When the pipeline is
+ * set from PLAYING to PAUSED, the current clock time is sampled and used to
+ * configure the base time for the elements when the pipeline is set
+ * to PLAYING again. The effect is that the running time (as the difference
+ * between the clock time and the base time) will count how much time was spent
+ * in the PLAYING state. This default behaviour can be changed with the
+ * gst_element_set_start_time() method.
+ *
+ * When sending a flushing seek event to a GstPipeline (see
+ * gst_element_seek()), it will make sure that the pipeline is properly
+ * PAUSED and resumed as well as set the new running time to 0 when the
+ * seek succeeded.
+ *
+ * Last reviewed on 2009-05-29 (0.10.24)
+ */
+
+#include "gst_private.h"
+#include "gsterror.h"
+#include "gst-i18n-lib.h"
+
+#include "gstpipeline.h"
+#include "gstinfo.h"
+#include "gstsystemclock.h"
+#include "gstutils.h"
+
+GST_DEBUG_CATEGORY_STATIC (pipeline_debug);
+#define GST_CAT_DEFAULT pipeline_debug
+
+/* Pipeline signals and args */
+enum
+{
+ /* FILL ME */
+ LAST_SIGNAL
+};
+
+#define DEFAULT_DELAY 0
+#define DEFAULT_AUTO_FLUSH_BUS TRUE
+
+enum
+{
+ PROP_0,
+ PROP_DELAY,
+ PROP_AUTO_FLUSH_BUS
+};
+
+#define GST_PIPELINE_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_PIPELINE, GstPipelinePrivate))
+
+struct _GstPipelinePrivate
+{
+ /* with LOCK */
+ gboolean auto_flush_bus;
+
+ /* when we need to update stream_time or clock when going back to
+ * PLAYING*/
+ GstClockTime last_start_time;
+ gboolean update_clock;
+};
+
+
+static void gst_pipeline_dispose (GObject * object);
+static void gst_pipeline_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+static void gst_pipeline_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
+
+static GstClock *gst_pipeline_provide_clock_func (GstElement * element);
+static GstStateChangeReturn gst_pipeline_change_state (GstElement * element,
+ GstStateChange transition);
+
+static void gst_pipeline_handle_message (GstBin * bin, GstMessage * message);
+
+/* static guint gst_pipeline_signals[LAST_SIGNAL] = { 0 }; */
+
+#define _do_init \
+{ \
+ GST_DEBUG_CATEGORY_INIT (pipeline_debug, "pipeline", GST_DEBUG_BOLD, \
+ "debugging info for the 'pipeline' container element"); \
+}
+
+#define gst_pipeline_parent_class parent_class
+G_DEFINE_TYPE_WITH_CODE (GstPipeline, gst_pipeline, GST_TYPE_BIN, _do_init);
+
+static void
+gst_pipeline_class_init (GstPipelineClass * klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
+ GstBinClass *gstbin_class = GST_BIN_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (GstPipelinePrivate));
+
+ gobject_class->set_property = gst_pipeline_set_property;
+ gobject_class->get_property = gst_pipeline_get_property;
+
+ /**
+ * GstPipeline:delay
+ *
+ * The expected delay needed for elements to spin up to the
+ * PLAYING state expressed in nanoseconds.
+ * see gst_pipeline_set_delay() for more information on this option.
+ *
+ * Since: 0.10.5
+ **/
+ g_object_class_install_property (gobject_class, PROP_DELAY,
+ g_param_spec_uint64 ("delay", "Delay",
+ "Expected delay needed for elements "
+ "to spin up to PLAYING in nanoseconds", 0, G_MAXUINT64, DEFAULT_DELAY,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GstPipeline:auto-flush-bus:
+ *
+ * Whether or not to automatically flush all messages on the
+ * pipeline's bus when going from READY to NULL state. Please see
+ * gst_pipeline_set_auto_flush_bus() for more information on this option.
+ *
+ * Since: 0.10.4
+ **/
+ g_object_class_install_property (gobject_class, PROP_AUTO_FLUSH_BUS,
+ g_param_spec_boolean ("auto-flush-bus", "Auto Flush Bus",
+ "Whether to automatically flush the pipeline's bus when going "
+ "from READY into NULL state", DEFAULT_AUTO_FLUSH_BUS,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ gobject_class->dispose = gst_pipeline_dispose;
+
+ gst_element_class_set_metadata (gstelement_class, "Pipeline object",
+ "Generic/Bin",
+ "Complete pipeline object",
+ "Erik Walthinsen <omega@cse.ogi.edu>, Wim Taymans <wim@fluendo.com>");
+
+ gstelement_class->change_state =
+ GST_DEBUG_FUNCPTR (gst_pipeline_change_state);
+ gstelement_class->provide_clock =
+ GST_DEBUG_FUNCPTR (gst_pipeline_provide_clock_func);
+ gstbin_class->handle_message =
+ GST_DEBUG_FUNCPTR (gst_pipeline_handle_message);
+}
+
+static void
+gst_pipeline_init (GstPipeline * pipeline)
+{
+ GstBus *bus;
+
+ pipeline->priv = GST_PIPELINE_GET_PRIVATE (pipeline);
+
+ /* set default property values */
+ pipeline->priv->auto_flush_bus = DEFAULT_AUTO_FLUSH_BUS;
+ pipeline->delay = DEFAULT_DELAY;
+
+ /* create and set a default bus */
+ bus = gst_bus_new ();
+#if 0
+ /* FIXME, disabled for 0.10.5 release as it caused to many regressions */
+ /* Start our bus in flushing if appropriate */
+ if (pipeline->priv->auto_flush_bus)
+ gst_bus_set_flushing (bus, TRUE);
+#endif
+
+ gst_element_set_bus (GST_ELEMENT_CAST (pipeline), bus);
+ GST_DEBUG_OBJECT (pipeline, "set bus %" GST_PTR_FORMAT " on pipeline", bus);
+ gst_object_unref (bus);
+}
+
+static void
+gst_pipeline_dispose (GObject * object)
+{
+ GstPipeline *pipeline = GST_PIPELINE (object);
+ GstClock **clock_p = &pipeline->fixed_clock;
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_REFCOUNTING, pipeline, "dispose");
+
+ /* clear and unref any fixed clock */
+ gst_object_replace ((GstObject **) clock_p, NULL);
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+gst_pipeline_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstPipeline *pipeline = GST_PIPELINE (object);
+
+ switch (prop_id) {
+ case PROP_DELAY:
+ gst_pipeline_set_delay (pipeline, g_value_get_uint64 (value));
+ break;
+ case PROP_AUTO_FLUSH_BUS:
+ gst_pipeline_set_auto_flush_bus (pipeline, g_value_get_boolean (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_pipeline_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GstPipeline *pipeline = GST_PIPELINE (object);
+
+ switch (prop_id) {
+ case PROP_DELAY:
+ g_value_set_uint64 (value, gst_pipeline_get_delay (pipeline));
+ break;
+ case PROP_AUTO_FLUSH_BUS:
+ g_value_set_boolean (value, gst_pipeline_get_auto_flush_bus (pipeline));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+/* set the start_time to 0, this will cause us to select a new base_time and
+ * make the running_time start from 0 again. */
+static void
+reset_start_time (GstPipeline * pipeline)
+{
+ GST_OBJECT_LOCK (pipeline);
+ if (GST_ELEMENT_START_TIME (pipeline) != GST_CLOCK_TIME_NONE) {
+ GST_DEBUG_OBJECT (pipeline, "reset start_time to 0");
+ GST_ELEMENT_START_TIME (pipeline) = 0;
+ pipeline->priv->last_start_time = -1;
+ } else {
+ GST_DEBUG_OBJECT (pipeline, "application asked to not reset stream_time");
+ }
+ GST_OBJECT_UNLOCK (pipeline);
+}
+
+/**
+ * gst_pipeline_new:
+ * @name: name of new pipeline
+ *
+ * Create a new pipeline with the given name.
+ *
+ * Returns: (transfer full): newly created GstPipeline
+ *
+ * MT safe.
+ */
+GstElement *
+gst_pipeline_new (const gchar * name)
+{
+ return gst_element_factory_make ("pipeline", name);
+}
+
+/* takes a snapshot of the running_time of the pipeline and store this as the
+ * element start_time. This is the time we will set as the running_time of the
+ * pipeline when we go to PLAYING next. */
+static void
+pipeline_update_start_time (GstElement * element)
+{
+ GstPipeline *pipeline = GST_PIPELINE_CAST (element);
+ GstClock *clock;
+
+ GST_OBJECT_LOCK (element);
+ if ((clock = element->clock)) {
+ GstClockTime now;
+
+ gst_object_ref (clock);
+ GST_OBJECT_UNLOCK (element);
+
+ /* calculate the time when we stopped */
+ now = gst_clock_get_time (clock);
+ gst_object_unref (clock);
+
+ GST_OBJECT_LOCK (element);
+ /* store the current running time */
+ if (GST_ELEMENT_START_TIME (pipeline) != GST_CLOCK_TIME_NONE) {
+ if (now != GST_CLOCK_TIME_NONE)
+ GST_ELEMENT_START_TIME (pipeline) = now - element->base_time;
+ else
+ GST_WARNING_OBJECT (element,
+ "Clock %s returned invalid time, can't calculate "
+ "running_time when going to the PAUSED state",
+ GST_OBJECT_NAME (clock));
+
+ /* we went to PAUSED, when going to PLAYING select clock and new
+ * base_time */
+ pipeline->priv->update_clock = TRUE;
+ }
+ GST_DEBUG_OBJECT (element,
+ "start_time=%" GST_TIME_FORMAT ", now=%" GST_TIME_FORMAT
+ ", base_time %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (GST_ELEMENT_START_TIME (pipeline)),
+ GST_TIME_ARGS (now), GST_TIME_ARGS (element->base_time));
+ }
+ GST_OBJECT_UNLOCK (element);
+}
+
+/* MT safe */
+static GstStateChangeReturn
+gst_pipeline_change_state (GstElement * element, GstStateChange transition)
+{
+ GstStateChangeReturn result = GST_STATE_CHANGE_SUCCESS;
+ GstPipeline *pipeline = GST_PIPELINE_CAST (element);
+ GstClock *clock;
+
+ switch (transition) {
+ case GST_STATE_CHANGE_NULL_TO_READY:
+ GST_OBJECT_LOCK (element);
+ if (element->bus)
+ gst_bus_set_flushing (element->bus, FALSE);
+ GST_OBJECT_UNLOCK (element);
+ break;
+ case GST_STATE_CHANGE_READY_TO_PAUSED:
+ GST_OBJECT_LOCK (element);
+ pipeline->priv->update_clock = TRUE;
+ GST_OBJECT_UNLOCK (element);
+ break;
+ case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
+ {
+ GstClockTime now, start_time, last_start_time, delay;
+ gboolean update_clock;
+ GstClock *cur_clock;
+
+ GST_DEBUG_OBJECT (element, "selecting clock and base_time");
+
+ GST_OBJECT_LOCK (element);
+ cur_clock = element->clock;
+ if (cur_clock)
+ gst_object_ref (cur_clock);
+ /* get the desired running_time of the first buffer aka the start_time */
+ start_time = GST_ELEMENT_START_TIME (pipeline);
+ last_start_time = pipeline->priv->last_start_time;
+ pipeline->priv->last_start_time = start_time;
+ /* see if we need to update the clock */
+ update_clock = pipeline->priv->update_clock;
+ pipeline->priv->update_clock = FALSE;
+ delay = pipeline->delay;
+ GST_OBJECT_UNLOCK (element);
+
+ /* running time changed, either with a PAUSED or a flush, we need to check
+ * if there is a new clock & update the base time */
+ if (update_clock || last_start_time != start_time) {
+ GST_DEBUG_OBJECT (pipeline, "Need to update start_time");
+
+ /* when going to PLAYING, select a clock when needed. If we just got
+ * flushed, we don't reselect the clock. */
+ if (update_clock) {
+ GST_DEBUG_OBJECT (pipeline, "Need to update clock.");
+ clock = gst_element_provide_clock (element);
+ } else {
+ GST_DEBUG_OBJECT (pipeline,
+ "Don't need to update clock, using old clock.");
+ clock = gst_object_ref (cur_clock);
+ }
+
+ if (clock) {
+ now = gst_clock_get_time (clock);
+ } else {
+ GST_DEBUG ("no clock, using base time of NONE");
+ now = GST_CLOCK_TIME_NONE;
+ }
+
+ if (clock != cur_clock) {
+ /* now distribute the clock (which could be NULL). If some
+ * element refuses the clock, this will return FALSE and
+ * we effectively fail the state change. */
+ if (!gst_element_set_clock (element, clock))
+ goto invalid_clock;
+
+ /* if we selected and distributed a new clock, let the app
+ * know about it */
+ gst_element_post_message (element,
+ gst_message_new_new_clock (GST_OBJECT_CAST (element), clock));
+ }
+
+ if (clock)
+ gst_object_unref (clock);
+
+ if (start_time != GST_CLOCK_TIME_NONE && now != GST_CLOCK_TIME_NONE) {
+ GstClockTime new_base_time = now - start_time + delay;
+ GST_DEBUG_OBJECT (element,
+ "start_time=%" GST_TIME_FORMAT ", now=%" GST_TIME_FORMAT
+ ", base_time %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (start_time), GST_TIME_ARGS (now),
+ GST_TIME_ARGS (new_base_time));
+
+ gst_element_set_base_time (element, new_base_time);
+ } else {
+ GST_DEBUG_OBJECT (pipeline,
+ "NOT adjusting base_time because start_time is NONE");
+ }
+ } else {
+ GST_DEBUG_OBJECT (pipeline,
+ "NOT adjusting base_time because we selected one before");
+ }
+
+ if (cur_clock)
+ gst_object_unref (cur_clock);
+ break;
+ }
+ case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
+ {
+ /* we take a start_time snapshot before calling the children state changes
+ * so that they know about when the pipeline PAUSED. */
+ pipeline_update_start_time (element);
+ break;
+ }
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ case GST_STATE_CHANGE_READY_TO_NULL:
+ break;
+ }
+
+ result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+
+ switch (transition) {
+ case GST_STATE_CHANGE_NULL_TO_READY:
+ break;
+ case GST_STATE_CHANGE_READY_TO_PAUSED:
+ {
+ reset_start_time (pipeline);
+ break;
+ }
+ case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
+ break;
+ case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
+ {
+ /* Take a new snapshot of the start_time after calling the state change on
+ * all children. This will be the running_time of the pipeline when we go
+ * back to PLAYING */
+ pipeline_update_start_time (element);
+ break;
+ }
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ break;
+ case GST_STATE_CHANGE_READY_TO_NULL:
+ {
+ GstBus *bus;
+ gboolean auto_flush;
+
+ /* grab some stuff before we release the lock to flush out the bus */
+ GST_OBJECT_LOCK (element);
+ if ((bus = element->bus))
+ gst_object_ref (bus);
+ auto_flush = pipeline->priv->auto_flush_bus;
+ GST_OBJECT_UNLOCK (element);
+
+ if (bus) {
+ if (auto_flush) {
+ gst_bus_set_flushing (bus, TRUE);
+ } else {
+ GST_INFO_OBJECT (element, "not flushing bus, auto-flushing disabled");
+ }
+ gst_object_unref (bus);
+ }
+ break;
+ }
+ }
+ return result;
+
+ /* ERRORS */
+invalid_clock:
+ {
+ /* we generate this error when the selected clock was not
+ * accepted by some element */
+ GST_ELEMENT_ERROR (pipeline, CORE, CLOCK,
+ (_("Selected clock cannot be used in pipeline.")),
+ ("Pipeline cannot operate with selected clock"));
+ GST_DEBUG_OBJECT (pipeline,
+ "Pipeline cannot operate with selected clock %p", clock);
+ if (clock)
+ gst_object_unref (clock);
+ return GST_STATE_CHANGE_FAILURE;
+ }
+}
+
+/* intercept the bus messages from our children. We watch for the ASYNC_START
+ * message with is posted by the elements (sinks) that require a reset of the
+ * running_time after a flush. ASYNC_START also brings the pipeline back into
+ * the PAUSED, pending PAUSED state. When the ASYNC_DONE message is received the
+ * pipeline will redistribute the new base_time and will bring the elements back
+ * to the desired state of the pipeline. */
+static void
+gst_pipeline_handle_message (GstBin * bin, GstMessage * message)
+{
+ GstPipeline *pipeline = GST_PIPELINE_CAST (bin);
+
+ switch (GST_MESSAGE_TYPE (message)) {
+ case GST_MESSAGE_ASYNC_DONE:
+ {
+ gboolean reset_time;
+
+ gst_message_parse_async_done (message, &reset_time);
+
+ /* reset our running time if we need to distribute a new base_time to the
+ * children. */
+ if (reset_time)
+ reset_start_time (pipeline);
+
+ break;
+ }
+ case GST_MESSAGE_CLOCK_LOST:
+ {
+ GstClock *clock;
+
+ gst_message_parse_clock_lost (message, &clock);
+
+ GST_OBJECT_LOCK (bin);
+ if (clock == GST_ELEMENT_CAST (bin)->clock) {
+ GST_DEBUG_OBJECT (bin, "Used clock '%s' got lost",
+ GST_OBJECT_NAME (clock));
+ pipeline->priv->update_clock = TRUE;
+ }
+ GST_OBJECT_UNLOCK (bin);
+ }
+ default:
+ break;
+ }
+ GST_BIN_CLASS (parent_class)->handle_message (bin, message);
+}
+
+/**
+ * gst_pipeline_get_bus:
+ * @pipeline: a #GstPipeline
+ *
+ * Gets the #GstBus of @pipeline. The bus allows applications to receive
+ * #GstMessage packets.
+ *
+ * Returns: (transfer full): a #GstBus, unref after usage.
+ *
+ * MT safe.
+ */
+GstBus *
+gst_pipeline_get_bus (GstPipeline * pipeline)
+{
+ return gst_element_get_bus (GST_ELEMENT_CAST (pipeline));
+}
+
+static GstClock *
+gst_pipeline_provide_clock_func (GstElement * element)
+{
+ GstClock *clock = NULL;
+ GstPipeline *pipeline = GST_PIPELINE (element);
+
+ /* if we have a fixed clock, use that one */
+ GST_OBJECT_LOCK (pipeline);
+ if (GST_OBJECT_FLAG_IS_SET (pipeline, GST_PIPELINE_FLAG_FIXED_CLOCK)) {
+ clock = pipeline->fixed_clock;
+ if (clock)
+ gst_object_ref (clock);
+ GST_OBJECT_UNLOCK (pipeline);
+
+ GST_CAT_DEBUG (GST_CAT_CLOCK, "pipeline using fixed clock %p (%s)",
+ clock, clock ? GST_STR_NULL (GST_OBJECT_NAME (clock)) : "-");
+ } else {
+ GST_OBJECT_UNLOCK (pipeline);
+ /* let the parent bin select a clock */
+ clock =
+ GST_ELEMENT_CLASS (parent_class)->provide_clock (GST_ELEMENT
+ (pipeline));
+ /* no clock, use a system clock */
+ if (!clock) {
+ clock = gst_system_clock_obtain ();
+
+ GST_CAT_DEBUG (GST_CAT_CLOCK, "pipeline obtained system clock: %p (%s)",
+ clock, clock ? GST_STR_NULL (GST_OBJECT_NAME (clock)) : "-");
+ } else {
+ GST_CAT_DEBUG (GST_CAT_CLOCK, "pipeline obtained clock: %p (%s)",
+ clock, clock ? GST_STR_NULL (GST_OBJECT_NAME (clock)) : "-");
+ }
+ }
+ return clock;
+}
+
+/**
+ * gst_pipeline_get_clock:
+ * @pipeline: a #GstPipeline
+ *
+ * Gets the current clock used by @pipeline.
+ *
+ * Returns: (transfer full): a #GstClock, unref after usage.
+ */
+GstClock *
+gst_pipeline_get_clock (GstPipeline * pipeline)
+{
+ g_return_val_if_fail (GST_IS_PIPELINE (pipeline), NULL);
+
+ return gst_pipeline_provide_clock_func (GST_ELEMENT_CAST (pipeline));
+}
+
+
+/**
+ * gst_pipeline_use_clock:
+ * @pipeline: a #GstPipeline
+ * @clock: (transfer none): the clock to use
+ *
+ * Force @pipeline to use the given @clock. The pipeline will
+ * always use the given clock even if new clock providers are added
+ * to this pipeline.
+ *
+ * If @clock is NULL all clocking will be disabled which will make
+ * the pipeline run as fast as possible.
+ *
+ * MT safe.
+ */
+void
+gst_pipeline_use_clock (GstPipeline * pipeline, GstClock * clock)
+{
+ GstClock **clock_p;
+
+ g_return_if_fail (GST_IS_PIPELINE (pipeline));
+
+ GST_OBJECT_LOCK (pipeline);
+ GST_OBJECT_FLAG_SET (pipeline, GST_PIPELINE_FLAG_FIXED_CLOCK);
+
+ clock_p = &pipeline->fixed_clock;
+ gst_object_replace ((GstObject **) clock_p, (GstObject *) clock);
+ GST_OBJECT_UNLOCK (pipeline);
+
+ GST_CAT_DEBUG (GST_CAT_CLOCK, "pipeline using fixed clock %p (%s)", clock,
+ (clock ? GST_OBJECT_NAME (clock) : "nil"));
+}
+
+/**
+ * gst_pipeline_set_clock:
+ * @pipeline: a #GstPipeline
+ * @clock: (transfer none): the clock to set
+ *
+ * Set the clock for @pipeline. The clock will be distributed
+ * to all the elements managed by the pipeline.
+ *
+ * Returns: TRUE if the clock could be set on the pipeline. FALSE if
+ * some element did not accept the clock.
+ *
+ * MT safe.
+ */
+gboolean
+gst_pipeline_set_clock (GstPipeline * pipeline, GstClock * clock)
+{
+ g_return_val_if_fail (pipeline != NULL, FALSE);
+ g_return_val_if_fail (GST_IS_PIPELINE (pipeline), FALSE);
+
+ return
+ GST_ELEMENT_CLASS (parent_class)->set_clock (GST_ELEMENT_CAST (pipeline),
+ clock);
+}
+
+/**
+ * gst_pipeline_auto_clock:
+ * @pipeline: a #GstPipeline
+ *
+ * Let @pipeline select a clock automatically. This is the default
+ * behaviour.
+ *
+ * Use this function if you previous forced a fixed clock with
+ * gst_pipeline_use_clock() and want to restore the default
+ * pipeline clock selection algorithm.
+ *
+ * MT safe.
+ */
+void
+gst_pipeline_auto_clock (GstPipeline * pipeline)
+{
+ GstClock **clock_p;
+
+ g_return_if_fail (pipeline != NULL);
+ g_return_if_fail (GST_IS_PIPELINE (pipeline));
+
+ GST_OBJECT_LOCK (pipeline);
+ GST_OBJECT_FLAG_UNSET (pipeline, GST_PIPELINE_FLAG_FIXED_CLOCK);
+
+ clock_p = &pipeline->fixed_clock;
+ gst_object_replace ((GstObject **) clock_p, NULL);
+ GST_OBJECT_UNLOCK (pipeline);
+
+ GST_CAT_DEBUG (GST_CAT_CLOCK, "pipeline using automatic clock");
+}
+
+/**
+ * gst_pipeline_set_delay:
+ * @pipeline: a #GstPipeline
+ * @delay: the delay
+ *
+ * Set the expected delay needed for all elements to perform the
+ * PAUSED to PLAYING state change. @delay will be added to the
+ * base time of the elements so that they wait an additional @delay
+ * amount of time before starting to process buffers and cannot be
+ * #GST_CLOCK_TIME_NONE.
+ *
+ * This option is used for tuning purposes and should normally not be
+ * used.
+ *
+ * MT safe.
+ *
+ * Since: 0.10.5
+ */
+void
+gst_pipeline_set_delay (GstPipeline * pipeline, GstClockTime delay)
+{
+ g_return_if_fail (GST_IS_PIPELINE (pipeline));
+ g_return_if_fail (delay != GST_CLOCK_TIME_NONE);
+
+ GST_OBJECT_LOCK (pipeline);
+ pipeline->delay = delay;
+ GST_OBJECT_UNLOCK (pipeline);
+}
+
+/**
+ * gst_pipeline_get_delay:
+ * @pipeline: a #GstPipeline
+ *
+ * Get the configured delay (see gst_pipeline_set_delay()).
+ *
+ * Returns: The configured delay.
+ *
+ * MT safe.
+ *
+ * Since: 0.10.5
+ */
+GstClockTime
+gst_pipeline_get_delay (GstPipeline * pipeline)
+{
+ GstClockTime res;
+
+ g_return_val_if_fail (GST_IS_PIPELINE (pipeline), GST_CLOCK_TIME_NONE);
+
+ GST_OBJECT_LOCK (pipeline);
+ res = pipeline->delay;
+ GST_OBJECT_UNLOCK (pipeline);
+
+ return res;
+}
+
+/**
+ * gst_pipeline_set_auto_flush_bus:
+ * @pipeline: a #GstPipeline
+ * @auto_flush: whether or not to automatically flush the bus when
+ * the pipeline goes from READY to NULL state
+ *
+ * Usually, when a pipeline goes from READY to NULL state, it automatically
+ * flushes all pending messages on the bus, which is done for refcounting
+ * purposes, to break circular references.
+ *
+ * This means that applications that update state using (async) bus messages
+ * (e.g. do certain things when a pipeline goes from PAUSED to READY) might
+ * not get to see messages when the pipeline is shut down, because they might
+ * be flushed before they can be dispatched in the main thread. This behaviour
+ * can be disabled using this function.
+ *
+ * It is important that all messages on the bus are handled when the
+ * automatic flushing is disabled else memory leaks will be introduced.
+ *
+ * MT safe.
+ *
+ * Since: 0.10.4
+ */
+void
+gst_pipeline_set_auto_flush_bus (GstPipeline * pipeline, gboolean auto_flush)
+{
+ g_return_if_fail (GST_IS_PIPELINE (pipeline));
+
+ GST_OBJECT_LOCK (pipeline);
+ pipeline->priv->auto_flush_bus = auto_flush;
+ GST_OBJECT_UNLOCK (pipeline);
+}
+
+/**
+ * gst_pipeline_get_auto_flush_bus:
+ * @pipeline: a #GstPipeline
+ *
+ * Check if @pipeline will automatically flush messages when going to
+ * the NULL state.
+ *
+ * Returns: whether the pipeline will automatically flush its bus when
+ * going from READY to NULL state or not.
+ *
+ * MT safe.
+ *
+ * Since: 0.10.4
+ */
+gboolean
+gst_pipeline_get_auto_flush_bus (GstPipeline * pipeline)
+{
+ gboolean res;
+
+ g_return_val_if_fail (GST_IS_PIPELINE (pipeline), FALSE);
+
+ GST_OBJECT_LOCK (pipeline);
+ res = pipeline->priv->auto_flush_bus;
+ GST_OBJECT_UNLOCK (pipeline);
+
+ return res;
+}
diff --git a/gst/gstpipeline.h b/gst/gstpipeline.h
new file mode 100644
index 0000000..7dbf96f
--- /dev/null
+++ b/gst/gstpipeline.h
@@ -0,0 +1,110 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2000 Wim Taymans <wtay@chello.be>
+ *
+ * gstpipeline.h: Header for GstPipeline element
+ *
+ * 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_PIPELINE_H__
+#define __GST_PIPELINE_H__
+
+#include <gst/gstbin.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_PIPELINE (gst_pipeline_get_type ())
+#define GST_PIPELINE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_PIPELINE, GstPipeline))
+#define GST_IS_PIPELINE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_PIPELINE))
+#define GST_PIPELINE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_PIPELINE, GstPipelineClass))
+#define GST_IS_PIPELINE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_PIPELINE))
+#define GST_PIPELINE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_PIPELINE, GstPipelineClass))
+#define GST_PIPELINE_CAST(obj) ((GstPipeline*)(obj))
+
+typedef struct _GstPipeline GstPipeline;
+typedef struct _GstPipelineClass GstPipelineClass;
+typedef struct _GstPipelinePrivate GstPipelinePrivate;
+
+/**
+ * GstPipelineFlags:
+ * @GST_PIPELINE_FLAG_FIXED_CLOCK: this pipeline works with a fixed clock
+ * @GST_PIPELINE_FLAG_LAST: offset to define more flags
+ *
+ * Pipeline flags
+ */
+typedef enum {
+ GST_PIPELINE_FLAG_FIXED_CLOCK = (GST_BIN_FLAG_LAST << 0),
+ /* padding */
+ GST_PIPELINE_FLAG_LAST = (GST_BIN_FLAG_LAST << 4)
+} GstPipelineFlags;
+
+/**
+ * GstPipeline:
+ * @fixed_clock: The fixed clock of the pipeline, used when
+ * GST_PIPELINE_FLAG_FIXED_CLOCK is set.
+ * @stream_time: The stream time of the pipeline. A better name for this
+ * property would be the running_time, the total time spent in the
+ * PLAYING state without being flushed. (deprecated, use the start_time
+ * on GstElement).
+ * @delay: Extra delay added to base_time to compensate for computing delays
+ * when setting elements to PLAYING.
+ *
+ * The #GstPipeline structure.
+ */
+struct _GstPipeline {
+ GstBin bin;
+
+ /*< public >*/ /* with LOCK */
+ GstClock *fixed_clock;
+
+ GstClockTime stream_time;
+ GstClockTime delay;
+
+ /*< private >*/
+ GstPipelinePrivate *priv;
+
+ gpointer _gst_reserved[GST_PADDING-1];
+};
+
+struct _GstPipelineClass {
+ GstBinClass parent_class;
+
+ /*< private >*/
+ gpointer _gst_reserved[GST_PADDING];
+};
+
+GType gst_pipeline_get_type (void);
+GstElement* gst_pipeline_new (const gchar *name);
+
+GstBus* gst_pipeline_get_bus (GstPipeline *pipeline);
+
+void gst_pipeline_use_clock (GstPipeline *pipeline, GstClock *clock);
+gboolean gst_pipeline_set_clock (GstPipeline *pipeline, GstClock *clock);
+GstClock* gst_pipeline_get_clock (GstPipeline *pipeline);
+void gst_pipeline_auto_clock (GstPipeline *pipeline);
+
+void gst_pipeline_set_delay (GstPipeline *pipeline, GstClockTime delay);
+GstClockTime gst_pipeline_get_delay (GstPipeline *pipeline);
+
+void gst_pipeline_set_auto_flush_bus (GstPipeline *pipeline, gboolean auto_flush);
+gboolean gst_pipeline_get_auto_flush_bus (GstPipeline *pipeline);
+
+G_END_DECLS
+
+#endif /* __GST_PIPELINE_H__ */
+
diff --git a/gst/gstplugin.c b/gst/gstplugin.c
new file mode 100644
index 0000000..dfb5f0c
--- /dev/null
+++ b/gst/gstplugin.c
@@ -0,0 +1,1859 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2000 Wim Taymans <wtay@chello.be>
+ *
+ * gstplugin.c: Plugin subsystem for loading elements, types, and libs
+ *
+ * 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:gstplugin
+ * @short_description: Container for features loaded from a shared object module
+ * @see_also: #GstPluginFeature, #GstElementFactory
+ *
+ * GStreamer is extensible, so #GstElement instances can be loaded at runtime.
+ * A plugin system can provide one or more of the basic
+ * <application>GStreamer</application> #GstPluginFeature subclasses.
+ *
+ * A plugin should export a symbol <symbol>gst_plugin_desc</symbol> that is a
+ * struct of type #GstPluginDesc.
+ * the plugin loader will check the version of the core library the plugin was
+ * linked against and will create a new #GstPlugin. It will then call the
+ * #GstPluginInitFunc function that was provided in the
+ * <symbol>gst_plugin_desc</symbol>.
+ *
+ * Once you have a handle to a #GstPlugin (e.g. from the #GstRegistry), you
+ * can add any object that subclasses #GstPluginFeature.
+ *
+ * Usually plugins are always automaticlly loaded so you don't need to call
+ * gst_plugin_load() explicitly to bring it into memory. There are options to
+ * statically link plugins to an app or even use GStreamer without a plugin
+ * repository in which case gst_plugin_load() can be needed to bring the plugin
+ * into memory.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gst_private.h"
+
+#include <glib/gstdio.h>
+#include <sys/types.h>
+#ifdef HAVE_DIRENT_H
+#include <dirent.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <signal.h>
+#include <errno.h>
+#include <string.h>
+
+#include "glib-compat-private.h"
+
+#include <gst/gst.h>
+
+#define GST_CAT_DEFAULT GST_CAT_PLUGIN_LOADING
+
+static guint _num_static_plugins; /* 0 */
+static GstPluginDesc *_static_plugins; /* NULL */
+static gboolean _gst_plugin_inited;
+static gchar **_plugin_loading_whitelist; /* NULL */
+
+/* static variables for segfault handling of plugin loading */
+static char *_gst_plugin_fault_handler_filename = NULL;
+
+/* list of valid licenses.
+ * One of these must be specified or the plugin won't be loaded
+ * Contact gstreamer-devel@lists.sourceforge.net if your license should be
+ * added.
+ *
+ * GPL: http://www.gnu.org/copyleft/gpl.html
+ * LGPL: http://www.gnu.org/copyleft/lesser.html
+ * QPL: http://www.trolltech.com/licenses/qpl.html
+ * MPL: http://www.opensource.org/licenses/mozilla1.1.php
+ * MIT/X11: http://www.opensource.org/licenses/mit-license.php
+ * 3-clause BSD: http://www.opensource.org/licenses/bsd-license.php
+ */
+static const gchar *valid_licenses[] = {
+ "LGPL", /* GNU Lesser General Public License */
+ "GPL", /* GNU General Public License */
+ "QPL", /* Trolltech Qt Public License */
+ "GPL/QPL", /* Combi-license of GPL + QPL */
+ "MPL", /* MPL 1.1 license */
+ "BSD", /* 3-clause BSD license */
+ "MIT/X11", /* MIT/X11 license */
+ "Proprietary", /* Proprietary license */
+ GST_LICENSE_UNKNOWN, /* some other license */
+ NULL
+};
+
+static GstPlugin *gst_plugin_register_func (GstPlugin * plugin,
+ const GstPluginDesc * desc, gpointer user_data);
+static void gst_plugin_desc_copy (GstPluginDesc * dest,
+ const GstPluginDesc * src);
+
+static void gst_plugin_ext_dep_free (GstPluginDep * dep);
+
+G_DEFINE_TYPE (GstPlugin, gst_plugin, GST_TYPE_OBJECT);
+
+static void
+gst_plugin_init (GstPlugin * plugin)
+{
+ plugin->priv =
+ G_TYPE_INSTANCE_GET_PRIVATE (plugin, GST_TYPE_PLUGIN, GstPluginPrivate);
+}
+
+static void
+gst_plugin_finalize (GObject * object)
+{
+ GstPlugin *plugin = GST_PLUGIN_CAST (object);
+ GstRegistry *registry = gst_registry_get_default ();
+ GList *g;
+
+ GST_DEBUG ("finalizing plugin %" GST_PTR_FORMAT, plugin);
+ for (g = registry->plugins; g; g = g->next) {
+ if (g->data == (gpointer) plugin) {
+ g_warning ("removing plugin that is still in registry");
+ }
+ }
+ g_free (plugin->filename);
+ g_free (plugin->basename);
+
+ g_list_foreach (plugin->priv->deps, (GFunc) gst_plugin_ext_dep_free, NULL);
+ g_list_free (plugin->priv->deps);
+ plugin->priv->deps = NULL;
+
+ if (plugin->priv->cache_data) {
+ gst_structure_free (plugin->priv->cache_data);
+ }
+
+ G_OBJECT_CLASS (gst_plugin_parent_class)->finalize (object);
+}
+
+static void
+gst_plugin_class_init (GstPluginClass * klass)
+{
+ G_OBJECT_CLASS (klass)->finalize = gst_plugin_finalize;
+
+ g_type_class_add_private (klass, sizeof (GstPluginPrivate));
+}
+
+GQuark
+gst_plugin_error_quark (void)
+{
+ static GQuark quark = 0;
+
+ if (!quark)
+ quark = g_quark_from_static_string ("gst_plugin_error");
+ return quark;
+}
+
+/**
+ * gst_plugin_register_static:
+ * @major_version: the major version number of the GStreamer core that the
+ * plugin was compiled for, you can just use GST_VERSION_MAJOR here
+ * @minor_version: the minor version number of the GStreamer core that the
+ * plugin was compiled for, you can just use GST_VERSION_MINOR here
+ * @name: a unique name of the plugin (ideally prefixed with an application- or
+ * library-specific namespace prefix in order to avoid name conflicts in
+ * case a similar plugin with the same name ever gets added to GStreamer)
+ * @description: description of the plugin
+ * @init_func: (scope call): pointer to the init function of this plugin.
+ * @version: version string of the plugin
+ * @license: effective license of plugin. Must be one of the approved licenses
+ * (see #GstPluginDesc above) or the plugin will not be registered.
+ * @source: source module plugin belongs to
+ * @package: shipped package plugin belongs to
+ * @origin: URL to provider of plugin
+ *
+ * Registers a static plugin, ie. a plugin which is private to an application
+ * or library and contained within the application or library (as opposed to
+ * being shipped as a separate module file).
+ *
+ * You must make sure that GStreamer has been initialised (with gst_init() or
+ * via gst_init_get_option_group()) before calling this function.
+ *
+ * Returns: TRUE if the plugin was registered correctly, otherwise FALSE.
+ *
+ * Since: 0.10.16
+ */
+gboolean
+gst_plugin_register_static (gint major_version, gint minor_version,
+ const gchar * name, const gchar * description, GstPluginInitFunc init_func,
+ const gchar * version, const gchar * license, const gchar * source,
+ const gchar * package, const gchar * origin)
+{
+ GstPluginDesc desc = { major_version, minor_version, name, description,
+ init_func, version, license, source, package, origin, NULL,
+ };
+ GstPlugin *plugin;
+ gboolean res = FALSE;
+
+ g_return_val_if_fail (name != NULL, FALSE);
+ g_return_val_if_fail (description != NULL, FALSE);
+ g_return_val_if_fail (init_func != NULL, FALSE);
+ g_return_val_if_fail (version != NULL, FALSE);
+ g_return_val_if_fail (license != NULL, FALSE);
+ g_return_val_if_fail (source != NULL, FALSE);
+ g_return_val_if_fail (package != NULL, FALSE);
+ g_return_val_if_fail (origin != NULL, FALSE);
+
+ /* make sure gst_init() has been called */
+ g_return_val_if_fail (_gst_plugin_inited != FALSE, FALSE);
+
+ GST_LOG ("attempting to load static plugin \"%s\" now...", name);
+ plugin = g_object_newv (GST_TYPE_PLUGIN, 0, NULL);
+ if (gst_plugin_register_func (plugin, &desc, NULL) != NULL) {
+ GST_INFO ("registered static plugin \"%s\"", name);
+ res = gst_default_registry_add_plugin (plugin);
+ GST_INFO ("added static plugin \"%s\", result: %d", name, res);
+ }
+ return res;
+}
+
+/**
+ * gst_plugin_register_static_full:
+ * @major_version: the major version number of the GStreamer core that the
+ * plugin was compiled for, you can just use GST_VERSION_MAJOR here
+ * @minor_version: the minor version number of the GStreamer core that the
+ * plugin was compiled for, you can just use GST_VERSION_MINOR here
+ * @name: a unique name of the plugin (ideally prefixed with an application- or
+ * library-specific namespace prefix in order to avoid name conflicts in
+ * case a similar plugin with the same name ever gets added to GStreamer)
+ * @description: description of the plugin
+ * @init_full_func: (scope call): pointer to the init function with user data
+ * of this plugin.
+ * @version: version string of the plugin
+ * @license: effective license of plugin. Must be one of the approved licenses
+ * (see #GstPluginDesc above) or the plugin will not be registered.
+ * @source: source module plugin belongs to
+ * @package: shipped package plugin belongs to
+ * @origin: URL to provider of plugin
+ * @user_data: gpointer to user data
+ *
+ * Registers a static plugin, ie. a plugin which is private to an application
+ * or library and contained within the application or library (as opposed to
+ * being shipped as a separate module file) with a #GstPluginInitFullFunc
+ * which allows user data to be passed to the callback function (useful
+ * for bindings).
+ *
+ * You must make sure that GStreamer has been initialised (with gst_init() or
+ * via gst_init_get_option_group()) before calling this function.
+ *
+ * Returns: TRUE if the plugin was registered correctly, otherwise FALSE.
+ *
+ * Since: 0.10.24
+ *
+ */
+gboolean
+gst_plugin_register_static_full (gint major_version, gint minor_version,
+ const gchar * name, const gchar * description,
+ GstPluginInitFullFunc init_full_func, const gchar * version,
+ const gchar * license, const gchar * source, const gchar * package,
+ const gchar * origin, gpointer user_data)
+{
+ GstPluginDesc desc = { major_version, minor_version, name, description,
+ (GstPluginInitFunc) init_full_func, version, license, source, package,
+ origin, NULL,
+ };
+ GstPlugin *plugin;
+ gboolean res = FALSE;
+
+ g_return_val_if_fail (name != NULL, FALSE);
+ g_return_val_if_fail (description != NULL, FALSE);
+ g_return_val_if_fail (init_full_func != NULL, FALSE);
+ g_return_val_if_fail (version != NULL, FALSE);
+ g_return_val_if_fail (license != NULL, FALSE);
+ g_return_val_if_fail (source != NULL, FALSE);
+ g_return_val_if_fail (package != NULL, FALSE);
+ g_return_val_if_fail (origin != NULL, FALSE);
+
+ /* make sure gst_init() has been called */
+ g_return_val_if_fail (_gst_plugin_inited != FALSE, FALSE);
+
+ GST_LOG ("attempting to load static plugin \"%s\" now...", name);
+ plugin = g_object_newv (GST_TYPE_PLUGIN, 0, NULL);
+ if (gst_plugin_register_func (plugin, &desc, user_data) != NULL) {
+ GST_INFO ("registered static plugin \"%s\"", name);
+ res = gst_default_registry_add_plugin (plugin);
+ GST_INFO ("added static plugin \"%s\", result: %d", name, res);
+ }
+ return res;
+}
+
+void
+_priv_gst_plugin_initialize (void)
+{
+ const gchar *whitelist;
+ guint i;
+
+ _gst_plugin_inited = TRUE;
+
+ whitelist = g_getenv ("GST_PLUGIN_LOADING_WHITELIST");
+ if (whitelist != NULL && *whitelist != '\0') {
+ _plugin_loading_whitelist = g_strsplit (whitelist,
+ G_SEARCHPATH_SEPARATOR_S, -1);
+ for (i = 0; _plugin_loading_whitelist[i] != NULL; ++i) {
+ GST_INFO ("plugins whitelist entry: %s", _plugin_loading_whitelist[i]);
+ }
+ }
+
+ /* now register all static plugins */
+ GST_INFO ("registering %u static plugins", _num_static_plugins);
+ for (i = 0; i < _num_static_plugins; ++i) {
+ gst_plugin_register_static (_static_plugins[i].major_version,
+ _static_plugins[i].minor_version, _static_plugins[i].name,
+ _static_plugins[i].description, _static_plugins[i].plugin_init,
+ _static_plugins[i].version, _static_plugins[i].license,
+ _static_plugins[i].source, _static_plugins[i].package,
+ _static_plugins[i].origin);
+ }
+
+ if (_static_plugins) {
+ free (_static_plugins);
+ _static_plugins = NULL;
+ _num_static_plugins = 0;
+ }
+}
+
+/* Whitelist entry format:
+ *
+ * plugin1,plugin2@pathprefix or
+ * plugin1,plugin2@* or just
+ * plugin1,plugin2 or
+ * source-package@pathprefix or
+ * source-package@* or just
+ * source-package
+ *
+ * ie. the bit before the path will be checked against both the plugin
+ * name and the plugin's source package name, to keep the format simple.
+ */
+static gboolean
+gst_plugin_desc_matches_whitelist_entry (GstPluginDesc * desc,
+ const gchar * filename, const gchar * pattern)
+{
+ const gchar *sep;
+ gboolean ret = FALSE;
+ gchar *name;
+
+ GST_LOG ("Whitelist pattern '%s', plugin: %s of %s@%s", pattern, desc->name,
+ desc->source, GST_STR_NULL (filename));
+
+ /* do we have a path prefix? */
+ sep = strchr (pattern, '@');
+ if (sep != NULL && strcmp (sep, "@*") != 0 && strcmp (sep, "@") != 0) {
+ /* paths are not canonicalised or treated with realpath() here. This
+ * should be good enough for our use case, since we just use the paths
+ * autotools uses, and those will be constructed from the same prefix. */
+ if (filename != NULL && !g_str_has_prefix (filename, sep + 1))
+ return FALSE;
+
+ GST_LOG ("%s matches path prefix %s", GST_STR_NULL (filename), sep + 1);
+ }
+
+ if (sep != NULL) {
+ name = g_strndup (pattern, (gsize) (sep - pattern));
+ } else {
+ name = g_strdup (pattern);
+ }
+
+ g_strstrip (name);
+ if (!g_ascii_isalnum (*name)) {
+ GST_WARNING ("Invalid whitelist pattern: %s", pattern);
+ goto done;
+ }
+
+ /* now check plugin names / source package name */
+ if (strchr (name, ',') == NULL) {
+ /* only a single name: either a plugin name or the source package name */
+ ret = (strcmp (desc->source, name) == 0 || strcmp (desc->name, name) == 0);
+ } else {
+ gchar **n, **names;
+
+ /* multiple names: assume these are plugin names */
+ names = g_strsplit (name, ",", -1);
+ for (n = names; n != NULL && *n != NULL; ++n) {
+ g_strstrip (*n);
+ if (strcmp (desc->name, *n) == 0) {
+ ret = TRUE;
+ break;
+ }
+ }
+ g_strfreev (names);
+ }
+
+ GST_LOG ("plugin / source package name match: %d", ret);
+
+done:
+
+ g_free (name);
+ return ret;
+}
+
+gboolean
+priv_gst_plugin_desc_is_whitelisted (GstPluginDesc * desc,
+ const gchar * filename)
+{
+ gchar **entry;
+
+ if (_plugin_loading_whitelist == NULL)
+ return TRUE;
+
+ for (entry = _plugin_loading_whitelist; *entry != NULL; ++entry) {
+ if (gst_plugin_desc_matches_whitelist_entry (desc, filename, *entry)) {
+ GST_LOG ("Plugin %s is in whitelist", filename);
+ return TRUE;
+ }
+ }
+
+ GST_LOG ("Plugin %s (package %s, file %s) not in whitelist", desc->name,
+ desc->source, filename);
+ return FALSE;
+}
+
+gboolean
+priv_gst_plugin_loading_have_whitelist (void)
+{
+ return (_plugin_loading_whitelist != NULL);
+}
+
+guint32
+priv_gst_plugin_loading_get_whitelist_hash (void)
+{
+ guint32 hash = 0;
+
+ if (_plugin_loading_whitelist != NULL) {
+ gchar **w;
+
+ for (w = _plugin_loading_whitelist; *w != NULL; ++w)
+ hash = (hash << 1) ^ g_str_hash (*w);
+ }
+
+ return hash;
+}
+
+/* this function could be extended to check if the plugin license matches the
+ * applications license (would require the app to register its license somehow).
+ * We'll wait for someone who's interested in it to code it :)
+ */
+static gboolean
+gst_plugin_check_license (const gchar * license)
+{
+ const gchar **check_license = valid_licenses;
+
+ g_assert (check_license);
+
+ while (*check_license) {
+ if (strcmp (license, *check_license) == 0)
+ return TRUE;
+ check_license++;
+ }
+ return FALSE;
+}
+
+static gboolean
+gst_plugin_check_version (gint major, gint minor)
+{
+ /* return NULL if the major and minor version numbers are not compatible */
+ /* with ours. */
+ if (major != GST_VERSION_MAJOR || minor != GST_VERSION_MINOR)
+ return FALSE;
+
+ return TRUE;
+}
+
+static GstPlugin *
+gst_plugin_register_func (GstPlugin * plugin, const GstPluginDesc * desc,
+ gpointer user_data)
+{
+ if (!gst_plugin_check_version (desc->major_version, desc->minor_version)) {
+ if (GST_CAT_DEFAULT)
+ GST_WARNING ("plugin \"%s\" has incompatible version, not loading",
+ plugin->filename);
+ return NULL;
+ }
+
+ if (!desc->license || !desc->description || !desc->source ||
+ !desc->package || !desc->origin) {
+ if (GST_CAT_DEFAULT)
+ GST_WARNING ("plugin \"%s\" has incorrect GstPluginDesc, not loading",
+ plugin->filename);
+ return NULL;
+ }
+
+ if (!gst_plugin_check_license (desc->license)) {
+ if (GST_CAT_DEFAULT)
+ GST_WARNING ("plugin \"%s\" has invalid license \"%s\", not loading",
+ plugin->filename, desc->license);
+ return NULL;
+ }
+
+ if (GST_CAT_DEFAULT)
+ GST_LOG ("plugin \"%s\" looks good", GST_STR_NULL (plugin->filename));
+
+ gst_plugin_desc_copy (&plugin->desc, desc);
+
+ /* make resident so we're really sure it never gets unloaded again.
+ * Theoretically this is not needed, but practically it doesn't hurt.
+ * And we're rather safe than sorry. */
+ if (plugin->module)
+ g_module_make_resident (plugin->module);
+
+ if (user_data) {
+ if (!(((GstPluginInitFullFunc) (desc->plugin_init)) (plugin, user_data))) {
+ if (GST_CAT_DEFAULT)
+ GST_WARNING ("plugin \"%s\" failed to initialise", plugin->filename);
+ return NULL;
+ }
+ } else {
+ if (!((desc->plugin_init) (plugin))) {
+ if (GST_CAT_DEFAULT)
+ GST_WARNING ("plugin \"%s\" failed to initialise", plugin->filename);
+ return NULL;
+ }
+ }
+
+ if (GST_CAT_DEFAULT)
+ GST_LOG ("plugin \"%s\" initialised", GST_STR_NULL (plugin->filename));
+
+ return plugin;
+}
+
+#ifdef HAVE_SIGACTION
+static struct sigaction oldaction;
+static gboolean _gst_plugin_fault_handler_is_setup = FALSE;
+
+/*
+ * _gst_plugin_fault_handler_restore:
+ * segfault handler restorer
+ */
+static void
+_gst_plugin_fault_handler_restore (void)
+{
+ if (!_gst_plugin_fault_handler_is_setup)
+ return;
+
+ _gst_plugin_fault_handler_is_setup = FALSE;
+
+ sigaction (SIGSEGV, &oldaction, NULL);
+}
+
+/*
+ * _gst_plugin_fault_handler_sighandler:
+ * segfault handler implementation
+ */
+static void
+_gst_plugin_fault_handler_sighandler (int signum)
+{
+ /* We need to restore the fault handler or we'll keep getting it */
+ _gst_plugin_fault_handler_restore ();
+
+ switch (signum) {
+ case SIGSEGV:
+ g_print ("\nERROR: ");
+ g_print ("Caught a segmentation fault while loading plugin file:\n");
+ g_print ("%s\n\n", _gst_plugin_fault_handler_filename);
+ g_print ("Please either:\n");
+ g_print ("- remove it and restart.\n");
+ g_print
+ ("- run with --gst-disable-segtrap --gst-disable-registry-fork and debug.\n");
+ exit (-1);
+ break;
+ default:
+ g_print ("Caught unhandled signal on plugin loading\n");
+ break;
+ }
+}
+
+/*
+ * _gst_plugin_fault_handler_setup:
+ * sets up the segfault handler
+ */
+static void
+_gst_plugin_fault_handler_setup (void)
+{
+ struct sigaction action;
+
+ /* if asked to leave segfaults alone, just return */
+ if (!gst_segtrap_is_enabled ())
+ return;
+
+ if (_gst_plugin_fault_handler_is_setup)
+ return;
+
+ _gst_plugin_fault_handler_is_setup = TRUE;
+
+ memset (&action, 0, sizeof (action));
+ action.sa_handler = _gst_plugin_fault_handler_sighandler;
+
+ sigaction (SIGSEGV, &action, &oldaction);
+}
+#else /* !HAVE_SIGACTION */
+static void
+_gst_plugin_fault_handler_restore (void)
+{
+}
+
+static void
+_gst_plugin_fault_handler_setup (void)
+{
+}
+#endif /* HAVE_SIGACTION */
+
+/* g_time_val_from_iso8601() doesn't do quite what we want */
+static gboolean
+check_release_datetime (const gchar * date_time)
+{
+ guint64 val;
+
+ /* we require YYYY-MM-DD or YYYY-MM-DDTHH:MMZ format */
+ if (!g_ascii_isdigit (*date_time))
+ return FALSE;
+
+ val = g_ascii_strtoull (date_time, (gchar **) & date_time, 10);
+ if (val < 2000 || val > 2100 || *date_time != '-')
+ return FALSE;
+
+ val = g_ascii_strtoull (date_time + 1, (gchar **) & date_time, 10);
+ if (val == 0 || val > 12 || *date_time != '-')
+ return FALSE;
+
+ val = g_ascii_strtoull (date_time + 1, (gchar **) & date_time, 10);
+ if (val == 0 || val > 32)
+ return FALSE;
+
+ /* end of string or date/time separator + HH:MMZ */
+ if (*date_time == 'T' || *date_time == ' ') {
+ val = g_ascii_strtoull (date_time + 1, (gchar **) & date_time, 10);
+ if (val > 24 || *date_time != ':')
+ return FALSE;
+
+ val = g_ascii_strtoull (date_time + 1, (gchar **) & date_time, 10);
+ if (val > 59 || *date_time != 'Z')
+ return FALSE;
+
+ ++date_time;
+ }
+
+ return (*date_time == '\0');
+}
+
+static GStaticMutex gst_plugin_loading_mutex = G_STATIC_MUTEX_INIT;
+
+#define CHECK_PLUGIN_DESC_FIELD(desc,field,fn) \
+ if (G_UNLIKELY ((desc)->field == NULL)) { \
+ GST_ERROR ("GstPluginDesc for '%s' has no %s", fn, G_STRINGIFY (field)); \
+ }
+
+/**
+ * gst_plugin_load_file:
+ * @filename: the plugin filename to load
+ * @error: pointer to a NULL-valued GError
+ *
+ * Loads the given plugin and refs it. Caller needs to unref after use.
+ *
+ * Returns: (transfer full): a reference to the existing loaded GstPlugin, a
+ * reference to the newly-loaded GstPlugin, or NULL if an error occurred.
+ */
+GstPlugin *
+gst_plugin_load_file (const gchar * filename, GError ** error)
+{
+ GstPluginDesc *desc;
+ GstPlugin *plugin;
+ GModule *module;
+ gboolean ret;
+ gpointer ptr;
+ GStatBuf file_status;
+ GstRegistry *registry;
+ gboolean new_plugin = TRUE;
+ GModuleFlags flags;
+
+ g_return_val_if_fail (filename != NULL, NULL);
+
+ registry = gst_registry_get_default ();
+ g_static_mutex_lock (&gst_plugin_loading_mutex);
+
+ plugin = gst_registry_lookup (registry, filename);
+ if (plugin) {
+ if (plugin->module) {
+ /* already loaded */
+ g_static_mutex_unlock (&gst_plugin_loading_mutex);
+ return plugin;
+ } else {
+ /* load plugin and update fields */
+ new_plugin = FALSE;
+ }
+ }
+
+ GST_CAT_DEBUG (GST_CAT_PLUGIN_LOADING, "attempt to load plugin \"%s\"",
+ filename);
+
+ if (g_module_supported () == FALSE) {
+ GST_CAT_DEBUG (GST_CAT_PLUGIN_LOADING, "module loading not supported");
+ g_set_error (error,
+ GST_PLUGIN_ERROR,
+ GST_PLUGIN_ERROR_MODULE, "Dynamic loading not supported");
+ goto return_error;
+ }
+
+ if (g_stat (filename, &file_status)) {
+ GST_CAT_DEBUG (GST_CAT_PLUGIN_LOADING, "problem accessing file");
+ g_set_error (error,
+ GST_PLUGIN_ERROR,
+ GST_PLUGIN_ERROR_MODULE, "Problem accessing file %s: %s", filename,
+ g_strerror (errno));
+ goto return_error;
+ }
+
+ flags = G_MODULE_BIND_LOCAL;
+ /* libgstpython.so is the gst-python plugin loader. It needs to be loaded with
+ * G_MODULE_BIND_LAZY.
+ *
+ * Ideally there should be a generic way for plugins to specify that they
+ * need to be loaded with _LAZY.
+ * */
+ if (strstr (filename, "libgstpython"))
+ flags |= G_MODULE_BIND_LAZY;
+
+ module = g_module_open (filename, flags);
+ if (module == NULL) {
+ GST_CAT_WARNING (GST_CAT_PLUGIN_LOADING, "module_open failed: %s",
+ g_module_error ());
+ g_set_error (error,
+ GST_PLUGIN_ERROR, GST_PLUGIN_ERROR_MODULE, "Opening module failed: %s",
+ g_module_error ());
+ /* If we failed to open the shared object, then it's probably because a
+ * plugin is linked against the wrong libraries. Print out an easy-to-see
+ * message in this case. */
+ g_warning ("Failed to load plugin '%s': %s", filename, g_module_error ());
+ goto return_error;
+ }
+
+ ret = g_module_symbol (module, "gst_plugin_desc", &ptr);
+ if (!ret) {
+ GST_DEBUG ("Could not find plugin entry point in \"%s\"", filename);
+ g_set_error (error,
+ GST_PLUGIN_ERROR,
+ GST_PLUGIN_ERROR_MODULE,
+ "File \"%s\" is not a GStreamer plugin", filename);
+ g_module_close (module);
+ goto return_error;
+ }
+
+ desc = (GstPluginDesc *) ptr;
+
+ if (priv_gst_plugin_loading_have_whitelist () &&
+ !priv_gst_plugin_desc_is_whitelisted (desc, filename)) {
+ GST_INFO ("Whitelist specified and plugin not in whitelist, not loading: "
+ "name=%s, package=%s, file=%s", desc->name, desc->source, filename);
+ g_set_error (error, GST_PLUGIN_ERROR, GST_PLUGIN_ERROR_MODULE,
+ "Not loading plugin file \"%s\", not in whitelist", filename);
+ g_module_close (module);
+ goto return_error;
+ }
+
+ if (new_plugin) {
+ plugin = g_object_newv (GST_TYPE_PLUGIN, 0, NULL);
+ plugin->file_mtime = file_status.st_mtime;
+ plugin->file_size = file_status.st_size;
+ plugin->filename = g_strdup (filename);
+ plugin->basename = g_path_get_basename (filename);
+ }
+
+ plugin->module = module;
+ plugin->orig_desc = desc;
+
+ if (new_plugin) {
+ /* check plugin description: complain about bad values but accept them, to
+ * maintain backwards compatibility (FIXME: 0.11) */
+ CHECK_PLUGIN_DESC_FIELD (plugin->orig_desc, name, filename);
+ CHECK_PLUGIN_DESC_FIELD (plugin->orig_desc, description, filename);
+ CHECK_PLUGIN_DESC_FIELD (plugin->orig_desc, version, filename);
+ CHECK_PLUGIN_DESC_FIELD (plugin->orig_desc, license, filename);
+ CHECK_PLUGIN_DESC_FIELD (plugin->orig_desc, source, filename);
+ CHECK_PLUGIN_DESC_FIELD (plugin->orig_desc, package, filename);
+ CHECK_PLUGIN_DESC_FIELD (plugin->orig_desc, origin, filename);
+
+ if (plugin->orig_desc->release_datetime != NULL &&
+ !check_release_datetime (plugin->orig_desc->release_datetime)) {
+ GST_ERROR ("GstPluginDesc for '%s' has invalid datetime '%s'",
+ filename, plugin->orig_desc->release_datetime);
+ plugin->orig_desc->release_datetime = NULL;
+ }
+ }
+
+ GST_LOG ("Plugin %p for file \"%s\" prepared, calling entry function...",
+ plugin, filename);
+
+ /* this is where we load the actual .so, so let's trap SIGSEGV */
+ _gst_plugin_fault_handler_setup ();
+ _gst_plugin_fault_handler_filename = plugin->filename;
+
+ GST_LOG ("Plugin %p for file \"%s\" prepared, registering...",
+ plugin, filename);
+
+ if (!gst_plugin_register_func (plugin, plugin->orig_desc, NULL)) {
+ /* remove signal handler */
+ _gst_plugin_fault_handler_restore ();
+ GST_DEBUG ("gst_plugin_register_func failed for plugin \"%s\"", filename);
+ /* plugin == NULL */
+ g_set_error (error,
+ GST_PLUGIN_ERROR,
+ GST_PLUGIN_ERROR_MODULE,
+ "File \"%s\" appears to be a GStreamer plugin, but it failed to initialize",
+ filename);
+ goto return_error;
+ }
+
+ /* remove signal handler */
+ _gst_plugin_fault_handler_restore ();
+ _gst_plugin_fault_handler_filename = NULL;
+ GST_INFO ("plugin \"%s\" loaded", plugin->filename);
+
+ if (new_plugin) {
+ gst_object_ref (plugin);
+ gst_default_registry_add_plugin (plugin);
+ }
+
+ g_static_mutex_unlock (&gst_plugin_loading_mutex);
+ return plugin;
+
+return_error:
+ {
+ if (plugin)
+ gst_object_unref (plugin);
+ g_static_mutex_unlock (&gst_plugin_loading_mutex);
+ return NULL;
+ }
+}
+
+static void
+gst_plugin_desc_copy (GstPluginDesc * dest, const GstPluginDesc * src)
+{
+ dest->major_version = src->major_version;
+ dest->minor_version = src->minor_version;
+ dest->name = g_intern_string (src->name);
+ dest->description = g_intern_string (src->description);
+ dest->plugin_init = src->plugin_init;
+ dest->version = g_intern_string (src->version);
+ dest->license = g_intern_string (src->license);
+ dest->source = g_intern_string (src->source);
+ dest->package = g_intern_string (src->package);
+ dest->origin = g_intern_string (src->origin);
+ dest->release_datetime = g_intern_string (src->release_datetime);
+}
+
+/**
+ * gst_plugin_get_name:
+ * @plugin: plugin to get the name of
+ *
+ * Get the short name of the plugin
+ *
+ * Returns: the name of the plugin
+ */
+const gchar *
+gst_plugin_get_name (GstPlugin * plugin)
+{
+ g_return_val_if_fail (plugin != NULL, NULL);
+
+ return plugin->desc.name;
+}
+
+/**
+ * gst_plugin_get_description:
+ * @plugin: plugin to get long name of
+ *
+ * Get the long descriptive name of the plugin
+ *
+ * Returns: the long name of the plugin
+ */
+const gchar *
+gst_plugin_get_description (GstPlugin * plugin)
+{
+ g_return_val_if_fail (plugin != NULL, NULL);
+
+ return plugin->desc.description;
+}
+
+/**
+ * gst_plugin_get_filename:
+ * @plugin: plugin to get the filename of
+ *
+ * get the filename of the plugin
+ *
+ * Returns: the filename of the plugin
+ */
+const gchar *
+gst_plugin_get_filename (GstPlugin * plugin)
+{
+ g_return_val_if_fail (plugin != NULL, NULL);
+
+ return plugin->filename;
+}
+
+/**
+ * gst_plugin_get_version:
+ * @plugin: plugin to get the version of
+ *
+ * get the version of the plugin
+ *
+ * Returns: the version of the plugin
+ */
+const gchar *
+gst_plugin_get_version (GstPlugin * plugin)
+{
+ g_return_val_if_fail (plugin != NULL, NULL);
+
+ return plugin->desc.version;
+}
+
+/**
+ * gst_plugin_get_license:
+ * @plugin: plugin to get the license of
+ *
+ * get the license of the plugin
+ *
+ * Returns: the license of the plugin
+ */
+const gchar *
+gst_plugin_get_license (GstPlugin * plugin)
+{
+ g_return_val_if_fail (plugin != NULL, NULL);
+
+ return plugin->desc.license;
+}
+
+/**
+ * gst_plugin_get_source:
+ * @plugin: plugin to get the source of
+ *
+ * get the source module the plugin belongs to.
+ *
+ * Returns: the source of the plugin
+ */
+const gchar *
+gst_plugin_get_source (GstPlugin * plugin)
+{
+ g_return_val_if_fail (plugin != NULL, NULL);
+
+ return plugin->desc.source;
+}
+
+/**
+ * gst_plugin_get_package:
+ * @plugin: plugin to get the package of
+ *
+ * get the package the plugin belongs to.
+ *
+ * Returns: the package of the plugin
+ */
+const gchar *
+gst_plugin_get_package (GstPlugin * plugin)
+{
+ g_return_val_if_fail (plugin != NULL, NULL);
+
+ return plugin->desc.package;
+}
+
+/**
+ * gst_plugin_get_origin:
+ * @plugin: plugin to get the origin of
+ *
+ * get the URL where the plugin comes from
+ *
+ * Returns: the origin of the plugin
+ */
+const gchar *
+gst_plugin_get_origin (GstPlugin * plugin)
+{
+ g_return_val_if_fail (plugin != NULL, NULL);
+
+ return plugin->desc.origin;
+}
+
+/**
+ * gst_plugin_get_module:
+ * @plugin: plugin to query
+ *
+ * Gets the #GModule of the plugin. If the plugin isn't loaded yet, NULL is
+ * returned.
+ *
+ * Returns: (transfer none): module belonging to the plugin or NULL if the
+ * plugin isn't loaded yet.
+ */
+GModule *
+gst_plugin_get_module (GstPlugin * plugin)
+{
+ g_return_val_if_fail (plugin != NULL, NULL);
+
+ return plugin->module;
+}
+
+/**
+ * gst_plugin_is_loaded:
+ * @plugin: plugin to query
+ *
+ * queries if the plugin is loaded into memory
+ *
+ * Returns: TRUE is loaded, FALSE otherwise
+ */
+gboolean
+gst_plugin_is_loaded (GstPlugin * plugin)
+{
+ g_return_val_if_fail (plugin != NULL, FALSE);
+
+ return (plugin->module != NULL || plugin->filename == NULL);
+}
+
+/**
+ * gst_plugin_get_cache_data:
+ * @plugin: a plugin
+ *
+ * Gets the plugin specific data cache. If it is %NULL there is no cached data
+ * stored. This is the case when the registry is getting rebuilt.
+ *
+ * Returns: (transfer none): The cached data as a #GstStructure or %NULL.
+ *
+ * Since: 0.10.24
+ */
+const GstStructure *
+gst_plugin_get_cache_data (GstPlugin * plugin)
+{
+ g_return_val_if_fail (GST_IS_PLUGIN (plugin), NULL);
+
+ return plugin->priv->cache_data;
+}
+
+/**
+ * gst_plugin_set_cache_data:
+ * @plugin: a plugin
+ * @cache_data: (transfer full): a structure containing the data to cache
+ *
+ * Adds plugin specific data to cache. Passes the ownership of the structure to
+ * the @plugin.
+ *
+ * The cache is flushed every time the registry is rebuilt.
+ *
+ * Since: 0.10.24
+ */
+void
+gst_plugin_set_cache_data (GstPlugin * plugin, GstStructure * cache_data)
+{
+ g_return_if_fail (GST_IS_PLUGIN (plugin));
+ g_return_if_fail (GST_IS_STRUCTURE (cache_data));
+
+ if (plugin->priv->cache_data) {
+ gst_structure_free (plugin->priv->cache_data);
+ }
+ plugin->priv->cache_data = cache_data;
+}
+
+#if 0
+/**
+ * gst_plugin_feature_list:
+ * @plugin: plugin to query
+ * @filter: the filter to use
+ * @first: only return first match
+ * @user_data: user data passed to the filter function
+ *
+ * Runs a filter against all plugin features and returns a GList with
+ * the results. If the first flag is set, only the first match is
+ * returned (as a list with a single object).
+ *
+ * Returns: a GList of features, g_list_free after use.
+ */
+GList *
+gst_plugin_feature_filter (GstPlugin * plugin,
+ GstPluginFeatureFilter filter, gboolean first, gpointer user_data)
+{
+ GList *list;
+ GList *g;
+
+ list = gst_filter_run (plugin->features, (GstFilterFunc) filter, first,
+ user_data);
+ for (g = list; g; g = g->next) {
+ gst_object_ref (plugin);
+ }
+
+ return list;
+}
+
+typedef struct
+{
+ GstPluginFeatureFilter filter;
+ gboolean first;
+ gpointer user_data;
+ GList *result;
+}
+FeatureFilterData;
+
+static gboolean
+_feature_filter (GstPlugin * plugin, gpointer user_data)
+{
+ GList *result;
+ FeatureFilterData *data = (FeatureFilterData *) user_data;
+
+ result = gst_plugin_feature_filter (plugin, data->filter, data->first,
+ data->user_data);
+ if (result) {
+ data->result = g_list_concat (data->result, result);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ * gst_plugin_list_feature_filter:
+ * @list: a #GList of plugins to query
+ * @filter: the filter function to use
+ * @first: only return first match
+ * @user_data: user data passed to the filter function
+ *
+ * Runs a filter against all plugin features of the plugins in the given
+ * list and returns a GList with the results.
+ * If the first flag is set, only the first match is
+ * returned (as a list with a single object).
+ *
+ * Returns: a GList of features, g_list_free after use.
+ */
+GList *
+gst_plugin_list_feature_filter (GList * list,
+ GstPluginFeatureFilter filter, gboolean first, gpointer user_data)
+{
+ FeatureFilterData data;
+ GList *result;
+
+ data.filter = filter;
+ data.first = first;
+ data.user_data = user_data;
+ data.result = NULL;
+
+ result = gst_filter_run (list, (GstFilterFunc) _feature_filter, first, &data);
+ g_list_free (result);
+
+ return data.result;
+}
+#endif
+
+/**
+ * gst_plugin_name_filter:
+ * @plugin: the plugin to check
+ * @name: the name of the plugin
+ *
+ * A standard filter that returns TRUE when the plugin is of the
+ * given name.
+ *
+ * Returns: TRUE if the plugin is of the given name.
+ */
+gboolean
+gst_plugin_name_filter (GstPlugin * plugin, const gchar * name)
+{
+ return (plugin->desc.name && !strcmp (plugin->desc.name, name));
+}
+
+#if 0
+/**
+ * gst_plugin_find_feature:
+ * @plugin: plugin to get the feature from
+ * @name: The name of the feature to find
+ * @type: The type of the feature to find
+ *
+ * Find a feature of the given name and type in the given plugin.
+ *
+ * Returns: a GstPluginFeature or NULL if the feature was not found.
+ */
+GstPluginFeature *
+gst_plugin_find_feature (GstPlugin * plugin, const gchar * name, GType type)
+{
+ GList *walk;
+ GstPluginFeature *result = NULL;
+ GstTypeNameData data;
+
+ g_return_val_if_fail (name != NULL, NULL);
+
+ data.type = type;
+ data.name = name;
+
+ walk = gst_filter_run (plugin->features,
+ (GstFilterFunc) gst_plugin_feature_type_name_filter, TRUE, &data);
+
+ if (walk) {
+ result = GST_PLUGIN_FEATURE (walk->data);
+
+ gst_object_ref (result);
+ gst_plugin_feature_list_free (walk);
+ }
+
+ return result;
+}
+#endif
+
+#if 0
+static gboolean
+gst_plugin_feature_name_filter (GstPluginFeature * feature, const gchar * name)
+{
+ return !strcmp (name, GST_PLUGIN_FEATURE_NAME (feature));
+}
+#endif
+
+#if 0
+/**
+ * gst_plugin_find_feature_by_name:
+ * @plugin: plugin to get the feature from
+ * @name: The name of the feature to find
+ *
+ * Find a feature of the given name in the given plugin.
+ *
+ * Returns: a GstPluginFeature or NULL if the feature was not found.
+ */
+GstPluginFeature *
+gst_plugin_find_feature_by_name (GstPlugin * plugin, const gchar * name)
+{
+ GList *walk;
+ GstPluginFeature *result = NULL;
+
+ g_return_val_if_fail (name != NULL, NULL);
+
+ walk = gst_filter_run (plugin->features,
+ (GstFilterFunc) gst_plugin_feature_name_filter, TRUE, (void *) name);
+
+ if (walk) {
+ result = GST_PLUGIN_FEATURE (walk->data);
+
+ gst_object_ref (result);
+ gst_plugin_feature_list_free (walk);
+ }
+
+ return result;
+}
+#endif
+
+/**
+ * gst_plugin_load_by_name:
+ * @name: name of plugin to load
+ *
+ * Load the named plugin. Refs the plugin.
+ *
+ * Returns: (transfer full): a reference to a loaded plugin, or NULL on error.
+ */
+GstPlugin *
+gst_plugin_load_by_name (const gchar * name)
+{
+ GstPlugin *plugin, *newplugin;
+ GError *error = NULL;
+
+ GST_DEBUG ("looking up plugin %s in default registry", name);
+ plugin = gst_registry_find_plugin (gst_registry_get_default (), name);
+ if (plugin) {
+ GST_DEBUG ("loading plugin %s from file %s", name, plugin->filename);
+ newplugin = gst_plugin_load_file (plugin->filename, &error);
+ gst_object_unref (plugin);
+
+ if (!newplugin) {
+ GST_WARNING ("load_plugin error: %s", error->message);
+ g_error_free (error);
+ return NULL;
+ }
+ /* newplugin was reffed by load_file */
+ return newplugin;
+ }
+
+ GST_DEBUG ("Could not find plugin %s in registry", name);
+ return NULL;
+}
+
+/**
+ * gst_plugin_load:
+ * @plugin: (transfer none): plugin to load
+ *
+ * Loads @plugin. Note that the *return value* is the loaded plugin; @plugin is
+ * untouched. The normal use pattern of this function goes like this:
+ *
+ * <programlisting>
+ * GstPlugin *loaded_plugin;
+ * loaded_plugin = gst_plugin_load (plugin);
+ * // presumably, we're no longer interested in the potentially-unloaded plugin
+ * gst_object_unref (plugin);
+ * plugin = loaded_plugin;
+ * </programlisting>
+ *
+ * Returns: (transfer full): a reference to a loaded plugin, or NULL on error.
+ */
+GstPlugin *
+gst_plugin_load (GstPlugin * plugin)
+{
+ GError *error = NULL;
+ GstPlugin *newplugin;
+
+ if (gst_plugin_is_loaded (plugin)) {
+ return plugin;
+ }
+
+ if (!(newplugin = gst_plugin_load_file (plugin->filename, &error)))
+ goto load_error;
+
+ return newplugin;
+
+load_error:
+ {
+ GST_WARNING ("load_plugin error: %s", error->message);
+ g_error_free (error);
+ return NULL;
+ }
+}
+
+/**
+ * gst_plugin_list_free:
+ * @list: (transfer full) (element-type Gst.Plugin): list of #GstPlugin
+ *
+ * Unrefs each member of @list, then frees the list.
+ */
+void
+gst_plugin_list_free (GList * list)
+{
+ GList *g;
+
+ for (g = list; g; g = g->next) {
+ gst_object_unref (GST_PLUGIN_CAST (g->data));
+ }
+ g_list_free (list);
+}
+
+/* ===== plugin dependencies ===== */
+
+/* Scenarios:
+ * ENV + xyz where ENV can contain multiple values separated by SEPARATOR
+ * xyz may be "" (if ENV contains path to file rather than dir)
+ * ENV + *xyz same as above, but xyz acts as suffix filter
+ * ENV + xyz* same as above, but xyz acts as prefix filter (is this needed?)
+ * ENV + *xyz* same as above, but xyz acts as strstr filter (is this needed?)
+ *
+ * same as above, with additional paths hard-coded at compile-time:
+ * - only check paths + ... if ENV is not set or yields not paths
+ * - always check paths + ... in addition to ENV
+ *
+ * When user specifies set of environment variables, he/she may also use e.g.
+ * "HOME/.mystuff/plugins", and we'll expand the content of $HOME with the
+ * remainder
+ */
+
+/* we store in registry:
+ * sets of:
+ * {
+ * - environment variables (array of strings)
+ * - last hash of env variable contents (uint) (so we can avoid doing stats
+ * if one of the env vars has changed; premature optimisation galore)
+ * - hard-coded paths (array of strings)
+ * - xyz filename/suffix/prefix strings (array of strings)
+ * - flags (int)
+ * - last hash of file/dir stats (int)
+ * }
+ * (= struct GstPluginDep)
+ */
+
+static guint
+gst_plugin_ext_dep_get_env_vars_hash (GstPlugin * plugin, GstPluginDep * dep)
+{
+ gchar **e;
+ guint hash;
+
+ /* there's no deeper logic to what we do here; all we want to know (when
+ * checking if the plugin needs to be rescanned) is whether the content of
+ * one of the environment variables in the list is different from when it
+ * was last scanned */
+ hash = 0;
+ for (e = dep->env_vars; e != NULL && *e != NULL; ++e) {
+ const gchar *val;
+ gchar env_var[256];
+
+ /* order matters: "val",NULL needs to yield a different hash than
+ * NULL,"val", so do a shift here whether the var is set or not */
+ hash = hash << 5;
+
+ /* want environment variable at beginning of string */
+ if (!g_ascii_isalnum (**e)) {
+ GST_WARNING_OBJECT (plugin, "string prefix is not a valid environment "
+ "variable string: %s", *e);
+ continue;
+ }
+
+ /* user is allowed to specify e.g. "HOME/.pitivi/plugins" */
+ g_strlcpy (env_var, *e, sizeof (env_var));
+ g_strdelimit (env_var, "/\\", '\0');
+
+ if ((val = g_getenv (env_var)))
+ hash += g_str_hash (val);
+ }
+
+ return hash;
+}
+
+gboolean
+_priv_plugin_deps_env_vars_changed (GstPlugin * plugin)
+{
+ GList *l;
+
+ for (l = plugin->priv->deps; l != NULL; l = l->next) {
+ GstPluginDep *dep = l->data;
+
+ if (dep->env_hash != gst_plugin_ext_dep_get_env_vars_hash (plugin, dep))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static GList *
+gst_plugin_ext_dep_extract_env_vars_paths (GstPlugin * plugin,
+ GstPluginDep * dep)
+{
+ gchar **evars;
+ GList *paths = NULL;
+
+ for (evars = dep->env_vars; evars != NULL && *evars != NULL; ++evars) {
+ const gchar *e;
+ gchar **components;
+
+ /* want environment variable at beginning of string */
+ if (!g_ascii_isalnum (**evars)) {
+ GST_WARNING_OBJECT (plugin, "string prefix is not a valid environment "
+ "variable string: %s", *evars);
+ continue;
+ }
+
+ /* user is allowed to specify e.g. "HOME/.pitivi/plugins", which we want to
+ * split into the env_var name component and the path component */
+ components = g_strsplit_set (*evars, "/\\", 2);
+ g_assert (components != NULL);
+
+ e = g_getenv (components[0]);
+ GST_LOG_OBJECT (plugin, "expanding %s = '%s' (path suffix: %s)",
+ components[0], GST_STR_NULL (e), GST_STR_NULL (components[1]));
+
+ if (components[1] != NULL) {
+ g_strdelimit (components[1], "/\\", G_DIR_SEPARATOR);
+ }
+
+ if (e != NULL && *e != '\0') {
+ gchar **arr;
+ guint i;
+
+ arr = g_strsplit (e, G_SEARCHPATH_SEPARATOR_S, -1);
+
+ for (i = 0; arr != NULL && arr[i] != NULL; ++i) {
+ gchar *full_path;
+
+ if (!g_path_is_absolute (arr[i])) {
+ GST_INFO_OBJECT (plugin, "ignoring environment variable content '%s'"
+ ": either not an absolute path or not a path at all", arr[i]);
+ continue;
+ }
+
+ if (components[1] != NULL) {
+ full_path = g_build_filename (arr[i], components[1], NULL);
+ } else {
+ full_path = g_strdup (arr[i]);
+ }
+
+ if (!g_list_find_custom (paths, full_path, (GCompareFunc) strcmp)) {
+ GST_LOG_OBJECT (plugin, "path: '%s'", full_path);
+ paths = g_list_prepend (paths, full_path);
+ full_path = NULL;
+ } else {
+ GST_LOG_OBJECT (plugin, "path: '%s' (duplicate,ignoring)", full_path);
+ g_free (full_path);
+ }
+ }
+
+ g_strfreev (arr);
+ }
+
+ g_strfreev (components);
+ }
+
+ GST_LOG_OBJECT (plugin, "Extracted %d paths from environment",
+ g_list_length (paths));
+
+ return paths;
+}
+
+static guint
+gst_plugin_ext_dep_get_hash_from_stat_entry (GStatBuf * s)
+{
+ if (!(s->st_mode & (S_IFDIR | S_IFREG)))
+ return (guint) - 1;
+
+ /* completely random formula */
+ return ((s->st_size << 3) + (s->st_mtime << 5)) ^ s->st_ctime;
+}
+
+static gboolean
+gst_plugin_ext_dep_direntry_matches (GstPlugin * plugin, const gchar * entry,
+ const gchar ** filenames, GstPluginDependencyFlags flags)
+{
+ /* no filenames specified, match all entries for now (could probably
+ * optimise by just taking the dir stat hash or so) */
+ if (filenames == NULL || *filenames == NULL || **filenames == '\0')
+ return TRUE;
+
+ while (*filenames != NULL) {
+ /* suffix match? */
+ if (((flags & GST_PLUGIN_DEPENDENCY_FLAG_FILE_NAME_IS_SUFFIX)) &&
+ g_str_has_suffix (entry, *filenames)) {
+ return TRUE;
+ /* else it's an exact match that's needed */
+ } else if (strcmp (entry, *filenames) == 0) {
+ return TRUE;
+ }
+ GST_LOG ("%s does not match %s, flags=0x%04x", entry, *filenames, flags);
+ ++filenames;
+ }
+ return FALSE;
+}
+
+static guint
+gst_plugin_ext_dep_scan_dir_and_match_names (GstPlugin * plugin,
+ const gchar * path, const gchar ** filenames,
+ GstPluginDependencyFlags flags, int depth)
+{
+ const gchar *entry;
+ gboolean recurse_dirs;
+ GError *err = NULL;
+ GDir *dir;
+ guint hash = 0;
+
+ recurse_dirs = ! !(flags & GST_PLUGIN_DEPENDENCY_FLAG_RECURSE);
+
+ dir = g_dir_open (path, 0, &err);
+ if (dir == NULL) {
+ GST_DEBUG_OBJECT (plugin, "g_dir_open(%s) failed: %s", path, err->message);
+ g_error_free (err);
+ return (guint) - 1;
+ }
+
+ /* FIXME: we're assuming here that we always get the directory entries in
+ * the same order, and not in a random order */
+ while ((entry = g_dir_read_name (dir))) {
+ gboolean have_match;
+ GStatBuf s;
+ gchar *full_path;
+ guint fhash;
+
+ have_match =
+ gst_plugin_ext_dep_direntry_matches (plugin, entry, filenames, flags);
+
+ /* avoid the stat if possible */
+ if (!have_match && !recurse_dirs)
+ continue;
+
+ full_path = g_build_filename (path, entry, NULL);
+ if (g_stat (full_path, &s) < 0) {
+ fhash = (guint) - 1;
+ GST_LOG_OBJECT (plugin, "stat: %s (error: %s)", full_path,
+ g_strerror (errno));
+ } else if (have_match) {
+ fhash = gst_plugin_ext_dep_get_hash_from_stat_entry (&s);
+ GST_LOG_OBJECT (plugin, "stat: %s (result: %u)", full_path, fhash);
+ } else if ((s.st_mode & (S_IFDIR))) {
+ fhash = gst_plugin_ext_dep_scan_dir_and_match_names (plugin, full_path,
+ filenames, flags, depth + 1);
+ } else {
+ /* it's not a name match, we want to recurse, but it's not a directory */
+ g_free (full_path);
+ continue;
+ }
+
+ hash = (hash + fhash) << 1;
+ g_free (full_path);
+ }
+
+ g_dir_close (dir);
+ return hash;
+}
+
+static guint
+gst_plugin_ext_dep_scan_path_with_filenames (GstPlugin * plugin,
+ const gchar * path, const gchar ** filenames,
+ GstPluginDependencyFlags flags)
+{
+ const gchar *empty_filenames[] = { "", NULL };
+ gboolean recurse_into_dirs, partial_names;
+ guint i, hash = 0;
+
+ /* to avoid special-casing below (FIXME?) */
+ if (filenames == NULL || *filenames == NULL)
+ filenames = empty_filenames;
+
+ recurse_into_dirs = ! !(flags & GST_PLUGIN_DEPENDENCY_FLAG_RECURSE);
+ partial_names = ! !(flags & GST_PLUGIN_DEPENDENCY_FLAG_FILE_NAME_IS_SUFFIX);
+
+ /* if we can construct the exact paths to check with the data we have, just
+ * stat them one by one; this is more efficient than opening the directory
+ * and going through each entry to see if it matches one of our filenames. */
+ if (!recurse_into_dirs && !partial_names) {
+ for (i = 0; filenames[i] != NULL; ++i) {
+ GStatBuf s;
+ gchar *full_path;
+ guint fhash;
+
+ full_path = g_build_filename (path, filenames[i], NULL);
+ if (g_stat (full_path, &s) < 0) {
+ fhash = (guint) - 1;
+ GST_LOG_OBJECT (plugin, "stat: %s (error: %s)", full_path,
+ g_strerror (errno));
+ } else {
+ fhash = gst_plugin_ext_dep_get_hash_from_stat_entry (&s);
+ GST_LOG_OBJECT (plugin, "stat: %s (result: %08x)", full_path, fhash);
+ }
+ hash = (hash + fhash) << 1;
+ g_free (full_path);
+ }
+ } else {
+ hash = gst_plugin_ext_dep_scan_dir_and_match_names (plugin, path,
+ filenames, flags, 0);
+ }
+
+ return hash;
+}
+
+static guint
+gst_plugin_ext_dep_get_stat_hash (GstPlugin * plugin, GstPluginDep * dep)
+{
+ gboolean paths_are_default_only;
+ GList *scan_paths;
+ guint scan_hash = 0;
+
+ GST_LOG_OBJECT (plugin, "start");
+
+ paths_are_default_only =
+ dep->flags & GST_PLUGIN_DEPENDENCY_FLAG_PATHS_ARE_DEFAULT_ONLY;
+
+ scan_paths = gst_plugin_ext_dep_extract_env_vars_paths (plugin, dep);
+
+ if (scan_paths == NULL || !paths_are_default_only) {
+ gchar **paths;
+
+ for (paths = dep->paths; paths != NULL && *paths != NULL; ++paths) {
+ const gchar *path = *paths;
+
+ if (!g_list_find_custom (scan_paths, path, (GCompareFunc) strcmp)) {
+ GST_LOG_OBJECT (plugin, "path: '%s'", path);
+ scan_paths = g_list_prepend (scan_paths, g_strdup (path));
+ } else {
+ GST_LOG_OBJECT (plugin, "path: '%s' (duplicate, ignoring)", path);
+ }
+ }
+ }
+
+ /* not that the order really matters, but it makes debugging easier */
+ scan_paths = g_list_reverse (scan_paths);
+
+ while (scan_paths != NULL) {
+ const gchar *path = scan_paths->data;
+
+ scan_hash += gst_plugin_ext_dep_scan_path_with_filenames (plugin, path,
+ (const gchar **) dep->names, dep->flags);
+ scan_hash = scan_hash << 1;
+
+ g_free (scan_paths->data);
+ scan_paths = g_list_delete_link (scan_paths, scan_paths);
+ }
+
+ GST_LOG_OBJECT (plugin, "done, scan_hash: %08x", scan_hash);
+ return scan_hash;
+}
+
+gboolean
+_priv_plugin_deps_files_changed (GstPlugin * plugin)
+{
+ GList *l;
+
+ for (l = plugin->priv->deps; l != NULL; l = l->next) {
+ GstPluginDep *dep = l->data;
+
+ if (dep->stat_hash != gst_plugin_ext_dep_get_stat_hash (plugin, dep))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+gst_plugin_ext_dep_free (GstPluginDep * dep)
+{
+ g_strfreev (dep->env_vars);
+ g_strfreev (dep->paths);
+ g_strfreev (dep->names);
+ g_slice_free (GstPluginDep, dep);
+}
+
+static gboolean
+gst_plugin_ext_dep_strv_equal (gchar ** arr1, gchar ** arr2)
+{
+ if (arr1 == arr2)
+ return TRUE;
+ if (arr1 == NULL || arr2 == NULL)
+ return FALSE;
+ for (; *arr1 != NULL && *arr2 != NULL; ++arr1, ++arr2) {
+ if (strcmp (*arr1, *arr2) != 0)
+ return FALSE;
+ }
+ return (*arr1 == *arr2);
+}
+
+static gboolean
+gst_plugin_ext_dep_equals (GstPluginDep * dep, const gchar ** env_vars,
+ const gchar ** paths, const gchar ** names, GstPluginDependencyFlags flags)
+{
+ if (dep->flags != flags)
+ return FALSE;
+
+ return gst_plugin_ext_dep_strv_equal (dep->env_vars, (gchar **) env_vars) &&
+ gst_plugin_ext_dep_strv_equal (dep->paths, (gchar **) paths) &&
+ gst_plugin_ext_dep_strv_equal (dep->names, (gchar **) names);
+}
+
+/**
+ * gst_plugin_add_dependency:
+ * @plugin: a #GstPlugin
+ * @env_vars: NULL-terminated array of environment variables affecting the
+ * feature set of the plugin (e.g. an environment variable containing
+ * paths where to look for additional modules/plugins of a library),
+ * or NULL. Environment variable names may be followed by a path component
+ * which will be added to the content of the environment variable, e.g.
+ * "HOME/.mystuff/plugins".
+ * @paths: NULL-terminated array of directories/paths where dependent files
+ * may be.
+ * @names: NULL-terminated array of file names (or file name suffixes,
+ * depending on @flags) to be used in combination with the paths from
+ * @paths and/or the paths extracted from the environment variables in
+ * @env_vars, or NULL.
+ * @flags: optional flags, or #GST_PLUGIN_DEPENDENCY_FLAG_NONE
+ *
+ * Make GStreamer aware of external dependencies which affect the feature
+ * set of this plugin (ie. the elements or typefinders associated with it).
+ *
+ * GStreamer will re-inspect plugins with external dependencies whenever any
+ * of the external dependencies change. This is useful for plugins which wrap
+ * other plugin systems, e.g. a plugin which wraps a plugin-based visualisation
+ * library and makes visualisations available as GStreamer elements, or a
+ * codec loader which exposes elements and/or caps dependent on what external
+ * codec libraries are currently installed.
+ *
+ * Since: 0.10.22
+ */
+void
+gst_plugin_add_dependency (GstPlugin * plugin, const gchar ** env_vars,
+ const gchar ** paths, const gchar ** names, GstPluginDependencyFlags flags)
+{
+ GstPluginDep *dep;
+ GList *l;
+
+ g_return_if_fail (GST_IS_PLUGIN (plugin));
+
+ if ((env_vars == NULL || env_vars[0] == NULL) &&
+ (paths == NULL || paths[0] == NULL)) {
+ GST_DEBUG_OBJECT (plugin,
+ "plugin registered empty dependency set. Ignoring");
+ return;
+ }
+
+ for (l = plugin->priv->deps; l != NULL; l = l->next) {
+ if (gst_plugin_ext_dep_equals (l->data, env_vars, paths, names, flags)) {
+ GST_LOG_OBJECT (plugin, "dependency already registered");
+ return;
+ }
+ }
+
+ dep = g_slice_new (GstPluginDep);
+
+ dep->env_vars = g_strdupv ((gchar **) env_vars);
+ dep->paths = g_strdupv ((gchar **) paths);
+ dep->names = g_strdupv ((gchar **) names);
+ dep->flags = flags;
+
+ dep->env_hash = gst_plugin_ext_dep_get_env_vars_hash (plugin, dep);
+ dep->stat_hash = gst_plugin_ext_dep_get_stat_hash (plugin, dep);
+
+ plugin->priv->deps = g_list_append (plugin->priv->deps, dep);
+
+ GST_DEBUG_OBJECT (plugin, "added dependency:");
+ for (; env_vars != NULL && *env_vars != NULL; ++env_vars)
+ GST_DEBUG_OBJECT (plugin, " evar: %s", *env_vars);
+ for (; paths != NULL && *paths != NULL; ++paths)
+ GST_DEBUG_OBJECT (plugin, " path: %s", *paths);
+ for (; names != NULL && *names != NULL; ++names)
+ GST_DEBUG_OBJECT (plugin, " name: %s", *names);
+}
+
+/**
+ * gst_plugin_add_dependency_simple:
+ * @plugin: the #GstPlugin
+ * @env_vars: one or more environment variables (separated by ':', ';' or ','),
+ * or NULL. Environment variable names may be followed by a path component
+ * which will be added to the content of the environment variable, e.g.
+ * "HOME/.mystuff/plugins:MYSTUFF_PLUGINS_PATH"
+ * @paths: one ore more directory paths (separated by ':' or ';' or ','),
+ * or NULL. Example: "/usr/lib/mystuff/plugins"
+ * @names: one or more file names or file name suffixes (separated by commas),
+ * or NULL
+ * @flags: optional flags, or #GST_PLUGIN_DEPENDENCY_FLAG_NONE
+ *
+ * Make GStreamer aware of external dependencies which affect the feature
+ * set of this plugin (ie. the elements or typefinders associated with it).
+ *
+ * GStreamer will re-inspect plugins with external dependencies whenever any
+ * of the external dependencies change. This is useful for plugins which wrap
+ * other plugin systems, e.g. a plugin which wraps a plugin-based visualisation
+ * library and makes visualisations available as GStreamer elements, or a
+ * codec loader which exposes elements and/or caps dependent on what external
+ * codec libraries are currently installed.
+ *
+ * Convenience wrapper function for gst_plugin_add_dependency() which
+ * takes simple strings as arguments instead of string arrays, with multiple
+ * arguments separated by predefined delimiters (see above).
+ *
+ * Since: 0.10.22
+ */
+void
+gst_plugin_add_dependency_simple (GstPlugin * plugin,
+ const gchar * env_vars, const gchar * paths, const gchar * names,
+ GstPluginDependencyFlags flags)
+{
+ gchar **a_evars = NULL;
+ gchar **a_paths = NULL;
+ gchar **a_names = NULL;
+
+ if (env_vars)
+ a_evars = g_strsplit_set (env_vars, ":;,", -1);
+ if (paths)
+ a_paths = g_strsplit_set (paths, ":;,", -1);
+ if (names)
+ a_names = g_strsplit_set (names, ",", -1);
+
+ gst_plugin_add_dependency (plugin, (const gchar **) a_evars,
+ (const gchar **) a_paths, (const gchar **) a_names, flags);
+
+ if (a_evars)
+ g_strfreev (a_evars);
+ if (a_paths)
+ g_strfreev (a_paths);
+ if (a_names)
+ g_strfreev (a_names);
+}
diff --git a/gst/gstplugin.h b/gst/gstplugin.h
new file mode 100644
index 0000000..32bec0d
--- /dev/null
+++ b/gst/gstplugin.h
@@ -0,0 +1,360 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2000 Wim Taymans <wtay@chello.be>
+ *
+ * gstplugin.h: Header for plugin subsystem
+ *
+ * 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_PLUGIN_H__
+#define __GST_PLUGIN_H__
+
+#include <gst/gstconfig.h>
+
+#include <time.h> /* time_t */
+#include <sys/types.h> /* off_t */
+#include <sys/stat.h> /* off_t */
+#include <gmodule.h>
+#include <gst/gstobject.h>
+#include <gst/gstmacros.h>
+#include <gst/gststructure.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GstPlugin GstPlugin;
+typedef struct _GstPluginClass GstPluginClass;
+typedef struct _GstPluginPrivate GstPluginPrivate;
+typedef struct _GstPluginDesc GstPluginDesc;
+
+/**
+ * gst_plugin_error_quark:
+ *
+ * Get the error quark.
+ *
+ * Returns: The error quark used in GError messages
+ */
+GQuark gst_plugin_error_quark (void);
+/**
+ * GST_PLUGIN_ERROR:
+ *
+ * The error message category quark
+ */
+#define GST_PLUGIN_ERROR gst_plugin_error_quark ()
+
+/**
+ * GstPluginError:
+ * @GST_PLUGIN_ERROR_MODULE: The plugin could not be loaded
+ * @GST_PLUGIN_ERROR_DEPENDENCIES: The plugin has unresolved dependencies
+ * @GST_PLUGIN_ERROR_NAME_MISMATCH: The plugin has already be loaded from a different file
+ *
+ * The plugin loading errors
+ */
+typedef enum
+{
+ GST_PLUGIN_ERROR_MODULE,
+ GST_PLUGIN_ERROR_DEPENDENCIES,
+ GST_PLUGIN_ERROR_NAME_MISMATCH
+} GstPluginError;
+
+/**
+ * GstPluginFlags:
+ * @GST_PLUGIN_FLAG_CACHED: Temporarily loaded plugins
+ * @GST_PLUGIN_FLAG_BLACKLISTED: The plugin won't be scanned (again)
+ *
+ * The plugin loading state
+ */
+typedef enum
+{
+ GST_PLUGIN_FLAG_CACHED = (1<<0),
+ GST_PLUGIN_FLAG_BLACKLISTED = (1<<1)
+} GstPluginFlags;
+
+/**
+ * GstPluginDependencyFlags:
+ * @GST_PLUGIN_DEPENDENCY_FLAG_NONE : no special flags
+ * @GST_PLUGIN_DEPENDENCY_FLAG_RECURSE : recurse into subdirectories
+ * @GST_PLUGIN_DEPENDENCY_FLAG_PATHS_ARE_DEFAULT_ONLY : use paths
+ * argument only if none of the environment variables is set
+ * @GST_PLUGIN_DEPENDENCY_FLAG_FILE_NAME_IS_SUFFIX : interpret
+ * filename argument as filter suffix and check all matching files in
+ * the directory
+ *
+ * Flags used in connection with gst_plugin_add_dependency().
+ *
+ * Since: 0.10.22
+ */
+typedef enum {
+ GST_PLUGIN_DEPENDENCY_FLAG_NONE = 0,
+ GST_PLUGIN_DEPENDENCY_FLAG_RECURSE = (1 << 0),
+ GST_PLUGIN_DEPENDENCY_FLAG_PATHS_ARE_DEFAULT_ONLY = (1 << 1),
+ GST_PLUGIN_DEPENDENCY_FLAG_FILE_NAME_IS_SUFFIX = (1 << 2)
+} GstPluginDependencyFlags;
+
+/**
+ * GstPluginInitFunc:
+ * @plugin: The plugin object
+ *
+ * A plugin should provide a pointer to a function of this type in the
+ * plugin_desc struct.
+ * This function will be called by the loader at startup. One would then
+ * register each #GstPluginFeature.
+ *
+ * Returns: %TRUE if plugin initialised successfully
+ */
+/* FIXME 0.11: Make return void */
+typedef gboolean (*GstPluginInitFunc) (GstPlugin *plugin);
+
+/**
+ * GstPluginInitFullFunc:
+ * @plugin: The plugin object
+ * @user_data: extra data
+ *
+ * A plugin should provide a pointer to a function of either #GstPluginInitFunc
+ * or this type in the plugin_desc struct.
+ * The function will be called by the loader at startup. One would then
+ * register each #GstPluginFeature. This version allows
+ * user data to be passed to init function (useful for bindings).
+ *
+ * Returns: %TRUE if plugin initialised successfully
+ *
+ * Since: 0.10.24
+ *
+ */
+/* FIXME 0.11: Merge with GstPluginInitFunc */
+typedef gboolean (*GstPluginInitFullFunc) (GstPlugin *plugin, gpointer user_data);
+
+/**
+ * GstPluginDesc:
+ * @major_version: the major version number of core that plugin was compiled for
+ * @minor_version: the minor version number of core that plugin was compiled for
+ * @name: a unique name of the plugin
+ * @description: description of plugin
+ * @plugin_init: pointer to the init function of this plugin.
+ * @version: version of the plugin
+ * @license: effective license of plugin
+ * @source: source module plugin belongs to
+ * @package: shipped package plugin belongs to
+ * @origin: URL to provider of plugin
+ * @release_datetime: date time string in ISO 8601 format (or rather, a
+ * subset thereof), or NULL. Allowed are the following formats:
+ * "YYYY-MM-DD" and "YYY-MM-DDTHH:MMZ" (with 'T' a separator and 'Z'
+ * indicating UTC/Zulu time). This field should be set via the
+ * GST_PACKAGE_RELEASE_DATETIME preprocessor macro (Since: 0.10.31)
+ *
+ * A plugin should export a variable of this type called plugin_desc. The plugin
+ * loader will use the data provided there to initialize the plugin.
+ *
+ * The @licence parameter must be one of: LGPL, GPL, QPL, GPL/QPL, MPL,
+ * BSD, MIT/X11, Proprietary, unknown.
+ */
+struct _GstPluginDesc {
+ gint major_version;
+ gint minor_version;
+ const gchar *name;
+ const gchar *description;
+ GstPluginInitFunc plugin_init;
+ const gchar *version;
+ const gchar *license;
+ const gchar *source;
+ const gchar *package;
+ const gchar *origin;
+ const gchar *release_datetime;
+ /*< private >*/
+ gpointer _gst_reserved[GST_PADDING];
+};
+
+
+#define GST_TYPE_PLUGIN (gst_plugin_get_type())
+#define GST_IS_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_PLUGIN))
+#define GST_IS_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_PLUGIN))
+#define GST_PLUGIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_PLUGIN, GstPluginClass))
+#define GST_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_PLUGIN, GstPlugin))
+#define GST_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_PLUGIN, GstPluginClass))
+#define GST_PLUGIN_CAST(obj) ((GstPlugin*)(obj))
+
+/**
+ * GstPlugin:
+ *
+ * The plugin object
+ */
+struct _GstPlugin {
+ GstObject object;
+
+ /*< private >*/
+ GstPluginDesc desc;
+
+ GstPluginDesc *orig_desc;
+
+ unsigned int flags;
+
+ gchar * filename;
+ gchar * basename; /* base name (non-dir part) of plugin path */
+
+ GModule * module; /* contains the module if plugin is loaded */
+
+ off_t file_size;
+ time_t file_mtime;
+ gboolean registered; /* TRUE when the registry has seen a filename
+ * that matches the plugin's basename */
+
+ GstPluginPrivate *priv;
+ gpointer _gst_reserved[GST_PADDING];
+};
+
+struct _GstPluginClass {
+ GstObjectClass object_class;
+
+ /*< private >*/
+ gpointer _gst_reserved[GST_PADDING];
+};
+
+#ifdef GST_PACKAGE_RELEASE_DATETIME
+#define __GST_PACKAGE_RELEASE_DATETIME GST_PACKAGE_RELEASE_DATETIME
+#else
+#define __GST_PACKAGE_RELEASE_DATETIME NULL
+#endif
+
+/**
+ * GST_PLUGIN_DEFINE:
+ * @major: major version number of the gstreamer-core that plugin was compiled for
+ * @minor: minor version number of the gstreamer-core that plugin was compiled for
+ * @name: short, but unique name of the plugin
+ * @description: information about the purpose of the plugin
+ * @init: function pointer to the plugin_init method with the signature of <code>static gboolean plugin_init (GstPlugin * plugin)</code>.
+ * @version: full version string (e.g. VERSION from config.h)
+ * @license: under which licence the package has been released, e.g. GPL, LGPL.
+ * @package: the package-name (e.g. PACKAGE_NAME from config.h)
+ * @origin: a description from where the package comes from (e.g. the homepage URL)
+ *
+ * This macro needs to be used to define the entry point and meta data of a
+ * plugin. One would use this macro to export a plugin, so that it can be used
+ * by other applications.
+ *
+ * The macro uses a define named PACKAGE for the #GstPluginDesc,source field.
+ * When using autoconf, this is usually set automatically via the AC_INIT
+ * macro, and set in config.h. If you are not using autoconf, you will need to
+ * define PACKAGE yourself and set it to a short mnemonic string identifying
+ * your application/package, e.g. 'someapp' or 'my-plugins-foo.
+ *
+ * If defined, the GST_PACKAGE_RELEASE_DATETIME will also be used for the
+ * #GstPluginDesc,release_datetime field.
+ */
+#define GST_PLUGIN_DEFINE(major,minor,name,description,init,version,license,package,origin) \
+G_BEGIN_DECLS \
+GST_PLUGIN_EXPORT GstPluginDesc gst_plugin_desc = { \
+ major, \
+ minor, \
+ name, \
+ (gchar *) description, \
+ init, \
+ version, \
+ license, \
+ PACKAGE, \
+ package, \
+ origin, \
+ __GST_PACKAGE_RELEASE_DATETIME, \
+ GST_PADDING_INIT \
+}; \
+G_END_DECLS
+
+/**
+ * GST_LICENSE_UNKNOWN:
+ *
+ * To be used in GST_PLUGIN_DEFINE or GST_PLUGIN_DEFINE_STATIC if usure about
+ * the licence.
+ */
+#define GST_LICENSE_UNKNOWN "unknown"
+
+
+/* function for filters */
+/**
+ * GstPluginFilter:
+ * @plugin: the plugin to check
+ * @user_data: the user_data that has been passed on e.g. gst_registry_plugin_filter()
+ *
+ * A function that can be used with e.g. gst_registry_plugin_filter()
+ * to get a list of plugins that match certain criteria.
+ *
+ * Returns: TRUE for a positive match, FALSE otherwise
+ */
+typedef gboolean (*GstPluginFilter) (GstPlugin *plugin,
+ gpointer user_data);
+
+GType gst_plugin_get_type (void);
+
+gboolean gst_plugin_register_static (gint major_version,
+ gint minor_version,
+ const gchar *name,
+ const gchar *description,
+ GstPluginInitFunc init_func,
+ const gchar *version,
+ const gchar *license,
+ const gchar *source,
+ const gchar *package,
+ const gchar *origin);
+
+gboolean gst_plugin_register_static_full (gint major_version,
+ gint minor_version,
+ const gchar *name,
+ const gchar *description,
+ GstPluginInitFullFunc init_full_func,
+ const gchar *version,
+ const gchar *license,
+ const gchar *source,
+ const gchar *package,
+ const gchar *origin,
+ gpointer user_data);
+
+const gchar* gst_plugin_get_name (GstPlugin *plugin);
+const gchar* gst_plugin_get_description (GstPlugin *plugin);
+const gchar* gst_plugin_get_filename (GstPlugin *plugin);
+const gchar* gst_plugin_get_version (GstPlugin *plugin);
+const gchar* gst_plugin_get_license (GstPlugin *plugin);
+const gchar* gst_plugin_get_source (GstPlugin *plugin);
+const gchar* gst_plugin_get_package (GstPlugin *plugin);
+const gchar* gst_plugin_get_origin (GstPlugin *plugin);
+const GstStructure* gst_plugin_get_cache_data (GstPlugin * plugin);
+void gst_plugin_set_cache_data (GstPlugin * plugin, GstStructure *cache_data);
+
+GModule * gst_plugin_get_module (GstPlugin *plugin);
+gboolean gst_plugin_is_loaded (GstPlugin *plugin);
+
+gboolean gst_plugin_name_filter (GstPlugin *plugin, const gchar *name);
+
+GstPlugin * gst_plugin_load_file (const gchar *filename, GError** error);
+
+GstPlugin * gst_plugin_load (GstPlugin *plugin);
+GstPlugin * gst_plugin_load_by_name (const gchar *name);
+
+void gst_plugin_add_dependency (GstPlugin * plugin,
+ const gchar ** env_vars,
+ const gchar ** paths,
+ const gchar ** names,
+ GstPluginDependencyFlags flags);
+
+void gst_plugin_add_dependency_simple (GstPlugin * plugin,
+ const gchar * env_vars,
+ const gchar * paths,
+ const gchar * names,
+ GstPluginDependencyFlags flags);
+
+void gst_plugin_list_free (GList *list);
+
+G_END_DECLS
+
+#endif /* __GST_PLUGIN_H__ */
diff --git a/gst/gstpluginfeature.c b/gst/gstpluginfeature.c
new file mode 100644
index 0000000..79925a6
--- /dev/null
+++ b/gst/gstpluginfeature.c
@@ -0,0 +1,387 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2000 Wim Taymans <wtay@chello.be>
+ *
+ * gstpluginfeature.c: Abstract base class for all plugin features
+ *
+ * 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:gstpluginfeature
+ * @short_description: Base class for contents of a GstPlugin
+ * @see_also: #GstPlugin
+ *
+ * This is a base class for anything that can be added to a #GstPlugin.
+ */
+
+#include "gst_private.h"
+
+#include "gstpluginfeature.h"
+#include "gstplugin.h"
+#include "gstregistry.h"
+#include "gstinfo.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#define GST_CAT_DEFAULT GST_CAT_PLUGIN_LOADING
+
+static void gst_plugin_feature_finalize (GObject * object);
+
+/* static guint gst_plugin_feature_signals[LAST_SIGNAL] = { 0 }; */
+
+G_DEFINE_ABSTRACT_TYPE (GstPluginFeature, gst_plugin_feature, GST_TYPE_OBJECT);
+
+static void
+gst_plugin_feature_class_init (GstPluginFeatureClass * klass)
+{
+ G_OBJECT_CLASS (klass)->finalize = gst_plugin_feature_finalize;
+}
+
+static void
+gst_plugin_feature_init (GstPluginFeature * feature)
+{
+ /* do nothing, needed because of G_DEFINE_TYPE */
+}
+
+static void
+gst_plugin_feature_finalize (GObject * object)
+{
+ GstPluginFeature *feature = GST_PLUGIN_FEATURE_CAST (object);
+
+ GST_DEBUG ("finalizing feature %p: '%s'", feature, GST_OBJECT_NAME (feature));
+
+ if (feature->plugin != NULL) {
+ g_object_remove_weak_pointer ((GObject *) feature->plugin,
+ (gpointer *) & feature->plugin);
+ }
+
+ G_OBJECT_CLASS (gst_plugin_feature_parent_class)->finalize (object);
+}
+
+/**
+ * gst_plugin_feature_load:
+ * @feature: (transfer none): the plugin feature to check
+ *
+ * Loads the plugin containing @feature if it's not already loaded. @feature is
+ * unaffected; use the return value instead.
+ *
+ * Normally this function is used like this:
+ * |[
+ * GstPluginFeature *loaded_feature;
+ *
+ * loaded_feature = gst_plugin_feature_load (feature);
+ * // presumably, we're no longer interested in the potentially-unloaded feature
+ * gst_object_unref (feature);
+ * feature = loaded_feature;
+ * ]|
+ *
+ * Returns: (transfer full): a reference to the loaded feature, or NULL on error
+ */
+GstPluginFeature *
+gst_plugin_feature_load (GstPluginFeature * feature)
+{
+ GstPlugin *plugin;
+ GstPluginFeature *real_feature;
+
+ g_return_val_if_fail (feature != NULL, FALSE);
+ g_return_val_if_fail (GST_IS_PLUGIN_FEATURE (feature), FALSE);
+
+ GST_DEBUG ("loading plugin for feature %p; '%s'", feature,
+ GST_OBJECT_NAME (feature));
+ if (feature->loaded)
+ return gst_object_ref (feature);
+
+ GST_DEBUG ("loading plugin %s", feature->plugin_name);
+ plugin = gst_plugin_load_by_name (feature->plugin_name);
+ if (!plugin)
+ goto load_failed;
+
+ GST_DEBUG ("loaded plugin %s", feature->plugin_name);
+ gst_object_unref (plugin);
+
+ real_feature =
+ gst_registry_lookup_feature (gst_registry_get_default (),
+ GST_OBJECT_NAME (feature));
+
+ if (real_feature == NULL)
+ goto disappeared;
+ else if (!real_feature->loaded)
+ goto not_found;
+
+ return real_feature;
+
+ /* ERRORS */
+load_failed:
+ {
+ GST_WARNING ("Failed to load plugin containing feature '%s'.",
+ GST_OBJECT_NAME (feature));
+ return NULL;
+ }
+disappeared:
+ {
+ GST_INFO
+ ("Loaded plugin containing feature '%s', but feature disappeared.",
+ GST_OBJECT_NAME (feature));
+ return NULL;
+ }
+not_found:
+ {
+ GST_INFO ("Tried to load plugin containing feature '%s', but feature was "
+ "not found.", GST_OBJECT_NAME (real_feature));
+ return NULL;
+ }
+}
+
+/**
+ * gst_plugin_feature_type_name_filter:
+ * @feature: the #GstPluginFeature
+ * @data: (in): the type and name to check against
+ *
+ * Compares type and name of plugin feature. Can be used with gst_filter_run().
+ *
+ * Returns: TRUE if equal.
+ */
+gboolean
+gst_plugin_feature_type_name_filter (GstPluginFeature * feature,
+ GstTypeNameData * data)
+{
+ g_return_val_if_fail (GST_IS_PLUGIN_FEATURE (feature), FALSE);
+
+ return ((data->type == 0 || data->type == G_OBJECT_TYPE (feature)) &&
+ (data->name == NULL || !strcmp (data->name, GST_OBJECT_NAME (feature))));
+}
+
+/**
+ * gst_plugin_feature_set_rank:
+ * @feature: feature to rank
+ * @rank: rank value - higher number means more priority rank
+ *
+ * Specifies a rank for a plugin feature, so that autoplugging uses
+ * the most appropriate feature.
+ */
+void
+gst_plugin_feature_set_rank (GstPluginFeature * feature, guint rank)
+{
+ g_return_if_fail (feature != NULL);
+ g_return_if_fail (GST_IS_PLUGIN_FEATURE (feature));
+
+ feature->rank = rank;
+}
+
+/**
+ * gst_plugin_feature_get_rank:
+ * @feature: a feature
+ *
+ * Gets the rank of a plugin feature.
+ *
+ * Returns: The rank of the feature
+ */
+guint
+gst_plugin_feature_get_rank (GstPluginFeature * feature)
+{
+ g_return_val_if_fail (GST_IS_PLUGIN_FEATURE (feature), GST_RANK_NONE);
+
+ return feature->rank;
+}
+
+/**
+ * gst_plugin_feature_list_free:
+ * @list: (transfer full) (element-type Gst.PluginFeature): list
+ * of #GstPluginFeature
+ *
+ * Unrefs each member of @list, then frees the list.
+ */
+void
+gst_plugin_feature_list_free (GList * list)
+{
+ GList *g;
+
+ for (g = list; g; g = g->next) {
+ GstPluginFeature *feature = GST_PLUGIN_FEATURE_CAST (g->data);
+
+ gst_object_unref (feature);
+ }
+ g_list_free (list);
+}
+
+/**
+ * gst_plugin_feature_list_copy:
+ * @list: (transfer none) (element-type Gst.PluginFeature): list
+ * of #GstPluginFeature
+ *
+ * Copies the list of features. Caller should call @gst_plugin_feature_list_free
+ * when done with the list.
+ *
+ * Returns: (transfer full) (element-type Gst.PluginFeature): a copy of @list,
+ * with each feature's reference count incremented.
+ *
+ * Since: 0.10.26
+ */
+GList *
+gst_plugin_feature_list_copy (GList * list)
+{
+ GList *new_list = NULL;
+
+ if (G_LIKELY (list)) {
+ GList *last;
+
+ new_list = g_list_alloc ();
+ new_list->data = g_object_ref ((GObject *) list->data);
+ new_list->prev = NULL;
+ last = new_list;
+ list = list->next;
+ while (list) {
+ last->next = g_list_alloc ();
+ last->next->prev = last;
+ last = last->next;
+ last->data = g_object_ref ((GObject *) list->data);
+ list = list->next;
+ }
+ last->next = NULL;
+ }
+
+ return new_list;
+}
+
+/**
+ * gst_plugin_feature_list_debug:
+ * @list: (transfer none) (element-type Gst.PluginFeature): a #GList of
+ * plugin features
+ *
+ * Debug the plugin feature names in @list.
+ *
+ * Since: 0.10.31
+ */
+void
+gst_plugin_feature_list_debug (GList * list)
+{
+#ifndef GST_DISABLE_GST_DEBUG
+ while (list) {
+ GST_DEBUG ("%s",
+ gst_plugin_feature_get_name ((GstPluginFeature *) list->data));
+ list = list->next;
+ }
+#endif
+}
+
+/**
+ * gst_plugin_feature_check_version:
+ * @feature: a feature
+ * @min_major: minimum required major version
+ * @min_minor: minimum required minor version
+ * @min_micro: minimum required micro version
+ *
+ * Checks whether the given plugin feature is at least
+ * the required version
+ *
+ * Returns: #TRUE if the plugin feature has at least
+ * the required version, otherwise #FALSE.
+ */
+gboolean
+gst_plugin_feature_check_version (GstPluginFeature * feature,
+ guint min_major, guint min_minor, guint min_micro)
+{
+ GstRegistry *registry;
+ GstPlugin *plugin;
+ gboolean ret = FALSE;
+
+ g_return_val_if_fail (feature != NULL, FALSE);
+ g_return_val_if_fail (GST_IS_PLUGIN_FEATURE (feature), FALSE);
+
+ GST_DEBUG ("Looking up plugin '%s' containing plugin feature '%s'",
+ feature->plugin_name, GST_OBJECT_NAME (feature));
+
+ registry = gst_registry_get_default ();
+ plugin = gst_registry_find_plugin (registry, feature->plugin_name);
+
+ if (plugin) {
+ const gchar *ver_str;
+ guint major, minor, micro, nano;
+ gint nscan;
+
+ ver_str = gst_plugin_get_version (plugin);
+ g_return_val_if_fail (ver_str != NULL, FALSE);
+
+ nscan = sscanf (ver_str, "%u.%u.%u.%u", &major, &minor, &micro, &nano);
+ GST_DEBUG ("version string '%s' parsed to %d values", ver_str, nscan);
+
+ if (nscan >= 3) {
+ if (major > min_major)
+ ret = TRUE;
+ else if (major < min_major)
+ ret = FALSE;
+ else if (minor > min_minor)
+ ret = TRUE;
+ else if (minor < min_minor)
+ ret = FALSE;
+ else if (micro > min_micro)
+ ret = TRUE;
+ /* micro is 1 smaller but we have a nano version, this is the upcoming
+ * release of the requested version and we're ok then */
+ else if (nscan == 4 && nano > 0 && (micro + 1 == min_micro))
+ ret = TRUE;
+ else
+ ret = (micro == min_micro);
+
+ GST_DEBUG ("Checking whether %u.%u.%u >= %u.%u.%u? %s", major, minor,
+ micro, min_major, min_minor, min_micro, (ret) ? "yes" : "no");
+ } else {
+ GST_WARNING ("Could not parse version string '%s' of plugin '%s'",
+ ver_str, feature->plugin_name);
+ }
+
+ gst_object_unref (plugin);
+ } else {
+ GST_DEBUG ("Could not find plugin '%s'", feature->plugin_name);
+ }
+
+ return ret;
+}
+
+/**
+ * gst_plugin_feature_rank_compare_func:
+ * @p1: a #GstPluginFeature
+ * @p2: a #GstPluginFeature
+ *
+ * Compares the two given #GstPluginFeature instances. This function can be
+ * used as a #GCompareFunc when sorting by rank and then by name.
+ *
+ * Returns: negative value if the rank of p1 > the rank of p2 or the ranks are
+ * equal but the name of p1 comes before the name of p2; zero if the rank
+ * and names are equal; positive value if the rank of p1 < the rank of p2 or the
+ * ranks are equal but the name of p2 comes after the name of p1
+ *
+ * Since: 0.10.31
+ */
+gint
+gst_plugin_feature_rank_compare_func (gconstpointer p1, gconstpointer p2)
+{
+ GstPluginFeature *f1, *f2;
+ gint diff;
+
+ f1 = (GstPluginFeature *) p1;
+ f2 = (GstPluginFeature *) p2;
+
+ diff = f2->rank - f1->rank;
+ if (diff != 0)
+ return diff;
+
+ diff = strcmp (GST_OBJECT_NAME (f2), GST_OBJECT_NAME (f1));
+
+ return diff;
+}
diff --git a/gst/gstpluginfeature.h b/gst/gstpluginfeature.h
new file mode 100644
index 0000000..b8db0f4
--- /dev/null
+++ b/gst/gstpluginfeature.h
@@ -0,0 +1,183 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2000 Wim Taymans <wtay@chello.be>
+ *
+ * gstpluginfeature.h: Header for base GstPluginFeature
+ *
+ * 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_PLUGIN_FEATURE_H__
+#define __GST_PLUGIN_FEATURE_H__
+
+#include <glib-object.h>
+#include <gst/gstobject.h>
+#include <gst/gstplugin.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_PLUGIN_FEATURE (gst_plugin_feature_get_type())
+#define GST_PLUGIN_FEATURE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_PLUGIN_FEATURE, GstPluginFeature))
+#define GST_IS_PLUGIN_FEATURE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_PLUGIN_FEATURE))
+#define GST_PLUGIN_FEATURE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_PLUGIN_FEATURE, GstPluginFeatureClass))
+#define GST_IS_PLUGIN_FEATURE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_PLUGIN_FEATURE))
+#define GST_PLUGIN_FEATURE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_PLUGIN_FEATURE, GstPluginFeatureClass))
+#define GST_PLUGIN_FEATURE_CAST(obj) ((GstPluginFeature*)(obj))
+
+typedef struct _GstPluginFeature GstPluginFeature;
+typedef struct _GstPluginFeatureClass GstPluginFeatureClass;
+
+/**
+ * GstRank:
+ * @GST_RANK_NONE: will be chosen last or not at all
+ * @GST_RANK_MARGINAL: unlikely to be chosen
+ * @GST_RANK_SECONDARY: likely to be chosen
+ * @GST_RANK_PRIMARY: will be chosen first
+ *
+ * Element priority ranks. Defines the order in which the autoplugger (or
+ * similar rank-picking mechanisms, such as e.g. gst_element_make_from_uri())
+ * will choose this element over an alternative one with the same function.
+ *
+ * These constants serve as a rough guidance for defining the rank of a
+ * #GstPluginFeature. Any value is valid, including values bigger than
+ * @GST_RANK_PRIMARY.
+ */
+typedef enum {
+ GST_RANK_NONE = 0,
+ GST_RANK_MARGINAL = 64,
+ GST_RANK_SECONDARY = 128,
+ GST_RANK_PRIMARY = 256
+} GstRank;
+
+/**
+ * gst_plugin_feature_get_name:
+ * @feature: a #GstPluginFeature to get the name of @feature.
+ *
+ * Returns a copy of the name of @feature.
+ * Caller should g_free() the return value after usage.
+ * For a nameless plugin feature, this returns NULL, which you can safely g_free()
+ * as well.
+ *
+ * Returns: (transfer full): the name of @feature. g_free() after usage. MT safe.
+ *
+ */
+#define gst_plugin_feature_get_name(feature) gst_object_get_name(GST_OBJECT_CAST(feature))
+
+/**
+ * gst_plugin_feature_set_name:
+ * @feature: a #GstPluginFeature to set the name of.
+ * @name: the new name
+ *
+ * Sets the name of the plugin feature, getting rid of the old name if there was one.
+ */
+#define gst_plugin_feature_set_name(feature,name) gst_object_set_name(GST_OBJECT_CAST(feature),name)
+
+/**
+ * GstPluginFeature:
+ *
+ * Opaque #GstPluginFeature structure.
+ */
+struct _GstPluginFeature {
+ GstObject object;
+
+ /*< private >*/
+ gboolean loaded;
+ guint rank;
+
+ const gchar *plugin_name;
+ GstPlugin *plugin; /* weak ref */
+
+ /*< private >*/
+ gpointer _gst_reserved[GST_PADDING - 1];
+};
+
+struct _GstPluginFeatureClass {
+ GstObjectClass parent_class;
+
+ /*< private >*/
+ gpointer _gst_reserved[GST_PADDING];
+};
+
+/**
+ * GstTypeNameData:
+ * @name: a name
+ * @type: a GType
+ *
+ * Structure used for filtering based on @name and @type.
+ */
+typedef struct {
+ const gchar *name;
+ GType type;
+} GstTypeNameData;
+
+/**
+ * GstPluginFeatureFilter:
+ * @feature: the pluginfeature to check
+ * @user_data: the user_data that has been passed on e.g.
+ * gst_registry_feature_filter()
+ *
+ * A function that can be used with e.g. gst_registry_feature_filter()
+ * to get a list of pluginfeature that match certain criteria.
+ *
+ * Returns: %TRUE for a positive match, %FALSE otherwise
+ */
+typedef gboolean (*GstPluginFeatureFilter) (GstPluginFeature *feature,
+ gpointer user_data);
+
+/* normal GObject stuff */
+GType gst_plugin_feature_get_type (void);
+
+GstPluginFeature *
+ gst_plugin_feature_load (GstPluginFeature *feature);
+
+gboolean gst_plugin_feature_type_name_filter (GstPluginFeature *feature,
+ GstTypeNameData *data);
+
+void gst_plugin_feature_set_rank (GstPluginFeature *feature, guint rank);
+guint gst_plugin_feature_get_rank (GstPluginFeature *feature);
+
+void gst_plugin_feature_list_free (GList *list);
+GList *gst_plugin_feature_list_copy (GList *list);
+void gst_plugin_feature_list_debug (GList *list);
+
+/**
+ * GST_PLUGIN_FEATURE_LIST_DEBUG:
+ * @list: (transfer none) (element-type Gst.PluginFeature): a #GList of
+ * plugin features
+ *
+ * Debug the plugin feature names in @list.
+ *
+ * Since: 0.10.31
+ */
+#ifndef GST_DISABLE_GST_DEBUG
+#define GST_PLUGIN_FEATURE_LIST_DEBUG(list) gst_plugin_feature_list_debug(list)
+#else
+#define GST_PLUGIN_FEATURE_LIST_DEBUG(list)
+#endif
+
+gboolean gst_plugin_feature_check_version (GstPluginFeature *feature,
+ guint min_major,
+ guint min_minor,
+ guint min_micro);
+gint gst_plugin_feature_rank_compare_func (gconstpointer p1,
+ gconstpointer p2);
+
+G_END_DECLS
+
+
+#endif /* __GST_PLUGIN_FEATURE_H__ */
+
diff --git a/gst/gstpluginloader.c b/gst/gstpluginloader.c
new file mode 100644
index 0000000..8bb2df8
--- /dev/null
+++ b/gst/gstpluginloader.c
@@ -0,0 +1,1026 @@
+/* GStreamer
+ * Copyright (C) 2008 Jan Schmidt <jan.schmidt@sun.com>
+ *
+ * gstpluginloader.c: GstPluginLoader helper for loading plugin files
+ * out of process.
+ *
+ * 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_private.h>
+
+#ifndef G_OS_WIN32
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#else
+#define fsync(fd) _commit(fd)
+#include <io.h>
+#endif
+
+#ifdef HAVE_SYS_UTSNAME_H
+#include <sys/utsname.h>
+#endif
+
+#include <errno.h>
+
+#include <gst/gstconfig.h>
+
+#include <gst/gstpoll.h>
+#include <gst/gstutils.h>
+
+#include <gst/gstpluginloader.h>
+#include <gst/gstregistrychunks.h>
+#include <gst/gstregistrybinary.h>
+
+/* IMPORTANT: Bump the version number if the plugin loader packet protocol
+ * changes. Changes in the binary registry format itself are handled by
+ * bumping the GST_MAGIC_BINARY_VERSION_STR
+ */
+static const guint32 loader_protocol_version = 3;
+
+#define GST_CAT_DEFAULT GST_CAT_PLUGIN_LOADING
+
+static GstPluginLoader *plugin_loader_new (GstRegistry * registry);
+static gboolean plugin_loader_free (GstPluginLoader * loader);
+static gboolean plugin_loader_load (GstPluginLoader * loader,
+ const gchar * filename, off_t file_size, time_t file_mtime);
+
+/* functions used in GstRegistry scanning */
+const GstPluginLoaderFuncs _priv_gst_plugin_loader_funcs = {
+ plugin_loader_new, plugin_loader_free, plugin_loader_load
+};
+
+typedef struct _PendingPluginEntry
+{
+ /* sequence number */
+ guint32 tag;
+ gchar *filename;
+ off_t file_size;
+ time_t file_mtime;
+} PendingPluginEntry;
+
+struct _GstPluginLoader
+{
+ GstRegistry *registry;
+ GstPoll *fdset;
+
+ gboolean child_running;
+ GPid child_pid;
+ GstPollFD fd_w;
+ GstPollFD fd_r;
+
+ gboolean is_child;
+ gboolean got_plugin_details;
+
+ /* Transmit buffer */
+ guint8 *tx_buf;
+ guint tx_buf_size;
+ guint tx_buf_write;
+ guint tx_buf_read;
+
+ /* next sequence number (for PendingPluginEntry) */
+ guint32 next_tag;
+
+ guint8 *rx_buf;
+ guint rx_buf_size;
+ gboolean rx_done;
+ gboolean rx_got_sync;
+
+ /* Head and tail of the pending plugins list. List of
+ PendingPluginEntry structs */
+ GList *pending_plugins;
+ GList *pending_plugins_tail;
+};
+
+#define PACKET_EXIT 1
+#define PACKET_LOAD_PLUGIN 2
+#define PACKET_SYNC 3
+#define PACKET_PLUGIN_DETAILS 4
+#define PACKET_VERSION 5
+
+#define BUF_INIT_SIZE 512
+#define BUF_GROW_EXTRA 512
+#define BUF_MAX_SIZE (32 * 1024 * 1024)
+
+#define HEADER_SIZE 12
+/* 4 magic hex bytes to mark each packet */
+#define HEADER_MAGIC 0xbefec0ae
+#define ALIGNMENT (sizeof (void *))
+
+static gboolean gst_plugin_loader_spawn (GstPluginLoader * loader);
+static void put_packet (GstPluginLoader * loader, guint type, guint32 tag,
+ const guint8 * payload, guint32 payload_len);
+static gboolean exchange_packets (GstPluginLoader * l);
+static gboolean plugin_loader_replay_pending (GstPluginLoader * l);
+static gboolean plugin_loader_load_and_sync (GstPluginLoader * l,
+ PendingPluginEntry * entry);
+static void plugin_loader_create_blacklist_plugin (GstPluginLoader * l,
+ PendingPluginEntry * entry);
+static void plugin_loader_cleanup_child (GstPluginLoader * loader);
+static gboolean plugin_loader_sync_with_child (GstPluginLoader * l);
+
+static GstPluginLoader *
+plugin_loader_new (GstRegistry * registry)
+{
+ GstPluginLoader *l = g_slice_new0 (GstPluginLoader);
+
+ if (registry)
+ l->registry = gst_object_ref (registry);
+ l->fdset = gst_poll_new (FALSE);
+ gst_poll_fd_init (&l->fd_w);
+ gst_poll_fd_init (&l->fd_r);
+
+ l->tx_buf_size = BUF_INIT_SIZE;
+ l->tx_buf = g_malloc (BUF_INIT_SIZE);
+
+ l->next_tag = 0;
+
+ l->rx_buf_size = BUF_INIT_SIZE;
+ l->rx_buf = g_malloc (BUF_INIT_SIZE);
+
+ return l;
+}
+
+static gboolean
+plugin_loader_free (GstPluginLoader * loader)
+{
+ GList *cur;
+ gboolean got_plugin_details;
+
+ fsync (loader->fd_w.fd);
+
+ if (loader->child_running) {
+ put_packet (loader, PACKET_EXIT, 0, NULL, 0);
+
+ /* Swap packets with the child until it exits cleanly */
+ while (!loader->rx_done) {
+ if (exchange_packets (loader) || loader->rx_done)
+ continue;
+
+ if (!plugin_loader_replay_pending (loader))
+ break;
+ put_packet (loader, PACKET_EXIT, 0, NULL, 0);
+ }
+
+ plugin_loader_cleanup_child (loader);
+ } else {
+ close (loader->fd_w.fd);
+ close (loader->fd_r.fd);
+ }
+
+ gst_poll_free (loader->fdset);
+
+ g_free (loader->rx_buf);
+ g_free (loader->tx_buf);
+
+ if (loader->registry)
+ gst_object_unref (loader->registry);
+
+ got_plugin_details = loader->got_plugin_details;
+
+ /* Free any pending plugin entries */
+ cur = loader->pending_plugins;
+ while (cur) {
+ PendingPluginEntry *entry = (PendingPluginEntry *) (cur->data);
+ g_free (entry->filename);
+ g_slice_free (PendingPluginEntry, entry);
+
+ cur = g_list_delete_link (cur, cur);
+ }
+
+ g_slice_free (GstPluginLoader, loader);
+
+ return got_plugin_details;
+}
+
+static gboolean
+plugin_loader_load (GstPluginLoader * loader, const gchar * filename,
+ off_t file_size, time_t file_mtime)
+{
+ gint len;
+ PendingPluginEntry *entry;
+
+ if (!gst_plugin_loader_spawn (loader))
+ return FALSE;
+
+ /* Send a packet to the child requesting that it load the given file */
+ GST_LOG_OBJECT (loader->registry,
+ "Sending file %s to child. tag %u", filename, loader->next_tag);
+
+ entry = g_slice_new (PendingPluginEntry);
+ entry->tag = loader->next_tag++;
+ entry->filename = g_strdup (filename);
+ entry->file_size = file_size;
+ entry->file_mtime = file_mtime;
+ loader->pending_plugins_tail =
+ g_list_append (loader->pending_plugins_tail, entry);
+
+ if (loader->pending_plugins == NULL)
+ loader->pending_plugins = loader->pending_plugins_tail;
+ else
+ loader->pending_plugins_tail = g_list_next (loader->pending_plugins_tail);
+
+ len = strlen (filename);
+ put_packet (loader, PACKET_LOAD_PLUGIN, entry->tag,
+ (guint8 *) filename, len + 1);
+
+ if (!exchange_packets (loader)) {
+ if (!plugin_loader_replay_pending (loader))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+plugin_loader_replay_pending (GstPluginLoader * l)
+{
+ GList *cur, *next;
+
+restart:
+ if (!gst_plugin_loader_spawn (l))
+ return FALSE;
+
+ /* Load each plugin one by one synchronously until we find the
+ * crashing one */
+ while ((cur = l->pending_plugins)) {
+ PendingPluginEntry *entry = (PendingPluginEntry *) (cur->data);
+
+ if (!plugin_loader_load_and_sync (l, entry)) {
+ /* Create dummy plugin entry to block re-scanning this file */
+ GST_ERROR ("Plugin file %s failed to load. Blacklisting",
+ entry->filename);
+ plugin_loader_create_blacklist_plugin (l, entry);
+ l->got_plugin_details = TRUE;
+ /* Now remove this crashy plugin from the head of the list */
+ l->pending_plugins = g_list_delete_link (cur, cur);
+ g_free (entry->filename);
+ g_slice_free (PendingPluginEntry, entry);
+ if (l->pending_plugins == NULL)
+ l->pending_plugins_tail = NULL;
+ if (!gst_plugin_loader_spawn (l))
+ return FALSE;
+ break;
+ }
+ }
+
+ /* We exited after finding the crashing one. If there's any more pending,
+ * dispatch them post-haste, but don't wait */
+ for (cur = l->pending_plugins; cur != NULL; cur = next) {
+ PendingPluginEntry *entry = (PendingPluginEntry *) (cur->data);
+
+ next = g_list_next (cur);
+
+ put_packet (l, PACKET_LOAD_PLUGIN, entry->tag,
+ (guint8 *) entry->filename, strlen (entry->filename) + 1);
+
+ /* This might invalidate cur, which is why we grabbed 'next' above */
+ if (!exchange_packets (l))
+ goto restart;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+plugin_loader_sync_with_child (GstPluginLoader * l)
+{
+ put_packet (l, PACKET_SYNC, 0, NULL, 0);
+
+ l->rx_got_sync = FALSE;
+ while (!l->rx_got_sync) {
+ if (!exchange_packets (l))
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+plugin_loader_load_and_sync (GstPluginLoader * l, PendingPluginEntry * entry)
+{
+ gint len;
+
+ GST_DEBUG_OBJECT (l->registry, "Synchronously loading plugin file %s",
+ entry->filename);
+
+ len = strlen (entry->filename);
+ put_packet (l, PACKET_LOAD_PLUGIN, entry->tag,
+ (guint8 *) entry->filename, len + 1);
+
+ return plugin_loader_sync_with_child (l);
+}
+
+static void
+plugin_loader_create_blacklist_plugin (GstPluginLoader * l,
+ PendingPluginEntry * entry)
+{
+ GstPlugin *plugin = g_object_newv (GST_TYPE_PLUGIN, 0, NULL);
+
+ plugin->filename = g_strdup (entry->filename);
+ plugin->file_mtime = entry->file_mtime;
+ plugin->file_size = entry->file_size;
+ plugin->flags |= GST_PLUGIN_FLAG_BLACKLISTED;
+
+ plugin->basename = g_path_get_basename (plugin->filename);
+ plugin->desc.name = g_intern_string (plugin->basename);
+ plugin->desc.description = "Plugin for blacklisted file";
+ plugin->desc.version = "0.0.0";
+ plugin->desc.license = "BLACKLIST";
+ plugin->desc.source = plugin->desc.license;
+ plugin->desc.package = plugin->desc.license;
+ plugin->desc.origin = plugin->desc.license;
+
+ GST_DEBUG ("Adding blacklist plugin '%s'", plugin->desc.name);
+ gst_registry_add_plugin (l->registry, plugin);
+}
+
+#ifdef __APPLE__
+#if defined(__x86_64__)
+#define USR_BIN_ARCH_SWITCH "-x86_64"
+#elif defined(__i386__)
+#define USR_BIN_ARCH_SWITCH "-i386"
+#elif defined(__ppc__)
+#define USR_BIN_ARCH_SWITCH "-ppc"
+#elif defined(__ppc64__)
+#define USR_BIN_ARCH_SWITCH "-ppc64"
+#endif
+#endif
+
+#define YES_MULTIARCH 1
+#define NO_MULTIARCH 2
+
+#if defined (__APPLE__) && defined (USR_BIN_ARCH_SWITCH)
+static gboolean
+gst_plugin_loader_use_usr_bin_arch (void)
+{
+ static volatile gsize multiarch = 0;
+
+ if (g_once_init_enter (&multiarch)) {
+ gsize res = NO_MULTIARCH;
+
+#ifdef HAVE_SYS_UTSNAME_H
+ {
+ struct utsname uname_data;
+
+ if (uname (&uname_data) == 0) {
+ /* Check for OS X >= 10.5 (darwin kernel 9.0) */
+ GST_LOG ("%s %s", uname_data.sysname, uname_data.release);
+ if (g_ascii_strcasecmp (uname_data.sysname, "Darwin") == 0 &&
+ g_strtod (uname_data.release, NULL) >= 9.0) {
+ res = YES_MULTIARCH;
+ }
+ }
+ }
+#endif
+
+ GST_INFO ("multiarch: %s", (res == YES_MULTIARCH) ? "yes" : "no");
+ g_once_init_leave (&multiarch, res);
+ }
+ return (multiarch == YES_MULTIARCH);
+}
+#endif /* __APPLE__ && USR_BIN_ARCH_SWITCH */
+
+static gboolean
+gst_plugin_loader_try_helper (GstPluginLoader * loader, gchar * location)
+{
+ char *argv[5] = { NULL, };
+ int c = 0;
+
+#if defined (__APPLE__) && defined (USR_BIN_ARCH_SWITCH)
+ if (gst_plugin_loader_use_usr_bin_arch ()) {
+ argv[c++] = (char *) "/usr/bin/arch";
+ argv[c++] = (char *) USR_BIN_ARCH_SWITCH;
+ }
+#endif
+ argv[c++] = location;
+ argv[c++] = (char *) "-l";
+ argv[c++] = NULL;
+
+ if (c > 3) {
+ GST_LOG ("Trying to spawn gst-plugin-scanner helper at %s with arch %s",
+ location, argv[1]);
+ } else {
+ GST_LOG ("Trying to spawn gst-plugin-scanner helper at %s", location);
+ }
+
+ if (!g_spawn_async_with_pipes (NULL, argv, NULL,
+ G_SPAWN_DO_NOT_REAP_CHILD /* | G_SPAWN_STDERR_TO_DEV_NULL */ ,
+ NULL, NULL, &loader->child_pid, &loader->fd_w.fd, &loader->fd_r.fd,
+ NULL, NULL))
+ return FALSE;
+
+ gst_poll_add_fd (loader->fdset, &loader->fd_w);
+ gst_poll_add_fd (loader->fdset, &loader->fd_r);
+
+ gst_poll_fd_ctl_read (loader->fdset, &loader->fd_r, TRUE);
+
+ loader->tx_buf_write = loader->tx_buf_read = 0;
+
+ put_packet (loader, PACKET_VERSION, 0, NULL, 0);
+ if (!plugin_loader_sync_with_child (loader))
+ return FALSE;
+
+ loader->child_running = TRUE;
+
+ return TRUE;
+}
+
+static gboolean
+gst_plugin_loader_spawn (GstPluginLoader * loader)
+{
+ const gchar *env;
+ char *helper_bin;
+ gboolean res = FALSE;
+
+ if (loader->child_running)
+ return TRUE;
+
+ /* Find the gst-plugin-scanner: first try the env-var if it is set,
+ * otherwise use the installed version */
+ env = g_getenv ("GST_PLUGIN_SCANNER");
+
+ if (env != NULL && *env != '\0') {
+ GST_LOG ("Trying GST_PLUGIN_SCANNER env var: %s", env);
+ helper_bin = g_strdup (env);
+ res = gst_plugin_loader_try_helper (loader, helper_bin);
+ g_free (helper_bin);
+ }
+
+ if (!res) {
+ GST_LOG ("Trying installed plugin scanner");
+ helper_bin = g_strdup (GST_PLUGIN_SCANNER_INSTALLED);
+ res = gst_plugin_loader_try_helper (loader, helper_bin);
+ g_free (helper_bin);
+
+ if (!res) {
+ GST_INFO ("No gst-plugin-scanner available, or not working");
+ }
+ }
+
+ return loader->child_running;
+}
+
+static void
+plugin_loader_cleanup_child (GstPluginLoader * l)
+{
+ if (!l->child_running || l->is_child)
+ return;
+
+ gst_poll_remove_fd (l->fdset, &l->fd_w);
+ gst_poll_remove_fd (l->fdset, &l->fd_r);
+
+ close (l->fd_w.fd);
+ close (l->fd_r.fd);
+
+#ifndef G_OS_WIN32
+ GST_LOG ("waiting for child process to exit");
+ waitpid (l->child_pid, NULL, 0);
+#else
+ g_warning ("FIXME: Implement child process shutdown for Win32");
+#endif
+ g_spawn_close_pid (l->child_pid);
+
+ l->child_running = FALSE;
+}
+
+gboolean
+_gst_plugin_loader_client_run (void)
+{
+ GstPluginLoader *l;
+
+ l = plugin_loader_new (NULL);
+ if (l == NULL)
+ return FALSE;
+
+ /* On entry, the inward pipe is STDIN, and outward is STDOUT.
+ * Dup those somewhere better so that plugins printing things
+ * won't interfere with anything */
+#ifndef G_OS_WIN32
+ {
+ int dup_fd;
+
+ dup_fd = dup (0); /* STDIN */
+ if (dup_fd == -1) {
+ GST_ERROR ("Failed to start. Could no dup STDIN, errno %d", errno);
+ return FALSE;
+ }
+ l->fd_r.fd = dup_fd;
+ close (0);
+
+ dup_fd = dup (1); /* STDOUT */
+ if (dup_fd == -1) {
+ GST_ERROR ("Failed to start. Could no dup STDOUT, errno %d", errno);
+ return FALSE;
+ }
+ l->fd_w.fd = dup_fd;
+ close (1);
+
+ /* Dup stderr down to stdout so things that plugins print are visible,
+ * but don't care if it fails */
+ dup2 (2, 1);
+ }
+#else
+ /* FIXME: Use DuplicateHandle and friends on win32 */
+ l->fd_w.fd = 1; /* STDOUT */
+ l->fd_r.fd = 0; /* STDIN */
+#endif
+
+ gst_poll_add_fd (l->fdset, &l->fd_w);
+ gst_poll_add_fd (l->fdset, &l->fd_r);
+ gst_poll_fd_ctl_read (l->fdset, &l->fd_r, TRUE);
+
+ l->is_child = TRUE;
+
+ GST_DEBUG ("Plugin scanner child running. Waiting for instructions");
+
+ /* Loop, listening for incoming packets on the fd and writing responses */
+ while (!l->rx_done && exchange_packets (l));
+
+ plugin_loader_free (l);
+
+ return TRUE;
+}
+
+static void
+put_packet (GstPluginLoader * l, guint type, guint32 tag,
+ const guint8 * payload, guint32 payload_len)
+{
+ guint8 *out;
+ guint len = payload_len + HEADER_SIZE;
+
+ if (l->tx_buf_write + len >= l->tx_buf_size) {
+ GST_LOG ("Expanding tx buf from %d to %d for packet of size %d",
+ l->tx_buf_size, l->tx_buf_write + len + BUF_GROW_EXTRA, len);
+ l->tx_buf_size = l->tx_buf_write + len + BUF_GROW_EXTRA;
+ l->tx_buf = g_realloc (l->tx_buf, l->tx_buf_size);
+ }
+
+ out = l->tx_buf + l->tx_buf_write;
+
+ /* one byte packet type */
+ out[0] = type;
+ /* 3 byte packet tag number */
+ GST_WRITE_UINT24_BE (out + 1, tag);
+ /* 4 bytes packet length */
+ GST_WRITE_UINT32_BE (out + 4, payload_len);
+ /* payload */
+ memcpy (out + HEADER_SIZE, payload, payload_len);
+ /* Write magic into the header */
+ GST_WRITE_UINT32_BE (out + 8, HEADER_MAGIC);
+
+ l->tx_buf_write += len;
+ gst_poll_fd_ctl_write (l->fdset, &l->fd_w, TRUE);
+}
+
+static void
+put_chunk (GstPluginLoader * l, GstRegistryChunk * chunk, guint * pos)
+{
+ guint padsize = 0;
+ guint len;
+ guint8 *out;
+
+ /* Might need to align the chunk */
+ if (chunk->align && ((*pos) % ALIGNMENT) != 0)
+ padsize = ALIGNMENT - ((*pos) % ALIGNMENT);
+
+ len = padsize + chunk->size;
+
+ if (G_UNLIKELY (l->tx_buf_write + len >= l->tx_buf_size)) {
+ guint new_size = MAX (l->tx_buf_write + len,
+ l->tx_buf_size + l->tx_buf_size / 4) + BUF_GROW_EXTRA;
+ GST_LOG ("Expanding tx buf from %d to %d for chunk of size %d",
+ l->tx_buf_size, new_size, chunk->size);
+ l->tx_buf_size = new_size;
+ l->tx_buf = g_realloc (l->tx_buf, l->tx_buf_size);
+ }
+
+ out = l->tx_buf + l->tx_buf_write;
+ /* Clear the padding */
+ if (padsize)
+ memset (out, 0, padsize);
+ memcpy (out + padsize, chunk->data, chunk->size);
+
+ l->tx_buf_write += len;
+ *pos += len;
+
+ gst_poll_fd_ctl_write (l->fdset, &l->fd_w, TRUE);
+};
+
+static gboolean
+write_one (GstPluginLoader * l)
+{
+ guint8 *out;
+ guint32 to_write, magic;
+ int res;
+
+ if (l->tx_buf_read + HEADER_SIZE > l->tx_buf_write)
+ return FALSE;
+
+ out = l->tx_buf + l->tx_buf_read;
+
+ magic = GST_READ_UINT32_BE (out + 8);
+ if (magic != HEADER_MAGIC) {
+ GST_ERROR ("Packet magic number is missing. Memory corruption detected");
+ goto fail_and_cleanup;
+ }
+
+ to_write = GST_READ_UINT32_BE (out + 4) + HEADER_SIZE;
+ /* Check that the magic is intact, and the size is sensible */
+ if (to_write > l->tx_buf_size) {
+ GST_ERROR ("Indicated packet size is too large. Corruption detected");
+ goto fail_and_cleanup;
+ }
+
+ l->tx_buf_read += to_write;
+
+ GST_LOG ("Writing packet of size %d bytes to fd %d", to_write, l->fd_w.fd);
+
+ do {
+ res = write (l->fd_w.fd, out, to_write);
+ if (G_UNLIKELY (res < 0)) {
+ if (errno == EAGAIN || errno == EINTR)
+ continue;
+ /* Failed to write -> child died */
+ goto fail_and_cleanup;
+ }
+ to_write -= res;
+ out += res;
+ } while (to_write > 0);
+
+ if (l->tx_buf_read == l->tx_buf_write) {
+ gst_poll_fd_ctl_write (l->fdset, &l->fd_w, FALSE);
+ l->tx_buf_read = l->tx_buf_write = 0;
+ }
+
+ return TRUE;
+
+fail_and_cleanup:
+ plugin_loader_cleanup_child (l);
+ return FALSE;
+}
+
+static gboolean
+do_plugin_load (GstPluginLoader * l, const gchar * filename, guint tag)
+{
+ GstPlugin *newplugin;
+ GList *chunks = NULL;
+
+ GST_DEBUG ("Plugin scanner loading file %s. tag %u", filename, tag);
+
+#if 0 /* Test code - crash based on filename */
+ if (strstr (filename, "coreelements") == NULL) {
+ g_printerr ("Crashing on file %s\n", filename);
+ g_printerr ("%d", *(gint *) (NULL));
+ }
+#endif
+
+ newplugin = gst_plugin_load_file ((gchar *) filename, NULL);
+ if (newplugin) {
+ guint hdr_pos;
+ guint offset;
+
+ /* Now serialise the plugin details and send */
+ if (!_priv_gst_registry_chunks_save_plugin (&chunks,
+ gst_registry_get_default (), newplugin))
+ goto fail;
+
+ /* Store where the header is, write an empty one, then write
+ * all the payload chunks, then fix up the header size */
+ hdr_pos = l->tx_buf_write;
+ offset = HEADER_SIZE;
+ put_packet (l, PACKET_PLUGIN_DETAILS, tag, NULL, 0);
+
+ if (chunks) {
+ GList *walk;
+ for (walk = chunks; walk; walk = g_list_next (walk)) {
+ GstRegistryChunk *cur = walk->data;
+ put_chunk (l, cur, &offset);
+
+ _priv_gst_registry_chunk_free (cur);
+ }
+
+ g_list_free (chunks);
+
+ /* Store the size of the written payload */
+ GST_WRITE_UINT32_BE (l->tx_buf + hdr_pos + 4, offset - HEADER_SIZE);
+ }
+#if 0 /* Test code - corrupt the tx buffer based on filename */
+ if (strstr (filename, "sink") != NULL) {
+ int fd, res;
+ g_printerr ("Corrupting tx buf on file %s\n", filename);
+ fd = open ("/dev/urandom", O_RDONLY);
+ res = read (fd, l->tx_buf, l->tx_buf_size);
+ close (fd);
+ }
+#endif
+
+ gst_object_unref (newplugin);
+ } else {
+ put_packet (l, PACKET_PLUGIN_DETAILS, tag, NULL, 0);
+ }
+
+ return TRUE;
+fail:
+ put_packet (l, PACKET_PLUGIN_DETAILS, tag, NULL, 0);
+ if (chunks) {
+ GList *walk;
+ for (walk = chunks; walk; walk = g_list_next (walk)) {
+ GstRegistryChunk *cur = walk->data;
+
+ _priv_gst_registry_chunk_free (cur);
+ }
+
+ g_list_free (chunks);
+ }
+
+ return FALSE;
+}
+
+static gboolean
+check_protocol_version (GstPluginLoader * l, guint8 * payload,
+ guint payload_len)
+{
+ guint32 got_version;
+ guint8 *binary_reg_ver;
+
+ if (payload_len < sizeof (guint32) + GST_MAGIC_BINARY_VERSION_LEN)
+ return FALSE;
+
+ got_version = GST_READ_UINT32_BE (payload);
+ GST_LOG ("Got VERSION %u from child. Ours is %u", got_version,
+ loader_protocol_version);
+ if (got_version != loader_protocol_version)
+ return FALSE;
+
+ binary_reg_ver = payload + sizeof (guint32);
+ if (strcmp ((gchar *) binary_reg_ver, GST_MAGIC_BINARY_VERSION_STR)) {
+ GST_LOG ("Binary chunk format of child is different. Ours: %s, child %s\n",
+ GST_MAGIC_BINARY_VERSION_STR, binary_reg_ver);
+ return FALSE;
+ }
+
+ return TRUE;
+};
+
+static gboolean
+handle_rx_packet (GstPluginLoader * l,
+ guint pack_type, guint32 tag, guint8 * payload, guint payload_len)
+{
+ gboolean res = TRUE;
+
+ switch (pack_type) {
+ case PACKET_EXIT:
+ gst_poll_fd_ctl_read (l->fdset, &l->fd_r, FALSE);
+ if (l->is_child) {
+ /* Respond */
+ put_packet (l, PACKET_EXIT, 0, NULL, 0);
+ }
+ l->rx_done = TRUE;
+ return TRUE;
+ case PACKET_LOAD_PLUGIN:{
+ if (!l->is_child)
+ return TRUE;
+
+ /* Payload is the filename to load */
+ res = do_plugin_load (l, (gchar *) payload, tag);
+
+ break;
+ }
+ case PACKET_PLUGIN_DETAILS:{
+ gchar *tmp = (gchar *) payload;
+ PendingPluginEntry *entry = NULL;
+ GList *cur;
+
+ GST_DEBUG_OBJECT (l->registry,
+ "Received plugin details from child w/ tag %u. %d bytes info",
+ tag, payload_len);
+
+ /* Assume that tagged details come back in the order
+ * we requested, and delete anything before (but not
+ * including) this one */
+ cur = l->pending_plugins;
+ while (cur) {
+ PendingPluginEntry *e = (PendingPluginEntry *) (cur->data);
+
+ if (e->tag > tag)
+ break;
+
+ if (e->tag == tag) {
+ entry = e;
+ break;
+ } else {
+ cur = g_list_delete_link (cur, cur);
+ g_free (e->filename);
+ g_slice_free (PendingPluginEntry, e);
+ }
+ }
+
+ l->pending_plugins = cur;
+ if (cur == NULL)
+ l->pending_plugins_tail = NULL;
+
+ if (payload_len > 0) {
+ GstPlugin *newplugin = NULL;
+ if (!_priv_gst_registry_chunks_load_plugin (l->registry, &tmp,
+ tmp + payload_len, &newplugin)) {
+ /* Got garbage from the child, so fail and trigger replay of plugins */
+ GST_ERROR_OBJECT (l->registry,
+ "Problems loading plugin details with tag %u from scanner", tag);
+ return FALSE;
+ }
+
+ newplugin->flags &= ~GST_PLUGIN_FLAG_CACHED;
+ GST_LOG_OBJECT (l->registry,
+ "marking plugin %p as registered as %s", newplugin,
+ newplugin->filename);
+ newplugin->registered = TRUE;
+
+ /* We got a set of plugin details - remember it for later */
+ l->got_plugin_details = TRUE;
+ } else if (entry != NULL) {
+ /* Create a blacklist entry for this file to prevent scanning every time */
+ plugin_loader_create_blacklist_plugin (l, entry);
+ l->got_plugin_details = TRUE;
+ }
+
+ if (entry != NULL) {
+ g_free (entry->filename);
+ g_slice_free (PendingPluginEntry, entry);
+ }
+
+ /* Remove the plugin entry we just loaded */
+ cur = l->pending_plugins;
+ if (cur != NULL)
+ cur = g_list_delete_link (cur, cur);
+ l->pending_plugins = cur;
+ if (cur == NULL)
+ l->pending_plugins_tail = NULL;
+
+ break;
+ }
+ case PACKET_SYNC:
+ if (l->is_child) {
+ /* Respond with our reply - also a sync */
+ put_packet (l, PACKET_SYNC, tag, NULL, 0);
+ GST_LOG ("Got SYNC in child - replying");
+ } else
+ l->rx_got_sync = TRUE;
+ break;
+ case PACKET_VERSION:
+ if (l->is_child) {
+ /* Respond with our reply - a version packet, with the version */
+ const gint version_len =
+ sizeof (guint32) + GST_MAGIC_BINARY_VERSION_LEN;
+ guint8 version_info[sizeof (guint32) + GST_MAGIC_BINARY_VERSION_LEN];
+ memset (version_info, 0, version_len);
+ GST_WRITE_UINT32_BE (version_info, loader_protocol_version);
+ memcpy (version_info + sizeof (guint32), GST_MAGIC_BINARY_VERSION_STR,
+ strlen (GST_MAGIC_BINARY_VERSION_STR));
+ put_packet (l, PACKET_VERSION, tag, version_info, version_len);
+ GST_LOG ("Got VERSION in child - replying %u", loader_protocol_version);
+ } else {
+ res = check_protocol_version (l, payload, payload_len);
+ }
+ break;
+ default:
+ return FALSE; /* Invalid packet -> something is wrong */
+ }
+
+ return res;
+}
+
+static gboolean
+read_one (GstPluginLoader * l)
+{
+ guint64 magic;
+ guint32 to_read, packet_len, tag;
+ guint8 *in;
+ gint res;
+
+ to_read = HEADER_SIZE;
+ in = l->rx_buf;
+ do {
+ res = read (l->fd_r.fd, in, to_read);
+ if (G_UNLIKELY (res < 0)) {
+ if (errno == EAGAIN || errno == EINTR)
+ continue;
+ GST_LOG ("Failed reading packet header");
+ return FALSE;
+ }
+ to_read -= res;
+ in += res;
+ } while (to_read > 0);
+
+ magic = GST_READ_UINT32_BE (l->rx_buf + 8);
+ if (magic != HEADER_MAGIC) {
+ GST_WARNING
+ ("Invalid packet (bad magic number) received from plugin scanner subprocess");
+ return FALSE;
+ }
+
+ packet_len = GST_READ_UINT32_BE (l->rx_buf + 4);
+ if (packet_len + HEADER_SIZE > BUF_MAX_SIZE) {
+ GST_WARNING
+ ("Received excessively large packet for plugin scanner subprocess");
+ return FALSE;
+ }
+ tag = GST_READ_UINT24_BE (l->rx_buf + 1);
+
+ if (packet_len > 0) {
+ if (packet_len + HEADER_SIZE >= l->rx_buf_size) {
+ GST_LOG ("Expanding rx buf from %d to %d",
+ l->rx_buf_size, packet_len + HEADER_SIZE + BUF_GROW_EXTRA);
+ l->rx_buf_size = packet_len + HEADER_SIZE + BUF_GROW_EXTRA;
+ l->rx_buf = g_realloc (l->rx_buf, l->rx_buf_size);
+ }
+
+ in = l->rx_buf + HEADER_SIZE;
+ to_read = packet_len;
+ do {
+ res = read (l->fd_r.fd, in, to_read);
+ if (G_UNLIKELY (res < 0)) {
+ if (errno == EAGAIN || errno == EINTR)
+ continue;
+ GST_ERROR ("Packet payload read failed");
+ return FALSE;
+ }
+ to_read -= res;
+ in += res;
+ } while (to_read > 0);
+ } else {
+ GST_LOG ("No payload to read for 0 length packet type %d tag %u",
+ l->rx_buf[0], tag);
+ }
+
+ return handle_rx_packet (l, l->rx_buf[0], tag,
+ l->rx_buf + HEADER_SIZE, packet_len);
+}
+
+static gboolean
+exchange_packets (GstPluginLoader * l)
+{
+ gint res;
+
+ /* Wait for activity on our FDs */
+ do {
+ do {
+ res = gst_poll_wait (l->fdset, GST_SECOND);
+ } while (res == -1 && (errno == EINTR || errno == EAGAIN));
+
+ if (res < 0)
+ return FALSE;
+
+ GST_LOG ("Poll res = %d. %d bytes pending for write", res,
+ l->tx_buf_write - l->tx_buf_read);
+
+ if (!l->rx_done) {
+ if (gst_poll_fd_has_error (l->fdset, &l->fd_r) ||
+ gst_poll_fd_has_closed (l->fdset, &l->fd_r)) {
+ GST_LOG ("read fd %d closed/errored", l->fd_r.fd);
+ goto fail_and_cleanup;
+ }
+
+ if (gst_poll_fd_can_read (l->fdset, &l->fd_r)) {
+ if (!read_one (l))
+ goto fail_and_cleanup;
+ }
+ }
+
+ if (l->tx_buf_read < l->tx_buf_write) {
+ if (gst_poll_fd_has_error (l->fdset, &l->fd_w) ||
+ gst_poll_fd_has_closed (l->fdset, &l->fd_r)) {
+ GST_ERROR ("write fd %d closed/errored", l->fd_w.fd);
+ goto fail_and_cleanup;
+ }
+ if (gst_poll_fd_can_write (l->fdset, &l->fd_w)) {
+ if (!write_one (l))
+ goto fail_and_cleanup;
+ }
+ }
+ } while (l->tx_buf_read < l->tx_buf_write);
+
+ return TRUE;
+fail_and_cleanup:
+ plugin_loader_cleanup_child (l);
+ return FALSE;
+}
diff --git a/gst/gstpluginloader.h b/gst/gstpluginloader.h
new file mode 100644
index 0000000..b2dbabd
--- /dev/null
+++ b/gst/gstpluginloader.h
@@ -0,0 +1,39 @@
+/* GStreamer
+ * Copyright (C) 2008 Jan Schmidt <jan.schmidt@sun.com>
+ *
+ * gstpluginloader.h: Helper for out-of-process plugin loading. Private header.
+ *
+ * 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_PLUGINLOADER_H__
+#define __GST_PLUGINLOADER_H__
+
+G_BEGIN_DECLS
+
+typedef struct _GstPluginLoader GstPluginLoader;
+
+typedef struct _GstPluginLoaderFuncs {
+ GstPluginLoader * (*create)(GstRegistry *registry);
+ gboolean (*destroy)(GstPluginLoader *loader);
+ gboolean (*load)(GstPluginLoader *loader, const gchar *filename,
+ off_t file_size, time_t file_mtime);
+} GstPluginLoaderFuncs;
+
+extern const GstPluginLoaderFuncs _priv_gst_plugin_loader_funcs;
+
+G_END_DECLS
+
+#endif /* __GST_PLUGINLOADER_H__ */
diff --git a/gst/gstpoll.c b/gst/gstpoll.c
new file mode 100644
index 0000000..83c8497
--- /dev/null
+++ b/gst/gstpoll.c
@@ -0,0 +1,1592 @@
+/* GStreamer
+ * Copyright (C) 1999 Erik Walthinsen <omega@cse.ogi.edu>
+ * Copyright (C) 2004 Wim Taymans <wim.taymans@gmail.com>
+ * Copyright (C) 2007 Peter Kjellerstedt <pkj@axis.com>
+ * Copyright (C) 2008 Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.com>
+ *
+ * gstpoll.c: File descriptor set
+ *
+ * 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:gstpoll
+ * @short_description: Keep track of file descriptors and make it possible
+ * to wait on them in a cancelable way
+ *
+ * A #GstPoll keeps track of file descriptors much like fd_set (used with
+ * select()) or a struct pollfd array (used with poll()). Once created with
+ * gst_poll_new(), the set can be used to wait for file descriptors to be
+ * readable and/or writeable. It is possible to make this wait be controlled
+ * by specifying %TRUE for the @controllable flag when creating the set (or
+ * later calling gst_poll_set_controllable()).
+ *
+ * New file descriptors are added to the set using gst_poll_add_fd(), and
+ * removed using gst_poll_remove_fd(). Controlling which file descriptors
+ * should be waited for to become readable and/or writeable are done using
+ * gst_poll_fd_ctl_read() and gst_poll_fd_ctl_write().
+ *
+ * Use gst_poll_wait() to wait for the file descriptors to actually become
+ * readable and/or writeable, or to timeout if no file descriptor is available
+ * in time. The wait can be controlled by calling gst_poll_restart() and
+ * gst_poll_set_flushing().
+ *
+ * Once the file descriptor set has been waited for, one can use
+ * gst_poll_fd_has_closed() to see if the file descriptor has been closed,
+ * gst_poll_fd_has_error() to see if it has generated an error,
+ * gst_poll_fd_can_read() to see if it is possible to read from the file
+ * descriptor, and gst_poll_fd_can_write() to see if it is possible to
+ * write to it.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gst_private.h"
+#include "glib-compat-private.h"
+
+#include <sys/types.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+
+#include <glib.h>
+
+#ifdef G_OS_WIN32
+#include <winsock2.h>
+#define EINPROGRESS WSAEINPROGRESS
+#else
+#define _GNU_SOURCE 1
+#include <sys/poll.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#endif
+
+/* OS/X needs this because of bad headers */
+#include <string.h>
+
+/* The poll() emulation on OS/X doesn't handle fds=NULL, nfds=0,
+ * so we prefer our own poll emulation.
+ */
+#if defined(BROKEN_POLL)
+#undef HAVE_POLL
+#endif
+
+#include "gstpoll.h"
+
+#define GST_CAT_DEFAULT GST_CAT_POLL
+
+#ifdef G_OS_WIN32
+typedef struct _WinsockFd WinsockFd;
+
+struct _WinsockFd
+{
+ gint fd;
+ glong event_mask;
+ WSANETWORKEVENTS events;
+ glong ignored_event_mask;
+};
+#endif
+
+typedef enum
+{
+ GST_POLL_MODE_AUTO,
+ GST_POLL_MODE_SELECT,
+ GST_POLL_MODE_PSELECT,
+ GST_POLL_MODE_POLL,
+ GST_POLL_MODE_PPOLL,
+ GST_POLL_MODE_WINDOWS
+} GstPollMode;
+
+struct _GstPoll
+{
+ GstPollMode mode;
+
+ GMutex *lock;
+ /* array of fds, always written to and read from with lock */
+ GArray *fds;
+ /* array of active fds, only written to from the waiting thread with the
+ * lock and read from with the lock or without the lock from the waiting
+ * thread */
+ GArray *active_fds;
+
+#ifndef G_OS_WIN32
+ gchar buf[1];
+ GstPollFD control_read_fd;
+ GstPollFD control_write_fd;
+#else
+ GArray *active_fds_ignored;
+ GArray *events;
+ GArray *active_events;
+
+ HANDLE wakeup_event;
+#endif
+
+ gboolean controllable;
+ volatile gint waiting;
+ volatile gint control_pending;
+ volatile gint flushing;
+ gboolean timer;
+ volatile gint rebuild;
+};
+
+static gboolean gst_poll_fd_ctl_read_unlocked (GstPoll * set, GstPollFD * fd,
+ gboolean active);
+static gboolean gst_poll_add_fd_unlocked (GstPoll * set, GstPollFD * fd);
+
+#define IS_FLUSHING(s) (g_atomic_int_get(&(s)->flushing))
+#define SET_FLUSHING(s,val) (g_atomic_int_set(&(s)->flushing, (val)))
+
+#define INC_WAITING(s) (G_ATOMIC_INT_ADD(&(s)->waiting, 1))
+#define DEC_WAITING(s) (G_ATOMIC_INT_ADD(&(s)->waiting, -1))
+#define GET_WAITING(s) (g_atomic_int_get(&(s)->waiting))
+
+#define TEST_REBUILD(s) (g_atomic_int_compare_and_exchange(&(s)->rebuild, 1, 0))
+#define MARK_REBUILD(s) (g_atomic_int_set(&(s)->rebuild, 1))
+
+#ifndef G_OS_WIN32
+#define WAKE_EVENT(s) (write ((s)->control_write_fd.fd, "W", 1) == 1)
+#define RELEASE_EVENT(s) (read ((s)->control_read_fd.fd, (s)->buf, 1) == 1)
+#else
+#define WAKE_EVENT(s) (SetEvent ((s)->wakeup_event), errno = GetLastError () == NO_ERROR ? 0 : EACCES, errno == 0 ? 1 : 0)
+#define RELEASE_EVENT(s) (ResetEvent ((s)->wakeup_event))
+#endif
+
+/* the poll/select call is also performed on a control socket, that way
+ * we can send special commands to control it */
+static inline gboolean
+raise_wakeup (GstPoll * set)
+{
+ gboolean result = TRUE;
+
+ if (G_ATOMIC_INT_ADD (&set->control_pending, 1) == 0) {
+ /* raise when nothing pending */
+ GST_LOG ("%p: raise", set);
+ result = WAKE_EVENT (set);
+ }
+ return result;
+}
+
+/* note how bad things can happen when the 2 threads both raise and release the
+ * wakeup. This is however not a problem because you must always pair a raise
+ * with a release */
+static inline gboolean
+release_wakeup (GstPoll * set)
+{
+ gboolean result = TRUE;
+
+ if (g_atomic_int_dec_and_test (&set->control_pending)) {
+ GST_LOG ("%p: release", set);
+ result = RELEASE_EVENT (set);
+ }
+ return result;
+}
+
+static inline gint
+release_all_wakeup (GstPoll * set)
+{
+ gint old;
+
+ while (TRUE) {
+ if (!(old = g_atomic_int_get (&set->control_pending)))
+ /* nothing pending, just exit */
+ break;
+
+ /* try to remove all pending control messages */
+ if (g_atomic_int_compare_and_exchange (&set->control_pending, old, 0)) {
+ /* we managed to remove all messages, read the control socket */
+ if (RELEASE_EVENT (set))
+ break;
+ else
+ /* retry again until we read it successfully */
+ G_ATOMIC_INT_ADD (&set->control_pending, 1);
+ }
+ }
+ return old;
+}
+
+static gint
+find_index (GArray * array, GstPollFD * fd)
+{
+#ifndef G_OS_WIN32
+ struct pollfd *ifd;
+#else
+ WinsockFd *ifd;
+#endif
+ guint i;
+
+ /* start by assuming the index found in the fd is still valid */
+ if (fd->idx >= 0 && fd->idx < array->len) {
+#ifndef G_OS_WIN32
+ ifd = &g_array_index (array, struct pollfd, fd->idx);
+#else
+ ifd = &g_array_index (array, WinsockFd, fd->idx);
+#endif
+
+ if (ifd->fd == fd->fd) {
+ return fd->idx;
+ }
+ }
+
+ /* the pollfd array has changed and we need to lookup the fd again */
+ for (i = 0; i < array->len; i++) {
+#ifndef G_OS_WIN32
+ ifd = &g_array_index (array, struct pollfd, i);
+#else
+ ifd = &g_array_index (array, WinsockFd, i);
+#endif
+
+ if (ifd->fd == fd->fd) {
+ fd->idx = (gint) i;
+ return fd->idx;
+ }
+ }
+
+ fd->idx = -1;
+ return fd->idx;
+}
+
+#if !defined(HAVE_PPOLL) && defined(HAVE_POLL)
+/* check if all file descriptors will fit in an fd_set */
+static gboolean
+selectable_fds (const GstPoll * set)
+{
+ guint i;
+
+ g_mutex_lock (set->lock);
+ for (i = 0; i < set->fds->len; i++) {
+ struct pollfd *pfd = &g_array_index (set->fds, struct pollfd, i);
+
+ if (pfd->fd >= FD_SETSIZE)
+ goto too_many;
+ }
+ g_mutex_unlock (set->lock);
+
+ return TRUE;
+
+too_many:
+ {
+ g_mutex_unlock (set->lock);
+ return FALSE;
+ }
+}
+
+/* check if the timeout will convert to a timeout value used for poll()
+ * without a loss of precision
+ */
+static gboolean
+pollable_timeout (GstClockTime timeout)
+{
+ if (timeout == GST_CLOCK_TIME_NONE)
+ return TRUE;
+
+ /* not a nice multiple of milliseconds */
+ if (timeout % 1000000)
+ return FALSE;
+
+ return TRUE;
+}
+#endif
+
+static GstPollMode
+choose_mode (const GstPoll * set, GstClockTime timeout)
+{
+ GstPollMode mode;
+
+ if (set->mode == GST_POLL_MODE_AUTO) {
+#ifdef HAVE_PPOLL
+ mode = GST_POLL_MODE_PPOLL;
+#elif defined(HAVE_POLL)
+ if (!selectable_fds (set) || pollable_timeout (timeout)) {
+ mode = GST_POLL_MODE_POLL;
+ } else {
+#ifdef HAVE_PSELECT
+ mode = GST_POLL_MODE_PSELECT;
+#else
+ mode = GST_POLL_MODE_SELECT;
+#endif
+ }
+#elif defined(HAVE_PSELECT)
+ mode = GST_POLL_MODE_PSELECT;
+#else
+ mode = GST_POLL_MODE_SELECT;
+#endif
+ } else {
+ mode = set->mode;
+ }
+ return mode;
+}
+
+#ifndef G_OS_WIN32
+static gint
+pollfd_to_fd_set (GstPoll * set, fd_set * readfds, fd_set * writefds,
+ fd_set * errorfds)
+{
+ gint max_fd = -1;
+ guint i;
+
+ FD_ZERO (readfds);
+ FD_ZERO (writefds);
+ FD_ZERO (errorfds);
+
+ g_mutex_lock (set->lock);
+
+ for (i = 0; i < set->active_fds->len; i++) {
+ struct pollfd *pfd = &g_array_index (set->fds, struct pollfd, i);
+
+ if (pfd->fd < FD_SETSIZE) {
+ if (pfd->events & POLLIN)
+ FD_SET (pfd->fd, readfds);
+ if (pfd->events & POLLOUT)
+ FD_SET (pfd->fd, writefds);
+ if (pfd->events)
+ FD_SET (pfd->fd, errorfds);
+ if (pfd->fd > max_fd && (pfd->events & (POLLIN | POLLOUT)))
+ max_fd = pfd->fd;
+ }
+ }
+
+ g_mutex_unlock (set->lock);
+
+ return max_fd;
+}
+
+static void
+fd_set_to_pollfd (GstPoll * set, fd_set * readfds, fd_set * writefds,
+ fd_set * errorfds)
+{
+ guint i;
+
+ g_mutex_lock (set->lock);
+
+ for (i = 0; i < set->active_fds->len; i++) {
+ struct pollfd *pfd = &g_array_index (set->active_fds, struct pollfd, i);
+
+ if (pfd->fd < FD_SETSIZE) {
+ pfd->revents = 0;
+ if (FD_ISSET (pfd->fd, readfds))
+ pfd->revents |= POLLIN;
+ if (FD_ISSET (pfd->fd, writefds))
+ pfd->revents |= POLLOUT;
+ if (FD_ISSET (pfd->fd, errorfds))
+ pfd->revents |= POLLERR;
+ }
+ }
+
+ g_mutex_unlock (set->lock);
+}
+#else /* G_OS_WIN32 */
+/*
+ * Translate errors thrown by the Winsock API used by GstPoll:
+ * WSAEventSelect, WSAWaitForMultipleEvents and WSAEnumNetworkEvents
+ */
+static gint
+gst_poll_winsock_error_to_errno (DWORD last_error)
+{
+ switch (last_error) {
+ case WSA_INVALID_HANDLE:
+ case WSAEINVAL:
+ case WSAENOTSOCK:
+ return EBADF;
+
+ case WSA_NOT_ENOUGH_MEMORY:
+ return ENOMEM;
+
+ /*
+ * Anything else, including:
+ * WSA_INVALID_PARAMETER, WSAEFAULT, WSAEINPROGRESS, WSAENETDOWN,
+ * WSANOTINITIALISED
+ */
+ default:
+ return EINVAL;
+ }
+}
+
+static void
+gst_poll_free_winsock_event (GstPoll * set, gint idx)
+{
+ WinsockFd *wfd = &g_array_index (set->fds, WinsockFd, idx);
+ HANDLE event = g_array_index (set->events, HANDLE, idx);
+
+ WSAEventSelect (wfd->fd, event, 0);
+ CloseHandle (event);
+}
+
+static void
+gst_poll_update_winsock_event_mask (GstPoll * set, gint idx, glong flags,
+ gboolean active)
+{
+ WinsockFd *wfd;
+
+ wfd = &g_array_index (set->fds, WinsockFd, idx);
+
+ if (active)
+ wfd->event_mask |= flags;
+ else
+ wfd->event_mask &= ~flags;
+
+ /* reset ignored state if the new mask doesn't overlap at all */
+ if ((wfd->ignored_event_mask & wfd->event_mask) == 0)
+ wfd->ignored_event_mask = 0;
+}
+
+static gboolean
+gst_poll_prepare_winsock_active_sets (GstPoll * set)
+{
+ guint i;
+
+ g_array_set_size (set->active_fds, 0);
+ g_array_set_size (set->active_fds_ignored, 0);
+ g_array_set_size (set->active_events, 0);
+ g_array_append_val (set->active_events, set->wakeup_event);
+
+ for (i = 0; i < set->fds->len; i++) {
+ WinsockFd *wfd = &g_array_index (set->fds, WinsockFd, i);
+ HANDLE event = g_array_index (set->events, HANDLE, i);
+
+ if (wfd->ignored_event_mask == 0) {
+ gint ret;
+
+ g_array_append_val (set->active_fds, *wfd);
+ g_array_append_val (set->active_events, event);
+
+ ret = WSAEventSelect (wfd->fd, event, wfd->event_mask);
+ if (G_UNLIKELY (ret != 0)) {
+ errno = gst_poll_winsock_error_to_errno (WSAGetLastError ());
+ return FALSE;
+ }
+ } else {
+ g_array_append_val (set->active_fds_ignored, wfd);
+ }
+ }
+
+ return TRUE;
+}
+
+static gint
+gst_poll_collect_winsock_events (GstPoll * set)
+{
+ gint res, i;
+
+ /*
+ * We need to check which events are signaled, and call
+ * WSAEnumNetworkEvents for those that are, which resets
+ * the event and clears the internal network event records.
+ */
+ res = 0;
+ for (i = 0; i < set->active_fds->len; i++) {
+ WinsockFd *wfd = &g_array_index (set->active_fds, WinsockFd, i);
+ HANDLE event = g_array_index (set->active_events, HANDLE, i + 1);
+ DWORD wait_ret;
+
+ wait_ret = WaitForSingleObject (event, 0);
+ if (wait_ret == WAIT_OBJECT_0) {
+ gint enum_ret = WSAEnumNetworkEvents (wfd->fd, event, &wfd->events);
+
+ if (G_UNLIKELY (enum_ret != 0)) {
+ res = -1;
+ errno = gst_poll_winsock_error_to_errno (WSAGetLastError ());
+ break;
+ }
+
+ res++;
+ } else {
+ /* clear any previously stored result */
+ memset (&wfd->events, 0, sizeof (wfd->events));
+ }
+ }
+
+ /* If all went well we also need to reset the ignored fds. */
+ if (res >= 0) {
+ res += set->active_fds_ignored->len;
+
+ for (i = 0; i < set->active_fds_ignored->len; i++) {
+ WinsockFd *wfd = g_array_index (set->active_fds_ignored, WinsockFd *, i);
+
+ wfd->ignored_event_mask = 0;
+ }
+
+ g_array_set_size (set->active_fds_ignored, 0);
+ }
+
+ return res;
+}
+#endif
+
+/**
+ * gst_poll_new:
+ * @controllable: whether it should be possible to control a wait.
+ *
+ * Create a new file descriptor set. If @controllable, it
+ * is possible to restart or flush a call to gst_poll_wait() with
+ * gst_poll_restart() and gst_poll_set_flushing() respectively.
+ *
+ * Free-function: gst_poll_free
+ *
+ * Returns: (transfer full): a new #GstPoll, or %NULL in case of an error.
+ * Free with gst_poll_free().
+ *
+ * Since: 0.10.18
+ */
+GstPoll *
+gst_poll_new (gboolean controllable)
+{
+ GstPoll *nset;
+
+ GST_DEBUG ("controllable : %d", controllable);
+
+ nset = g_slice_new0 (GstPoll);
+ nset->lock = g_mutex_new ();
+#ifndef G_OS_WIN32
+ nset->mode = GST_POLL_MODE_AUTO;
+ nset->fds = g_array_new (FALSE, FALSE, sizeof (struct pollfd));
+ nset->active_fds = g_array_new (FALSE, FALSE, sizeof (struct pollfd));
+ nset->control_read_fd.fd = -1;
+ nset->control_write_fd.fd = -1;
+ {
+ gint control_sock[2];
+
+ if (socketpair (PF_UNIX, SOCK_STREAM, 0, control_sock) < 0)
+ goto no_socket_pair;
+
+ fcntl (control_sock[0], F_SETFL, O_NONBLOCK);
+ fcntl (control_sock[1], F_SETFL, O_NONBLOCK);
+
+ nset->control_read_fd.fd = control_sock[0];
+ nset->control_write_fd.fd = control_sock[1];
+
+ gst_poll_add_fd_unlocked (nset, &nset->control_read_fd);
+ gst_poll_fd_ctl_read_unlocked (nset, &nset->control_read_fd, TRUE);
+ }
+#else
+ nset->mode = GST_POLL_MODE_WINDOWS;
+ nset->fds = g_array_new (FALSE, FALSE, sizeof (WinsockFd));
+ nset->active_fds = g_array_new (FALSE, FALSE, sizeof (WinsockFd));
+ nset->active_fds_ignored = g_array_new (FALSE, FALSE, sizeof (WinsockFd *));
+ nset->events = g_array_new (FALSE, FALSE, sizeof (HANDLE));
+ nset->active_events = g_array_new (FALSE, FALSE, sizeof (HANDLE));
+
+ nset->wakeup_event = CreateEvent (NULL, TRUE, FALSE, NULL);
+#endif
+
+ /* ensure (re)build, though already sneakily set in non-windows case */
+ MARK_REBUILD (nset);
+
+ nset->controllable = controllable;
+
+ return nset;
+
+ /* ERRORS */
+#ifndef G_OS_WIN32
+no_socket_pair:
+ {
+ GST_WARNING ("%p: can't create socket pair !", nset);
+ gst_poll_free (nset);
+ return NULL;
+ }
+#endif
+}
+
+/**
+ * gst_poll_new_timer:
+ *
+ * Create a new poll object that can be used for scheduling cancellable
+ * timeouts.
+ *
+ * A timeout is performed with gst_poll_wait(). Multiple timeouts can be
+ * performed from different threads.
+ *
+ * Free-function: gst_poll_free
+ *
+ * Returns: (transfer full): a new #GstPoll, or %NULL in case of an error.
+ * Free with gst_poll_free().
+ *
+ * Since: 0.10.23
+ */
+GstPoll *
+gst_poll_new_timer (void)
+{
+ GstPoll *poll;
+
+ /* make a new controllable poll set */
+ if (!(poll = gst_poll_new (TRUE)))
+ goto done;
+
+ /* we are a timer */
+ poll->timer = TRUE;
+
+done:
+ return poll;
+}
+
+/**
+ * gst_poll_free:
+ * @set: (transfer full): a file descriptor set.
+ *
+ * Free a file descriptor set.
+ *
+ * Since: 0.10.18
+ */
+void
+gst_poll_free (GstPoll * set)
+{
+ g_return_if_fail (set != NULL);
+
+ GST_DEBUG ("%p: freeing", set);
+
+#ifndef G_OS_WIN32
+ if (set->control_write_fd.fd >= 0)
+ close (set->control_write_fd.fd);
+ if (set->control_read_fd.fd >= 0)
+ close (set->control_read_fd.fd);
+#else
+ CloseHandle (set->wakeup_event);
+
+ {
+ guint i;
+
+ for (i = 0; i < set->events->len; i++)
+ gst_poll_free_winsock_event (set, i);
+ }
+
+ g_array_free (set->active_events, TRUE);
+ g_array_free (set->events, TRUE);
+ g_array_free (set->active_fds_ignored, TRUE);
+#endif
+
+ g_array_free (set->active_fds, TRUE);
+ g_array_free (set->fds, TRUE);
+ g_mutex_free (set->lock);
+ g_slice_free (GstPoll, set);
+}
+
+/**
+ * gst_poll_get_read_gpollfd:
+ * @set: a #GstPoll
+ * @fd: a #GPollFD
+ *
+ * Get a GPollFD for the reading part of the control socket. This is useful when
+ * integrating with a GSource and GMainLoop.
+ *
+ * Since: 0.10.32
+ */
+void
+gst_poll_get_read_gpollfd (GstPoll * set, GPollFD * fd)
+{
+ g_return_if_fail (set != NULL);
+ g_return_if_fail (fd != NULL);
+
+#ifndef G_OS_WIN32
+ fd->fd = set->control_read_fd.fd;
+#else
+#if GLIB_SIZEOF_VOID_P == 8
+ fd->fd = (gint64) set->wakeup_event;
+#else
+ fd->fd = (gint) set->wakeup_event;
+#endif
+#endif
+ fd->events = G_IO_IN | G_IO_HUP | G_IO_ERR;
+ fd->revents = 0;
+}
+
+/**
+ * gst_poll_fd_init:
+ * @fd: a #GstPollFD
+ *
+ * Initializes @fd. Alternatively you can initialize it with
+ * #GST_POLL_FD_INIT.
+ *
+ * Since: 0.10.18
+ */
+void
+gst_poll_fd_init (GstPollFD * fd)
+{
+ g_return_if_fail (fd != NULL);
+
+ fd->fd = -1;
+ fd->idx = -1;
+}
+
+static gboolean
+gst_poll_add_fd_unlocked (GstPoll * set, GstPollFD * fd)
+{
+ gint idx;
+
+ GST_DEBUG ("%p: fd (fd:%d, idx:%d)", set, fd->fd, fd->idx);
+
+ idx = find_index (set->fds, fd);
+ if (idx < 0) {
+#ifndef G_OS_WIN32
+ struct pollfd nfd;
+
+ nfd.fd = fd->fd;
+ nfd.events = POLLERR | POLLNVAL | POLLHUP;
+ nfd.revents = 0;
+
+ g_array_append_val (set->fds, nfd);
+
+ fd->idx = set->fds->len - 1;
+#else
+ WinsockFd wfd;
+ HANDLE event;
+
+ wfd.fd = fd->fd;
+ wfd.event_mask = FD_CLOSE;
+ memset (&wfd.events, 0, sizeof (wfd.events));
+ wfd.ignored_event_mask = 0;
+ event = WSACreateEvent ();
+
+ g_array_append_val (set->fds, wfd);
+ g_array_append_val (set->events, event);
+
+ fd->idx = set->fds->len - 1;
+#endif
+ MARK_REBUILD (set);
+ } else {
+ GST_WARNING ("%p: couldn't find fd !", set);
+ }
+
+ return TRUE;
+}
+
+/**
+ * gst_poll_add_fd:
+ * @set: a file descriptor set.
+ * @fd: a file descriptor.
+ *
+ * Add a file descriptor to the file descriptor set.
+ *
+ * Returns: %TRUE if the file descriptor was successfully added to the set.
+ *
+ * Since: 0.10.18
+ */
+gboolean
+gst_poll_add_fd (GstPoll * set, GstPollFD * fd)
+{
+ gboolean ret;
+
+ g_return_val_if_fail (set != NULL, FALSE);
+ g_return_val_if_fail (fd != NULL, FALSE);
+ g_return_val_if_fail (fd->fd >= 0, FALSE);
+
+ g_mutex_lock (set->lock);
+
+ ret = gst_poll_add_fd_unlocked (set, fd);
+
+ g_mutex_unlock (set->lock);
+
+ return ret;
+}
+
+/**
+ * gst_poll_remove_fd:
+ * @set: a file descriptor set.
+ * @fd: a file descriptor.
+ *
+ * Remove a file descriptor from the file descriptor set.
+ *
+ * Returns: %TRUE if the file descriptor was successfully removed from the set.
+ *
+ * Since: 0.10.18
+ */
+gboolean
+gst_poll_remove_fd (GstPoll * set, GstPollFD * fd)
+{
+ gint idx;
+
+ g_return_val_if_fail (set != NULL, FALSE);
+ g_return_val_if_fail (fd != NULL, FALSE);
+ g_return_val_if_fail (fd->fd >= 0, FALSE);
+
+
+ GST_DEBUG ("%p: fd (fd:%d, idx:%d)", set, fd->fd, fd->idx);
+
+ g_mutex_lock (set->lock);
+
+ /* get the index, -1 is an fd that is not added */
+ idx = find_index (set->fds, fd);
+ if (idx >= 0) {
+#ifdef G_OS_WIN32
+ gst_poll_free_winsock_event (set, idx);
+ g_array_remove_index_fast (set->events, idx);
+#endif
+
+ /* remove the fd at index, we use _remove_index_fast, which copies the last
+ * element of the array to the freed index */
+ g_array_remove_index_fast (set->fds, idx);
+
+ /* mark fd as removed by setting the index to -1 */
+ fd->idx = -1;
+ MARK_REBUILD (set);
+ } else {
+ GST_WARNING ("%p: couldn't find fd !", set);
+ }
+
+ g_mutex_unlock (set->lock);
+
+ return idx >= 0;
+}
+
+/**
+ * gst_poll_fd_ctl_write:
+ * @set: a file descriptor set.
+ * @fd: a file descriptor.
+ * @active: a new status.
+ *
+ * Control whether the descriptor @fd in @set will be monitored for
+ * writability.
+ *
+ * Returns: %TRUE if the descriptor was successfully updated.
+ *
+ * Since: 0.10.18
+ */
+gboolean
+gst_poll_fd_ctl_write (GstPoll * set, GstPollFD * fd, gboolean active)
+{
+ gint idx;
+
+ g_return_val_if_fail (set != NULL, FALSE);
+ g_return_val_if_fail (fd != NULL, FALSE);
+ g_return_val_if_fail (fd->fd >= 0, FALSE);
+
+ GST_DEBUG ("%p: fd (fd:%d, idx:%d), active : %d", set,
+ fd->fd, fd->idx, active);
+
+ g_mutex_lock (set->lock);
+
+ idx = find_index (set->fds, fd);
+ if (idx >= 0) {
+#ifndef G_OS_WIN32
+ struct pollfd *pfd = &g_array_index (set->fds, struct pollfd, idx);
+
+ if (active)
+ pfd->events |= POLLOUT;
+ else
+ pfd->events &= ~POLLOUT;
+
+ GST_LOG ("pfd->events now %d (POLLOUT:%d)", pfd->events, POLLOUT);
+#else
+ gst_poll_update_winsock_event_mask (set, idx, FD_WRITE | FD_CONNECT,
+ active);
+#endif
+ MARK_REBUILD (set);
+ } else {
+ GST_WARNING ("%p: couldn't find fd !", set);
+ }
+
+ g_mutex_unlock (set->lock);
+
+ return idx >= 0;
+}
+
+static gboolean
+gst_poll_fd_ctl_read_unlocked (GstPoll * set, GstPollFD * fd, gboolean active)
+{
+ gint idx;
+
+ GST_DEBUG ("%p: fd (fd:%d, idx:%d), active : %d", set,
+ fd->fd, fd->idx, active);
+
+ idx = find_index (set->fds, fd);
+
+ if (idx >= 0) {
+#ifndef G_OS_WIN32
+ struct pollfd *pfd = &g_array_index (set->fds, struct pollfd, idx);
+
+ if (active)
+ pfd->events |= (POLLIN | POLLPRI);
+ else
+ pfd->events &= ~(POLLIN | POLLPRI);
+#else
+ gst_poll_update_winsock_event_mask (set, idx, FD_READ | FD_ACCEPT, active);
+#endif
+ MARK_REBUILD (set);
+ } else {
+ GST_WARNING ("%p: couldn't find fd !", set);
+ }
+
+ return idx >= 0;
+}
+
+/**
+ * gst_poll_fd_ctl_read:
+ * @set: a file descriptor set.
+ * @fd: a file descriptor.
+ * @active: a new status.
+ *
+ * Control whether the descriptor @fd in @set will be monitored for
+ * readability.
+ *
+ * Returns: %TRUE if the descriptor was successfully updated.
+ *
+ * Since: 0.10.18
+ */
+gboolean
+gst_poll_fd_ctl_read (GstPoll * set, GstPollFD * fd, gboolean active)
+{
+ gboolean ret;
+
+ g_return_val_if_fail (set != NULL, FALSE);
+ g_return_val_if_fail (fd != NULL, FALSE);
+ g_return_val_if_fail (fd->fd >= 0, FALSE);
+
+ g_mutex_lock (set->lock);
+
+ ret = gst_poll_fd_ctl_read_unlocked (set, fd, active);
+
+ g_mutex_unlock (set->lock);
+
+ return ret;
+}
+
+/**
+ * gst_poll_fd_ignored:
+ * @set: a file descriptor set.
+ * @fd: a file descriptor.
+ *
+ * Mark @fd as ignored so that the next call to gst_poll_wait() will yield
+ * the same result for @fd as last time. This function must be called if no
+ * operation (read/write/recv/send/etc.) will be performed on @fd before
+ * the next call to gst_poll_wait().
+ *
+ * The reason why this is needed is because the underlying implementation
+ * might not allow querying the fd more than once between calls to one of
+ * the re-enabling operations.
+ *
+ * Since: 0.10.18
+ */
+void
+gst_poll_fd_ignored (GstPoll * set, GstPollFD * fd)
+{
+#ifdef G_OS_WIN32
+ gint idx;
+
+ g_return_if_fail (set != NULL);
+ g_return_if_fail (fd != NULL);
+ g_return_if_fail (fd->fd >= 0);
+
+ g_mutex_lock (set->lock);
+
+ idx = find_index (set->fds, fd);
+ if (idx >= 0) {
+ WinsockFd *wfd = &g_array_index (set->fds, WinsockFd, idx);
+
+ wfd->ignored_event_mask = wfd->event_mask & (FD_READ | FD_WRITE);
+ MARK_REBUILD (set);
+ }
+
+ g_mutex_unlock (set->lock);
+#endif
+}
+
+/**
+ * gst_poll_fd_has_closed:
+ * @set: a file descriptor set.
+ * @fd: a file descriptor.
+ *
+ * Check if @fd in @set has closed the connection.
+ *
+ * Returns: %TRUE if the connection was closed.
+ *
+ * Since: 0.10.18
+ */
+gboolean
+gst_poll_fd_has_closed (const GstPoll * set, GstPollFD * fd)
+{
+ gboolean res = FALSE;
+ gint idx;
+
+ g_return_val_if_fail (set != NULL, FALSE);
+ g_return_val_if_fail (fd != NULL, FALSE);
+ g_return_val_if_fail (fd->fd >= 0, FALSE);
+
+ GST_DEBUG ("%p: fd (fd:%d, idx:%d)", set, fd->fd, fd->idx);
+
+ g_mutex_lock (set->lock);
+
+ idx = find_index (set->active_fds, fd);
+ if (idx >= 0) {
+#ifndef G_OS_WIN32
+ struct pollfd *pfd = &g_array_index (set->active_fds, struct pollfd, idx);
+
+ res = (pfd->revents & POLLHUP) != 0;
+#else
+ WinsockFd *wfd = &g_array_index (set->active_fds, WinsockFd, idx);
+
+ res = (wfd->events.lNetworkEvents & FD_CLOSE) != 0;
+#endif
+ } else {
+ GST_WARNING ("%p: couldn't find fd !", set);
+ }
+
+ g_mutex_unlock (set->lock);
+
+ return res;
+}
+
+/**
+ * gst_poll_fd_has_error:
+ * @set: a file descriptor set.
+ * @fd: a file descriptor.
+ *
+ * Check if @fd in @set has an error.
+ *
+ * Returns: %TRUE if the descriptor has an error.
+ *
+ * Since: 0.10.18
+ */
+gboolean
+gst_poll_fd_has_error (const GstPoll * set, GstPollFD * fd)
+{
+ gboolean res = FALSE;
+ gint idx;
+
+ g_return_val_if_fail (set != NULL, FALSE);
+ g_return_val_if_fail (fd != NULL, FALSE);
+ g_return_val_if_fail (fd->fd >= 0, FALSE);
+
+ GST_DEBUG ("%p: fd (fd:%d, idx:%d)", set, fd->fd, fd->idx);
+
+ g_mutex_lock (set->lock);
+
+ idx = find_index (set->active_fds, fd);
+ if (idx >= 0) {
+#ifndef G_OS_WIN32
+ struct pollfd *pfd = &g_array_index (set->active_fds, struct pollfd, idx);
+
+ res = (pfd->revents & (POLLERR | POLLNVAL)) != 0;
+#else
+ WinsockFd *wfd = &g_array_index (set->active_fds, WinsockFd, idx);
+
+ res = (wfd->events.iErrorCode[FD_CLOSE_BIT] != 0) ||
+ (wfd->events.iErrorCode[FD_READ_BIT] != 0) ||
+ (wfd->events.iErrorCode[FD_WRITE_BIT] != 0) ||
+ (wfd->events.iErrorCode[FD_ACCEPT_BIT] != 0) ||
+ (wfd->events.iErrorCode[FD_CONNECT_BIT] != 0);
+#endif
+ } else {
+ GST_WARNING ("%p: couldn't find fd !", set);
+ }
+
+ g_mutex_unlock (set->lock);
+
+ return res;
+}
+
+static gboolean
+gst_poll_fd_can_read_unlocked (const GstPoll * set, GstPollFD * fd)
+{
+ gboolean res = FALSE;
+ gint idx;
+
+ GST_DEBUG ("%p: fd (fd:%d, idx:%d)", set, fd->fd, fd->idx);
+
+ idx = find_index (set->active_fds, fd);
+ if (idx >= 0) {
+#ifndef G_OS_WIN32
+ struct pollfd *pfd = &g_array_index (set->active_fds, struct pollfd, idx);
+
+ res = (pfd->revents & (POLLIN | POLLPRI)) != 0;
+#else
+ WinsockFd *wfd = &g_array_index (set->active_fds, WinsockFd, idx);
+
+ res = (wfd->events.lNetworkEvents & (FD_READ | FD_ACCEPT)) != 0;
+#endif
+ } else {
+ GST_WARNING ("%p: couldn't find fd !", set);
+ }
+
+ return res;
+}
+
+/**
+ * gst_poll_fd_can_read:
+ * @set: a file descriptor set.
+ * @fd: a file descriptor.
+ *
+ * Check if @fd in @set has data to be read.
+ *
+ * Returns: %TRUE if the descriptor has data to be read.
+ *
+ * Since: 0.10.18
+ */
+gboolean
+gst_poll_fd_can_read (const GstPoll * set, GstPollFD * fd)
+{
+ gboolean res = FALSE;
+
+ g_return_val_if_fail (set != NULL, FALSE);
+ g_return_val_if_fail (fd != NULL, FALSE);
+ g_return_val_if_fail (fd->fd >= 0, FALSE);
+
+ g_mutex_lock (set->lock);
+
+ res = gst_poll_fd_can_read_unlocked (set, fd);
+
+ g_mutex_unlock (set->lock);
+
+ return res;
+}
+
+/**
+ * gst_poll_fd_can_write:
+ * @set: a file descriptor set.
+ * @fd: a file descriptor.
+ *
+ * Check if @fd in @set can be used for writing.
+ *
+ * Returns: %TRUE if the descriptor can be used for writing.
+ *
+ * Since: 0.10.18
+ */
+gboolean
+gst_poll_fd_can_write (const GstPoll * set, GstPollFD * fd)
+{
+ gboolean res = FALSE;
+ gint idx;
+
+ g_return_val_if_fail (set != NULL, FALSE);
+ g_return_val_if_fail (fd != NULL, FALSE);
+ g_return_val_if_fail (fd->fd >= 0, FALSE);
+
+ GST_DEBUG ("%p: fd (fd:%d, idx:%d)", set, fd->fd, fd->idx);
+
+ g_mutex_lock (set->lock);
+
+ idx = find_index (set->active_fds, fd);
+ if (idx >= 0) {
+#ifndef G_OS_WIN32
+ struct pollfd *pfd = &g_array_index (set->active_fds, struct pollfd, idx);
+
+ res = (pfd->revents & POLLOUT) != 0;
+#else
+ WinsockFd *wfd = &g_array_index (set->active_fds, WinsockFd, idx);
+
+ res = (wfd->events.lNetworkEvents & FD_WRITE) != 0;
+#endif
+ } else {
+ GST_WARNING ("%p: couldn't find fd !", set);
+ }
+
+ g_mutex_unlock (set->lock);
+
+ return res;
+}
+
+/**
+ * gst_poll_wait:
+ * @set: a #GstPoll.
+ * @timeout: a timeout in nanoseconds.
+ *
+ * Wait for activity on the file descriptors in @set. This function waits up to
+ * the specified @timeout. A timeout of #GST_CLOCK_TIME_NONE waits forever.
+ *
+ * For #GstPoll objects created with gst_poll_new(), this function can only be
+ * called from a single thread at a time. If called from multiple threads,
+ * -1 will be returned with errno set to EPERM.
+ *
+ * This is not true for timer #GstPoll objects created with
+ * gst_poll_new_timer(), where it is allowed to have multiple threads waiting
+ * simultaneously.
+ *
+ * Returns: The number of #GstPollFD in @set that have activity or 0 when no
+ * activity was detected after @timeout. If an error occurs, -1 is returned
+ * and errno is set.
+ *
+ * Since: 0.10.18
+ */
+gint
+gst_poll_wait (GstPoll * set, GstClockTime timeout)
+{
+ gboolean restarting;
+ gboolean is_timer;
+ int res;
+ gint old_waiting;
+
+ g_return_val_if_fail (set != NULL, -1);
+
+ GST_DEBUG ("timeout :%" GST_TIME_FORMAT, GST_TIME_ARGS (timeout));
+
+ is_timer = set->timer;
+
+ /* add one more waiter */
+ old_waiting = INC_WAITING (set);
+
+ /* we cannot wait from multiple threads unless we are a timer */
+ if (G_UNLIKELY (old_waiting > 0 && !is_timer))
+ goto already_waiting;
+
+ /* flushing, exit immediately */
+ if (G_UNLIKELY (IS_FLUSHING (set)))
+ goto flushing;
+
+ do {
+ GstPollMode mode;
+
+ res = -1;
+ restarting = FALSE;
+
+ mode = choose_mode (set, timeout);
+
+ if (TEST_REBUILD (set)) {
+ g_mutex_lock (set->lock);
+#ifndef G_OS_WIN32
+ g_array_set_size (set->active_fds, set->fds->len);
+ memcpy (set->active_fds->data, set->fds->data,
+ set->fds->len * sizeof (struct pollfd));
+#else
+ if (!gst_poll_prepare_winsock_active_sets (set))
+ goto winsock_error;
+#endif
+ g_mutex_unlock (set->lock);
+ }
+
+ switch (mode) {
+ case GST_POLL_MODE_AUTO:
+ g_assert_not_reached ();
+ break;
+ case GST_POLL_MODE_PPOLL:
+ {
+#ifdef HAVE_PPOLL
+ struct timespec ts;
+ struct timespec *tsptr;
+
+ if (timeout != GST_CLOCK_TIME_NONE) {
+ GST_TIME_TO_TIMESPEC (timeout, ts);
+ tsptr = &ts;
+ } else {
+ tsptr = NULL;
+ }
+
+ res =
+ ppoll ((struct pollfd *) set->active_fds->data,
+ set->active_fds->len, tsptr, NULL);
+#else
+ g_assert_not_reached ();
+ errno = ENOSYS;
+#endif
+ break;
+ }
+ case GST_POLL_MODE_POLL:
+ {
+#ifdef HAVE_POLL
+ gint t;
+
+ if (timeout != GST_CLOCK_TIME_NONE) {
+ t = GST_TIME_AS_MSECONDS (timeout);
+ } else {
+ t = -1;
+ }
+
+ res =
+ poll ((struct pollfd *) set->active_fds->data,
+ set->active_fds->len, t);
+#else
+ g_assert_not_reached ();
+ errno = ENOSYS;
+#endif
+ break;
+ }
+ case GST_POLL_MODE_PSELECT:
+#ifndef HAVE_PSELECT
+ {
+ g_assert_not_reached ();
+ errno = ENOSYS;
+ break;
+ }
+#endif
+ case GST_POLL_MODE_SELECT:
+ {
+#ifndef G_OS_WIN32
+ fd_set readfds;
+ fd_set writefds;
+ fd_set errorfds;
+ gint max_fd;
+
+ max_fd = pollfd_to_fd_set (set, &readfds, &writefds, &errorfds);
+
+ if (mode == GST_POLL_MODE_SELECT) {
+ struct timeval tv;
+ struct timeval *tvptr;
+
+ if (timeout != GST_CLOCK_TIME_NONE) {
+ GST_TIME_TO_TIMEVAL (timeout, tv);
+ tvptr = &tv;
+ } else {
+ tvptr = NULL;
+ }
+
+ GST_DEBUG ("Calling select");
+ res = select (max_fd + 1, &readfds, &writefds, &errorfds, tvptr);
+ GST_DEBUG ("After select, res:%d", res);
+ } else {
+#ifdef HAVE_PSELECT
+ struct timespec ts;
+ struct timespec *tsptr;
+
+ if (timeout != GST_CLOCK_TIME_NONE) {
+ GST_TIME_TO_TIMESPEC (timeout, ts);
+ tsptr = &ts;
+ } else {
+ tsptr = NULL;
+ }
+
+ GST_DEBUG ("Calling pselect");
+ res =
+ pselect (max_fd + 1, &readfds, &writefds, &errorfds, tsptr, NULL);
+ GST_DEBUG ("After pselect, res:%d", res);
+#endif
+ }
+
+ if (res >= 0) {
+ fd_set_to_pollfd (set, &readfds, &writefds, &errorfds);
+ }
+#else /* G_OS_WIN32 */
+ g_assert_not_reached ();
+ errno = ENOSYS;
+#endif
+ break;
+ }
+ case GST_POLL_MODE_WINDOWS:
+ {
+#ifdef G_OS_WIN32
+ gint ignore_count = set->active_fds_ignored->len;
+ DWORD t, wait_ret;
+
+ if (G_LIKELY (ignore_count == 0)) {
+ if (timeout != GST_CLOCK_TIME_NONE)
+ t = GST_TIME_AS_MSECONDS (timeout);
+ else
+ t = INFINITE;
+ } else {
+ /* already one or more ignored fds, so we quickly sweep the others */
+ t = 0;
+ }
+
+ if (set->active_events->len != 0) {
+ wait_ret = WSAWaitForMultipleEvents (set->active_events->len,
+ (HANDLE *) set->active_events->data, FALSE, t, FALSE);
+ } else {
+ wait_ret = WSA_WAIT_FAILED;
+ WSASetLastError (WSA_INVALID_PARAMETER);
+ }
+
+ if (ignore_count == 0 && wait_ret == WSA_WAIT_TIMEOUT) {
+ res = 0;
+ } else if (wait_ret == WSA_WAIT_FAILED) {
+ res = -1;
+ errno = gst_poll_winsock_error_to_errno (WSAGetLastError ());
+ } else {
+ /* the first entry is the wakeup event */
+ if (wait_ret - WSA_WAIT_EVENT_0 >= 1) {
+ res = gst_poll_collect_winsock_events (set);
+ } else {
+ res = 1; /* wakeup event */
+ }
+ }
+#else
+ g_assert_not_reached ();
+ errno = ENOSYS;
+#endif
+ break;
+ }
+ }
+
+ if (!is_timer) {
+ /* Applications needs to clear the control socket themselves for timer
+ * polls.
+ * For other polls, we need to clear the control socket. If there was only
+ * one socket with activity and it was the control socket, we need to
+ * restart */
+ if (release_all_wakeup (set) > 0 && res == 1)
+ restarting = TRUE;
+ }
+
+ /* we got woken up and we are flushing, we need to stop */
+ if (G_UNLIKELY (IS_FLUSHING (set)))
+ goto flushing;
+
+ } while (G_UNLIKELY (restarting));
+
+ DEC_WAITING (set);
+
+ return res;
+
+ /* ERRORS */
+already_waiting:
+ {
+ GST_LOG ("%p: we are already waiting", set);
+ DEC_WAITING (set);
+ errno = EPERM;
+ return -1;
+ }
+flushing:
+ {
+ GST_LOG ("%p: we are flushing", set);
+ DEC_WAITING (set);
+ errno = EBUSY;
+ return -1;
+ }
+#ifdef G_OS_WIN32
+winsock_error:
+ {
+ GST_LOG ("%p: winsock error", set);
+ g_mutex_unlock (set->lock);
+ DEC_WAITING (set);
+ return -1;
+ }
+#endif
+}
+
+/**
+ * gst_poll_set_controllable:
+ * @set: a #GstPoll.
+ * @controllable: new controllable state.
+ *
+ * When @controllable is %TRUE, this function ensures that future calls to
+ * gst_poll_wait() will be affected by gst_poll_restart() and
+ * gst_poll_set_flushing().
+ *
+ * Returns: %TRUE if the controllability of @set could be updated.
+ *
+ * Since: 0.10.18
+ */
+gboolean
+gst_poll_set_controllable (GstPoll * set, gboolean controllable)
+{
+ g_return_val_if_fail (set != NULL, FALSE);
+
+ GST_LOG ("%p: controllable : %d", set, controllable);
+
+ set->controllable = controllable;
+
+ return TRUE;
+}
+
+/**
+ * gst_poll_restart:
+ * @set: a #GstPoll.
+ *
+ * Restart any gst_poll_wait() that is in progress. This function is typically
+ * used after adding or removing descriptors to @set.
+ *
+ * If @set is not controllable, then this call will have no effect.
+ *
+ * Since: 0.10.18
+ */
+void
+gst_poll_restart (GstPoll * set)
+{
+ g_return_if_fail (set != NULL);
+
+ if (set->controllable && GET_WAITING (set) > 0) {
+ /* we are controllable and waiting, wake up the waiter. The socket will be
+ * cleared by the _wait() thread and the poll will be restarted */
+ raise_wakeup (set);
+ }
+}
+
+/**
+ * gst_poll_set_flushing:
+ * @set: a #GstPoll.
+ * @flushing: new flushing state.
+ *
+ * When @flushing is %TRUE, this function ensures that current and future calls
+ * to gst_poll_wait() will return -1, with errno set to EBUSY.
+ *
+ * Unsetting the flushing state will restore normal operation of @set.
+ *
+ * Since: 0.10.18
+ */
+void
+gst_poll_set_flushing (GstPoll * set, gboolean flushing)
+{
+ g_return_if_fail (set != NULL);
+
+ GST_LOG ("%p: flushing: %d", set, flushing);
+
+ /* update the new state first */
+ SET_FLUSHING (set, flushing);
+
+ if (flushing && set->controllable && GET_WAITING (set) > 0) {
+ /* we are flushing, controllable and waiting, wake up the waiter. When we
+ * stop the flushing operation we don't clear the wakeup fd here, this will
+ * happen in the _wait() thread. */
+ raise_wakeup (set);
+ }
+}
+
+/**
+ * gst_poll_write_control:
+ * @set: a #GstPoll.
+ *
+ * Write a byte to the control socket of the controllable @set.
+ * This function is mostly useful for timer #GstPoll objects created with
+ * gst_poll_new_timer().
+ *
+ * It will make any current and future gst_poll_wait() function return with
+ * 1, meaning the control socket is set. After an equal amount of calls to
+ * gst_poll_read_control() have been performed, calls to gst_poll_wait() will
+ * block again until their timeout expired.
+ *
+ * Returns: %TRUE on success. %FALSE when @set is not controllable or when the
+ * byte could not be written.
+ *
+ * Since: 0.10.23
+ */
+gboolean
+gst_poll_write_control (GstPoll * set)
+{
+ gboolean res;
+
+ g_return_val_if_fail (set != NULL, FALSE);
+ g_return_val_if_fail (set->timer, FALSE);
+
+ res = raise_wakeup (set);
+
+ return res;
+}
+
+/**
+ * gst_poll_read_control:
+ * @set: a #GstPoll.
+ *
+ * Read a byte from the control socket of the controllable @set.
+ * This function is mostly useful for timer #GstPoll objects created with
+ * gst_poll_new_timer().
+ *
+ * Returns: %TRUE on success. %FALSE when @set is not controllable or when there
+ * was no byte to read.
+ *
+ * Since: 0.10.23
+ */
+gboolean
+gst_poll_read_control (GstPoll * set)
+{
+ gboolean res;
+
+ g_return_val_if_fail (set != NULL, FALSE);
+ g_return_val_if_fail (set->timer, FALSE);
+
+ res = release_wakeup (set);
+
+ return res;
+}
diff --git a/gst/gstpoll.h b/gst/gstpoll.h
new file mode 100644
index 0000000..741a79b
--- /dev/null
+++ b/gst/gstpoll.h
@@ -0,0 +1,97 @@
+/* GStreamer
+ * Copyright (C) 1999 Erik Walthinsen <omega@cse.ogi.edu>
+ * Copyright (C) 2004 Wim Taymans <wim.taymans@gmail.com>
+ * Copyright (C) 2007 Peter Kjellerstedt <pkj@axis.com>
+ *
+ * gstpoll.h: File descriptor set
+ *
+ * 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_POLL_H__
+#define __GST_POLL_H__
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <gst/gstclock.h>
+
+G_BEGIN_DECLS
+
+/**
+ * GstPoll:
+ *
+ * A set of file/network descriptors.
+ */
+typedef struct _GstPoll GstPoll;
+
+/**
+ * GstPollFD:
+ * @fd: a file descriptor
+ *
+ * A file descriptor object.
+ */
+typedef struct {
+ int fd;
+
+ /*< private >*/
+ gint idx;
+} GstPollFD;
+
+/**
+ * GST_POLL_FD_INIT:
+ *
+ * A #GstPollFD must be initialized with this macro, before it can be
+ * used. This macro can used be to initialize a variable, but it cannot
+ * be assigned to a variable. In that case you have to use
+ * gst_poll_fd_init().
+ *
+ * Since: 0.10.18
+ */
+#define GST_POLL_FD_INIT { -1, -1 }
+
+GstPoll* gst_poll_new (gboolean controllable);
+GstPoll* gst_poll_new_timer (void);
+void gst_poll_free (GstPoll *set);
+
+void gst_poll_get_read_gpollfd (GstPoll *set, GPollFD *fd);
+
+void gst_poll_fd_init (GstPollFD *fd);
+
+gboolean gst_poll_add_fd (GstPoll *set, GstPollFD *fd);
+gboolean gst_poll_remove_fd (GstPoll *set, GstPollFD *fd);
+
+gboolean gst_poll_fd_ctl_write (GstPoll *set, GstPollFD *fd, gboolean active);
+gboolean gst_poll_fd_ctl_read (GstPoll *set, GstPollFD *fd, gboolean active);
+void gst_poll_fd_ignored (GstPoll *set, GstPollFD *fd);
+
+gboolean gst_poll_fd_has_closed (const GstPoll *set, GstPollFD *fd);
+gboolean gst_poll_fd_has_error (const GstPoll *set, GstPollFD *fd);
+gboolean gst_poll_fd_can_read (const GstPoll *set, GstPollFD *fd);
+gboolean gst_poll_fd_can_write (const GstPoll *set, GstPollFD *fd);
+
+gint gst_poll_wait (GstPoll *set, GstClockTime timeout);
+
+gboolean gst_poll_set_controllable (GstPoll *set, gboolean controllable);
+void gst_poll_restart (GstPoll *set);
+void gst_poll_set_flushing (GstPoll *set, gboolean flushing);
+
+gboolean gst_poll_write_control (GstPoll *set);
+gboolean gst_poll_read_control (GstPoll *set);
+
+G_END_DECLS
+
+#endif /* __GST_POLL_H__ */
diff --git a/gst/gstpreset.c b/gst/gstpreset.c
new file mode 100644
index 0000000..6280896
--- /dev/null
+++ b/gst/gstpreset.c
@@ -0,0 +1,1122 @@
+/* GStreamer
+ * Copyright (C) 2006 Stefan Kost <ensonic@users.sf.net>
+ *
+ * gstpreset.c: helper interface for element presets
+ *
+ * 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:gstpreset
+ * @short_description: helper interface for element presets
+ *
+ * This interface offers methods to query and manipulate parameter preset sets.
+ * A preset is a bunch of property settings, together with meta data and a name.
+ * The name of a preset serves as key for subsequent method calls to manipulate
+ * single presets.
+ * All instances of one type will share the list of presets. The list is created
+ * on demand, if presets are not used, the list is not created.
+ *
+ * The interface comes with a default implementation that serves most plugins.
+ * Wrapper plugins will override most methods to implement support for the
+ * native preset format of those wrapped plugins.
+ * One method that is useful to be overridden is gst_preset_get_property_names().
+ * With that one can control which properties are saved and in which order.
+ */
+/* FIXME:
+ * - non racyness
+ * - we need to avoid two instances writing the preset file
+ * -> flock(fileno()), http://www.ecst.csuchico.edu/~beej/guide/ipc/flock.html
+ * -> open exclusive
+ * -> better save the new file to a tempfile and then rename?
+ * - we like to know when any other instance makes changes to the keyfile
+ * - then ui can be updated
+ * - and we make sure that we don't lose edits
+ * -> its the same problem actually, once for inside a process, once system-
+ * wide
+ * - can we use a lock inside a names shared memory segment?
+ *
+ * - need to add support for GstChildProxy
+ * we can do this in a next iteration, the format is flexible enough
+ * http://www.buzztard.org/index.php/Preset_handling_interface
+ *
+ * - should there be a 'preset-list' property to get the preset list
+ * (and to connect a notify:: to to listen for changes)
+ * we could use gnome_vfs_monitor_add() to monitor the user preset_file.
+ *
+ * - should there be a 'preset-name' property so that we can set a preset via
+ * gst-launch, or should we handle this with special syntax in gst-launch:
+ * gst-launch element preset:<preset-name> property=value ...
+ * - this would alloow to hanve preset-bundles too (a preset on bins that
+ * specifies presets for children
+ *
+ * - GstChildProxy suport
+ * - if we stick with GParamSpec **_list_properties()
+ * we need to use g_param_spec_set_qdata() to specify the instance on each GParamSpec
+ * OBJECT_LOCK(obj); // ChildProxy needs GstIterator support
+ * num=gst_child_proxy_get_children_count(obj);
+ * for(i=0;i<num;i++) {
+ * child=gst_child_proxy_get_child_by_index(obj,i);
+ * // v1 ----
+ * g_object_class_list_properties(child,&num);
+ * // foreach prop
+ * // g_param_spec_set_qdata(prop, quark, (gpointer)child);
+ * // add to result
+ * // v2 ----
+ * // children have to implement preset-iface too tag the returned GParamSpec* with the owner
+ * props=gst_preset_list_properties(child);
+ * // add props to result
+ * }
+ * OBJECT_UNLOCK(obj);
+ *
+ */
+
+#include "gst_private.h"
+
+#include "gstpreset.h"
+#include "gstinfo.h"
+#include "gstvalue.h"
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <glib/gstdio.h>
+
+#define GST_CAT_DEFAULT preset_debug
+GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
+
+/* defines for keyfile usage, this group contains the element type name and
+ * version these presets belong to. */
+#define PRESET_HEADER "_presets_"
+
+/* keys of the preset header section */
+#define PRESET_HEADER_ELEMENT_NAME "element-name"
+#define PRESET_HEADER_VERSION "version"
+
+static GQuark preset_user_path_quark = 0;
+static GQuark preset_system_path_quark = 0;
+static GQuark preset_quark = 0;
+
+/*static GQuark property_list_quark = 0;*/
+
+/* default iface implementation */
+
+static gboolean gst_preset_default_save_presets_file (GstPreset * preset);
+
+/*
+ * preset_get_paths:
+ * @preset: a #GObject that implements #GstPreset
+ * @preset_user_path: location for path or %NULL
+ * @preset_system_path: location for path or %NULL
+ *
+ * Fetch the preset_path for user local and system wide settings. Don't free
+ * after use.
+ *
+ * Returns: %FALSE if no paths could be found.
+ */
+static gboolean
+preset_get_paths (GstPreset * preset, const gchar ** preset_user_path,
+ const gchar ** preset_system_path)
+{
+ GType type = G_TYPE_FROM_INSTANCE (preset);
+ gchar *preset_path;
+ const gchar *element_name;
+
+ /* we use the element name when we must contruct the paths */
+ element_name = G_OBJECT_TYPE_NAME (preset);
+ GST_INFO_OBJECT (preset, "element_name: '%s'", element_name);
+
+ if (preset_user_path) {
+ /* preset user path requested, see if we have it cached in the qdata */
+ if (!(preset_path = g_type_get_qdata (type, preset_user_path_quark))) {
+ gchar *preset_dir;
+
+ /* user presets go in user's XDG data directory. */
+ preset_dir = g_build_filename (g_get_user_data_dir (),
+ "gstreamer-" GST_MAJORMINOR, "presets", NULL);
+ GST_INFO_OBJECT (preset, "user_preset_dir: '%s'", preset_dir);
+ preset_path =
+ g_strdup_printf ("%s" G_DIR_SEPARATOR_S "%s.prs", preset_dir,
+ element_name);
+ GST_INFO_OBJECT (preset, "user_preset_path: '%s'", preset_path);
+ /* create dirs */
+ g_mkdir_with_parents (preset_dir, 0755);
+ g_free (preset_dir);
+
+ /* cache the preset path to the type */
+ g_type_set_qdata (type, preset_user_path_quark, preset_path);
+ }
+ *preset_user_path = preset_path;
+ }
+
+ if (preset_system_path) {
+ /* preset system path requested, see if we have it cached in the qdata */
+ if (!(preset_path = g_type_get_qdata (type, preset_system_path_quark))) {
+ gchar *preset_dir;
+
+ /* system presets in '$GST_DATADIR/gstreamer-0.10/presets/GstAudioPanorama.prs' */
+ preset_dir = g_build_filename (GST_DATADIR, "gstreamer-" GST_MAJORMINOR,
+ "presets", NULL);
+ GST_INFO_OBJECT (preset, "system_preset_dir: '%s'", preset_dir);
+ preset_path = g_strdup_printf ("%s" G_DIR_SEPARATOR_S "%s.prs",
+ preset_dir, element_name);
+ GST_INFO_OBJECT (preset, "system_preset_path: '%s'", preset_path);
+ /* create dirs */
+ g_mkdir_with_parents (preset_dir, 0755);
+ g_free (preset_dir);
+
+ /* cache the preset path to the type */
+ g_type_set_qdata (type, preset_system_path_quark, preset_path);
+ }
+ *preset_system_path = preset_path;
+ }
+ return TRUE;
+}
+
+static gboolean
+preset_skip_property (GParamSpec * property)
+{
+ if (((property->flags & G_PARAM_READWRITE) != G_PARAM_READWRITE) ||
+ (property->flags & G_PARAM_CONSTRUCT_ONLY))
+ return TRUE;
+ /* FIXME: skip GST_PARAM_NOT_PRESETABLE, see #522205 */
+ return FALSE;
+}
+
+/* caller must free @preset_version after use */
+static GKeyFile *
+preset_open_and_parse_header (GstPreset * preset, const gchar * preset_path,
+ gchar ** preset_version)
+{
+ GKeyFile *in;
+ GError *error = NULL;
+ gboolean res;
+ const gchar *element_name;
+ gchar *name;
+
+ in = g_key_file_new ();
+
+ res = g_key_file_load_from_file (in, preset_path,
+ G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS, &error);
+ if (!res || error != NULL)
+ goto load_error;
+
+ /* element type name and preset name must match or we are dealing with a wrong
+ * preset file */
+ element_name = G_OBJECT_TYPE_NAME (preset);
+ name =
+ g_key_file_get_value (in, PRESET_HEADER, PRESET_HEADER_ELEMENT_NAME,
+ NULL);
+
+ if (!name || strcmp (name, element_name))
+ goto wrong_name;
+
+ g_free (name);
+
+ /* get the version now so that the caller can check it */
+ if (preset_version)
+ *preset_version =
+ g_key_file_get_value (in, PRESET_HEADER, PRESET_HEADER_VERSION, NULL);
+
+ return in;
+
+ /* ERRORS */
+load_error:
+ {
+ GST_WARNING_OBJECT (preset, "Unable to read preset file %s: %s",
+ preset_path, error->message);
+ g_error_free (error);
+ g_key_file_free (in);
+ return NULL;
+ }
+wrong_name:
+ {
+ GST_WARNING_OBJECT (preset,
+ "Wrong element name in preset file %s. Expected %s, got %s",
+ preset_path, element_name, GST_STR_NULL (name));
+ g_free (name);
+ g_key_file_free (in);
+ return NULL;
+ }
+}
+
+static guint64
+preset_parse_version (const gchar * str_version)
+{
+ guint major, minor, micro, nano;
+ gint num;
+
+ major = minor = micro = nano = 0;
+
+ /* parse version (e.g. 0.10.15.1) to guint64 */
+ num = sscanf (str_version, "%u.%u.%u.%u", &major, &minor, &micro, &nano);
+ /* make sure we have atleast "major.minor" */
+ if (num > 1) {
+ guint64 version;
+
+ version = ((((major << 8 | minor) << 8) | micro) << 8) | nano;
+ GST_DEBUG ("version %s -> %" G_GUINT64_FORMAT, str_version, version);
+ return version;
+ }
+ return G_GUINT64_CONSTANT (0);
+}
+
+static void
+preset_merge (GKeyFile * system, GKeyFile * user)
+{
+ gchar *str;
+ gchar **groups, **keys;
+ gsize i, j, num_groups, num_keys;
+
+ /* copy file comment if there is any */
+ if ((str = g_key_file_get_comment (user, NULL, NULL, NULL))) {
+ g_key_file_set_comment (system, NULL, NULL, str, NULL);
+ g_free (str);
+ }
+
+ /* get groups in user and copy into system */
+ groups = g_key_file_get_groups (user, &num_groups);
+ for (i = 0; i < num_groups; i++) {
+ /* copy group comment if there is any */
+ if ((str = g_key_file_get_comment (user, groups[i], NULL, NULL))) {
+ g_key_file_set_comment (system, groups[i], NULL, str, NULL);
+ g_free (str);
+ }
+
+ /* ignore private groups */
+ if (groups[i][0] == '_')
+ continue;
+
+ /* if group already exists in system, remove and re-add keys from user */
+ if (g_key_file_has_group (system, groups[i])) {
+ g_key_file_remove_group (system, groups[i], NULL);
+ }
+
+ keys = g_key_file_get_keys (user, groups[i], &num_keys, NULL);
+ for (j = 0; j < num_keys; j++) {
+ /* copy key comment if there is any */
+ if ((str = g_key_file_get_comment (user, groups[i], keys[j], NULL))) {
+ g_key_file_set_comment (system, groups[i], keys[j], str, NULL);
+ g_free (str);
+ }
+ str = g_key_file_get_value (user, groups[i], keys[j], NULL);
+ g_key_file_set_value (system, groups[i], keys[j], str);
+ g_free (str);
+ }
+ g_strfreev (keys);
+ }
+ g_strfreev (groups);
+}
+
+/* reads the user and system presets files and merges them together. This
+ * function caches the GKeyFile on the element type. If there is no existing
+ * preset file, a new in-memory GKeyFile will be created. */
+static GKeyFile *
+preset_get_keyfile (GstPreset * preset)
+{
+ GKeyFile *presets;
+ GType type = G_TYPE_FROM_INSTANCE (preset);
+
+ /* first see if the have a cached version for the type */
+ if (!(presets = g_type_get_qdata (type, preset_quark))) {
+ const gchar *preset_user_path, *preset_system_path;
+ gchar *str_version_user = NULL, *str_version_system = NULL;
+ gboolean updated_from_system = FALSE;
+ GKeyFile *in_user, *in_system;
+
+ preset_get_paths (preset, &preset_user_path, &preset_system_path);
+
+ /* try to load the user and system presets, we do this to get the versions
+ * of both files. */
+ in_user = preset_open_and_parse_header (preset, preset_user_path,
+ &str_version_user);
+ in_system = preset_open_and_parse_header (preset, preset_system_path,
+ &str_version_system);
+
+ /* compare version to check for merge */
+ if (in_system) {
+ /* keep system presets if there is no user preset or when the system
+ * version is higher than the user version. */
+ if (!in_user) {
+ presets = in_system;
+ } else if (preset_parse_version (str_version_system) >
+ preset_parse_version (str_version_user)) {
+ presets = in_system;
+ updated_from_system = TRUE;
+ }
+ }
+ if (in_user) {
+ if (updated_from_system) {
+ /* merge user on top of system presets */
+ preset_merge (presets, in_user);
+ g_key_file_free (in_user);
+ } else {
+ /* keep user presets */
+ presets = in_user;
+ }
+ }
+ if (!in_user && !in_system) {
+ /* we did not load a user or system presets file, create a new one */
+ presets = g_key_file_new ();
+ g_key_file_set_string (presets, PRESET_HEADER, PRESET_HEADER_ELEMENT_NAME,
+ G_OBJECT_TYPE_NAME (preset));
+ }
+
+ g_free (str_version_user);
+ g_free (str_version_system);
+
+ /* attach the preset to the type */
+ g_type_set_qdata (type, preset_quark, (gpointer) presets);
+
+ if (updated_from_system) {
+ gst_preset_default_save_presets_file (preset);
+ }
+ }
+ return presets;
+}
+
+/* get a list of all supported preset names for an element */
+static gchar **
+gst_preset_default_get_preset_names (GstPreset * preset)
+{
+ GKeyFile *presets;
+ gsize i, num_groups;
+ gchar **groups;
+
+ /* get the presets from the type */
+ if (!(presets = preset_get_keyfile (preset)))
+ goto no_presets;
+
+ /* get the groups, which are also the preset names */
+ if (!(groups = g_key_file_get_groups (presets, &num_groups)))
+ goto no_groups;
+
+ /* remove all private group names starting with '_' from the array */
+ for (i = 0; i < num_groups; i++) {
+ if (groups[i][0] == '_') {
+ /* free private group */
+ g_free (groups[i]);
+ /* move last element of list down */
+ num_groups--;
+ /* move last element into removed element */
+ groups[i] = groups[num_groups];
+ groups[num_groups] = NULL;
+ }
+ }
+ /* sort the array now */
+ g_qsort_with_data (groups, num_groups, sizeof (gchar *),
+ (GCompareDataFunc) strcmp, NULL);
+
+ return groups;
+
+ /* ERRORS */
+no_presets:
+ {
+ GST_WARNING_OBJECT (preset, "Could not load presets");
+ return NULL;
+ }
+no_groups:
+ {
+ GST_WARNING_OBJECT (preset, "Could not find preset groups");
+ return NULL;
+ }
+}
+
+/* get a list of all property names that are used for presets */
+static gchar **
+gst_preset_default_get_property_names (GstPreset * preset)
+{
+ GParamSpec **props;
+ guint i, j, n_props;
+ GObjectClass *gclass;
+ gchar **result;
+
+ gclass = G_OBJECT_CLASS (GST_ELEMENT_GET_CLASS (preset));
+
+ /* get a list of normal properties.
+ * FIXME, change this for childproxy support. */
+ props = g_object_class_list_properties (gclass, &n_props);
+ if (!props)
+ goto no_properties;
+
+ /* allocate array big enough to hold the worst case, including a terminating
+ * NULL pointer. */
+ result = g_new (gchar *, n_props + 1);
+
+ /* now filter out the properties that we can use for presets */
+ GST_DEBUG_OBJECT (preset, " filtering properties: %u", n_props);
+ for (i = j = 0; i < n_props; i++) {
+ if (preset_skip_property (props[i]))
+ continue;
+
+ /* copy and increment out pointer */
+ result[j++] = g_strdup (props[i]->name);
+ }
+ result[j] = NULL;
+ g_free (props);
+
+ return result;
+
+ /* ERRORS */
+no_properties:
+ {
+ GST_INFO_OBJECT (preset, "object has no properties");
+ return NULL;
+ }
+}
+
+/* load the presets of @name for the instance @preset. Returns %FALSE if something
+ * failed. */
+static gboolean
+gst_preset_default_load_preset (GstPreset * preset, const gchar * name)
+{
+ GKeyFile *presets;
+ gchar **props;
+ guint i;
+ GObjectClass *gclass;
+
+ /* get the presets from the type */
+ if (!(presets = preset_get_keyfile (preset)))
+ goto no_presets;
+
+ /* get the preset name */
+ if (!g_key_file_has_group (presets, name))
+ goto no_group;
+
+ GST_DEBUG_OBJECT (preset, "loading preset : '%s'", name);
+
+ /* get the properties that we can configure in this element */
+ if (!(props = gst_preset_get_property_names (preset)))
+ goto no_properties;
+
+ gclass = G_OBJECT_CLASS (GST_ELEMENT_GET_CLASS (preset));
+
+ /* for each of the property names, find the preset parameter and try to
+ * configure the property with its value */
+ for (i = 0; props[i]; i++) {
+ gchar *str;
+ GValue gvalue = { 0, };
+ GParamSpec *property;
+
+ /* check if we have a settings for this element property */
+ if (!(str = g_key_file_get_value (presets, name, props[i], NULL))) {
+ /* the element has a property but the parameter is not in the keyfile */
+ GST_WARNING_OBJECT (preset, "parameter '%s' not in preset", props[i]);
+ continue;
+ }
+
+ GST_DEBUG_OBJECT (preset, "setting value '%s' for property '%s'", str,
+ props[i]);
+
+ /* FIXME, change for childproxy to get the property and element. */
+ if (!(property = g_object_class_find_property (gclass, props[i]))) {
+ /* the parameter was in the keyfile, the element said it supported it but
+ * then the property was not found in the element. This should not happen. */
+ GST_WARNING_OBJECT (preset, "property '%s' not in object", props[i]);
+ g_free (str);
+ continue;
+ }
+
+ /* try to deserialize the property value from the keyfile and set it as
+ * the object property */
+ g_value_init (&gvalue, property->value_type);
+ if (gst_value_deserialize (&gvalue, str)) {
+ /* FIXME, change for childproxy support */
+ g_object_set_property (G_OBJECT (preset), props[i], &gvalue);
+ } else {
+ GST_WARNING_OBJECT (preset,
+ "deserialization of value '%s' for property '%s' failed", str,
+ props[i]);
+ }
+ g_value_unset (&gvalue);
+ g_free (str);
+ }
+ g_strfreev (props);
+
+ return TRUE;
+
+ /* ERRORS */
+no_presets:
+ {
+ GST_WARNING_OBJECT (preset, "no presets");
+ return FALSE;
+ }
+no_group:
+ {
+ GST_WARNING_OBJECT (preset, "no preset named '%s'", name);
+ return FALSE;
+ }
+no_properties:
+ {
+ GST_INFO_OBJECT (preset, "no properties");
+ return FALSE;
+ }
+}
+
+/* save the presets file. A copy of the existing presets file is stored in a
+ * .bak file */
+static gboolean
+gst_preset_default_save_presets_file (GstPreset * preset)
+{
+ GKeyFile *presets;
+ const gchar *preset_path;
+ GError *error = NULL;
+ gchar *bak_file_name;
+ gboolean backup = TRUE;
+ gchar *data;
+ gsize data_size;
+
+ preset_get_paths (preset, &preset_path, NULL);
+
+ /* get the presets from the type */
+ if (!(presets = preset_get_keyfile (preset)))
+ goto no_presets;
+
+ GST_DEBUG_OBJECT (preset, "saving preset file: '%s'", preset_path);
+
+ /* create backup if possible */
+ bak_file_name = g_strdup_printf ("%s.bak", preset_path);
+ if (g_file_test (bak_file_name, G_FILE_TEST_EXISTS)) {
+ if (g_unlink (bak_file_name)) {
+ backup = FALSE;
+ GST_INFO_OBJECT (preset, "cannot remove old backup file : %s",
+ bak_file_name);
+ }
+ }
+ if (backup) {
+ if (g_rename (preset_path, bak_file_name)) {
+ GST_INFO_OBJECT (preset, "cannot backup file : %s -> %s", preset_path,
+ bak_file_name);
+ }
+ }
+ g_free (bak_file_name);
+
+ /* update gstreamer version */
+ g_key_file_set_string (presets, PRESET_HEADER, PRESET_HEADER_VERSION,
+ PACKAGE_VERSION);
+
+ /* get new contents, wee need this to save it */
+ if (!(data = g_key_file_to_data (presets, &data_size, &error)))
+ goto convert_failed;
+
+ /* write presets */
+ if (!g_file_set_contents (preset_path, data, data_size, &error))
+ goto write_failed;
+
+ g_free (data);
+
+ return TRUE;
+
+ /* ERRORS */
+no_presets:
+ {
+ GST_WARNING_OBJECT (preset,
+ "no presets, trying to unlink possibly existing preset file: '%s'",
+ preset_path);
+ g_unlink (preset_path);
+ return FALSE;
+ }
+convert_failed:
+ {
+ GST_WARNING_OBJECT (preset, "can not get the keyfile contents: %s",
+ error->message);
+ g_error_free (error);
+ g_free (data);
+ return FALSE;
+ }
+write_failed:
+ {
+ GST_WARNING_OBJECT (preset, "Unable to store preset file %s: %s",
+ preset_path, error->message);
+ g_error_free (error);
+ g_free (data);
+ return FALSE;
+ }
+}
+
+/* save the preset with the given name */
+static gboolean
+gst_preset_default_save_preset (GstPreset * preset, const gchar * name)
+{
+ GKeyFile *presets;
+ gchar **props;
+ guint i;
+ GObjectClass *gclass;
+
+ GST_INFO_OBJECT (preset, "saving new preset: %s", name);
+
+ /* get the presets from the type */
+ if (!(presets = preset_get_keyfile (preset)))
+ goto no_presets;
+
+ /* take copies of current gobject properties from preset */
+ if (!(props = gst_preset_get_property_names (preset)))
+ goto no_properties;
+
+ gclass = G_OBJECT_CLASS (GST_ELEMENT_GET_CLASS (preset));
+
+ /* loop over the object properties and store the property value in the
+ * keyfile */
+ for (i = 0; props[i]; i++) {
+ GValue gvalue = { 0, };
+ gchar *str;
+ GParamSpec *property;
+
+ /* FIXME, change for childproxy to get the property and element. */
+ if (!(property = g_object_class_find_property (gclass, props[i]))) {
+ /* the element said it supported the property but then it does not have
+ * that property. This should not happen. */
+ GST_WARNING_OBJECT (preset, "property '%s' not in object", props[i]);
+ continue;
+ }
+
+ g_value_init (&gvalue, property->value_type);
+ /* FIXME, change for childproxy */
+ g_object_get_property (G_OBJECT (preset), props[i], &gvalue);
+
+ if ((str = gst_value_serialize (&gvalue))) {
+ g_key_file_set_string (presets, name, props[i], (gpointer) str);
+ g_free (str);
+ } else {
+ GST_WARNING_OBJECT (preset, "serialization for property '%s' failed",
+ props[i]);
+ }
+ g_value_unset (&gvalue);
+ }
+ GST_INFO_OBJECT (preset, " saved");
+ g_strfreev (props);
+
+ /* save updated version */
+ return gst_preset_default_save_presets_file (preset);
+
+ /* ERRORS */
+no_presets:
+ {
+ GST_WARNING_OBJECT (preset, "no presets");
+ return FALSE;
+ }
+no_properties:
+ {
+ GST_INFO_OBJECT (preset, "no properties");
+ return FALSE;
+ }
+}
+
+/* copies all keys and comments from one group to another, deleting the old
+ * group. */
+static gboolean
+gst_preset_default_rename_preset (GstPreset * preset, const gchar * old_name,
+ const gchar * new_name)
+{
+ GKeyFile *presets;
+ gchar *str;
+ gchar **keys;
+ gsize i, num_keys;
+
+ /* get the presets from the type */
+ if (!(presets = preset_get_keyfile (preset)))
+ goto no_presets;
+
+ if (!g_key_file_has_group (presets, old_name))
+ goto no_group;
+
+ /* copy group comment if there is any */
+ if ((str = g_key_file_get_comment (presets, old_name, NULL, NULL))) {
+ g_key_file_set_comment (presets, new_name, NULL, str, NULL);
+ g_free (str);
+ }
+
+ /* get all keys from the old group and copy them in the new group */
+ keys = g_key_file_get_keys (presets, old_name, &num_keys, NULL);
+ for (i = 0; i < num_keys; i++) {
+ /* copy key comment if there is any */
+ if ((str = g_key_file_get_comment (presets, old_name, keys[i], NULL))) {
+ g_key_file_set_comment (presets, new_name, keys[i], str, NULL);
+ g_free (str);
+ }
+ /* copy key value */
+ str = g_key_file_get_value (presets, old_name, keys[i], NULL);
+ g_key_file_set_value (presets, new_name, keys[i], str);
+ g_free (str);
+ }
+ g_strfreev (keys);
+
+ /* remove old group */
+ g_key_file_remove_group (presets, old_name, NULL);
+
+ /* save updated version */
+ return gst_preset_default_save_presets_file (preset);
+
+ /* ERRORS */
+no_presets:
+ {
+ GST_WARNING_OBJECT (preset, "no presets");
+ return FALSE;
+ }
+no_group:
+ {
+ GST_WARNING_OBJECT (preset, "no preset named %s", old_name);
+ return FALSE;
+ }
+}
+
+/* delete a group from the keyfile */
+static gboolean
+gst_preset_default_delete_preset (GstPreset * preset, const gchar * name)
+{
+ GKeyFile *presets;
+
+ /* get the presets from the type */
+ if (!(presets = preset_get_keyfile (preset)))
+ goto no_presets;
+
+ /* get the group */
+ if (!g_key_file_has_group (presets, name))
+ goto no_group;
+
+ /* remove the group */
+ g_key_file_remove_group (presets, name, NULL);
+
+ /* save updated version */
+ return gst_preset_default_save_presets_file (preset);
+
+ /* ERRORS */
+no_presets:
+ {
+ GST_WARNING_OBJECT (preset, "no presets");
+ return FALSE;
+ }
+no_group:
+ {
+ GST_WARNING_OBJECT (preset, "no preset named %s", name);
+ return FALSE;
+ }
+}
+
+static gboolean
+gst_preset_default_set_meta (GstPreset * preset, const gchar * name,
+ const gchar * tag, const gchar * value)
+{
+ GKeyFile *presets;
+ gchar *key;
+
+ /* get the presets from the type */
+ if (!(presets = preset_get_keyfile (preset)))
+ goto no_presets;
+
+ key = g_strdup_printf ("_meta/%s", tag);
+ if (value && *value) {
+ g_key_file_set_value (presets, name, key, value);
+ } else {
+ g_key_file_remove_key (presets, name, key, NULL);
+ }
+ g_free (key);
+
+ /* save updated keyfile */
+ return gst_preset_default_save_presets_file (preset);
+
+ /* ERRORS */
+no_presets:
+ {
+ GST_WARNING_OBJECT (preset, "no presets");
+ return FALSE;
+ }
+}
+
+/* the caller must free @value after usage */
+static gboolean
+gst_preset_default_get_meta (GstPreset * preset, const gchar * name,
+ const gchar * tag, gchar ** value)
+{
+ GKeyFile *presets;
+ gchar *key;
+
+ /* get the presets from the type */
+ if (!(presets = preset_get_keyfile (preset)))
+ goto no_presets;
+
+ key = g_strdup_printf ("_meta/%s", tag);
+ *value = g_key_file_get_value (presets, name, key, NULL);
+ g_free (key);
+
+ return TRUE;
+
+ /* ERRORS */
+no_presets:
+ {
+ GST_WARNING_OBJECT (preset, "no presets");
+ *value = NULL;
+ return FALSE;
+ }
+}
+
+/* wrapper */
+
+/**
+ * gst_preset_get_preset_names:
+ * @preset: a #GObject that implements #GstPreset
+ *
+ * Get a copy of preset names as a NULL terminated string array.
+ *
+ * Returns: (transfer full) (array zero-terminated=1) (element-type gchar*):
+ * list with names, ue g_strfreev() after usage.
+ *
+ * Since: 0.10.20
+ */
+gchar **
+gst_preset_get_preset_names (GstPreset * preset)
+{
+ g_return_val_if_fail (GST_IS_PRESET (preset), NULL);
+
+ return (GST_PRESET_GET_INTERFACE (preset)->get_preset_names (preset));
+}
+
+/**
+ * gst_preset_get_property_names:
+ * @preset: a #GObject that implements #GstPreset
+ *
+ * Get a the names of the GObject properties that can be used for presets.
+ *
+ * Returns: (transfer full) (array zero-terminated=1) (element-type gchar*): an
+ * array of property names which should be freed with g_strfreev() after use.
+ *
+ * Since: 0.10.20
+ */
+gchar **
+gst_preset_get_property_names (GstPreset * preset)
+{
+ g_return_val_if_fail (GST_IS_PRESET (preset), NULL);
+
+ return (GST_PRESET_GET_INTERFACE (preset)->get_property_names (preset));
+}
+
+/**
+ * gst_preset_load_preset:
+ * @preset: a #GObject that implements #GstPreset
+ * @name: preset name to load
+ *
+ * Load the given preset.
+ *
+ * Returns: %TRUE for success, %FALSE if e.g. there is no preset with that @name
+ *
+ * Since: 0.10.20
+ */
+gboolean
+gst_preset_load_preset (GstPreset * preset, const gchar * name)
+{
+ g_return_val_if_fail (GST_IS_PRESET (preset), FALSE);
+ g_return_val_if_fail (name, FALSE);
+
+ return (GST_PRESET_GET_INTERFACE (preset)->load_preset (preset, name));
+}
+
+/**
+ * gst_preset_save_preset:
+ * @preset: a #GObject that implements #GstPreset
+ * @name: preset name to save
+ *
+ * Save the current object settings as a preset under the given name. If there
+ * is already a preset by this @name it will be overwritten.
+ *
+ * Returns: %TRUE for success, %FALSE
+ *
+ * Since: 0.10.20
+ */
+gboolean
+gst_preset_save_preset (GstPreset * preset, const gchar * name)
+{
+ g_return_val_if_fail (GST_IS_PRESET (preset), FALSE);
+ g_return_val_if_fail (name, FALSE);
+
+ return (GST_PRESET_GET_INTERFACE (preset)->save_preset (preset, name));
+}
+
+/**
+ * gst_preset_rename_preset:
+ * @preset: a #GObject that implements #GstPreset
+ * @old_name: current preset name
+ * @new_name: new preset name
+ *
+ * Renames a preset. If there is already a preset by the @new_name it will be
+ * overwritten.
+ *
+ * Returns: %TRUE for success, %FALSE if e.g. there is no preset with @old_name
+ *
+ * Since: 0.10.20
+ */
+gboolean
+gst_preset_rename_preset (GstPreset * preset, const gchar * old_name,
+ const gchar * new_name)
+{
+ g_return_val_if_fail (GST_IS_PRESET (preset), FALSE);
+ g_return_val_if_fail (old_name, FALSE);
+ g_return_val_if_fail (new_name, FALSE);
+
+ return (GST_PRESET_GET_INTERFACE (preset)->rename_preset (preset, old_name,
+ new_name));
+}
+
+/**
+ * gst_preset_delete_preset:
+ * @preset: a #GObject that implements #GstPreset
+ * @name: preset name to remove
+ *
+ * Delete the given preset.
+ *
+ * Returns: %TRUE for success, %FALSE if e.g. there is no preset with that @name
+ *
+ * Since: 0.10.20
+ */
+gboolean
+gst_preset_delete_preset (GstPreset * preset, const gchar * name)
+{
+ g_return_val_if_fail (GST_IS_PRESET (preset), FALSE);
+ g_return_val_if_fail (name, FALSE);
+
+ return (GST_PRESET_GET_INTERFACE (preset)->delete_preset (preset, name));
+}
+
+/**
+ * gst_preset_set_meta:
+ * @preset: a #GObject that implements #GstPreset
+ * @name: preset name
+ * @tag: meta data item name
+ * @value: new value
+ *
+ * Sets a new @value for an existing meta data item or adds a new item. Meta
+ * data @tag names can be something like e.g. "comment". Supplying %NULL for the
+ * @value will unset an existing value.
+ *
+ * Returns: %TRUE for success, %FALSE if e.g. there is no preset with that @name
+ *
+ * Since: 0.10.20
+ */
+gboolean
+gst_preset_set_meta (GstPreset * preset, const gchar * name, const gchar * tag,
+ const gchar * value)
+{
+ g_return_val_if_fail (GST_IS_PRESET (preset), FALSE);
+ g_return_val_if_fail (name, FALSE);
+ g_return_val_if_fail (tag, FALSE);
+
+ return GST_PRESET_GET_INTERFACE (preset)->set_meta (preset, name, tag, value);
+}
+
+/**
+ * gst_preset_get_meta:
+ * @preset: a #GObject that implements #GstPreset
+ * @name: preset name
+ * @tag: meta data item name
+ * @value: (out callee-allocates): value
+ *
+ * Gets the @value for an existing meta data @tag. Meta data @tag names can be
+ * something like e.g. "comment". Returned values need to be released when done.
+ *
+ * Returns: %TRUE for success, %FALSE if e.g. there is no preset with that @name
+ * or no value for the given @tag
+ *
+ * Since: 0.10.20
+ */
+gboolean
+gst_preset_get_meta (GstPreset * preset, const gchar * name, const gchar * tag,
+ gchar ** value)
+{
+ g_return_val_if_fail (GST_IS_PRESET (preset), FALSE);
+ g_return_val_if_fail (name, FALSE);
+ g_return_val_if_fail (tag, FALSE);
+ g_return_val_if_fail (value, FALSE);
+
+ return GST_PRESET_GET_INTERFACE (preset)->get_meta (preset, name, tag, value);
+}
+
+/* class internals */
+
+static void
+gst_preset_class_init (GstPresetInterface * iface)
+{
+ iface->get_preset_names = gst_preset_default_get_preset_names;
+ iface->get_property_names = gst_preset_default_get_property_names;
+
+ iface->load_preset = gst_preset_default_load_preset;
+ iface->save_preset = gst_preset_default_save_preset;
+ iface->rename_preset = gst_preset_default_rename_preset;
+ iface->delete_preset = gst_preset_default_delete_preset;
+
+ iface->set_meta = gst_preset_default_set_meta;
+ iface->get_meta = gst_preset_default_get_meta;
+}
+
+static void
+gst_preset_base_init (gpointer g_class)
+{
+ static gboolean initialized = FALSE;
+
+ if (!initialized) {
+ /* init default implementation */
+ GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "preset",
+ GST_DEBUG_FG_WHITE | GST_DEBUG_BG_BLACK, "preset interface");
+
+ /* create quarks for use with g_type_{g,s}et_qdata() */
+ preset_quark = g_quark_from_static_string ("GstPreset::presets");
+ preset_user_path_quark =
+ g_quark_from_static_string ("GstPreset::user_path");
+ preset_system_path_quark =
+ g_quark_from_static_string ("GstPreset::system_path");
+
+#if 0
+ property_list_quark = g_quark_from_static_string ("GstPreset::properties");
+
+ /* create interface properties, each element would need to override this
+ * g_object_class_override_property(gobject_class, PROP_PRESET_NAME, "preset-name");
+ * and in _set_property() do
+ * case PROP_PRESET_NAME: {
+ * gchar *name = g_value_get_string (value);
+ * if (name)
+ * gst_preset_load_preset(preset, name);
+ * } break;
+ */
+ g_object_interface_install_property (g_class,
+ g_param_spec_string ("preset-name",
+ "preset-name property",
+ "load given preset", NULL, G_PARAM_WRITABLE));
+#endif
+
+ initialized = TRUE;
+ }
+}
+
+GType
+gst_preset_get_type (void)
+{
+ static volatile gsize type = 0;
+
+ if (g_once_init_enter (&type)) {
+ GType _type;
+ const GTypeInfo info = {
+ sizeof (GstPresetInterface),
+ (GBaseInitFunc) gst_preset_base_init, /* base_init */
+ NULL, /* base_finalize */
+ (GClassInitFunc) gst_preset_class_init, /* class_init */
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ 0,
+ 0, /* n_preallocs */
+ NULL /* instance_init */
+ };
+ _type = g_type_register_static (G_TYPE_INTERFACE, "GstPreset", &info, 0);
+ g_once_init_leave (&type, _type);
+ }
+ return type;
+}
diff --git a/gst/gstpreset.h b/gst/gstpreset.h
new file mode 100644
index 0000000..ccf1366
--- /dev/null
+++ b/gst/gstpreset.h
@@ -0,0 +1,98 @@
+/* GStreamer
+ * Copyright (C) 2006 Stefan Kost <ensonic@users.sf.net>
+ *
+ * gstpreset.h: helper interface header for element presets
+ *
+ * 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_PRESET_H__
+#define __GST_PRESET_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_PRESET (gst_preset_get_type())
+#define GST_PRESET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_PRESET, GstPreset))
+#define GST_IS_PRESET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_PRESET))
+#define GST_PRESET_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GST_TYPE_PRESET, GstPresetInterface))
+
+/**
+ * GstPreset:
+ *
+ * Opaque #GstPreset data structure.
+ */
+typedef struct _GstPreset GstPreset; /* dummy object */
+typedef struct _GstPresetInterface GstPresetInterface;
+
+/**
+ * GstPresetInterface:
+ * @parent: parent interface type.
+ * @get_preset_names: virtual method to get list of presets
+ * @get_property_names: virtual methods to get properties that are persistent
+ * @load_preset: virtual methods to load a preset into properties
+ * @save_preset: virtual methods to save properties into a preset
+ * @rename_preset: virtual methods to rename a preset
+ * @delete_preset: virtual methods to remove a preset
+ * @set_meta: virtual methods to set textual meta data to a preset
+ * @get_meta: virtual methods to get textual meta data from a preset
+ *
+ * #GstPreset interface.
+ */
+struct _GstPresetInterface
+{
+ GTypeInterface parent;
+
+ /* methods */
+ gchar** (*get_preset_names) (GstPreset *preset);
+
+ gchar** (*get_property_names) (GstPreset *preset);
+
+ gboolean (*load_preset) (GstPreset *preset, const gchar *name);
+ gboolean (*save_preset) (GstPreset *preset, const gchar *name);
+ gboolean (*rename_preset) (GstPreset *preset, const gchar *old_name,
+ const gchar *new_name);
+ gboolean (*delete_preset) (GstPreset *preset, const gchar *name);
+
+ gboolean (*set_meta) (GstPreset *preset, const gchar *name,
+ const gchar *tag, const gchar *value);
+ gboolean (*get_meta) (GstPreset *preset, const gchar *name,
+ const gchar *tag, gchar **value);
+ /*< private >*/
+ gpointer _gst_reserved[GST_PADDING];
+};
+
+GType gst_preset_get_type(void);
+
+gchar** gst_preset_get_preset_names (GstPreset *preset);
+
+gchar** gst_preset_get_property_names (GstPreset *preset);
+
+gboolean gst_preset_load_preset (GstPreset *preset, const gchar *name);
+gboolean gst_preset_save_preset (GstPreset *preset, const gchar *name);
+gboolean gst_preset_rename_preset (GstPreset *preset, const gchar *old_name,
+ const gchar *new_name);
+gboolean gst_preset_delete_preset (GstPreset *preset, const gchar *name);
+
+gboolean gst_preset_set_meta (GstPreset *preset, const gchar *name,
+ const gchar *tag, const gchar *value);
+gboolean gst_preset_get_meta (GstPreset *preset, const gchar *name,
+ const gchar *tag, gchar **value);
+
+G_END_DECLS
+
+#endif /* __GST_PRESET_H__ */
diff --git a/gst/gstquark.c b/gst/gstquark.c
new file mode 100644
index 0000000..26b3f16
--- /dev/null
+++ b/gst/gstquark.c
@@ -0,0 +1,74 @@
+/* GStreamer
+ * Copyright (C) 2006 Jan Schmidt <thaytan@noraisin.net>
+ *
+ * gstquark.c: Registered quarks for the _priv_gst_quark_table, private to
+ * GStreamer
+ *
+ * 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.
+ */
+
+#include "gst_private.h"
+#include "gstquark.h"
+
+/* These strings must match order and number declared in the GstQuarkId
+ * enum in gstquark.h! */
+static const gchar *_quark_strings[] = {
+ "format", "current", "duration", "rate",
+ "seekable", "segment-start", "segment-end",
+ "src_format", "src_value", "dest_format", "dest_value",
+ "start_format", "start_value", "stop_format", "stop_value",
+ "gerror", "debug", "buffer-percent", "buffering-mode",
+ "avg-in-rate", "avg-out-rate", "buffering-left",
+ "estimated-total", "old-state", "new-state", "pending-state",
+ "clock", "ready", "position", "reset-time", "live", "min-latency",
+ "max-latency", "busy", "type", "owner", "update", "applied-rate",
+ "start", "stop", "minsize", "maxsize", "async", "proportion",
+ "diff", "timestamp", "flags", "cur-type", "cur", "stop-type",
+ "latency", "uri", "object", "taglist", "GstEventSegment",
+ "GstEventBufferSize", "GstEventQOS", "GstEventSeek", "GstEventLatency",
+ "GstMessageError", "GstMessageWarning", "GstMessageInfo",
+ "GstMessageBuffering", "GstMessageState", "GstMessageClockProvide",
+ "GstMessageClockLost", "GstMessageNewClock", "GstMessageStructureChange",
+ "GstMessageSegmentStart", "GstMessageSegmentDone", "GstMessageDuration",
+ "GstMessageAsyncDone", "GstMessageRequestState", "GstMessageStreamStatus",
+ "GstQueryPosition", "GstQueryDuration", "GstQueryLatency", "GstQueryConvert",
+ "GstQuerySegment", "GstQuerySeeking", "GstQueryFormats", "GstQueryBuffering",
+ "GstQueryURI", "GstEventStep", "GstMessageStepDone", "amount", "flush",
+ "intermediate", "GstMessageStepStart", "active", "eos", "sink-message",
+ "message", "GstMessageQOS", "running-time", "stream-time", "jitter",
+ "quality", "processed", "dropped", "buffering-ranges", "GstMessageProgress",
+ "code", "text", "percent", "timeout", "GstBufferPoolConfig", "caps", "size",
+ "min-buffers", "max-buffers", "prefix", "postfix", "align", "time",
+ "GstQueryAllocation", "need-pool", "meta", "pool", "GstEventCaps",
+ "GstEventReconfigure", "segment", "GstQueryScheduling", "pull-mode",
+ "random-access", "sequential", "allocator", "GstEventFlushStop", "options"
+};
+
+GQuark _priv_gst_quark_table[GST_QUARK_MAX];
+
+void
+_priv_gst_quarks_initialize (void)
+{
+ gint i;
+
+ if (G_N_ELEMENTS (_quark_strings) != GST_QUARK_MAX)
+ g_warning ("the quark table is not consistent! %d != %d",
+ (int) G_N_ELEMENTS (_quark_strings), GST_QUARK_MAX);
+
+ for (i = 0; i < GST_QUARK_MAX; i++) {
+ _priv_gst_quark_table[i] = g_quark_from_static_string (_quark_strings[i]);
+ }
+}
diff --git a/gst/gstquark.h b/gst/gstquark.h
new file mode 100644
index 0000000..06eb709
--- /dev/null
+++ b/gst/gstquark.h
@@ -0,0 +1,166 @@
+/* GStreamer
+ * Copyright (C) 2006 Jan Schmidt <thaytan@noraisin.net>
+ *
+ * gstquark.h: Private header for storing quark info
+ *
+ * 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_QUARK_H__
+#define __GST_QUARK_H__
+
+/* These enums need to match the number and order
+ * of strings declared in _quark_table, in gstquark.c */
+typedef enum _GstQuarkId
+{
+ GST_QUARK_FORMAT = 0,
+ GST_QUARK_CURRENT = 1,
+ GST_QUARK_DURATION = 2,
+ GST_QUARK_RATE = 3,
+ GST_QUARK_SEEKABLE = 4,
+ GST_QUARK_SEGMENT_START = 5,
+ GST_QUARK_SEGMENT_END = 6,
+ GST_QUARK_SRC_FORMAT = 7,
+ GST_QUARK_SRC_VALUE = 8,
+ GST_QUARK_DEST_FORMAT = 9,
+ GST_QUARK_DEST_VALUE = 10,
+ GST_QUARK_START_FORMAT = 11,
+ GST_QUARK_START_VALUE = 12,
+ GST_QUARK_STOP_FORMAT = 13,
+ GST_QUARK_STOP_VALUE = 14,
+ GST_QUARK_GERROR = 15,
+ GST_QUARK_DEBUG = 16,
+ GST_QUARK_BUFFER_PERCENT = 17,
+ GST_QUARK_BUFFERING_MODE = 18,
+ GST_QUARK_AVG_IN_RATE = 19,
+ GST_QUARK_AVG_OUT_RATE = 20,
+ GST_QUARK_BUFFERING_LEFT = 21,
+ GST_QUARK_ESTIMATED_TOTAL = 22,
+ GST_QUARK_OLD_STATE = 23,
+ GST_QUARK_NEW_STATE = 24,
+ GST_QUARK_PENDING_STATE = 25,
+ GST_QUARK_CLOCK = 26,
+ GST_QUARK_READY = 27,
+ GST_QUARK_POSITION = 28,
+ GST_QUARK_RESET_TIME = 29,
+ GST_QUARK_LIVE = 30,
+ GST_QUARK_MIN_LATENCY = 31,
+ GST_QUARK_MAX_LATENCY = 32,
+ GST_QUARK_BUSY = 33,
+ GST_QUARK_TYPE = 34,
+ GST_QUARK_OWNER = 35,
+ GST_QUARK_UPDATE = 36,
+ GST_QUARK_APPLIED_RATE = 37,
+ GST_QUARK_START = 38,
+ GST_QUARK_STOP = 39,
+ GST_QUARK_MINSIZE = 40,
+ GST_QUARK_MAXSIZE = 41,
+ GST_QUARK_ASYNC = 42,
+ GST_QUARK_PROPORTION = 43,
+ GST_QUARK_DIFF = 44,
+ GST_QUARK_TIMESTAMP = 45,
+ GST_QUARK_FLAGS = 46,
+ GST_QUARK_CUR_TYPE = 47,
+ GST_QUARK_CUR = 48,
+ GST_QUARK_STOP_TYPE = 49,
+ GST_QUARK_LATENCY = 50,
+ GST_QUARK_URI = 51,
+ GST_QUARK_OBJECT = 52,
+ GST_QUARK_TAGLIST = 53,
+ GST_QUARK_EVENT_SEGMENT = 54,
+ GST_QUARK_EVENT_BUFFER_SIZE = 55,
+ GST_QUARK_EVENT_QOS = 56,
+ GST_QUARK_EVENT_SEEK = 57,
+ GST_QUARK_EVENT_LATENCY = 58,
+ GST_QUARK_MESSAGE_ERROR = 59,
+ GST_QUARK_MESSAGE_WARNING = 60,
+ GST_QUARK_MESSAGE_INFO = 61,
+ GST_QUARK_MESSAGE_BUFFERING = 62,
+ GST_QUARK_MESSAGE_STATE = 63,
+ GST_QUARK_MESSAGE_CLOCK_PROVIDE = 64,
+ GST_QUARK_MESSAGE_CLOCK_LOST = 65,
+ GST_QUARK_MESSAGE_NEW_CLOCK = 66,
+ GST_QUARK_MESSAGE_STRUCTURE_CHANGE = 67,
+ GST_QUARK_MESSAGE_SEGMENT_START = 68,
+ GST_QUARK_MESSAGE_SEGMENT_DONE = 69,
+ GST_QUARK_MESSAGE_DURATION = 70,
+ GST_QUARK_MESSAGE_ASYNC_DONE = 71,
+ GST_QUARK_MESSAGE_REQUEST_STATE = 72,
+ GST_QUARK_MESSAGE_STREAM_STATUS = 73,
+ GST_QUARK_QUERY_POSITION = 74,
+ GST_QUARK_QUERY_DURATION = 75,
+ GST_QUARK_QUERY_LATENCY = 76,
+ GST_QUARK_QUERY_CONVERT = 77,
+ GST_QUARK_QUERY_SEGMENT = 78,
+ GST_QUARK_QUERY_SEEKING = 79,
+ GST_QUARK_QUERY_FORMATS = 80,
+ GST_QUARK_QUERY_BUFFERING = 81,
+ GST_QUARK_QUERY_URI = 82,
+ GST_QUARK_EVENT_STEP = 83,
+ GST_QUARK_MESSAGE_STEP_DONE = 84,
+ GST_QUARK_AMOUNT = 85,
+ GST_QUARK_FLUSH = 86,
+ GST_QUARK_INTERMEDIATE = 87,
+ GST_QUARK_MESSAGE_STEP_START = 88,
+ GST_QUARK_ACTIVE = 89,
+ GST_QUARK_EOS = 90,
+ GST_QUARK_EVENT_SINK_MESSAGE = 91,
+ GST_QUARK_MESSAGE = 92,
+ GST_QUARK_MESSAGE_QOS = 93,
+ GST_QUARK_RUNNING_TIME = 94,
+ GST_QUARK_STREAM_TIME = 95,
+ GST_QUARK_JITTER = 96,
+ GST_QUARK_QUALITY = 97,
+ GST_QUARK_PROCESSED = 98,
+ GST_QUARK_DROPPED = 99,
+ GST_QUARK_BUFFERING_RANGES = 100,
+ GST_QUARK_MESSAGE_PROGRESS = 101,
+ GST_QUARK_CODE = 102,
+ GST_QUARK_TEXT = 103,
+ GST_QUARK_PERCENT = 104,
+ GST_QUARK_TIMEOUT = 105,
+ GST_QUARK_BUFFER_POOL_CONFIG = 106,
+ GST_QUARK_CAPS = 107,
+ GST_QUARK_SIZE = 108,
+ GST_QUARK_MIN_BUFFERS = 109,
+ GST_QUARK_MAX_BUFFERS = 110,
+ GST_QUARK_PREFIX = 111,
+ GST_QUARK_POSTFIX = 112,
+ GST_QUARK_ALIGN = 113,
+ GST_QUARK_TIME = 114,
+ GST_QUARK_QUERY_ALLOCATION = 115,
+ GST_QUARK_NEED_POOL = 116,
+ GST_QUARK_META = 117,
+ GST_QUARK_POOL = 118,
+ GST_QUARK_EVENT_CAPS = 119,
+ GST_QUARK_EVENT_RECONFIGURE = 120,
+ GST_QUARK_SEGMENT = 121,
+ GST_QUARK_QUERY_SCHEDULING = 122,
+ GST_QUARK_PULL_MODE = 123,
+ GST_QUARK_RANDOM_ACCESS = 124,
+ GST_QUARK_SEQUENTIAL = 125,
+ GST_QUARK_ALLOCATOR = 126,
+ GST_QUARK_EVENT_FLUSH_STOP = 127,
+ GST_QUARK_OPTIONS = 128,
+
+ GST_QUARK_MAX = 129
+} GstQuarkId;
+
+extern GQuark _priv_gst_quark_table[GST_QUARK_MAX];
+
+#define GST_QUARK(q) _priv_gst_quark_table[GST_QUARK_##q]
+
+#endif
diff --git a/gst/gstquery.c b/gst/gstquery.c
new file mode 100644
index 0000000..eb28019
--- /dev/null
+++ b/gst/gstquery.c
@@ -0,0 +1,2004 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2000 Wim Taymans <wim.taymans@chello.be>
+ * 2005 Wim Taymans <wim@fluendo.com>
+ *
+ * gstquery.c: GstQueryType registration and Query parsing/creation
+ *
+ * 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:gstquery
+ * @short_description: Dynamically register new query types. Provide functions
+ * to create queries, and to set and parse values in them.
+ * @see_also: #GstPad, #GstElement
+ *
+ * GstQuery functions are used to register new query types to the gstreamer
+ * core and use them.
+ * Queries can be performed on pads (gst_pad_query()) and elements
+ * (gst_element_query()). Please note that some queries might need a running
+ * pipeline to work.
+ *
+ * Queries can be created using the gst_query_new_*() functions.
+ * Query values can be set using gst_query_set_*(), and parsed using
+ * gst_query_parse_*() helpers.
+ *
+ * The following example shows how to query the duration of a pipeline:
+ *
+ * <example>
+ * <title>Query duration on a pipeline</title>
+ * <programlisting>
+ * GstQuery *query;
+ * gboolean res;
+ * query = gst_query_new_duration (GST_FORMAT_TIME);
+ * res = gst_element_query (pipeline, query);
+ * if (res) {
+ * gint64 duration;
+ * gst_query_parse_duration (query, NULL, &amp;duration);
+ * g_print ("duration = %"GST_TIME_FORMAT, GST_TIME_ARGS (duration));
+ * }
+ * else {
+ * g_print ("duration query failed...");
+ * }
+ * gst_query_unref (query);
+ * </programlisting>
+ * </example>
+ *
+ * Last reviewed on 2006-02-14 (0.10.4)
+ */
+
+#include "gst_private.h"
+#include "gstinfo.h"
+#include "gstquery.h"
+#include "gstvalue.h"
+#include "gstenumtypes.h"
+#include "gstquark.h"
+#include "gsturi.h"
+#include "gstbufferpool.h"
+
+GST_DEBUG_CATEGORY_STATIC (gst_query_debug);
+#define GST_CAT_DEFAULT gst_query_debug
+
+static GType _gst_query_type = 0;
+
+typedef struct
+{
+ GstQuery query;
+
+ GstStructure *structure;
+} GstQueryImpl;
+
+#define GST_QUERY_STRUCTURE(q) (((GstQueryImpl *)(q))->structure)
+
+static GStaticMutex mutex = G_STATIC_MUTEX_INIT;
+static GList *_gst_queries = NULL;
+static GHashTable *_nick_to_query = NULL;
+static GHashTable *_query_type_to_nick = NULL;
+static guint32 _n_values = 1; /* we start from 1 because 0 reserved for NONE */
+
+static GstQueryTypeDefinition standard_definitions[] = {
+ {GST_QUERY_POSITION, "position", "Current position", 0},
+ {GST_QUERY_DURATION, "duration", "Total duration", 0},
+ {GST_QUERY_LATENCY, "latency", "Latency", 0},
+ {GST_QUERY_JITTER, "jitter", "Jitter", 0},
+ {GST_QUERY_RATE, "rate", "Configured rate 1000000 = 1", 0},
+ {GST_QUERY_SEEKING, "seeking", "Seeking capabilities and parameters", 0},
+ {GST_QUERY_SEGMENT, "segment", "currently configured segment", 0},
+ {GST_QUERY_CONVERT, "convert", "Converting between formats", 0},
+ {GST_QUERY_FORMATS, "formats", "Supported formats for conversion", 0},
+ {GST_QUERY_BUFFERING, "buffering", "Buffering status", 0},
+ {GST_QUERY_CUSTOM, "custom", "Custom query", 0},
+ {GST_QUERY_URI, "uri", "URI of the source or sink", 0},
+ {GST_QUERY_ALLOCATION, "allocation", "Allocation properties", 0},
+ {GST_QUERY_SCHEDULING, "scheduling", "Scheduling properties", 0},
+ {GST_QUERY_NONE, NULL, NULL, 0}
+};
+
+GST_DEFINE_MINI_OBJECT_TYPE (GstQuery, gst_query);
+
+void
+_priv_gst_query_initialize (void)
+{
+ GstQueryTypeDefinition *standards = standard_definitions;
+
+ GST_CAT_INFO (GST_CAT_GST_INIT, "init queries");
+
+ GST_DEBUG_CATEGORY_INIT (gst_query_debug, "query", 0, "query system");
+
+ g_static_mutex_lock (&mutex);
+ if (_nick_to_query == NULL) {
+ _nick_to_query = g_hash_table_new (g_str_hash, g_str_equal);
+ _query_type_to_nick = g_hash_table_new (NULL, NULL);
+ }
+
+ while (standards->nick) {
+ standards->quark = g_quark_from_static_string (standards->nick);
+ g_hash_table_insert (_nick_to_query, (gpointer) standards->nick, standards);
+ g_hash_table_insert (_query_type_to_nick,
+ GINT_TO_POINTER (standards->value), standards);
+
+ _gst_queries = g_list_append (_gst_queries, standards);
+ standards++;
+ _n_values++;
+ }
+ g_static_mutex_unlock (&mutex);
+
+ _gst_query_type = gst_query_get_type ();
+}
+
+/**
+ * gst_query_type_get_name:
+ * @query: the query type
+ *
+ * Get a printable name for the given query type. Do not modify or free.
+ *
+ * Returns: a reference to the static name of the query.
+ */
+const gchar *
+gst_query_type_get_name (GstQueryType query)
+{
+ const GstQueryTypeDefinition *def;
+
+ def = gst_query_type_get_details (query);
+ g_return_val_if_fail (def != NULL, NULL);
+
+ return def->nick;
+}
+
+/**
+ * gst_query_type_to_quark:
+ * @query: the query type
+ *
+ * Get the unique quark for the given query type.
+ *
+ * Returns: the quark associated with the query type
+ */
+GQuark
+gst_query_type_to_quark (GstQueryType query)
+{
+ const GstQueryTypeDefinition *def;
+
+ def = gst_query_type_get_details (query);
+ g_return_val_if_fail (def != NULL, 0);
+
+ return def->quark;
+}
+
+/**
+ * gst_query_type_register:
+ * @nick: The nick of the new query
+ * @description: The description of the new query
+ *
+ * Create a new GstQueryType based on the nick or return an
+ * already registered query with that nick
+ *
+ * Returns: A new GstQueryType or an already registered query
+ * with the same nick.
+ */
+GstQueryType
+gst_query_type_register (const gchar * nick, const gchar * description)
+{
+ GstQueryTypeDefinition *query;
+ GstQueryType lookup;
+
+ g_return_val_if_fail (nick != NULL, GST_QUERY_NONE);
+ g_return_val_if_fail (description != NULL, GST_QUERY_NONE);
+
+ lookup = gst_query_type_get_by_nick (nick);
+ if (lookup != GST_QUERY_NONE)
+ return lookup;
+
+ query = g_slice_new (GstQueryTypeDefinition);
+ query->value = (GstQueryType) _n_values;
+ query->nick = g_strdup (nick);
+ query->description = g_strdup (description);
+ query->quark = g_quark_from_static_string (query->nick);
+
+ g_static_mutex_lock (&mutex);
+ g_hash_table_insert (_nick_to_query, (gpointer) query->nick, query);
+ g_hash_table_insert (_query_type_to_nick, GINT_TO_POINTER (query->value),
+ query);
+ _gst_queries = g_list_append (_gst_queries, query);
+ _n_values++;
+ g_static_mutex_unlock (&mutex);
+
+ return query->value;
+}
+
+/**
+ * gst_query_type_get_by_nick:
+ * @nick: The nick of the query
+ *
+ * Get the query type registered with @nick.
+ *
+ * Returns: The query registered with @nick or #GST_QUERY_NONE
+ * if the query was not registered.
+ */
+GstQueryType
+gst_query_type_get_by_nick (const gchar * nick)
+{
+ GstQueryTypeDefinition *query;
+
+ g_return_val_if_fail (nick != NULL, GST_QUERY_NONE);
+
+ g_static_mutex_lock (&mutex);
+ query = g_hash_table_lookup (_nick_to_query, nick);
+ g_static_mutex_unlock (&mutex);
+
+ if (query != NULL)
+ return query->value;
+ else
+ return GST_QUERY_NONE;
+}
+
+/**
+ * gst_query_types_contains:
+ * @types: The query array to search
+ * @type: the #GstQueryType to find
+ *
+ * See if the given #GstQueryType is inside the @types query types array.
+ *
+ * Returns: TRUE if the type is found inside the array
+ */
+gboolean
+gst_query_types_contains (const GstQueryType * types, GstQueryType type)
+{
+ if (!types)
+ return FALSE;
+
+ while (*types) {
+ if (*types == type)
+ return TRUE;
+
+ types++;
+ }
+ return FALSE;
+}
+
+
+/**
+ * gst_query_type_get_details:
+ * @type: a #GstQueryType
+ *
+ * Get details about the given #GstQueryType.
+ *
+ * Returns: The #GstQueryTypeDefinition for @type or NULL on failure.
+ */
+const GstQueryTypeDefinition *
+gst_query_type_get_details (GstQueryType type)
+{
+ const GstQueryTypeDefinition *result;
+
+ g_static_mutex_lock (&mutex);
+ result = g_hash_table_lookup (_query_type_to_nick, GINT_TO_POINTER (type));
+ g_static_mutex_unlock (&mutex);
+
+ return result;
+}
+
+/**
+ * gst_query_type_iterate_definitions:
+ *
+ * Get a #GstIterator of all the registered query types. The definitions
+ * iterated over are read only.
+ *
+ * Free-function: gst_iterator_free
+ *
+ * Returns: (transfer full): a #GstIterator of #GstQueryTypeDefinition.
+ */
+GstIterator *
+gst_query_type_iterate_definitions (void)
+{
+ GstIterator *result;
+
+ g_static_mutex_lock (&mutex);
+ /* FIXME: register a boxed type for GstQueryTypeDefinition */
+ result = gst_iterator_new_list (G_TYPE_POINTER,
+ g_static_mutex_get_mutex (&mutex), &_n_values, &_gst_queries, NULL, NULL);
+ g_static_mutex_unlock (&mutex);
+
+ return result;
+}
+
+static void
+_gst_query_free (GstQuery * query)
+{
+ GstStructure *s;
+
+ g_return_if_fail (query != NULL);
+
+ s = GST_QUERY_STRUCTURE (query);
+ if (s) {
+ gst_structure_set_parent_refcount (s, NULL);
+ gst_structure_free (s);
+ }
+
+ g_slice_free1 (GST_MINI_OBJECT_SIZE (query), query);
+}
+
+static GstQuery *gst_query_new (GstQueryType type, GstStructure * structure);
+
+static GstQuery *
+_gst_query_copy (GstQuery * query)
+{
+ GstQuery *copy;
+
+ copy = gst_query_new (query->type, GST_QUERY_STRUCTURE (query));
+
+ return copy;
+}
+
+static GstQuery *
+gst_query_new (GstQueryType type, GstStructure * structure)
+{
+ GstQueryImpl *query;
+
+ query = g_slice_new0 (GstQueryImpl);
+
+ gst_mini_object_init (GST_MINI_OBJECT_CAST (query),
+ _gst_query_type, sizeof (GstQueryImpl));
+
+ query->query.mini_object.copy = (GstMiniObjectCopyFunction) _gst_query_copy;
+ query->query.mini_object.free = (GstMiniObjectFreeFunction) _gst_query_free;
+
+ GST_DEBUG ("creating new query %p %d", query, type);
+
+ GST_QUERY_TYPE (query) = type;
+ query->structure = structure;
+
+ if (structure)
+ gst_structure_set_parent_refcount (structure,
+ &query->query.mini_object.refcount);
+
+ return GST_QUERY_CAST (query);
+}
+
+/**
+ * gst_query_new_position:
+ * @format: the default #GstFormat for the new query
+ *
+ * Constructs a new query stream position query object. Use gst_query_unref()
+ * when done with it. A position query is used to query the current position
+ * of playback in the streams, in some format.
+ *
+ * Free-function: gst_query_unref
+ *
+ * Returns: (transfer full): a new #GstQuery
+ */
+GstQuery *
+gst_query_new_position (GstFormat format)
+{
+ GstQuery *query;
+ GstStructure *structure;
+
+ structure = gst_structure_id_new (GST_QUARK (QUERY_POSITION),
+ GST_QUARK (FORMAT), GST_TYPE_FORMAT, format,
+ GST_QUARK (CURRENT), G_TYPE_INT64, G_GINT64_CONSTANT (-1), NULL);
+
+ query = gst_query_new (GST_QUERY_POSITION, structure);
+
+ return query;
+}
+
+/**
+ * gst_query_set_position:
+ * @query: a #GstQuery with query type GST_QUERY_POSITION
+ * @format: the requested #GstFormat
+ * @cur: the position to set
+ *
+ * Answer a position query by setting the requested value in the given format.
+ */
+void
+gst_query_set_position (GstQuery * query, GstFormat format, gint64 cur)
+{
+ GstStructure *s;
+
+ g_return_if_fail (GST_QUERY_TYPE (query) == GST_QUERY_POSITION);
+
+ s = GST_QUERY_STRUCTURE (query);
+ g_return_if_fail (format == g_value_get_enum (gst_structure_id_get_value (s,
+ GST_QUARK (FORMAT))));
+
+ gst_structure_id_set (s,
+ GST_QUARK (FORMAT), GST_TYPE_FORMAT, format,
+ GST_QUARK (CURRENT), G_TYPE_INT64, cur, NULL);
+}
+
+/**
+ * gst_query_parse_position:
+ * @query: a #GstQuery
+ * @format: (out) (allow-none): the storage for the #GstFormat of the
+ * position values (may be NULL)
+ * @cur: (out) (allow-none): the storage for the current position (may be NULL)
+ *
+ * Parse a position query, writing the format into @format, and the position
+ * into @cur, if the respective parameters are non-NULL.
+ */
+void
+gst_query_parse_position (GstQuery * query, GstFormat * format, gint64 * cur)
+{
+ GstStructure *structure;
+
+ g_return_if_fail (GST_QUERY_TYPE (query) == GST_QUERY_POSITION);
+
+ structure = GST_QUERY_STRUCTURE (query);
+ if (format)
+ *format =
+ (GstFormat) g_value_get_enum (gst_structure_id_get_value (structure,
+ GST_QUARK (FORMAT)));
+ if (cur)
+ *cur = g_value_get_int64 (gst_structure_id_get_value (structure,
+ GST_QUARK (CURRENT)));
+}
+
+
+/**
+ * gst_query_new_duration:
+ * @format: the #GstFormat for this duration query
+ *
+ * Constructs a new stream duration query object to query in the given format.
+ * Use gst_query_unref() when done with it. A duration query will give the
+ * total length of the stream.
+ *
+ * Free-function: gst_query_unref
+ *
+ * Returns: (transfer full): a new #GstQuery
+ */
+GstQuery *
+gst_query_new_duration (GstFormat format)
+{
+ GstQuery *query;
+ GstStructure *structure;
+
+ structure = gst_structure_id_new (GST_QUARK (QUERY_DURATION),
+ GST_QUARK (FORMAT), GST_TYPE_FORMAT, format,
+ GST_QUARK (DURATION), G_TYPE_INT64, G_GINT64_CONSTANT (-1), NULL);
+
+ query = gst_query_new (GST_QUERY_DURATION, structure);
+
+ return query;
+}
+
+/**
+ * gst_query_set_duration:
+ * @query: a #GstQuery
+ * @format: the #GstFormat for the duration
+ * @duration: the duration of the stream
+ *
+ * Answer a duration query by setting the requested value in the given format.
+ */
+void
+gst_query_set_duration (GstQuery * query, GstFormat format, gint64 duration)
+{
+ GstStructure *s;
+
+ g_return_if_fail (GST_QUERY_TYPE (query) == GST_QUERY_DURATION);
+
+ s = GST_QUERY_STRUCTURE (query);
+ g_return_if_fail (format == g_value_get_enum (gst_structure_id_get_value (s,
+ GST_QUARK (FORMAT))));
+ gst_structure_id_set (s, GST_QUARK (FORMAT), GST_TYPE_FORMAT, format,
+ GST_QUARK (DURATION), G_TYPE_INT64, duration, NULL);
+}
+
+/**
+ * gst_query_parse_duration:
+ * @query: a #GstQuery
+ * @format: (out) (allow-none): the storage for the #GstFormat of the duration
+ * value, or NULL.
+ * @duration: (out) (allow-none): the storage for the total duration, or NULL.
+ *
+ * Parse a duration query answer. Write the format of the duration into @format,
+ * and the value into @duration, if the respective variables are non-NULL.
+ */
+void
+gst_query_parse_duration (GstQuery * query, GstFormat * format,
+ gint64 * duration)
+{
+ GstStructure *structure;
+
+ g_return_if_fail (GST_QUERY_TYPE (query) == GST_QUERY_DURATION);
+
+ structure = GST_QUERY_STRUCTURE (query);
+ if (format)
+ *format =
+ (GstFormat) g_value_get_enum (gst_structure_id_get_value (structure,
+ GST_QUARK (FORMAT)));
+ if (duration)
+ *duration = g_value_get_int64 (gst_structure_id_get_value (structure,
+ GST_QUARK (DURATION)));
+}
+
+/**
+ * gst_query_new_latency:
+ *
+ * Constructs a new latency query object.
+ * Use gst_query_unref() when done with it. A latency query is usually performed
+ * by sinks to compensate for additional latency introduced by elements in the
+ * pipeline.
+ *
+ * Free-function: gst_query_unref
+ *
+ * Returns: (transfer full): a #GstQuery
+ *
+ * Since: 0.10.12
+ */
+GstQuery *
+gst_query_new_latency (void)
+{
+ GstQuery *query;
+ GstStructure *structure;
+
+ structure = gst_structure_id_new (GST_QUARK (QUERY_LATENCY),
+ GST_QUARK (LIVE), G_TYPE_BOOLEAN, FALSE,
+ GST_QUARK (MIN_LATENCY), G_TYPE_UINT64, G_GUINT64_CONSTANT (0),
+ GST_QUARK (MAX_LATENCY), G_TYPE_UINT64, G_GUINT64_CONSTANT (-1), NULL);
+
+ query = gst_query_new (GST_QUERY_LATENCY, structure);
+
+ return query;
+}
+
+/**
+ * gst_query_set_latency:
+ * @query: a #GstQuery
+ * @live: if there is a live element upstream
+ * @min_latency: the minimal latency of the live element
+ * @max_latency: the maximal latency of the live element
+ *
+ * Answer a latency query by setting the requested values in the given format.
+ *
+ * Since: 0.10.12
+ */
+void
+gst_query_set_latency (GstQuery * query, gboolean live,
+ GstClockTime min_latency, GstClockTime max_latency)
+{
+ GstStructure *structure;
+
+ g_return_if_fail (GST_QUERY_TYPE (query) == GST_QUERY_LATENCY);
+
+ structure = GST_QUERY_STRUCTURE (query);
+ gst_structure_id_set (structure,
+ GST_QUARK (LIVE), G_TYPE_BOOLEAN, live,
+ GST_QUARK (MIN_LATENCY), G_TYPE_UINT64, min_latency,
+ GST_QUARK (MAX_LATENCY), G_TYPE_UINT64, max_latency, NULL);
+}
+
+/**
+ * gst_query_parse_latency:
+ * @query: a #GstQuery
+ * @live: (out) (allow-none): storage for live or NULL
+ * @min_latency: (out) (allow-none): the storage for the min latency or NULL
+ * @max_latency: (out) (allow-none): the storage for the max latency or NULL
+ *
+ * Parse a latency query answer.
+ *
+ * Since: 0.10.12
+ */
+void
+gst_query_parse_latency (GstQuery * query, gboolean * live,
+ GstClockTime * min_latency, GstClockTime * max_latency)
+{
+ GstStructure *structure;
+
+ g_return_if_fail (GST_QUERY_TYPE (query) == GST_QUERY_LATENCY);
+
+ structure = GST_QUERY_STRUCTURE (query);
+ if (live)
+ *live =
+ g_value_get_boolean (gst_structure_id_get_value (structure,
+ GST_QUARK (LIVE)));
+ if (min_latency)
+ *min_latency = g_value_get_uint64 (gst_structure_id_get_value (structure,
+ GST_QUARK (MIN_LATENCY)));
+ if (max_latency)
+ *max_latency = g_value_get_uint64 (gst_structure_id_get_value (structure,
+ GST_QUARK (MAX_LATENCY)));
+}
+
+/**
+ * gst_query_new_convert:
+ * @src_format: the source #GstFormat for the new query
+ * @value: the value to convert
+ * @dest_format: the target #GstFormat
+ *
+ * Constructs a new convert query object. Use gst_query_unref()
+ * when done with it. A convert query is used to ask for a conversion between
+ * one format and another.
+ *
+ * Free-function: gst_query_unref
+ *
+ * Returns: (transfer full): a #GstQuery
+ */
+GstQuery *
+gst_query_new_convert (GstFormat src_format, gint64 value,
+ GstFormat dest_format)
+{
+ GstQuery *query;
+ GstStructure *structure;
+
+ structure = gst_structure_id_new (GST_QUARK (QUERY_CONVERT),
+ GST_QUARK (SRC_FORMAT), GST_TYPE_FORMAT, src_format,
+ GST_QUARK (SRC_VALUE), G_TYPE_INT64, value,
+ GST_QUARK (DEST_FORMAT), GST_TYPE_FORMAT, dest_format,
+ GST_QUARK (DEST_VALUE), G_TYPE_INT64, G_GINT64_CONSTANT (-1), NULL);
+
+ query = gst_query_new (GST_QUERY_CONVERT, structure);
+
+ return query;
+}
+
+/**
+ * gst_query_set_convert:
+ * @query: a #GstQuery
+ * @src_format: the source #GstFormat
+ * @src_value: the source value
+ * @dest_format: the destination #GstFormat
+ * @dest_value: the destination value
+ *
+ * Answer a convert query by setting the requested values.
+ */
+void
+gst_query_set_convert (GstQuery * query, GstFormat src_format, gint64 src_value,
+ GstFormat dest_format, gint64 dest_value)
+{
+ GstStructure *structure;
+
+ g_return_if_fail (GST_QUERY_TYPE (query) == GST_QUERY_CONVERT);
+
+ structure = GST_QUERY_STRUCTURE (query);
+ gst_structure_id_set (structure,
+ GST_QUARK (SRC_FORMAT), GST_TYPE_FORMAT, src_format,
+ GST_QUARK (SRC_VALUE), G_TYPE_INT64, src_value,
+ GST_QUARK (DEST_FORMAT), GST_TYPE_FORMAT, dest_format,
+ GST_QUARK (DEST_VALUE), G_TYPE_INT64, dest_value, NULL);
+}
+
+/**
+ * gst_query_parse_convert:
+ * @query: a #GstQuery
+ * @src_format: (out) (allow-none): the storage for the #GstFormat of the
+ * source value, or NULL
+ * @src_value: (out) (allow-none): the storage for the source value, or NULL
+ * @dest_format: (out) (allow-none): the storage for the #GstFormat of the
+ * destination value, or NULL
+ * @dest_value: (out) (allow-none): the storage for the destination value,
+ * or NULL
+ *
+ * Parse a convert query answer. Any of @src_format, @src_value, @dest_format,
+ * and @dest_value may be NULL, in which case that value is omitted.
+ */
+void
+gst_query_parse_convert (GstQuery * query, GstFormat * src_format,
+ gint64 * src_value, GstFormat * dest_format, gint64 * dest_value)
+{
+ GstStructure *structure;
+
+ g_return_if_fail (GST_QUERY_TYPE (query) == GST_QUERY_CONVERT);
+
+ structure = GST_QUERY_STRUCTURE (query);
+ if (src_format)
+ *src_format =
+ (GstFormat) g_value_get_enum (gst_structure_id_get_value (structure,
+ GST_QUARK (SRC_FORMAT)));
+ if (src_value)
+ *src_value = g_value_get_int64 (gst_structure_id_get_value (structure,
+ GST_QUARK (SRC_VALUE)));
+ if (dest_format)
+ *dest_format =
+ (GstFormat) g_value_get_enum (gst_structure_id_get_value (structure,
+ GST_QUARK (DEST_FORMAT)));
+ if (dest_value)
+ *dest_value = g_value_get_int64 (gst_structure_id_get_value (structure,
+ GST_QUARK (DEST_VALUE)));
+}
+
+/**
+ * gst_query_new_segment:
+ * @format: the #GstFormat for the new query
+ *
+ * Constructs a new segment query object. Use gst_query_unref()
+ * when done with it. A segment query is used to discover information about the
+ * currently configured segment for playback.
+ *
+ * Free-function: gst_query_unref
+ *
+ * Returns: (transfer full): a new #GstQuery
+ */
+GstQuery *
+gst_query_new_segment (GstFormat format)
+{
+ GstQuery *query;
+ GstStructure *structure;
+
+ structure = gst_structure_id_new (GST_QUARK (QUERY_SEGMENT),
+ GST_QUARK (RATE), G_TYPE_DOUBLE, (gdouble) 0.0,
+ GST_QUARK (FORMAT), GST_TYPE_FORMAT, format,
+ GST_QUARK (START_VALUE), G_TYPE_INT64, G_GINT64_CONSTANT (-1),
+ GST_QUARK (STOP_VALUE), G_TYPE_INT64, G_GINT64_CONSTANT (-1), NULL);
+
+ query = gst_query_new (GST_QUERY_SEGMENT, structure);
+
+ return query;
+}
+
+/**
+ * gst_query_set_segment:
+ * @query: a #GstQuery
+ * @rate: the rate of the segment
+ * @format: the #GstFormat of the segment values (@start_value and @stop_value)
+ * @start_value: the start value
+ * @stop_value: the stop value
+ *
+ * Answer a segment query by setting the requested values. The normal
+ * playback segment of a pipeline is 0 to duration at the default rate of
+ * 1.0. If a seek was performed on the pipeline to play a different
+ * segment, this query will return the range specified in the last seek.
+ *
+ * @start_value and @stop_value will respectively contain the configured
+ * playback range start and stop values expressed in @format.
+ * The values are always between 0 and the duration of the media and
+ * @start_value <= @stop_value. @rate will contain the playback rate. For
+ * negative rates, playback will actually happen from @stop_value to
+ * @start_value.
+ */
+void
+gst_query_set_segment (GstQuery * query, gdouble rate, GstFormat format,
+ gint64 start_value, gint64 stop_value)
+{
+ GstStructure *structure;
+
+ g_return_if_fail (GST_QUERY_TYPE (query) == GST_QUERY_SEGMENT);
+
+ structure = GST_QUERY_STRUCTURE (query);
+ gst_structure_id_set (structure,
+ GST_QUARK (RATE), G_TYPE_DOUBLE, rate,
+ GST_QUARK (FORMAT), GST_TYPE_FORMAT, format,
+ GST_QUARK (START_VALUE), G_TYPE_INT64, start_value,
+ GST_QUARK (STOP_VALUE), G_TYPE_INT64, stop_value, NULL);
+}
+
+/**
+ * gst_query_parse_segment:
+ * @query: a #GstQuery
+ * @rate: (out) (allow-none): the storage for the rate of the segment, or NULL
+ * @format: (out) (allow-none): the storage for the #GstFormat of the values,
+ * or NULL
+ * @start_value: (out) (allow-none): the storage for the start value, or NULL
+ * @stop_value: (out) (allow-none): the storage for the stop value, or NULL
+ *
+ * Parse a segment query answer. Any of @rate, @format, @start_value, and
+ * @stop_value may be NULL, which will cause this value to be omitted.
+ *
+ * See gst_query_set_segment() for an explanation of the function arguments.
+ */
+void
+gst_query_parse_segment (GstQuery * query, gdouble * rate, GstFormat * format,
+ gint64 * start_value, gint64 * stop_value)
+{
+ GstStructure *structure;
+
+ g_return_if_fail (GST_QUERY_TYPE (query) == GST_QUERY_SEGMENT);
+
+ structure = GST_QUERY_STRUCTURE (query);
+ if (rate)
+ *rate = g_value_get_double (gst_structure_id_get_value (structure,
+ GST_QUARK (RATE)));
+ if (format)
+ *format =
+ (GstFormat) g_value_get_enum (gst_structure_id_get_value (structure,
+ GST_QUARK (FORMAT)));
+ if (start_value)
+ *start_value = g_value_get_int64 (gst_structure_id_get_value (structure,
+ GST_QUARK (START_VALUE)));
+ if (stop_value)
+ *stop_value = g_value_get_int64 (gst_structure_id_get_value (structure,
+ GST_QUARK (STOP_VALUE)));
+}
+
+/**
+ * gst_query_new_custom:
+ * @type: the query type
+ * @structure: a structure for the query
+ *
+ * Constructs a new custom query object. Use gst_query_unref()
+ * when done with it.
+ *
+ * Free-function: gst_query_unref
+ *
+ * Returns: (transfer full): a new #GstQuery
+ */
+GstQuery *
+gst_query_new_custom (GstQueryType type, GstStructure * structure)
+{
+ g_return_val_if_fail (gst_query_type_get_details (type) != NULL, NULL);
+ g_return_val_if_fail (structure != NULL, NULL);
+
+ return gst_query_new (type, structure);
+}
+
+/**
+ * gst_query_get_structure:
+ * @query: a #GstQuery
+ *
+ * Get the structure of a query.
+ *
+ * Returns: (transfer none): the #GstStructure of the query. The structure is
+ * still owned by the query and will therefore be freed when the query
+ * is unreffed.
+ */
+const GstStructure *
+gst_query_get_structure (GstQuery * query)
+{
+ g_return_val_if_fail (GST_IS_QUERY (query), NULL);
+
+ return GST_QUERY_STRUCTURE (query);
+}
+
+/**
+ * gst_query_writable_structure:
+ * @query: a #GstQuery
+ *
+ * Get the structure of a query.
+ *
+ * Returns: (transfer none): the #GstStructure of the query. The structure is
+ * still owned by the query and will therefore be freed when the query
+ * is unreffed.
+ */
+GstStructure *
+gst_query_writable_structure (GstQuery * query)
+{
+ g_return_val_if_fail (GST_IS_QUERY (query), NULL);
+ g_return_val_if_fail (gst_query_is_writable (query), NULL);
+
+ return GST_QUERY_STRUCTURE (query);
+}
+
+/**
+ * gst_query_new_seeking:
+ * @format: the default #GstFormat for the new query
+ *
+ * Constructs a new query object for querying seeking properties of
+ * the stream.
+ *
+ * Free-function: gst_query_unref
+ *
+ * Returns: (transfer full): a new #GstQuery
+ */
+GstQuery *
+gst_query_new_seeking (GstFormat format)
+{
+ GstQuery *query;
+ GstStructure *structure;
+
+ structure = gst_structure_id_new (GST_QUARK (QUERY_SEEKING),
+ GST_QUARK (FORMAT), GST_TYPE_FORMAT, format,
+ GST_QUARK (SEEKABLE), G_TYPE_BOOLEAN, FALSE,
+ GST_QUARK (SEGMENT_START), G_TYPE_INT64, G_GINT64_CONSTANT (-1),
+ GST_QUARK (SEGMENT_END), G_TYPE_INT64, G_GINT64_CONSTANT (-1), NULL);
+
+ query = gst_query_new (GST_QUERY_SEEKING, structure);
+
+ return query;
+}
+
+/**
+ * gst_query_set_seeking:
+ * @query: a #GstQuery
+ * @format: the format to set for the @segment_start and @segment_end values
+ * @seekable: the seekable flag to set
+ * @segment_start: the segment_start to set
+ * @segment_end: the segment_end to set
+ *
+ * Set the seeking query result fields in @query.
+ */
+void
+gst_query_set_seeking (GstQuery * query, GstFormat format,
+ gboolean seekable, gint64 segment_start, gint64 segment_end)
+{
+ GstStructure *structure;
+
+ g_return_if_fail (GST_QUERY_TYPE (query) == GST_QUERY_SEEKING);
+ g_return_if_fail (gst_query_is_writable (query));
+
+ structure = GST_QUERY_STRUCTURE (query);
+ gst_structure_id_set (structure,
+ GST_QUARK (FORMAT), GST_TYPE_FORMAT, format,
+ GST_QUARK (SEEKABLE), G_TYPE_BOOLEAN, seekable,
+ GST_QUARK (SEGMENT_START), G_TYPE_INT64, segment_start,
+ GST_QUARK (SEGMENT_END), G_TYPE_INT64, segment_end, NULL);
+}
+
+/**
+ * gst_query_parse_seeking:
+ * @query: a GST_QUERY_SEEKING type query #GstQuery
+ * @format: (out) (allow-none): the format to set for the @segment_start
+ * and @segment_end values, or NULL
+ * @seekable: (out) (allow-none): the seekable flag to set, or NULL
+ * @segment_start: (out) (allow-none): the segment_start to set, or NULL
+ * @segment_end: (out) (allow-none): the segment_end to set, or NULL
+ *
+ * Parse a seeking query, writing the format into @format, and
+ * other results into the passed parameters, if the respective parameters
+ * are non-NULL
+ */
+void
+gst_query_parse_seeking (GstQuery * query, GstFormat * format,
+ gboolean * seekable, gint64 * segment_start, gint64 * segment_end)
+{
+ GstStructure *structure;
+
+ g_return_if_fail (GST_QUERY_TYPE (query) == GST_QUERY_SEEKING);
+
+ structure = GST_QUERY_STRUCTURE (query);
+ if (format)
+ *format =
+ (GstFormat) g_value_get_enum (gst_structure_id_get_value (structure,
+ GST_QUARK (FORMAT)));
+ if (seekable)
+ *seekable = g_value_get_boolean (gst_structure_id_get_value (structure,
+ GST_QUARK (SEEKABLE)));
+ if (segment_start)
+ *segment_start = g_value_get_int64 (gst_structure_id_get_value (structure,
+ GST_QUARK (SEGMENT_START)));
+ if (segment_end)
+ *segment_end = g_value_get_int64 (gst_structure_id_get_value (structure,
+ GST_QUARK (SEGMENT_END)));
+}
+
+/**
+ * gst_query_new_formats:
+ *
+ * Constructs a new query object for querying formats of
+ * the stream.
+ *
+ * Free-function: gst_query_unref
+ *
+ * Returns: (transfer full): a new #GstQuery
+ *
+ * Since: 0.10.4
+ */
+GstQuery *
+gst_query_new_formats (void)
+{
+ GstQuery *query;
+ GstStructure *structure;
+
+ structure = gst_structure_id_empty_new (GST_QUARK (QUERY_FORMATS));
+ query = gst_query_new (GST_QUERY_FORMATS, structure);
+
+ return query;
+}
+
+static void
+gst_query_list_add_format (GValue * list, GstFormat format)
+{
+ GValue item = { 0, };
+
+ g_value_init (&item, GST_TYPE_FORMAT);
+ g_value_set_enum (&item, format);
+ gst_value_list_append_value (list, &item);
+ g_value_unset (&item);
+}
+
+/**
+ * gst_query_set_formats:
+ * @query: a #GstQuery
+ * @n_formats: the number of formats to set.
+ * @...: A number of @GstFormats equal to @n_formats.
+ *
+ * Set the formats query result fields in @query. The number of formats passed
+ * must be equal to @n_formats.
+ */
+void
+gst_query_set_formats (GstQuery * query, gint n_formats, ...)
+{
+ va_list ap;
+ GValue list = { 0, };
+ gint i;
+ GstStructure *structure;
+
+ g_return_if_fail (GST_QUERY_TYPE (query) == GST_QUERY_FORMATS);
+ g_return_if_fail (gst_query_is_writable (query));
+
+ g_value_init (&list, GST_TYPE_LIST);
+
+ va_start (ap, n_formats);
+ for (i = 0; i < n_formats; i++) {
+ gst_query_list_add_format (&list, va_arg (ap, GstFormat));
+ }
+ va_end (ap);
+
+ structure = GST_QUERY_STRUCTURE (query);
+ gst_structure_set_value (structure, "formats", &list);
+
+ g_value_unset (&list);
+
+}
+
+/**
+ * gst_query_set_formatsv:
+ * @query: a #GstQuery
+ * @n_formats: the number of formats to set.
+ * @formats: (in) (array length=n_formats): an array containing @n_formats
+ * @GstFormat values.
+ *
+ * Set the formats query result fields in @query. The number of formats passed
+ * in the @formats array must be equal to @n_formats.
+ *
+ * Since: 0.10.4
+ */
+void
+gst_query_set_formatsv (GstQuery * query, gint n_formats,
+ const GstFormat * formats)
+{
+ GValue list = { 0, };
+ gint i;
+ GstStructure *structure;
+
+ g_return_if_fail (GST_QUERY_TYPE (query) == GST_QUERY_FORMATS);
+ g_return_if_fail (gst_query_is_writable (query));
+
+ g_value_init (&list, GST_TYPE_LIST);
+ for (i = 0; i < n_formats; i++) {
+ gst_query_list_add_format (&list, formats[i]);
+ }
+ structure = GST_QUERY_STRUCTURE (query);
+ gst_structure_set_value (structure, "formats", &list);
+
+ g_value_unset (&list);
+}
+
+/**
+ * gst_query_parse_n_formats:
+ * @query: a #GstQuery
+ * @n_formats: (out): the number of formats in this query.
+ *
+ * Parse the number of formats in the formats @query.
+ *
+ * Since: 0.10.4
+ */
+void
+gst_query_parse_n_formats (GstQuery * query, guint * n_formats)
+{
+ GstStructure *structure;
+
+ g_return_if_fail (GST_QUERY_TYPE (query) == GST_QUERY_FORMATS);
+
+ if (n_formats) {
+ const GValue *list;
+
+ structure = GST_QUERY_STRUCTURE (query);
+ list = gst_structure_get_value (structure, "formats");
+ if (list == NULL)
+ *n_formats = 0;
+ else
+ *n_formats = gst_value_list_get_size (list);
+ }
+}
+
+/**
+ * gst_query_parse_nth_format:
+ * @query: a #GstQuery
+ * @nth: (out): the nth format to retrieve.
+ * @format: (out): a pointer to store the nth format
+ *
+ * Parse the format query and retrieve the @nth format from it into
+ * @format. If the list contains less elements than @nth, @format will be
+ * set to GST_FORMAT_UNDEFINED.
+ */
+void
+gst_query_parse_nth_format (GstQuery * query, guint nth, GstFormat * format)
+{
+ GstStructure *structure;
+
+ g_return_if_fail (GST_QUERY_TYPE (query) == GST_QUERY_FORMATS);
+
+ if (format) {
+ const GValue *list;
+
+ structure = GST_QUERY_STRUCTURE (query);
+ list = gst_structure_get_value (structure, "formats");
+ if (list == NULL) {
+ *format = GST_FORMAT_UNDEFINED;
+ } else {
+ if (nth < gst_value_list_get_size (list)) {
+ *format =
+ (GstFormat) g_value_get_enum (gst_value_list_get_value (list, nth));
+ } else
+ *format = GST_FORMAT_UNDEFINED;
+ }
+ }
+}
+
+/**
+ * gst_query_new_buffering
+ * @format: the default #GstFormat for the new query
+ *
+ * Constructs a new query object for querying the buffering status of
+ * a stream.
+ *
+ * Free-function: gst_query_unref
+ *
+ * Returns: (transfer full): a new #GstQuery
+ *
+ * Since: 0.10.20
+ */
+GstQuery *
+gst_query_new_buffering (GstFormat format)
+{
+ GstQuery *query;
+ GstStructure *structure;
+
+ /* by default, we configure the answer as no buffering with a 100% buffering
+ * progress */
+ structure = gst_structure_id_new (GST_QUARK (QUERY_BUFFERING),
+ GST_QUARK (BUSY), G_TYPE_BOOLEAN, FALSE,
+ GST_QUARK (BUFFER_PERCENT), G_TYPE_INT, 100,
+ GST_QUARK (BUFFERING_MODE), GST_TYPE_BUFFERING_MODE, GST_BUFFERING_STREAM,
+ GST_QUARK (AVG_IN_RATE), G_TYPE_INT, -1,
+ GST_QUARK (AVG_OUT_RATE), G_TYPE_INT, -1,
+ GST_QUARK (BUFFERING_LEFT), G_TYPE_INT64, G_GINT64_CONSTANT (0),
+ GST_QUARK (ESTIMATED_TOTAL), G_TYPE_INT64, G_GINT64_CONSTANT (-1),
+ GST_QUARK (FORMAT), GST_TYPE_FORMAT, format,
+ GST_QUARK (START_VALUE), G_TYPE_INT64, G_GINT64_CONSTANT (-1),
+ GST_QUARK (STOP_VALUE), G_TYPE_INT64, G_GINT64_CONSTANT (-1), NULL);
+
+ query = gst_query_new (GST_QUERY_BUFFERING, structure);
+
+ return query;
+}
+
+/**
+ * gst_query_set_buffering_percent
+ * @query: A valid #GstQuery of type GST_QUERY_BUFFERING.
+ * @busy: if buffering is busy
+ * @percent: a buffering percent
+ *
+ * Set the percentage of buffered data. This is a value between 0 and 100.
+ * The @busy indicator is %TRUE when the buffering is in progress.
+ *
+ * Since: 0.10.20
+ */
+void
+gst_query_set_buffering_percent (GstQuery * query, gboolean busy, gint percent)
+{
+ GstStructure *structure;
+
+ g_return_if_fail (GST_QUERY_TYPE (query) == GST_QUERY_BUFFERING);
+ g_return_if_fail (gst_query_is_writable (query));
+ g_return_if_fail (percent >= 0 && percent <= 100);
+
+ structure = GST_QUERY_STRUCTURE (query);
+ gst_structure_id_set (structure,
+ GST_QUARK (BUSY), G_TYPE_BOOLEAN, busy,
+ GST_QUARK (BUFFER_PERCENT), G_TYPE_INT, percent, NULL);
+}
+
+/**
+ * gst_query_parse_buffering_percent
+ * @query: A valid #GstQuery of type GST_QUERY_BUFFERING.
+ * @busy: (out) (allow-none): if buffering is busy, or NULL
+ * @percent: (out) (allow-none): a buffering percent, or NULL
+ *
+ * Get the percentage of buffered data. This is a value between 0 and 100.
+ * The @busy indicator is %TRUE when the buffering is in progress.
+ *
+ * Since: 0.10.20
+ */
+void
+gst_query_parse_buffering_percent (GstQuery * query, gboolean * busy,
+ gint * percent)
+{
+ GstStructure *structure;
+
+ g_return_if_fail (GST_QUERY_TYPE (query) == GST_QUERY_BUFFERING);
+
+ structure = GST_QUERY_STRUCTURE (query);
+ if (busy)
+ *busy = g_value_get_boolean (gst_structure_id_get_value (structure,
+ GST_QUARK (BUSY)));
+ if (percent)
+ *percent = g_value_get_int (gst_structure_id_get_value (structure,
+ GST_QUARK (BUFFER_PERCENT)));
+}
+
+/**
+ * gst_query_set_buffering_stats:
+ * @query: A valid #GstQuery of type GST_QUERY_BUFFERING.
+ * @mode: a buffering mode
+ * @avg_in: the average input rate
+ * @avg_out: the average output rate
+ * @buffering_left: amount of buffering time left
+ *
+ * Configures the buffering stats values in @query.
+ *
+ * Since: 0.10.20
+ */
+void
+gst_query_set_buffering_stats (GstQuery * query, GstBufferingMode mode,
+ gint avg_in, gint avg_out, gint64 buffering_left)
+{
+ GstStructure *structure;
+
+ g_return_if_fail (GST_QUERY_TYPE (query) == GST_QUERY_BUFFERING);
+ g_return_if_fail (gst_query_is_writable (query));
+
+ structure = GST_QUERY_STRUCTURE (query);
+ gst_structure_id_set (structure,
+ GST_QUARK (BUFFERING_MODE), GST_TYPE_BUFFERING_MODE, mode,
+ GST_QUARK (AVG_IN_RATE), G_TYPE_INT, avg_in,
+ GST_QUARK (AVG_OUT_RATE), G_TYPE_INT, avg_out,
+ GST_QUARK (BUFFERING_LEFT), G_TYPE_INT64, buffering_left, NULL);
+}
+
+/**
+ * gst_query_parse_buffering_stats:
+ * @query: A valid #GstQuery of type GST_QUERY_BUFFERING.
+ * @mode: (out) (allow-none): a buffering mode, or NULL
+ * @avg_in: (out) (allow-none): the average input rate, or NULL
+ * @avg_out: (out) (allow-none): the average output rat, or NULLe
+ * @buffering_left: (out) (allow-none): amount of buffering time left, or NULL
+ *
+ * Extracts the buffering stats values from @query.
+ *
+ * Since: 0.10.20
+ */
+void
+gst_query_parse_buffering_stats (GstQuery * query,
+ GstBufferingMode * mode, gint * avg_in, gint * avg_out,
+ gint64 * buffering_left)
+{
+ GstStructure *structure;
+
+ g_return_if_fail (GST_QUERY_TYPE (query) == GST_QUERY_BUFFERING);
+
+ structure = GST_QUERY_STRUCTURE (query);
+ if (mode)
+ *mode = (GstBufferingMode)
+ g_value_get_enum (gst_structure_id_get_value (structure,
+ GST_QUARK (BUFFERING_MODE)));
+ if (avg_in)
+ *avg_in = g_value_get_int (gst_structure_id_get_value (structure,
+ GST_QUARK (AVG_IN_RATE)));
+ if (avg_out)
+ *avg_out = g_value_get_int (gst_structure_id_get_value (structure,
+ GST_QUARK (AVG_OUT_RATE)));
+ if (buffering_left)
+ *buffering_left =
+ g_value_get_int64 (gst_structure_id_get_value (structure,
+ GST_QUARK (BUFFERING_LEFT)));
+}
+
+
+/**
+ * gst_query_set_buffering_range:
+ * @query: a #GstQuery
+ * @format: the format to set for the @start and @stop values
+ * @start: the start to set
+ * @stop: the stop to set
+ * @estimated_total: estimated total amount of download time
+ *
+ * Set the available query result fields in @query.
+ *
+ * Since: 0.10.20
+ */
+void
+gst_query_set_buffering_range (GstQuery * query, GstFormat format,
+ gint64 start, gint64 stop, gint64 estimated_total)
+{
+ GstStructure *structure;
+
+ g_return_if_fail (GST_QUERY_TYPE (query) == GST_QUERY_BUFFERING);
+ g_return_if_fail (gst_query_is_writable (query));
+
+ structure = GST_QUERY_STRUCTURE (query);
+ gst_structure_id_set (structure,
+ GST_QUARK (FORMAT), GST_TYPE_FORMAT, format,
+ GST_QUARK (START_VALUE), G_TYPE_INT64, start,
+ GST_QUARK (STOP_VALUE), G_TYPE_INT64, stop,
+ GST_QUARK (ESTIMATED_TOTAL), G_TYPE_INT64, estimated_total, NULL);
+}
+
+/**
+ * gst_query_parse_buffering_range:
+ * @query: a GST_QUERY_BUFFERING type query #GstQuery
+ * @format: (out) (allow-none): the format to set for the @segment_start
+ * and @segment_end values, or NULL
+ * @start: (out) (allow-none): the start to set, or NULL
+ * @stop: (out) (allow-none): the stop to set, or NULL
+ * @estimated_total: (out) (allow-none): estimated total amount of download
+ * time, or NULL
+ *
+ * Parse an available query, writing the format into @format, and
+ * other results into the passed parameters, if the respective parameters
+ * are non-NULL
+ *
+ * Since: 0.10.20
+ */
+void
+gst_query_parse_buffering_range (GstQuery * query, GstFormat * format,
+ gint64 * start, gint64 * stop, gint64 * estimated_total)
+{
+ GstStructure *structure;
+
+ g_return_if_fail (GST_QUERY_TYPE (query) == GST_QUERY_BUFFERING);
+
+ structure = GST_QUERY_STRUCTURE (query);
+ if (format)
+ *format =
+ (GstFormat) g_value_get_enum (gst_structure_id_get_value (structure,
+ GST_QUARK (FORMAT)));
+ if (start)
+ *start = g_value_get_int64 (gst_structure_id_get_value (structure,
+ GST_QUARK (START_VALUE)));
+ if (stop)
+ *stop = g_value_get_int64 (gst_structure_id_get_value (structure,
+ GST_QUARK (STOP_VALUE)));
+ if (estimated_total)
+ *estimated_total =
+ g_value_get_int64 (gst_structure_id_get_value (structure,
+ GST_QUARK (ESTIMATED_TOTAL)));
+}
+
+/**
+ * gst_query_add_buffering_range
+ * @query: a GST_QUERY_BUFFERING type query #GstQuery
+ * @start: start position of the range
+ * @stop: stop position of the range
+ *
+ * Set the buffering-ranges array field in @query. The current last
+ * start position of the array should be inferior to @start.
+ *
+ * Returns: a #gboolean indicating if the range was added or not.
+ *
+ * Since: 0.10.31
+ */
+gboolean
+gst_query_add_buffering_range (GstQuery * query, gint64 start, gint64 stop)
+{
+ GValueArray *array;
+ GValue *last_array_value;
+ const GValue *value;
+ GValue range_value = { 0 };
+ GstStructure *structure;
+
+ g_return_val_if_fail (GST_QUERY_TYPE (query) == GST_QUERY_BUFFERING, FALSE);
+ g_return_val_if_fail (gst_query_is_writable (query), FALSE);
+
+ if (G_UNLIKELY (start >= stop))
+ return FALSE;
+
+ structure = GST_QUERY_STRUCTURE (query);
+ value = gst_structure_id_get_value (structure, GST_QUARK (BUFFERING_RANGES));
+ if (value) {
+ array = (GValueArray *) g_value_get_boxed (value);
+ last_array_value = g_value_array_get_nth (array, array->n_values - 1);
+ if (G_UNLIKELY (start <= gst_value_get_int64_range_min (last_array_value)))
+ return FALSE;
+ } else {
+ GValue new_array_val = { 0, };
+
+ array = g_value_array_new (0);
+
+ g_value_init (&new_array_val, G_TYPE_VALUE_ARRAY);
+ g_value_take_boxed (&new_array_val, array);
+
+ /* set the value array only once, so we then modify (append to) the
+ * existing value array owned by the GstStructure / the field's GValue */
+ gst_structure_id_take_value (structure, GST_QUARK (BUFFERING_RANGES),
+ &new_array_val);
+ }
+
+ g_value_init (&range_value, GST_TYPE_INT64_RANGE);
+ gst_value_set_int64_range (&range_value, start, stop);
+ g_value_array_append (array, &range_value);
+ /* skip the g_value_unset(&range_value) here, we know it's not needed */
+
+ return TRUE;
+}
+
+/**
+ * gst_query_get_n_buffering_ranges
+ * @query: a GST_QUERY_BUFFERING type query #GstQuery
+ *
+ * Retrieve the number of values currently stored in the
+ * buffered-ranges array of the query's structure.
+ *
+ * Returns: the range array size as a #guint.
+ *
+ * Since: 0.10.31
+ */
+guint
+gst_query_get_n_buffering_ranges (GstQuery * query)
+{
+ GValueArray *array;
+ const GValue *value;
+ guint size = 0;
+ GstStructure *structure;
+
+ g_return_val_if_fail (GST_QUERY_TYPE (query) == GST_QUERY_BUFFERING, 0);
+
+ structure = GST_QUERY_STRUCTURE (query);
+ value = gst_structure_id_get_value (structure, GST_QUARK (BUFFERING_RANGES));
+ if (value) {
+ array = (GValueArray *) g_value_get_boxed (value);
+ size = array->n_values;
+ }
+ return size;
+}
+
+
+/**
+ * gst_query_parse_nth_buffering_range
+ * @query: a GST_QUERY_BUFFERING type query #GstQuery
+ * @index: position in the buffered-ranges array to read
+ * @start: (out) (allow-none): the start position to set, or NULL
+ * @stop: (out) (allow-none): the stop position to set, or NULL
+ *
+ * Parse an available query and get the start and stop values stored
+ * at the @index of the buffered ranges array.
+ *
+ * Returns: a #gboolean indicating if the parsing succeeded.
+ *
+ * Since: 0.10.31
+ */
+gboolean
+gst_query_parse_nth_buffering_range (GstQuery * query, guint index,
+ gint64 * start, gint64 * stop)
+{
+ const GValue *value;
+ GValueArray *ranges;
+ GValue *range_value;
+ gboolean ret = FALSE;
+ GstStructure *structure;
+
+ g_return_val_if_fail (GST_QUERY_TYPE (query) == GST_QUERY_BUFFERING, ret);
+
+ structure = GST_QUERY_STRUCTURE (query);
+ value = gst_structure_id_get_value (structure, GST_QUARK (BUFFERING_RANGES));
+ ranges = (GValueArray *) g_value_get_boxed (value);
+ range_value = g_value_array_get_nth (ranges, index);
+ if (range_value) {
+ if (start)
+ *start = gst_value_get_int64_range_min (range_value);
+ if (stop)
+ *stop = gst_value_get_int64_range_max (range_value);
+ ret = TRUE;
+ }
+
+ return ret;
+}
+
+
+/**
+ * gst_query_new_uri:
+ *
+ * Constructs a new query URI query object. Use gst_query_unref()
+ * when done with it. An URI query is used to query the current URI
+ * that is used by the source or sink.
+ *
+ * Free-function: gst_query_unref
+ *
+ * Returns: (transfer full): a new #GstQuery
+ *
+ * Since: 0.10.22
+ */
+GstQuery *
+gst_query_new_uri (void)
+{
+ GstQuery *query;
+ GstStructure *structure;
+
+ structure = gst_structure_id_new (GST_QUARK (QUERY_URI),
+ GST_QUARK (URI), G_TYPE_STRING, NULL, NULL);
+
+ query = gst_query_new (GST_QUERY_URI, structure);
+
+ return query;
+}
+
+/**
+ * gst_query_set_uri:
+ * @query: a #GstQuery with query type GST_QUERY_URI
+ * @uri: the URI to set
+ *
+ * Answer a URI query by setting the requested URI.
+ *
+ * Since: 0.10.22
+ */
+void
+gst_query_set_uri (GstQuery * query, const gchar * uri)
+{
+ GstStructure *structure;
+
+ g_return_if_fail (GST_QUERY_TYPE (query) == GST_QUERY_URI);
+ g_return_if_fail (gst_query_is_writable (query));
+ g_return_if_fail (gst_uri_is_valid (uri));
+
+ structure = GST_QUERY_STRUCTURE (query);
+ gst_structure_id_set (structure, GST_QUARK (URI), G_TYPE_STRING, uri, NULL);
+}
+
+/**
+ * gst_query_parse_uri:
+ * @query: a #GstQuery
+ * @uri: (out callee-allocates) (allow-none): the storage for the current URI
+ * (may be NULL)
+ *
+ * Parse an URI query, writing the URI into @uri as a newly
+ * allocated string, if the respective parameters are non-NULL.
+ * Free the string with g_free() after usage.
+ *
+ * Since: 0.10.22
+ */
+void
+gst_query_parse_uri (GstQuery * query, gchar ** uri)
+{
+ GstStructure *structure;
+
+ g_return_if_fail (GST_QUERY_TYPE (query) == GST_QUERY_URI);
+
+ structure = GST_QUERY_STRUCTURE (query);
+ if (uri)
+ *uri = g_value_dup_string (gst_structure_id_get_value (structure,
+ GST_QUARK (URI)));
+}
+
+/**
+ * gst_query_new_allocation
+ * @caps: the negotiated caps
+ * @need_pool: return a pool
+ *
+ * Constructs a new query object for querying the allocation properties.
+ *
+ * Free-function: gst_query_unref
+ *
+ * Returns: (transfer full): a new #GstQuery
+ */
+GstQuery *
+gst_query_new_allocation (GstCaps * caps, gboolean need_pool)
+{
+ GstQuery *query;
+ GstStructure *structure;
+
+ structure = gst_structure_id_new (GST_QUARK (QUERY_ALLOCATION),
+ GST_QUARK (CAPS), GST_TYPE_CAPS, caps,
+ GST_QUARK (NEED_POOL), G_TYPE_BOOLEAN, need_pool,
+ GST_QUARK (SIZE), G_TYPE_UINT, 0,
+ GST_QUARK (MIN_BUFFERS), G_TYPE_UINT, 0,
+ GST_QUARK (MAX_BUFFERS), G_TYPE_UINT, 0,
+ GST_QUARK (PREFIX), G_TYPE_UINT, 0,
+ GST_QUARK (ALIGN), G_TYPE_UINT, 0,
+ GST_QUARK (POOL), GST_TYPE_BUFFER_POOL, NULL, NULL);
+
+ query = gst_query_new (GST_QUERY_ALLOCATION, structure);
+
+ return query;
+}
+
+void
+gst_query_parse_allocation (GstQuery * query, GstCaps ** caps,
+ gboolean * need_pool)
+{
+ GstStructure *structure;
+
+ g_return_if_fail (GST_QUERY_TYPE (query) == GST_QUERY_ALLOCATION);
+
+ structure = GST_QUERY_STRUCTURE (query);
+ gst_structure_id_get (structure,
+ GST_QUARK (CAPS), GST_TYPE_CAPS, caps,
+ GST_QUARK (NEED_POOL), G_TYPE_BOOLEAN, need_pool, NULL);
+}
+
+/**
+ * gst_query_set_allocation_params
+ * @query: A valid #GstQuery of type GST_QUERY_ALLOCATION.
+ * @size: the size
+ * @min_buffers: the min buffers
+ * @max_buffers: the max buffers
+ * @prefix: the prefix
+ * @alignment: the alignment
+ * @pool: the #GstBufferPool
+ *
+ * Set the allocation parameters in @query.
+ */
+void
+gst_query_set_allocation_params (GstQuery * query, guint size,
+ guint min_buffers, guint max_buffers, guint prefix,
+ guint alignment, GstBufferPool * pool)
+{
+ GstStructure *structure;
+
+ g_return_if_fail (GST_QUERY_TYPE (query) == GST_QUERY_ALLOCATION);
+ g_return_if_fail (gst_query_is_writable (query));
+ g_return_if_fail (((alignment + 1) & alignment) == 0);
+ g_return_if_fail (size != 0 || pool == NULL);
+
+ structure = GST_QUERY_STRUCTURE (query);
+ gst_structure_id_set (structure,
+ GST_QUARK (SIZE), G_TYPE_UINT, size,
+ GST_QUARK (MIN_BUFFERS), G_TYPE_UINT, min_buffers,
+ GST_QUARK (MAX_BUFFERS), G_TYPE_UINT, max_buffers,
+ GST_QUARK (PREFIX), G_TYPE_UINT, prefix,
+ GST_QUARK (ALIGN), G_TYPE_UINT, alignment,
+ GST_QUARK (POOL), GST_TYPE_BUFFER_POOL, pool, NULL);
+}
+
+/**
+ * gst_query_parse_allocation_params
+ * @query: A valid #GstQuery of type GST_QUERY_ALLOCATION.
+ * @size: the size
+ * @min_buffers: the min buffers
+ * @max_buffers: the max buffers
+ * @prefix: the prefix
+ * @alignment: the alignment
+ * @pool: the #GstBufferPool
+ *
+ * Get the allocation parameters in @query.
+ */
+void
+gst_query_parse_allocation_params (GstQuery * query, guint * size,
+ guint * min_buffers, guint * max_buffers, guint * prefix,
+ guint * alignment, GstBufferPool ** pool)
+{
+ GstStructure *structure;
+
+ g_return_if_fail (GST_QUERY_TYPE (query) == GST_QUERY_ALLOCATION);
+
+ structure = GST_QUERY_STRUCTURE (query);
+ gst_structure_id_get (structure,
+ GST_QUARK (SIZE), G_TYPE_UINT, size,
+ GST_QUARK (MIN_BUFFERS), G_TYPE_UINT, min_buffers,
+ GST_QUARK (MAX_BUFFERS), G_TYPE_UINT, max_buffers,
+ GST_QUARK (PREFIX), G_TYPE_UINT, prefix,
+ GST_QUARK (ALIGN), G_TYPE_UINT, alignment,
+ GST_QUARK (POOL), GST_TYPE_BUFFER_POOL, pool, NULL);
+}
+
+/**
+ * gst_query_add_allocation_meta
+ * @query: a GST_QUERY_ALLOCATION type query #GstQuery
+ * @api: the metadata API
+ *
+ * Add @api as aone of the supported metadata API to @query.
+ */
+void
+gst_query_add_allocation_meta (GstQuery * query, const gchar * api)
+{
+ GValueArray *array;
+ const GValue *value;
+ GValue api_value = { 0 };
+ GstStructure *structure;
+
+ g_return_if_fail (GST_QUERY_TYPE (query) == GST_QUERY_ALLOCATION);
+ g_return_if_fail (api != NULL);
+ g_return_if_fail (gst_query_is_writable (query));
+
+ structure = GST_QUERY_STRUCTURE (query);
+ value = gst_structure_id_get_value (structure, GST_QUARK (META));
+ if (value) {
+ array = (GValueArray *) g_value_get_boxed (value);
+ } else {
+ GValue new_array_val = { 0, };
+
+ array = g_value_array_new (0);
+
+ g_value_init (&new_array_val, G_TYPE_VALUE_ARRAY);
+ g_value_take_boxed (&new_array_val, array);
+
+ gst_structure_id_take_value (structure, GST_QUARK (META), &new_array_val);
+ }
+
+ g_value_init (&api_value, G_TYPE_STRING);
+ g_value_set_string (&api_value, api);
+ g_value_array_append (array, &api_value);
+ g_value_unset (&api_value);
+}
+
+/**
+ * gst_query_get_n_allocation_metas:
+ * @query: a GST_QUERY_ALLOCATION type query #GstQuery
+ *
+ * Retrieve the number of values currently stored in the
+ * meta API array of the query's structure.
+ *
+ * Returns: the metadata API array size as a #guint.
+ */
+guint
+gst_query_get_n_allocation_metas (GstQuery * query)
+{
+ GValueArray *array;
+ const GValue *value;
+ guint size = 0;
+ GstStructure *structure;
+
+ g_return_val_if_fail (GST_QUERY_TYPE (query) == GST_QUERY_ALLOCATION, 0);
+
+ structure = GST_QUERY_STRUCTURE (query);
+ value = gst_structure_id_get_value (structure, GST_QUARK (META));
+ if (value) {
+ array = (GValueArray *) g_value_get_boxed (value);
+ size = array->n_values;
+ }
+ return size;
+}
+
+/**
+ * gst_query_parse_nth_allocation_meta
+ * @query: a GST_QUERY_ALLOCATION type query #GstQuery
+ * @index: position in the metadata API array to read
+ *
+ * Parse an available query and get the metadata API
+ * at @index of the metadata API array.
+ *
+ * Returns: a #gchar of the metadata API at @index.
+ */
+const gchar *
+gst_query_parse_nth_allocation_meta (GstQuery * query, guint index)
+{
+ const GValue *value;
+ const gchar *ret = NULL;
+ GstStructure *structure;
+
+ g_return_val_if_fail (GST_QUERY_TYPE (query) == GST_QUERY_ALLOCATION, NULL);
+
+ structure = GST_QUERY_STRUCTURE (query);
+ value = gst_structure_id_get_value (structure, GST_QUARK (META));
+ if (value) {
+ GValueArray *meta;
+ GValue *api_value;
+
+ meta = (GValueArray *) g_value_get_boxed (value);
+ api_value = g_value_array_get_nth (meta, index);
+
+ if (api_value)
+ ret = g_value_get_string (api_value);
+ }
+ return ret;
+}
+
+/**
+ * gst_query_has_allocation_meta
+ * @query: a GST_QUERY_ALLOCATION type query #GstQuery
+ * @api: the metadata API
+ *
+ * Check if @query has metadata @api set.
+ *
+ * Returns: TRUE when @api is in the list of metadata.
+ */
+gboolean
+gst_query_has_allocation_meta (GstQuery * query, const gchar * api)
+{
+ const GValue *value;
+ GstStructure *structure;
+
+ g_return_val_if_fail (GST_QUERY_TYPE (query) == GST_QUERY_ALLOCATION, FALSE);
+ g_return_val_if_fail (api != NULL, FALSE);
+
+ structure = GST_QUERY_STRUCTURE (query);
+ value = gst_structure_id_get_value (structure, GST_QUARK (META));
+ if (value) {
+ GValueArray *array;
+ GValue *api_value;
+ guint i;
+
+ array = (GValueArray *) g_value_get_boxed (value);
+ for (i = 0; i < array->n_values; i++) {
+ api_value = g_value_array_get_nth (array, i);
+ if (!strcmp (api, g_value_get_string (api_value)))
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * gst_query_add_allocation_memory
+ * @query: a GST_QUERY_ALLOCATION type query #GstQuery
+ * @alloc: the memory allocator
+ *
+ * Add @alloc as a supported memory allocator.
+ */
+void
+gst_query_add_allocation_memory (GstQuery * query, const gchar * alloc)
+{
+ GValueArray *array;
+ const GValue *value;
+ GValue alloc_value = { 0 };
+ GstStructure *structure;
+
+ g_return_if_fail (GST_QUERY_TYPE (query) == GST_QUERY_ALLOCATION);
+ g_return_if_fail (gst_query_is_writable (query));
+
+ structure = GST_QUERY_STRUCTURE (query);
+ value = gst_structure_id_get_value (structure, GST_QUARK (ALLOCATOR));
+ if (value) {
+ array = (GValueArray *) g_value_get_boxed (value);
+ } else {
+ GValue new_array_val = { 0, };
+
+ array = g_value_array_new (0);
+
+ g_value_init (&new_array_val, G_TYPE_VALUE_ARRAY);
+ g_value_take_boxed (&new_array_val, array);
+
+ gst_structure_id_take_value (structure, GST_QUARK (ALLOCATOR),
+ &new_array_val);
+ }
+
+ g_value_init (&alloc_value, G_TYPE_STRING);
+ g_value_set_string (&alloc_value, alloc);
+ g_value_array_append (array, &alloc_value);
+ g_value_unset (&alloc_value);
+}
+
+/**
+ * gst_query_get_n_allocation_memories:
+ * @query: a GST_QUERY_ALLOCATION type query #GstQuery
+ *
+ * Retrieve the number of values currently stored in the
+ * allocator array of the query's structure.
+ *
+ * If no memory allocator is specified, the downstream element can handle
+ * the default memory allocator.
+ *
+ * Returns: the allocator array size as a #guint.
+ */
+guint
+gst_query_get_n_allocation_memories (GstQuery * query)
+{
+ GValueArray *array;
+ const GValue *value;
+ guint size = 0;
+ GstStructure *structure;
+
+ g_return_val_if_fail (GST_QUERY_TYPE (query) == GST_QUERY_ALLOCATION, 0);
+
+ structure = GST_QUERY_STRUCTURE (query);
+ value = gst_structure_id_get_value (structure, GST_QUARK (ALLOCATOR));
+ if (value) {
+ array = (GValueArray *) g_value_get_boxed (value);
+ size = array->n_values;
+ }
+ return size;
+}
+
+/**
+ * gst_query_parse_nth_allocation_memory
+ * @query: a GST_QUERY_ALLOCATION type query #GstQuery
+ * @index: position in the allocator array to read
+ *
+ * Parse an available query and get the alloctor
+ * at @index of the allocator array.
+ *
+ * Returns: the name of the allocator at @index.
+ */
+const gchar *
+gst_query_parse_nth_allocation_memory (GstQuery * query, guint index)
+{
+ const GValue *value;
+ const gchar *ret = NULL;
+ GstStructure *structure;
+
+ g_return_val_if_fail (GST_QUERY_TYPE (query) == GST_QUERY_ALLOCATION, NULL);
+
+ structure = GST_QUERY_STRUCTURE (query);
+ value = gst_structure_id_get_value (structure, GST_QUARK (ALLOCATOR));
+ if (value) {
+ GValueArray *memory;
+ GValue *alloc_value;
+
+ memory = (GValueArray *) g_value_get_boxed (value);
+ alloc_value = g_value_array_get_nth (memory, index);
+
+ if (alloc_value)
+ ret = g_value_get_string (alloc_value);
+ }
+ return ret;
+}
+
+/**
+ * gst_query_new_scheduling
+ *
+ * Constructs a new query object for querying the scheduling properties.
+ *
+ * Free-function: gst_query_unref
+ *
+ * Returns: (transfer full): a new #GstQuery
+ */
+GstQuery *
+gst_query_new_scheduling (void)
+{
+ GstQuery *query;
+ GstStructure *structure;
+
+ structure = gst_structure_id_new (GST_QUARK (QUERY_SCHEDULING),
+ GST_QUARK (PULL_MODE), G_TYPE_BOOLEAN, FALSE,
+ GST_QUARK (RANDOM_ACCESS), G_TYPE_BOOLEAN, FALSE,
+ GST_QUARK (SEQUENTIAL), G_TYPE_BOOLEAN, TRUE,
+ GST_QUARK (MINSIZE), G_TYPE_INT, 1,
+ GST_QUARK (MAXSIZE), G_TYPE_INT, -1,
+ GST_QUARK (ALIGN), G_TYPE_INT, 1, NULL);
+ query = gst_query_new (GST_QUERY_SCHEDULING, structure);
+
+ return query;
+}
+
+/**
+ * gst_query_set_scheduling
+ * @query: A valid #GstQuery of type GST_QUERY_SCHEDULING.
+ * @pull_mode: if pull mode scheduling is supported
+ * @random_access: if random access is possible
+ * @sequential: if sequential access is recommended
+ * @minsize: the suggested minimum size of pull requests
+ * @maxsize: the suggested maximum size of pull requests:
+ * @align: the suggested alignment of pull requests
+ *
+ * Set the scheduling properties.
+ */
+void
+gst_query_set_scheduling (GstQuery * query, gboolean pull_mode,
+ gboolean random_access, gboolean sequential,
+ gint minsize, gint maxsize, gint align)
+{
+ GstStructure *structure;
+
+ g_return_if_fail (GST_QUERY_TYPE (query) == GST_QUERY_SCHEDULING);
+ g_return_if_fail (gst_query_is_writable (query));
+
+ structure = GST_QUERY_STRUCTURE (query);
+ gst_structure_id_set (structure,
+ GST_QUARK (PULL_MODE), G_TYPE_BOOLEAN, pull_mode,
+ GST_QUARK (RANDOM_ACCESS), G_TYPE_BOOLEAN, random_access,
+ GST_QUARK (SEQUENTIAL), G_TYPE_BOOLEAN, sequential,
+ GST_QUARK (MINSIZE), G_TYPE_INT, minsize,
+ GST_QUARK (MAXSIZE), G_TYPE_INT, maxsize,
+ GST_QUARK (ALIGN), G_TYPE_INT, align, NULL);
+}
+
+/**
+ * gst_query_parse_scheduling
+ * @query: A valid #GstQuery of type GST_QUERY_SCHEDULING.
+ * @pull_mode: if pull mode scheduling is supported
+ * @random_access: if random access is possible
+ * @sequential: if sequential access is recommended
+ * @minsize: the suggested minimum size of pull requests
+ * @maxsize: the suggested maximum size of pull requests:
+ * @align: the suggested alignment of pull requests
+ *
+ * Set the scheduling properties.
+ */
+void
+gst_query_parse_scheduling (GstQuery * query, gboolean * pull_mode,
+ gboolean * random_access, gboolean * sequential,
+ gint * minsize, gint * maxsize, gint * align)
+{
+ GstStructure *structure;
+
+ g_return_if_fail (GST_QUERY_TYPE (query) == GST_QUERY_SCHEDULING);
+
+ structure = GST_QUERY_STRUCTURE (query);
+ gst_structure_id_get (structure,
+ GST_QUARK (PULL_MODE), G_TYPE_BOOLEAN, pull_mode,
+ GST_QUARK (RANDOM_ACCESS), G_TYPE_BOOLEAN, random_access,
+ GST_QUARK (SEQUENTIAL), G_TYPE_BOOLEAN, sequential,
+ GST_QUARK (MINSIZE), G_TYPE_INT, minsize,
+ GST_QUARK (MAXSIZE), G_TYPE_INT, maxsize,
+ GST_QUARK (ALIGN), G_TYPE_INT, align, NULL);
+}
diff --git a/gst/gstquery.h b/gst/gstquery.h
new file mode 100644
index 0000000..7b83cbc
--- /dev/null
+++ b/gst/gstquery.h
@@ -0,0 +1,385 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2000 Wim Taymans <wim.taymans@chello.be>
+ * 2005 Wim Taymans <wim@fluendo.com>
+ * 2011 Wim Taymans <wim.taymans@gmail.com>
+ *
+ * gstquery.h: GstQuery API declaration
+ *
+ * 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_QUERY_H__
+#define __GST_QUERY_H__
+
+#include <glib.h>
+
+#include <gst/gstiterator.h>
+#include <gst/gstminiobject.h>
+#include <gst/gststructure.h>
+#include <gst/gstformat.h>
+#include <gst/gstpad.h>
+
+G_BEGIN_DECLS
+
+/**
+ * GstQueryType:
+ * @GST_QUERY_NONE: invalid query type
+ * @GST_QUERY_POSITION: current position in stream
+ * @GST_QUERY_DURATION: total duration of the stream
+ * @GST_QUERY_LATENCY: latency of stream
+ * @GST_QUERY_JITTER: current jitter of stream
+ * @GST_QUERY_RATE: current rate of the stream
+ * @GST_QUERY_SEEKING: seeking capabilities
+ * @GST_QUERY_SEGMENT: segment start/stop positions
+ * @GST_QUERY_CONVERT: convert values between formats
+ * @GST_QUERY_FORMATS: query supported formats for convert
+ * @GST_QUERY_BUFFERING: query available media for efficient seeking. Since
+ * 0.10.20.
+ * @GST_QUERY_CUSTOM: a custom application or element defined query. Since
+ * 0.10.22.
+ * @GST_QUERY_URI: query the URI of the source or sink. Since 0.10.22.
+ * @GST_QUERY_ALLOCATION: the buffer allocation properties
+ * @GST_QUERY_SCHEDULING: the scheduling properties
+ *
+ * Standard predefined Query types
+ */
+/* NOTE: don't forget to update the table in gstquery.c when changing
+ * this enum */
+typedef enum {
+ GST_QUERY_NONE = 0,
+ GST_QUERY_POSITION,
+ GST_QUERY_DURATION,
+ GST_QUERY_LATENCY,
+ GST_QUERY_JITTER, /* not in draft-query, necessary? */
+ GST_QUERY_RATE,
+ GST_QUERY_SEEKING,
+ GST_QUERY_SEGMENT,
+ GST_QUERY_CONVERT,
+ GST_QUERY_FORMATS,
+ GST_QUERY_BUFFERING,
+ GST_QUERY_CUSTOM,
+ GST_QUERY_URI,
+ GST_QUERY_ALLOCATION,
+ GST_QUERY_SCHEDULING
+} GstQueryType;
+
+/**
+ * GstBufferingMode:
+ * @GST_BUFFERING_STREAM: a small amount of data is buffered
+ * @GST_BUFFERING_DOWNLOAD: the stream is being downloaded
+ * @GST_BUFFERING_TIMESHIFT: the stream is being downloaded in a ringbuffer
+ * @GST_BUFFERING_LIVE: the stream is a live stream
+ *
+ * The different types of buffering methods.
+ */
+typedef enum {
+ GST_BUFFERING_STREAM,
+ GST_BUFFERING_DOWNLOAD,
+ GST_BUFFERING_TIMESHIFT,
+ GST_BUFFERING_LIVE
+} GstBufferingMode;
+
+typedef struct _GstQueryTypeDefinition GstQueryTypeDefinition;
+typedef struct _GstQuery GstQuery;
+
+/**
+ * GstQueryTypeDefinition:
+ * @value: the unique id of the Query type
+ * @nick: a short nick
+ * @description: a longer description of the query type
+ * @quark: the quark for the nick
+ *
+ * A Query Type definition
+ */
+struct _GstQueryTypeDefinition
+{
+ GstQueryType value;
+ const gchar *nick;
+ const gchar *description;
+ GQuark quark;
+};
+
+#define GST_TYPE_QUERY (gst_query_get_type())
+#define GST_IS_QUERY(obj) (GST_IS_MINI_OBJECT_TYPE (obj, GST_TYPE_QUERY))
+#define GST_QUERY_CAST(obj) ((GstQuery*)(obj))
+#define GST_QUERY(obj) (GST_QUERY_CAST(obj))
+
+
+/**
+ * GST_QUERY_TYPE:
+ * @query: the query to query
+ *
+ * Get the #GstQueryType of the query.
+ */
+#define GST_QUERY_TYPE(query) (((GstQuery*)(query))->type)
+
+/**
+ * GST_QUERY_TYPE_NAME:
+ * @query: the query to query
+ *
+ * Get a constant string representation of the #GstQueryType of the query.
+ *
+ * Since: 0.10.4
+ */
+#define GST_QUERY_TYPE_NAME(query) (gst_query_type_get_name(GST_QUERY_TYPE(query)))
+
+
+/**
+ * GstQuery:
+ * @mini_object: The parent #GstMiniObject type
+ * @type: the #GstQueryType
+ *
+ * The #GstQuery structure.
+ */
+struct _GstQuery
+{
+ GstMiniObject mini_object;
+
+ /*< public > *//* with COW */
+ GstQueryType type;
+};
+
+const gchar* gst_query_type_get_name (GstQueryType query);
+GQuark gst_query_type_to_quark (GstQueryType query);
+
+GType gst_query_get_type (void);
+
+/* register a new query */
+GstQueryType gst_query_type_register (const gchar *nick,
+ const gchar *description);
+GstQueryType gst_query_type_get_by_nick (const gchar *nick);
+
+/* check if a query is in an array of querys */
+gboolean gst_query_types_contains (const GstQueryType *types,
+ GstQueryType type);
+
+/* query for query details */
+
+const GstQueryTypeDefinition*
+ gst_query_type_get_details (GstQueryType type);
+GstIterator* gst_query_type_iterate_definitions (void);
+
+/* refcounting */
+/**
+ * gst_query_ref:
+ * @q: a #GstQuery to increase the refcount of.
+ *
+ * Increases the refcount of the given query by one.
+ *
+ * Returns: @q
+ */
+#ifdef _FOOL_GTK_DOC_
+G_INLINE_FUNC GstQuery * gst_query_ref (GstQuery * q);
+#endif
+
+static inline GstQuery *
+gst_query_ref (GstQuery * q)
+{
+ return GST_QUERY_CAST (gst_mini_object_ref (GST_MINI_OBJECT_CAST (q)));
+}
+
+/**
+ * gst_query_unref:
+ * @q: a #GstQuery to decrease the refcount of.
+ *
+ * Decreases the refcount of the query. If the refcount reaches 0, the query
+ * will be freed.
+ */
+#ifdef _FOOL_GTK_DOC_
+G_INLINE_FUNC void gst_query_unref (GstQuery * q);
+#endif
+
+static inline void
+gst_query_unref (GstQuery * q)
+{
+ gst_mini_object_unref (GST_MINI_OBJECT_CAST (q));
+}
+
+/* copy query */
+/**
+ * gst_query_copy:
+ * @q: a #GstQuery to copy.
+ *
+ * Copies the given query using the copy function of the parent #GstStructure.
+ *
+ * Free-function: gst_query_unref
+ *
+ * Returns: (transfer full): a new copy of @q.
+ */
+#ifdef _FOOL_GTK_DOC_
+G_INLINE_FUNC GstQuery * gst_query_copy (const GstQuery * q);
+#endif
+
+static inline GstQuery *
+gst_query_copy (const GstQuery * q)
+{
+ return GST_QUERY_CAST (gst_mini_object_copy (GST_MINI_OBJECT_CONST_CAST (q)));
+}
+
+/**
+ * gst_query_is_writable:
+ * @q: a #GstQuery
+ *
+ * Tests if you can safely write data into a query's structure.
+ */
+#define gst_query_is_writable(q) gst_mini_object_is_writable (GST_MINI_OBJECT_CAST (q))
+/**
+ * gst_query_make_writable:
+ * @q: (transfer full): a #GstQuery to make writable
+ *
+ * Makes a writable query from the given query.
+ *
+ * Returns: (transfer full): a new writable query (possibly same as @q)
+ */
+#define gst_query_make_writable(q) GST_QUERY_CAST (gst_mini_object_make_writable (GST_MINI_OBJECT_CAST (q)))
+/**
+ * gst_query_replace:
+ * @old_query: (inout) (transfer full): pointer to a pointer to a #GstQuery
+ * to be replaced.
+ * @new_query: (allow-none) (transfer none): pointer to a #GstQuery that will
+ * replace the query pointed to by @old_query.
+ *
+ * Modifies a pointer to a #GstQuery to point to a different #GstQuery. The
+ * modification is done atomically (so this is useful for ensuring thread safety
+ * in some cases), and the reference counts are updated appropriately (the old
+ * query is unreffed, the new one is reffed).
+ *
+ * Either @new_query or the #GstQuery pointed to by @old_query may be NULL.
+ */
+#define gst_query_replace(old_query,new_query) \
+ gst_mini_object_replace ((GstMiniObject **)(old_query), GST_MINI_OBJECT_CAST (new_query))
+
+
+/* application specific query */
+GstQuery * gst_query_new_custom (GstQueryType type, GstStructure *structure);
+const GstStructure *
+ gst_query_get_structure (GstQuery *query);
+GstStructure * gst_query_writable_structure (GstQuery *query);
+
+/* position query */
+GstQuery* gst_query_new_position (GstFormat format);
+void gst_query_set_position (GstQuery *query, GstFormat format, gint64 cur);
+void gst_query_parse_position (GstQuery *query, GstFormat *format, gint64 *cur);
+
+/* duration query */
+GstQuery* gst_query_new_duration (GstFormat format);
+void gst_query_set_duration (GstQuery *query, GstFormat format, gint64 duration);
+void gst_query_parse_duration (GstQuery *query, GstFormat *format, gint64 *duration);
+
+/* latency query */
+GstQuery* gst_query_new_latency (void);
+void gst_query_set_latency (GstQuery *query, gboolean live, GstClockTime min_latency,
+ GstClockTime max_latency);
+void gst_query_parse_latency (GstQuery *query, gboolean *live, GstClockTime *min_latency,
+ GstClockTime *max_latency);
+
+/* convert query */
+GstQuery* gst_query_new_convert (GstFormat src_format, gint64 value, GstFormat dest_format);
+void gst_query_set_convert (GstQuery *query, GstFormat src_format, gint64 src_value,
+ GstFormat dest_format, gint64 dest_value);
+void gst_query_parse_convert (GstQuery *query, GstFormat *src_format, gint64 *src_value,
+ GstFormat *dest_format, gint64 *dest_value);
+/* segment query */
+GstQuery* gst_query_new_segment (GstFormat format);
+void gst_query_set_segment (GstQuery *query, gdouble rate, GstFormat format,
+ gint64 start_value, gint64 stop_value);
+void gst_query_parse_segment (GstQuery *query, gdouble *rate, GstFormat *format,
+ gint64 *start_value, gint64 *stop_value);
+
+/* seeking query */
+GstQuery* gst_query_new_seeking (GstFormat format);
+void gst_query_set_seeking (GstQuery *query, GstFormat format,
+ gboolean seekable,
+ gint64 segment_start,
+ gint64 segment_end);
+void gst_query_parse_seeking (GstQuery *query, GstFormat *format,
+ gboolean *seekable,
+ gint64 *segment_start,
+ gint64 *segment_end);
+/* formats query */
+GstQuery* gst_query_new_formats (void);
+void gst_query_set_formats (GstQuery *query, gint n_formats, ...);
+void gst_query_set_formatsv (GstQuery *query, gint n_formats, const GstFormat *formats);
+void gst_query_parse_n_formats (GstQuery *query, guint *n_formats);
+void gst_query_parse_nth_format (GstQuery *query, guint nth, GstFormat *format);
+
+/* buffering query */
+GstQuery* gst_query_new_buffering (GstFormat format);
+void gst_query_set_buffering_percent (GstQuery *query, gboolean busy, gint percent);
+void gst_query_parse_buffering_percent (GstQuery *query, gboolean *busy, gint *percent);
+
+void gst_query_set_buffering_stats (GstQuery *query, GstBufferingMode mode,
+ gint avg_in, gint avg_out,
+ gint64 buffering_left);
+void gst_query_parse_buffering_stats (GstQuery *query, GstBufferingMode *mode,
+ gint *avg_in, gint *avg_out,
+ gint64 *buffering_left);
+
+void gst_query_set_buffering_range (GstQuery *query, GstFormat format,
+ gint64 start, gint64 stop,
+ gint64 estimated_total);
+void gst_query_parse_buffering_range (GstQuery *query, GstFormat *format,
+ gint64 *start, gint64 *stop,
+ gint64 *estimated_total);
+
+gboolean gst_query_add_buffering_range (GstQuery *query,
+ gint64 start, gint64 stop);
+guint gst_query_get_n_buffering_ranges (GstQuery *query);
+gboolean gst_query_parse_nth_buffering_range (GstQuery *query,
+ guint index, gint64 *start,
+ gint64 *stop);
+
+/* URI query */
+GstQuery * gst_query_new_uri (void);
+void gst_query_parse_uri (GstQuery *query, gchar **uri);
+void gst_query_set_uri (GstQuery *query, const gchar *uri);
+
+/* allocation query */
+GstQuery * gst_query_new_allocation (GstCaps *caps, gboolean need_pool);
+void gst_query_parse_allocation (GstQuery *query, GstCaps **caps, gboolean *need_pool);
+
+void gst_query_set_allocation_params (GstQuery *query, guint size, guint min_buffers,
+ guint max_buffers, guint prefix, guint alignment,
+ GstBufferPool *pool);
+void gst_query_parse_allocation_params (GstQuery *query, guint *size, guint *min_buffers,
+ guint *max_buffers, guint *prefix, guint *alignment,
+ GstBufferPool **pool);
+
+void gst_query_add_allocation_meta (GstQuery *query, const gchar *api);
+guint gst_query_get_n_allocation_metas (GstQuery *query);
+const gchar * gst_query_parse_nth_allocation_meta (GstQuery *query, guint index);
+gboolean gst_query_has_allocation_meta (GstQuery *query, const gchar *api);
+
+void gst_query_add_allocation_memory (GstQuery *query, const gchar *alloc);
+guint gst_query_get_n_allocation_memories (GstQuery *query);
+const gchar * gst_query_parse_nth_allocation_memory (GstQuery *query, guint index);
+
+/* scheduling query */
+GstQuery * gst_query_new_scheduling (void);
+
+void gst_query_set_scheduling (GstQuery *query, gboolean pull_mode,
+ gboolean random_access, gboolean sequential,
+ gint minsize, gint maxsize, gint align);
+void gst_query_parse_scheduling (GstQuery *query, gboolean *pull_mode,
+ gboolean *random_access, gboolean *sequential,
+ gint *minsize, gint *maxsize, gint *align);
+
+G_END_DECLS
+
+#endif /* __GST_QUERY_H__ */
+
diff --git a/gst/gstregistry.c b/gst/gstregistry.c
new file mode 100644
index 0000000..345068e
--- /dev/null
+++ b/gst/gstregistry.c
@@ -0,0 +1,1745 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2000 Wim Taymans <wtay@chello.be>
+ * 2005 David A. Schleef <ds@schleef.org>
+ *
+ * gstregistry.c: handle registry
+ *
+ * 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:gstregistry
+ * @short_description: Abstract base class for management of #GstPlugin objects
+ * @see_also: #GstPlugin, #GstPluginFeature
+ *
+ * One registry holds the metadata of a set of plugins.
+ *
+ * <emphasis role="bold">Design:</emphasis>
+ *
+ * The #GstRegistry object is a list of plugins and some functions for dealing
+ * with them. Each #GstPlugin is matched 1-1 with a file on disk, and may or may
+ * not be loaded at a given time. There may be multiple #GstRegistry objects,
+ * but the "default registry" is the only object that has any meaning to the
+ * core.
+ *
+ * The registry file is actually a cache of plugin information. This is
+ * unlike versions prior to 0.10, where the registry file was the primary source
+ * of plugin information, and was created by the gst-register command.
+ *
+ * The primary source, at all times, of plugin information is each plugin file
+ * itself. Thus, if an application wants information about a particular plugin,
+ * or wants to search for a feature that satisfies given criteria, the primary
+ * means of doing so is to load every plugin and look at the resulting
+ * information that is gathered in the default registry. Clearly, this is a time
+ * consuming process, so we cache information in the registry file. The format
+ * and location of the cache file is internal to gstreamer.
+ *
+ * On startup, plugins are searched for in the plugin search path. The following
+ * locations are checked in this order:
+ * <itemizedlist>
+ * <listitem>
+ * <para>location from --gst-plugin-path commandline option.</para>
+ * </listitem>
+ * <listitem>
+ * <para>the GST_PLUGIN_PATH environment variable.</para>
+ * </listitem>
+ * <listitem>
+ * <para>the GST_PLUGIN_SYSTEM_PATH environment variable.</para>
+ * </listitem>
+ * <listitem>
+ * <para>default locations (if GST_PLUGIN_SYSTEM_PATH is not set). Those
+ * default locations are:
+ * <filename>~/.gstreamer-$GST_MAJORMINOR/plugins/</filename>
+ * and <filename>$prefix/libs/gstreamer-$GST_MAJORMINOR/</filename>.
+ * </para>
+ * </listitem>
+ * </itemizedlist>
+ * The registry cache file is loaded from
+ * <filename>~/.gstreamer-$GST_MAJORMINOR/registry-$ARCH.bin</filename> or the
+ * file listed in the GST_REGISTRY env var. One reason to change the registry
+ * location is for testing.
+ *
+ * For each plugin that is found in the plugin search path, there could be 3
+ * possibilities for cached information:
+ * <itemizedlist>
+ * <listitem>
+ * <para>the cache may not contain information about a given file.</para>
+ * </listitem>
+ * <listitem>
+ * <para>the cache may have stale information.</para>
+ * </listitem>
+ * <listitem>
+ * <para>the cache may have current information.</para>
+ * </listitem>
+ * </itemizedlist>
+ *
+ * In the first two cases, the plugin is loaded and the cache updated. In
+ * addition to these cases, the cache may have entries for plugins that are not
+ * relevant to the current process. These are marked as not available to the
+ * current process. If the cache is updated for whatever reason, it is marked
+ * dirty.
+ *
+ * A dirty cache is written out at the end of initialization. Each entry is
+ * checked to make sure the information is minimally valid. If not, the entry is
+ * simply dropped.
+ *
+ * <emphasis role="bold">Implementation notes:</emphasis>
+ *
+ * The "cache" and "default registry" are different concepts and can represent
+ * different sets of plugins. For various reasons, at init time, the cache is
+ * stored in the default registry, and plugins not relevant to the current
+ * process are marked with the %GST_PLUGIN_FLAG_CACHED bit. These plugins are
+ * removed at the end of initialization.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "gstconfig.h"
+#include "gst_private.h"
+#include <glib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+/* For g_stat () */
+#include <glib/gstdio.h>
+
+#include "gstinfo.h"
+#include "gsterror.h"
+#include "gstregistry.h"
+#include "gstmarshal.h"
+#include "gstfilter.h"
+
+#include "gstpluginloader.h"
+
+#include "gst-i18n-lib.h"
+
+#include "gst.h"
+#include "glib-compat-private.h"
+
+#ifdef G_OS_WIN32
+#include <windows.h>
+extern HMODULE _priv_gst_dll_handle;
+#endif
+
+#define GST_CAT_DEFAULT GST_CAT_REGISTRY
+
+struct _GstRegistryPrivate
+{
+ /* updated whenever the feature list changes */
+ guint32 cookie;
+ /* speedup for searching features */
+ GList *element_factory_list;
+ guint32 efl_cookie;
+ GList *typefind_factory_list;
+ guint32 tfl_cookie;
+};
+
+/* the one instance of the default registry and the mutex protecting the
+ * variable. */
+static GStaticMutex _gst_registry_mutex = G_STATIC_MUTEX_INIT;
+static GstRegistry *_gst_registry_default = NULL;
+
+/* defaults */
+#define DEFAULT_FORK TRUE
+
+/* control the behaviour of registry rebuild */
+static gboolean _gst_enable_registry_fork = DEFAULT_FORK;
+/* List of plugins that need preloading/reloading after scanning registry */
+extern GSList *_priv_gst_preload_plugins;
+
+#ifndef GST_DISABLE_REGISTRY
+/*set to TRUE when registry needn't to be updated */
+gboolean _priv_gst_disable_registry_update = FALSE;
+extern GList *_priv_gst_plugin_paths;
+
+/* Set to TRUE when the registry cache should be disabled */
+gboolean _gst_disable_registry_cache = FALSE;
+#endif
+
+/* Element signals and args */
+enum
+{
+ PLUGIN_ADDED,
+ FEATURE_ADDED,
+ LAST_SIGNAL
+};
+
+static void gst_registry_finalize (GObject * object);
+
+static guint gst_registry_signals[LAST_SIGNAL] = { 0 };
+
+static GstPluginFeature *gst_registry_lookup_feature_locked (GstRegistry *
+ registry, const char *name);
+static GstPlugin *gst_registry_lookup_bn_locked (GstRegistry * registry,
+ const char *basename);
+
+G_DEFINE_TYPE (GstRegistry, gst_registry, GST_TYPE_OBJECT);
+static GstObjectClass *parent_class = NULL;
+
+static void
+gst_registry_class_init (GstRegistryClass * klass)
+{
+ GObjectClass *gobject_class;
+
+ gobject_class = (GObjectClass *) klass;
+
+ parent_class = g_type_class_peek_parent (klass);
+ g_type_class_add_private (klass, sizeof (GstRegistryPrivate));
+
+ /**
+ * GstRegistry::plugin-added:
+ * @registry: the registry that emitted the signal
+ * @plugin: the plugin that has been added
+ *
+ * Signals that a plugin has been added to the registry (possibly
+ * replacing a previously-added one by the same name)
+ */
+ gst_registry_signals[PLUGIN_ADDED] =
+ g_signal_new ("plugin-added", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRegistryClass, plugin_added), NULL,
+ NULL, gst_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER);
+
+ /**
+ * GstRegistry::feature-added:
+ * @registry: the registry that emitted the signal
+ * @feature: the feature that has been added
+ *
+ * Signals that a feature has been added to the registry (possibly
+ * replacing a previously-added one by the same name)
+ */
+ gst_registry_signals[FEATURE_ADDED] =
+ g_signal_new ("feature-added", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRegistryClass, feature_added),
+ NULL, NULL, gst_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER);
+
+ gobject_class->finalize = gst_registry_finalize;
+}
+
+static void
+gst_registry_init (GstRegistry * registry)
+{
+ registry->feature_hash = g_hash_table_new (g_str_hash, g_str_equal);
+ registry->basename_hash = g_hash_table_new (g_str_hash, g_str_equal);
+ registry->priv =
+ G_TYPE_INSTANCE_GET_PRIVATE (registry, GST_TYPE_REGISTRY,
+ GstRegistryPrivate);
+}
+
+static void
+gst_registry_finalize (GObject * object)
+{
+ GstRegistry *registry = GST_REGISTRY (object);
+ GList *plugins, *p;
+ GList *features, *f;
+
+ plugins = registry->plugins;
+ registry->plugins = NULL;
+
+ GST_DEBUG_OBJECT (registry, "registry finalize");
+ p = plugins;
+ while (p) {
+ GstPlugin *plugin = p->data;
+
+ if (plugin) {
+ GST_LOG_OBJECT (registry, "removing plugin %s",
+ gst_plugin_get_name (plugin));
+ gst_object_unref (plugin);
+ }
+ p = g_list_next (p);
+ }
+ g_list_free (plugins);
+
+ features = registry->features;
+ registry->features = NULL;
+
+ f = features;
+ while (f) {
+ GstPluginFeature *feature = f->data;
+
+ if (feature) {
+ GST_LOG_OBJECT (registry, "removing feature %p (%s)",
+ feature, gst_plugin_feature_get_name (feature));
+ gst_object_unparent (GST_OBJECT_CAST (feature));
+ }
+ f = g_list_next (f);
+ }
+ g_list_free (features);
+
+ g_hash_table_destroy (registry->feature_hash);
+ registry->feature_hash = NULL;
+ g_hash_table_destroy (registry->basename_hash);
+ registry->basename_hash = NULL;
+
+ if (registry->priv->element_factory_list) {
+ GST_DEBUG_OBJECT (registry, "Cleaning up cached element factory list");
+ gst_plugin_feature_list_free (registry->priv->element_factory_list);
+ }
+
+ if (registry->priv->typefind_factory_list) {
+ GST_DEBUG_OBJECT (registry, "Cleaning up cached typefind factory list");
+ gst_plugin_feature_list_free (registry->priv->typefind_factory_list);
+ }
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+/**
+ * gst_registry_get_default:
+ *
+ * Retrieves the default registry. The caller does not own a reference on the
+ * registry, as it is alive as long as GStreamer is initialized.
+ *
+ * Returns: (transfer none): The default #GstRegistry.
+ */
+GstRegistry *
+gst_registry_get_default (void)
+{
+ GstRegistry *registry;
+
+ g_static_mutex_lock (&_gst_registry_mutex);
+ if (G_UNLIKELY (!_gst_registry_default)) {
+ _gst_registry_default = g_object_newv (GST_TYPE_REGISTRY, 0, NULL);
+ gst_object_ref_sink (GST_OBJECT_CAST (_gst_registry_default));
+ }
+ registry = _gst_registry_default;
+ g_static_mutex_unlock (&_gst_registry_mutex);
+
+ return registry;
+}
+
+/**
+ * gst_registry_add_path:
+ * @registry: the registry to add the path to
+ * @path: the path to add to the registry
+ *
+ * Add the given path to the registry. The syntax of the
+ * path is specific to the registry. If the path has already been
+ * added, do nothing.
+ */
+void
+gst_registry_add_path (GstRegistry * registry, const gchar * path)
+{
+ g_return_if_fail (GST_IS_REGISTRY (registry));
+ g_return_if_fail (path != NULL);
+
+ if (strlen (path) == 0)
+ goto empty_path;
+
+ GST_OBJECT_LOCK (registry);
+ if (g_list_find_custom (registry->paths, path, (GCompareFunc) strcmp))
+ goto was_added;
+
+ GST_INFO ("Adding plugin path: \"%s\"", path);
+ registry->paths = g_list_append (registry->paths, g_strdup (path));
+ GST_OBJECT_UNLOCK (registry);
+
+ return;
+
+empty_path:
+ {
+ GST_INFO ("Ignoring empty plugin path");
+ return;
+ }
+was_added:
+ {
+ g_warning ("path %s already added to registry", path);
+ GST_OBJECT_UNLOCK (registry);
+ return;
+ }
+}
+
+/**
+ * gst_registry_get_path_list:
+ * @registry: the registry to get the pathlist of
+ *
+ * Get the list of paths for the given registry.
+ *
+ * Returns: (transfer container) (element-type char*): A #GList of paths as
+ * strings. g_list_free after use.
+ *
+ * MT safe.
+ */
+GList *
+gst_registry_get_path_list (GstRegistry * registry)
+{
+ GList *list;
+
+ g_return_val_if_fail (GST_IS_REGISTRY (registry), NULL);
+
+ GST_OBJECT_LOCK (registry);
+ /* We don't need to copy the strings, because they won't be deleted
+ * as long as the GstRegistry is around */
+ list = g_list_copy (registry->paths);
+ GST_OBJECT_UNLOCK (registry);
+
+ return list;
+}
+
+
+/**
+ * gst_registry_add_plugin:
+ * @registry: the registry to add the plugin to
+ * @plugin: (transfer full): the plugin to add
+ *
+ * Add the plugin to the registry. The plugin-added signal will be emitted.
+ * This function will sink @plugin.
+ *
+ * Returns: TRUE on success.
+ *
+ * MT safe.
+ */
+gboolean
+gst_registry_add_plugin (GstRegistry * registry, GstPlugin * plugin)
+{
+ GstPlugin *existing_plugin;
+
+ g_return_val_if_fail (GST_IS_REGISTRY (registry), FALSE);
+ g_return_val_if_fail (GST_IS_PLUGIN (plugin), FALSE);
+
+ GST_OBJECT_LOCK (registry);
+ if (G_LIKELY (plugin->basename)) {
+ /* we have a basename, see if we find the plugin */
+ existing_plugin =
+ gst_registry_lookup_bn_locked (registry, plugin->basename);
+ if (existing_plugin) {
+ GST_DEBUG_OBJECT (registry,
+ "Replacing existing plugin \"%s\" %p with new plugin %p for filename \"%s\"",
+ GST_STR_NULL (existing_plugin->filename), existing_plugin, plugin,
+ GST_STR_NULL (plugin->filename));
+ /* If the new plugin is blacklisted and the existing one isn't cached, do not
+ * accept if it's from a different location than the existing one */
+ if ((plugin->flags & GST_PLUGIN_FLAG_BLACKLISTED) &&
+ strcmp (plugin->filename, existing_plugin->filename)) {
+ GST_WARNING_OBJECT (registry,
+ "Not replacing plugin because new one (%s) is blacklisted but for a different location than existing one (%s)",
+ plugin->filename, existing_plugin->filename);
+ gst_object_unref (plugin);
+ GST_OBJECT_UNLOCK (registry);
+ return FALSE;
+ }
+ registry->plugins = g_list_remove (registry->plugins, existing_plugin);
+ if (G_LIKELY (existing_plugin->basename))
+ g_hash_table_remove (registry->basename_hash,
+ existing_plugin->basename);
+ gst_object_unref (existing_plugin);
+ }
+ }
+
+ GST_DEBUG_OBJECT (registry, "adding plugin %p for filename \"%s\"",
+ plugin, GST_STR_NULL (plugin->filename));
+
+ registry->plugins = g_list_prepend (registry->plugins, plugin);
+ if (G_LIKELY (plugin->basename))
+ g_hash_table_replace (registry->basename_hash, plugin->basename, plugin);
+
+ gst_object_ref_sink (plugin);
+ GST_OBJECT_UNLOCK (registry);
+
+ GST_LOG_OBJECT (registry, "emitting plugin-added for filename \"%s\"",
+ GST_STR_NULL (plugin->filename));
+ g_signal_emit (registry, gst_registry_signals[PLUGIN_ADDED], 0, plugin);
+
+ return TRUE;
+}
+
+static void
+gst_registry_remove_features_for_plugin_unlocked (GstRegistry * registry,
+ GstPlugin * plugin)
+{
+ GList *f;
+
+ g_return_if_fail (GST_IS_REGISTRY (registry));
+ g_return_if_fail (GST_IS_PLUGIN (plugin));
+
+ /* Remove all features for this plugin */
+ f = registry->features;
+ while (f != NULL) {
+ GList *next = g_list_next (f);
+ GstPluginFeature *feature = f->data;
+
+ if (G_UNLIKELY (feature && feature->plugin == plugin)) {
+ GST_DEBUG_OBJECT (registry, "removing feature %p (%s) for plugin %p (%s)",
+ feature, gst_plugin_feature_get_name (feature), plugin,
+ plugin->desc.name);
+
+ registry->features = g_list_delete_link (registry->features, f);
+ g_hash_table_remove (registry->feature_hash, GST_OBJECT_NAME (feature));
+ gst_object_unparent (GST_OBJECT_CAST (feature));
+ }
+ f = next;
+ }
+ registry->priv->cookie++;
+}
+
+/**
+ * gst_registry_remove_plugin:
+ * @registry: the registry to remove the plugin from
+ * @plugin: (transfer none): the plugin to remove
+ *
+ * Remove the plugin from the registry.
+ *
+ * MT safe.
+ */
+void
+gst_registry_remove_plugin (GstRegistry * registry, GstPlugin * plugin)
+{
+ g_return_if_fail (GST_IS_REGISTRY (registry));
+ g_return_if_fail (GST_IS_PLUGIN (plugin));
+
+ GST_DEBUG_OBJECT (registry, "removing plugin %p (%s)",
+ plugin, gst_plugin_get_name (plugin));
+
+ GST_OBJECT_LOCK (registry);
+ registry->plugins = g_list_remove (registry->plugins, plugin);
+ if (G_LIKELY (plugin->basename))
+ g_hash_table_remove (registry->basename_hash, plugin->basename);
+ gst_registry_remove_features_for_plugin_unlocked (registry, plugin);
+ GST_OBJECT_UNLOCK (registry);
+ gst_object_unref (plugin);
+}
+
+/**
+ * gst_registry_add_feature:
+ * @registry: the registry to add the plugin to
+ * @feature: (transfer full): the feature to add
+ *
+ * Add the feature to the registry. The feature-added signal will be emitted.
+ * This function sinks @feature.
+ *
+ * Returns: TRUE on success.
+ *
+ * MT safe.
+ */
+gboolean
+gst_registry_add_feature (GstRegistry * registry, GstPluginFeature * feature)
+{
+ GstPluginFeature *existing_feature;
+
+ g_return_val_if_fail (GST_IS_REGISTRY (registry), FALSE);
+ g_return_val_if_fail (GST_IS_PLUGIN_FEATURE (feature), FALSE);
+ g_return_val_if_fail (GST_OBJECT_NAME (feature) != NULL, FALSE);
+ g_return_val_if_fail (feature->plugin_name != NULL, FALSE);
+
+ GST_OBJECT_LOCK (registry);
+ existing_feature = gst_registry_lookup_feature_locked (registry,
+ GST_OBJECT_NAME (feature));
+ if (G_UNLIKELY (existing_feature)) {
+ GST_DEBUG_OBJECT (registry, "replacing existing feature %p (%s)",
+ existing_feature, GST_OBJECT_NAME (feature));
+ /* Remove the existing feature from the list now, before we insert the new
+ * one, but don't unref yet because the hash is still storing a reference to
+ * it. */
+ registry->features = g_list_remove (registry->features, existing_feature);
+ }
+
+ GST_DEBUG_OBJECT (registry, "adding feature %p (%s)", feature,
+ GST_OBJECT_NAME (feature));
+
+ registry->features = g_list_prepend (registry->features, feature);
+ g_hash_table_replace (registry->feature_hash, GST_OBJECT_NAME (feature),
+ feature);
+
+ if (G_UNLIKELY (existing_feature)) {
+ /* We unref now. No need to remove the feature name from the hash table, it
+ * got replaced by the new feature */
+ gst_object_unparent (GST_OBJECT_CAST (existing_feature));
+ }
+
+ gst_object_set_parent (GST_OBJECT_CAST (feature), GST_OBJECT_CAST (registry));
+
+ registry->priv->cookie++;
+ GST_OBJECT_UNLOCK (registry);
+
+ GST_LOG_OBJECT (registry, "emitting feature-added for %s",
+ GST_OBJECT_NAME (feature));
+ g_signal_emit (registry, gst_registry_signals[FEATURE_ADDED], 0, feature);
+
+ return TRUE;
+}
+
+/**
+ * gst_registry_remove_feature:
+ * @registry: the registry to remove the feature from
+ * @feature: (transfer none): the feature to remove
+ *
+ * Remove the feature from the registry.
+ *
+ * MT safe.
+ */
+void
+gst_registry_remove_feature (GstRegistry * registry, GstPluginFeature * feature)
+{
+ g_return_if_fail (GST_IS_REGISTRY (registry));
+ g_return_if_fail (GST_IS_PLUGIN_FEATURE (feature));
+
+ GST_DEBUG_OBJECT (registry, "removing feature %p (%s)",
+ feature, gst_plugin_feature_get_name (feature));
+
+ GST_OBJECT_LOCK (registry);
+ registry->features = g_list_remove (registry->features, feature);
+ g_hash_table_remove (registry->feature_hash, GST_OBJECT_NAME (feature));
+ registry->priv->cookie++;
+ GST_OBJECT_UNLOCK (registry);
+
+ gst_object_unparent ((GstObject *) feature);
+}
+
+/**
+ * gst_registry_plugin_filter:
+ * @registry: registry to query
+ * @filter: (scope call): the filter to use
+ * @first: only return first match
+ * @user_data: (closure): user data passed to the filter function
+ *
+ * Runs a filter against all plugins in the registry and returns a #GList with
+ * the results. If the first flag is set, only the first match is
+ * returned (as a list with a single object).
+ * Every plugin is reffed; use gst_plugin_list_free() after use, which
+ * will unref again.
+ *
+ * Returns: (transfer full) (element-type Gst.Plugin): a #GList of #GstPlugin.
+ * Use gst_plugin_list_free() after usage.
+ *
+ * MT safe.
+ */
+GList *
+gst_registry_plugin_filter (GstRegistry * registry,
+ GstPluginFilter filter, gboolean first, gpointer user_data)
+{
+ GList *list;
+ GList *g;
+
+ g_return_val_if_fail (GST_IS_REGISTRY (registry), NULL);
+
+ GST_OBJECT_LOCK (registry);
+ list = gst_filter_run (registry->plugins, (GstFilterFunc) filter, first,
+ user_data);
+ for (g = list; g; g = g->next) {
+ gst_object_ref (GST_PLUGIN_CAST (g->data));
+ }
+ GST_OBJECT_UNLOCK (registry);
+
+ return list;
+}
+
+/* returns TRUE if the list was changed
+ *
+ * Must be called with the object lock taken */
+static gboolean
+gst_registry_get_feature_list_or_create (GstRegistry * registry,
+ GList ** previous, guint32 * cookie, GType type)
+{
+ gboolean res = FALSE;
+ GstRegistryPrivate *priv = registry->priv;
+
+ if (G_UNLIKELY (!*previous || priv->cookie != *cookie)) {
+ GstTypeNameData data;
+
+ if (*previous)
+ gst_plugin_feature_list_free (*previous);
+
+ data.type = type;
+ data.name = NULL;
+ *previous =
+ gst_filter_run (registry->features,
+ (GstFilterFunc) gst_plugin_feature_type_name_filter, FALSE, &data);
+ g_list_foreach (*previous, (GFunc) gst_object_ref, NULL);
+ *cookie = priv->cookie;
+ res = TRUE;
+ }
+
+ return res;
+}
+
+static gint
+type_find_factory_rank_cmp (const GstPluginFeature * fac1,
+ const GstPluginFeature * fac2)
+{
+ if (G_LIKELY (fac1->rank != fac2->rank))
+ return fac2->rank - fac1->rank;
+
+ /* to make the order in which things happen more deterministic,
+ * sort by name when the ranks are the same. */
+ return strcmp (GST_OBJECT_NAME (fac1), GST_OBJECT_NAME (fac2));
+}
+
+static GList *
+gst_registry_get_element_factory_list (GstRegistry * registry)
+{
+ GList *list;
+
+ GST_OBJECT_LOCK (registry);
+
+ gst_registry_get_feature_list_or_create (registry,
+ &registry->priv->element_factory_list, &registry->priv->efl_cookie,
+ GST_TYPE_ELEMENT_FACTORY);
+
+ /* Return reffed copy */
+ list = gst_plugin_feature_list_copy (registry->priv->element_factory_list);
+
+ GST_OBJECT_UNLOCK (registry);
+
+ return list;
+}
+
+static GList *
+gst_registry_get_typefind_factory_list (GstRegistry * registry)
+{
+ GList *list;
+
+ GST_OBJECT_LOCK (registry);
+
+ if (G_UNLIKELY (gst_registry_get_feature_list_or_create (registry,
+ &registry->priv->typefind_factory_list,
+ &registry->priv->tfl_cookie, GST_TYPE_TYPE_FIND_FACTORY)))
+ registry->priv->typefind_factory_list =
+ g_list_sort (registry->priv->typefind_factory_list,
+ (GCompareFunc) type_find_factory_rank_cmp);
+
+ /* Return reffed copy */
+ list = gst_plugin_feature_list_copy (registry->priv->typefind_factory_list);
+
+ GST_OBJECT_UNLOCK (registry);
+
+ return list;
+}
+
+/**
+ * gst_registry_feature_filter:
+ * @registry: registry to query
+ * @filter: (scope call): the filter to use
+ * @first: only return first match
+ * @user_data: (closure): user data passed to the filter function
+ *
+ * Runs a filter against all features of the plugins in the registry
+ * and returns a GList with the results.
+ * If the first flag is set, only the first match is
+ * returned (as a list with a single object).
+ *
+ * Returns: (transfer full) (element-type Gst.PluginFeature): a #GList of
+ * #GstPluginFeature. Use gst_plugin_feature_list_free() after usage.
+ *
+ * MT safe.
+ */
+GList *
+gst_registry_feature_filter (GstRegistry * registry,
+ GstPluginFeatureFilter filter, gboolean first, gpointer user_data)
+{
+ GList *list;
+ GList *g;
+
+ g_return_val_if_fail (GST_IS_REGISTRY (registry), NULL);
+
+ GST_OBJECT_LOCK (registry);
+ list = gst_filter_run (registry->features, (GstFilterFunc) filter, first,
+ user_data);
+ for (g = list; g; g = g->next) {
+ gst_object_ref (GST_PLUGIN_FEATURE_CAST (g->data));
+ }
+ GST_OBJECT_UNLOCK (registry);
+
+ return list;
+}
+
+/**
+ * gst_registry_find_plugin:
+ * @registry: the registry to search
+ * @name: the plugin name to find
+ *
+ * Find the plugin with the given name in the registry.
+ * The plugin will be reffed; caller is responsible for unreffing.
+ *
+ * Returns: (transfer full): the plugin with the given name or NULL if the
+ * plugin was not found. gst_object_unref() after usage.
+ *
+ * MT safe.
+ */
+GstPlugin *
+gst_registry_find_plugin (GstRegistry * registry, const gchar * name)
+{
+ GList *walk;
+ GstPlugin *result = NULL;
+
+ g_return_val_if_fail (GST_IS_REGISTRY (registry), NULL);
+ g_return_val_if_fail (name != NULL, NULL);
+
+ walk = gst_registry_plugin_filter (registry,
+ (GstPluginFilter) gst_plugin_name_filter, TRUE, (gpointer) name);
+ if (walk) {
+ result = GST_PLUGIN_CAST (walk->data);
+
+ gst_object_ref (result);
+ gst_plugin_list_free (walk);
+ }
+
+ return result;
+}
+
+/**
+ * gst_registry_find_feature:
+ * @registry: the registry to search
+ * @name: the pluginfeature name to find
+ * @type: the pluginfeature type to find
+ *
+ * Find the pluginfeature with the given name and type in the registry.
+ *
+ * Returns: (transfer full): the pluginfeature with the given name and type
+ * or NULL if the plugin was not found. gst_object_unref() after usage.
+ *
+ * MT safe.
+ */
+GstPluginFeature *
+gst_registry_find_feature (GstRegistry * registry, const gchar * name,
+ GType type)
+{
+ GstPluginFeature *feature = NULL;
+
+ g_return_val_if_fail (GST_IS_REGISTRY (registry), NULL);
+ g_return_val_if_fail (name != NULL, NULL);
+ g_return_val_if_fail (g_type_is_a (type, GST_TYPE_PLUGIN_FEATURE), NULL);
+
+ feature = gst_registry_lookup_feature (registry, name);
+ if (feature && !g_type_is_a (G_TYPE_FROM_INSTANCE (feature), type)) {
+ gst_object_unref (feature);
+ feature = NULL;
+ }
+
+ return feature;
+}
+
+/**
+ * gst_registry_get_feature_list:
+ * @registry: a #GstRegistry
+ * @type: a #GType.
+ *
+ * Retrieves a #GList of #GstPluginFeature of @type.
+ *
+ * Returns: (transfer full) (element-type Gst.PluginFeature): a #GList of
+ * #GstPluginFeature of @type. Use gst_plugin_feature_list_free() after use
+ *
+ * MT safe.
+ */
+GList *
+gst_registry_get_feature_list (GstRegistry * registry, GType type)
+{
+ GstTypeNameData data;
+
+ g_return_val_if_fail (GST_IS_REGISTRY (registry), NULL);
+ g_return_val_if_fail (g_type_is_a (type, GST_TYPE_PLUGIN_FEATURE), NULL);
+
+ /* Speed up */
+ if (type == GST_TYPE_ELEMENT_FACTORY)
+ return gst_registry_get_element_factory_list (registry);
+ else if (type == GST_TYPE_TYPE_FIND_FACTORY)
+ return gst_registry_get_typefind_factory_list (registry);
+
+ data.type = type;
+ data.name = NULL;
+
+ return gst_registry_feature_filter (registry,
+ (GstPluginFeatureFilter) gst_plugin_feature_type_name_filter,
+ FALSE, &data);
+}
+
+/**
+ * gst_registry_get_plugin_list:
+ * @registry: the registry to search
+ *
+ * Get a copy of all plugins registered in the given registry. The refcount
+ * of each element in the list in incremented.
+ *
+ * Returns: (transfer full) (element-type Gst.Plugin): a #GList of #GstPlugin.
+ * Use gst_plugin_list_free() after usage.
+ *
+ * MT safe.
+ */
+GList *
+gst_registry_get_plugin_list (GstRegistry * registry)
+{
+ GList *list;
+ GList *g;
+
+ g_return_val_if_fail (GST_IS_REGISTRY (registry), NULL);
+
+ GST_OBJECT_LOCK (registry);
+ list = g_list_copy (registry->plugins);
+ for (g = list; g; g = g->next) {
+ gst_object_ref (GST_PLUGIN_CAST (g->data));
+ }
+ GST_OBJECT_UNLOCK (registry);
+
+ return list;
+}
+
+static GstPluginFeature *
+gst_registry_lookup_feature_locked (GstRegistry * registry, const char *name)
+{
+ return g_hash_table_lookup (registry->feature_hash, name);
+}
+
+/**
+ * gst_registry_lookup_feature:
+ * @registry: a #GstRegistry
+ * @name: a #GstPluginFeature name
+ *
+ * Find a #GstPluginFeature with @name in @registry.
+ *
+ * Returns: (transfer full): a #GstPluginFeature with its refcount incremented,
+ * use gst_object_unref() after usage.
+ *
+ * MT safe.
+ */
+GstPluginFeature *
+gst_registry_lookup_feature (GstRegistry * registry, const char *name)
+{
+ GstPluginFeature *feature;
+
+ g_return_val_if_fail (GST_IS_REGISTRY (registry), NULL);
+ g_return_val_if_fail (name != NULL, NULL);
+
+ GST_OBJECT_LOCK (registry);
+ feature = gst_registry_lookup_feature_locked (registry, name);
+ if (feature)
+ gst_object_ref (feature);
+ GST_OBJECT_UNLOCK (registry);
+
+ return feature;
+}
+
+static GstPlugin *
+gst_registry_lookup_bn_locked (GstRegistry * registry, const char *basename)
+{
+ return g_hash_table_lookup (registry->basename_hash, basename);
+}
+
+static GstPlugin *
+gst_registry_lookup_bn (GstRegistry * registry, const char *basename)
+{
+ GstPlugin *plugin;
+
+ GST_OBJECT_LOCK (registry);
+ plugin = gst_registry_lookup_bn_locked (registry, basename);
+ if (plugin)
+ gst_object_ref (plugin);
+ GST_OBJECT_UNLOCK (registry);
+
+ return plugin;
+}
+
+/**
+ * gst_registry_lookup:
+ * @registry: the registry to look up in
+ * @filename: the name of the file to look up
+ *
+ * Look up a plugin in the given registry with the given filename.
+ * If found, plugin is reffed.
+ *
+ * Returns: (transfer full): the #GstPlugin if found, or NULL if not.
+ * gst_object_unref() after usage.
+ */
+GstPlugin *
+gst_registry_lookup (GstRegistry * registry, const char *filename)
+{
+ GstPlugin *plugin;
+ gchar *basename;
+
+ g_return_val_if_fail (GST_IS_REGISTRY (registry), NULL);
+ g_return_val_if_fail (filename != NULL, NULL);
+
+ basename = g_path_get_basename (filename);
+ if (G_UNLIKELY (basename == NULL))
+ return NULL;
+
+ plugin = gst_registry_lookup_bn (registry, basename);
+
+ g_free (basename);
+
+ return plugin;
+}
+
+typedef enum
+{
+ REGISTRY_SCAN_HELPER_NOT_STARTED = 0,
+ REGISTRY_SCAN_HELPER_DISABLED,
+ REGISTRY_SCAN_HELPER_RUNNING
+} GstRegistryScanHelperState;
+
+typedef struct
+{
+ GstRegistry *registry;
+ GstRegistryScanHelperState helper_state;
+ GstPluginLoader *helper;
+ gboolean changed;
+} GstRegistryScanContext;
+
+static void
+init_scan_context (GstRegistryScanContext * context, GstRegistry * registry)
+{
+ gboolean do_fork;
+
+ context->registry = registry;
+
+ /* see if forking is enabled and set up the scan helper state accordingly */
+ do_fork = _gst_enable_registry_fork;
+ if (do_fork) {
+ const gchar *fork_env;
+
+ /* forking enabled, see if it is disabled with an env var */
+ if ((fork_env = g_getenv ("GST_REGISTRY_FORK"))) {
+ /* fork enabled for any value different from "no" */
+ do_fork = strcmp (fork_env, "no") != 0;
+ }
+ }
+
+ if (do_fork)
+ context->helper_state = REGISTRY_SCAN_HELPER_NOT_STARTED;
+ else
+ context->helper_state = REGISTRY_SCAN_HELPER_DISABLED;
+
+ context->helper = NULL;
+ context->changed = FALSE;
+}
+
+static void
+clear_scan_context (GstRegistryScanContext * context)
+{
+ if (context->helper) {
+ context->changed |= _priv_gst_plugin_loader_funcs.destroy (context->helper);
+ context->helper = NULL;
+ }
+}
+
+static gboolean
+gst_registry_scan_plugin_file (GstRegistryScanContext * context,
+ const gchar * filename, off_t file_size, time_t file_mtime)
+{
+ gboolean changed = FALSE;
+ GstPlugin *newplugin = NULL;
+
+#ifdef G_OS_WIN32
+ /* Disable external plugin loader on Windows until it is ported properly. */
+ context->helper_state = REGISTRY_SCAN_HELPER_DISABLED;
+#endif
+
+
+ /* Have a plugin to load - see if the scan-helper needs starting */
+ if (context->helper_state == REGISTRY_SCAN_HELPER_NOT_STARTED) {
+ GST_DEBUG ("Starting plugin scanner for file %s", filename);
+ context->helper = _priv_gst_plugin_loader_funcs.create (context->registry);
+ if (context->helper != NULL)
+ context->helper_state = REGISTRY_SCAN_HELPER_RUNNING;
+ else {
+ GST_WARNING ("Failed starting plugin scanner. Scanning in-process");
+ context->helper_state = REGISTRY_SCAN_HELPER_DISABLED;
+ }
+ }
+
+ if (context->helper_state == REGISTRY_SCAN_HELPER_RUNNING) {
+ GST_DEBUG ("Using scan-helper to load plugin %s", filename);
+ if (!_priv_gst_plugin_loader_funcs.load (context->helper,
+ filename, file_size, file_mtime)) {
+ g_warning ("External plugin loader failed. This most likely means that "
+ "the plugin loader helper binary was not found or could not be run. "
+ "%s", (g_getenv ("GST_PLUGIN_PATH") != NULL) ?
+ "If you are running an uninstalled GStreamer setup, you might need "
+ "to update your gst-uninstalled script so that the "
+ "GST_PLUGIN_SCANNER environment variable gets set." : "");
+ context->helper_state = REGISTRY_SCAN_HELPER_DISABLED;
+ }
+ }
+
+ /* Check if the helper is disabled (or just got disabled above) */
+ if (context->helper_state == REGISTRY_SCAN_HELPER_DISABLED) {
+ /* Load plugin the old fashioned way... */
+
+ /* We don't use a GError here because a failure to load some shared
+ * objects as plugins is normal (particularly in the uninstalled case)
+ */
+ newplugin = gst_plugin_load_file (filename, NULL);
+ }
+
+ if (newplugin) {
+ GST_DEBUG_OBJECT (context->registry, "marking new plugin %p as registered",
+ newplugin);
+ newplugin->registered = TRUE;
+ gst_object_unref (newplugin);
+ changed = TRUE;
+ }
+
+ return changed;
+}
+
+static gboolean
+is_blacklisted_hidden_directory (const gchar * dirent)
+{
+ if (G_LIKELY (dirent[0] != '.'))
+ return FALSE;
+
+ /* skip the .debug directory, these contain elf files that are not
+ * useful or worse, can crash dlopen () */
+ if (strcmp (dirent, ".debug") == 0)
+ return TRUE;
+
+ /* can also skip .git and .deps dirs, those won't contain useful files.
+ * This speeds up scanning a bit in uninstalled setups. */
+ if (strcmp (dirent, ".git") == 0 || strcmp (dirent, ".deps") == 0)
+ return TRUE;
+
+ return FALSE;
+}
+
+static gboolean
+gst_registry_scan_path_level (GstRegistryScanContext * context,
+ const gchar * path, int level)
+{
+ GDir *dir;
+ const gchar *dirent;
+ gchar *filename;
+ GstPlugin *plugin;
+ gboolean changed = FALSE;
+
+ dir = g_dir_open (path, 0, NULL);
+ if (!dir)
+ return FALSE;
+
+ while ((dirent = g_dir_read_name (dir))) {
+ GStatBuf file_status;
+
+ filename = g_build_filename (path, dirent, NULL);
+ if (g_stat (filename, &file_status) < 0) {
+ /* Plugin will be removed from cache after the scan completes if it
+ * is still marked 'cached' */
+ g_free (filename);
+ continue;
+ }
+
+ if (file_status.st_mode & S_IFDIR) {
+ if (G_UNLIKELY (is_blacklisted_hidden_directory (dirent))) {
+ GST_TRACE_OBJECT (context->registry, "ignoring %s directory", dirent);
+ g_free (filename);
+ continue;
+ }
+ /* FIXME 0.11: Don't recurse into directories, this behaviour
+ * is inconsistent with other PATH environment variables
+ */
+ if (level > 0) {
+ GST_LOG_OBJECT (context->registry, "recursing into directory %s",
+ filename);
+ changed |= gst_registry_scan_path_level (context, filename, level - 1);
+ } else {
+ GST_LOG_OBJECT (context->registry, "not recursing into directory %s, "
+ "recursion level too deep", filename);
+ }
+ g_free (filename);
+ continue;
+ }
+ if (!(file_status.st_mode & S_IFREG)) {
+ GST_TRACE_OBJECT (context->registry, "%s is not a regular file, ignoring",
+ filename);
+ g_free (filename);
+ continue;
+ }
+ if (!g_str_has_suffix (dirent, G_MODULE_SUFFIX)
+#ifdef GST_EXTRA_MODULE_SUFFIX
+ && !g_str_has_suffix (dirent, GST_EXTRA_MODULE_SUFFIX)
+#endif
+ ) {
+ GST_TRACE_OBJECT (context->registry,
+ "extension is not recognized as module file, ignoring file %s",
+ filename);
+ g_free (filename);
+ continue;
+ }
+
+ GST_LOG_OBJECT (context->registry, "file %s looks like a possible module",
+ filename);
+
+ /* try to avoid unnecessary plugin-move pain */
+ if (g_str_has_prefix (dirent, "libgstvalve") ||
+ g_str_has_prefix (dirent, "libgstselector")) {
+ GST_WARNING_OBJECT (context->registry, "ignoring old plugin %s which "
+ "has been merged into the corelements plugin", filename);
+ /* Plugin will be removed from cache after the scan completes if it
+ * is still marked 'cached' */
+ g_free (filename);
+ continue;
+ }
+
+ /* plug-ins are considered unique by basename; if the given name
+ * was already seen by the registry, we ignore it */
+ plugin = gst_registry_lookup_bn (context->registry, dirent);
+ if (plugin) {
+ gboolean env_vars_changed, deps_changed = FALSE;
+
+ if (plugin->registered) {
+ GST_DEBUG_OBJECT (context->registry,
+ "plugin already registered from path \"%s\"",
+ GST_STR_NULL (plugin->filename));
+ g_free (filename);
+ gst_object_unref (plugin);
+ continue;
+ }
+
+ env_vars_changed = _priv_plugin_deps_env_vars_changed (plugin);
+
+ /* If a file with a certain basename is seen on a different path,
+ * update the plugin to ensure the registry cache will reflect up
+ * to date information */
+
+ if (plugin->file_mtime == file_status.st_mtime &&
+ plugin->file_size == file_status.st_size && !env_vars_changed &&
+ !(deps_changed = _priv_plugin_deps_files_changed (plugin)) &&
+ !strcmp (plugin->filename, filename)) {
+ GST_LOG_OBJECT (context->registry, "file %s cached", filename);
+ plugin->flags &= ~GST_PLUGIN_FLAG_CACHED;
+ GST_LOG_OBJECT (context->registry,
+ "marking plugin %p as registered as %s", plugin, filename);
+ plugin->registered = TRUE;
+ } else {
+ GST_INFO_OBJECT (context->registry, "cached info for %s is stale",
+ filename);
+ GST_DEBUG_OBJECT (context->registry, "mtime %" G_GINT64_FORMAT " != %"
+ G_GINT64_FORMAT " or size %" G_GINT64_FORMAT " != %"
+ G_GINT64_FORMAT " or external dependency env_vars changed: %d or"
+ " external dependencies changed: %d or old path %s != new path %s",
+ (gint64) plugin->file_mtime, (gint64) file_status.st_mtime,
+ (gint64) plugin->file_size, (gint64) file_status.st_size,
+ env_vars_changed, deps_changed, plugin->filename, filename);
+ gst_registry_remove_plugin (context->registry, plugin);
+ changed |= gst_registry_scan_plugin_file (context, filename,
+ file_status.st_size, file_status.st_mtime);
+ }
+ gst_object_unref (plugin);
+
+ } else {
+ GST_DEBUG_OBJECT (context->registry, "file %s not yet in registry",
+ filename);
+ changed |= gst_registry_scan_plugin_file (context, filename,
+ file_status.st_size, file_status.st_mtime);
+ }
+
+ g_free (filename);
+ }
+
+ g_dir_close (dir);
+
+ return changed;
+}
+
+static gboolean
+gst_registry_scan_path_internal (GstRegistryScanContext * context,
+ const gchar * path)
+{
+ gboolean changed;
+
+ GST_DEBUG_OBJECT (context->registry, "scanning path %s", path);
+ changed = gst_registry_scan_path_level (context, path, 10);
+
+ GST_DEBUG_OBJECT (context->registry, "registry changed in path %s: %d", path,
+ changed);
+ return changed;
+}
+
+/**
+ * gst_registry_scan_path:
+ * @registry: the registry to add found plugins to
+ * @path: the path to scan
+ *
+ * Scan the given path for plugins to add to the registry. The syntax of the
+ * path is specific to the registry.
+ *
+ * Returns: %TRUE if registry changed
+ */
+gboolean
+gst_registry_scan_path (GstRegistry * registry, const gchar * path)
+{
+ GstRegistryScanContext context;
+ gboolean result;
+
+ g_return_val_if_fail (GST_IS_REGISTRY (registry), FALSE);
+ g_return_val_if_fail (path != NULL, FALSE);
+
+ init_scan_context (&context, registry);
+
+ result = gst_registry_scan_path_internal (&context, path);
+
+ clear_scan_context (&context);
+ result |= context.changed;
+
+ return result;
+}
+
+static gboolean
+_gst_plugin_feature_filter_plugin_name (GstPluginFeature * feature,
+ gpointer user_data)
+{
+ return (strcmp (feature->plugin_name, (gchar *) user_data) == 0);
+}
+
+/**
+ * gst_registry_get_feature_list_by_plugin:
+ * @registry: a #GstRegistry.
+ * @name: a plugin name.
+ *
+ * Retrieves a #GList of features of the plugin with name @name.
+ *
+ * Returns: (transfer full) (element-type Gst.PluginFeature): a #GList of
+ * #GstPluginFeature. Use gst_plugin_feature_list_free() after usage.
+ */
+GList *
+gst_registry_get_feature_list_by_plugin (GstRegistry * registry,
+ const gchar * name)
+{
+ g_return_val_if_fail (GST_IS_REGISTRY (registry), NULL);
+ g_return_val_if_fail (name != NULL, NULL);
+
+ return gst_registry_feature_filter (registry,
+ _gst_plugin_feature_filter_plugin_name, FALSE, (gpointer) name);
+}
+
+/* Unref and delete the default registry */
+void
+_priv_gst_registry_cleanup (void)
+{
+ GstRegistry *registry;
+
+ g_static_mutex_lock (&_gst_registry_mutex);
+ if ((registry = _gst_registry_default) != NULL) {
+ _gst_registry_default = NULL;
+ }
+ g_static_mutex_unlock (&_gst_registry_mutex);
+
+ /* unref outside of the lock because we can. */
+ if (registry)
+ gst_object_unref (registry);
+}
+
+/**
+ * gst_default_registry_check_feature_version:
+ * @feature_name: the name of the feature (e.g. "oggdemux")
+ * @min_major: the minimum major version number
+ * @min_minor: the minimum minor version number
+ * @min_micro: the minimum micro version number
+ *
+ * Checks whether a plugin feature by the given name exists in the
+ * default registry and whether its version is at least the
+ * version required.
+ *
+ * Returns: #TRUE if the feature could be found and the version is
+ * the same as the required version or newer, and #FALSE otherwise.
+ */
+gboolean
+gst_default_registry_check_feature_version (const gchar * feature_name,
+ guint min_major, guint min_minor, guint min_micro)
+{
+ GstPluginFeature *feature;
+ GstRegistry *registry;
+ gboolean ret = FALSE;
+
+ g_return_val_if_fail (feature_name != NULL, FALSE);
+
+ GST_DEBUG ("Looking up plugin feature '%s'", feature_name);
+
+ registry = gst_registry_get_default ();
+ feature = gst_registry_lookup_feature (registry, feature_name);
+ if (feature) {
+ ret = gst_plugin_feature_check_version (feature, min_major, min_minor,
+ min_micro);
+ gst_object_unref (feature);
+ } else {
+ GST_DEBUG ("Could not find plugin feature '%s'", feature_name);
+ }
+
+ return ret;
+}
+
+static void
+load_plugin_func (gpointer data, gpointer user_data)
+{
+ GstPlugin *plugin;
+ const gchar *filename;
+ GError *err = NULL;
+
+ filename = (const gchar *) data;
+ GST_DEBUG ("Pre-loading plugin %s", filename);
+
+ plugin = gst_plugin_load_file (filename, &err);
+
+ if (plugin) {
+ GST_INFO ("Loaded plugin: \"%s\"", filename);
+
+ gst_default_registry_add_plugin (plugin);
+ } else {
+ if (err) {
+ /* Report error to user, and free error */
+ GST_ERROR ("Failed to load plugin: %s", err->message);
+ g_error_free (err);
+ } else {
+ GST_WARNING ("Failed to load plugin: \"%s\"", filename);
+ }
+ }
+}
+
+#ifndef GST_DISABLE_REGISTRY
+/* Unref all plugins marked 'cached', to clear old plugins that no
+ * longer exist. Returns TRUE if any plugins were removed */
+static gboolean
+gst_registry_remove_cache_plugins (GstRegistry * registry)
+{
+ GList *g;
+ GList *g_next;
+ GstPlugin *plugin;
+ gboolean changed = FALSE;
+
+ g_return_val_if_fail (GST_IS_REGISTRY (registry), FALSE);
+
+ GST_OBJECT_LOCK (registry);
+
+ GST_DEBUG_OBJECT (registry, "removing cached plugins");
+ g = registry->plugins;
+ while (g) {
+ g_next = g->next;
+ plugin = g->data;
+ if (plugin->flags & GST_PLUGIN_FLAG_CACHED) {
+ GST_DEBUG_OBJECT (registry, "removing cached plugin \"%s\"",
+ GST_STR_NULL (plugin->filename));
+ registry->plugins = g_list_delete_link (registry->plugins, g);
+ if (G_LIKELY (plugin->basename))
+ g_hash_table_remove (registry->basename_hash, plugin->basename);
+ gst_registry_remove_features_for_plugin_unlocked (registry, plugin);
+ gst_object_unref (plugin);
+ changed = TRUE;
+ }
+ g = g_next;
+ }
+
+ GST_OBJECT_UNLOCK (registry);
+
+ return changed;
+}
+
+typedef enum
+{
+ REGISTRY_SCAN_AND_UPDATE_FAILURE = 0,
+ REGISTRY_SCAN_AND_UPDATE_SUCCESS_NOT_CHANGED,
+ REGISTRY_SCAN_AND_UPDATE_SUCCESS_UPDATED
+} GstRegistryScanAndUpdateResult;
+
+/*
+ * scan_and_update_registry:
+ * @default_registry: the #GstRegistry
+ * @registry_file: registry filename
+ * @write_changes: write registry if it has changed?
+ *
+ * Scans for registry changes and eventually updates the registry cache.
+ *
+ * Return: %REGISTRY_SCAN_AND_UPDATE_FAILURE if the registry could not scanned
+ * or updated, %REGISTRY_SCAN_AND_UPDATE_SUCCESS_NOT_CHANGED if the
+ * registry is clean and %REGISTRY_SCAN_AND_UPDATE_SUCCESS_UPDATED if
+ * it has been updated and the cache needs to be re-read.
+ */
+static GstRegistryScanAndUpdateResult
+scan_and_update_registry (GstRegistry * default_registry,
+ const gchar * registry_file, gboolean write_changes, GError ** error)
+{
+ const gchar *plugin_path;
+ gboolean changed = FALSE;
+ GList *l;
+ GstRegistryScanContext context;
+
+ GST_INFO ("Validating plugins from registry cache: %s", registry_file);
+
+ init_scan_context (&context, default_registry);
+
+ /* It sounds tempting to just compare the mtime of directories with the mtime
+ * of the registry cache, but it does not work. It would not catch updated
+ * plugins, which might bring more or less features.
+ */
+
+ /* scan paths specified via --gst-plugin-path */
+ GST_DEBUG ("scanning paths added via --gst-plugin-path");
+ for (l = _priv_gst_plugin_paths; l != NULL; l = l->next) {
+ GST_INFO ("Scanning plugin path: \"%s\"", (gchar *) l->data);
+ changed |= gst_registry_scan_path_internal (&context, (gchar *) l->data);
+ }
+ /* keep plugin_paths around in case a re-scan is forced later on */
+
+ /* GST_PLUGIN_PATH specifies a list of directories to scan for
+ * additional plugins. These take precedence over the system plugins */
+ plugin_path = g_getenv ("GST_PLUGIN_PATH");
+ if (plugin_path) {
+ char **list;
+ int i;
+
+ GST_DEBUG ("GST_PLUGIN_PATH set to %s", plugin_path);
+ list = g_strsplit (plugin_path, G_SEARCHPATH_SEPARATOR_S, 0);
+ for (i = 0; list[i]; i++) {
+ changed |= gst_registry_scan_path_internal (&context, list[i]);
+ }
+ g_strfreev (list);
+ } else {
+ GST_DEBUG ("GST_PLUGIN_PATH not set");
+ }
+
+ /* GST_PLUGIN_SYSTEM_PATH specifies a list of plugins that are always
+ * loaded by default. If not set, this defaults to the system-installed
+ * path, and the plugins installed in the user's home directory */
+ plugin_path = g_getenv ("GST_PLUGIN_SYSTEM_PATH");
+ if (plugin_path == NULL) {
+ char *home_plugins;
+
+ GST_DEBUG ("GST_PLUGIN_SYSTEM_PATH not set");
+
+ /* plugins in the user's home directory take precedence over
+ * system-installed ones */
+ home_plugins = g_build_filename (g_get_user_data_dir (),
+ "gstreamer-" GST_MAJORMINOR, "plugins", NULL);
+
+ GST_DEBUG ("scanning home plugins %s", home_plugins);
+ changed |= gst_registry_scan_path_internal (&context, home_plugins);
+ g_free (home_plugins);
+
+ /* add the main (installed) library path */
+ GST_DEBUG ("scanning main plugins %s", PLUGINDIR);
+ changed |= gst_registry_scan_path_internal (&context, PLUGINDIR);
+
+#ifdef G_OS_WIN32
+ {
+ char *base_dir;
+ char *dir;
+
+ base_dir =
+ g_win32_get_package_installation_directory_of_module
+ (_priv_gst_dll_handle);
+
+ dir = g_build_filename (base_dir, "lib", "gstreamer-0.10", NULL);
+ GST_DEBUG ("scanning DLL dir %s", dir);
+
+ changed |= gst_registry_scan_path_internal (&context, dir);
+
+ g_free (dir);
+ g_free (base_dir);
+ }
+#endif
+ } else {
+ gchar **list;
+ gint i;
+
+ GST_DEBUG ("GST_PLUGIN_SYSTEM_PATH set to %s", plugin_path);
+ list = g_strsplit (plugin_path, G_SEARCHPATH_SEPARATOR_S, 0);
+ for (i = 0; list[i]; i++) {
+ changed |= gst_registry_scan_path_internal (&context, list[i]);
+ }
+ g_strfreev (list);
+ }
+
+ clear_scan_context (&context);
+ changed |= context.changed;
+
+ /* Remove cached plugins so stale info is cleared. */
+ changed |= gst_registry_remove_cache_plugins (default_registry);
+
+ if (!changed) {
+ GST_INFO ("Registry cache has not changed");
+ return REGISTRY_SCAN_AND_UPDATE_SUCCESS_NOT_CHANGED;
+ }
+
+ if (!write_changes) {
+ GST_INFO ("Registry cache changed, but writing is disabled. Not writing.");
+ return REGISTRY_SCAN_AND_UPDATE_FAILURE;
+ }
+
+ GST_INFO ("Registry cache changed. Writing new registry cache");
+ if (!priv_gst_registry_binary_write_cache (default_registry, registry_file)) {
+ g_set_error (error, GST_CORE_ERROR, GST_CORE_ERROR_FAILED,
+ _("Error writing registry cache to %s: %s"),
+ registry_file, g_strerror (errno));
+ return REGISTRY_SCAN_AND_UPDATE_FAILURE;
+ }
+
+ GST_INFO ("Registry cache written successfully");
+ return REGISTRY_SCAN_AND_UPDATE_SUCCESS_UPDATED;
+}
+
+static gboolean
+ensure_current_registry (GError ** error)
+{
+ gchar *registry_file;
+ GstRegistry *default_registry;
+ gboolean ret = TRUE;
+ gboolean do_update = TRUE;
+ gboolean have_cache = TRUE;
+
+ default_registry = gst_registry_get_default ();
+ registry_file = g_strdup (g_getenv ("GST_REGISTRY"));
+ if (registry_file == NULL) {
+ registry_file = g_build_filename (g_get_user_cache_dir (),
+ "gstreamer-" GST_MAJORMINOR, "registry." HOST_CPU ".bin", NULL);
+ }
+
+ if (!_gst_disable_registry_cache) {
+ GST_INFO ("reading registry cache: %s", registry_file);
+ have_cache = priv_gst_registry_binary_read_cache (default_registry,
+ registry_file);
+ /* Only ever read the registry cache once, then disable it for
+ * subsequent updates during the program lifetime */
+ _gst_disable_registry_cache = TRUE;
+ }
+
+ if (have_cache) {
+ do_update = !_priv_gst_disable_registry_update;
+ if (do_update) {
+ const gchar *update_env;
+
+ if ((update_env = g_getenv ("GST_REGISTRY_UPDATE"))) {
+ /* do update for any value different from "no" */
+ do_update = (strcmp (update_env, "no") != 0);
+ }
+ }
+ }
+
+ if (do_update) {
+ /* now check registry */
+ GST_DEBUG ("Updating registry cache");
+ scan_and_update_registry (default_registry, registry_file, TRUE, error);
+ } else {
+ GST_DEBUG ("Not updating registry cache (disabled)");
+ }
+
+ g_free (registry_file);
+ GST_INFO ("registry reading and updating done, result = %d", ret);
+
+ return ret;
+}
+#endif /* GST_DISABLE_REGISTRY */
+
+/**
+ * gst_registry_fork_is_enabled:
+ *
+ * By default GStreamer will perform scanning and rebuilding of the
+ * registry file using a helper child process.
+ *
+ * Applications might want to disable this behaviour with the
+ * gst_registry_fork_set_enabled() function, in which case new plugins
+ * are scanned (and loaded) into the application process.
+ *
+ * Returns: %TRUE if GStreamer will use the child helper process when
+ * rebuilding the registry.
+ *
+ * Since: 0.10.10
+ */
+gboolean
+gst_registry_fork_is_enabled (void)
+{
+ return _gst_enable_registry_fork;
+}
+
+/**
+ * gst_registry_fork_set_enabled:
+ * @enabled: whether rebuilding the registry can use a temporary child helper process.
+ *
+ * Applications might want to disable/enable spawning of a child helper process
+ * when rebuilding the registry. See gst_registry_fork_is_enabled() for more
+ * information.
+ *
+ * Since: 0.10.10
+ */
+void
+gst_registry_fork_set_enabled (gboolean enabled)
+{
+ _gst_enable_registry_fork = enabled;
+}
+
+/**
+ * gst_update_registry:
+ *
+ * Forces GStreamer to re-scan its plugin paths and update the default
+ * plugin registry.
+ *
+ * Applications will almost never need to call this function, it is only
+ * useful if the application knows new plugins have been installed (or old
+ * ones removed) since the start of the application (or, to be precise, the
+ * first call to gst_init()) and the application wants to make use of any
+ * newly-installed plugins without restarting the application.
+ *
+ * Applications should assume that the registry update is neither atomic nor
+ * thread-safe and should therefore not have any dynamic pipelines running
+ * (including the playbin and decodebin elements) and should also not create
+ * any elements or access the GStreamer registry while the update is in
+ * progress.
+ *
+ * Note that this function may block for a significant amount of time.
+ *
+ * Returns: %TRUE if the registry has been updated successfully (does not
+ * imply that there were changes), otherwise %FALSE.
+ *
+ * Since: 0.10.12
+ */
+gboolean
+gst_update_registry (void)
+{
+ gboolean res;
+
+#ifndef GST_DISABLE_REGISTRY
+ GError *err = NULL;
+
+ res = ensure_current_registry (&err);
+ if (err) {
+ GST_WARNING ("registry update failed: %s", err->message);
+ g_error_free (err);
+ } else {
+ GST_LOG ("registry update succeeded");
+ }
+
+#else
+ GST_WARNING ("registry update failed: %s", "registry disabled");
+ res = TRUE;
+#endif /* GST_DISABLE_REGISTRY */
+
+ if (_priv_gst_preload_plugins) {
+ GST_DEBUG ("Preloading indicated plugins...");
+ g_slist_foreach (_priv_gst_preload_plugins, load_plugin_func, NULL);
+ }
+
+ return res;
+}
+
+/**
+ * gst_registry_get_feature_list_cookie:
+ * @registry: the registry
+ *
+ * Returns the registrys feature list cookie. This changes
+ * every time a feature is added or removed from the registry.
+ *
+ * Returns: the feature list cookie.
+ *
+ * Since: 0.10.26
+ */
+guint32
+gst_registry_get_feature_list_cookie (GstRegistry * registry)
+{
+ g_return_val_if_fail (GST_IS_REGISTRY (registry), 0);
+
+ return registry->priv->cookie;
+}
diff --git a/gst/gstregistry.h b/gst/gstregistry.h
new file mode 100644
index 0000000..f2d07c1
--- /dev/null
+++ b/gst/gstregistry.h
@@ -0,0 +1,229 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2000 Wim Taymans <wim.taymans@chello.be>
+ *
+ * gstregistry.h: Header for registry handling
+ *
+ * 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_REGISTRY_H__
+#define __GST_REGISTRY_H__
+
+#include <gst/gstconfig.h>
+#include <gst/gstplugin.h>
+#include <gst/gstpluginfeature.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_REGISTRY (gst_registry_get_type ())
+#define GST_REGISTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_REGISTRY, GstRegistry))
+#define GST_IS_REGISTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_REGISTRY))
+#define GST_REGISTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_REGISTRY, GstRegistryClass))
+#define GST_IS_REGISTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_REGISTRY))
+#define GST_REGISTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_REGISTRY, GstRegistryClass))
+
+typedef struct _GstRegistry GstRegistry;
+typedef struct _GstRegistryClass GstRegistryClass;
+typedef struct _GstRegistryPrivate GstRegistryPrivate;
+
+/**
+ * GstRegistry:
+ *
+ * Opaque #GstRegistry structure.
+ */
+struct _GstRegistry {
+ GstObject object;
+
+ /*< private >*/
+ GList *plugins;
+ GList *features;
+
+ GList *paths;
+
+ /* FIXME move these elsewhere */
+ int cache_file;
+
+ /* hash to speedup _lookup_feature_locked() */
+ GHashTable *feature_hash;
+ /* hash to speedup _lookup */
+ GHashTable *basename_hash;
+
+ GstRegistryPrivate *priv;
+
+ /*< private >*/
+ gpointer _gst_reserved[GST_PADDING];
+};
+
+struct _GstRegistryClass {
+ GstObjectClass parent_class;
+
+ /* signals */
+ void (*plugin_added) (GstRegistry *registry, GstPlugin *plugin);
+ void (*feature_added) (GstRegistry *registry, GstPluginFeature *feature);
+
+ /*< private >*/
+ gpointer _gst_reserved[GST_PADDING];
+};
+
+
+/* normal GObject stuff */
+GType gst_registry_get_type (void);
+
+GstRegistry * gst_registry_get_default (void);
+
+gboolean gst_registry_scan_path (GstRegistry *registry, const gchar *path);
+void gst_registry_add_path (GstRegistry * registry, const gchar * path);
+GList* gst_registry_get_path_list (GstRegistry *registry);
+
+gboolean gst_registry_add_plugin (GstRegistry *registry, GstPlugin *plugin);
+void gst_registry_remove_plugin (GstRegistry *registry, GstPlugin *plugin);
+gboolean gst_registry_add_feature (GstRegistry * registry, GstPluginFeature * feature);
+void gst_registry_remove_feature (GstRegistry * registry, GstPluginFeature * feature);
+
+GList* gst_registry_get_plugin_list (GstRegistry *registry);
+GList* gst_registry_plugin_filter (GstRegistry *registry,
+ GstPluginFilter filter,
+ gboolean first,
+ gpointer user_data);
+GList* gst_registry_feature_filter (GstRegistry *registry,
+ GstPluginFeatureFilter filter,
+ gboolean first,
+ gpointer user_data);
+GList * gst_registry_get_feature_list (GstRegistry *registry,
+ GType type);
+GList * gst_registry_get_feature_list_by_plugin (GstRegistry *registry, const gchar *name);
+guint32 gst_registry_get_feature_list_cookie (GstRegistry *registry);
+
+GstPlugin* gst_registry_find_plugin (GstRegistry *registry, const gchar *name);
+GstPluginFeature* gst_registry_find_feature (GstRegistry *registry, const gchar *name, GType type);
+
+GstPlugin * gst_registry_lookup (GstRegistry *registry, const char *filename);
+GstPluginFeature * gst_registry_lookup_feature (GstRegistry *registry, const char *name);
+
+/* convinience defines for the default registry */
+
+/**
+ * gst_default_registry_add_plugin:
+ * @plugin: (transfer full): the plugin to add
+ *
+ * Add the plugin to the default registry.
+ * The plugin-added signal will be emitted.
+ *
+ * Returns: TRUE on success.
+ */
+#define gst_default_registry_add_plugin(plugin) \
+ gst_registry_add_plugin (gst_registry_get_default(), plugin)
+
+/**
+ * gst_default_registry_add_path:
+ * @path: the path to add to the registry
+ *
+ * Add the given path to the default registry. The syntax of the
+ * path is specific to the registry. If the path has already been
+ * added, do nothing.
+ */
+#define gst_default_registry_add_path(path) \
+ gst_registry_add_path (gst_registry_get_default(), path)
+
+/**
+ * gst_default_registry_get_path_list:
+ *
+ * Get the list of paths for the default registry.
+ *
+ * Returns: (transfer container) (element-type char*): a #GList of paths as
+ * strings. g_list_free() after use.
+ */
+#define gst_default_registry_get_path_list() \
+ gst_registry_get_path_list (gst_registry_get_default())
+
+/**
+ * gst_default_registry_get_plugin_list:
+ *
+ * Get a copy of all plugins registered in the default registry.
+ *
+ * Returns: (transfer full) (element-type Gst.Plugin): a copy of the list.
+ * Free after use.
+ */
+#define gst_default_registry_get_plugin_list() \
+ gst_registry_get_plugin_list (gst_registry_get_default())
+
+/**
+ * gst_default_registry_find_feature:
+ * @name: the pluginfeature name to find
+ * @type: the pluginfeature type to find
+ *
+ * Find the pluginfeature with the given name and type in the default registry.
+ *
+ * Returns: (transfer full): the pluginfeature with the given name and type or
+ * NULL if the plugin was not found.
+ */
+#define gst_default_registry_find_feature(name,type) \
+ gst_registry_find_feature (gst_registry_get_default(),name,type)
+
+/**
+ * gst_default_registry_find_plugin:
+ * @name: the plugin name to find
+ *
+ * Find the plugin with the given name in the default registry.
+ * The plugin will be reffed; caller is responsible for unreffing.
+ *
+ * Returns: (transfer full): The plugin with the given name or NULL if the
+ * plugin was not found.
+ */
+#define gst_default_registry_find_plugin(name) \
+ gst_registry_find_plugin (gst_registry_get_default(),name)
+
+/**
+ * gst_default_registry_feature_filter:
+ * @filter: the filter to use
+ * @first: only return first match
+ * @user_data: user data passed to the filter function
+ *
+ * Runs a filter against all features of the plugins in the default registry
+ * and returns a GList with the results.
+ * If the first flag is set, only the first match is
+ * returned (as a list with a single object).
+ *
+ * Returns: (transfer full) (element-type Gst.PluginFeature): a #GList of
+ * plugin features, gst_plugin_feature_list_free after use.
+ */
+#define gst_default_registry_feature_filter(filter,first,user_data) \
+ gst_registry_feature_filter (gst_registry_get_default(),filter,first,user_data)
+
+/**
+ * gst_default_registry_get_feature_list_cookie:
+ *
+ * Returns the default registrys feature list cookie. This changes
+ * every time a feature is added or removed from the registry.
+ *
+ * Returns: the feature list cookie.
+ *
+ * Since: 0.10.26
+ */
+#define gst_default_registry_get_feature_list_cookie() \
+ gst_registry_get_feature_list_cookie (gst_registry_get_default())
+
+gboolean gst_default_registry_check_feature_version (const gchar *feature_name,
+ guint min_major,
+ guint min_minor,
+ guint min_micro);
+
+G_END_DECLS
+
+#endif /* __GST_REGISTRY_H__ */
+
diff --git a/gst/gstregistrybinary.c b/gst/gstregistrybinary.c
new file mode 100644
index 0000000..85648be
--- /dev/null
+++ b/gst/gstregistrybinary.c
@@ -0,0 +1,631 @@
+/* GStreamer
+ * Copyright (C) 2006 Josep Torra <josep@fluendo.com>
+ * 2006 Mathieu Garcia <matthieu@fluendo.com>
+ * 2006,2007 Stefan Kost <ensonic@users.sf.net>
+ * 2008 Sebastian Dröge <slomo@circular-chaos.org>
+ *
+ * gstregistrybinary.c: GstRegistryBinary object, support routines
+ *
+ * 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.
+ */
+
+/* FIXME:
+ * - keep registry binary blob and reference strings
+ * - don't free/unmmap contents when leaving gst_registry_binary_read_cache()
+ * - free at gst_deinit() / _priv_gst_registry_cleanup() ?
+ * - GstPlugin:
+ * - GST_PLUGIN_FLAG_CONST
+ * - GstPluginFeature, GstIndexFactory, GstElementFactory
+ * - needs Flags (GST_PLUGIN_FEATURE_FLAG_CONST)
+ * - can we turn loaded into flag?
+ * - why do we collect a list of binary chunks and not write immediately
+ * - because we need to process subchunks, before we can set e.g. nr_of_items
+ * in parent chunk
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+
+#if defined (_MSC_VER) && _MSC_VER >= 1400
+#include <io.h>
+#endif
+
+#include <gst/gst_private.h>
+#include <gst/gstconfig.h>
+#include <gst/gstelement.h>
+#include <gst/gsttypefind.h>
+#include <gst/gsttypefindfactory.h>
+#include <gst/gsturi.h>
+#include <gst/gstinfo.h>
+#include <gst/gstenumtypes.h>
+#include <gst/gstpadtemplate.h>
+
+#include <gst/gstregistrychunks.h>
+#include <gst/gstregistrybinary.h>
+
+#include <glib/gstdio.h> /* for g_stat(), g_mapped_file(), ... */
+
+#include "glib-compat-private.h"
+
+
+#define GST_CAT_DEFAULT GST_CAT_REGISTRY
+
+/* reading macros */
+#define unpack_element(inptr, outptr, element, endptr, error_label) G_STMT_START{ \
+ if (inptr + sizeof(element) >= endptr) \
+ goto error_label; \
+ outptr = (element *) inptr; \
+ inptr += sizeof (element); \
+}G_STMT_END
+
+#define ALIGNMENT (sizeof (void *))
+#define alignment(_address) (gsize)_address%ALIGNMENT
+#define align(_ptr) _ptr += (( alignment(_ptr) == 0) ? 0 : ALIGNMENT-alignment(_ptr))
+
+/* Registry saving */
+
+#ifdef G_OS_WIN32
+/* On win32, we can't use g_mkstmp(), because of cross-DLL file I/O problems.
+ * So, we just create the entire binary registry in memory, then write it out
+ * with g_file_set_contents(), which creates a temporary file internally
+ */
+
+typedef struct BinaryRegistryCache
+{
+ const char *location;
+ guint8 *mem;
+ gssize len;
+} BinaryRegistryCache;
+
+static BinaryRegistryCache *
+gst_registry_binary_cache_init (GstRegistry * registry, const char *location)
+{
+ BinaryRegistryCache *cache = g_slice_new0 (BinaryRegistryCache);
+ cache->location = location;
+ return cache;
+}
+
+static int
+gst_registry_binary_cache_write (BinaryRegistryCache * cache,
+ unsigned long offset, const void *data, int length)
+{
+ cache->len = MAX (offset + length, cache->len);
+ cache->mem = g_realloc (cache->mem, cache->len);
+
+ memcpy (cache->mem + offset, data, length);
+
+ return length;
+}
+
+static gboolean
+gst_registry_binary_cache_finish (BinaryRegistryCache * cache, gboolean success)
+{
+ gboolean ret = TRUE;
+ GError *error = NULL;
+ if (!g_file_set_contents (cache->location, (const gchar *) cache->mem,
+ cache->len, &error)) {
+ /* Probably the directory didn't exist; create it */
+ gchar *dir;
+ dir = g_path_get_dirname (cache->location);
+ g_mkdir_with_parents (dir, 0777);
+ g_free (dir);
+
+ g_error_free (error);
+ error = NULL;
+
+ if (!g_file_set_contents (cache->location, (const gchar *) cache->mem,
+ cache->len, &error)) {
+ /* Probably the directory didn't exist; create it */
+ gchar *dir;
+ dir = g_path_get_dirname (cache->location);
+ g_mkdir_with_parents (dir, 0777);
+ g_free (dir);
+
+ g_error_free (error);
+ error = NULL;
+
+ if (!g_file_set_contents (cache->location, (const gchar *) cache->mem,
+ cache->len, &error)) {
+ GST_ERROR ("Failed to write to cache file: %s", error->message);
+ g_error_free (error);
+ ret = FALSE;
+ }
+ }
+ }
+
+ g_free (cache->mem);
+ g_slice_free (BinaryRegistryCache, cache);
+ return ret;
+}
+
+#else
+typedef struct BinaryRegistryCache
+{
+ const char *location;
+ char *tmp_location;
+ unsigned long currentoffset;
+ int cache_fd;
+} BinaryRegistryCache;
+
+static BinaryRegistryCache *
+gst_registry_binary_cache_init (GstRegistry * registry, const char *location)
+{
+ BinaryRegistryCache *cache = g_slice_new0 (BinaryRegistryCache);
+
+ cache->location = location;
+ cache->tmp_location = g_strconcat (location, ".tmpXXXXXX", NULL);
+ cache->cache_fd = g_mkstemp (cache->tmp_location);
+ if (cache->cache_fd == -1) {
+ int ret;
+ GStatBuf statbuf;
+ gchar *dir;
+
+ /* oops, I bet the directory doesn't exist */
+ dir = g_path_get_dirname (location);
+ g_mkdir_with_parents (dir, 0777);
+
+ ret = g_stat (dir, &statbuf);
+ if (ret != -1 && (statbuf.st_mode & 0700) != 0700) {
+ g_chmod (dir, 0700);
+ }
+
+ g_free (dir);
+
+ /* the previous g_mkstemp call overwrote the XXXXXX placeholder ... */
+ g_free (cache->tmp_location);
+ cache->tmp_location = g_strconcat (location, ".tmpXXXXXX", NULL);
+ cache->cache_fd = g_mkstemp (cache->tmp_location);
+
+ if (cache->cache_fd == -1) {
+ GST_DEBUG ("g_mkstemp() failed: %s", g_strerror (errno));
+ g_free (cache->tmp_location);
+ g_slice_free (BinaryRegistryCache, cache);
+ return NULL;
+ }
+
+ ret = g_stat (cache->tmp_location, &statbuf);
+ if (ret != -1 && (statbuf.st_mode & 0600) != 0600) {
+ g_chmod (cache->tmp_location, 0600);
+ }
+ }
+
+ return cache;
+}
+
+static int
+gst_registry_binary_cache_write (BinaryRegistryCache * cache,
+ unsigned long offset, const void *data, int length)
+{
+ long written;
+ if (offset != cache->currentoffset) {
+ if (lseek (cache->cache_fd, offset, SEEK_SET) != 0) {
+ GST_ERROR ("Seeking to new offset failed");
+ return FALSE;
+ }
+ cache->currentoffset = offset;
+ }
+
+ written = write (cache->cache_fd, data, length);
+ if (written != length) {
+ GST_ERROR ("Failed to write to cache file");
+ }
+ cache->currentoffset += written;
+
+ return written;
+}
+
+static gboolean
+gst_registry_binary_cache_finish (BinaryRegistryCache * cache, gboolean success)
+{
+ /* only fsync if we're actually going to use and rename the file below */
+ if (success && fsync (cache->cache_fd) < 0)
+ goto fsync_failed;
+
+ if (close (cache->cache_fd) < 0)
+ goto close_failed;
+
+ if (success) {
+ /* Only do the rename if we wrote the entire file successfully */
+ if (g_rename (cache->tmp_location, cache->location) < 0) {
+ GST_ERROR ("g_rename() failed: %s", g_strerror (errno));
+ goto rename_failed;
+ }
+ }
+
+ g_free (cache->tmp_location);
+ g_slice_free (BinaryRegistryCache, cache);
+ GST_INFO ("Wrote binary registry cache");
+ return TRUE;
+
+/* ERRORS */
+fail_after_close:
+ {
+ g_unlink (cache->tmp_location);
+ g_free (cache->tmp_location);
+ g_slice_free (BinaryRegistryCache, cache);
+ return FALSE;
+ }
+fsync_failed:
+ {
+ GST_ERROR ("fsync() failed: %s", g_strerror (errno));
+ goto fail_after_close;
+ }
+close_failed:
+ {
+ GST_ERROR ("close() failed: %s", g_strerror (errno));
+ goto fail_after_close;
+ }
+rename_failed:
+ {
+ GST_ERROR ("g_rename() failed: %s", g_strerror (errno));
+ goto fail_after_close;
+ }
+}
+#endif
+
+/*
+ * gst_registry_binary_write_chunk:
+ *
+ * Write from a memory location to the registry cache file
+ *
+ * Returns: %TRUE for success
+ */
+inline static gboolean
+gst_registry_binary_write_chunk (BinaryRegistryCache * cache,
+ GstRegistryChunk * chunk, unsigned long *file_position)
+{
+ gchar padder[ALIGNMENT] = { 0, };
+ int padsize = 0;
+
+ /* Padding to insert the struct that requiere word alignment */
+ if ((chunk->align) && (alignment (*file_position) != 0)) {
+ padsize = ALIGNMENT - alignment (*file_position);
+ if (gst_registry_binary_cache_write (cache, *file_position,
+ padder, padsize) != padsize) {
+ GST_ERROR ("Failed to write binary registry padder");
+ return FALSE;
+ }
+ *file_position += padsize;
+ }
+
+ if (gst_registry_binary_cache_write (cache, *file_position,
+ chunk->data, chunk->size) != chunk->size) {
+ GST_ERROR ("Failed to write binary registry element");
+ return FALSE;
+ }
+
+ *file_position += chunk->size;
+
+ return TRUE;
+}
+
+
+/*
+ * gst_registry_binary_initialize_magic:
+ *
+ * Initialize the GstBinaryRegistryMagic, setting both our magic number and
+ * gstreamer major/minor version
+ */
+inline static gboolean
+gst_registry_binary_initialize_magic (GstBinaryRegistryMagic * m)
+{
+ memset (m, 0, sizeof (GstBinaryRegistryMagic));
+
+ if (!strncpy (m->magic, GST_MAGIC_BINARY_REGISTRY_STR,
+ GST_MAGIC_BINARY_REGISTRY_LEN)
+ || !strncpy (m->version, GST_MAGIC_BINARY_VERSION_STR,
+ GST_MAGIC_BINARY_VERSION_LEN)) {
+ GST_ERROR ("Failed to write magic to the registry magic structure");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ * gst_registry_binary_write_cache:
+ * @registry: a #GstRegistry
+ * @location: a filename
+ *
+ * Write the @registry to a cache to file at given @location.
+ *
+ * Returns: %TRUE on success.
+ */
+gboolean
+priv_gst_registry_binary_write_cache (GstRegistry * registry,
+ const char *location)
+{
+ GList *walk;
+ GstBinaryRegistryMagic magic;
+ GList *to_write = NULL;
+ unsigned long file_position = 0;
+ BinaryRegistryCache *cache;
+
+ GST_INFO ("Building binary registry cache image");
+
+ g_return_val_if_fail (GST_IS_REGISTRY (registry), FALSE);
+
+ if (!gst_registry_binary_initialize_magic (&magic))
+ goto fail;
+
+ /* iterate trough the list of plugins and fit them into binary structures */
+ for (walk = registry->plugins; walk; walk = g_list_next (walk)) {
+ GstPlugin *plugin = GST_PLUGIN (walk->data);
+
+ if (!plugin->filename)
+ continue;
+
+ if (plugin->flags & GST_PLUGIN_FLAG_CACHED) {
+ GStatBuf statbuf;
+
+ if (g_stat (plugin->filename, &statbuf) < 0 ||
+ plugin->file_mtime != statbuf.st_mtime ||
+ plugin->file_size != statbuf.st_size)
+ continue;
+ }
+
+ if (!_priv_gst_registry_chunks_save_plugin (&to_write, registry, plugin)) {
+ GST_ERROR ("Can't write binary plugin information for \"%s\"",
+ plugin->filename);
+ }
+ }
+
+ _priv_gst_registry_chunks_save_global_header (&to_write, registry,
+ priv_gst_plugin_loading_get_whitelist_hash ());
+
+ GST_INFO ("Writing binary registry cache");
+
+ cache = gst_registry_binary_cache_init (registry, location);
+ if (!cache)
+ goto fail_free_list;
+
+ /* write magic */
+ if (gst_registry_binary_cache_write (cache, file_position,
+ &magic, sizeof (GstBinaryRegistryMagic)) !=
+ sizeof (GstBinaryRegistryMagic)) {
+ GST_ERROR ("Failed to write binary registry magic");
+ goto fail_free_list;
+ }
+ file_position += sizeof (GstBinaryRegistryMagic);
+
+ /* write out data chunks */
+ for (walk = to_write; walk; walk = g_list_next (walk)) {
+ GstRegistryChunk *cur = walk->data;
+ gboolean res;
+
+ res = gst_registry_binary_write_chunk (cache, cur, &file_position);
+
+ _priv_gst_registry_chunk_free (cur);
+ walk->data = NULL;
+ if (!res)
+ goto fail_free_list;
+ }
+ g_list_free (to_write);
+
+ if (!gst_registry_binary_cache_finish (cache, TRUE))
+ return FALSE;
+
+ return TRUE;
+
+ /* Errors */
+fail_free_list:
+ {
+ for (walk = to_write; walk; walk = g_list_next (walk)) {
+ GstRegistryChunk *cur = walk->data;
+
+ if (cur)
+ _priv_gst_registry_chunk_free (cur);
+ }
+ g_list_free (to_write);
+
+ if (cache)
+ (void) gst_registry_binary_cache_finish (cache, FALSE);
+ /* fall through */
+ }
+fail:
+ {
+ return FALSE;
+ }
+}
+
+
+/* Registry loading */
+
+/*
+ * gst_registry_binary_check_magic:
+ *
+ * Check GstBinaryRegistryMagic validity.
+ * Return < 0 if something is wrong, -2 means
+ * that just the version of the registry is out of
+ * date, -1 is a general failure.
+ */
+static gint
+gst_registry_binary_check_magic (gchar ** in, gsize size)
+{
+ GstBinaryRegistryMagic *m;
+
+ align (*in);
+ GST_DEBUG ("Reading/casting for GstBinaryRegistryMagic at address %p", *in);
+ unpack_element (*in, m, GstBinaryRegistryMagic, (*in + size), fail);
+
+ if (strncmp (m->magic, GST_MAGIC_BINARY_REGISTRY_STR,
+ GST_MAGIC_BINARY_REGISTRY_LEN) != 0) {
+ GST_WARNING
+ ("Binary registry magic is different : %02x%02x%02x%02x != %02x%02x%02x%02x",
+ GST_MAGIC_BINARY_REGISTRY_STR[0] & 0xff,
+ GST_MAGIC_BINARY_REGISTRY_STR[1] & 0xff,
+ GST_MAGIC_BINARY_REGISTRY_STR[2] & 0xff,
+ GST_MAGIC_BINARY_REGISTRY_STR[3] & 0xff, m->magic[0] & 0xff,
+ m->magic[1] & 0xff, m->magic[2] & 0xff, m->magic[3] & 0xff);
+ return -1;
+ }
+ if (strncmp (m->version, GST_MAGIC_BINARY_VERSION_STR,
+ GST_MAGIC_BINARY_VERSION_LEN)) {
+ GST_WARNING ("Binary registry magic version is different : %s != %s",
+ GST_MAGIC_BINARY_VERSION_STR, m->version);
+ return -2;
+ }
+
+ return 0;
+
+fail:
+ GST_WARNING ("Not enough data for binary registry magic structure");
+ return -1;
+}
+
+/**
+ * gst_registry_binary_read_cache:
+ * @registry: a #GstRegistry
+ * @location: a filename
+ *
+ * Read the contents of the binary cache file at @location into @registry.
+ *
+ * Returns: %TRUE on success.
+ */
+gboolean
+priv_gst_registry_binary_read_cache (GstRegistry * registry,
+ const char *location)
+{
+ GMappedFile *mapped = NULL;
+ gchar *contents = NULL;
+ gchar *in = NULL;
+ gsize size;
+ GError *err = NULL;
+ gboolean res = FALSE;
+ guint32 filter_env_hash = 0;
+ gint check_magic_result;
+#ifndef GST_DISABLE_GST_DEBUG
+ GTimer *timer = NULL;
+ gdouble seconds;
+#endif
+
+ /* make sure these types exist */
+ GST_TYPE_ELEMENT_FACTORY;
+ GST_TYPE_TYPE_FIND_FACTORY;
+ GST_TYPE_INDEX_FACTORY;
+
+#ifndef GST_DISABLE_GST_DEBUG
+ timer = g_timer_new ();
+#endif
+
+ mapped = g_mapped_file_new (location, FALSE, &err);
+ if (G_UNLIKELY (err != NULL)) {
+ GST_INFO ("Unable to mmap file %s : %s", location, err->message);
+ g_error_free (err);
+ err = NULL;
+ }
+
+ if (mapped == NULL) {
+ /* Error mmap-ing the cache, try a plain memory read */
+
+ g_file_get_contents (location, &contents, &size, &err);
+ if (err != NULL) {
+ GST_INFO ("Unable to read file %s : %s", location, err->message);
+#ifndef GST_DISABLE_GST_DEBUG
+ g_timer_destroy (timer);
+#endif
+ g_error_free (err);
+ return FALSE;
+ }
+ } else {
+ /* This can't fail if g_mapped_file_new() succeeded */
+ contents = g_mapped_file_get_contents (mapped);
+ size = g_mapped_file_get_length (mapped);
+ }
+
+ /* in is a cursor pointer, we initialize it with the begin of registry and is updated on each read */
+ in = contents;
+ GST_DEBUG ("File data at address %p", in);
+ if (G_UNLIKELY (size < sizeof (GstBinaryRegistryMagic))) {
+ GST_ERROR ("No or broken registry header for file at %s", location);
+ goto Error;
+ }
+
+ /* check if header is valid */
+ if (G_UNLIKELY ((check_magic_result =
+ gst_registry_binary_check_magic (&in, size)) < 0)) {
+
+ if (check_magic_result == -1)
+ GST_ERROR
+ ("Binary registry type not recognized (invalid magic) for file at %s",
+ location);
+ goto Error;
+ }
+
+ if (!_priv_gst_registry_chunks_load_global_header (registry, &in,
+ contents + size, &filter_env_hash)) {
+ GST_ERROR ("Couldn't read global header chunk");
+ goto Error;
+ }
+
+ if (filter_env_hash != priv_gst_plugin_loading_get_whitelist_hash ()) {
+ GST_INFO_OBJECT (registry, "Plugin loading filter environment changed, "
+ "ignoring plugin cache to force update with new filter environment");
+ goto done;
+ }
+
+ /* check if there are plugins in the file */
+ if (G_UNLIKELY (!(((gsize) in + sizeof (GstRegistryChunkPluginElement)) <
+ (gsize) contents + size))) {
+ GST_INFO ("No binary plugins structure to read");
+ /* empty file, this is not an error */
+ } else {
+ gchar *end = contents + size;
+ /* read as long as we still have space for a GstRegistryChunkPluginElement */
+ for (;
+ ((gsize) in + sizeof (GstRegistryChunkPluginElement)) <
+ (gsize) contents + size;) {
+ GST_DEBUG ("reading binary registry %" G_GSIZE_FORMAT "(%x)/%"
+ G_GSIZE_FORMAT, (gsize) in - (gsize) contents,
+ (guint) ((gsize) in - (gsize) contents), size);
+ if (!_priv_gst_registry_chunks_load_plugin (registry, &in, end, NULL)) {
+ GST_ERROR ("Problem while reading binary registry %s", location);
+ goto Error;
+ }
+ }
+ }
+
+done:
+
+#ifndef GST_DISABLE_GST_DEBUG
+ g_timer_stop (timer);
+ seconds = g_timer_elapsed (timer, NULL);
+#endif
+
+ GST_INFO ("loaded %s in %lf seconds", location, seconds);
+
+ res = TRUE;
+ /* TODO: once we re-use the pointers to registry contents, return here */
+
+Error:
+#ifndef GST_DISABLE_GST_DEBUG
+ g_timer_destroy (timer);
+#endif
+ if (mapped) {
+ g_mapped_file_unref (mapped);
+ } else {
+ g_free (contents);
+ }
+ return res;
+}
diff --git a/gst/gstregistrybinary.h b/gst/gstregistrybinary.h
new file mode 100644
index 0000000..aa2c6d3
--- /dev/null
+++ b/gst/gstregistrybinary.h
@@ -0,0 +1,76 @@
+/* GStreamer
+ * Copyright (C) 2006 Josep Torra <josep@fluendo.com>
+ * Copyright (C) 2006 Mathieu Garcia <matthieu@fluendo.com>
+ * Copyright (C) 2006 Stefan Kost <ensonic@sonicpulse.de>
+ *
+ * gstregistrybinary.h: Header for registry handling
+ *
+ * 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.
+ */
+
+/* SUGGESTIONS AND TODO :
+** ====================
+** - Use a compressed registry, but would induce performance loss
+** - Encrypt the registry, for security purpose, but would also reduce performances
+*/
+
+#ifndef __GST_REGISTRYBINARY_H__
+#define __GST_REGISTRYBINARY_H__
+
+#include <gst/gstpad.h>
+#include <gst/gstregistry.h>
+
+G_BEGIN_DECLS
+
+/*
+ * GST_MAGIC_BINARY_REGISTRY_STR:
+ *
+ * A tag, written at the beginning of the file
+ */
+#define GST_MAGIC_BINARY_REGISTRY_STR "\xc0\xde\xf0\x0d"
+/*
+ * GST_MAGIC_BINARY_REGISTRY_LEN:
+ *
+ * length of the header tag.
+ */
+#define GST_MAGIC_BINARY_REGISTRY_LEN (4)
+
+/*
+ * GST_MAGIC_BINARY_VERSION_STR:
+ *
+ * The current version of the binary registry format.
+ * This _must_ be updated whenever the registry format changes,
+ * we currently use the core version where this change happened.
+ */
+#define GST_MAGIC_BINARY_VERSION_STR "0.10.30.1"
+
+/*
+ * GST_MAGIC_BINARY_VERSION_LEN:
+ *
+ * Maximum length of the version string in the header.
+ */
+#define GST_MAGIC_BINARY_VERSION_LEN (64)
+
+typedef struct _GstBinaryRegistryMagic
+{
+ gchar magic[GST_MAGIC_BINARY_REGISTRY_LEN];
+ gchar version[GST_MAGIC_BINARY_VERSION_LEN];
+} GstBinaryRegistryMagic;
+
+G_END_DECLS
+
+#endif /* !__GST_REGISTRYBINARY_H__ */
+
diff --git a/gst/gstregistrychunks.c b/gst/gstregistrychunks.c
new file mode 100644
index 0000000..bc1ca6b
--- /dev/null
+++ b/gst/gstregistrychunks.c
@@ -0,0 +1,904 @@
+/* GStreamer
+ * Copyright (C) 2006 Josep Torra <josep@fluendo.com>
+ * 2006 Mathieu Garcia <matthieu@fluendo.com>
+ * 2006,2007 Stefan Kost <ensonic@users.sf.net>
+ * 2008 Sebastian Dröge <slomo@circular-chaos.org>
+ * 2008 Jan Schmidt <jan.schmidt@sun.com>
+ *
+ * gstregistrychunks.c: GstRegistryChunk helper for serialising/deserialising
+ * plugin entries and features.
+ *
+ * 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_private.h>
+#include <gst/gstconfig.h>
+#include <gst/gstelement.h>
+#include <gst/gsttypefind.h>
+#include <gst/gsttypefindfactory.h>
+#include <gst/gsturi.h>
+#include <gst/gstinfo.h>
+#include <gst/gstenumtypes.h>
+#include <gst/gstpadtemplate.h>
+
+#include <gst/gstregistrychunks.h>
+
+#define GST_CAT_DEFAULT GST_CAT_REGISTRY
+
+/* count string length, but return -1 if we hit the eof */
+static gint
+_strnlen (const gchar * str, gint maxlen)
+{
+ gint len = 0;
+
+ while (G_LIKELY (len < maxlen)) {
+ if (G_UNLIKELY (str[len] == '\0'))
+ return len;
+ len++;
+ }
+ return -1;
+}
+
+/* Macros */
+#define unpack_element(inptr, outptr, element, endptr, error_label) G_STMT_START{ \
+ if (inptr + sizeof(element) > endptr) { \
+ GST_ERROR ("Failed reading element " G_STRINGIFY (element) \
+ ". Have %d bytes need %" G_GSSIZE_FORMAT, \
+ (int) (endptr - inptr), sizeof(element)); \
+ goto error_label; \
+ } \
+ outptr = (element *) inptr; \
+ inptr += sizeof (element); \
+}G_STMT_END
+
+#define unpack_const_string(inptr, outptr, endptr, error_label) G_STMT_START{\
+ gint _len = _strnlen (inptr, (endptr-inptr)); \
+ if (_len == -1) \
+ goto error_label; \
+ outptr = g_intern_string ((const gchar *)inptr); \
+ inptr += _len + 1; \
+}G_STMT_END
+
+#define unpack_string(inptr, outptr, endptr, error_label) G_STMT_START{\
+ gint _len = _strnlen (inptr, (endptr-inptr)); \
+ if (_len == -1) \
+ goto error_label; \
+ outptr = g_memdup ((gconstpointer)inptr, _len + 1); \
+ inptr += _len + 1; \
+}G_STMT_END
+
+#define unpack_string_nocopy(inptr, outptr, endptr, error_label) G_STMT_START{\
+ gint _len = _strnlen (inptr, (endptr-inptr)); \
+ if (_len == -1) \
+ goto error_label; \
+ outptr = (const gchar *)inptr; \
+ inptr += _len + 1; \
+}G_STMT_END
+
+#define ALIGNMENT (sizeof (void *))
+#define alignment(_address) (gsize)_address%ALIGNMENT
+#define align(_ptr) _ptr += (( alignment(_ptr) == 0) ? 0 : ALIGNMENT-alignment(_ptr))
+
+void
+_priv_gst_registry_chunk_free (GstRegistryChunk * chunk)
+{
+ if (!(chunk->flags & GST_REGISTRY_CHUNK_FLAG_CONST)) {
+ if ((chunk->flags & GST_REGISTRY_CHUNK_FLAG_MALLOC))
+ g_free (chunk->data);
+ else
+ g_slice_free1 (chunk->size, chunk->data);
+ }
+ g_slice_free (GstRegistryChunk, chunk);
+}
+
+/*
+ * gst_registry_chunks_save_const_string:
+ *
+ * Store a const string in a binary chunk.
+ *
+ * Returns: %TRUE for success
+ */
+inline static gboolean
+gst_registry_chunks_save_const_string (GList ** list, const gchar * str)
+{
+ GstRegistryChunk *chunk;
+
+ if (G_UNLIKELY (str == NULL)) {
+ GST_ERROR ("unexpected NULL string in plugin or plugin feature data");
+ str = "";
+ }
+
+ chunk = g_slice_new (GstRegistryChunk);
+ chunk->data = (gpointer) str;
+ chunk->size = strlen ((gchar *) chunk->data) + 1;
+ chunk->flags = GST_REGISTRY_CHUNK_FLAG_CONST;
+ chunk->align = FALSE;
+ *list = g_list_prepend (*list, chunk);
+ return TRUE;
+}
+
+/*
+ * gst_registry_chunks_save_string:
+ *
+ * Store a string in a binary chunk.
+ *
+ * Returns: %TRUE for success
+ */
+inline static gboolean
+gst_registry_chunks_save_string (GList ** list, gchar * str)
+{
+ GstRegistryChunk *chunk;
+
+ chunk = g_slice_new (GstRegistryChunk);
+ chunk->data = str;
+ chunk->size = strlen ((gchar *) chunk->data) + 1;
+ chunk->flags = GST_REGISTRY_CHUNK_FLAG_MALLOC;
+ chunk->align = FALSE;
+ *list = g_list_prepend (*list, chunk);
+ return TRUE;
+}
+
+/*
+ * gst_registry_chunks_save_data:
+ *
+ * Store some data in a binary chunk.
+ *
+ * Returns: the initialized chunk
+ */
+inline static GstRegistryChunk *
+gst_registry_chunks_make_data (gpointer data, gulong size)
+{
+ GstRegistryChunk *chunk;
+
+ chunk = g_slice_new (GstRegistryChunk);
+ chunk->data = data;
+ chunk->size = size;
+ chunk->flags = GST_REGISTRY_CHUNK_FLAG_NONE;
+ chunk->align = TRUE;
+ return chunk;
+}
+
+
+/*
+ * gst_registry_chunks_save_pad_template:
+ *
+ * Store pad_templates in binary chunks.
+ *
+ * Returns: %TRUE for success
+ */
+static gboolean
+gst_registry_chunks_save_pad_template (GList ** list,
+ GstStaticPadTemplate * template)
+{
+ GstRegistryChunkPadTemplate *pt;
+ GstRegistryChunk *chk;
+
+ pt = g_slice_new (GstRegistryChunkPadTemplate);
+ chk =
+ gst_registry_chunks_make_data (pt, sizeof (GstRegistryChunkPadTemplate));
+
+ pt->presence = template->presence;
+ pt->direction = template->direction;
+
+ /* pack pad template strings */
+ gst_registry_chunks_save_const_string (list,
+ (gchar *) (template->static_caps.string));
+ gst_registry_chunks_save_const_string (list, template->name_template);
+
+ *list = g_list_prepend (*list, chk);
+
+ return TRUE;
+}
+
+#define VALIDATE_UTF8(__details, __entry) \
+G_STMT_START { \
+ if (!g_utf8_validate (__details->__entry, -1, NULL)) { \
+ g_warning ("Invalid UTF-8 in " G_STRINGIFY (__entry) ": %s", \
+ __details->__entry); \
+ g_free (__details->__entry); \
+ __details->__entry = g_strdup ("[ERROR: invalid UTF-8]"); \
+ } \
+} G_STMT_END
+
+/*
+ * gst_registry_chunks_save_feature:
+ *
+ * Store features in binary chunks.
+ *
+ * Returns: %TRUE for success
+ */
+static gboolean
+gst_registry_chunks_save_feature (GList ** list, GstPluginFeature * feature)
+{
+ const gchar *type_name = g_type_name (G_OBJECT_TYPE (feature));
+ GstRegistryChunkPluginFeature *pf = NULL;
+ GstRegistryChunk *chk = NULL;
+ GList *walk;
+
+ if (!type_name) {
+ GST_ERROR ("NULL feature type_name, aborting.");
+ return FALSE;
+ }
+
+ if (GST_IS_ELEMENT_FACTORY (feature)) {
+ GstRegistryChunkElementFactory *ef;
+ GstElementFactory *factory = GST_ELEMENT_FACTORY (feature);
+
+ /* Initialize with zeroes because of struct padding and
+ * valgrind complaining about copying unitialized memory
+ */
+ ef = g_slice_new0 (GstRegistryChunkElementFactory);
+ chk =
+ gst_registry_chunks_make_data (ef,
+ sizeof (GstRegistryChunkElementFactory));
+ ef->npadtemplates = ef->ninterfaces = ef->nuriprotocols = 0;
+ pf = (GstRegistryChunkPluginFeature *) ef;
+
+ /* save interfaces */
+ for (walk = factory->interfaces; walk;
+ walk = g_list_next (walk), ef->ninterfaces++) {
+ gst_registry_chunks_save_const_string (list, (gchar *) walk->data);
+ }
+ GST_DEBUG ("Feature %s: saved %d interfaces %d pad templates",
+ GST_OBJECT_NAME (feature), ef->ninterfaces, ef->npadtemplates);
+
+ /* save uritypes */
+ if (GST_URI_TYPE_IS_VALID (factory->uri_type)) {
+ if (factory->uri_protocols && *factory->uri_protocols) {
+ GstRegistryChunk *subchk;
+ gchar **protocol;
+
+ subchk =
+ gst_registry_chunks_make_data (&factory->uri_type,
+ sizeof (factory->uri_type));
+ subchk->flags = GST_REGISTRY_CHUNK_FLAG_CONST;
+
+ protocol = factory->uri_protocols;
+ while (*protocol) {
+ gst_registry_chunks_save_const_string (list, *protocol++);
+ ef->nuriprotocols++;
+ }
+ *list = g_list_prepend (*list, subchk);
+ GST_DEBUG ("Saved %d UriTypes", ef->nuriprotocols);
+ } else {
+ g_warning ("GStreamer feature '%s' is URI handler but does not provide"
+ " any protocols it can handle", GST_OBJECT_NAME (feature));
+ }
+ }
+
+ /* save pad-templates */
+ for (walk = factory->staticpadtemplates; walk;
+ walk = g_list_next (walk), ef->npadtemplates++) {
+ GstStaticPadTemplate *template = walk->data;
+
+ if (!gst_registry_chunks_save_pad_template (list, template)) {
+ GST_ERROR ("Can't fill pad template, aborting.");
+ goto fail;
+ }
+ }
+
+ /* pack element metadata strings */
+ gst_registry_chunks_save_string (list,
+ gst_structure_to_string (factory->metadata));
+ } else if (GST_IS_TYPE_FIND_FACTORY (feature)) {
+ GstRegistryChunkTypeFindFactory *tff;
+ GstTypeFindFactory *factory = GST_TYPE_FIND_FACTORY (feature);
+ gchar *str;
+
+ /* Initialize with zeroes because of struct padding and
+ * valgrind complaining about copying unitialized memory
+ */
+ tff = g_slice_new0 (GstRegistryChunkTypeFindFactory);
+ chk =
+ gst_registry_chunks_make_data (tff,
+ sizeof (GstRegistryChunkTypeFindFactory));
+ tff->nextensions = 0;
+ pf = (GstRegistryChunkPluginFeature *) tff;
+
+ /* save extensions */
+ if (factory->extensions) {
+ while (factory->extensions[tff->nextensions]) {
+ gst_registry_chunks_save_const_string (list,
+ factory->extensions[tff->nextensions++]);
+ }
+ }
+ /* save caps */
+ if (factory->caps) {
+ /* we copy the caps here so we can simplify them before saving. This
+ * is a lot faster when loading them later on */
+ GstCaps *copy = gst_caps_copy (factory->caps);
+
+ gst_caps_do_simplify (copy);
+ str = gst_caps_to_string (copy);
+ gst_caps_unref (copy);
+ gst_registry_chunks_save_string (list, str);
+ } else {
+ gst_registry_chunks_save_const_string (list, "");
+ }
+ } else if (GST_IS_INDEX_FACTORY (feature)) {
+ GstIndexFactory *factory = GST_INDEX_FACTORY (feature);
+
+ pf = g_slice_new (GstRegistryChunkPluginFeature);
+ chk =
+ gst_registry_chunks_make_data (pf,
+ sizeof (GstRegistryChunkPluginFeature));
+
+ /* pack element factory strings */
+ gst_registry_chunks_save_const_string (list, factory->longdesc);
+ } else {
+ GST_WARNING ("unhandled feature type '%s'", type_name);
+ }
+
+ if (pf) {
+ pf->rank = feature->rank;
+ *list = g_list_prepend (*list, chk);
+
+ /* pack plugin feature strings */
+ gst_registry_chunks_save_const_string (list, GST_OBJECT_NAME (feature));
+ gst_registry_chunks_save_const_string (list, (gchar *) type_name);
+
+ return TRUE;
+ }
+
+ /* Errors */
+fail:
+ g_free (chk);
+ g_free (pf);
+ return FALSE;
+}
+
+static gboolean
+gst_registry_chunks_save_plugin_dep (GList ** list, GstPluginDep * dep)
+{
+ GstRegistryChunkDep *ed;
+ GstRegistryChunk *chk;
+ gchar **s;
+
+ ed = g_slice_new (GstRegistryChunkDep);
+ chk = gst_registry_chunks_make_data (ed, sizeof (GstRegistryChunkDep));
+
+ ed->flags = dep->flags;
+ ed->n_env_vars = 0;
+ ed->n_paths = 0;
+ ed->n_names = 0;
+
+ ed->env_hash = dep->env_hash;
+ ed->stat_hash = dep->stat_hash;
+
+ for (s = dep->env_vars; s != NULL && *s != NULL; ++s, ++ed->n_env_vars)
+ gst_registry_chunks_save_string (list, g_strdup (*s));
+
+ for (s = dep->paths; s != NULL && *s != NULL; ++s, ++ed->n_paths)
+ gst_registry_chunks_save_string (list, g_strdup (*s));
+
+ for (s = dep->names; s != NULL && *s != NULL; ++s, ++ed->n_names)
+ gst_registry_chunks_save_string (list, g_strdup (*s));
+
+ *list = g_list_prepend (*list, chk);
+
+ GST_LOG ("Saved external plugin dependency");
+ return TRUE;
+}
+
+/*
+ * _priv_gst_registry_chunks_save_plugin:
+ *
+ * Adapt a GstPlugin to our GstRegistryChunkPluginElement structure, and
+ * prepend it as a GstRegistryChunk in the provided list.
+ *
+ */
+gboolean
+_priv_gst_registry_chunks_save_plugin (GList ** list, GstRegistry * registry,
+ GstPlugin * plugin)
+{
+ GstRegistryChunkPluginElement *pe;
+ GstRegistryChunk *chk;
+ GList *plugin_features = NULL;
+ GList *walk;
+
+ pe = g_slice_new (GstRegistryChunkPluginElement);
+ chk =
+ gst_registry_chunks_make_data (pe,
+ sizeof (GstRegistryChunkPluginElement));
+
+ pe->file_size = plugin->file_size;
+ pe->file_mtime = plugin->file_mtime;
+ pe->nfeatures = 0;
+ pe->n_deps = 0;
+
+ /* pack external deps */
+ for (walk = plugin->priv->deps; walk != NULL; walk = walk->next) {
+ if (!gst_registry_chunks_save_plugin_dep (list, walk->data)) {
+ GST_ERROR ("Could not save external plugin dependency, aborting.");
+ goto fail;
+ }
+ ++pe->n_deps;
+ }
+
+ /* pack plugin features */
+ plugin_features =
+ gst_registry_get_feature_list_by_plugin (registry, plugin->desc.name);
+ for (walk = plugin_features; walk; walk = g_list_next (walk), pe->nfeatures++) {
+ GstPluginFeature *feature = GST_PLUGIN_FEATURE (walk->data);
+
+ if (!gst_registry_chunks_save_feature (list, feature)) {
+ GST_ERROR ("Can't fill plugin feature, aborting.");
+ goto fail;
+ }
+ }
+
+ gst_plugin_feature_list_free (plugin_features);
+
+ /* pack cache data */
+ if (plugin->priv->cache_data) {
+ gchar *cache_str = gst_structure_to_string (plugin->priv->cache_data);
+ gst_registry_chunks_save_string (list, cache_str);
+ } else {
+ gst_registry_chunks_save_const_string (list, "");
+ }
+
+ /* pack plugin element strings */
+ gst_registry_chunks_save_const_string (list,
+ (plugin->desc.release_datetime) ? plugin->desc.release_datetime : "");
+ gst_registry_chunks_save_const_string (list, plugin->desc.origin);
+ gst_registry_chunks_save_const_string (list, plugin->desc.package);
+ gst_registry_chunks_save_const_string (list, plugin->desc.source);
+ gst_registry_chunks_save_const_string (list, plugin->desc.license);
+ gst_registry_chunks_save_const_string (list, plugin->desc.version);
+ gst_registry_chunks_save_const_string (list, plugin->filename);
+ gst_registry_chunks_save_const_string (list, plugin->desc.description);
+ gst_registry_chunks_save_const_string (list, plugin->desc.name);
+
+ *list = g_list_prepend (*list, chk);
+
+ GST_DEBUG ("Found %d features in plugin \"%s\"", pe->nfeatures,
+ plugin->desc.name);
+ return TRUE;
+
+ /* Errors */
+fail:
+ gst_plugin_feature_list_free (plugin_features);
+ g_free (chk);
+ g_free (pe);
+ return FALSE;
+}
+
+/*
+ * gst_registry_chunks_load_pad_template:
+ *
+ * Make a new GstStaticPadTemplate from current GstRegistryChunkPadTemplate
+ * structure.
+ *
+ * Returns: new GstStaticPadTemplate
+ */
+static gboolean
+gst_registry_chunks_load_pad_template (GstElementFactory * factory, gchar ** in,
+ gchar * end)
+{
+ GstRegistryChunkPadTemplate *pt;
+ GstStaticPadTemplate *template = NULL;
+
+ align (*in);
+ GST_DEBUG ("Reading/casting for GstRegistryChunkPadTemplate at address %p",
+ *in);
+ unpack_element (*in, pt, GstRegistryChunkPadTemplate, end, fail);
+
+ template = g_slice_new (GstStaticPadTemplate);
+ template->presence = pt->presence;
+ template->direction = (GstPadDirection) pt->direction;
+ template->static_caps.caps.mini_object.refcount = 0;
+
+ /* unpack pad template strings */
+ unpack_const_string (*in, template->name_template, end, fail);
+ unpack_const_string (*in, template->static_caps.string, end, fail);
+
+ __gst_element_factory_add_static_pad_template (factory, template);
+ GST_DEBUG ("Added pad_template %s", template->name_template);
+
+ return TRUE;
+fail:
+ GST_INFO ("Reading pad template failed");
+ if (template)
+ g_slice_free (GstStaticPadTemplate, template);
+ return FALSE;
+}
+
+/*
+ * gst_registry_chunks_load_feature:
+ *
+ * Make a new GstPluginFeature from current binary plugin feature structure
+ *
+ * Returns: new GstPluginFeature
+ */
+static gboolean
+gst_registry_chunks_load_feature (GstRegistry * registry, gchar ** in,
+ gchar * end, GstPlugin * plugin)
+{
+ GstRegistryChunkPluginFeature *pf = NULL;
+ GstPluginFeature *feature = NULL;
+ const gchar *const_str, *type_name;
+ const gchar *feature_name;
+ const gchar *plugin_name;
+ gchar *str;
+ GType type;
+ guint i;
+
+ plugin_name = plugin->desc.name;
+
+ /* unpack plugin feature strings */
+ unpack_string_nocopy (*in, type_name, end, fail);
+
+ if (G_UNLIKELY (!type_name)) {
+ GST_ERROR ("No feature type name");
+ return FALSE;
+ }
+
+ /* unpack more plugin feature strings */
+ unpack_string_nocopy (*in, feature_name, end, fail);
+
+ GST_DEBUG ("Plugin '%s' feature '%s' typename : '%s'", plugin_name,
+ feature_name, type_name);
+
+ if (G_UNLIKELY (!(type = g_type_from_name (type_name)))) {
+ GST_ERROR ("Unknown type from typename '%s' for plugin '%s'", type_name,
+ plugin_name);
+ return FALSE;
+ }
+ if (G_UNLIKELY ((feature = g_object_newv (type, 0, NULL)) == NULL)) {
+ GST_ERROR ("Can't create feature from type");
+ return FALSE;
+ }
+ gst_plugin_feature_set_name (feature, feature_name);
+
+ if (G_UNLIKELY (!GST_IS_PLUGIN_FEATURE (feature))) {
+ GST_ERROR ("typename : '%s' is not a plugin feature", type_name);
+ goto fail;
+ }
+
+ if (GST_IS_ELEMENT_FACTORY (feature)) {
+ GstRegistryChunkElementFactory *ef;
+ guint n;
+ GstElementFactory *factory = GST_ELEMENT_FACTORY_CAST (feature);
+ gchar *str;
+ const gchar *meta_data_str;
+
+ align (*in);
+ GST_LOG ("Reading/casting for GstRegistryChunkElementFactory at address %p",
+ *in);
+ unpack_element (*in, ef, GstRegistryChunkElementFactory, end, fail);
+ pf = (GstRegistryChunkPluginFeature *) ef;
+
+ /* unpack element factory strings */
+ unpack_string_nocopy (*in, meta_data_str, end, fail);
+ if (meta_data_str && *meta_data_str) {
+ factory->metadata = gst_structure_from_string (meta_data_str, NULL);
+ if (!factory->metadata) {
+ GST_ERROR
+ ("Error when trying to deserialize structure for metadata '%s'",
+ meta_data_str);
+ goto fail;
+ }
+ }
+ n = ef->npadtemplates;
+ GST_DEBUG ("Element factory : npadtemplates=%d", n);
+
+ /* load pad templates */
+ for (i = 0; i < n; i++) {
+ if (G_UNLIKELY (!gst_registry_chunks_load_pad_template (factory, in,
+ end))) {
+ GST_ERROR ("Error while loading binary pad template");
+ goto fail;
+ }
+ }
+
+ /* load uritypes */
+ if (G_UNLIKELY ((n = ef->nuriprotocols))) {
+ GST_DEBUG ("Reading %d UriTypes at address %p", n, *in);
+
+ align (*in);
+ factory->uri_type = *((guint *) * in);
+ *in += sizeof (factory->uri_type);
+ /*unpack_element(*in, &factory->uri_type, factory->uri_type, end, fail); */
+
+ factory->uri_protocols = g_new0 (gchar *, n + 1);
+ for (i = 0; i < n; i++) {
+ unpack_string (*in, str, end, fail);
+ factory->uri_protocols[i] = str;
+ }
+ }
+ /* load interfaces */
+ if (G_UNLIKELY ((n = ef->ninterfaces))) {
+ GST_DEBUG ("Reading %d Interfaces at address %p", n, *in);
+ for (i = 0; i < n; i++) {
+ unpack_string_nocopy (*in, const_str, end, fail);
+ __gst_element_factory_add_interface (factory, const_str);
+ }
+ }
+ } else if (GST_IS_TYPE_FIND_FACTORY (feature)) {
+ GstRegistryChunkTypeFindFactory *tff;
+ GstTypeFindFactory *factory = GST_TYPE_FIND_FACTORY (feature);
+
+ align (*in);
+ GST_DEBUG
+ ("Reading/casting for GstRegistryChunkPluginFeature at address %p",
+ *in);
+ unpack_element (*in, tff, GstRegistryChunkTypeFindFactory, end, fail);
+ pf = (GstRegistryChunkPluginFeature *) tff;
+
+ /* load typefinder caps */
+ unpack_string_nocopy (*in, const_str, end, fail);
+ if (const_str != NULL && *const_str != '\0')
+ factory->caps = gst_caps_from_string (const_str);
+ else
+ factory->caps = NULL;
+
+ /* load extensions */
+ if (tff->nextensions) {
+ GST_DEBUG ("Reading %d Typefind extensions at address %p",
+ tff->nextensions, *in);
+ factory->extensions = g_new0 (gchar *, tff->nextensions + 1);
+ /* unpack in reverse order to maintain the correct order */
+ for (i = tff->nextensions; i > 0; i--) {
+ unpack_string (*in, str, end, fail);
+ factory->extensions[i - 1] = str;
+ }
+ }
+ } else if (GST_IS_INDEX_FACTORY (feature)) {
+ GstIndexFactory *factory = GST_INDEX_FACTORY (feature);
+
+ align (*in);
+ GST_DEBUG
+ ("Reading/casting for GstRegistryChunkPluginFeature at address %p",
+ *in);
+ unpack_element (*in, pf, GstRegistryChunkPluginFeature, end, fail);
+
+ /* unpack index factory strings */
+ unpack_string (*in, factory->longdesc, end, fail);
+ } else {
+ GST_WARNING ("unhandled factory type : %s", G_OBJECT_TYPE_NAME (feature));
+ goto fail;
+ }
+
+ feature->rank = pf->rank;
+
+ feature->plugin_name = plugin_name;
+ feature->plugin = plugin;
+ g_object_add_weak_pointer ((GObject *) plugin,
+ (gpointer *) & feature->plugin);
+
+ gst_registry_add_feature (registry, feature);
+ GST_DEBUG ("Added feature %s, plugin %p %s", GST_OBJECT_NAME (feature),
+ plugin, plugin_name);
+
+ return TRUE;
+
+ /* Errors */
+fail:
+ GST_INFO ("Reading plugin feature failed");
+ if (feature) {
+ if (GST_IS_OBJECT (feature))
+ gst_object_unref (feature);
+ else
+ g_object_unref (feature);
+ }
+ return FALSE;
+}
+
+static gchar **
+gst_registry_chunks_load_plugin_dep_strv (gchar ** in, gchar * end, guint n)
+{
+ gchar **arr;
+
+ if (n == 0)
+ return NULL;
+
+ arr = g_new0 (gchar *, n + 1);
+ while (n > 0) {
+ unpack_string (*in, arr[n - 1], end, fail);
+ --n;
+ }
+ return arr;
+fail:
+ GST_INFO ("Reading plugin dependency strings failed");
+ return NULL;
+}
+
+static gboolean
+gst_registry_chunks_load_plugin_dep (GstPlugin * plugin, gchar ** in,
+ gchar * end)
+{
+ GstPluginDep *dep;
+ GstRegistryChunkDep *d;
+ gchar **s;
+
+ align (*in);
+ GST_LOG_OBJECT (plugin, "Unpacking GstRegistryChunkDep from %p", *in);
+ unpack_element (*in, d, GstRegistryChunkDep, end, fail);
+
+ dep = g_slice_new (GstPluginDep);
+
+ dep->env_hash = d->env_hash;
+ dep->stat_hash = d->stat_hash;
+
+ dep->flags = (GstPluginDependencyFlags) d->flags;
+
+ dep->names = gst_registry_chunks_load_plugin_dep_strv (in, end, d->n_names);
+ dep->paths = gst_registry_chunks_load_plugin_dep_strv (in, end, d->n_paths);
+ dep->env_vars =
+ gst_registry_chunks_load_plugin_dep_strv (in, end, d->n_env_vars);
+
+ plugin->priv->deps = g_list_append (plugin->priv->deps, dep);
+
+ GST_DEBUG_OBJECT (plugin, "Loaded external plugin dependency from registry: "
+ "env_hash: %08x, stat_hash: %08x", dep->env_hash, dep->stat_hash);
+ for (s = dep->env_vars; s != NULL && *s != NULL; ++s)
+ GST_LOG_OBJECT (plugin, " evar: %s", *s);
+ for (s = dep->paths; s != NULL && *s != NULL; ++s)
+ GST_LOG_OBJECT (plugin, " path: %s", *s);
+ for (s = dep->names; s != NULL && *s != NULL; ++s)
+ GST_LOG_OBJECT (plugin, " name: %s", *s);
+
+ return TRUE;
+fail:
+ GST_INFO ("Reading plugin dependency failed");
+ return FALSE;
+}
+
+
+/*
+ * _priv_gst_registry_chunks_load_plugin:
+ *
+ * Make a new GstPlugin from current GstRegistryChunkPluginElement structure
+ * and add it to the GstRegistry. Return an offset to the next
+ * GstRegistryChunkPluginElement structure.
+ */
+gboolean
+_priv_gst_registry_chunks_load_plugin (GstRegistry * registry, gchar ** in,
+ gchar * end, GstPlugin ** out_plugin)
+{
+#ifndef GST_DISABLE_GST_DEBUG
+ gchar *start = *in;
+#endif
+ GstRegistryChunkPluginElement *pe;
+ const gchar *cache_str = NULL;
+ GstPlugin *plugin = NULL;
+ guint i, n;
+
+ align (*in);
+ GST_LOG ("Reading/casting for GstRegistryChunkPluginElement at address %p",
+ *in);
+ unpack_element (*in, pe, GstRegistryChunkPluginElement, end, fail);
+
+ plugin = g_object_newv (GST_TYPE_PLUGIN, 0, NULL);
+
+ /* TODO: also set GST_PLUGIN_FLAG_CONST */
+ plugin->flags |= GST_PLUGIN_FLAG_CACHED;
+ plugin->file_mtime = pe->file_mtime;
+ plugin->file_size = pe->file_size;
+
+ /* unpack plugin element strings */
+ unpack_const_string (*in, plugin->desc.name, end, fail);
+ unpack_const_string (*in, plugin->desc.description, end, fail);
+ unpack_string (*in, plugin->filename, end, fail);
+ unpack_const_string (*in, plugin->desc.version, end, fail);
+ unpack_const_string (*in, plugin->desc.license, end, fail);
+ unpack_const_string (*in, plugin->desc.source, end, fail);
+ unpack_const_string (*in, plugin->desc.package, end, fail);
+ unpack_const_string (*in, plugin->desc.origin, end, fail);
+ unpack_const_string (*in, plugin->desc.release_datetime, end, fail);
+
+ GST_LOG ("read strings for name='%s'", plugin->desc.name);
+ GST_LOG (" desc.description='%s'", plugin->desc.description);
+ GST_LOG (" filename='%s'", plugin->filename);
+ GST_LOG (" desc.version='%s'", plugin->desc.version);
+ GST_LOG (" desc.license='%s'", plugin->desc.license);
+ GST_LOG (" desc.source='%s'", plugin->desc.source);
+ GST_LOG (" desc.package='%s'", plugin->desc.package);
+ GST_LOG (" desc.origin='%s'", plugin->desc.origin);
+ GST_LOG (" desc.datetime=%s", plugin->desc.release_datetime);
+
+ if (plugin->desc.release_datetime[0] == '\0')
+ plugin->desc.release_datetime = NULL;
+
+ /* unpack cache data */
+ unpack_string_nocopy (*in, cache_str, end, fail);
+ if (cache_str != NULL && *cache_str != '\0')
+ plugin->priv->cache_data = gst_structure_from_string (cache_str, NULL);
+
+ /* If the license string is 'BLACKLIST', mark this as a blacklisted
+ * plugin */
+ if (strcmp (plugin->desc.license, "BLACKLIST") == 0)
+ plugin->flags |= GST_PLUGIN_FLAG_BLACKLISTED;
+
+ plugin->basename = g_path_get_basename (plugin->filename);
+
+ /* Takes ownership of plugin */
+ gst_registry_add_plugin (registry, plugin);
+ n = pe->nfeatures;
+ GST_DEBUG ("Added plugin '%s' plugin with %d features from binary registry",
+ plugin->desc.name, n);
+
+ /* Load plugin features */
+ for (i = 0; i < n; i++) {
+ if (G_UNLIKELY (!gst_registry_chunks_load_feature (registry, in, end,
+ plugin))) {
+ GST_ERROR ("Error while loading binary feature for plugin '%s'",
+ GST_STR_NULL (plugin->desc.name));
+ gst_registry_remove_plugin (registry, plugin);
+ goto fail;
+ }
+ }
+
+ /* Load external plugin dependencies */
+ for (i = 0; i < pe->n_deps; ++i) {
+ if (G_UNLIKELY (!gst_registry_chunks_load_plugin_dep (plugin, in, end))) {
+ GST_ERROR_OBJECT (plugin, "Could not read external plugin dependency "
+ "for plugin '%s'", GST_STR_NULL (plugin->desc.name));
+ gst_registry_remove_plugin (registry, plugin);
+ goto fail;
+ }
+ }
+
+ if (out_plugin)
+ *out_plugin = plugin;
+
+ return TRUE;
+
+ /* Errors */
+fail:
+ GST_INFO ("Reading plugin failed after %u bytes", (guint) (end - start));
+ return FALSE;
+}
+
+void
+_priv_gst_registry_chunks_save_global_header (GList ** list,
+ GstRegistry * registry, guint32 filter_env_hash)
+{
+ GstRegistryChunkGlobalHeader *hdr;
+ GstRegistryChunk *chk;
+
+ hdr = g_slice_new (GstRegistryChunkGlobalHeader);
+ chk = gst_registry_chunks_make_data (hdr,
+ sizeof (GstRegistryChunkGlobalHeader));
+
+ hdr->filter_env_hash = filter_env_hash;
+
+ *list = g_list_prepend (*list, chk);
+
+ GST_LOG ("Saved global header (filter_env_hash=0x%08x)", filter_env_hash);
+}
+
+gboolean
+_priv_gst_registry_chunks_load_global_header (GstRegistry * registry,
+ gchar ** in, gchar * end, guint32 * filter_env_hash)
+{
+ GstRegistryChunkGlobalHeader *hdr;
+
+ align (*in);
+ GST_LOG ("Reading/casting for GstRegistryChunkGlobalHeader at %p", *in);
+ unpack_element (*in, hdr, GstRegistryChunkGlobalHeader, end, fail);
+ *filter_env_hash = hdr->filter_env_hash;
+ return TRUE;
+
+ /* Errors */
+fail:
+ GST_WARNING ("Reading global header failed");
+ return FALSE;
+}
diff --git a/gst/gstregistrychunks.h b/gst/gstregistrychunks.h
new file mode 100644
index 0000000..fdbac5e
--- /dev/null
+++ b/gst/gstregistrychunks.h
@@ -0,0 +1,168 @@
+/* GStreamer
+ * Copyright (C) 2006 Josep Torra <josep@fluendo.com>
+ * Copyright (C) 2006 Mathieu Garcia <matthieu@fluendo.com>
+ * Copyright (C) 2006 Stefan Kost <ensonic@sonicpulse.de>
+ *
+ * gstregistrybinary.h: Header for registry handling
+ *
+ * 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_REGISTRYCHUNKS_H__
+#define __GST_REGISTRYCHUNKS_H__
+
+#include <gst/gstpad.h>
+#include <gst/gstregistry.h>
+
+/*
+ * we reference strings directly from the plugins and in this case set CONST to
+ * avoid freeing them. If g_free() should be used, the MALLOC flag is set,
+ * otherwise g_slice_free1() will be used!
+ */
+enum {
+ GST_REGISTRY_CHUNK_FLAG_NONE = 0,
+ GST_REGISTRY_CHUNK_FLAG_CONST = 1,
+ GST_REGISTRY_CHUNK_FLAG_MALLOC = 2,
+};
+
+/*
+ * GstRegistryChunk:
+ *
+ * Header for binary blobs
+ */
+typedef struct _GstRegistryChunk
+{
+ gpointer data;
+ guint size;
+ guint flags;
+ gboolean align;
+} GstRegistryChunk;
+
+typedef struct _GstRegistryChunkGlobalHeader
+{
+ guint32 filter_env_hash;
+} GstRegistryChunkGlobalHeader;
+
+/*
+ * GstRegistryChunkPluginElement:
+ *
+ * @n_deps: Says how many dependency structures follows.
+ *
+ * @nfeatures: says how many binary plugin feature structures we will have
+ * right after the structure itself.
+ *
+ * A structure containing (staticely) every information needed for a plugin
+ */
+
+typedef struct _GstRegistryChunkPluginElement
+{
+ gulong file_size;
+ gulong file_mtime;
+
+ guint n_deps;
+
+ guint nfeatures;
+} GstRegistryChunkPluginElement;
+
+/* GstRegistryChunkDep:
+ */
+typedef struct _GstRegistryChunkDep
+{
+ guint flags;
+ guint n_env_vars;
+ guint n_paths;
+ guint n_names;
+
+ guint env_hash;
+ guint stat_hash;
+} GstRegistryChunkDep;
+
+/*
+ * GstRegistryChunkPluginFeature:
+ * @rank: rank of the feature
+ *
+ * A structure containing the plugin features
+ */
+typedef struct _GstRegistryChunkPluginFeature
+{
+ gulong rank;
+} GstRegistryChunkPluginFeature;
+
+/*
+ * GstRegistryChunkElementFactory:
+ * @npadtemplates: stores the number of GstRegistryChunkPadTemplate structures
+ * following the structure
+ * @ninterfaces: stores the number of interface names following the structure
+ * @nuriprotocols: stores the number of protocol strings following the structure
+ *
+ * A structure containing the element factory fields
+ */
+typedef struct _GstRegistryChunkElementFactory
+{
+ GstRegistryChunkPluginFeature plugin_feature;
+
+ guint npadtemplates;
+ guint ninterfaces;
+ guint nuriprotocols;
+} GstRegistryChunkElementFactory;
+
+/*
+ * GstRegistryChunkTypeFindFactory:
+ * @nextensions: stores the number of typefind extensions
+ *
+ * A structure containing the element factory fields
+ */
+typedef struct _GstRegistryChunkTypeFindFactory
+{
+ GstRegistryChunkPluginFeature plugin_feature;
+
+ guint nextensions;
+} GstRegistryChunkTypeFindFactory;
+
+/*
+ * GstRegistryChunkPadTemplate:
+ *
+ * A structure containing the static pad templates of a plugin feature
+ */
+typedef struct _GstRegistryChunkPadTemplate
+{
+ guint direction; /* Either 0:"sink" or 1:"src" */
+ GstPadPresence presence;
+} GstRegistryChunkPadTemplate;
+
+G_BEGIN_DECLS
+
+gboolean
+_priv_gst_registry_chunks_save_plugin (GList ** list, GstRegistry * registry,
+ GstPlugin * plugin);
+
+gboolean
+_priv_gst_registry_chunks_load_plugin (GstRegistry * registry, gchar ** in,
+ gchar *end, GstPlugin **out_plugin);
+
+void
+_priv_gst_registry_chunks_save_global_header (GList ** list,
+ GstRegistry * registry, guint32 filter_env_hash);
+
+gboolean
+_priv_gst_registry_chunks_load_global_header (GstRegistry * registry,
+ gchar ** in, gchar *end, guint32 * filter_env_hash);
+
+void
+_priv_gst_registry_chunk_free (GstRegistryChunk *chunk);
+
+G_END_DECLS
+
+#endif /* __GST_REGISTRYCHUNKS_H__ */
diff --git a/gst/gstsegment.c b/gst/gstsegment.c
new file mode 100644
index 0000000..456bed5
--- /dev/null
+++ b/gst/gstsegment.c
@@ -0,0 +1,720 @@
+/* GStreamer
+ * Copyright (C) 2005 Wim Taymans <wim@fluendo.com>
+ *
+ * gstsegment.c: GstSegment subsystem
+ *
+ * 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.
+ */
+
+#include "gst_private.h"
+
+#include <math.h>
+
+#include "gstutils.h"
+#include "gstsegment.h"
+
+/**
+ * SECTION:gstsegment
+ * @short_description: Structure describing the configured region of interest
+ * in a media file.
+ * @see_also: #GstEvent
+ *
+ * This helper structure holds the relevant values for tracking the region of
+ * interest in a media file, called a segment.
+ *
+ * The structure can be used for two purposes:
+ * <itemizedlist>
+ * <listitem><para>performing seeks (handling seek events)</para></listitem>
+ * <listitem><para>tracking playback regions (handling newsegment events)</para></listitem>
+ * </itemizedlist>
+ *
+ * The segment is usually configured by the application with a seek event which
+ * is propagated upstream and eventually handled by an element that performs the seek.
+ *
+ * The configured segment is then propagated back downstream with a newsegment event.
+ * This information is then used to clip media to the segment boundaries.
+ *
+ * A segment structure is initialized with gst_segment_init(), which takes a #GstFormat
+ * that will be used as the format of the segment values. The segment will be configured
+ * with a start value of 0 and a stop/duration of -1, which is undefined. The default
+ * rate and applied_rate is 1.0.
+ *
+ * If the segment is used for managing seeks, the segment duration should be set with
+ * gst_segment_set_duration(). The public duration field contains the duration of the
+ * segment. When using the segment for seeking, the start and time members should
+ * normally be left to their default 0 value. The stop position is left to -1 unless
+ * explicitly configured to a different value after a seek event.
+ *
+ * The current position in the segment should be set with the gst_segment_set_last_stop().
+ * The public last_stop field contains the last set stop position in the segment.
+ *
+ * For elements that perform seeks, the current segment should be updated with the
+ * gst_segment_set_seek() and the values from the seek event. This method will update
+ * all the segment fields. The last_stop field will contain the new playback position.
+ * If the cur_type was different from GST_SEEK_TYPE_NONE, playback continues from
+ * the last_stop position, possibly with updated flags or rate.
+ *
+ * For elements that want to use #GstSegment to track the playback region, use
+ * gst_segment_set_newsegment() to update the segment fields with the information from
+ * the newsegment event. The gst_segment_clip() method can be used to check and clip
+ * the media data to the segment boundaries.
+ *
+ * For elements that want to synchronize to the pipeline clock, gst_segment_to_running_time()
+ * can be used to convert a timestamp to a value that can be used to synchronize
+ * to the clock. This function takes into account all accumulated segments as well as
+ * any rate or applied_rate conversions.
+ *
+ * For elements that need to perform operations on media data in stream_time,
+ * gst_segment_to_stream_time() can be used to convert a timestamp and the segment
+ * info to stream time (which is always between 0 and the duration of the stream).
+ *
+ * Last reviewed on 2007-05-17 (0.10.13)
+ */
+
+/**
+ * gst_segment_copy:
+ * @segment: (transfer none): a #GstSegment
+ *
+ * Create a copy of given @segment.
+ *
+ * Free-function: gst_segment_free
+ *
+ * Returns: (transfer full): a new #GstSegment, free with gst_segment_free().
+ *
+ * Since: 0.10.20
+ */
+GstSegment *
+gst_segment_copy (const GstSegment * segment)
+{
+ GstSegment *result = NULL;
+
+ if (segment) {
+ result = (GstSegment *) g_slice_copy (sizeof (GstSegment), segment);
+ }
+ return result;
+}
+
+void
+gst_segment_copy_into (const GstSegment * src, GstSegment * dest)
+{
+ memcpy (dest, src, sizeof (GstSegment));
+}
+
+GType
+gst_segment_get_type (void)
+{
+ static GType gst_segment_type = 0;
+
+ if (G_UNLIKELY (gst_segment_type == 0)) {
+ gst_segment_type = g_boxed_type_register_static ("GstSegment",
+ (GBoxedCopyFunc) gst_segment_copy, (GBoxedFreeFunc) gst_segment_free);
+ }
+
+ return gst_segment_type;
+}
+
+/**
+ * gst_segment_new:
+ *
+ * Allocate a new #GstSegment structure and initialize it using
+ * gst_segment_init().
+ *
+ * Free-function: gst_segment_free
+ *
+ * Returns: (transfer full): a new #GstSegment, free with gst_segment_free().
+ */
+GstSegment *
+gst_segment_new (void)
+{
+ GstSegment *result;
+
+ result = g_slice_new0 (GstSegment);
+ gst_segment_init (result, GST_FORMAT_UNDEFINED);
+
+ return result;
+}
+
+/**
+ * gst_segment_free:
+ * @segment: (in) (transfer full): a #GstSegment
+ *
+ * Free the allocated segment @segment.
+ */
+void
+gst_segment_free (GstSegment * segment)
+{
+ g_slice_free (GstSegment, segment);
+}
+
+/**
+ * gst_segment_init:
+ * @segment: a #GstSegment structure.
+ * @format: the format of the segment.
+ *
+ * The start/last_stop positions are set to 0 and the stop/duration
+ * fields are set to -1 (unknown). The default rate of 1.0 and no
+ * flags are set.
+ *
+ * Initialize @segment to its default values.
+ */
+void
+gst_segment_init (GstSegment * segment, GstFormat format)
+{
+ g_return_if_fail (segment != NULL);
+
+ segment->flags = GST_SEEK_FLAG_NONE;
+ segment->rate = 1.0;
+ segment->applied_rate = 1.0;
+ segment->format = format;
+ segment->base = 0;
+ segment->start = 0;
+ segment->stop = -1;
+ segment->time = 0;
+ segment->position = 0;
+ segment->duration = -1;
+}
+
+/**
+ * gst_segment_do_seek:
+ * @segment: a #GstSegment structure.
+ * @rate: the rate of the segment.
+ * @format: the format of the segment.
+ * @flags: the segment flags for the segment
+ * @start_type: the seek method
+ * @start: the seek start value
+ * @stop_type: the seek method
+ * @stop: the seek stop value
+ * @update: boolean holding whether position was updated.
+ *
+ * Update the segment structure with the field values of a seek event (see
+ * gst_event_new_seek()).
+ *
+ * After calling this method, the segment field position and time will
+ * contain the requested new position in the segment. The new requested
+ * position in the segment depends on @rate and @start_type and @stop_type.
+ *
+ * For positive @rate, the new position in the segment is the new @segment
+ * start field when it was updated with a @start_type different from
+ * #GST_SEEK_TYPE_NONE. If no update was performed on @segment start position
+ * (#GST_SEEK_TYPE_NONE), @start is ignored and @segment position is
+ * unmodified.
+ *
+ * For negative @rate, the new position in the segment is the new @segment
+ * stop field when it was updated with a @stop_type different from
+ * #GST_SEEK_TYPE_NONE. If no stop was previously configured in the segment, the
+ * duration of the segment will be used to update the stop position.
+ * If no update was performed on @segment stop position (#GST_SEEK_TYPE_NONE),
+ * @stop is ignored and @segment position is unmodified.
+ *
+ * The applied rate of the segment will be set to 1.0 by default.
+ * If the caller can apply a rate change, it should update @segment
+ * rate and applied_rate after calling this function.
+ *
+ * @update will be set to TRUE if a seek should be performed to the segment
+ * position field. This field can be FALSE if, for example, only the @rate
+ * has been changed but not the playback position.
+ *
+ * Returns: %TRUE if the seek could be performed.
+ */
+gboolean
+gst_segment_do_seek (GstSegment * segment, gdouble rate,
+ GstFormat format, GstSeekFlags flags,
+ GstSeekType start_type, guint64 start,
+ GstSeekType stop_type, guint64 stop, gboolean * update)
+{
+ gboolean update_stop, update_start;
+ guint64 position, base;
+
+ g_return_val_if_fail (rate != 0.0, FALSE);
+ g_return_val_if_fail (segment != NULL, FALSE);
+ g_return_val_if_fail (segment->format == format, FALSE);
+
+ update_start = update_stop = TRUE;
+
+ position = segment->position;
+
+ if (flags & GST_SEEK_FLAG_FLUSH) {
+ /* flush resets the running_time */
+ base = 0;
+ } else {
+ base = gst_segment_to_running_time (segment, format, position);
+ }
+
+ /* segment->start is never invalid */
+ switch (start_type) {
+ case GST_SEEK_TYPE_NONE:
+ /* no update to segment, take previous start */
+ start = segment->start;
+ update_start = FALSE;
+ break;
+ case GST_SEEK_TYPE_SET:
+ /* start holds desired position, map -1 to the start */
+ if (start == -1)
+ start = 0;
+ break;
+ case GST_SEEK_TYPE_CUR:
+ {
+ gint64 sstart = (gint64) start;
+ /* add start to currently configured segment */
+ if (sstart > 0 || segment->start > -sstart)
+ start = segment->start + start;
+ else
+ start = 0;
+ break;
+ }
+ case GST_SEEK_TYPE_END:
+ if (segment->duration != -1) {
+ /* add start to total length */
+ start = segment->duration + start;
+ } else {
+ /* no update if duration unknown */
+ start = segment->start;
+ update_start = FALSE;
+ }
+ break;
+ }
+ /* bring in sane range */
+ if (segment->duration != -1)
+ start = MIN (start, segment->duration);
+ else
+ start = MAX (start, 0);
+
+ /* stop can be -1 if we have not configured a stop. */
+ switch (stop_type) {
+ case GST_SEEK_TYPE_NONE:
+ stop = segment->stop;
+ update_stop = FALSE;
+ break;
+ case GST_SEEK_TYPE_SET:
+ /* stop holds required value */
+ break;
+ case GST_SEEK_TYPE_CUR:
+ if (segment->stop != -1) {
+ gint64 sstop = (gint64) stop;
+ if (sstop > 0 || segment->stop > -sstop)
+ stop = segment->stop + stop;
+ else
+ stop = 0;
+ } else
+ stop = -1;
+ break;
+ case GST_SEEK_TYPE_END:
+ if (segment->duration != -1) {
+ stop = segment->duration + stop;
+ } else {
+ stop = segment->stop;
+ update_stop = FALSE;
+ }
+ break;
+ }
+
+ /* if we have a valid stop time, make sure it is clipped */
+ if (stop != -1) {
+ if (segment->duration != -1)
+ stop = CLAMP (stop, 0, segment->duration);
+ else
+ stop = MAX (stop, 0);
+ }
+
+ /* we can't have stop before start */
+ if (stop != -1) {
+ if (start > stop) {
+ g_return_val_if_fail (start <= stop, FALSE);
+ return FALSE;
+ }
+ }
+
+ segment->rate = rate;
+ segment->applied_rate = 1.0;
+ segment->base = base;
+ segment->flags = (GstSegmentFlags) flags;
+ segment->start = start;
+ segment->stop = stop;
+ segment->time = start;
+
+ if (update_start && rate > 0.0) {
+ position = start;
+ }
+ if (update_stop && rate < 0.0) {
+ if (stop != -1)
+ position = stop;
+ else {
+ if (segment->duration != -1)
+ position = segment->duration;
+ else
+ position = 0;
+ }
+ }
+ /* set update arg to reflect update of position */
+ if (update)
+ *update = position != segment->position;
+
+ /* update new position */
+ segment->position = position;
+
+ return TRUE;
+}
+
+/**
+ * gst_segment_to_stream_time:
+ * @segment: a #GstSegment structure.
+ * @format: the format of the segment.
+ * @position: the position in the segment
+ *
+ * Translate @position to stream time using the currently configured
+ * segment. The @position value must be between @segment start and
+ * stop value.
+ *
+ * This function is typically used by elements that need to operate on
+ * the stream time of the buffers it receives, such as effect plugins.
+ * In those use cases, @position is typically the buffer timestamp or
+ * clock time that one wants to convert to the stream time.
+ * The stream time is always between 0 and the total duration of the
+ * media stream.
+ *
+ * Returns: the position in stream_time or -1 when an invalid position
+ * was given.
+ */
+guint64
+gst_segment_to_stream_time (const GstSegment * segment, GstFormat format,
+ guint64 position)
+{
+ guint64 result, start, stop, time;
+ gdouble abs_applied_rate;
+
+ /* format does not matter for -1 */
+ if (G_UNLIKELY (position == -1))
+ return -1;
+
+ g_return_val_if_fail (segment != NULL, -1);
+ g_return_val_if_fail (segment->format == format, -1);
+
+ /* if we have the position for the same format as the segment, we can compare
+ * the start and stop values, otherwise we assume 0 and -1 */
+ if (G_LIKELY (segment->format == format)) {
+ start = segment->start;
+ stop = segment->stop;
+ time = segment->time;
+ } else {
+ start = 0;
+ stop = -1;
+ time = 0;
+ }
+
+ /* outside of the segment boundary stop */
+ if (G_UNLIKELY (stop != -1 && position > stop))
+ return -1;
+
+ /* before the segment boundary */
+ if (G_UNLIKELY (position < start))
+ return -1;
+
+ /* time must be known */
+ if (G_UNLIKELY (time == -1))
+ return -1;
+
+ /* bring to uncorrected position in segment */
+ result = position - start;
+
+ abs_applied_rate = ABS (segment->applied_rate);
+
+ /* correct for applied rate if needed */
+ if (G_UNLIKELY (abs_applied_rate != 1.0))
+ result *= abs_applied_rate;
+
+ /* add or subtract from segment time based on applied rate */
+ if (G_LIKELY (segment->applied_rate > 0.0)) {
+ /* correct for segment time */
+ result += time;
+ } else {
+ /* correct for segment time, clamp at 0. Streams with a negative
+ * applied_rate have timestamps between start and stop, as usual, but have
+ * the time member starting high and going backwards. */
+ if (G_LIKELY (time > result))
+ result = time - result;
+ else
+ result = 0;
+ }
+
+ return result;
+}
+
+/**
+ * gst_segment_to_running_time:
+ * @segment: a #GstSegment structure.
+ * @format: the format of the segment.
+ * @position: the position in the segment
+ *
+ * Translate @position to the total running time using the currently configured
+ * and previously accumulated segments. Position is a value between @segment
+ * start and stop time.
+ *
+ * This function is typically used by elements that need to synchronize to the
+ * global clock in a pipeline. The runnning time is a constantly increasing value
+ * starting from 0. When gst_segment_init() is called, this value will reset to
+ * 0.
+ *
+ * This function returns -1 if the position is outside of @segment start and stop.
+ *
+ * Returns: the position as the total running time or -1 when an invalid position
+ * was given.
+ */
+guint64
+gst_segment_to_running_time (const GstSegment * segment, GstFormat format,
+ guint64 position)
+{
+ guint64 result;
+ guint64 start, stop, base;
+ gdouble abs_rate;
+
+ if (G_UNLIKELY (position == -1))
+ return -1;
+
+ g_return_val_if_fail (segment != NULL, -1);
+ g_return_val_if_fail (segment->format == format, -1);
+
+ /* if we have the position for the same format as the segment, we can compare
+ * the start and stop values, otherwise we assume 0 and -1 */
+ if (G_LIKELY (segment->format == format)) {
+ start = segment->start;
+ stop = segment->stop;
+ base = segment->base;
+ } else {
+ start = 0;
+ stop = -1;
+ base = 0;
+ }
+
+ /* before the segment boundary */
+ if (G_UNLIKELY (position < start))
+ return -1;
+
+ if (G_LIKELY (segment->rate > 0.0)) {
+ /* outside of the segment boundary stop */
+ if (G_UNLIKELY (stop != -1 && position > stop))
+ return -1;
+
+ /* bring to uncorrected position in segment */
+ result = position - start;
+ } else {
+ /* cannot continue if no stop position set or outside of
+ * the segment. */
+ if (G_UNLIKELY (stop == -1 || position > stop))
+ return -1;
+
+ /* bring to uncorrected position in segment */
+ result = stop - position;
+ }
+
+ /* scale based on the rate, avoid division by and conversion to
+ * float when not needed */
+ abs_rate = ABS (segment->rate);
+ if (G_UNLIKELY (abs_rate != 1.0))
+ result /= abs_rate;
+
+ /* correct for base of the segment */
+ result += base;
+
+ return result;
+}
+
+/**
+ * gst_segment_clip:
+ * @segment: a #GstSegment structure.
+ * @format: the format of the segment.
+ * @start: the start position in the segment
+ * @stop: the stop position in the segment
+ * @clip_start: (out) (allow-none): the clipped start position in the segment
+ * @clip_stop: (out) (allow-none): the clipped stop position in the segment
+ *
+ * Clip the given @start and @stop values to the segment boundaries given
+ * in @segment. @start and @stop are compared and clipped to @segment
+ * start and stop values.
+ *
+ * If the function returns FALSE, @start and @stop are known to fall
+ * outside of @segment and @clip_start and @clip_stop are not updated.
+ *
+ * When the function returns TRUE, @clip_start and @clip_stop will be
+ * updated. If @clip_start or @clip_stop are different from @start or @stop
+ * respectively, the region fell partially in the segment.
+ *
+ * Note that when @stop is -1, @clip_stop will be set to the end of the
+ * segment. Depending on the use case, this may or may not be what you want.
+ *
+ * Returns: TRUE if the given @start and @stop times fall partially or
+ * completely in @segment, FALSE if the values are completely outside
+ * of the segment.
+ */
+gboolean
+gst_segment_clip (const GstSegment * segment, GstFormat format, guint64 start,
+ guint64 stop, guint64 * clip_start, guint64 * clip_stop)
+{
+ g_return_val_if_fail (segment != NULL, FALSE);
+ g_return_val_if_fail (segment->format == format, FALSE);
+
+ /* if we have a stop position and a valid start and start is bigger,
+ * we're outside of the segment */
+ if (G_UNLIKELY (segment->stop != -1 && start != -1 && start >= segment->stop))
+ return FALSE;
+
+ /* if a stop position is given and is before the segment start,
+ * we're outside of the segment. Special case is were start
+ * and stop are equal to the segment start. In that case we
+ * are inside the segment. */
+ if (G_UNLIKELY (stop != -1 && (stop < segment->start || (start != stop
+ && stop == segment->start))))
+ return FALSE;
+
+ if (clip_start) {
+ if (start == -1)
+ *clip_start = -1;
+ else
+ *clip_start = MAX (start, segment->start);
+ }
+
+ if (clip_stop) {
+ if (stop == -1)
+ *clip_stop = segment->stop;
+ else if (segment->stop == -1)
+ *clip_stop = stop;
+ else
+ *clip_stop = MIN (stop, segment->stop);
+
+ if (segment->duration != -1 && *clip_stop != -1)
+ *clip_stop = MIN (*clip_stop, segment->duration);
+ }
+
+ return TRUE;
+}
+
+/**
+ * gst_segment_to_position:
+ * @segment: a #GstSegment structure.
+ * @format: the format of the segment.
+ * @running_time: the running_time in the segment
+ *
+ * Convert @running_time into a position in the segment so that
+ * gst_segment_to_running_time() with that position returns @running_time.
+ *
+ * Returns: the position in the segment for @running_time. This function returns
+ * -1 when @running_time is -1 or when it is not inside @segment.
+ *
+ * Since: 0.10.24
+ */
+guint64
+gst_segment_to_position (const GstSegment * segment, GstFormat format,
+ guint64 running_time)
+{
+ guint64 result;
+ guint64 start, stop, base;
+ gdouble abs_rate;
+
+ if (G_UNLIKELY (running_time == -1))
+ return -1;
+
+ g_return_val_if_fail (segment != NULL, -1);
+ g_return_val_if_fail (segment->format == format, FALSE);
+
+ /* if we have the position for the same format as the segment, we can compare
+ * the start and stop values, otherwise we assume 0 and -1 */
+ if (G_LIKELY (segment->format == format)) {
+ start = segment->start;
+ stop = segment->stop;
+ base = segment->base;
+ } else {
+ start = 0;
+ stop = -1;
+ base = 0;
+ }
+
+ /* this running_time was for a previous segment */
+ if (running_time < base)
+ return -1;
+
+ /* start by subtracting the base time */
+ result = running_time - base;
+
+ /* move into the segment at the right rate */
+ abs_rate = ABS (segment->rate);
+ if (G_UNLIKELY (abs_rate != 1.0))
+ result = ceil (result * abs_rate);
+
+ if (G_LIKELY (segment->rate > 0.0)) {
+ /* bring to corrected position in segment */
+ result += start;
+
+ /* outside of the segment boundary stop */
+ if (G_UNLIKELY (stop != -1 && result > stop))
+ return -1;
+ } else {
+ /* cannot continue if no stop position set or outside of
+ * the segment. */
+ if (G_UNLIKELY (stop == -1 || result + start > stop))
+ return -1;
+
+ /* bring to corrected position in segment */
+ result = stop - result;
+ }
+ return result;
+}
+
+
+/**
+ * gst_segment_set_running_time:
+ * @segment: a #GstSegment structure.
+ * @format: the format of the segment.
+ * @running_time: the running_time in the segment
+ *
+ * Adjust the start/stop and base values of @segment such that the next valid
+ * buffer will be one with @running_time.
+ *
+ * Returns: %TRUE if the segment could be updated successfully. If %FALSE is
+ * returned, @running_time is -1 or not in @segment.
+ *
+ * Since: 0.10.24
+ */
+gboolean
+gst_segment_set_running_time (GstSegment * segment, GstFormat format,
+ guint64 running_time)
+{
+ guint64 position;
+ guint64 start, stop;
+
+ /* start by bringing the running_time into the segment position */
+ position = gst_segment_to_position (segment, format, running_time);
+
+ /* we must have a valid position now */
+ if (G_UNLIKELY (position == -1))
+ return FALSE;
+
+ start = segment->start;
+ stop = segment->stop;
+
+ if (G_LIKELY (segment->rate > 0.0)) {
+ /* update the start and time values */
+ start = position;
+ } else {
+ /* reverse, update stop */
+ stop = position;
+ }
+ /* and base time is exactly the running time */
+ segment->time = gst_segment_to_stream_time (segment, format, start);
+ segment->start = start;
+ segment->stop = stop;
+ segment->base = running_time;
+
+ return TRUE;
+}
diff --git a/gst/gstsegment.h b/gst/gstsegment.h
new file mode 100644
index 0000000..db5e1ad
--- /dev/null
+++ b/gst/gstsegment.h
@@ -0,0 +1,178 @@
+/* GStreamer
+ * Copyright (C) 2005 Wim Taymans <wim@fluendo.com>
+ *
+ * gstsegment.h: Header for GstSegment subsystem
+ *
+ * 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_SEGMENT_H__
+#define __GST_SEGMENT_H__
+
+#include <gst/gstformat.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_SEGMENT (gst_segment_get_type())
+
+typedef struct _GstSegment GstSegment;
+
+/**
+ * GstSeekType:
+ * @GST_SEEK_TYPE_NONE: no change in position is required
+ * @GST_SEEK_TYPE_CUR: change relative to currently configured segment. This
+ * can't be used to seek relative to the current playback position - do a
+ * position query, calculate the desired position and then do an absolute
+ * position seek instead if that's what you want to do.
+ * @GST_SEEK_TYPE_SET: absolute position is requested
+ * @GST_SEEK_TYPE_END: relative position to duration is requested
+ *
+ * The different types of seek events. When constructing a seek event with
+ * gst_event_new_seek() or when doing gst_segment_do_seek ().
+ */
+typedef enum {
+ /* one of these */
+ GST_SEEK_TYPE_NONE = 0,
+ GST_SEEK_TYPE_CUR = 1,
+ GST_SEEK_TYPE_SET = 2,
+ GST_SEEK_TYPE_END = 3
+} GstSeekType;
+
+/**
+ * GstSeekFlags:
+ * @GST_SEEK_FLAG_NONE: no flag
+ * @GST_SEEK_FLAG_FLUSH: flush pipeline
+ * @GST_SEEK_FLAG_ACCURATE: accurate position is requested, this might
+ * be considerably slower for some formats.
+ * @GST_SEEK_FLAG_KEY_UNIT: seek to the nearest keyframe. This might be
+ * faster but less accurate.
+ * @GST_SEEK_FLAG_SEGMENT: perform a segment seek.
+ * @GST_SEEK_FLAG_SKIP: when doing fast foward or fast reverse playback, allow
+ * elements to skip frames instead of generating all
+ * frames. Since 0.10.22.
+ *
+ * Flags to be used with gst_element_seek() or gst_event_new_seek(). All flags
+ * can be used together.
+ *
+ * A non flushing seek might take some time to perform as the currently
+ * playing data in the pipeline will not be cleared.
+ *
+ * An accurate seek might be slower for formats that don't have any indexes
+ * or timestamp markers in the stream. Specifying this flag might require a
+ * complete scan of the file in those cases.
+ *
+ * When performing a segment seek: after the playback of the segment completes,
+ * no EOS will be emmited by the element that performed the seek, but a
+ * #GST_MESSAGE_SEGMENT_DONE message will be posted on the bus by the element.
+ * When this message is posted, it is possible to send a new seek event to
+ * continue playback. With this seek method it is possible to perform seamless
+ * looping or simple linear editing.
+ *
+ * When doing fast forward (rate > 1.0) or fast reverse (rate < -1.0) trickmode
+ * playback, the @GST_SEEK_FLAG_SKIP flag can be used to instruct decoders
+ * and demuxers to adjust the playback rate by skipping frames. This can improve
+ * performance and decrease CPU usage because not all frames need to be decoded.
+ *
+ * Also see part-seeking.txt in the GStreamer design documentation for more
+ * details on the meaning of these flags and the behaviour expected of
+ * elements that handle them.
+ */
+typedef enum {
+ GST_SEEK_FLAG_NONE = 0,
+ GST_SEEK_FLAG_FLUSH = (1 << 0),
+ GST_SEEK_FLAG_ACCURATE = (1 << 1),
+ GST_SEEK_FLAG_KEY_UNIT = (1 << 2),
+ GST_SEEK_FLAG_SEGMENT = (1 << 3),
+ GST_SEEK_FLAG_SKIP = (1 << 4)
+} GstSeekFlags;
+
+/**
+ * GstSegmentFlags:
+ * @GST_SEGMENT_FLAG_NONE: no flags
+ * @GST_SEGMENT_FLAG_RESET: reset the pipeline running_time to the segment
+ * running_time
+ * @GST_SEGMENT_FLAG_SKIP: perform skip playback
+ *
+ * Flags for the GstSegment structure. Currently mapped to the corresponding
+ * values of the seek flags.
+ */
+typedef enum {
+ GST_SEGMENT_FLAG_NONE = GST_SEEK_FLAG_NONE,
+ GST_SEGMENT_FLAG_RESET = GST_SEEK_FLAG_FLUSH,
+ GST_SEGMENT_FLAG_SKIP = GST_SEEK_FLAG_SKIP
+} GstSegmentFlags;
+
+/**
+ * GstSegment:
+ * @flags: flags for this segment
+ * @rate: the rate of the segment
+ * @applied_rate: the already applied rate to the segment
+ * @format: the format of the segment values
+ * @base: the base time of the segment
+ * @start: the start of the segment
+ * @stop: the stop of the segment
+ * @time: the stream time of the segment
+ * @position: the position in the segment
+ * @duration: the duration of the segment
+ *
+ * A helper structure that holds the configured region of
+ * interest in a media file.
+ */
+struct _GstSegment {
+ /*< public >*/
+ GstSegmentFlags flags;
+
+ gdouble rate;
+ gdouble applied_rate;
+
+ GstFormat format;
+ guint64 base;
+ guint64 start;
+ guint64 stop;
+ guint64 time;
+
+ guint64 position;
+ guint64 duration;
+};
+
+GType gst_segment_get_type (void);
+
+GstSegment * gst_segment_new (void);
+GstSegment * gst_segment_copy (const GstSegment *segment);
+void gst_segment_copy_into (const GstSegment *src, GstSegment *dest);
+void gst_segment_free (GstSegment *segment);
+
+void gst_segment_init (GstSegment *segment, GstFormat format);
+
+guint64 gst_segment_to_stream_time (const GstSegment *segment, GstFormat format, guint64 position);
+guint64 gst_segment_to_running_time (const GstSegment *segment, GstFormat format, guint64 position);
+guint64 gst_segment_to_position (const GstSegment *segment, GstFormat format, guint64 running_time);
+
+gboolean gst_segment_set_running_time (GstSegment *segment, GstFormat format, guint64 running_time);
+
+gboolean gst_segment_clip (const GstSegment *segment, GstFormat format, guint64 start,
+ guint64 stop, guint64 *clip_start, guint64 *clip_stop);
+
+
+gboolean gst_segment_do_seek (GstSegment * segment, gdouble rate,
+ GstFormat format, GstSeekFlags flags,
+ GstSeekType start_type, guint64 start,
+ GstSeekType stop_type, guint64 stop, gboolean * update);
+
+G_END_DECLS
+
+#endif /* __GST_SEGMENT_H__ */
diff --git a/gst/gststructure.c b/gst/gststructure.c
new file mode 100644
index 0000000..ca16622
--- /dev/null
+++ b/gst/gststructure.c
@@ -0,0 +1,3205 @@
+/* GStreamer
+ * Copyright (C) 2003 David A. Schleef <ds@schleef.org>
+ *
+ * gststructure.c: lists of { GQuark, GValue } tuples
+ *
+ * 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:gststructure
+ * @short_description: Generic structure containing fields of names and values
+ * @see_also: #GstCaps, #GstMessage, #GstEvent, #GstQuery
+ *
+ * A #GstStructure is a collection of key/value pairs. The keys are expressed
+ * as GQuarks and the values can be of any GType.
+ *
+ * In addition to the key/value pairs, a #GstStructure also has a name. The name
+ * starts with a letter and can be folled by letters, numbers and any of "/-_.:".
+ *
+ * #GstStructure is used by various GStreamer subsystems to store information
+ * in a flexible and extensible way. A #GstStructure does not have a refcount
+ * because it usually is part of a higher level object such as #GstCaps. It
+ * provides a means to enforce mutability using the refcount of the parent
+ * with the gst_structure_set_parent_refcount() method.
+ *
+ * A #GstStructure can be created with gst_structure_empty_new() or
+ * gst_structure_new(), which both take a name and an optional set of
+ * key/value pairs along with the types of the values.
+ *
+ * Field values can be changed with gst_structure_set_value() or
+ * gst_structure_set().
+ *
+ * Field values can be retrieved with gst_structure_get_value() or the more
+ * convenient gst_structure_get_*() functions.
+ *
+ * Fields can be removed with gst_structure_remove_field() or
+ * gst_structure_remove_fields().
+ *
+ * Strings in structures must be ASCII or UTF-8 encoded. Other encodings are
+ * not allowed. Strings must not be empty either, but may be NULL.
+ *
+ * Last reviewed on 2009-06-08 (0.10.23)
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+
+#include "gst_private.h"
+#include "gstquark.h"
+#include <gst/gst.h>
+#include <gobject/gvaluecollector.h>
+
+typedef struct _GstStructureField GstStructureField;
+
+struct _GstStructureField
+{
+ GQuark name;
+ GValue value;
+};
+
+typedef struct
+{
+ GstStructure s;
+
+ /* owned by parent structure, NULL if no parent */
+ gint *parent_refcount;
+
+ GArray *fields;
+} GstStructureImpl;
+
+#define GST_STRUCTURE_REFCOUNT(s) (((GstStructureImpl*)(s))->parent_refcount)
+#define GST_STRUCTURE_FIELDS(s) (((GstStructureImpl*)(s))->fields)
+
+#define GST_STRUCTURE_FIELD(structure, index) \
+ &g_array_index(GST_STRUCTURE_FIELDS(structure), GstStructureField, (index))
+
+#define IS_MUTABLE(structure) \
+ (!GST_STRUCTURE_REFCOUNT(structure) || \
+ g_atomic_int_get (GST_STRUCTURE_REFCOUNT(structure)) == 1)
+
+#define IS_TAGLIST(structure) \
+ (structure->name == GST_QUARK (TAGLIST))
+
+static void gst_structure_set_field (GstStructure * structure,
+ GstStructureField * field);
+static GstStructureField *gst_structure_get_field (const GstStructure *
+ structure, const gchar * fieldname);
+static GstStructureField *gst_structure_id_get_field (const GstStructure *
+ structure, GQuark field);
+static void gst_structure_transform_to_string (const GValue * src_value,
+ GValue * dest_value);
+static GstStructure *gst_structure_copy_conditional (const GstStructure *
+ structure);
+static gboolean gst_structure_parse_value (gchar * str, gchar ** after,
+ GValue * value, GType default_type);
+static gboolean gst_structure_parse_simple_string (gchar * s, gchar ** end);
+
+GType _gst_structure_type = 0;
+
+void
+_priv_gst_structure_initialize (void)
+{
+ _gst_structure_type = g_boxed_type_register_static ("GstStructure",
+ (GBoxedCopyFunc) gst_structure_copy_conditional,
+ (GBoxedFreeFunc) gst_structure_free);
+
+ g_value_register_transform_func (_gst_structure_type, G_TYPE_STRING,
+ gst_structure_transform_to_string);
+}
+
+static GstStructure *
+gst_structure_id_empty_new_with_size (GQuark quark, guint prealloc)
+{
+ GstStructureImpl *structure;
+
+ structure = g_slice_new (GstStructureImpl);
+ ((GstStructure *) structure)->type = _gst_structure_type;
+ ((GstStructure *) structure)->name = quark;
+ GST_STRUCTURE_REFCOUNT (structure) = NULL;
+ GST_STRUCTURE_FIELDS (structure) =
+ g_array_sized_new (FALSE, FALSE, sizeof (GstStructureField), prealloc);
+
+ return GST_STRUCTURE_CAST (structure);
+}
+
+/**
+ * gst_structure_id_empty_new:
+ * @quark: name of new structure
+ *
+ * Creates a new, empty #GstStructure with the given name as a GQuark.
+ *
+ * Free-function: gst_structure_free
+ *
+ * Returns: (transfer full): a new, empty #GstStructure
+ */
+GstStructure *
+gst_structure_id_empty_new (GQuark quark)
+{
+ g_return_val_if_fail (quark != 0, NULL);
+
+ return gst_structure_id_empty_new_with_size (quark, 0);
+}
+
+#ifndef G_DISABLE_CHECKS
+static gboolean
+gst_structure_validate_name (const gchar * name)
+{
+ const gchar *s;
+
+ g_return_val_if_fail (name != NULL, FALSE);
+
+ if (G_UNLIKELY (!g_ascii_isalpha (*name))) {
+ GST_WARNING ("Invalid character '%c' at offset 0 in structure name: %s",
+ *name, name);
+ return FALSE;
+ }
+
+ /* FIXME: test name string more */
+ s = &name[1];
+ while (*s && (g_ascii_isalnum (*s) || strchr ("/-_.:+", *s) != NULL))
+ s++;
+ if (G_UNLIKELY (*s != '\0')) {
+ GST_WARNING ("Invalid character '%c' at offset %" G_GUINTPTR_FORMAT " in"
+ " structure name: %s", *s, ((guintptr) s - (guintptr) name), name);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+#endif
+
+/**
+ * gst_structure_empty_new:
+ * @name: name of new structure
+ *
+ * Creates a new, empty #GstStructure with the given @name.
+ *
+ * See gst_structure_set_name() for constraints on the @name parameter.
+ *
+ * Free-function: gst_structure_free
+ *
+ * Returns: (transfer full): a new, empty #GstStructure
+ */
+GstStructure *
+gst_structure_empty_new (const gchar * name)
+{
+ g_return_val_if_fail (gst_structure_validate_name (name), NULL);
+
+ return gst_structure_id_empty_new_with_size (g_quark_from_string (name), 0);
+}
+
+/**
+ * gst_structure_new:
+ * @name: name of new structure
+ * @firstfield: name of first field to set
+ * @...: additional arguments
+ *
+ * Creates a new #GstStructure with the given name. Parses the
+ * list of variable arguments and sets fields to the values listed.
+ * Variable arguments should be passed as field name, field type,
+ * and value. Last variable argument should be NULL.
+ *
+ * Free-function: gst_structure_free
+ *
+ * Returns: (transfer full): a new #GstStructure
+ */
+GstStructure *
+gst_structure_new (const gchar * name, const gchar * firstfield, ...)
+{
+ GstStructure *structure;
+ va_list varargs;
+
+ va_start (varargs, firstfield);
+ structure = gst_structure_new_valist (name, firstfield, varargs);
+ va_end (varargs);
+
+ return structure;
+}
+
+/**
+ * gst_structure_new_valist:
+ * @name: name of new structure
+ * @firstfield: name of first field to set
+ * @varargs: variable argument list
+ *
+ * Creates a new #GstStructure with the given @name. Structure fields
+ * are set according to the varargs in a manner similar to
+ * gst_structure_new().
+ *
+ * See gst_structure_set_name() for constraints on the @name parameter.
+ *
+ * Free-function: gst_structure_free
+ *
+ * Returns: (transfer full): a new #GstStructure
+ */
+GstStructure *
+gst_structure_new_valist (const gchar * name,
+ const gchar * firstfield, va_list varargs)
+{
+ GstStructure *structure;
+
+ structure = gst_structure_empty_new (name);
+
+ if (structure)
+ gst_structure_set_valist (structure, firstfield, varargs);
+
+ return structure;
+}
+
+/**
+ * gst_structure_set_parent_refcount:
+ * @structure: a #GstStructure
+ * @refcount: (in): a pointer to the parent's refcount
+ *
+ * Sets the parent_refcount field of #GstStructure. This field is used to
+ * determine whether a structure is mutable or not. This function should only be
+ * called by code implementing parent objects of #GstStructure, as described in
+ * the MT Refcounting section of the design documents.
+ *
+ * Returns: %TRUE if the parent refcount could be set.
+ */
+gboolean
+gst_structure_set_parent_refcount (GstStructure * structure, gint * refcount)
+{
+ g_return_val_if_fail (structure != NULL, FALSE);
+
+ /* if we have a parent_refcount already, we can only clear
+ * if with a NULL refcount */
+ if (GST_STRUCTURE_REFCOUNT (structure)) {
+ if (refcount != NULL) {
+ g_return_val_if_fail (refcount == NULL, FALSE);
+ return FALSE;
+ }
+ } else {
+ if (refcount == NULL) {
+ g_return_val_if_fail (refcount != NULL, FALSE);
+ return FALSE;
+ }
+ }
+
+ GST_STRUCTURE_REFCOUNT (structure) = refcount;
+
+ return TRUE;
+}
+
+/**
+ * gst_structure_copy:
+ * @structure: a #GstStructure to duplicate
+ *
+ * Duplicates a #GstStructure and all its fields and values.
+ *
+ * Free-function: gst_structure_free
+ *
+ * Returns: (transfer none): a new #GstStructure.
+ */
+GstStructure *
+gst_structure_copy (const GstStructure * structure)
+{
+ GstStructure *new_structure;
+ GstStructureField *field;
+ guint i, len;
+
+ g_return_val_if_fail (structure != NULL, NULL);
+
+ len = GST_STRUCTURE_FIELDS (structure)->len;
+ new_structure = gst_structure_id_empty_new_with_size (structure->name, len);
+
+ for (i = 0; i < len; i++) {
+ GstStructureField new_field = { 0 };
+
+ field = GST_STRUCTURE_FIELD (structure, i);
+
+ new_field.name = field->name;
+ gst_value_init_and_copy (&new_field.value, &field->value);
+ g_array_append_val (GST_STRUCTURE_FIELDS (new_structure), new_field);
+ }
+
+ return new_structure;
+}
+
+/**
+ * gst_structure_free:
+ * @structure: (in) (transfer full): the #GstStructure to free
+ *
+ * Frees a #GstStructure and all its fields and values. The structure must not
+ * have a parent when this function is called.
+ */
+void
+gst_structure_free (GstStructure * structure)
+{
+ GstStructureField *field;
+ guint i, len;
+
+ g_return_if_fail (structure != NULL);
+ g_return_if_fail (GST_STRUCTURE_REFCOUNT (structure) == NULL);
+
+ len = GST_STRUCTURE_FIELDS (structure)->len;
+ for (i = 0; i < len; i++) {
+ field = GST_STRUCTURE_FIELD (structure, i);
+
+ if (G_IS_VALUE (&field->value)) {
+ g_value_unset (&field->value);
+ }
+ }
+ g_array_free (GST_STRUCTURE_FIELDS (structure), TRUE);
+#ifdef USE_POISONING
+ memset (structure, 0xff, sizeof (GstStructure));
+#endif
+ g_slice_free1 (sizeof (GstStructureImpl), structure);
+}
+
+/**
+ * gst_structure_get_name:
+ * @structure: a #GstStructure
+ *
+ * Get the name of @structure as a string.
+ *
+ * Returns: the name of the structure.
+ */
+const gchar *
+gst_structure_get_name (const GstStructure * structure)
+{
+ g_return_val_if_fail (structure != NULL, NULL);
+
+ return g_quark_to_string (structure->name);
+}
+
+/**
+ * gst_structure_has_name:
+ * @structure: a #GstStructure
+ * @name: structure name to check for
+ *
+ * Checks if the structure has the given name
+ *
+ * Returns: TRUE if @name matches the name of the structure.
+ */
+gboolean
+gst_structure_has_name (const GstStructure * structure, const gchar * name)
+{
+ const gchar *structure_name;
+
+ g_return_val_if_fail (structure != NULL, FALSE);
+ g_return_val_if_fail (name != NULL, FALSE);
+
+ /* getting the string is cheap and comparing short strings is too
+ * should be faster than getting the quark for name and comparing the quarks
+ */
+ structure_name = g_quark_to_string (structure->name);
+
+ return (structure_name && strcmp (structure_name, name) == 0);
+}
+
+/**
+ * gst_structure_get_name_id:
+ * @structure: a #GstStructure
+ *
+ * Get the name of @structure as a GQuark.
+ *
+ * Returns: the quark representing the name of the structure.
+ */
+GQuark
+gst_structure_get_name_id (const GstStructure * structure)
+{
+ g_return_val_if_fail (structure != NULL, 0);
+
+ return structure->name;
+}
+
+/**
+ * gst_structure_set_name:
+ * @structure: a #GstStructure
+ * @name: the new name of the structure
+ *
+ * Sets the name of the structure to the given @name. The string
+ * provided is copied before being used. It must not be empty, start with a
+ * letter and can be followed by letters, numbers and any of "/-_.:".
+ */
+void
+gst_structure_set_name (GstStructure * structure, const gchar * name)
+{
+ g_return_if_fail (structure != NULL);
+ g_return_if_fail (IS_MUTABLE (structure));
+ g_return_if_fail (gst_structure_validate_name (name));
+
+ structure->name = g_quark_from_string (name);
+}
+
+static inline void
+gst_structure_id_set_value_internal (GstStructure * structure, GQuark field,
+ const GValue * value)
+{
+ GstStructureField gsfield = { 0, {0,} };
+
+ gsfield.name = field;
+ gst_value_init_and_copy (&gsfield.value, value);
+
+ gst_structure_set_field (structure, &gsfield);
+}
+
+/**
+ * gst_structure_id_set_value:
+ * @structure: a #GstStructure
+ * @field: a #GQuark representing a field
+ * @value: the new value of the field
+ *
+ * Sets the field with the given GQuark @field to @value. If the field
+ * does not exist, it is created. If the field exists, the previous
+ * value is replaced and freed.
+ */
+void
+gst_structure_id_set_value (GstStructure * structure,
+ GQuark field, const GValue * value)
+{
+
+ g_return_if_fail (structure != NULL);
+ g_return_if_fail (G_IS_VALUE (value));
+ g_return_if_fail (IS_MUTABLE (structure));
+
+ gst_structure_id_set_value_internal (structure, field, value);
+}
+
+/**
+ * gst_structure_set_value:
+ * @structure: a #GstStructure
+ * @fieldname: the name of the field to set
+ * @value: the new value of the field
+ *
+ * Sets the field with the given name @field to @value. If the field
+ * does not exist, it is created. If the field exists, the previous
+ * value is replaced and freed.
+ */
+void
+gst_structure_set_value (GstStructure * structure,
+ const gchar * fieldname, const GValue * value)
+{
+ g_return_if_fail (structure != NULL);
+ g_return_if_fail (fieldname != NULL);
+ g_return_if_fail (G_IS_VALUE (value));
+ g_return_if_fail (IS_MUTABLE (structure));
+
+ gst_structure_id_set_value_internal (structure,
+ g_quark_from_string (fieldname), value);
+}
+
+static inline void
+gst_structure_id_take_value_internal (GstStructure * structure, GQuark field,
+ GValue * value)
+{
+ GstStructureField gsfield = { 0, {0,} };
+
+ gsfield.name = field;
+ gsfield.value = *value;
+
+ gst_structure_set_field (structure, &gsfield);
+
+ /* we took ownership */
+#ifdef USE_POISONING
+ memset (value, 0, sizeof (GValue));
+#else
+ value->g_type = G_TYPE_INVALID;
+#endif
+}
+
+/**
+ * gst_structure_id_take_value:
+ * @structure: a #GstStructure
+ * @field: a #GQuark representing a field
+ * @value: (transfer full): the new value of the field
+ *
+ * Sets the field with the given GQuark @field to @value. If the field
+ * does not exist, it is created. If the field exists, the previous
+ * value is replaced and freed.
+ *
+ * Since: 0.10.31
+ */
+void
+gst_structure_id_take_value (GstStructure * structure, GQuark field,
+ GValue * value)
+{
+ g_return_if_fail (structure != NULL);
+ g_return_if_fail (G_IS_VALUE (value));
+ g_return_if_fail (IS_MUTABLE (structure));
+
+ gst_structure_id_take_value_internal (structure, field, value);
+}
+
+/**
+ * gst_structure_take_value:
+ * @structure: a #GstStructure
+ * @fieldname: the name of the field to set
+ * @value: (transfer full): the new value of the field
+ *
+ * Sets the field with the given name @field to @value. If the field
+ * does not exist, it is created. If the field exists, the previous
+ * value is replaced and freed. The function will take ownership of @value.
+ *
+ * Since: 0.10.31
+ */
+void
+gst_structure_take_value (GstStructure * structure, const gchar * fieldname,
+ GValue * value)
+{
+ g_return_if_fail (structure != NULL);
+ g_return_if_fail (fieldname != NULL);
+ g_return_if_fail (G_IS_VALUE (value));
+ g_return_if_fail (IS_MUTABLE (structure));
+
+ gst_structure_id_take_value_internal (structure,
+ g_quark_from_string (fieldname), value);
+}
+
+static void
+gst_structure_set_valist_internal (GstStructure * structure,
+ const gchar * fieldname, va_list varargs)
+{
+ gchar *err = NULL;
+ GType type;
+
+ while (fieldname) {
+ GstStructureField field = { 0 };
+
+ field.name = g_quark_from_string (fieldname);
+
+ type = va_arg (varargs, GType);
+
+ if (G_UNLIKELY (type == G_TYPE_DATE)) {
+ g_warning ("Don't use G_TYPE_DATE, use GST_TYPE_DATE instead\n");
+ type = GST_TYPE_DATE;
+ }
+ G_VALUE_COLLECT_INIT (&field.value, type, varargs, 0, &err);
+ if (G_UNLIKELY (err)) {
+ g_critical ("%s", err);
+ return;
+ }
+ gst_structure_set_field (structure, &field);
+
+ fieldname = va_arg (varargs, gchar *);
+ }
+}
+
+/**
+ * gst_structure_set:
+ * @structure: a #GstStructure
+ * @fieldname: the name of the field to set
+ * @...: variable arguments
+ *
+ * Parses the variable arguments and sets fields accordingly.
+ * Variable arguments should be in the form field name, field type
+ * (as a GType), value(s). The last variable argument should be NULL.
+ */
+void
+gst_structure_set (GstStructure * structure, const gchar * field, ...)
+{
+ va_list varargs;
+
+ g_return_if_fail (structure != NULL);
+ g_return_if_fail (IS_MUTABLE (structure) || field == NULL);
+
+ va_start (varargs, field);
+ gst_structure_set_valist_internal (structure, field, varargs);
+ va_end (varargs);
+}
+
+/**
+ * gst_structure_set_valist:
+ * @structure: a #GstStructure
+ * @fieldname: the name of the field to set
+ * @varargs: variable arguments
+ *
+ * va_list form of gst_structure_set().
+ */
+void
+gst_structure_set_valist (GstStructure * structure,
+ const gchar * fieldname, va_list varargs)
+{
+ g_return_if_fail (structure != NULL);
+ g_return_if_fail (IS_MUTABLE (structure));
+
+ gst_structure_set_valist_internal (structure, fieldname, varargs);
+}
+
+static void
+gst_structure_id_set_valist_internal (GstStructure * structure,
+ GQuark fieldname, va_list varargs)
+{
+ gchar *err = NULL;
+ GType type;
+
+ while (fieldname) {
+ GstStructureField field = { 0 };
+
+ field.name = fieldname;
+
+ type = va_arg (varargs, GType);
+
+ if (G_UNLIKELY (type == G_TYPE_DATE)) {
+ g_warning ("Don't use G_TYPE_DATE, use GST_TYPE_DATE instead\n");
+ type = GST_TYPE_DATE;
+ }
+#ifndef G_VALUE_COLLECT_INIT
+ g_value_init (&field.value, type);
+ G_VALUE_COLLECT (&field.value, varargs, 0, &err);
+#else
+ G_VALUE_COLLECT_INIT (&field.value, type, varargs, 0, &err);
+#endif
+ if (G_UNLIKELY (err)) {
+ g_critical ("%s", err);
+ return;
+ }
+ gst_structure_set_field (structure, &field);
+
+ fieldname = va_arg (varargs, GQuark);
+ }
+}
+
+/**
+ * gst_structure_id_set:
+ * @structure: a #GstStructure
+ * @fieldname: the GQuark for the name of the field to set
+ * @...: variable arguments
+ *
+ * Identical to gst_structure_set, except that field names are
+ * passed using the GQuark for the field name. This allows more efficient
+ * setting of the structure if the caller already knows the associated
+ * quark values.
+ * The last variable argument must be NULL.
+ *
+ * Since: 0.10.10
+ */
+void
+gst_structure_id_set (GstStructure * structure, GQuark field, ...)
+{
+ va_list varargs;
+
+ g_return_if_fail (structure != NULL);
+
+ va_start (varargs, field);
+ gst_structure_id_set_valist_internal (structure, field, varargs);
+ va_end (varargs);
+}
+
+/**
+ * gst_structure_id_set_valist:
+ * @structure: a #GstStructure
+ * @fieldname: the name of the field to set
+ * @varargs: variable arguments
+ *
+ * va_list form of gst_structure_id_set().
+ *
+ * Since: 0.10.10
+ */
+void
+gst_structure_id_set_valist (GstStructure * structure,
+ GQuark fieldname, va_list varargs)
+{
+ g_return_if_fail (structure != NULL);
+ g_return_if_fail (IS_MUTABLE (structure));
+
+ gst_structure_id_set_valist_internal (structure, fieldname, varargs);
+}
+
+/**
+ * gst_structure_id_new:
+ * @name_quark: name of new structure
+ * @field_quark: the GQuark for the name of the field to set
+ * @...: variable arguments
+ *
+ * Creates a new #GstStructure with the given name as a GQuark, followed by
+ * fieldname quark, GType, argument(s) "triplets" in the same format as
+ * gst_structure_id_set(). Basically a convenience wrapper around
+ * gst_structure_id_empty_new() and gst_structure_id_set().
+ *
+ * The last variable argument must be NULL (or 0).
+ *
+ * Free-function: gst_structure_free
+ *
+ * Returns: (transfer full): a new #GstStructure
+ *
+ * Since: 0.10.24
+ */
+GstStructure *
+gst_structure_id_new (GQuark name_quark, GQuark field_quark, ...)
+{
+ GstStructure *s;
+ va_list varargs;
+
+ g_return_val_if_fail (name_quark != 0, NULL);
+ g_return_val_if_fail (field_quark != 0, NULL);
+
+ s = gst_structure_id_empty_new (name_quark);
+
+ va_start (varargs, field_quark);
+ gst_structure_id_set_valist_internal (s, field_quark, varargs);
+ va_end (varargs);
+
+ return s;
+}
+
+#if GST_VERSION_NANO == 1
+#define GIT_G_WARNING g_warning
+#else
+#define GIT_G_WARNING GST_WARNING
+#endif
+
+/* If the structure currently contains a field with the same name, it is
+ * replaced with the provided field. Otherwise, the field is added to the
+ * structure. The field's value is not deeply copied.
+ */
+static void
+gst_structure_set_field (GstStructure * structure, GstStructureField * field)
+{
+ GstStructureField *f;
+ guint i, len = GST_STRUCTURE_FIELDS (structure)->len;
+
+ if (G_UNLIKELY (G_VALUE_HOLDS_STRING (&field->value))) {
+ const gchar *s;
+
+ s = g_value_get_string (&field->value);
+ /* only check for NULL strings in taglists, as they are allowed in message
+ * structs, e.g. error message debug strings */
+ if (G_UNLIKELY (IS_TAGLIST (structure) && (s == NULL || *s == '\0'))) {
+ if (s == NULL) {
+ GIT_G_WARNING ("Trying to set NULL string on field '%s' on taglist. "
+ "Please file a bug.", g_quark_to_string (field->name));
+ g_value_unset (&field->value);
+ return;
+ } else {
+ /* empty strings never make sense */
+ GIT_G_WARNING ("Trying to set empty string on taglist field '%s'. "
+ "Please file a bug.", g_quark_to_string (field->name));
+ g_value_unset (&field->value);
+ return;
+ }
+ } else if (G_UNLIKELY (s != NULL && !g_utf8_validate (s, -1, NULL))) {
+ g_warning ("Trying to set string on %s field '%s', but string is not "
+ "valid UTF-8. Please file a bug.",
+ IS_TAGLIST (structure) ? "taglist" : "structure",
+ g_quark_to_string (field->name));
+ g_value_unset (&field->value);
+ return;
+ }
+ } else if (G_UNLIKELY (GST_VALUE_HOLDS_DATE (&field->value))) {
+ const GDate *d;
+
+ d = gst_value_get_date (&field->value);
+ /* only check for NULL GDates in taglists, as they might make sense
+ * in other, generic structs */
+ if (G_UNLIKELY ((IS_TAGLIST (structure) && d == NULL))) {
+ GIT_G_WARNING ("Trying to set NULL GDate on field '%s' on taglist. "
+ "Please file a bug.", g_quark_to_string (field->name));
+ g_value_unset (&field->value);
+ return;
+ } else if (G_UNLIKELY (d != NULL && !g_date_valid (d))) {
+ g_warning
+ ("Trying to set invalid GDate on %s field '%s'. Please file a bug.",
+ IS_TAGLIST (structure) ? "taglist" : "structure",
+ g_quark_to_string (field->name));
+ g_value_unset (&field->value);
+ return;
+ }
+ }
+
+ for (i = 0; i < len; i++) {
+ f = GST_STRUCTURE_FIELD (structure, i);
+
+ if (G_UNLIKELY (f->name == field->name)) {
+ g_value_unset (&f->value);
+ memcpy (f, field, sizeof (GstStructureField));
+ return;
+ }
+ }
+
+ g_array_append_val (GST_STRUCTURE_FIELDS (structure), *field);
+}
+
+/* If there is no field with the given ID, NULL is returned.
+ */
+static GstStructureField *
+gst_structure_id_get_field (const GstStructure * structure, GQuark field_id)
+{
+ GstStructureField *field;
+ guint i, len;
+
+ len = GST_STRUCTURE_FIELDS (structure)->len;
+
+ for (i = 0; i < len; i++) {
+ field = GST_STRUCTURE_FIELD (structure, i);
+
+ if (G_UNLIKELY (field->name == field_id))
+ return field;
+ }
+
+ return NULL;
+}
+
+/* If there is no field with the given ID, NULL is returned.
+ */
+static GstStructureField *
+gst_structure_get_field (const GstStructure * structure,
+ const gchar * fieldname)
+{
+ g_return_val_if_fail (structure != NULL, NULL);
+ g_return_val_if_fail (fieldname != NULL, NULL);
+
+ return gst_structure_id_get_field (structure,
+ g_quark_from_string (fieldname));
+}
+
+/**
+ * gst_structure_get_value:
+ * @structure: a #GstStructure
+ * @fieldname: the name of the field to get
+ *
+ * Get the value of the field with name @fieldname.
+ *
+ * Returns: the #GValue corresponding to the field with the given name.
+ */
+const GValue *
+gst_structure_get_value (const GstStructure * structure,
+ const gchar * fieldname)
+{
+ GstStructureField *field;
+
+ g_return_val_if_fail (structure != NULL, NULL);
+ g_return_val_if_fail (fieldname != NULL, NULL);
+
+ field = gst_structure_get_field (structure, fieldname);
+ if (field == NULL)
+ return NULL;
+
+ return &field->value;
+}
+
+/**
+ * gst_structure_id_get_value:
+ * @structure: a #GstStructure
+ * @field: the #GQuark of the field to get
+ *
+ * Get the value of the field with GQuark @field.
+ *
+ * Returns: the #GValue corresponding to the field with the given name
+ * identifier.
+ */
+const GValue *
+gst_structure_id_get_value (const GstStructure * structure, GQuark field)
+{
+ GstStructureField *gsfield;
+
+ g_return_val_if_fail (structure != NULL, NULL);
+
+ gsfield = gst_structure_id_get_field (structure, field);
+ if (gsfield == NULL)
+ return NULL;
+
+ return &gsfield->value;
+}
+
+/**
+ * gst_structure_remove_field:
+ * @structure: a #GstStructure
+ * @fieldname: the name of the field to remove
+ *
+ * Removes the field with the given name. If the field with the given
+ * name does not exist, the structure is unchanged.
+ */
+void
+gst_structure_remove_field (GstStructure * structure, const gchar * fieldname)
+{
+ GstStructureField *field;
+ GQuark id;
+ guint i, len;
+
+ g_return_if_fail (structure != NULL);
+ g_return_if_fail (fieldname != NULL);
+ g_return_if_fail (IS_MUTABLE (structure));
+
+ id = g_quark_from_string (fieldname);
+ len = GST_STRUCTURE_FIELDS (structure)->len;
+
+ for (i = 0; i < len; i++) {
+ field = GST_STRUCTURE_FIELD (structure, i);
+
+ if (field->name == id) {
+ if (G_IS_VALUE (&field->value)) {
+ g_value_unset (&field->value);
+ }
+ GST_STRUCTURE_FIELDS (structure) =
+ g_array_remove_index (GST_STRUCTURE_FIELDS (structure), i);
+ return;
+ }
+ }
+}
+
+/**
+ * gst_structure_remove_fields:
+ * @structure: a #GstStructure
+ * @fieldname: the name of the field to remove
+ * @...: NULL-terminated list of more fieldnames to remove
+ *
+ * Removes the fields with the given names. If a field does not exist, the
+ * argument is ignored.
+ */
+void
+gst_structure_remove_fields (GstStructure * structure,
+ const gchar * fieldname, ...)
+{
+ va_list varargs;
+
+ g_return_if_fail (structure != NULL);
+ g_return_if_fail (fieldname != NULL);
+ /* mutability checked in remove_field */
+
+ va_start (varargs, fieldname);
+ gst_structure_remove_fields_valist (structure, fieldname, varargs);
+ va_end (varargs);
+}
+
+/**
+ * gst_structure_remove_fields_valist:
+ * @structure: a #GstStructure
+ * @fieldname: the name of the field to remove
+ * @varargs: NULL-terminated list of more fieldnames to remove
+ *
+ * va_list form of gst_structure_remove_fields().
+ */
+void
+gst_structure_remove_fields_valist (GstStructure * structure,
+ const gchar * fieldname, va_list varargs)
+{
+ gchar *field = (gchar *) fieldname;
+
+ g_return_if_fail (structure != NULL);
+ g_return_if_fail (fieldname != NULL);
+ /* mutability checked in remove_field */
+
+ while (field) {
+ gst_structure_remove_field (structure, field);
+ field = va_arg (varargs, char *);
+ }
+}
+
+/**
+ * gst_structure_remove_all_fields:
+ * @structure: a #GstStructure
+ *
+ * Removes all fields in a GstStructure.
+ */
+void
+gst_structure_remove_all_fields (GstStructure * structure)
+{
+ GstStructureField *field;
+ int i;
+
+ g_return_if_fail (structure != NULL);
+ g_return_if_fail (IS_MUTABLE (structure));
+
+ for (i = GST_STRUCTURE_FIELDS (structure)->len - 1; i >= 0; i--) {
+ field = GST_STRUCTURE_FIELD (structure, i);
+
+ if (G_IS_VALUE (&field->value)) {
+ g_value_unset (&field->value);
+ }
+ GST_STRUCTURE_FIELDS (structure) =
+ g_array_remove_index (GST_STRUCTURE_FIELDS (structure), i);
+ }
+}
+
+/**
+ * gst_structure_get_field_type:
+ * @structure: a #GstStructure
+ * @fieldname: the name of the field
+ *
+ * Finds the field with the given name, and returns the type of the
+ * value it contains. If the field is not found, G_TYPE_INVALID is
+ * returned.
+ *
+ * Returns: the #GValue of the field
+ */
+GType
+gst_structure_get_field_type (const GstStructure * structure,
+ const gchar * fieldname)
+{
+ GstStructureField *field;
+
+ g_return_val_if_fail (structure != NULL, G_TYPE_INVALID);
+ g_return_val_if_fail (fieldname != NULL, G_TYPE_INVALID);
+
+ field = gst_structure_get_field (structure, fieldname);
+ if (field == NULL)
+ return G_TYPE_INVALID;
+
+ return G_VALUE_TYPE (&field->value);
+}
+
+/**
+ * gst_structure_n_fields:
+ * @structure: a #GstStructure
+ *
+ * Get the number of fields in the structure.
+ *
+ * Returns: the number of fields in the structure
+ */
+gint
+gst_structure_n_fields (const GstStructure * structure)
+{
+ g_return_val_if_fail (structure != NULL, 0);
+
+ return GST_STRUCTURE_FIELDS (structure)->len;
+}
+
+/**
+ * gst_structure_nth_field_name:
+ * @structure: a #GstStructure
+ * @index: the index to get the name of
+ *
+ * Get the name of the given field number, counting from 0 onwards.
+ *
+ * Returns: the name of the given field number
+ */
+const gchar *
+gst_structure_nth_field_name (const GstStructure * structure, guint index)
+{
+ GstStructureField *field;
+
+ g_return_val_if_fail (structure != NULL, NULL);
+ g_return_val_if_fail (index < GST_STRUCTURE_FIELDS (structure)->len, NULL);
+
+ field = GST_STRUCTURE_FIELD (structure, index);
+
+ return g_quark_to_string (field->name);
+}
+
+/**
+ * gst_structure_foreach:
+ * @structure: a #GstStructure
+ * @func: (scope call): a function to call for each field
+ * @user_data: (closure): private data
+ *
+ * Calls the provided function once for each field in the #GstStructure. The
+ * function must not modify the fields. Also see gst_structure_map_in_place().
+ *
+ * Returns: TRUE if the supplied function returns TRUE For each of the fields,
+ * FALSE otherwise.
+ */
+gboolean
+gst_structure_foreach (const GstStructure * structure,
+ GstStructureForeachFunc func, gpointer user_data)
+{
+ guint i, len;
+ GstStructureField *field;
+ gboolean ret;
+
+ g_return_val_if_fail (structure != NULL, FALSE);
+ g_return_val_if_fail (func != NULL, FALSE);
+
+ len = GST_STRUCTURE_FIELDS (structure)->len;
+
+ for (i = 0; i < len; i++) {
+ field = GST_STRUCTURE_FIELD (structure, i);
+
+ ret = func (field->name, &field->value, user_data);
+ if (G_UNLIKELY (!ret))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ * gst_structure_map_in_place:
+ * @structure: a #GstStructure
+ * @func: (scope call): a function to call for each field
+ * @user_data: (closure): private data
+ *
+ * Calls the provided function once for each field in the #GstStructure. In
+ * contrast to gst_structure_foreach(), the function may modify but not delete the
+ * fields. The structure must be mutable.
+ *
+ * Returns: TRUE if the supplied function returns TRUE For each of the fields,
+ * FALSE otherwise.
+ */
+gboolean
+gst_structure_map_in_place (GstStructure * structure,
+ GstStructureMapFunc func, gpointer user_data)
+{
+ guint i, len;
+ GstStructureField *field;
+ gboolean ret;
+
+ g_return_val_if_fail (structure != NULL, FALSE);
+ g_return_val_if_fail (IS_MUTABLE (structure), FALSE);
+ g_return_val_if_fail (func != NULL, FALSE);
+ len = GST_STRUCTURE_FIELDS (structure)->len;
+
+ for (i = 0; i < len; i++) {
+ field = GST_STRUCTURE_FIELD (structure, i);
+
+ ret = func (field->name, &field->value, user_data);
+ if (!ret)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ * gst_structure_id_has_field:
+ * @structure: a #GstStructure
+ * @field: #GQuark of the field name
+ *
+ * Check if @structure contains a field named @field.
+ *
+ * Returns: TRUE if the structure contains a field with the given name
+ *
+ * Since: 0.10.26
+ */
+gboolean
+gst_structure_id_has_field (const GstStructure * structure, GQuark field)
+{
+ GstStructureField *f;
+
+ g_return_val_if_fail (structure != NULL, FALSE);
+ g_return_val_if_fail (field != 0, FALSE);
+
+ f = gst_structure_id_get_field (structure, field);
+
+ return (f != NULL);
+}
+
+/**
+ * gst_structure_has_field:
+ * @structure: a #GstStructure
+ * @fieldname: the name of a field
+ *
+ * Check if @structure contains a field named @fieldname.
+ *
+ * Returns: TRUE if the structure contains a field with the given name
+ */
+gboolean
+gst_structure_has_field (const GstStructure * structure,
+ const gchar * fieldname)
+{
+ g_return_val_if_fail (structure != NULL, FALSE);
+ g_return_val_if_fail (fieldname != NULL, FALSE);
+
+ return gst_structure_id_has_field (structure,
+ g_quark_from_string (fieldname));
+}
+
+/**
+ * gst_structure_id_has_field_typed:
+ * @structure: a #GstStructure
+ * @field: #GQuark of the field name
+ * @type: the type of a value
+ *
+ * Check if @structure contains a field named @field and with GType @type.
+ *
+ * Returns: TRUE if the structure contains a field with the given name and type
+ *
+ * Since: 0.10.26
+ */
+gboolean
+gst_structure_id_has_field_typed (const GstStructure * structure,
+ GQuark field, GType type)
+{
+ GstStructureField *f;
+
+ g_return_val_if_fail (structure != NULL, FALSE);
+ g_return_val_if_fail (field != 0, FALSE);
+
+ f = gst_structure_id_get_field (structure, field);
+ if (f == NULL)
+ return FALSE;
+
+ return (G_VALUE_TYPE (&f->value) == type);
+}
+
+/**
+ * gst_structure_has_field_typed:
+ * @structure: a #GstStructure
+ * @fieldname: the name of a field
+ * @type: the type of a value
+ *
+ * Check if @structure contains a field named @fieldname and with GType @type.
+ *
+ * Returns: TRUE if the structure contains a field with the given name and type
+ */
+gboolean
+gst_structure_has_field_typed (const GstStructure * structure,
+ const gchar * fieldname, GType type)
+{
+ g_return_val_if_fail (structure != NULL, FALSE);
+ g_return_val_if_fail (fieldname != NULL, FALSE);
+
+ return gst_structure_id_has_field_typed (structure,
+ g_quark_from_string (fieldname), type);
+}
+
+/* utility functions */
+
+/**
+ * gst_structure_get_boolean:
+ * @structure: a #GstStructure
+ * @fieldname: the name of a field
+ * @value: (out): a pointer to a #gboolean to set
+ *
+ * Sets the boolean pointed to by @value corresponding to the value of the
+ * given field. Caller is responsible for making sure the field exists
+ * and has the correct type.
+ *
+ * Returns: TRUE if the value could be set correctly. If there was no field
+ * with @fieldname or the existing field did not contain a boolean, this
+ * function returns FALSE.
+ */
+gboolean
+gst_structure_get_boolean (const GstStructure * structure,
+ const gchar * fieldname, gboolean * value)
+{
+ GstStructureField *field;
+
+ g_return_val_if_fail (structure != NULL, FALSE);
+ g_return_val_if_fail (fieldname != NULL, FALSE);
+
+ field = gst_structure_get_field (structure, fieldname);
+
+ if (field == NULL)
+ return FALSE;
+ if (!G_VALUE_HOLDS_BOOLEAN (&field->value))
+ return FALSE;
+
+ *value = gst_g_value_get_boolean_unchecked (&field->value);
+
+ return TRUE;
+}
+
+/**
+ * gst_structure_get_int:
+ * @structure: a #GstStructure
+ * @fieldname: the name of a field
+ * @value: (out): a pointer to an int to set
+ *
+ * Sets the int pointed to by @value corresponding to the value of the
+ * given field. Caller is responsible for making sure the field exists
+ * and has the correct type.
+ *
+ * Returns: %TRUE if the value could be set correctly. If there was no field
+ * with @fieldname or the existing field did not contain an int, this function
+ * returns %FALSE.
+ */
+gboolean
+gst_structure_get_int (const GstStructure * structure,
+ const gchar * fieldname, gint * value)
+{
+ GstStructureField *field;
+
+ g_return_val_if_fail (structure != NULL, FALSE);
+ g_return_val_if_fail (fieldname != NULL, FALSE);
+ g_return_val_if_fail (value != NULL, FALSE);
+
+ field = gst_structure_get_field (structure, fieldname);
+
+ if (field == NULL)
+ return FALSE;
+ if (!G_VALUE_HOLDS_INT (&field->value))
+ return FALSE;
+
+ *value = gst_g_value_get_int_unchecked (&field->value);
+
+ return TRUE;
+}
+
+/**
+ * gst_structure_get_uint:
+ * @structure: a #GstStructure
+ * @fieldname: the name of a field
+ * @value: (out): a pointer to a uint to set
+ *
+ * Sets the uint pointed to by @value corresponding to the value of the
+ * given field. Caller is responsible for making sure the field exists
+ * and has the correct type.
+ *
+ * Returns: %TRUE if the value could be set correctly. If there was no field
+ * with @fieldname or the existing field did not contain a uint, this function
+ * returns %FALSE.
+ *
+ * Since: 0.10.15
+ */
+gboolean
+gst_structure_get_uint (const GstStructure * structure,
+ const gchar * fieldname, guint * value)
+{
+ GstStructureField *field;
+
+ g_return_val_if_fail (structure != NULL, FALSE);
+ g_return_val_if_fail (fieldname != NULL, FALSE);
+ g_return_val_if_fail (value != NULL, FALSE);
+
+ field = gst_structure_get_field (structure, fieldname);
+
+ if (field == NULL)
+ return FALSE;
+ if (!G_VALUE_HOLDS_UINT (&field->value))
+ return FALSE;
+
+ *value = gst_g_value_get_uint_unchecked (&field->value);
+
+ return TRUE;
+}
+
+/**
+ * gst_structure_get_date:
+ * @structure: a #GstStructure
+ * @fieldname: the name of a field
+ * @value: (out callee-allocates): a pointer to a #GDate to set
+ *
+ * Sets the date pointed to by @value corresponding to the date of the
+ * given field. Caller is responsible for making sure the field exists
+ * and has the correct type.
+ *
+ * On success @value will point to a newly-allocated copy of the date which
+ * should be freed with g_date_free() when no longer needed (note: this is
+ * inconsistent with e.g. gst_structure_get_string() which doesn't return a
+ * copy of the string).
+ *
+ * Returns: TRUE if the value could be set correctly. If there was no field
+ * with @fieldname or the existing field did not contain a data, this function
+ * returns FALSE.
+ */
+gboolean
+gst_structure_get_date (const GstStructure * structure, const gchar * fieldname,
+ GDate ** value)
+{
+ GstStructureField *field;
+
+ g_return_val_if_fail (structure != NULL, FALSE);
+ g_return_val_if_fail (fieldname != NULL, FALSE);
+ g_return_val_if_fail (value != NULL, FALSE);
+
+ field = gst_structure_get_field (structure, fieldname);
+
+ if (field == NULL)
+ return FALSE;
+ if (!GST_VALUE_HOLDS_DATE (&field->value))
+ return FALSE;
+
+ /* FIXME: 0.11 g_value_dup_boxed() -> g_value_get_boxed() */
+ *value = g_value_dup_boxed (&field->value);
+
+ return TRUE;
+}
+
+/**
+ * gst_structure_get_date_time:
+ * @structure: a #GstStructure
+ * @fieldname: the name of a field
+ * @value: (out callee-allocates): a pointer to a #GstDateTime to set
+ *
+ * Sets the datetime pointed to by @value corresponding to the datetime of the
+ * given field. Caller is responsible for making sure the field exists
+ * and has the correct type.
+ *
+ * On success @value will point to a reference of the datetime which
+ * should be unreffed with gst_date_time_unref() when no longer needed
+ * (note: this is inconsistent with e.g. gst_structure_get_string()
+ * which doesn't return a copy of the string).
+ *
+ * Returns: TRUE if the value could be set correctly. If there was no field
+ * with @fieldname or the existing field did not contain a data, this function
+ * returns FALSE.
+ *
+ * Since: 0.10.31
+ */
+gboolean
+gst_structure_get_date_time (const GstStructure * structure,
+ const gchar * fieldname, GstDateTime ** value)
+{
+ GstStructureField *field;
+
+ g_return_val_if_fail (structure != NULL, FALSE);
+ g_return_val_if_fail (fieldname != NULL, FALSE);
+ g_return_val_if_fail (value != NULL, FALSE);
+
+ field = gst_structure_get_field (structure, fieldname);
+
+ if (field == NULL)
+ return FALSE;
+ if (!GST_VALUE_HOLDS_DATE_TIME (&field->value))
+ return FALSE;
+
+ /* FIXME: 0.11 g_value_dup_boxed() -> g_value_get_boxed() */
+ *value = g_value_dup_boxed (&field->value);
+
+ return TRUE;
+}
+
+/**
+ * gst_structure_get_clock_time:
+ * @structure: a #GstStructure
+ * @fieldname: the name of a field
+ * @value: (out): a pointer to a #GstClockTime to set
+ *
+ * Sets the clock time pointed to by @value corresponding to the clock time
+ * of the given field. Caller is responsible for making sure the field exists
+ * and has the correct type.
+ *
+ * Returns: TRUE if the value could be set correctly. If there was no field
+ * with @fieldname or the existing field did not contain a #GstClockTime, this
+ * function returns FALSE.
+ */
+gboolean
+gst_structure_get_clock_time (const GstStructure * structure,
+ const gchar * fieldname, GstClockTime * value)
+{
+ GstStructureField *field;
+
+ g_return_val_if_fail (structure != NULL, FALSE);
+ g_return_val_if_fail (fieldname != NULL, FALSE);
+ g_return_val_if_fail (value != NULL, FALSE);
+
+ field = gst_structure_get_field (structure, fieldname);
+
+ if (field == NULL)
+ return FALSE;
+ if (!G_VALUE_HOLDS_UINT64 (&field->value))
+ return FALSE;
+
+ *value = gst_g_value_get_uint64_unchecked (&field->value);
+
+ return TRUE;
+}
+
+/**
+ * gst_structure_get_double:
+ * @structure: a #GstStructure
+ * @fieldname: the name of a field
+ * @value: (out): a pointer to a gdouble to set
+ *
+ * Sets the double pointed to by @value corresponding to the value of the
+ * given field. Caller is responsible for making sure the field exists
+ * and has the correct type.
+ *
+ * Returns: TRUE if the value could be set correctly. If there was no field
+ * with @fieldname or the existing field did not contain a double, this
+ * function returns FALSE.
+ */
+gboolean
+gst_structure_get_double (const GstStructure * structure,
+ const gchar * fieldname, gdouble * value)
+{
+ GstStructureField *field;
+
+ g_return_val_if_fail (structure != NULL, FALSE);
+ g_return_val_if_fail (fieldname != NULL, FALSE);
+ g_return_val_if_fail (value != NULL, FALSE);
+
+ field = gst_structure_get_field (structure, fieldname);
+
+ if (field == NULL)
+ return FALSE;
+ if (!G_VALUE_HOLDS_DOUBLE (&field->value))
+ return FALSE;
+
+ *value = gst_g_value_get_double_unchecked (&field->value);
+
+ return TRUE;
+}
+
+/**
+ * gst_structure_get_string:
+ * @structure: a #GstStructure
+ * @fieldname: the name of a field
+ *
+ * Finds the field corresponding to @fieldname, and returns the string
+ * contained in the field's value. Caller is responsible for making
+ * sure the field exists and has the correct type.
+ *
+ * The string should not be modified, and remains valid until the next
+ * call to a gst_structure_*() function with the given structure.
+ *
+ * Returns: a pointer to the string or NULL when the field did not exist
+ * or did not contain a string.
+ */
+const gchar *
+gst_structure_get_string (const GstStructure * structure,
+ const gchar * fieldname)
+{
+ GstStructureField *field;
+
+ g_return_val_if_fail (structure != NULL, NULL);
+ g_return_val_if_fail (fieldname != NULL, NULL);
+
+ field = gst_structure_get_field (structure, fieldname);
+
+ if (field == NULL)
+ return NULL;
+ if (!G_VALUE_HOLDS_STRING (&field->value))
+ return NULL;
+
+ return gst_g_value_get_string_unchecked (&field->value);
+}
+
+/**
+ * gst_structure_get_enum:
+ * @structure: a #GstStructure
+ * @fieldname: the name of a field
+ * @enumtype: the enum type of a field
+ * @value: (out): a pointer to an int to set
+ *
+ * Sets the int pointed to by @value corresponding to the value of the
+ * given field. Caller is responsible for making sure the field exists,
+ * has the correct type and that the enumtype is correct.
+ *
+ * Returns: TRUE if the value could be set correctly. If there was no field
+ * with @fieldname or the existing field did not contain an enum of the given
+ * type, this function returns FALSE.
+ */
+gboolean
+gst_structure_get_enum (const GstStructure * structure,
+ const gchar * fieldname, GType enumtype, gint * value)
+{
+ GstStructureField *field;
+
+ g_return_val_if_fail (structure != NULL, FALSE);
+ g_return_val_if_fail (fieldname != NULL, FALSE);
+ g_return_val_if_fail (enumtype != G_TYPE_INVALID, FALSE);
+ g_return_val_if_fail (value != NULL, FALSE);
+
+ field = gst_structure_get_field (structure, fieldname);
+
+ if (field == NULL)
+ return FALSE;
+ if (!G_TYPE_CHECK_VALUE_TYPE (&field->value, enumtype))
+ return FALSE;
+
+ *value = g_value_get_enum (&field->value);
+
+ return TRUE;
+}
+
+/**
+ * gst_structure_get_fraction:
+ * @structure: a #GstStructure
+ * @fieldname: the name of a field
+ * @value_numerator: (out): a pointer to an int to set
+ * @value_denominator: (out): a pointer to an int to set
+ *
+ * Sets the integers pointed to by @value_numerator and @value_denominator
+ * corresponding to the value of the given field. Caller is responsible
+ * for making sure the field exists and has the correct type.
+ *
+ * Returns: TRUE if the values could be set correctly. If there was no field
+ * with @fieldname or the existing field did not contain a GstFraction, this
+ * function returns FALSE.
+ */
+gboolean
+gst_structure_get_fraction (const GstStructure * structure,
+ const gchar * fieldname, gint * value_numerator, gint * value_denominator)
+{
+ GstStructureField *field;
+
+ g_return_val_if_fail (structure != NULL, FALSE);
+ g_return_val_if_fail (fieldname != NULL, FALSE);
+ g_return_val_if_fail (value_numerator != NULL, FALSE);
+ g_return_val_if_fail (value_denominator != NULL, FALSE);
+
+ field = gst_structure_get_field (structure, fieldname);
+
+ if (field == NULL)
+ return FALSE;
+ if (!GST_VALUE_HOLDS_FRACTION (&field->value))
+ return FALSE;
+
+ *value_numerator = gst_value_get_fraction_numerator (&field->value);
+ *value_denominator = gst_value_get_fraction_denominator (&field->value);
+
+ return TRUE;
+}
+
+typedef struct _GstStructureAbbreviation
+{
+ const gchar *type_name;
+ GType type;
+}
+GstStructureAbbreviation;
+
+/* return a copy of an array of GstStructureAbbreviation containing all the
+ * known type_string, GType maps, including abbreviations for common types */
+static GstStructureAbbreviation *
+gst_structure_get_abbrs (gint * n_abbrs)
+{
+ static GstStructureAbbreviation *abbrs = NULL;
+ static volatile gsize num = 0;
+
+ if (g_once_init_enter (&num)) {
+ /* dynamically generate the array */
+ gsize _num;
+ GstStructureAbbreviation dyn_abbrs[] = {
+ {"int", G_TYPE_INT}
+ ,
+ {"i", G_TYPE_INT}
+ ,
+ {"uint", G_TYPE_UINT}
+ ,
+ {"u", G_TYPE_UINT}
+ ,
+ {"float", G_TYPE_FLOAT}
+ ,
+ {"f", G_TYPE_FLOAT}
+ ,
+ {"double", G_TYPE_DOUBLE}
+ ,
+ {"d", G_TYPE_DOUBLE}
+ ,
+ {"buffer", GST_TYPE_BUFFER}
+ ,
+ {"fraction", GST_TYPE_FRACTION}
+ ,
+ {"boolean", G_TYPE_BOOLEAN}
+ ,
+ {"bool", G_TYPE_BOOLEAN}
+ ,
+ {"b", G_TYPE_BOOLEAN}
+ ,
+ {"string", G_TYPE_STRING}
+ ,
+ {"str", G_TYPE_STRING}
+ ,
+ {"s", G_TYPE_STRING}
+ ,
+ {"structure", GST_TYPE_STRUCTURE}
+ ,
+ {"date", GST_TYPE_DATE}
+ ,
+ {"datetime", GST_TYPE_DATE_TIME}
+ };
+ _num = G_N_ELEMENTS (dyn_abbrs);
+ /* permanently allocate and copy the array now */
+ abbrs = g_new0 (GstStructureAbbreviation, _num);
+ memcpy (abbrs, dyn_abbrs, sizeof (GstStructureAbbreviation) * _num);
+ g_once_init_leave (&num, _num);
+ }
+ *n_abbrs = num;
+
+ return abbrs;
+}
+
+/* given a type_name that could be a type abbreviation or a registered GType,
+ * return a matching GType */
+static GType
+gst_structure_gtype_from_abbr (const char *type_name)
+{
+ int i;
+ GstStructureAbbreviation *abbrs;
+ gint n_abbrs;
+
+ g_return_val_if_fail (type_name != NULL, G_TYPE_INVALID);
+
+ abbrs = gst_structure_get_abbrs (&n_abbrs);
+
+ for (i = 0; i < n_abbrs; i++) {
+ if (strcmp (type_name, abbrs[i].type_name) == 0) {
+ return abbrs[i].type;
+ }
+ }
+
+ /* this is the fallback */
+ return g_type_from_name (type_name);
+}
+
+static const char *
+gst_structure_to_abbr (GType type)
+{
+ int i;
+ GstStructureAbbreviation *abbrs;
+ gint n_abbrs;
+
+ g_return_val_if_fail (type != G_TYPE_INVALID, NULL);
+
+ abbrs = gst_structure_get_abbrs (&n_abbrs);
+
+ for (i = 0; i < n_abbrs; i++) {
+ if (type == abbrs[i].type) {
+ return abbrs[i].type_name;
+ }
+ }
+
+ return g_type_name (type);
+}
+
+static GType
+gst_structure_value_get_generic_type (GValue * val)
+{
+ if (G_VALUE_TYPE (val) == GST_TYPE_LIST
+ || G_VALUE_TYPE (val) == GST_TYPE_ARRAY) {
+ GArray *array = g_value_peek_pointer (val);
+
+ if (array->len > 0) {
+ GValue *value = &g_array_index (array, GValue, 0);
+
+ return gst_structure_value_get_generic_type (value);
+ } else {
+ return G_TYPE_INT;
+ }
+ } else if (G_VALUE_TYPE (val) == GST_TYPE_INT_RANGE) {
+ return G_TYPE_INT;
+ } else if (G_VALUE_TYPE (val) == GST_TYPE_INT64_RANGE) {
+ return G_TYPE_INT64;
+ } else if (G_VALUE_TYPE (val) == GST_TYPE_DOUBLE_RANGE) {
+ return G_TYPE_DOUBLE;
+ } else if (G_VALUE_TYPE (val) == GST_TYPE_FRACTION_RANGE) {
+ return GST_TYPE_FRACTION;
+ }
+ return G_VALUE_TYPE (val);
+}
+
+gboolean
+priv_gst_structure_append_to_gstring (const GstStructure * structure,
+ GString * s)
+{
+ GstStructureField *field;
+ guint i, len;
+
+ g_return_val_if_fail (s != NULL, FALSE);
+
+ g_string_append (s, g_quark_to_string (structure->name));
+ len = GST_STRUCTURE_FIELDS (structure)->len;
+ for (i = 0; i < len; i++) {
+ char *t;
+ GType type;
+
+ field = GST_STRUCTURE_FIELD (structure, i);
+
+ t = gst_value_serialize (&field->value);
+ type = gst_structure_value_get_generic_type (&field->value);
+
+ g_string_append_len (s, ", ", 2);
+ /* FIXME: do we need to escape fieldnames? */
+ g_string_append (s, g_quark_to_string (field->name));
+ g_string_append_len (s, "=(", 2);
+ g_string_append (s, gst_structure_to_abbr (type));
+ g_string_append_c (s, ')');
+ g_string_append (s, t == NULL ? "NULL" : t);
+ g_free (t);
+ }
+
+ g_string_append_c (s, ';');
+ return TRUE;
+}
+
+/**
+ * gst_structure_to_string:
+ * @structure: a #GstStructure
+ *
+ * Converts @structure to a human-readable string representation.
+ *
+ * For debugging purposes its easier to do something like this:
+ * |[
+ * GST_LOG ("structure is %" GST_PTR_FORMAT, structure);
+ * ]|
+ * This prints the structure in human readble form.
+ *
+ * Free-function: g_free
+ *
+ * Returns: (transfer full)L a pointer to string allocated by g_malloc().
+ * g_free() after usage.
+ */
+gchar *
+gst_structure_to_string (const GstStructure * structure)
+{
+ GString *s;
+
+ /* NOTE: This function is potentially called by the debug system,
+ * so any calls to gst_log() (and GST_DEBUG(), GST_LOG(), etc.)
+ * should be careful to avoid recursion. This includes any functions
+ * called by gst_structure_to_string. In particular, calls should
+ * not use the GST_PTR_FORMAT extension. */
+
+ g_return_val_if_fail (structure != NULL, NULL);
+
+ /* we estimate a minimum size based on the number of fields in order to
+ * avoid unnecessary reallocs within GString */
+ s = g_string_sized_new (STRUCTURE_ESTIMATED_STRING_LEN (structure));
+ priv_gst_structure_append_to_gstring (structure, s);
+ return g_string_free (s, FALSE);
+}
+
+/*
+ * r will still point to the string. if end == next, the string will not be
+ * null-terminated. In all other cases it will be.
+ * end = pointer to char behind end of string, next = pointer to start of
+ * unread data.
+ * THIS FUNCTION MODIFIES THE STRING AND DETECTS INSIDE A NONTERMINATED STRING
+ */
+static gboolean
+gst_structure_parse_string (gchar * s, gchar ** end, gchar ** next,
+ gboolean unescape)
+{
+ gchar *w;
+
+ if (*s == 0)
+ return FALSE;
+
+ if (*s != '"') {
+ int ret;
+
+ ret = gst_structure_parse_simple_string (s, end);
+ *next = *end;
+
+ return ret;
+ }
+
+ if (unescape) {
+ w = s;
+ s++;
+ while (*s != '"') {
+ if (G_UNLIKELY (*s == 0))
+ return FALSE;
+ if (G_UNLIKELY (*s == '\\'))
+ s++;
+ *w = *s;
+ w++;
+ s++;
+ }
+ s++;
+ } else {
+ /* Find the closing quotes */
+ s++;
+ while (*s != '"') {
+ if (G_UNLIKELY (*s == 0))
+ return FALSE;
+ if (G_UNLIKELY (*s == '\\'))
+ s++;
+ s++;
+ }
+ s++;
+ w = s;
+ }
+
+ *end = w;
+ *next = s;
+
+ return TRUE;
+}
+
+static gboolean
+gst_structure_parse_range (gchar * s, gchar ** after, GValue * value,
+ GType type)
+{
+ GValue value1 = { 0 };
+ GValue value2 = { 0 };
+ GType range_type;
+ gboolean ret;
+
+ if (*s != '[')
+ return FALSE;
+ s++;
+
+ ret = gst_structure_parse_value (s, &s, &value1, type);
+ if (ret == FALSE)
+ return FALSE;
+
+ while (g_ascii_isspace (*s))
+ s++;
+
+ if (*s != ',')
+ return FALSE;
+ s++;
+
+ while (g_ascii_isspace (*s))
+ s++;
+
+ ret = gst_structure_parse_value (s, &s, &value2, type);
+ if (ret == FALSE)
+ return FALSE;
+
+ while (g_ascii_isspace (*s))
+ s++;
+
+ if (*s != ']')
+ return FALSE;
+ s++;
+
+ if (G_VALUE_TYPE (&value1) != G_VALUE_TYPE (&value2))
+ return FALSE;
+
+ if (G_VALUE_TYPE (&value1) == G_TYPE_DOUBLE) {
+ range_type = GST_TYPE_DOUBLE_RANGE;
+ g_value_init (value, range_type);
+ gst_value_set_double_range (value,
+ gst_g_value_get_double_unchecked (&value1),
+ gst_g_value_get_double_unchecked (&value2));
+ } else if (G_VALUE_TYPE (&value1) == G_TYPE_INT) {
+ range_type = GST_TYPE_INT_RANGE;
+ g_value_init (value, range_type);
+ gst_value_set_int_range (value, gst_g_value_get_int_unchecked (&value1),
+ gst_g_value_get_int_unchecked (&value2));
+ } else if (G_VALUE_TYPE (&value1) == G_TYPE_INT64) {
+ range_type = GST_TYPE_INT64_RANGE;
+ g_value_init (value, range_type);
+ gst_value_set_int64_range (value, gst_g_value_get_int64_unchecked (&value1),
+ gst_g_value_get_int64_unchecked (&value2));
+ } else if (G_VALUE_TYPE (&value1) == GST_TYPE_FRACTION) {
+ range_type = GST_TYPE_FRACTION_RANGE;
+ g_value_init (value, range_type);
+ gst_value_set_fraction_range (value, &value1, &value2);
+ } else {
+ return FALSE;
+ }
+
+ *after = s;
+ return TRUE;
+}
+
+static gboolean
+gst_structure_parse_any_list (gchar * s, gchar ** after, GValue * value,
+ GType type, GType list_type, char begin, char end)
+{
+ GValue list_value = { 0 };
+ gboolean ret;
+ GArray *array;
+
+ g_value_init (value, list_type);
+ array = g_value_peek_pointer (value);
+
+ if (*s != begin)
+ return FALSE;
+ s++;
+
+ while (g_ascii_isspace (*s))
+ s++;
+ if (*s == end) {
+ s++;
+ *after = s;
+ return TRUE;
+ }
+
+ ret = gst_structure_parse_value (s, &s, &list_value, type);
+ if (ret == FALSE)
+ return FALSE;
+
+ g_array_append_val (array, list_value);
+
+ while (g_ascii_isspace (*s))
+ s++;
+
+ while (*s != end) {
+ if (*s != ',')
+ return FALSE;
+ s++;
+
+ while (g_ascii_isspace (*s))
+ s++;
+
+ memset (&list_value, 0, sizeof (list_value));
+ ret = gst_structure_parse_value (s, &s, &list_value, type);
+ if (ret == FALSE)
+ return FALSE;
+
+ g_array_append_val (array, list_value);
+ while (g_ascii_isspace (*s))
+ s++;
+ }
+
+ s++;
+
+ *after = s;
+ return TRUE;
+}
+
+static gboolean
+gst_structure_parse_list (gchar * s, gchar ** after, GValue * value, GType type)
+{
+ return gst_structure_parse_any_list (s, after, value, type, GST_TYPE_LIST,
+ '{', '}');
+}
+
+static gboolean
+gst_structure_parse_array (gchar * s, gchar ** after, GValue * value,
+ GType type)
+{
+ return gst_structure_parse_any_list (s, after, value, type,
+ GST_TYPE_ARRAY, '<', '>');
+}
+
+static gboolean
+gst_structure_parse_simple_string (gchar * str, gchar ** end)
+{
+ char *s = str;
+
+ while (G_LIKELY (GST_ASCII_IS_STRING (*s))) {
+ s++;
+ }
+
+ *end = s;
+
+ return (s != str);
+}
+
+static gboolean
+gst_structure_parse_field (gchar * str,
+ gchar ** after, GstStructureField * field)
+{
+ gchar *name;
+ gchar *name_end;
+ gchar *s;
+ gchar c;
+
+ s = str;
+
+ while (g_ascii_isspace (*s) || (s[0] == '\\' && g_ascii_isspace (s[1])))
+ s++;
+ name = s;
+ if (G_UNLIKELY (!gst_structure_parse_simple_string (s, &name_end))) {
+ GST_WARNING ("failed to parse simple string, str=%s", str);
+ return FALSE;
+ }
+
+ s = name_end;
+ while (g_ascii_isspace (*s) || (s[0] == '\\' && g_ascii_isspace (s[1])))
+ s++;
+
+ if (G_UNLIKELY (*s != '=')) {
+ GST_WARNING ("missing assignment operator in the field, str=%s", str);
+ return FALSE;
+ }
+ s++;
+
+ c = *name_end;
+ *name_end = '\0';
+ field->name = g_quark_from_string (name);
+ GST_DEBUG ("trying field name '%s'", name);
+ *name_end = c;
+
+ if (G_UNLIKELY (!gst_structure_parse_value (s, &s, &field->value,
+ G_TYPE_INVALID))) {
+ GST_WARNING ("failed to parse value %s", str);
+ return FALSE;
+ }
+
+ *after = s;
+ return TRUE;
+}
+
+static gboolean
+gst_structure_parse_value (gchar * str,
+ gchar ** after, GValue * value, GType default_type)
+{
+ gchar *type_name;
+ gchar *type_end;
+ gchar *value_s;
+ gchar *value_end;
+ gchar *s;
+ gchar c;
+ int ret = 0;
+ GType type = default_type;
+
+ s = str;
+ while (g_ascii_isspace (*s))
+ s++;
+
+ /* check if there's a (type_name) 'cast' */
+ type_name = NULL;
+ if (*s == '(') {
+ s++;
+ while (g_ascii_isspace (*s))
+ s++;
+ type_name = s;
+ if (G_UNLIKELY (!gst_structure_parse_simple_string (s, &type_end)))
+ return FALSE;
+ s = type_end;
+ while (g_ascii_isspace (*s))
+ s++;
+ if (G_UNLIKELY (*s != ')'))
+ return FALSE;
+ s++;
+ while (g_ascii_isspace (*s))
+ s++;
+
+ c = *type_end;
+ *type_end = 0;
+ type = gst_structure_gtype_from_abbr (type_name);
+ GST_DEBUG ("trying type name '%s'", type_name);
+ *type_end = c;
+
+ if (G_UNLIKELY (type == G_TYPE_INVALID)) {
+ GST_WARNING ("invalid type");
+ return FALSE;
+ }
+ }
+
+ while (g_ascii_isspace (*s))
+ s++;
+ if (*s == '[') {
+ ret = gst_structure_parse_range (s, &s, value, type);
+ } else if (*s == '{') {
+ ret = gst_structure_parse_list (s, &s, value, type);
+ } else if (*s == '<') {
+ ret = gst_structure_parse_array (s, &s, value, type);
+ } else {
+ value_s = s;
+
+ if (G_UNLIKELY (type == G_TYPE_INVALID)) {
+ GType try_types[] =
+ { G_TYPE_INT, G_TYPE_DOUBLE, GST_TYPE_FRACTION, G_TYPE_BOOLEAN,
+ G_TYPE_STRING
+ };
+ int i;
+
+ if (G_UNLIKELY (!gst_structure_parse_string (s, &value_end, &s, TRUE)))
+ return FALSE;
+ /* Set NULL terminator for deserialization */
+ c = *value_end;
+ *value_end = '\0';
+
+ for (i = 0; i < G_N_ELEMENTS (try_types); i++) {
+ g_value_init (value, try_types[i]);
+ ret = gst_value_deserialize (value, value_s);
+ if (ret)
+ break;
+ g_value_unset (value);
+ }
+ } else {
+ g_value_init (value, type);
+
+ if (G_UNLIKELY (!gst_structure_parse_string (s, &value_end, &s,
+ (type != G_TYPE_STRING))))
+ return FALSE;
+ /* Set NULL terminator for deserialization */
+ c = *value_end;
+ *value_end = '\0';
+
+ ret = gst_value_deserialize (value, value_s);
+ if (G_UNLIKELY (!ret))
+ g_value_unset (value);
+ }
+ *value_end = c;
+ }
+
+ *after = s;
+
+ return ret;
+}
+
+/**
+ * gst_structure_from_string:
+ * @string: a string representation of a #GstStructure.
+ * @end: (out) (allow-none): pointer to store the end of the string in.
+ *
+ * Creates a #GstStructure from a string representation.
+ * If end is not NULL, a pointer to the place inside the given string
+ * where parsing ended will be returned.
+ *
+ * Free-function: gst_structure_free
+ *
+ * Returns: (transfer full): a new #GstStructure or NULL when the string could
+ * not be parsed. Free with gst_structure_free() after use.
+ */
+GstStructure *
+gst_structure_from_string (const gchar * string, gchar ** end)
+{
+ char *name;
+ char *copy;
+ char *w;
+ char *r;
+ char save;
+ GstStructure *structure = NULL;
+ GstStructureField field;
+
+ g_return_val_if_fail (string != NULL, NULL);
+
+ copy = g_strdup (string);
+ r = copy;
+
+ /* skip spaces (FIXME: _isspace treats tabs and newlines as space!) */
+ while (*r && (g_ascii_isspace (*r) || (r[0] == '\\'
+ && g_ascii_isspace (r[1]))))
+ r++;
+
+ name = r;
+ if (G_UNLIKELY (!gst_structure_parse_string (r, &w, &r, TRUE))) {
+ GST_WARNING ("Failed to parse structure string '%s'", string);
+ goto error;
+ }
+
+ save = *w;
+ *w = '\0';
+ structure = gst_structure_empty_new (name);
+ *w = save;
+
+ if (G_UNLIKELY (structure == NULL))
+ goto error;
+
+ do {
+ while (*r && (g_ascii_isspace (*r) || (r[0] == '\\'
+ && g_ascii_isspace (r[1]))))
+ r++;
+ if (*r == ';') {
+ /* end of structure, get the next char and finish */
+ r++;
+ break;
+ }
+ if (*r == '\0') {
+ /* accept \0 as end delimiter */
+ break;
+ }
+ if (G_UNLIKELY (*r != ',')) {
+ GST_WARNING ("Failed to find delimiter, r=%s", r);
+ goto error;
+ }
+ r++;
+ while (*r && (g_ascii_isspace (*r) || (r[0] == '\\'
+ && g_ascii_isspace (r[1]))))
+ r++;
+
+ memset (&field, 0, sizeof (field));
+ if (G_UNLIKELY (!gst_structure_parse_field (r, &r, &field))) {
+ GST_WARNING ("Failed to parse field, r=%s", r);
+ goto error;
+ }
+ gst_structure_set_field (structure, &field);
+ } while (TRUE);
+
+ if (end)
+ *end = (char *) string + (r - copy);
+ else if (*r)
+ g_warning ("gst_structure_from_string did not consume whole string,"
+ " but caller did not provide end pointer (\"%s\")", string);
+
+ g_free (copy);
+ return structure;
+
+error:
+ if (structure)
+ gst_structure_free (structure);
+ g_free (copy);
+ return NULL;
+}
+
+static void
+gst_structure_transform_to_string (const GValue * src_value,
+ GValue * dest_value)
+{
+ g_return_if_fail (src_value != NULL);
+ g_return_if_fail (dest_value != NULL);
+
+ dest_value->data[0].v_pointer =
+ gst_structure_to_string (src_value->data[0].v_pointer);
+}
+
+static GstStructure *
+gst_structure_copy_conditional (const GstStructure * structure)
+{
+ if (structure)
+ return gst_structure_copy (structure);
+ return NULL;
+}
+
+/* fixate utility functions */
+
+/**
+ * gst_structure_fixate_field_nearest_int:
+ * @structure: a #GstStructure
+ * @field_name: a field in @structure
+ * @target: the target value of the fixation
+ *
+ * Fixates a #GstStructure by changing the given field to the nearest
+ * integer to @target that is a subset of the existing field.
+ *
+ * Returns: TRUE if the structure could be fixated
+ */
+gboolean
+gst_structure_fixate_field_nearest_int (GstStructure * structure,
+ const char *field_name, int target)
+{
+ const GValue *value;
+
+ g_return_val_if_fail (gst_structure_has_field (structure, field_name), FALSE);
+ g_return_val_if_fail (IS_MUTABLE (structure), FALSE);
+
+ value = gst_structure_get_value (structure, field_name);
+
+ if (G_VALUE_TYPE (value) == G_TYPE_INT) {
+ /* already fixed */
+ return FALSE;
+ } else if (G_VALUE_TYPE (value) == GST_TYPE_INT_RANGE) {
+ int x;
+
+ x = gst_value_get_int_range_min (value);
+ if (target < x)
+ target = x;
+ x = gst_value_get_int_range_max (value);
+ if (target > x)
+ target = x;
+ gst_structure_set (structure, field_name, G_TYPE_INT, target, NULL);
+ return TRUE;
+ } else if (G_VALUE_TYPE (value) == GST_TYPE_LIST) {
+ const GValue *list_value;
+ int i, n;
+ int best = 0;
+ int best_index = -1;
+
+ n = gst_value_list_get_size (value);
+ for (i = 0; i < n; i++) {
+ list_value = gst_value_list_get_value (value, i);
+ if (G_VALUE_TYPE (list_value) == G_TYPE_INT) {
+ int x = gst_g_value_get_int_unchecked (list_value);
+
+ if (best_index == -1 || (ABS (target - x) < ABS (target - best))) {
+ best_index = i;
+ best = x;
+ }
+ }
+ }
+ if (best_index != -1) {
+ gst_structure_set (structure, field_name, G_TYPE_INT, best, NULL);
+ return TRUE;
+ }
+ return FALSE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * gst_structure_fixate_field_nearest_double:
+ * @structure: a #GstStructure
+ * @field_name: a field in @structure
+ * @target: the target value of the fixation
+ *
+ * Fixates a #GstStructure by changing the given field to the nearest
+ * double to @target that is a subset of the existing field.
+ *
+ * Returns: TRUE if the structure could be fixated
+ */
+gboolean
+gst_structure_fixate_field_nearest_double (GstStructure * structure,
+ const char *field_name, double target)
+{
+ const GValue *value;
+
+ g_return_val_if_fail (gst_structure_has_field (structure, field_name), FALSE);
+ g_return_val_if_fail (IS_MUTABLE (structure), FALSE);
+
+ value = gst_structure_get_value (structure, field_name);
+
+ if (G_VALUE_TYPE (value) == G_TYPE_DOUBLE) {
+ /* already fixed */
+ return FALSE;
+ } else if (G_VALUE_TYPE (value) == GST_TYPE_DOUBLE_RANGE) {
+ double x;
+
+ x = gst_value_get_double_range_min (value);
+ if (target < x)
+ target = x;
+ x = gst_value_get_double_range_max (value);
+ if (target > x)
+ target = x;
+ gst_structure_set (structure, field_name, G_TYPE_DOUBLE, target, NULL);
+ return TRUE;
+ } else if (G_VALUE_TYPE (value) == GST_TYPE_LIST) {
+ const GValue *list_value;
+ int i, n;
+ double best = 0;
+ int best_index = -1;
+
+ n = gst_value_list_get_size (value);
+ for (i = 0; i < n; i++) {
+ list_value = gst_value_list_get_value (value, i);
+ if (G_VALUE_TYPE (list_value) == G_TYPE_DOUBLE) {
+ double x = gst_g_value_get_double_unchecked (list_value);
+
+ if (best_index == -1 || (ABS (target - x) < ABS (target - best))) {
+ best_index = i;
+ best = x;
+ }
+ }
+ }
+ if (best_index != -1) {
+ gst_structure_set (structure, field_name, G_TYPE_DOUBLE, best, NULL);
+ return TRUE;
+ }
+ return FALSE;
+ }
+
+ return FALSE;
+
+}
+
+/**
+ * gst_structure_fixate_field_boolean:
+ * @structure: a #GstStructure
+ * @field_name: a field in @structure
+ * @target: the target value of the fixation
+ *
+ * Fixates a #GstStructure by changing the given @field_name field to the given
+ * @target boolean if that field is not fixed yet.
+ *
+ * Returns: TRUE if the structure could be fixated
+ */
+gboolean
+gst_structure_fixate_field_boolean (GstStructure * structure,
+ const char *field_name, gboolean target)
+{
+ const GValue *value;
+
+ g_return_val_if_fail (gst_structure_has_field (structure, field_name), FALSE);
+ g_return_val_if_fail (IS_MUTABLE (structure), FALSE);
+
+ value = gst_structure_get_value (structure, field_name);
+
+ if (G_VALUE_TYPE (value) == G_TYPE_BOOLEAN) {
+ /* already fixed */
+ return FALSE;
+ } else if (G_VALUE_TYPE (value) == GST_TYPE_LIST) {
+ const GValue *list_value;
+ int i, n;
+ int best = 0;
+ int best_index = -1;
+
+ n = gst_value_list_get_size (value);
+ for (i = 0; i < n; i++) {
+ list_value = gst_value_list_get_value (value, i);
+ if (G_VALUE_TYPE (list_value) == G_TYPE_BOOLEAN) {
+ gboolean x = gst_g_value_get_boolean_unchecked (list_value);
+
+ if (best_index == -1 || x == target) {
+ best_index = i;
+ best = x;
+ }
+ }
+ }
+ if (best_index != -1) {
+ gst_structure_set (structure, field_name, G_TYPE_BOOLEAN, best, NULL);
+ return TRUE;
+ }
+ return FALSE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * gst_structure_fixate_field_string:
+ * @structure: a #GstStructure
+ * @field_name: a field in @structure
+ * @target: the target value of the fixation
+ *
+ * Fixates a #GstStructure by changing the given @field_name field to the given
+ * @target string if that field is not fixed yet.
+ *
+ * Returns: TRUE if the structure could be fixated
+ *
+ * Since: 0.10.30
+ */
+gboolean
+gst_structure_fixate_field_string (GstStructure * structure,
+ const gchar * field_name, const gchar * target)
+{
+ const GValue *value;
+
+ g_return_val_if_fail (gst_structure_has_field (structure, field_name), FALSE);
+ g_return_val_if_fail (IS_MUTABLE (structure), FALSE);
+
+ value = gst_structure_get_value (structure, field_name);
+
+ if (G_VALUE_TYPE (value) == G_TYPE_STRING) {
+ /* already fixed */
+ return FALSE;
+ } else if (G_VALUE_TYPE (value) == GST_TYPE_LIST) {
+ const GValue *list_value;
+ int i, n;
+ const gchar *best = NULL;
+ int best_index = -1;
+
+ n = gst_value_list_get_size (value);
+ for (i = 0; i < n; i++) {
+ list_value = gst_value_list_get_value (value, i);
+ if (G_VALUE_TYPE (list_value) == G_TYPE_STRING) {
+ const gchar *x = g_value_get_string (list_value);
+
+ if (best_index == -1 || g_str_equal (x, target)) {
+ best_index = i;
+ best = x;
+ }
+ }
+ }
+ if (best_index != -1) {
+ gst_structure_set (structure, field_name, G_TYPE_STRING, best, NULL);
+ return TRUE;
+ }
+ return FALSE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * gst_structure_fixate_field_nearest_fraction:
+ * @structure: a #GstStructure
+ * @field_name: a field in @structure
+ * @target_numerator: The numerator of the target value of the fixation
+ * @target_denominator: The denominator of the target value of the fixation
+ *
+ * Fixates a #GstStructure by changing the given field to the nearest
+ * fraction to @target_numerator/@target_denominator that is a subset
+ * of the existing field.
+ *
+ * Returns: TRUE if the structure could be fixated
+ */
+gboolean
+gst_structure_fixate_field_nearest_fraction (GstStructure * structure,
+ const char *field_name, const gint target_numerator,
+ const gint target_denominator)
+{
+ const GValue *value;
+
+ g_return_val_if_fail (gst_structure_has_field (structure, field_name), FALSE);
+ g_return_val_if_fail (IS_MUTABLE (structure), FALSE);
+
+ value = gst_structure_get_value (structure, field_name);
+
+ if (G_VALUE_TYPE (value) == GST_TYPE_FRACTION) {
+ /* already fixed */
+ return FALSE;
+ } else if (G_VALUE_TYPE (value) == GST_TYPE_FRACTION_RANGE) {
+ const GValue *x, *new_value;
+ GValue target = { 0 };
+ g_value_init (&target, GST_TYPE_FRACTION);
+ gst_value_set_fraction (&target, target_numerator, target_denominator);
+
+ new_value = &target;
+ x = gst_value_get_fraction_range_min (value);
+ if (gst_value_compare (&target, x) == GST_VALUE_LESS_THAN)
+ new_value = x;
+ x = gst_value_get_fraction_range_max (value);
+ if (gst_value_compare (&target, x) == GST_VALUE_GREATER_THAN)
+ new_value = x;
+
+ gst_structure_set_value (structure, field_name, new_value);
+ g_value_unset (&target);
+ return TRUE;
+ } else if (G_VALUE_TYPE (value) == GST_TYPE_LIST) {
+ const GValue *list_value;
+ int i, n;
+ const GValue *best = NULL;
+ gdouble target;
+ gdouble cur_diff;
+ gdouble best_diff = G_MAXDOUBLE;
+
+ target = (gdouble) target_numerator / (gdouble) target_denominator;
+
+ GST_DEBUG ("target %g, best %g", target, best_diff);
+
+ best = NULL;
+
+ n = gst_value_list_get_size (value);
+ for (i = 0; i < n; i++) {
+ list_value = gst_value_list_get_value (value, i);
+ if (G_VALUE_TYPE (list_value) == GST_TYPE_FRACTION) {
+ gint num, denom;
+ gdouble list_double;
+
+ num = gst_value_get_fraction_numerator (list_value);
+ denom = gst_value_get_fraction_denominator (list_value);
+
+ list_double = ((gdouble) num / (gdouble) denom);
+ cur_diff = target - list_double;
+
+ GST_DEBUG ("curr diff %g, list %g", cur_diff, list_double);
+
+ if (cur_diff < 0)
+ cur_diff = -cur_diff;
+
+ if (!best || cur_diff < best_diff) {
+ GST_DEBUG ("new best %g", list_double);
+ best = list_value;
+ best_diff = cur_diff;
+ }
+ }
+ }
+ if (best != NULL) {
+ gst_structure_set_value (structure, field_name, best);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static gboolean
+default_fixate (GQuark field_id, const GValue * value, gpointer data)
+{
+ GstStructure *s = data;
+ GValue v = { 0 };
+
+ if (gst_value_fixate (&v, value)) {
+ gst_structure_id_set_value (s, field_id, &v);
+ g_value_unset (&v);
+ }
+ return TRUE;
+}
+
+/**
+ * gst_structure_fixate_field:
+ * @structure: a #GstStructure
+ * @field_name: a field in @structure
+ *
+ * Fixates a #GstStructure by changing the given field with its fixated value.
+ *
+ * Returns: TRUE if the structure field could be fixated
+ */
+gboolean
+gst_structure_fixate_field (GstStructure * structure, const char *field_name)
+{
+ GstStructureField *field;
+
+ g_return_val_if_fail (structure != NULL, FALSE);
+ g_return_val_if_fail (IS_MUTABLE (structure), FALSE);
+
+ if (!(field = gst_structure_get_field (structure, field_name)))
+ return FALSE;
+
+ return default_fixate (field->name, &field->value, structure);
+}
+
+/* our very own version of G_VALUE_LCOPY that allows NULL return locations
+ * (useful for message parsing functions where the return location is user
+ * supplied and the user may pass NULL if the value isn't of interest) */
+#define GST_VALUE_LCOPY(value, var_args, flags, __error, fieldname) \
+G_STMT_START { \
+ const GValue *_value = (value); \
+ guint _flags = (flags); \
+ GType _value_type = G_VALUE_TYPE (_value); \
+ GTypeValueTable *_vtable = g_type_value_table_peek (_value_type); \
+ gchar *_lcopy_format = _vtable->lcopy_format; \
+ GTypeCValue _cvalues[G_VALUE_COLLECT_FORMAT_MAX_LENGTH] = { { 0, }, }; \
+ guint _n_values = 0; \
+ \
+ while (*_lcopy_format != '\0') { \
+ g_assert (*_lcopy_format == G_VALUE_COLLECT_POINTER); \
+ _cvalues[_n_values++].v_pointer = va_arg ((var_args), gpointer); \
+ _lcopy_format++; \
+ } \
+ if (_n_values == 2 && !!_cvalues[0].v_pointer != !!_cvalues[1].v_pointer) { \
+ *(__error) = g_strdup_printf ("either all or none of the return " \
+ "locations for field '%s' need to be NULL", fieldname); \
+ } else if (_cvalues[0].v_pointer != NULL) { \
+ *(__error) = _vtable->lcopy_value (_value, _n_values, _cvalues, _flags); \
+ } \
+} G_STMT_END
+
+/**
+ * gst_structure_get_valist:
+ * @structure: a #GstStructure
+ * @first_fieldname: the name of the first field to read
+ * @args: variable arguments
+ *
+ * Parses the variable arguments and reads fields from @structure accordingly.
+ * valist-variant of gst_structure_get(). Look at the documentation of
+ * gst_structure_get() for more details.
+ *
+ * Returns: TRUE, or FALSE if there was a problem reading any of the fields
+ *
+ * Since: 0.10.24
+ */
+gboolean
+gst_structure_get_valist (const GstStructure * structure,
+ const char *first_fieldname, va_list args)
+{
+ const char *field_name;
+ GType expected_type = G_TYPE_INVALID;
+
+ g_return_val_if_fail (GST_IS_STRUCTURE (structure), FALSE);
+ g_return_val_if_fail (first_fieldname != NULL, FALSE);
+
+ field_name = first_fieldname;
+ while (field_name) {
+ const GValue *val = NULL;
+ gchar *err = NULL;
+
+ expected_type = va_arg (args, GType);
+
+ val = gst_structure_get_value (structure, field_name);
+
+ if (val == NULL)
+ goto no_such_field;
+
+ if (G_VALUE_TYPE (val) != expected_type)
+ goto wrong_type;
+
+ GST_VALUE_LCOPY (val, args, 0, &err, field_name);
+ if (err) {
+ g_warning ("%s: %s", G_STRFUNC, err);
+ g_free (err);
+ return FALSE;
+ }
+
+ field_name = va_arg (args, const gchar *);
+ }
+
+ return TRUE;
+
+/* ERRORS */
+no_such_field:
+ {
+ GST_WARNING ("Expected field '%s' in structure: %" GST_PTR_FORMAT,
+ field_name, structure);
+ return FALSE;
+ }
+wrong_type:
+ {
+ GST_WARNING ("Expected field '%s' in structure to be of type '%s', but "
+ "field was of type '%s': %" GST_PTR_FORMAT, field_name,
+ GST_STR_NULL (g_type_name (expected_type)),
+ G_VALUE_TYPE_NAME (gst_structure_get_value (structure, field_name)),
+ structure);
+ return FALSE;
+ }
+}
+
+/**
+ * gst_structure_id_get_valist:
+ * @structure: a #GstStructure
+ * @first_field_id: the quark of the first field to read
+ * @args: variable arguments
+ *
+ * Parses the variable arguments and reads fields from @structure accordingly.
+ * valist-variant of gst_structure_id_get(). Look at the documentation of
+ * gst_structure_id_get() for more details.
+ *
+ * Returns: TRUE, or FALSE if there was a problem reading any of the fields
+ *
+ * Since: 0.10.24
+ */
+gboolean
+gst_structure_id_get_valist (const GstStructure * structure,
+ GQuark first_field_id, va_list args)
+{
+ GQuark field_id;
+ GType expected_type = G_TYPE_INVALID;
+
+ g_return_val_if_fail (GST_IS_STRUCTURE (structure), FALSE);
+ g_return_val_if_fail (first_field_id != 0, FALSE);
+
+ field_id = first_field_id;
+ while (field_id) {
+ const GValue *val = NULL;
+ gchar *err = NULL;
+
+ expected_type = va_arg (args, GType);
+
+ val = gst_structure_id_get_value (structure, field_id);
+
+ if (val == NULL)
+ goto no_such_field;
+
+ if (G_VALUE_TYPE (val) != expected_type)
+ goto wrong_type;
+
+ GST_VALUE_LCOPY (val, args, 0, &err, g_quark_to_string (field_id));
+ if (err) {
+ g_warning ("%s: %s", G_STRFUNC, err);
+ g_free (err);
+ return FALSE;
+ }
+
+ field_id = va_arg (args, GQuark);
+ }
+
+ return TRUE;
+
+/* ERRORS */
+no_such_field:
+ {
+ GST_WARNING ("Expected field '%s' in structure: %" GST_PTR_FORMAT,
+ GST_STR_NULL (g_quark_to_string (field_id)), structure);
+ return FALSE;
+ }
+wrong_type:
+ {
+ GST_WARNING ("Expected field '%s' in structure to be of type '%s', but "
+ "field was of type '%s': %" GST_PTR_FORMAT,
+ g_quark_to_string (field_id),
+ GST_STR_NULL (g_type_name (expected_type)),
+ G_VALUE_TYPE_NAME (gst_structure_id_get_value (structure, field_id)),
+ structure);
+ return FALSE;
+ }
+}
+
+/**
+ * gst_structure_get:
+ * @structure: a #GstStructure
+ * @first_fieldname: the name of the first field to read
+ * @...: variable arguments
+ *
+ * Parses the variable arguments and reads fields from @structure accordingly.
+ * Variable arguments should be in the form field name, field type
+ * (as a GType), pointer(s) to a variable(s) to hold the return value(s).
+ * The last variable argument should be NULL.
+ *
+ * For refcounted (mini)objects you will acquire your own reference which
+ * you must release with a suitable _unref() when no longer needed. For
+ * strings and boxed types you will acquire a copy which you will need to
+ * release with either g_free() or the suitable function for the boxed type.
+ *
+ * Returns: FALSE if there was a problem reading any of the fields (e.g.
+ * because the field requested did not exist, or was of a type other
+ * than the type specified), otherwise TRUE.
+ *
+ * Since: 0.10.24
+ */
+gboolean
+gst_structure_get (const GstStructure * structure, const char *first_fieldname,
+ ...)
+{
+ gboolean ret;
+ va_list args;
+
+ g_return_val_if_fail (GST_IS_STRUCTURE (structure), FALSE);
+ g_return_val_if_fail (first_fieldname != NULL, FALSE);
+
+ va_start (args, first_fieldname);
+ ret = gst_structure_get_valist (structure, first_fieldname, args);
+ va_end (args);
+
+ return ret;
+}
+
+/**
+ * gst_structure_id_get:
+ * @structure: a #GstStructure
+ * @first_field_id: the quark of the first field to read
+ * @...: variable arguments
+ *
+ * Parses the variable arguments and reads fields from @structure accordingly.
+ * Variable arguments should be in the form field id quark, field type
+ * (as a GType), pointer(s) to a variable(s) to hold the return value(s).
+ * The last variable argument should be NULL (technically it should be a
+ * 0 quark, but we require NULL so compilers that support it can check for
+ * the NULL terminator and warn if it's not there).
+ *
+ * This function is just like gst_structure_get() only that it is slightly
+ * more efficient since it saves the string-to-quark lookup in the global
+ * quark hashtable.
+ *
+ * For refcounted (mini)objects you will acquire your own reference which
+ * you must release with a suitable _unref() when no longer needed. For
+ * strings and boxed types you will acquire a copy which you will need to
+ * release with either g_free() or the suitable function for the boxed type.
+ *
+ * Returns: FALSE if there was a problem reading any of the fields (e.g.
+ * because the field requested did not exist, or was of a type other
+ * than the type specified), otherwise TRUE.
+ *
+ * Since: 0.10.24
+ */
+gboolean
+gst_structure_id_get (const GstStructure * structure, GQuark first_field_id,
+ ...)
+{
+ gboolean ret;
+ va_list args;
+
+ g_return_val_if_fail (GST_IS_STRUCTURE (structure), FALSE);
+ g_return_val_if_fail (first_field_id != 0, FALSE);
+
+ va_start (args, first_field_id);
+ ret = gst_structure_id_get_valist (structure, first_field_id, args);
+ va_end (args);
+
+ return ret;
+}
+
+static gboolean
+gst_structure_is_equal_foreach (GQuark field_id, const GValue * val2,
+ gpointer data)
+{
+ const GstStructure *struct1 = (const GstStructure *) data;
+ const GValue *val1 = gst_structure_id_get_value (struct1, field_id);
+
+ if (G_UNLIKELY (val1 == NULL))
+ return FALSE;
+ if (gst_value_compare (val1, val2) == GST_VALUE_EQUAL) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * gst_structure_is_equal:
+ * @structure1: a #GstStructure.
+ * @structure2: a #GstStructure.
+ *
+ * Tests if the two #GstStructure are equal.
+ *
+ * Returns: TRUE if the two structures have the same name and field.
+ *
+ * Since: 0.10.35
+ **/
+gboolean
+gst_structure_is_equal (const GstStructure * structure1,
+ const GstStructure * structure2)
+{
+ g_return_val_if_fail (GST_IS_STRUCTURE (structure1), FALSE);
+ g_return_val_if_fail (GST_IS_STRUCTURE (structure2), FALSE);
+
+ if (structure1->name != structure2->name) {
+ return FALSE;
+ }
+ if (GST_STRUCTURE_FIELDS (structure1)->len !=
+ GST_STRUCTURE_FIELDS (structure2)->len) {
+ return FALSE;
+ }
+
+ return gst_structure_foreach (structure1, gst_structure_is_equal_foreach,
+ (gpointer) structure2);
+}
+
+
+typedef struct
+{
+ GstStructure *dest;
+ const GstStructure *intersect;
+}
+IntersectData;
+
+static gboolean
+gst_structure_intersect_field1 (GQuark id, const GValue * val1, gpointer data)
+{
+ IntersectData *idata = (IntersectData *) data;
+ const GValue *val2 = gst_structure_id_get_value (idata->intersect, id);
+
+ if (G_UNLIKELY (val2 == NULL)) {
+ gst_structure_id_set_value (idata->dest, id, val1);
+ } else {
+ GValue dest_value = { 0 };
+ if (gst_value_intersect (&dest_value, val1, val2)) {
+ gst_structure_id_set_value (idata->dest, id, &dest_value);
+ g_value_unset (&dest_value);
+ } else {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+static gboolean
+gst_structure_intersect_field2 (GQuark id, const GValue * val1, gpointer data)
+{
+ IntersectData *idata = (IntersectData *) data;
+ const GValue *val2 = gst_structure_id_get_value (idata->intersect, id);
+
+ if (G_UNLIKELY (val2 == NULL)) {
+ gst_structure_id_set_value (idata->dest, id, val1);
+ }
+ return TRUE;
+}
+
+/**
+ * gst_structure_intersect:
+ * @struct1: a #GstStructure
+ * @struct2: a #GstStructure
+ *
+ * Interesects @struct1 and @struct2 and returns the intersection.
+ *
+ * Returns: Intersection of @struct1 and @struct2
+ *
+ * Since: 0.10.35
+ */
+GstStructure *
+gst_structure_intersect (const GstStructure * struct1,
+ const GstStructure * struct2)
+{
+ IntersectData data;
+
+ g_assert (struct1 != NULL);
+ g_assert (struct2 != NULL);
+
+ if (G_UNLIKELY (struct1->name != struct2->name))
+ return NULL;
+
+ /* copy fields from struct1 which we have not in struct2 to target
+ * intersect if we have the field in both */
+ data.dest = gst_structure_id_empty_new (struct1->name);
+ data.intersect = struct2;
+ if (G_UNLIKELY (!gst_structure_foreach ((GstStructure *) struct1,
+ gst_structure_intersect_field1, &data)))
+ goto error;
+
+ /* copy fields from struct2 which we have not in struct1 to target */
+ data.intersect = struct1;
+ if (G_UNLIKELY (!gst_structure_foreach ((GstStructure *) struct2,
+ gst_structure_intersect_field2, &data)))
+ goto error;
+
+ return data.dest;
+
+error:
+ gst_structure_free (data.dest);
+ return NULL;
+}
+
+static gboolean
+gst_caps_structure_can_intersect_field (GQuark id, const GValue * val1,
+ gpointer data)
+{
+ GstStructure *other = (GstStructure *) data;
+ const GValue *val2 = gst_structure_id_get_value (other, id);
+
+ if (G_LIKELY (val2)) {
+ if (!gst_value_can_intersect (val1, val2)) {
+ return FALSE;
+ } else {
+ gint eq = gst_value_compare (val1, val2);
+
+ if (eq == GST_VALUE_UNORDERED) {
+ /* we need to try interseting */
+ GValue dest_value = { 0 };
+ if (gst_value_intersect (&dest_value, val1, val2)) {
+ g_value_unset (&dest_value);
+ } else {
+ return FALSE;
+ }
+ } else if (eq != GST_VALUE_EQUAL) {
+ return FALSE;
+ }
+ }
+ }
+ return TRUE;
+}
+
+/**
+ * gst_structure_can_intersect:
+ * @struct1: a #GstStructure
+ * @struct2: a #GstStructure
+ *
+ * Tries intersecting @struct1 and @struct2 and reports whether the result
+ * would not be empty.
+ *
+ * Returns: %TRUE if intersection would not be empty
+ *
+ * Since: 0.10.35
+ */
+gboolean
+gst_structure_can_intersect (const GstStructure * struct1,
+ const GstStructure * struct2)
+{
+ g_return_val_if_fail (GST_IS_STRUCTURE (struct1), FALSE);
+ g_return_val_if_fail (GST_IS_STRUCTURE (struct2), FALSE);
+
+ if (G_UNLIKELY (struct1->name != struct2->name))
+ return FALSE;
+
+ /* tries to intersect if we have the field in both */
+ if (G_UNLIKELY (!gst_structure_foreach ((GstStructure *) struct1,
+ gst_caps_structure_can_intersect_field, (gpointer) struct2)))
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean
+gst_caps_structure_is_subset_field (GQuark field_id, const GValue * value,
+ gpointer user_data)
+{
+ GstStructure *superset = user_data;
+ GValue subtraction = { 0, };
+ const GValue *other;
+
+ if (!(other = gst_structure_id_get_value (superset, field_id)))
+ /* field is missing in the superset => is subset */
+ return TRUE;
+
+ /* equal values are subset */
+ if (gst_value_compare (other, value) == GST_VALUE_EQUAL)
+ return TRUE;
+
+ /*
+ * 1 - [1,2] = empty
+ * -> !subset
+ *
+ * [1,2] - 1 = 2
+ * -> 1 - [1,2] = empty
+ * -> subset
+ *
+ * [1,3] - [1,2] = 3
+ * -> [1,2] - [1,3] = empty
+ * -> subset
+ *
+ * {1,2} - {1,3} = 2
+ * -> {1,3} - {1,2} = 3
+ * -> !subset
+ *
+ * First caps subtraction needs to return a non-empty set, second
+ * subtractions needs to give en empty set.
+ */
+ if (gst_value_subtract (&subtraction, other, value)) {
+ g_value_unset (&subtraction);
+ /* !empty result, swapping must be empty */
+ if (!gst_value_subtract (&subtraction, value, other))
+ return TRUE;
+
+ g_value_unset (&subtraction);
+ }
+ return FALSE;
+}
+
+/**
+ * gst_structure_is_subset:
+ * @subset: a #GstStructure
+ * @superset: a potentially greater #GstStructure
+ *
+ * Checks if @subset is a subset of @superset, i.e. has the same
+ * structure name and for all fields that are existing in @superset,
+ * @subset has a value that is a subset of the value in @superset.
+ *
+ * Returns: %TRUE if @subset is a subset of @superset
+ *
+ * Since: 0.10.35
+ */
+gboolean
+gst_structure_is_subset (const GstStructure * subset,
+ const GstStructure * superset)
+{
+ if ((superset->name != subset->name) ||
+ (gst_structure_n_fields (superset) > gst_structure_n_fields (subset)))
+ return FALSE;
+
+ return gst_structure_foreach ((GstStructure *) subset,
+ gst_caps_structure_is_subset_field, (gpointer) superset);
+}
+
+
+/**
+ * gst_structure_fixate:
+ * @structure: a #GstStructure
+ *
+ * Fixate all values in @structure using gst_value_fixate().
+ * @structure will be modified in-place and should be writable.
+ */
+void
+gst_structure_fixate (GstStructure * structure)
+{
+ g_return_if_fail (GST_IS_STRUCTURE (structure));
+
+ gst_structure_foreach (structure, default_fixate, structure);
+}
diff --git a/gst/gststructure.h b/gst/gststructure.h
new file mode 100644
index 0000000..61e4253
--- /dev/null
+++ b/gst/gststructure.h
@@ -0,0 +1,258 @@
+/* GStreamer
+ * Copyright (C) 2003 David A. Schleef <ds@schleef.org>
+ *
+ * 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_STRUCTURE_H__
+#define __GST_STRUCTURE_H__
+
+#include <gst/gstconfig.h>
+#include <glib-object.h>
+#include <gst/gstclock.h>
+#include <gst/gstdatetime.h>
+#include <gst/glib-compat.h>
+
+G_BEGIN_DECLS
+
+extern GType _gst_structure_type;
+
+typedef struct _GstStructure GstStructure;
+
+#define GST_TYPE_STRUCTURE (_gst_structure_type)
+#define GST_IS_STRUCTURE(object) ((object) && (GST_STRUCTURE(object)->type == GST_TYPE_STRUCTURE))
+#define GST_STRUCTURE_CAST(object) ((GstStructure *)(object))
+#define GST_STRUCTURE(object) (GST_STRUCTURE_CAST(object))
+
+
+/**
+ * GstStructureForeachFunc:
+ * @field_id: the #GQuark of the field name
+ * @value: the #GValue of the field
+ * @user_data: user data
+ *
+ * A function that will be called in gst_structure_foreach(). The function may
+ * not modify @value.
+ *
+ * Returns: TRUE if the foreach operation should continue, FALSE if
+ * the foreach operation should stop with FALSE.
+ */
+typedef gboolean (*GstStructureForeachFunc) (GQuark field_id,
+ const GValue * value,
+ gpointer user_data);
+
+/**
+ * GstStructureMapFunc:
+ * @field_id: the #GQuark of the field name
+ * @value: the #GValue of the field
+ * @user_data: user data
+ *
+ * A function that will be called in gst_structure_map_in_place(). The function
+ * may modify @value.
+ *
+ * Returns: TRUE if the map operation should continue, FALSE if
+ * the map operation should stop with FALSE.
+ */
+typedef gboolean (*GstStructureMapFunc) (GQuark field_id,
+ GValue * value,
+ gpointer user_data);
+
+/**
+ * GstStructure:
+ * @type: the GType of a structure
+ *
+ * The GstStructure object. Most fields are private.
+ */
+struct _GstStructure {
+ GType type;
+
+ /*< private >*/
+ GQuark name;
+};
+
+GstStructure * gst_structure_empty_new (const gchar * name);
+GstStructure * gst_structure_id_empty_new (GQuark quark);
+GstStructure * gst_structure_new (const gchar * name,
+ const gchar * firstfield,
+ ...);
+GstStructure * gst_structure_new_valist (const gchar * name,
+ const gchar * firstfield,
+ va_list varargs);
+GstStructure * gst_structure_id_new (GQuark name_quark,
+ GQuark field_quark,
+ ...);
+GstStructure * gst_structure_copy (const GstStructure *structure);
+gboolean gst_structure_set_parent_refcount (GstStructure *structure,
+ gint *refcount);
+void gst_structure_free (GstStructure *structure);
+
+const gchar * gst_structure_get_name (const GstStructure *structure);
+GQuark gst_structure_get_name_id (const GstStructure *structure);
+gboolean gst_structure_has_name (const GstStructure *structure,
+ const gchar *name);
+void gst_structure_set_name (GstStructure *structure,
+ const gchar *name);
+
+void gst_structure_id_set_value (GstStructure *structure,
+ GQuark field,
+ const GValue *value);
+void gst_structure_set_value (GstStructure *structure,
+ const gchar *fieldname,
+ const GValue *value);
+void gst_structure_id_take_value (GstStructure *structure,
+ GQuark field,
+ GValue *value);
+void gst_structure_take_value (GstStructure *structure,
+ const gchar *fieldname,
+ GValue *value);
+void gst_structure_set (GstStructure *structure,
+ const gchar *fieldname,
+ ...) G_GNUC_NULL_TERMINATED;
+
+void gst_structure_set_valist (GstStructure *structure,
+ const gchar *fieldname,
+ va_list varargs);
+
+void gst_structure_id_set (GstStructure *structure,
+ GQuark fieldname,
+ ...) G_GNUC_NULL_TERMINATED;
+
+void gst_structure_id_set_valist (GstStructure *structure,
+ GQuark fieldname,
+ va_list varargs);
+
+gboolean gst_structure_get_valist (const GstStructure *structure,
+ const char *first_fieldname,
+ va_list args);
+
+gboolean gst_structure_get (const GstStructure *structure,
+ const char *first_fieldname,
+ ...) G_GNUC_NULL_TERMINATED;
+
+gboolean gst_structure_id_get_valist (const GstStructure *structure,
+ GQuark first_field_id,
+ va_list args);
+
+gboolean gst_structure_id_get (const GstStructure *structure,
+ GQuark first_field_id,
+ ...) G_GNUC_NULL_TERMINATED;
+
+const GValue * gst_structure_id_get_value (const GstStructure *structure,
+ GQuark field);
+const GValue * gst_structure_get_value (const GstStructure *structure,
+ const gchar *fieldname);
+void gst_structure_remove_field (GstStructure *structure,
+ const gchar *fieldname);
+void gst_structure_remove_fields (GstStructure *structure,
+ const gchar *fieldname,
+ ...) G_GNUC_NULL_TERMINATED;
+void gst_structure_remove_fields_valist (GstStructure *structure,
+ const gchar *fieldname,
+ va_list varargs);
+void gst_structure_remove_all_fields (GstStructure *structure);
+
+GType gst_structure_get_field_type (const GstStructure *structure,
+ const gchar *fieldname);
+gboolean gst_structure_foreach (const GstStructure *structure,
+ GstStructureForeachFunc func,
+ gpointer user_data);
+gboolean gst_structure_map_in_place (GstStructure *structure,
+ GstStructureMapFunc func,
+ gpointer user_data);
+gint gst_structure_n_fields (const GstStructure *structure);
+const gchar * gst_structure_nth_field_name (const GstStructure *structure, guint index);
+gboolean gst_structure_id_has_field (const GstStructure *structure,
+ GQuark field);
+gboolean gst_structure_id_has_field_typed (const GstStructure *structure,
+ GQuark field,
+ GType type);
+gboolean gst_structure_has_field (const GstStructure *structure,
+ const gchar *fieldname);
+gboolean gst_structure_has_field_typed (const GstStructure *structure,
+ const gchar *fieldname,
+ GType type);
+
+/* utility functions */
+gboolean gst_structure_get_boolean (const GstStructure *structure,
+ const gchar *fieldname,
+ gboolean *value);
+gboolean gst_structure_get_int (const GstStructure *structure,
+ const gchar *fieldname,
+ gint *value);
+gboolean gst_structure_get_uint (const GstStructure *structure,
+ const gchar *fieldname,
+ guint *value);
+gboolean gst_structure_get_double (const GstStructure *structure,
+ const gchar *fieldname,
+ gdouble *value);
+gboolean gst_structure_get_date (const GstStructure *structure,
+ const gchar *fieldname,
+ GDate **value);
+gboolean gst_structure_get_date_time (const GstStructure *structure,
+ const gchar *fieldname,
+ GstDateTime **value);
+gboolean gst_structure_get_clock_time (const GstStructure *structure,
+ const gchar *fieldname,
+ GstClockTime *value);
+const gchar * gst_structure_get_string (const GstStructure *structure,
+ const gchar *fieldname);
+gboolean gst_structure_get_enum (const GstStructure *structure,
+ const gchar *fieldname,
+ GType enumtype,
+ gint *value);
+gboolean gst_structure_get_fraction (const GstStructure *structure,
+ const gchar *fieldname,
+ gint *value_numerator,
+ gint *value_denominator);
+
+gchar * gst_structure_to_string (const GstStructure *structure);
+GstStructure * gst_structure_from_string (const gchar *string,
+ gchar **end);
+
+gboolean gst_structure_fixate_field_nearest_int (GstStructure *structure,
+ const char *field_name,
+ int target);
+gboolean gst_structure_fixate_field_nearest_double (GstStructure *structure,
+ const char *field_name,
+ double target);
+
+gboolean gst_structure_fixate_field_boolean (GstStructure *structure,
+ const char *field_name,
+ gboolean target);
+gboolean gst_structure_fixate_field_string (GstStructure *structure,
+ const char *field_name,
+ const gchar *target);
+gboolean gst_structure_fixate_field_nearest_fraction (GstStructure *structure,
+ const char *field_name,
+ const gint target_numerator,
+ const gint target_denominator);
+gboolean gst_structure_fixate_field (GstStructure *structure,
+ const char *field_name);
+
+gboolean gst_structure_is_equal(const GstStructure *structure1,
+ const GstStructure *structure2);
+gboolean gst_structure_is_subset(const GstStructure *subset,
+ const GstStructure *superset);
+gboolean gst_structure_can_intersect(const GstStructure *struct1,
+ const GstStructure *struct2);
+GstStructure* gst_structure_intersect (const GstStructure *struct1,
+ const GstStructure *struct2);
+void gst_structure_fixate (GstStructure *structure);
+
+G_END_DECLS
+
+#endif
+
diff --git a/gst/gstsystemclock.c b/gst/gstsystemclock.c
new file mode 100644
index 0000000..b592813
--- /dev/null
+++ b/gst/gstsystemclock.c
@@ -0,0 +1,875 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2004 Wim Taymans <wim@fluendo.com>
+ *
+ * gstsystemclock.c: Default clock, uses the system clock
+ *
+ * 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:gstsystemclock
+ * @short_description: Default clock that uses the current system time
+ * @see_also: #GstClock
+ *
+ * The GStreamer core provides a GstSystemClock based on the system time.
+ * Asynchronous callbacks are scheduled from an internal thread.
+ *
+ * Clock implementors are encouraged to subclass this systemclock as it
+ * implements the async notification.
+ *
+ * Subclasses can however override all of the important methods for sync and
+ * async notifications to implement their own callback methods or blocking
+ * wait operations.
+ *
+ * Last reviewed on 2006-03-08 (0.10.4)
+ */
+
+#include "gst_private.h"
+#include "gstinfo.h"
+#include "gstsystemclock.h"
+#include "gstenumtypes.h"
+#include "gstpoll.h"
+#include "gstutils.h"
+#include "glib-compat-private.h"
+
+#include <errno.h>
+
+#ifdef G_OS_WIN32
+# define WIN32_LEAN_AND_MEAN /* prevents from including too many things */
+# include <windows.h> /* QueryPerformance* stuff */
+# undef WIN32_LEAN_AND_MEAN
+# define EWOULDBLOCK EAGAIN /* This is just to placate gcc */
+#endif /* G_OS_WIN32 */
+
+#define GET_ENTRY_STATUS(e) ((GstClockReturn) g_atomic_int_get(&GST_CLOCK_ENTRY_STATUS(e)))
+#define SET_ENTRY_STATUS(e,val) (g_atomic_int_set(&GST_CLOCK_ENTRY_STATUS(e),(val)))
+#define CAS_ENTRY_STATUS(e,old,val) (G_ATOMIC_INT_COMPARE_AND_EXCHANGE(\
+ (&GST_CLOCK_ENTRY_STATUS(e)), (old), (val)))
+
+/* Define this to get some extra debug about jitter from each clock_wait */
+#undef WAIT_DEBUGGING
+
+struct _GstSystemClockPrivate
+{
+ GstClockType clock_type;
+ GstPoll *timer;
+ gint wakeup_count; /* the number of entries with a pending wakeup */
+ gboolean async_wakeup; /* if the wakeup was because of a async list change */
+
+#ifdef G_OS_WIN32
+ LARGE_INTEGER start;
+ LARGE_INTEGER frequency;
+#endif /* G_OS_WIN32 */
+};
+
+#define GST_SYSTEM_CLOCK_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_SYSTEM_CLOCK, \
+ GstSystemClockPrivate))
+
+#ifdef HAVE_POSIX_TIMERS
+# ifdef HAVE_MONOTONIC_CLOCK
+# define DEFAULT_CLOCK_TYPE GST_CLOCK_TYPE_MONOTONIC
+# else
+# define DEFAULT_CLOCK_TYPE GST_CLOCK_TYPE_REALTIME
+# endif
+#else
+#define DEFAULT_CLOCK_TYPE GST_CLOCK_TYPE_REALTIME
+#endif
+
+enum
+{
+ PROP_0,
+ PROP_CLOCK_TYPE,
+ /* FILL ME */
+};
+
+/* the one instance of the systemclock */
+static GstClock *_the_system_clock = NULL;
+
+static void gst_system_clock_dispose (GObject * object);
+static void gst_system_clock_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+static void gst_system_clock_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
+
+static GstClockTime gst_system_clock_get_internal_time (GstClock * clock);
+static guint64 gst_system_clock_get_resolution (GstClock * clock);
+static GstClockReturn gst_system_clock_id_wait_jitter (GstClock * clock,
+ GstClockEntry * entry, GstClockTimeDiff * jitter);
+static GstClockReturn gst_system_clock_id_wait_jitter_unlocked
+ (GstClock * clock, GstClockEntry * entry, GstClockTimeDiff * jitter,
+ gboolean restart);
+static GstClockReturn gst_system_clock_id_wait_async (GstClock * clock,
+ GstClockEntry * entry);
+static void gst_system_clock_id_unschedule (GstClock * clock,
+ GstClockEntry * entry);
+static void gst_system_clock_async_thread (GstClock * clock);
+static gboolean gst_system_clock_start_async (GstSystemClock * clock);
+static void gst_system_clock_add_wakeup (GstSystemClock * sysclock);
+
+static GStaticMutex _gst_sysclock_mutex = G_STATIC_MUTEX_INIT;
+
+static GstClockClass *parent_class = NULL;
+
+/* static guint gst_system_clock_signals[LAST_SIGNAL] = { 0 }; */
+
+G_DEFINE_TYPE (GstSystemClock, gst_system_clock, GST_TYPE_CLOCK);
+
+static void
+gst_system_clock_class_init (GstSystemClockClass * klass)
+{
+ GObjectClass *gobject_class;
+ GstClockClass *gstclock_class;
+
+ gobject_class = (GObjectClass *) klass;
+ gstclock_class = (GstClockClass *) klass;
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ g_type_class_add_private (klass, sizeof (GstSystemClockPrivate));
+
+ gobject_class->dispose = gst_system_clock_dispose;
+ gobject_class->set_property = gst_system_clock_set_property;
+ gobject_class->get_property = gst_system_clock_get_property;
+
+ g_object_class_install_property (gobject_class, PROP_CLOCK_TYPE,
+ g_param_spec_enum ("clock-type", "Clock type",
+ "The type of underlying clock implementation used",
+ GST_TYPE_CLOCK_TYPE, DEFAULT_CLOCK_TYPE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ gstclock_class->get_internal_time = gst_system_clock_get_internal_time;
+ gstclock_class->get_resolution = gst_system_clock_get_resolution;
+ gstclock_class->wait = gst_system_clock_id_wait_jitter;
+ gstclock_class->wait_async = gst_system_clock_id_wait_async;
+ gstclock_class->unschedule = gst_system_clock_id_unschedule;
+}
+
+static void
+gst_system_clock_init (GstSystemClock * clock)
+{
+ GST_OBJECT_FLAG_SET (clock,
+ GST_CLOCK_FLAG_CAN_DO_SINGLE_SYNC |
+ GST_CLOCK_FLAG_CAN_DO_SINGLE_ASYNC |
+ GST_CLOCK_FLAG_CAN_DO_PERIODIC_SYNC |
+ GST_CLOCK_FLAG_CAN_DO_PERIODIC_ASYNC);
+
+ clock->priv = GST_SYSTEM_CLOCK_GET_PRIVATE (clock);
+
+ clock->priv->clock_type = DEFAULT_CLOCK_TYPE;
+ clock->priv->timer = gst_poll_new_timer ();
+
+#ifdef G_OS_WIN32
+ QueryPerformanceFrequency (&clock->priv->frequency);
+ /* can be 0 if the hardware does not have hardware support */
+ if (clock->priv->frequency.QuadPart != 0)
+ /* we take a base time so that time starts from 0 to ease debugging */
+ QueryPerformanceCounter (&clock->priv->start);
+#endif /* G_OS_WIN32 */
+
+#if 0
+ /* Uncomment this to start the async clock thread straight away */
+ GST_OBJECT_LOCK (clock);
+ gst_system_clock_start_async (clock);
+ GST_OBJECT_UNLOCK (clock);
+#endif
+}
+
+static void
+gst_system_clock_dispose (GObject * object)
+{
+ GstClock *clock = (GstClock *) object;
+ GstSystemClock *sysclock = GST_SYSTEM_CLOCK_CAST (clock);
+ GList *entries;
+
+ /* else we have to stop the thread */
+ GST_OBJECT_LOCK (clock);
+ sysclock->stopping = TRUE;
+ /* unschedule all entries */
+ for (entries = clock->entries; entries; entries = g_list_next (entries)) {
+ GstClockEntry *entry = (GstClockEntry *) entries->data;
+
+ GST_CAT_DEBUG (GST_CAT_CLOCK, "unscheduling entry %p", entry);
+ SET_ENTRY_STATUS (entry, GST_CLOCK_UNSCHEDULED);
+ }
+ GST_CLOCK_BROADCAST (clock);
+ gst_system_clock_add_wakeup (sysclock);
+ GST_OBJECT_UNLOCK (clock);
+
+ if (sysclock->thread)
+ g_thread_join (sysclock->thread);
+ sysclock->thread = NULL;
+ GST_CAT_DEBUG (GST_CAT_CLOCK, "joined thread");
+
+ g_list_foreach (clock->entries, (GFunc) gst_clock_id_unref, NULL);
+ g_list_free (clock->entries);
+ clock->entries = NULL;
+
+ gst_poll_free (sysclock->priv->timer);
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+
+ if (_the_system_clock == clock) {
+ _the_system_clock = NULL;
+ GST_CAT_DEBUG (GST_CAT_CLOCK, "disposed system clock");
+ }
+}
+
+static void
+gst_system_clock_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstSystemClock *sysclock = GST_SYSTEM_CLOCK (object);
+
+ switch (prop_id) {
+ case PROP_CLOCK_TYPE:
+ sysclock->priv->clock_type = (GstClockType) g_value_get_enum (value);
+ GST_CAT_DEBUG (GST_CAT_CLOCK, "clock-type set to %d",
+ sysclock->priv->clock_type);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_system_clock_get_property (GObject * object, guint prop_id, GValue * value,
+ GParamSpec * pspec)
+{
+ GstSystemClock *sysclock = GST_SYSTEM_CLOCK (object);
+
+ switch (prop_id) {
+ case PROP_CLOCK_TYPE:
+ g_value_set_enum (value, sysclock->priv->clock_type);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+/**
+ * gst_system_clock_obtain:
+ *
+ * Get a handle to the default system clock. The refcount of the
+ * clock will be increased so you need to unref the clock after
+ * usage.
+ *
+ * Returns: (transfer full): the default clock.
+ *
+ * MT safe.
+ */
+GstClock *
+gst_system_clock_obtain (void)
+{
+ GstClock *clock;
+
+ g_static_mutex_lock (&_gst_sysclock_mutex);
+ clock = _the_system_clock;
+
+ if (clock == NULL) {
+ GST_CAT_DEBUG (GST_CAT_CLOCK, "creating new static system clock");
+ clock = g_object_new (GST_TYPE_SYSTEM_CLOCK,
+ "name", "GstSystemClock", NULL);
+
+ /* we created the global clock; take ownership so
+ * we can hand out instances later */
+ gst_object_ref_sink (clock);
+
+ _the_system_clock = clock;
+ g_static_mutex_unlock (&_gst_sysclock_mutex);
+ } else {
+ g_static_mutex_unlock (&_gst_sysclock_mutex);
+ GST_CAT_DEBUG (GST_CAT_CLOCK, "returning static system clock");
+ }
+
+ /* we ref it since we are a clock factory. */
+ gst_object_ref (clock);
+ return clock;
+}
+
+static void
+gst_system_clock_remove_wakeup (GstSystemClock * sysclock)
+{
+ g_return_if_fail (sysclock->priv->wakeup_count > 0);
+
+ sysclock->priv->wakeup_count--;
+ if (sysclock->priv->wakeup_count == 0) {
+ /* read the control socket byte when we removed the last wakeup count */
+ GST_CAT_DEBUG (GST_CAT_CLOCK, "reading control");
+ while (!gst_poll_read_control (sysclock->priv->timer)) {
+ g_warning ("gstsystemclock: read control failed, trying again\n");
+ }
+ GST_CLOCK_BROADCAST (sysclock);
+ }
+ GST_CAT_DEBUG (GST_CAT_CLOCK, "wakeup count %d",
+ sysclock->priv->wakeup_count);
+}
+
+static void
+gst_system_clock_add_wakeup (GstSystemClock * sysclock)
+{
+ /* only write the control socket for the first wakeup */
+ if (sysclock->priv->wakeup_count == 0) {
+ GST_CAT_DEBUG (GST_CAT_CLOCK, "writing control");
+ while (!gst_poll_write_control (sysclock->priv->timer)) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
+ g_warning
+ ("gstsystemclock: write control failed in wakeup_async, trying again: %d:%s\n",
+ errno, g_strerror (errno));
+ } else {
+ g_critical
+ ("gstsystemclock: write control failed in wakeup_async: %d:%s\n",
+ errno, g_strerror (errno));
+ return;
+ }
+ }
+ }
+ sysclock->priv->wakeup_count++;
+ GST_CAT_DEBUG (GST_CAT_CLOCK, "wakeup count %d",
+ sysclock->priv->wakeup_count);
+}
+
+static void
+gst_system_clock_wait_wakeup (GstSystemClock * sysclock)
+{
+ while (sysclock->priv->wakeup_count > 0) {
+ GST_CLOCK_WAIT (sysclock);
+ }
+}
+
+/* this thread reads the sorted clock entries from the queue.
+ *
+ * It waits on each of them and fires the callback when the timeout occurs.
+ *
+ * When an entry in the queue was canceled before we wait for it, it is
+ * simply skipped.
+ *
+ * When waiting for an entry, it can become canceled, in that case we don't
+ * call the callback but move to the next item in the queue.
+ *
+ * MT safe.
+ */
+static void
+gst_system_clock_async_thread (GstClock * clock)
+{
+ GstSystemClock *sysclock = GST_SYSTEM_CLOCK_CAST (clock);
+
+ GST_CAT_DEBUG (GST_CAT_CLOCK, "enter system clock thread");
+ GST_OBJECT_LOCK (clock);
+ /* signal spinup */
+ GST_CLOCK_BROADCAST (clock);
+ /* now enter our (almost) infinite loop */
+ while (!sysclock->stopping) {
+ GstClockEntry *entry;
+ GstClockTime requested;
+ GstClockReturn res;
+
+ /* check if something to be done */
+ while (clock->entries == NULL) {
+ GST_CAT_DEBUG (GST_CAT_CLOCK, "no clock entries, waiting..");
+ /* wait for work to do */
+ GST_CLOCK_WAIT (clock);
+ GST_CAT_DEBUG (GST_CAT_CLOCK, "got signal");
+ /* clock was stopping, exit */
+ if (sysclock->stopping)
+ goto exit;
+ }
+
+ /* see if we have a pending wakeup because the order of the list
+ * changed. */
+ if (sysclock->priv->async_wakeup) {
+ GST_CAT_DEBUG (GST_CAT_CLOCK, "clear async wakeup");
+ gst_system_clock_remove_wakeup (sysclock);
+ sysclock->priv->async_wakeup = FALSE;
+ }
+
+ /* pick the next entry */
+ entry = clock->entries->data;
+ GST_OBJECT_UNLOCK (clock);
+
+ requested = entry->time;
+
+ /* now wait for the entry, we already hold the lock */
+ res =
+ gst_system_clock_id_wait_jitter_unlocked (clock, (GstClockID) entry,
+ NULL, FALSE);
+
+ GST_OBJECT_LOCK (clock);
+
+ switch (res) {
+ case GST_CLOCK_UNSCHEDULED:
+ /* entry was unscheduled, move to the next */
+ GST_CAT_DEBUG (GST_CAT_CLOCK, "async entry %p unscheduled", entry);
+ goto next_entry;
+ case GST_CLOCK_OK:
+ case GST_CLOCK_EARLY:
+ {
+ /* entry timed out normally, fire the callback and move to the next
+ * entry */
+ GST_CAT_DEBUG (GST_CAT_CLOCK, "async entry %p timed out", entry);
+ if (entry->func) {
+ /* unlock before firing the callback */
+ GST_OBJECT_UNLOCK (clock);
+ entry->func (clock, entry->time, (GstClockID) entry,
+ entry->user_data);
+ GST_OBJECT_LOCK (clock);
+ }
+ if (entry->type == GST_CLOCK_ENTRY_PERIODIC) {
+ GST_CAT_DEBUG (GST_CAT_CLOCK, "updating periodic entry %p", entry);
+ /* adjust time now */
+ entry->time = requested + entry->interval;
+ /* and resort the list now */
+ clock->entries =
+ g_list_sort (clock->entries, gst_clock_id_compare_func);
+ /* and restart */
+ continue;
+ } else {
+ GST_CAT_DEBUG (GST_CAT_CLOCK, "moving to next entry");
+ goto next_entry;
+ }
+ }
+ case GST_CLOCK_BUSY:
+ /* somebody unlocked the entry but is was not canceled, This means that
+ * either a new entry was added in front of the queue or some other entry
+ * was canceled. Whatever it is, pick the head entry of the list and
+ * continue waiting. */
+ GST_CAT_DEBUG (GST_CAT_CLOCK, "async entry %p needs restart", entry);
+
+ /* we set the entry back to the OK state. This is needed so that the
+ * _unschedule() code can see if an entry is currently being waited
+ * on (when its state is BUSY). */
+ SET_ENTRY_STATUS (entry, GST_CLOCK_OK);
+ continue;
+ default:
+ GST_CAT_DEBUG (GST_CAT_CLOCK,
+ "strange result %d waiting for %p, skipping", res, entry);
+ g_warning ("%s: strange result %d waiting for %p, skipping",
+ GST_OBJECT_NAME (clock), res, entry);
+ goto next_entry;
+ }
+ next_entry:
+ /* we remove the current entry and unref it */
+ clock->entries = g_list_remove (clock->entries, entry);
+ gst_clock_id_unref ((GstClockID) entry);
+ }
+exit:
+ /* signal exit */
+ GST_CLOCK_BROADCAST (clock);
+ GST_OBJECT_UNLOCK (clock);
+ GST_CAT_DEBUG (GST_CAT_CLOCK, "exit system clock thread");
+}
+
+#ifdef HAVE_POSIX_TIMERS
+static inline clockid_t
+clock_type_to_posix_id (GstClockType clock_type)
+{
+#ifdef HAVE_MONOTONIC_CLOCK
+ if (clock_type == GST_CLOCK_TYPE_MONOTONIC)
+ return CLOCK_MONOTONIC;
+ else
+#endif
+ return CLOCK_REALTIME;
+}
+#endif
+
+/* MT safe */
+static GstClockTime
+gst_system_clock_get_internal_time (GstClock * clock)
+{
+#ifdef G_OS_WIN32
+ GstSystemClock *sysclock = GST_SYSTEM_CLOCK_CAST (clock);
+
+ if (sysclock->priv->frequency.QuadPart != 0) {
+ LARGE_INTEGER now;
+
+ /* we prefer the highly accurate performance counters on windows */
+ QueryPerformanceCounter (&now);
+
+ return gst_util_uint64_scale (now.QuadPart - sysclock->priv->start.QuadPart,
+ GST_SECOND, sysclock->priv->frequency.QuadPart);
+ } else
+#endif /* G_OS_WIN32 */
+#if !defined HAVE_POSIX_TIMERS
+ {
+ GTimeVal timeval;
+
+ g_get_current_time (&timeval);
+
+ return GST_TIMEVAL_TO_TIME (timeval);
+ }
+#else
+ {
+ GstSystemClock *sysclock = GST_SYSTEM_CLOCK_CAST (clock);
+ clockid_t ptype;
+ struct timespec ts;
+
+ ptype = clock_type_to_posix_id (sysclock->priv->clock_type);
+
+ if (G_UNLIKELY (clock_gettime (ptype, &ts)))
+ return GST_CLOCK_TIME_NONE;
+
+ return GST_TIMESPEC_TO_TIME (ts);
+ }
+#endif
+}
+
+static guint64
+gst_system_clock_get_resolution (GstClock * clock)
+{
+#ifdef G_OS_WIN32
+ GstSystemClock *sysclock = GST_SYSTEM_CLOCK_CAST (clock);
+
+ if (sysclock->priv->frequency.QuadPart != 0) {
+ return GST_SECOND / sysclock->priv->frequency.QuadPart;
+ } else
+#endif /* G_OS_WIN32 */
+#ifdef HAVE_POSIX_TIMERS
+ {
+ GstSystemClock *sysclock = GST_SYSTEM_CLOCK_CAST (clock);
+ clockid_t ptype;
+ struct timespec ts;
+
+ ptype = clock_type_to_posix_id (sysclock->priv->clock_type);
+
+ if (G_UNLIKELY (clock_getres (ptype, &ts)))
+ return GST_CLOCK_TIME_NONE;
+
+ return GST_TIMESPEC_TO_TIME (ts);
+ }
+#else
+ {
+ return 1 * GST_USECOND;
+ }
+#endif
+}
+
+/* synchronously wait on the given GstClockEntry.
+ *
+ * We do this by blocking on the global GstPoll timer with
+ * the requested timeout. This allows us to unblock the
+ * entry by writing on the control fd.
+ *
+ * Note that writing the global GstPoll unlocks all waiting entries. So
+ * we need to check if an unlocked entry has changed when it unlocks.
+ *
+ * Entries that arrive too late are simply not waited on and a
+ * GST_CLOCK_EARLY result is returned.
+ *
+ * MT safe.
+ */
+static GstClockReturn
+gst_system_clock_id_wait_jitter_unlocked (GstClock * clock,
+ GstClockEntry * entry, GstClockTimeDiff * jitter, gboolean restart)
+{
+ GstSystemClock *sysclock = GST_SYSTEM_CLOCK_CAST (clock);
+ GstClockTime entryt, now;
+ GstClockTimeDiff diff;
+ GstClockReturn status;
+
+ if (G_UNLIKELY (GET_ENTRY_STATUS (entry) == GST_CLOCK_UNSCHEDULED))
+ return GST_CLOCK_UNSCHEDULED;
+
+ /* need to call the overridden method because we want to sync against the time
+ * of the clock, whatever the subclass uses as a clock. */
+ now = gst_clock_get_time (clock);
+
+ /* get the time of the entry */
+ entryt = GST_CLOCK_ENTRY_TIME (entry);
+
+ /* the diff of the entry with the clock is the amount of time we have to
+ * wait */
+ diff = GST_CLOCK_DIFF (now, entryt);
+ if (G_LIKELY (jitter))
+ *jitter = -diff;
+
+ GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p"
+ " time %" GST_TIME_FORMAT
+ " now %" GST_TIME_FORMAT
+ " diff (time-now) %" G_GINT64_FORMAT,
+ entry, GST_TIME_ARGS (entryt), GST_TIME_ARGS (now), diff);
+
+ if (G_LIKELY (diff > 0)) {
+#ifdef WAIT_DEBUGGING
+ GstClockTime final;
+#endif
+
+ while (TRUE) {
+ gint pollret;
+
+ do {
+ status = GET_ENTRY_STATUS (entry);
+
+ /* stop when we are unscheduled */
+ if (G_UNLIKELY (status == GST_CLOCK_UNSCHEDULED))
+ goto done;
+
+ /* mark the entry as busy but watch out for intermediate unscheduled
+ * statuses */
+ } while (G_UNLIKELY (!CAS_ENTRY_STATUS (entry, status, GST_CLOCK_BUSY)));
+
+ /* now wait on the entry, it either times out or the fd is written. The
+ * status of the entry is only BUSY around the poll. */
+ pollret = gst_poll_wait (sysclock->priv->timer, diff);
+
+ /* get the new status, mark as DONE. We do this so that the unschedule
+ * function knows when we left the poll and doesn't need to wakeup the
+ * poll anymore. */
+ do {
+ status = GET_ENTRY_STATUS (entry);
+ /* we were unscheduled, exit immediately */
+ if (G_UNLIKELY (status == GST_CLOCK_UNSCHEDULED))
+ break;
+ } while (G_UNLIKELY (!CAS_ENTRY_STATUS (entry, status, GST_CLOCK_DONE)));
+
+ GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p unlocked, status %d, ret %d",
+ entry, status, pollret);
+
+ if (G_UNLIKELY (status == GST_CLOCK_UNSCHEDULED)) {
+ /* try to clean up The unschedule function managed to set the status to
+ * unscheduled. We now take the lock and mark the entry as unscheduled.
+ * This makes sure that the unschedule function doesn't perform a
+ * wakeup anymore. If the unschedule function has a change to perform
+ * the wakeup before us, we clean up here */
+ GST_OBJECT_LOCK (sysclock);
+ entry->unscheduled = TRUE;
+ if (entry->woken_up) {
+ gst_system_clock_remove_wakeup (sysclock);
+ }
+ GST_OBJECT_UNLOCK (sysclock);
+ goto done;
+ } else {
+ if (G_UNLIKELY (pollret != 0)) {
+ /* some other id got unlocked */
+ if (!restart) {
+ /* this can happen if the entry got unlocked because of an async
+ * entry was added to the head of the async queue. */
+ GST_CAT_DEBUG (GST_CAT_CLOCK, "wakeup waiting for entry %p", entry);
+ goto done;
+ }
+
+ /* wait till all the entries got woken up */
+ GST_OBJECT_LOCK (sysclock);
+ gst_system_clock_wait_wakeup (sysclock);
+ GST_OBJECT_UNLOCK (sysclock);
+
+ GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p needs to be restarted",
+ entry);
+ } else {
+ GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p unlocked after timeout",
+ entry);
+ }
+
+ /* reschedule if gst_poll_wait returned early or we have to reschedule after
+ * an unlock*/
+ now = gst_clock_get_time (clock);
+ diff = GST_CLOCK_DIFF (now, entryt);
+
+ if (diff <= 0) {
+ /* timeout, this is fine, we can report success now */
+ status = GST_CLOCK_OK;
+ SET_ENTRY_STATUS (entry, status);
+
+ GST_CAT_DEBUG (GST_CAT_CLOCK,
+ "entry %p finished, diff %" G_GINT64_FORMAT, entry, diff);
+
+#ifdef WAIT_DEBUGGING
+ final = gst_system_clock_get_internal_time (clock);
+ GST_CAT_DEBUG (GST_CAT_CLOCK, "Waited for %" G_GINT64_FORMAT
+ " got %" G_GINT64_FORMAT " diff %" G_GINT64_FORMAT
+ " %g target-offset %" G_GINT64_FORMAT " %g", entryt, now,
+ now - entryt,
+ (double) (GstClockTimeDiff) (now - entryt) / GST_SECOND,
+ (final - target),
+ ((double) (GstClockTimeDiff) (final - target)) / GST_SECOND);
+#endif
+ goto done;
+ } else {
+ GST_CAT_DEBUG (GST_CAT_CLOCK,
+ "entry %p restart, diff %" G_GINT64_FORMAT, entry, diff);
+ }
+ }
+ }
+ } else {
+ /* we are right on time or too late */
+ if (G_UNLIKELY (diff == 0))
+ status = GST_CLOCK_OK;
+ else
+ status = GST_CLOCK_EARLY;
+
+ SET_ENTRY_STATUS (entry, status);
+ }
+done:
+ return status;
+}
+
+static GstClockReturn
+gst_system_clock_id_wait_jitter (GstClock * clock, GstClockEntry * entry,
+ GstClockTimeDiff * jitter)
+{
+ return gst_system_clock_id_wait_jitter_unlocked (clock, entry, jitter, TRUE);
+}
+
+/* Start the async clock thread. Must be called with the object lock
+ * held */
+static gboolean
+gst_system_clock_start_async (GstSystemClock * clock)
+{
+ GError *error = NULL;
+
+ if (G_LIKELY (clock->thread != NULL))
+ return TRUE; /* Thread already running. Nothing to do */
+
+ clock->thread = g_thread_create ((GThreadFunc) gst_system_clock_async_thread,
+ clock, TRUE, &error);
+ if (G_UNLIKELY (error))
+ goto no_thread;
+
+ /* wait for it to spin up */
+ GST_CLOCK_WAIT (clock);
+
+ return TRUE;
+
+ /* ERRORS */
+no_thread:
+ {
+ g_warning ("could not create async clock thread: %s", error->message);
+ g_error_free (error);
+ }
+ return FALSE;
+}
+
+/* Add an entry to the list of pending async waits. The entry is inserted
+ * in sorted order. If we inserted the entry at the head of the list, we
+ * need to signal the thread as it might either be waiting on it or waiting
+ * for a new entry.
+ *
+ * MT safe.
+ */
+static GstClockReturn
+gst_system_clock_id_wait_async (GstClock * clock, GstClockEntry * entry)
+{
+ GstSystemClock *sysclock;
+ GstClockEntry *head;
+
+ sysclock = GST_SYSTEM_CLOCK_CAST (clock);
+
+ GST_CAT_DEBUG (GST_CAT_CLOCK, "adding async entry %p", entry);
+
+ GST_OBJECT_LOCK (clock);
+ /* Start the clock async thread if needed */
+ if (G_UNLIKELY (!gst_system_clock_start_async (sysclock)))
+ goto thread_error;
+
+ if (G_UNLIKELY (GET_ENTRY_STATUS (entry) == GST_CLOCK_UNSCHEDULED))
+ goto was_unscheduled;
+
+ if (clock->entries)
+ head = clock->entries->data;
+ else
+ head = NULL;
+
+ /* need to take a ref */
+ gst_clock_id_ref ((GstClockID) entry);
+ /* insert the entry in sorted order */
+ clock->entries = g_list_insert_sorted (clock->entries, entry,
+ gst_clock_id_compare_func);
+
+ /* only need to send the signal if the entry was added to the
+ * front, else the thread is just waiting for another entry and
+ * will get to this entry automatically. */
+ if (clock->entries->data == entry) {
+ GST_CAT_DEBUG (GST_CAT_CLOCK, "async entry added to head %p", head);
+ if (head == NULL) {
+ /* the list was empty before, signal the cond so that the async thread can
+ * start taking a look at the queue */
+ GST_CAT_DEBUG (GST_CAT_CLOCK, "first entry, sending signal");
+ GST_CLOCK_BROADCAST (clock);
+ } else {
+ GstClockReturn status;
+
+ status = GET_ENTRY_STATUS (head);
+ GST_CAT_DEBUG (GST_CAT_CLOCK, "head entry %p status %d", head, status);
+
+ if (status == GST_CLOCK_BUSY) {
+ GST_CAT_DEBUG (GST_CAT_CLOCK, "head entry is busy");
+ /* the async thread was waiting for an entry, unlock the wait so that it
+ * looks at the new head entry instead, we only need to do this once */
+ if (!sysclock->priv->async_wakeup) {
+ GST_CAT_DEBUG (GST_CAT_CLOCK, "wakeup async thread");
+ sysclock->priv->async_wakeup = TRUE;
+ gst_system_clock_add_wakeup (sysclock);
+ }
+ }
+ }
+ }
+ GST_OBJECT_UNLOCK (clock);
+
+ return GST_CLOCK_OK;
+
+ /* ERRORS */
+thread_error:
+ {
+ /* Could not start the async clock thread */
+ GST_OBJECT_UNLOCK (clock);
+ return GST_CLOCK_ERROR;
+ }
+was_unscheduled:
+ {
+ GST_OBJECT_UNLOCK (clock);
+ return GST_CLOCK_UNSCHEDULED;
+ }
+}
+
+/* unschedule an entry. This will set the state of the entry to GST_CLOCK_UNSCHEDULED
+ * and will signal any thread waiting for entries to recheck their entry.
+ * We cannot really decide if the signal is needed or not because the entry
+ * could be waited on in async or sync mode.
+ *
+ * MT safe.
+ */
+static void
+gst_system_clock_id_unschedule (GstClock * clock, GstClockEntry * entry)
+{
+ GstSystemClock *sysclock;
+ GstClockReturn status;
+
+ sysclock = GST_SYSTEM_CLOCK_CAST (clock);
+
+ GST_CAT_DEBUG (GST_CAT_CLOCK, "unscheduling entry %p", entry);
+
+ GST_OBJECT_LOCK (clock);
+ /* change the entry status to unscheduled */
+ do {
+ status = GET_ENTRY_STATUS (entry);
+ } while (G_UNLIKELY (!CAS_ENTRY_STATUS (entry, status,
+ GST_CLOCK_UNSCHEDULED)));
+
+ if (G_LIKELY (status == GST_CLOCK_BUSY)) {
+ /* the entry was being busy, wake up all entries so that they recheck their
+ * status. We cannot wake up just one entry because allocating such a
+ * datastructure for each entry would be too heavy and unlocking an entry
+ * is usually done when shutting down or some other exceptional case. */
+ GST_CAT_DEBUG (GST_CAT_CLOCK, "entry was BUSY, doing wakeup");
+ if (!entry->unscheduled && !entry->woken_up) {
+ gst_system_clock_add_wakeup (sysclock);
+ entry->woken_up = TRUE;
+ }
+ }
+ GST_OBJECT_UNLOCK (clock);
+}
diff --git a/gst/gstsystemclock.h b/gst/gstsystemclock.h
new file mode 100644
index 0000000..490f700
--- /dev/null
+++ b/gst/gstsystemclock.h
@@ -0,0 +1,89 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2000 Wim Taymans <wtay@chello.be>
+ * 2005 Wim Taymans <wim@fluendo.com>
+ *
+ * gstsystemclock.h: A clock implementation based on system time
+ *
+ * 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_SYSTEM_CLOCK_H__
+#define __GST_SYSTEM_CLOCK_H__
+
+#include <gst/gstclock.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_SYSTEM_CLOCK (gst_system_clock_get_type ())
+#define GST_SYSTEM_CLOCK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_SYSTEM_CLOCK, GstSystemClock))
+#define GST_SYSTEM_CLOCK_CAST(obj) ((GstSystemClock *)(obj))
+#define GST_IS_SYSTEM_CLOCK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_SYSTEM_CLOCK))
+#define GST_SYSTEM_CLOCK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_SYSTEM_CLOCK, GstSystemClockClass))
+#define GST_IS_SYSTEM_CLOCK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_SYSTEM_CLOCK))
+#define GST_SYSTEM_CLOCK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_SYSTEM_CLOCK, GstSystemClockClass))
+
+
+typedef struct _GstSystemClock GstSystemClock;
+typedef struct _GstSystemClockClass GstSystemClockClass;
+typedef struct _GstSystemClockPrivate GstSystemClockPrivate;
+
+/**
+ * GstClockType:
+ * @GST_CLOCK_TYPE_REALTIME: time since Epoch
+ * @GST_CLOCK_TYPE_MONOTONIC: monotonic time since some unspecified starting
+ * point
+ *
+ * The different kind of clocks.
+ */
+typedef enum {
+ GST_CLOCK_TYPE_REALTIME = 0,
+ GST_CLOCK_TYPE_MONOTONIC = 1
+} GstClockType;
+
+/**
+ * GstSystemClock:
+ *
+ * The default implementation of a #GstClock that uses the system time.
+ */
+struct _GstSystemClock {
+ GstClock clock;
+
+ /*< private >*/
+ GThread *thread; /* thread for async notify */
+ gboolean stopping;
+
+ /* ABI added */
+ GstSystemClockPrivate *priv;
+
+ gpointer _gst_reserved[GST_PADDING];
+};
+
+struct _GstSystemClockClass {
+ GstClockClass parent_class;
+
+ /*< private >*/
+ gpointer _gst_reserved[GST_PADDING];
+};
+
+GType gst_system_clock_get_type (void);
+
+GstClock* gst_system_clock_obtain (void);
+
+G_END_DECLS
+
+#endif /* __GST_SYSTEM_CLOCK_H__ */
diff --git a/gst/gsttaglist.c b/gst/gsttaglist.c
new file mode 100644
index 0000000..b62fbc7
--- /dev/null
+++ b/gst/gsttaglist.c
@@ -0,0 +1,1885 @@
+/* GStreamer
+ * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
+ *
+ * gsttaglist.c: tag support (aka metadata)
+ *
+ * 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:gsttaglist
+ * @short_description: List of tags and values used to describe media metadata
+ *
+ * List of tags and values used to describe media metadata.
+ *
+ * Strings must be in ASCII or UTF-8 encoding. No other encodings are allowed.
+ *
+ * Last reviewed on 2009-06-09 (0.10.23)
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "gst_private.h"
+#include "gst-i18n-lib.h"
+#include "gsttaglist.h"
+#include "gstinfo.h"
+#include "gstvalue.h"
+#include "gstbuffer.h"
+#include "gstquark.h"
+
+#include <gobject/gvaluecollector.h>
+#include <string.h>
+
+#define GST_TAG_IS_VALID(tag) (gst_tag_get_info (tag) != NULL)
+
+/* FIXME 0.11: use GParamSpecs or something similar for tag registrations,
+ * possibly even gst_tag_register(). Especially value ranges might be
+ * useful for some tags. */
+
+typedef struct
+{
+ GType type; /* type the data is in */
+
+ gchar *nick; /* translated name */
+ gchar *blurb; /* translated description of type */
+
+ GstTagMergeFunc merge_func; /* functions to merge the values */
+ GstTagFlag flag; /* type of tag */
+}
+GstTagInfo;
+
+static GMutex *__tag_mutex;
+
+static GHashTable *__tags;
+
+#define TAG_LOCK g_mutex_lock (__tag_mutex)
+#define TAG_UNLOCK g_mutex_unlock (__tag_mutex)
+
+GType
+gst_tag_list_get_type (void)
+{
+ static GType _gst_tag_list_type = 0;
+
+ if (G_UNLIKELY (_gst_tag_list_type == 0)) {
+ _gst_tag_list_type = g_boxed_type_register_static ("GstTagList",
+ (GBoxedCopyFunc) gst_tag_list_copy, (GBoxedFreeFunc) gst_tag_list_free);
+
+#if 0
+ g_value_register_transform_func (_gst_tag_list_type, G_TYPE_STRING,
+ _gst_structure_transform_to_string);
+#endif
+ }
+
+ return _gst_tag_list_type;
+}
+
+void
+_priv_gst_tag_initialize (void)
+{
+ __tag_mutex = g_mutex_new ();
+ __tags = g_hash_table_new (g_direct_hash, g_direct_equal);
+ gst_tag_register (GST_TAG_TITLE, GST_TAG_FLAG_META,
+ G_TYPE_STRING,
+ _("title"), _("commonly used title"), gst_tag_merge_strings_with_comma);
+ gst_tag_register (GST_TAG_TITLE_SORTNAME, GST_TAG_FLAG_META,
+ G_TYPE_STRING,
+ _("title sortname"), _("commonly used title for sorting purposes"), NULL);
+ gst_tag_register (GST_TAG_ARTIST, GST_TAG_FLAG_META,
+ G_TYPE_STRING,
+ _("artist"),
+ _("person(s) responsible for the recording"),
+ gst_tag_merge_strings_with_comma);
+ gst_tag_register (GST_TAG_ARTIST_SORTNAME, GST_TAG_FLAG_META,
+ G_TYPE_STRING,
+ _("artist sortname"),
+ _("person(s) responsible for the recording for sorting purposes"), NULL);
+ gst_tag_register (GST_TAG_ALBUM, GST_TAG_FLAG_META,
+ G_TYPE_STRING,
+ _("album"),
+ _("album containing this data"), gst_tag_merge_strings_with_comma);
+ gst_tag_register (GST_TAG_ALBUM_SORTNAME, GST_TAG_FLAG_META,
+ G_TYPE_STRING,
+ _("album sortname"),
+ _("album containing this data for sorting purposes"), NULL);
+ gst_tag_register (GST_TAG_ALBUM_ARTIST, GST_TAG_FLAG_META,
+ G_TYPE_STRING,
+ _("album artist"),
+ _("The artist of the entire album, as it should be displayed"),
+ gst_tag_merge_strings_with_comma);
+ gst_tag_register (GST_TAG_ALBUM_ARTIST_SORTNAME, GST_TAG_FLAG_META,
+ G_TYPE_STRING,
+ _("album artist sortname"),
+ _("The artist of the entire album, as it should be sorted"), NULL);
+ gst_tag_register (GST_TAG_DATE, GST_TAG_FLAG_META, GST_TYPE_DATE,
+ _("date"), _("date the data was created (as a GDate structure)"), NULL);
+ gst_tag_register (GST_TAG_DATE_TIME, GST_TAG_FLAG_META, GST_TYPE_DATE_TIME,
+ _("datetime"),
+ _("date and time the data was created (as a GstDateTime structure)"),
+ NULL);
+ gst_tag_register (GST_TAG_GENRE, GST_TAG_FLAG_META,
+ G_TYPE_STRING,
+ _("genre"),
+ _("genre this data belongs to"), gst_tag_merge_strings_with_comma);
+ gst_tag_register (GST_TAG_COMMENT, GST_TAG_FLAG_META,
+ G_TYPE_STRING,
+ _("comment"),
+ _("free text commenting the data"), gst_tag_merge_use_first);
+ gst_tag_register (GST_TAG_EXTENDED_COMMENT, GST_TAG_FLAG_META,
+ G_TYPE_STRING,
+ _("extended comment"),
+ _("free text commenting the data in key=value or key[en]=comment form"),
+ gst_tag_merge_use_first);
+ gst_tag_register (GST_TAG_TRACK_NUMBER, GST_TAG_FLAG_META,
+ G_TYPE_UINT,
+ _("track number"),
+ _("track number inside a collection"), gst_tag_merge_use_first);
+ gst_tag_register (GST_TAG_TRACK_COUNT, GST_TAG_FLAG_META,
+ G_TYPE_UINT,
+ _("track count"),
+ _("count of tracks inside collection this track belongs to"),
+ gst_tag_merge_use_first);
+ gst_tag_register (GST_TAG_ALBUM_VOLUME_NUMBER, GST_TAG_FLAG_META,
+ G_TYPE_UINT,
+ _("disc number"),
+ _("disc number inside a collection"), gst_tag_merge_use_first);
+ gst_tag_register (GST_TAG_ALBUM_VOLUME_COUNT, GST_TAG_FLAG_META,
+ G_TYPE_UINT,
+ _("disc count"),
+ _("count of discs inside collection this disc belongs to"),
+ gst_tag_merge_use_first);
+ gst_tag_register (GST_TAG_LOCATION, GST_TAG_FLAG_META,
+ G_TYPE_STRING,
+ _("location"), _("Origin of media as a URI (location, where the "
+ "original of the file or stream is hosted)"),
+ gst_tag_merge_strings_with_comma);
+ gst_tag_register (GST_TAG_HOMEPAGE, GST_TAG_FLAG_META,
+ G_TYPE_STRING,
+ _("homepage"),
+ _("Homepage for this media (i.e. artist or movie homepage)"),
+ gst_tag_merge_strings_with_comma);
+ gst_tag_register (GST_TAG_DESCRIPTION, GST_TAG_FLAG_META, G_TYPE_STRING,
+ _("description"), _("short text describing the content of the data"),
+ gst_tag_merge_strings_with_comma);
+ gst_tag_register (GST_TAG_VERSION, GST_TAG_FLAG_META, G_TYPE_STRING,
+ _("version"), _("version of this data"), NULL);
+ gst_tag_register (GST_TAG_ISRC, GST_TAG_FLAG_META, G_TYPE_STRING, _("ISRC"),
+ _
+ ("International Standard Recording Code - see http://www.ifpi.org/isrc/"),
+ NULL);
+ /* FIXME: organization (fix what? tpm) */
+ gst_tag_register (GST_TAG_ORGANIZATION, GST_TAG_FLAG_META, G_TYPE_STRING,
+ _("organization"), _("organization"), gst_tag_merge_strings_with_comma);
+ gst_tag_register (GST_TAG_COPYRIGHT, GST_TAG_FLAG_META,
+ G_TYPE_STRING, _("copyright"), _("copyright notice of the data"), NULL);
+ gst_tag_register (GST_TAG_COPYRIGHT_URI, GST_TAG_FLAG_META,
+ G_TYPE_STRING, _("copyright uri"),
+ _("URI to the copyright notice of the data"), NULL);
+ gst_tag_register (GST_TAG_ENCODED_BY, GST_TAG_FLAG_META, G_TYPE_STRING,
+ _("encoded by"), _("name of the encoding person or organization"),
+ gst_tag_merge_strings_with_comma);
+ gst_tag_register (GST_TAG_CONTACT, GST_TAG_FLAG_META,
+ G_TYPE_STRING,
+ _("contact"), _("contact information"), gst_tag_merge_strings_with_comma);
+ gst_tag_register (GST_TAG_LICENSE, GST_TAG_FLAG_META,
+ G_TYPE_STRING, _("license"), _("license of data"), NULL);
+ gst_tag_register (GST_TAG_LICENSE_URI, GST_TAG_FLAG_META,
+ G_TYPE_STRING, _("license uri"),
+ _("URI to the license of the data"), NULL);
+ gst_tag_register (GST_TAG_PERFORMER, GST_TAG_FLAG_META,
+ G_TYPE_STRING,
+ _("performer"),
+ _("person(s) performing"), gst_tag_merge_strings_with_comma);
+ gst_tag_register (GST_TAG_COMPOSER, GST_TAG_FLAG_META,
+ G_TYPE_STRING,
+ _("composer"),
+ _("person(s) who composed the recording"),
+ gst_tag_merge_strings_with_comma);
+ gst_tag_register (GST_TAG_DURATION, GST_TAG_FLAG_DECODED,
+ G_TYPE_UINT64,
+ _("duration"), _("length in GStreamer time units (nanoseconds)"), NULL);
+ gst_tag_register (GST_TAG_CODEC, GST_TAG_FLAG_ENCODED,
+ G_TYPE_STRING,
+ _("codec"),
+ _("codec the data is stored in"), gst_tag_merge_strings_with_comma);
+ gst_tag_register (GST_TAG_VIDEO_CODEC, GST_TAG_FLAG_ENCODED,
+ G_TYPE_STRING,
+ _("video codec"), _("codec the video data is stored in"), NULL);
+ gst_tag_register (GST_TAG_AUDIO_CODEC, GST_TAG_FLAG_ENCODED,
+ G_TYPE_STRING,
+ _("audio codec"), _("codec the audio data is stored in"), NULL);
+ gst_tag_register (GST_TAG_SUBTITLE_CODEC, GST_TAG_FLAG_ENCODED,
+ G_TYPE_STRING,
+ _("subtitle codec"), _("codec the subtitle data is stored in"), NULL);
+ gst_tag_register (GST_TAG_CONTAINER_FORMAT, GST_TAG_FLAG_ENCODED,
+ G_TYPE_STRING, _("container format"),
+ _("container format the data is stored in"), NULL);
+ gst_tag_register (GST_TAG_BITRATE, GST_TAG_FLAG_ENCODED,
+ G_TYPE_UINT, _("bitrate"), _("exact or average bitrate in bits/s"), NULL);
+ gst_tag_register (GST_TAG_NOMINAL_BITRATE, GST_TAG_FLAG_ENCODED,
+ G_TYPE_UINT, _("nominal bitrate"), _("nominal bitrate in bits/s"), NULL);
+ gst_tag_register (GST_TAG_MINIMUM_BITRATE, GST_TAG_FLAG_ENCODED,
+ G_TYPE_UINT, _("minimum bitrate"), _("minimum bitrate in bits/s"), NULL);
+ gst_tag_register (GST_TAG_MAXIMUM_BITRATE, GST_TAG_FLAG_ENCODED,
+ G_TYPE_UINT, _("maximum bitrate"), _("maximum bitrate in bits/s"), NULL);
+ gst_tag_register (GST_TAG_ENCODER, GST_TAG_FLAG_ENCODED,
+ G_TYPE_STRING,
+ _("encoder"), _("encoder used to encode this stream"), NULL);
+ gst_tag_register (GST_TAG_ENCODER_VERSION, GST_TAG_FLAG_ENCODED,
+ G_TYPE_UINT,
+ _("encoder version"),
+ _("version of the encoder used to encode this stream"), NULL);
+ gst_tag_register (GST_TAG_SERIAL, GST_TAG_FLAG_ENCODED,
+ G_TYPE_UINT, _("serial"), _("serial number of track"), NULL);
+ gst_tag_register (GST_TAG_TRACK_GAIN, GST_TAG_FLAG_META,
+ G_TYPE_DOUBLE, _("replaygain track gain"), _("track gain in db"), NULL);
+ gst_tag_register (GST_TAG_TRACK_PEAK, GST_TAG_FLAG_META,
+ G_TYPE_DOUBLE, _("replaygain track peak"), _("peak of the track"), NULL);
+ gst_tag_register (GST_TAG_ALBUM_GAIN, GST_TAG_FLAG_META,
+ G_TYPE_DOUBLE, _("replaygain album gain"), _("album gain in db"), NULL);
+ gst_tag_register (GST_TAG_ALBUM_PEAK, GST_TAG_FLAG_META,
+ G_TYPE_DOUBLE, _("replaygain album peak"), _("peak of the album"), NULL);
+ gst_tag_register (GST_TAG_REFERENCE_LEVEL, GST_TAG_FLAG_META,
+ G_TYPE_DOUBLE, _("replaygain reference level"),
+ _("reference level of track and album gain values"), NULL);
+ gst_tag_register (GST_TAG_LANGUAGE_CODE, GST_TAG_FLAG_META, G_TYPE_STRING,
+ _("language code"),
+ _("language code for this stream, conforming to ISO-639-1"), NULL);
+ gst_tag_register (GST_TAG_IMAGE, GST_TAG_FLAG_META, GST_TYPE_BUFFER,
+ _("image"), _("image related to this stream"), gst_tag_merge_use_first);
+ gst_tag_register (GST_TAG_PREVIEW_IMAGE, GST_TAG_FLAG_META, GST_TYPE_BUFFER,
+ /* TRANSLATORS: 'preview image' = image that shows a preview of the full image */
+ _("preview image"), _("preview image related to this stream"), NULL);
+ gst_tag_register (GST_TAG_ATTACHMENT, GST_TAG_FLAG_META, GST_TYPE_BUFFER,
+ _("attachment"), _("file attached to this stream"),
+ gst_tag_merge_use_first);
+ gst_tag_register (GST_TAG_BEATS_PER_MINUTE, GST_TAG_FLAG_META, G_TYPE_DOUBLE,
+ _("beats per minute"), _("number of beats per minute in audio"), NULL);
+ gst_tag_register (GST_TAG_KEYWORDS, GST_TAG_FLAG_META, G_TYPE_STRING,
+ _("keywords"), _("comma separated keywords describing the content"),
+ gst_tag_merge_strings_with_comma);
+ gst_tag_register (GST_TAG_GEO_LOCATION_NAME, GST_TAG_FLAG_META, G_TYPE_STRING,
+ _("geo location name"), _("human readable descriptive location of where "
+ "the media has been recorded or produced"), NULL);
+ gst_tag_register (GST_TAG_GEO_LOCATION_LATITUDE, GST_TAG_FLAG_META,
+ G_TYPE_DOUBLE, _("geo location latitude"),
+ _("geo latitude location of where the media has been recorded or "
+ "produced in degrees according to WGS84 (zero at the equator, "
+ "negative values for southern latitudes)"), NULL);
+ gst_tag_register (GST_TAG_GEO_LOCATION_LONGITUDE, GST_TAG_FLAG_META,
+ G_TYPE_DOUBLE, _("geo location longitude"),
+ _("geo longitude location of where the media has been recorded or "
+ "produced in degrees according to WGS84 (zero at the prime meridian "
+ "in Greenwich/UK, negative values for western longitudes)"), NULL);
+ gst_tag_register (GST_TAG_GEO_LOCATION_ELEVATION, GST_TAG_FLAG_META,
+ G_TYPE_DOUBLE, _("geo location elevation"),
+ _("geo elevation of where the media has been recorded or produced in "
+ "meters according to WGS84 (zero is average sea level)"), NULL);
+ gst_tag_register (GST_TAG_GEO_LOCATION_COUNTRY, GST_TAG_FLAG_META,
+ G_TYPE_STRING, _("geo location country"),
+ _("country (english name) where the media has been recorded "
+ "or produced"), NULL);
+ gst_tag_register (GST_TAG_GEO_LOCATION_CITY, GST_TAG_FLAG_META,
+ G_TYPE_STRING, _("geo location city"),
+ _("city (english name) where the media has been recorded "
+ "or produced"), NULL);
+ gst_tag_register (GST_TAG_GEO_LOCATION_SUBLOCATION, GST_TAG_FLAG_META,
+ G_TYPE_STRING, _("geo location sublocation"),
+ _("a location whithin a city where the media has been produced "
+ "or created (e.g. the neighborhood)"), NULL);
+ gst_tag_register (GST_TAG_GEO_LOCATION_HORIZONTAL_ERROR, GST_TAG_FLAG_META,
+ G_TYPE_DOUBLE, _("geo location horizontal error"),
+ _("expected error of the horizontal positioning measures (in meters)"),
+ NULL);
+ gst_tag_register (GST_TAG_GEO_LOCATION_MOVEMENT_SPEED, GST_TAG_FLAG_META,
+ G_TYPE_DOUBLE, _("geo location movement speed"),
+ _("movement speed of the capturing device while performing the capture "
+ "in m/s"), NULL);
+ gst_tag_register (GST_TAG_GEO_LOCATION_MOVEMENT_DIRECTION, GST_TAG_FLAG_META,
+ G_TYPE_DOUBLE, _("geo location movement direction"),
+ _("indicates the movement direction of the device performing the capture"
+ " of a media. It is represented as degrees in floating point "
+ "representation, 0 means the geographic north, and increases "
+ "clockwise"), NULL);
+ gst_tag_register (GST_TAG_GEO_LOCATION_CAPTURE_DIRECTION, GST_TAG_FLAG_META,
+ G_TYPE_DOUBLE, _("geo location capture direction"),
+ _("indicates the direction the device is pointing to when capturing "
+ " a media. It is represented as degrees in floating point "
+ " representation, 0 means the geographic north, and increases "
+ "clockwise"), NULL);
+ gst_tag_register (GST_TAG_SHOW_NAME, GST_TAG_FLAG_META, G_TYPE_STRING,
+ /* TRANSLATORS: 'show name' = 'TV/radio/podcast show name' here */
+ _("show name"),
+ _("Name of the tv/podcast/series show the media is from"),
+ gst_tag_merge_strings_with_comma);
+ gst_tag_register (GST_TAG_SHOW_SORTNAME, GST_TAG_FLAG_META, G_TYPE_STRING,
+ /* TRANSLATORS: 'show sortname' = 'TV/radio/podcast show name as used for sorting purposes' here */
+ _("show sortname"),
+ _("Name of the tv/podcast/series show the media is from, for sorting "
+ "purposes"), NULL);
+ gst_tag_register (GST_TAG_SHOW_EPISODE_NUMBER, GST_TAG_FLAG_META, G_TYPE_UINT,
+ _("episode number"),
+ _("The episode number in the season the media is part of"),
+ gst_tag_merge_use_first);
+ gst_tag_register (GST_TAG_SHOW_SEASON_NUMBER, GST_TAG_FLAG_META, G_TYPE_UINT,
+ _("season number"),
+ _("The season number of the show the media is part of"),
+ gst_tag_merge_use_first);
+ gst_tag_register (GST_TAG_LYRICS, GST_TAG_FLAG_META, G_TYPE_STRING,
+ _("lyrics"), _("The lyrics of the media, commonly used for songs"),
+ gst_tag_merge_strings_with_comma);
+ gst_tag_register (GST_TAG_COMPOSER_SORTNAME, GST_TAG_FLAG_META, G_TYPE_STRING,
+ _("composer sortname"),
+ _("person(s) who composed the recording, for sorting purposes"), NULL);
+ gst_tag_register (GST_TAG_GROUPING, GST_TAG_FLAG_META, G_TYPE_STRING,
+ _("grouping"),
+ _("Groups related media that spans multiple tracks, like the different "
+ "pieces of a concerto. It is a higher level than a track, "
+ "but lower than an album"), NULL);
+ gst_tag_register (GST_TAG_USER_RATING, GST_TAG_FLAG_META, G_TYPE_UINT,
+ _("user rating"),
+ _("Rating attributed by a user. The higher the rank, "
+ "the more the user likes this media"), NULL);
+ gst_tag_register (GST_TAG_DEVICE_MANUFACTURER, GST_TAG_FLAG_META,
+ G_TYPE_STRING, _("device manufacturer"),
+ _("Manufacturer of the device used to create this media"), NULL);
+ gst_tag_register (GST_TAG_DEVICE_MODEL, GST_TAG_FLAG_META, G_TYPE_STRING,
+ _("device model"),
+ _("Model of the device used to create this media"), NULL);
+ gst_tag_register (GST_TAG_APPLICATION_NAME, GST_TAG_FLAG_META, G_TYPE_STRING,
+ _("application name"), _("Application used to create the media"), NULL);
+ gst_tag_register (GST_TAG_APPLICATION_DATA, GST_TAG_FLAG_META,
+ GST_TYPE_BUFFER, _("application data"),
+ _("Arbitrary application data to be serialized into the media"), NULL);
+ gst_tag_register (GST_TAG_IMAGE_ORIENTATION, GST_TAG_FLAG_META, G_TYPE_STRING,
+ _("image orientation"),
+ _("How the image should be rotated or flipped before display"), NULL);
+}
+
+/**
+ * gst_tag_merge_use_first:
+ * @dest: (out caller-allocates): uninitialized GValue to store result in
+ * @src: GValue to copy from
+ *
+ * This is a convenience function for the func argument of gst_tag_register().
+ * It creates a copy of the first value from the list.
+ */
+void
+gst_tag_merge_use_first (GValue * dest, const GValue * src)
+{
+ const GValue *ret = gst_value_list_get_value (src, 0);
+
+ g_value_init (dest, G_VALUE_TYPE (ret));
+ g_value_copy (ret, dest);
+}
+
+/**
+ * gst_tag_merge_strings_with_comma:
+ * @dest: (out caller-allocates): uninitialized GValue to store result in
+ * @src: GValue to copy from
+ *
+ * This is a convenience function for the func argument of gst_tag_register().
+ * It concatenates all given strings using a comma. The tag must be registered
+ * as a G_TYPE_STRING or this function will fail.
+ */
+void
+gst_tag_merge_strings_with_comma (GValue * dest, const GValue * src)
+{
+ GString *str;
+ gint i, count;
+
+ count = gst_value_list_get_size (src);
+ str = g_string_new (g_value_get_string (gst_value_list_get_value (src, 0)));
+ for (i = 1; i < count; i++) {
+ /* separator between two strings */
+ g_string_append (str, _(", "));
+ g_string_append (str,
+ g_value_get_string (gst_value_list_get_value (src, i)));
+ }
+
+ g_value_init (dest, G_TYPE_STRING);
+ g_value_take_string (dest, str->str);
+ g_string_free (str, FALSE);
+}
+
+static GstTagInfo *
+gst_tag_lookup (GQuark entry)
+{
+ GstTagInfo *ret;
+
+ TAG_LOCK;
+ ret = g_hash_table_lookup (__tags, GUINT_TO_POINTER (entry));
+ TAG_UNLOCK;
+
+ return ret;
+}
+
+/**
+ * gst_tag_register:
+ * @name: the name or identifier string
+ * @flag: a flag describing the type of tag info
+ * @type: the type this data is in
+ * @nick: human-readable name
+ * @blurb: a human-readable description about this tag
+ * @func: function for merging multiple values of this tag, or NULL
+ *
+ * Registers a new tag type for the use with GStreamer's type system. If a type
+ * with that name is already registered, that one is used.
+ * The old registration may have used a different type however. So don't rely
+ * on your supplied values.
+ *
+ * Important: if you do not supply a merge function the implication will be
+ * that there can only be one single value for this tag in a tag list and
+ * any additional values will silenty be discarded when being added (unless
+ * #GST_TAG_MERGE_REPLACE, #GST_TAG_MERGE_REPLACE_ALL, or
+ * #GST_TAG_MERGE_PREPEND is used as merge mode, in which case the new
+ * value will replace the old one in the list).
+ *
+ * The merge function will be called from gst_tag_list_copy_value() when
+ * it is required that one or more values for a tag be condensed into
+ * one single value. This may happen from gst_tag_list_get_string(),
+ * gst_tag_list_get_int(), gst_tag_list_get_double() etc. What will happen
+ * exactly in that case depends on how the tag was registered and if a
+ * merge function was supplied and if so which one.
+ *
+ * Two default merge functions are provided: gst_tag_merge_use_first() and
+ * gst_tag_merge_strings_with_comma().
+ */
+void
+gst_tag_register (const gchar * name, GstTagFlag flag, GType type,
+ const gchar * nick, const gchar * blurb, GstTagMergeFunc func)
+{
+ GQuark key;
+ GstTagInfo *info;
+
+ g_return_if_fail (name != NULL);
+ g_return_if_fail (nick != NULL);
+ g_return_if_fail (blurb != NULL);
+ g_return_if_fail (type != 0 && type != GST_TYPE_LIST);
+
+ key = g_quark_from_string (name);
+ info = gst_tag_lookup (key);
+
+ if (info) {
+ g_return_if_fail (info->type == type);
+ return;
+ }
+
+ info = g_slice_new (GstTagInfo);
+ info->flag = flag;
+ info->type = type;
+ info->nick = g_strdup (nick);
+ info->blurb = g_strdup (blurb);
+ info->merge_func = func;
+
+ TAG_LOCK;
+ g_hash_table_insert (__tags, GUINT_TO_POINTER (key), info);
+ TAG_UNLOCK;
+}
+
+/**
+ * gst_tag_exists:
+ * @tag: name of the tag
+ *
+ * Checks if the given type is already registered.
+ *
+ * Returns: TRUE if the type is already registered
+ */
+gboolean
+gst_tag_exists (const gchar * tag)
+{
+ g_return_val_if_fail (tag != NULL, FALSE);
+
+ return gst_tag_lookup (g_quark_from_string (tag)) != NULL;
+}
+
+/**
+ * gst_tag_get_type:
+ * @tag: the tag
+ *
+ * Gets the #GType used for this tag.
+ *
+ * Returns: the #GType of this tag
+ */
+GType
+gst_tag_get_type (const gchar * tag)
+{
+ GstTagInfo *info;
+
+ g_return_val_if_fail (tag != NULL, 0);
+ info = gst_tag_lookup (g_quark_from_string (tag));
+ g_return_val_if_fail (info != NULL, 0);
+
+ return info->type;
+}
+
+/**
+ * gst_tag_get_nick
+ * @tag: the tag
+ *
+ * Returns the human-readable name of this tag, You must not change or free
+ * this string.
+ *
+ * Returns: the human-readable name of this tag
+ */
+const gchar *
+gst_tag_get_nick (const gchar * tag)
+{
+ GstTagInfo *info;
+
+ g_return_val_if_fail (tag != NULL, NULL);
+ info = gst_tag_lookup (g_quark_from_string (tag));
+ g_return_val_if_fail (info != NULL, NULL);
+
+ return info->nick;
+}
+
+/**
+ * gst_tag_get_description:
+ * @tag: the tag
+ *
+ * Returns the human-readable description of this tag, You must not change or
+ * free this string.
+ *
+ * Returns: the human-readable description of this tag
+ */
+const gchar *
+gst_tag_get_description (const gchar * tag)
+{
+ GstTagInfo *info;
+
+ g_return_val_if_fail (tag != NULL, NULL);
+ info = gst_tag_lookup (g_quark_from_string (tag));
+ g_return_val_if_fail (info != NULL, NULL);
+
+ return info->blurb;
+}
+
+/**
+ * gst_tag_get_flag:
+ * @tag: the tag
+ *
+ * Gets the flag of @tag.
+ *
+ * Returns: the flag of this tag.
+ */
+GstTagFlag
+gst_tag_get_flag (const gchar * tag)
+{
+ GstTagInfo *info;
+
+ g_return_val_if_fail (tag != NULL, GST_TAG_FLAG_UNDEFINED);
+ info = gst_tag_lookup (g_quark_from_string (tag));
+ g_return_val_if_fail (info != NULL, GST_TAG_FLAG_UNDEFINED);
+
+ return info->flag;
+}
+
+/**
+ * gst_tag_is_fixed:
+ * @tag: tag to check
+ *
+ * Checks if the given tag is fixed. A fixed tag can only contain one value.
+ * Unfixed tags can contain lists of values.
+ *
+ * Returns: TRUE, if the given tag is fixed.
+ */
+gboolean
+gst_tag_is_fixed (const gchar * tag)
+{
+ GstTagInfo *info;
+
+ g_return_val_if_fail (tag != NULL, FALSE);
+ info = gst_tag_lookup (g_quark_from_string (tag));
+ g_return_val_if_fail (info != NULL, FALSE);
+
+ return info->merge_func == NULL;
+}
+
+/**
+ * gst_tag_list_new:
+ *
+ * Creates a new empty GstTagList.
+ *
+ * Free-function: gst_tag_list_free
+ *
+ * Returns: (transfer full): An empty tag list
+ */
+GstTagList *
+gst_tag_list_new (void)
+{
+ return GST_TAG_LIST (gst_structure_id_empty_new (GST_QUARK (TAGLIST)));
+}
+
+/**
+ * gst_tag_list_new_full:
+ * @tag: tag
+ * @...: NULL-terminated list of values to set
+ *
+ * Creates a new taglist and appends the values for the given tags. It expects
+ * tag-value pairs like gst_tag_list_add(), and a NULL terminator after the
+ * last pair. The type of the values is implicit and is documented in the API
+ * reference, but can also be queried at runtime with gst_tag_get_type(). It
+ * is an error to pass a value of a type not matching the tag type into this
+ * function. The tag list will make copies of any arguments passed
+ * (e.g. strings, buffers).
+ *
+ * Free-function: gst_tag_list_free
+ *
+ * Returns: (transfer full): a new #GstTagList. Free with gst_tag_list_free()
+ * when no longer needed.
+ *
+ * Since: 0.10.24
+ */
+/* FIXME 0.11: rename gst_tag_list_new_full to _new and _new to _new_empty */
+GstTagList *
+gst_tag_list_new_full (const gchar * tag, ...)
+{
+ GstTagList *list;
+ va_list args;
+
+ g_return_val_if_fail (tag != NULL, NULL);
+
+ list = gst_tag_list_new ();
+ va_start (args, tag);
+ gst_tag_list_add_valist (list, GST_TAG_MERGE_APPEND, tag, args);
+ va_end (args);
+
+ return list;
+}
+
+/**
+ * gst_tag_list_new_full_valist:
+ * @var_args: tag / value pairs to set
+ *
+ * Just like gst_tag_list_new_full(), only that it takes a va_list argument.
+ * Useful mostly for language bindings.
+ *
+ * Free-function: gst_tag_list_free
+ *
+ * Returns: (transfer full): a new #GstTagList. Free with gst_tag_list_free()
+ * when no longer needed.
+ *
+ * Since: 0.10.24
+ */
+GstTagList *
+gst_tag_list_new_full_valist (va_list var_args)
+{
+ GstTagList *list;
+ const gchar *tag;
+
+ list = gst_tag_list_new ();
+
+ tag = va_arg (var_args, gchar *);
+ gst_tag_list_add_valist (list, GST_TAG_MERGE_APPEND, tag, var_args);
+
+ return list;
+}
+
+/**
+ * gst_tag_list_is_empty:
+ * @list: A #GstTagList.
+ *
+ * Checks if the given taglist is empty.
+ *
+ * Returns: TRUE if the taglist is empty, otherwise FALSE.
+ *
+ * Since: 0.10.11
+ */
+gboolean
+gst_tag_list_is_empty (const GstTagList * list)
+{
+ g_return_val_if_fail (list != NULL, FALSE);
+ g_return_val_if_fail (GST_IS_TAG_LIST (list), FALSE);
+
+ return (gst_structure_n_fields ((GstStructure *) list) == 0);
+}
+
+/**
+ * gst_is_tag_list:
+ * @p: Object that might be a taglist
+ *
+ * Checks if the given pointer is a taglist.
+ *
+ * Returns: TRUE, if the given pointer is a taglist
+ */
+gboolean
+gst_is_tag_list (gconstpointer p)
+{
+ GstStructure *s = (GstStructure *) p;
+
+ g_return_val_if_fail (p != NULL, FALSE);
+
+ return (GST_IS_STRUCTURE (s) && s->name == GST_QUARK (TAGLIST));
+}
+
+typedef struct
+{
+ GstStructure *list;
+ GstTagMergeMode mode;
+}
+GstTagCopyData;
+
+static void
+gst_tag_list_add_value_internal (GstStructure * list, GstTagMergeMode mode,
+ GQuark tag, const GValue * value, GstTagInfo * info)
+{
+ const GValue *value2;
+
+ if (info == NULL) {
+ info = gst_tag_lookup (tag);
+ if (G_UNLIKELY (info == NULL)) {
+ g_warning ("unknown tag '%s'", g_quark_to_string (tag));
+ return;
+ }
+ }
+
+ if (info->merge_func
+ && (value2 = gst_structure_id_get_value (list, tag)) != NULL) {
+ GValue dest = { 0, };
+
+ switch (mode) {
+ case GST_TAG_MERGE_REPLACE_ALL:
+ case GST_TAG_MERGE_REPLACE:
+ gst_structure_id_set_value (list, tag, value);
+ break;
+ case GST_TAG_MERGE_PREPEND:
+ gst_value_list_merge (&dest, value, value2);
+ gst_structure_id_set_value (list, tag, &dest);
+ g_value_unset (&dest);
+ break;
+ case GST_TAG_MERGE_APPEND:
+ gst_value_list_merge (&dest, value2, value);
+ gst_structure_id_set_value (list, tag, &dest);
+ g_value_unset (&dest);
+ break;
+ case GST_TAG_MERGE_KEEP:
+ case GST_TAG_MERGE_KEEP_ALL:
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+ } else {
+ switch (mode) {
+ case GST_TAG_MERGE_APPEND:
+ case GST_TAG_MERGE_KEEP:
+ if (gst_structure_id_get_value (list, tag) != NULL)
+ break;
+ /* fall through */
+ case GST_TAG_MERGE_REPLACE_ALL:
+ case GST_TAG_MERGE_REPLACE:
+ case GST_TAG_MERGE_PREPEND:
+ gst_structure_id_set_value (list, tag, value);
+ break;
+ case GST_TAG_MERGE_KEEP_ALL:
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+ }
+}
+
+static gboolean
+gst_tag_list_copy_foreach (GQuark tag, const GValue * value, gpointer user_data)
+{
+ GstTagCopyData *copy = (GstTagCopyData *) user_data;
+
+ gst_tag_list_add_value_internal (copy->list, copy->mode, tag, value, NULL);
+
+ return TRUE;
+}
+
+/**
+ * gst_tag_list_insert:
+ * @into: list to merge into
+ * @from: list to merge from
+ * @mode: the mode to use
+ *
+ * Inserts the tags of the @from list into the first list using the given mode.
+ */
+void
+gst_tag_list_insert (GstTagList * into, const GstTagList * from,
+ GstTagMergeMode mode)
+{
+ GstTagCopyData data;
+
+ g_return_if_fail (GST_IS_TAG_LIST (into));
+ g_return_if_fail (GST_IS_TAG_LIST (from));
+ g_return_if_fail (GST_TAG_MODE_IS_VALID (mode));
+
+ data.list = (GstStructure *) into;
+ data.mode = mode;
+ if (mode == GST_TAG_MERGE_REPLACE_ALL) {
+ gst_structure_remove_all_fields (data.list);
+ }
+ gst_structure_foreach ((GstStructure *) from, gst_tag_list_copy_foreach,
+ &data);
+}
+
+/**
+ * gst_tag_list_copy:
+ * @list: list to copy
+ *
+ * Copies a given #GstTagList.
+ *
+ * Free-function: gst_tag_list_free
+ *
+ * Returns: (transfer full): copy of the given list
+ */
+GstTagList *
+gst_tag_list_copy (const GstTagList * list)
+{
+ g_return_val_if_fail (GST_IS_TAG_LIST (list), NULL);
+
+ return GST_TAG_LIST (gst_structure_copy ((GstStructure *) list));
+}
+
+/**
+ * gst_tag_list_merge:
+ * @list1: first list to merge
+ * @list2: second list to merge
+ * @mode: the mode to use
+ *
+ * Merges the two given lists into a new list. If one of the lists is NULL, a
+ * copy of the other is returned. If both lists are NULL, NULL is returned.
+ *
+ * Free-function: gst_tag_list_free
+ *
+ * Returns: (transfer full): the new list
+ */
+GstTagList *
+gst_tag_list_merge (const GstTagList * list1, const GstTagList * list2,
+ GstTagMergeMode mode)
+{
+ GstTagList *list1_cp;
+ const GstTagList *list2_cp;
+
+ g_return_val_if_fail (list1 == NULL || GST_IS_TAG_LIST (list1), NULL);
+ g_return_val_if_fail (list2 == NULL || GST_IS_TAG_LIST (list2), NULL);
+ g_return_val_if_fail (GST_TAG_MODE_IS_VALID (mode), NULL);
+
+ /* nothing to merge */
+ if (!list1 && !list2) {
+ return NULL;
+ }
+
+ /* create empty list, we need to do this to correctly handling merge modes */
+ list1_cp = (list1) ? gst_tag_list_copy (list1) : gst_tag_list_new ();
+ list2_cp = (list2) ? list2 : gst_tag_list_new ();
+
+ gst_tag_list_insert (list1_cp, list2_cp, mode);
+
+ if (!list2)
+ gst_tag_list_free ((GstTagList *) list2_cp);
+
+ return list1_cp;
+}
+
+/**
+ * gst_tag_list_free:
+ * @list: (in) (transfer full): the list to free
+ *
+ * Frees the given list and all associated values.
+ */
+void
+gst_tag_list_free (GstTagList * list)
+{
+ g_return_if_fail (GST_IS_TAG_LIST (list));
+ gst_structure_free ((GstStructure *) list);
+}
+
+/**
+ * gst_tag_list_get_tag_size:
+ * @list: a taglist
+ * @tag: the tag to query
+ *
+ * Checks how many value are stored in this tag list for the given tag.
+ *
+ * Returns: The number of tags stored
+ */
+guint
+gst_tag_list_get_tag_size (const GstTagList * list, const gchar * tag)
+{
+ const GValue *value;
+
+ g_return_val_if_fail (GST_IS_TAG_LIST (list), 0);
+
+ value = gst_structure_get_value ((GstStructure *) list, tag);
+ if (value == NULL)
+ return 0;
+ if (G_VALUE_TYPE (value) != GST_TYPE_LIST)
+ return 1;
+
+ return gst_value_list_get_size (value);
+}
+
+/**
+ * gst_tag_list_add:
+ * @list: list to set tags in
+ * @mode: the mode to use
+ * @tag: tag
+ * @...: NULL-terminated list of values to set
+ *
+ * Sets the values for the given tags using the specified mode.
+ */
+void
+gst_tag_list_add (GstTagList * list, GstTagMergeMode mode, const gchar * tag,
+ ...)
+{
+ va_list args;
+
+ g_return_if_fail (GST_IS_TAG_LIST (list));
+ g_return_if_fail (GST_TAG_MODE_IS_VALID (mode));
+ g_return_if_fail (tag != NULL);
+
+ va_start (args, tag);
+ gst_tag_list_add_valist (list, mode, tag, args);
+ va_end (args);
+}
+
+/**
+ * gst_tag_list_add_values:
+ * @list: list to set tags in
+ * @mode: the mode to use
+ * @tag: tag
+ * @...: GValues to set
+ *
+ * Sets the GValues for the given tags using the specified mode.
+ */
+void
+gst_tag_list_add_values (GstTagList * list, GstTagMergeMode mode,
+ const gchar * tag, ...)
+{
+ va_list args;
+
+ g_return_if_fail (GST_IS_TAG_LIST (list));
+ g_return_if_fail (GST_TAG_MODE_IS_VALID (mode));
+ g_return_if_fail (tag != NULL);
+
+ va_start (args, tag);
+ gst_tag_list_add_valist_values (list, mode, tag, args);
+ va_end (args);
+}
+
+/**
+ * gst_tag_list_add_valist:
+ * @list: list to set tags in
+ * @mode: the mode to use
+ * @tag: tag
+ * @var_args: tag / value pairs to set
+ *
+ * Sets the values for the given tags using the specified mode.
+ */
+void
+gst_tag_list_add_valist (GstTagList * list, GstTagMergeMode mode,
+ const gchar * tag, va_list var_args)
+{
+ GstTagInfo *info;
+ GQuark quark;
+ gchar *error = NULL;
+
+ g_return_if_fail (GST_IS_TAG_LIST (list));
+ g_return_if_fail (GST_TAG_MODE_IS_VALID (mode));
+ g_return_if_fail (tag != NULL);
+
+ if (mode == GST_TAG_MERGE_REPLACE_ALL) {
+ gst_structure_remove_all_fields (list);
+ }
+
+ while (tag != NULL) {
+ GValue value = { 0, };
+
+ quark = g_quark_from_string (tag);
+ info = gst_tag_lookup (quark);
+ if (G_UNLIKELY (info == NULL)) {
+ g_warning ("unknown tag '%s'", tag);
+ return;
+ }
+ G_VALUE_COLLECT_INIT (&value, info->type, var_args, 0, &error);
+ if (error) {
+ g_warning ("%s: %s", G_STRLOC, error);
+ g_free (error);
+ /* we purposely leak the value here, it might not be
+ * in a sane state if an error condition occoured
+ */
+ return;
+ }
+ gst_tag_list_add_value_internal (list, mode, quark, &value, info);
+ g_value_unset (&value);
+ tag = va_arg (var_args, gchar *);
+ }
+}
+
+/**
+ * gst_tag_list_add_valist_values:
+ * @list: list to set tags in
+ * @mode: the mode to use
+ * @tag: tag
+ * @var_args: tag / GValue pairs to set
+ *
+ * Sets the GValues for the given tags using the specified mode.
+ */
+void
+gst_tag_list_add_valist_values (GstTagList * list, GstTagMergeMode mode,
+ const gchar * tag, va_list var_args)
+{
+ GQuark quark;
+
+ g_return_if_fail (GST_IS_TAG_LIST (list));
+ g_return_if_fail (GST_TAG_MODE_IS_VALID (mode));
+ g_return_if_fail (tag != NULL);
+
+ if (mode == GST_TAG_MERGE_REPLACE_ALL) {
+ gst_structure_remove_all_fields (list);
+ }
+
+ while (tag != NULL) {
+ quark = g_quark_from_string (tag);
+ g_return_if_fail (gst_tag_lookup (quark) != NULL);
+ gst_tag_list_add_value_internal (list, mode, quark, va_arg (var_args,
+ GValue *), NULL);
+ tag = va_arg (var_args, gchar *);
+ }
+}
+
+/**
+ * gst_tag_list_add_value:
+ * @list: list to set tags in
+ * @mode: the mode to use
+ * @tag: tag
+ * @value: GValue for this tag
+ *
+ * Sets the GValue for a given tag using the specified mode.
+ *
+ * Since: 0.10.24
+ */
+void
+gst_tag_list_add_value (GstTagList * list, GstTagMergeMode mode,
+ const gchar * tag, const GValue * value)
+{
+ g_return_if_fail (GST_IS_TAG_LIST (list));
+ g_return_if_fail (GST_TAG_MODE_IS_VALID (mode));
+ g_return_if_fail (tag != NULL);
+
+ gst_tag_list_add_value_internal (list, mode, g_quark_from_string (tag),
+ value, NULL);
+}
+
+/**
+ * gst_tag_list_remove_tag:
+ * @list: list to remove tag from
+ * @tag: tag to remove
+ *
+ * Removes the given tag from the taglist.
+ */
+void
+gst_tag_list_remove_tag (GstTagList * list, const gchar * tag)
+{
+ g_return_if_fail (GST_IS_TAG_LIST (list));
+ g_return_if_fail (tag != NULL);
+
+ gst_structure_remove_field ((GstStructure *) list, tag);
+}
+
+typedef struct
+{
+ GstTagForeachFunc func;
+ const GstTagList *tag_list;
+ gpointer data;
+}
+TagForeachData;
+
+static int
+structure_foreach_wrapper (GQuark field_id, const GValue * value,
+ gpointer user_data)
+{
+ TagForeachData *data = (TagForeachData *) user_data;
+
+ data->func (data->tag_list, g_quark_to_string (field_id), data->data);
+ return TRUE;
+}
+
+/**
+ * gst_tag_list_foreach:
+ * @list: list to iterate over
+ * @func: (scope call): function to be called for each tag
+ * @user_data: (closure): user specified data
+ *
+ * Calls the given function for each tag inside the tag list. Note that if there
+ * is no tag, the function won't be called at all.
+ */
+void
+gst_tag_list_foreach (const GstTagList * list, GstTagForeachFunc func,
+ gpointer user_data)
+{
+ TagForeachData data;
+
+ g_return_if_fail (GST_IS_TAG_LIST (list));
+ g_return_if_fail (func != NULL);
+
+ data.func = func;
+ data.tag_list = list;
+ data.data = user_data;
+ gst_structure_foreach ((GstStructure *) list, structure_foreach_wrapper,
+ &data);
+}
+
+/**
+ * gst_tag_list_get_value_index:
+ * @list: a #GstTagList
+ * @tag: tag to read out
+ * @index: number of entry to read out
+ *
+ * Gets the value that is at the given index for the given tag in the given
+ * list.
+ *
+ * Returns: (transfer none): The GValue for the specified entry or NULL if the
+ * tag wasn't available or the tag doesn't have as many entries
+ */
+const GValue *
+gst_tag_list_get_value_index (const GstTagList * list, const gchar * tag,
+ guint index)
+{
+ const GValue *value;
+
+ g_return_val_if_fail (GST_IS_TAG_LIST (list), NULL);
+ g_return_val_if_fail (tag != NULL, NULL);
+
+ value = gst_structure_get_value ((GstStructure *) list, tag);
+ if (value == NULL)
+ return NULL;
+
+ if (GST_VALUE_HOLDS_LIST (value)) {
+ if (index >= gst_value_list_get_size (value))
+ return NULL;
+ return gst_value_list_get_value (value, index);
+ } else {
+ if (index > 0)
+ return NULL;
+ return value;
+ }
+}
+
+/**
+ * gst_tag_list_copy_value:
+ * @dest: (out caller-allocates): uninitialized #GValue to copy into
+ * @list: list to get the tag from
+ * @tag: tag to read out
+ *
+ * Copies the contents for the given tag into the value,
+ * merging multiple values into one if multiple values are associated
+ * with the tag.
+ * You must g_value_unset() the value after use.
+ *
+ * Returns: TRUE, if a value was copied, FALSE if the tag didn't exist in the
+ * given list.
+ */
+gboolean
+gst_tag_list_copy_value (GValue * dest, const GstTagList * list,
+ const gchar * tag)
+{
+ const GValue *src;
+
+ g_return_val_if_fail (GST_IS_TAG_LIST (list), FALSE);
+ g_return_val_if_fail (tag != NULL, FALSE);
+ g_return_val_if_fail (dest != NULL, FALSE);
+ g_return_val_if_fail (G_VALUE_TYPE (dest) == 0, FALSE);
+
+ src = gst_structure_get_value ((GstStructure *) list, tag);
+ if (!src)
+ return FALSE;
+
+ if (G_VALUE_TYPE (src) == GST_TYPE_LIST) {
+ GstTagInfo *info = gst_tag_lookup (g_quark_from_string (tag));
+
+ if (!info)
+ return FALSE;
+
+ /* must be there or lists aren't allowed */
+ g_assert (info->merge_func);
+ info->merge_func (dest, src);
+ } else {
+ g_value_init (dest, G_VALUE_TYPE (src));
+ g_value_copy (src, dest);
+ }
+ return TRUE;
+}
+
+/* FIXME 0.11: this whole merge function business is overdesigned, and the
+ * _get_foo() API is misleading as well - how many application developers will
+ * expect gst_tag_list_get_string (list, GST_TAG_ARTIST, &val) might return a
+ * string with multiple comma-separated artists? _get_foo() should just be
+ * a convenience wrapper around _get_foo_index (list, tag, 0, &val),
+ * supplemented by a special _tag_list_get_string_merged() function if needed
+ * (unless someone can actually think of real use cases where the merge
+ * function is not 'use first' for non-strings and merge for strings) */
+
+/***** evil macros to get all the gst_tag_list_get_*() functions right *****/
+
+#define TAG_MERGE_FUNCS(name,type,ret) \
+gboolean \
+gst_tag_list_get_ ## name (const GstTagList *list, const gchar *tag, \
+ type *value) \
+{ \
+ GValue v = { 0, }; \
+ \
+ g_return_val_if_fail (GST_IS_TAG_LIST (list), FALSE); \
+ g_return_val_if_fail (tag != NULL, FALSE); \
+ g_return_val_if_fail (value != NULL, FALSE); \
+ \
+ if (!gst_tag_list_copy_value (&v, list, tag)) \
+ return FALSE; \
+ *value = COPY_FUNC (g_value_get_ ## name (&v)); \
+ g_value_unset (&v); \
+ return ret; \
+} \
+ \
+gboolean \
+gst_tag_list_get_ ## name ## _index (const GstTagList *list, \
+ const gchar *tag, \
+ guint index, type *value) \
+{ \
+ const GValue *v; \
+ \
+ g_return_val_if_fail (GST_IS_TAG_LIST (list), FALSE); \
+ g_return_val_if_fail (tag != NULL, FALSE); \
+ g_return_val_if_fail (value != NULL, FALSE); \
+ \
+ if ((v = gst_tag_list_get_value_index (list, tag, index)) == NULL) \
+ return FALSE; \
+ *value = COPY_FUNC (g_value_get_ ## name (v)); \
+ return ret; \
+}
+
+/* FIXME 0.11: maybe get rid of _get_char*(), _get_uchar*(), _get_long*(),
+ * _get_ulong*() and _get_pointer*()? - they are not really useful/common
+ * enough to warrant convenience accessor functions */
+
+#define COPY_FUNC /**/
+/**
+ * gst_tag_list_get_char:
+ * @list: a #GstTagList to get the tag from
+ * @tag: tag to read out
+ * @value: (out): location for the result
+ *
+ * Copies the contents for the given tag into the value, merging multiple values
+ * into one if multiple values are associated with the tag.
+ *
+ * Returns: TRUE, if a value was copied, FALSE if the tag didn't exist in the
+ * given list.
+ */
+/**
+ * gst_tag_list_get_char_index:
+ * @list: a #GstTagList to get the tag from
+ * @tag: tag to read out
+ * @index: number of entry to read out
+ * @value: (out): location for the result
+ *
+ * Gets the value that is at the given index for the given tag in the given
+ * list.
+ *
+ * Returns: TRUE, if a value was copied, FALSE if the tag didn't exist in the
+ * given list.
+ */
+TAG_MERGE_FUNCS (char, gchar, TRUE);
+/**
+ * gst_tag_list_get_uchar:
+ * @list: a #GstTagList to get the tag from
+ * @tag: tag to read out
+ * @value: (out): location for the result
+ *
+ * Copies the contents for the given tag into the value, merging multiple values
+ * into one if multiple values are associated with the tag.
+ *
+ * Returns: TRUE, if a value was copied, FALSE if the tag didn't exist in the
+ * given list.
+ */
+/**
+ * gst_tag_list_get_uchar_index:
+ * @list: a #GstTagList to get the tag from
+ * @tag: tag to read out
+ * @index: number of entry to read out
+ * @value: (out): location for the result
+ *
+ * Gets the value that is at the given index for the given tag in the given
+ * list.
+ *
+ * Returns: TRUE, if a value was copied, FALSE if the tag didn't exist in the
+ * given list.
+ */
+TAG_MERGE_FUNCS (uchar, guchar, TRUE);
+/**
+ * gst_tag_list_get_boolean:
+ * @list: a #GstTagList to get the tag from
+ * @tag: tag to read out
+ * @value: (out): location for the result
+ *
+ * Copies the contents for the given tag into the value, merging multiple values
+ * into one if multiple values are associated with the tag.
+ *
+ * Returns: TRUE, if a value was copied, FALSE if the tag didn't exist in the
+ * given list.
+ */
+/**
+ * gst_tag_list_get_boolean_index:
+ * @list: a #GstTagList to get the tag from
+ * @tag: tag to read out
+ * @index: number of entry to read out
+ * @value: (out): location for the result
+ *
+ * Gets the value that is at the given index for the given tag in the given
+ * list.
+ *
+ * Returns: TRUE, if a value was copied, FALSE if the tag didn't exist in the
+ * given list.
+ */
+TAG_MERGE_FUNCS (boolean, gboolean, TRUE);
+/**
+ * gst_tag_list_get_int:
+ * @list: a #GstTagList to get the tag from
+ * @tag: tag to read out
+ * @value: (out): location for the result
+ *
+ * Copies the contents for the given tag into the value, merging multiple values
+ * into one if multiple values are associated with the tag.
+ *
+ * Returns: TRUE, if a value was copied, FALSE if the tag didn't exist in the
+ * given list.
+ */
+/**
+ * gst_tag_list_get_int_index:
+ * @list: a #GstTagList to get the tag from
+ * @tag: tag to read out
+ * @index: number of entry to read out
+ * @value: (out): location for the result
+ *
+ * Gets the value that is at the given index for the given tag in the given
+ * list.
+ *
+ * Returns: TRUE, if a value was copied, FALSE if the tag didn't exist in the
+ * given list.
+ */
+TAG_MERGE_FUNCS (int, gint, TRUE);
+/**
+ * gst_tag_list_get_uint:
+ * @list: a #GstTagList to get the tag from
+ * @tag: tag to read out
+ * @value: (out): location for the result
+ *
+ * Copies the contents for the given tag into the value, merging multiple values
+ * into one if multiple values are associated with the tag.
+ *
+ * Returns: TRUE, if a value was copied, FALSE if the tag didn't exist in the
+ * given list.
+ */
+/**
+ * gst_tag_list_get_uint_index:
+ * @list: a #GstTagList to get the tag from
+ * @tag: tag to read out
+ * @index: number of entry to read out
+ * @value: (out): location for the result
+ *
+ * Gets the value that is at the given index for the given tag in the given
+ * list.
+ *
+ * Returns: TRUE, if a value was copied, FALSE if the tag didn't exist in the
+ * given list.
+ */
+TAG_MERGE_FUNCS (uint, guint, TRUE);
+/**
+ * gst_tag_list_get_long:
+ * @list: a #GstTagList to get the tag from
+ * @tag: tag to read out
+ * @value: (out): location for the result
+ *
+ * Copies the contents for the given tag into the value, merging multiple values
+ * into one if multiple values are associated with the tag.
+ *
+ * Returns: TRUE, if a value was copied, FALSE if the tag didn't exist in the
+ * given list.
+ */
+/**
+ * gst_tag_list_get_long_index:
+ * @list: a #GstTagList to get the tag from
+ * @tag: tag to read out
+ * @index: number of entry to read out
+ * @value: (out): location for the result
+ *
+ * Gets the value that is at the given index for the given tag in the given
+ * list.
+ *
+ * Returns: TRUE, if a value was copied, FALSE if the tag didn't exist in the
+ * given list.
+ */
+TAG_MERGE_FUNCS (long, glong, TRUE);
+/**
+ * gst_tag_list_get_ulong:
+ * @list: a #GstTagList to get the tag from
+ * @tag: tag to read out
+ * @value: (out): location for the result
+ *
+ * Copies the contents for the given tag into the value, merging multiple values
+ * into one if multiple values are associated with the tag.
+ *
+ * Returns: TRUE, if a value was copied, FALSE if the tag didn't exist in the
+ * given list.
+ */
+/**
+ * gst_tag_list_get_ulong_index:
+ * @list: a #GstTagList to get the tag from
+ * @tag: tag to read out
+ * @index: number of entry to read out
+ * @value: (out): location for the result
+ *
+ * Gets the value that is at the given index for the given tag in the given
+ * list.
+ *
+ * Returns: TRUE, if a value was copied, FALSE if the tag didn't exist in the
+ * given list.
+ */
+TAG_MERGE_FUNCS (ulong, gulong, TRUE);
+/**
+ * gst_tag_list_get_int64:
+ * @list: a #GstTagList to get the tag from
+ * @tag: tag to read out
+ * @value: (out): location for the result
+ *
+ * Copies the contents for the given tag into the value, merging multiple values
+ * into one if multiple values are associated with the tag.
+ *
+ * Returns: TRUE, if a value was copied, FALSE if the tag didn't exist in the
+ * given list.
+ */
+/**
+ * gst_tag_list_get_int64_index:
+ * @list: a #GstTagList to get the tag from
+ * @tag: tag to read out
+ * @index: number of entry to read out
+ * @value: (out): location for the result
+ *
+ * Gets the value that is at the given index for the given tag in the given
+ * list.
+ *
+ * Returns: TRUE, if a value was copied, FALSE if the tag didn't exist in the
+ * given list.
+ */
+TAG_MERGE_FUNCS (int64, gint64, TRUE);
+/**
+ * gst_tag_list_get_uint64:
+ * @list: a #GstTagList to get the tag from
+ * @tag: tag to read out
+ * @value: (out): location for the result
+ *
+ * Copies the contents for the given tag into the value, merging multiple values
+ * into one if multiple values are associated with the tag.
+ *
+ * Returns: TRUE, if a value was copied, FALSE if the tag didn't exist in the
+ * given list.
+ */
+/**
+ * gst_tag_list_get_uint64_index:
+ * @list: a #GstTagList to get the tag from
+ * @tag: tag to read out
+ * @index: number of entry to read out
+ * @value: (out): location for the result
+ *
+ * Gets the value that is at the given index for the given tag in the given
+ * list.
+ *
+ * Returns: TRUE, if a value was copied, FALSE if the tag didn't exist in the
+ * given list.
+ */
+TAG_MERGE_FUNCS (uint64, guint64, TRUE);
+/**
+ * gst_tag_list_get_float:
+ * @list: a #GstTagList to get the tag from
+ * @tag: tag to read out
+ * @value: (out): location for the result
+ *
+ * Copies the contents for the given tag into the value, merging multiple values
+ * into one if multiple values are associated with the tag.
+ *
+ * Returns: TRUE, if a value was copied, FALSE if the tag didn't exist in the
+ * given list.
+ */
+/**
+ * gst_tag_list_get_float_index:
+ * @list: a #GstTagList to get the tag from
+ * @tag: tag to read out
+ * @index: number of entry to read out
+ * @value: (out): location for the result
+ *
+ * Gets the value that is at the given index for the given tag in the given
+ * list.
+ *
+ * Returns: TRUE, if a value was copied, FALSE if the tag didn't exist in the
+ * given list.
+ */
+TAG_MERGE_FUNCS (float, gfloat, TRUE);
+/**
+ * gst_tag_list_get_double:
+ * @list: a #GstTagList to get the tag from
+ * @tag: tag to read out
+ * @value: (out): location for the result
+ *
+ * Copies the contents for the given tag into the value, merging multiple values
+ * into one if multiple values are associated with the tag.
+ *
+ * Returns: TRUE, if a value was copied, FALSE if the tag didn't exist in the
+ * given list.
+ */
+/**
+ * gst_tag_list_get_double_index:
+ * @list: a #GstTagList to get the tag from
+ * @tag: tag to read out
+ * @index: number of entry to read out
+ * @value: (out): location for the result
+ *
+ * Gets the value that is at the given index for the given tag in the given
+ * list.
+ *
+ * Returns: TRUE, if a value was copied, FALSE if the tag didn't exist in the
+ * given list.
+ */
+TAG_MERGE_FUNCS (double, gdouble, TRUE);
+/**
+ * gst_tag_list_get_pointer:
+ * @list: a #GstTagList to get the tag from
+ * @tag: tag to read out
+ * @value: (out) (transfer none): location for the result
+ *
+ * Copies the contents for the given tag into the value, merging multiple values
+ * into one if multiple values are associated with the tag.
+ *
+ * Returns: TRUE, if a value was copied, FALSE if the tag didn't exist in the
+ * given list.
+ */
+/**
+ * gst_tag_list_get_pointer_index:
+ * @list: a #GstTagList to get the tag from
+ * @tag: tag to read out
+ * @index: number of entry to read out
+ * @value: (out) (transfer none): location for the result
+ *
+ * Gets the value that is at the given index for the given tag in the given
+ * list.
+ *
+ * Returns: TRUE, if a value was copied, FALSE if the tag didn't exist in the
+ * given list.
+ */
+TAG_MERGE_FUNCS (pointer, gpointer, (*value != NULL));
+
+static inline gchar *
+_gst_strdup0 (const gchar * s)
+{
+ if (s == NULL || *s == '\0')
+ return NULL;
+
+ return g_strdup (s);
+}
+
+#undef COPY_FUNC
+#define COPY_FUNC _gst_strdup0
+
+/**
+ * gst_tag_list_get_string:
+ * @list: a #GstTagList to get the tag from
+ * @tag: tag to read out
+ * @value: (out callee-allocates) (transfer full): location for the result
+ *
+ * Copies the contents for the given tag into the value, possibly merging
+ * multiple values into one if multiple values are associated with the tag.
+ *
+ * Use gst_tag_list_get_string_index (list, tag, 0, value) if you want
+ * to retrieve the first string associated with this tag unmodified.
+ *
+ * The resulting string in @value will be in UTF-8 encoding and should be
+ * freed by the caller using g_free when no longer needed. Since 0.10.24 the
+ * returned string is also guaranteed to be non-NULL and non-empty.
+ *
+ * Free-function: g_free
+ *
+ * Returns: TRUE, if a value was copied, FALSE if the tag didn't exist in the
+ * given list.
+ */
+/**
+ * gst_tag_list_get_string_index:
+ * @list: a #GstTagList to get the tag from
+ * @tag: tag to read out
+ * @index: number of entry to read out
+ * @value: (out callee-allocates) (transfer full): location for the result
+ *
+ * Gets the value that is at the given index for the given tag in the given
+ * list.
+ *
+ * The resulting string in @value will be in UTF-8 encoding and should be
+ * freed by the caller using g_free when no longer needed. Since 0.10.24 the
+ * returned string is also guaranteed to be non-NULL and non-empty.
+ *
+ * Free-function: g_free
+ *
+ * Returns: TRUE, if a value was copied, FALSE if the tag didn't exist in the
+ * given list.
+ */
+TAG_MERGE_FUNCS (string, gchar *, (*value != NULL));
+
+/*
+ *FIXME 0.11: Instead of _peek (non-copy) and _get (copy), we could have
+ * _get (non-copy) and _dup (copy) for strings, seems more
+ * widely used
+ */
+/**
+ * gst_tag_list_peek_string_index:
+ * @list: a #GstTagList to get the tag from
+ * @tag: tag to read out
+ * @index: number of entry to read out
+ * @value: (out) (transfer none): location for the result
+ *
+ * Peeks at the value that is at the given index for the given tag in the given
+ * list.
+ *
+ * The resulting string in @value will be in UTF-8 encoding and doesn't need
+ * to be freed by the caller. The returned string is also guaranteed to
+ * be non-NULL and non-empty.
+ *
+ * Returns: TRUE, if a value was set, FALSE if the tag didn't exist in the
+ * given list.
+ */
+gboolean
+gst_tag_list_peek_string_index (const GstTagList * list,
+ const gchar * tag, guint index, const gchar ** value)
+{
+ const GValue *v;
+
+ g_return_val_if_fail (GST_IS_TAG_LIST (list), FALSE);
+ g_return_val_if_fail (tag != NULL, FALSE);
+ g_return_val_if_fail (value != NULL, FALSE);
+
+ if ((v = gst_tag_list_get_value_index (list, tag, index)) == NULL)
+ return FALSE;
+ *value = g_value_get_string (v);
+ return *value != NULL && **value != '\0';
+}
+
+/**
+ * gst_tag_list_get_date:
+ * @list: a #GstTagList to get the tag from
+ * @tag: tag to read out
+ * @value: (out callee-allocates) (transfer full): address of a GDate pointer
+ * variable to store the result into
+ *
+ * Copies the first date for the given tag in the taglist into the variable
+ * pointed to by @value. Free the date with g_date_free() when it is no longer
+ * needed.
+ *
+ * Free-function: g_date_free
+ *
+ * Returns: TRUE, if a date was copied, FALSE if the tag didn't exist in the
+ * given list or if it was #NULL.
+ */
+gboolean
+gst_tag_list_get_date (const GstTagList * list, const gchar * tag,
+ GDate ** value)
+{
+ GValue v = { 0, };
+
+ g_return_val_if_fail (GST_IS_TAG_LIST (list), FALSE);
+ g_return_val_if_fail (tag != NULL, FALSE);
+ g_return_val_if_fail (value != NULL, FALSE);
+
+ if (!gst_tag_list_copy_value (&v, list, tag))
+ return FALSE;
+ *value = (GDate *) g_value_dup_boxed (&v);
+ g_value_unset (&v);
+ return (*value != NULL);
+}
+
+/**
+ * gst_tag_list_get_date_index:
+ * @list: a #GstTagList to get the tag from
+ * @tag: tag to read out
+ * @index: number of entry to read out
+ * @value: (out callee-allocates) (transfer full): location for the result
+ *
+ * Gets the date that is at the given index for the given tag in the given
+ * list and copies it into the variable pointed to by @value. Free the date
+ * with g_date_free() when it is no longer needed.
+ *
+ * Free-function: g_date_free
+ *
+ * Returns: TRUE, if a value was copied, FALSE if the tag didn't exist in the
+ * given list or if it was #NULL.
+ */
+gboolean
+gst_tag_list_get_date_index (const GstTagList * list,
+ const gchar * tag, guint index, GDate ** value)
+{
+ const GValue *v;
+
+ g_return_val_if_fail (GST_IS_TAG_LIST (list), FALSE);
+ g_return_val_if_fail (tag != NULL, FALSE);
+ g_return_val_if_fail (value != NULL, FALSE);
+
+ if ((v = gst_tag_list_get_value_index (list, tag, index)) == NULL)
+ return FALSE;
+ *value = (GDate *) g_value_dup_boxed (v);
+ return (*value != NULL);
+}
+
+/**
+ * gst_tag_list_get_date_time:
+ * @list: a #GstTagList to get the tag from
+ * @tag: tag to read out
+ * @value: (out callee-allocates) (transfer full): address of a #GstDateTime
+ * pointer variable to store the result into
+ *
+ * Copies the first datetime for the given tag in the taglist into the variable
+ * pointed to by @value. Unref the date with gst_date_time_unref() when
+ * it is no longer needed.
+ *
+ * Free-function: gst_date_time_unref
+ *
+ * Returns: TRUE, if a datetime was copied, FALSE if the tag didn't exist in
+ * thegiven list or if it was #NULL.
+ *
+ * Since: 0.10.31
+ */
+gboolean
+gst_tag_list_get_date_time (const GstTagList * list, const gchar * tag,
+ GstDateTime ** value)
+{
+ GValue v = { 0, };
+
+ g_return_val_if_fail (GST_IS_TAG_LIST (list), FALSE);
+ g_return_val_if_fail (tag != NULL, FALSE);
+ g_return_val_if_fail (value != NULL, FALSE);
+
+ if (!gst_tag_list_copy_value (&v, list, tag))
+ return FALSE;
+
+ g_return_val_if_fail (GST_VALUE_HOLDS_DATE_TIME (&v), FALSE);
+
+ *value = (GstDateTime *) g_value_dup_boxed (&v);
+ g_value_unset (&v);
+ return (*value != NULL);
+}
+
+/**
+ * gst_tag_list_get_date_time_index:
+ * @list: a #GstTagList to get the tag from
+ * @tag: tag to read out
+ * @index: number of entry to read out
+ * @value: (out callee-allocates) (transfer full): location for the result
+ *
+ * Gets the datetime that is at the given index for the given tag in the given
+ * list and copies it into the variable pointed to by @value. Unref the datetime
+ * with gst_date_time_unref() when it is no longer needed.
+ *
+ * Free-function: gst_date_time_unref
+ *
+ * Returns: TRUE, if a value was copied, FALSE if the tag didn't exist in the
+ * given list or if it was #NULL.
+ *
+ * Since: 0.10.31
+ */
+gboolean
+gst_tag_list_get_date_time_index (const GstTagList * list,
+ const gchar * tag, guint index, GstDateTime ** value)
+{
+ const GValue *v;
+
+ g_return_val_if_fail (GST_IS_TAG_LIST (list), FALSE);
+ g_return_val_if_fail (tag != NULL, FALSE);
+ g_return_val_if_fail (value != NULL, FALSE);
+
+ if ((v = gst_tag_list_get_value_index (list, tag, index)) == NULL)
+ return FALSE;
+ *value = (GstDateTime *) g_value_dup_boxed (v);
+ return (*value != NULL);
+}
+
+/**
+ * gst_tag_list_get_buffer:
+ * @list: a #GstTagList to get the tag from
+ * @tag: tag to read out
+ * @value: (out callee-allocates) (transfer full): address of a GstBuffer
+ * pointer variable to store the result into
+ *
+ * Copies the first buffer for the given tag in the taglist into the variable
+ * pointed to by @value. Free the buffer with gst_buffer_unref() when it is
+ * no longer needed.
+ *
+ * Free-function: gst_buffer_unref
+ *
+ * Returns: TRUE, if a buffer was copied, FALSE if the tag didn't exist in the
+ * given list or if it was #NULL.
+ *
+ * Since: 0.10.23
+ */
+gboolean
+gst_tag_list_get_buffer (const GstTagList * list, const gchar * tag,
+ GstBuffer ** value)
+{
+ GValue v = { 0, };
+
+ g_return_val_if_fail (GST_IS_TAG_LIST (list), FALSE);
+ g_return_val_if_fail (tag != NULL, FALSE);
+ g_return_val_if_fail (value != NULL, FALSE);
+
+ if (!gst_tag_list_copy_value (&v, list, tag))
+ return FALSE;
+ *value = g_value_dup_boxed (&v);
+ g_value_unset (&v);
+ return (*value != NULL);
+}
+
+/**
+ * gst_tag_list_get_buffer_index:
+ * @list: a #GstTagList to get the tag from
+ * @tag: tag to read out
+ * @index: number of entry to read out
+ * @value: (out callee-allocates) (transfer full): address of a GstBuffer
+ * pointer variable to store the result into
+ *
+ * Gets the buffer that is at the given index for the given tag in the given
+ * list and copies it into the variable pointed to by @value. Free the buffer
+ * with gst_buffer_unref() when it is no longer needed.
+ *
+ * Free-function: gst_buffer_unref
+ *
+ * Returns: TRUE, if a buffer was copied, FALSE if the tag didn't exist in the
+ * given list or if it was #NULL.
+ *
+ * Since: 0.10.23
+ */
+gboolean
+gst_tag_list_get_buffer_index (const GstTagList * list,
+ const gchar * tag, guint index, GstBuffer ** value)
+{
+ const GValue *v;
+
+ g_return_val_if_fail (GST_IS_TAG_LIST (list), FALSE);
+ g_return_val_if_fail (tag != NULL, FALSE);
+ g_return_val_if_fail (value != NULL, FALSE);
+
+ if ((v = gst_tag_list_get_value_index (list, tag, index)) == NULL)
+ return FALSE;
+ *value = g_value_dup_boxed (v);
+ return (*value != NULL);
+}
diff --git a/gst/gsttaglist.h b/gst/gsttaglist.h
new file mode 100644
index 0000000..e40d52a
--- /dev/null
+++ b/gst/gsttaglist.h
@@ -0,0 +1,1034 @@
+/* GStreamer
+ * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
+ *
+ * gsttaglist.h: Header for tag support
+ *
+ * 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_TAGLIST_H__
+#define __GST_TAGLIST_H__
+
+#include <gst/gstdatetime.h>
+#include <gst/gstbuffer.h>
+#include <gst/gststructure.h>
+#include <gst/glib-compat.h>
+
+G_BEGIN_DECLS
+
+/**
+ * GstTagMergeMode:
+ * @GST_TAG_MERGE_UNDEFINED: undefined merge mode
+ * @GST_TAG_MERGE_REPLACE_ALL: replace all tags (clear list and append)
+ * @GST_TAG_MERGE_REPLACE: replace tags
+ * @GST_TAG_MERGE_APPEND: append tags
+ * @GST_TAG_MERGE_PREPEND: prepend tags
+ * @GST_TAG_MERGE_KEEP: keep existing tags
+ * @GST_TAG_MERGE_KEEP_ALL: keep all existing tags
+ * @GST_TAG_MERGE_COUNT: the number of merge modes
+ *
+ * The different tag merging modes are basically replace, overwrite and append,
+ * but they can be seen from two directions. Given two taglists: (A) the tags
+ * already in the element and (B) the ones that are supplied to the element (
+ * e.g. via gst_tag_setter_merge_tags() / gst_tag_setter_add_tags() or a
+ * %GST_EVENT_TAG), how are these tags merged?
+ * In the table below this is shown for the cases that a tag exists in the list
+ * (A) or does not exists (!A) and combinations thereof.
+ *
+ * <table frame="all" colsep="1" rowsep="1">
+ * <title>merge mode</title>
+ * <tgroup cols='5' align='left'>
+ * <thead>
+ * <row>
+ * <entry>merge mode</entry>
+ * <entry>A + B</entry>
+ * <entry>A + !B</entry>
+ * <entry>!A + B</entry>
+ * <entry>!A + !B</entry>
+ * </row>
+ * </thead>
+ * <tbody>
+ * <row>
+ * <entry>REPLACE_ALL</entry>
+ * <entry>B</entry>
+ * <entry>-</entry>
+ * <entry>B</entry>
+ * <entry>-</entry>
+ * </row>
+ * <row>
+ * <entry>REPLACE</entry>
+ * <entry>B</entry>
+ * <entry>A</entry>
+ * <entry>B</entry>
+ * <entry>-</entry>
+ * </row>
+ * <row>
+ * <entry>APPEND</entry>
+ * <entry>A, B</entry>
+ * <entry>A</entry>
+ * <entry>B</entry>
+ * <entry>-</entry>
+ * </row>
+ * <row>
+ * <entry>PREPEND</entry>
+ * <entry>B, A</entry>
+ * <entry>A</entry>
+ * <entry>B</entry>
+ * <entry>-</entry>
+ * </row>
+ * <row>
+ * <entry>KEEP</entry>
+ * <entry>A</entry>
+ * <entry>A</entry>
+ * <entry>B</entry>
+ * <entry>-</entry>
+ * </row>
+ * <row>
+ * <entry>KEEP_ALL</entry>
+ * <entry>A</entry>
+ * <entry>A</entry>
+ * <entry>-</entry>
+ * <entry>-</entry>
+ * </row>
+ * </tbody>
+ * </tgroup>
+ * </table>
+ */
+typedef enum {
+ GST_TAG_MERGE_UNDEFINED,
+ GST_TAG_MERGE_REPLACE_ALL,
+ GST_TAG_MERGE_REPLACE,
+ GST_TAG_MERGE_APPEND,
+ GST_TAG_MERGE_PREPEND,
+ GST_TAG_MERGE_KEEP,
+ GST_TAG_MERGE_KEEP_ALL,
+ /* add more */
+ GST_TAG_MERGE_COUNT
+} GstTagMergeMode;
+
+#define GST_TAG_MODE_IS_VALID(mode) (((mode) > GST_TAG_MERGE_UNDEFINED) && ((mode) < GST_TAG_MERGE_COUNT))
+
+/**
+ * GstTagFlag:
+ * @GST_TAG_FLAG_UNDEFINED: undefined flag
+ * @GST_TAG_FLAG_META: tag is meta data
+ * @GST_TAG_FLAG_ENCODED: tag is encoded
+ * @GST_TAG_FLAG_DECODED: tag is decoded
+ * @GST_TAG_FLAG_COUNT: number of tag flags
+ *
+ * Extra tag flags used when registering tags.
+ */
+typedef enum {
+ GST_TAG_FLAG_UNDEFINED,
+ GST_TAG_FLAG_META,
+ GST_TAG_FLAG_ENCODED,
+ GST_TAG_FLAG_DECODED,
+ GST_TAG_FLAG_COUNT
+} GstTagFlag;
+
+#define GST_TAG_FLAG_IS_VALID(flag) (((flag) > GST_TAG_FLAG_UNDEFINED) && ((flag) < GST_TAG_FLAG_COUNT))
+
+/* FIXME 0.11: Don't typedef GstTagList to be a GstStructure, they're
+ * internally the same but not from an API point of view.
+ * See bug #518934.
+ */
+/**
+ * GstTagList:
+ *
+ * Opaque #GstTagList data structure.
+ */
+#ifdef _FOOL_GTK_DOC_
+typedef struct _GstTagList GstTagList;
+#else
+#ifdef IN_GOBJECT_INTROSPECTION
+typedef struct _GstTagList GstTagList;
+#else
+typedef GstStructure GstTagList;
+#endif
+#endif
+
+#define GST_TAG_LIST(x) ((GstTagList *) (x))
+#define GST_IS_TAG_LIST(x) ((x) != NULL && gst_is_tag_list (GST_TAG_LIST (x)))
+#define GST_TYPE_TAG_LIST (gst_tag_list_get_type ())
+
+/**
+ * GstTagForeachFunc:
+ * @list: the #GstTagList
+ * @tag: a name of a tag in @list
+ * @user_data: user data
+ *
+ * A function that will be called in gst_tag_list_foreach(). The function may
+ * not modify the tag list.
+ */
+typedef void (*GstTagForeachFunc) (const GstTagList * list,
+ const gchar * tag,
+ gpointer user_data);
+
+/**
+ * GstTagMergeFunc:
+ * @dest: the destination #GValue
+ * @src: the source #GValue
+ *
+ * A function for merging multiple values of a tag used when registering
+ * tags.
+ */
+typedef void (* GstTagMergeFunc) (GValue *dest, const GValue *src);
+
+GType gst_tag_list_get_type (void);
+
+/* tag registration */
+void gst_tag_register (const gchar * name,
+ GstTagFlag flag,
+ GType type,
+ const gchar * nick,
+ const gchar * blurb,
+ GstTagMergeFunc func);
+
+/* some default merging functions */
+void gst_tag_merge_use_first (GValue * dest, const GValue * src);
+void gst_tag_merge_strings_with_comma (GValue * dest, const GValue * src);
+
+/* basic tag support */
+gboolean gst_tag_exists (const gchar * tag);
+GType gst_tag_get_type (const gchar * tag);
+const gchar * gst_tag_get_nick (const gchar * tag);
+const gchar * gst_tag_get_description (const gchar * tag);
+GstTagFlag gst_tag_get_flag (const gchar * tag);
+gboolean gst_tag_is_fixed (const gchar * tag);
+
+/* tag lists */
+GstTagList * gst_tag_list_new (void);
+GstTagList * gst_tag_list_new_full (const gchar * tag, ...);
+GstTagList * gst_tag_list_new_full_valist (va_list var_args);
+
+gboolean gst_is_tag_list (gconstpointer p);
+GstTagList * gst_tag_list_copy (const GstTagList * list);
+gboolean gst_tag_list_is_empty (const GstTagList * list);
+void gst_tag_list_insert (GstTagList * into,
+ const GstTagList * from,
+ GstTagMergeMode mode);
+GstTagList * gst_tag_list_merge (const GstTagList * list1,
+ const GstTagList * list2,
+ GstTagMergeMode mode);
+void gst_tag_list_free (GstTagList * list);
+guint gst_tag_list_get_tag_size (const GstTagList * list,
+ const gchar * tag);
+void gst_tag_list_add (GstTagList * list,
+ GstTagMergeMode mode,
+ const gchar * tag,
+ ...) G_GNUC_NULL_TERMINATED;
+void gst_tag_list_add_values (GstTagList * list,
+ GstTagMergeMode mode,
+ const gchar * tag,
+ ...) G_GNUC_NULL_TERMINATED;
+void gst_tag_list_add_valist (GstTagList * list,
+ GstTagMergeMode mode,
+ const gchar * tag,
+ va_list var_args);
+void gst_tag_list_add_valist_values (GstTagList * list,
+ GstTagMergeMode mode,
+ const gchar * tag,
+ va_list var_args);
+void gst_tag_list_add_value (GstTagList * list,
+ GstTagMergeMode mode,
+ const gchar * tag,
+ const GValue * value);
+void gst_tag_list_remove_tag (GstTagList * list,
+ const gchar * tag);
+void gst_tag_list_foreach (const GstTagList * list,
+ GstTagForeachFunc func,
+ gpointer user_data);
+
+const GValue *
+ gst_tag_list_get_value_index (const GstTagList * list,
+ const gchar * tag,
+ guint index);
+gboolean gst_tag_list_copy_value (GValue * dest,
+ const GstTagList * list,
+ const gchar * tag);
+
+/* simplifications (FIXME: do we want them?) */
+gboolean gst_tag_list_get_char (const GstTagList * list,
+ const gchar * tag,
+ gchar * value);
+gboolean gst_tag_list_get_char_index (const GstTagList * list,
+ const gchar * tag,
+ guint index,
+ gchar * value);
+gboolean gst_tag_list_get_uchar (const GstTagList * list,
+ const gchar * tag,
+ guchar * value);
+gboolean gst_tag_list_get_uchar_index (const GstTagList * list,
+ const gchar * tag,
+ guint index,
+ guchar * value);
+gboolean gst_tag_list_get_boolean (const GstTagList * list,
+ const gchar * tag,
+ gboolean * value);
+gboolean gst_tag_list_get_boolean_index (const GstTagList * list,
+ const gchar * tag,
+ guint index,
+ gboolean * value);
+gboolean gst_tag_list_get_int (const GstTagList * list,
+ const gchar * tag,
+ gint * value);
+gboolean gst_tag_list_get_int_index (const GstTagList * list,
+ const gchar * tag,
+ guint index,
+ gint * value);
+gboolean gst_tag_list_get_uint (const GstTagList * list,
+ const gchar * tag,
+ guint * value);
+gboolean gst_tag_list_get_uint_index (const GstTagList * list,
+ const gchar * tag,
+ guint index,
+ guint * value);
+gboolean gst_tag_list_get_long (const GstTagList * list,
+ const gchar * tag,
+ glong * value);
+gboolean gst_tag_list_get_long_index (const GstTagList * list,
+ const gchar * tag,
+ guint index,
+ glong * value);
+gboolean gst_tag_list_get_ulong (const GstTagList * list,
+ const gchar * tag,
+ gulong * value);
+gboolean gst_tag_list_get_ulong_index (const GstTagList * list,
+ const gchar * tag,
+ guint index,
+ gulong * value);
+gboolean gst_tag_list_get_int64 (const GstTagList * list,
+ const gchar * tag,
+ gint64 * value);
+gboolean gst_tag_list_get_int64_index (const GstTagList * list,
+ const gchar * tag,
+ guint index,
+ gint64 * value);
+gboolean gst_tag_list_get_uint64 (const GstTagList * list,
+ const gchar * tag,
+ guint64 * value);
+gboolean gst_tag_list_get_uint64_index (const GstTagList * list,
+ const gchar * tag,
+ guint index,
+ guint64 * value);
+gboolean gst_tag_list_get_float (const GstTagList * list,
+ const gchar * tag,
+ gfloat * value);
+gboolean gst_tag_list_get_float_index (const GstTagList * list,
+ const gchar * tag,
+ guint index,
+ gfloat * value);
+gboolean gst_tag_list_get_double (const GstTagList * list,
+ const gchar * tag,
+ gdouble * value);
+gboolean gst_tag_list_get_double_index (const GstTagList * list,
+ const gchar * tag,
+ guint index,
+ gdouble * value);
+gboolean gst_tag_list_get_string (const GstTagList * list,
+ const gchar * tag,
+ gchar ** value);
+gboolean gst_tag_list_get_string_index (const GstTagList * list,
+ const gchar * tag,
+ guint index,
+ gchar ** value);
+gboolean gst_tag_list_peek_string_index (const GstTagList * list,
+ const gchar * tag,
+ guint index,
+ const gchar ** value);
+gboolean gst_tag_list_get_pointer (const GstTagList * list,
+ const gchar * tag,
+ gpointer * value);
+gboolean gst_tag_list_get_pointer_index (const GstTagList * list,
+ const gchar * tag,
+ guint index,
+ gpointer * value);
+gboolean gst_tag_list_get_date (const GstTagList * list,
+ const gchar * tag,
+ GDate ** value);
+gboolean gst_tag_list_get_date_index (const GstTagList * list,
+ const gchar * tag,
+ guint index,
+ GDate ** value);
+gboolean gst_tag_list_get_date_time (const GstTagList * list,
+ const gchar * tag,
+ GstDateTime ** value);
+gboolean gst_tag_list_get_date_time_index (const GstTagList * list,
+ const gchar * tag,
+ guint index,
+ GstDateTime ** value);
+gboolean gst_tag_list_get_buffer (const GstTagList * list,
+ const gchar * tag,
+ GstBuffer ** value);
+gboolean gst_tag_list_get_buffer_index (const GstTagList * list,
+ const gchar * tag,
+ guint index,
+ GstBuffer ** value);
+
+/* GStreamer core tags */
+/**
+ * GST_TAG_TITLE:
+ *
+ * commonly used title (string)
+ *
+ * The title as it should be displayed, e.g. 'The Doll House'
+ */
+#define GST_TAG_TITLE "title"
+/**
+ * GST_TAG_TITLE_SORTNAME:
+ *
+ * commonly used title, as used for sorting (string)
+ *
+ * The title as it should be sorted, e.g. 'Doll House, The'
+ *
+ * Since: 0.10.15
+ */
+#define GST_TAG_TITLE_SORTNAME "title-sortname"
+/**
+ * GST_TAG_ARTIST:
+ *
+ * person(s) responsible for the recording (string)
+ *
+ * The artist name as it should be displayed, e.g. 'Jimi Hendrix' or
+ * 'The Guitar Heroes'
+ */
+#define GST_TAG_ARTIST "artist"
+/**
+ * GST_TAG_ARTIST_SORTNAME:
+ *
+ * person(s) responsible for the recording, as used for sorting (string)
+ *
+ * The artist name as it should be sorted, e.g. 'Hendrix, Jimi' or
+ * 'Guitar Heroes, The'
+ *
+ * Since: 0.10.15
+ */
+/* FIXME 0.11: change to "artist-sortname" */
+#define GST_TAG_ARTIST_SORTNAME "musicbrainz-sortname"
+/**
+ * GST_TAG_ALBUM:
+ *
+ * album containing this data (string)
+ *
+ * The album name as it should be displayed, e.g. 'The Jazz Guitar'
+ */
+#define GST_TAG_ALBUM "album"
+/**
+ * GST_TAG_ALBUM_SORTNAME:
+ *
+ * album containing this data, as used for sorting (string)
+ *
+ * The album name as it should be sorted, e.g. 'Jazz Guitar, The'
+ *
+ * Since: 0.10.15
+ */
+#define GST_TAG_ALBUM_SORTNAME "album-sortname"
+/**
+ * GST_TAG_ALBUM_ARTIST:
+ *
+ * The artist of the entire album, as it should be displayed.
+ *
+ * Since: 0.10.25
+ */
+#define GST_TAG_ALBUM_ARTIST "album-artist"
+/**
+ * GST_TAG_ALBUM_ARTIST_SORTNAME:
+ *
+ * The artist of the entire album, as it should be sorted.
+ *
+ * Since: 0.10.25
+ */
+#define GST_TAG_ALBUM_ARTIST_SORTNAME "album-artist-sortname"
+/**
+ * GST_TAG_COMPOSER:
+ *
+ * person(s) who composed the recording (string)
+ *
+ * Since: 0.10.15
+ */
+#define GST_TAG_COMPOSER "composer"
+/**
+ * GST_TAG_DATE:
+ *
+ * date the data was created (#GDate structure)
+ */
+#define GST_TAG_DATE "date"
+/**
+ * GST_TAG_DATE_TIME:
+ *
+ * date and time the data was created (#GstDateTime structure)
+ *
+ * Since: 0.10.31
+ */
+#define GST_TAG_DATE_TIME "datetime"
+/**
+ * GST_TAG_GENRE:
+ *
+ * genre this data belongs to (string)
+ */
+#define GST_TAG_GENRE "genre"
+/**
+ * GST_TAG_COMMENT:
+ *
+ * free text commenting the data (string)
+ */
+#define GST_TAG_COMMENT "comment"
+/**
+ * GST_TAG_EXTENDED_COMMENT:
+ *
+ * key/value text commenting the data (string)
+ *
+ * Must be in the form of 'key=comment' or
+ * 'key[lc]=comment' where 'lc' is an ISO-639
+ * language code.
+ *
+ * This tag is used for unknown Vorbis comment tags,
+ * unknown APE tags and certain ID3v2 comment fields.
+ *
+ * Since: 0.10.10
+ */
+#define GST_TAG_EXTENDED_COMMENT "extended-comment"
+/**
+ * GST_TAG_TRACK_NUMBER:
+ *
+ * track number inside a collection (unsigned integer)
+ */
+#define GST_TAG_TRACK_NUMBER "track-number"
+/**
+ * GST_TAG_TRACK_COUNT:
+ *
+ * count of tracks inside collection this track belongs to (unsigned integer)
+ */
+#define GST_TAG_TRACK_COUNT "track-count"
+/**
+ * GST_TAG_ALBUM_VOLUME_NUMBER:
+ *
+ * disc number inside a collection (unsigned integer)
+ */
+#define GST_TAG_ALBUM_VOLUME_NUMBER "album-disc-number"
+/**
+ * GST_TAG_ALBUM_VOLUME_COUNT:
+ *
+ * count of discs inside collection this disc belongs to (unsigned integer)
+ */
+#define GST_TAG_ALBUM_VOLUME_COUNT "album-disc-count"
+/**
+ * GST_TAG_LOCATION:
+ *
+ * Origin of media as a URI (location, where the original of the file or stream
+ * is hosted) (string)
+ */
+#define GST_TAG_LOCATION "location"
+/**
+ * GST_TAG_HOMEPAGE:
+ *
+ * Homepage for this media (i.e. artist or movie homepage) (string)
+ *
+ * Since: 0.10.23
+ */
+#define GST_TAG_HOMEPAGE "homepage"
+/**
+ * GST_TAG_DESCRIPTION:
+ *
+ * short text describing the content of the data (string)
+ */
+#define GST_TAG_DESCRIPTION "description"
+/**
+ * GST_TAG_VERSION:
+ *
+ * version of this data (string)
+ */
+#define GST_TAG_VERSION "version"
+/**
+ * GST_TAG_ISRC:
+ *
+ * International Standard Recording Code - see http://www.ifpi.org/isrc/ (string)
+ */
+#define GST_TAG_ISRC "isrc"
+/**
+ * GST_TAG_ORGANIZATION:
+ *
+ * organization (string)
+ */
+#define GST_TAG_ORGANIZATION "organization"
+/**
+ * GST_TAG_COPYRIGHT:
+ *
+ * copyright notice of the data (string)
+ */
+#define GST_TAG_COPYRIGHT "copyright"
+/**
+ * GST_TAG_COPYRIGHT_URI:
+ *
+ * URI to location where copyright details can be found (string)
+ *
+ * Since: 0.10.14
+ */
+#define GST_TAG_COPYRIGHT_URI "copyright-uri"
+/**
+ * GST_TAG_ENCODED_BY:
+ *
+ * name of the person or organisation that encoded the file. May contain a
+ * copyright message if the person or organisation also holds the copyright
+ * (string)
+ *
+ * Note: do not use this field to describe the encoding application. Use
+ * #GST_TAG_APPLICATION_NAME or #GST_TAG_COMMENT for that.
+ *
+ * Since: 0.10.33
+ */
+#define GST_TAG_ENCODED_BY "encoded-by"
+/**
+ * GST_TAG_CONTACT:
+ *
+ * contact information (string)
+ */
+#define GST_TAG_CONTACT "contact"
+/**
+ * GST_TAG_LICENSE:
+ *
+ * license of data (string)
+ */
+#define GST_TAG_LICENSE "license"
+/**
+ * GST_TAG_LICENSE_URI:
+ *
+ * URI to location where license details can be found (string)
+ *
+ * Since: 0.10.14
+ */
+#define GST_TAG_LICENSE_URI "license-uri"
+/**
+ * GST_TAG_PERFORMER:
+ *
+ * person(s) performing (string)
+ */
+#define GST_TAG_PERFORMER "performer"
+/**
+ * GST_TAG_DURATION:
+ *
+ * length in GStreamer time units (nanoseconds) (unsigned 64-bit integer)
+ */
+#define GST_TAG_DURATION "duration"
+/**
+ * GST_TAG_CODEC:
+ *
+ * codec the data is stored in (string)
+ */
+#define GST_TAG_CODEC "codec"
+/**
+ * GST_TAG_VIDEO_CODEC:
+ *
+ * codec the video data is stored in (string)
+ */
+#define GST_TAG_VIDEO_CODEC "video-codec"
+/**
+ * GST_TAG_AUDIO_CODEC:
+ *
+ * codec the audio data is stored in (string)
+ */
+#define GST_TAG_AUDIO_CODEC "audio-codec"
+/**
+ * GST_TAG_SUBTITLE_CODEC:
+ *
+ * codec/format the subtitle data is stored in (string)
+ *
+ * Since: 0.10.23
+ */
+#define GST_TAG_SUBTITLE_CODEC "subtitle-codec"
+/**
+ * GST_TAG_CONTAINER_FORMAT:
+ *
+ * container format the data is stored in (string)
+ *
+ * Since: 0.10.24
+ */
+#define GST_TAG_CONTAINER_FORMAT "container-format"
+/**
+ * GST_TAG_BITRATE:
+ *
+ * exact or average bitrate in bits/s (unsigned integer)
+ */
+#define GST_TAG_BITRATE "bitrate"
+/**
+ * GST_TAG_NOMINAL_BITRATE:
+ *
+ * nominal bitrate in bits/s (unsigned integer). The actual bitrate might be
+ * different from this target bitrate.
+ */
+#define GST_TAG_NOMINAL_BITRATE "nominal-bitrate"
+/**
+ * GST_TAG_MINIMUM_BITRATE:
+ *
+ * minimum bitrate in bits/s (unsigned integer)
+ */
+#define GST_TAG_MINIMUM_BITRATE "minimum-bitrate"
+/**
+ * GST_TAG_MAXIMUM_BITRATE:
+ *
+ * maximum bitrate in bits/s (unsigned integer)
+ */
+#define GST_TAG_MAXIMUM_BITRATE "maximum-bitrate"
+/**
+ * GST_TAG_SERIAL:
+ *
+ * serial number of track (unsigned integer)
+ */
+#define GST_TAG_SERIAL "serial"
+/**
+ * GST_TAG_ENCODER:
+ *
+ * encoder used to encode this stream (string)
+ */
+#define GST_TAG_ENCODER "encoder"
+/**
+ * GST_TAG_ENCODER_VERSION:
+ *
+ * version of the encoder used to encode this stream (unsigned integer)
+ */
+#define GST_TAG_ENCODER_VERSION "encoder-version"
+/**
+ * GST_TAG_TRACK_GAIN:
+ *
+ * track gain in db (double)
+ */
+#define GST_TAG_TRACK_GAIN "replaygain-track-gain"
+/**
+ * GST_TAG_TRACK_PEAK:
+ *
+ * peak of the track (double)
+ */
+#define GST_TAG_TRACK_PEAK "replaygain-track-peak"
+/**
+ * GST_TAG_ALBUM_GAIN:
+ *
+ * album gain in db (double)
+ */
+#define GST_TAG_ALBUM_GAIN "replaygain-album-gain"
+/**
+ * GST_TAG_ALBUM_PEAK:
+ *
+ * peak of the album (double)
+ */
+#define GST_TAG_ALBUM_PEAK "replaygain-album-peak"
+/**
+ * GST_TAG_REFERENCE_LEVEL:
+ *
+ * reference level of track and album gain values (double)
+ *
+ * Since: 0.10.12
+ */
+#define GST_TAG_REFERENCE_LEVEL "replaygain-reference-level"
+/**
+ * GST_TAG_LANGUAGE_CODE:
+ *
+ * Language code (ISO-639-1) (string) of the content
+ */
+#define GST_TAG_LANGUAGE_CODE "language-code"
+/**
+ * GST_TAG_IMAGE:
+ *
+ * image (buffer) (buffer caps should specify the content type and preferably
+ * also set "image-type" field as #GstTagImageType)
+ *
+ * Since: 0.10.6
+ */
+#define GST_TAG_IMAGE "image"
+/**
+ * GST_TAG_PREVIEW_IMAGE:
+ *
+ * image that is meant for preview purposes, e.g. small icon-sized version
+ * (buffer) (buffer caps should specify the content type)
+ *
+ * Since: 0.10.7
+ */
+#define GST_TAG_PREVIEW_IMAGE "preview-image"
+
+/**
+ * GST_TAG_ATTACHMENT:
+ *
+ * generic file attachment (buffer) (buffer caps should specify the content
+ * type and if possible set "filename" to the file name of the
+ * attachment)
+ *
+ * Since: 0.10.21
+ */
+#define GST_TAG_ATTACHMENT "attachment"
+
+/**
+ * GST_TAG_BEATS_PER_MINUTE:
+ *
+ * number of beats per minute in audio (double)
+ *
+ * Since: 0.10.12
+ */
+#define GST_TAG_BEATS_PER_MINUTE "beats-per-minute"
+
+/**
+ * GST_TAG_KEYWORDS:
+ *
+ * comma separated keywords describing the content (string).
+ *
+ * Since: 0.10.21
+ */
+#define GST_TAG_KEYWORDS "keywords"
+
+/**
+ * GST_TAG_GEO_LOCATION_NAME:
+ *
+ * human readable descriptive location of where the media has been recorded or
+ * produced. (string).
+ *
+ * Since: 0.10.21
+ */
+#define GST_TAG_GEO_LOCATION_NAME "geo-location-name"
+
+/**
+ * GST_TAG_GEO_LOCATION_LATITUDE:
+ *
+ * geo latitude location of where the media has been recorded or produced in
+ * degrees according to WGS84 (zero at the equator, negative values for southern
+ * latitudes) (double).
+ *
+ * Since: 0.10.21
+ */
+#define GST_TAG_GEO_LOCATION_LATITUDE "geo-location-latitude"
+
+/**
+ * GST_TAG_GEO_LOCATION_LONGITUDE:
+ *
+ * geo longitude location of where the media has been recorded or produced in
+ * degrees according to WGS84 (zero at the prime meridian in Greenwich/UK,
+ * negative values for western longitudes). (double).
+ *
+ * Since: 0.10.21
+ */
+#define GST_TAG_GEO_LOCATION_LONGITUDE "geo-location-longitude"
+
+/**
+ * GST_TAG_GEO_LOCATION_ELEVATION:
+ *
+ * geo elevation of where the media has been recorded or produced in meters
+ * according to WGS84 (zero is average sea level) (double).
+ *
+ * Since: 0.10.21
+ */
+#define GST_TAG_GEO_LOCATION_ELEVATION "geo-location-elevation"
+/**
+ * GST_TAG_GEO_LOCATION_COUNTRY:
+ *
+ * The country (english name) where the media has been produced (string).
+ *
+ * Since: 0.10.29
+ */
+#define GST_TAG_GEO_LOCATION_COUNTRY "geo-location-country"
+/**
+ * GST_TAG_GEO_LOCATION_CITY:
+ *
+ * The city (english name) where the media has been produced (string).
+ *
+ * Since: 0.10.29
+ */
+#define GST_TAG_GEO_LOCATION_CITY "geo-location-city"
+/**
+ * GST_TAG_GEO_LOCATION_SUBLOCATION:
+ *
+ * A location 'smaller' than GST_TAG_GEO_LOCATION_CITY that specifies better
+ * where the media has been produced. (e.g. the neighborhood) (string).
+ *
+ * This tag has been added as this is how it is handled/named in XMP's
+ * Iptc4xmpcore schema.
+ *
+ * Since: 0.10.29
+ */
+#define GST_TAG_GEO_LOCATION_SUBLOCATION "geo-location-sublocation"
+/**
+ * GST_TAG_GEO_LOCATION_HORIZONTAL_ERROR:
+ *
+ * Represents the expected error on the horizontal positioning in
+ * meters (double).
+ *
+ * Since: 0.10.31
+ */
+#define GST_TAG_GEO_LOCATION_HORIZONTAL_ERROR "geo-location-horizontal-error"
+/**
+ * GST_TAG_GEO_LOCATION_MOVEMENT_SPEED:
+ *
+ * Speed of the capturing device when performing the capture.
+ * Represented in m/s. (double)
+ *
+ * See also #GST_TAG_GEO_LOCATION_MOVEMENT_DIRECTION
+ *
+ * Since 0.10.30
+ */
+#define GST_TAG_GEO_LOCATION_MOVEMENT_SPEED "geo-location-movement-speed"
+/**
+ * GST_TAG_GEO_LOCATION_MOVEMENT_DIRECTION:
+ *
+ * Indicates the movement direction of the device performing the capture
+ * of a media. It is represented as degrees in floating point representation,
+ * 0 means the geographic north, and increases clockwise (double from 0 to 360)
+ *
+ * See also #GST_TAG_GEO_LOCATION_CAPTURE_DIRECTION
+ *
+ * Since: 0.10.30
+ */
+#define GST_TAG_GEO_LOCATION_MOVEMENT_DIRECTION "geo-location-movement-direction"
+/**
+ * GST_TAG_GEO_LOCATION_CAPTURE_DIRECTION:
+ *
+ * Indicates the direction the device is pointing to when capturing
+ * a media. It is represented as degrees in floating point representation,
+ * 0 means the geographic north, and increases clockwise (double from 0 to 360)
+ *
+ * See also #GST_TAG_GEO_LOCATION_MOVEMENT_DIRECTION
+ *
+ * Since: 0.10.30
+ */
+#define GST_TAG_GEO_LOCATION_CAPTURE_DIRECTION "geo-location-capture-direction"
+/**
+ * GST_TAG_SHOW_NAME:
+ *
+ * Name of the show, used for displaying (string)
+ *
+ * Since: 0.10.26
+ */
+#define GST_TAG_SHOW_NAME "show-name"
+/**
+ * GST_TAG_SHOW_SORTNAME:
+ *
+ * Name of the show, used for sorting (string)
+ *
+ * Since: 0.10.26
+ */
+#define GST_TAG_SHOW_SORTNAME "show-sortname"
+/**
+ * GST_TAG_SHOW_EPISODE_NUMBER:
+ *
+ * Number of the episode within a season/show (unsigned integer)
+ *
+ * Since: 0.10.26
+ */
+#define GST_TAG_SHOW_EPISODE_NUMBER "show-episode-number"
+/**
+ * GST_TAG_SHOW_SEASON_NUMBER:
+ *
+ * Number of the season of a show/series (unsigned integer)
+ *
+ * Since: 0.10.26
+ */
+#define GST_TAG_SHOW_SEASON_NUMBER "show-season-number"
+/**
+ * GST_TAG_LYRICS:
+ *
+ * The lyrics of the media (string)
+ *
+ * Since: 0.10.26
+ */
+#define GST_TAG_LYRICS "lyrics"
+/**
+ * GST_TAG_COMPOSER_SORTNAME:
+ *
+ * The composer's name, used for sorting (string)
+ *
+ * Since: 0.10.26
+ */
+#define GST_TAG_COMPOSER_SORTNAME "composer-sortname"
+/**
+ * GST_TAG_GROUPING:
+ *
+ * Groups together media that are related and spans multiple tracks. An
+ * example are multiple pieces of a concerto. (string)
+ *
+ * Since: 0.10.26
+ */
+#define GST_TAG_GROUPING "grouping"
+/**
+ * GST_TAG_USER_RATING:
+ *
+ * Rating attributed by a person (likely the application user).
+ * The higher the value, the more the user likes this media
+ * (unsigned int from 0 to 100)
+ *
+ * Since: 0.10.29
+ */
+#define GST_TAG_USER_RATING "user-rating"
+/**
+ * GST_TAG_DEVICE_MANUFACTURER:
+ *
+ * Manufacturer of the device used to create the media (string)
+ *
+ * Since: 0.10.30
+ */
+#define GST_TAG_DEVICE_MANUFACTURER "device-manufacturer"
+/**
+ * GST_TAG_DEVICE_MODEL:
+ *
+ * Model of the device used to create the media (string)
+ *
+ * Since: 0.10.30
+ */
+#define GST_TAG_DEVICE_MODEL "device-model"
+/**
+ * GST_TAG_APPLICATION_NAME:
+ *
+ * Name of the application used to create the media (string)
+ *
+ * Since: 0.10.31
+ */
+#define GST_TAG_APPLICATION_NAME "application-name"
+/**
+ * GST_TAG_APPLICATION_DATA:
+ *
+ * Arbitrary application data (buffer)
+ *
+ * Some formats allow application's to add their own arbitrary data
+ * into files. This data is application's dependent.
+ *
+ * Since: 0.10.31
+ */
+#define GST_TAG_APPLICATION_DATA "application-data"
+/**
+ * GST_TAG_IMAGE_ORIENTATION:
+ *
+ * Represents the 'Orientation' tag from EXIF. Defines how the image
+ * should be rotated and mirrored for display. (string)
+ *
+ * This tag has a predefined set of allowed values:
+ * "rotate-0"
+ * "rotate-90"
+ * "rotate-180"
+ * "rotate-270"
+ * "flip-rotate-0"
+ * "flip-rotate-90"
+ * "flip-rotate-180"
+ * "flip-rotate-270"
+ *
+ * The naming is adopted according to a possible transformation to perform
+ * on the image to fix its orientation, obviously equivalent operations will
+ * yield the same result.
+ *
+ * Rotations indicated by the values are in clockwise direction and
+ * 'flip' means an horizontal mirroring.
+ *
+ * Since: 0.10.30
+ */
+#define GST_TAG_IMAGE_ORIENTATION "image-orientation"
+
+G_END_DECLS
+
+#endif /* __GST_TAGLIST_H__ */
diff --git a/gst/gsttagsetter.c b/gst/gsttagsetter.c
new file mode 100644
index 0000000..d08d7e8
--- /dev/null
+++ b/gst/gsttagsetter.c
@@ -0,0 +1,424 @@
+/* GStreamer
+ * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
+ *
+ * gsttagsetter.c: interface for tag setting on elements
+ *
+ * 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:gsttagsetter
+ * @short_description: Element interface that allows setting and retrieval
+ * of media metadata
+ *
+ * Element interface that allows setting of media metadata.
+ *
+ * Elements that support changing a stream's metadata will implement this
+ * interface. Examples of such elements are 'vorbisenc', 'theoraenc' and
+ * 'id3v2mux'.
+ *
+ * If you just want to retrieve metadata in your application then all you
+ * need to do is watch for tag messages on your pipeline's bus. This
+ * interface is only for setting metadata, not for extracting it. To set tags
+ * from the application, find tagsetter elements and set tags using e.g.
+ * gst_tag_setter_merge_tags() or gst_tag_setter_add_tags(). Also consider
+ * setting the #GstTagMergeMode that is used for tag events that arrive at the
+ * tagsetter element (default mode is to keep existing tags).
+ * The application should do that before the element goes to %GST_STATE_PAUSED.
+ *
+ * Elements implementing the #GstTagSetter interface often have to merge
+ * any tags received from upstream and the tags set by the application via
+ * the interface. This can be done like this:
+ *
+ * |[
+ * GstTagMergeMode merge_mode;
+ * const GstTagList *application_tags;
+ * const GstTagList *event_tags;
+ * GstTagSetter *tagsetter;
+ * GstTagList *result;
+ *
+ * tagsetter = GST_TAG_SETTER (element);
+ *
+ * merge_mode = gst_tag_setter_get_tag_merge_mode (tagsetter);
+ * application_tags = gst_tag_setter_get_tag_list (tagsetter);
+ * event_tags = (const GstTagList *) element->event_tags;
+ *
+ * GST_LOG_OBJECT (tagsetter, "merging tags, merge mode = %d", merge_mode);
+ * GST_LOG_OBJECT (tagsetter, "event tags: %" GST_PTR_FORMAT, event_tags);
+ * GST_LOG_OBJECT (tagsetter, "set tags: %" GST_PTR_FORMAT, application_tags);
+ *
+ * result = gst_tag_list_merge (application_tags, event_tags, merge_mode);
+ *
+ * GST_LOG_OBJECT (tagsetter, "final tags: %" GST_PTR_FORMAT, result);
+ * ]|
+ *
+ * Last reviewed on 2006-05-18 (0.10.6)
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "gst_private.h"
+#include "gsttagsetter.h"
+#include <gobject/gvaluecollector.h>
+#include <string.h>
+
+static GQuark gst_tag_key;
+
+typedef struct
+{
+ GstTagMergeMode mode;
+ GstTagList *list;
+ GStaticMutex lock;
+} GstTagData;
+
+GType
+gst_tag_setter_get_type (void)
+{
+ static volatile gsize tag_setter_type = 0;
+
+ if (g_once_init_enter (&tag_setter_type)) {
+ GType _type;
+ static const GTypeInfo tag_setter_info = {
+ sizeof (GstTagSetterIFace), /* class_size */
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ NULL,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ 0,
+ 0,
+ NULL
+ };
+
+ _type = g_type_register_static (G_TYPE_INTERFACE, "GstTagSetter",
+ &tag_setter_info, 0);
+
+ g_type_interface_add_prerequisite (_type, GST_TYPE_ELEMENT);
+
+ gst_tag_key = g_quark_from_static_string ("GST_TAG_SETTER");
+ g_once_init_leave (&tag_setter_type, _type);
+ }
+
+ return tag_setter_type;
+}
+
+static void
+gst_tag_data_free (gpointer p)
+{
+ GstTagData *data = (GstTagData *) p;
+
+ if (data->list)
+ gst_tag_list_free (data->list);
+
+ g_static_mutex_free (&data->lock);
+
+ g_slice_free (GstTagData, data);
+}
+
+static GstTagData *
+gst_tag_setter_get_data (GstTagSetter * setter)
+{
+ GstTagData *data;
+
+ data = g_object_get_qdata (G_OBJECT (setter), gst_tag_key);
+ if (!data) {
+ static GStaticMutex create_mutex = G_STATIC_MUTEX_INIT;
+
+ /* make sure no other thread is creating a GstTagData at the same time */
+ g_static_mutex_lock (&create_mutex);
+ data = g_object_get_qdata (G_OBJECT (setter), gst_tag_key);
+ if (!data) {
+ data = g_slice_new (GstTagData);
+ g_static_mutex_init (&data->lock);
+ data->list = NULL;
+ data->mode = GST_TAG_MERGE_KEEP;
+ g_object_set_qdata_full (G_OBJECT (setter), gst_tag_key, data,
+ gst_tag_data_free);
+ }
+ g_static_mutex_unlock (&create_mutex);
+ }
+
+ return data;
+}
+
+/**
+ * gst_tag_setter_reset_tags:
+ * @setter: a #GstTagSetter
+ *
+ * Reset the internal taglist. Elements should call this from within the
+ * state-change handler.
+ *
+ * Since: 0.10.22
+ */
+void
+gst_tag_setter_reset_tags (GstTagSetter * setter)
+{
+ GstTagData *data;
+
+ g_return_if_fail (GST_IS_TAG_SETTER (setter));
+
+ data = gst_tag_setter_get_data (setter);
+
+ g_static_mutex_lock (&data->lock);
+ if (data->list) {
+ gst_tag_list_free (data->list);
+ data->list = NULL;
+ }
+ g_static_mutex_unlock (&data->lock);
+}
+
+/**
+ * gst_tag_setter_merge_tags:
+ * @setter: a #GstTagSetter
+ * @list: a tag list to merge from
+ * @mode: the mode to merge with
+ *
+ * Merges the given list into the setter's list using the given mode.
+ */
+void
+gst_tag_setter_merge_tags (GstTagSetter * setter, const GstTagList * list,
+ GstTagMergeMode mode)
+{
+ GstTagData *data;
+
+ g_return_if_fail (GST_IS_TAG_SETTER (setter));
+ g_return_if_fail (GST_TAG_MODE_IS_VALID (mode));
+ g_return_if_fail (GST_IS_TAG_LIST (list));
+
+ data = gst_tag_setter_get_data (setter);
+
+ g_static_mutex_lock (&data->lock);
+ if (data->list == NULL) {
+ if (mode != GST_TAG_MERGE_KEEP_ALL)
+ data->list = gst_tag_list_copy (list);
+ } else {
+ gst_tag_list_insert (data->list, list, mode);
+ }
+ g_static_mutex_unlock (&data->lock);
+}
+
+/**
+ * gst_tag_setter_add_tags:
+ * @setter: a #GstTagSetter
+ * @mode: the mode to use
+ * @tag: tag to set
+ * @...: more tag / value pairs to set
+ *
+ * Adds the given tag / value pairs on the setter using the given merge mode.
+ * The list must be terminated with NULL.
+ */
+void
+gst_tag_setter_add_tags (GstTagSetter * setter, GstTagMergeMode mode,
+ const gchar * tag, ...)
+{
+ va_list args;
+
+ g_return_if_fail (GST_IS_TAG_SETTER (setter));
+ g_return_if_fail (GST_TAG_MODE_IS_VALID (mode));
+
+ va_start (args, tag);
+ gst_tag_setter_add_tag_valist (setter, mode, tag, args);
+ va_end (args);
+}
+
+/**
+ * gst_tag_setter_add_tag_values:
+ * @setter: a #GstTagSetter
+ * @mode: the mode to use
+ * @tag: tag to set
+ * @...: more tag / GValue pairs to set
+ *
+ * Adds the given tag / GValue pairs on the setter using the given merge mode.
+ * The list must be terminated with NULL.
+ */
+void
+gst_tag_setter_add_tag_values (GstTagSetter * setter, GstTagMergeMode mode,
+ const gchar * tag, ...)
+{
+ va_list args;
+
+ g_return_if_fail (GST_IS_TAG_SETTER (setter));
+ g_return_if_fail (GST_TAG_MODE_IS_VALID (mode));
+
+ va_start (args, tag);
+ gst_tag_setter_add_tag_valist_values (setter, mode, tag, args);
+ va_end (args);
+}
+
+/**
+ * gst_tag_setter_add_tag_valist:
+ * @setter: a #GstTagSetter
+ * @mode: the mode to use
+ * @tag: tag to set
+ * @var_args: tag / value pairs to set
+ *
+ * Adds the given tag / value pairs on the setter using the given merge mode.
+ * The list must be terminated with NULL.
+ */
+void
+gst_tag_setter_add_tag_valist (GstTagSetter * setter, GstTagMergeMode mode,
+ const gchar * tag, va_list var_args)
+{
+ GstTagData *data;
+
+ g_return_if_fail (GST_IS_TAG_SETTER (setter));
+ g_return_if_fail (GST_TAG_MODE_IS_VALID (mode));
+
+ data = gst_tag_setter_get_data (setter);
+
+ g_static_mutex_lock (&data->lock);
+ if (!data->list)
+ data->list = gst_tag_list_new ();
+
+ gst_tag_list_add_valist (data->list, mode, tag, var_args);
+
+ g_static_mutex_unlock (&data->lock);
+}
+
+/**
+ * gst_tag_setter_add_tag_valist_values:
+ * @setter: a #GstTagSetter
+ * @mode: the mode to use
+ * @tag: tag to set
+ * @var_args: tag / GValue pairs to set
+ *
+ * Adds the given tag / GValue pairs on the setter using the given merge mode.
+ * The list must be terminated with NULL.
+ */
+void
+gst_tag_setter_add_tag_valist_values (GstTagSetter * setter,
+ GstTagMergeMode mode, const gchar * tag, va_list var_args)
+{
+ GstTagData *data;
+
+ g_return_if_fail (GST_IS_TAG_SETTER (setter));
+ g_return_if_fail (GST_TAG_MODE_IS_VALID (mode));
+
+ data = gst_tag_setter_get_data (setter);
+
+ g_static_mutex_lock (&data->lock);
+
+ if (!data->list)
+ data->list = gst_tag_list_new ();
+
+ gst_tag_list_add_valist_values (data->list, mode, tag, var_args);
+
+ g_static_mutex_unlock (&data->lock);
+}
+
+/**
+ * gst_tag_setter_add_tag_value:
+ * @setter: a #GstTagSetter
+ * @mode: the mode to use
+ * @tag: tag to set
+ * @value: GValue to set for the tag
+ *
+ * Adds the given tag / GValue pair on the setter using the given merge mode.
+ *
+ * Since: 0.10.24
+ */
+void
+gst_tag_setter_add_tag_value (GstTagSetter * setter,
+ GstTagMergeMode mode, const gchar * tag, const GValue * value)
+{
+ GstTagData *data;
+
+ g_return_if_fail (GST_IS_TAG_SETTER (setter));
+ g_return_if_fail (GST_TAG_MODE_IS_VALID (mode));
+
+ data = gst_tag_setter_get_data (setter);
+
+ g_static_mutex_lock (&data->lock);
+
+ if (!data->list)
+ data->list = gst_tag_list_new ();
+
+ gst_tag_list_add_value (data->list, mode, tag, value);
+
+ g_static_mutex_unlock (&data->lock);
+}
+
+/**
+ * gst_tag_setter_get_tag_list:
+ * @setter: a #GstTagSetter
+ *
+ * Returns the current list of tags the setter uses. The list should not be
+ * modified or freed.
+ *
+ * This function is not thread-safe.
+ *
+ * Returns: (transfer none): a current snapshot of the taglist used in the
+ * setter or NULL if none is used.
+ */
+const GstTagList *
+gst_tag_setter_get_tag_list (GstTagSetter * setter)
+{
+ g_return_val_if_fail (GST_IS_TAG_SETTER (setter), NULL);
+
+ return gst_tag_setter_get_data (setter)->list;
+}
+
+/**
+ * gst_tag_setter_set_tag_merge_mode:
+ * @setter: a #GstTagSetter
+ * @mode: The mode with which tags are added
+ *
+ * Sets the given merge mode that is used for adding tags from events to tags
+ * specified by this interface. The default is #GST_TAG_MERGE_KEEP, which keeps
+ * the tags set with this interface and discards tags from events.
+ */
+void
+gst_tag_setter_set_tag_merge_mode (GstTagSetter * setter, GstTagMergeMode mode)
+{
+ GstTagData *data;
+
+ g_return_if_fail (GST_IS_TAG_SETTER (setter));
+ g_return_if_fail (GST_TAG_MODE_IS_VALID (mode));
+
+ data = gst_tag_setter_get_data (setter);
+
+ g_static_mutex_lock (&data->lock);
+ data->mode = mode;
+ g_static_mutex_unlock (&data->lock);
+}
+
+/**
+ * gst_tag_setter_get_tag_merge_mode:
+ * @setter: a #GstTagSetter
+ *
+ * Queries the mode by which tags inside the setter are overwritten by tags
+ * from events
+ *
+ * Returns: the merge mode used inside the element.
+ */
+GstTagMergeMode
+gst_tag_setter_get_tag_merge_mode (GstTagSetter * setter)
+{
+ GstTagMergeMode mode;
+ GstTagData *data;
+
+ g_return_val_if_fail (GST_IS_TAG_SETTER (setter), GST_TAG_MERGE_UNDEFINED);
+
+ data = gst_tag_setter_get_data (setter);
+
+ g_static_mutex_lock (&data->lock);
+ mode = data->mode;
+ g_static_mutex_unlock (&data->lock);
+
+ return mode;
+}
diff --git a/gst/gsttagsetter.h b/gst/gsttagsetter.h
new file mode 100644
index 0000000..44c794b
--- /dev/null
+++ b/gst/gsttagsetter.h
@@ -0,0 +1,100 @@
+/* GStreamer
+ * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
+ *
+ * gsttagsetter.h: Interfaces for tagging
+ *
+ * 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_TAG_SETTER_H__
+#define __GST_TAG_SETTER_H__
+
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_TAG_SETTER (gst_tag_setter_get_type ())
+#define GST_TAG_SETTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_TAG_SETTER, GstTagSetter))
+#define GST_IS_TAG_SETTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_TAG_SETTER))
+#define GST_TAG_SETTER_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GST_TYPE_TAG_SETTER, GstTagSetterIFace))
+
+/**
+ * GstTagSetter:
+ *
+ * Opaque #GstTagSetter data structure.
+ */
+typedef struct _GstTagSetter GstTagSetter; /* Dummy typedef */
+typedef struct _GstTagSetterIFace GstTagSetterIFace;
+
+/**
+ * GstTagSetterIFace:
+ * @g_iface: parent interface type.
+ *
+ * #GstTagSetterIFace interface.
+ */
+/* use an empty interface here to allow detection of elements using user-set
+ tags */
+struct _GstTagSetterIFace
+{
+ GTypeInterface g_iface;
+
+ /* signals */
+
+ /* virtual table */
+};
+
+GType gst_tag_setter_get_type (void);
+
+void gst_tag_setter_reset_tags (GstTagSetter * setter);
+
+void gst_tag_setter_merge_tags (GstTagSetter * setter,
+ const GstTagList * list,
+ GstTagMergeMode mode);
+void gst_tag_setter_add_tags (GstTagSetter * setter,
+ GstTagMergeMode mode,
+ const gchar * tag,
+ ...) G_GNUC_NULL_TERMINATED;
+
+void gst_tag_setter_add_tag_values (GstTagSetter * setter,
+ GstTagMergeMode mode,
+ const gchar * tag,
+ ...) G_GNUC_NULL_TERMINATED;
+
+void gst_tag_setter_add_tag_valist (GstTagSetter * setter,
+ GstTagMergeMode mode,
+ const gchar * tag,
+ va_list var_args);
+
+void gst_tag_setter_add_tag_valist_values(GstTagSetter * setter,
+ GstTagMergeMode mode,
+ const gchar * tag,
+ va_list var_args);
+
+void gst_tag_setter_add_tag_value (GstTagSetter * setter,
+ GstTagMergeMode mode,
+ const gchar * tag,
+ const GValue * value);
+
+const GstTagList *
+ gst_tag_setter_get_tag_list (GstTagSetter * setter);
+
+void gst_tag_setter_set_tag_merge_mode (GstTagSetter * setter,
+ GstTagMergeMode mode);
+GstTagMergeMode gst_tag_setter_get_tag_merge_mode (GstTagSetter * setter);
+
+G_END_DECLS
+
+#endif /* __GST_TAG_SETTER_H__ */
diff --git a/gst/gsttask.c b/gst/gsttask.c
new file mode 100644
index 0000000..aac6b7a
--- /dev/null
+++ b/gst/gsttask.c
@@ -0,0 +1,855 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2005 Wim Taymans <wim@fluendo.com>
+ *
+ * gsttask.c: Streaming tasks
+ *
+ * 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:gsttask
+ * @short_description: Abstraction of GStreamer streaming threads.
+ * @see_also: #GstElement, #GstPad
+ *
+ * #GstTask is used by #GstElement and #GstPad to provide the data passing
+ * threads in a #GstPipeline.
+ *
+ * A #GstPad will typically start a #GstTask to push or pull data to/from the
+ * peer pads. Most source elements start a #GstTask to push data. In some cases
+ * a demuxer element can start a #GstTask to pull data from a peer element. This
+ * is typically done when the demuxer can perform random access on the upstream
+ * peer element for improved performance.
+ *
+ * Although convenience functions exist on #GstPad to start/pause/stop tasks, it
+ * might sometimes be needed to create a #GstTask manually if it is not related to
+ * a #GstPad.
+ *
+ * Before the #GstTask can be run, it needs a #GStaticRecMutex that can be set with
+ * gst_task_set_lock().
+ *
+ * The task can be started, paused and stopped with gst_task_start(), gst_task_pause()
+ * and gst_task_stop() respectively or with the gst_task_set_state() function.
+ *
+ * A #GstTask will repeatedly call the #GstTaskFunction with the user data
+ * that was provided when creating the task with gst_task_create(). While calling
+ * the function it will acquire the provided lock. The provided lock is released
+ * when the task pauses or stops.
+ *
+ * Stopping a task with gst_task_stop() will not immediately make sure the task is
+ * not running anymore. Use gst_task_join() to make sure the task is completely
+ * stopped and the thread is stopped.
+ *
+ * After creating a #GstTask, use gst_object_unref() to free its resources. This can
+ * only be done it the task is not running anymore.
+ *
+ * Task functions can send a #GstMessage to send out-of-band data to the
+ * application. The application can receive messages from the #GstBus in its
+ * mainloop.
+ *
+ * For debugging perposes, the task will configure its object name as the thread
+ * name on Linux. Please note that the object name should be configured before the
+ * task is started; changing the object name after the task has been started, has
+ * no effect on the thread name.
+ *
+ * Last reviewed on 2010-03-15 (0.10.29)
+ */
+
+#include "gst_private.h"
+
+#include "gstinfo.h"
+#include "gsttask.h"
+
+#include <stdio.h>
+
+#ifdef HAVE_SYS_PRCTL_H
+#include <sys/prctl.h>
+#endif
+
+GST_DEBUG_CATEGORY_STATIC (task_debug);
+#define GST_CAT_DEFAULT (task_debug)
+
+#define SET_TASK_STATE(t,s) (g_atomic_int_set (&GST_TASK_STATE(t), (s)))
+#define GET_TASK_STATE(t) ((GstTaskState) g_atomic_int_get (&GST_TASK_STATE(t)))
+
+#define GST_TASK_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_TASK, GstTaskPrivate))
+
+struct _GstTaskPrivate
+{
+ /* callbacks for managing the thread of this task */
+ GstTaskThreadCallbacks thr_callbacks;
+ gpointer thr_user_data;
+ GDestroyNotify thr_notify;
+
+ gboolean prio_set;
+ GThreadPriority priority;
+
+ /* configured pool */
+ GstTaskPool *pool;
+
+ /* remember the pool and id that is currently running. */
+ gpointer id;
+ GstTaskPool *pool_id;
+};
+
+#ifdef _MSC_VER
+#include <windows.h>
+
+struct _THREADNAME_INFO
+{
+ DWORD dwType; // must be 0x1000
+ LPCSTR szName; // pointer to name (in user addr space)
+ DWORD dwThreadID; // thread ID (-1=caller thread)
+ DWORD dwFlags; // reserved for future use, must be zero
+};
+typedef struct _THREADNAME_INFO THREADNAME_INFO;
+
+void
+SetThreadName (DWORD dwThreadID, LPCSTR szThreadName)
+{
+ THREADNAME_INFO info;
+ info.dwType = 0x1000;
+ info.szName = szThreadName;
+ info.dwThreadID = dwThreadID;
+ info.dwFlags = 0;
+
+ __try {
+ RaiseException (0x406D1388, 0, sizeof (info) / sizeof (DWORD),
+ (DWORD *) & info);
+ }
+ __except (EXCEPTION_CONTINUE_EXECUTION) {
+ }
+}
+#endif
+
+static void gst_task_finalize (GObject * object);
+
+static void gst_task_func (GstTask * task);
+
+static GStaticMutex pool_lock = G_STATIC_MUTEX_INIT;
+
+#define _do_init \
+{ \
+ GST_DEBUG_CATEGORY_INIT (task_debug, "task", 0, "Processing tasks"); \
+}
+
+G_DEFINE_TYPE_WITH_CODE (GstTask, gst_task, GST_TYPE_OBJECT, _do_init);
+
+static void
+init_klass_pool (GstTaskClass * klass)
+{
+ g_static_mutex_lock (&pool_lock);
+ if (klass->pool) {
+ gst_task_pool_cleanup (klass->pool);
+ gst_object_unref (klass->pool);
+ }
+ klass->pool = gst_task_pool_new ();
+ gst_task_pool_prepare (klass->pool, NULL);
+ g_static_mutex_unlock (&pool_lock);
+}
+
+static void
+gst_task_class_init (GstTaskClass * klass)
+{
+ GObjectClass *gobject_class;
+
+ gobject_class = (GObjectClass *) klass;
+
+ g_type_class_add_private (klass, sizeof (GstTaskPrivate));
+
+ gobject_class->finalize = gst_task_finalize;
+
+ init_klass_pool (klass);
+}
+
+static void
+gst_task_init (GstTask * task)
+{
+ GstTaskClass *klass;
+
+ klass = GST_TASK_GET_CLASS (task);
+
+ task->priv = GST_TASK_GET_PRIVATE (task);
+ task->running = FALSE;
+ task->thread = NULL;
+ task->lock = NULL;
+ task->cond = g_cond_new ();
+ SET_TASK_STATE (task, GST_TASK_STOPPED);
+ task->priv->prio_set = FALSE;
+
+ /* use the default klass pool for this task, users can
+ * override this later */
+ g_static_mutex_lock (&pool_lock);
+ task->priv->pool = gst_object_ref (klass->pool);
+ g_static_mutex_unlock (&pool_lock);
+}
+
+static void
+gst_task_finalize (GObject * object)
+{
+ GstTask *task = GST_TASK (object);
+ GstTaskPrivate *priv = task->priv;
+
+ GST_DEBUG ("task %p finalize", task);
+
+ if (priv->thr_notify)
+ priv->thr_notify (priv->thr_user_data);
+ priv->thr_notify = NULL;
+ priv->thr_user_data = NULL;
+
+ gst_object_unref (priv->pool);
+
+ /* task thread cannot be running here since it holds a ref
+ * to the task so that the finalize could not have happened */
+ g_cond_free (task->cond);
+ task->cond = NULL;
+
+ G_OBJECT_CLASS (gst_task_parent_class)->finalize (object);
+}
+
+/* should be called with the object LOCK */
+static void
+gst_task_configure_name (GstTask * task)
+{
+#if defined(HAVE_SYS_PRCTL_H) && defined(PR_SET_NAME)
+ const gchar *name;
+ gchar thread_name[17] = { 0, };
+
+ GST_OBJECT_LOCK (task);
+ name = GST_OBJECT_NAME (task);
+
+ /* set the thread name to something easily identifiable */
+ if (!snprintf (thread_name, 17, "%s", GST_STR_NULL (name))) {
+ GST_DEBUG_OBJECT (task, "Could not create thread name for '%s'", name);
+ } else {
+ GST_DEBUG_OBJECT (task, "Setting thread name to '%s'", thread_name);
+ if (prctl (PR_SET_NAME, (unsigned long int) thread_name, 0, 0, 0))
+ GST_DEBUG_OBJECT (task, "Failed to set thread name");
+ }
+ GST_OBJECT_UNLOCK (task);
+#endif
+#ifdef _MSC_VER
+ const gchar *name;
+ name = GST_OBJECT_NAME (task);
+
+ /* set the thread name to something easily identifiable */
+ GST_DEBUG_OBJECT (task, "Setting thread name to '%s'", name);
+ SetThreadName (-1, name);
+#endif
+}
+
+static void
+gst_task_func (GstTask * task)
+{
+ GStaticRecMutex *lock;
+ GThread *tself;
+ GstTaskPrivate *priv;
+
+ priv = task->priv;
+
+ tself = g_thread_self ();
+
+ GST_DEBUG ("Entering task %p, thread %p", task, tself);
+
+ /* we have to grab the lock to get the mutex. We also
+ * mark our state running so that nobody can mess with
+ * the mutex. */
+ GST_OBJECT_LOCK (task);
+ if (GET_TASK_STATE (task) == GST_TASK_STOPPED)
+ goto exit;
+ lock = GST_TASK_GET_LOCK (task);
+ if (G_UNLIKELY (lock == NULL))
+ goto no_lock;
+ task->thread = tself;
+ /* only update the priority when it was changed */
+ if (priv->prio_set)
+ g_thread_set_priority (tself, priv->priority);
+ GST_OBJECT_UNLOCK (task);
+
+ /* fire the enter_thread callback when we need to */
+ if (priv->thr_callbacks.enter_thread)
+ priv->thr_callbacks.enter_thread (task, tself, priv->thr_user_data);
+
+ /* locking order is TASK_LOCK, LOCK */
+ g_static_rec_mutex_lock (lock);
+ /* configure the thread name now */
+ gst_task_configure_name (task);
+
+ while (G_LIKELY (GET_TASK_STATE (task) != GST_TASK_STOPPED)) {
+ if (G_UNLIKELY (GET_TASK_STATE (task) == GST_TASK_PAUSED)) {
+ GST_OBJECT_LOCK (task);
+ while (G_UNLIKELY (GST_TASK_STATE (task) == GST_TASK_PAUSED)) {
+ gint t;
+
+ t = g_static_rec_mutex_unlock_full (lock);
+ if (t <= 0) {
+ g_warning ("wrong STREAM_LOCK count %d", t);
+ }
+ GST_TASK_SIGNAL (task);
+ GST_TASK_WAIT (task);
+ GST_OBJECT_UNLOCK (task);
+ /* locking order.. */
+ if (t > 0)
+ g_static_rec_mutex_lock_full (lock, t);
+
+ GST_OBJECT_LOCK (task);
+ if (G_UNLIKELY (GET_TASK_STATE (task) == GST_TASK_STOPPED)) {
+ GST_OBJECT_UNLOCK (task);
+ goto done;
+ }
+ }
+ GST_OBJECT_UNLOCK (task);
+ }
+
+ task->func (task->data);
+ }
+done:
+ g_static_rec_mutex_unlock (lock);
+
+ GST_OBJECT_LOCK (task);
+ task->thread = NULL;
+
+exit:
+ if (priv->thr_callbacks.leave_thread) {
+ /* fire the leave_thread callback when we need to. We need to do this before
+ * we signal the task and with the task lock released. */
+ GST_OBJECT_UNLOCK (task);
+ priv->thr_callbacks.leave_thread (task, tself, priv->thr_user_data);
+ GST_OBJECT_LOCK (task);
+ } else {
+ /* restore normal priority when releasing back into the pool, we will not
+ * touch the priority when a custom callback has been installed. */
+ g_thread_set_priority (tself, G_THREAD_PRIORITY_NORMAL);
+ }
+ /* now we allow messing with the lock again by setting the running flag to
+ * FALSE. Together with the SIGNAL this is the sign for the _join() to
+ * complete.
+ * Note that we still have not dropped the final ref on the task. We could
+ * check here if there is a pending join() going on and drop the last ref
+ * before releasing the lock as we can be sure that a ref is held by the
+ * caller of the join(). */
+ task->running = FALSE;
+ GST_TASK_SIGNAL (task);
+ GST_OBJECT_UNLOCK (task);
+
+ GST_DEBUG ("Exit task %p, thread %p", task, g_thread_self ());
+
+ gst_object_unref (task);
+ return;
+
+no_lock:
+ {
+ g_warning ("starting task without a lock");
+ goto exit;
+ }
+}
+
+/**
+ * gst_task_cleanup_all:
+ *
+ * Wait for all tasks to be stopped. This is mainly used internally
+ * to ensure proper cleanup of internal data structures in test suites.
+ *
+ * MT safe.
+ */
+void
+gst_task_cleanup_all (void)
+{
+ GstTaskClass *klass;
+
+ if ((klass = g_type_class_peek (GST_TYPE_TASK))) {
+ init_klass_pool (klass);
+ }
+}
+
+/**
+ * gst_task_create:
+ * @func: The #GstTaskFunction to use
+ * @data: (closure): User data to pass to @func
+ *
+ * Create a new Task that will repeatedly call the provided @func
+ * with @data as a parameter. Typically the task will run in
+ * a new thread.
+ *
+ * The function cannot be changed after the task has been created. You
+ * must create a new #GstTask to change the function.
+ *
+ * This function will not yet create and start a thread. Use gst_task_start() or
+ * gst_task_pause() to create and start the GThread.
+ *
+ * Before the task can be used, a #GStaticRecMutex must be configured using the
+ * gst_task_set_lock() function. This lock will always be acquired while
+ * @func is called.
+ *
+ * Returns: (transfer full): A new #GstTask.
+ *
+ * MT safe.
+ */
+GstTask *
+gst_task_create (GstTaskFunction func, gpointer data)
+{
+ GstTask *task;
+
+ task = g_object_newv (GST_TYPE_TASK, 0, NULL);
+ task->func = func;
+ task->data = data;
+
+ GST_DEBUG ("Created task %p", task);
+
+ return task;
+}
+
+/**
+ * gst_task_set_lock:
+ * @task: The #GstTask to use
+ * @mutex: The #GMutex to use
+ *
+ * Set the mutex used by the task. The mutex will be acquired before
+ * calling the #GstTaskFunction.
+ *
+ * This function has to be called before calling gst_task_pause() or
+ * gst_task_start().
+ *
+ * MT safe.
+ */
+void
+gst_task_set_lock (GstTask * task, GStaticRecMutex * mutex)
+{
+ GST_OBJECT_LOCK (task);
+ if (G_UNLIKELY (task->running))
+ goto is_running;
+ GST_TASK_GET_LOCK (task) = mutex;
+ GST_OBJECT_UNLOCK (task);
+
+ return;
+
+ /* ERRORS */
+is_running:
+ {
+ GST_OBJECT_UNLOCK (task);
+ g_warning ("cannot call set_lock on a running task");
+ }
+}
+
+/**
+ * gst_task_set_priority:
+ * @task: a #GstTask
+ * @priority: a new priority for @task
+ *
+ * Changes the priority of @task to @priority.
+ *
+ * Note: try not to depend on task priorities.
+ *
+ * MT safe.
+ *
+ * Since: 0.10.24
+ */
+void
+gst_task_set_priority (GstTask * task, GThreadPriority priority)
+{
+ GstTaskPrivate *priv;
+ GThread *thread;
+
+ g_return_if_fail (GST_IS_TASK (task));
+
+ priv = task->priv;
+
+ GST_OBJECT_LOCK (task);
+ priv->prio_set = TRUE;
+ priv->priority = priority;
+ thread = task->thread;
+ if (thread != NULL) {
+ /* if this task already has a thread, we can configure the priority right
+ * away, else we do that when we assign a thread to the task. */
+ g_thread_set_priority (thread, priority);
+ }
+ GST_OBJECT_UNLOCK (task);
+}
+
+/**
+ * gst_task_get_pool:
+ * @task: a #GstTask
+ *
+ * Get the #GstTaskPool that this task will use for its streaming
+ * threads.
+ *
+ * MT safe.
+ *
+ * Returns: (transfer full): the #GstTaskPool used by @task. gst_object_unref()
+ * after usage.
+ *
+ * Since: 0.10.24
+ */
+GstTaskPool *
+gst_task_get_pool (GstTask * task)
+{
+ GstTaskPool *result;
+ GstTaskPrivate *priv;
+
+ g_return_val_if_fail (GST_IS_TASK (task), NULL);
+
+ priv = task->priv;
+
+ GST_OBJECT_LOCK (task);
+ result = gst_object_ref (priv->pool);
+ GST_OBJECT_UNLOCK (task);
+
+ return result;
+}
+
+/**
+ * gst_task_set_pool:
+ * @task: a #GstTask
+ * @pool: (transfer none): a #GstTaskPool
+ *
+ * Set @pool as the new GstTaskPool for @task. Any new streaming threads that
+ * will be created by @task will now use @pool.
+ *
+ * MT safe.
+ *
+ * Since: 0.10.24
+ */
+void
+gst_task_set_pool (GstTask * task, GstTaskPool * pool)
+{
+ GstTaskPool *old;
+ GstTaskPrivate *priv;
+
+ g_return_if_fail (GST_IS_TASK (task));
+ g_return_if_fail (GST_IS_TASK_POOL (pool));
+
+ priv = task->priv;
+
+ GST_OBJECT_LOCK (task);
+ if (priv->pool != pool) {
+ old = priv->pool;
+ priv->pool = gst_object_ref (pool);
+ } else
+ old = NULL;
+ GST_OBJECT_UNLOCK (task);
+
+ if (old)
+ gst_object_unref (old);
+}
+
+
+/**
+ * gst_task_set_thread_callbacks:
+ * @task: The #GstTask to use
+ * @callbacks: (in): a #GstTaskThreadCallbacks pointer
+ * @user_data: (closure): user data passed to the callbacks
+ * @notify: called when @user_data is no longer referenced
+ *
+ * Set callbacks which will be executed when a new thread is needed, the thread
+ * function is entered and left and when the thread is joined.
+ *
+ * By default a thread for @task will be created from a default thread pool.
+ *
+ * Objects can use custom GThreads or can perform additional configuration of
+ * the threads (such as changing the thread priority) by installing callbacks.
+ *
+ * MT safe.
+ *
+ * Since: 0.10.24
+ */
+void
+gst_task_set_thread_callbacks (GstTask * task,
+ GstTaskThreadCallbacks * callbacks, gpointer user_data,
+ GDestroyNotify notify)
+{
+ GDestroyNotify old_notify;
+
+ g_return_if_fail (task != NULL);
+ g_return_if_fail (GST_IS_TASK (task));
+ g_return_if_fail (callbacks != NULL);
+
+ GST_OBJECT_LOCK (task);
+ old_notify = task->priv->thr_notify;
+
+ if (old_notify) {
+ gpointer old_data;
+
+ old_data = task->priv->thr_user_data;
+
+ task->priv->thr_user_data = NULL;
+ task->priv->thr_notify = NULL;
+ GST_OBJECT_UNLOCK (task);
+
+ old_notify (old_data);
+
+ GST_OBJECT_LOCK (task);
+ }
+ task->priv->thr_callbacks = *callbacks;
+ task->priv->thr_user_data = user_data;
+ task->priv->thr_notify = notify;
+ GST_OBJECT_UNLOCK (task);
+}
+
+/**
+ * gst_task_get_state:
+ * @task: The #GstTask to query
+ *
+ * Get the current state of the task.
+ *
+ * Returns: The #GstTaskState of the task
+ *
+ * MT safe.
+ */
+GstTaskState
+gst_task_get_state (GstTask * task)
+{
+ GstTaskState result;
+
+ g_return_val_if_fail (GST_IS_TASK (task), GST_TASK_STOPPED);
+
+ result = GET_TASK_STATE (task);
+
+ return result;
+}
+
+/* make sure the task is running and start a thread if it's not.
+ * This function must be called with the task LOCK. */
+static gboolean
+start_task (GstTask * task)
+{
+ gboolean res = TRUE;
+ GError *error = NULL;
+ GstTaskPrivate *priv;
+
+ priv = task->priv;
+
+ /* new task, We ref before so that it remains alive while
+ * the thread is running. */
+ gst_object_ref (task);
+ /* mark task as running so that a join will wait until we schedule
+ * and exit the task function. */
+ task->running = TRUE;
+
+ /* push on the thread pool, we remember the original pool because the user
+ * could change it later on and then we join to the wrong pool. */
+ priv->pool_id = gst_object_ref (priv->pool);
+ priv->id =
+ gst_task_pool_push (priv->pool_id, (GstTaskPoolFunction) gst_task_func,
+ task, &error);
+
+ if (error != NULL) {
+ g_warning ("failed to create thread: %s", error->message);
+ g_error_free (error);
+ res = FALSE;
+ }
+ return res;
+}
+
+
+/**
+ * gst_task_set_state:
+ * @task: a #GstTask
+ * @state: the new task state
+ *
+ * Sets the state of @task to @state.
+ *
+ * The @task must have a lock associated with it using
+ * gst_task_set_lock() when going to GST_TASK_STARTED or GST_TASK_PAUSED or
+ * this function will return %FALSE.
+ *
+ * MT safe.
+ *
+ * Returns: %TRUE if the state could be changed.
+ *
+ * Since: 0.10.24
+ */
+gboolean
+gst_task_set_state (GstTask * task, GstTaskState state)
+{
+ GstTaskState old;
+ gboolean res = TRUE;
+
+ g_return_val_if_fail (GST_IS_TASK (task), FALSE);
+
+ GST_DEBUG_OBJECT (task, "Changing task %p to state %d", task, state);
+
+ GST_OBJECT_LOCK (task);
+ if (state != GST_TASK_STOPPED)
+ if (G_UNLIKELY (GST_TASK_GET_LOCK (task) == NULL))
+ goto no_lock;
+
+ /* if the state changed, do our thing */
+ old = GET_TASK_STATE (task);
+ if (old != state) {
+ SET_TASK_STATE (task, state);
+ switch (old) {
+ case GST_TASK_STOPPED:
+ /* If the task already has a thread scheduled we don't have to do
+ * anything. */
+ if (G_UNLIKELY (!task->running))
+ res = start_task (task);
+ break;
+ case GST_TASK_PAUSED:
+ /* when we are paused, signal to go to the new state */
+ GST_TASK_SIGNAL (task);
+ break;
+ case GST_TASK_STARTED:
+ /* if we were started, we'll go to the new state after the next
+ * iteration. */
+ break;
+ }
+ }
+ GST_OBJECT_UNLOCK (task);
+
+ return res;
+
+ /* ERRORS */
+no_lock:
+ {
+ GST_WARNING_OBJECT (task, "state %d set on task without a lock", state);
+ GST_OBJECT_UNLOCK (task);
+ g_warning ("task without a lock can't be set to state %d", state);
+ return FALSE;
+ }
+}
+
+/**
+ * gst_task_start:
+ * @task: The #GstTask to start
+ *
+ * Starts @task. The @task must have a lock associated with it using
+ * gst_task_set_lock() or this function will return %FALSE.
+ *
+ * Returns: %TRUE if the task could be started.
+ *
+ * MT safe.
+ */
+gboolean
+gst_task_start (GstTask * task)
+{
+ return gst_task_set_state (task, GST_TASK_STARTED);
+}
+
+/**
+ * gst_task_stop:
+ * @task: The #GstTask to stop
+ *
+ * Stops @task. This method merely schedules the task to stop and
+ * will not wait for the task to have completely stopped. Use
+ * gst_task_join() to stop and wait for completion.
+ *
+ * Returns: %TRUE if the task could be stopped.
+ *
+ * MT safe.
+ */
+gboolean
+gst_task_stop (GstTask * task)
+{
+ return gst_task_set_state (task, GST_TASK_STOPPED);
+}
+
+/**
+ * gst_task_pause:
+ * @task: The #GstTask to pause
+ *
+ * Pauses @task. This method can also be called on a task in the
+ * stopped state, in which case a thread will be started and will remain
+ * in the paused state. This function does not wait for the task to complete
+ * the paused state.
+ *
+ * Returns: %TRUE if the task could be paused.
+ *
+ * MT safe.
+ */
+gboolean
+gst_task_pause (GstTask * task)
+{
+ return gst_task_set_state (task, GST_TASK_PAUSED);
+}
+
+/**
+ * gst_task_join:
+ * @task: The #GstTask to join
+ *
+ * Joins @task. After this call, it is safe to unref the task
+ * and clean up the lock set with gst_task_set_lock().
+ *
+ * The task will automatically be stopped with this call.
+ *
+ * This function cannot be called from within a task function as this
+ * would cause a deadlock. The function will detect this and print a
+ * g_warning.
+ *
+ * Returns: %TRUE if the task could be joined.
+ *
+ * MT safe.
+ */
+gboolean
+gst_task_join (GstTask * task)
+{
+ GThread *tself;
+ GstTaskPrivate *priv;
+ gpointer id;
+ GstTaskPool *pool = NULL;
+
+ priv = task->priv;
+
+ g_return_val_if_fail (GST_IS_TASK (task), FALSE);
+
+ tself = g_thread_self ();
+
+ GST_DEBUG_OBJECT (task, "Joining task %p, thread %p", task, tself);
+
+ /* we don't use a real thread join here because we are using
+ * thread pools */
+ GST_OBJECT_LOCK (task);
+ if (G_UNLIKELY (tself == task->thread))
+ goto joining_self;
+ SET_TASK_STATE (task, GST_TASK_STOPPED);
+ /* signal the state change for when it was blocked in PAUSED. */
+ GST_TASK_SIGNAL (task);
+ /* we set the running flag when pushing the task on the thread pool.
+ * This means that the task function might not be called when we try
+ * to join it here. */
+ while (G_LIKELY (task->running))
+ GST_TASK_WAIT (task);
+ /* clean the thread */
+ task->thread = NULL;
+ /* get the id and pool to join */
+ pool = priv->pool_id;
+ id = priv->id;
+ priv->pool_id = NULL;
+ priv->id = NULL;
+ GST_OBJECT_UNLOCK (task);
+
+ if (pool) {
+ if (id)
+ gst_task_pool_join (pool, id);
+ gst_object_unref (pool);
+ }
+
+ GST_DEBUG_OBJECT (task, "Joined task %p", task);
+
+ return TRUE;
+
+ /* ERRORS */
+joining_self:
+ {
+ GST_WARNING_OBJECT (task, "trying to join task from its thread");
+ GST_OBJECT_UNLOCK (task);
+ g_warning ("\nTrying to join task %p from its thread would deadlock.\n"
+ "You cannot change the state of an element from its streaming\n"
+ "thread. Use g_idle_add() or post a GstMessage on the bus to\n"
+ "schedule the state change from the main thread.\n", task);
+ return FALSE;
+ }
+}
diff --git a/gst/gsttask.h b/gst/gsttask.h
new file mode 100644
index 0000000..21784b7
--- /dev/null
+++ b/gst/gsttask.h
@@ -0,0 +1,201 @@
+/* GStreamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ * <2005> Wim Taymans <wim@fluendo.com>
+ *
+ * gsttask.h: Streaming tasks
+ *
+ * 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_TASK_H__
+#define __GST_TASK_H__
+
+#include <gst/gstobject.h>
+#include <gst/gsttaskpool.h>
+
+G_BEGIN_DECLS
+
+/**
+ * GstTaskFunction:
+ * @data: user data passed to the function
+ *
+ * A function that will repeatedly be called in the thread created by
+ * a #GstTask.
+ */
+typedef void (*GstTaskFunction) (void *data);
+
+/* --- standard type macros --- */
+#define GST_TYPE_TASK (gst_task_get_type ())
+#define GST_TASK(task) (G_TYPE_CHECK_INSTANCE_CAST ((task), GST_TYPE_TASK, GstTask))
+#define GST_IS_TASK(task) (G_TYPE_CHECK_INSTANCE_TYPE ((task), GST_TYPE_TASK))
+#define GST_TASK_CLASS(tclass) (G_TYPE_CHECK_CLASS_CAST ((tclass), GST_TYPE_TASK, GstTaskClass))
+#define GST_IS_TASK_CLASS(tclass) (G_TYPE_CHECK_CLASS_TYPE ((tclass), GST_TYPE_TASK))
+#define GST_TASK_GET_CLASS(task) (G_TYPE_INSTANCE_GET_CLASS ((task), GST_TYPE_TASK, GstTaskClass))
+#define GST_TASK_CAST(task) ((GstTask*)(task))
+
+typedef struct _GstTask GstTask;
+typedef struct _GstTaskClass GstTaskClass;
+typedef struct _GstTaskPrivate GstTaskPrivate;
+
+/**
+ * GstTaskState:
+ * @GST_TASK_STARTED: the task is started and running
+ * @GST_TASK_STOPPED: the task is stopped
+ * @GST_TASK_PAUSED: the task is paused
+ *
+ * The different states a task can be in
+ */
+typedef enum {
+ GST_TASK_STARTED,
+ GST_TASK_STOPPED,
+ GST_TASK_PAUSED
+} GstTaskState;
+
+/**
+ * GST_TASK_STATE:
+ * @task: Task to get the state of
+ *
+ * Get access to the state of the task.
+ */
+#define GST_TASK_STATE(task) (GST_TASK_CAST(task)->state)
+
+/**
+ * GST_TASK_GET_COND:
+ * @task: Task to get the cond of
+ *
+ * Get access to the cond of the task.
+ */
+#define GST_TASK_GET_COND(task) (GST_TASK_CAST(task)->cond)
+/**
+ * GST_TASK_WAIT:
+ * @task: Task to wait for
+ *
+ * Wait for the task cond to be signalled
+ */
+#define GST_TASK_WAIT(task) g_cond_wait(GST_TASK_GET_COND (task), GST_OBJECT_GET_LOCK (task))
+/**
+ * GST_TASK_SIGNAL:
+ * @task: Task to signal
+ *
+ * Signal the task cond
+ */
+#define GST_TASK_SIGNAL(task) g_cond_signal(GST_TASK_GET_COND (task))
+/**
+ * GST_TASK_BROADCAST:
+ * @task: Task to broadcast
+ *
+ * Send a broadcast signal to all waiting task conds
+ */
+#define GST_TASK_BROADCAST(task) g_cond_broadcast(GST_TASK_GET_COND (task))
+
+/**
+ * GST_TASK_GET_LOCK:
+ * @task: Task to get the lock of
+ *
+ * Get access to the task lock.
+ */
+#define GST_TASK_GET_LOCK(task) (GST_TASK_CAST(task)->lock)
+
+/**
+ * GstTaskThreadCallbacks:
+ * @enter_thread: a thread is entered, this callback is called when the new
+ * thread enters its function.
+ * @leave_thread: a thread is exiting, this is called when the thread is about
+ * to leave its function
+ *
+ * Custom GstTask thread callback functions that can be installed.
+ *
+ * Since: 0.10.24
+ */
+typedef struct {
+ /* manage the lifetime of the thread */
+ void (*enter_thread) (GstTask *task, GThread *thread, gpointer user_data);
+ void (*leave_thread) (GstTask *task, GThread *thread, gpointer user_data);
+ /*< private >*/
+ gpointer _gst_reserved[GST_PADDING];
+} GstTaskThreadCallbacks;
+
+/**
+ * GstTask:
+ * @state: the state of the task
+ * @cond: used to pause/resume the task
+ * @lock: The lock taken when iterating the task function
+ * @func: the function executed by this task
+ * @data: data passed to the task function
+ * @running: a flag indicating that the task is running
+ *
+ * The #GstTask object.
+ */
+struct _GstTask {
+ GstObject object;
+
+ /*< public >*/ /* with LOCK */
+ GstTaskState state;
+ GCond *cond;
+
+ GStaticRecMutex *lock;
+
+ GstTaskFunction func;
+ gpointer data;
+
+ gboolean running;
+
+ /*< private >*/
+ GThread *thread;
+
+ GstTaskPrivate *priv;
+
+ gpointer _gst_reserved[GST_PADDING];
+};
+
+struct _GstTaskClass {
+ GstObjectClass parent_class;
+
+ /*< private >*/
+ GstTaskPool *pool;
+
+ /*< private >*/
+ gpointer _gst_reserved[GST_PADDING];
+};
+
+void gst_task_cleanup_all (void);
+
+GType gst_task_get_type (void);
+
+GstTask* gst_task_create (GstTaskFunction func, gpointer data);
+void gst_task_set_lock (GstTask *task, GStaticRecMutex *mutex);
+void gst_task_set_priority (GstTask *task, GThreadPriority priority);
+
+GstTaskPool * gst_task_get_pool (GstTask *task);
+void gst_task_set_pool (GstTask *task, GstTaskPool *pool);
+
+void gst_task_set_thread_callbacks (GstTask *task,
+ GstTaskThreadCallbacks *callbacks,
+ gpointer user_data,
+ GDestroyNotify notify);
+
+GstTaskState gst_task_get_state (GstTask *task);
+gboolean gst_task_set_state (GstTask *task, GstTaskState state);
+
+gboolean gst_task_start (GstTask *task);
+gboolean gst_task_stop (GstTask *task);
+gboolean gst_task_pause (GstTask *task);
+
+gboolean gst_task_join (GstTask *task);
+
+G_END_DECLS
+
+#endif /* __GST_TASK_H__ */
diff --git a/gst/gsttaskpool.c b/gst/gsttaskpool.c
new file mode 100644
index 0000000..d5981e6
--- /dev/null
+++ b/gst/gsttaskpool.c
@@ -0,0 +1,282 @@
+/* GStreamer
+ * Copyright (C) 2009 Wim Taymans <wim.taymans@gmail.com>
+ *
+ * gsttaskpool.c: Pool for streaming threads
+ *
+ * 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:gsttaskpool
+ * @short_description: Pool of GStreamer streaming threads
+ * @see_also: #GstTask, #GstPad
+ *
+ * This object provides an abstraction for creating threads. The default
+ * implementation uses a regular GThreadPool to start tasks.
+ *
+ * Subclasses can be made to create custom threads.
+ *
+ * Last reviewed on 2009-04-23 (0.10.24)
+ */
+
+#include "gst_private.h"
+
+#include "gstinfo.h"
+#include "gsttaskpool.h"
+
+GST_DEBUG_CATEGORY_STATIC (taskpool_debug);
+#define GST_CAT_DEFAULT (taskpool_debug)
+
+#ifndef GST_DISABLE_GST_DEBUG
+static void gst_task_pool_finalize (GObject * object);
+#endif
+
+#define _do_init \
+{ \
+ GST_DEBUG_CATEGORY_INIT (taskpool_debug, "taskpool", 0, "Thread pool"); \
+}
+
+G_DEFINE_TYPE_WITH_CODE (GstTaskPool, gst_task_pool, GST_TYPE_OBJECT, _do_init);
+
+typedef struct
+{
+ GstTaskPoolFunction func;
+ gpointer user_data;
+} TaskData;
+
+static void
+default_func (TaskData * tdata, GstTaskPool * pool)
+{
+ GstTaskPoolFunction func;
+ gpointer user_data;
+
+ func = tdata->func;
+ user_data = tdata->user_data;
+ g_slice_free (TaskData, tdata);
+
+ func (user_data);
+}
+
+static void
+default_prepare (GstTaskPool * pool, GError ** error)
+{
+ GST_OBJECT_LOCK (pool);
+ pool->pool = g_thread_pool_new ((GFunc) default_func, pool, -1, FALSE, NULL);
+ GST_OBJECT_UNLOCK (pool);
+}
+
+static void
+default_cleanup (GstTaskPool * pool)
+{
+ GST_OBJECT_LOCK (pool);
+ if (pool->pool) {
+ /* Shut down all the threads, we still process the ones scheduled
+ * because the unref happens in the thread function.
+ * Also wait for currently running ones to finish. */
+ g_thread_pool_free (pool->pool, FALSE, TRUE);
+ pool->pool = NULL;
+ }
+ GST_OBJECT_UNLOCK (pool);
+}
+
+static gpointer
+default_push (GstTaskPool * pool, GstTaskPoolFunction func,
+ gpointer user_data, GError ** error)
+{
+ TaskData *tdata;
+
+ tdata = g_slice_new (TaskData);
+ tdata->func = func;
+ tdata->user_data = user_data;
+
+ GST_OBJECT_LOCK (pool);
+ if (pool->pool)
+ g_thread_pool_push (pool->pool, tdata, error);
+ else {
+ g_slice_free (TaskData, tdata);
+ }
+ GST_OBJECT_UNLOCK (pool);
+
+ return NULL;
+}
+
+static void
+default_join (GstTaskPool * pool, gpointer id)
+{
+ /* we do nothing here, we can't join from the pools */
+}
+
+static void
+gst_task_pool_class_init (GstTaskPoolClass * klass)
+{
+ GObjectClass *gobject_class;
+ GstTaskPoolClass *gsttaskpool_class;
+
+ gobject_class = (GObjectClass *) klass;
+ gsttaskpool_class = (GstTaskPoolClass *) klass;
+
+#ifndef GST_DISABLE_GST_DEBUG
+ gobject_class->finalize = gst_task_pool_finalize;
+#endif
+
+ gsttaskpool_class->prepare = default_prepare;
+ gsttaskpool_class->cleanup = default_cleanup;
+ gsttaskpool_class->push = default_push;
+ gsttaskpool_class->join = default_join;
+}
+
+static void
+gst_task_pool_init (GstTaskPool * pool)
+{
+}
+
+#ifndef GST_DISABLE_GST_DEBUG
+static void
+gst_task_pool_finalize (GObject * object)
+{
+ GST_DEBUG ("taskpool %p finalize", object);
+
+ G_OBJECT_CLASS (gst_task_pool_parent_class)->finalize (object);
+}
+#endif
+/**
+ * gst_task_pool_new:
+ *
+ * Create a new default task pool. The default task pool will use a regular
+ * GThreadPool for threads.
+ *
+ * Returns: (transfer full): a new #GstTaskPool. gst_object_unref() after usage.
+ *
+ * Since: 0.10.24
+ */
+GstTaskPool *
+gst_task_pool_new (void)
+{
+ GstTaskPool *pool;
+
+ pool = g_object_newv (GST_TYPE_TASK_POOL, 0, NULL);
+
+ return pool;
+}
+
+/**
+ * gst_task_pool_prepare:
+ * @pool: a #GstTaskPool
+ * @error: an error return location
+ *
+ * Prepare the taskpool for accepting gst_task_pool_push() operations.
+ *
+ * MT safe.
+ *
+ * Since: 0.10.24
+ */
+void
+gst_task_pool_prepare (GstTaskPool * pool, GError ** error)
+{
+ GstTaskPoolClass *klass;
+
+ g_return_if_fail (GST_IS_TASK_POOL (pool));
+
+ klass = GST_TASK_POOL_GET_CLASS (pool);
+
+ if (klass->prepare)
+ klass->prepare (pool, error);
+}
+
+/**
+ * gst_task_pool_cleanup:
+ * @pool: a #GstTaskPool
+ *
+ * Wait for all tasks to be stopped. This is mainly used internally
+ * to ensure proper cleanup of internal data structures in test suites.
+ *
+ * MT safe.
+ *
+ * Since: 0.10.24
+ */
+void
+gst_task_pool_cleanup (GstTaskPool * pool)
+{
+ GstTaskPoolClass *klass;
+
+ g_return_if_fail (GST_IS_TASK_POOL (pool));
+
+ klass = GST_TASK_POOL_GET_CLASS (pool);
+
+ if (klass->cleanup)
+ klass->cleanup (pool);
+}
+
+/**
+ * gst_task_pool_push:
+ * @pool: a #GstTaskPool
+ * @func: the function to call
+ * @user_data: (closure): data to pass to @func
+ * @error: return location for an error
+ *
+ * Start the execution of a new thread from @pool.
+ *
+ * Returns: a pointer that should be used for the gst_task_pool_join
+ * function. This pointer can be NULL, you must check @error to detect
+ * errors.
+ *
+ * Since: 0.10.24
+ */
+gpointer
+gst_task_pool_push (GstTaskPool * pool, GstTaskPoolFunction func,
+ gpointer user_data, GError ** error)
+{
+ GstTaskPoolClass *klass;
+
+ g_return_val_if_fail (GST_IS_TASK_POOL (pool), NULL);
+
+ klass = GST_TASK_POOL_GET_CLASS (pool);
+
+ if (klass->push == NULL)
+ goto not_supported;
+
+ return klass->push (pool, func, user_data, error);
+
+ /* ERRORS */
+not_supported:
+ {
+ g_warning ("pushing tasks on pool %p is not supported", pool);
+ return NULL;
+ }
+}
+
+/**
+ * gst_task_pool_join:
+ * @pool: a #GstTaskPool
+ * @id: the id
+ *
+ * Join a task and/or return it to the pool. @id is the id obtained from
+ * gst_task_pool_push().
+ *
+ * Since: 0.10.24
+ */
+void
+gst_task_pool_join (GstTaskPool * pool, gpointer id)
+{
+ GstTaskPoolClass *klass;
+
+ g_return_if_fail (GST_IS_TASK_POOL (pool));
+
+ klass = GST_TASK_POOL_GET_CLASS (pool);
+
+ if (klass->join)
+ klass->join (pool, id);
+}
diff --git a/gst/gsttaskpool.h b/gst/gsttaskpool.h
new file mode 100644
index 0000000..912015e
--- /dev/null
+++ b/gst/gsttaskpool.h
@@ -0,0 +1,103 @@
+/* GStreamer
+ * Copyright (C) <2009> Wim Taymans <wim.taymans@gmail.com>
+ *
+ * gsttaskpool.h: Pool for creating streaming threads
+ *
+ * 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_TASK_POOL_H__
+#define __GST_TASK_POOL_H__
+
+#include <gst/gstobject.h>
+
+G_BEGIN_DECLS
+
+/* --- standard type macros --- */
+#define GST_TYPE_TASK_POOL (gst_task_pool_get_type ())
+#define GST_TASK_POOL(pool) (G_TYPE_CHECK_INSTANCE_CAST ((pool), GST_TYPE_TASK_POOL, GstTaskPool))
+#define GST_IS_TASK_POOL(pool) (G_TYPE_CHECK_INSTANCE_TYPE ((pool), GST_TYPE_TASK_POOL))
+#define GST_TASK_POOL_CLASS(pclass) (G_TYPE_CHECK_CLASS_CAST ((pclass), GST_TYPE_TASK_POOL, GstTaskPoolClass))
+#define GST_IS_TASK_POOL_CLASS(pclass) (G_TYPE_CHECK_CLASS_TYPE ((pclass), GST_TYPE_TASK_POOL))
+#define GST_TASK_POOL_GET_CLASS(pool) (G_TYPE_INSTANCE_GET_CLASS ((pool), GST_TYPE_TASK_POOL, GstTaskPoolClass))
+#define GST_TASK_POOL_CAST(pool) ((GstTaskPool*)(pool))
+
+typedef struct _GstTaskPool GstTaskPool;
+typedef struct _GstTaskPoolClass GstTaskPoolClass;
+
+/**
+ * GstTaskPoolFunction:
+ * @data: user data for the task function
+ *
+ * Task function, see gst_task_pool_push().
+ *
+ * Since: 0.10.24
+ */
+typedef void (*GstTaskPoolFunction) (void *data);
+
+/**
+ * GstTaskPool:
+ *
+ * The #GstTaskPool object.
+ */
+struct _GstTaskPool {
+ GstObject object;
+
+ /*< private >*/
+ GThreadPool *pool;
+
+ gpointer _gst_reserved[GST_PADDING];
+};
+
+/**
+ * GstTaskPoolClass:
+ * @parent_class: the parent class structure
+ * @prepare: prepare the threadpool
+ * @cleanup: make sure all threads are stopped
+ * @push: start a new thread
+ * @join: join a thread
+ *
+ * The #GstTaskPoolClass object.
+ */
+struct _GstTaskPoolClass {
+ GstObjectClass parent_class;
+
+ /*< public >*/
+ void (*prepare) (GstTaskPool *pool, GError **error);
+ void (*cleanup) (GstTaskPool *pool);
+
+ gpointer (*push) (GstTaskPool *pool, GstTaskPoolFunction func,
+ gpointer user_data, GError **error);
+ void (*join) (GstTaskPool *pool, gpointer id);
+
+ /*< private >*/
+ gpointer _gst_reserved[GST_PADDING];
+};
+
+GType gst_task_pool_get_type (void);
+
+GstTaskPool * gst_task_pool_new (void);
+void gst_task_pool_prepare (GstTaskPool *pool, GError **error);
+
+gpointer gst_task_pool_push (GstTaskPool *pool, GstTaskPoolFunction func,
+ gpointer user_data, GError **error);
+void gst_task_pool_join (GstTaskPool *pool, gpointer id);
+
+void gst_task_pool_cleanup (GstTaskPool *pool);
+
+G_END_DECLS
+
+#endif /* __GST_TASK_POOL_H__ */
diff --git a/gst/gsttrace.c b/gst/gsttrace.c
new file mode 100644
index 0000000..b857458
--- /dev/null
+++ b/gst/gsttrace.c
@@ -0,0 +1,515 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2000 Wim Taymans <wtay@chello.be>
+ *
+ * gsttrace.c: Tracing functions (deprecated)
+ *
+ * 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:gsttrace
+ * @short_description: Tracing functionality
+ *
+ * Traces allows to track object allocation. They provide a instance counter per
+ * #GType. The counter is incremented for each object allocated and decremented
+ * it when it's freed.
+ *
+ * <example>
+ * <title>Tracing object instances</title>
+ * <programlisting>
+ * // trace un-freed object instances
+ * gst_alloc_trace_set_flags_all (GST_ALLOC_TRACE_LIVE);
+ * if (!gst_alloc_trace_available ()) {
+ * g_warning ("Trace not available (recompile with trace enabled).");
+ * }
+ * gst_alloc_trace_print_live ();
+ * // do something here
+ * gst_alloc_trace_print_live ();
+ * </programlisting>
+ * </example>
+ *
+ * Last reviewed on 2005-11-21 (0.9.5)
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <stdio.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+
+#if defined (_MSC_VER) && _MSC_VER >= 1400
+# include <io.h>
+#endif
+
+#include "gst_private.h"
+#include "gstinfo.h"
+
+#include "gsttrace.h"
+
+GStaticMutex _gst_trace_mutex = G_STATIC_MUTEX_INIT;
+
+static
+#ifdef __inline__
+ __inline__
+#endif
+ void
+read_tsc (gint64 * dst)
+{
+#if defined(HAVE_RDTSC) && defined(__GNUC__)
+ guint64 tsc;
+ __asm__ __volatile__ ("rdtsc":"=A" (tsc));
+
+ *dst = tsc;
+#else
+ *dst = 0;
+#endif
+}
+
+/**
+ * gst_trace_read_tsc:
+ * @dst: (out) pointer to hold the result.
+ *
+ * Read a platform independent timer value that can be used in
+ * benchmarks.
+ */
+void
+gst_trace_read_tsc (gint64 * dst)
+{
+ read_tsc (dst);
+}
+
+static GstTrace *_gst_trace_default = NULL;
+gint _gst_trace_on = 1;
+
+/**
+ * gst_trace_new:
+ * @filename: a filename
+ * @size: the max size of the file
+ *
+ * Create a ringbuffer of @size in the file with @filename to
+ * store trace results in.
+ *
+ * Free-function: gst_trace_destroy
+ *
+ * Returns: (transfer full): a new #GstTrace.
+ */
+GstTrace *
+gst_trace_new (const gchar * filename, gint size)
+{
+ GstTrace *trace = g_slice_new (GstTrace);
+
+ g_return_val_if_fail (trace != NULL, NULL);
+ trace->filename = g_strdup (filename);
+ GST_DEBUG ("opening '%s'", trace->filename);
+#ifndef S_IWUSR
+#define S_IWUSR S_IWRITE
+#endif
+#ifndef S_IRUSR
+#define S_IRUSR S_IREAD
+#endif
+ trace->fd =
+ open (trace->filename, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
+ perror ("opening trace file");
+ g_return_val_if_fail (trace->fd > 0, NULL);
+ trace->buf = g_malloc (size * sizeof (GstTraceEntry));
+ g_return_val_if_fail (trace->buf != NULL, NULL);
+ trace->bufsize = size;
+ trace->bufoffset = 0;
+
+ return trace;
+}
+
+/**
+ * gst_trace_destroy:
+ * @trace: (in) (transfer full): the #GstTrace to destroy
+ *
+ * Flush an close the previously allocated @trace.
+ */
+void
+gst_trace_destroy (GstTrace * trace)
+{
+ g_return_if_fail (trace != NULL);
+ g_return_if_fail (trace->buf != NULL);
+
+ if (gst_trace_get_remaining (trace) > 0)
+ gst_trace_flush (trace);
+ close (trace->fd);
+ g_free (trace->buf);
+ g_slice_free (GstTrace, trace);
+}
+
+/**
+ * gst_trace_flush:
+ * @trace: the #GstTrace to flush.
+ *
+ * Flush any pending trace entries in @trace to the trace file.
+ * @trace can be NULL in which case the default #GstTrace will be
+ * flushed.
+ */
+void
+gst_trace_flush (GstTrace * trace)
+{
+ int res, buf_len;
+
+ if (!trace) {
+ trace = _gst_trace_default;
+ if (!trace)
+ return;
+ }
+
+ buf_len = trace->bufoffset * sizeof (GstTraceEntry);
+ res = write (trace->fd, trace->buf, buf_len);
+ if (res < 0) {
+ g_warning ("Failed to write trace: %s", g_strerror (errno));
+ return;
+ } else if (res < buf_len) {
+ g_warning ("Failed to write trace: only wrote %d/%d bytes", res, buf_len);
+ return;
+ }
+ trace->bufoffset = 0;
+}
+
+/**
+ * gst_trace_text_flush:
+ * @trace: the #GstTrace to flush.
+ *
+ * Flush any pending trace entries in @trace to the trace file,
+ * formatted as a text line with timestamp and sequence numbers.
+ * @trace can be NULL in which case the default #GstTrace will be
+ * flushed.
+ */
+void
+gst_trace_text_flush (GstTrace * trace)
+{
+ int i;
+
+#define STRSIZE (20 + 1 + 10 + 1 + 10 + 1 + 112 + 1 + 1)
+ char str[STRSIZE];
+
+ if (!trace) {
+ trace = _gst_trace_default;
+ if (!trace)
+ return;
+ }
+
+ for (i = 0; i < trace->bufoffset; i++) {
+ g_snprintf (str, STRSIZE, "%20" G_GINT64_FORMAT " %10d %10d %s\n",
+ trace->buf[i].timestamp,
+ trace->buf[i].sequence, trace->buf[i].data, trace->buf[i].message);
+ if (write (trace->fd, str, strlen (str)) < 0) {
+ g_warning ("Failed to write trace %d: %s", i, g_strerror (errno));
+ return;
+ }
+ }
+ trace->bufoffset = 0;
+#undef STRSIZE
+}
+
+/**
+ * gst_trace_set_default:
+ * @trace: the #GstTrace to set as the default.
+ *
+ * Set the default #GstTrace to @trace.
+ */
+void
+gst_trace_set_default (GstTrace * trace)
+{
+ g_return_if_fail (trace != NULL);
+ _gst_trace_default = trace;
+}
+
+void
+_gst_trace_add_entry (GstTrace * trace, guint32 seq, guint32 data, gchar * msg)
+{
+ GstTraceEntry *entry;
+
+ if (!trace) {
+ trace = _gst_trace_default;
+ if (!trace)
+ return;
+ }
+
+ entry = trace->buf + trace->bufoffset;
+ read_tsc (&(entry->timestamp));
+ entry->sequence = seq;
+ entry->data = data;
+ strncpy (entry->message, msg, 112);
+ entry->message[111] = '\0';
+ trace->bufoffset++;
+
+ gst_trace_flush (trace);
+}
+
+
+/* global flags */
+static GstAllocTraceFlags _gst_trace_flags = GST_ALLOC_TRACE_NONE;
+
+/* list of registered tracers */
+static GList *_gst_alloc_tracers = NULL;
+
+/**
+ * gst_alloc_trace_available:
+ *
+ * Check if alloc tracing was compiled into the core
+ *
+ * Returns: TRUE if the core was compiled with alloc
+ * tracing enabled.
+ */
+gboolean
+gst_alloc_trace_available (void)
+{
+#ifdef GST_DISABLE_ALLOC_TRACE
+ return FALSE;
+#else
+ return TRUE;
+#endif
+}
+
+/**
+ * _gst_alloc_trace_register:
+ * @name: the name of the new alloc trace object.
+ *
+ * Register an get a handle to a GstAllocTrace object that
+ * can be used to trace memory allocations.
+ *
+ * Returns: A handle to a GstAllocTrace.
+ */
+GstAllocTrace *
+_gst_alloc_trace_register (const gchar * name)
+{
+ GstAllocTrace *trace;
+
+ g_return_val_if_fail (name, NULL);
+
+ trace = g_slice_new (GstAllocTrace);
+ trace->name = g_strdup (name);
+ trace->live = 0;
+ trace->mem_live = NULL;
+ trace->flags = _gst_trace_flags;
+
+ _gst_alloc_tracers = g_list_prepend (_gst_alloc_tracers, trace);
+
+ return trace;
+}
+
+/**
+ * gst_alloc_trace_list:
+ *
+ * Get a list of all registered alloc trace objects.
+ *
+ * Returns: a GList of GstAllocTrace objects.
+ */
+const GList *
+gst_alloc_trace_list (void)
+{
+ return _gst_alloc_tracers;
+}
+
+/**
+ * gst_alloc_trace_live_all:
+ *
+ * Get the total number of live registered alloc trace objects.
+ *
+ * Returns: the total number of live registered alloc trace objects.
+ */
+int
+gst_alloc_trace_live_all (void)
+{
+ GList *walk = _gst_alloc_tracers;
+ int num = 0;
+
+ while (walk) {
+ GstAllocTrace *trace = (GstAllocTrace *) walk->data;
+
+ num += trace->live;
+
+ walk = g_list_next (walk);
+ }
+
+ return num;
+}
+
+static gint
+compare_func (GstAllocTrace * a, GstAllocTrace * b)
+{
+ return strcmp (a->name, b->name);
+}
+
+static GList *
+gst_alloc_trace_list_sorted (void)
+{
+ GList *ret;
+
+ ret = g_list_sort (g_list_copy (_gst_alloc_tracers),
+ (GCompareFunc) compare_func);
+
+ return ret;
+}
+
+/**
+ * gst_alloc_trace_print_all:
+ *
+ * Print the status of all registered alloc trace objects.
+ */
+void
+gst_alloc_trace_print_all (void)
+{
+ GList *orig, *walk;
+
+ orig = walk = gst_alloc_trace_list_sorted ();
+
+ while (walk) {
+ GstAllocTrace *trace = (GstAllocTrace *) walk->data;
+
+ gst_alloc_trace_print (trace);
+
+ walk = g_list_next (walk);
+ }
+
+ g_list_free (orig);
+}
+
+/**
+ * gst_alloc_trace_print_live:
+ *
+ * Print the status of all registered alloc trace objects, ignoring those
+ * without live objects.
+ */
+void
+gst_alloc_trace_print_live (void)
+{
+ GList *orig, *walk;
+
+ orig = walk = gst_alloc_trace_list_sorted ();
+
+ while (walk) {
+ GstAllocTrace *trace = (GstAllocTrace *) walk->data;
+
+ if (trace->live)
+ gst_alloc_trace_print (trace);
+
+ walk = g_list_next (walk);
+ }
+
+ g_list_free (orig);
+}
+
+/**
+ * gst_alloc_trace_set_flags_all:
+ * @flags: the options to enable
+ *
+ * Enable the specified options on all registered alloc trace
+ * objects.
+ */
+void
+gst_alloc_trace_set_flags_all (GstAllocTraceFlags flags)
+{
+ GList *walk = _gst_alloc_tracers;
+
+ while (walk) {
+ GstAllocTrace *trace = (GstAllocTrace *) walk->data;
+
+ GST_DEBUG ("setting flags %d on %p", (gint) flags, trace);
+ gst_alloc_trace_set_flags (trace, flags);
+
+ walk = g_list_next (walk);
+ }
+ _gst_trace_flags = flags;
+}
+
+/**
+ * gst_alloc_trace_get:
+ * @name: the name of the alloc trace object
+ *
+ * Get the named alloc trace object.
+ *
+ * Returns: a GstAllocTrace with the given name or NULL when
+ * no alloc tracer was registered with that name.
+ */
+GstAllocTrace *
+gst_alloc_trace_get (const gchar * name)
+{
+ GList *walk = _gst_alloc_tracers;
+
+ g_return_val_if_fail (name, NULL);
+
+ while (walk) {
+ GstAllocTrace *trace = (GstAllocTrace *) walk->data;
+
+ if (!strcmp (trace->name, name))
+ return trace;
+
+ walk = g_list_next (walk);
+ }
+ return NULL;
+}
+
+/**
+ * gst_alloc_trace_print:
+ * @trace: the GstAllocTrace to print
+ *
+ * Print the status of the given GstAllocTrace.
+ */
+void
+gst_alloc_trace_print (const GstAllocTrace * trace)
+{
+ GSList *mem_live;
+
+ g_return_if_fail (trace != NULL);
+
+ if (trace->flags & GST_ALLOC_TRACE_LIVE) {
+ g_print ("%-22.22s : %d\n", trace->name, trace->live);
+ } else {
+ g_print ("%-22.22s : (no live count)\n", trace->name);
+ }
+
+ if (trace->flags & GST_ALLOC_TRACE_MEM_LIVE) {
+ mem_live = trace->mem_live;
+
+ while (mem_live) {
+ gpointer data = mem_live->data;
+
+ if (G_IS_OBJECT (data)) {
+ g_print ("%-22.22s : %p\n", g_type_name (G_OBJECT_TYPE (data)), data);
+ } else {
+ g_print ("%-22.22s : %p\n", "", data);
+ }
+ mem_live = mem_live->next;
+ }
+ }
+}
+
+/**
+ * gst_alloc_trace_set_flags:
+ * @trace: the GstAllocTrace
+ * @flags: flags to set
+ *
+ * Enable the given features on the given GstAllocTrace object.
+ */
+void
+gst_alloc_trace_set_flags (GstAllocTrace * trace, GstAllocTraceFlags flags)
+{
+ g_return_if_fail (trace != NULL);
+
+ trace->flags = flags;
+}
diff --git a/gst/gsttrace.h b/gst/gsttrace.h
new file mode 100644
index 0000000..817ae1f
--- /dev/null
+++ b/gst/gsttrace.h
@@ -0,0 +1,253 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2000 Wim Taymans <wtay@chello.be>
+ *
+ * gsttrace.h: Header for tracing functions (deprecated)
+ *
+ * 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_TRACE_H__
+#define __GST_TRACE_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+/**
+ * GstAllocTraceFlags:
+ * @GST_ALLOC_TRACE_NONE: No tracing specified or desired. Since 0.10.36.
+ * @GST_ALLOC_TRACE_LIVE: Trace number of non-freed memory.
+ * @GST_ALLOC_TRACE_MEM_LIVE: Trace pointers of unfreed memory.
+ *
+ * Flags indicating which tracing feature to enable.
+ */
+typedef enum {
+ GST_ALLOC_TRACE_NONE = 0,
+ GST_ALLOC_TRACE_LIVE = (1 << 0),
+ GST_ALLOC_TRACE_MEM_LIVE = (1 << 1)
+} GstAllocTraceFlags;
+
+typedef struct _GstAllocTrace GstAllocTrace;
+
+/**
+ * GstAllocTrace:
+ * @name: The name of the tracing object
+ * @flags: Flags for this object
+ * @live: counter for live memory
+ * @mem_live: list with pointers to unfreed memory
+ *
+ * The main tracing object
+ */
+struct _GstAllocTrace {
+ gchar *name;
+ gint flags;
+
+ gint live;
+ GSList *mem_live;
+};
+
+#ifndef GST_DISABLE_TRACE
+
+typedef struct _GstTrace GstTrace;
+typedef struct _GstTraceEntry GstTraceEntry;
+
+/**
+ * GstTrace:
+ *
+ * Opaque #GstTrace structure.
+ */
+struct _GstTrace {
+ /*< private >*/
+ /* where this trace is going */
+ gchar *filename;
+ int fd;
+
+ /* current buffer, size, head offset */
+ GstTraceEntry *buf;
+ gint bufsize;
+ gint bufoffset;
+};
+
+struct _GstTraceEntry {
+ gint64 timestamp;
+ guint32 sequence;
+ guint32 data;
+ gchar message[112];
+};
+
+GstTrace* gst_trace_new (const gchar *filename, gint size);
+
+void gst_trace_destroy (GstTrace *trace);
+void gst_trace_flush (GstTrace *trace);
+void gst_trace_text_flush (GstTrace *trace);
+/**
+ * gst_trace_get_size:
+ * @trace: a #GstTrace
+ *
+ * Retrieve the buffer size of @trace.
+ */
+#define gst_trace_get_size(trace) ((trace)->bufsize)
+/**
+ * gst_trace_get_offset:
+ * @trace: a #GstTrace
+ *
+ * Retrieve the current buffer offset of @trace.
+ */
+#define gst_trace_get_offset(trace) ((trace)->bufoffset)
+/**
+ * gst_trace_get_remaining:
+ * @trace: a #GstTrace
+ *
+ * Retrieve the remaining size in the @trace buffer.
+ */
+#define gst_trace_get_remaining(trace) ((trace)->bufsize - (trace)->bufoffset)
+void gst_trace_set_default (GstTrace *trace);
+
+void _gst_trace_add_entry (GstTrace *trace, guint32 seq,
+ guint32 data, gchar *msg);
+
+void gst_trace_read_tsc (gint64 *dst);
+
+
+extern GStaticMutex _gst_trace_mutex;
+
+gboolean gst_alloc_trace_available (void);
+const GList* gst_alloc_trace_list (void);
+GstAllocTrace* _gst_alloc_trace_register (const gchar *name);
+
+int gst_alloc_trace_live_all (void);
+void gst_alloc_trace_print_all (void);
+void gst_alloc_trace_print_live (void);
+void gst_alloc_trace_set_flags_all (GstAllocTraceFlags flags);
+
+GstAllocTrace* gst_alloc_trace_get (const gchar *name);
+void gst_alloc_trace_print (const GstAllocTrace *trace);
+void gst_alloc_trace_set_flags (GstAllocTrace *trace, GstAllocTraceFlags flags);
+
+
+#ifndef GST_DISABLE_ALLOC_TRACE
+/**
+ * gst_alloc_trace_register:
+ * @name: The name of the tracer object
+ *
+ * Register a new alloc tracer with the given name
+ */
+#define gst_alloc_trace_register(name) _gst_alloc_trace_register (name);
+
+/**
+ * gst_alloc_trace_new:
+ * @trace: The tracer to use
+ * @mem: The memory allocated
+ *
+ * Use the tracer to trace a new memory allocation
+ */
+#define gst_alloc_trace_new(trace, mem) \
+G_STMT_START { \
+ if (G_UNLIKELY ((trace)->flags)) { \
+ g_static_mutex_lock (&_gst_trace_mutex); \
+ if ((trace)->flags & GST_ALLOC_TRACE_LIVE) \
+ (trace)->live++; \
+ if ((trace)->flags & GST_ALLOC_TRACE_MEM_LIVE) \
+ (trace)->mem_live = \
+ g_slist_prepend ((trace)->mem_live, mem); \
+ g_static_mutex_unlock (&_gst_trace_mutex); \
+ } \
+} G_STMT_END
+
+/**
+ * gst_alloc_trace_free:
+ * @trace: The tracer to use
+ * @mem: The memory that is freed
+ *
+ * Trace a memory free operation
+ */
+#define gst_alloc_trace_free(trace, mem) \
+G_STMT_START { \
+ if (G_UNLIKELY ((trace)->flags)) { \
+ g_static_mutex_lock (&_gst_trace_mutex); \
+ if ((trace)->flags & GST_ALLOC_TRACE_LIVE) \
+ (trace)->live--; \
+ if ((trace)->flags & GST_ALLOC_TRACE_MEM_LIVE) \
+ (trace)->mem_live = \
+ g_slist_remove ((trace)->mem_live, mem); \
+ g_static_mutex_unlock (&_gst_trace_mutex); \
+ } \
+} G_STMT_END
+
+#else
+#define gst_alloc_trace_register(name) (NULL)
+#define gst_alloc_trace_new(trace, mem)
+#define gst_alloc_trace_free(trace, mem)
+#endif
+
+
+extern gint _gst_trace_on;
+/**
+ * gst_trace_add_entry:
+ * @trace: a #GstTrace
+ * @seq: a sequence number
+ * @data: the data to trace
+ * @msg: the trace message
+ *
+ * Add an entry to @trace with sequence number @seq, @data and @msg.
+ * If @trace is NULL, the entry will be added to the default #GstTrace.
+ */
+#define gst_trace_add_entry(trace,seq,data,msg) \
+ if (_gst_trace_on) { \
+ _gst_trace_add_entry(trace,(guint32)seq,(guint32)data,msg); \
+ }
+
+#else /* GST_DISABLE_TRACE */
+
+#if defined __GNUC__ && __GNUC__ >= 3
+#pragma GCC poison gst_trace_new
+#pragma GCC poison gst_trace_destroy
+#pragma GCC poison gst_trace_flush
+#pragma GCC poison gst_trace_text_flush
+#pragma GCC poison gst_trace_get_size
+#pragma GCC poison gst_trace_get_offset
+#pragma GCC poison gst_trace_get_remaining
+#pragma GCC poison gst_trace_set_default
+#pragma GCC poison _gst_trace_add_entry
+#pragma GCC poison gst_trace_read_tsc
+#endif
+
+#define gst_alloc_trace_register(name) (NULL)
+#define gst_alloc_trace_new(trace, mem)
+#define gst_alloc_trace_free(trace, mem)
+
+#define gst_alloc_trace_available() (FALSE)
+#define gst_alloc_trace_list() (NULL)
+#define _gst_alloc_trace_register(name) (NULL)
+
+#define gst_alloc_trace_live_all() (0)
+#define gst_alloc_trace_print_all()
+#define gst_alloc_trace_print_live()
+#define gst_alloc_trace_set_flags_all(flags)
+
+#define gst_alloc_trace_get(name) (NULL)
+#define gst_alloc_trace_print(trace)
+#define gst_alloc_trace_set_flags(trace,flags)
+
+#define gst_trace_add_entry(trace,seq,data,msg)
+
+#endif /* GST_DISABLE_TRACE */
+
+G_END_DECLS
+
+#endif /* __GST_TRACE_H__ */
diff --git a/gst/gsttypefind.c b/gst/gsttypefind.c
new file mode 100644
index 0000000..1f0b16d
--- /dev/null
+++ b/gst/gsttypefind.c
@@ -0,0 +1,231 @@
+/* GStreamer
+ * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
+ *
+ * gsttypefind.c: typefinding subsystem
+ *
+ * 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:gsttypefind
+ * @short_description: Stream type detection
+ *
+ * The following functions allow you to detect the media type of an unknown
+ * stream.
+ *
+ * Last reviewed on 2005-11-09 (0.9.4)
+ */
+
+#include "gst_private.h"
+#include "gstinfo.h"
+#include "gsttypefind.h"
+#include "gstregistry.h"
+#include "gsttypefindfactory.h"
+
+GST_DEBUG_CATEGORY_EXTERN (type_find_debug);
+#define GST_CAT_DEFAULT type_find_debug
+
+GType
+gst_type_find_get_type (void)
+{
+ static GType typefind_type = 0;
+
+ if (G_UNLIKELY (typefind_type == 0)) {
+ typefind_type = g_pointer_type_register_static ("GstTypeFind");
+ }
+ return typefind_type;
+}
+
+/**
+ * gst_type_find_register:
+ * @plugin: A #GstPlugin, or NULL for a static typefind function (note that
+ * passing NULL only works in GStreamer 0.10.16 and later)
+ * @name: The name for registering
+ * @rank: The rank (or importance) of this typefind function
+ * @func: The #GstTypeFindFunction to use
+ * @extensions: (transfer none) (array zero-terminated=1) (element-type utf8):
+ * Optional extensions that could belong to this type
+ * @possible_caps: Optionally the caps that could be returned when typefinding
+ * succeeds
+ * @data: Optional user data. This user data must be available until the plugin
+ * is unloaded.
+ * @data_notify: a #GDestroyNotify that will be called on @data when the plugin
+ * is unloaded.
+ *
+ * Registers a new typefind function to be used for typefinding. After
+ * registering this function will be available for typefinding.
+ * This function is typically called during an element's plugin initialization.
+ *
+ * Returns: TRUE on success, FALSE otherwise
+ */
+gboolean
+gst_type_find_register (GstPlugin * plugin, const gchar * name, guint rank,
+ GstTypeFindFunction func, gchar ** extensions,
+ const GstCaps * possible_caps, gpointer data, GDestroyNotify data_notify)
+{
+ GstTypeFindFactory *factory;
+
+ g_return_val_if_fail (name != NULL, FALSE);
+
+ GST_INFO ("registering typefind function for %s", name);
+
+ factory = g_object_newv (GST_TYPE_TYPE_FIND_FACTORY, 0, NULL);
+ GST_DEBUG_OBJECT (factory, "using new typefind factory for %s", name);
+ g_assert (GST_IS_TYPE_FIND_FACTORY (factory));
+
+ gst_plugin_feature_set_name (GST_PLUGIN_FEATURE_CAST (factory), name);
+ gst_plugin_feature_set_rank (GST_PLUGIN_FEATURE_CAST (factory), rank);
+
+ if (factory->extensions)
+ g_strfreev (factory->extensions);
+ factory->extensions = g_strdupv (extensions);
+
+ gst_caps_replace (&factory->caps, (GstCaps *) possible_caps);
+ factory->function = func;
+ factory->user_data = data;
+ factory->user_data_notify = data_notify;
+ if (plugin && plugin->desc.name) {
+ GST_PLUGIN_FEATURE_CAST (factory)->plugin_name = plugin->desc.name; /* interned string */
+ GST_PLUGIN_FEATURE_CAST (factory)->plugin = plugin;
+ g_object_add_weak_pointer ((GObject *) plugin,
+ (gpointer *) & GST_PLUGIN_FEATURE_CAST (factory)->plugin);
+ } else {
+ GST_PLUGIN_FEATURE_CAST (factory)->plugin_name = "NULL";
+ GST_PLUGIN_FEATURE_CAST (factory)->plugin = NULL;
+ }
+ GST_PLUGIN_FEATURE_CAST (factory)->loaded = TRUE;
+
+ gst_registry_add_feature (gst_registry_get_default (),
+ GST_PLUGIN_FEATURE_CAST (factory));
+
+ return TRUE;
+}
+
+/*** typefind function interface **********************************************/
+
+/**
+ * gst_type_find_peek:
+ * @find: The #GstTypeFind object the function was called with
+ * @offset: The offset
+ * @size: The number of bytes to return
+ *
+ * Returns the @size bytes of the stream to identify beginning at offset. If
+ * offset is a positive number, the offset is relative to the beginning of the
+ * stream, if offset is a negative number the offset is relative to the end of
+ * the stream. The returned memory is valid until the typefinding function
+ * returns and must not be freed.
+ *
+ * Returns: (transfer none) (array length=size): the requested data, or NULL
+ * if that data is not available.
+ */
+const guint8 *
+gst_type_find_peek (GstTypeFind * find, gint64 offset, guint size)
+{
+ g_return_val_if_fail (find->peek != NULL, NULL);
+
+ return find->peek (find->data, offset, size);
+}
+
+/**
+ * gst_type_find_suggest:
+ * @find: The #GstTypeFind object the function was called with
+ * @probability: The probability in percent that the suggestion is right
+ * @caps: The fixed #GstCaps to suggest
+ *
+ * If a #GstTypeFindFunction calls this function it suggests the caps with the
+ * given probability. A #GstTypeFindFunction may supply different suggestions
+ * in one call.
+ * It is up to the caller of the #GstTypeFindFunction to interpret these values.
+ */
+void
+gst_type_find_suggest (GstTypeFind * find, guint probability,
+ const GstCaps * caps)
+{
+ g_return_if_fail (find->suggest != NULL);
+ g_return_if_fail (probability <= 100);
+ g_return_if_fail (caps != NULL);
+ g_return_if_fail (gst_caps_is_fixed (caps));
+
+ find->suggest (find->data, probability, caps);
+}
+
+/**
+ * gst_type_find_suggest_simple:
+ * @find: The #GstTypeFind object the function was called with
+ * @probability: The probability in percent that the suggestion is right
+ * @media_type: the media type of the suggested caps
+ * @fieldname: first field of the suggested caps, or NULL
+ * @...: additional arguments to the suggested caps in the same format as the
+ * arguments passed to gst_structure_new() (ie. triplets of field name,
+ * field GType and field value)
+ *
+ * If a #GstTypeFindFunction calls this function it suggests the caps with the
+ * given probability. A #GstTypeFindFunction may supply different suggestions
+ * in one call. It is up to the caller of the #GstTypeFindFunction to interpret
+ * these values.
+ *
+ * This function is similar to gst_type_find_suggest(), only that instead of
+ * passing a #GstCaps argument you can create the caps on the fly in the same
+ * way as you can with gst_caps_new_simple().
+ *
+ * Make sure you terminate the list of arguments with a NULL argument and that
+ * the values passed have the correct type (in terms of width in bytes when
+ * passed to the vararg function - this applies particularly to gdouble and
+ * guint64 arguments).
+ *
+ * Since: 0.10.20
+ */
+void
+gst_type_find_suggest_simple (GstTypeFind * find, guint probability,
+ const char *media_type, const char *fieldname, ...)
+{
+ GstStructure *structure;
+ va_list var_args;
+ GstCaps *caps;
+
+ g_return_if_fail (find->suggest != NULL);
+ g_return_if_fail (probability <= 100);
+ g_return_if_fail (media_type != NULL);
+
+ caps = gst_caps_new_empty ();
+
+ va_start (var_args, fieldname);
+ structure = gst_structure_new_valist (media_type, fieldname, var_args);
+ va_end (var_args);
+
+ gst_caps_append_structure (caps, structure);
+ g_return_if_fail (gst_caps_is_fixed (caps));
+
+ find->suggest (find->data, probability, caps);
+ gst_caps_unref (caps);
+}
+
+/**
+ * gst_type_find_get_length:
+ * @find: The #GstTypeFind the function was called with
+ *
+ * Get the length of the data stream.
+ *
+ * Returns: The length of the data stream, or 0 if it is not available.
+ */
+guint64
+gst_type_find_get_length (GstTypeFind * find)
+{
+ if (find->get_length == NULL)
+ return 0;
+
+ return find->get_length (find->data);
+}
diff --git a/gst/gsttypefind.h b/gst/gsttypefind.h
new file mode 100644
index 0000000..27c7e32
--- /dev/null
+++ b/gst/gsttypefind.h
@@ -0,0 +1,124 @@
+/* GStreamer
+ * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
+ *
+ * gsttypefind.h: typefinding subsystem
+ *
+ * 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_TYPE_FIND_H__
+#define __GST_TYPE_FIND_H__
+
+#include <gst/gstcaps.h>
+#include <gst/gstplugin.h>
+#include <gst/gstpluginfeature.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_TYPE_FIND (gst_type_find_get_type())
+
+typedef struct _GstTypeFind GstTypeFind;
+
+/**
+ * GstTypeFindFunction:
+ * @find: A #GstTypeFind structure
+ * @data: optionnal data to pass to the function
+ *
+ * A function that will be called by typefinding.
+ */
+typedef void (* GstTypeFindFunction) (GstTypeFind *find, gpointer data);
+
+/**
+ * GstTypeFindProbability:
+ * @GST_TYPE_FIND_NONE: type undetected. Since 0.10.36.
+ * @GST_TYPE_FIND_MINIMUM: unlikely typefind.
+ * @GST_TYPE_FIND_POSSIBLE: possible type detected.
+ * @GST_TYPE_FIND_LIKELY: likely a type was detected.
+ * @GST_TYPE_FIND_NEARLY_CERTAIN: nearly certain that a type was detected.
+ * @GST_TYPE_FIND_MAXIMUM: very certain a type was detected.
+ *
+ * The probability of the typefind function. Higher values have more certainty
+ * in doing a reliable typefind.
+ */
+typedef enum {
+ GST_TYPE_FIND_NONE = 0,
+ GST_TYPE_FIND_MINIMUM = 1,
+ GST_TYPE_FIND_POSSIBLE = 50,
+ GST_TYPE_FIND_LIKELY = 80,
+ GST_TYPE_FIND_NEARLY_CERTAIN = 99,
+ GST_TYPE_FIND_MAXIMUM = 100
+} GstTypeFindProbability;
+
+/**
+ * GstTypeFind:
+ * @peek: Method to peek data.
+ * @suggest: Method to suggest #GstCaps with a given probability.
+ * @data: The data used by the caller of the typefinding function.
+ * @get_length: Returns the length of current data.
+ *
+ * Object that stores typefind callbacks. To use with #GstTypeFindFactory.
+ */
+struct _GstTypeFind {
+ /* private to the caller of the typefind function */
+ const guint8 * (* peek) (gpointer data,
+ gint64 offset,
+ guint size);
+
+ void (* suggest) (gpointer data,
+ guint probability,
+ const GstCaps * caps);
+
+ gpointer data;
+
+ /* optional */
+ guint64 (* get_length) (gpointer data);
+
+ /* <private> */
+ gpointer _gst_reserved[GST_PADDING];
+};
+
+GType gst_type_find_get_type (void);
+
+/* typefind function interface */
+const guint8 * gst_type_find_peek (GstTypeFind * find,
+ gint64 offset,
+ guint size);
+
+void gst_type_find_suggest (GstTypeFind * find,
+ guint probability,
+ const GstCaps * caps);
+
+void gst_type_find_suggest_simple (GstTypeFind * find,
+ guint probability,
+ const char * media_type,
+ const char * fieldname, ...);
+
+guint64 gst_type_find_get_length (GstTypeFind * find);
+
+/* registration interface */
+gboolean gst_type_find_register (GstPlugin * plugin,
+ const gchar * name,
+ guint rank,
+ GstTypeFindFunction func,
+ gchar ** extensions,
+ const GstCaps * possible_caps,
+ gpointer data,
+ GDestroyNotify data_notify);
+
+G_END_DECLS
+
+#endif /* __GST_TYPE_FIND_H__ */
diff --git a/gst/gsttypefindfactory.c b/gst/gsttypefindfactory.c
new file mode 100644
index 0000000..26cbfb5
--- /dev/null
+++ b/gst/gsttypefindfactory.c
@@ -0,0 +1,227 @@
+/* GStreamer
+ * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
+ *
+ * gsttypefindfactory.c: typefinding subsystem
+ *
+ * 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:gsttypefindfactory
+ * @short_description: Information about registered typefind functions
+ *
+ * These functions allow querying informations about registered typefind
+ * functions. How to create and register these functions is described in
+ * the section <link linkend="gstreamer-Writing-typefind-functions">
+ * "Writing typefind functions"</link>.
+ *
+ * <example>
+ * <title>how to write a simple typefinder</title>
+ * <programlisting>
+ * typedef struct {
+ * guint8 *data;
+ * guint size;
+ * guint probability;
+ * GstCaps *data;
+ * } MyTypeFind;
+ * static void
+ * my_peek (gpointer data, gint64 offset, guint size)
+ * {
+ * MyTypeFind *find = (MyTypeFind *) data;
+ * if (offset &gt;= 0 &amp;&amp; offset + size &lt;= find->size) {
+ * return find->data + offset;
+ * }
+ * return NULL;
+ * }
+ * static void
+ * my_suggest (gpointer data, guint probability, GstCaps *caps)
+ * {
+ * MyTypeFind *find = (MyTypeFind *) data;
+ * if (probability &gt; find->probability) {
+ * find->probability = probability;
+ * gst_caps_replace (&amp;find->caps, caps);
+ * }
+ * }
+ * static GstCaps *
+ * find_type (guint8 *data, guint size)
+ * {
+ * GList *walk, *type_list;
+ * MyTypeFind find = {data, size, 0, NULL};
+ * GstTypeFind gst_find = {my_peek, my_suggest, &amp;find, };
+ * walk = type_list = gst_type_find_factory_get_list ();
+ * while (walk) {
+ * GstTypeFindFactory *factory = GST_TYPE_FIND_FACTORY (walk->data);
+ * walk = g_list_next (walk)
+ * gst_type_find_factory_call_function (factory, &amp;gst_find);
+ * }
+ * g_list_free (type_list);
+ * return find.caps;
+ * };
+ * </programlisting>
+ * </example>
+ *
+ * The above example shows how to write a very simple typefinder that
+ * identifies the given data. You can get quite a bit more complicated than
+ * that though.
+ *
+ * Last reviewed on 2005-11-09 (0.9.4)
+ */
+
+#include "gst_private.h"
+#include "gstinfo.h"
+#include "gsttypefind.h"
+#include "gsttypefindfactory.h"
+#include "gstregistry.h"
+
+GST_DEBUG_CATEGORY (type_find_debug);
+#define GST_CAT_DEFAULT type_find_debug
+
+static void gst_type_find_factory_dispose (GObject * object);
+
+static GstPluginFeatureClass *parent_class = NULL;
+
+#define _do_init \
+{ \
+ GST_DEBUG_CATEGORY_INIT (type_find_debug, "GST_TYPEFIND", \
+ GST_DEBUG_FG_GREEN, "typefinding subsystem"); \
+}
+
+G_DEFINE_TYPE_WITH_CODE (GstTypeFindFactory, gst_type_find_factory,
+ GST_TYPE_PLUGIN_FEATURE, _do_init);
+
+static void
+gst_type_find_factory_class_init (GstTypeFindFactoryClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ object_class->dispose = gst_type_find_factory_dispose;
+}
+
+static void
+gst_type_find_factory_init (GstTypeFindFactory * factory)
+{
+ factory->user_data = factory;
+ factory->user_data_notify = NULL;
+}
+
+static void
+gst_type_find_factory_dispose (GObject * object)
+{
+ GstTypeFindFactory *factory = GST_TYPE_FIND_FACTORY (object);
+
+ if (factory->caps) {
+ gst_caps_unref (factory->caps);
+ factory->caps = NULL;
+ }
+ if (factory->extensions) {
+ g_strfreev (factory->extensions);
+ factory->extensions = NULL;
+ }
+ if (factory->user_data_notify && factory->user_data) {
+ factory->user_data_notify (factory->user_data);
+ factory->user_data = NULL;
+ }
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+/**
+ * gst_type_find_factory_get_list:
+ *
+ * Gets the list of all registered typefind factories. You must free the
+ * list using gst_plugin_feature_list_free().
+ *
+ * The returned factories are sorted by highest rank first, and then by
+ * factory name. (behaviour change since 0.10.26)
+ *
+ * Free-function: gst_plugin_feature_list_free
+ *
+ * Returns: (transfer full) (element-type Gst.TypeFindFactory): the list of all
+ * registered #GstTypeFindFactory.
+ */
+GList *
+gst_type_find_factory_get_list (void)
+{
+ return gst_registry_get_feature_list (gst_registry_get_default (),
+ GST_TYPE_TYPE_FIND_FACTORY);
+}
+
+/**
+ * gst_type_find_factory_get_caps:
+ * @factory: A #GstTypeFindFactory
+ *
+ * Gets the #GstCaps associated with a typefind factory.
+ *
+ * Returns: (transfer none): the #GstCaps associated with this factory
+ */
+GstCaps *
+gst_type_find_factory_get_caps (GstTypeFindFactory * factory)
+{
+ g_return_val_if_fail (GST_IS_TYPE_FIND_FACTORY (factory), NULL);
+
+ return factory->caps;
+}
+
+/**
+ * gst_type_find_factory_get_extensions:
+ * @factory: A #GstTypeFindFactory
+ *
+ * Gets the extensions associated with a #GstTypeFindFactory. The returned
+ * array should not be changed. If you need to change stuff in it, you should
+ * copy it using g_strdupv(). This function may return NULL to indicate
+ * a 0-length list.
+ *
+ * Returns: (transfer none) (array zero-terminated=1) (element-type utf8): a
+ * NULL-terminated array of extensions associated with this factory
+ */
+gchar **
+gst_type_find_factory_get_extensions (GstTypeFindFactory * factory)
+{
+ g_return_val_if_fail (GST_IS_TYPE_FIND_FACTORY (factory), NULL);
+
+ return factory->extensions;
+}
+
+/**
+ * gst_type_find_factory_call_function:
+ * @factory: A #GstTypeFindFactory
+ * @find: (transfer none): a properly setup #GstTypeFind entry. The get_data
+ * and suggest_type members must be set.
+ *
+ * Calls the #GstTypeFindFunction associated with this factory.
+ */
+void
+gst_type_find_factory_call_function (GstTypeFindFactory * factory,
+ GstTypeFind * find)
+{
+ GstTypeFindFactory *new_factory;
+
+ g_return_if_fail (GST_IS_TYPE_FIND_FACTORY (factory));
+ g_return_if_fail (find != NULL);
+ g_return_if_fail (find->peek != NULL);
+ g_return_if_fail (find->suggest != NULL);
+
+ new_factory =
+ GST_TYPE_FIND_FACTORY (gst_plugin_feature_load (GST_PLUGIN_FEATURE
+ (factory)));
+ if (new_factory) {
+ if (new_factory->function)
+ new_factory->function (find, new_factory->user_data);
+ gst_object_unref (new_factory);
+ }
+}
diff --git a/gst/gsttypefindfactory.h b/gst/gsttypefindfactory.h
new file mode 100644
index 0000000..19792ee
--- /dev/null
+++ b/gst/gsttypefindfactory.h
@@ -0,0 +1,81 @@
+/* GStreamer
+ * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
+ *
+ * gsttypefindfactory.h: typefinding subsystem
+ *
+ * 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_TYPE_FIND_FACTORY_H__
+#define __GST_TYPE_FIND_FACTORY_H__
+
+#include <gst/gstcaps.h>
+#include <gst/gstplugin.h>
+#include <gst/gstpluginfeature.h>
+#include <gst/gsttypefind.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_TYPE_FIND_FACTORY (gst_type_find_factory_get_type())
+#define GST_TYPE_FIND_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_TYPE_FIND_FACTORY, GstTypeFindFactory))
+#define GST_IS_TYPE_FIND_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_TYPE_FIND_FACTORY))
+#define GST_TYPE_FIND_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_TYPE_FIND_FACTORY, GstTypeFindFactoryClass))
+#define GST_IS_TYPE_FIND_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_TYPE_FIND_FACTORY))
+#define GST_TYPE_FIND_FACTORY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_TYPE_FIND_FACTORY, GstTypeFindFactoryClass))
+
+typedef struct _GstTypeFindFactory GstTypeFindFactory;
+typedef struct _GstTypeFindFactoryClass GstTypeFindFactoryClass;
+
+/**
+ * GstTypeFindFactory:
+ *
+ * Object that stores information about a typefind function.
+ */
+struct _GstTypeFindFactory {
+ GstPluginFeature feature;
+ /* <private> */
+
+ GstTypeFindFunction function;
+ gchar ** extensions;
+ GstCaps * caps; /* FIXME: not yet saved in registry */
+
+ gpointer user_data;
+ GDestroyNotify user_data_notify;
+
+ gpointer _gst_reserved[GST_PADDING];
+};
+
+struct _GstTypeFindFactoryClass {
+ GstPluginFeatureClass parent;
+ /* <private> */
+
+ gpointer _gst_reserved[GST_PADDING];
+};
+
+/* typefinding interface */
+
+GType gst_type_find_factory_get_type (void);
+
+GList * gst_type_find_factory_get_list (void);
+
+gchar ** gst_type_find_factory_get_extensions (GstTypeFindFactory *factory);
+GstCaps * gst_type_find_factory_get_caps (GstTypeFindFactory *factory);
+void gst_type_find_factory_call_function (GstTypeFindFactory *factory,
+ GstTypeFind *find);
+
+G_END_DECLS
+
+#endif /* __GST_TYPE_FIND_FACTORY_H__ */
diff --git a/gst/gsturi.c b/gst/gsturi.c
new file mode 100644
index 0000000..66ab40c
--- /dev/null
+++ b/gst/gsturi.c
@@ -0,0 +1,888 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2000 Wim Taymans <wtay@chello.be>
+ * Copyright (C) 2011 Tim-Philipp Müller <tim centricular net>
+ *
+ * gsturi.c: register URI handlers
+ *
+ * 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:gsturihandler
+ * @short_description: Interface to ease URI handling in plugins.
+ *
+ * The URIHandler is an interface that is implemented by Source and Sink
+ * #GstElement to simplify then handling of URI.
+ *
+ * An application can use the following functions to quickly get an element
+ * that handles the given URI for reading or writing
+ * (gst_element_make_from_uri()).
+ *
+ * Source and Sink plugins should implement this interface when possible.
+ *
+ * Last reviewed on 2005-11-09 (0.9.4)
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "gst_private.h"
+#include "gsturi.h"
+#include "gstinfo.h"
+#include "gstmarshal.h"
+#include "gstregistry.h"
+
+#include <string.h>
+
+GST_DEBUG_CATEGORY_STATIC (gst_uri_handler_debug);
+#define GST_CAT_DEFAULT gst_uri_handler_debug
+
+enum
+{
+ NEW_URI,
+ LAST_SIGNAL
+};
+
+static guint gst_uri_handler_signals[LAST_SIGNAL] = { 0 };
+
+static void gst_uri_handler_base_init (gpointer g_class);
+
+GType
+gst_uri_handler_get_type (void)
+{
+ static volatile gsize urihandler_type = 0;
+
+ if (g_once_init_enter (&urihandler_type)) {
+ GType _type;
+ static const GTypeInfo urihandler_info = {
+ sizeof (GstURIHandlerInterface),
+ gst_uri_handler_base_init,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ 0,
+ 0,
+ NULL,
+ NULL
+ };
+
+ _type = g_type_register_static (G_TYPE_INTERFACE,
+ "GstURIHandler", &urihandler_info, 0);
+
+ GST_DEBUG_CATEGORY_INIT (gst_uri_handler_debug, "GST_URI", GST_DEBUG_BOLD,
+ "handling of URIs");
+ g_once_init_leave (&urihandler_type, _type);
+ }
+ return urihandler_type;
+}
+
+static void
+gst_uri_handler_base_init (gpointer g_class)
+{
+ static gboolean initialized = FALSE;
+
+ if (G_UNLIKELY (!initialized)) {
+
+ /**
+ * GstURIHandler::new-uri:
+ * @handler: The #GstURIHandler which emitted the signal
+ * @uri: (transfer none): The new URI, or NULL if the URI was removed
+ *
+ * The URI of the given @handler has changed.
+ */
+
+ gst_uri_handler_signals[NEW_URI] =
+ g_signal_new ("new-uri", GST_TYPE_URI_HANDLER, G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GstURIHandlerInterface, new_uri), NULL, NULL,
+ gst_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING);
+ initialized = TRUE;
+ }
+}
+
+static const guchar acceptable[96] = { /* X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 XA XB XC XD XE XF */
+ 0x00, 0x3F, 0x20, 0x20, 0x20, 0x00, 0x2C, 0x3F, 0x3F, 0x3F, 0x3F, 0x22, 0x20, 0x3F, 0x3F, 0x1C, /* 2X !"#$%&'()*+,-./ */
+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x38, 0x20, 0x20, 0x2C, 0x20, 0x2C, /* 3X 0123456789:;<=>? */
+ 0x30, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, /* 4X @ABCDEFGHIJKLMNO */
+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x20, 0x20, 0x20, 0x20, 0x3F, /* 5X PQRSTUVWXYZ[\]^_ */
+ 0x20, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, /* 6X `abcdefghijklmno */
+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x20, 0x20, 0x20, 0x3F, 0x20 /* 7X pqrstuvwxyz{|}~DEL */
+};
+
+typedef enum
+{
+ UNSAFE_ALL = 0x1, /* Escape all unsafe characters */
+ UNSAFE_ALLOW_PLUS = 0x2, /* Allows '+' */
+ UNSAFE_PATH = 0x4, /* Allows '/' and '?' and '&' and '=' */
+ UNSAFE_DOS_PATH = 0x8, /* Allows '/' and '?' and '&' and '=' and ':' */
+ UNSAFE_HOST = 0x10, /* Allows '/' and ':' and '@' */
+ UNSAFE_SLASHES = 0x20 /* Allows all characters except for '/' and '%' */
+} UnsafeCharacterSet;
+
+#define HEX_ESCAPE '%'
+
+/* Escape undesirable characters using %
+ * -------------------------------------
+ *
+ * This function takes a pointer to a string in which
+ * some characters may be unacceptable unescaped.
+ * It returns a string which has these characters
+ * represented by a '%' character followed by two hex digits.
+ *
+ * This routine returns a g_malloced string.
+ */
+
+static const gchar hex[16] = "0123456789ABCDEF";
+
+static gchar *
+escape_string_internal (const gchar * string, UnsafeCharacterSet mask)
+{
+#define ACCEPTABLE_CHAR(a) ((a)>=32 && (a)<128 && (acceptable[(a)-32] & use_mask))
+
+ const gchar *p;
+ gchar *q;
+ gchar *result;
+ guchar c;
+ gint unacceptable;
+ UnsafeCharacterSet use_mask;
+
+ g_return_val_if_fail (mask == UNSAFE_ALL
+ || mask == UNSAFE_ALLOW_PLUS
+ || mask == UNSAFE_PATH
+ || mask == UNSAFE_DOS_PATH
+ || mask == UNSAFE_HOST || mask == UNSAFE_SLASHES, NULL);
+
+ if (string == NULL) {
+ return NULL;
+ }
+
+ unacceptable = 0;
+ use_mask = mask;
+ for (p = string; *p != '\0'; p++) {
+ c = *p;
+ if (!ACCEPTABLE_CHAR (c)) {
+ unacceptable++;
+ }
+ if ((use_mask == UNSAFE_HOST) && (unacceptable || (c == '/'))) {
+ /* when escaping a host, if we hit something that needs to be escaped, or we finally
+ * hit a path separator, revert to path mode (the host segment of the url is over).
+ */
+ use_mask = UNSAFE_PATH;
+ }
+ }
+
+ result = g_malloc (p - string + unacceptable * 2 + 1);
+
+ use_mask = mask;
+ for (q = result, p = string; *p != '\0'; p++) {
+ c = *p;
+
+ if (!ACCEPTABLE_CHAR (c)) {
+ *q++ = HEX_ESCAPE; /* means hex coming */
+ *q++ = hex[c >> 4];
+ *q++ = hex[c & 15];
+ } else {
+ *q++ = c;
+ }
+ if ((use_mask == UNSAFE_HOST) && (!ACCEPTABLE_CHAR (c) || (c == '/'))) {
+ use_mask = UNSAFE_PATH;
+ }
+ }
+
+ *q = '\0';
+
+ return result;
+}
+
+/* escape_string:
+ * @string: string to be escaped
+ *
+ * Escapes @string, replacing any and all special characters
+ * with equivalent escape sequences.
+ *
+ * Return value: a newly allocated string equivalent to @string
+ * but with all special characters escaped
+ **/
+static gchar *
+escape_string (const gchar * string)
+{
+ return escape_string_internal (string, UNSAFE_ALL);
+}
+
+static int
+hex_to_int (gchar c)
+{
+ return c >= '0' && c <= '9' ? c - '0'
+ : c >= 'A' && c <= 'F' ? c - 'A' + 10
+ : c >= 'a' && c <= 'f' ? c - 'a' + 10 : -1;
+}
+
+static int
+unescape_character (const char *scanner)
+{
+ int first_digit;
+ int second_digit;
+
+ first_digit = hex_to_int (*scanner++);
+ if (first_digit < 0) {
+ return -1;
+ }
+
+ second_digit = hex_to_int (*scanner);
+ if (second_digit < 0) {
+ return -1;
+ }
+
+ return (first_digit << 4) | second_digit;
+}
+
+/* unescape_string:
+ * @escaped_string: an escaped URI, path, or other string
+ * @illegal_characters: a string containing a sequence of characters
+ * considered "illegal", '\0' is automatically in this list.
+ *
+ * Decodes escaped characters (i.e. PERCENTxx sequences) in @escaped_string.
+ * Characters are encoded in PERCENTxy form, where xy is the ASCII hex code
+ * for character 16x+y.
+ *
+ * Return value: a newly allocated string with the unescaped equivalents,
+ * or %NULL if @escaped_string contained one of the characters
+ * in @illegal_characters.
+ **/
+static char *
+unescape_string (const gchar * escaped_string, const gchar * illegal_characters)
+{
+ const gchar *in;
+ gchar *out, *result;
+ gint character;
+
+ if (escaped_string == NULL) {
+ return NULL;
+ }
+
+ result = g_malloc (strlen (escaped_string) + 1);
+
+ out = result;
+ for (in = escaped_string; *in != '\0'; in++) {
+ character = *in;
+ if (*in == HEX_ESCAPE) {
+ character = unescape_character (in + 1);
+
+ /* Check for an illegal character. We consider '\0' illegal here. */
+ if (character <= 0
+ || (illegal_characters != NULL
+ && strchr (illegal_characters, (char) character) != NULL)) {
+ g_free (result);
+ return NULL;
+ }
+ in += 2;
+ }
+ *out++ = (char) character;
+ }
+
+ *out = '\0';
+ g_assert ((gsize) (out - result) <= strlen (escaped_string));
+ return result;
+
+}
+
+
+static void
+gst_uri_protocol_check_internal (const gchar * uri, gchar ** endptr)
+{
+ gchar *check = (gchar *) uri;
+
+ g_assert (uri != NULL);
+ g_assert (endptr != NULL);
+
+ if (g_ascii_isalpha (*check)) {
+ check++;
+ while (g_ascii_isalnum (*check) || *check == '+'
+ || *check == '-' || *check == '.')
+ check++;
+ }
+
+ *endptr = check;
+}
+
+/**
+ * gst_uri_protocol_is_valid:
+ * @protocol: A string
+ *
+ * Tests if the given string is a valid protocol identifier. Protocols
+ * must consist of alphanumeric characters, '+', '-' and '.' and must
+ * start with a alphabetic character. See RFC 3986 Section 3.1.
+ *
+ * Returns: TRUE if the string is a valid protocol identifier, FALSE otherwise.
+ */
+gboolean
+gst_uri_protocol_is_valid (const gchar * protocol)
+{
+ gchar *endptr;
+
+ g_return_val_if_fail (protocol != NULL, FALSE);
+
+ gst_uri_protocol_check_internal (protocol, &endptr);
+
+ return *endptr == '\0' && endptr != protocol;
+}
+
+/**
+ * gst_uri_is_valid:
+ * @uri: A URI string
+ *
+ * Tests if the given string is a valid URI identifier. URIs start with a valid
+ * scheme followed by ":" and maybe a string identifying the location.
+ *
+ * Returns: TRUE if the string is a valid URI
+ */
+gboolean
+gst_uri_is_valid (const gchar * uri)
+{
+ gchar *endptr;
+
+ g_return_val_if_fail (uri != NULL, FALSE);
+
+ gst_uri_protocol_check_internal (uri, &endptr);
+
+ return *endptr == ':';
+}
+
+/**
+ * gst_uri_get_protocol:
+ * @uri: A URI string
+ *
+ * Extracts the protocol out of a given valid URI. The returned string must be
+ * freed using g_free().
+ *
+ * Returns: The protocol for this URI.
+ */
+gchar *
+gst_uri_get_protocol (const gchar * uri)
+{
+ gchar *colon;
+
+ g_return_val_if_fail (uri != NULL, NULL);
+ g_return_val_if_fail (gst_uri_is_valid (uri), NULL);
+
+ colon = strstr (uri, ":");
+
+ return g_ascii_strdown (uri, colon - uri);
+}
+
+/**
+ * gst_uri_has_protocol:
+ * @uri: a URI string
+ * @protocol: a protocol string (e.g. "http")
+ *
+ * Checks if the protocol of a given valid URI matches @protocol.
+ *
+ * Returns: %TRUE if the protocol matches.
+ *
+ * Since: 0.10.4
+ */
+gboolean
+gst_uri_has_protocol (const gchar * uri, const gchar * protocol)
+{
+ gchar *colon;
+
+ g_return_val_if_fail (uri != NULL, FALSE);
+ g_return_val_if_fail (protocol != NULL, FALSE);
+ g_return_val_if_fail (gst_uri_is_valid (uri), FALSE);
+
+ colon = strstr (uri, ":");
+
+ if (colon == NULL)
+ return FALSE;
+
+ return (g_ascii_strncasecmp (uri, protocol, (gsize) (colon - uri)) == 0);
+}
+
+/**
+ * gst_uri_get_location:
+ * @uri: A URI string
+ *
+ * Extracts the location out of a given valid URI, ie. the protocol and "://"
+ * are stripped from the URI, which means that the location returned includes
+ * the hostname if one is specified. The returned string must be freed using
+ * g_free().
+ *
+ * Free-function: g_free
+ *
+ * Returns: (transfer full) (array zero-terminated=1): the location for this
+ * URI. Returns NULL if the URI isn't valid. If the URI does not contain
+ * a location, an empty string is returned.
+ */
+gchar *
+gst_uri_get_location (const gchar * uri)
+{
+ const gchar *colon;
+ gchar *unescaped = NULL;
+
+ g_return_val_if_fail (uri != NULL, NULL);
+ g_return_val_if_fail (gst_uri_is_valid (uri), NULL);
+
+ colon = strstr (uri, "://");
+ if (!colon)
+ return NULL;
+
+ unescaped = unescape_string (colon + 3, "/");
+
+ /* On Windows an URI might look like file:///c:/foo/bar.txt or
+ * file:///c|/foo/bar.txt (some Netscape versions) and we want to
+ * return c:/foo/bar.txt as location rather than /c:/foo/bar.txt.
+ * Can't use g_filename_from_uri() here because it will only handle the
+ * file:// protocol */
+#ifdef G_OS_WIN32
+ if (unescaped != NULL && unescaped[0] == '/' &&
+ g_ascii_isalpha (unescaped[1]) &&
+ (unescaped[2] == ':' || unescaped[2] == '|')) {
+ unescaped[2] = ':';
+ g_memmove (unescaped, unescaped + 1, strlen (unescaped + 1) + 1);
+ }
+#endif
+
+ GST_LOG ("extracted location '%s' from URI '%s'", GST_STR_NULL (unescaped),
+ uri);
+ return unescaped;
+}
+
+/**
+ * gst_uri_construct:
+ * @protocol: Protocol for URI
+ * @location: (array zero-terminated=1) (transfer none): Location for URI
+ *
+ * Constructs a URI for a given valid protocol and location.
+ *
+ * Free-function: g_free
+ *
+ * Returns: (transfer full) (array zero-terminated=1): a new string for this
+ * URI. Returns NULL if the given URI protocol is not valid, or the given
+ * location is NULL.
+ */
+gchar *
+gst_uri_construct (const gchar * protocol, const gchar * location)
+{
+ char *escaped, *proto_lowercase;
+ char *retval;
+
+ g_return_val_if_fail (gst_uri_protocol_is_valid (protocol), NULL);
+ g_return_val_if_fail (location != NULL, NULL);
+
+ proto_lowercase = g_ascii_strdown (protocol, -1);
+ escaped = escape_string (location);
+ retval = g_strdup_printf ("%s://%s", proto_lowercase, escaped);
+ g_free (escaped);
+ g_free (proto_lowercase);
+
+ return retval;
+}
+
+typedef struct
+{
+ GstURIType type;
+ const gchar *protocol;
+}
+SearchEntry;
+
+static gboolean
+search_by_entry (GstPluginFeature * feature, gpointer search_entry)
+{
+ gchar **protocols;
+ GstElementFactory *factory;
+ SearchEntry *entry = (SearchEntry *) search_entry;
+
+ if (!GST_IS_ELEMENT_FACTORY (feature))
+ return FALSE;
+ factory = GST_ELEMENT_FACTORY_CAST (feature);
+
+ if (factory->uri_type != entry->type)
+ return FALSE;
+
+ protocols = gst_element_factory_get_uri_protocols (factory);
+
+ if (protocols == NULL) {
+ g_warning ("Factory '%s' implements GstUriHandler interface but returned "
+ "no supported protocols!", gst_plugin_feature_get_name (feature));
+ return FALSE;
+ }
+
+ while (*protocols != NULL) {
+ if (g_ascii_strcasecmp (*protocols, entry->protocol) == 0)
+ return TRUE;
+ protocols++;
+ }
+ return FALSE;
+}
+
+static gint
+sort_by_rank (GstPluginFeature * first, GstPluginFeature * second)
+{
+ return gst_plugin_feature_get_rank (second) -
+ gst_plugin_feature_get_rank (first);
+}
+
+static GList *
+get_element_factories_from_uri_protocol (const GstURIType type,
+ const gchar * protocol)
+{
+ GList *possibilities;
+ SearchEntry entry;
+
+ g_return_val_if_fail (protocol, NULL);
+
+ entry.type = type;
+ entry.protocol = protocol;
+ possibilities = gst_registry_feature_filter (gst_registry_get_default (),
+ search_by_entry, FALSE, &entry);
+
+ return possibilities;
+}
+
+/**
+ * gst_uri_protocol_is_supported:
+ * @type: Whether to check for a source or a sink
+ * @protocol: Protocol that should be checked for (e.g. "http" or "smb")
+ *
+ * Checks if an element exists that supports the given URI protocol. Note
+ * that a positive return value does not imply that a subsequent call to
+ * gst_element_make_from_uri() is guaranteed to work.
+ *
+ * Returns: TRUE
+ *
+ * Since: 0.10.13
+*/
+gboolean
+gst_uri_protocol_is_supported (const GstURIType type, const gchar * protocol)
+{
+ GList *possibilities;
+
+ g_return_val_if_fail (protocol, FALSE);
+
+ possibilities = get_element_factories_from_uri_protocol (type, protocol);
+
+ if (possibilities) {
+ g_list_free (possibilities);
+ return TRUE;
+ } else
+ return FALSE;
+}
+
+/**
+ * gst_element_make_from_uri:
+ * @type: Whether to create a source or a sink
+ * @uri: URI to create an element for
+ * @elementname: (allow-none): Name of created element, can be NULL.
+ *
+ * Creates an element for handling the given URI.
+ *
+ * Returns: (transfer full): a new element or NULL if none could be created
+ */
+GstElement *
+gst_element_make_from_uri (const GstURIType type, const gchar * uri,
+ const gchar * elementname)
+{
+ GList *possibilities, *walk;
+ gchar *protocol;
+ GstElement *ret = NULL;
+
+ g_return_val_if_fail (GST_URI_TYPE_IS_VALID (type), NULL);
+ g_return_val_if_fail (gst_uri_is_valid (uri), NULL);
+
+ protocol = gst_uri_get_protocol (uri);
+ possibilities = get_element_factories_from_uri_protocol (type, protocol);
+ g_free (protocol);
+
+ if (!possibilities) {
+ GST_DEBUG ("No %s for URI '%s'", type == GST_URI_SINK ? "sink" : "source",
+ uri);
+ return NULL;
+ }
+
+ possibilities = g_list_sort (possibilities, (GCompareFunc) sort_by_rank);
+ walk = possibilities;
+ while (walk) {
+ if ((ret =
+ gst_element_factory_create (GST_ELEMENT_FACTORY_CAST (walk->data),
+ elementname)) != NULL) {
+ GstURIHandler *handler = GST_URI_HANDLER (ret);
+
+ if (gst_uri_handler_set_uri (handler, uri))
+ break;
+ gst_object_unref (ret);
+ ret = NULL;
+ }
+ walk = walk->next;
+ }
+ gst_plugin_feature_list_free (possibilities);
+
+ GST_LOG_OBJECT (ret, "created %s for URL '%s'",
+ type == GST_URI_SINK ? "sink" : "source", uri);
+ return ret;
+}
+
+/**
+ * gst_uri_handler_get_uri_type:
+ * @handler: A #GstURIHandler.
+ *
+ * Gets the type of the given URI handler
+ *
+ * Returns: the #GstURIType of the URI handler.
+ * Returns #GST_URI_UNKNOWN if the @handler isn't implemented correctly.
+ */
+guint
+gst_uri_handler_get_uri_type (GstURIHandler * handler)
+{
+ GstURIHandlerInterface *iface;
+ guint ret;
+
+ g_return_val_if_fail (GST_IS_URI_HANDLER (handler), GST_URI_UNKNOWN);
+
+ iface = GST_URI_HANDLER_GET_INTERFACE (handler);
+ g_return_val_if_fail (iface != NULL, GST_URI_UNKNOWN);
+ g_return_val_if_fail (iface->get_type != NULL, GST_URI_UNKNOWN);
+
+ ret = iface->get_type (G_OBJECT_TYPE (handler));
+ g_return_val_if_fail (GST_URI_TYPE_IS_VALID (ret), GST_URI_UNKNOWN);
+
+ return ret;
+}
+
+/**
+ * gst_uri_handler_get_protocols:
+ * @handler: A #GstURIHandler.
+ *
+ * Gets the list of protocols supported by @handler. This list may not be
+ * modified.
+ *
+ * Returns: (transfer none) (array zero-terminated=1) (element-type utf8): the
+ * supported protocols. Returns NULL if the @handler isn't implemented
+ * properly, or the @handler doesn't support any protocols.
+ */
+gchar **
+gst_uri_handler_get_protocols (GstURIHandler * handler)
+{
+ GstURIHandlerInterface *iface;
+ gchar **ret;
+
+ g_return_val_if_fail (GST_IS_URI_HANDLER (handler), NULL);
+
+ iface = GST_URI_HANDLER_GET_INTERFACE (handler);
+ g_return_val_if_fail (iface != NULL, NULL);
+ g_return_val_if_fail (iface->get_protocols != NULL, NULL);
+
+ ret = iface->get_protocols (G_OBJECT_TYPE (handler));
+ g_return_val_if_fail (ret != NULL, NULL);
+
+ return ret;
+}
+
+/**
+ * gst_uri_handler_get_uri:
+ * @handler: A #GstURIHandler
+ *
+ * Gets the currently handled URI.
+ *
+ * Returns: (transfer none): the URI currently handled by the @handler.
+ * Returns NULL if there are no URI currently handled. The
+ * returned string must not be modified or freed.
+ */
+const gchar *
+gst_uri_handler_get_uri (GstURIHandler * handler)
+{
+ GstURIHandlerInterface *iface;
+ const gchar *ret;
+
+ g_return_val_if_fail (GST_IS_URI_HANDLER (handler), NULL);
+
+ iface = GST_URI_HANDLER_GET_INTERFACE (handler);
+ g_return_val_if_fail (iface != NULL, NULL);
+ g_return_val_if_fail (iface->get_uri != NULL, NULL);
+ ret = iface->get_uri (handler);
+ if (ret != NULL)
+ g_return_val_if_fail (gst_uri_is_valid (ret), NULL);
+
+ return ret;
+}
+
+/**
+ * gst_uri_handler_set_uri:
+ * @handler: A #GstURIHandler
+ * @uri: URI to set
+ *
+ * Tries to set the URI of the given handler.
+ *
+ * Returns: TRUE if the URI was set successfully, else FALSE.
+ */
+gboolean
+gst_uri_handler_set_uri (GstURIHandler * handler, const gchar * uri)
+{
+ GstURIHandlerInterface *iface;
+ gboolean ret;
+ gchar *new_uri, *protocol, *location, *colon;
+
+ g_return_val_if_fail (GST_IS_URI_HANDLER (handler), FALSE);
+ g_return_val_if_fail (gst_uri_is_valid (uri), FALSE);
+
+ iface = GST_URI_HANDLER_GET_INTERFACE (handler);
+ g_return_val_if_fail (iface != NULL, FALSE);
+ g_return_val_if_fail (iface->set_uri != NULL, FALSE);
+
+ protocol = gst_uri_get_protocol (uri);
+
+ colon = strstr (uri, ":");
+ location = g_strdup (colon);
+
+ new_uri = g_strdup_printf ("%s%s", protocol, location);
+
+ ret = iface->set_uri (handler, uri);
+
+ g_free (new_uri);
+ g_free (location);
+ g_free (protocol);
+
+ return ret;
+}
+
+/**
+ * gst_uri_handler_new_uri:
+ * @handler: A #GstURIHandler
+ * @uri: new URI or NULL if it was unset
+ *
+ * Emits the new-uri signal for a given handler, when that handler has a new URI.
+ * This function should only be called by URI handlers themselves.
+ */
+void
+gst_uri_handler_new_uri (GstURIHandler * handler, const gchar * uri)
+{
+ g_return_if_fail (GST_IS_URI_HANDLER (handler));
+
+ g_signal_emit (handler, gst_uri_handler_signals[NEW_URI], 0, uri);
+}
+
+static gchar *
+gst_file_utils_canonicalise_path (const gchar * path)
+{
+ gchar **parts, **p, *clean_path;
+
+#ifdef G_OS_WIN32
+ {
+ GST_WARNING ("FIXME: canonicalise win32 path");
+ return g_strdup (path);
+ }
+#endif
+
+ parts = g_strsplit (path, "/", -1);
+
+ p = parts;
+ while (*p != NULL) {
+ if (strcmp (*p, ".") == 0) {
+ /* just move all following parts on top of this, incl. NUL terminator */
+ g_free (*p);
+ g_memmove (p, p + 1, (g_strv_length (p + 1) + 1) * sizeof (gchar *));
+ /* re-check the new current part again in the next iteration */
+ continue;
+ } else if (strcmp (*p, "..") == 0 && p > parts) {
+ /* just move all following parts on top of the previous part, incl.
+ * NUL terminator */
+ g_free (*(p - 1));
+ g_free (*p);
+ g_memmove (p - 1, p + 1, (g_strv_length (p + 1) + 1) * sizeof (gchar *));
+ /* re-check the new current part again in the next iteration */
+ --p;
+ continue;
+ }
+ ++p;
+ }
+ if (*path == '/') {
+ guint num_parts;
+
+ num_parts = g_strv_length (parts) + 1; /* incl. terminator */
+ parts = g_renew (gchar *, parts, num_parts + 1);
+ g_memmove (parts + 1, parts, num_parts * sizeof (gchar *));
+ parts[0] = g_strdup ("/");
+ }
+
+ clean_path = g_build_filenamev (parts);
+ g_strfreev (parts);
+ return clean_path;
+}
+
+static gboolean
+file_path_contains_relatives (const gchar * path)
+{
+ return (strstr (path, "/./") != NULL || strstr (path, "/../") != NULL ||
+ strstr (path, G_DIR_SEPARATOR_S "." G_DIR_SEPARATOR_S) != NULL ||
+ strstr (path, G_DIR_SEPARATOR_S ".." G_DIR_SEPARATOR_S) != NULL);
+}
+
+/**
+ * gst_filename_to_uri:
+ * @filename: absolute or relative file name path
+ * @error: pointer to error, or NULL
+ *
+ * Similar to g_filename_to_uri(), but attempts to handle relative file paths
+ * as well. Before converting @filename into an URI, it will be prefixed by
+ * the current working directory if it is a relative path, and then the path
+ * will be canonicalised so that it doesn't contain any './' or '../' segments.
+ *
+ * On Windows #filename should be in UTF-8 encoding.
+ *
+ * Since: 0.10.33
+ */
+gchar *
+gst_filename_to_uri (const gchar * filename, GError ** error)
+{
+ gchar *abs_location = NULL;
+ gchar *uri, *abs_clean;
+
+ g_return_val_if_fail (filename != NULL, NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ if (g_path_is_absolute (filename)) {
+ if (!file_path_contains_relatives (filename)) {
+ uri = g_filename_to_uri (filename, NULL, error);
+ goto beach;
+ }
+
+ abs_location = g_strdup (filename);
+ } else {
+ gchar *cwd;
+
+ cwd = g_get_current_dir ();
+ abs_location = g_build_filename (cwd, filename, NULL);
+ g_free (cwd);
+
+ if (!file_path_contains_relatives (abs_location)) {
+ uri = g_filename_to_uri (abs_location, NULL, error);
+ goto beach;
+ }
+ }
+
+ /* path is now absolute, but contains '.' or '..' */
+ abs_clean = gst_file_utils_canonicalise_path (abs_location);
+ GST_LOG ("'%s' -> '%s' -> '%s'", filename, abs_location, abs_clean);
+ uri = g_filename_to_uri (abs_clean, NULL, error);
+ g_free (abs_clean);
+
+beach:
+
+ g_free (abs_location);
+ GST_DEBUG ("'%s' -> '%s'", filename, uri);
+ return uri;
+}
diff --git a/gst/gsturi.h b/gst/gsturi.h
new file mode 100644
index 0000000..176d70e
--- /dev/null
+++ b/gst/gsturi.h
@@ -0,0 +1,145 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2000 Wim Taymans <wtay@chello.be>
+ *
+ * gsturi.h: Header for uri to element mappings
+ *
+ * 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_URI_H__
+#define __GST_URI_H__
+
+#include <glib.h>
+#include <gst/gstelement.h>
+#include <gst/gstpluginfeature.h>
+
+G_BEGIN_DECLS
+
+/**
+ * GstURIType:
+ * @GST_URI_UNKNOWN : The URI direction is unknown
+ * @GST_URI_SINK : The URI is a consumer.
+ * @GST_URI_SRC : The URI is a producer.
+ *
+ * The different types of URI direction.
+ */
+
+typedef enum {
+ GST_URI_UNKNOWN,
+ GST_URI_SINK,
+ GST_URI_SRC
+} GstURIType;
+
+/**
+ * GST_URI_TYPE_IS_VALID:
+ * @type: A #GstURIType
+ *
+ * Tests if the type direction is valid.
+ */
+#define GST_URI_TYPE_IS_VALID(type) ((type) == GST_URI_SRC || (type) == GST_URI_SINK)
+
+/* uri handler functions */
+#define GST_TYPE_URI_HANDLER (gst_uri_handler_get_type ())
+#define GST_URI_HANDLER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_URI_HANDLER, GstURIHandler))
+#define GST_IS_URI_HANDLER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_URI_HANDLER))
+#define GST_URI_HANDLER_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GST_TYPE_URI_HANDLER, GstURIHandlerInterface))
+
+/**
+ * GstURIHandler:
+ *
+ * Opaque #GstURIHandler structure.
+ */
+typedef struct _GstURIHandler GstURIHandler;
+typedef struct _GstURIHandlerInterface GstURIHandlerInterface;
+
+/**
+ * GstURIHandlerInterface:
+ * @parent: The parent interface type
+ * @get_type: Method to tell whether the element handles source or sink URI.
+ * @get_protocols: Method to return the list of protocols handled by the element.
+ * @get_uri: Method to return the URI currently handled by the element.
+ * @set_uri: Method to set a new URI.
+ *
+ * Any #GstElement using this interface should implement these methods.
+ */
+struct _GstURIHandlerInterface {
+ GTypeInterface parent;
+
+ /*< private >*/
+ /* signals */
+ void (* new_uri) (GstURIHandler * handler,
+ const gchar * uri);
+ /* idea for the future ?
+ gboolean (* require_password) (GstURIHandler * handler,
+ gchar ** username,
+ gchar ** password);
+ */
+
+ /* vtable */
+
+ /*< public >*/
+ /* querying capabilities */
+ GstURIType (* get_type) (GType type);
+ gchar ** (* get_protocols) (GType type);
+
+ /* using the interface */
+ const gchar * (* get_uri) (GstURIHandler * handler);
+ gboolean (* set_uri) (GstURIHandler * handler,
+ const gchar * uri);
+
+ /*< private >*/
+ /* we might want to add functions here to query features,
+ * someone with gnome-vfs knowledge go ahead */
+
+ gpointer _gst_reserved[GST_PADDING];
+};
+
+/* general URI functions */
+
+gboolean gst_uri_protocol_is_valid (const gchar * protocol);
+gboolean gst_uri_protocol_is_supported (const GstURIType type,
+ const gchar *protocol);
+gboolean gst_uri_is_valid (const gchar * uri);
+gchar * gst_uri_get_protocol (const gchar * uri);
+gboolean gst_uri_has_protocol (const gchar * uri,
+ const gchar * protocol);
+gchar * gst_uri_get_location (const gchar * uri);
+gchar * gst_uri_construct (const gchar * protocol,
+ const gchar * location);
+
+gchar * gst_filename_to_uri (const gchar * filename,
+ GError ** error);
+
+GstElement * gst_element_make_from_uri (const GstURIType type,
+ const gchar * uri,
+ const gchar * elementname);
+
+/* accessing the interface */
+GType gst_uri_handler_get_type (void);
+
+guint gst_uri_handler_get_uri_type (GstURIHandler * handler);
+gchar ** gst_uri_handler_get_protocols (GstURIHandler * handler);
+const gchar * gst_uri_handler_get_uri (GstURIHandler * handler);
+gboolean gst_uri_handler_set_uri (GstURIHandler * handler,
+ const gchar * uri);
+void gst_uri_handler_new_uri (GstURIHandler * handler,
+ const gchar * uri);
+
+G_END_DECLS
+
+#endif /* __GST_URI_H__ */
diff --git a/gst/gstutils.c b/gst/gstutils.c
new file mode 100644
index 0000000..07eac6b
--- /dev/null
+++ b/gst/gstutils.c
@@ -0,0 +1,3813 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2000 Wim Taymans <wtay@chello.be>
+ * 2002 Thomas Vander Stichele <thomas@apestaart.org>
+ *
+ * gstutils.c: Utility functions
+ *
+ * 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:gstutils
+ * @short_description: Various utility functions
+ *
+ */
+
+#include "gst_private.h"
+#include <stdio.h>
+#include <string.h>
+
+#include "gstghostpad.h"
+#include "gstutils.h"
+#include "gsterror.h"
+#include "gstinfo.h"
+#include "gstparse.h"
+#include "gstvalue.h"
+#include "gst-i18n-lib.h"
+#include "glib-compat-private.h"
+#include <math.h>
+
+/**
+ * gst_util_dump_mem:
+ * @mem: a pointer to the memory to dump
+ * @size: the size of the memory block to dump
+ *
+ * Dumps the memory block into a hex representation. Useful for debugging.
+ */
+void
+gst_util_dump_mem (const guchar * mem, guint size)
+{
+ guint i, j;
+ GString *string = g_string_sized_new (50);
+ GString *chars = g_string_sized_new (18);
+
+ i = j = 0;
+ while (i < size) {
+ if (g_ascii_isprint (mem[i]))
+ g_string_append_c (chars, mem[i]);
+ else
+ g_string_append_c (chars, '.');
+
+ g_string_append_printf (string, "%02x ", mem[i]);
+
+ j++;
+ i++;
+
+ if (j == 16 || i == size) {
+ g_print ("%08x (%p): %-48.48s %-16.16s\n", i - j, mem + i - j,
+ string->str, chars->str);
+ g_string_set_size (string, 0);
+ g_string_set_size (chars, 0);
+ j = 0;
+ }
+ }
+ g_string_free (string, TRUE);
+ g_string_free (chars, TRUE);
+}
+
+
+/**
+ * gst_util_set_value_from_string:
+ * @value: (out caller-allocates): the value to set
+ * @value_str: the string to get the value from
+ *
+ * Converts the string to the type of the value and
+ * sets the value with it.
+ *
+ * Note that this function is dangerous as it does not return any indication
+ * if the conversion worked or not.
+ */
+void
+gst_util_set_value_from_string (GValue * value, const gchar * value_str)
+{
+ gboolean res;
+
+ g_return_if_fail (value != NULL);
+ g_return_if_fail (value_str != NULL);
+
+ GST_CAT_DEBUG (GST_CAT_PARAMS, "parsing '%s' to type %s", value_str,
+ g_type_name (G_VALUE_TYPE (value)));
+
+ res = gst_value_deserialize (value, value_str);
+ if (!res && G_VALUE_TYPE (value) == G_TYPE_BOOLEAN) {
+ /* backwards compat, all booleans that fail to parse are false */
+ g_value_set_boolean (value, FALSE);
+ res = TRUE;
+ }
+ g_return_if_fail (res);
+}
+
+/**
+ * gst_util_set_object_arg:
+ * @object: the object to set the argument of
+ * @name: the name of the argument to set
+ * @value: the string value to set
+ *
+ * Convertes the string value to the type of the objects argument and
+ * sets the argument with it.
+ *
+ * Note that this function silently returns if @object has no property named
+ * @name or when @value cannot be converted to the type of the property.
+ */
+void
+gst_util_set_object_arg (GObject * object, const gchar * name,
+ const gchar * value)
+{
+ GParamSpec *pspec;
+ GType value_type;
+ GValue v = { 0, };
+
+ g_return_if_fail (G_IS_OBJECT (object));
+ g_return_if_fail (name != NULL);
+ g_return_if_fail (value != NULL);
+
+ pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (object), name);
+ if (!pspec)
+ return;
+
+ value_type = pspec->value_type;
+
+ GST_DEBUG ("pspec->flags is %d, pspec->value_type is %s",
+ pspec->flags, g_type_name (value_type));
+
+ if (!(pspec->flags & G_PARAM_WRITABLE))
+ return;
+
+ g_value_init (&v, value_type);
+
+ /* special case for element <-> xml (de)serialisation */
+ if (GST_VALUE_HOLDS_STRUCTURE (&v) && strcmp (value, "NULL") == 0) {
+ g_value_set_boxed (&v, NULL);
+ goto done;
+ }
+
+ if (!gst_value_deserialize (&v, value))
+ return;
+
+done:
+
+ g_object_set_property (object, pspec->name, &v);
+ g_value_unset (&v);
+}
+
+/* work around error C2520: conversion from unsigned __int64 to double
+ * not implemented, use signed __int64
+ *
+ * These are implemented as functions because on some platforms a 64bit int to
+ * double conversion is not defined/implemented.
+ */
+
+gdouble
+gst_util_guint64_to_gdouble (guint64 value)
+{
+ if (value & G_GINT64_CONSTANT (0x8000000000000000))
+ return (gdouble) ((gint64) value) + (gdouble) 18446744073709551616.;
+ else
+ return (gdouble) ((gint64) value);
+}
+
+guint64
+gst_util_gdouble_to_guint64 (gdouble value)
+{
+ if (value < (gdouble) 9223372036854775808.) /* 1 << 63 */
+ return ((guint64) ((gint64) value));
+
+ value -= (gdouble) 18446744073709551616.;
+ return ((guint64) ((gint64) value));
+}
+
+#ifndef HAVE_UINT128_T
+/* convenience struct for getting high and low uint32 parts of
+ * a guint64 */
+typedef union
+{
+ guint64 ll;
+ struct
+ {
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+ guint32 high, low;
+#else
+ guint32 low, high;
+#endif
+ } l;
+} GstUInt64;
+
+#if defined (__x86_64__) && defined (__GNUC__)
+static inline void
+gst_util_uint64_mul_uint64 (GstUInt64 * c1, GstUInt64 * c0, guint64 arg1,
+ guint64 arg2)
+{
+ __asm__ __volatile__ ("mulq %3":"=a" (c0->ll), "=d" (c1->ll)
+ :"a" (arg1), "g" (arg2)
+ );
+}
+#else /* defined (__x86_64__) */
+/* multiply two 64-bit unsigned ints into a 128-bit unsigned int. the high
+ * and low 64 bits of the product are placed in c1 and c0 respectively.
+ * this operation cannot overflow. */
+static inline void
+gst_util_uint64_mul_uint64 (GstUInt64 * c1, GstUInt64 * c0, guint64 arg1,
+ guint64 arg2)
+{
+ GstUInt64 a1, b0;
+ GstUInt64 v, n;
+
+ /* prepare input */
+ v.ll = arg1;
+ n.ll = arg2;
+
+ /* do 128 bits multiply
+ * nh nl
+ * * vh vl
+ * ----------
+ * a0 = vl * nl
+ * a1 = vl * nh
+ * b0 = vh * nl
+ * b1 = + vh * nh
+ * -------------------
+ * c1h c1l c0h c0l
+ *
+ * "a0" is optimized away, result is stored directly in c0. "b1" is
+ * optimized away, result is stored directly in c1.
+ */
+ c0->ll = (guint64) v.l.low * n.l.low;
+ a1.ll = (guint64) v.l.low * n.l.high;
+ b0.ll = (guint64) v.l.high * n.l.low;
+
+ /* add the high word of a0 to the low words of a1 and b0 using c1 as
+ * scrach space to capture the carry. the low word of the result becomes
+ * the final high word of c0 */
+ c1->ll = (guint64) c0->l.high + a1.l.low + b0.l.low;
+ c0->l.high = c1->l.low;
+
+ /* add the carry from the result above (found in the high word of c1) and
+ * the high words of a1 and b0 to b1, the result is c1. */
+ c1->ll = (guint64) v.l.high * n.l.high + c1->l.high + a1.l.high + b0.l.high;
+}
+#endif /* defined (__x86_64__) */
+
+#if defined (__x86_64__) && defined (__GNUC__)
+static inline guint64
+gst_util_div128_64 (GstUInt64 c1, GstUInt64 c0, guint64 denom)
+{
+ guint64 res;
+
+ __asm__ __volatile__ ("divq %3":"=a" (res)
+ :"d" (c1.ll), "a" (c0.ll), "g" (denom)
+ );
+
+ return res;
+}
+#else
+/* count leading zeros */
+static inline guint
+gst_util_clz (guint32 val)
+{
+ guint s;
+
+ s = val | (val >> 1);
+ s |= (s >> 2);
+ s |= (s >> 4);
+ s |= (s >> 8);
+ s = ~(s | (s >> 16));
+ s = s - ((s >> 1) & 0x55555555);
+ s = (s & 0x33333333) + ((s >> 2) & 0x33333333);
+ s = (s + (s >> 4)) & 0x0f0f0f0f;
+ s += (s >> 8);
+ s = (s + (s >> 16)) & 0x3f;
+
+ return s;
+}
+
+/* based on Hacker's Delight p152 */
+static inline guint64
+gst_util_div128_64 (GstUInt64 c1, GstUInt64 c0, guint64 denom)
+{
+ GstUInt64 q1, q0, rhat;
+ GstUInt64 v, cmp1, cmp2;
+ guint s;
+
+ v.ll = denom;
+
+ /* count number of leading zeroes, we know they must be in the high
+ * part of denom since denom > G_MAXUINT32. */
+ s = gst_util_clz (v.l.high);
+
+ if (s > 0) {
+ /* normalize divisor and dividend */
+ v.ll <<= s;
+ c1.ll = (c1.ll << s) | (c0.l.high >> (32 - s));
+ c0.ll <<= s;
+ }
+
+ q1.ll = c1.ll / v.l.high;
+ rhat.ll = c1.ll - q1.ll * v.l.high;
+
+ cmp1.l.high = rhat.l.low;
+ cmp1.l.low = c0.l.high;
+ cmp2.ll = q1.ll * v.l.low;
+
+ while (q1.l.high || cmp2.ll > cmp1.ll) {
+ q1.ll--;
+ rhat.ll += v.l.high;
+ if (rhat.l.high)
+ break;
+ cmp1.l.high = rhat.l.low;
+ cmp2.ll -= v.l.low;
+ }
+ c1.l.high = c1.l.low;
+ c1.l.low = c0.l.high;
+ c1.ll -= q1.ll * v.ll;
+ q0.ll = c1.ll / v.l.high;
+ rhat.ll = c1.ll - q0.ll * v.l.high;
+
+ cmp1.l.high = rhat.l.low;
+ cmp1.l.low = c0.l.low;
+ cmp2.ll = q0.ll * v.l.low;
+
+ while (q0.l.high || cmp2.ll > cmp1.ll) {
+ q0.ll--;
+ rhat.ll += v.l.high;
+ if (rhat.l.high)
+ break;
+ cmp1.l.high = rhat.l.low;
+ cmp2.ll -= v.l.low;
+ }
+ q0.l.high += q1.l.low;
+
+ return q0.ll;
+}
+#endif /* defined (__GNUC__) */
+
+/* This always gives the correct result because:
+ * a) val <= G_MAXUINT64-1
+ * b) (c0,c1) <= G_MAXUINT64 * (G_MAXUINT64-1)
+ * or
+ * (c0,c1) == G_MAXUINT64 * G_MAXUINT64 and denom < G_MAXUINT64
+ * (note: num==denom case is handled by short path)
+ * This means that (c0,c1) either has enough space for val
+ * or that the overall result will overflow anyway.
+ */
+
+/* add correction with carry */
+#define CORRECT(c0,c1,val) \
+ if (val) { \
+ if (G_MAXUINT64 - c0.ll < val) { \
+ if (G_UNLIKELY (c1.ll == G_MAXUINT64)) \
+ /* overflow */ \
+ return G_MAXUINT64; \
+ c1.ll++; \
+ } \
+ c0.ll += val; \
+ }
+
+static guint64
+gst_util_uint64_scale_uint64_unchecked (guint64 val, guint64 num,
+ guint64 denom, guint64 correct)
+{
+ GstUInt64 c1, c0;
+
+ /* compute 128-bit numerator product */
+ gst_util_uint64_mul_uint64 (&c1, &c0, val, num);
+
+ /* perform rounding correction */
+ CORRECT (c0, c1, correct);
+
+ /* high word as big as or bigger than denom --> overflow */
+ if (G_UNLIKELY (c1.ll >= denom))
+ return G_MAXUINT64;
+
+ /* compute quotient, fits in 64 bits */
+ return gst_util_div128_64 (c1, c0, denom);
+}
+#else
+
+#define GST_MAXUINT128 ((__uint128_t) -1)
+static guint64
+gst_util_uint64_scale_uint64_unchecked (guint64 val, guint64 num,
+ guint64 denom, guint64 correct)
+{
+ __uint128_t tmp;
+
+ /* Calculate val * num */
+ tmp = ((__uint128_t) val) * ((__uint128_t) num);
+
+ /* overflow checks */
+ if (G_UNLIKELY (GST_MAXUINT128 - correct < tmp))
+ return G_MAXUINT64;
+
+ /* perform rounding correction */
+ tmp += correct;
+
+ /* Divide by denom */
+ tmp /= denom;
+
+ /* if larger than G_MAXUINT64 --> overflow */
+ if (G_UNLIKELY (tmp > G_MAXUINT64))
+ return G_MAXUINT64;
+
+ /* compute quotient, fits in 64 bits */
+ return (guint64) tmp;
+}
+
+#endif
+
+#if !defined (__x86_64__) && !defined (HAVE_UINT128_T)
+static inline void
+gst_util_uint64_mul_uint32 (GstUInt64 * c1, GstUInt64 * c0, guint64 arg1,
+ guint32 arg2)
+{
+ GstUInt64 a;
+
+ a.ll = arg1;
+
+ c0->ll = (guint64) a.l.low * arg2;
+ c1->ll = (guint64) a.l.high * arg2 + c0->l.high;
+ c0->l.high = 0;
+}
+
+/* divide a 96-bit unsigned int by a 32-bit unsigned int when we know the
+ * quotient fits into 64 bits. the high 64 bits and low 32 bits of the
+ * numerator are expected in c1 and c0 respectively. */
+static inline guint64
+gst_util_div96_32 (guint64 c1, guint64 c0, guint32 denom)
+{
+ c0 += (c1 % denom) << 32;
+ return ((c1 / denom) << 32) + (c0 / denom);
+}
+
+static inline guint64
+gst_util_uint64_scale_uint32_unchecked (guint64 val, guint32 num,
+ guint32 denom, guint32 correct)
+{
+ GstUInt64 c1, c0;
+
+ /* compute 96-bit numerator product */
+ gst_util_uint64_mul_uint32 (&c1, &c0, val, num);
+
+ /* condition numerator based on rounding mode */
+ CORRECT (c0, c1, correct);
+
+ /* high 32 bits as big as or bigger than denom --> overflow */
+ if (G_UNLIKELY (c1.l.high >= denom))
+ return G_MAXUINT64;
+
+ /* compute quotient, fits in 64 bits */
+ return gst_util_div96_32 (c1.ll, c0.ll, denom);
+}
+#endif
+
+/* the guts of the gst_util_uint64_scale() variants */
+static guint64
+_gst_util_uint64_scale (guint64 val, guint64 num, guint64 denom,
+ guint64 correct)
+{
+ g_return_val_if_fail (denom != 0, G_MAXUINT64);
+
+ if (G_UNLIKELY (num == 0))
+ return 0;
+
+ if (G_UNLIKELY (num == denom))
+ return val;
+
+ /* on 64bits we always use a full 128bits multiply/division */
+#if !defined (__x86_64__) && !defined (HAVE_UINT128_T)
+ /* denom is low --> try to use 96 bit muldiv */
+ if (G_LIKELY (denom <= G_MAXUINT32)) {
+ /* num is low --> use 96 bit muldiv */
+ if (G_LIKELY (num <= G_MAXUINT32))
+ return gst_util_uint64_scale_uint32_unchecked (val, (guint32) num,
+ (guint32) denom, correct);
+
+ /* num is high but val is low --> swap and use 96-bit muldiv */
+ if (G_LIKELY (val <= G_MAXUINT32))
+ return gst_util_uint64_scale_uint32_unchecked (num, (guint32) val,
+ (guint32) denom, correct);
+ }
+#endif /* !defined (__x86_64__) && !defined (HAVE_UINT128_T) */
+
+ /* val is high and num is high --> use 128-bit muldiv */
+ return gst_util_uint64_scale_uint64_unchecked (val, num, denom, correct);
+}
+
+/**
+ * gst_util_uint64_scale:
+ * @val: the number to scale
+ * @num: the numerator of the scale ratio
+ * @denom: the denominator of the scale ratio
+ *
+ * Scale @val by the rational number @num / @denom, avoiding overflows and
+ * underflows and without loss of precision.
+ *
+ * This function can potentially be very slow if val and num are both
+ * greater than G_MAXUINT32.
+ *
+ * Returns: @val * @num / @denom. In the case of an overflow, this
+ * function returns G_MAXUINT64. If the result is not exactly
+ * representable as an integer it is truncated. See also
+ * gst_util_uint64_scale_round(), gst_util_uint64_scale_ceil(),
+ * gst_util_uint64_scale_int(), gst_util_uint64_scale_int_round(),
+ * gst_util_uint64_scale_int_ceil().
+ */
+guint64
+gst_util_uint64_scale (guint64 val, guint64 num, guint64 denom)
+{
+ return _gst_util_uint64_scale (val, num, denom, 0);
+}
+
+/**
+ * gst_util_uint64_scale_round:
+ * @val: the number to scale
+ * @num: the numerator of the scale ratio
+ * @denom: the denominator of the scale ratio
+ *
+ * Scale @val by the rational number @num / @denom, avoiding overflows and
+ * underflows and without loss of precision.
+ *
+ * This function can potentially be very slow if val and num are both
+ * greater than G_MAXUINT32.
+ *
+ * Returns: @val * @num / @denom. In the case of an overflow, this
+ * function returns G_MAXUINT64. If the result is not exactly
+ * representable as an integer, it is rounded to the nearest integer
+ * (half-way cases are rounded up). See also gst_util_uint64_scale(),
+ * gst_util_uint64_scale_ceil(), gst_util_uint64_scale_int(),
+ * gst_util_uint64_scale_int_round(), gst_util_uint64_scale_int_ceil().
+ */
+guint64
+gst_util_uint64_scale_round (guint64 val, guint64 num, guint64 denom)
+{
+ return _gst_util_uint64_scale (val, num, denom, denom >> 1);
+}
+
+/**
+ * gst_util_uint64_scale_ceil:
+ * @val: the number to scale
+ * @num: the numerator of the scale ratio
+ * @denom: the denominator of the scale ratio
+ *
+ * Scale @val by the rational number @num / @denom, avoiding overflows and
+ * underflows and without loss of precision.
+ *
+ * This function can potentially be very slow if val and num are both
+ * greater than G_MAXUINT32.
+ *
+ * Returns: @val * @num / @denom. In the case of an overflow, this
+ * function returns G_MAXUINT64. If the result is not exactly
+ * representable as an integer, it is rounded up. See also
+ * gst_util_uint64_scale(), gst_util_uint64_scale_round(),
+ * gst_util_uint64_scale_int(), gst_util_uint64_scale_int_round(),
+ * gst_util_uint64_scale_int_ceil().
+ */
+guint64
+gst_util_uint64_scale_ceil (guint64 val, guint64 num, guint64 denom)
+{
+ return _gst_util_uint64_scale (val, num, denom, denom - 1);
+}
+
+/* the guts of the gst_util_uint64_scale_int() variants */
+static guint64
+_gst_util_uint64_scale_int (guint64 val, gint num, gint denom, gint correct)
+{
+ g_return_val_if_fail (denom > 0, G_MAXUINT64);
+ g_return_val_if_fail (num >= 0, G_MAXUINT64);
+
+ if (G_UNLIKELY (num == 0))
+ return 0;
+
+ if (G_UNLIKELY (num == denom))
+ return val;
+
+ if (val <= G_MAXUINT32) {
+ /* simple case. num and denom are not negative so casts are OK. when
+ * not truncating, the additions to the numerator cannot overflow
+ * because val*num <= G_MAXUINT32 * G_MAXINT32 < G_MAXUINT64 -
+ * G_MAXINT32, so there's room to add another gint32. */
+ val *= (guint64) num;
+ /* add rounding correction */
+ val += correct;
+
+ return val / (guint64) denom;
+ }
+#if !defined (__x86_64__) && !defined (HAVE_UINT128_T)
+ /* num and denom are not negative so casts are OK */
+ return gst_util_uint64_scale_uint32_unchecked (val, (guint32) num,
+ (guint32) denom, (guint32) correct);
+#else
+ /* always use full 128bits scale */
+ return gst_util_uint64_scale_uint64_unchecked (val, num, denom, correct);
+#endif
+}
+
+/**
+ * gst_util_uint64_scale_int:
+ * @val: guint64 (such as a #GstClockTime) to scale.
+ * @num: numerator of the scale factor.
+ * @denom: denominator of the scale factor.
+ *
+ * Scale @val by the rational number @num / @denom, avoiding overflows and
+ * underflows and without loss of precision. @num must be non-negative and
+ * @denom must be positive.
+ *
+ * Returns: @val * @num / @denom. In the case of an overflow, this
+ * function returns G_MAXUINT64. If the result is not exactly
+ * representable as an integer, it is truncated. See also
+ * gst_util_uint64_scale_int_round(), gst_util_uint64_scale_int_ceil(),
+ * gst_util_uint64_scale(), gst_util_uint64_scale_round(),
+ * gst_util_uint64_scale_ceil().
+ */
+guint64
+gst_util_uint64_scale_int (guint64 val, gint num, gint denom)
+{
+ return _gst_util_uint64_scale_int (val, num, denom, 0);
+}
+
+/**
+ * gst_util_uint64_scale_int_round:
+ * @val: guint64 (such as a #GstClockTime) to scale.
+ * @num: numerator of the scale factor.
+ * @denom: denominator of the scale factor.
+ *
+ * Scale @val by the rational number @num / @denom, avoiding overflows and
+ * underflows and without loss of precision. @num must be non-negative and
+ * @denom must be positive.
+ *
+ * Returns: @val * @num / @denom. In the case of an overflow, this
+ * function returns G_MAXUINT64. If the result is not exactly
+ * representable as an integer, it is rounded to the nearest integer
+ * (half-way cases are rounded up). See also gst_util_uint64_scale_int(),
+ * gst_util_uint64_scale_int_ceil(), gst_util_uint64_scale(),
+ * gst_util_uint64_scale_round(), gst_util_uint64_scale_ceil().
+ */
+guint64
+gst_util_uint64_scale_int_round (guint64 val, gint num, gint denom)
+{
+ /* we can use a shift to divide by 2 because denom is required to be
+ * positive. */
+ return _gst_util_uint64_scale_int (val, num, denom, denom >> 1);
+}
+
+/**
+ * gst_util_uint64_scale_int_ceil:
+ * @val: guint64 (such as a #GstClockTime) to scale.
+ * @num: numerator of the scale factor.
+ * @denom: denominator of the scale factor.
+ *
+ * Scale @val by the rational number @num / @denom, avoiding overflows and
+ * underflows and without loss of precision. @num must be non-negative and
+ * @denom must be positive.
+ *
+ * Returns: @val * @num / @denom. In the case of an overflow, this
+ * function returns G_MAXUINT64. If the result is not exactly
+ * representable as an integer, it is rounded up. See also
+ * gst_util_uint64_scale_int(), gst_util_uint64_scale_int_round(),
+ * gst_util_uint64_scale(), gst_util_uint64_scale_round(),
+ * gst_util_uint64_scale_ceil().
+ */
+guint64
+gst_util_uint64_scale_int_ceil (guint64 val, gint num, gint denom)
+{
+ return _gst_util_uint64_scale_int (val, num, denom, denom - 1);
+}
+
+/**
+ * gst_util_seqnum_next:
+ *
+ * Return a constantly incrementing sequence number.
+ *
+ * This function is used internally to GStreamer to be able to determine which
+ * events and messages are "the same". For example, elements may set the seqnum
+ * on a segment-done message to be the same as that of the last seek event, to
+ * indicate that event and the message correspond to the same segment.
+ *
+ * Returns: A constantly incrementing 32-bit unsigned integer, which might
+ * overflow back to 0 at some point. Use gst_util_seqnum_compare() to make sure
+ * you handle wraparound correctly.
+ *
+ * Since: 0.10.22
+ */
+guint32
+gst_util_seqnum_next (void)
+{
+ static gint counter = 0;
+ return G_ATOMIC_INT_ADD (&counter, 1);
+}
+
+/**
+ * gst_util_seqnum_compare:
+ * @s1: A sequence number.
+ * @s2: Another sequence number.
+ *
+ * Compare two sequence numbers, handling wraparound.
+ *
+ * The current implementation just returns (gint32)(@s1 - @s2).
+ *
+ * Returns: A negative number if @s1 is before @s2, 0 if they are equal, or a
+ * positive number if @s1 is after @s2.
+ *
+ * Since: 0.10.22
+ */
+gint32
+gst_util_seqnum_compare (guint32 s1, guint32 s2)
+{
+ return (gint32) (s1 - s2);
+}
+
+/* -----------------------------------------------------
+ *
+ * The following code will be moved out of the main
+ * gstreamer library someday.
+ */
+
+#include "gstpad.h"
+
+static void
+string_append_indent (GString * str, gint count)
+{
+ gint xx;
+
+ for (xx = 0; xx < count; xx++)
+ g_string_append_c (str, ' ');
+}
+
+/**
+ * gst_print_pad_caps:
+ * @buf: the buffer to print the caps in
+ * @indent: initial indentation
+ * @pad: (transfer none): the pad to print the caps from
+ *
+ * Write the pad capabilities in a human readable format into
+ * the given GString.
+ */
+void
+gst_print_pad_caps (GString * buf, gint indent, GstPad * pad)
+{
+ GstCaps *caps;
+
+ caps = gst_pad_get_current_caps (pad);
+
+ if (!caps) {
+ string_append_indent (buf, indent);
+ g_string_printf (buf, "%s:%s has no capabilities",
+ GST_DEBUG_PAD_NAME (pad));
+ } else {
+ char *s;
+
+ s = gst_caps_to_string (caps);
+ g_string_append (buf, s);
+ g_free (s);
+
+ gst_caps_unref (caps);
+ }
+}
+
+/**
+ * gst_print_element_args:
+ * @buf: the buffer to print the args in
+ * @indent: initial indentation
+ * @element: (transfer none): the element to print the args of
+ *
+ * Print the element argument in a human readable format in the given
+ * GString.
+ */
+void
+gst_print_element_args (GString * buf, gint indent, GstElement * element)
+{
+ guint width;
+ GValue value = { 0, }; /* the important thing is that value.type = 0 */
+ gchar *str = NULL;
+ GParamSpec *spec, **specs, **walk;
+
+ specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (element), NULL);
+
+ width = 0;
+ for (walk = specs; *walk; walk++) {
+ spec = *walk;
+ if (width < strlen (spec->name))
+ width = strlen (spec->name);
+ }
+
+ for (walk = specs; *walk; walk++) {
+ spec = *walk;
+
+ if (spec->flags & G_PARAM_READABLE) {
+ g_value_init (&value, spec->value_type);
+ g_object_get_property (G_OBJECT (element), spec->name, &value);
+ str = g_strdup_value_contents (&value);
+ g_value_unset (&value);
+ } else {
+ str = g_strdup ("Parameter not readable.");
+ }
+
+ string_append_indent (buf, indent);
+ g_string_append (buf, spec->name);
+ string_append_indent (buf, 2 + width - strlen (spec->name));
+ g_string_append (buf, str);
+ g_string_append_c (buf, '\n');
+
+ g_free (str);
+ }
+
+ g_free (specs);
+}
+
+/**
+ * gst_element_create_all_pads:
+ * @element: (transfer none): a #GstElement to create pads for
+ *
+ * Creates a pad for each pad template that is always available.
+ * This function is only useful during object initialization of
+ * subclasses of #GstElement.
+ */
+void
+gst_element_create_all_pads (GstElement * element)
+{
+ GList *padlist;
+
+ /* FIXME: lock element */
+
+ padlist =
+ gst_element_class_get_pad_template_list (GST_ELEMENT_CLASS
+ (G_OBJECT_GET_CLASS (element)));
+
+ while (padlist) {
+ GstPadTemplate *padtempl = (GstPadTemplate *) padlist->data;
+
+ if (padtempl->presence == GST_PAD_ALWAYS) {
+ GstPad *pad;
+
+ pad = gst_pad_new_from_template (padtempl, padtempl->name_template);
+
+ gst_element_add_pad (element, pad);
+ }
+ padlist = padlist->next;
+ }
+}
+
+/**
+ * gst_element_get_compatible_pad_template:
+ * @element: (transfer none): a #GstElement to get a compatible pad template for
+ * @compattempl: (transfer none): the #GstPadTemplate to find a compatible
+ * template for
+ *
+ * Retrieves a pad template from @element that is compatible with @compattempl.
+ * Pads from compatible templates can be linked together.
+ *
+ * Returns: (transfer none): a compatible #GstPadTemplate, or NULL if none
+ * was found. No unreferencing is necessary.
+ */
+GstPadTemplate *
+gst_element_get_compatible_pad_template (GstElement * element,
+ GstPadTemplate * compattempl)
+{
+ GstPadTemplate *newtempl = NULL;
+ GList *padlist;
+ GstElementClass *class;
+ gboolean compatible;
+
+ g_return_val_if_fail (element != NULL, NULL);
+ g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
+ g_return_val_if_fail (compattempl != NULL, NULL);
+
+ class = GST_ELEMENT_GET_CLASS (element);
+
+ padlist = gst_element_class_get_pad_template_list (class);
+
+ GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS,
+ "Looking for a suitable pad template in %s out of %d templates...",
+ GST_ELEMENT_NAME (element), g_list_length (padlist));
+
+ while (padlist) {
+ GstPadTemplate *padtempl = (GstPadTemplate *) padlist->data;
+
+ /* Ignore name
+ * Ignore presence
+ * Check direction (must be opposite)
+ * Check caps
+ */
+ GST_CAT_LOG (GST_CAT_CAPS,
+ "checking pad template %s", padtempl->name_template);
+ if (padtempl->direction != compattempl->direction) {
+ GST_CAT_DEBUG (GST_CAT_CAPS,
+ "compatible direction: found %s pad template \"%s\"",
+ padtempl->direction == GST_PAD_SRC ? "src" : "sink",
+ padtempl->name_template);
+
+ GST_CAT_DEBUG (GST_CAT_CAPS,
+ "intersecting %" GST_PTR_FORMAT, GST_PAD_TEMPLATE_CAPS (compattempl));
+ GST_CAT_DEBUG (GST_CAT_CAPS,
+ "..and %" GST_PTR_FORMAT, GST_PAD_TEMPLATE_CAPS (padtempl));
+
+ compatible = gst_caps_can_intersect (GST_PAD_TEMPLATE_CAPS (compattempl),
+ GST_PAD_TEMPLATE_CAPS (padtempl));
+
+ GST_CAT_DEBUG (GST_CAT_CAPS, "caps are %scompatible",
+ (compatible ? "" : "not "));
+
+ if (compatible) {
+ newtempl = padtempl;
+ break;
+ }
+ }
+
+ padlist = g_list_next (padlist);
+ }
+ if (newtempl)
+ GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS,
+ "Returning new pad template %p", newtempl);
+ else
+ GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "No compatible pad template found");
+
+ return newtempl;
+}
+
+/**
+ * gst_element_get_pad_from_template:
+ * @element: (transfer none): a #GstElement.
+ * @templ: (transfer none): a #GstPadTemplate belonging to @element.
+ *
+ * Gets a pad from @element described by @templ. If the presence of @templ is
+ * #GST_PAD_REQUEST, requests a new pad. Can return %NULL for #GST_PAD_SOMETIMES
+ * templates.
+ *
+ * Returns: (transfer full): the #GstPad, or NULL if one could not be found
+ * or created.
+ */
+static GstPad *
+gst_element_get_pad_from_template (GstElement * element, GstPadTemplate * templ)
+{
+ GstPad *ret = NULL;
+ GstPadPresence presence;
+
+ /* If this function is ever exported, we need check the validity of `element'
+ * and `templ', and to make sure the template actually belongs to the
+ * element. */
+
+ presence = GST_PAD_TEMPLATE_PRESENCE (templ);
+
+ switch (presence) {
+ case GST_PAD_ALWAYS:
+ case GST_PAD_SOMETIMES:
+ ret = gst_element_get_static_pad (element, templ->name_template);
+ if (!ret && presence == GST_PAD_ALWAYS)
+ g_warning
+ ("Element %s has an ALWAYS template %s, but no pad of the same name",
+ GST_OBJECT_NAME (element), templ->name_template);
+ break;
+
+ case GST_PAD_REQUEST:
+ ret = gst_element_request_pad (element, templ, NULL, NULL);
+ break;
+ }
+
+ return ret;
+}
+
+/*
+ * gst_element_request_compatible_pad:
+ * @element: a #GstElement.
+ * @templ: the #GstPadTemplate to which the new pad should be able to link.
+ *
+ * Requests a pad from @element. The returned pad should be unlinked and
+ * compatible with @templ. Might return an existing pad, or request a new one.
+ *
+ * Returns: a #GstPad, or %NULL if one could not be found or created.
+ */
+static GstPad *
+gst_element_request_compatible_pad (GstElement * element,
+ GstPadTemplate * templ)
+{
+ GstPadTemplate *templ_new;
+ GstPad *pad = NULL;
+
+ g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
+ g_return_val_if_fail (GST_IS_PAD_TEMPLATE (templ), NULL);
+
+ /* FIXME: should really loop through the templates, testing each for
+ * compatibility and pad availability. */
+ templ_new = gst_element_get_compatible_pad_template (element, templ);
+ if (templ_new)
+ pad = gst_element_get_pad_from_template (element, templ_new);
+
+ /* This can happen for non-request pads. No need to unref. */
+ if (pad && GST_PAD_PEER (pad))
+ pad = NULL;
+
+ return pad;
+}
+
+/*
+ * Checks if the source pad and the sink pad can be linked.
+ * Both @srcpad and @sinkpad must be unlinked and have a parent.
+ */
+static gboolean
+gst_pad_check_link (GstPad * srcpad, GstPad * sinkpad)
+{
+ /* FIXME This function is gross. It's almost a direct copy of
+ * gst_pad_link_filtered(). Any decent programmer would attempt
+ * to merge the two functions, which I will do some day. --ds
+ */
+
+ /* generic checks */
+ g_return_val_if_fail (GST_IS_PAD (srcpad), FALSE);
+ g_return_val_if_fail (GST_IS_PAD (sinkpad), FALSE);
+
+ GST_CAT_INFO (GST_CAT_PADS, "trying to link %s:%s and %s:%s",
+ GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad));
+
+ /* FIXME: shouldn't we convert this to g_return_val_if_fail? */
+ if (GST_PAD_PEER (srcpad) != NULL) {
+ GST_CAT_INFO (GST_CAT_PADS, "Source pad %s:%s has a peer, failed",
+ GST_DEBUG_PAD_NAME (srcpad));
+ return FALSE;
+ }
+ if (GST_PAD_PEER (sinkpad) != NULL) {
+ GST_CAT_INFO (GST_CAT_PADS, "Sink pad %s:%s has a peer, failed",
+ GST_DEBUG_PAD_NAME (sinkpad));
+ return FALSE;
+ }
+ if (!GST_PAD_IS_SRC (srcpad)) {
+ GST_CAT_INFO (GST_CAT_PADS, "Src pad %s:%s is not source pad, failed",
+ GST_DEBUG_PAD_NAME (srcpad));
+ return FALSE;
+ }
+ if (!GST_PAD_IS_SINK (sinkpad)) {
+ GST_CAT_INFO (GST_CAT_PADS, "Sink pad %s:%s is not sink pad, failed",
+ GST_DEBUG_PAD_NAME (sinkpad));
+ return FALSE;
+ }
+ if (GST_PAD_PARENT (srcpad) == NULL) {
+ GST_CAT_INFO (GST_CAT_PADS, "Src pad %s:%s has no parent, failed",
+ GST_DEBUG_PAD_NAME (srcpad));
+ return FALSE;
+ }
+ if (GST_PAD_PARENT (sinkpad) == NULL) {
+ GST_CAT_INFO (GST_CAT_PADS, "Sink pad %s:%s has no parent, failed",
+ GST_DEBUG_PAD_NAME (srcpad));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ * gst_element_get_compatible_pad:
+ * @element: (transfer none): a #GstElement in which the pad should be found.
+ * @pad: (transfer none): the #GstPad to find a compatible one for.
+ * @caps: the #GstCaps to use as a filter.
+ *
+ * Looks for an unlinked pad to which the given pad can link. It is not
+ * guaranteed that linking the pads will work, though it should work in most
+ * cases.
+ *
+ * This function will first attempt to find a compatible unlinked ALWAYS pad,
+ * and if none can be found, it will request a compatible REQUEST pad by looking
+ * at the templates of @element.
+ *
+ * Returns: (transfer full): the #GstPad to which a link can be made, or %NULL
+ * if one cannot be found. gst_object_unref() after usage.
+ */
+GstPad *
+gst_element_get_compatible_pad (GstElement * element, GstPad * pad,
+ const GstCaps * caps)
+{
+ GstIterator *pads;
+ GstPadTemplate *templ;
+ GstCaps *templcaps;
+ GstPad *foundpad = NULL;
+ gboolean done;
+ GValue padptr = { 0, };
+
+ g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
+ g_return_val_if_fail (GST_IS_PAD (pad), NULL);
+
+ GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS,
+ "finding pad in %s compatible with %s:%s",
+ GST_ELEMENT_NAME (element), GST_DEBUG_PAD_NAME (pad));
+
+ g_return_val_if_fail (GST_PAD_PEER (pad) == NULL, NULL);
+
+ done = FALSE;
+
+ /* try to get an existing unlinked pad */
+ if (GST_PAD_IS_SRC (pad)) {
+ pads = gst_element_iterate_sink_pads (element);
+ } else if (GST_PAD_IS_SINK (pad)) {
+ pads = gst_element_iterate_src_pads (element);
+ } else {
+ pads = gst_element_iterate_pads (element);
+ }
+
+ while (!done) {
+ switch (gst_iterator_next (pads, &padptr)) {
+ case GST_ITERATOR_OK:
+ {
+ GstPad *peer;
+ GstPad *current;
+ GstPad *srcpad;
+ GstPad *sinkpad;
+
+ current = g_value_get_object (&padptr);
+
+ GST_CAT_LOG (GST_CAT_ELEMENT_PADS, "examining pad %s:%s",
+ GST_DEBUG_PAD_NAME (current));
+
+ if (GST_PAD_IS_SRC (current)) {
+ srcpad = current;
+ sinkpad = pad;
+ } else {
+ srcpad = pad;
+ sinkpad = current;
+ }
+ peer = gst_pad_get_peer (current);
+
+ if (peer == NULL && gst_pad_check_link (srcpad, sinkpad)) {
+ GstCaps *temp, *intersection;
+ gboolean compatible;
+
+ /* Now check if the two pads' caps are compatible */
+ temp = gst_pad_get_caps (pad, NULL);
+ if (caps) {
+ intersection = gst_caps_intersect (temp, caps);
+ gst_caps_unref (temp);
+ } else {
+ intersection = temp;
+ }
+
+ temp = gst_pad_get_caps (current, NULL);
+ compatible = gst_caps_can_intersect (temp, intersection);
+ gst_caps_unref (temp);
+ gst_caps_unref (intersection);
+
+ if (compatible) {
+ GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS,
+ "found existing unlinked compatible pad %s:%s",
+ GST_DEBUG_PAD_NAME (current));
+ gst_iterator_free (pads);
+
+ current = gst_object_ref (current);
+ g_value_unset (&padptr);
+
+ return current;
+ } else {
+ GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "incompatible pads");
+ }
+ } else {
+ GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS,
+ "already linked or cannot be linked (peer = %p)", peer);
+ }
+ GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "unreffing pads");
+
+ g_value_reset (&padptr);
+ if (peer)
+ gst_object_unref (peer);
+ break;
+ }
+ case GST_ITERATOR_DONE:
+ done = TRUE;
+ break;
+ case GST_ITERATOR_RESYNC:
+ gst_iterator_resync (pads);
+ break;
+ case GST_ITERATOR_ERROR:
+ g_assert_not_reached ();
+ break;
+ }
+ }
+ g_value_unset (&padptr);
+ gst_iterator_free (pads);
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_ELEMENT_PADS, element,
+ "Could not find a compatible unlinked always pad to link to %s:%s, now checking request pads",
+ GST_DEBUG_PAD_NAME (pad));
+
+ /* try to create a new one */
+ /* requesting is a little crazy, we need a template. Let's create one */
+ /* FIXME: why not gst_pad_get_pad_template (pad); */
+ templcaps = gst_pad_get_caps (pad, NULL);
+ templ = gst_pad_template_new ((gchar *) GST_PAD_NAME (pad),
+ GST_PAD_DIRECTION (pad), GST_PAD_ALWAYS, templcaps);
+ gst_caps_unref (templcaps);
+
+ foundpad = gst_element_request_compatible_pad (element, templ);
+ gst_object_unref (templ);
+
+ if (foundpad) {
+ GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS,
+ "found existing request pad %s:%s", GST_DEBUG_PAD_NAME (foundpad));
+ return foundpad;
+ }
+
+ GST_CAT_INFO_OBJECT (GST_CAT_ELEMENT_PADS, element,
+ "Could not find a compatible pad to link to %s:%s",
+ GST_DEBUG_PAD_NAME (pad));
+ return NULL;
+}
+
+/**
+ * gst_element_state_get_name:
+ * @state: a #GstState to get the name of.
+ *
+ * Gets a string representing the given state.
+ *
+ * Returns: (transfer none): a string with the name of the state.
+ */
+const gchar *
+gst_element_state_get_name (GstState state)
+{
+ switch (state) {
+ case GST_STATE_VOID_PENDING:
+ return "VOID_PENDING";
+ case GST_STATE_NULL:
+ return "NULL";
+ case GST_STATE_READY:
+ return "READY";
+ case GST_STATE_PLAYING:
+ return "PLAYING";
+ case GST_STATE_PAUSED:
+ return "PAUSED";
+ default:
+ /* This is a memory leak */
+ return g_strdup_printf ("UNKNOWN!(%d)", state);
+ }
+}
+
+/**
+ * gst_element_state_change_return_get_name:
+ * @state_ret: a #GstStateChangeReturn to get the name of.
+ *
+ * Gets a string representing the given state change result.
+ *
+ * Returns: (transfer none): a string with the name of the state
+ * result.
+ *
+ * Since: 0.10.11
+ */
+const gchar *
+gst_element_state_change_return_get_name (GstStateChangeReturn state_ret)
+{
+ switch (state_ret) {
+ case GST_STATE_CHANGE_FAILURE:
+ return "FAILURE";
+ case GST_STATE_CHANGE_SUCCESS:
+ return "SUCCESS";
+ case GST_STATE_CHANGE_ASYNC:
+ return "ASYNC";
+ case GST_STATE_CHANGE_NO_PREROLL:
+ return "NO PREROLL";
+ default:
+ /* This is a memory leak */
+ return g_strdup_printf ("UNKNOWN!(%d)", state_ret);
+ }
+}
+
+
+static gboolean
+gst_element_factory_can_accept_all_caps_in_direction (GstElementFactory *
+ factory, const GstCaps * caps, GstPadDirection direction)
+{
+ GList *templates;
+
+ g_return_val_if_fail (factory != NULL, FALSE);
+ g_return_val_if_fail (caps != NULL, FALSE);
+
+ templates = factory->staticpadtemplates;
+
+ while (templates) {
+ GstStaticPadTemplate *template = (GstStaticPadTemplate *) templates->data;
+
+ if (template->direction == direction) {
+ GstCaps *templcaps = gst_static_caps_get (&template->static_caps);
+
+ if (gst_caps_is_always_compatible (caps, templcaps)) {
+ gst_caps_unref (templcaps);
+ return TRUE;
+ }
+ gst_caps_unref (templcaps);
+ }
+ templates = g_list_next (templates);
+ }
+
+ return FALSE;
+}
+
+static gboolean
+gst_element_factory_can_accept_any_caps_in_direction (GstElementFactory *
+ factory, const GstCaps * caps, GstPadDirection direction)
+{
+ GList *templates;
+
+ g_return_val_if_fail (factory != NULL, FALSE);
+ g_return_val_if_fail (caps != NULL, FALSE);
+
+ templates = factory->staticpadtemplates;
+
+ while (templates) {
+ GstStaticPadTemplate *template = (GstStaticPadTemplate *) templates->data;
+
+ if (template->direction == direction) {
+ GstCaps *templcaps = gst_static_caps_get (&template->static_caps);
+
+ if (gst_caps_can_intersect (caps, templcaps)) {
+ gst_caps_unref (templcaps);
+ return TRUE;
+ }
+ gst_caps_unref (templcaps);
+ }
+ templates = g_list_next (templates);
+ }
+
+ return FALSE;
+}
+
+/**
+ * gst_element_factory_can_sink_all_caps:
+ * @factory: factory to query
+ * @caps: the caps to check
+ *
+ * Checks if the factory can sink all possible capabilities.
+ *
+ * Returns: %TRUE if the caps are fully compatible.
+ *
+ * Since: 0.10.33
+ */
+gboolean
+gst_element_factory_can_sink_all_caps (GstElementFactory * factory,
+ const GstCaps * caps)
+{
+ return gst_element_factory_can_accept_all_caps_in_direction (factory, caps,
+ GST_PAD_SINK);
+}
+
+/**
+ * gst_element_factory_can_src_all_caps:
+ * @factory: factory to query
+ * @caps: the caps to check
+ *
+ * Checks if the factory can src all possible capabilities.
+ *
+ * Returns: %TRUE if the caps are fully compatible.
+ *
+ * Since: 0.10.33
+ */
+gboolean
+gst_element_factory_can_src_all_caps (GstElementFactory * factory,
+ const GstCaps * caps)
+{
+ return gst_element_factory_can_accept_all_caps_in_direction (factory, caps,
+ GST_PAD_SRC);
+}
+
+/**
+ * gst_element_factory_can_sink_any_caps:
+ * @factory: factory to query
+ * @caps: the caps to check
+ *
+ * Checks if the factory can sink any possible capability.
+ *
+ * Returns: %TRUE if the caps have a common subset.
+ *
+ * Since: 0.10.33
+ */
+gboolean
+gst_element_factory_can_sink_any_caps (GstElementFactory * factory,
+ const GstCaps * caps)
+{
+ return gst_element_factory_can_accept_any_caps_in_direction (factory, caps,
+ GST_PAD_SINK);
+}
+
+/**
+ * gst_element_factory_can_src_any_caps:
+ * @factory: factory to query
+ * @caps: the caps to check
+ *
+ * Checks if the factory can src any possible capability.
+ *
+ * Returns: %TRUE if the caps have a common subset.
+ *
+ * Since: 0.10.33
+ */
+gboolean
+gst_element_factory_can_src_any_caps (GstElementFactory * factory,
+ const GstCaps * caps)
+{
+ return gst_element_factory_can_accept_any_caps_in_direction (factory, caps,
+ GST_PAD_SRC);
+}
+
+/* if return val is true, *direct_child is a caller-owned ref on the direct
+ * child of ancestor that is part of object's ancestry */
+static gboolean
+object_has_ancestor (GstObject * object, GstObject * ancestor,
+ GstObject ** direct_child)
+{
+ GstObject *child, *parent;
+
+ if (direct_child)
+ *direct_child = NULL;
+
+ child = gst_object_ref (object);
+ parent = gst_object_get_parent (object);
+
+ while (parent) {
+ if (ancestor == parent) {
+ if (direct_child)
+ *direct_child = child;
+ else
+ gst_object_unref (child);
+ gst_object_unref (parent);
+ return TRUE;
+ }
+
+ gst_object_unref (child);
+ child = parent;
+ parent = gst_object_get_parent (parent);
+ }
+
+ gst_object_unref (child);
+
+ return FALSE;
+}
+
+/* caller owns return */
+static GstObject *
+find_common_root (GstObject * o1, GstObject * o2)
+{
+ GstObject *top = o1;
+ GstObject *kid1, *kid2;
+ GstObject *root = NULL;
+
+ while (GST_OBJECT_PARENT (top))
+ top = GST_OBJECT_PARENT (top);
+
+ /* the itsy-bitsy spider... */
+
+ if (!object_has_ancestor (o2, top, &kid2))
+ return NULL;
+
+ root = gst_object_ref (top);
+ while (TRUE) {
+ if (!object_has_ancestor (o1, kid2, &kid1)) {
+ gst_object_unref (kid2);
+ return root;
+ }
+ root = kid2;
+ if (!object_has_ancestor (o2, kid1, &kid2)) {
+ gst_object_unref (kid1);
+ return root;
+ }
+ root = kid1;
+ }
+}
+
+/* caller does not own return */
+static GstPad *
+ghost_up (GstElement * e, GstPad * pad)
+{
+ static gint ghost_pad_index = 0;
+ GstPad *gpad;
+ gchar *name;
+ GstState current;
+ GstState next;
+ GstObject *parent = GST_OBJECT_PARENT (e);
+
+ name = g_strdup_printf ("ghost%d", ghost_pad_index++);
+ gpad = gst_ghost_pad_new (name, pad);
+ g_free (name);
+
+ GST_STATE_LOCK (e);
+ gst_element_get_state (e, &current, &next, 0);
+
+ if (current > GST_STATE_READY || next == GST_STATE_PAUSED)
+ gst_pad_set_active (gpad, TRUE);
+
+ if (!gst_element_add_pad ((GstElement *) parent, gpad)) {
+ g_warning ("Pad named %s already exists in element %s\n",
+ GST_OBJECT_NAME (gpad), GST_OBJECT_NAME (parent));
+ gst_object_unref ((GstObject *) gpad);
+ GST_STATE_UNLOCK (e);
+ return NULL;
+ }
+ GST_STATE_UNLOCK (e);
+
+ return gpad;
+}
+
+static void
+remove_pad (gpointer ppad, gpointer unused)
+{
+ GstPad *pad = ppad;
+
+ if (!gst_element_remove_pad ((GstElement *) GST_OBJECT_PARENT (pad), pad))
+ g_warning ("Couldn't remove pad %s from element %s",
+ GST_OBJECT_NAME (pad), GST_OBJECT_NAME (GST_OBJECT_PARENT (pad)));
+}
+
+static gboolean
+prepare_link_maybe_ghosting (GstPad ** src, GstPad ** sink,
+ GSList ** pads_created)
+{
+ GstObject *root;
+ GstObject *e1, *e2;
+ GSList *pads_created_local = NULL;
+
+ g_assert (pads_created);
+
+ e1 = GST_OBJECT_PARENT (*src);
+ e2 = GST_OBJECT_PARENT (*sink);
+
+ if (G_UNLIKELY (e1 == NULL)) {
+ GST_WARNING ("Trying to ghost a pad that doesn't have a parent: %"
+ GST_PTR_FORMAT, *src);
+ return FALSE;
+ }
+ if (G_UNLIKELY (e2 == NULL)) {
+ GST_WARNING ("Trying to ghost a pad that doesn't have a parent: %"
+ GST_PTR_FORMAT, *sink);
+ return FALSE;
+ }
+
+ if (GST_OBJECT_PARENT (e1) == GST_OBJECT_PARENT (e2)) {
+ GST_CAT_INFO (GST_CAT_PADS, "%s and %s in same bin, no need for ghost pads",
+ GST_OBJECT_NAME (e1), GST_OBJECT_NAME (e2));
+ return TRUE;
+ }
+
+ GST_CAT_INFO (GST_CAT_PADS, "%s and %s not in same bin, making ghost pads",
+ GST_OBJECT_NAME (e1), GST_OBJECT_NAME (e2));
+
+ /* we need to setup some ghost pads */
+ root = find_common_root (e1, e2);
+ if (!root) {
+ g_warning ("Trying to connect elements that don't share a common "
+ "ancestor: %s and %s", GST_ELEMENT_NAME (e1), GST_ELEMENT_NAME (e2));
+ return FALSE;
+ }
+
+ while (GST_OBJECT_PARENT (e1) != root) {
+ *src = ghost_up ((GstElement *) e1, *src);
+ if (!*src)
+ goto cleanup_fail;
+ e1 = GST_OBJECT_PARENT (*src);
+ pads_created_local = g_slist_prepend (pads_created_local, *src);
+ }
+ while (GST_OBJECT_PARENT (e2) != root) {
+ *sink = ghost_up ((GstElement *) e2, *sink);
+ if (!*sink)
+ goto cleanup_fail;
+ e2 = GST_OBJECT_PARENT (*sink);
+ pads_created_local = g_slist_prepend (pads_created_local, *sink);
+ }
+
+ gst_object_unref (root);
+ *pads_created = g_slist_concat (*pads_created, pads_created_local);
+ return TRUE;
+
+cleanup_fail:
+ gst_object_unref (root);
+ g_slist_foreach (pads_created_local, remove_pad, NULL);
+ g_slist_free (pads_created_local);
+ return FALSE;
+}
+
+static gboolean
+pad_link_maybe_ghosting (GstPad * src, GstPad * sink, GstPadLinkCheck flags)
+{
+ GSList *pads_created = NULL;
+ gboolean ret;
+
+ if (!prepare_link_maybe_ghosting (&src, &sink, &pads_created)) {
+ ret = FALSE;
+ } else {
+ ret = (gst_pad_link_full (src, sink, flags) == GST_PAD_LINK_OK);
+ }
+
+ if (!ret) {
+ g_slist_foreach (pads_created, remove_pad, NULL);
+ }
+ g_slist_free (pads_created);
+
+ return ret;
+}
+
+/**
+ * gst_element_link_pads_full:
+ * @src: a #GstElement containing the source pad.
+ * @srcpadname: (allow-none): the name of the #GstPad in source element
+ * or NULL for any pad.
+ * @dest: (transfer none): the #GstElement containing the destination pad.
+ * @destpadname: (allow-none): the name of the #GstPad in destination element,
+ * or NULL for any pad.
+ * @flags: the #GstPadLinkCheck to be performed when linking pads.
+ *
+ * Links the two named pads of the source and destination elements.
+ * Side effect is that if one of the pads has no parent, it becomes a
+ * child of the parent of the other element. If they have different
+ * parents, the link fails.
+ *
+ * Calling gst_element_link_pads_full() with @flags == %GST_PAD_LINK_CHECK_DEFAULT
+ * is the same as calling gst_element_link_pads() and the recommended way of
+ * linking pads with safety checks applied.
+ *
+ * This is a convenience function for gst_pad_link_full().
+ *
+ * Returns: TRUE if the pads could be linked, FALSE otherwise.
+ *
+ * Since: 0.10.30
+ */
+gboolean
+gst_element_link_pads_full (GstElement * src, const gchar * srcpadname,
+ GstElement * dest, const gchar * destpadname, GstPadLinkCheck flags)
+{
+ const GList *srcpads, *destpads, *srctempls, *desttempls, *l;
+ GstPad *srcpad, *destpad;
+ GstPadTemplate *srctempl, *desttempl;
+ GstElementClass *srcclass, *destclass;
+
+ /* checks */
+ g_return_val_if_fail (GST_IS_ELEMENT (src), FALSE);
+ g_return_val_if_fail (GST_IS_ELEMENT (dest), FALSE);
+
+ GST_CAT_INFO (GST_CAT_ELEMENT_PADS,
+ "trying to link element %s:%s to element %s:%s", GST_ELEMENT_NAME (src),
+ srcpadname ? srcpadname : "(any)", GST_ELEMENT_NAME (dest),
+ destpadname ? destpadname : "(any)");
+
+ /* get a src pad */
+ if (srcpadname) {
+ /* name specified, look it up */
+ if (!(srcpad = gst_element_get_static_pad (src, srcpadname)))
+ srcpad = gst_element_get_request_pad (src, srcpadname);
+ if (!srcpad) {
+ GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "no pad %s:%s",
+ GST_ELEMENT_NAME (src), srcpadname);
+ return FALSE;
+ } else {
+ if (!(GST_PAD_DIRECTION (srcpad) == GST_PAD_SRC)) {
+ GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "pad %s:%s is no src pad",
+ GST_DEBUG_PAD_NAME (srcpad));
+ gst_object_unref (srcpad);
+ return FALSE;
+ }
+ if (GST_PAD_PEER (srcpad) != NULL) {
+ GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS,
+ "pad %s:%s is already linked to %s:%s", GST_DEBUG_PAD_NAME (srcpad),
+ GST_DEBUG_PAD_NAME (GST_PAD_PEER (srcpad)));
+ gst_object_unref (srcpad);
+ return FALSE;
+ }
+ }
+ srcpads = NULL;
+ } else {
+ /* no name given, get the first available pad */
+ GST_OBJECT_LOCK (src);
+ srcpads = GST_ELEMENT_PADS (src);
+ srcpad = srcpads ? GST_PAD_CAST (srcpads->data) : NULL;
+ if (srcpad)
+ gst_object_ref (srcpad);
+ GST_OBJECT_UNLOCK (src);
+ }
+
+ /* get a destination pad */
+ if (destpadname) {
+ /* name specified, look it up */
+ if (!(destpad = gst_element_get_static_pad (dest, destpadname)))
+ destpad = gst_element_get_request_pad (dest, destpadname);
+ if (!destpad) {
+ GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "no pad %s:%s",
+ GST_ELEMENT_NAME (dest), destpadname);
+ return FALSE;
+ } else {
+ if (!(GST_PAD_DIRECTION (destpad) == GST_PAD_SINK)) {
+ GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "pad %s:%s is no sink pad",
+ GST_DEBUG_PAD_NAME (destpad));
+ gst_object_unref (destpad);
+ return FALSE;
+ }
+ if (GST_PAD_PEER (destpad) != NULL) {
+ GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS,
+ "pad %s:%s is already linked to %s:%s",
+ GST_DEBUG_PAD_NAME (destpad),
+ GST_DEBUG_PAD_NAME (GST_PAD_PEER (destpad)));
+ gst_object_unref (destpad);
+ return FALSE;
+ }
+ }
+ destpads = NULL;
+ } else {
+ /* no name given, get the first available pad */
+ GST_OBJECT_LOCK (dest);
+ destpads = GST_ELEMENT_PADS (dest);
+ destpad = destpads ? GST_PAD_CAST (destpads->data) : NULL;
+ if (destpad)
+ gst_object_ref (destpad);
+ GST_OBJECT_UNLOCK (dest);
+ }
+
+ if (srcpadname && destpadname) {
+ gboolean result;
+
+ /* two explicitly specified pads */
+ result = pad_link_maybe_ghosting (srcpad, destpad, flags);
+
+ gst_object_unref (srcpad);
+ gst_object_unref (destpad);
+
+ return result;
+ }
+
+ if (srcpad) {
+ /* loop through the allowed pads in the source, trying to find a
+ * compatible destination pad */
+ GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS,
+ "looping through allowed src and dest pads");
+ do {
+ GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "trying src pad %s:%s",
+ GST_DEBUG_PAD_NAME (srcpad));
+ if ((GST_PAD_DIRECTION (srcpad) == GST_PAD_SRC) &&
+ (GST_PAD_PEER (srcpad) == NULL)) {
+ GstPad *temp;
+
+ if (destpadname) {
+ temp = destpad;
+ gst_object_ref (temp);
+ } else {
+ temp = gst_element_get_compatible_pad (dest, srcpad, NULL);
+ }
+
+ if (temp && pad_link_maybe_ghosting (srcpad, temp, flags)) {
+ GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "linked pad %s:%s to pad %s:%s",
+ GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (temp));
+ if (destpad)
+ gst_object_unref (destpad);
+ gst_object_unref (srcpad);
+ gst_object_unref (temp);
+ return TRUE;
+ }
+
+ if (temp) {
+ gst_object_unref (temp);
+ }
+ }
+ /* find a better way for this mess */
+ if (srcpads) {
+ srcpads = g_list_next (srcpads);
+ if (srcpads) {
+ gst_object_unref (srcpad);
+ srcpad = GST_PAD_CAST (srcpads->data);
+ gst_object_ref (srcpad);
+ }
+ }
+ } while (srcpads);
+ }
+ if (srcpadname) {
+ GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "no link possible from %s:%s to %s",
+ GST_DEBUG_PAD_NAME (srcpad), GST_ELEMENT_NAME (dest));
+ if (destpad)
+ gst_object_unref (destpad);
+ destpad = NULL;
+ }
+ if (srcpad)
+ gst_object_unref (srcpad);
+ srcpad = NULL;
+
+ if (destpad) {
+ /* loop through the existing pads in the destination */
+ do {
+ GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "trying dest pad %s:%s",
+ GST_DEBUG_PAD_NAME (destpad));
+ if ((GST_PAD_DIRECTION (destpad) == GST_PAD_SINK) &&
+ (GST_PAD_PEER (destpad) == NULL)) {
+ GstPad *temp = gst_element_get_compatible_pad (src, destpad, NULL);
+
+ if (temp && pad_link_maybe_ghosting (temp, destpad, flags)) {
+ GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "linked pad %s:%s to pad %s:%s",
+ GST_DEBUG_PAD_NAME (temp), GST_DEBUG_PAD_NAME (destpad));
+ gst_object_unref (temp);
+ gst_object_unref (destpad);
+ return TRUE;
+ }
+ if (temp) {
+ gst_object_unref (temp);
+ }
+ }
+ if (destpads) {
+ destpads = g_list_next (destpads);
+ if (destpads) {
+ gst_object_unref (destpad);
+ destpad = GST_PAD_CAST (destpads->data);
+ gst_object_ref (destpad);
+ }
+ }
+ } while (destpads);
+ }
+
+ if (destpadname) {
+ GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "no link possible from %s to %s:%s",
+ GST_ELEMENT_NAME (src), GST_DEBUG_PAD_NAME (destpad));
+ gst_object_unref (destpad);
+ return FALSE;
+ } else {
+ if (destpad)
+ gst_object_unref (destpad);
+ destpad = NULL;
+ }
+
+ srcclass = GST_ELEMENT_GET_CLASS (src);
+ destclass = GST_ELEMENT_GET_CLASS (dest);
+
+ GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS,
+ "we might have request pads on both sides, checking...");
+ srctempls = gst_element_class_get_pad_template_list (srcclass);
+ desttempls = gst_element_class_get_pad_template_list (destclass);
+
+ if (srctempls && desttempls) {
+ while (srctempls) {
+ srctempl = (GstPadTemplate *) srctempls->data;
+ if (srctempl->presence == GST_PAD_REQUEST) {
+ for (l = desttempls; l; l = l->next) {
+ desttempl = (GstPadTemplate *) l->data;
+ if (desttempl->presence == GST_PAD_REQUEST &&
+ desttempl->direction != srctempl->direction) {
+ GstCaps *srccaps, *destcaps;
+
+ srccaps = gst_pad_template_get_caps (srctempl);
+ destcaps = gst_pad_template_get_caps (desttempl);
+ if (gst_caps_is_always_compatible (srccaps, destcaps)) {
+ srcpad =
+ gst_element_request_pad (src, srctempl,
+ srctempl->name_template, NULL);
+ destpad =
+ gst_element_request_pad (dest, desttempl,
+ desttempl->name_template, NULL);
+ if (srcpad && destpad
+ && pad_link_maybe_ghosting (srcpad, destpad, flags)) {
+ GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS,
+ "linked pad %s:%s to pad %s:%s",
+ GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (destpad));
+ gst_object_unref (srcpad);
+ gst_object_unref (destpad);
+ gst_caps_unref (srccaps);
+ gst_caps_unref (destcaps);
+ return TRUE;
+ }
+ /* it failed, so we release the request pads */
+ if (srcpad)
+ gst_element_release_request_pad (src, srcpad);
+ if (destpad)
+ gst_element_release_request_pad (dest, destpad);
+ }
+ gst_caps_unref (srccaps);
+ gst_caps_unref (destcaps);
+ }
+ }
+ }
+ srctempls = srctempls->next;
+ }
+ }
+
+ GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "no link possible from %s to %s",
+ GST_ELEMENT_NAME (src), GST_ELEMENT_NAME (dest));
+ return FALSE;
+}
+
+/**
+ * gst_element_link_pads:
+ * @src: a #GstElement containing the source pad.
+ * @srcpadname: (allow-none): the name of the #GstPad in source element
+ * or NULL for any pad.
+ * @dest: (transfer none): the #GstElement containing the destination pad.
+ * @destpadname: (allow-none): the name of the #GstPad in destination element,
+ * or NULL for any pad.
+ *
+ * Links the two named pads of the source and destination elements.
+ * Side effect is that if one of the pads has no parent, it becomes a
+ * child of the parent of the other element. If they have different
+ * parents, the link fails.
+ *
+ * Returns: TRUE if the pads could be linked, FALSE otherwise.
+ */
+gboolean
+gst_element_link_pads (GstElement * src, const gchar * srcpadname,
+ GstElement * dest, const gchar * destpadname)
+{
+ return gst_element_link_pads_full (src, srcpadname, dest, destpadname,
+ GST_PAD_LINK_CHECK_DEFAULT);
+}
+
+/**
+ * gst_element_link_pads_filtered:
+ * @src: a #GstElement containing the source pad.
+ * @srcpadname: (allow-none): the name of the #GstPad in source element
+ * or NULL for any pad.
+ * @dest: (transfer none): the #GstElement containing the destination pad.
+ * @destpadname: (allow-none): the name of the #GstPad in destination element
+ * or NULL for any pad.
+ * @filter: (transfer none) (allow-none): the #GstCaps to filter the link,
+ * or #NULL for no filter.
+ *
+ * Links the two named pads of the source and destination elements. Side effect
+ * is that if one of the pads has no parent, it becomes a child of the parent of
+ * the other element. If they have different parents, the link fails. If @caps
+ * is not #NULL, makes sure that the caps of the link is a subset of @caps.
+ *
+ * Returns: TRUE if the pads could be linked, FALSE otherwise.
+ */
+gboolean
+gst_element_link_pads_filtered (GstElement * src, const gchar * srcpadname,
+ GstElement * dest, const gchar * destpadname, GstCaps * filter)
+{
+ /* checks */
+ g_return_val_if_fail (GST_IS_ELEMENT (src), FALSE);
+ g_return_val_if_fail (GST_IS_ELEMENT (dest), FALSE);
+ g_return_val_if_fail (filter == NULL || GST_IS_CAPS (filter), FALSE);
+
+ if (filter) {
+ GstElement *capsfilter;
+ GstObject *parent;
+ GstState state, pending;
+ gboolean lr1, lr2;
+
+ capsfilter = gst_element_factory_make ("capsfilter", NULL);
+ if (!capsfilter) {
+ GST_ERROR ("Could not make a capsfilter");
+ return FALSE;
+ }
+
+ parent = gst_object_get_parent (GST_OBJECT (src));
+ g_return_val_if_fail (GST_IS_BIN (parent), FALSE);
+
+ gst_element_get_state (GST_ELEMENT_CAST (parent), &state, &pending, 0);
+
+ if (!gst_bin_add (GST_BIN (parent), capsfilter)) {
+ GST_ERROR ("Could not add capsfilter");
+ gst_object_unref (capsfilter);
+ gst_object_unref (parent);
+ return FALSE;
+ }
+
+ if (pending != GST_STATE_VOID_PENDING)
+ state = pending;
+
+ gst_element_set_state (capsfilter, state);
+
+ gst_object_unref (parent);
+
+ g_object_set (capsfilter, "caps", filter, NULL);
+
+ lr1 = gst_element_link_pads (src, srcpadname, capsfilter, "sink");
+ lr2 = gst_element_link_pads (capsfilter, "src", dest, destpadname);
+ if (lr1 && lr2) {
+ return TRUE;
+ } else {
+ if (!lr1) {
+ GST_INFO ("Could not link pads: %s:%s - capsfilter:sink",
+ GST_ELEMENT_NAME (src), srcpadname);
+ } else {
+ GST_INFO ("Could not link pads: capsfilter:src - %s:%s",
+ GST_ELEMENT_NAME (dest), destpadname);
+ }
+ gst_element_set_state (capsfilter, GST_STATE_NULL);
+ /* this will unlink and unref as appropriate */
+ gst_bin_remove (GST_BIN (GST_OBJECT_PARENT (capsfilter)), capsfilter);
+ return FALSE;
+ }
+ } else {
+ if (gst_element_link_pads (src, srcpadname, dest, destpadname)) {
+ return TRUE;
+ } else {
+ GST_INFO ("Could not link pads: %s:%s - %s:%s", GST_ELEMENT_NAME (src),
+ srcpadname, GST_ELEMENT_NAME (dest), destpadname);
+ return FALSE;
+ }
+ }
+}
+
+/**
+ * gst_element_link:
+ * @src: (transfer none): a #GstElement containing the source pad.
+ * @dest: (transfer none): the #GstElement containing the destination pad.
+ *
+ * Links @src to @dest. The link must be from source to
+ * destination; the other direction will not be tried. The function looks for
+ * existing pads that aren't linked yet. It will request new pads if necessary.
+ * Such pads need to be released manually when unlinking.
+ * If multiple links are possible, only one is established.
+ *
+ * Make sure you have added your elements to a bin or pipeline with
+ * gst_bin_add() before trying to link them.
+ *
+ * Returns: TRUE if the elements could be linked, FALSE otherwise.
+ */
+gboolean
+gst_element_link (GstElement * src, GstElement * dest)
+{
+ return gst_element_link_pads (src, NULL, dest, NULL);
+}
+
+/**
+ * gst_element_link_many:
+ * @element_1: (transfer none): the first #GstElement in the link chain.
+ * @element_2: (transfer none): the second #GstElement in the link chain.
+ * @...: the NULL-terminated list of elements to link in order.
+ *
+ * Chain together a series of elements. Uses gst_element_link().
+ * Make sure you have added your elements to a bin or pipeline with
+ * gst_bin_add() before trying to link them.
+ *
+ * Returns: TRUE on success, FALSE otherwise.
+ */
+gboolean
+gst_element_link_many (GstElement * element_1, GstElement * element_2, ...)
+{
+ gboolean res = TRUE;
+ va_list args;
+
+ g_return_val_if_fail (GST_IS_ELEMENT (element_1), FALSE);
+ g_return_val_if_fail (GST_IS_ELEMENT (element_2), FALSE);
+
+ va_start (args, element_2);
+
+ while (element_2) {
+ if (!gst_element_link (element_1, element_2)) {
+ res = FALSE;
+ break;
+ }
+
+ element_1 = element_2;
+ element_2 = va_arg (args, GstElement *);
+ }
+
+ va_end (args);
+
+ return res;
+}
+
+/**
+ * gst_element_link_filtered:
+ * @src: a #GstElement containing the source pad.
+ * @dest: (transfer none): the #GstElement containing the destination pad.
+ * @filter: (transfer none) (allow-none): the #GstCaps to filter the link,
+ * or #NULL for no filter.
+ *
+ * Links @src to @dest using the given caps as filtercaps.
+ * The link must be from source to
+ * destination; the other direction will not be tried. The function looks for
+ * existing pads that aren't linked yet. It will request new pads if necessary.
+ * If multiple links are possible, only one is established.
+ *
+ * Make sure you have added your elements to a bin or pipeline with
+ * gst_bin_add() before trying to link them.
+ *
+ * Returns: TRUE if the pads could be linked, FALSE otherwise.
+ */
+gboolean
+gst_element_link_filtered (GstElement * src, GstElement * dest,
+ GstCaps * filter)
+{
+ return gst_element_link_pads_filtered (src, NULL, dest, NULL, filter);
+}
+
+/**
+ * gst_element_unlink_pads:
+ * @src: a (transfer none): #GstElement containing the source pad.
+ * @srcpadname: the name of the #GstPad in source element.
+ * @dest: (transfer none): a #GstElement containing the destination pad.
+ * @destpadname: the name of the #GstPad in destination element.
+ *
+ * Unlinks the two named pads of the source and destination elements.
+ *
+ * This is a convenience function for gst_pad_unlink().
+ */
+void
+gst_element_unlink_pads (GstElement * src, const gchar * srcpadname,
+ GstElement * dest, const gchar * destpadname)
+{
+ GstPad *srcpad, *destpad;
+ gboolean srcrequest, destrequest;
+
+ srcrequest = destrequest = FALSE;
+
+ g_return_if_fail (src != NULL);
+ g_return_if_fail (GST_IS_ELEMENT (src));
+ g_return_if_fail (srcpadname != NULL);
+ g_return_if_fail (dest != NULL);
+ g_return_if_fail (GST_IS_ELEMENT (dest));
+ g_return_if_fail (destpadname != NULL);
+
+ /* obtain the pads requested */
+ if (!(srcpad = gst_element_get_static_pad (src, srcpadname)))
+ if ((srcpad = gst_element_get_request_pad (src, srcpadname)))
+ srcrequest = TRUE;
+ if (srcpad == NULL) {
+ GST_WARNING_OBJECT (src, "source element has no pad \"%s\"", srcpadname);
+ return;
+ }
+ if (!(destpad = gst_element_get_static_pad (dest, destpadname)))
+ if ((destpad = gst_element_get_request_pad (dest, destpadname)))
+ destrequest = TRUE;
+ if (destpad == NULL) {
+ GST_WARNING_OBJECT (dest, "destination element has no pad \"%s\"",
+ destpadname);
+ goto free_src;
+ }
+
+ /* we're satisfied they can be unlinked, let's do it */
+ gst_pad_unlink (srcpad, destpad);
+
+ if (destrequest)
+ gst_element_release_request_pad (dest, destpad);
+ gst_object_unref (destpad);
+
+free_src:
+ if (srcrequest)
+ gst_element_release_request_pad (src, srcpad);
+ gst_object_unref (srcpad);
+}
+
+/**
+ * gst_element_unlink_many:
+ * @element_1: (transfer none): the first #GstElement in the link chain.
+ * @element_2: (transfer none): the second #GstElement in the link chain.
+ * @...: the NULL-terminated list of elements to unlink in order.
+ *
+ * Unlinks a series of elements. Uses gst_element_unlink().
+ */
+void
+gst_element_unlink_many (GstElement * element_1, GstElement * element_2, ...)
+{
+ va_list args;
+
+ g_return_if_fail (element_1 != NULL && element_2 != NULL);
+ g_return_if_fail (GST_IS_ELEMENT (element_1) && GST_IS_ELEMENT (element_2));
+
+ va_start (args, element_2);
+
+ while (element_2) {
+ gst_element_unlink (element_1, element_2);
+
+ element_1 = element_2;
+ element_2 = va_arg (args, GstElement *);
+ }
+
+ va_end (args);
+}
+
+/**
+ * gst_element_unlink:
+ * @src: (transfer none): the source #GstElement to unlink.
+ * @dest: (transfer none): the sink #GstElement to unlink.
+ *
+ * Unlinks all source pads of the source element with all sink pads
+ * of the sink element to which they are linked.
+ *
+ * If the link has been made using gst_element_link(), it could have created an
+ * requestpad, which has to be released using gst_element_release_request_pad().
+ */
+void
+gst_element_unlink (GstElement * src, GstElement * dest)
+{
+ GstIterator *pads;
+ gboolean done = FALSE;
+ GValue data = { 0, };
+
+ g_return_if_fail (GST_IS_ELEMENT (src));
+ g_return_if_fail (GST_IS_ELEMENT (dest));
+
+ GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "unlinking \"%s\" and \"%s\"",
+ GST_ELEMENT_NAME (src), GST_ELEMENT_NAME (dest));
+
+ pads = gst_element_iterate_pads (src);
+ while (!done) {
+ switch (gst_iterator_next (pads, &data)) {
+ case GST_ITERATOR_OK:
+ {
+ GstPad *pad = g_value_get_object (&data);
+
+ if (GST_PAD_IS_SRC (pad)) {
+ GstPad *peerpad = gst_pad_get_peer (pad);
+
+ /* see if the pad is linked and is really a pad of dest */
+ if (peerpad) {
+ GstElement *peerelem;
+
+ peerelem = gst_pad_get_parent_element (peerpad);
+
+ if (peerelem == dest) {
+ gst_pad_unlink (pad, peerpad);
+ }
+ if (peerelem)
+ gst_object_unref (peerelem);
+
+ gst_object_unref (peerpad);
+ }
+ }
+ g_value_reset (&data);
+ break;
+ }
+ case GST_ITERATOR_RESYNC:
+ gst_iterator_resync (pads);
+ break;
+ case GST_ITERATOR_DONE:
+ done = TRUE;
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+ }
+ g_value_unset (&data);
+ gst_iterator_free (pads);
+}
+
+/**
+ * gst_element_query_position:
+ * @element: a #GstElement to invoke the position query on.
+ * @format: the #GstFormat requested
+ * @cur: (out) (allow-none): a location in which to store the current
+ * position, or NULL.
+ *
+ * Queries an element for the stream position. If one repeatedly calls this
+ * function one can also create and reuse it in gst_element_query().
+ *
+ * Returns: TRUE if the query could be performed.
+ */
+gboolean
+gst_element_query_position (GstElement * element, GstFormat format,
+ gint64 * cur)
+{
+ GstQuery *query;
+ gboolean ret;
+
+ g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
+ g_return_val_if_fail (format != GST_FORMAT_UNDEFINED, FALSE);
+
+ query = gst_query_new_position (format);
+ ret = gst_element_query (element, query);
+
+ if (ret)
+ gst_query_parse_position (query, NULL, cur);
+
+ gst_query_unref (query);
+
+ return ret;
+}
+
+/**
+ * gst_element_query_duration:
+ * @element: a #GstElement to invoke the duration query on.
+ * @format: the #GstFormat requested
+ * @duration: (out): A location in which to store the total duration, or NULL.
+ *
+ * Queries an element for the total stream duration.
+ *
+ * Returns: TRUE if the query could be performed.
+ */
+gboolean
+gst_element_query_duration (GstElement * element, GstFormat format,
+ gint64 * duration)
+{
+ GstQuery *query;
+ gboolean ret;
+
+ g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
+ g_return_val_if_fail (format != GST_FORMAT_UNDEFINED, FALSE);
+
+ query = gst_query_new_duration (format);
+ ret = gst_element_query (element, query);
+
+ if (ret)
+ gst_query_parse_duration (query, NULL, duration);
+
+ gst_query_unref (query);
+
+ return ret;
+}
+
+/**
+ * gst_element_query_convert:
+ * @element: a #GstElement to invoke the convert query on.
+ * @src_format: (inout): a #GstFormat to convert from.
+ * @src_val: a value to convert.
+ * @dest_format: the #GstFormat to convert to.
+ * @dest_val: (out): a pointer to the result.
+ *
+ * Queries an element to convert @src_val in @src_format to @dest_format.
+ *
+ * Returns: TRUE if the query could be performed.
+ */
+gboolean
+gst_element_query_convert (GstElement * element, GstFormat src_format,
+ gint64 src_val, GstFormat dest_format, gint64 * dest_val)
+{
+ GstQuery *query;
+ gboolean ret;
+
+ g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
+ g_return_val_if_fail (dest_format != GST_FORMAT_UNDEFINED, FALSE);
+ g_return_val_if_fail (dest_val != NULL, FALSE);
+
+ if (dest_format == src_format || src_val == -1) {
+ *dest_val = src_val;
+ return TRUE;
+ }
+
+ query = gst_query_new_convert (src_format, src_val, dest_format);
+ ret = gst_element_query (element, query);
+
+ if (ret)
+ gst_query_parse_convert (query, NULL, NULL, NULL, dest_val);
+
+ gst_query_unref (query);
+
+ return ret;
+}
+
+/**
+ * gst_element_seek_simple
+ * @element: a #GstElement to seek on
+ * @format: a #GstFormat to execute the seek in, such as #GST_FORMAT_TIME
+ * @seek_flags: seek options; playback applications will usually want to use
+ * GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT here
+ * @seek_pos: position to seek to (relative to the start); if you are doing
+ * a seek in #GST_FORMAT_TIME this value is in nanoseconds -
+ * multiply with #GST_SECOND to convert seconds to nanoseconds or
+ * with #GST_MSECOND to convert milliseconds to nanoseconds.
+ *
+ * Simple API to perform a seek on the given element, meaning it just seeks
+ * to the given position relative to the start of the stream. For more complex
+ * operations like segment seeks (e.g. for looping) or changing the playback
+ * rate or seeking relative to the last configured playback segment you should
+ * use gst_element_seek().
+ *
+ * In a completely prerolled PAUSED or PLAYING pipeline, seeking is always
+ * guaranteed to return %TRUE on a seekable media type or %FALSE when the media
+ * type is certainly not seekable (such as a live stream).
+ *
+ * Some elements allow for seeking in the READY state, in this
+ * case they will store the seek event and execute it when they are put to
+ * PAUSED. If the element supports seek in READY, it will always return %TRUE when
+ * it receives the event in the READY state.
+ *
+ * Returns: %TRUE if the seek operation succeeded (the seek might not always be
+ * executed instantly though)
+ *
+ * Since: 0.10.7
+ */
+gboolean
+gst_element_seek_simple (GstElement * element, GstFormat format,
+ GstSeekFlags seek_flags, gint64 seek_pos)
+{
+ g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
+ g_return_val_if_fail (seek_pos >= 0, FALSE);
+
+ return gst_element_seek (element, 1.0, format, seek_flags,
+ GST_SEEK_TYPE_SET, seek_pos, GST_SEEK_TYPE_NONE, 0);
+}
+
+/**
+ * gst_pad_use_fixed_caps:
+ * @pad: the pad to use
+ *
+ * A helper function you can use that sets the FIXED_CAPS flag
+ * This way the default getcaps function will always return the negotiated caps
+ * or in case the pad is not negotiated, the padtemplate caps.
+ *
+ * Use this function on a pad that, once gst_pad_set_caps() has been called
+ * on it, cannot be renegotiated to something else.
+ */
+void
+gst_pad_use_fixed_caps (GstPad * pad)
+{
+ GST_OBJECT_FLAG_SET (pad, GST_PAD_FIXED_CAPS);
+}
+
+/**
+ * gst_pad_get_parent_element:
+ * @pad: a pad
+ *
+ * Gets the parent of @pad, cast to a #GstElement. If a @pad has no parent or
+ * its parent is not an element, return NULL.
+ *
+ * Returns: (transfer full): the parent of the pad. The caller has a
+ * reference on the parent, so unref when you're finished with it.
+ *
+ * MT safe.
+ */
+GstElement *
+gst_pad_get_parent_element (GstPad * pad)
+{
+ GstObject *p;
+
+ g_return_val_if_fail (GST_IS_PAD (pad), NULL);
+
+ p = gst_object_get_parent (GST_OBJECT_CAST (pad));
+
+ if (p && !GST_IS_ELEMENT (p)) {
+ gst_object_unref (p);
+ p = NULL;
+ }
+ return GST_ELEMENT_CAST (p);
+}
+
+/**
+ * gst_object_default_error:
+ * @source: the #GstObject that initiated the error.
+ * @error: (in): the GError.
+ * @debug: (in) (allow-none): an additional debug information string, or NULL
+ *
+ * A default error function that uses g_printerr() to display the error message
+ * and the optional debug sting..
+ *
+ * The default handler will simply print the error string using g_print.
+ */
+void
+gst_object_default_error (GstObject * source, const GError * error,
+ const gchar * debug)
+{
+ gchar *name = gst_object_get_path_string (source);
+
+ g_printerr (_("ERROR: from element %s: %s\n"), name, error->message);
+ if (debug)
+ g_printerr (_("Additional debug info:\n%s\n"), debug);
+
+ g_free (name);
+}
+
+/**
+ * gst_bin_add_many:
+ * @bin: a #GstBin
+ * @element_1: (transfer full): the #GstElement element to add to the bin
+ * @...: (transfer full): additional elements to add to the bin
+ *
+ * Adds a NULL-terminated list of elements to a bin. This function is
+ * equivalent to calling gst_bin_add() for each member of the list. The return
+ * value of each gst_bin_add() is ignored.
+ */
+void
+gst_bin_add_many (GstBin * bin, GstElement * element_1, ...)
+{
+ va_list args;
+
+ g_return_if_fail (GST_IS_BIN (bin));
+ g_return_if_fail (GST_IS_ELEMENT (element_1));
+
+ va_start (args, element_1);
+
+ while (element_1) {
+ gst_bin_add (bin, element_1);
+
+ element_1 = va_arg (args, GstElement *);
+ }
+
+ va_end (args);
+}
+
+/**
+ * gst_bin_remove_many:
+ * @bin: a #GstBin
+ * @element_1: (transfer none): the first #GstElement to remove from the bin
+ * @...: (transfer none): NULL-terminated list of elements to remove from the bin
+ *
+ * Remove a list of elements from a bin. This function is equivalent
+ * to calling gst_bin_remove() with each member of the list.
+ */
+void
+gst_bin_remove_many (GstBin * bin, GstElement * element_1, ...)
+{
+ va_list args;
+
+ g_return_if_fail (GST_IS_BIN (bin));
+ g_return_if_fail (GST_IS_ELEMENT (element_1));
+
+ va_start (args, element_1);
+
+ while (element_1) {
+ gst_bin_remove (bin, element_1);
+
+ element_1 = va_arg (args, GstElement *);
+ }
+
+ va_end (args);
+}
+
+static void
+gst_element_populate_std_props (GObjectClass * klass, const gchar * prop_name,
+ guint arg_id, GParamFlags flags)
+{
+ GQuark prop_id = g_quark_from_string (prop_name);
+ GParamSpec *pspec;
+
+ static GQuark fd_id = 0;
+ static GQuark blocksize_id;
+ static GQuark bytesperread_id;
+ static GQuark dump_id;
+ static GQuark filesize_id;
+ static GQuark mmapsize_id;
+ static GQuark location_id;
+ static GQuark offset_id;
+ static GQuark silent_id;
+ static GQuark touch_id;
+
+ flags |= G_PARAM_STATIC_STRINGS;
+
+ if (!fd_id) {
+ fd_id = g_quark_from_static_string ("fd");
+ blocksize_id = g_quark_from_static_string ("blocksize");
+ bytesperread_id = g_quark_from_static_string ("bytesperread");
+ dump_id = g_quark_from_static_string ("dump");
+ filesize_id = g_quark_from_static_string ("filesize");
+ mmapsize_id = g_quark_from_static_string ("mmapsize");
+ location_id = g_quark_from_static_string ("location");
+ offset_id = g_quark_from_static_string ("offset");
+ silent_id = g_quark_from_static_string ("silent");
+ touch_id = g_quark_from_static_string ("touch");
+ }
+
+ if (prop_id == fd_id) {
+ pspec = g_param_spec_int ("fd", "File-descriptor",
+ "File-descriptor for the file being read", 0, G_MAXINT, 0, flags);
+ } else if (prop_id == blocksize_id) {
+ pspec = g_param_spec_ulong ("blocksize", "Block Size",
+ "Block size to read per buffer", 0, G_MAXULONG, 4096, flags);
+
+ } else if (prop_id == bytesperread_id) {
+ pspec = g_param_spec_int ("bytesperread", "Bytes per read",
+ "Number of bytes to read per buffer", G_MININT, G_MAXINT, 0, flags);
+
+ } else if (prop_id == dump_id) {
+ pspec = g_param_spec_boolean ("dump", "Dump",
+ "Dump bytes to stdout", FALSE, flags);
+
+ } else if (prop_id == filesize_id) {
+ pspec = g_param_spec_int64 ("filesize", "File Size",
+ "Size of the file being read", 0, G_MAXINT64, 0, flags);
+
+ } else if (prop_id == mmapsize_id) {
+ pspec = g_param_spec_ulong ("mmapsize", "mmap() Block Size",
+ "Size in bytes of mmap()d regions", 0, G_MAXULONG, 4 * 1048576, flags);
+
+ } else if (prop_id == location_id) {
+ pspec = g_param_spec_string ("location", "File Location",
+ "Location of the file to read", NULL, flags);
+
+ } else if (prop_id == offset_id) {
+ pspec = g_param_spec_int64 ("offset", "File Offset",
+ "Byte offset of current read pointer", 0, G_MAXINT64, 0, flags);
+
+ } else if (prop_id == silent_id) {
+ pspec = g_param_spec_boolean ("silent", "Silent", "Don't produce events",
+ FALSE, flags);
+
+ } else if (prop_id == touch_id) {
+ pspec = g_param_spec_boolean ("touch", "Touch read data",
+ "Touch data to force disk read before " "push ()", TRUE, flags);
+ } else {
+ g_warning ("Unknown - 'standard' property '%s' id %d from klass %s",
+ prop_name, arg_id, g_type_name (G_OBJECT_CLASS_TYPE (klass)));
+ pspec = NULL;
+ }
+
+ if (pspec) {
+ g_object_class_install_property (klass, arg_id, pspec);
+ }
+}
+
+/**
+ * gst_element_class_install_std_props:
+ * @klass: the #GstElementClass to add the properties to.
+ * @first_name: the name of the first property.
+ * in a NULL terminated
+ * @...: the id and flags of the first property, followed by
+ * further 'name', 'id', 'flags' triplets and terminated by NULL.
+ *
+ * Adds a list of standardized properties with types to the @klass.
+ * the id is for the property switch in your get_prop method, and
+ * the flags determine readability / writeability.
+ **/
+void
+gst_element_class_install_std_props (GstElementClass * klass,
+ const gchar * first_name, ...)
+{
+ const char *name;
+
+ va_list args;
+
+ g_return_if_fail (GST_IS_ELEMENT_CLASS (klass));
+
+ va_start (args, first_name);
+
+ name = first_name;
+
+ while (name) {
+ int arg_id = va_arg (args, int);
+ GParamFlags flags = (GParamFlags) va_arg (args, int);
+
+ gst_element_populate_std_props ((GObjectClass *) klass, name, arg_id,
+ flags);
+
+ name = va_arg (args, char *);
+ }
+
+ va_end (args);
+}
+
+
+/**
+ * gst_buffer_merge:
+ * @buf1: (transfer none): the first source #GstBuffer to merge.
+ * @buf2: (transfer none): the second source #GstBuffer to merge.
+ *
+ * Create a new buffer that is the concatenation of the two source
+ * buffers. The original source buffers will not be modified or
+ * unref'd. Make sure you unref the source buffers if they are not used
+ * anymore afterwards.
+ *
+ * If the buffers point to contiguous areas of memory, the buffer
+ * is created without copying the data.
+ *
+ * Free-function: gst_buffer_unref
+ *
+ * Returns: (transfer full): the new #GstBuffer which is the concatenation
+ * of the source buffers.
+ */
+GstBuffer *
+gst_buffer_merge (GstBuffer * buf1, GstBuffer * buf2)
+{
+ GstBuffer *result;
+ gsize size1, size2;
+
+ size1 = gst_buffer_get_size (buf1);
+ size2 = gst_buffer_get_size (buf2);
+
+ /* we're just a specific case of the more general gst_buffer_span() */
+ result = gst_buffer_span (buf1, 0, buf2, size1 + size2);
+
+ return result;
+}
+
+/**
+ * gst_buffer_join:
+ * @buf1: the first source #GstBuffer.
+ * @buf2: the second source #GstBuffer.
+ *
+ * Create a new buffer that is the concatenation of the two source
+ * buffers, and unrefs the original source buffers.
+ *
+ * If the buffers point to contiguous areas of memory, the buffer
+ * is created without copying the data.
+ *
+ * This is a convenience function for C programmers. See also
+ * gst_buffer_merge(), which does the same thing without
+ * unreffing the input parameters. Language bindings without
+ * explicit reference counting should not wrap this function.
+ *
+ * Returns: (transfer full): the new #GstBuffer which is the concatenation of
+ * the source buffers.
+ */
+GstBuffer *
+gst_buffer_join (GstBuffer * buf1, GstBuffer * buf2)
+{
+ GstBuffer *result;
+ gsize size1, size2;
+
+ size1 = gst_buffer_get_size (buf1);
+ size2 = gst_buffer_get_size (buf2);
+
+ result = gst_buffer_span (buf1, 0, buf2, size1 + size2);
+ gst_buffer_unref (buf1);
+ gst_buffer_unref (buf2);
+
+ return result;
+}
+
+static gboolean
+getcaps_fold_func (const GValue * vpad, GValue * ret, GstCaps * filter)
+{
+ GstPad *pad = g_value_get_object (vpad);
+ gboolean empty = FALSE;
+ GstCaps *peercaps, *existing;
+
+ existing = g_value_get_pointer (ret);
+ peercaps = gst_pad_peer_get_caps (pad, filter);
+ if (G_LIKELY (peercaps)) {
+ GstCaps *intersection = gst_caps_intersect (existing, peercaps);
+
+ empty = gst_caps_is_empty (intersection);
+
+ g_value_set_pointer (ret, intersection);
+ gst_caps_unref (existing);
+ gst_caps_unref (peercaps);
+ }
+ return !empty;
+}
+
+/**
+ * gst_pad_proxy_getcaps:
+ * @pad: a #GstPad to proxy.
+ * @filter: a #GstCaps filter.
+ *
+ * Calls gst_pad_get_allowed_caps() for every other pad belonging to the
+ * same element as @pad, and returns the intersection of the results.
+ *
+ * This function is useful as a default getcaps function for an element
+ * that can handle any stream format, but requires all its pads to have
+ * the same caps. Two such elements are tee and adder.
+ *
+ * Free-function: gst_caps_unref
+ *
+ * Returns: (transfer full): the intersection of the other pads' allowed caps.
+ */
+GstCaps *
+gst_pad_proxy_getcaps (GstPad * pad, GstCaps * filter)
+{
+ GstElement *element;
+ GstCaps *caps, *intersected;
+ GstIterator *iter;
+ GstIteratorResult res;
+ GValue ret = { 0, };
+
+ g_return_val_if_fail (GST_IS_PAD (pad), NULL);
+
+ GST_CAT_DEBUG (GST_CAT_PADS, "proxying getcaps for %s:%s",
+ GST_DEBUG_PAD_NAME (pad));
+
+ element = gst_pad_get_parent_element (pad);
+ if (element == NULL)
+ goto no_parent;
+
+ /* value to hold the return, by default it holds ANY, the ref is taken by
+ * the GValue. */
+ g_value_init (&ret, G_TYPE_POINTER);
+ g_value_set_pointer (&ret, gst_caps_new_any ());
+
+ /* only iterate the pads in the oposite direction */
+ if (GST_PAD_IS_SRC (pad))
+ iter = gst_element_iterate_sink_pads (element);
+ else
+ iter = gst_element_iterate_src_pads (element);
+
+ while (1) {
+ res =
+ gst_iterator_fold (iter, (GstIteratorFoldFunction) getcaps_fold_func,
+ &ret, filter);
+ switch (res) {
+ case GST_ITERATOR_RESYNC:
+ /* unref any value stored */
+ if ((caps = g_value_get_pointer (&ret)))
+ gst_caps_unref (caps);
+ /* need to reset the result again to ANY */
+ g_value_set_pointer (&ret, gst_caps_new_any ());
+ gst_iterator_resync (iter);
+ break;
+ case GST_ITERATOR_DONE:
+ /* all pads iterated, return collected value */
+ goto done;
+ case GST_ITERATOR_OK:
+ /* premature exit (happens if caps intersection is empty) */
+ goto done;
+ default:
+ /* iterator returned _ERROR, mark an error and exit */
+ if ((caps = g_value_get_pointer (&ret)))
+ gst_caps_unref (caps);
+ g_value_set_pointer (&ret, NULL);
+ goto error;
+ }
+ }
+done:
+ gst_iterator_free (iter);
+
+ gst_object_unref (element);
+
+ caps = g_value_get_pointer (&ret);
+ g_value_unset (&ret);
+
+ if (caps) {
+ intersected =
+ gst_caps_intersect (caps, gst_pad_get_pad_template_caps (pad));
+ gst_caps_unref (caps);
+ } else {
+ intersected = gst_caps_copy (gst_pad_get_pad_template_caps (pad));
+ }
+
+ return intersected;
+
+ /* ERRORS */
+no_parent:
+ {
+ GST_DEBUG_OBJECT (pad, "no parent");
+ return gst_caps_copy (gst_pad_get_pad_template_caps (pad));
+ }
+error:
+ {
+ g_warning ("Pad list returned error on element %s",
+ GST_ELEMENT_NAME (element));
+ gst_iterator_free (iter);
+ gst_object_unref (element);
+ return gst_caps_copy (gst_pad_get_pad_template_caps (pad));
+ }
+}
+
+/**
+ * gst_pad_query_position:
+ * @pad: a #GstPad to invoke the position query on.
+ * @format: the #GstFormat requested
+ * @cur: (out): A location in which to store the current position, or NULL.
+ *
+ * Queries a pad for the stream position.
+ *
+ * Returns: TRUE if the query could be performed.
+ */
+gboolean
+gst_pad_query_position (GstPad * pad, GstFormat format, gint64 * cur)
+{
+ GstQuery *query;
+ gboolean ret;
+
+ g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
+ g_return_val_if_fail (format != GST_FORMAT_UNDEFINED, FALSE);
+
+ query = gst_query_new_position (format);
+ ret = gst_pad_query (pad, query);
+
+ if (ret)
+ gst_query_parse_position (query, NULL, cur);
+
+ gst_query_unref (query);
+
+ return ret;
+}
+
+/**
+ * gst_pad_query_peer_position:
+ * @pad: a #GstPad on whose peer to invoke the position query on.
+ * Must be a sink pad.
+ * @format: the #GstFormat requested
+ * @cur: (out) (allow-none): a location in which to store the current
+ * position, or NULL.
+ *
+ * Queries the peer of a given sink pad for the stream position.
+ *
+ * Returns: TRUE if the query could be performed.
+ */
+gboolean
+gst_pad_query_peer_position (GstPad * pad, GstFormat format, gint64 * cur)
+{
+ gboolean ret = FALSE;
+ GstPad *peer;
+
+ g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
+ g_return_val_if_fail (GST_PAD_IS_SINK (pad), FALSE);
+ g_return_val_if_fail (format != GST_FORMAT_UNDEFINED, FALSE);
+
+ peer = gst_pad_get_peer (pad);
+ if (peer) {
+ ret = gst_pad_query_position (peer, format, cur);
+ gst_object_unref (peer);
+ }
+
+ return ret;
+}
+
+/**
+ * gst_pad_query_duration:
+ * @pad: a #GstPad to invoke the duration query on.
+ * @format: the #GstFormat requested
+ * @duration: (out) (allow-none): a location in which to store the total
+ * duration, or NULL.
+ *
+ * Queries a pad for the total stream duration.
+ *
+ * Returns: TRUE if the query could be performed.
+ */
+gboolean
+gst_pad_query_duration (GstPad * pad, GstFormat format, gint64 * duration)
+{
+ GstQuery *query;
+ gboolean ret;
+
+ g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
+ g_return_val_if_fail (format != GST_FORMAT_UNDEFINED, FALSE);
+
+ query = gst_query_new_duration (format);
+ ret = gst_pad_query (pad, query);
+
+ if (ret)
+ gst_query_parse_duration (query, NULL, duration);
+
+ gst_query_unref (query);
+
+ return ret;
+}
+
+/**
+ * gst_pad_query_peer_duration:
+ * @pad: a #GstPad on whose peer pad to invoke the duration query on.
+ * Must be a sink pad.
+ * @format: the #GstFormat requested
+ * @duration: (out) (allow-none): a location in which to store the total
+ * duration, or NULL.
+ *
+ * Queries the peer pad of a given sink pad for the total stream duration.
+ *
+ * Returns: TRUE if the query could be performed.
+ */
+gboolean
+gst_pad_query_peer_duration (GstPad * pad, GstFormat format, gint64 * duration)
+{
+ gboolean ret = FALSE;
+ GstPad *peer;
+
+ g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
+ g_return_val_if_fail (GST_PAD_IS_SINK (pad), FALSE);
+ g_return_val_if_fail (format != GST_FORMAT_UNDEFINED, FALSE);
+
+ peer = gst_pad_get_peer (pad);
+ if (peer) {
+ ret = gst_pad_query_duration (peer, format, duration);
+ gst_object_unref (peer);
+ }
+
+ return ret;
+}
+
+/**
+ * gst_pad_query_convert:
+ * @pad: a #GstPad to invoke the convert query on.
+ * @src_format: a #GstFormat to convert from.
+ * @src_val: a value to convert.
+ * @dest_format: the #GstFormat to convert to.
+ * @dest_val: (out): a pointer to the result.
+ *
+ * Queries a pad to convert @src_val in @src_format to @dest_format.
+ *
+ * Returns: TRUE if the query could be performed.
+ */
+gboolean
+gst_pad_query_convert (GstPad * pad, GstFormat src_format, gint64 src_val,
+ GstFormat dest_format, gint64 * dest_val)
+{
+ GstQuery *query;
+ gboolean ret;
+
+ g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
+ g_return_val_if_fail (dest_format != GST_FORMAT_UNDEFINED, FALSE);
+ g_return_val_if_fail (dest_val != NULL, FALSE);
+
+ if (dest_format == src_format || src_val == -1) {
+ *dest_val = src_val;
+ return TRUE;
+ }
+
+ query = gst_query_new_convert (src_format, src_val, dest_format);
+ ret = gst_pad_query (pad, query);
+
+ if (ret)
+ gst_query_parse_convert (query, NULL, NULL, NULL, dest_val);
+
+ gst_query_unref (query);
+
+ return ret;
+}
+
+/**
+ * gst_pad_query_peer_convert:
+ * @pad: a #GstPad, on whose peer pad to invoke the convert query on.
+ * Must be a sink pad.
+ * @src_format: a #GstFormat to convert from.
+ * @src_val: a value to convert.
+ * @dest_format: the #GstFormat to convert to.
+ * @dest_val: (out): a pointer to the result.
+ *
+ * Queries the peer pad of a given sink pad to convert @src_val in @src_format
+ * to @dest_format.
+ *
+ * Returns: TRUE if the query could be performed.
+ */
+gboolean
+gst_pad_query_peer_convert (GstPad * pad, GstFormat src_format, gint64 src_val,
+ GstFormat dest_format, gint64 * dest_val)
+{
+ gboolean ret = FALSE;
+ GstPad *peer;
+
+ g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
+ g_return_val_if_fail (GST_PAD_IS_SINK (pad), FALSE);
+ g_return_val_if_fail (dest_format != GST_FORMAT_UNDEFINED, FALSE);
+ g_return_val_if_fail (dest_val != NULL, FALSE);
+
+ peer = gst_pad_get_peer (pad);
+ if (peer) {
+ ret = gst_pad_query_convert (peer, src_format, src_val, dest_format,
+ dest_val);
+ gst_object_unref (peer);
+ }
+
+ return ret;
+}
+
+/**
+ * gst_element_found_tags_for_pad:
+ * @element: element for which to post taglist to bus.
+ * @pad: (transfer none): pad on which to push tag-event
+ * @list: (transfer full): the taglist to post on the bus and create event from
+ *
+ * Posts a message to the bus that new tags were found and pushes the
+ * tags as event. Takes ownership of the @list.
+ *
+ * This is a utility method for elements. Applications should use the
+ * #GstTagSetter interface.
+ */
+void
+gst_element_found_tags_for_pad (GstElement * element,
+ GstPad * pad, GstTagList * list)
+{
+ g_return_if_fail (element != NULL);
+ g_return_if_fail (pad != NULL);
+ g_return_if_fail (list != NULL);
+
+ gst_pad_push_event (pad, gst_event_new_tag (gst_tag_list_copy (list)));
+}
+
+static void
+push_and_ref (const GValue * vpad, GstEvent * event)
+{
+ GstPad *pad = g_value_get_object (vpad);
+
+ gst_pad_push_event (pad, gst_event_ref (event));
+}
+
+/**
+ * gst_element_found_tags:
+ * @element: element for which we found the tags.
+ * @list: (transfer full): list of tags.
+ *
+ * Posts a message to the bus that new tags were found, and pushes an event
+ * to all sourcepads. Takes ownership of the @list.
+ *
+ * This is a utility method for elements. Applications should use the
+ * #GstTagSetter interface.
+ */
+void
+gst_element_found_tags (GstElement * element, GstTagList * list)
+{
+ GstIterator *iter;
+ GstEvent *event;
+
+ g_return_if_fail (element != NULL);
+ g_return_if_fail (list != NULL);
+
+ iter = gst_element_iterate_src_pads (element);
+ event = gst_event_new_tag (gst_tag_list_copy (list));
+ gst_iterator_foreach (iter, (GstIteratorForeachFunction) push_and_ref, event);
+ gst_iterator_free (iter);
+ gst_event_unref (event);
+}
+
+static GstPad *
+element_find_unlinked_pad (GstElement * element, GstPadDirection direction)
+{
+ GstIterator *iter;
+ GstPad *unlinked_pad = NULL;
+ gboolean done;
+ GValue data = { 0, };
+
+ switch (direction) {
+ case GST_PAD_SRC:
+ iter = gst_element_iterate_src_pads (element);
+ break;
+ case GST_PAD_SINK:
+ iter = gst_element_iterate_sink_pads (element);
+ break;
+ default:
+ g_return_val_if_reached (NULL);
+ }
+
+ done = FALSE;
+ while (!done) {
+ switch (gst_iterator_next (iter, &data)) {
+ case GST_ITERATOR_OK:{
+ GstPad *peer;
+ GstPad *pad = g_value_get_object (&data);
+
+ GST_CAT_LOG (GST_CAT_ELEMENT_PADS, "examining pad %s:%s",
+ GST_DEBUG_PAD_NAME (pad));
+
+ peer = gst_pad_get_peer (pad);
+ if (peer == NULL) {
+ unlinked_pad = gst_object_ref (pad);
+ done = TRUE;
+ GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS,
+ "found existing unlinked pad %s:%s",
+ GST_DEBUG_PAD_NAME (unlinked_pad));
+ } else {
+ gst_object_unref (peer);
+ }
+ g_value_reset (&data);
+ break;
+ }
+ case GST_ITERATOR_DONE:
+ done = TRUE;
+ break;
+ case GST_ITERATOR_RESYNC:
+ gst_iterator_resync (iter);
+ break;
+ case GST_ITERATOR_ERROR:
+ g_return_val_if_reached (NULL);
+ break;
+ }
+ }
+ g_value_unset (&data);
+ gst_iterator_free (iter);
+
+ return unlinked_pad;
+}
+
+/**
+ * gst_bin_find_unlinked_pad:
+ * @bin: bin in which to look for elements with unlinked pads
+ * @direction: whether to look for an unlinked source or sink pad
+ *
+ * Recursively looks for elements with an unlinked pad of the given
+ * direction within the specified bin and returns an unlinked pad
+ * if one is found, or NULL otherwise. If a pad is found, the caller
+ * owns a reference to it and should use gst_object_unref() on the
+ * pad when it is not needed any longer.
+ *
+ * Returns: (transfer full): unlinked pad of the given direction, or NULL.
+ *
+ * Since: 0.10.20
+ */
+GstPad *
+gst_bin_find_unlinked_pad (GstBin * bin, GstPadDirection direction)
+{
+ GstIterator *iter;
+ gboolean done;
+ GstPad *pad = NULL;
+ GValue data = { 0, };
+
+ g_return_val_if_fail (GST_IS_BIN (bin), NULL);
+ g_return_val_if_fail (direction != GST_PAD_UNKNOWN, NULL);
+
+ done = FALSE;
+ iter = gst_bin_iterate_recurse (bin);
+ while (!done) {
+ switch (gst_iterator_next (iter, &data)) {
+ case GST_ITERATOR_OK:{
+ GstElement *element = g_value_get_object (&data);
+
+ pad = element_find_unlinked_pad (element, direction);
+ if (pad != NULL)
+ done = TRUE;
+ g_value_reset (&data);
+ break;
+ }
+ case GST_ITERATOR_DONE:
+ done = TRUE;
+ break;
+ case GST_ITERATOR_RESYNC:
+ gst_iterator_resync (iter);
+ break;
+ case GST_ITERATOR_ERROR:
+ g_return_val_if_reached (NULL);
+ break;
+ }
+ }
+ g_value_unset (&data);
+ gst_iterator_free (iter);
+
+ return pad;
+}
+
+/**
+ * gst_parse_bin_from_description:
+ * @bin_description: command line describing the bin
+ * @ghost_unlinked_pads: whether to automatically create ghost pads
+ * for unlinked source or sink pads within the bin
+ * @err: where to store the error message in case of an error, or NULL
+ *
+ * This is a convenience wrapper around gst_parse_launch() to create a
+ * #GstBin from a gst-launch-style pipeline description. See
+ * gst_parse_launch() and the gst-launch man page for details about the
+ * syntax. Ghost pads on the bin for unlinked source or sink pads
+ * within the bin can automatically be created (but only a maximum of
+ * one ghost pad for each direction will be created; if you expect
+ * multiple unlinked source pads or multiple unlinked sink pads
+ * and want them all ghosted, you will have to create the ghost pads
+ * yourself).
+ *
+ * Returns: (transfer full): a newly-created bin, or NULL if an error occurred.
+ *
+ * Since: 0.10.3
+ */
+GstElement *
+gst_parse_bin_from_description (const gchar * bin_description,
+ gboolean ghost_unlinked_pads, GError ** err)
+{
+ return gst_parse_bin_from_description_full (bin_description,
+ ghost_unlinked_pads, NULL, GST_PARSE_FLAG_NONE, err);
+}
+
+/**
+ * gst_parse_bin_from_description_full:
+ * @bin_description: command line describing the bin
+ * @ghost_unlinked_pads: whether to automatically create ghost pads
+ * for unlinked source or sink pads within the bin
+ * @context: (transfer none) (allow-none): a parse context allocated with
+ * gst_parse_context_new(), or %NULL
+ * @flags: parsing options, or #GST_PARSE_FLAG_NONE
+ * @err: where to store the error message in case of an error, or NULL
+ *
+ * This is a convenience wrapper around gst_parse_launch() to create a
+ * #GstBin from a gst-launch-style pipeline description. See
+ * gst_parse_launch() and the gst-launch man page for details about the
+ * syntax. Ghost pads on the bin for unlinked source or sink pads
+ * within the bin can automatically be created (but only a maximum of
+ * one ghost pad for each direction will be created; if you expect
+ * multiple unlinked source pads or multiple unlinked sink pads
+ * and want them all ghosted, you will have to create the ghost pads
+ * yourself).
+ *
+ * Returns: (transfer full): a newly-created bin, or NULL if an error occurred.
+ *
+ * Since: 0.10.20
+ */
+GstElement *
+gst_parse_bin_from_description_full (const gchar * bin_description,
+ gboolean ghost_unlinked_pads, GstParseContext * context,
+ GstParseFlags flags, GError ** err)
+{
+#ifndef GST_DISABLE_PARSE
+ GstPad *pad = NULL;
+ GstBin *bin;
+ gchar *desc;
+
+ g_return_val_if_fail (bin_description != NULL, NULL);
+ g_return_val_if_fail (err == NULL || *err == NULL, NULL);
+
+ GST_DEBUG ("Making bin from description '%s'", bin_description);
+
+ /* parse the pipeline to a bin */
+ desc = g_strdup_printf ("bin.( %s )", bin_description);
+ bin = (GstBin *) gst_parse_launch_full (desc, context, flags, err);
+ g_free (desc);
+
+ if (bin == NULL || (err && *err != NULL)) {
+ if (bin)
+ gst_object_unref (bin);
+ return NULL;
+ }
+
+ /* find pads and ghost them if necessary */
+ if (ghost_unlinked_pads) {
+ if ((pad = gst_bin_find_unlinked_pad (bin, GST_PAD_SRC))) {
+ gst_element_add_pad (GST_ELEMENT (bin), gst_ghost_pad_new ("src", pad));
+ gst_object_unref (pad);
+ }
+ if ((pad = gst_bin_find_unlinked_pad (bin, GST_PAD_SINK))) {
+ gst_element_add_pad (GST_ELEMENT (bin), gst_ghost_pad_new ("sink", pad));
+ gst_object_unref (pad);
+ }
+ }
+
+ return GST_ELEMENT (bin);
+#else
+ gchar *msg;
+
+ GST_WARNING ("Disabled API called");
+
+ msg = gst_error_get_message (GST_CORE_ERROR, GST_CORE_ERROR_DISABLED);
+ g_set_error (err, GST_CORE_ERROR, GST_CORE_ERROR_DISABLED, "%s", msg);
+ g_free (msg);
+
+ return NULL;
+#endif
+}
+
+/**
+ * gst_type_register_static_full:
+ * @parent_type: The GType of the parent type the newly registered type will
+ * derive from
+ * @type_name: NULL-terminated string used as the name of the new type
+ * @class_size: Size of the class structure.
+ * @base_init: Location of the base initialization function (optional).
+ * @base_finalize: Location of the base finalization function (optional).
+ * @class_init: Location of the class initialization function for class types
+ * Location of the default vtable initialization function for interface
+ * types. (optional)
+ * @class_finalize: Location of the class finalization function for class types.
+ * Location of the default vtable finalization function for interface types.
+ * (optional)
+ * @class_data: User-supplied data passed to the class init/finalize functions.
+ * @instance_size: Size of the instance (object) structure (required for
+ * instantiatable types only).
+ * @n_preallocs: The number of pre-allocated (cached) instances to reserve
+ * memory for (0 indicates no caching). Ignored on recent GLib's.
+ * @instance_init: Location of the instance initialization function (optional,
+ * for instantiatable types only).
+ * @value_table: A GTypeValueTable function table for generic handling of
+ * GValues of this type (usually only useful for fundamental types).
+ * @flags: #GTypeFlags for this GType. E.g: G_TYPE_FLAG_ABSTRACT
+ *
+ * Helper function which constructs a #GTypeInfo structure and registers a
+ * GType, but which generates less linker overhead than a static const
+ * #GTypeInfo structure. For further details of the parameters, please see
+ * #GTypeInfo in the GLib documentation.
+ *
+ * Registers type_name as the name of a new static type derived from
+ * parent_type. The value of flags determines the nature (e.g. abstract or
+ * not) of the type. It works by filling a GTypeInfo struct and calling
+ * g_type_register_static().
+ *
+ * Returns: A #GType for the newly-registered type.
+ *
+ * Since: 0.10.14
+ */
+GType
+gst_type_register_static_full (GType parent_type,
+ const gchar * type_name,
+ guint class_size,
+ GBaseInitFunc base_init,
+ GBaseFinalizeFunc base_finalize,
+ GClassInitFunc class_init,
+ GClassFinalizeFunc class_finalize,
+ gconstpointer class_data,
+ guint instance_size,
+ guint16 n_preallocs,
+ GInstanceInitFunc instance_init,
+ const GTypeValueTable * value_table, GTypeFlags flags)
+{
+ GTypeInfo info;
+
+ info.class_size = class_size;
+ info.base_init = base_init;
+ info.base_finalize = base_finalize;
+ info.class_init = class_init;
+ info.class_finalize = class_finalize;
+ info.class_data = class_data;
+ info.instance_size = instance_size;
+ info.n_preallocs = n_preallocs;
+ info.instance_init = instance_init;
+ info.value_table = value_table;
+
+ return g_type_register_static (parent_type, type_name, &info, flags);
+}
+
+
+/**
+ * gst_util_get_timestamp:
+ *
+ * Get a timestamp as GstClockTime to be used for interval measurements.
+ * The timestamp should not be interpreted in any other way.
+ *
+ * Returns: the timestamp
+ *
+ * Since: 0.10.16
+ */
+GstClockTime
+gst_util_get_timestamp (void)
+{
+#if defined (HAVE_POSIX_TIMERS) && defined(HAVE_MONOTONIC_CLOCK)
+ struct timespec now;
+
+ clock_gettime (CLOCK_MONOTONIC, &now);
+ return GST_TIMESPEC_TO_TIME (now);
+#else
+ GTimeVal now;
+
+ g_get_current_time (&now);
+ return GST_TIMEVAL_TO_TIME (now);
+#endif
+}
+
+/**
+ * gst_util_array_binary_search:
+ * @array: the sorted input array
+ * @num_elements: number of elements in the array
+ * @element_size: size of every element in bytes
+ * @search_func: (scope call): function to compare two elements, @search_data will always be passed as second argument
+ * @mode: search mode that should be used
+ * @search_data: element that should be found
+ * @user_data: (closure): data to pass to @search_func
+ *
+ * Searches inside @array for @search_data by using the comparison function
+ * @search_func. @array must be sorted ascending.
+ *
+ * As @search_data is always passed as second argument to @search_func it's
+ * not required that @search_data has the same type as the array elements.
+ *
+ * The complexity of this search function is O(log (num_elements)).
+ *
+ * Returns: (transfer none): The address of the found element or %NULL if nothing was found
+ *
+ * Since: 0.10.23
+ */
+gpointer
+gst_util_array_binary_search (gpointer array, guint num_elements,
+ gsize element_size, GCompareDataFunc search_func, GstSearchMode mode,
+ gconstpointer search_data, gpointer user_data)
+{
+ glong left = 0, right = num_elements - 1, m;
+ gint ret;
+ guint8 *data = (guint8 *) array;
+
+ g_return_val_if_fail (array != NULL, NULL);
+ g_return_val_if_fail (element_size > 0, NULL);
+ g_return_val_if_fail (search_func != NULL, NULL);
+
+ /* 0. No elements => return NULL */
+ if (num_elements == 0)
+ return NULL;
+
+ /* 1. If search_data is before the 0th element return the 0th element */
+ ret = search_func (data, search_data, user_data);
+ if ((ret >= 0 && mode == GST_SEARCH_MODE_AFTER) || ret == 0)
+ return data;
+ else if (ret > 0)
+ return NULL;
+
+ /* 2. If search_data is after the last element return the last element */
+ ret =
+ search_func (data + (num_elements - 1) * element_size, search_data,
+ user_data);
+ if ((ret <= 0 && mode == GST_SEARCH_MODE_BEFORE) || ret == 0)
+ return data + (num_elements - 1) * element_size;
+ else if (ret < 0)
+ return NULL;
+
+ /* 3. else binary search */
+ while (TRUE) {
+ m = left + (right - left) / 2;
+
+ ret = search_func (data + m * element_size, search_data, user_data);
+
+ if (ret == 0) {
+ return data + m * element_size;
+ } else if (ret < 0) {
+ left = m + 1;
+ } else {
+ right = m - 1;
+ }
+
+ /* No exact match found */
+ if (right < left) {
+ if (mode == GST_SEARCH_MODE_EXACT) {
+ return NULL;
+ } else if (mode == GST_SEARCH_MODE_AFTER) {
+ if (ret < 0)
+ return (m < num_elements) ? data + (m + 1) * element_size : NULL;
+ else
+ return data + m * element_size;
+ } else {
+ if (ret < 0)
+ return data + m * element_size;
+ else
+ return (m > 0) ? data + (m - 1) * element_size : NULL;
+ }
+ }
+ }
+}
+
+/* Finds the greatest common divisor.
+ * Returns 1 if none other found.
+ * This is Euclid's algorithm. */
+
+/**
+ * gst_util_greatest_common_divisor:
+ * @a: First value as #gint
+ * @b: Second value as #gint
+ *
+ * Calculates the greatest common divisor of @a
+ * and @b.
+ *
+ * Returns: Greatest common divisor of @a and @b
+ *
+ * Since: 0.10.26
+ */
+gint
+gst_util_greatest_common_divisor (gint a, gint b)
+{
+ while (b != 0) {
+ int temp = a;
+
+ a = b;
+ b = temp % b;
+ }
+
+ return ABS (a);
+}
+
+/**
+ * gst_util_fraction_to_double:
+ * @src_n: Fraction numerator as #gint
+ * @src_d: Fraction denominator #gint
+ * @dest: (out): pointer to a #gdouble for the result
+ *
+ * Transforms a fraction to a #gdouble.
+ *
+ * Since: 0.10.26
+ */
+void
+gst_util_fraction_to_double (gint src_n, gint src_d, gdouble * dest)
+{
+ g_return_if_fail (dest != NULL);
+ g_return_if_fail (src_d != 0);
+
+ *dest = ((gdouble) src_n) / ((gdouble) src_d);
+}
+
+#define MAX_TERMS 30
+#define MIN_DIVISOR 1.0e-10
+#define MAX_ERROR 1.0e-20
+
+/* use continued fractions to transform a double into a fraction,
+ * see http://mathforum.org/dr.math/faq/faq.fractions.html#decfrac.
+ * This algorithm takes care of overflows.
+ */
+
+/**
+ * gst_util_double_to_fraction:
+ * @src: #gdouble to transform
+ * @dest_n: (out): pointer to a #gint to hold the result numerator
+ * @dest_d: (out): pointer to a #gint to hold the result denominator
+ *
+ * Transforms a #gdouble to a fraction and simplifies
+ * the result.
+ *
+ * Since: 0.10.26
+ */
+void
+gst_util_double_to_fraction (gdouble src, gint * dest_n, gint * dest_d)
+{
+
+ gdouble V, F; /* double being converted */
+ gint N, D; /* will contain the result */
+ gint A; /* current term in continued fraction */
+ gint64 N1, D1; /* numerator, denominator of last approx */
+ gint64 N2, D2; /* numerator, denominator of previous approx */
+ gint i;
+ gint gcd;
+ gboolean negative = FALSE;
+
+ g_return_if_fail (dest_n != NULL);
+ g_return_if_fail (dest_d != NULL);
+
+ /* initialize fraction being converted */
+ F = src;
+ if (F < 0.0) {
+ F = -F;
+ negative = TRUE;
+ }
+
+ V = F;
+ /* initialize fractions with 1/0, 0/1 */
+ N1 = 1;
+ D1 = 0;
+ N2 = 0;
+ D2 = 1;
+ N = 1;
+ D = 1;
+
+ for (i = 0; i < MAX_TERMS; i++) {
+ /* get next term */
+ A = (gint) F; /* no floor() needed, F is always >= 0 */
+ /* get new divisor */
+ F = F - A;
+
+ /* calculate new fraction in temp */
+ N2 = N1 * A + N2;
+ D2 = D1 * A + D2;
+
+ /* guard against overflow */
+ if (N2 > G_MAXINT || D2 > G_MAXINT) {
+ break;
+ }
+
+ N = N2;
+ D = D2;
+
+ /* save last two fractions */
+ N2 = N1;
+ D2 = D1;
+ N1 = N;
+ D1 = D;
+
+ /* quit if dividing by zero or close enough to target */
+ if (F < MIN_DIVISOR || fabs (V - ((gdouble) N) / D) < MAX_ERROR) {
+ break;
+ }
+
+ /* Take reciprocal */
+ F = 1 / F;
+ }
+ /* fix for overflow */
+ if (D == 0) {
+ N = G_MAXINT;
+ D = 1;
+ }
+ /* fix for negative */
+ if (negative)
+ N = -N;
+
+ /* simplify */
+ gcd = gst_util_greatest_common_divisor (N, D);
+ if (gcd) {
+ N /= gcd;
+ D /= gcd;
+ }
+
+ /* set results */
+ *dest_n = N;
+ *dest_d = D;
+}
+
+/**
+ * gst_util_fraction_multiply:
+ * @a_n: Numerator of first value
+ * @a_d: Denominator of first value
+ * @b_n: Numerator of second value
+ * @b_d: Denominator of second value
+ * @res_n: (out): Pointer to #gint to hold the result numerator
+ * @res_d: (out): Pointer to #gint to hold the result denominator
+ *
+ * Multiplies the fractions @a_n/@a_d and @b_n/@b_d and stores
+ * the result in @res_n and @res_d.
+ *
+ * Returns: %FALSE on overflow, %TRUE otherwise.
+ *
+ * Since: 0.10.26
+ */
+gboolean
+gst_util_fraction_multiply (gint a_n, gint a_d, gint b_n, gint b_d,
+ gint * res_n, gint * res_d)
+{
+ gint gcd;
+
+ g_return_val_if_fail (res_n != NULL, FALSE);
+ g_return_val_if_fail (res_d != NULL, FALSE);
+ g_return_val_if_fail (a_d != 0, FALSE);
+ g_return_val_if_fail (b_d != 0, FALSE);
+
+ gcd = gst_util_greatest_common_divisor (a_n, a_d);
+ a_n /= gcd;
+ a_d /= gcd;
+
+ gcd = gst_util_greatest_common_divisor (b_n, b_d);
+ b_n /= gcd;
+ b_d /= gcd;
+
+ gcd = gst_util_greatest_common_divisor (a_n, b_d);
+ a_n /= gcd;
+ b_d /= gcd;
+
+ gcd = gst_util_greatest_common_divisor (a_d, b_n);
+ a_d /= gcd;
+ b_n /= gcd;
+
+ /* This would result in overflow */
+ if (a_n != 0 && G_MAXINT / ABS (a_n) < ABS (b_n))
+ return FALSE;
+ if (G_MAXINT / ABS (a_d) < ABS (b_d))
+ return FALSE;
+
+ *res_n = a_n * b_n;
+ *res_d = a_d * b_d;
+
+ gcd = gst_util_greatest_common_divisor (*res_n, *res_d);
+ *res_n /= gcd;
+ *res_d /= gcd;
+
+ return TRUE;
+}
+
+/**
+ * gst_util_fraction_add:
+ * @a_n: Numerator of first value
+ * @a_d: Denominator of first value
+ * @b_n: Numerator of second value
+ * @b_d: Denominator of second value
+ * @res_n: (out): Pointer to #gint to hold the result numerator
+ * @res_d: (out): Pointer to #gint to hold the result denominator
+ *
+ * Adds the fractions @a_n/@a_d and @b_n/@b_d and stores
+ * the result in @res_n and @res_d.
+ *
+ * Returns: %FALSE on overflow, %TRUE otherwise.
+ *
+ * Since: 0.10.26
+ */
+gboolean
+gst_util_fraction_add (gint a_n, gint a_d, gint b_n, gint b_d, gint * res_n,
+ gint * res_d)
+{
+ gint gcd;
+
+ g_return_val_if_fail (res_n != NULL, FALSE);
+ g_return_val_if_fail (res_d != NULL, FALSE);
+ g_return_val_if_fail (a_d != 0, FALSE);
+ g_return_val_if_fail (b_d != 0, FALSE);
+
+ gcd = gst_util_greatest_common_divisor (a_n, a_d);
+ a_n /= gcd;
+ a_d /= gcd;
+
+ gcd = gst_util_greatest_common_divisor (b_n, b_d);
+ b_n /= gcd;
+ b_d /= gcd;
+
+ if (a_n == 0) {
+ *res_n = b_n;
+ *res_d = b_d;
+ return TRUE;
+ }
+ if (b_n == 0) {
+ *res_n = a_n;
+ *res_d = a_d;
+ return TRUE;
+ }
+
+ /* This would result in overflow */
+ if (G_MAXINT / ABS (a_n) < ABS (b_n))
+ return FALSE;
+ if (G_MAXINT / ABS (a_d) < ABS (b_d))
+ return FALSE;
+ if (G_MAXINT / ABS (a_d) < ABS (b_d))
+ return FALSE;
+
+ *res_n = (a_n * b_d) + (a_d * b_n);
+ *res_d = a_d * b_d;
+
+ gcd = gst_util_greatest_common_divisor (*res_n, *res_d);
+ if (gcd) {
+ *res_n /= gcd;
+ *res_d /= gcd;
+ } else {
+ /* res_n == 0 */
+ *res_d = 1;
+ }
+
+ return TRUE;
+}
+
+/**
+ * gst_util_fraction_compare:
+ * @a_n: Numerator of first value
+ * @a_d: Denominator of first value
+ * @b_n: Numerator of second value
+ * @b_d: Denominator of second value
+ *
+ * Compares the fractions @a_n/@a_d and @b_n/@b_d and returns
+ * -1 if a < b, 0 if a = b and 1 if a > b.
+ *
+ * Returns: -1 if a < b; 0 if a = b; 1 if a > b.
+ *
+ * Since: 0.10.31
+ */
+gint
+gst_util_fraction_compare (gint a_n, gint a_d, gint b_n, gint b_d)
+{
+ gint64 new_num_1;
+ gint64 new_num_2;
+ gint gcd;
+
+ g_return_val_if_fail (a_d != 0 && b_d != 0, 0);
+
+ /* Simplify */
+ gcd = gst_util_greatest_common_divisor (a_n, a_d);
+ a_n /= gcd;
+ a_d /= gcd;
+
+ gcd = gst_util_greatest_common_divisor (b_n, b_d);
+ b_n /= gcd;
+ b_d /= gcd;
+
+ /* fractions are reduced when set, so we can quickly see if they're equal */
+ if (a_n == b_n && a_d == b_d)
+ return 0;
+
+ /* extend to 64 bits */
+ new_num_1 = ((gint64) a_n) * b_d;
+ new_num_2 = ((gint64) b_n) * a_d;
+ if (new_num_1 < new_num_2)
+ return -1;
+ if (new_num_1 > new_num_2)
+ return 1;
+
+ /* Should not happen because a_d and b_d are not 0 */
+ g_return_val_if_reached (0);
+}
diff --git a/gst/gstutils.h b/gst/gstutils.h
new file mode 100644
index 0000000..8f1445a
--- /dev/null
+++ b/gst/gstutils.h
@@ -0,0 +1,983 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2000 Wim Taymans <wtay@chello.be>
+ * 2002 Thomas Vander Stichele <thomas@apestaart.org>
+ *
+ * gstutils.h: Header for various utility functions
+ *
+ * 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_UTILS_H__
+#define __GST_UTILS_H__
+
+#include <glib.h>
+#include <gst/gstbin.h>
+#include <gst/gstparse.h>
+
+G_BEGIN_DECLS
+
+void gst_util_set_value_from_string (GValue *value, const gchar *value_str);
+void gst_util_set_object_arg (GObject *object, const gchar *name, const gchar *value);
+void gst_util_dump_mem (const guchar *mem, guint size);
+
+guint64 gst_util_gdouble_to_guint64 (gdouble value) G_GNUC_CONST;
+gdouble gst_util_guint64_to_gdouble (guint64 value) G_GNUC_CONST;
+
+/**
+ * gst_guint64_to_gdouble:
+ * @value: the #guint64 value to convert
+ *
+ * Convert @value to a gdouble.
+ *
+ * Returns: @value converted to a #gdouble.
+ */
+
+/**
+ * gst_gdouble_to_guint64:
+ * @value: the #gdouble value to convert
+ *
+ * Convert @value to a guint64.
+ *
+ * Returns: @value converted to a #guint64.
+ */
+#ifdef WIN32
+#define gst_gdouble_to_guint64(value) gst_util_gdouble_to_guint64(value)
+#define gst_guint64_to_gdouble(value) gst_util_guint64_to_gdouble(value)
+#else
+#define gst_gdouble_to_guint64(value) ((guint64) (value))
+#define gst_guint64_to_gdouble(value) ((gdouble) (value))
+#endif
+
+guint64 gst_util_uint64_scale (guint64 val, guint64 num, guint64 denom);
+guint64 gst_util_uint64_scale_round (guint64 val, guint64 num, guint64 denom);
+guint64 gst_util_uint64_scale_ceil (guint64 val, guint64 num, guint64 denom);
+
+guint64 gst_util_uint64_scale_int (guint64 val, gint num, gint denom);
+guint64 gst_util_uint64_scale_int_round (guint64 val, gint num, gint denom);
+guint64 gst_util_uint64_scale_int_ceil (guint64 val, gint num, gint denom);
+
+guint32 gst_util_seqnum_next (void);
+gint32 gst_util_seqnum_compare (guint32 s1, guint32 s2);
+
+void gst_print_pad_caps (GString *buf, gint indent, GstPad *pad);
+void gst_print_element_args (GString *buf, gint indent, GstElement *element);
+
+
+GType gst_type_register_static_full (GType parent_type,
+ const gchar *type_name,
+ guint class_size,
+ GBaseInitFunc base_init,
+ GBaseFinalizeFunc base_finalize,
+ GClassInitFunc class_init,
+ GClassFinalizeFunc class_finalize,
+ gconstpointer class_data,
+ guint instance_size,
+ guint16 n_preallocs,
+ GInstanceInitFunc instance_init,
+ const GTypeValueTable *value_table,
+ GTypeFlags flags);
+
+/**
+ * GST_CALL_PARENT:
+ * @parent_class_cast: the name of the class cast macro for the parent type
+ * @name: name of the function to call
+ * @args: arguments enclosed in '( )'
+ *
+ * Just call the parent handler. This assumes that there is a variable
+ * named parent_class that points to the (duh!) parent class. Note that
+ * this macro is not to be used with things that return something, use
+ * the _WITH_DEFAULT version for that
+ */
+#define GST_CALL_PARENT(parent_class_cast, name, args) \
+ ((parent_class_cast(parent_class)->name != NULL) ? \
+ parent_class_cast(parent_class)->name args : (void) 0)
+
+/**
+ * GST_CALL_PARENT_WITH_DEFAULT:
+ * @parent_class_cast: the name of the class cast macro for the parent type
+ * @name: name of the function to call
+ * @args: arguments enclosed in '( )'
+ * @def_return: default result
+ *
+ * Same as GST_CALL_PARENT(), but in case there is no implementation, it
+ * evaluates to @def_return.
+ */
+#define GST_CALL_PARENT_WITH_DEFAULT(parent_class_cast, name, args, def_return)\
+ ((parent_class_cast(parent_class)->name != NULL) ? \
+ parent_class_cast(parent_class)->name args : def_return)
+
+/* Define PUT and GET functions for unaligned memory */
+#define _GST_GET(__data, __idx, __size, __shift) \
+ (((guint##__size) (((const guint8 *) (__data))[__idx])) << (__shift))
+
+#define _GST_PUT(__data, __idx, __size, __shift, __num) \
+ (((guint8 *) (__data))[__idx] = (((guint##__size) (__num)) >> (__shift)) & 0xff)
+
+/**
+ * GST_READ_UINT64_BE:
+ * @data: memory location
+ *
+ * Read a 64 bit unsigned integer value in big endian format from the memory buffer.
+ */
+#define GST_READ_UINT64_BE(data) (_GST_GET (data, 0, 64, 56) | \
+ _GST_GET (data, 1, 64, 48) | \
+ _GST_GET (data, 2, 64, 40) | \
+ _GST_GET (data, 3, 64, 32) | \
+ _GST_GET (data, 4, 64, 24) | \
+ _GST_GET (data, 5, 64, 16) | \
+ _GST_GET (data, 6, 64, 8) | \
+ _GST_GET (data, 7, 64, 0))
+
+/**
+ * GST_READ_UINT64_LE:
+ * @data: memory location
+ *
+ * Read a 64 bit unsigned integer value in little endian format from the memory buffer.
+ */
+#define GST_READ_UINT64_LE(data) (_GST_GET (data, 7, 64, 56) | \
+ _GST_GET (data, 6, 64, 48) | \
+ _GST_GET (data, 5, 64, 40) | \
+ _GST_GET (data, 4, 64, 32) | \
+ _GST_GET (data, 3, 64, 24) | \
+ _GST_GET (data, 2, 64, 16) | \
+ _GST_GET (data, 1, 64, 8) | \
+ _GST_GET (data, 0, 64, 0))
+
+/**
+ * GST_READ_UINT32_BE:
+ * @data: memory location
+ *
+ * Read a 32 bit unsigned integer value in big endian format from the memory buffer.
+ */
+#define GST_READ_UINT32_BE(data) (_GST_GET (data, 0, 32, 24) | \
+ _GST_GET (data, 1, 32, 16) | \
+ _GST_GET (data, 2, 32, 8) | \
+ _GST_GET (data, 3, 32, 0))
+
+/**
+ * GST_READ_UINT32_LE:
+ * @data: memory location
+ *
+ * Read a 32 bit unsigned integer value in little endian format from the memory buffer.
+ */
+#define GST_READ_UINT32_LE(data) (_GST_GET (data, 3, 32, 24) | \
+ _GST_GET (data, 2, 32, 16) | \
+ _GST_GET (data, 1, 32, 8) | \
+ _GST_GET (data, 0, 32, 0))
+
+/**
+ * GST_READ_UINT24_BE:
+ * @data: memory location
+ *
+ * Read a 24 bit unsigned integer value in big endian format from the memory buffer.
+ *
+ * Since: 0.10.22
+ */
+#define GST_READ_UINT24_BE(data) (_GST_GET (data, 0, 32, 16) | \
+ _GST_GET (data, 1, 32, 8) | \
+ _GST_GET (data, 2, 32, 0))
+
+/**
+ * GST_READ_UINT24_LE:
+ * @data: memory location
+ *
+ * Read a 24 bit unsigned integer value in little endian format from the memory buffer.
+ *
+ * Since: 0.10.22
+ */
+#define GST_READ_UINT24_LE(data) (_GST_GET (data, 2, 32, 16) | \
+ _GST_GET (data, 1, 32, 8) | \
+ _GST_GET (data, 0, 32, 0))
+
+/**
+ * GST_READ_UINT16_BE:
+ * @data: memory location
+ *
+ * Read a 16 bit unsigned integer value in big endian format from the memory buffer.
+ */
+#define GST_READ_UINT16_BE(data) (_GST_GET (data, 0, 16, 8) | \
+ _GST_GET (data, 1, 16, 0))
+
+/**
+ * GST_READ_UINT16_LE:
+ * @data: memory location
+ *
+ * Read a 16 bit unsigned integer value in little endian format from the memory buffer.
+ */
+#define GST_READ_UINT16_LE(data) (_GST_GET (data, 1, 16, 8) | \
+ _GST_GET (data, 0, 16, 0))
+
+/**
+ * GST_READ_UINT8:
+ * @data: memory location
+ *
+ * Read an 8 bit unsigned integer value from the memory buffer.
+ */
+#define GST_READ_UINT8(data) (_GST_GET (data, 0, 8, 0))
+
+/**
+ * GST_WRITE_UINT64_BE:
+ * @data: memory location
+ * @num: value to store
+ *
+ * Store a 64 bit unsigned integer value in big endian format into the memory buffer.
+ */
+#define GST_WRITE_UINT64_BE(data, num) do { \
+ _GST_PUT (data, 0, 64, 56, num); \
+ _GST_PUT (data, 1, 64, 48, num); \
+ _GST_PUT (data, 2, 64, 40, num); \
+ _GST_PUT (data, 3, 64, 32, num); \
+ _GST_PUT (data, 4, 64, 24, num); \
+ _GST_PUT (data, 5, 64, 16, num); \
+ _GST_PUT (data, 6, 64, 8, num); \
+ _GST_PUT (data, 7, 64, 0, num); \
+ } while (0)
+
+/**
+ * GST_WRITE_UINT64_LE:
+ * @data: memory location
+ * @num: value to store
+ *
+ * Store a 64 bit unsigned integer value in little endian format into the memory buffer.
+ */
+#define GST_WRITE_UINT64_LE(data, num) do { \
+ _GST_PUT (data, 0, 64, 0, num); \
+ _GST_PUT (data, 1, 64, 8, num); \
+ _GST_PUT (data, 2, 64, 16, num); \
+ _GST_PUT (data, 3, 64, 24, num); \
+ _GST_PUT (data, 4, 64, 32, num); \
+ _GST_PUT (data, 5, 64, 40, num); \
+ _GST_PUT (data, 6, 64, 48, num); \
+ _GST_PUT (data, 7, 64, 56, num); \
+ } while (0)
+
+/**
+ * GST_WRITE_UINT32_BE:
+ * @data: memory location
+ * @num: value to store
+ *
+ * Store a 32 bit unsigned integer value in big endian format into the memory buffer.
+ */
+#define GST_WRITE_UINT32_BE(data, num) do { \
+ _GST_PUT (data, 0, 32, 24, num); \
+ _GST_PUT (data, 1, 32, 16, num); \
+ _GST_PUT (data, 2, 32, 8, num); \
+ _GST_PUT (data, 3, 32, 0, num); \
+ } while (0)
+
+/**
+ * GST_WRITE_UINT32_LE:
+ * @data: memory location
+ * @num: value to store
+ *
+ * Store a 32 bit unsigned integer value in little endian format into the memory buffer.
+ */
+#define GST_WRITE_UINT32_LE(data, num) do { \
+ _GST_PUT (data, 0, 32, 0, num); \
+ _GST_PUT (data, 1, 32, 8, num); \
+ _GST_PUT (data, 2, 32, 16, num); \
+ _GST_PUT (data, 3, 32, 24, num); \
+ } while (0)
+
+/**
+ * GST_WRITE_UINT24_BE:
+ * @data: memory location
+ * @num: value to store
+ *
+ * Store a 24 bit unsigned integer value in big endian format into the memory buffer.
+ *
+ * Since: 0.10.22
+ */
+#define GST_WRITE_UINT24_BE(data, num) do { \
+ _GST_PUT (data, 0, 32, 16, num); \
+ _GST_PUT (data, 1, 32, 8, num); \
+ _GST_PUT (data, 2, 32, 0, num); \
+ } while (0)
+
+/**
+ * GST_WRITE_UINT24_LE:
+ * @data: memory location
+ * @num: value to store
+ *
+ * Store a 24 bit unsigned integer value in little endian format into the memory buffer.
+ *
+ * Since: 0.10.22
+ */
+#define GST_WRITE_UINT24_LE(data, num) do { \
+ _GST_PUT (data, 0, 32, 0, num); \
+ _GST_PUT (data, 1, 32, 8, num); \
+ _GST_PUT (data, 2, 32, 16, num); \
+ } while (0)
+
+/**
+ * GST_WRITE_UINT16_BE:
+ * @data: memory location
+ * @num: value to store
+ *
+ * Store a 16 bit unsigned integer value in big endian format into the memory buffer.
+ */
+#define GST_WRITE_UINT16_BE(data, num) do { \
+ _GST_PUT (data, 0, 16, 8, num); \
+ _GST_PUT (data, 1, 16, 0, num); \
+ } while (0)
+
+/**
+ * GST_WRITE_UINT16_LE:
+ * @data: memory location
+ * @num: value to store
+ *
+ * Store a 16 bit unsigned integer value in little endian format into the memory buffer.
+ */
+#define GST_WRITE_UINT16_LE(data, num) do { \
+ _GST_PUT (data, 0, 16, 0, num); \
+ _GST_PUT (data, 1, 16, 8, num); \
+ } while (0)
+
+/**
+ * GST_WRITE_UINT8:
+ * @data: memory location
+ * @num: value to store
+ *
+ * Store an 8 bit unsigned integer value into the memory buffer.
+ */
+#define GST_WRITE_UINT8(data, num) do { \
+ _GST_PUT (data, 0, 8, 0, num); \
+ } while (0)
+
+/* Float endianness conversion macros */
+
+/* FIXME: Remove this once we depend on a GLib version with this */
+#ifndef GFLOAT_FROM_LE
+/**
+ * GFLOAT_SWAP_LE_BE:
+ * @in: input value
+ *
+ * Swap byte order of a 32-bit floating point value (float).
+ *
+ * Returns: @in byte-swapped.
+ *
+ * Since: 0.10.22
+ *
+ */
+#ifdef _FOOL_GTK_DOC_
+G_INLINE_FUNC gfloat GFLOAT_SWAP_LE_BE (gfloat in);
+#endif
+
+inline static gfloat
+GFLOAT_SWAP_LE_BE(gfloat in)
+{
+ union
+ {
+ guint32 i;
+ gfloat f;
+ } u;
+
+ u.f = in;
+ u.i = GUINT32_SWAP_LE_BE (u.i);
+ return u.f;
+}
+
+/**
+ * GDOUBLE_SWAP_LE_BE:
+ * @in: input value
+ *
+ * Swap byte order of a 64-bit floating point value (double).
+ *
+ * Returns: @in byte-swapped.
+ *
+ * Since: 0.10.22
+ *
+ */
+#ifdef _FOOL_GTK_DOC_
+G_INLINE_FUNC gdouble GDOUBLE_SWAP_LE_BE (gdouble in);
+#endif
+
+inline static gdouble
+GDOUBLE_SWAP_LE_BE(gdouble in)
+{
+ union
+ {
+ guint64 i;
+ gdouble d;
+ } u;
+
+ u.d = in;
+ u.i = GUINT64_SWAP_LE_BE (u.i);
+ return u.d;
+}
+
+/**
+ * GDOUBLE_TO_LE:
+ * @val: value
+ *
+ * Convert 64-bit floating point value (double) from native byte order into
+ * little endian byte order.
+ *
+ * Since: 0.10.22
+ *
+ */
+/**
+ * GDOUBLE_TO_BE:
+ * @val: value
+ *
+ * Convert 64-bit floating point value (double) from native byte order into
+ * big endian byte order.
+ *
+ * Since: 0.10.22
+ *
+ */
+/**
+ * GDOUBLE_FROM_LE:
+ * @val: value
+ *
+ * Convert 64-bit floating point value (double) from little endian byte order
+ * into native byte order.
+ *
+ * Since: 0.10.22
+ *
+ */
+/**
+ * GDOUBLE_FROM_BE:
+ * @val: value
+ *
+ * Convert 64-bit floating point value (double) from big endian byte order
+ * into native byte order.
+ *
+ * Since: 0.10.22
+ *
+ */
+
+/**
+ * GFLOAT_TO_LE:
+ * @val: value
+ *
+ * Convert 32-bit floating point value (float) from native byte order into
+ * little endian byte order.
+ *
+ * Since: 0.10.22
+ *
+ */
+/**
+ * GFLOAT_TO_BE:
+ * @val: value
+ *
+ * Convert 32-bit floating point value (float) from native byte order into
+ * big endian byte order.
+ *
+ * Since: 0.10.22
+ *
+ */
+/**
+ * GFLOAT_FROM_LE:
+ * @val: value
+ *
+ * Convert 32-bit floating point value (float) from little endian byte order
+ * into native byte order.
+ *
+ * Since: 0.10.22
+ *
+ */
+/**
+ * GFLOAT_FROM_BE:
+ * @val: value
+ *
+ * Convert 32-bit floating point value (float) from big endian byte order
+ * into native byte order.
+ *
+ * Since: 0.10.22
+ *
+ */
+
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+#define GFLOAT_TO_LE(val) ((gfloat) (val))
+#define GFLOAT_TO_BE(val) (GFLOAT_SWAP_LE_BE (val))
+#define GDOUBLE_TO_LE(val) ((gdouble) (val))
+#define GDOUBLE_TO_BE(val) (GDOUBLE_SWAP_LE_BE (val))
+
+#elif G_BYTE_ORDER == G_BIG_ENDIAN
+#define GFLOAT_TO_LE(val) (GFLOAT_SWAP_LE_BE (val))
+#define GFLOAT_TO_BE(val) ((gfloat) (val))
+#define GDOUBLE_TO_LE(val) (GDOUBLE_SWAP_LE_BE (val))
+#define GDOUBLE_TO_BE(val) ((gdouble) (val))
+
+#else /* !G_LITTLE_ENDIAN && !G_BIG_ENDIAN */
+#error unknown ENDIAN type
+#endif /* !G_LITTLE_ENDIAN && !G_BIG_ENDIAN */
+
+#define GFLOAT_FROM_LE(val) (GFLOAT_TO_LE (val))
+#define GFLOAT_FROM_BE(val) (GFLOAT_TO_BE (val))
+#define GDOUBLE_FROM_LE(val) (GDOUBLE_TO_LE (val))
+#define GDOUBLE_FROM_BE(val) (GDOUBLE_TO_BE (val))
+
+#endif /* !defined(GFLOAT_FROM_LE) */
+
+/**
+ * GST_READ_FLOAT_LE:
+ * @data: memory location
+ *
+ * Read a 32 bit float value in little endian format from the memory buffer.
+ *
+ * Returns: The floating point value read from @data
+ *
+ * Since: 0.10.22
+ *
+ */
+#ifdef _FOOL_GTK_DOC_
+G_INLINE_FUNC gfloat GST_READ_FLOAT_LE (const guint8 *data);
+#endif
+
+inline static gfloat
+GST_READ_FLOAT_LE(const guint8 *data)
+{
+ union
+ {
+ guint32 i;
+ gfloat f;
+ } u;
+
+ u.i = GST_READ_UINT32_LE (data);
+ return u.f;
+}
+
+/**
+ * GST_READ_FLOAT_BE:
+ * @data: memory location
+ *
+ * Read a 32 bit float value in big endian format from the memory buffer.
+ *
+ * Returns: The floating point value read from @data
+ *
+ * Since: 0.10.22
+ *
+ */
+#ifdef _FOOL_GTK_DOC_
+G_INLINE_FUNC gfloat GST_READ_FLOAT_BE (const guint8 *data);
+#endif
+
+inline static gfloat
+GST_READ_FLOAT_BE(const guint8 *data)
+{
+ union
+ {
+ guint32 i;
+ gfloat f;
+ } u;
+
+ u.i = GST_READ_UINT32_BE (data);
+ return u.f;
+}
+
+/**
+ * GST_READ_DOUBLE_LE:
+ * @data: memory location
+ *
+ * Read a 64 bit double value in little endian format from the memory buffer.
+ *
+ * Returns: The double-precision floating point value read from @data
+ *
+ * Since: 0.10.22
+ *
+ */
+#ifdef _FOOL_GTK_DOC_
+G_INLINE_FUNC gdouble GST_READ_DOUBLE_LE (const guint8 *data);
+#endif
+
+inline static gdouble
+GST_READ_DOUBLE_LE(const guint8 *data)
+{
+ union
+ {
+ guint64 i;
+ gdouble d;
+ } u;
+
+ u.i = GST_READ_UINT64_LE (data);
+ return u.d;
+}
+
+/**
+ * GST_READ_DOUBLE_BE:
+ * @data: memory location
+ *
+ * Read a 64 bit double value in big endian format from the memory buffer.
+ *
+ * Returns: The double-precision floating point value read from @data
+ *
+ * Since: 0.10.22
+ *
+ */
+#ifdef _FOOL_GTK_DOC_
+G_INLINE_FUNC gdouble GST_READ_DOUBLE_BE (const guint8 *data);
+#endif
+
+inline static gdouble
+GST_READ_DOUBLE_BE(const guint8 *data)
+{
+ union
+ {
+ guint64 i;
+ gdouble d;
+ } u;
+
+ u.i = GST_READ_UINT64_BE (data);
+ return u.d;
+}
+
+/**
+ * GST_WRITE_FLOAT_LE:
+ * @data: memory location
+ * @num: value to store
+ *
+ * Store a 32 bit float value in little endian format into the memory buffer.
+ *
+ * Since: 0.10.22
+ *
+ */
+#ifdef _FOOL_GTK_DOC_
+G_INLINE_FUNC void GST_WRITE_FLOAT_LE (guint8 *data, gfloat num);
+#endif
+
+inline static void
+GST_WRITE_FLOAT_LE(guint8 *data, gfloat num)
+{
+ union
+ {
+ guint32 i;
+ gfloat f;
+ } u;
+
+ u.f = num;
+ GST_WRITE_UINT32_LE (data, u.i);
+}
+
+/**
+ * GST_WRITE_FLOAT_BE:
+ * @data: memory location
+ * @num: value to store
+ *
+ * Store a 32 bit float value in big endian format into the memory buffer.
+ *
+ * Since: 0.10.22
+ *
+ */
+#ifdef _FOOL_GTK_DOC_
+G_INLINE_FUNC void GST_WRITE_FLOAT_BE (guint8 *data, gfloat num);
+#endif
+
+inline static void
+GST_WRITE_FLOAT_BE(guint8 *data, gfloat num)
+{
+ union
+ {
+ guint32 i;
+ gfloat f;
+ } u;
+
+ u.f = num;
+ GST_WRITE_UINT32_BE (data, u.i);
+}
+
+/**
+ * GST_WRITE_DOUBLE_LE:
+ * @data: memory location
+ * @num: value to store
+ *
+ * Store a 64 bit double value in little endian format into the memory buffer.
+ *
+ * Since: 0.10.22
+ *
+ */
+#ifdef _FOOL_GTK_DOC_
+G_INLINE_FUNC void GST_WRITE_DOUBLE_LE (guint8 *data, gdouble num);
+#endif
+
+inline static void
+GST_WRITE_DOUBLE_LE(guint8 *data, gdouble num)
+{
+ union
+ {
+ guint64 i;
+ gdouble d;
+ } u;
+
+ u.d = num;
+ GST_WRITE_UINT64_LE (data, u.i);
+}
+
+/**
+ * GST_WRITE_DOUBLE_BE:
+ * @data: memory location
+ * @num: value to store
+ *
+ * Store a 64 bit double value in big endian format into the memory buffer.
+ *
+ * Since: 0.10.22
+ *
+ */
+#ifdef _FOOL_GTK_DOC_
+G_INLINE_FUNC void GST_WRITE_DOUBLE_BE (guint8 *data, gdouble num);
+#endif
+
+inline static void
+GST_WRITE_DOUBLE_BE(guint8 *data, gdouble num)
+{
+ union
+ {
+ guint64 i;
+ gdouble d;
+ } u;
+
+ u.d = num;
+ GST_WRITE_UINT64_BE (data, u.i);
+}
+
+/* Miscellaneous utility macros */
+
+/**
+ * GST_ROUND_UP_2:
+ * @num: integer value to round up
+ *
+ * Rounds an integer value up to the next multiple of 2.
+ */
+#define GST_ROUND_UP_2(num) (((num)+1)&~1)
+/**
+ * GST_ROUND_UP_4:
+ * @num: integer value to round up
+ *
+ * Rounds an integer value up to the next multiple of 4.
+ */
+#define GST_ROUND_UP_4(num) (((num)+3)&~3)
+/**
+ * GST_ROUND_UP_8:
+ * @num: integer value to round up
+ *
+ * Rounds an integer value up to the next multiple of 8.
+ */
+#define GST_ROUND_UP_8(num) (((num)+7)&~7)
+/**
+ * GST_ROUND_UP_16:
+ * @num: integer value to round up
+ *
+ * Rounds an integer value up to the next multiple of 16.
+ */
+#define GST_ROUND_UP_16(num) (((num)+15)&~15)
+/**
+ * GST_ROUND_UP_32:
+ * @num: integer value to round up
+ *
+ * Rounds an integer value up to the next multiple of 32.
+ */
+#define GST_ROUND_UP_32(num) (((num)+31)&~31)
+/**
+ * GST_ROUND_UP_64:
+ * @num: integer value to round up
+ *
+ * Rounds an integer value up to the next multiple of 64.
+ */
+#define GST_ROUND_UP_64(num) (((num)+63)&~63)
+
+/**
+ * GST_ROUND_DOWN_2:
+ * @num: integer value to round down
+ *
+ * Rounds an integer value down to the next multiple of 2.
+ *
+ * Since: 0.10.12
+ */
+#define GST_ROUND_DOWN_2(num) ((num)&(~1))
+/**
+ * GST_ROUND_DOWN_4:
+ * @num: integer value to round down
+ *
+ * Rounds an integer value down to the next multiple of 4.
+ *
+ * Since: 0.10.12
+ */
+#define GST_ROUND_DOWN_4(num) ((num)&(~3))
+/**
+ * GST_ROUND_DOWN_8:
+ * @num: integer value to round down
+ *
+ * Rounds an integer value down to the next multiple of 8.
+ *
+ * Since: 0.10.12
+ */
+#define GST_ROUND_DOWN_8(num) ((num)&(~7))
+/**
+ * GST_ROUND_DOWN_16:
+ * @num: integer value to round down
+ *
+ * Rounds an integer value down to the next multiple of 16.
+ *
+ * Since: 0.10.12
+ */
+#define GST_ROUND_DOWN_16(num) ((num)&(~15))
+/**
+ * GST_ROUND_DOWN_32:
+ * @num: integer value to round down
+ *
+ * Rounds an integer value down to the next multiple of 32.
+ *
+ * Since: 0.10.12
+ */
+#define GST_ROUND_DOWN_32(num) ((num)&(~31))
+/**
+ * GST_ROUND_DOWN_64:
+ * @num: integer value to round down
+ *
+ * Rounds an integer value down to the next multiple of 64.
+ *
+ * Since: 0.10.12
+ */
+#define GST_ROUND_DOWN_64(num) ((num)&(~63))
+
+void gst_object_default_error (GstObject * source,
+ const GError * error,
+ const gchar * debug);
+
+/* element functions */
+void gst_element_create_all_pads (GstElement *element);
+GstPad* gst_element_get_compatible_pad (GstElement *element, GstPad *pad,
+ const GstCaps *caps);
+
+GstPadTemplate* gst_element_get_compatible_pad_template (GstElement *element, GstPadTemplate *compattempl);
+
+const gchar* gst_element_state_get_name (GstState state);
+const gchar * gst_element_state_change_return_get_name (GstStateChangeReturn state_ret);
+
+gboolean gst_element_link (GstElement *src, GstElement *dest);
+gboolean gst_element_link_many (GstElement *element_1,
+ GstElement *element_2, ...) G_GNUC_NULL_TERMINATED;
+gboolean gst_element_link_filtered (GstElement * src,
+ GstElement * dest,
+ GstCaps *filter);
+void gst_element_unlink (GstElement *src, GstElement *dest);
+void gst_element_unlink_many (GstElement *element_1,
+ GstElement *element_2, ...) G_GNUC_NULL_TERMINATED;
+
+gboolean gst_element_link_pads (GstElement *src, const gchar *srcpadname,
+ GstElement *dest, const gchar *destpadname);
+gboolean gst_element_link_pads_full (GstElement *src, const gchar *srcpadname,
+ GstElement *dest, const gchar *destpadname,
+ GstPadLinkCheck flags);
+void gst_element_unlink_pads (GstElement *src, const gchar *srcpadname,
+ GstElement *dest, const gchar *destpadname);
+
+gboolean gst_element_link_pads_filtered (GstElement * src, const gchar * srcpadname,
+ GstElement * dest, const gchar * destpadname,
+ GstCaps *filter);
+
+gboolean gst_element_seek_simple (GstElement *element,
+ GstFormat format,
+ GstSeekFlags seek_flags,
+ gint64 seek_pos);
+
+/* util elementfactory functions */
+gboolean gst_element_factory_can_sink_all_caps (GstElementFactory *factory, const GstCaps *caps);
+gboolean gst_element_factory_can_src_all_caps (GstElementFactory *factory, const GstCaps *caps);
+gboolean gst_element_factory_can_sink_any_caps (GstElementFactory *factory, const GstCaps *caps);
+gboolean gst_element_factory_can_src_any_caps (GstElementFactory *factory, const GstCaps *caps);
+
+/* util query functions */
+gboolean gst_element_query_position (GstElement *element, GstFormat format, gint64 *cur);
+gboolean gst_element_query_duration (GstElement *element, GstFormat format, gint64 *duration);
+gboolean gst_element_query_convert (GstElement *element, GstFormat src_format, gint64 src_val,
+ GstFormat dest_format, gint64 *dest_val);
+
+/* element class functions */
+void gst_element_class_install_std_props (GstElementClass * klass,
+ const gchar * first_name, ...) G_GNUC_NULL_TERMINATED;
+
+/* pad functions */
+void gst_pad_use_fixed_caps (GstPad *pad);
+GstCaps* gst_pad_proxy_getcaps (GstPad * pad, GstCaps * filter);
+
+GstElement* gst_pad_get_parent_element (GstPad *pad);
+
+/* util query functions */
+gboolean gst_pad_query_position (GstPad *pad, GstFormat format, gint64 *cur);
+gboolean gst_pad_query_duration (GstPad *pad, GstFormat format, gint64 *duration);
+gboolean gst_pad_query_convert (GstPad *pad, GstFormat src_format, gint64 src_val,
+ GstFormat dest_format, gint64 *dest_val);
+
+gboolean gst_pad_query_peer_position (GstPad *pad, GstFormat format, gint64 *cur);
+gboolean gst_pad_query_peer_duration (GstPad *pad, GstFormat format, gint64 *duration);
+gboolean gst_pad_query_peer_convert (GstPad *pad, GstFormat src_format, gint64 src_val,
+ GstFormat dest_format, gint64 *dest_val);
+
+/* bin functions */
+void gst_bin_add_many (GstBin *bin, GstElement *element_1, ...) G_GNUC_NULL_TERMINATED;
+void gst_bin_remove_many (GstBin *bin, GstElement *element_1, ...) G_GNUC_NULL_TERMINATED;
+GstPad * gst_bin_find_unlinked_pad (GstBin *bin, GstPadDirection direction);
+
+/* buffer functions */
+GstBuffer * gst_buffer_merge (GstBuffer * buf1, GstBuffer * buf2);
+GstBuffer * gst_buffer_join (GstBuffer * buf1, GstBuffer * buf2);
+
+/* tag emission utility functions */
+void gst_element_found_tags_for_pad (GstElement * element,
+ GstPad * pad,
+ GstTagList * list);
+void gst_element_found_tags (GstElement * element,
+ GstTagList * list);
+
+/* parse utility functions */
+GstElement * gst_parse_bin_from_description (const gchar * bin_description,
+ gboolean ghost_unlinked_pads,
+ GError ** err);
+
+GstElement * gst_parse_bin_from_description_full (const gchar * bin_description,
+ gboolean ghost_unlinked_pads,
+ GstParseContext * context,
+ GstParseFlags flags,
+ GError ** err);
+
+GstClockTime gst_util_get_timestamp (void);
+
+/**
+ * GstSearchMode:
+ * @GST_SEARCH_MODE_EXACT : Only search for exact matches.
+ * @GST_SEARCH_MODE_BEFORE: Search for an exact match or the element just before.
+ * @GST_SEARCH_MODE_AFTER : Search for an exact match or the element just after.
+ *
+ * The different search modes.
+ *
+ * Since: 0.10.23
+ */
+typedef enum {
+ GST_SEARCH_MODE_EXACT = 0,
+ GST_SEARCH_MODE_BEFORE,
+ GST_SEARCH_MODE_AFTER
+} GstSearchMode;
+
+gpointer gst_util_array_binary_search (gpointer array, guint num_elements,
+ gsize element_size, GCompareDataFunc search_func,
+ GstSearchMode mode, gconstpointer search_data,
+ gpointer user_data);
+
+gint gst_util_greatest_common_divisor (gint a, gint b);
+void gst_util_fraction_to_double (gint src_n, gint src_d, gdouble *dest);
+void gst_util_double_to_fraction (gdouble src, gint *dest_n, gint *dest_d);
+gboolean gst_util_fraction_multiply (gint a_n, gint a_d, gint b_n, gint b_d, gint *res_n, gint *res_d);
+gboolean gst_util_fraction_add (gint a_n, gint a_d, gint b_n, gint b_d, gint *res_n, gint *res_d);
+gint gst_util_fraction_compare (gint a_n, gint a_d, gint b_n, gint b_d);
+
+
+G_END_DECLS
+
+#endif /* __GST_UTILS_H__ */
diff --git a/gst/gstvalue.c b/gst/gstvalue.c
new file mode 100644
index 0000000..6cbed59
--- /dev/null
+++ b/gst/gstvalue.c
@@ -0,0 +1,4971 @@
+/* GStreamer
+ * Copyright (C) <2003> David A. Schleef <ds@schleef.org>
+ *
+ * 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:gstvalue
+ * @short_description: GValue implementations specific
+ * to GStreamer
+ *
+ * GValue implementations specific to GStreamer.
+ *
+ * Note that operations on the same #GValue from multiple threads may lead to
+ * undefined behaviour.
+ *
+ * Last reviewed on 2008-03-11 (0.10.18)
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "gst_private.h"
+#include "glib-compat-private.h"
+#include <gst/gst.h>
+#include <gobject/gvaluecollector.h>
+#include "gstutils.h"
+
+typedef struct _GstValueUnionInfo GstValueUnionInfo;
+struct _GstValueUnionInfo
+{
+ GType type1;
+ GType type2;
+ GstValueUnionFunc func;
+};
+
+typedef struct _GstValueIntersectInfo GstValueIntersectInfo;
+struct _GstValueIntersectInfo
+{
+ GType type1;
+ GType type2;
+ GstValueIntersectFunc func;
+};
+
+typedef struct _GstValueSubtractInfo GstValueSubtractInfo;
+struct _GstValueSubtractInfo
+{
+ GType minuend;
+ GType subtrahend;
+ GstValueSubtractFunc func;
+};
+
+#define FUNDAMENTAL_TYPE_ID_MAX \
+ (G_TYPE_FUNDAMENTAL_MAX >> G_TYPE_FUNDAMENTAL_SHIFT)
+#define FUNDAMENTAL_TYPE_ID(type) \
+ ((type) >> G_TYPE_FUNDAMENTAL_SHIFT)
+
+#define VALUE_LIST_SIZE(v) (((GArray *) (v)->data[0].v_pointer)->len)
+#define VALUE_LIST_GET_VALUE(v, index) ((const GValue *) &g_array_index ((GArray *) (v)->data[0].v_pointer, GValue, (index)))
+
+static GArray *gst_value_table;
+static GHashTable *gst_value_hash;
+static GstValueTable *gst_value_tables_fundamental[FUNDAMENTAL_TYPE_ID_MAX + 1];
+static GArray *gst_value_union_funcs;
+static GArray *gst_value_intersect_funcs;
+static GArray *gst_value_subtract_funcs;
+
+/* Forward declarations */
+static gchar *gst_value_serialize_fraction (const GValue * value);
+
+static GstValueCompareFunc gst_value_get_compare_func (const GValue * value1);
+static gint gst_value_compare_with_func (const GValue * value1,
+ const GValue * value2, GstValueCompareFunc compare);
+
+static gchar *gst_string_wrap (const gchar * s);
+static gchar *gst_string_take_and_wrap (gchar * s);
+static gchar *gst_string_unwrap (const gchar * s);
+
+static inline GstValueTable *
+gst_value_hash_lookup_type (GType type)
+{
+ if (G_LIKELY (G_TYPE_IS_FUNDAMENTAL (type)))
+ return gst_value_tables_fundamental[FUNDAMENTAL_TYPE_ID (type)];
+ else
+ return g_hash_table_lookup (gst_value_hash, (gpointer) type);
+}
+
+static void
+gst_value_hash_add_type (GType type, const GstValueTable * table)
+{
+ if (G_TYPE_IS_FUNDAMENTAL (type))
+ gst_value_tables_fundamental[FUNDAMENTAL_TYPE_ID (type)] = (gpointer) table;
+
+ g_hash_table_insert (gst_value_hash, (gpointer) type, (gpointer) table);
+}
+
+/********
+ * list *
+ ********/
+
+/* two helper functions to serialize/stringify any type of list
+ * regular lists are done with { }, arrays with < >
+ */
+static gchar *
+gst_value_serialize_any_list (const GValue * value, const gchar * begin,
+ const gchar * end)
+{
+ guint i;
+ GArray *array = value->data[0].v_pointer;
+ GString *s;
+ GValue *v;
+ gchar *s_val;
+ guint alen = array->len;
+
+ /* estimate minimum string length to minimise re-allocs in GString */
+ s = g_string_sized_new (2 + (6 * alen) + 2);
+ g_string_append (s, begin);
+ for (i = 0; i < alen; i++) {
+ v = &g_array_index (array, GValue, i);
+ s_val = gst_value_serialize (v);
+ g_string_append (s, s_val);
+ g_free (s_val);
+ if (i < alen - 1) {
+ g_string_append_len (s, ", ", 2);
+ }
+ }
+ g_string_append (s, end);
+ return g_string_free (s, FALSE);
+}
+
+static void
+gst_value_transform_any_list_string (const GValue * src_value,
+ GValue * dest_value, const gchar * begin, const gchar * end)
+{
+ GValue *list_value;
+ GArray *array;
+ GString *s;
+ guint i;
+ gchar *list_s;
+ guint alen;
+
+ array = src_value->data[0].v_pointer;
+ alen = array->len;
+
+ /* estimate minimum string length to minimise re-allocs in GString */
+ s = g_string_sized_new (2 + (10 * alen) + 2);
+ g_string_append (s, begin);
+ for (i = 0; i < alen; i++) {
+ list_value = &g_array_index (array, GValue, i);
+
+ if (i != 0) {
+ g_string_append_len (s, ", ", 2);
+ }
+ list_s = g_strdup_value_contents (list_value);
+ g_string_append (s, list_s);
+ g_free (list_s);
+ }
+ g_string_append (s, end);
+
+ dest_value->data[0].v_pointer = g_string_free (s, FALSE);
+}
+
+/*
+ * helper function to see if a type is fixed. Is used internally here and
+ * there. Do not export, since it doesn't work for types where the content
+ * decides the fixedness (e.g. GST_TYPE_ARRAY).
+ */
+static gboolean
+gst_type_is_fixed (GType type)
+{
+ /* the basic int, string, double types */
+ if (type <= G_TYPE_MAKE_FUNDAMENTAL (G_TYPE_RESERVED_GLIB_LAST)) {
+ return TRUE;
+ }
+ /* our fundamental types that are certainly not fixed */
+ if (type == GST_TYPE_INT_RANGE || type == GST_TYPE_DOUBLE_RANGE ||
+ type == GST_TYPE_INT64_RANGE ||
+ type == GST_TYPE_LIST || type == GST_TYPE_FRACTION_RANGE) {
+ return FALSE;
+ }
+ /* other (boxed) types that are fixed */
+ if (type == GST_TYPE_BUFFER) {
+ return TRUE;
+ }
+ /* heavy checks */
+ if (G_TYPE_IS_FUNDAMENTAL (type) || G_TYPE_FUNDAMENTAL (type) <=
+ G_TYPE_MAKE_FUNDAMENTAL (G_TYPE_RESERVED_GLIB_LAST)) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/* GValue functions usable for both regular lists and arrays */
+static void
+gst_value_init_list_or_array (GValue * value)
+{
+ value->data[0].v_pointer = g_array_new (FALSE, TRUE, sizeof (GValue));
+}
+
+static GArray *
+copy_garray_of_gstvalue (const GArray * src)
+{
+ GArray *dest;
+ guint i, len;
+
+ len = src->len;
+ dest = g_array_sized_new (FALSE, TRUE, sizeof (GValue), len);
+ g_array_set_size (dest, len);
+ for (i = 0; i < len; i++) {
+ gst_value_init_and_copy (&g_array_index (dest, GValue, i),
+ &g_array_index (src, GValue, i));
+ }
+
+ return dest;
+}
+
+static void
+gst_value_copy_list_or_array (const GValue * src_value, GValue * dest_value)
+{
+ dest_value->data[0].v_pointer =
+ copy_garray_of_gstvalue ((GArray *) src_value->data[0].v_pointer);
+}
+
+static void
+gst_value_free_list_or_array (GValue * value)
+{
+ guint i, len;
+ GArray *src = (GArray *) value->data[0].v_pointer;
+ len = src->len;
+
+ if ((value->data[1].v_uint & G_VALUE_NOCOPY_CONTENTS) == 0) {
+ for (i = 0; i < len; i++) {
+ g_value_unset (&g_array_index (src, GValue, i));
+ }
+ g_array_free (src, TRUE);
+ }
+}
+
+static gpointer
+gst_value_list_or_array_peek_pointer (const GValue * value)
+{
+ return value->data[0].v_pointer;
+}
+
+static gchar *
+gst_value_collect_list_or_array (GValue * value, guint n_collect_values,
+ GTypeCValue * collect_values, guint collect_flags)
+{
+ if (collect_flags & G_VALUE_NOCOPY_CONTENTS) {
+ value->data[0].v_pointer = collect_values[0].v_pointer;
+ value->data[1].v_uint = G_VALUE_NOCOPY_CONTENTS;
+ } else {
+ value->data[0].v_pointer =
+ copy_garray_of_gstvalue ((GArray *) collect_values[0].v_pointer);
+ }
+ return NULL;
+}
+
+static gchar *
+gst_value_lcopy_list_or_array (const GValue * value, guint n_collect_values,
+ GTypeCValue * collect_values, guint collect_flags)
+{
+ GArray **dest = collect_values[0].v_pointer;
+
+ if (!dest)
+ return g_strdup_printf ("value location for `%s' passed as NULL",
+ G_VALUE_TYPE_NAME (value));
+ if (!value->data[0].v_pointer)
+ return g_strdup_printf ("invalid value given for `%s'",
+ G_VALUE_TYPE_NAME (value));
+ if (collect_flags & G_VALUE_NOCOPY_CONTENTS) {
+ *dest = (GArray *) value->data[0].v_pointer;
+ } else {
+ *dest = copy_garray_of_gstvalue ((GArray *) value->data[0].v_pointer);
+ }
+ return NULL;
+}
+
+/**
+ * gst_value_list_append_value:
+ * @value: a #GValue of type #GST_TYPE_LIST
+ * @append_value: the value to append
+ *
+ * Appends @append_value to the GstValueList in @value.
+ */
+void
+gst_value_list_append_value (GValue * value, const GValue * append_value)
+{
+ GValue val = { 0, };
+
+ g_return_if_fail (GST_VALUE_HOLDS_LIST (value));
+ g_return_if_fail (G_IS_VALUE (append_value));
+
+ gst_value_init_and_copy (&val, append_value);
+ g_array_append_vals ((GArray *) value->data[0].v_pointer, &val, 1);
+}
+
+/**
+ * gst_value_list_prepend_value:
+ * @value: a #GValue of type #GST_TYPE_LIST
+ * @prepend_value: the value to prepend
+ *
+ * Prepends @prepend_value to the GstValueList in @value.
+ */
+void
+gst_value_list_prepend_value (GValue * value, const GValue * prepend_value)
+{
+ GValue val = { 0, };
+
+ g_return_if_fail (GST_VALUE_HOLDS_LIST (value));
+ g_return_if_fail (G_IS_VALUE (prepend_value));
+
+ gst_value_init_and_copy (&val, prepend_value);
+ g_array_prepend_vals ((GArray *) value->data[0].v_pointer, &val, 1);
+}
+
+/**
+ * gst_value_list_concat:
+ * @dest: (out caller-allocates): an uninitialized #GValue to take the result
+ * @value1: a #GValue
+ * @value2: a #GValue
+ *
+ * Concatenates copies of @value1 and @value2 into a list. Values that are not
+ * of type #GST_TYPE_LIST are treated as if they were lists of length 1.
+ * @dest will be initialized to the type #GST_TYPE_LIST.
+ */
+void
+gst_value_list_concat (GValue * dest, const GValue * value1,
+ const GValue * value2)
+{
+ guint i, value1_length, value2_length;
+ GArray *array;
+
+ g_return_if_fail (dest != NULL);
+ g_return_if_fail (G_VALUE_TYPE (dest) == 0);
+ g_return_if_fail (G_IS_VALUE (value1));
+ g_return_if_fail (G_IS_VALUE (value2));
+
+ value1_length =
+ (GST_VALUE_HOLDS_LIST (value1) ? VALUE_LIST_SIZE (value1) : 1);
+ value2_length =
+ (GST_VALUE_HOLDS_LIST (value2) ? VALUE_LIST_SIZE (value2) : 1);
+ g_value_init (dest, GST_TYPE_LIST);
+ array = (GArray *) dest->data[0].v_pointer;
+ g_array_set_size (array, value1_length + value2_length);
+
+ if (GST_VALUE_HOLDS_LIST (value1)) {
+ for (i = 0; i < value1_length; i++) {
+ gst_value_init_and_copy (&g_array_index (array, GValue, i),
+ VALUE_LIST_GET_VALUE (value1, i));
+ }
+ } else {
+ gst_value_init_and_copy (&g_array_index (array, GValue, 0), value1);
+ }
+
+ if (GST_VALUE_HOLDS_LIST (value2)) {
+ for (i = 0; i < value2_length; i++) {
+ gst_value_init_and_copy (&g_array_index (array, GValue,
+ i + value1_length), VALUE_LIST_GET_VALUE (value2, i));
+ }
+ } else {
+ gst_value_init_and_copy (&g_array_index (array, GValue, value1_length),
+ value2);
+ }
+}
+
+/**
+ * gst_value_list_merge:
+ * @dest: (out caller-allocates): an uninitialized #GValue to take the result
+ * @value1: a #GValue
+ * @value2: a #GValue
+ *
+ * Merges copies of @value1 and @value2. Values that are not
+ * of type #GST_TYPE_LIST are treated as if they were lists of length 1.
+ *
+ * The result will be put into @dest and will either be a list that will not
+ * contain any duplicates, or a non-list type (if @value1 and @value2
+ * were equal).
+ *
+ * Since: 0.10.32
+ */
+void
+gst_value_list_merge (GValue * dest, const GValue * value1,
+ const GValue * value2)
+{
+ guint i, j, k, value1_length, value2_length, skipped;
+ const GValue *src;
+ gboolean skip;
+ GArray *array;
+
+ g_return_if_fail (dest != NULL);
+ g_return_if_fail (G_VALUE_TYPE (dest) == 0);
+ g_return_if_fail (G_IS_VALUE (value1));
+ g_return_if_fail (G_IS_VALUE (value2));
+
+ value1_length =
+ (GST_VALUE_HOLDS_LIST (value1) ? VALUE_LIST_SIZE (value1) : 1);
+ value2_length =
+ (GST_VALUE_HOLDS_LIST (value2) ? VALUE_LIST_SIZE (value2) : 1);
+ g_value_init (dest, GST_TYPE_LIST);
+ array = (GArray *) dest->data[0].v_pointer;
+ g_array_set_size (array, value1_length + value2_length);
+
+ if (GST_VALUE_HOLDS_LIST (value1)) {
+ for (i = 0; i < value1_length; i++) {
+ gst_value_init_and_copy (&g_array_index (array, GValue, i),
+ VALUE_LIST_GET_VALUE (value1, i));
+ }
+ } else {
+ gst_value_init_and_copy (&g_array_index (array, GValue, 0), value1);
+ }
+
+ j = value1_length;
+ skipped = 0;
+ if (GST_VALUE_HOLDS_LIST (value2)) {
+ for (i = 0; i < value2_length; i++) {
+ skip = FALSE;
+ src = VALUE_LIST_GET_VALUE (value2, i);
+ for (k = 0; k < value1_length; k++) {
+ if (gst_value_compare (&g_array_index (array, GValue, k),
+ src) == GST_VALUE_EQUAL) {
+ skip = TRUE;
+ skipped++;
+ break;
+ }
+ }
+ if (!skip) {
+ gst_value_init_and_copy (&g_array_index (array, GValue, j), src);
+ j++;
+ }
+ }
+ } else {
+ skip = FALSE;
+ for (k = 0; k < value1_length; k++) {
+ if (gst_value_compare (&g_array_index (array, GValue, k),
+ value2) == GST_VALUE_EQUAL) {
+ skip = TRUE;
+ skipped++;
+ break;
+ }
+ }
+ if (!skip) {
+ gst_value_init_and_copy (&g_array_index (array, GValue, j), value2);
+ }
+ }
+ if (skipped) {
+ guint new_size = value1_length + (value2_length - skipped);
+
+ if (new_size > 1) {
+ /* shrink list */
+ g_array_set_size (array, new_size);
+ } else {
+ GValue single_dest;
+
+ /* size is 1, take single value in list and make it new dest */
+ single_dest = g_array_index (array, GValue, 0);
+
+ /* clean up old value allocations: must set array size to 0, because
+ * allocated values are not inited meaning g_value_unset() will not
+ * work on them */
+ g_array_set_size (array, 0);
+ g_value_unset (dest);
+
+ /* the single value is our new result */
+ *dest = single_dest;
+ }
+ }
+}
+
+/**
+ * gst_value_list_get_size:
+ * @value: a #GValue of type #GST_TYPE_LIST
+ *
+ * Gets the number of values contained in @value.
+ *
+ * Returns: the number of values
+ */
+guint
+gst_value_list_get_size (const GValue * value)
+{
+ g_return_val_if_fail (GST_VALUE_HOLDS_LIST (value), 0);
+
+ return ((GArray *) value->data[0].v_pointer)->len;
+}
+
+/**
+ * gst_value_list_get_value:
+ * @value: a #GValue of type #GST_TYPE_LIST
+ * @index: index of value to get from the list
+ *
+ * Gets the value that is a member of the list contained in @value and
+ * has the index @index.
+ *
+ * Returns: (transfer none): the value at the given index
+ */
+const GValue *
+gst_value_list_get_value (const GValue * value, guint index)
+{
+ g_return_val_if_fail (GST_VALUE_HOLDS_LIST (value), NULL);
+ g_return_val_if_fail (index < VALUE_LIST_SIZE (value), NULL);
+
+ return (const GValue *) &g_array_index ((GArray *) value->data[0].v_pointer,
+ GValue, index);
+}
+
+/**
+ * gst_value_array_append_value:
+ * @value: a #GValue of type #GST_TYPE_ARRAY
+ * @append_value: the value to append
+ *
+ * Appends @append_value to the GstValueArray in @value.
+ */
+void
+gst_value_array_append_value (GValue * value, const GValue * append_value)
+{
+ GValue val = { 0, };
+
+ g_return_if_fail (GST_VALUE_HOLDS_ARRAY (value));
+ g_return_if_fail (G_IS_VALUE (append_value));
+
+ gst_value_init_and_copy (&val, append_value);
+ g_array_append_vals ((GArray *) value->data[0].v_pointer, &val, 1);
+}
+
+/**
+ * gst_value_array_prepend_value:
+ * @value: a #GValue of type #GST_TYPE_ARRAY
+ * @prepend_value: the value to prepend
+ *
+ * Prepends @prepend_value to the GstValueArray in @value.
+ */
+void
+gst_value_array_prepend_value (GValue * value, const GValue * prepend_value)
+{
+ GValue val = { 0, };
+
+ g_return_if_fail (GST_VALUE_HOLDS_ARRAY (value));
+ g_return_if_fail (G_IS_VALUE (prepend_value));
+
+ gst_value_init_and_copy (&val, prepend_value);
+ g_array_prepend_vals ((GArray *) value->data[0].v_pointer, &val, 1);
+}
+
+/**
+ * gst_value_array_get_size:
+ * @value: a #GValue of type #GST_TYPE_ARRAY
+ *
+ * Gets the number of values contained in @value.
+ *
+ * Returns: the number of values
+ */
+guint
+gst_value_array_get_size (const GValue * value)
+{
+ g_return_val_if_fail (GST_VALUE_HOLDS_ARRAY (value), 0);
+
+ return ((GArray *) value->data[0].v_pointer)->len;
+}
+
+/**
+ * gst_value_array_get_value:
+ * @value: a #GValue of type #GST_TYPE_ARRAY
+ * @index: index of value to get from the array
+ *
+ * Gets the value that is a member of the array contained in @value and
+ * has the index @index.
+ *
+ * Returns: (transfer none): the value at the given index
+ */
+const GValue *
+gst_value_array_get_value (const GValue * value, guint index)
+{
+ g_return_val_if_fail (GST_VALUE_HOLDS_ARRAY (value), NULL);
+ g_return_val_if_fail (index < gst_value_array_get_size (value), NULL);
+
+ return (const GValue *) &g_array_index ((GArray *) value->data[0].v_pointer,
+ GValue, index);
+}
+
+static void
+gst_value_transform_list_string (const GValue * src_value, GValue * dest_value)
+{
+ gst_value_transform_any_list_string (src_value, dest_value, "{ ", " }");
+}
+
+static void
+gst_value_transform_array_string (const GValue * src_value, GValue * dest_value)
+{
+ gst_value_transform_any_list_string (src_value, dest_value, "< ", " >");
+}
+
+/* Do an unordered compare of the contents of a list */
+static gint
+gst_value_compare_list (const GValue * value1, const GValue * value2)
+{
+ guint i, j;
+ GArray *array1 = value1->data[0].v_pointer;
+ GArray *array2 = value2->data[0].v_pointer;
+ GValue *v1;
+ GValue *v2;
+ gint len, to_remove;
+ guint8 *removed;
+ GstValueCompareFunc compare;
+
+ /* get length and do initial length check. */
+ len = array1->len;
+ if (len != array2->len)
+ return GST_VALUE_UNORDERED;
+
+ /* place to mark removed value indices of array2 */
+ removed = g_newa (guint8, len);
+ memset (removed, 0, len);
+ to_remove = len;
+
+ /* loop over array1, all items should be in array2. When we find an
+ * item in array2, remove it from array2 by marking it as removed */
+ for (i = 0; i < len; i++) {
+ v1 = &g_array_index (array1, GValue, i);
+ if ((compare = gst_value_get_compare_func (v1))) {
+ for (j = 0; j < len; j++) {
+ /* item is removed, we can skip it */
+ if (removed[j])
+ continue;
+ v2 = &g_array_index (array2, GValue, j);
+ if (gst_value_compare_with_func (v1, v2, compare) == GST_VALUE_EQUAL) {
+ /* mark item as removed now that we found it in array2 and
+ * decrement the number of remaining items in array2. */
+ removed[j] = 1;
+ to_remove--;
+ break;
+ }
+ }
+ /* item in array1 and not in array2, UNORDERED */
+ if (j == len)
+ return GST_VALUE_UNORDERED;
+ } else
+ return GST_VALUE_UNORDERED;
+ }
+ /* if not all items were removed, array2 contained something not in array1 */
+ if (to_remove != 0)
+ return GST_VALUE_UNORDERED;
+
+ /* arrays are equal */
+ return GST_VALUE_EQUAL;
+}
+
+/* Perform an ordered comparison of the contents of an array */
+static gint
+gst_value_compare_array (const GValue * value1, const GValue * value2)
+{
+ guint i;
+ GArray *array1 = value1->data[0].v_pointer;
+ GArray *array2 = value2->data[0].v_pointer;
+ guint len = array1->len;
+ GValue *v1;
+ GValue *v2;
+
+ if (len != array2->len)
+ return GST_VALUE_UNORDERED;
+
+ for (i = 0; i < len; i++) {
+ v1 = &g_array_index (array1, GValue, i);
+ v2 = &g_array_index (array2, GValue, i);
+ if (gst_value_compare (v1, v2) != GST_VALUE_EQUAL)
+ return GST_VALUE_UNORDERED;
+ }
+
+ return GST_VALUE_EQUAL;
+}
+
+static gchar *
+gst_value_serialize_list (const GValue * value)
+{
+ return gst_value_serialize_any_list (value, "{ ", " }");
+}
+
+static gboolean
+gst_value_deserialize_list (GValue * dest, const gchar * s)
+{
+ g_warning ("gst_value_deserialize_list: unimplemented");
+ return FALSE;
+}
+
+static gchar *
+gst_value_serialize_array (const GValue * value)
+{
+ return gst_value_serialize_any_list (value, "< ", " >");
+}
+
+static gboolean
+gst_value_deserialize_array (GValue * dest, const gchar * s)
+{
+ g_warning ("gst_value_deserialize_array: unimplemented");
+ return FALSE;
+}
+
+/*************
+ * int range *
+ *************/
+
+static void
+gst_value_init_int_range (GValue * value)
+{
+ value->data[0].v_int = 0;
+ value->data[1].v_int = 0;
+}
+
+static void
+gst_value_copy_int_range (const GValue * src_value, GValue * dest_value)
+{
+ dest_value->data[0].v_int = src_value->data[0].v_int;
+ dest_value->data[1].v_int = src_value->data[1].v_int;
+}
+
+static gchar *
+gst_value_collect_int_range (GValue * value, guint n_collect_values,
+ GTypeCValue * collect_values, guint collect_flags)
+{
+ if (n_collect_values != 2)
+ return g_strdup_printf ("not enough value locations for `%s' passed",
+ G_VALUE_TYPE_NAME (value));
+ if (collect_values[0].v_int >= collect_values[1].v_int)
+ return g_strdup_printf ("range start is not smaller than end for `%s'",
+ G_VALUE_TYPE_NAME (value));
+
+ value->data[0].v_int = collect_values[0].v_int;
+ value->data[1].v_int = collect_values[1].v_int;
+
+ return NULL;
+}
+
+static gchar *
+gst_value_lcopy_int_range (const GValue * value, guint n_collect_values,
+ GTypeCValue * collect_values, guint collect_flags)
+{
+ guint32 *int_range_start = collect_values[0].v_pointer;
+ guint32 *int_range_end = collect_values[1].v_pointer;
+
+ if (!int_range_start)
+ return g_strdup_printf ("start value location for `%s' passed as NULL",
+ G_VALUE_TYPE_NAME (value));
+ if (!int_range_end)
+ return g_strdup_printf ("end value location for `%s' passed as NULL",
+ G_VALUE_TYPE_NAME (value));
+
+ *int_range_start = value->data[0].v_int;
+ *int_range_end = value->data[1].v_int;
+
+ return NULL;
+}
+
+/**
+ * gst_value_set_int_range:
+ * @value: a GValue initialized to GST_TYPE_INT_RANGE
+ * @start: the start of the range
+ * @end: the end of the range
+ *
+ * Sets @value to the range specified by @start and @end.
+ */
+void
+gst_value_set_int_range (GValue * value, gint start, gint end)
+{
+ g_return_if_fail (GST_VALUE_HOLDS_INT_RANGE (value));
+ g_return_if_fail (start < end);
+
+ value->data[0].v_int = start;
+ value->data[1].v_int = end;
+}
+
+/**
+ * gst_value_get_int_range_min:
+ * @value: a GValue initialized to GST_TYPE_INT_RANGE
+ *
+ * Gets the minimum of the range specified by @value.
+ *
+ * Returns: the minimum of the range
+ */
+gint
+gst_value_get_int_range_min (const GValue * value)
+{
+ g_return_val_if_fail (GST_VALUE_HOLDS_INT_RANGE (value), 0);
+
+ return value->data[0].v_int;
+}
+
+/**
+ * gst_value_get_int_range_max:
+ * @value: a GValue initialized to GST_TYPE_INT_RANGE
+ *
+ * Gets the maximum of the range specified by @value.
+ *
+ * Returns: the maxumum of the range
+ */
+gint
+gst_value_get_int_range_max (const GValue * value)
+{
+ g_return_val_if_fail (GST_VALUE_HOLDS_INT_RANGE (value), 0);
+
+ return value->data[1].v_int;
+}
+
+static void
+gst_value_transform_int_range_string (const GValue * src_value,
+ GValue * dest_value)
+{
+ dest_value->data[0].v_pointer = g_strdup_printf ("[%d,%d]",
+ (int) src_value->data[0].v_int, (int) src_value->data[1].v_int);
+}
+
+static gint
+gst_value_compare_int_range (const GValue * value1, const GValue * value2)
+{
+ if (value2->data[0].v_int == value1->data[0].v_int &&
+ value2->data[1].v_int == value1->data[1].v_int)
+ return GST_VALUE_EQUAL;
+ return GST_VALUE_UNORDERED;
+}
+
+static gchar *
+gst_value_serialize_int_range (const GValue * value)
+{
+ return g_strdup_printf ("[ %d, %d ]", value->data[0].v_int,
+ value->data[1].v_int);
+}
+
+static gboolean
+gst_value_deserialize_int_range (GValue * dest, const gchar * s)
+{
+ g_warning ("unimplemented");
+ return FALSE;
+}
+
+/***************
+ * int64 range *
+ ***************/
+
+static void
+gst_value_init_int64_range (GValue * value)
+{
+ value->data[0].v_int64 = 0;
+ value->data[1].v_int64 = 0;
+}
+
+static void
+gst_value_copy_int64_range (const GValue * src_value, GValue * dest_value)
+{
+ dest_value->data[0].v_int64 = src_value->data[0].v_int64;
+ dest_value->data[1].v_int64 = src_value->data[1].v_int64;
+}
+
+static gchar *
+gst_value_collect_int64_range (GValue * value, guint n_collect_values,
+ GTypeCValue * collect_values, guint collect_flags)
+{
+ if (n_collect_values != 2)
+ return g_strdup_printf ("not enough value locations for `%s' passed",
+ G_VALUE_TYPE_NAME (value));
+ if (collect_values[0].v_int64 >= collect_values[1].v_int64)
+ return g_strdup_printf ("range start is not smaller than end for `%s'",
+ G_VALUE_TYPE_NAME (value));
+
+ value->data[0].v_int64 = collect_values[0].v_int64;
+ value->data[1].v_int64 = collect_values[1].v_int64;
+
+ return NULL;
+}
+
+static gchar *
+gst_value_lcopy_int64_range (const GValue * value, guint n_collect_values,
+ GTypeCValue * collect_values, guint collect_flags)
+{
+ guint64 *int_range_start = collect_values[0].v_pointer;
+ guint64 *int_range_end = collect_values[1].v_pointer;
+
+ if (!int_range_start)
+ return g_strdup_printf ("start value location for `%s' passed as NULL",
+ G_VALUE_TYPE_NAME (value));
+ if (!int_range_end)
+ return g_strdup_printf ("end value location for `%s' passed as NULL",
+ G_VALUE_TYPE_NAME (value));
+
+ *int_range_start = value->data[0].v_int64;
+ *int_range_end = value->data[1].v_int64;
+
+ return NULL;
+}
+
+/**
+ * gst_value_set_int64_range:
+ * @value: a GValue initialized to GST_TYPE_INT64_RANGE
+ * @start: the start of the range
+ * @end: the end of the range
+ *
+ * Sets @value to the range specified by @start and @end.
+ *
+ * Since: 0.10.31
+ */
+void
+gst_value_set_int64_range (GValue * value, gint64 start, gint64 end)
+{
+ g_return_if_fail (GST_VALUE_HOLDS_INT64_RANGE (value));
+ g_return_if_fail (start < end);
+
+ value->data[0].v_int64 = start;
+ value->data[1].v_int64 = end;
+}
+
+/**
+ * gst_value_get_int64_range_min:
+ * @value: a GValue initialized to GST_TYPE_INT64_RANGE
+ *
+ * Gets the minimum of the range specified by @value.
+ *
+ * Returns: the minimum of the range
+ *
+ * Since: 0.10.31
+ */
+gint64
+gst_value_get_int64_range_min (const GValue * value)
+{
+ g_return_val_if_fail (GST_VALUE_HOLDS_INT64_RANGE (value), 0);
+
+ return value->data[0].v_int64;
+}
+
+/**
+ * gst_value_get_int64_range_max:
+ * @value: a GValue initialized to GST_TYPE_INT64_RANGE
+ *
+ * Gets the maximum of the range specified by @value.
+ *
+ * Returns: the maxumum of the range
+ *
+ * Since: 0.10.31
+ */
+gint64
+gst_value_get_int64_range_max (const GValue * value)
+{
+ g_return_val_if_fail (GST_VALUE_HOLDS_INT64_RANGE (value), 0);
+
+ return value->data[1].v_int64;
+}
+
+static void
+gst_value_transform_int64_range_string (const GValue * src_value,
+ GValue * dest_value)
+{
+ dest_value->data[0].v_pointer =
+ g_strdup_printf ("(gint64)[%" G_GINT64_FORMAT ",%" G_GINT64_FORMAT "]",
+ src_value->data[0].v_int64, src_value->data[1].v_int64);
+}
+
+static gint
+gst_value_compare_int64_range (const GValue * value1, const GValue * value2)
+{
+ if (value2->data[0].v_int64 == value1->data[0].v_int64 &&
+ value2->data[1].v_int64 == value1->data[1].v_int64)
+ return GST_VALUE_EQUAL;
+ return GST_VALUE_UNORDERED;
+}
+
+static gchar *
+gst_value_serialize_int64_range (const GValue * value)
+{
+ return g_strdup_printf ("[ %" G_GINT64_FORMAT ", %" G_GINT64_FORMAT " ]",
+ value->data[0].v_int64, value->data[1].v_int64);
+}
+
+static gboolean
+gst_value_deserialize_int64_range (GValue * dest, const gchar * s)
+{
+ g_warning ("unimplemented");
+ return FALSE;
+}
+
+/****************
+ * double range *
+ ****************/
+
+static void
+gst_value_init_double_range (GValue * value)
+{
+ value->data[0].v_double = 0;
+ value->data[1].v_double = 0;
+}
+
+static void
+gst_value_copy_double_range (const GValue * src_value, GValue * dest_value)
+{
+ dest_value->data[0].v_double = src_value->data[0].v_double;
+ dest_value->data[1].v_double = src_value->data[1].v_double;
+}
+
+static gchar *
+gst_value_collect_double_range (GValue * value, guint n_collect_values,
+ GTypeCValue * collect_values, guint collect_flags)
+{
+ if (n_collect_values != 2)
+ return g_strdup_printf ("not enough value locations for `%s' passed",
+ G_VALUE_TYPE_NAME (value));
+ if (collect_values[0].v_double >= collect_values[1].v_double)
+ return g_strdup_printf ("range start is not smaller than end for `%s'",
+ G_VALUE_TYPE_NAME (value));
+
+ value->data[0].v_double = collect_values[0].v_double;
+ value->data[1].v_double = collect_values[1].v_double;
+
+ return NULL;
+}
+
+static gchar *
+gst_value_lcopy_double_range (const GValue * value, guint n_collect_values,
+ GTypeCValue * collect_values, guint collect_flags)
+{
+ gdouble *double_range_start = collect_values[0].v_pointer;
+ gdouble *double_range_end = collect_values[1].v_pointer;
+
+ if (!double_range_start)
+ return g_strdup_printf ("start value location for `%s' passed as NULL",
+ G_VALUE_TYPE_NAME (value));
+ if (!double_range_end)
+ return g_strdup_printf ("end value location for `%s' passed as NULL",
+ G_VALUE_TYPE_NAME (value));
+
+ *double_range_start = value->data[0].v_double;
+ *double_range_end = value->data[1].v_double;
+
+ return NULL;
+}
+
+/**
+ * gst_value_set_double_range:
+ * @value: a GValue initialized to GST_TYPE_DOUBLE_RANGE
+ * @start: the start of the range
+ * @end: the end of the range
+ *
+ * Sets @value to the range specified by @start and @end.
+ */
+void
+gst_value_set_double_range (GValue * value, gdouble start, gdouble end)
+{
+ g_return_if_fail (GST_VALUE_HOLDS_DOUBLE_RANGE (value));
+ g_return_if_fail (start < end);
+
+ value->data[0].v_double = start;
+ value->data[1].v_double = end;
+}
+
+/**
+ * gst_value_get_double_range_min:
+ * @value: a GValue initialized to GST_TYPE_DOUBLE_RANGE
+ *
+ * Gets the minimum of the range specified by @value.
+ *
+ * Returns: the minimum of the range
+ */
+gdouble
+gst_value_get_double_range_min (const GValue * value)
+{
+ g_return_val_if_fail (GST_VALUE_HOLDS_DOUBLE_RANGE (value), 0);
+
+ return value->data[0].v_double;
+}
+
+/**
+ * gst_value_get_double_range_max:
+ * @value: a GValue initialized to GST_TYPE_DOUBLE_RANGE
+ *
+ * Gets the maximum of the range specified by @value.
+ *
+ * Returns: the maxumum of the range
+ */
+gdouble
+gst_value_get_double_range_max (const GValue * value)
+{
+ g_return_val_if_fail (GST_VALUE_HOLDS_DOUBLE_RANGE (value), 0);
+
+ return value->data[1].v_double;
+}
+
+static void
+gst_value_transform_double_range_string (const GValue * src_value,
+ GValue * dest_value)
+{
+ gchar s1[G_ASCII_DTOSTR_BUF_SIZE], s2[G_ASCII_DTOSTR_BUF_SIZE];
+
+ dest_value->data[0].v_pointer = g_strdup_printf ("[%s,%s]",
+ g_ascii_dtostr (s1, G_ASCII_DTOSTR_BUF_SIZE,
+ src_value->data[0].v_double),
+ g_ascii_dtostr (s2, G_ASCII_DTOSTR_BUF_SIZE,
+ src_value->data[1].v_double));
+}
+
+static gint
+gst_value_compare_double_range (const GValue * value1, const GValue * value2)
+{
+ if (value2->data[0].v_double == value1->data[0].v_double &&
+ value2->data[0].v_double == value1->data[0].v_double)
+ return GST_VALUE_EQUAL;
+ return GST_VALUE_UNORDERED;
+}
+
+static gchar *
+gst_value_serialize_double_range (const GValue * value)
+{
+ gchar d1[G_ASCII_DTOSTR_BUF_SIZE];
+ gchar d2[G_ASCII_DTOSTR_BUF_SIZE];
+
+ g_ascii_dtostr (d1, G_ASCII_DTOSTR_BUF_SIZE, value->data[0].v_double);
+ g_ascii_dtostr (d2, G_ASCII_DTOSTR_BUF_SIZE, value->data[1].v_double);
+ return g_strdup_printf ("[ %s, %s ]", d1, d2);
+}
+
+static gboolean
+gst_value_deserialize_double_range (GValue * dest, const gchar * s)
+{
+ g_warning ("unimplemented");
+ return FALSE;
+}
+
+/****************
+ * fraction range *
+ ****************/
+
+static void
+gst_value_init_fraction_range (GValue * value)
+{
+ GValue *vals;
+ GType ftype;
+
+ ftype = GST_TYPE_FRACTION;
+
+ value->data[0].v_pointer = vals = g_slice_alloc0 (2 * sizeof (GValue));
+ g_value_init (&vals[0], ftype);
+ g_value_init (&vals[1], ftype);
+}
+
+static void
+gst_value_free_fraction_range (GValue * value)
+{
+ GValue *vals = (GValue *) value->data[0].v_pointer;
+
+ if (vals != NULL) {
+ g_value_unset (&vals[0]);
+ g_value_unset (&vals[1]);
+ g_slice_free1 (2 * sizeof (GValue), vals);
+ value->data[0].v_pointer = NULL;
+ }
+}
+
+static void
+gst_value_copy_fraction_range (const GValue * src_value, GValue * dest_value)
+{
+ GValue *vals = (GValue *) dest_value->data[0].v_pointer;
+ GValue *src_vals = (GValue *) src_value->data[0].v_pointer;
+
+ if (vals == NULL) {
+ gst_value_init_fraction_range (dest_value);
+ vals = dest_value->data[0].v_pointer;
+ }
+ if (src_vals != NULL) {
+ g_value_copy (&src_vals[0], &vals[0]);
+ g_value_copy (&src_vals[1], &vals[1]);
+ }
+}
+
+static gchar *
+gst_value_collect_fraction_range (GValue * value, guint n_collect_values,
+ GTypeCValue * collect_values, guint collect_flags)
+{
+ GValue *vals = (GValue *) value->data[0].v_pointer;
+
+ if (n_collect_values != 4)
+ return g_strdup_printf ("not enough value locations for `%s' passed",
+ G_VALUE_TYPE_NAME (value));
+ if (collect_values[1].v_int == 0)
+ return g_strdup_printf ("passed '0' as first denominator for `%s'",
+ G_VALUE_TYPE_NAME (value));
+ if (collect_values[3].v_int == 0)
+ return g_strdup_printf ("passed '0' as second denominator for `%s'",
+ G_VALUE_TYPE_NAME (value));
+ if (gst_util_fraction_compare (collect_values[0].v_int,
+ collect_values[1].v_int, collect_values[2].v_int,
+ collect_values[3].v_int) >= 0)
+ return g_strdup_printf ("range start is not smaller than end for `%s'",
+ G_VALUE_TYPE_NAME (value));
+
+ if (vals == NULL) {
+ gst_value_init_fraction_range (value);
+ vals = value->data[0].v_pointer;
+ }
+
+ gst_value_set_fraction (&vals[0], collect_values[0].v_int,
+ collect_values[1].v_int);
+ gst_value_set_fraction (&vals[1], collect_values[2].v_int,
+ collect_values[3].v_int);
+
+ return NULL;
+}
+
+static gchar *
+gst_value_lcopy_fraction_range (const GValue * value, guint n_collect_values,
+ GTypeCValue * collect_values, guint collect_flags)
+{
+ gint i;
+ gint *dest_values[4];
+ GValue *vals = (GValue *) value->data[0].v_pointer;
+
+ if (G_UNLIKELY (n_collect_values != 4))
+ return g_strdup_printf ("not enough value locations for `%s' passed",
+ G_VALUE_TYPE_NAME (value));
+
+ for (i = 0; i < 4; i++) {
+ if (G_UNLIKELY (collect_values[i].v_pointer == NULL)) {
+ return g_strdup_printf ("value location for `%s' passed as NULL",
+ G_VALUE_TYPE_NAME (value));
+ }
+ dest_values[i] = collect_values[i].v_pointer;
+ }
+
+ if (G_UNLIKELY (vals == NULL)) {
+ return g_strdup_printf ("Uninitialised `%s' passed",
+ G_VALUE_TYPE_NAME (value));
+ }
+
+ dest_values[0][0] = gst_value_get_fraction_numerator (&vals[0]);
+ dest_values[1][0] = gst_value_get_fraction_denominator (&vals[0]);
+ dest_values[2][0] = gst_value_get_fraction_numerator (&vals[1]);
+ dest_values[3][0] = gst_value_get_fraction_denominator (&vals[1]);
+ return NULL;
+}
+
+/**
+ * gst_value_set_fraction_range:
+ * @value: a GValue initialized to GST_TYPE_FRACTION_RANGE
+ * @start: the start of the range (a GST_TYPE_FRACTION GValue)
+ * @end: the end of the range (a GST_TYPE_FRACTION GValue)
+ *
+ * Sets @value to the range specified by @start and @end.
+ */
+void
+gst_value_set_fraction_range (GValue * value, const GValue * start,
+ const GValue * end)
+{
+ GValue *vals;
+
+ g_return_if_fail (GST_VALUE_HOLDS_FRACTION_RANGE (value));
+ g_return_if_fail (GST_VALUE_HOLDS_FRACTION (start));
+ g_return_if_fail (GST_VALUE_HOLDS_FRACTION (end));
+ g_return_if_fail (gst_util_fraction_compare (start->data[0].v_int,
+ start->data[1].v_int, end->data[0].v_int, end->data[1].v_int) < 0);
+
+ vals = (GValue *) value->data[0].v_pointer;
+ if (vals == NULL) {
+ gst_value_init_fraction_range (value);
+ vals = value->data[0].v_pointer;
+ }
+ g_value_copy (start, &vals[0]);
+ g_value_copy (end, &vals[1]);
+}
+
+/**
+ * gst_value_set_fraction_range_full:
+ * @value: a GValue initialized to GST_TYPE_FRACTION_RANGE
+ * @numerator_start: the numerator start of the range
+ * @denominator_start: the denominator start of the range
+ * @numerator_end: the numerator end of the range
+ * @denominator_end: the denominator end of the range
+ *
+ * Sets @value to the range specified by @numerator_start/@denominator_start
+ * and @numerator_end/@denominator_end.
+ */
+void
+gst_value_set_fraction_range_full (GValue * value,
+ gint numerator_start, gint denominator_start,
+ gint numerator_end, gint denominator_end)
+{
+ GValue start = { 0 };
+ GValue end = { 0 };
+
+ g_return_if_fail (value != NULL);
+ g_return_if_fail (denominator_start != 0);
+ g_return_if_fail (denominator_end != 0);
+ g_return_if_fail (gst_util_fraction_compare (numerator_start,
+ denominator_start, numerator_end, denominator_end) < 0);
+
+ g_value_init (&start, GST_TYPE_FRACTION);
+ g_value_init (&end, GST_TYPE_FRACTION);
+
+ gst_value_set_fraction (&start, numerator_start, denominator_start);
+ gst_value_set_fraction (&end, numerator_end, denominator_end);
+ gst_value_set_fraction_range (value, &start, &end);
+
+ g_value_unset (&start);
+ g_value_unset (&end);
+}
+
+/**
+ * gst_value_get_fraction_range_min:
+ * @value: a GValue initialized to GST_TYPE_FRACTION_RANGE
+ *
+ * Gets the minimum of the range specified by @value.
+ *
+ * Returns: the minimum of the range
+ */
+const GValue *
+gst_value_get_fraction_range_min (const GValue * value)
+{
+ GValue *vals;
+
+ g_return_val_if_fail (GST_VALUE_HOLDS_FRACTION_RANGE (value), NULL);
+
+ vals = (GValue *) value->data[0].v_pointer;
+ if (vals != NULL) {
+ return &vals[0];
+ }
+
+ return NULL;
+}
+
+/**
+ * gst_value_get_fraction_range_max:
+ * @value: a GValue initialized to GST_TYPE_FRACTION_RANGE
+ *
+ * Gets the maximum of the range specified by @value.
+ *
+ * Returns: the maximum of the range
+ */
+const GValue *
+gst_value_get_fraction_range_max (const GValue * value)
+{
+ GValue *vals;
+
+ g_return_val_if_fail (GST_VALUE_HOLDS_FRACTION_RANGE (value), NULL);
+
+ vals = (GValue *) value->data[0].v_pointer;
+ if (vals != NULL) {
+ return &vals[1];
+ }
+
+ return NULL;
+}
+
+static gchar *
+gst_value_serialize_fraction_range (const GValue * value)
+{
+ GValue *vals = (GValue *) value->data[0].v_pointer;
+ gchar *retval;
+
+ if (vals == NULL) {
+ retval = g_strdup ("[ 0/1, 0/1 ]");
+ } else {
+ gchar *start, *end;
+
+ start = gst_value_serialize_fraction (&vals[0]);
+ end = gst_value_serialize_fraction (&vals[1]);
+
+ retval = g_strdup_printf ("[ %s, %s ]", start, end);
+ g_free (start);
+ g_free (end);
+ }
+
+ return retval;
+}
+
+static void
+gst_value_transform_fraction_range_string (const GValue * src_value,
+ GValue * dest_value)
+{
+ dest_value->data[0].v_pointer =
+ gst_value_serialize_fraction_range (src_value);
+}
+
+static gint
+gst_value_compare_fraction_range (const GValue * value1, const GValue * value2)
+{
+ GValue *vals1, *vals2;
+ GstValueCompareFunc compare;
+
+ if (value2->data[0].v_pointer == value1->data[0].v_pointer)
+ return GST_VALUE_EQUAL; /* Only possible if both are NULL */
+
+ if (value2->data[0].v_pointer == NULL || value1->data[0].v_pointer == NULL)
+ return GST_VALUE_UNORDERED;
+
+ vals1 = (GValue *) value1->data[0].v_pointer;
+ vals2 = (GValue *) value2->data[0].v_pointer;
+ if ((compare = gst_value_get_compare_func (&vals1[0]))) {
+ if (gst_value_compare_with_func (&vals1[0], &vals2[0], compare) ==
+ GST_VALUE_EQUAL &&
+ gst_value_compare_with_func (&vals1[1], &vals2[1], compare) ==
+ GST_VALUE_EQUAL)
+ return GST_VALUE_EQUAL;
+ }
+ return GST_VALUE_UNORDERED;
+}
+
+static gboolean
+gst_value_deserialize_fraction_range (GValue * dest, const gchar * s)
+{
+ g_warning ("unimplemented");
+ return FALSE;
+}
+
+/***********
+ * GstCaps *
+ ***********/
+
+/**
+ * gst_value_set_caps:
+ * @value: a GValue initialized to GST_TYPE_CAPS
+ * @caps: (transfer none): the caps to set the value to
+ *
+ * Sets the contents of @value to @caps. A reference to the
+ * provided @caps will be taken by the @value.
+ */
+void
+gst_value_set_caps (GValue * value, const GstCaps * caps)
+{
+ g_return_if_fail (G_IS_VALUE (value));
+ g_return_if_fail (G_VALUE_TYPE (value) == GST_TYPE_CAPS);
+ g_return_if_fail (caps == NULL || GST_IS_CAPS (caps));
+
+ g_value_set_boxed (value, caps);
+}
+
+/**
+ * gst_value_get_caps:
+ * @value: a GValue initialized to GST_TYPE_CAPS
+ *
+ * Gets the contents of @value. The reference count of the returned
+ * #GstCaps will not be modified, therefore the caller must take one
+ * before getting rid of the @value.
+ *
+ * Returns: (transfer none): the contents of @value
+ */
+const GstCaps *
+gst_value_get_caps (const GValue * value)
+{
+ g_return_val_if_fail (G_IS_VALUE (value), NULL);
+ g_return_val_if_fail (G_VALUE_TYPE (value) == GST_TYPE_CAPS, NULL);
+
+ return (GstCaps *) g_value_get_boxed (value);
+}
+
+static gchar *
+gst_value_serialize_caps (const GValue * value)
+{
+ GstCaps *caps = g_value_get_boxed (value);
+
+ return gst_caps_to_string (caps);
+}
+
+static gboolean
+gst_value_deserialize_caps (GValue * dest, const gchar * s)
+{
+ GstCaps *caps;
+
+ caps = gst_caps_from_string (s);
+
+ if (caps) {
+ g_value_take_boxed (dest, caps);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/****************
+ * GstStructure *
+ ****************/
+
+/**
+ * gst_value_set_structure:
+ * @value: a GValue initialized to GST_TYPE_STRUCTURE
+ * @structure: the structure to set the value to
+ *
+ * Sets the contents of @value to @structure. The actual
+ *
+ * Since: 0.10.15
+ */
+void
+gst_value_set_structure (GValue * value, const GstStructure * structure)
+{
+ g_return_if_fail (G_IS_VALUE (value));
+ g_return_if_fail (G_VALUE_TYPE (value) == GST_TYPE_STRUCTURE);
+ g_return_if_fail (structure == NULL || GST_IS_STRUCTURE (structure));
+
+ g_value_set_boxed (value, structure);
+}
+
+/**
+ * gst_value_get_structure:
+ * @value: a GValue initialized to GST_TYPE_STRUCTURE
+ *
+ * Gets the contents of @value.
+ *
+ * Returns: (transfer none): the contents of @value
+ *
+ * Since: 0.10.15
+ */
+const GstStructure *
+gst_value_get_structure (const GValue * value)
+{
+ g_return_val_if_fail (G_IS_VALUE (value), NULL);
+ g_return_val_if_fail (G_VALUE_TYPE (value) == GST_TYPE_STRUCTURE, NULL);
+
+ return (GstStructure *) g_value_get_boxed (value);
+}
+
+static gchar *
+gst_value_serialize_structure (const GValue * value)
+{
+ GstStructure *structure = g_value_get_boxed (value);
+
+ return gst_string_take_and_wrap (gst_structure_to_string (structure));
+}
+
+static gboolean
+gst_value_deserialize_structure (GValue * dest, const gchar * s)
+{
+ GstStructure *structure;
+
+ if (*s != '"') {
+ structure = gst_structure_from_string (s, NULL);
+ } else {
+ gchar *str = gst_string_unwrap (s);
+
+ if (G_UNLIKELY (!str))
+ return FALSE;
+
+ structure = gst_structure_from_string (str, NULL);
+ g_free (str);
+ }
+
+ if (G_LIKELY (structure)) {
+ g_value_take_boxed (dest, structure);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*************
+ * GstBuffer *
+ *************/
+
+static gint
+gst_value_compare_buffer (const GValue * value1, const GValue * value2)
+{
+ GstBuffer *buf1 = gst_value_get_buffer (value1);
+ GstBuffer *buf2 = gst_value_get_buffer (value2);
+ gsize size1, size2;
+ gpointer data1, data2;
+ gint result = GST_VALUE_UNORDERED;
+
+ size1 = gst_buffer_get_size (buf1);
+ size2 = gst_buffer_get_size (buf2);
+
+ if (size1 != size2)
+ return GST_VALUE_UNORDERED;
+
+ if (size1 == 0)
+ return GST_VALUE_EQUAL;
+
+ data1 = gst_buffer_map (buf1, &size1, NULL, GST_MAP_READ);
+ data2 = gst_buffer_map (buf2, &size2, NULL, GST_MAP_READ);
+ g_assert (data1);
+ g_assert (data2);
+
+ if (memcmp (data1, data2, size1) == 0)
+ result = GST_VALUE_EQUAL;
+
+ gst_buffer_unmap (buf2, data2, size2);
+ gst_buffer_unmap (buf1, data1, size1);
+
+ return result;
+}
+
+static gchar *
+gst_value_serialize_buffer (const GValue * value)
+{
+ guint8 *data;
+ gint i;
+ gsize size;
+ gchar *string;
+ GstBuffer *buffer;
+
+ buffer = gst_value_get_buffer (value);
+ if (buffer == NULL)
+ return NULL;
+
+ data = gst_buffer_map (buffer, &size, NULL, GST_MAP_READ);
+
+ string = g_malloc (size * 2 + 1);
+ for (i = 0; i < size; i++) {
+ sprintf (string + i * 2, "%02x", data[i]);
+ }
+ string[size * 2] = 0;
+
+ gst_buffer_unmap (buffer, data, size);
+
+ return string;
+}
+
+static gboolean
+gst_value_deserialize_buffer (GValue * dest, const gchar * s)
+{
+ GstBuffer *buffer;
+ gint len;
+ gchar ts[3];
+ guint8 *data;
+ gint i;
+ gsize size;
+
+ len = strlen (s);
+ if (len & 1)
+ goto wrong_length;
+
+ buffer = gst_buffer_new_allocate (NULL, len / 2, 0);
+ data = gst_buffer_map (buffer, &size, NULL, GST_MAP_WRITE);
+
+ for (i = 0; i < len / 2; i++) {
+ if (!isxdigit ((int) s[i * 2]) || !isxdigit ((int) s[i * 2 + 1]))
+ goto wrong_char;
+
+ ts[0] = s[i * 2 + 0];
+ ts[1] = s[i * 2 + 1];
+ ts[2] = 0;
+
+ data[i] = (guint8) strtoul (ts, NULL, 16);
+ }
+ gst_buffer_unmap (buffer, data, size);
+
+ gst_value_take_buffer (dest, buffer);
+
+ return TRUE;
+
+ /* ERRORS */
+wrong_length:
+ {
+ return FALSE;
+ }
+wrong_char:
+ {
+ gst_buffer_unref (buffer);
+ gst_buffer_unmap (buffer, data, size);
+ return FALSE;
+ }
+}
+
+
+/***********
+ * boolean *
+ ***********/
+
+static gint
+gst_value_compare_boolean (const GValue * value1, const GValue * value2)
+{
+ if ((value1->data[0].v_int != 0) == (value2->data[0].v_int != 0))
+ return GST_VALUE_EQUAL;
+ return GST_VALUE_UNORDERED;
+}
+
+static gchar *
+gst_value_serialize_boolean (const GValue * value)
+{
+ if (value->data[0].v_int) {
+ return g_strdup ("true");
+ }
+ return g_strdup ("false");
+}
+
+static gboolean
+gst_value_deserialize_boolean (GValue * dest, const gchar * s)
+{
+ gboolean ret = FALSE;
+
+ if (g_ascii_strcasecmp (s, "true") == 0 ||
+ g_ascii_strcasecmp (s, "yes") == 0 ||
+ g_ascii_strcasecmp (s, "t") == 0 || strcmp (s, "1") == 0) {
+ g_value_set_boolean (dest, TRUE);
+ ret = TRUE;
+ } else if (g_ascii_strcasecmp (s, "false") == 0 ||
+ g_ascii_strcasecmp (s, "no") == 0 ||
+ g_ascii_strcasecmp (s, "f") == 0 || strcmp (s, "0") == 0) {
+ g_value_set_boolean (dest, FALSE);
+ ret = TRUE;
+ }
+
+ return ret;
+}
+
+#define CREATE_SERIALIZATION_START(_type,_macro) \
+static gint \
+gst_value_compare_ ## _type \
+(const GValue * value1, const GValue * value2) \
+{ \
+ g ## _type val1 = g_value_get_ ## _type (value1); \
+ g ## _type val2 = g_value_get_ ## _type (value2); \
+ if (val1 > val2) \
+ return GST_VALUE_GREATER_THAN; \
+ if (val1 < val2) \
+ return GST_VALUE_LESS_THAN; \
+ return GST_VALUE_EQUAL; \
+} \
+ \
+static gchar * \
+gst_value_serialize_ ## _type (const GValue * value) \
+{ \
+ GValue val = { 0, }; \
+ g_value_init (&val, G_TYPE_STRING); \
+ if (!g_value_transform (value, &val)) \
+ g_assert_not_reached (); \
+ /* NO_COPY_MADNESS!!! */ \
+ return (char *) g_value_get_string (&val); \
+}
+
+/* deserialize the given s into to as a gint64.
+ * check if the result is actually storeable in the given size number of
+ * bytes.
+ */
+static gboolean
+gst_value_deserialize_int_helper (gint64 * to, const gchar * s,
+ gint64 min, gint64 max, gint size)
+{
+ gboolean ret = FALSE;
+ gchar *end;
+ gint64 mask = -1;
+
+ errno = 0;
+ *to = g_ascii_strtoull (s, &end, 0);
+ /* a range error is a definitive no-no */
+ if (errno == ERANGE) {
+ return FALSE;
+ }
+
+ if (*end == 0) {
+ ret = TRUE;
+ } else {
+ if (g_ascii_strcasecmp (s, "little_endian") == 0) {
+ *to = G_LITTLE_ENDIAN;
+ ret = TRUE;
+ } else if (g_ascii_strcasecmp (s, "big_endian") == 0) {
+ *to = G_BIG_ENDIAN;
+ ret = TRUE;
+ } else if (g_ascii_strcasecmp (s, "byte_order") == 0) {
+ *to = G_BYTE_ORDER;
+ ret = TRUE;
+ } else if (g_ascii_strcasecmp (s, "min") == 0) {
+ *to = min;
+ ret = TRUE;
+ } else if (g_ascii_strcasecmp (s, "max") == 0) {
+ *to = max;
+ ret = TRUE;
+ }
+ }
+ if (ret) {
+ /* by definition, a gint64 fits into a gint64; so ignore those */
+ if (size != sizeof (mask)) {
+ if (*to >= 0) {
+ /* for positive numbers, we create a mask of 1's outside of the range
+ * and 0's inside the range. An and will thus keep only 1 bits
+ * outside of the range */
+ mask <<= (size * 8);
+ if ((mask & *to) != 0) {
+ ret = FALSE;
+ }
+ } else {
+ /* for negative numbers, we do a 2's complement version */
+ mask <<= ((size * 8) - 1);
+ if ((mask & *to) != mask) {
+ ret = FALSE;
+ }
+ }
+ }
+ }
+ return ret;
+}
+
+#define CREATE_SERIALIZATION(_type,_macro) \
+CREATE_SERIALIZATION_START(_type,_macro) \
+ \
+static gboolean \
+gst_value_deserialize_ ## _type (GValue * dest, const gchar *s) \
+{ \
+ gint64 x; \
+ \
+ if (gst_value_deserialize_int_helper (&x, s, G_MIN ## _macro, \
+ G_MAX ## _macro, sizeof (g ## _type))) { \
+ g_value_set_ ## _type (dest, /*(g ## _type)*/ x); \
+ return TRUE; \
+ } else { \
+ return FALSE; \
+ } \
+}
+
+#define CREATE_USERIALIZATION(_type,_macro) \
+CREATE_SERIALIZATION_START(_type,_macro) \
+ \
+static gboolean \
+gst_value_deserialize_ ## _type (GValue * dest, const gchar *s) \
+{ \
+ gint64 x; \
+ gchar *end; \
+ gboolean ret = FALSE; \
+ \
+ errno = 0; \
+ x = g_ascii_strtoull (s, &end, 0); \
+ /* a range error is a definitive no-no */ \
+ if (errno == ERANGE) { \
+ return FALSE; \
+ } \
+ /* the cast ensures the range check later on makes sense */ \
+ x = (g ## _type) x; \
+ if (*end == 0) { \
+ ret = TRUE; \
+ } else { \
+ if (g_ascii_strcasecmp (s, "little_endian") == 0) { \
+ x = G_LITTLE_ENDIAN; \
+ ret = TRUE; \
+ } else if (g_ascii_strcasecmp (s, "big_endian") == 0) { \
+ x = G_BIG_ENDIAN; \
+ ret = TRUE; \
+ } else if (g_ascii_strcasecmp (s, "byte_order") == 0) { \
+ x = G_BYTE_ORDER; \
+ ret = TRUE; \
+ } else if (g_ascii_strcasecmp (s, "min") == 0) { \
+ x = 0; \
+ ret = TRUE; \
+ } else if (g_ascii_strcasecmp (s, "max") == 0) { \
+ x = G_MAX ## _macro; \
+ ret = TRUE; \
+ } \
+ } \
+ if (ret) { \
+ if (x > G_MAX ## _macro) { \
+ ret = FALSE; \
+ } else { \
+ g_value_set_ ## _type (dest, x); \
+ } \
+ } \
+ return ret; \
+}
+
+#define REGISTER_SERIALIZATION(_gtype, _type) \
+G_STMT_START { \
+ static const GstValueTable gst_value = { \
+ _gtype, \
+ gst_value_compare_ ## _type, \
+ gst_value_serialize_ ## _type, \
+ gst_value_deserialize_ ## _type, \
+ }; \
+ \
+ gst_value_register (&gst_value); \
+} G_STMT_END
+
+CREATE_SERIALIZATION (int, INT);
+CREATE_SERIALIZATION (int64, INT64);
+CREATE_SERIALIZATION (long, LONG);
+
+CREATE_USERIALIZATION (uint, UINT);
+CREATE_USERIALIZATION (uint64, UINT64);
+CREATE_USERIALIZATION (ulong, ULONG);
+
+/* FIXME 0.11: remove this again, plugins shouldn't have uchar properties */
+#ifndef G_MAXUCHAR
+#define G_MAXUCHAR 255
+#endif
+CREATE_USERIALIZATION (uchar, UCHAR);
+
+/**********
+ * double *
+ **********/
+static gint
+gst_value_compare_double (const GValue * value1, const GValue * value2)
+{
+ if (value1->data[0].v_double > value2->data[0].v_double)
+ return GST_VALUE_GREATER_THAN;
+ if (value1->data[0].v_double < value2->data[0].v_double)
+ return GST_VALUE_LESS_THAN;
+ if (value1->data[0].v_double == value2->data[0].v_double)
+ return GST_VALUE_EQUAL;
+ return GST_VALUE_UNORDERED;
+}
+
+static gchar *
+gst_value_serialize_double (const GValue * value)
+{
+ gchar d[G_ASCII_DTOSTR_BUF_SIZE];
+
+ g_ascii_dtostr (d, G_ASCII_DTOSTR_BUF_SIZE, value->data[0].v_double);
+ return g_strdup (d);
+}
+
+static gboolean
+gst_value_deserialize_double (GValue * dest, const gchar * s)
+{
+ gdouble x;
+ gboolean ret = FALSE;
+ gchar *end;
+
+ x = g_ascii_strtod (s, &end);
+ if (*end == 0) {
+ ret = TRUE;
+ } else {
+ if (g_ascii_strcasecmp (s, "min") == 0) {
+ x = -G_MAXDOUBLE;
+ ret = TRUE;
+ } else if (g_ascii_strcasecmp (s, "max") == 0) {
+ x = G_MAXDOUBLE;
+ ret = TRUE;
+ }
+ }
+ if (ret) {
+ g_value_set_double (dest, x);
+ }
+ return ret;
+}
+
+/*********
+ * float *
+ *********/
+
+static gint
+gst_value_compare_float (const GValue * value1, const GValue * value2)
+{
+ if (value1->data[0].v_float > value2->data[0].v_float)
+ return GST_VALUE_GREATER_THAN;
+ if (value1->data[0].v_float < value2->data[0].v_float)
+ return GST_VALUE_LESS_THAN;
+ if (value1->data[0].v_float == value2->data[0].v_float)
+ return GST_VALUE_EQUAL;
+ return GST_VALUE_UNORDERED;
+}
+
+static gchar *
+gst_value_serialize_float (const GValue * value)
+{
+ gchar d[G_ASCII_DTOSTR_BUF_SIZE];
+
+ g_ascii_dtostr (d, G_ASCII_DTOSTR_BUF_SIZE, value->data[0].v_float);
+ return g_strdup (d);
+}
+
+static gboolean
+gst_value_deserialize_float (GValue * dest, const gchar * s)
+{
+ gdouble x;
+ gboolean ret = FALSE;
+ gchar *end;
+
+ x = g_ascii_strtod (s, &end);
+ if (*end == 0) {
+ ret = TRUE;
+ } else {
+ if (g_ascii_strcasecmp (s, "min") == 0) {
+ x = -G_MAXFLOAT;
+ ret = TRUE;
+ } else if (g_ascii_strcasecmp (s, "max") == 0) {
+ x = G_MAXFLOAT;
+ ret = TRUE;
+ }
+ }
+ if (x > G_MAXFLOAT || x < -G_MAXFLOAT)
+ ret = FALSE;
+ if (ret) {
+ g_value_set_float (dest, (float) x);
+ }
+ return ret;
+}
+
+/**********
+ * string *
+ **********/
+
+static gint
+gst_value_compare_string (const GValue * value1, const GValue * value2)
+{
+ if (G_UNLIKELY (!value1->data[0].v_pointer || !value2->data[0].v_pointer)) {
+ /* if only one is NULL, no match - otherwise both NULL == EQUAL */
+ if (value1->data[0].v_pointer != value2->data[0].v_pointer)
+ return GST_VALUE_UNORDERED;
+ } else {
+ gint x = strcmp (value1->data[0].v_pointer, value2->data[0].v_pointer);
+
+ if (x < 0)
+ return GST_VALUE_LESS_THAN;
+ if (x > 0)
+ return GST_VALUE_GREATER_THAN;
+ }
+
+ return GST_VALUE_EQUAL;
+}
+
+static gint
+gst_string_measure_wrapping (const gchar * s)
+{
+ gint len;
+ gboolean wrap = FALSE;
+
+ if (G_UNLIKELY (s == NULL))
+ return -1;
+
+ /* Special case: the actual string NULL needs wrapping */
+ if (G_UNLIKELY (strcmp (s, "NULL") == 0))
+ return 4;
+
+ len = 0;
+ while (*s) {
+ if (GST_ASCII_IS_STRING (*s)) {
+ len++;
+ } else if (*s < 0x20 || *s >= 0x7f) {
+ wrap = TRUE;
+ len += 4;
+ } else {
+ wrap = TRUE;
+ len += 2;
+ }
+ s++;
+ }
+
+ /* Wrap the string if we found something that needs
+ * wrapping, or the empty string (len == 0) */
+ return (wrap || len == 0) ? len : -1;
+}
+
+static gchar *
+gst_string_wrap_inner (const gchar * s, gint len)
+{
+ gchar *d, *e;
+
+ e = d = g_malloc (len + 3);
+
+ *e++ = '\"';
+ while (*s) {
+ if (GST_ASCII_IS_STRING (*s)) {
+ *e++ = *s++;
+ } else if (*s < 0x20 || *s >= 0x7f) {
+ *e++ = '\\';
+ *e++ = '0' + ((*(guchar *) s) >> 6);
+ *e++ = '0' + (((*s) >> 3) & 0x7);
+ *e++ = '0' + ((*s++) & 0x7);
+ } else {
+ *e++ = '\\';
+ *e++ = *s++;
+ }
+ }
+ *e++ = '\"';
+ *e = 0;
+
+ g_assert (e - d <= len + 3);
+ return d;
+}
+
+/* Do string wrapping/escaping */
+static gchar *
+gst_string_wrap (const gchar * s)
+{
+ gint len = gst_string_measure_wrapping (s);
+
+ if (G_LIKELY (len < 0))
+ return g_strdup (s);
+
+ return gst_string_wrap_inner (s, len);
+}
+
+/* Same as above, but take ownership of the string */
+static gchar *
+gst_string_take_and_wrap (gchar * s)
+{
+ gchar *out;
+ gint len = gst_string_measure_wrapping (s);
+
+ if (G_LIKELY (len < 0))
+ return s;
+
+ out = gst_string_wrap_inner (s, len);
+ g_free (s);
+
+ return out;
+}
+
+/*
+ * This function takes a string delimited with double quotes (")
+ * and unescapes any \xxx octal numbers.
+ *
+ * If sequences of \y are found where y is not in the range of
+ * 0->3, y is copied unescaped.
+ *
+ * If \xyy is found where x is an octal number but y is not, an
+ * error is encountered and NULL is returned.
+ *
+ * the input string must be \0 terminated.
+ */
+static gchar *
+gst_string_unwrap (const gchar * s)
+{
+ gchar *ret;
+ gchar *read, *write;
+
+ /* NULL string returns NULL */
+ if (s == NULL)
+ return NULL;
+
+ /* strings not starting with " are invalid */
+ if (*s != '"')
+ return NULL;
+
+ /* make copy of original string to hold the result. This
+ * string will always be smaller than the original */
+ ret = g_strdup (s);
+ read = ret;
+ write = ret;
+
+ /* need to move to the next position as we parsed the " */
+ read++;
+
+ while (*read) {
+ if (GST_ASCII_IS_STRING (*read)) {
+ /* normal chars are just copied */
+ *write++ = *read++;
+ } else if (*read == '"') {
+ /* quote marks end of string */
+ break;
+ } else if (*read == '\\') {
+ /* got an escape char, move to next position to read a tripplet
+ * of octal numbers */
+ read++;
+ /* is the next char a possible first octal number? */
+ if (*read >= '0' && *read <= '3') {
+ /* parse other 2 numbers, if one of them is not in the range of
+ * an octal number, we error. We also catch the case where a zero
+ * byte is found here. */
+ if (read[1] < '0' || read[1] > '7' || read[2] < '0' || read[2] > '7')
+ goto beach;
+
+ /* now convert the octal number to a byte again. */
+ *write++ = ((read[0] - '0') << 6) +
+ ((read[1] - '0') << 3) + (read[2] - '0');
+
+ read += 3;
+ } else {
+ /* if we run into a \0 here, we definitely won't get a quote later */
+ if (*read == 0)
+ goto beach;
+
+ /* else copy \X sequence */
+ *write++ = *read++;
+ }
+ } else {
+ /* weird character, error */
+ goto beach;
+ }
+ }
+ /* if the string is not ending in " and zero terminated, we error */
+ if (*read != '"' || read[1] != '\0')
+ goto beach;
+
+ /* null terminate result string and return */
+ *write = '\0';
+ return ret;
+
+beach:
+ g_free (ret);
+ return NULL;
+}
+
+static gchar *
+gst_value_serialize_string (const GValue * value)
+{
+ return gst_string_wrap (value->data[0].v_pointer);
+}
+
+static gboolean
+gst_value_deserialize_string (GValue * dest, const gchar * s)
+{
+ if (G_UNLIKELY (strcmp (s, "NULL") == 0)) {
+ g_value_set_string (dest, NULL);
+ return TRUE;
+ } else if (G_LIKELY (*s != '"')) {
+ if (!g_utf8_validate (s, -1, NULL))
+ return FALSE;
+ g_value_set_string (dest, s);
+ return TRUE;
+ } else {
+ gchar *str = gst_string_unwrap (s);
+ if (G_UNLIKELY (!str))
+ return FALSE;
+ g_value_take_string (dest, str);
+ }
+
+ return TRUE;
+}
+
+/********
+ * enum *
+ ********/
+
+static gint
+gst_value_compare_enum (const GValue * value1, const GValue * value2)
+{
+ GEnumValue *en1, *en2;
+ GEnumClass *klass1 = (GEnumClass *) g_type_class_ref (G_VALUE_TYPE (value1));
+ GEnumClass *klass2 = (GEnumClass *) g_type_class_ref (G_VALUE_TYPE (value2));
+
+ g_return_val_if_fail (klass1, GST_VALUE_UNORDERED);
+ g_return_val_if_fail (klass2, GST_VALUE_UNORDERED);
+ en1 = g_enum_get_value (klass1, g_value_get_enum (value1));
+ en2 = g_enum_get_value (klass2, g_value_get_enum (value2));
+ g_type_class_unref (klass1);
+ g_type_class_unref (klass2);
+ g_return_val_if_fail (en1, GST_VALUE_UNORDERED);
+ g_return_val_if_fail (en2, GST_VALUE_UNORDERED);
+ if (en1->value < en2->value)
+ return GST_VALUE_LESS_THAN;
+ if (en1->value > en2->value)
+ return GST_VALUE_GREATER_THAN;
+
+ return GST_VALUE_EQUAL;
+}
+
+static gchar *
+gst_value_serialize_enum (const GValue * value)
+{
+ GEnumValue *en;
+ GEnumClass *klass = (GEnumClass *) g_type_class_ref (G_VALUE_TYPE (value));
+
+ g_return_val_if_fail (klass, NULL);
+ en = g_enum_get_value (klass, g_value_get_enum (value));
+ g_type_class_unref (klass);
+
+ /* might be one of the custom formats registered later */
+ if (G_UNLIKELY (en == NULL && G_VALUE_TYPE (value) == GST_TYPE_FORMAT)) {
+ const GstFormatDefinition *format_def;
+
+ format_def = gst_format_get_details ((GstFormat) g_value_get_enum (value));
+ g_return_val_if_fail (format_def != NULL, NULL);
+ return g_strdup (format_def->description);
+ }
+
+ g_return_val_if_fail (en, NULL);
+ return g_strdup (en->value_name);
+}
+
+static gint
+gst_value_deserialize_enum_iter_cmp (const GValue * format_def_value,
+ const gchar * s)
+{
+ const GstFormatDefinition *format_def =
+ g_value_get_pointer (format_def_value);
+
+ if (g_ascii_strcasecmp (s, format_def->nick) == 0)
+ return 0;
+
+ return g_ascii_strcasecmp (s, format_def->description);
+}
+
+static gboolean
+gst_value_deserialize_enum (GValue * dest, const gchar * s)
+{
+ GEnumValue *en;
+ gchar *endptr = NULL;
+ GEnumClass *klass = (GEnumClass *) g_type_class_ref (G_VALUE_TYPE (dest));
+
+ g_return_val_if_fail (klass, FALSE);
+ if (!(en = g_enum_get_value_by_name (klass, s))) {
+ if (!(en = g_enum_get_value_by_nick (klass, s))) {
+ gint i = strtol (s, &endptr, 0);
+
+ if (endptr && *endptr == '\0') {
+ en = g_enum_get_value (klass, i);
+ }
+ }
+ }
+ g_type_class_unref (klass);
+
+ /* might be one of the custom formats registered later */
+ if (G_UNLIKELY (en == NULL && G_VALUE_TYPE (dest) == GST_TYPE_FORMAT)) {
+ GValue res = { 0, };
+ const GstFormatDefinition *format_def;
+ GstIterator *iter;
+ gboolean found;
+
+ iter = gst_format_iterate_definitions ();
+
+ found = gst_iterator_find_custom (iter,
+ (GCompareFunc) gst_value_deserialize_enum_iter_cmp, &res, (gpointer) s);
+
+ g_return_val_if_fail (found, FALSE);
+ format_def = g_value_get_pointer (&res);
+ g_return_val_if_fail (format_def != NULL, FALSE);
+ g_value_set_enum (dest, (gint) format_def->value);
+ g_value_unset (&res);
+ gst_iterator_free (iter);
+ return TRUE;
+ }
+
+ g_return_val_if_fail (en, FALSE);
+ g_value_set_enum (dest, en->value);
+ return TRUE;
+}
+
+/********
+ * flags *
+ ********/
+
+/* we just compare the value here */
+static gint
+gst_value_compare_flags (const GValue * value1, const GValue * value2)
+{
+ guint fl1, fl2;
+ GFlagsClass *klass1 =
+ (GFlagsClass *) g_type_class_ref (G_VALUE_TYPE (value1));
+ GFlagsClass *klass2 =
+ (GFlagsClass *) g_type_class_ref (G_VALUE_TYPE (value2));
+
+ g_return_val_if_fail (klass1, GST_VALUE_UNORDERED);
+ g_return_val_if_fail (klass2, GST_VALUE_UNORDERED);
+ fl1 = g_value_get_flags (value1);
+ fl2 = g_value_get_flags (value2);
+ g_type_class_unref (klass1);
+ g_type_class_unref (klass2);
+ if (fl1 < fl2)
+ return GST_VALUE_LESS_THAN;
+ if (fl1 > fl2)
+ return GST_VALUE_GREATER_THAN;
+
+ return GST_VALUE_EQUAL;
+}
+
+/* the different flags are serialized separated with a + */
+static gchar *
+gst_value_serialize_flags (const GValue * value)
+{
+ guint flags;
+ GFlagsValue *fl;
+ GFlagsClass *klass = (GFlagsClass *) g_type_class_ref (G_VALUE_TYPE (value));
+ gchar *result, *tmp;
+ gboolean first = TRUE;
+
+ g_return_val_if_fail (klass, NULL);
+
+ flags = g_value_get_flags (value);
+
+ /* if no flags are set, try to serialize to the _NONE string */
+ if (!flags) {
+ fl = g_flags_get_first_value (klass, flags);
+ return g_strdup (fl->value_name);
+ }
+
+ /* some flags are set, so serialize one by one */
+ result = g_strdup ("");
+ while (flags) {
+ fl = g_flags_get_first_value (klass, flags);
+ if (fl != NULL) {
+ tmp = g_strconcat (result, (first ? "" : "+"), fl->value_name, NULL);
+ g_free (result);
+ result = tmp;
+ first = FALSE;
+
+ /* clear flag */
+ flags &= ~fl->value;
+ }
+ }
+ g_type_class_unref (klass);
+
+ return result;
+}
+
+static gboolean
+gst_value_deserialize_flags (GValue * dest, const gchar * s)
+{
+ GFlagsValue *fl;
+ gchar *endptr = NULL;
+ GFlagsClass *klass = (GFlagsClass *) g_type_class_ref (G_VALUE_TYPE (dest));
+ gchar **split;
+ guint flags;
+ gint i;
+
+ g_return_val_if_fail (klass, FALSE);
+
+ /* split into parts delimited with + */
+ split = g_strsplit (s, "+", 0);
+
+ flags = 0;
+ i = 0;
+ /* loop over each part */
+ while (split[i]) {
+ if (!(fl = g_flags_get_value_by_name (klass, split[i]))) {
+ if (!(fl = g_flags_get_value_by_nick (klass, split[i]))) {
+ gint val = strtol (split[i], &endptr, 0);
+
+ /* just or numeric value */
+ if (endptr && *endptr == '\0') {
+ flags |= val;
+ }
+ }
+ }
+ if (fl) {
+ flags |= fl->value;
+ }
+ i++;
+ }
+ g_strfreev (split);
+ g_type_class_unref (klass);
+ g_value_set_flags (dest, flags);
+
+ return TRUE;
+}
+
+/*********
+ * union *
+ *********/
+
+static gboolean
+gst_value_union_int_int_range (GValue * dest, const GValue * src1,
+ const GValue * src2)
+{
+ if (src2->data[0].v_int <= src1->data[0].v_int &&
+ src2->data[1].v_int >= src1->data[0].v_int) {
+ gst_value_init_and_copy (dest, src2);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static gboolean
+gst_value_union_int_range_int_range (GValue * dest, const GValue * src1,
+ const GValue * src2)
+{
+ gint min;
+ gint max;
+
+ min = MAX (src1->data[0].v_int, src2->data[0].v_int);
+ max = MIN (src1->data[1].v_int, src2->data[1].v_int);
+
+ if (min <= max) {
+ g_value_init (dest, GST_TYPE_INT_RANGE);
+ gst_value_set_int_range (dest,
+ MIN (src1->data[0].v_int, src2->data[0].v_int),
+ MAX (src1->data[1].v_int, src2->data[1].v_int));
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/****************
+ * intersection *
+ ****************/
+
+static gboolean
+gst_value_intersect_int_int_range (GValue * dest, const GValue * src1,
+ const GValue * src2)
+{
+ if (src2->data[0].v_int <= src1->data[0].v_int &&
+ src2->data[1].v_int >= src1->data[0].v_int) {
+ gst_value_init_and_copy (dest, src1);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+gst_value_intersect_int_range_int_range (GValue * dest, const GValue * src1,
+ const GValue * src2)
+{
+ gint min;
+ gint max;
+
+ min = MAX (src1->data[0].v_int, src2->data[0].v_int);
+ max = MIN (src1->data[1].v_int, src2->data[1].v_int);
+
+ if (min < max) {
+ g_value_init (dest, GST_TYPE_INT_RANGE);
+ gst_value_set_int_range (dest, min, max);
+ return TRUE;
+ }
+ if (min == max) {
+ g_value_init (dest, G_TYPE_INT);
+ g_value_set_int (dest, min);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+gst_value_intersect_int64_int64_range (GValue * dest, const GValue * src1,
+ const GValue * src2)
+{
+ if (src2->data[0].v_int64 <= src1->data[0].v_int64 &&
+ src2->data[1].v_int64 >= src1->data[0].v_int64) {
+ gst_value_init_and_copy (dest, src1);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+gst_value_intersect_int64_range_int64_range (GValue * dest, const GValue * src1,
+ const GValue * src2)
+{
+ gint64 min;
+ gint64 max;
+
+ min = MAX (src1->data[0].v_int64, src2->data[0].v_int64);
+ max = MIN (src1->data[1].v_int64, src2->data[1].v_int64);
+
+ if (min < max) {
+ g_value_init (dest, GST_TYPE_INT64_RANGE);
+ gst_value_set_int64_range (dest, min, max);
+ return TRUE;
+ }
+ if (min == max) {
+ g_value_init (dest, G_TYPE_INT64);
+ g_value_set_int64 (dest, min);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+gst_value_intersect_double_double_range (GValue * dest, const GValue * src1,
+ const GValue * src2)
+{
+ if (src2->data[0].v_double <= src1->data[0].v_double &&
+ src2->data[1].v_double >= src1->data[0].v_double) {
+ gst_value_init_and_copy (dest, src1);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+gst_value_intersect_double_range_double_range (GValue * dest,
+ const GValue * src1, const GValue * src2)
+{
+ gdouble min;
+ gdouble max;
+
+ min = MAX (src1->data[0].v_double, src2->data[0].v_double);
+ max = MIN (src1->data[1].v_double, src2->data[1].v_double);
+
+ if (min < max) {
+ g_value_init (dest, GST_TYPE_DOUBLE_RANGE);
+ gst_value_set_double_range (dest, min, max);
+ return TRUE;
+ }
+ if (min == max) {
+ g_value_init (dest, G_TYPE_DOUBLE);
+ g_value_set_int (dest, (int) min);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+gst_value_intersect_list (GValue * dest, const GValue * value1,
+ const GValue * value2)
+{
+ guint i, size;
+ GValue intersection = { 0, };
+ gboolean ret = FALSE;
+
+ size = VALUE_LIST_SIZE (value1);
+ for (i = 0; i < size; i++) {
+ const GValue *cur = VALUE_LIST_GET_VALUE (value1, i);
+
+ if (gst_value_intersect (&intersection, cur, value2)) {
+ /* append value */
+ if (!ret) {
+ gst_value_init_and_copy (dest, &intersection);
+ ret = TRUE;
+ } else if (GST_VALUE_HOLDS_LIST (dest)) {
+ gst_value_list_append_value (dest, &intersection);
+ } else {
+ GValue temp = { 0, };
+
+ gst_value_init_and_copy (&temp, dest);
+ g_value_unset (dest);
+ gst_value_list_concat (dest, &temp, &intersection);
+ g_value_unset (&temp);
+ }
+ g_value_unset (&intersection);
+ }
+ }
+
+ return ret;
+}
+
+static gboolean
+gst_value_intersect_array (GValue * dest, const GValue * src1,
+ const GValue * src2)
+{
+ guint size;
+ guint n;
+ GValue val = { 0 };
+
+ /* only works on similar-sized arrays */
+ size = gst_value_array_get_size (src1);
+ if (size != gst_value_array_get_size (src2))
+ return FALSE;
+ g_value_init (dest, GST_TYPE_ARRAY);
+
+ for (n = 0; n < size; n++) {
+ if (!gst_value_intersect (&val, gst_value_array_get_value (src1, n),
+ gst_value_array_get_value (src2, n))) {
+ g_value_unset (dest);
+ return FALSE;
+ }
+ gst_value_array_append_value (dest, &val);
+ g_value_unset (&val);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gst_value_intersect_fraction_fraction_range (GValue * dest, const GValue * src1,
+ const GValue * src2)
+{
+ gint res1, res2;
+ GValue *vals;
+ GstValueCompareFunc compare;
+
+ vals = src2->data[0].v_pointer;
+
+ if (vals == NULL)
+ return FALSE;
+
+ if ((compare = gst_value_get_compare_func (src1))) {
+ res1 = gst_value_compare_with_func (&vals[0], src1, compare);
+ res2 = gst_value_compare_with_func (&vals[1], src1, compare);
+
+ if ((res1 == GST_VALUE_EQUAL || res1 == GST_VALUE_LESS_THAN) &&
+ (res2 == GST_VALUE_EQUAL || res2 == GST_VALUE_GREATER_THAN)) {
+ gst_value_init_and_copy (dest, src1);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static gboolean
+gst_value_intersect_fraction_range_fraction_range (GValue * dest,
+ const GValue * src1, const GValue * src2)
+{
+ GValue *min;
+ GValue *max;
+ gint res;
+ GValue *vals1, *vals2;
+ GstValueCompareFunc compare;
+
+ vals1 = src1->data[0].v_pointer;
+ vals2 = src2->data[0].v_pointer;
+ g_return_val_if_fail (vals1 != NULL && vals2 != NULL, FALSE);
+
+ if ((compare = gst_value_get_compare_func (&vals1[0]))) {
+ /* min = MAX (src1.start, src2.start) */
+ res = gst_value_compare_with_func (&vals1[0], &vals2[0], compare);
+ g_return_val_if_fail (res != GST_VALUE_UNORDERED, FALSE);
+ if (res == GST_VALUE_LESS_THAN)
+ min = &vals2[0]; /* Take the max of the 2 */
+ else
+ min = &vals1[0];
+
+ /* max = MIN (src1.end, src2.end) */
+ res = gst_value_compare_with_func (&vals1[1], &vals2[1], compare);
+ g_return_val_if_fail (res != GST_VALUE_UNORDERED, FALSE);
+ if (res == GST_VALUE_GREATER_THAN)
+ max = &vals2[1]; /* Take the min of the 2 */
+ else
+ max = &vals1[1];
+
+ res = gst_value_compare_with_func (min, max, compare);
+ g_return_val_if_fail (res != GST_VALUE_UNORDERED, FALSE);
+ if (res == GST_VALUE_LESS_THAN) {
+ g_value_init (dest, GST_TYPE_FRACTION_RANGE);
+ vals1 = dest->data[0].v_pointer;
+ g_value_copy (min, &vals1[0]);
+ g_value_copy (max, &vals1[1]);
+ return TRUE;
+ }
+ if (res == GST_VALUE_EQUAL) {
+ gst_value_init_and_copy (dest, min);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/***************
+ * subtraction *
+ ***************/
+
+static gboolean
+gst_value_subtract_int_int_range (GValue * dest, const GValue * minuend,
+ const GValue * subtrahend)
+{
+ gint min = gst_value_get_int_range_min (subtrahend);
+ gint max = gst_value_get_int_range_max (subtrahend);
+ gint val = g_value_get_int (minuend);
+
+ /* subtracting a range from an int only works if the int is not in the
+ * range */
+ if (val < min || val > max) {
+ /* and the result is the int */
+ gst_value_init_and_copy (dest, minuend);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/* creates a new int range based on input values.
+ */
+static gboolean
+gst_value_create_new_range (GValue * dest, gint min1, gint max1, gint min2,
+ gint max2)
+{
+ GValue v1 = { 0, };
+ GValue v2 = { 0, };
+ GValue *pv1, *pv2; /* yeah, hungarian! */
+
+ if (min1 <= max1 && min2 <= max2) {
+ pv1 = &v1;
+ pv2 = &v2;
+ } else if (min1 <= max1) {
+ pv1 = dest;
+ pv2 = NULL;
+ } else if (min2 <= max2) {
+ pv1 = NULL;
+ pv2 = dest;
+ } else {
+ return FALSE;
+ }
+
+ if (min1 < max1) {
+ g_value_init (pv1, GST_TYPE_INT_RANGE);
+ gst_value_set_int_range (pv1, min1, max1);
+ } else if (min1 == max1) {
+ g_value_init (pv1, G_TYPE_INT);
+ g_value_set_int (pv1, min1);
+ }
+ if (min2 < max2) {
+ g_value_init (pv2, GST_TYPE_INT_RANGE);
+ gst_value_set_int_range (pv2, min2, max2);
+ } else if (min2 == max2) {
+ g_value_init (pv2, G_TYPE_INT);
+ g_value_set_int (pv2, min2);
+ }
+
+ if (min1 <= max1 && min2 <= max2) {
+ gst_value_list_concat (dest, pv1, pv2);
+ g_value_unset (pv1);
+ g_value_unset (pv2);
+ }
+ return TRUE;
+}
+
+static gboolean
+gst_value_subtract_int_range_int (GValue * dest, const GValue * minuend,
+ const GValue * subtrahend)
+{
+ gint min = gst_value_get_int_range_min (minuend);
+ gint max = gst_value_get_int_range_max (minuend);
+ gint val = g_value_get_int (subtrahend);
+
+ g_return_val_if_fail (min < max, FALSE);
+
+ /* value is outside of the range, return range unchanged */
+ if (val < min || val > max) {
+ gst_value_init_and_copy (dest, minuend);
+ return TRUE;
+ } else {
+ /* max must be MAXINT too as val <= max */
+ if (val == G_MAXINT) {
+ max--;
+ val--;
+ }
+ /* min must be MININT too as val >= max */
+ if (val == G_MININT) {
+ min++;
+ val++;
+ }
+ gst_value_create_new_range (dest, min, val - 1, val + 1, max);
+ }
+ return TRUE;
+}
+
+static gboolean
+gst_value_subtract_int_range_int_range (GValue * dest, const GValue * minuend,
+ const GValue * subtrahend)
+{
+ gint min1 = gst_value_get_int_range_min (minuend);
+ gint max1 = gst_value_get_int_range_max (minuend);
+ gint min2 = gst_value_get_int_range_min (subtrahend);
+ gint max2 = gst_value_get_int_range_max (subtrahend);
+
+ if (max2 == G_MAXINT && min2 == G_MININT) {
+ return FALSE;
+ } else if (max2 == G_MAXINT) {
+ return gst_value_create_new_range (dest, min1, MIN (min2 - 1, max1), 1, 0);
+ } else if (min2 == G_MININT) {
+ return gst_value_create_new_range (dest, MAX (max2 + 1, min1), max1, 1, 0);
+ } else {
+ return gst_value_create_new_range (dest, min1, MIN (min2 - 1, max1),
+ MAX (max2 + 1, min1), max1);
+ }
+}
+
+static gboolean
+gst_value_subtract_int64_int64_range (GValue * dest, const GValue * minuend,
+ const GValue * subtrahend)
+{
+ gint64 min = gst_value_get_int64_range_min (subtrahend);
+ gint64 max = gst_value_get_int64_range_max (subtrahend);
+ gint64 val = g_value_get_int64 (minuend);
+
+ /* subtracting a range from an int64 only works if the int64 is not in the
+ * range */
+ if (val < min || val > max) {
+ /* and the result is the int64 */
+ gst_value_init_and_copy (dest, minuend);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/* creates a new int64 range based on input values.
+ */
+static gboolean
+gst_value_create_new_int64_range (GValue * dest, gint64 min1, gint64 max1,
+ gint64 min2, gint64 max2)
+{
+ GValue v1 = { 0, };
+ GValue v2 = { 0, };
+ GValue *pv1, *pv2; /* yeah, hungarian! */
+
+ if (min1 <= max1 && min2 <= max2) {
+ pv1 = &v1;
+ pv2 = &v2;
+ } else if (min1 <= max1) {
+ pv1 = dest;
+ pv2 = NULL;
+ } else if (min2 <= max2) {
+ pv1 = NULL;
+ pv2 = dest;
+ } else {
+ return FALSE;
+ }
+
+ if (min1 < max1) {
+ g_value_init (pv1, GST_TYPE_INT64_RANGE);
+ gst_value_set_int64_range (pv1, min1, max1);
+ } else if (min1 == max1) {
+ g_value_init (pv1, G_TYPE_INT64);
+ g_value_set_int64 (pv1, min1);
+ }
+ if (min2 < max2) {
+ g_value_init (pv2, GST_TYPE_INT64_RANGE);
+ gst_value_set_int64_range (pv2, min2, max2);
+ } else if (min2 == max2) {
+ g_value_init (pv2, G_TYPE_INT64);
+ g_value_set_int64 (pv2, min2);
+ }
+
+ if (min1 <= max1 && min2 <= max2) {
+ gst_value_list_concat (dest, pv1, pv2);
+ g_value_unset (pv1);
+ g_value_unset (pv2);
+ }
+ return TRUE;
+}
+
+static gboolean
+gst_value_subtract_int64_range_int64 (GValue * dest, const GValue * minuend,
+ const GValue * subtrahend)
+{
+ gint64 min = gst_value_get_int64_range_min (minuend);
+ gint64 max = gst_value_get_int64_range_max (minuend);
+ gint64 val = g_value_get_int64 (subtrahend);
+
+ g_return_val_if_fail (min < max, FALSE);
+
+ /* value is outside of the range, return range unchanged */
+ if (val < min || val > max) {
+ gst_value_init_and_copy (dest, minuend);
+ return TRUE;
+ } else {
+ /* max must be MAXINT64 too as val <= max */
+ if (val == G_MAXINT64) {
+ max--;
+ val--;
+ }
+ /* min must be MININT64 too as val >= max */
+ if (val == G_MININT64) {
+ min++;
+ val++;
+ }
+ gst_value_create_new_int64_range (dest, min, val - 1, val + 1, max);
+ }
+ return TRUE;
+}
+
+static gboolean
+gst_value_subtract_int64_range_int64_range (GValue * dest,
+ const GValue * minuend, const GValue * subtrahend)
+{
+ gint64 min1 = gst_value_get_int64_range_min (minuend);
+ gint64 max1 = gst_value_get_int64_range_max (minuend);
+ gint64 min2 = gst_value_get_int64_range_min (subtrahend);
+ gint64 max2 = gst_value_get_int64_range_max (subtrahend);
+
+ if (max2 == G_MAXINT64 && min2 == G_MININT64) {
+ return FALSE;
+ } else if (max2 == G_MAXINT64) {
+ return gst_value_create_new_int64_range (dest, min1, MIN (min2 - 1, max1),
+ 1, 0);
+ } else if (min2 == G_MININT64) {
+ return gst_value_create_new_int64_range (dest, MAX (max2 + 1, min1), max1,
+ 1, 0);
+ } else {
+ return gst_value_create_new_int64_range (dest, min1, MIN (min2 - 1, max1),
+ MAX (max2 + 1, min1), max1);
+ }
+}
+
+static gboolean
+gst_value_subtract_double_double_range (GValue * dest, const GValue * minuend,
+ const GValue * subtrahend)
+{
+ gdouble min = gst_value_get_double_range_min (subtrahend);
+ gdouble max = gst_value_get_double_range_max (subtrahend);
+ gdouble val = g_value_get_double (minuend);
+
+ if (val < min || val > max) {
+ gst_value_init_and_copy (dest, minuend);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static gboolean
+gst_value_subtract_double_range_double (GValue * dest, const GValue * minuend,
+ const GValue * subtrahend)
+{
+ /* since we don't have open ranges, we cannot create a hole in
+ * a double range. We return the original range */
+ gst_value_init_and_copy (dest, minuend);
+ return TRUE;
+}
+
+static gboolean
+gst_value_subtract_double_range_double_range (GValue * dest,
+ const GValue * minuend, const GValue * subtrahend)
+{
+ /* since we don't have open ranges, we have to approximate */
+ /* done like with ints */
+ gdouble min1 = gst_value_get_double_range_min (minuend);
+ gdouble max2 = gst_value_get_double_range_max (minuend);
+ gdouble max1 = MIN (gst_value_get_double_range_min (subtrahend), max2);
+ gdouble min2 = MAX (gst_value_get_double_range_max (subtrahend), min1);
+ GValue v1 = { 0, };
+ GValue v2 = { 0, };
+ GValue *pv1, *pv2; /* yeah, hungarian! */
+
+ if (min1 < max1 && min2 < max2) {
+ pv1 = &v1;
+ pv2 = &v2;
+ } else if (min1 < max1) {
+ pv1 = dest;
+ pv2 = NULL;
+ } else if (min2 < max2) {
+ pv1 = NULL;
+ pv2 = dest;
+ } else {
+ return FALSE;
+ }
+
+ if (min1 < max1) {
+ g_value_init (pv1, GST_TYPE_DOUBLE_RANGE);
+ gst_value_set_double_range (pv1, min1, max1);
+ }
+ if (min2 < max2) {
+ g_value_init (pv2, GST_TYPE_DOUBLE_RANGE);
+ gst_value_set_double_range (pv2, min2, max2);
+ }
+
+ if (min1 < max1 && min2 < max2) {
+ gst_value_list_concat (dest, pv1, pv2);
+ g_value_unset (pv1);
+ g_value_unset (pv2);
+ }
+ return TRUE;
+}
+
+static gboolean
+gst_value_subtract_from_list (GValue * dest, const GValue * minuend,
+ const GValue * subtrahend)
+{
+ guint i, size;
+ GValue subtraction = { 0, };
+ gboolean ret = FALSE;
+ GType ltype;
+
+ ltype = gst_value_list_get_type ();
+
+ size = VALUE_LIST_SIZE (minuend);
+ for (i = 0; i < size; i++) {
+ const GValue *cur = VALUE_LIST_GET_VALUE (minuend, i);
+
+ if (gst_value_subtract (&subtraction, cur, subtrahend)) {
+ if (!ret) {
+ gst_value_init_and_copy (dest, &subtraction);
+ ret = TRUE;
+ } else if (G_VALUE_HOLDS (dest, ltype)
+ && !G_VALUE_HOLDS (&subtraction, ltype)) {
+ gst_value_list_append_value (dest, &subtraction);
+ } else {
+ GValue temp = { 0, };
+
+ gst_value_init_and_copy (&temp, dest);
+ g_value_unset (dest);
+ gst_value_list_concat (dest, &temp, &subtraction);
+ g_value_unset (&temp);
+ }
+ g_value_unset (&subtraction);
+ }
+ }
+ return ret;
+}
+
+static gboolean
+gst_value_subtract_list (GValue * dest, const GValue * minuend,
+ const GValue * subtrahend)
+{
+ guint i, size;
+ GValue data[2] = { {0,}, {0,} };
+ GValue *subtraction = &data[0], *result = &data[1];
+
+ gst_value_init_and_copy (result, minuend);
+ size = VALUE_LIST_SIZE (subtrahend);
+ for (i = 0; i < size; i++) {
+ const GValue *cur = VALUE_LIST_GET_VALUE (subtrahend, i);
+
+ if (gst_value_subtract (subtraction, result, cur)) {
+ GValue *temp = result;
+
+ result = subtraction;
+ subtraction = temp;
+ g_value_unset (subtraction);
+ } else {
+ g_value_unset (result);
+ return FALSE;
+ }
+ }
+ gst_value_init_and_copy (dest, result);
+ g_value_unset (result);
+ return TRUE;
+}
+
+static gboolean
+gst_value_subtract_fraction_fraction_range (GValue * dest,
+ const GValue * minuend, const GValue * subtrahend)
+{
+ const GValue *min = gst_value_get_fraction_range_min (subtrahend);
+ const GValue *max = gst_value_get_fraction_range_max (subtrahend);
+ GstValueCompareFunc compare;
+
+ if ((compare = gst_value_get_compare_func (minuend))) {
+ /* subtracting a range from an fraction only works if the fraction
+ * is not in the range */
+ if (gst_value_compare_with_func (minuend, min, compare) ==
+ GST_VALUE_LESS_THAN ||
+ gst_value_compare_with_func (minuend, max, compare) ==
+ GST_VALUE_GREATER_THAN) {
+ /* and the result is the value */
+ gst_value_init_and_copy (dest, minuend);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+static gboolean
+gst_value_subtract_fraction_range_fraction (GValue * dest,
+ const GValue * minuend, const GValue * subtrahend)
+{
+ /* since we don't have open ranges, we cannot create a hole in
+ * a range. We return the original range */
+ gst_value_init_and_copy (dest, minuend);
+ return TRUE;
+}
+
+static gboolean
+gst_value_subtract_fraction_range_fraction_range (GValue * dest,
+ const GValue * minuend, const GValue * subtrahend)
+{
+ /* since we don't have open ranges, we have to approximate */
+ /* done like with ints and doubles. Creates a list of 2 fraction ranges */
+ const GValue *min1 = gst_value_get_fraction_range_min (minuend);
+ const GValue *max2 = gst_value_get_fraction_range_max (minuend);
+ const GValue *max1 = gst_value_get_fraction_range_min (subtrahend);
+ const GValue *min2 = gst_value_get_fraction_range_max (subtrahend);
+ gint cmp1, cmp2;
+ GValue v1 = { 0, };
+ GValue v2 = { 0, };
+ GValue *pv1, *pv2; /* yeah, hungarian! */
+ GstValueCompareFunc compare;
+
+ g_return_val_if_fail (min1 != NULL && max1 != NULL, FALSE);
+ g_return_val_if_fail (min2 != NULL && max2 != NULL, FALSE);
+
+ compare = gst_value_get_compare_func (min1);
+ g_return_val_if_fail (compare, FALSE);
+
+ cmp1 = gst_value_compare_with_func (max2, max1, compare);
+ g_return_val_if_fail (cmp1 != GST_VALUE_UNORDERED, FALSE);
+ if (cmp1 == GST_VALUE_LESS_THAN)
+ max1 = max2;
+ cmp1 = gst_value_compare_with_func (min1, min2, compare);
+ g_return_val_if_fail (cmp1 != GST_VALUE_UNORDERED, FALSE);
+ if (cmp1 == GST_VALUE_GREATER_THAN)
+ min2 = min1;
+
+ cmp1 = gst_value_compare_with_func (min1, max1, compare);
+ cmp2 = gst_value_compare_with_func (min2, max2, compare);
+
+ if (cmp1 == GST_VALUE_LESS_THAN && cmp2 == GST_VALUE_LESS_THAN) {
+ pv1 = &v1;
+ pv2 = &v2;
+ } else if (cmp1 == GST_VALUE_LESS_THAN) {
+ pv1 = dest;
+ pv2 = NULL;
+ } else if (cmp2 == GST_VALUE_LESS_THAN) {
+ pv1 = NULL;
+ pv2 = dest;
+ } else {
+ return FALSE;
+ }
+
+ if (cmp1 == GST_VALUE_LESS_THAN) {
+ g_value_init (pv1, GST_TYPE_FRACTION_RANGE);
+ gst_value_set_fraction_range (pv1, min1, max1);
+ }
+ if (cmp2 == GST_VALUE_LESS_THAN) {
+ g_value_init (pv2, GST_TYPE_FRACTION_RANGE);
+ gst_value_set_fraction_range (pv2, min2, max2);
+ }
+
+ if (cmp1 == GST_VALUE_LESS_THAN && cmp2 == GST_VALUE_LESS_THAN) {
+ gst_value_list_concat (dest, pv1, pv2);
+ g_value_unset (pv1);
+ g_value_unset (pv2);
+ }
+ return TRUE;
+}
+
+
+/**************
+ * comparison *
+ **************/
+
+/*
+ * gst_value_get_compare_func:
+ * @value1: a value to get the compare function for
+ *
+ * Determines the compare function to be used with values of the same type as
+ * @value1. The function can be given to gst_value_compare_with_func().
+ *
+ * Returns: A #GstValueCompareFunc value
+ */
+static GstValueCompareFunc
+gst_value_get_compare_func (const GValue * value1)
+{
+ GstValueTable *table, *best = NULL;
+ guint i;
+ GType type1;
+
+ type1 = G_VALUE_TYPE (value1);
+
+ /* this is a fast check */
+ best = gst_value_hash_lookup_type (type1);
+
+ /* slower checks */
+ if (G_UNLIKELY (!best || !best->compare)) {
+ guint len = gst_value_table->len;
+
+ best = NULL;
+ for (i = 0; i < len; i++) {
+ table = &g_array_index (gst_value_table, GstValueTable, i);
+ if (table->compare && g_type_is_a (type1, table->type)) {
+ if (!best || g_type_is_a (table->type, best->type))
+ best = table;
+ }
+ }
+ }
+ if (G_LIKELY (best))
+ return best->compare;
+
+ return NULL;
+}
+
+/**
+ * gst_value_can_compare:
+ * @value1: a value to compare
+ * @value2: another value to compare
+ *
+ * Determines if @value1 and @value2 can be compared.
+ *
+ * Returns: TRUE if the values can be compared
+ */
+gboolean
+gst_value_can_compare (const GValue * value1, const GValue * value2)
+{
+ g_return_val_if_fail (G_IS_VALUE (value1), FALSE);
+ g_return_val_if_fail (G_IS_VALUE (value2), FALSE);
+
+ if (G_VALUE_TYPE (value1) != G_VALUE_TYPE (value2))
+ return FALSE;
+
+ return gst_value_get_compare_func (value1) != NULL;
+}
+
+/**
+ * gst_value_compare:
+ * @value1: a value to compare
+ * @value2: another value to compare
+ *
+ * Compares @value1 and @value2. If @value1 and @value2 cannot be
+ * compared, the function returns GST_VALUE_UNORDERED. Otherwise,
+ * if @value1 is greater than @value2, GST_VALUE_GREATER_THAN is returned.
+ * If @value1 is less than @value2, GST_VALUE_LESS_THAN is returned.
+ * If the values are equal, GST_VALUE_EQUAL is returned.
+ *
+ * Returns: comparison result
+ */
+gint
+gst_value_compare (const GValue * value1, const GValue * value2)
+{
+ GstValueCompareFunc compare;
+ GType ltype;
+
+ g_return_val_if_fail (G_IS_VALUE (value1), GST_VALUE_LESS_THAN);
+ g_return_val_if_fail (G_IS_VALUE (value2), GST_VALUE_GREATER_THAN);
+
+ /* Special case: lists and scalar values
+ * "{ 1 }" and "1" are equal */
+ ltype = gst_value_list_get_type ();
+ if (G_VALUE_HOLDS (value1, ltype) && !G_VALUE_HOLDS (value2, ltype)
+ && gst_value_list_get_size (value1) == 1) {
+ const GValue *elt;
+
+ elt = gst_value_list_get_value (value1, 0);
+ return gst_value_compare (elt, value2);
+ } else if (G_VALUE_HOLDS (value2, ltype) && !G_VALUE_HOLDS (value1, ltype)
+ && gst_value_list_get_size (value2) == 1) {
+ const GValue *elt;
+
+ elt = gst_value_list_get_value (value2, 0);
+ return gst_value_compare (elt, value1);
+ }
+
+ if (G_VALUE_TYPE (value1) != G_VALUE_TYPE (value2))
+ return GST_VALUE_UNORDERED;
+
+ compare = gst_value_get_compare_func (value1);
+ if (compare) {
+ return compare (value1, value2);
+ }
+
+ g_critical ("unable to compare values of type %s\n",
+ g_type_name (G_VALUE_TYPE (value1)));
+ return GST_VALUE_UNORDERED;
+}
+
+/*
+ * gst_value_compare_with_func:
+ * @value1: a value to compare
+ * @value2: another value to compare
+ * @compare: compare function
+ *
+ * Compares @value1 and @value2 using the @compare function. Works like
+ * gst_value_compare() but allows to save time determining the compare function
+ * a multiple times.
+ *
+ * Returns: comparison result
+ */
+static gint
+gst_value_compare_with_func (const GValue * value1, const GValue * value2,
+ GstValueCompareFunc compare)
+{
+ g_assert (compare);
+
+ if (G_VALUE_TYPE (value1) != G_VALUE_TYPE (value2))
+ return GST_VALUE_UNORDERED;
+
+ return compare (value1, value2);
+}
+
+/* union */
+
+/**
+ * gst_value_can_union:
+ * @value1: a value to union
+ * @value2: another value to union
+ *
+ * Determines if @value1 and @value2 can be non-trivially unioned.
+ * Any two values can be trivially unioned by adding both of them
+ * to a GstValueList. However, certain types have the possibility
+ * to be unioned in a simpler way. For example, an integer range
+ * and an integer can be unioned if the integer is a subset of the
+ * integer range. If there is the possibility that two values can
+ * be unioned, this function returns TRUE.
+ *
+ * Returns: TRUE if there is a function allowing the two values to
+ * be unioned.
+ */
+gboolean
+gst_value_can_union (const GValue * value1, const GValue * value2)
+{
+ GstValueUnionInfo *union_info;
+ guint i, len;
+
+ g_return_val_if_fail (G_IS_VALUE (value1), FALSE);
+ g_return_val_if_fail (G_IS_VALUE (value2), FALSE);
+
+ len = gst_value_union_funcs->len;
+
+ for (i = 0; i < len; i++) {
+ union_info = &g_array_index (gst_value_union_funcs, GstValueUnionInfo, i);
+ if (union_info->type1 == G_VALUE_TYPE (value1) &&
+ union_info->type2 == G_VALUE_TYPE (value2))
+ return TRUE;
+ if (union_info->type1 == G_VALUE_TYPE (value2) &&
+ union_info->type2 == G_VALUE_TYPE (value1))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * gst_value_union:
+ * @dest: (out caller-allocates): the destination value
+ * @value1: a value to union
+ * @value2: another value to union
+ *
+ * Creates a GValue corresponding to the union of @value1 and @value2.
+ *
+ * Returns: always returns %TRUE
+ */
+/* FIXME: change return type to 'void'? */
+gboolean
+gst_value_union (GValue * dest, const GValue * value1, const GValue * value2)
+{
+ GstValueUnionInfo *union_info;
+ guint i, len;
+
+ g_return_val_if_fail (dest != NULL, FALSE);
+ g_return_val_if_fail (G_IS_VALUE (value1), FALSE);
+ g_return_val_if_fail (G_IS_VALUE (value2), FALSE);
+
+ len = gst_value_union_funcs->len;
+
+ for (i = 0; i < len; i++) {
+ union_info = &g_array_index (gst_value_union_funcs, GstValueUnionInfo, i);
+ if (union_info->type1 == G_VALUE_TYPE (value1) &&
+ union_info->type2 == G_VALUE_TYPE (value2)) {
+ if (union_info->func (dest, value1, value2)) {
+ return TRUE;
+ }
+ }
+ if (union_info->type1 == G_VALUE_TYPE (value2) &&
+ union_info->type2 == G_VALUE_TYPE (value1)) {
+ if (union_info->func (dest, value2, value1)) {
+ return TRUE;
+ }
+ }
+ }
+
+ gst_value_list_concat (dest, value1, value2);
+ return TRUE;
+}
+
+/**
+ * gst_value_register_union_func:
+ * @type1: a type to union
+ * @type2: another type to union
+ * @func: a function that implements creating a union between the two types
+ *
+ * Registers a union function that can create a union between #GValue items
+ * of the type @type1 and @type2.
+ *
+ * Union functions should be registered at startup before any pipelines are
+ * started, as gst_value_register_union_func() is not thread-safe and cannot
+ * be used at the same time as gst_value_union() or gst_value_can_union().
+ */
+void
+gst_value_register_union_func (GType type1, GType type2, GstValueUnionFunc func)
+{
+ GstValueUnionInfo union_info;
+
+ union_info.type1 = type1;
+ union_info.type2 = type2;
+ union_info.func = func;
+
+ g_array_append_val (gst_value_union_funcs, union_info);
+}
+
+/* intersection */
+
+/**
+ * gst_value_can_intersect:
+ * @value1: a value to intersect
+ * @value2: another value to intersect
+ *
+ * Determines if intersecting two values will produce a valid result.
+ * Two values will produce a valid intersection if they have the same
+ * type, or if there is a method (registered by
+ * gst_value_register_intersect_func()) to calculate the intersection.
+ *
+ * Returns: TRUE if the values can intersect
+ */
+gboolean
+gst_value_can_intersect (const GValue * value1, const GValue * value2)
+{
+ GstValueIntersectInfo *intersect_info;
+ guint i, len;
+ GType ltype, type1, type2;
+
+ g_return_val_if_fail (G_IS_VALUE (value1), FALSE);
+ g_return_val_if_fail (G_IS_VALUE (value2), FALSE);
+
+ ltype = gst_value_list_get_type ();
+
+ /* special cases */
+ if (G_VALUE_HOLDS (value1, ltype) || G_VALUE_HOLDS (value2, ltype))
+ return TRUE;
+
+ type1 = G_VALUE_TYPE (value1);
+ type2 = G_VALUE_TYPE (value2);
+
+ /* practically all GstValue types have a compare function (_can_compare=TRUE)
+ * GstStructure and GstCaps have npot, but are intersectable */
+ if (type1 == type2)
+ return TRUE;
+
+ /* check registered intersect functions */
+ len = gst_value_intersect_funcs->len;
+ for (i = 0; i < len; i++) {
+ intersect_info = &g_array_index (gst_value_intersect_funcs,
+ GstValueIntersectInfo, i);
+ if ((intersect_info->type1 == type1 && intersect_info->type2 == type2) ||
+ (intersect_info->type1 == type2 && intersect_info->type2 == type1))
+ return TRUE;
+ }
+
+ return gst_value_can_compare (value1, value2);
+}
+
+/**
+ * gst_value_intersect:
+ * @dest: (out caller-allocates): a uninitialized #GValue that will hold the calculated
+ * intersection value
+ * @value1: a value to intersect
+ * @value2: another value to intersect
+ *
+ * Calculates the intersection of two values. If the values have
+ * a non-empty intersection, the value representing the intersection
+ * is placed in @dest. If the intersection is non-empty, @dest is
+ * not modified.
+ *
+ * Returns: TRUE if the intersection is non-empty
+ */
+gboolean
+gst_value_intersect (GValue * dest, const GValue * value1,
+ const GValue * value2)
+{
+ GstValueIntersectInfo *intersect_info;
+ guint i, len;
+ GType ltype, type1, type2;
+
+ g_return_val_if_fail (dest != NULL, FALSE);
+ g_return_val_if_fail (G_IS_VALUE (value1), FALSE);
+ g_return_val_if_fail (G_IS_VALUE (value2), FALSE);
+
+ ltype = gst_value_list_get_type ();
+
+ /* special cases first */
+ if (G_VALUE_HOLDS (value1, ltype))
+ return gst_value_intersect_list (dest, value1, value2);
+ if (G_VALUE_HOLDS (value2, ltype))
+ return gst_value_intersect_list (dest, value2, value1);
+
+ if (gst_value_compare (value1, value2) == GST_VALUE_EQUAL) {
+ gst_value_init_and_copy (dest, value1);
+ return TRUE;
+ }
+
+ type1 = G_VALUE_TYPE (value1);
+ type2 = G_VALUE_TYPE (value2);
+
+ len = gst_value_intersect_funcs->len;
+ for (i = 0; i < len; i++) {
+ intersect_info = &g_array_index (gst_value_intersect_funcs,
+ GstValueIntersectInfo, i);
+ if (intersect_info->type1 == type1 && intersect_info->type2 == type2) {
+ return intersect_info->func (dest, value1, value2);
+ }
+ if (intersect_info->type1 == type2 && intersect_info->type2 == type1) {
+ return intersect_info->func (dest, value2, value1);
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * gst_value_register_intersect_func:
+ * @type1: the first type to intersect
+ * @type2: the second type to intersect
+ * @func: the intersection function
+ *
+ * Registers a function that is called to calculate the intersection
+ * of the values having the types @type1 and @type2.
+ *
+ * Intersect functions should be registered at startup before any pipelines are
+ * started, as gst_value_register_intersect_func() is not thread-safe and
+ * cannot be used at the same time as gst_value_intersect() or
+ * gst_value_can_intersect().
+ */
+void
+gst_value_register_intersect_func (GType type1, GType type2,
+ GstValueIntersectFunc func)
+{
+ GstValueIntersectInfo intersect_info;
+
+ intersect_info.type1 = type1;
+ intersect_info.type2 = type2;
+ intersect_info.func = func;
+
+ g_array_append_val (gst_value_intersect_funcs, intersect_info);
+}
+
+
+/* subtraction */
+
+/**
+ * gst_value_subtract:
+ * @dest: (out caller-allocates): the destination value for the result if the
+ * subtraction is not empty
+ * @minuend: the value to subtract from
+ * @subtrahend: the value to subtract
+ *
+ * Subtracts @subtrahend from @minuend and stores the result in @dest.
+ * Note that this means subtraction as in sets, not as in mathematics.
+ *
+ * Returns: %TRUE if the subtraction is not empty
+ */
+gboolean
+gst_value_subtract (GValue * dest, const GValue * minuend,
+ const GValue * subtrahend)
+{
+ GstValueSubtractInfo *info;
+ guint i, len;
+ GType ltype, mtype, stype;
+
+ g_return_val_if_fail (dest != NULL, FALSE);
+ g_return_val_if_fail (G_IS_VALUE (minuend), FALSE);
+ g_return_val_if_fail (G_IS_VALUE (subtrahend), FALSE);
+
+ ltype = gst_value_list_get_type ();
+
+ /* special cases first */
+ if (G_VALUE_HOLDS (minuend, ltype))
+ return gst_value_subtract_from_list (dest, minuend, subtrahend);
+ if (G_VALUE_HOLDS (subtrahend, ltype))
+ return gst_value_subtract_list (dest, minuend, subtrahend);
+
+ mtype = G_VALUE_TYPE (minuend);
+ stype = G_VALUE_TYPE (subtrahend);
+
+ len = gst_value_subtract_funcs->len;
+ for (i = 0; i < len; i++) {
+ info = &g_array_index (gst_value_subtract_funcs, GstValueSubtractInfo, i);
+ if (info->minuend == mtype && info->subtrahend == stype) {
+ return info->func (dest, minuend, subtrahend);
+ }
+ }
+
+ if (gst_value_compare (minuend, subtrahend) != GST_VALUE_EQUAL) {
+ gst_value_init_and_copy (dest, minuend);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+#if 0
+gboolean
+gst_value_subtract (GValue * dest, const GValue * minuend,
+ const GValue * subtrahend)
+{
+ gboolean ret = gst_value_subtract2 (dest, minuend, subtrahend);
+
+ g_printerr ("\"%s\" - \"%s\" = \"%s\"\n", gst_value_serialize (minuend),
+ gst_value_serialize (subtrahend),
+ ret ? gst_value_serialize (dest) : "---");
+ return ret;
+}
+#endif
+
+/**
+ * gst_value_can_subtract:
+ * @minuend: the value to subtract from
+ * @subtrahend: the value to subtract
+ *
+ * Checks if it's possible to subtract @subtrahend from @minuend.
+ *
+ * Returns: TRUE if a subtraction is possible
+ */
+gboolean
+gst_value_can_subtract (const GValue * minuend, const GValue * subtrahend)
+{
+ GstValueSubtractInfo *info;
+ guint i, len;
+ GType ltype, mtype, stype;
+
+ g_return_val_if_fail (G_IS_VALUE (minuend), FALSE);
+ g_return_val_if_fail (G_IS_VALUE (subtrahend), FALSE);
+
+ ltype = gst_value_list_get_type ();
+
+ /* special cases */
+ if (G_VALUE_HOLDS (minuend, ltype) || G_VALUE_HOLDS (subtrahend, ltype))
+ return TRUE;
+
+ mtype = G_VALUE_TYPE (minuend);
+ stype = G_VALUE_TYPE (subtrahend);
+
+ len = gst_value_subtract_funcs->len;
+ for (i = 0; i < len; i++) {
+ info = &g_array_index (gst_value_subtract_funcs, GstValueSubtractInfo, i);
+ if (info->minuend == mtype && info->subtrahend == stype)
+ return TRUE;
+ }
+
+ return gst_value_can_compare (minuend, subtrahend);
+}
+
+/**
+ * gst_value_register_subtract_func:
+ * @minuend_type: type of the minuend
+ * @subtrahend_type: type of the subtrahend
+ * @func: function to use
+ *
+ * Registers @func as a function capable of subtracting the values of
+ * @subtrahend_type from values of @minuend_type.
+ *
+ * Subtract functions should be registered at startup before any pipelines are
+ * started, as gst_value_register_subtract_func() is not thread-safe and
+ * cannot be used at the same time as gst_value_subtract().
+ */
+void
+gst_value_register_subtract_func (GType minuend_type, GType subtrahend_type,
+ GstValueSubtractFunc func)
+{
+ GstValueSubtractInfo info;
+
+ /* one type must be unfixed, other subtractions can be done as comparisons */
+ g_return_if_fail (!gst_type_is_fixed (minuend_type)
+ || !gst_type_is_fixed (subtrahend_type));
+
+ info.minuend = minuend_type;
+ info.subtrahend = subtrahend_type;
+ info.func = func;
+
+ g_array_append_val (gst_value_subtract_funcs, info);
+}
+
+/**
+ * gst_value_register:
+ * @table: structure containing functions to register
+ *
+ * Registers functions to perform calculations on #GValue items of a given
+ * type. Each type can only be added once.
+ */
+void
+gst_value_register (const GstValueTable * table)
+{
+ GstValueTable *found;
+
+ g_return_if_fail (table != NULL);
+
+ g_array_append_val (gst_value_table, *table);
+
+ found = gst_value_hash_lookup_type (table->type);
+ if (found)
+ g_warning ("adding type %s multiple times", g_type_name (table->type));
+
+ /* FIXME: we're not really doing the const justice, we assume the table is
+ * static */
+ gst_value_hash_add_type (table->type, table);
+}
+
+/**
+ * gst_value_init_and_copy:
+ * @dest: (out caller-allocates): the target value
+ * @src: the source value
+ *
+ * Initialises the target value to be of the same type as source and then copies
+ * the contents from source to target.
+ */
+void
+gst_value_init_and_copy (GValue * dest, const GValue * src)
+{
+ g_return_if_fail (G_IS_VALUE (src));
+ g_return_if_fail (dest != NULL);
+
+ g_value_init (dest, G_VALUE_TYPE (src));
+ g_value_copy (src, dest);
+}
+
+/**
+ * gst_value_serialize:
+ * @value: a #GValue to serialize
+ *
+ * tries to transform the given @value into a string representation that allows
+ * getting back this string later on using gst_value_deserialize().
+ *
+ * Free-function: g_free
+ *
+ * Returns: (transfer full): the serialization for @value or NULL if none exists
+ */
+gchar *
+gst_value_serialize (const GValue * value)
+{
+ guint i, len;
+ GValue s_val = { 0 };
+ GstValueTable *table, *best;
+ gchar *s;
+ GType type;
+
+ g_return_val_if_fail (G_IS_VALUE (value), NULL);
+
+ type = G_VALUE_TYPE (value);
+
+ best = gst_value_hash_lookup_type (type);
+
+ if (G_UNLIKELY (!best || !best->serialize)) {
+ len = gst_value_table->len;
+ best = NULL;
+ for (i = 0; i < len; i++) {
+ table = &g_array_index (gst_value_table, GstValueTable, i);
+ if (table->serialize && g_type_is_a (type, table->type)) {
+ if (!best || g_type_is_a (table->type, best->type))
+ best = table;
+ }
+ }
+ }
+ if (G_LIKELY (best))
+ return best->serialize (value);
+
+ g_value_init (&s_val, G_TYPE_STRING);
+ if (g_value_transform (value, &s_val)) {
+ s = gst_string_wrap (g_value_get_string (&s_val));
+ } else {
+ s = NULL;
+ }
+ g_value_unset (&s_val);
+
+ return s;
+}
+
+/**
+ * gst_value_deserialize:
+ * @dest: (out caller-allocates): #GValue to fill with contents of
+ * deserialization
+ * @src: string to deserialize
+ *
+ * Tries to deserialize a string into the type specified by the given GValue.
+ * If the operation succeeds, TRUE is returned, FALSE otherwise.
+ *
+ * Returns: TRUE on success
+ */
+gboolean
+gst_value_deserialize (GValue * dest, const gchar * src)
+{
+ GstValueTable *table, *best;
+ guint i, len;
+ GType type;
+
+ g_return_val_if_fail (src != NULL, FALSE);
+ g_return_val_if_fail (G_IS_VALUE (dest), FALSE);
+
+ type = G_VALUE_TYPE (dest);
+
+ best = gst_value_hash_lookup_type (type);
+ if (G_UNLIKELY (!best || !best->deserialize)) {
+ len = gst_value_table->len;
+ best = NULL;
+ for (i = 0; i < len; i++) {
+ table = &g_array_index (gst_value_table, GstValueTable, i);
+ if (table->deserialize && g_type_is_a (type, table->type)) {
+ if (!best || g_type_is_a (table->type, best->type))
+ best = table;
+ }
+ }
+ }
+ if (G_LIKELY (best))
+ return best->deserialize (dest, src);
+
+ return FALSE;
+}
+
+/**
+ * gst_value_is_fixed:
+ * @value: the #GValue to check
+ *
+ * Tests if the given GValue, if available in a GstStructure (or any other
+ * container) contains a "fixed" (which means: one value) or an "unfixed"
+ * (which means: multiple possible values, such as data lists or data
+ * ranges) value.
+ *
+ * Returns: true if the value is "fixed".
+ */
+
+gboolean
+gst_value_is_fixed (const GValue * value)
+{
+ GType type;
+
+ g_return_val_if_fail (G_IS_VALUE (value), FALSE);
+
+ type = G_VALUE_TYPE (value);
+
+ /* the most common types are just basic plain glib types */
+ if (type <= G_TYPE_MAKE_FUNDAMENTAL (G_TYPE_RESERVED_GLIB_LAST)) {
+ return TRUE;
+ }
+
+ if (type == GST_TYPE_ARRAY) {
+ gint size, n;
+ const GValue *kid;
+
+ /* check recursively */
+ size = gst_value_array_get_size (value);
+ for (n = 0; n < size; n++) {
+ kid = gst_value_array_get_value (value, n);
+ if (!gst_value_is_fixed (kid))
+ return FALSE;
+ }
+ return TRUE;
+ }
+ return gst_type_is_fixed (type);
+}
+
+/**
+ * gst_value_fixate:
+ * @dest: the #GValue destination
+ * @src: the #GValue to fixate
+ *
+ * Fixate @src into a new value @dest.
+ * For ranges, the first element is taken. For lists and arrays, the
+ * first item is fixated and returned.
+ * If @src is already fixed, this function returns FALSE.
+ *
+ * Returns: true if @dest contains a fixated version of @src.
+ */
+gboolean
+gst_value_fixate (GValue * dest, const GValue * src)
+{
+ g_return_val_if_fail (G_IS_VALUE (src), FALSE);
+ g_return_val_if_fail (dest != NULL, FALSE);
+
+ if (G_VALUE_TYPE (src) == GST_TYPE_INT_RANGE) {
+ g_value_init (dest, G_TYPE_INT);
+ g_value_set_int (dest, gst_value_get_int_range_min (src));
+ } else if (G_VALUE_TYPE (src) == GST_TYPE_DOUBLE_RANGE) {
+ g_value_init (dest, G_TYPE_DOUBLE);
+ g_value_set_double (dest, gst_value_get_double_range_min (src));
+ } else if (G_VALUE_TYPE (src) == GST_TYPE_FRACTION_RANGE) {
+ gst_value_init_and_copy (dest, gst_value_get_fraction_range_min (src));
+ } else if (G_VALUE_TYPE (src) == GST_TYPE_LIST) {
+ GValue temp = { 0 };
+
+ /* list could be empty */
+ if (gst_value_list_get_size (src) <= 0)
+ return FALSE;
+
+ gst_value_init_and_copy (&temp, gst_value_list_get_value (src, 0));
+
+ if (!gst_value_fixate (dest, &temp))
+ gst_value_init_and_copy (dest, &temp);
+ g_value_unset (&temp);
+ } else if (G_VALUE_TYPE (src) == GST_TYPE_ARRAY) {
+ gboolean res = FALSE;
+ guint n, len;
+
+ len = gst_value_array_get_size (src);
+ g_value_init (dest, GST_TYPE_ARRAY);
+ for (n = 0; n < len; n++) {
+ GValue kid = { 0 };
+ const GValue *orig_kid = gst_value_array_get_value (src, n);
+
+ if (!gst_value_fixate (&kid, orig_kid))
+ gst_value_init_and_copy (&kid, orig_kid);
+ else
+ res = TRUE;
+ gst_value_array_append_value (dest, &kid);
+ g_value_unset (&kid);
+ }
+
+ if (!res)
+ g_value_unset (dest);
+
+ return res;
+ } else {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+/************
+ * fraction *
+ ************/
+
+/* helper functions */
+static void
+gst_value_init_fraction (GValue * value)
+{
+ value->data[0].v_int = 0;
+ value->data[1].v_int = 1;
+}
+
+static void
+gst_value_copy_fraction (const GValue * src_value, GValue * dest_value)
+{
+ dest_value->data[0].v_int = src_value->data[0].v_int;
+ dest_value->data[1].v_int = src_value->data[1].v_int;
+}
+
+static gchar *
+gst_value_collect_fraction (GValue * value, guint n_collect_values,
+ GTypeCValue * collect_values, guint collect_flags)
+{
+ if (n_collect_values != 2)
+ return g_strdup_printf ("not enough value locations for `%s' passed",
+ G_VALUE_TYPE_NAME (value));
+ if (collect_values[1].v_int == 0)
+ return g_strdup_printf ("passed '0' as denominator for `%s'",
+ G_VALUE_TYPE_NAME (value));
+ if (collect_values[0].v_int < -G_MAXINT)
+ return
+ g_strdup_printf
+ ("passed value smaller than -G_MAXINT as numerator for `%s'",
+ G_VALUE_TYPE_NAME (value));
+ if (collect_values[1].v_int < -G_MAXINT)
+ return
+ g_strdup_printf
+ ("passed value smaller than -G_MAXINT as denominator for `%s'",
+ G_VALUE_TYPE_NAME (value));
+
+ gst_value_set_fraction (value,
+ collect_values[0].v_int, collect_values[1].v_int);
+
+ return NULL;
+}
+
+static gchar *
+gst_value_lcopy_fraction (const GValue * value, guint n_collect_values,
+ GTypeCValue * collect_values, guint collect_flags)
+{
+ gint *numerator = collect_values[0].v_pointer;
+ gint *denominator = collect_values[1].v_pointer;
+
+ if (!numerator)
+ return g_strdup_printf ("numerator for `%s' passed as NULL",
+ G_VALUE_TYPE_NAME (value));
+ if (!denominator)
+ return g_strdup_printf ("denominator for `%s' passed as NULL",
+ G_VALUE_TYPE_NAME (value));
+
+ *numerator = value->data[0].v_int;
+ *denominator = value->data[1].v_int;
+
+ return NULL;
+}
+
+/**
+ * gst_value_set_fraction:
+ * @value: a GValue initialized to #GST_TYPE_FRACTION
+ * @numerator: the numerator of the fraction
+ * @denominator: the denominator of the fraction
+ *
+ * Sets @value to the fraction specified by @numerator over @denominator.
+ * The fraction gets reduced to the smallest numerator and denominator,
+ * and if necessary the sign is moved to the numerator.
+ */
+void
+gst_value_set_fraction (GValue * value, gint numerator, gint denominator)
+{
+ gint gcd = 0;
+
+ g_return_if_fail (GST_VALUE_HOLDS_FRACTION (value));
+ g_return_if_fail (denominator != 0);
+ g_return_if_fail (denominator >= -G_MAXINT);
+ g_return_if_fail (numerator >= -G_MAXINT);
+
+ /* normalize sign */
+ if (denominator < 0) {
+ numerator = -numerator;
+ denominator = -denominator;
+ }
+
+ /* check for reduction */
+ gcd = gst_util_greatest_common_divisor (numerator, denominator);
+ if (gcd) {
+ numerator /= gcd;
+ denominator /= gcd;
+ }
+
+ g_assert (denominator > 0);
+
+ value->data[0].v_int = numerator;
+ value->data[1].v_int = denominator;
+}
+
+/**
+ * gst_value_get_fraction_numerator:
+ * @value: a GValue initialized to #GST_TYPE_FRACTION
+ *
+ * Gets the numerator of the fraction specified by @value.
+ *
+ * Returns: the numerator of the fraction.
+ */
+gint
+gst_value_get_fraction_numerator (const GValue * value)
+{
+ g_return_val_if_fail (GST_VALUE_HOLDS_FRACTION (value), 0);
+
+ return value->data[0].v_int;
+}
+
+/**
+ * gst_value_get_fraction_denominator:
+ * @value: a GValue initialized to #GST_TYPE_FRACTION
+ *
+ * Gets the denominator of the fraction specified by @value.
+ *
+ * Returns: the denominator of the fraction.
+ */
+gint
+gst_value_get_fraction_denominator (const GValue * value)
+{
+ g_return_val_if_fail (GST_VALUE_HOLDS_FRACTION (value), 1);
+
+ return value->data[1].v_int;
+}
+
+/**
+ * gst_value_fraction_multiply:
+ * @product: a GValue initialized to #GST_TYPE_FRACTION
+ * @factor1: a GValue initialized to #GST_TYPE_FRACTION
+ * @factor2: a GValue initialized to #GST_TYPE_FRACTION
+ *
+ * Multiplies the two #GValue items containing a #GST_TYPE_FRACTION and sets
+ * @product to the product of the two fractions.
+ *
+ * Returns: FALSE in case of an error (like integer overflow), TRUE otherwise.
+ */
+gboolean
+gst_value_fraction_multiply (GValue * product, const GValue * factor1,
+ const GValue * factor2)
+{
+ gint n1, n2, d1, d2;
+ gint res_n, res_d;
+
+ g_return_val_if_fail (product != NULL, FALSE);
+ g_return_val_if_fail (GST_VALUE_HOLDS_FRACTION (factor1), FALSE);
+ g_return_val_if_fail (GST_VALUE_HOLDS_FRACTION (factor2), FALSE);
+
+ n1 = factor1->data[0].v_int;
+ n2 = factor2->data[0].v_int;
+ d1 = factor1->data[1].v_int;
+ d2 = factor2->data[1].v_int;
+
+ if (!gst_util_fraction_multiply (n1, d1, n2, d2, &res_n, &res_d))
+ return FALSE;
+
+ gst_value_set_fraction (product, res_n, res_d);
+
+ return TRUE;
+}
+
+/**
+ * gst_value_fraction_subtract:
+ * @dest: a GValue initialized to #GST_TYPE_FRACTION
+ * @minuend: a GValue initialized to #GST_TYPE_FRACTION
+ * @subtrahend: a GValue initialized to #GST_TYPE_FRACTION
+ *
+ * Subtracts the @subtrahend from the @minuend and sets @dest to the result.
+ *
+ * Returns: FALSE in case of an error (like integer overflow), TRUE otherwise.
+ */
+gboolean
+gst_value_fraction_subtract (GValue * dest,
+ const GValue * minuend, const GValue * subtrahend)
+{
+ gint n1, n2, d1, d2;
+ gint res_n, res_d;
+
+ g_return_val_if_fail (dest != NULL, FALSE);
+ g_return_val_if_fail (GST_VALUE_HOLDS_FRACTION (minuend), FALSE);
+ g_return_val_if_fail (GST_VALUE_HOLDS_FRACTION (subtrahend), FALSE);
+
+ n1 = minuend->data[0].v_int;
+ n2 = subtrahend->data[0].v_int;
+ d1 = minuend->data[1].v_int;
+ d2 = subtrahend->data[1].v_int;
+
+ if (!gst_util_fraction_add (n1, d1, -n2, d2, &res_n, &res_d))
+ return FALSE;
+ gst_value_set_fraction (dest, res_n, res_d);
+
+ return TRUE;
+}
+
+static gchar *
+gst_value_serialize_fraction (const GValue * value)
+{
+ gint32 numerator = value->data[0].v_int;
+ gint32 denominator = value->data[1].v_int;
+ gboolean positive = TRUE;
+
+ /* get the sign and make components absolute */
+ if (numerator < 0) {
+ numerator = -numerator;
+ positive = !positive;
+ }
+ if (denominator < 0) {
+ denominator = -denominator;
+ positive = !positive;
+ }
+
+ return g_strdup_printf ("%s%d/%d",
+ positive ? "" : "-", numerator, denominator);
+}
+
+static gboolean
+gst_value_deserialize_fraction (GValue * dest, const gchar * s)
+{
+ gint num, den;
+ gint num_chars;
+
+ if (G_UNLIKELY (s == NULL))
+ return FALSE;
+
+ if (G_UNLIKELY (dest == NULL || !GST_VALUE_HOLDS_FRACTION (dest)))
+ return FALSE;
+
+ if (sscanf (s, "%d/%d%n", &num, &den, &num_chars) >= 2) {
+ if (s[num_chars] != 0)
+ return FALSE;
+ if (den == 0)
+ return FALSE;
+
+ gst_value_set_fraction (dest, num, den);
+ return TRUE;
+ } else if (g_ascii_strcasecmp (s, "1/max") == 0) {
+ gst_value_set_fraction (dest, 1, G_MAXINT);
+ return TRUE;
+ } else if (sscanf (s, "%d%n", &num, &num_chars) >= 1) {
+ if (s[num_chars] != 0)
+ return FALSE;
+ gst_value_set_fraction (dest, num, 1);
+ return TRUE;
+ } else if (g_ascii_strcasecmp (s, "min") == 0) {
+ gst_value_set_fraction (dest, -G_MAXINT, 1);
+ return TRUE;
+ } else if (g_ascii_strcasecmp (s, "max") == 0) {
+ gst_value_set_fraction (dest, G_MAXINT, 1);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+gst_value_transform_fraction_string (const GValue * src_value,
+ GValue * dest_value)
+{
+ dest_value->data[0].v_pointer = gst_value_serialize_fraction (src_value);
+}
+
+static void
+gst_value_transform_string_fraction (const GValue * src_value,
+ GValue * dest_value)
+{
+ if (!gst_value_deserialize_fraction (dest_value,
+ src_value->data[0].v_pointer))
+ /* If the deserialize fails, ensure we leave the fraction in a
+ * valid, if incorrect, state */
+ gst_value_set_fraction (dest_value, 0, 1);
+}
+
+static void
+gst_value_transform_double_fraction (const GValue * src_value,
+ GValue * dest_value)
+{
+ gdouble src = g_value_get_double (src_value);
+ gint n, d;
+
+ gst_util_double_to_fraction (src, &n, &d);
+ gst_value_set_fraction (dest_value, n, d);
+}
+
+static void
+gst_value_transform_float_fraction (const GValue * src_value,
+ GValue * dest_value)
+{
+ gfloat src = g_value_get_float (src_value);
+ gint n, d;
+
+ gst_util_double_to_fraction (src, &n, &d);
+ gst_value_set_fraction (dest_value, n, d);
+}
+
+static void
+gst_value_transform_fraction_double (const GValue * src_value,
+ GValue * dest_value)
+{
+ dest_value->data[0].v_double = ((double) src_value->data[0].v_int) /
+ ((double) src_value->data[1].v_int);
+}
+
+static void
+gst_value_transform_fraction_float (const GValue * src_value,
+ GValue * dest_value)
+{
+ dest_value->data[0].v_float = ((float) src_value->data[0].v_int) /
+ ((float) src_value->data[1].v_int);
+}
+
+static gint
+gst_value_compare_fraction (const GValue * value1, const GValue * value2)
+{
+ gint n1, n2;
+ gint d1, d2;
+ gint ret;
+
+ n1 = value1->data[0].v_int;
+ n2 = value2->data[0].v_int;
+ d1 = value1->data[1].v_int;
+ d2 = value2->data[1].v_int;
+
+ /* fractions are reduced when set, so we can quickly see if they're equal */
+ if (n1 == n2 && d1 == d2)
+ return GST_VALUE_EQUAL;
+
+ if (d1 == 0 && d2 == 0)
+ return GST_VALUE_UNORDERED;
+ else if (d1 == 0)
+ return GST_VALUE_GREATER_THAN;
+ else if (d2 == 0)
+ return GST_VALUE_LESS_THAN;
+
+ ret = gst_util_fraction_compare (n1, d1, n2, d2);
+ if (ret == -1)
+ return GST_VALUE_LESS_THAN;
+ else if (ret == 1)
+ return GST_VALUE_GREATER_THAN;
+
+ /* Equality can't happen here because we check for that
+ * first already */
+ g_return_val_if_reached (GST_VALUE_UNORDERED);
+}
+
+/*********
+ * GDate *
+ *********/
+
+/**
+ * gst_value_set_date:
+ * @value: a GValue initialized to GST_TYPE_DATE
+ * @date: the date to set the value to
+ *
+ * Sets the contents of @value to correspond to @date. The actual
+ * #GDate structure is copied before it is used.
+ */
+void
+gst_value_set_date (GValue * value, const GDate * date)
+{
+ g_return_if_fail (G_VALUE_TYPE (value) == GST_TYPE_DATE);
+ g_return_if_fail (g_date_valid (date));
+
+ g_value_set_boxed (value, date);
+}
+
+/**
+ * gst_value_get_date:
+ * @value: a GValue initialized to GST_TYPE_DATE
+ *
+ * Gets the contents of @value.
+ *
+ * Returns: (transfer none): the contents of @value
+ */
+const GDate *
+gst_value_get_date (const GValue * value)
+{
+ g_return_val_if_fail (G_VALUE_TYPE (value) == GST_TYPE_DATE, NULL);
+
+ return (const GDate *) g_value_get_boxed (value);
+}
+
+static gpointer
+gst_date_copy (gpointer boxed)
+{
+ const GDate *date = (const GDate *) boxed;
+
+ if (!g_date_valid (date)) {
+ GST_WARNING ("invalid GDate");
+ return NULL;
+ }
+
+ return g_date_new_julian (g_date_get_julian (date));
+}
+
+static gint
+gst_value_compare_date (const GValue * value1, const GValue * value2)
+{
+ const GDate *date1 = (const GDate *) g_value_get_boxed (value1);
+ const GDate *date2 = (const GDate *) g_value_get_boxed (value2);
+ guint32 j1, j2;
+
+ if (date1 == date2)
+ return GST_VALUE_EQUAL;
+
+ if ((date1 == NULL || !g_date_valid (date1))
+ && (date2 != NULL && g_date_valid (date2))) {
+ return GST_VALUE_LESS_THAN;
+ }
+
+ if ((date2 == NULL || !g_date_valid (date2))
+ && (date1 != NULL && g_date_valid (date1))) {
+ return GST_VALUE_GREATER_THAN;
+ }
+
+ if (date1 == NULL || date2 == NULL || !g_date_valid (date1)
+ || !g_date_valid (date2)) {
+ return GST_VALUE_UNORDERED;
+ }
+
+ j1 = g_date_get_julian (date1);
+ j2 = g_date_get_julian (date2);
+
+ if (j1 == j2)
+ return GST_VALUE_EQUAL;
+ else if (j1 < j2)
+ return GST_VALUE_LESS_THAN;
+ else
+ return GST_VALUE_GREATER_THAN;
+}
+
+static gchar *
+gst_value_serialize_date (const GValue * val)
+{
+ const GDate *date = (const GDate *) g_value_get_boxed (val);
+
+ if (date == NULL || !g_date_valid (date))
+ return g_strdup ("9999-99-99");
+
+ return g_strdup_printf ("%04u-%02u-%02u", g_date_get_year (date),
+ g_date_get_month (date), g_date_get_day (date));
+}
+
+static gboolean
+gst_value_deserialize_date (GValue * dest, const gchar * s)
+{
+ guint year, month, day;
+
+ if (!s || sscanf (s, "%04u-%02u-%02u", &year, &month, &day) != 3)
+ return FALSE;
+
+ if (!g_date_valid_dmy (day, month, year))
+ return FALSE;
+
+ g_value_take_boxed (dest, g_date_new_dmy (day, month, year));
+ return TRUE;
+}
+
+/*************
+ * GstDateTime *
+ *************/
+
+static gint
+gst_value_compare_date_time (const GValue * value1, const GValue * value2)
+{
+ const GstDateTime *date1 = (const GstDateTime *) g_value_get_boxed (value1);
+ const GstDateTime *date2 = (const GstDateTime *) g_value_get_boxed (value2);
+ gint ret;
+
+ if (date1 == date2)
+ return GST_VALUE_EQUAL;
+
+ if ((date1 == NULL) && (date2 != NULL)) {
+ return GST_VALUE_LESS_THAN;
+ }
+ if ((date2 == NULL) && (date1 != NULL)) {
+ return GST_VALUE_LESS_THAN;
+ }
+
+ ret = priv_gst_date_time_compare (date1, date2);
+
+ if (ret == 0)
+ return GST_VALUE_EQUAL;
+ else if (ret < 0)
+ return GST_VALUE_LESS_THAN;
+ else
+ return GST_VALUE_GREATER_THAN;
+}
+
+static gchar *
+gst_value_serialize_date_time (const GValue * val)
+{
+ GstDateTime *date = (GstDateTime *) g_value_get_boxed (val);
+ gfloat offset;
+ gint tzhour, tzminute;
+
+ if (date == NULL)
+ return g_strdup ("null");
+
+ offset = gst_date_time_get_time_zone_offset (date);
+
+ tzhour = (gint) ABS (offset);
+ tzminute = (gint) ((ABS (offset) - tzhour) * 60);
+
+ return g_strdup_printf ("\"%04d-%02d-%02dT%02d:%02d:%02d.%06d"
+ "%c%02d%02d\"", gst_date_time_get_year (date),
+ gst_date_time_get_month (date), gst_date_time_get_day (date),
+ gst_date_time_get_hour (date), gst_date_time_get_minute (date),
+ gst_date_time_get_second (date), gst_date_time_get_microsecond (date),
+ offset >= 0 ? '+' : '-', tzhour, tzminute);
+}
+
+static gboolean
+gst_value_deserialize_date_time (GValue * dest, const gchar * s)
+{
+ gint year, month, day, hour, minute, second, usecond;
+ gchar signal;
+ gint offset = 0;
+ gfloat tzoffset = 0;
+ gint ret;
+
+ if (!s || strcmp (s, "null") == 0) {
+ return FALSE;
+ }
+
+ ret = sscanf (s, "%04d-%02d-%02dT%02d:%02d:%02d.%06d%c%04d",
+ &year, &month, &day, &hour, &minute, &second, &usecond, &signal, &offset);
+ if (ret >= 9) {
+ tzoffset = (offset / 100) + ((offset % 100) / 60.0);
+ if (signal == '-')
+ tzoffset = -tzoffset;
+ } else
+ return FALSE;
+
+ g_value_take_boxed (dest, gst_date_time_new (tzoffset, year, month, day, hour,
+ minute, second + (usecond / 1000000.0)));
+ return TRUE;
+}
+
+static void
+gst_value_transform_date_string (const GValue * src_value, GValue * dest_value)
+{
+ dest_value->data[0].v_pointer = gst_value_serialize_date (src_value);
+}
+
+static void
+gst_value_transform_string_date (const GValue * src_value, GValue * dest_value)
+{
+ gst_value_deserialize_date (dest_value, src_value->data[0].v_pointer);
+}
+
+static void
+gst_value_transform_object_string (const GValue * src_value,
+ GValue * dest_value)
+{
+ GstObject *obj;
+ gchar *str;
+
+ obj = g_value_get_object (src_value);
+ if (obj) {
+ str =
+ g_strdup_printf ("(%s) %s", G_OBJECT_TYPE_NAME (obj),
+ GST_OBJECT_NAME (obj));
+ } else {
+ str = g_strdup ("NULL");
+ }
+
+ dest_value->data[0].v_pointer = str;
+}
+
+static GTypeInfo _info = {
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ 0,
+ 0,
+ NULL,
+ NULL,
+};
+
+static GTypeFundamentalInfo _finfo = {
+ 0
+};
+
+#define FUNC_VALUE_GET_TYPE(type, name) \
+GType gst_ ## type ## _get_type (void) \
+{ \
+ static volatile GType gst_ ## type ## _type = 0; \
+ \
+ if (g_once_init_enter (&gst_ ## type ## _type)) { \
+ GType _type; \
+ _info.value_table = & _gst_ ## type ## _value_table; \
+ _type = g_type_register_fundamental ( \
+ g_type_fundamental_next (), \
+ name, &_info, &_finfo, 0); \
+ g_once_init_leave(&gst_ ## type ## _type, _type); \
+ } \
+ \
+ return gst_ ## type ## _type; \
+}
+
+static const GTypeValueTable _gst_int_range_value_table = {
+ gst_value_init_int_range,
+ NULL,
+ gst_value_copy_int_range,
+ NULL,
+ (char *) "ii",
+ gst_value_collect_int_range,
+ (char *) "pp",
+ gst_value_lcopy_int_range
+};
+
+FUNC_VALUE_GET_TYPE (int_range, "GstIntRange");
+
+static const GTypeValueTable _gst_int64_range_value_table = {
+ gst_value_init_int64_range,
+ NULL,
+ gst_value_copy_int64_range,
+ NULL,
+ (char *) "qq",
+ gst_value_collect_int64_range,
+ (char *) "pp",
+ gst_value_lcopy_int64_range
+};
+
+FUNC_VALUE_GET_TYPE (int64_range, "GstInt64Range");
+
+static const GTypeValueTable _gst_double_range_value_table = {
+ gst_value_init_double_range,
+ NULL,
+ gst_value_copy_double_range,
+ NULL,
+ (char *) "dd",
+ gst_value_collect_double_range,
+ (char *) "pp",
+ gst_value_lcopy_double_range
+};
+
+FUNC_VALUE_GET_TYPE (double_range, "GstDoubleRange");
+
+static const GTypeValueTable _gst_fraction_range_value_table = {
+ gst_value_init_fraction_range,
+ gst_value_free_fraction_range,
+ gst_value_copy_fraction_range,
+ NULL,
+ (char *) "iiii",
+ gst_value_collect_fraction_range,
+ (char *) "pppp",
+ gst_value_lcopy_fraction_range
+};
+
+FUNC_VALUE_GET_TYPE (fraction_range, "GstFractionRange");
+
+static const GTypeValueTable _gst_value_list_value_table = {
+ gst_value_init_list_or_array,
+ gst_value_free_list_or_array,
+ gst_value_copy_list_or_array,
+ gst_value_list_or_array_peek_pointer,
+ (char *) "p",
+ gst_value_collect_list_or_array,
+ (char *) "p",
+ gst_value_lcopy_list_or_array
+};
+
+FUNC_VALUE_GET_TYPE (value_list, "GstValueList");
+
+static const GTypeValueTable _gst_value_array_value_table = {
+ gst_value_init_list_or_array,
+ gst_value_free_list_or_array,
+ gst_value_copy_list_or_array,
+ gst_value_list_or_array_peek_pointer,
+ (char *) "p",
+ gst_value_collect_list_or_array,
+ (char *) "p",
+ gst_value_lcopy_list_or_array
+};
+
+FUNC_VALUE_GET_TYPE (value_array, "GstValueArray");
+
+static const GTypeValueTable _gst_fraction_value_table = {
+ gst_value_init_fraction,
+ NULL,
+ gst_value_copy_fraction,
+ NULL,
+ (char *) "ii",
+ gst_value_collect_fraction,
+ (char *) "pp",
+ gst_value_lcopy_fraction
+};
+
+FUNC_VALUE_GET_TYPE (fraction, "GstFraction");
+
+
+GType
+gst_date_get_type (void)
+{
+ static GType gst_date_type = 0;
+
+ if (G_UNLIKELY (gst_date_type == 0)) {
+ /* FIXME 0.11: we require GLib 2.8 already
+ * Not using G_TYPE_DATE here on purpose, even if we could
+ * if GLIB_CHECK_VERSION(2,8,0) was true: we don't want the
+ * serialised strings to have different type strings depending
+ * on what version is used, so FIXME when we require GLib-2.8 */
+ gst_date_type = g_boxed_type_register_static ("GstDate",
+ (GBoxedCopyFunc) gst_date_copy, (GBoxedFreeFunc) g_date_free);
+ }
+
+ return gst_date_type;
+}
+
+GType
+gst_date_time_get_type (void)
+{
+ static GType gst_date_time_type = 0;
+
+ if (G_UNLIKELY (gst_date_time_type == 0)) {
+ gst_date_time_type = g_boxed_type_register_static ("GstDateTime",
+ (GBoxedCopyFunc) gst_date_time_ref,
+ (GBoxedFreeFunc) gst_date_time_unref);
+ }
+
+ return gst_date_time_type;
+}
+
+
+void
+_priv_gst_value_initialize (void)
+{
+ gst_value_table = g_array_new (FALSE, FALSE, sizeof (GstValueTable));
+ gst_value_hash = g_hash_table_new (NULL, NULL);
+ gst_value_union_funcs = g_array_new (FALSE, FALSE,
+ sizeof (GstValueUnionInfo));
+ gst_value_intersect_funcs = g_array_new (FALSE, FALSE,
+ sizeof (GstValueIntersectInfo));
+ gst_value_subtract_funcs = g_array_new (FALSE, FALSE,
+ sizeof (GstValueSubtractInfo));
+
+ {
+ static GstValueTable gst_value = {
+ 0,
+ gst_value_compare_int_range,
+ gst_value_serialize_int_range,
+ gst_value_deserialize_int_range,
+ };
+
+ gst_value.type = gst_int_range_get_type ();
+ gst_value_register (&gst_value);
+ }
+
+ {
+ static GstValueTable gst_value = {
+ 0,
+ gst_value_compare_int64_range,
+ gst_value_serialize_int64_range,
+ gst_value_deserialize_int64_range,
+ };
+
+ gst_value.type = gst_int64_range_get_type ();
+ gst_value_register (&gst_value);
+ }
+
+ {
+ static GstValueTable gst_value = {
+ 0,
+ gst_value_compare_double_range,
+ gst_value_serialize_double_range,
+ gst_value_deserialize_double_range,
+ };
+
+ gst_value.type = gst_double_range_get_type ();
+ gst_value_register (&gst_value);
+ }
+
+ {
+ static GstValueTable gst_value = {
+ 0,
+ gst_value_compare_fraction_range,
+ gst_value_serialize_fraction_range,
+ gst_value_deserialize_fraction_range,
+ };
+
+ gst_value.type = gst_fraction_range_get_type ();
+ gst_value_register (&gst_value);
+ }
+
+ {
+ static GstValueTable gst_value = {
+ 0,
+ gst_value_compare_list,
+ gst_value_serialize_list,
+ gst_value_deserialize_list,
+ };
+
+ gst_value.type = gst_value_list_get_type ();
+ gst_value_register (&gst_value);
+ }
+
+ {
+ static GstValueTable gst_value = {
+ 0,
+ gst_value_compare_array,
+ gst_value_serialize_array,
+ gst_value_deserialize_array,
+ };
+
+ gst_value.type = gst_value_array_get_type ();
+ gst_value_register (&gst_value);
+ }
+
+ {
+#if 0
+ static const GTypeValueTable value_table = {
+ gst_value_init_buffer,
+ NULL,
+ gst_value_copy_buffer,
+ NULL,
+ "i",
+ NULL, /*gst_value_collect_buffer, */
+ "p",
+ NULL /*gst_value_lcopy_buffer */
+ };
+#endif
+ static GstValueTable gst_value = {
+ 0,
+ gst_value_compare_buffer,
+ gst_value_serialize_buffer,
+ gst_value_deserialize_buffer,
+ };
+
+ gst_value.type = GST_TYPE_BUFFER;
+ gst_value_register (&gst_value);
+ }
+ {
+ static GstValueTable gst_value = {
+ 0,
+ gst_value_compare_fraction,
+ gst_value_serialize_fraction,
+ gst_value_deserialize_fraction,
+ };
+
+ gst_value.type = gst_fraction_get_type ();
+ gst_value_register (&gst_value);
+ }
+ {
+ static GstValueTable gst_value = {
+ 0,
+ NULL,
+ gst_value_serialize_caps,
+ gst_value_deserialize_caps,
+ };
+
+ gst_value.type = GST_TYPE_CAPS;
+ gst_value_register (&gst_value);
+ }
+ {
+ static GstValueTable gst_value = {
+ 0,
+ NULL,
+ gst_value_serialize_structure,
+ gst_value_deserialize_structure,
+ };
+
+ gst_value.type = GST_TYPE_STRUCTURE;
+ gst_value_register (&gst_value);
+ }
+ {
+ static GstValueTable gst_value = {
+ 0,
+ gst_value_compare_date,
+ gst_value_serialize_date,
+ gst_value_deserialize_date,
+ };
+
+ gst_value.type = gst_date_get_type ();
+ gst_value_register (&gst_value);
+ }
+ {
+ static GstValueTable gst_value = {
+ 0,
+ gst_value_compare_date_time,
+ gst_value_serialize_date_time,
+ gst_value_deserialize_date_time,
+ };
+
+ gst_value.type = gst_date_time_get_type ();
+ gst_value_register (&gst_value);
+ }
+
+ REGISTER_SERIALIZATION (G_TYPE_DOUBLE, double);
+ REGISTER_SERIALIZATION (G_TYPE_FLOAT, float);
+
+ REGISTER_SERIALIZATION (G_TYPE_STRING, string);
+ REGISTER_SERIALIZATION (G_TYPE_BOOLEAN, boolean);
+ REGISTER_SERIALIZATION (G_TYPE_ENUM, enum);
+
+ REGISTER_SERIALIZATION (G_TYPE_FLAGS, flags);
+
+ REGISTER_SERIALIZATION (G_TYPE_INT, int);
+
+ REGISTER_SERIALIZATION (G_TYPE_INT64, int64);
+ REGISTER_SERIALIZATION (G_TYPE_LONG, long);
+
+ REGISTER_SERIALIZATION (G_TYPE_UINT, uint);
+ REGISTER_SERIALIZATION (G_TYPE_UINT64, uint64);
+ REGISTER_SERIALIZATION (G_TYPE_ULONG, ulong);
+
+ REGISTER_SERIALIZATION (G_TYPE_UCHAR, uchar);
+
+ g_value_register_transform_func (GST_TYPE_INT_RANGE, G_TYPE_STRING,
+ gst_value_transform_int_range_string);
+ g_value_register_transform_func (GST_TYPE_INT64_RANGE, G_TYPE_STRING,
+ gst_value_transform_int64_range_string);
+ g_value_register_transform_func (GST_TYPE_DOUBLE_RANGE, G_TYPE_STRING,
+ gst_value_transform_double_range_string);
+ g_value_register_transform_func (GST_TYPE_FRACTION_RANGE, G_TYPE_STRING,
+ gst_value_transform_fraction_range_string);
+ g_value_register_transform_func (GST_TYPE_LIST, G_TYPE_STRING,
+ gst_value_transform_list_string);
+ g_value_register_transform_func (GST_TYPE_ARRAY, G_TYPE_STRING,
+ gst_value_transform_array_string);
+ g_value_register_transform_func (GST_TYPE_FRACTION, G_TYPE_STRING,
+ gst_value_transform_fraction_string);
+ g_value_register_transform_func (G_TYPE_STRING, GST_TYPE_FRACTION,
+ gst_value_transform_string_fraction);
+ g_value_register_transform_func (GST_TYPE_FRACTION, G_TYPE_DOUBLE,
+ gst_value_transform_fraction_double);
+ g_value_register_transform_func (GST_TYPE_FRACTION, G_TYPE_FLOAT,
+ gst_value_transform_fraction_float);
+ g_value_register_transform_func (G_TYPE_DOUBLE, GST_TYPE_FRACTION,
+ gst_value_transform_double_fraction);
+ g_value_register_transform_func (G_TYPE_FLOAT, GST_TYPE_FRACTION,
+ gst_value_transform_float_fraction);
+ g_value_register_transform_func (GST_TYPE_DATE, G_TYPE_STRING,
+ gst_value_transform_date_string);
+ g_value_register_transform_func (G_TYPE_STRING, GST_TYPE_DATE,
+ gst_value_transform_string_date);
+ g_value_register_transform_func (GST_TYPE_OBJECT, G_TYPE_STRING,
+ gst_value_transform_object_string);
+
+ gst_value_register_intersect_func (G_TYPE_INT, GST_TYPE_INT_RANGE,
+ gst_value_intersect_int_int_range);
+ gst_value_register_intersect_func (GST_TYPE_INT_RANGE, GST_TYPE_INT_RANGE,
+ gst_value_intersect_int_range_int_range);
+ gst_value_register_intersect_func (G_TYPE_INT64, GST_TYPE_INT64_RANGE,
+ gst_value_intersect_int64_int64_range);
+ gst_value_register_intersect_func (GST_TYPE_INT64_RANGE, GST_TYPE_INT64_RANGE,
+ gst_value_intersect_int64_range_int64_range);
+ gst_value_register_intersect_func (G_TYPE_DOUBLE, GST_TYPE_DOUBLE_RANGE,
+ gst_value_intersect_double_double_range);
+ gst_value_register_intersect_func (GST_TYPE_DOUBLE_RANGE,
+ GST_TYPE_DOUBLE_RANGE, gst_value_intersect_double_range_double_range);
+ gst_value_register_intersect_func (GST_TYPE_ARRAY,
+ GST_TYPE_ARRAY, gst_value_intersect_array);
+ gst_value_register_intersect_func (GST_TYPE_FRACTION, GST_TYPE_FRACTION_RANGE,
+ gst_value_intersect_fraction_fraction_range);
+ gst_value_register_intersect_func (GST_TYPE_FRACTION_RANGE,
+ GST_TYPE_FRACTION_RANGE,
+ gst_value_intersect_fraction_range_fraction_range);
+
+ gst_value_register_subtract_func (G_TYPE_INT, GST_TYPE_INT_RANGE,
+ gst_value_subtract_int_int_range);
+ gst_value_register_subtract_func (GST_TYPE_INT_RANGE, G_TYPE_INT,
+ gst_value_subtract_int_range_int);
+ gst_value_register_subtract_func (GST_TYPE_INT_RANGE, GST_TYPE_INT_RANGE,
+ gst_value_subtract_int_range_int_range);
+ gst_value_register_subtract_func (G_TYPE_INT64, GST_TYPE_INT64_RANGE,
+ gst_value_subtract_int64_int64_range);
+ gst_value_register_subtract_func (GST_TYPE_INT64_RANGE, G_TYPE_INT64,
+ gst_value_subtract_int64_range_int64);
+ gst_value_register_subtract_func (GST_TYPE_INT64_RANGE, GST_TYPE_INT64_RANGE,
+ gst_value_subtract_int64_range_int64_range);
+ gst_value_register_subtract_func (G_TYPE_DOUBLE, GST_TYPE_DOUBLE_RANGE,
+ gst_value_subtract_double_double_range);
+ gst_value_register_subtract_func (GST_TYPE_DOUBLE_RANGE, G_TYPE_DOUBLE,
+ gst_value_subtract_double_range_double);
+ gst_value_register_subtract_func (GST_TYPE_DOUBLE_RANGE,
+ GST_TYPE_DOUBLE_RANGE, gst_value_subtract_double_range_double_range);
+
+ gst_value_register_subtract_func (GST_TYPE_FRACTION, GST_TYPE_FRACTION_RANGE,
+ gst_value_subtract_fraction_fraction_range);
+ gst_value_register_subtract_func (GST_TYPE_FRACTION_RANGE, GST_TYPE_FRACTION,
+ gst_value_subtract_fraction_range_fraction);
+ gst_value_register_subtract_func (GST_TYPE_FRACTION_RANGE,
+ GST_TYPE_FRACTION_RANGE,
+ gst_value_subtract_fraction_range_fraction_range);
+
+ /* see bug #317246, #64994, #65041 */
+ {
+ volatile GType date_type = G_TYPE_DATE;
+
+ g_type_name (date_type);
+ }
+
+ gst_value_register_union_func (G_TYPE_INT, GST_TYPE_INT_RANGE,
+ gst_value_union_int_int_range);
+ gst_value_register_union_func (GST_TYPE_INT_RANGE, GST_TYPE_INT_RANGE,
+ gst_value_union_int_range_int_range);
+
+#if 0
+ /* Implement these if needed */
+ gst_value_register_union_func (GST_TYPE_FRACTION, GST_TYPE_FRACTION_RANGE,
+ gst_value_union_fraction_fraction_range);
+ gst_value_register_union_func (GST_TYPE_FRACTION_RANGE,
+ GST_TYPE_FRACTION_RANGE, gst_value_union_fraction_range_fraction_range);
+#endif
+}
diff --git a/gst/gstvalue.h b/gst/gstvalue.h
new file mode 100644
index 0000000..3bf9222
--- /dev/null
+++ b/gst/gstvalue.h
@@ -0,0 +1,573 @@
+/* GStreamer
+ * Copyright (C) <2003> David A. Schleef <ds@schleef.org>
+ *
+ * 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_VALUE_H__
+#define __GST_VALUE_H__
+
+#include <gst/gstconfig.h>
+#include <gst/gstcaps.h>
+#include <gst/gststructure.h>
+
+G_BEGIN_DECLS
+
+/**
+ * GST_MAKE_FOURCC:
+ * @a: the first character
+ * @b: the second character
+ * @c: the third character
+ * @d: the fourth character
+ *
+ * Transform four characters into a #guint32 fourcc value with host
+ * endianness.
+ * <informalexample>
+ * <programlisting>
+ * guint32 fourcc = GST_MAKE_FOURCC ('M', 'J', 'P', 'G');
+ * </programlisting>
+ * </informalexample>
+ */
+#define GST_MAKE_FOURCC(a,b,c,d) ((guint32)((a)|(b)<<8|(c)<<16|(d)<<24))
+
+/**
+ * GST_STR_FOURCC:
+ * @f: a string with at least four characters
+ *
+ * Transform an input string into a #guint32 fourcc value with host
+ * endianness.
+ * Caller is responsible for ensuring the input string consists of at least
+ * four characters.
+ * <informalexample>
+ * <programlisting>
+ * guint32 fourcc = GST_STR_FOURCC ("MJPG");
+ * </programlisting>
+ * </informalexample>
+ */
+#define GST_STR_FOURCC(f) ((guint32)(((f)[0])|((f)[1]<<8)|((f)[2]<<16)|((f)[3]<<24)))
+
+/**
+ * GST_FOURCC_FORMAT:
+ *
+ * Can be used together with #GST_FOURCC_ARGS to properly output a
+ * #guint32 fourcc value in a printf()-style text message.
+ * <informalexample>
+ * <programlisting>
+ * printf ("fourcc: %" GST_FOURCC_FORMAT "\n", GST_FOURCC_ARGS (fcc));
+ * </programlisting>
+ * </informalexample>
+ */
+#define GST_FOURCC_FORMAT "c%c%c%c"
+
+/**
+ * GST_FOURCC_ARGS:
+ * @fourcc: a #guint32 fourcc value to output
+ *
+ * Can be used together with #GST_FOURCC_FORMAT to properly output a
+ * #guint32 fourcc value in a printf()-style text message.
+ */
+#define GST_FOURCC_ARGS(fourcc) \
+ ((gchar) ((fourcc) &0xff)), \
+ ((gchar) (((fourcc)>>8 )&0xff)), \
+ ((gchar) (((fourcc)>>16)&0xff)), \
+ ((gchar) (((fourcc)>>24)&0xff))
+
+/**
+ * GST_VALUE_HOLDS_INT_RANGE:
+ * @x: the #GValue to check
+ *
+ * Checks if the given #GValue contains a #GST_TYPE_INT_RANGE value.
+ */
+#define GST_VALUE_HOLDS_INT_RANGE(x) (G_VALUE_HOLDS((x), gst_int_range_get_type ()))
+
+/**
+ * GST_VALUE_HOLDS_INT64_RANGE:
+ * @x: the #GValue to check
+ *
+ * Checks if the given #GValue contains a #GST_TYPE_INT64_RANGE value.
+ *
+ * Since: 0.10.31
+ */
+#define GST_VALUE_HOLDS_INT64_RANGE(x) (G_VALUE_HOLDS((x), gst_int64_range_get_type ()))
+
+/**
+ * GST_VALUE_HOLDS_DOUBLE_RANGE:
+ * @x: the #GValue to check
+ *
+ * Checks if the given #GValue contains a #GST_TYPE_DOUBLE_RANGE value.
+ */
+#define GST_VALUE_HOLDS_DOUBLE_RANGE(x) (G_VALUE_HOLDS((x), gst_double_range_get_type ()))
+
+/**
+ * GST_VALUE_HOLDS_FRACTION_RANGE:
+ * @x: the #GValue to check
+ *
+ * Checks if the given #GValue contains a #GST_TYPE_FRACTION_RANGE value.
+ */
+#define GST_VALUE_HOLDS_FRACTION_RANGE(x) (G_VALUE_HOLDS((x), gst_fraction_range_get_type ()))
+
+/**
+ * GST_VALUE_HOLDS_LIST:
+ * @x: the #GValue to check
+ *
+ * Checks if the given #GValue contains a #GST_TYPE_LIST value.
+ */
+#define GST_VALUE_HOLDS_LIST(x) (G_VALUE_HOLDS((x), gst_value_list_get_type ()))
+
+/**
+ * GST_VALUE_HOLDS_ARRAY:
+ * @x: the #GValue to check
+ *
+ * Checks if the given #GValue contains a #GST_TYPE_ARRAY value.
+ */
+#define GST_VALUE_HOLDS_ARRAY(x) (G_VALUE_HOLDS((x), gst_value_array_get_type ()))
+
+/**
+ * GST_VALUE_HOLDS_CAPS:
+ * @x: the #GValue to check
+ *
+ * Checks if the given #GValue contains a #GST_TYPE_CAPS value.
+ */
+#define GST_VALUE_HOLDS_CAPS(x) (G_VALUE_HOLDS((x), GST_TYPE_CAPS))
+
+/**
+ * GST_VALUE_HOLDS_STRUCTURE:
+ * @x: the #GValue to check
+ *
+ * Checks if the given #GValue contains a #GST_TYPE_STRUCTURE value.
+ *
+ * Since: 0.10.15
+ */
+#define GST_VALUE_HOLDS_STRUCTURE(x) (G_VALUE_HOLDS((x), GST_TYPE_STRUCTURE))
+
+/**
+ * GST_VALUE_HOLDS_BUFFER:
+ * @x: the #GValue to check
+ *
+ * Checks if the given #GValue contains a #GST_TYPE_BUFFER value.
+ */
+#define GST_VALUE_HOLDS_BUFFER(x) (G_VALUE_HOLDS((x), GST_TYPE_BUFFER))
+
+/**
+ * GST_VALUE_HOLDS_FRACTION:
+ * @x: the #GValue to check
+ *
+ * Checks if the given #GValue contains a #GST_TYPE_FRACTION value.
+ */
+#define GST_VALUE_HOLDS_FRACTION(x) (G_VALUE_HOLDS((x), gst_fraction_get_type ()))
+
+/**
+ * GST_VALUE_HOLDS_DATE:
+ * @x: the #GValue to check
+ *
+ * Checks if the given #GValue contains a #GST_TYPE_DATE value.
+ */
+#define GST_VALUE_HOLDS_DATE(x) (G_VALUE_HOLDS((x), gst_date_get_type ()))
+
+/**
+ * GST_VALUE_HOLDS_DATE_TIME:
+ * @x: the #GValue to check
+ *
+ * Checks if the given #GValue contains a #GST_TYPE_DATE_TIME value.
+ *
+ * Since: 0.10.31
+ */
+#define GST_VALUE_HOLDS_DATE_TIME(x) (G_VALUE_HOLDS((x), gst_date_time_get_type ()))
+
+/**
+ * GST_TYPE_INT_RANGE:
+ *
+ * a #GValue type that represents an integer range
+ *
+ * Returns: the #GType of GstIntRange
+ */
+#define GST_TYPE_INT_RANGE gst_int_range_get_type ()
+
+/**
+ * GST_TYPE_INT64_RANGE:
+ *
+ * a #GValue type that represents an #gint64 range
+ *
+ * Returns: the #GType of GstInt64Range
+ *
+ * Since: 0.10.31
+ */
+#define GST_TYPE_INT64_RANGE gst_int64_range_get_type ()
+
+/**
+ * GST_TYPE_DOUBLE_RANGE:
+ *
+ * a #GValue type that represents a floating point range with double precission
+ *
+ * Returns: the #GType of GstIntRange
+ */
+#define GST_TYPE_DOUBLE_RANGE gst_double_range_get_type ()
+
+/**
+ * GST_TYPE_FRACTION_RANGE:
+ *
+ * a #GValue type that represents a GstFraction range
+ *
+ * Returns: the #GType of GstFractionRange
+ */
+#define GST_TYPE_FRACTION_RANGE gst_fraction_range_get_type ()
+
+/**
+ * GST_TYPE_LIST:
+ *
+ * a #GValue type that represents an unordered list of #GValue values. This
+ * is used for example to express a list of possible values for a field in
+ * a caps structure, like a list of possible sample rates, of which only one
+ * will be chosen in the end. This means that all values in the list are
+ * meaningful on their own.
+ *
+ * Returns: the #GType of GstValueList (which is not explicitly typed)
+ */
+#define GST_TYPE_LIST gst_value_list_get_type ()
+
+/**
+ * GST_TYPE_ARRAY:
+ *
+ * a #GValue type that represents an ordered list of #GValue values. This is
+ * used to express a set of values that is meaningful only in their specific
+ * combination and order of values. Each value on its own is not particularly
+ * meaningful, only the ordered array in its entirety is meaningful. This is
+ * used for example to express channel layouts for multichannel audio where
+ * each channel needs to be mapped to a position in the room.
+ *
+ * Returns: the #GType of GstArrayList (which is not explicitly typed)
+ */
+#define GST_TYPE_ARRAY gst_value_array_get_type ()
+
+/**
+ * GST_TYPE_FRACTION:
+ *
+ * a #GValue type that represents a fraction of an integer numerator over
+ * an integer denominator
+ *
+ * Returns: the #GType of GstFraction (which is not explicitly typed)
+ */
+
+#define GST_TYPE_FRACTION gst_fraction_get_type ()
+
+/**
+ * GST_TYPE_DATE:
+ *
+ * a boxed #GValue type for #GDate that represents a date.
+ *
+ * Returns: the #GType of GstDate
+ */
+
+#define GST_TYPE_DATE gst_date_get_type ()
+
+/**
+ * GST_TYPE_DATE_TIME:
+ *
+ * a boxed #GValue type for #GstDateTime that represents a date and time.
+ *
+ * Returns: the #GType of GstDateTime
+ * Since: 0.10.31
+ */
+
+#define GST_TYPE_DATE_TIME gst_date_time_get_type ()
+
+/**
+ * GST_VALUE_LESS_THAN:
+ *
+ * Indicates that the first value provided to a comparison function
+ * (gst_value_compare()) is lesser than the second one.
+ */
+#define GST_VALUE_LESS_THAN (-1)
+
+/**
+ * GST_VALUE_EQUAL:
+ *
+ * Indicates that the first value provided to a comparison function
+ * (gst_value_compare()) is equal to the second one.
+ */
+#define GST_VALUE_EQUAL 0
+
+/**
+ * GST_VALUE_GREATER_THAN:
+ *
+ * Indicates that the first value provided to a comparison function
+ * (gst_value_compare()) is greater than the second one.
+ */
+#define GST_VALUE_GREATER_THAN 1
+
+/**
+ * GST_VALUE_UNORDERED:
+ *
+ * Indicates that the comparison function (gst_value_compare()) can not
+ * determine a order for the two provided values.
+ */
+#define GST_VALUE_UNORDERED 2
+
+/**
+ * GstValueCompareFunc:
+ * @value1: first value for comparison
+ * @value2: second value for comparison
+ *
+ * Used together with gst_value_compare() to compare #GValue items.
+ *
+ * Returns: one of GST_VALUE_LESS_THAN, GST_VALUE_EQUAL, GST_VALUE_GREATER_THAN
+ * or GST_VALUE_UNORDERED
+ */
+typedef gint (* GstValueCompareFunc) (const GValue *value1,
+ const GValue *value2);
+
+/**
+ * GstValueSerializeFunc:
+ * @value1: a #GValue
+ *
+ * Used by gst_value_serialize() to obtain a non-binary form of the #GValue.
+ *
+ * Free-function: g_free
+ *
+ * Returns: (transfer full): the string representation of the value
+ */
+typedef gchar * (* GstValueSerializeFunc) (const GValue *value1);
+
+/**
+ * GstValueDeserializeFunc:
+ * @dest: a #GValue
+ * @s: a string
+ *
+ * Used by gst_value_deserialize() to parse a non-binary form into the #GValue.
+ *
+ * Returns: %TRUE for success
+ */
+typedef gboolean (* GstValueDeserializeFunc) (GValue *dest,
+ const gchar *s);
+
+/**
+ * GstValueUnionFunc:
+ * @dest: a #GValue for the result
+ * @value1: a #GValue operand
+ * @value2: a #GValue operand
+ *
+ * Used by gst_value_union() to perform unification for a specific #GValue
+ * type. Register a new implementation with gst_value_register_union_func().
+ *
+ * Returns: %TRUE if a union was successful
+ */
+typedef gboolean (* GstValueUnionFunc) (GValue *dest,
+ const GValue *value1,
+ const GValue *value2);
+
+/**
+ * GstValueIntersectFunc:
+ * @dest: (out caller-allocates): a #GValue for the result
+ * @value1: a #GValue operand
+ * @value2: a #GValue operand
+ *
+ * Used by gst_value_intersect() to perform intersection for a specific #GValue
+ * type. If the intersection is non-empty, the result is
+ * placed in @dest and TRUE is returned. If the intersection is
+ * empty, @dest is unmodified and FALSE is returned.
+ * Register a new implementation with gst_value_register_intersect_func().
+ *
+ * Returns: %TRUE if the values can intersect
+ */
+typedef gboolean (* GstValueIntersectFunc) (GValue *dest,
+ const GValue *value1,
+ const GValue *value2);
+
+/**
+ * GstValueSubtractFunc:
+ * @dest: (out caller-allocates): a #GValue for the result
+ * @minuend: a #GValue operand
+ * @subtrahend: a #GValue operand
+ *
+ * Used by gst_value_subtract() to perform subtraction for a specific #GValue
+ * type. Register a new implementation with gst_value_register_subtract_func().
+ *
+ * Returns: %TRUE if the subtraction is not empty
+ */
+typedef gboolean (* GstValueSubtractFunc) (GValue *dest,
+ const GValue *minuend,
+ const GValue *subtrahend);
+
+typedef struct _GstValueTable GstValueTable;
+/**
+ * GstValueTable:
+ * @type: a #GType
+ * @compare: a #GstValueCompareFunc
+ * @serialize: a #GstValueSerializeFunc
+ * @deserialize: a #GstValueDeserializeFunc
+ *
+ * VTable for the #GValue @type.
+ */
+struct _GstValueTable {
+ GType type;
+ GstValueCompareFunc compare;
+ GstValueSerializeFunc serialize;
+ GstValueDeserializeFunc deserialize;
+
+ /*< private >*/
+ void *_gst_reserved [GST_PADDING];
+};
+
+GType gst_int_range_get_type (void);
+GType gst_int64_range_get_type (void);
+GType gst_double_range_get_type (void);
+GType gst_fraction_range_get_type (void);
+GType gst_fraction_get_type (void);
+GType gst_value_list_get_type (void);
+GType gst_value_array_get_type (void);
+
+GType gst_date_get_type (void);
+GType gst_date_time_get_type (void);
+
+void gst_value_register (const GstValueTable *table);
+void gst_value_init_and_copy (GValue *dest,
+ const GValue *src);
+
+gchar * gst_value_serialize (const GValue *value);
+gboolean gst_value_deserialize (GValue *dest,
+ const gchar *src);
+
+/* list */
+void gst_value_list_append_value (GValue *value,
+ const GValue *append_value);
+void gst_value_list_prepend_value (GValue *value,
+ const GValue *prepend_value);
+void gst_value_list_concat (GValue *dest,
+ const GValue *value1,
+ const GValue *value2);
+void gst_value_list_merge (GValue *dest,
+ const GValue *value1,
+ const GValue *value2);
+guint gst_value_list_get_size (const GValue *value);
+const GValue * gst_value_list_get_value (const GValue *value,
+ guint index);
+
+/* array */
+void gst_value_array_append_value (GValue *value,
+ const GValue *append_value);
+void gst_value_array_prepend_value (GValue *value,
+ const GValue *prepend_value);
+guint gst_value_array_get_size (const GValue *value);
+const GValue * gst_value_array_get_value (const GValue *value,
+ guint index);
+
+/* int range */
+void gst_value_set_int_range (GValue *value,
+ gint start,
+ gint end);
+gint gst_value_get_int_range_min (const GValue *value);
+gint gst_value_get_int_range_max (const GValue *value);
+
+/* int64 range */
+void gst_value_set_int64_range (GValue *value,
+ gint64 start,
+ gint64 end);
+gint64 gst_value_get_int64_range_min (const GValue *value);
+gint64 gst_value_get_int64_range_max (const GValue *value);
+
+/* double range */
+void gst_value_set_double_range (GValue *value,
+ gdouble start,
+ gdouble end);
+gdouble gst_value_get_double_range_min (const GValue *value);
+gdouble gst_value_get_double_range_max (const GValue *value);
+
+/* caps */
+const GstCaps * gst_value_get_caps (const GValue *value);
+void gst_value_set_caps (GValue *value,
+ const GstCaps *caps);
+
+/* structure */
+const GstStructure *
+ gst_value_get_structure (const GValue *value);
+void gst_value_set_structure (GValue *value,
+ const GstStructure *structure);
+
+/* fraction */
+void gst_value_set_fraction (GValue *value,
+ gint numerator,
+ gint denominator);
+gint gst_value_get_fraction_numerator (const GValue *value);
+gint gst_value_get_fraction_denominator(const GValue *value);
+gboolean gst_value_fraction_multiply (GValue *product,
+ const GValue *factor1,
+ const GValue *factor2);
+gboolean gst_value_fraction_subtract (GValue * dest,
+ const GValue * minuend,
+ const GValue * subtrahend);
+
+/* fraction range */
+void gst_value_set_fraction_range (GValue *value,
+ const GValue *start,
+ const GValue *end);
+void gst_value_set_fraction_range_full (GValue *value,
+ gint numerator_start,
+ gint denominator_start,
+ gint numerator_end,
+ gint denominator_end);
+const GValue *gst_value_get_fraction_range_min (const GValue *value);
+const GValue *gst_value_get_fraction_range_max (const GValue *value);
+
+/* date */
+const GDate * gst_value_get_date (const GValue *value);
+void gst_value_set_date (GValue *value,
+ const GDate *date);
+
+/* compare */
+gint gst_value_compare (const GValue *value1,
+ const GValue *value2);
+gboolean gst_value_can_compare (const GValue *value1,
+ const GValue *value2);
+/* union */
+gboolean gst_value_union (GValue *dest,
+ const GValue *value1,
+ const GValue *value2);
+gboolean gst_value_can_union (const GValue *value1,
+ const GValue *value2);
+void gst_value_register_union_func (GType type1,
+ GType type2,
+ GstValueUnionFunc func);
+
+/* intersection */
+gboolean gst_value_intersect (GValue *dest,
+ const GValue *value1,
+ const GValue *value2);
+gboolean gst_value_can_intersect (const GValue *value1,
+ const GValue *value2);
+void gst_value_register_intersect_func (GType type1,
+ GType type2,
+ GstValueIntersectFunc func);
+
+/* subtraction */
+gboolean gst_value_subtract (GValue *dest,
+ const GValue *minuend,
+ const GValue *subtrahend);
+gboolean gst_value_can_subtract (const GValue *minuend,
+ const GValue *subtrahend);
+void gst_value_register_subtract_func (GType minuend_type,
+ GType subtrahend_type,
+ GstValueSubtractFunc func);
+
+/* fixation */
+gboolean gst_value_is_fixed (const GValue *value);
+gboolean gst_value_fixate (GValue *dest,
+ const GValue *src);
+
+G_END_DECLS
+
+#endif
+
+
diff --git a/gst/gstversion.h.in b/gst/gstversion.h.in
new file mode 100644
index 0000000..86a6609
--- /dev/null
+++ b/gst/gstversion.h.in
@@ -0,0 +1,90 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2000 Wim Taymans <wtay@chello.be>
+ *
+ * gstversion.h: Version information for GStreamer
+ *
+ * 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:gstversion
+ * @short_description: GStreamer version macros.
+ *
+ * Use the GST_VERSION_* macros e.g. when defining own plugins. The GStreamer
+ * runtime checks if these plugin and core version match and refuses to use a
+ * plugin compiled against a different version of GStreamer.
+ * You can also use the macros to keep the GStreamer version information in
+ * your application.
+ *
+ * Use the gst_version() function if you want to know which version of
+ * GStreamer you are currently linked against.
+ *
+ * The version macros get defined by including "gst/gst.h".
+ */
+
+#ifndef __GST_VERSION_H__
+#define __GST_VERSION_H__
+
+G_BEGIN_DECLS
+
+/**
+ * GST_VERSION_MAJOR:
+ *
+ * The major version of GStreamer at compile time:
+ */
+#define GST_VERSION_MAJOR (@PACKAGE_VERSION_MAJOR@)
+/**
+ * GST_VERSION_MINOR:
+ *
+ * The minor version of GStreamer at compile time:
+ */
+#define GST_VERSION_MINOR (@PACKAGE_VERSION_MINOR@)
+/**
+ * GST_VERSION_MICRO:
+ *
+ * The micro version of GStreamer at compile time:
+ */
+#define GST_VERSION_MICRO (@PACKAGE_VERSION_MICRO@)
+/**
+ * GST_VERSION_NANO:
+ *
+ * The nano version of GStreamer at compile time:
+ * Actual releases have 0, GIT versions have 1, prerelease versions have 2-...
+ */
+#define GST_VERSION_NANO (@PACKAGE_VERSION_NANO@)
+
+/**
+ * GST_CHECK_VERSION:
+ * @major: a number indicating the major version
+ * @minor: a number indicating the minor version
+ * @micro: a number indicating the micro version
+ *
+ * Check whether a GStreamer version equal to or greater than
+ * major.minor.micro is present.
+ *
+ * Since: 0.10.18
+ */
+#define GST_CHECK_VERSION(major,minor,micro) \
+ (GST_VERSION_MAJOR > (major) || \
+ (GST_VERSION_MAJOR == (major) && GST_VERSION_MINOR > (minor)) || \
+ (GST_VERSION_MAJOR == (major) && GST_VERSION_MINOR == (minor) && \
+ GST_VERSION_MICRO >= (micro)) || \
+ (GST_VERSION_MAJOR == (major) && GST_VERSION_MINOR == (minor) && \
+ GST_VERSION_MICRO + 1 == (micro) && GST_VERSION_NANO > 0))
+
+G_END_DECLS
+
+#endif /* __GST_VERSION_H__ */
diff --git a/gst/math-compat.h b/gst/math-compat.h
new file mode 100644
index 0000000..d9b368c
--- /dev/null
+++ b/gst/math-compat.h
@@ -0,0 +1,84 @@
+/* GStreamer
+ * Copyright (C) 2010 Tim-Philipp Müller <tim centricular 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.
+ */
+
+#ifndef __GST_MATH_COMPAT_H__
+#define __GST_MATH_COMPAT_H__
+
+/* This header is not included automatically via gst/gst.h, you need to
+ * include it explicitly if you need it. */
+
+#ifndef _USE_MATH_DEFINES
+#define _USE_MATH_DEFINES /* so MSVC defines M_PI etc. */
+#endif
+#include <math.h>
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+/* http://en.wikipedia.org/wiki/Math.h */
+
+#define __GST_MATH_COMPAT_NEED_RINT
+#define __GST_MATH_COMPAT_NEED_RINTF
+
+#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+#undef __GST_MATH_COMPAT_NEED_RINT
+#undef __GST_MATH_COMPAT_NEED_RINTF
+#endif
+
+#if defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112L
+#undef __GST_MATH_COMPAT_NEED_RINT
+#undef __GST_MATH_COMPAT_NEED_RINTF
+#endif
+
+#ifndef M_PI
+#define M_PI G_PI
+#endif
+
+#ifndef M_PI_2
+#define M_PI_2 G_PI_2
+#endif
+
+#ifndef M_PI_4
+#define M_PI_4 G_PI_4
+#endif
+
+static inline double
+__gst_math_compat_rint (double x)
+{
+ return floor (x + 0.5);
+}
+
+static inline float
+__gst_math_compat_rintf (float x)
+{
+ return floorf (x + 0.5);
+}
+
+#if defined (__GST_MATH_COMPAT_NEED_RINT) && !defined (rint)
+#define rint(x) __gst_math_compat_rint(x)
+#endif
+
+#if defined (__GST_MATH_COMPAT_NEED_RINTF) && !defined (rintf)
+#define rintf(x) __gst_math_compat_rintf(x)
+#endif
+
+G_END_DECLS
+
+#endif /* __GST_MATH_COMPAT_H__ */
diff --git a/gst/parse/Makefile.am b/gst/parse/Makefile.am
new file mode 100644
index 0000000..b837ec2
--- /dev/null
+++ b/gst/parse/Makefile.am
@@ -0,0 +1,55 @@
+# libgstparse.la is an optionally built helper library linked into core
+noinst_LTLIBRARIES = libgstparse.la
+
+CLEANFILES = grammar.tab.h grammar.output
+EXTRA_DIST = \
+ grammar.y \
+ parse.l
+
+# uncomment these lines to dist the generated sources
+#BUILT_SOURCES = grammar.tab.h grammar.tab.c lex._gst_parse_yy.c
+#libgstparse_la_SOURCES = lex._gst_parse_yy.c grammar.tab.c
+
+# uncomment these lines to _NOT_ dist the generated sources
+nodist_libgstparse_la_SOURCES = lex._gst_parse_yy.c grammar.tab.c
+CLEANFILES += grammar.tab.c lex._gst_parse_yy.c
+
+# can't use GST_ALL_CFLAGS here because that'd pull in -Werror
+libgstparse_la_CFLAGS = -I$(top_srcdir) -I$(top_builddir) \
+ $(GLIB_CFLAGS) $(GST_OPTION_CFLAGS)
+libgstparse_la_LIBADD = $(GST_ALL_LIBS)
+
+noinst_HEADERS = grammar.tab.h types.h
+
+Android.mk: Makefile.am
+ androgenizer -:PROJECT gstreamer -:STATIC libgstparse -:TAGS eng debug \
+ -:REL_TOP $(top_srcdir) -:ABS_TOP $(abs_top_srcdir) \
+ -:SOURCES $(libgstparse_la_SOURCES) $(nodist_libgstparse_la_SOURCES) \
+ -:CFLAGS $(DEFS) $(libgstparse_la_CFLAGS) \
+ -:LDFLAGS $(libgstparse_la_LIBADD) \
+ > $@
+
+grammar.tab.c grammar.tab.h: grammar.y
+ $(AM_V_GEN)$(BISON_PATH) -d -v -p_gst_parse_yy $(srcdir)/grammar.y -o grammar.tab.c && \
+ mv grammar.tab.c grammar.tab_tmp.c && \
+ echo '#ifdef HAVE_CONFIG_H' > grammar.tab_tmp2.c && \
+ echo '#include <config.h>' >> grammar.tab_tmp2.c && \
+ echo '#endif' >> grammar.tab_tmp2.c && \
+ cat grammar.tab_tmp.c >> grammar.tab_tmp2.c && \
+ rm grammar.tab_tmp.c && \
+ mv grammar.tab_tmp2.c grammar.tab.c
+
+lex._gst_parse_yy.c: parse.l grammar.tab.h
+ $(AM_V_GEN)$(FLEX_PATH) -P_gst_parse_yy $^ && \
+ mv lex._gst_parse_yy.c lex._gst_parse_yy_tmp.c && \
+ echo '#ifdef HAVE_CONFIG_H' > lex._gst_parse_yy_tmp2.c && \
+ echo '#include <config.h>' >> lex._gst_parse_yy_tmp2.c && \
+ echo '#endif' >> lex._gst_parse_yy_tmp2.c && \
+ echo 'static inline int _gst_parse_yyget_column (void * yyscanner);' >> lex._gst_parse_yy_tmp2.c && \
+ echo 'static inline void _gst_parse_yyset_column (int column_no , void * yyscanner);' >> lex._gst_parse_yy_tmp2.c && \
+ cat lex._gst_parse_yy_tmp.c >> lex._gst_parse_yy_tmp2.c && \
+ rm lex._gst_parse_yy_tmp.c && \
+ mv lex._gst_parse_yy_tmp2.c lex._gst_parse_yy.c
+
+.NOTPARALLEL:
+
diff --git a/gst/parse/Makefile.in b/gst/parse/Makefile.in
new file mode 100644
index 0000000..4e34b8b
--- /dev/null
+++ b/gst/parse/Makefile.in
@@ -0,0 +1,745 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = gst/parse
+DIST_COMMON = $(noinst_HEADERS) $(srcdir)/Makefile.am \
+ $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/common/m4/as-ac-expand.m4 \
+ $(top_srcdir)/common/m4/as-auto-alt.m4 \
+ $(top_srcdir)/common/m4/as-compiler-flag.m4 \
+ $(top_srcdir)/common/m4/as-docbook.m4 \
+ $(top_srcdir)/common/m4/as-libtool.m4 \
+ $(top_srcdir)/common/m4/as-python.m4 \
+ $(top_srcdir)/common/m4/as-scrub-include.m4 \
+ $(top_srcdir)/common/m4/as-version.m4 \
+ $(top_srcdir)/common/m4/ax_create_stdint_h.m4 \
+ $(top_srcdir)/common/m4/gst-arch.m4 \
+ $(top_srcdir)/common/m4/gst-args.m4 \
+ $(top_srcdir)/common/m4/gst-check.m4 \
+ $(top_srcdir)/common/m4/gst-doc.m4 \
+ $(top_srcdir)/common/m4/gst-error.m4 \
+ $(top_srcdir)/common/m4/gst-feature.m4 \
+ $(top_srcdir)/common/m4/gst-function.m4 \
+ $(top_srcdir)/common/m4/gst-gettext.m4 \
+ $(top_srcdir)/common/m4/gst-glib2.m4 \
+ $(top_srcdir)/common/m4/gst-package-release-datetime.m4 \
+ $(top_srcdir)/common/m4/gst-parser.m4 \
+ $(top_srcdir)/common/m4/gst-platform.m4 \
+ $(top_srcdir)/common/m4/gst-plugin-docs.m4 \
+ $(top_srcdir)/common/m4/gst-plugindir.m4 \
+ $(top_srcdir)/common/m4/gst.m4 \
+ $(top_srcdir)/common/m4/gtk-doc.m4 \
+ $(top_srcdir)/common/m4/introspection.m4 \
+ $(top_srcdir)/common/m4/pkg.m4 \
+ $(top_srcdir)/m4/check-checks.m4 $(top_srcdir)/m4/gettext.m4 \
+ $(top_srcdir)/m4/iconv.m4 $(top_srcdir)/m4/intlmacosx.m4 \
+ $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \
+ $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libtool.m4 \
+ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
+ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
+ $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/po.m4 \
+ $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+am__DEPENDENCIES_1 =
+libgstparse_la_DEPENDENCIES = $(am__DEPENDENCIES_1)
+nodist_libgstparse_la_OBJECTS = libgstparse_la-lex._gst_parse_yy.lo \
+ libgstparse_la-grammar.tab.lo
+libgstparse_la_OBJECTS = $(nodist_libgstparse_la_OBJECTS)
+AM_V_lt = $(am__v_lt_$(V))
+am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY))
+am__v_lt_0 = --silent
+libgstparse_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(libgstparse_la_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o \
+ $@
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_$(V))
+am__v_CC_ = $(am__v_CC_$(AM_DEFAULT_VERBOSITY))
+am__v_CC_0 = @echo " CC " $@;
+AM_V_at = $(am__v_at_$(V))
+am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY))
+am__v_at_0 = @
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_$(V))
+am__v_CCLD_ = $(am__v_CCLD_$(AM_DEFAULT_VERBOSITY))
+am__v_CCLD_0 = @echo " CCLD " $@;
+AM_V_GEN = $(am__v_GEN_$(V))
+am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY))
+am__v_GEN_0 = @echo " GEN " $@;
+SOURCES = $(nodist_libgstparse_la_SOURCES)
+DIST_SOURCES =
+HEADERS = $(noinst_HEADERS)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AS = @AS@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BISON_PATH = @BISON_PATH@
+CAT_ENTRY_END = @CAT_ENTRY_END@
+CAT_ENTRY_START = @CAT_ENTRY_START@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CHECK_MAJOR_VERSION = @CHECK_MAJOR_VERSION@
+CHECK_MICRO_VERSION = @CHECK_MICRO_VERSION@
+CHECK_MINOR_VERSION = @CHECK_MINOR_VERSION@
+CHECK_VERSION = @CHECK_VERSION@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIR = @DATADIR@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DEPRECATED_CFLAGS = @DEPRECATED_CFLAGS@
+DLLTOOL = @DLLTOOL@
+DOCBOOK_ROOT = @DOCBOOK_ROOT@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+ENABLE_SUBUNIT = @ENABLE_SUBUNIT@
+ERROR_CFLAGS = @ERROR_CFLAGS@
+EXEEXT = @EXEEXT@
+FFLAGS = @FFLAGS@
+FGREP = @FGREP@
+FLEX_PATH = @FLEX_PATH@
+GCOV = @GCOV@
+GCOV_CFLAGS = @GCOV_CFLAGS@
+GCOV_LIBS = @GCOV_LIBS@
+GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_ONLY_CFLAGS = @GLIB_ONLY_CFLAGS@
+GLIB_ONLY_LIBS = @GLIB_ONLY_LIBS@
+GLIB_PREFIX = @GLIB_PREFIX@
+GLIB_REQ = @GLIB_REQ@
+GMP_LIBS = @GMP_LIBS@
+GMSGFMT = @GMSGFMT@
+GMSGFMT_015 = @GMSGFMT_015@
+GREP = @GREP@
+GSL_LIBS = @GSL_LIBS@
+GST_AGE = @GST_AGE@
+GST_ALL_CFLAGS = @GST_ALL_CFLAGS@
+GST_ALL_CXXFLAGS = @GST_ALL_CXXFLAGS@
+GST_ALL_LDFLAGS = @GST_ALL_LDFLAGS@
+GST_ALL_LIBS = @GST_ALL_LIBS@
+GST_CURRENT = @GST_CURRENT@
+GST_DISABLE_ALLOC_TRACE_DEFINE = @GST_DISABLE_ALLOC_TRACE_DEFINE@
+GST_DISABLE_GST_DEBUG_DEFINE = @GST_DISABLE_GST_DEBUG_DEFINE@
+GST_DISABLE_NET_DEFINE = @GST_DISABLE_NET_DEFINE@
+GST_DISABLE_OPTION_PARSING_DEFINE = @GST_DISABLE_OPTION_PARSING_DEFINE@
+GST_DISABLE_PARSE_DEFINE = @GST_DISABLE_PARSE_DEFINE@
+GST_DISABLE_PLUGIN_DEFINE = @GST_DISABLE_PLUGIN_DEFINE@
+GST_DISABLE_REGISTRY_DEFINE = @GST_DISABLE_REGISTRY_DEFINE@
+GST_DISABLE_TRACE_DEFINE = @GST_DISABLE_TRACE_DEFINE@
+GST_HAVE_MONOTONIC_CLOCK_DEFINE = @GST_HAVE_MONOTONIC_CLOCK_DEFINE@
+GST_HAVE_POSIX_TIMERS_DEFINE = @GST_HAVE_POSIX_TIMERS_DEFINE@
+GST_HAVE_UNALIGNED_ACCESS_DEFINE = @GST_HAVE_UNALIGNED_ACCESS_DEFINE@
+GST_LEVEL_DEFAULT = @GST_LEVEL_DEFAULT@
+GST_LIBVERSION = @GST_LIBVERSION@
+GST_LIB_LDFLAGS = @GST_LIB_LDFLAGS@
+GST_LICENSE = @GST_LICENSE@
+GST_LT_LDFLAGS = @GST_LT_LDFLAGS@
+GST_MAJORMINOR = @GST_MAJORMINOR@
+GST_OBJ_CFLAGS = @GST_OBJ_CFLAGS@
+GST_OBJ_CXXFLAGS = @GST_OBJ_CXXFLAGS@
+GST_OBJ_LIBS = @GST_OBJ_LIBS@
+GST_OPTION_CFLAGS = @GST_OPTION_CFLAGS@
+GST_OPTION_CXXFLAGS = @GST_OPTION_CXXFLAGS@
+GST_PACKAGE_NAME = @GST_PACKAGE_NAME@
+GST_PACKAGE_ORIGIN = @GST_PACKAGE_ORIGIN@
+GST_PKG_CONFIG_PATH = @GST_PKG_CONFIG_PATH@
+GST_PKG_DEPS = @GST_PKG_DEPS@
+GST_PLUGIN_LDFLAGS = @GST_PLUGIN_LDFLAGS@
+GST_PLUGIN_SCANNER_INSTALLED = @GST_PLUGIN_SCANNER_INSTALLED@
+GST_PRINTF_EXTENSION_POINTER_FORMAT_DEFINE = @GST_PRINTF_EXTENSION_POINTER_FORMAT_DEFINE@
+GST_PRINTF_EXTENSION_SEGMENT_FORMAT_DEFINE = @GST_PRINTF_EXTENSION_SEGMENT_FORMAT_DEFINE@
+GST_REGISTRY_DOC_TYPES = @GST_REGISTRY_DOC_TYPES@
+GST_REVISION = @GST_REVISION@
+GST_USING_PRINTF_EXTENSION_DEFINE = @GST_USING_PRINTF_EXTENSION_DEFINE@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+HAVE_DOCBOOK2HTML = @HAVE_DOCBOOK2HTML@
+HAVE_DOCBOOK2PS = @HAVE_DOCBOOK2PS@
+HAVE_DVIPS = @HAVE_DVIPS@
+HAVE_EPSTOPDF = @HAVE_EPSTOPDF@
+HAVE_FIG2DEV = @HAVE_FIG2DEV@
+HAVE_GMP = @HAVE_GMP@
+HAVE_GSL = @HAVE_GSL@
+HAVE_JADETEX = @HAVE_JADETEX@
+HAVE_PNGTOPNM = @HAVE_PNGTOPNM@
+HAVE_PNMTOPS = @HAVE_PNMTOPS@
+HAVE_PS2PDF = @HAVE_PS2PDF@
+HAVE_XMLLINT = @HAVE_XMLLINT@
+HOST_CPU = @HOST_CPU@
+HTML_DIR = @HTML_DIR@
+INET_ATON_LIBS = @INET_ATON_LIBS@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTLLIBS = @INTLLIBS@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+INTROSPECTION_CFLAGS = @INTROSPECTION_CFLAGS@
+INTROSPECTION_COMPILER = @INTROSPECTION_COMPILER@
+INTROSPECTION_GENERATE = @INTROSPECTION_GENERATE@
+INTROSPECTION_GIRDIR = @INTROSPECTION_GIRDIR@
+INTROSPECTION_LIBS = @INTROSPECTION_LIBS@
+INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@
+INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@
+INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBDIR = @LIBDIR@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBM = @LIBM@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LOCALEDIR = @LOCALEDIR@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MSGFMT = @MSGFMT@
+MSGFMT_015 = @MSGFMT_015@
+MSGMERGE = @MSGMERGE@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PACKAGE_VERSION_MAJOR = @PACKAGE_VERSION_MAJOR@
+PACKAGE_VERSION_MICRO = @PACKAGE_VERSION_MICRO@
+PACKAGE_VERSION_MINOR = @PACKAGE_VERSION_MINOR@
+PACKAGE_VERSION_NANO = @PACKAGE_VERSION_NANO@
+PACKAGE_VERSION_RELEASE = @PACKAGE_VERSION_RELEASE@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL_PATH = @PERL_PATH@
+PKG_CONFIG = @PKG_CONFIG@
+PLUGINDIR = @PLUGINDIR@
+POSUB = @POSUB@
+PROFILE_CFLAGS = @PROFILE_CFLAGS@
+PYTHON = @PYTHON@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+USE_NLS = @USE_NLS@
+VALGRIND_CFLAGS = @VALGRIND_CFLAGS@
+VALGRIND_LIBS = @VALGRIND_LIBS@
+VALGRIND_PATH = @VALGRIND_PATH@
+VERSION = @VERSION@
+WARNING_CFLAGS = @WARNING_CFLAGS@
+WIN32_LIBS = @WIN32_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_015 = @XGETTEXT_015@
+XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@
+XML_CATALOG = @XML_CATALOG@
+XSLTPROC = @XSLTPROC@
+XSLTPROC_FLAGS = @XSLTPROC_FLAGS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+plugindir = @plugindir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+
+# libgstparse.la is an optionally built helper library linked into core
+noinst_LTLIBRARIES = libgstparse.la
+CLEANFILES = grammar.tab.h grammar.output grammar.tab.c \
+ lex._gst_parse_yy.c
+EXTRA_DIST = \
+ grammar.y \
+ parse.l
+
+
+# uncomment these lines to dist the generated sources
+#BUILT_SOURCES = grammar.tab.h grammar.tab.c lex._gst_parse_yy.c
+#libgstparse_la_SOURCES = lex._gst_parse_yy.c grammar.tab.c
+
+# uncomment these lines to _NOT_ dist the generated sources
+nodist_libgstparse_la_SOURCES = lex._gst_parse_yy.c grammar.tab.c
+
+# can't use GST_ALL_CFLAGS here because that'd pull in -Werror
+libgstparse_la_CFLAGS = -I$(top_srcdir) -I$(top_builddir) \
+ $(GLIB_CFLAGS) $(GST_OPTION_CFLAGS)
+
+libgstparse_la_LIBADD = $(GST_ALL_LIBS)
+noinst_HEADERS = grammar.tab.h types.h
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu gst/parse/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu gst/parse/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \
+ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+ test "$$dir" != "$$p" || dir=.; \
+ echo "rm -f \"$${dir}/so_locations\""; \
+ rm -f "$${dir}/so_locations"; \
+ done
+libgstparse.la: $(libgstparse_la_OBJECTS) $(libgstparse_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libgstparse_la_LINK) $(libgstparse_la_OBJECTS) $(libgstparse_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstparse_la-grammar.tab.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstparse_la-lex._gst_parse_yy.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+
+libgstparse_la-lex._gst_parse_yy.lo: lex._gst_parse_yy.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstparse_la_CFLAGS) $(CFLAGS) -MT libgstparse_la-lex._gst_parse_yy.lo -MD -MP -MF $(DEPDIR)/libgstparse_la-lex._gst_parse_yy.Tpo -c -o libgstparse_la-lex._gst_parse_yy.lo `test -f 'lex._gst_parse_yy.c' || echo '$(srcdir)/'`lex._gst_parse_yy.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstparse_la-lex._gst_parse_yy.Tpo $(DEPDIR)/libgstparse_la-lex._gst_parse_yy.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='lex._gst_parse_yy.c' object='libgstparse_la-lex._gst_parse_yy.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstparse_la_CFLAGS) $(CFLAGS) -c -o libgstparse_la-lex._gst_parse_yy.lo `test -f 'lex._gst_parse_yy.c' || echo '$(srcdir)/'`lex._gst_parse_yy.c
+
+libgstparse_la-grammar.tab.lo: grammar.tab.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstparse_la_CFLAGS) $(CFLAGS) -MT libgstparse_la-grammar.tab.lo -MD -MP -MF $(DEPDIR)/libgstparse_la-grammar.tab.Tpo -c -o libgstparse_la-grammar.tab.lo `test -f 'grammar.tab.c' || echo '$(srcdir)/'`grammar.tab.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstparse_la-grammar.tab.Tpo $(DEPDIR)/libgstparse_la-grammar.tab.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='grammar.tab.c' object='libgstparse_la-grammar.tab.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstparse_la_CFLAGS) $(CFLAGS) -c -o libgstparse_la-grammar.tab.lo `test -f 'grammar.tab.c' || echo '$(srcdir)/'`grammar.tab.c
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ set x; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(HEADERS)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+ clean-libtool clean-noinstLTLIBRARIES ctags distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+ pdf pdf-am ps ps-am tags uninstall uninstall-am
+
+
+Android.mk: Makefile.am
+ androgenizer -:PROJECT gstreamer -:STATIC libgstparse -:TAGS eng debug \
+ -:REL_TOP $(top_srcdir) -:ABS_TOP $(abs_top_srcdir) \
+ -:SOURCES $(libgstparse_la_SOURCES) $(nodist_libgstparse_la_SOURCES) \
+ -:CFLAGS $(DEFS) $(libgstparse_la_CFLAGS) \
+ -:LDFLAGS $(libgstparse_la_LIBADD) \
+ > $@
+
+grammar.tab.c grammar.tab.h: grammar.y
+ $(AM_V_GEN)$(BISON_PATH) -d -v -p_gst_parse_yy $(srcdir)/grammar.y -o grammar.tab.c && \
+ mv grammar.tab.c grammar.tab_tmp.c && \
+ echo '#ifdef HAVE_CONFIG_H' > grammar.tab_tmp2.c && \
+ echo '#include <config.h>' >> grammar.tab_tmp2.c && \
+ echo '#endif' >> grammar.tab_tmp2.c && \
+ cat grammar.tab_tmp.c >> grammar.tab_tmp2.c && \
+ rm grammar.tab_tmp.c && \
+ mv grammar.tab_tmp2.c grammar.tab.c
+
+lex._gst_parse_yy.c: parse.l grammar.tab.h
+ $(AM_V_GEN)$(FLEX_PATH) -P_gst_parse_yy $^ && \
+ mv lex._gst_parse_yy.c lex._gst_parse_yy_tmp.c && \
+ echo '#ifdef HAVE_CONFIG_H' > lex._gst_parse_yy_tmp2.c && \
+ echo '#include <config.h>' >> lex._gst_parse_yy_tmp2.c && \
+ echo '#endif' >> lex._gst_parse_yy_tmp2.c && \
+ echo 'static inline int _gst_parse_yyget_column (void * yyscanner);' >> lex._gst_parse_yy_tmp2.c && \
+ echo 'static inline void _gst_parse_yyset_column (int column_no , void * yyscanner);' >> lex._gst_parse_yy_tmp2.c && \
+ cat lex._gst_parse_yy_tmp.c >> lex._gst_parse_yy_tmp2.c && \
+ rm lex._gst_parse_yy_tmp.c && \
+ mv lex._gst_parse_yy_tmp2.c lex._gst_parse_yy.c
+
+.NOTPARALLEL:
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/gst/parse/grammar.tab.h b/gst/parse/grammar.tab.h
new file mode 100644
index 0000000..95fa2b6
--- /dev/null
+++ b/gst/parse/grammar.tab.h
@@ -0,0 +1,80 @@
+
+/* A Bison parser, made by GNU Bison 2.4.1. */
+
+/* Skeleton interface for Bison's Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+ Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
+
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison. */
+
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ PARSE_URL = 258,
+ IDENTIFIER = 259,
+ BINREF = 260,
+ PADREF = 261,
+ REF = 262,
+ ASSIGNMENT = 263,
+ LINK = 264
+ };
+#endif
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+{
+
+/* Line 1676 of yacc.c */
+#line 623 "./grammar.y"
+
+ gchar *s;
+ chain_t *c;
+ link_t *l;
+ GstElement *e;
+ GSList *p;
+ graph_t *g;
+
+
+
+/* Line 1676 of yacc.c */
+#line 72 "grammar.tab.h"
+} YYSTYPE;
+# define YYSTYPE_IS_TRIVIAL 1
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+
+
+
diff --git a/gst/parse/grammar.y b/gst/parse/grammar.y
new file mode 100644
index 0000000..51755cf
--- /dev/null
+++ b/gst/parse/grammar.y
@@ -0,0 +1,1044 @@
+%{
+#include "../gst_private.h"
+
+#include <glib-object.h>
+#include <glib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "../gst-i18n-lib.h"
+
+#include "../gstconfig.h"
+#include "../gstparse.h"
+#include "../gstinfo.h"
+#include "../gsterror.h"
+#include "../gststructure.h"
+#include "../gsturi.h"
+#include "../gstutils.h"
+#include "../gstvalue.h"
+#include "../gstchildproxy.h"
+#include "types.h"
+
+/* All error messages in this file are user-visible and need to be translated.
+ * Don't start the message with a capital, and don't end them with a period,
+ * as they will be presented inside a sentence/error.
+ */
+
+#define YYERROR_VERBOSE 1
+#define YYLEX_PARAM scanner
+
+#define YYENABLE_NLS 0
+
+#ifndef YYLTYPE_IS_TRIVIAL
+#define YYLTYPE_IS_TRIVIAL 0
+#endif
+
+typedef void* yyscan_t;
+
+int _gst_parse_yylex (void * yylval_param , yyscan_t yyscanner);
+int _gst_parse_yylex_init (yyscan_t scanner);
+int _gst_parse_yylex_destroy (yyscan_t scanner);
+struct yy_buffer_state * _gst_parse_yy_scan_string (char* , yyscan_t);
+void _gst_parse_yypush_buffer_state (void * new_buffer ,yyscan_t yyscanner );
+void _gst_parse_yypop_buffer_state (yyscan_t yyscanner );
+
+#ifdef __GST_PARSE_TRACE
+static guint __strings;
+static guint __links;
+static guint __chains;
+gchar *
+__gst_parse_strdup (gchar *org)
+{
+ gchar *ret;
+ __strings++;
+ ret = g_strdup (org);
+ /* g_print ("ALLOCATED STR (%3u): %p %s\n", __strings, ret, ret); */
+ return ret;
+}
+void
+__gst_parse_strfree (gchar *str)
+{
+ if (str) {
+ /* g_print ("FREEING STR (%3u): %p %s\n", __strings - 1, str, str); */
+ g_free (str);
+ g_return_if_fail (__strings > 0);
+ __strings--;
+ }
+}
+link_t *__gst_parse_link_new ()
+{
+ link_t *ret;
+ __links++;
+ ret = g_slice_new0 (link_t);
+ /* g_print ("ALLOCATED LINK (%3u): %p\n", __links, ret); */
+ return ret;
+}
+void
+__gst_parse_link_free (link_t *data)
+{
+ if (data) {
+ /* g_print ("FREEING LINK (%3u): %p\n", __links - 1, data); */
+ g_slice_free (link_t, data);
+ g_return_if_fail (__links > 0);
+ __links--;
+ }
+}
+chain_t *
+__gst_parse_chain_new ()
+{
+ chain_t *ret;
+ __chains++;
+ ret = g_slice_new0 (chain_t);
+ /* g_print ("ALLOCATED CHAIN (%3u): %p\n", __chains, ret); */
+ return ret;
+}
+void
+__gst_parse_chain_free (chain_t *data)
+{
+ /* g_print ("FREEING CHAIN (%3u): %p\n", __chains - 1, data); */
+ g_slice_free (chain_t, data);
+ g_return_if_fail (__chains > 0);
+ __chains--;
+}
+
+#endif /* __GST_PARSE_TRACE */
+
+typedef struct {
+ gchar *src_pad;
+ gchar *sink_pad;
+ GstElement *sink;
+ GstCaps *caps;
+ gulong signal_id;
+} DelayedLink;
+
+typedef struct {
+ gchar *name;
+ gchar *value_str;
+ gulong signal_id;
+} DelayedSet;
+
+/*** define SET_ERROR macro/function */
+
+#ifdef G_HAVE_ISO_VARARGS
+
+# define SET_ERROR(error, type, ...) \
+G_STMT_START { \
+ GST_CAT_ERROR (GST_CAT_PIPELINE, __VA_ARGS__); \
+ if ((error) && !*(error)) { \
+ g_set_error ((error), GST_PARSE_ERROR, (type), __VA_ARGS__); \
+ } \
+} G_STMT_END
+
+#elif defined(G_HAVE_GNUC_VARARGS)
+
+# define SET_ERROR(error, type, args...) \
+G_STMT_START { \
+ GST_CAT_ERROR (GST_CAT_PIPELINE, args ); \
+ if ((error) && !*(error)) { \
+ g_set_error ((error), GST_PARSE_ERROR, (type), args ); \
+ } \
+} G_STMT_END
+
+#else
+
+static inline void
+SET_ERROR (GError **error, gint type, const char *format, ...)
+{
+ if (error) {
+ if (*error) {
+ g_warning ("error while parsing");
+ } else {
+ va_list varargs;
+ char *string;
+
+ va_start (varargs, format);
+ string = g_strdup_vprintf (format, varargs);
+ va_end (varargs);
+
+ g_set_error (error, GST_PARSE_ERROR, type, string);
+
+ g_free (string);
+ }
+ }
+}
+
+#endif /* G_HAVE_ISO_VARARGS */
+
+/*** define YYPRINTF macro/function if we're debugging */
+
+/* bison 1.35 calls this macro with side effects, we need to make sure the
+ side effects work - crappy bison */
+
+#ifndef GST_DISABLE_GST_DEBUG
+# define YYDEBUG 1
+
+# ifdef G_HAVE_ISO_VARARGS
+
+/* # define YYFPRINTF(a, ...) GST_CAT_DEBUG (GST_CAT_PIPELINE, __VA_ARGS__) */
+# define YYFPRINTF(a, ...) \
+G_STMT_START { \
+ GST_CAT_LOG (GST_CAT_PIPELINE, __VA_ARGS__); \
+} G_STMT_END
+
+# elif defined(G_HAVE_GNUC_VARARGS)
+
+# define YYFPRINTF(a, args...) \
+G_STMT_START { \
+ GST_CAT_LOG (GST_CAT_PIPELINE, args); \
+} G_STMT_END
+
+# else
+
+static inline void
+YYPRINTF(const char *format, ...)
+{
+ va_list varargs;
+ gchar *temp;
+
+ va_start (varargs, format);
+ temp = g_strdup_vprintf (format, varargs);
+ GST_CAT_LOG (GST_CAT_PIPELINE, "%s", temp);
+ g_free (temp);
+ va_end (varargs);
+}
+
+# endif /* G_HAVE_ISO_VARARGS */
+
+#endif /* GST_DISABLE_GST_DEBUG */
+
+#define ADD_MISSING_ELEMENT(graph,name) G_STMT_START { \
+ if ((graph)->ctx) { \
+ (graph)->ctx->missing_elements = \
+ g_list_append ((graph)->ctx->missing_elements, g_strdup (name)); \
+ } } G_STMT_END
+
+static void
+no_free (gconstpointer foo)
+{
+ /* do nothing */
+}
+
+#define GST_BIN_MAKE(res, type, chainval, assign, type_string_free_func) \
+G_STMT_START { \
+ chain_t *chain = chainval; \
+ GSList *walk; \
+ GstBin *bin = (GstBin *) gst_element_factory_make (type, NULL); \
+ if (!chain) { \
+ SET_ERROR (graph->error, GST_PARSE_ERROR_EMPTY_BIN, \
+ _("specified empty bin \"%s\", not allowed"), type); \
+ g_slist_foreach (assign, (GFunc) gst_parse_strfree, NULL); \
+ g_slist_free (assign); \
+ gst_object_unref (bin); \
+ type_string_free_func (type); /* Need to clean up the string */ \
+ YYERROR; \
+ } else if (!bin) { \
+ ADD_MISSING_ELEMENT(graph, type); \
+ SET_ERROR (graph->error, GST_PARSE_ERROR_NO_SUCH_ELEMENT, \
+ _("no bin \"%s\", skipping"), type); \
+ g_slist_foreach (assign, (GFunc) gst_parse_strfree, NULL); \
+ g_slist_free (assign); \
+ res = chain; \
+ } else { \
+ for (walk = chain->elements; walk; walk = walk->next ) \
+ gst_bin_add (bin, GST_ELEMENT (walk->data)); \
+ g_slist_free (chain->elements); \
+ chain->elements = g_slist_prepend (NULL, bin); \
+ res = chain; \
+ /* set the properties now */ \
+ for (walk = assign; walk; walk = walk->next) \
+ gst_parse_element_set ((gchar *) walk->data, GST_ELEMENT (bin), graph); \
+ g_slist_free (assign); \
+ } \
+} G_STMT_END
+
+#define MAKE_LINK(link, _src, _src_name, _src_pads, _sink, _sink_name, _sink_pads) \
+G_STMT_START { \
+ link = gst_parse_link_new (); \
+ link->src = _src; \
+ link->sink = _sink; \
+ link->src_name = _src_name; \
+ link->sink_name = _sink_name; \
+ link->src_pads = _src_pads; \
+ link->sink_pads = _sink_pads; \
+ link->caps = NULL; \
+} G_STMT_END
+
+#define MAKE_REF(link, _src, _pads) \
+G_STMT_START { \
+ gchar *padname = _src; \
+ GSList *pads = _pads; \
+ if (padname) { \
+ while (*padname != '.') padname++; \
+ *padname = '\0'; \
+ padname++; \
+ if (*padname != '\0') \
+ pads = g_slist_prepend (pads, gst_parse_strdup (padname)); \
+ } \
+ MAKE_LINK (link, NULL, _src, pads, NULL, NULL, NULL); \
+} G_STMT_END
+
+static void
+gst_parse_free_delayed_set (DelayedSet *set)
+{
+ g_free(set->name);
+ g_free(set->value_str);
+ g_slice_free(DelayedSet, set);
+}
+
+static void gst_parse_new_child(GstChildProxy *child_proxy, GObject *object,
+ gpointer data);
+
+static void
+gst_parse_add_delayed_set (GstElement *element, gchar *name, gchar *value_str)
+{
+ DelayedSet *data = g_slice_new0 (DelayedSet);
+
+ GST_CAT_LOG_OBJECT (GST_CAT_PIPELINE, element, "delaying property set %s to %s",
+ name, value_str);
+
+ data->name = g_strdup(name);
+ data->value_str = g_strdup(value_str);
+ data->signal_id = g_signal_connect_data(element, "child-added",
+ G_CALLBACK (gst_parse_new_child), data, (GClosureNotify)
+ gst_parse_free_delayed_set, (GConnectFlags) 0);
+
+ /* FIXME: we would need to listen on all intermediate bins too */
+ if (GST_IS_BIN (element)) {
+ gchar **names, **current;
+ GstElement *parent, *child;
+
+ current = names = g_strsplit (name, "::", -1);
+ parent = gst_bin_get_by_name (GST_BIN_CAST (element), current[0]);
+ current++;
+ while (parent && current[0]) {
+ child = gst_bin_get_by_name (GST_BIN (parent), current[0]);
+ if (!child && current[1]) {
+ char *sub_name = g_strjoinv ("::", &current[0]);
+
+ gst_parse_add_delayed_set(parent, sub_name, value_str);
+ g_free (sub_name);
+ }
+ parent = child;
+ current++;
+ }
+ g_strfreev (names);
+ }
+}
+
+static void gst_parse_new_child(GstChildProxy *child_proxy, GObject *object,
+ gpointer data)
+{
+ DelayedSet *set = (DelayedSet *) data;
+ GParamSpec *pspec;
+ GValue v = { 0, };
+ GstObject *target = NULL;
+ GType value_type;
+
+ GST_CAT_LOG_OBJECT (GST_CAT_PIPELINE, child_proxy, "new child %s, checking property %s",
+ GST_OBJECT_NAME(object), set->name);
+
+ if (gst_child_proxy_lookup (GST_OBJECT (child_proxy), set->name, &target, &pspec)) {
+ gboolean got_value = FALSE;
+
+ value_type = pspec->value_type;
+
+ GST_CAT_LOG_OBJECT (GST_CAT_PIPELINE, child_proxy, "parsing delayed property %s as a %s from %s",
+ pspec->name, g_type_name (value_type), set->value_str);
+ g_value_init (&v, value_type);
+ if (gst_value_deserialize (&v, set->value_str))
+ got_value = TRUE;
+ else if (g_type_is_a (value_type, GST_TYPE_ELEMENT)) {
+ GstElement *bin;
+
+ bin = gst_parse_bin_from_description (set->value_str, TRUE, NULL);
+ if (bin) {
+ g_value_set_object (&v, bin);
+ got_value = TRUE;
+ }
+ }
+ g_signal_handler_disconnect (child_proxy, set->signal_id);
+ if (!got_value)
+ goto error;
+ g_object_set_property (G_OBJECT (target), pspec->name, &v);
+ } else {
+ const gchar *obj_name = GST_OBJECT_NAME(object);
+ gint len = strlen (obj_name);
+
+ /* do a delayed set */
+ if ((strlen (set->name) > (len + 2)) && !strncmp (set->name, obj_name, len) && !strncmp (&set->name[len], "::", 2)) {
+ gst_parse_add_delayed_set (GST_ELEMENT(child_proxy), set->name, set->value_str);
+ }
+ }
+
+out:
+ if (G_IS_VALUE (&v))
+ g_value_unset (&v);
+ if (target)
+ gst_object_unref (target);
+ return;
+
+error:
+ GST_CAT_ERROR (GST_CAT_PIPELINE, "could not set property \"%s\" in element \"%s\"",
+ pspec->name, GST_ELEMENT_NAME (target));
+ goto out;
+}
+
+static void
+gst_parse_element_set (gchar *value, GstElement *element, graph_t *graph)
+{
+ GParamSpec *pspec;
+ gchar *pos = value;
+ GValue v = { 0, };
+ GstObject *target = NULL;
+ GType value_type;
+
+ /* do nothing if assignment is for missing element */
+ if (element == NULL)
+ goto out;
+
+ /* parse the string, so the property name is null-terminated an pos points
+ to the beginning of the value */
+ while (!g_ascii_isspace (*pos) && (*pos != '=')) pos++;
+ if (*pos == '=') {
+ *pos = '\0';
+ } else {
+ *pos = '\0';
+ pos++;
+ while (g_ascii_isspace (*pos)) pos++;
+ }
+ pos++;
+ while (g_ascii_isspace (*pos)) pos++;
+ if (*pos == '"') {
+ pos++;
+ pos[strlen (pos) - 1] = '\0';
+ }
+ gst_parse_unescape (pos);
+
+ if (gst_child_proxy_lookup (GST_OBJECT (element), value, &target, &pspec)) {
+ gboolean got_value = FALSE;
+
+ value_type = pspec->value_type;
+
+ GST_CAT_LOG_OBJECT (GST_CAT_PIPELINE, element, "parsing property %s as a %s", pspec->name,
+ g_type_name (value_type));
+ g_value_init (&v, value_type);
+ if (gst_value_deserialize (&v, pos))
+ got_value = TRUE;
+ else if (g_type_is_a (value_type, GST_TYPE_ELEMENT)) {
+ GstElement *bin;
+
+ bin = gst_parse_bin_from_description (pos, TRUE, NULL);
+ if (bin) {
+ g_value_set_object (&v, bin);
+ got_value = TRUE;
+ }
+ }
+ if (!got_value)
+ goto error;
+ g_object_set_property (G_OBJECT (target), pspec->name, &v);
+ } else {
+ /* do a delayed set */
+ if (GST_IS_CHILD_PROXY (element)) {
+ gst_parse_add_delayed_set (element, value, pos);
+ }
+ else {
+ SET_ERROR (graph->error, GST_PARSE_ERROR_NO_SUCH_PROPERTY, \
+ _("no property \"%s\" in element \"%s\""), value, \
+ GST_ELEMENT_NAME (element));
+ }
+ }
+
+out:
+ gst_parse_strfree (value);
+ if (G_IS_VALUE (&v))
+ g_value_unset (&v);
+ if (target)
+ gst_object_unref (target);
+ return;
+
+error:
+ SET_ERROR (graph->error, GST_PARSE_ERROR_COULD_NOT_SET_PROPERTY,
+ _("could not set property \"%s\" in element \"%s\" to \"%s\""),
+ value, GST_ELEMENT_NAME (element), pos);
+ goto out;
+}
+
+static inline void
+gst_parse_free_link (link_t *link)
+{
+ gst_parse_strfree (link->src_name);
+ gst_parse_strfree (link->sink_name);
+ g_slist_foreach (link->src_pads, (GFunc) gst_parse_strfree, NULL);
+ g_slist_foreach (link->sink_pads, (GFunc) gst_parse_strfree, NULL);
+ g_slist_free (link->src_pads);
+ g_slist_free (link->sink_pads);
+ if (link->caps) gst_caps_unref (link->caps);
+ gst_parse_link_free (link);
+}
+
+static void
+gst_parse_free_delayed_link (DelayedLink *link)
+{
+ g_free (link->src_pad);
+ g_free (link->sink_pad);
+ if (link->caps) gst_caps_unref (link->caps);
+ g_slice_free (DelayedLink, link);
+}
+
+static void
+gst_parse_found_pad (GstElement *src, GstPad *pad, gpointer data)
+{
+ DelayedLink *link = data;
+
+ GST_CAT_INFO (GST_CAT_PIPELINE, "trying delayed linking %s:%s to %s:%s",
+ GST_STR_NULL (GST_ELEMENT_NAME (src)), GST_STR_NULL (link->src_pad),
+ GST_STR_NULL (GST_ELEMENT_NAME (link->sink)), GST_STR_NULL (link->sink_pad));
+
+ if (gst_element_link_pads_filtered (src, link->src_pad, link->sink,
+ link->sink_pad, link->caps)) {
+ /* do this here, we don't want to get any problems later on when
+ * unlocking states */
+ GST_CAT_DEBUG (GST_CAT_PIPELINE, "delayed linking %s:%s to %s:%s worked",
+ GST_STR_NULL (GST_ELEMENT_NAME (src)), GST_STR_NULL (link->src_pad),
+ GST_STR_NULL (GST_ELEMENT_NAME (link->sink)), GST_STR_NULL (link->sink_pad));
+ g_signal_handler_disconnect (src, link->signal_id);
+ }
+}
+
+/* both padnames and the caps may be NULL */
+static gboolean
+gst_parse_perform_delayed_link (GstElement *src, const gchar *src_pad,
+ GstElement *sink, const gchar *sink_pad,
+ GstCaps *caps)
+{
+ GList *templs = gst_element_class_get_pad_template_list (
+ GST_ELEMENT_GET_CLASS (src));
+
+ for (; templs; templs = templs->next) {
+ GstPadTemplate *templ = (GstPadTemplate *) templs->data;
+ if ((GST_PAD_TEMPLATE_DIRECTION (templ) == GST_PAD_SRC) &&
+ (GST_PAD_TEMPLATE_PRESENCE(templ) == GST_PAD_SOMETIMES))
+ {
+ DelayedLink *data = g_slice_new (DelayedLink);
+
+ /* TODO: maybe we should check if src_pad matches this template's names */
+
+ GST_CAT_DEBUG (GST_CAT_PIPELINE, "trying delayed link %s:%s to %s:%s",
+ GST_STR_NULL (GST_ELEMENT_NAME (src)), GST_STR_NULL (src_pad),
+ GST_STR_NULL (GST_ELEMENT_NAME (sink)), GST_STR_NULL (sink_pad));
+
+ data->src_pad = g_strdup (src_pad);
+ data->sink = sink;
+ data->sink_pad = g_strdup (sink_pad);
+ if (caps) {
+ data->caps = gst_caps_copy (caps);
+ } else {
+ data->caps = NULL;
+ }
+ data->signal_id = g_signal_connect_data (src, "pad-added",
+ G_CALLBACK (gst_parse_found_pad), data,
+ (GClosureNotify) gst_parse_free_delayed_link, (GConnectFlags) 0);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/*
+ * performs a link and frees the struct. src and sink elements must be given
+ * return values 0 - link performed
+ * 1 - link delayed
+ * <0 - error
+ */
+static gint
+gst_parse_perform_link (link_t *link, graph_t *graph)
+{
+ GstElement *src = link->src;
+ GstElement *sink = link->sink;
+ GSList *srcs = link->src_pads;
+ GSList *sinks = link->sink_pads;
+ g_assert (GST_IS_ELEMENT (src));
+ g_assert (GST_IS_ELEMENT (sink));
+
+ GST_CAT_INFO (GST_CAT_PIPELINE,
+ "linking %s:%s to %s:%s (%u/%u) with caps \"%" GST_PTR_FORMAT "\"",
+ GST_ELEMENT_NAME (src), link->src_name ? link->src_name : "(any)",
+ GST_ELEMENT_NAME (sink), link->sink_name ? link->sink_name : "(any)",
+ g_slist_length (srcs), g_slist_length (sinks), link->caps);
+
+ if (!srcs || !sinks) {
+ if (gst_element_link_pads_filtered (src,
+ srcs ? (const gchar *) srcs->data : NULL, sink,
+ sinks ? (const gchar *) sinks->data : NULL, link->caps)) {
+ goto success;
+ } else {
+ if (gst_parse_perform_delayed_link (src,
+ srcs ? (const gchar *) srcs->data : NULL,
+ sink, sinks ? (const gchar *) sinks->data : NULL, link->caps)) {
+ goto success;
+ } else {
+ goto error;
+ }
+ }
+ }
+ if (g_slist_length (link->src_pads) != g_slist_length (link->sink_pads)) {
+ goto error;
+ }
+ while (srcs && sinks) {
+ const gchar *src_pad = (const gchar *) srcs->data;
+ const gchar *sink_pad = (const gchar *) sinks->data;
+ srcs = g_slist_next (srcs);
+ sinks = g_slist_next (sinks);
+ if (gst_element_link_pads_filtered (src, src_pad, sink, sink_pad,
+ link->caps)) {
+ continue;
+ } else {
+ if (gst_parse_perform_delayed_link (src, src_pad,
+ sink, sink_pad,
+ link->caps)) {
+ continue;
+ } else {
+ goto error;
+ }
+ }
+ }
+
+success:
+ gst_parse_free_link (link);
+ return 0;
+
+error:
+ SET_ERROR (graph->error, GST_PARSE_ERROR_LINK,
+ _("could not link %s to %s"), GST_ELEMENT_NAME (src),
+ GST_ELEMENT_NAME (sink));
+ gst_parse_free_link (link);
+ return -1;
+}
+
+
+static int yyerror (void *scanner, graph_t *graph, const char *s);
+%}
+
+%union {
+ gchar *s;
+ chain_t *c;
+ link_t *l;
+ GstElement *e;
+ GSList *p;
+ graph_t *g;
+}
+
+%token <s> PARSE_URL
+%token <s> IDENTIFIER
+%left <s> REF PADREF BINREF
+%token <s> ASSIGNMENT
+%token <s> LINK
+
+%type <g> graph
+%type <c> chain bin
+%type <l> reference
+%type <l> linkpart link
+%type <p> linklist
+%type <e> element
+%type <p> padlist pads assignments
+
+%left '(' ')'
+%left ','
+%right '.'
+%left '!' '='
+
+%parse-param { void *scanner }
+%parse-param { graph_t *graph }
+%pure-parser
+
+%start graph
+%%
+
+element: IDENTIFIER { $$ = gst_element_factory_make ($1, NULL);
+ if ($$ == NULL) {
+ ADD_MISSING_ELEMENT (graph, $1);
+ SET_ERROR (graph->error, GST_PARSE_ERROR_NO_SUCH_ELEMENT, _("no element \"%s\""), $1);
+ /* if FATAL_ERRORS flag is set, we don't have to worry about backwards
+ * compatibility and can continue parsing and check for other missing
+ * elements */
+ if ((graph->flags & GST_PARSE_FLAG_FATAL_ERRORS) == 0) {
+ gst_parse_strfree ($1);
+ YYERROR;
+ }
+ }
+ gst_parse_strfree ($1);
+ }
+ | element ASSIGNMENT { gst_parse_element_set ($2, $1, graph);
+ $$ = $1;
+ }
+ ;
+assignments: /* NOP */ { $$ = NULL; }
+ | assignments ASSIGNMENT { $$ = g_slist_prepend ($1, $2); }
+ ;
+bin: '(' assignments chain ')' { GST_BIN_MAKE ($$, "bin", $3, $2, no_free); }
+ | BINREF assignments chain ')' { GST_BIN_MAKE ($$, $1, $3, $2, gst_parse_strfree);
+ gst_parse_strfree ($1);
+ }
+ | BINREF assignments ')' { GST_BIN_MAKE ($$, $1, NULL, $2, gst_parse_strfree);
+ gst_parse_strfree ($1);
+ }
+ | BINREF assignments error ')' { GST_BIN_MAKE ($$, $1, NULL, $2, gst_parse_strfree);
+ gst_parse_strfree ($1);
+ }
+ ;
+
+pads: PADREF { $$ = g_slist_prepend (NULL, $1); }
+ | PADREF padlist { $$ = $2;
+ $$ = g_slist_prepend ($$, $1);
+ }
+ ;
+padlist: ',' IDENTIFIER { $$ = g_slist_prepend (NULL, $2); }
+ | ',' IDENTIFIER padlist { $$ = g_slist_prepend ($3, $2); }
+ ;
+
+reference: REF { MAKE_REF ($$, $1, NULL); }
+ | REF padlist { MAKE_REF ($$, $1, $2); }
+ ;
+
+linkpart: reference { $$ = $1; }
+ | pads { MAKE_REF ($$, NULL, $1); }
+ | /* NOP */ { MAKE_REF ($$, NULL, NULL); }
+ ;
+
+link: linkpart LINK linkpart { $$ = $1;
+ if ($2) {
+ $$->caps = gst_caps_from_string ($2);
+ if ($$->caps == NULL)
+ SET_ERROR (graph->error, GST_PARSE_ERROR_LINK, _("could not parse caps \"%s\""), $2);
+ gst_parse_strfree ($2);
+ }
+ $$->sink_name = $3->src_name;
+ $$->sink_pads = $3->src_pads;
+ gst_parse_link_free ($3);
+ }
+ ;
+
+linklist: link { $$ = g_slist_prepend (NULL, $1); }
+ | link linklist { $$ = g_slist_prepend ($2, $1); }
+ | linklist error { $$ = $1; }
+ ;
+
+chain: element { $$ = gst_parse_chain_new ();
+ $$->first = $$->last = $1;
+ $$->front = $$->back = NULL;
+ $$->elements = g_slist_prepend (NULL, $1);
+ }
+ | bin { $$ = $1; }
+ | chain chain { if ($1->back && $2->front) {
+ if (!$1->back->sink_name) {
+ SET_ERROR (graph->error, GST_PARSE_ERROR_LINK, _("link without source element"));
+ gst_parse_free_link ($1->back);
+ } else {
+ graph->links = g_slist_prepend (graph->links, $1->back);
+ }
+ if (!$2->front->src_name) {
+ SET_ERROR (graph->error, GST_PARSE_ERROR_LINK, _("link without sink element"));
+ gst_parse_free_link ($2->front);
+ } else {
+ graph->links = g_slist_prepend (graph->links, $2->front);
+ }
+ $1->back = NULL;
+ } else if ($1->back) {
+ if (!$1->back->sink_name) {
+ $1->back->sink = $2->first;
+ }
+ } else if ($2->front) {
+ if (!$2->front->src_name) {
+ $2->front->src = $1->last;
+ }
+ $1->back = $2->front;
+ }
+
+ if ($1->back) {
+ graph->links = g_slist_prepend (graph->links, $1->back);
+ }
+ $1->last = $2->last;
+ $1->back = $2->back;
+ $1->elements = g_slist_concat ($1->elements, $2->elements);
+ if ($2)
+ gst_parse_chain_free ($2);
+ $$ = $1;
+ }
+ | chain linklist { GSList *walk;
+ if ($1->back) {
+ $2 = g_slist_prepend ($2, $1->back);
+ $1->back = NULL;
+ } else {
+ if (!((link_t *) $2->data)->src_name) {
+ ((link_t *) $2->data)->src = $1->last;
+ }
+ }
+ for (walk = $2; walk; walk = walk->next) {
+ link_t *link = (link_t *) walk->data;
+ if (!link->sink_name && walk->next) {
+ SET_ERROR (graph->error, GST_PARSE_ERROR_LINK, _("link without sink element"));
+ gst_parse_free_link (link);
+ } else if (!link->src_name && !link->src) {
+ SET_ERROR (graph->error, GST_PARSE_ERROR_LINK, _("link without source element"));
+ gst_parse_free_link (link);
+ } else {
+ if (walk->next) {
+ graph->links = g_slist_prepend (graph->links, link);
+ } else {
+ $1->back = link;
+ }
+ }
+ }
+ g_slist_free ($2);
+ $$ = $1;
+ }
+ | chain error { $$ = $1; }
+ | link chain { if ($2->front) {
+ if (!$2->front->src_name) {
+ SET_ERROR (graph->error, GST_PARSE_ERROR_LINK, _("link without source element"));
+ gst_parse_free_link ($2->front);
+ } else {
+ graph->links = g_slist_prepend (graph->links, $2->front);
+ }
+ }
+ if (!$1->sink_name) {
+ $1->sink = $2->first;
+ }
+ $2->front = $1;
+ $$ = $2;
+ }
+ | PARSE_URL chain { $$ = $2;
+ if ($$->front) {
+ GstElement *element =
+ gst_element_make_from_uri (GST_URI_SRC, $1, NULL);
+ if (!element) {
+ SET_ERROR (graph->error, GST_PARSE_ERROR_NO_SUCH_ELEMENT,
+ _("no source element for URI \"%s\""), $1);
+ } else {
+ $$->front->src = element;
+ graph->links = g_slist_prepend (
+ graph->links, $$->front);
+ $$->front = NULL;
+ $$->elements = g_slist_prepend ($$->elements, element);
+ }
+ } else {
+ SET_ERROR (graph->error, GST_PARSE_ERROR_LINK,
+ _("no element to link URI \"%s\" to"), $1);
+ }
+ g_free ($1);
+ }
+ | link PARSE_URL { GstElement *element =
+ gst_element_make_from_uri (GST_URI_SINK, $2, NULL);
+ if (!element) {
+ SET_ERROR (graph->error, GST_PARSE_ERROR_NO_SUCH_ELEMENT,
+ _("no sink element for URI \"%s\""), $2);
+ gst_parse_link_free ($1);
+ g_free ($2);
+ YYERROR;
+ } else if ($1->sink_name || $1->sink_pads) {
+ gst_object_unref (element);
+ SET_ERROR (graph->error, GST_PARSE_ERROR_LINK,
+ _("could not link sink element for URI \"%s\""), $2);
+ gst_parse_link_free ($1);
+ g_free ($2);
+ YYERROR;
+ } else {
+ $$ = gst_parse_chain_new ();
+ $$->first = $$->last = element;
+ $$->front = $1;
+ $$->front->sink = element;
+ $$->elements = g_slist_prepend (NULL, element);
+ }
+ g_free ($2);
+ }
+ ;
+graph: /* NOP */ { SET_ERROR (graph->error, GST_PARSE_ERROR_EMPTY, _("empty pipeline not allowed"));
+ $$ = graph;
+ }
+ | chain { $$ = graph;
+ if ($1->front) {
+ if (!$1->front->src_name) {
+ SET_ERROR (graph->error, GST_PARSE_ERROR_LINK, _("link without source element"));
+ gst_parse_free_link ($1->front);
+ } else {
+ $$->links = g_slist_prepend ($$->links, $1->front);
+ }
+ $1->front = NULL;
+ }
+ if ($1->back) {
+ if (!$1->back->sink_name) {
+ SET_ERROR (graph->error, GST_PARSE_ERROR_LINK, _("link without sink element"));
+ gst_parse_free_link ($1->back);
+ } else {
+ $$->links = g_slist_prepend ($$->links, $1->back);
+ }
+ $1->back = NULL;
+ }
+ $$->chain = $1;
+ }
+ ;
+
+%%
+
+
+static int
+yyerror (void *scanner, graph_t *graph, const char *s)
+{
+ /* FIXME: This should go into the GError somehow, but how? */
+ GST_WARNING ("Error during parsing: %s", s);
+ return -1;
+}
+
+
+GstElement *
+_gst_parse_launch (const gchar *str, GError **error, GstParseContext *ctx,
+ GstParseFlags flags)
+{
+ graph_t g;
+ gchar *dstr;
+ GSList *walk;
+ GstBin *bin = NULL;
+ GstElement *ret;
+ yyscan_t scanner;
+
+ g_return_val_if_fail (str != NULL, NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ g.chain = NULL;
+ g.links = NULL;
+ g.error = error;
+ g.ctx = ctx;
+ g.flags = flags;
+
+#ifdef __GST_PARSE_TRACE
+ GST_CAT_DEBUG (GST_CAT_PIPELINE, "TRACE: tracing enabled");
+ __strings = __chains = __links = 0;
+#endif /* __GST_PARSE_TRACE */
+
+ dstr = g_strdup (str);
+ _gst_parse_yylex_init (&scanner);
+ _gst_parse_yy_scan_string (dstr, scanner);
+
+#ifndef YYDEBUG
+ yydebug = 1;
+#endif
+
+ if (yyparse (scanner, &g) != 0) {
+ SET_ERROR (error, GST_PARSE_ERROR_SYNTAX,
+ "Unrecoverable syntax error while parsing pipeline %s", str);
+
+ _gst_parse_yylex_destroy (scanner);
+ g_free (dstr);
+
+ goto error1;
+ }
+ _gst_parse_yylex_destroy (scanner);
+ g_free (dstr);
+
+ GST_CAT_DEBUG (GST_CAT_PIPELINE, "got %u elements and %u links",
+ g.chain ? g_slist_length (g.chain->elements) : 0,
+ g_slist_length (g.links));
+
+ if (!g.chain) {
+ ret = NULL;
+ } else if (!g.chain->elements->next) {
+ /* only one toplevel element */
+ ret = (GstElement *) g.chain->elements->data;
+ g_slist_free (g.chain->elements);
+ if (GST_IS_BIN (ret))
+ bin = GST_BIN (ret);
+ gst_parse_chain_free (g.chain);
+ } else {
+ /* put all elements in our bin */
+ bin = GST_BIN (gst_element_factory_make ("pipeline", NULL));
+ g_assert (bin);
+
+ for (walk = g.chain->elements; walk; walk = walk->next) {
+ if (walk->data != NULL)
+ gst_bin_add (bin, GST_ELEMENT (walk->data));
+ }
+
+ g_slist_free (g.chain->elements);
+ ret = GST_ELEMENT (bin);
+ gst_parse_chain_free (g.chain);
+ }
+
+ /* remove links */
+ for (walk = g.links; walk; walk = walk->next) {
+ link_t *l = (link_t *) walk->data;
+ if (!l->src) {
+ if (l->src_name) {
+ if (bin) {
+ l->src = gst_bin_get_by_name_recurse_up (bin, l->src_name);
+ if (l->src)
+ gst_object_unref (l->src);
+ } else {
+ l->src = strcmp (GST_ELEMENT_NAME (ret), l->src_name) == 0 ? ret : NULL;
+ }
+ }
+ if (!l->src) {
+ if (l->src_name) {
+ SET_ERROR (error, GST_PARSE_ERROR_NO_SUCH_ELEMENT,
+ "No element named \"%s\" - omitting link", l->src_name);
+ } else {
+ /* probably a missing element which we've handled already */
+ }
+ gst_parse_free_link (l);
+ continue;
+ }
+ }
+ if (!l->sink) {
+ if (l->sink_name) {
+ if (bin) {
+ l->sink = gst_bin_get_by_name_recurse_up (bin, l->sink_name);
+ if (l->sink)
+ gst_object_unref (l->sink);
+ } else {
+ l->sink = strcmp (GST_ELEMENT_NAME (ret), l->sink_name) == 0 ? ret : NULL;
+ }
+ }
+ if (!l->sink) {
+ if (l->sink_name) {
+ SET_ERROR (error, GST_PARSE_ERROR_NO_SUCH_ELEMENT,
+ "No element named \"%s\" - omitting link", l->sink_name);
+ } else {
+ /* probably a missing element which we've handled already */
+ }
+ gst_parse_free_link (l);
+ continue;
+ }
+ }
+ gst_parse_perform_link (l, &g);
+ }
+ g_slist_free (g.links);
+
+out:
+#ifdef __GST_PARSE_TRACE
+ GST_CAT_DEBUG (GST_CAT_PIPELINE,
+ "TRACE: %u strings, %u chains and %u links left", __strings, __chains,
+ __links);
+ if (__strings || __chains || __links) {
+ g_warning ("TRACE: %u strings, %u chains and %u links left", __strings,
+ __chains, __links);
+ }
+#endif /* __GST_PARSE_TRACE */
+
+ return ret;
+
+error1:
+ if (g.chain) {
+ g_slist_foreach (g.chain->elements, (GFunc)gst_object_unref, NULL);
+ g_slist_free (g.chain->elements);
+ gst_parse_chain_free (g.chain);
+ }
+
+ g_slist_foreach (g.links, (GFunc)gst_parse_free_link, NULL);
+ g_slist_free (g.links);
+
+ if (error)
+ g_assert (*error);
+ ret = NULL;
+
+ goto out;
+}
diff --git a/gst/parse/parse.l b/gst/parse/parse.l
new file mode 100644
index 0000000..bcd6527
--- /dev/null
+++ b/gst/parse/parse.l
@@ -0,0 +1,148 @@
+%{
+#include "../gst_private.h"
+
+#include <math.h>
+#include <string.h>
+
+#include <glib/gprintf.h>
+
+#include "types.h"
+#include "../gstinfo.h"
+#include "../gsturi.h"
+#include "grammar.tab.h"
+
+/* Override the default ECHO so as to avoid fortify warnings. Ignore the
+ embedded-NUL case for now. We know yytext is NUL-terminated. */
+#define ECHO g_fprintf(yyout, "%s", yytext)
+
+#ifdef G_HAVE_ISO_VARARGS
+#define PRINT(...) GST_CAT_DEBUG (GST_CAT_PIPELINE, "flex: " __VA_ARGS__)
+#elif defined(G_HAVE_GNUC_VARARGS)
+#define PRINT(args...) GST_CAT_DEBUG (GST_CAT_PIPELINE, "flex: " args)
+#else
+static inline void
+PRINT (const char *format, ...)
+{
+ va_list varargs;
+
+ va_start (varargs, format);
+ GST_CAT_LEVEL_LOG_valist (GST_CAT_PIPELINE, GST_LEVEL_DEBUG, NULL,
+ format, varargs);
+ va_end (varargs);
+}
+#endif
+
+%}
+
+_operator [(){}.!,;=]
+_identifier [[:alnum:]_][[:alnum:]\-_%:]*
+
+_char ("\\".)|([^[:space:]])
+_string {_char}+|("\""([^\"]|"\\\"")*"\"")|("'"([^']|"\\\'")*"'")
+
+_assign [[:space:]]*"="[[:space:]]*
+
+_protocol [[:alpha:]][[:alnum:]+-\.]*
+_url ({_protocol}"://"{_string}|["."{_identifier}]?"/"{_string})|({_protocol}"://")
+
+/* we must do this here, because nearly everything matches a {_string} */
+_assignment {_identifier}{_assign}{_string}
+
+/* get pad/element references and stuff with dots right */
+_padref "."{_identifier}
+_ref {_identifier}"."{_identifier}?
+_binref {_identifier}[[:space:]]*"."[[:space:]]*"("
+
+/* links */
+_mimechar [[:alnum:]-]
+_mimetype {_mimechar}+"/"{_mimechar}+
+_capschar ("\\".)|([^\;!])
+_capsstring {_capschar}+
+_caps {_mimetype}(","[^!]|{_capsstring})*
+_link ("!"[[:space:]]*{_caps}([[:space:]]*(";"[[:space:]]*{_caps})*[[:space:]]*)*"!")|("!")
+
+%x value
+%option noyywrap
+%option nounput
+%option reentrant
+%option bison-bridge
+%option never-interactive
+%option noinput
+%%
+
+{_assignment} {
+ /* "=" */
+ PRINT ("ASSIGNMENT: %s", yytext);
+ yylval->s = gst_parse_strdup (yytext);
+ BEGIN (INITIAL);
+ return ASSIGNMENT;
+}
+
+{_padref} {
+ yytext++;
+ PRINT ("PADREF: %s", yytext);
+ yylval->s = gst_parse_strdup (yytext);
+ BEGIN (INITIAL);
+ return PADREF;
+}
+
+{_ref} {
+ PRINT ("REF: %s", yytext);
+ yylval->s = gst_parse_strdup (yytext);
+ BEGIN (INITIAL);
+ return REF;
+}
+
+{_binref} {
+ gchar *pos = yytext;
+ while (!g_ascii_isspace (*pos) && (*pos != '.')) pos++;
+ *pos = '\0';
+ PRINT ("BINREF: %s", yytext);
+ yylval->s = gst_parse_strdup (yytext);
+ BEGIN (INITIAL);
+ return BINREF;
+}
+
+{_identifier} {
+ PRINT ("IDENTIFIER: %s", yytext);
+ yylval->s = gst_parse_strdup (yytext);
+ BEGIN (INITIAL);
+ return IDENTIFIER;
+}
+
+{_link} {
+ gchar *c = yytext;
+ PRINT ("LINK: %s", yytext);
+ c++;
+ if (*c) {
+ while (g_ascii_isspace (*c)) c++;
+ c = yylval->s = gst_parse_strdup (c);
+ while (*c) c++;
+ if (*--c != '!')
+ g_assert_not_reached ();
+ while (g_ascii_isspace (*--c));
+ *++c = '\0';
+ } else {
+ yylval->s = NULL;
+ }
+ BEGIN (INITIAL);
+ return LINK;
+}
+{_url} {
+ PRINT ("URL: %s", yytext);
+ yylval->s = g_strdup (yytext);
+ gst_parse_unescape (yylval->s);
+ BEGIN (INITIAL);
+ return PARSE_URL;
+}
+
+{_operator} { PRINT ("OPERATOR: [%s]", yytext); return *yytext; }
+
+[[:space:]]+ { PRINT ("SPACE: [%s]", yytext); }
+
+. {
+ PRINT ("Invalid Lexer element: %s\n", yytext);
+ return *yytext;
+}
+
+%%
diff --git a/gst/parse/types.h b/gst/parse/types.h
new file mode 100644
index 0000000..db1f585
--- /dev/null
+++ b/gst/parse/types.h
@@ -0,0 +1,101 @@
+#ifndef __GST_PARSE_TYPES_H__
+#define __GST_PARSE_TYPES_H__
+
+#include <glib-object.h>
+#include "../gstelement.h"
+#include "../gstparse.h"
+
+typedef struct {
+ GstElement *src;
+ GstElement *sink;
+ gchar *src_name;
+ gchar *sink_name;
+ GSList *src_pads;
+ GSList *sink_pads;
+ GstCaps *caps;
+} link_t;
+
+typedef struct {
+ GSList *elements;
+ GstElement *first;
+ GstElement *last;
+ link_t *front;
+ link_t *back;
+} chain_t;
+
+typedef struct _graph_t graph_t;
+struct _graph_t {
+ chain_t *chain; /* links are supposed to be done now */
+ GSList *links;
+ GError **error;
+ GstParseContext *ctx; /* may be NULL */
+ GstParseFlags flags;
+};
+
+
+/*
+ * Memory checking. Should probably be done with gsttrace stuff, but that
+ * doesn't really work.
+ * This is not safe from reentrance issues, but that doesn't matter as long as
+ * we lock a mutex before parsing anyway.
+ */
+#ifdef GST_DEBUG_ENABLED
+# define __GST_PARSE_TRACE
+#endif
+
+#ifdef __GST_PARSE_TRACE
+gchar *__gst_parse_strdup (gchar *org);
+void __gst_parse_strfree (gchar *str);
+link_t *__gst_parse_link_new ();
+void __gst_parse_link_free (link_t *data);
+chain_t *__gst_parse_chain_new ();
+void __gst_parse_chain_free (chain_t *data);
+# define gst_parse_strdup __gst_parse_strdup
+# define gst_parse_strfree __gst_parse_strfree
+# define gst_parse_link_new __gst_parse_link_new
+# define gst_parse_link_free __gst_parse_link_free
+# define gst_parse_chain_new __gst_parse_chain_new
+# define gst_parse_chain_free __gst_parse_chain_free
+#else /* __GST_PARSE_TRACE */
+# define gst_parse_strdup g_strdup
+# define gst_parse_strfree g_free
+# define gst_parse_link_new() g_new0 (link_t, 1)
+# define gst_parse_link_free g_free
+# define gst_parse_chain_new() g_new0 (chain_t, 1)
+# define gst_parse_chain_free g_free
+#endif /* __GST_PARSE_TRACE */
+
+static inline void
+gst_parse_unescape (gchar *str)
+{
+ gchar *walk;
+ gboolean in_quotes;
+
+ g_return_if_fail (str != NULL);
+
+ walk = str;
+ in_quotes = FALSE;
+
+ while (*walk) {
+ if (*walk == '\\' && !in_quotes) {
+ walk++;
+ /* make sure we don't read beyond the end of the string */
+ if (*walk == '\0')
+ break;
+ } else if (*walk == '"' && (!in_quotes || (in_quotes
+ && (*(walk - 1) != '\\')))) {
+ /* don't unescape inside quotes and don't switch
+ * state with escaped quoted inside quotes */
+ in_quotes = !in_quotes;
+ }
+ *str = *walk;
+ str++;
+ walk++;
+ }
+ *str = '\0';
+}
+
+GstElement *_gst_parse_launch (const gchar *, GError **,
+ GstParseContext *, GstParseFlags);
+
+#endif /* __GST_PARSE_TYPES_H__ */