summaryrefslogtreecommitdiffstats
path: root/gst/debugutils
diff options
context:
space:
mode:
authorStefan Kost <ensonic@users.sf.net>2009-04-15 16:03:27 +0300
committerStefan Kost <ensonic@users.sf.net>2009-04-15 16:13:34 +0300
commit7be792fa138dcb4634eb893074042b7440e56594 (patch)
tree4596cdc55fb0deae89c906515bcb72c3af662a74 /gst/debugutils
parent99fcc86ee441d34752b39e382014d5a8a5cb04b0 (diff)
debug: rename debug to debugutils to avoid clash with --disable-debug. Fixes #562168
Diffstat (limited to 'gst/debugutils')
-rw-r--r--gst/debugutils/Makefile.am49
-rw-r--r--gst/debugutils/breakmydata.c297
-rw-r--r--gst/debugutils/debug.vcproj166
-rw-r--r--gst/debugutils/efence.c537
-rw-r--r--gst/debugutils/efence.h50
-rw-r--r--gst/debugutils/efence.vcproj148
-rw-r--r--gst/debugutils/gstdebug.c63
-rw-r--r--gst/debugutils/gstnavigationtest.c357
-rw-r--r--gst/debugutils/gstnavigationtest.h68
-rw-r--r--gst/debugutils/gstnavseek.c340
-rw-r--r--gst/debugutils/gstnavseek.h60
-rw-r--r--gst/debugutils/gstpushfilesrc.c195
-rw-r--r--gst/debugutils/gstpushfilesrc.h56
-rw-r--r--gst/debugutils/gsttaginject.c204
-rw-r--r--gst/debugutils/gsttaginject.h66
-rw-r--r--gst/debugutils/navigationtest.vcproj148
-rw-r--r--gst/debugutils/negotiation.c287
-rw-r--r--gst/debugutils/progressreport.c478
-rw-r--r--gst/debugutils/progressreport.h66
-rw-r--r--gst/debugutils/rndbuffersize.c371
-rw-r--r--gst/debugutils/testplugin.c318
-rw-r--r--gst/debugutils/tests.c568
-rw-r--r--gst/debugutils/tests.h43
23 files changed, 4935 insertions, 0 deletions
diff --git a/gst/debugutils/Makefile.am b/gst/debugutils/Makefile.am
new file mode 100644
index 00000000..8c53cdf3
--- /dev/null
+++ b/gst/debugutils/Makefile.am
@@ -0,0 +1,49 @@
+if GST_HAVE_MMAP
+EFENCE_PLUGIN=libgstefence.la
+else
+EFENCE_PLUGIN=
+endif
+
+plugin_LTLIBRARIES = $(EFENCE_PLUGIN) libgstdebug.la libgstnavigationtest.la
+
+noinst_HEADERS = \
+ efence.h \
+ gstnavigationtest.h \
+ gstnavseek.h \
+ gstpushfilesrc.h \
+ gsttaginject.h \
+ progressreport.h \
+ tests.h
+
+libgstefence_la_SOURCES = efence.c
+libgstefence_la_CFLAGS = $(GST_CFLAGS)
+libgstefence_la_LIBADD = $(GST_LIBS)
+libgstefence_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+libgstefence_la_LIBTOOLFLAGS = --tag=disable-static
+
+libgstnavigationtest_la_SOURCES = gstnavigationtest.c
+libgstnavigationtest_la_CFLAGS = $(GST_CFLAGS) $(GST_BASE_CFLAGS) \
+ $(GST_PLUGINS_BASE_CFLAGS)
+libgstnavigationtest_la_LIBADD = $(GST_LIBS) $(GST_BASE_LIBS) \
+ $(GST_PLUGINS_BASE_LIBS) -lgstvideo-@GST_MAJORMINOR@ $(LIBM)
+libgstnavigationtest_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+libgstnavigationtest_la_LIBTOOLFLAGS = --tag=disable-static
+
+libgstdebug_la_SOURCES = \
+ gstdebug.c \
+ breakmydata.c \
+ gstnavseek.c \
+ gstpushfilesrc.c \
+ gsttaginject.c \
+ rndbuffersize.c \
+ progressreport.c \
+ tests.c \
+ testplugin.c
+
+# negotiation.c
+
+libgstdebug_la_CFLAGS = $(GST_CFLAGS) $(GST_BASE_CFLAGS)
+libgstdebug_la_LIBADD = $(GST_LIBS) $(GST_BASE_LIBS)
+libgstdebug_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+libgstdebug_la_LIBTOOLFLAGS = --tag=disable-static
+
diff --git a/gst/debugutils/breakmydata.c b/gst/debugutils/breakmydata.c
new file mode 100644
index 00000000..63e30b6c
--- /dev/null
+++ b/gst/debugutils/breakmydata.c
@@ -0,0 +1,297 @@
+/* GStreamer
+ * Copyright (C) 2004 Benjamin Otte <otte@gnome.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:element-breakmydata
+ *
+ * This element modifies the contents of the buffer it is passed randomly
+ * according to the parameters set.
+ * It otherwise acts as an identity.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <gst/gst.h>
+#include <gst/base/gstbasetransform.h>
+
+GST_DEBUG_CATEGORY_STATIC (gst_break_my_data_debug);
+#define GST_CAT_DEFAULT gst_break_my_data_debug
+
+#define GST_TYPE_BREAK_MY_DATA \
+ (gst_break_my_data_get_type())
+#define GST_BREAK_MY_DATA(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_BREAK_MY_DATA,GstBreakMyData))
+#define GST_BREAK_MY_DATA_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_BREAK_MY_DATA,GstBreakMyDataClass))
+#define GST_IS_BREAK_MY_DATA(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_BREAK_MY_DATA))
+#define GST_IS_BREAK_MY_DATA_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_BREAK_MY_DATA))
+
+enum
+{
+ ARG_0,
+ ARG_SEED,
+ ARG_SET_TO,
+ ARG_SKIP,
+ ARG_PROBABILITY
+};
+
+typedef struct _GstBreakMyData GstBreakMyData;
+typedef struct _GstBreakMyDataClass GstBreakMyDataClass;
+
+struct _GstBreakMyData
+{
+ GstBaseTransform basetransform;
+
+ GRand *rand;
+ guint skipped;
+
+ guint32 seed;
+ gint set;
+ guint skip;
+ gdouble probability;
+};
+
+struct _GstBreakMyDataClass
+{
+ GstBaseTransformClass parent_class;
+};
+
+static void gst_break_my_data_set_property (GObject * object,
+ guint prop_id, const GValue * value, GParamSpec * pspec);
+static void gst_break_my_data_get_property (GObject * object,
+ guint prop_id, GValue * value, GParamSpec * pspec);
+
+static GstFlowReturn gst_break_my_data_transform_ip (GstBaseTransform * trans,
+ GstBuffer * buf);
+static gboolean gst_break_my_data_stop (GstBaseTransform * trans);
+static gboolean gst_break_my_data_start (GstBaseTransform * trans);
+
+static const GstElementDetails details = GST_ELEMENT_DETAILS ("Break my data",
+ "Testing",
+ "randomly change data in the stream",
+ "Benjamin Otte <otte@gnome>");
+
+GstStaticPadTemplate bmd_src_template = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS_ANY);
+
+GstStaticPadTemplate bmd_sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS_ANY);
+
+
+#define DEBUG_INIT(bla) \
+ GST_DEBUG_CATEGORY_INIT (gst_break_my_data_debug, "breakmydata", 0, \
+ "debugging category for breakmydata element");
+
+GST_BOILERPLATE_FULL (GstBreakMyData, gst_break_my_data, GstBaseTransform,
+ GST_TYPE_BASE_TRANSFORM, DEBUG_INIT);
+
+
+static void
+gst_break_my_data_base_init (gpointer g_class)
+{
+ GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
+
+ gst_element_class_add_pad_template (gstelement_class,
+ gst_static_pad_template_get (&bmd_sink_template));
+ gst_element_class_add_pad_template (gstelement_class,
+ gst_static_pad_template_get (&bmd_src_template));
+
+ gst_element_class_set_details (gstelement_class, &details);
+}
+
+static void
+gst_break_my_data_class_init (GstBreakMyDataClass * klass)
+{
+ GstBaseTransformClass *gstbasetrans_class;
+ GObjectClass *gobject_class;
+
+ gobject_class = G_OBJECT_CLASS (klass);
+ gstbasetrans_class = GST_BASE_TRANSFORM_CLASS (klass);
+
+ gobject_class->set_property =
+ GST_DEBUG_FUNCPTR (gst_break_my_data_set_property);
+ gobject_class->get_property =
+ GST_DEBUG_FUNCPTR (gst_break_my_data_get_property);
+
+ g_object_class_install_property (gobject_class, ARG_SEED,
+ g_param_spec_uint ("seed", "seed",
+ "seed for randomness (initialized when going from READY to PAUSED)",
+ 0, G_MAXUINT32, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ g_object_class_install_property (gobject_class, ARG_SET_TO,
+ g_param_spec_int ("set-to", "set-to",
+ "set changed bytes to this value (-1 means random value",
+ -1, G_MAXUINT8, -1, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ g_object_class_install_property (gobject_class, ARG_SKIP,
+ g_param_spec_uint ("skip", "skip",
+ "amount of bytes skipped at the beginning of stream",
+ 0, G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ g_object_class_install_property (gobject_class, ARG_PROBABILITY,
+ g_param_spec_double ("probability", "probability",
+ "probability for each byte in the buffer to be changed", 0.0, 1.0,
+ 0.0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ gstbasetrans_class->transform_ip =
+ GST_DEBUG_FUNCPTR (gst_break_my_data_transform_ip);
+ gstbasetrans_class->start = GST_DEBUG_FUNCPTR (gst_break_my_data_start);
+ gstbasetrans_class->stop = GST_DEBUG_FUNCPTR (gst_break_my_data_stop);
+}
+
+static void
+gst_break_my_data_init (GstBreakMyData * bmd, GstBreakMyDataClass * g_class)
+{
+ gst_base_transform_set_in_place (GST_BASE_TRANSFORM (bmd), TRUE);
+ gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (bmd), TRUE);
+}
+
+static void
+gst_break_my_data_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstBreakMyData *bmd = GST_BREAK_MY_DATA (object);
+
+ GST_OBJECT_LOCK (bmd);
+
+ switch (prop_id) {
+ case ARG_SEED:
+ bmd->seed = g_value_get_uint (value);
+ break;
+ case ARG_SET_TO:
+ bmd->set = g_value_get_int (value);
+ break;
+ case ARG_SKIP:
+ bmd->skip = g_value_get_uint (value);
+ break;
+ case ARG_PROBABILITY:
+ bmd->probability = g_value_get_double (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+
+ GST_OBJECT_UNLOCK (bmd);
+}
+
+static void
+gst_break_my_data_get_property (GObject * object, guint prop_id, GValue * value,
+ GParamSpec * pspec)
+{
+ GstBreakMyData *bmd = GST_BREAK_MY_DATA (object);
+
+ GST_OBJECT_LOCK (bmd);
+
+ switch (prop_id) {
+ case ARG_SEED:
+ g_value_set_uint (value, bmd->seed);
+ break;
+ case ARG_SET_TO:
+ g_value_set_int (value, bmd->set);
+ break;
+ case ARG_SKIP:
+ g_value_set_uint (value, bmd->skip);
+ break;
+ case ARG_PROBABILITY:
+ g_value_set_double (value, bmd->probability);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+
+ GST_OBJECT_UNLOCK (bmd);
+}
+
+static GstFlowReturn
+gst_break_my_data_transform_ip (GstBaseTransform * trans, GstBuffer * buf)
+{
+ GstBreakMyData *bmd = GST_BREAK_MY_DATA (trans);
+ guint i, size;
+
+ g_return_val_if_fail (gst_buffer_is_writable (buf), GST_FLOW_ERROR);
+
+ GST_OBJECT_LOCK (bmd);
+
+ if (bmd->skipped < bmd->skip) {
+ i = bmd->skip - bmd->skipped;
+ } else {
+ i = 0;
+ }
+
+ size = GST_BUFFER_SIZE (buf);
+
+ GST_LOG_OBJECT (bmd,
+ "got buffer %p (size %u, timestamp %" G_GUINT64_FORMAT ", offset %"
+ G_GUINT64_FORMAT "", buf, size, GST_BUFFER_TIMESTAMP (buf),
+ GST_BUFFER_OFFSET (buf));
+
+ for (; i < size; i++) {
+ if (g_rand_double_range (bmd->rand, 0, 1.0) <= bmd->probability) {
+ guint8 new;
+
+ if (bmd->set < 0) {
+ new = g_rand_int_range (bmd->rand, 0, 256);
+ } else {
+ new = bmd->set;
+ }
+ GST_INFO_OBJECT (bmd, "changing byte %u from 0x%02X to 0x%02X", i,
+ (guint) GST_READ_UINT8 (GST_BUFFER_DATA (buf) + i),
+ (guint) ((guint8) new));
+ GST_BUFFER_DATA (buf)[i] = new;
+ }
+ }
+ /* don't overflow */
+ bmd->skipped += MIN (G_MAXUINT - bmd->skipped, GST_BUFFER_SIZE (buf));
+
+ GST_OBJECT_UNLOCK (bmd);
+
+ return GST_FLOW_OK;
+}
+
+static gboolean
+gst_break_my_data_start (GstBaseTransform * trans)
+{
+ GstBreakMyData *bmd = GST_BREAK_MY_DATA (trans);
+
+ GST_OBJECT_LOCK (bmd);
+ bmd->rand = g_rand_new_with_seed (bmd->seed);
+ bmd->skipped = 0;
+ GST_OBJECT_UNLOCK (bmd);
+
+ return TRUE;
+}
+
+static gboolean
+gst_break_my_data_stop (GstBaseTransform * trans)
+{
+ GstBreakMyData *bmd = GST_BREAK_MY_DATA (trans);
+
+ GST_OBJECT_LOCK (bmd);
+ g_rand_free (bmd->rand);
+ bmd->rand = NULL;
+ GST_OBJECT_UNLOCK (bmd);
+
+ return TRUE;
+}
diff --git a/gst/debugutils/debug.vcproj b/gst/debugutils/debug.vcproj
new file mode 100644
index 00000000..3093ae7b
--- /dev/null
+++ b/gst/debugutils/debug.vcproj
@@ -0,0 +1,166 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="7.10"
+ Name="debug"
+ ProjectGUID="{979C216F-0ACF-4956-AE00-055A42D678AF}"
+ Keyword="Win32Proj">
+ <Platforms>
+ <Platform
+ Name="Win32"/>
+ </Platforms>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="../../win32/Debug"
+ IntermediateDirectory="../../win32/Debug"
+ ConfigurationType="2"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="../../../gstreamer/win32;../../../gstreamer;../../../gstreamer/libs;../../../glib;../../../glib/glib;../../../glib/gmodule;&quot;../../gst-libs&quot;;../../../popt/include;../../../libxml2/include/libxml2"
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;debug_EXPORTS;HAVE_CONFIG_H;_USE_MATH_DEFINES"
+ MinimalRebuild="TRUE"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="TRUE"
+ DebugInformationFormat="4"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="glib-2.0.lib gmodule-2.0.lib gthread-2.0.lib gobject-2.0.lib libgstreamer.lib gstbytestream.lib iconv.lib intl.lib"
+ OutputFile="$(OutDir)/gstdebug.dll"
+ LinkIncremental="2"
+ AdditionalLibraryDirectories="../../../gstreamer/win32/Debug;../../../glib/glib;../../../glib/gmodule;../../../glib/gthread;../../../glib/gobject;../../../gettext/lib;../../../libiconv/lib"
+ ModuleDefinitionFile=""
+ GenerateDebugInformation="TRUE"
+ ProgramDatabaseFile="$(OutDir)/debug.pdb"
+ SubSystem="2"
+ OptimizeReferences="2"
+ ImportLibrary="$(OutDir)/gstdebug.lib"
+ TargetMachine="1"/>
+ <Tool
+ Name="VCMIDLTool"/>
+ <Tool
+ Name="VCPostBuildEventTool"
+ CommandLine="copy /Y $(TargetPath) c:\gstreamer\plugins"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="../../win32/Release"
+ IntermediateDirectory="../../win32/Release"
+ ConfigurationType="2"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="../../../gstreamer/win32;../../../gstreamer;../../../gstreamer/libs;../../../glib;../../../glib/glib;../../../glib/gmodule;&quot;../../gst-libs&quot;;../../../popt/include;../../../libxml2/include/libxml2"
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;debug_EXPORTS;HAVE_CONFIG_H;_USE_MATH_DEFINES"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="TRUE"
+ DebugInformationFormat="3"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="glib-2.0.lib gmodule-2.0.lib gthread-2.0.lib gobject-2.0.lib libgstreamer.lib gstbytestream.lib iconv.lib intl.lib"
+ OutputFile="$(OutDir)/gstdebug.dll"
+ LinkIncremental="1"
+ AdditionalLibraryDirectories="../../../gstreamer/win32/Release;../../../glib/glib;../../../glib/gmodule;../../../glib/gthread;../../../glib/gobject;../../../gettext/lib;../../../libiconv/lib"
+ ModuleDefinitionFile=""
+ GenerateDebugInformation="TRUE"
+ SubSystem="2"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ ImportLibrary="$(OutDir)/gstdebug.lib"
+ TargetMachine="1"/>
+ <Tool
+ Name="VCMIDLTool"/>
+ <Tool
+ Name="VCPostBuildEventTool"
+ CommandLine="copy /Y $(TargetPath) c:\gstreamer\plugins"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}">
+ <File
+ RelativePath=".\gstdebug.c">
+ </File>
+ <File
+ RelativePath=".\breakmydata.c">
+ </File>
+ <File
+ RelativePath=".\negotiation.c">
+ </File>
+ <File
+ RelativePath=".\gstnavseek.c">
+ </File>
+ <File
+ RelativePath=".\progressreport.c">
+ </File>
+ <File
+ RelativePath=".\tests.c">
+ </File>
+ <File
+ RelativePath=".\testplugin.c">
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}">
+ <File
+ RelativePath=".\tests.h">
+ </File>
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}">
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/gst/debugutils/efence.c b/gst/debugutils/efence.c
new file mode 100644
index 00000000..a46bf1ac
--- /dev/null
+++ b/gst/debugutils/efence.c
@@ -0,0 +1,537 @@
+/*
+ * GStreamer
+ * Copyright (C) 1999-2001 Erik Walthinsen <omega@cse.ogi.edu>
+ * Copyright (C) 2002 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <gst/gst.h>
+
+#include <string.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+#include "efence.h"
+
+#ifndef MAP_ANONYMOUS
+#ifdef MAP_ANON
+#define MAP_ANONYMOUS MAP_ANON
+#else
+/* assume we don't need it */
+#define MAP_ANONYMOUS 0
+#endif
+#endif
+
+GST_DEBUG_CATEGORY_STATIC (gst_efence_debug);
+#define GST_CAT_DEFAULT gst_efence_debug
+
+static const GstElementDetails plugin_details =
+GST_ELEMENT_DETAILS ("Electric Fence",
+ "Testing",
+ "This element converts a stream of normal GStreamer buffers into a "
+ "stream of buffers that are allocated in such a way that out-of-bounds "
+ "access to data in the buffer is more likely to cause segmentation "
+ "faults. This allocation method is very similar to the debugging tool "
+ "\"Electric Fence\".",
+ "David A. Schleef <ds@schleef.org>");
+
+/* Filter signals and args */
+enum
+{
+ /* FILL ME */
+ LAST_SIGNAL
+};
+
+enum
+{
+ ARG_0,
+ ARG_FENCE_TOP
+};
+
+static GstStaticPadTemplate gst_efence_sink_factory =
+GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS_ANY);
+
+static GstStaticPadTemplate gst_efence_src_factory =
+GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS_ANY);
+
+static void gst_efence_base_init (gpointer g_class);
+static void gst_efence_class_init (GstEFenceClass * klass);
+static void gst_efence_init (GstEFence * filter);
+
+static void gst_efence_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+static void gst_efence_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
+
+static GstFlowReturn gst_efence_chain (GstPad * pad, GstBuffer * buf);
+static GstFlowReturn gst_efence_getrange (GstPad * pad, guint64 offset,
+ guint length, GstBuffer ** buffer);
+static gboolean gst_efence_checkgetrange (GstPad * pad);
+static gboolean gst_efence_activate_src_pull (GstPad * pad, gboolean active);
+
+static GstElementClass *parent_class = NULL;
+
+typedef struct _GstFencedBuffer GstFencedBuffer;
+struct _GstFencedBuffer
+{
+ GstBuffer buffer;
+ void *region;
+ unsigned int length;
+};
+
+GType gst_fenced_buffer_get_type (void);
+static void gst_fenced_buffer_finalize (GstFencedBuffer * buf);
+static GstFencedBuffer *gst_fenced_buffer_copy (const GstBuffer * buffer);
+static void *gst_fenced_buffer_alloc (GstBuffer * buffer, unsigned int length,
+ gboolean fence_top);
+static GstFlowReturn gst_efence_buffer_alloc (GstPad * pad, guint64 offset,
+ guint size, GstCaps * caps, GstBuffer ** buf);
+
+#define GST_TYPE_FENCED_BUFFER (gst_fenced_buffer_get_type())
+
+#define GST_IS_FENCED_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_FENCED_BUFFER))
+#define GST_FENCED_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_FENCED_BUFFER, GstFencedBuffer))
+
+GType
+gst_gst_efence_get_type (void)
+{
+ static GType plugin_type = 0;
+
+ if (!plugin_type) {
+ static const GTypeInfo plugin_info = {
+ sizeof (GstEFenceClass),
+ gst_efence_base_init,
+ NULL,
+ (GClassInitFunc) gst_efence_class_init,
+ NULL,
+ NULL,
+ sizeof (GstEFence),
+ 0,
+ (GInstanceInitFunc) gst_efence_init,
+ };
+
+ plugin_type = g_type_register_static (GST_TYPE_ELEMENT,
+ "GstEFence", &plugin_info, 0);
+ }
+ return plugin_type;
+}
+
+static void
+gst_efence_base_init (gpointer g_class)
+{
+ GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&gst_efence_sink_factory));
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&gst_efence_src_factory));
+ gst_element_class_set_details (element_class, &plugin_details);
+}
+
+/* initialize the plugin's class */
+static void
+gst_efence_class_init (GstEFenceClass * klass)
+{
+ GObjectClass *gobject_class;
+ GstElementClass *gstelement_class;
+
+ gobject_class = (GObjectClass *) klass;
+ gstelement_class = (GstElementClass *) klass;
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ gobject_class->set_property = gst_efence_set_property;
+ gobject_class->get_property = gst_efence_get_property;
+
+ g_object_class_install_property (gobject_class, ARG_FENCE_TOP,
+ g_param_spec_boolean ("fence_top", "Fence Top",
+ "Align buffers with top of fenced region", TRUE, G_PARAM_READWRITE));
+}
+
+/* initialize the new element
+ * instantiate pads and add them to element
+ * set functions
+ * initialize structure
+ */
+static void
+gst_efence_init (GstEFence * filter)
+{
+ filter->sinkpad =
+ gst_pad_new_from_static_template (&gst_efence_sink_factory, "sink");
+ gst_pad_set_getcaps_function (filter->sinkpad,
+ GST_DEBUG_FUNCPTR (gst_pad_proxy_getcaps));
+ gst_pad_set_setcaps_function (filter->sinkpad,
+ GST_DEBUG_FUNCPTR (gst_pad_proxy_setcaps));
+ gst_pad_set_chain_function (filter->sinkpad,
+ GST_DEBUG_FUNCPTR (gst_efence_chain));
+ gst_pad_set_bufferalloc_function (filter->sinkpad,
+ GST_DEBUG_FUNCPTR (gst_efence_buffer_alloc));
+ gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad);
+
+ filter->srcpad =
+ gst_pad_new_from_static_template (&gst_efence_src_factory, "src");
+ gst_pad_set_getcaps_function (filter->srcpad,
+ GST_DEBUG_FUNCPTR (gst_pad_proxy_getcaps));
+ gst_pad_set_setcaps_function (filter->srcpad,
+ GST_DEBUG_FUNCPTR (gst_pad_proxy_setcaps));
+ gst_pad_set_checkgetrange_function (filter->srcpad,
+ GST_DEBUG_FUNCPTR (gst_efence_checkgetrange));
+ gst_pad_set_getrange_function (filter->srcpad,
+ GST_DEBUG_FUNCPTR (gst_efence_getrange));
+ gst_pad_set_activatepull_function (filter->srcpad,
+ GST_DEBUG_FUNCPTR (gst_efence_activate_src_pull));
+
+ gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);
+
+ filter->fence_top = TRUE;
+}
+
+/* chain function
+ * this function does the actual processing
+ */
+
+static GstFlowReturn
+gst_efence_chain (GstPad * pad, GstBuffer * buffer)
+{
+ GstEFence *efence;
+ GstBuffer *copy;
+
+ efence = GST_EFENCE (GST_OBJECT_PARENT (pad));
+ g_return_val_if_fail (GST_IS_EFENCE (efence), GST_FLOW_ERROR);
+
+ if (GST_IS_FENCED_BUFFER (buffer)) {
+ GST_DEBUG_OBJECT (efence, "Passing on existing fenced buffer with caps %"
+ GST_PTR_FORMAT, GST_BUFFER_CAPS (buffer));
+ return gst_pad_push (efence->srcpad, buffer);
+ }
+
+ copy = (GstBuffer *) gst_fenced_buffer_copy (buffer);
+
+ GST_DEBUG_OBJECT (efence, "Pushing newly fenced buffer with caps %"
+ GST_PTR_FORMAT ", data=%p, size=%u", GST_BUFFER_CAPS (copy),
+ GST_BUFFER_DATA (copy), GST_BUFFER_SIZE (copy));
+
+ gst_buffer_unref (buffer);
+
+ return gst_pad_push (efence->srcpad, copy);
+}
+
+static GstFlowReturn
+gst_efence_getrange (GstPad * pad, guint64 offset,
+ guint length, GstBuffer ** buffer)
+{
+ GstEFence *efence;
+ GstFlowReturn ret;
+ GstBuffer *ownbuf;
+ GstPad *peer;
+
+ efence = GST_EFENCE (GST_OBJECT_PARENT (pad));
+
+ peer = gst_pad_get_peer (efence->sinkpad);
+ if (!peer)
+ return GST_FLOW_NOT_LINKED;
+
+ if ((ret = gst_pad_get_range (peer, offset, length, buffer)) != GST_FLOW_OK)
+ goto beach;
+
+ ownbuf = (GstBuffer *) gst_fenced_buffer_copy (*buffer);
+ gst_buffer_unref ((GstBuffer *) * buffer);
+ *buffer = ownbuf;
+
+beach:
+ gst_object_unref (peer);
+ return ret;
+}
+
+static gboolean
+gst_efence_checkgetrange (GstPad * pad)
+{
+ GstEFence *efence = GST_EFENCE (GST_OBJECT_PARENT (pad));
+
+ return gst_pad_check_pull_range (efence->sinkpad);
+}
+
+static gboolean
+gst_efence_activate_src_pull (GstPad * pad, gboolean active)
+{
+ GstEFence *efence = GST_EFENCE (GST_OBJECT_PARENT (pad));
+
+ return gst_pad_activate_pull (efence->sinkpad, active);
+}
+
+static GstFlowReturn
+gst_efence_buffer_alloc (GstPad * pad, guint64 offset,
+ guint size, GstCaps * caps, GstBuffer ** buf)
+{
+ GstBuffer *buffer;
+ GstEFence *efence;
+
+ g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR);
+ g_return_val_if_fail (GST_IS_PAD (pad), GST_FLOW_ERROR);
+
+ efence = GST_EFENCE (GST_OBJECT_PARENT (pad));
+
+ buffer = (GstBuffer *) gst_mini_object_new (GST_TYPE_FENCED_BUFFER);
+
+ GST_BUFFER_DATA (buffer) = gst_fenced_buffer_alloc (buffer, size,
+ efence->fence_top);
+ GST_BUFFER_SIZE (buffer) = size;
+ GST_BUFFER_OFFSET (buffer) = offset;
+
+ if (caps)
+ gst_buffer_set_caps (buffer, caps);
+
+ *buf = buffer;
+
+ GST_DEBUG_OBJECT (efence, "Allocated buffer of size %u, caps: %"
+ GST_PTR_FORMAT, GST_BUFFER_SIZE (buffer), GST_BUFFER_CAPS (buffer));
+
+ return GST_FLOW_OK;
+}
+
+static void
+gst_efence_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstEFence *filter;
+
+ g_return_if_fail (GST_IS_EFENCE (object));
+ filter = GST_EFENCE (object);
+
+ switch (prop_id) {
+ case ARG_FENCE_TOP:
+ filter->fence_top = g_value_get_boolean (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_efence_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GstEFence *filter;
+
+ g_return_if_fail (GST_IS_EFENCE (object));
+ filter = GST_EFENCE (object);
+
+ switch (prop_id) {
+ case ARG_FENCE_TOP:
+ g_value_set_boolean (value, filter->fence_top);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+/* entry point to initialize the plug-in
+ * initialize the plug-in itself
+ * register the element factories and pad templates
+ * register the features
+ */
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+ if (!gst_element_register (plugin, "efence", GST_RANK_NONE, GST_TYPE_EFENCE))
+ return FALSE;
+
+ GST_DEBUG_CATEGORY_INIT (gst_efence_debug, "efence", 0,
+ "Debug output from the efence element");
+
+ /* plugin initialisation succeeded */
+ return TRUE;
+}
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+ GST_VERSION_MINOR,
+ "efence",
+ "This element converts a stream of normal GStreamer buffers into a "
+ "stream of buffers that are allocated in such a way that out-of-bounds "
+ "access to data in the buffer is more likely to cause segmentation "
+ "faults. This allocation method is very similar to the debugging tool "
+ "\"Electric Fence\".",
+ plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);
+
+
+static GstBufferClass *fenced_buffer_parent_class = NULL;
+
+static void
+gst_fenced_buffer_finalize (GstFencedBuffer * buffer)
+{
+ GstFencedBuffer *fenced_buffer;
+
+ GST_DEBUG ("free buffer=%p", buffer);
+
+ fenced_buffer = GST_FENCED_BUFFER (buffer);
+
+ /* free our data */
+ if (GST_BUFFER_DATA (buffer)) {
+ GST_DEBUG ("free region %p %d", fenced_buffer->region,
+ fenced_buffer->length);
+ munmap (fenced_buffer->region, fenced_buffer->length);
+ }
+
+ GST_MINI_OBJECT_CLASS (fenced_buffer_parent_class)->finalize (GST_MINI_OBJECT
+ (buffer));
+}
+
+static GstFencedBuffer *
+gst_fenced_buffer_copy (const GstBuffer * buffer)
+{
+ GstBuffer *copy;
+ void *ptr;
+ guint mask;
+
+ g_return_val_if_fail (buffer != NULL, NULL);
+
+ /* create a fresh new buffer */
+ copy = (GstBuffer *) gst_mini_object_new (GST_TYPE_FENCED_BUFFER);
+
+ /* we simply copy everything from our parent */
+ ptr = gst_fenced_buffer_alloc (GST_BUFFER (copy),
+ GST_BUFFER_SIZE (buffer), TRUE);
+ memcpy (ptr, GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
+
+ /* copy relevant flags */
+ mask = GST_BUFFER_FLAG_PREROLL | GST_BUFFER_FLAG_IN_CAPS |
+ GST_BUFFER_FLAG_DELTA_UNIT;
+ GST_MINI_OBJECT (copy)->flags |= GST_MINI_OBJECT (buffer)->flags & mask;
+
+ GST_BUFFER_DATA (copy) = ptr;
+ GST_BUFFER_SIZE (copy) = GST_BUFFER_SIZE (buffer);
+ GST_BUFFER_TIMESTAMP (copy) = GST_BUFFER_TIMESTAMP (buffer);
+ GST_BUFFER_DURATION (copy) = GST_BUFFER_DURATION (buffer);
+ GST_BUFFER_OFFSET (copy) = GST_BUFFER_OFFSET (buffer);
+ GST_BUFFER_OFFSET_END (copy) = GST_BUFFER_OFFSET_END (buffer);
+
+ if (GST_BUFFER_CAPS (buffer))
+ GST_BUFFER_CAPS (copy) = gst_caps_ref (GST_BUFFER_CAPS (buffer));
+ else
+ GST_BUFFER_CAPS (copy) = NULL;
+
+ GST_DEBUG ("Copied buffer %p with ts %" GST_TIME_FORMAT
+ ", caps: %" GST_PTR_FORMAT, buffer,
+ GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (copy)), GST_BUFFER_CAPS (copy));
+
+ return GST_FENCED_BUFFER (copy);
+}
+
+void *
+gst_fenced_buffer_alloc (GstBuffer * buffer, unsigned int length,
+ gboolean fence_top)
+{
+ int alloc_size;
+ void *region;
+ GstFencedBuffer *fenced_buffer = (GstFencedBuffer *) buffer;
+ int page_size;
+
+ GST_DEBUG ("buffer=%p length=%d fence_top=%d", buffer, length, fence_top);
+
+ if (length == 0)
+ return NULL;
+
+#ifdef _SC_PAGESIZE
+ page_size = sysconf (_SC_PAGESIZE);
+#else
+ page_size = getpagesize ();
+#endif
+
+ /* Allocate a complete page, and one on either side */
+ alloc_size = ((length - 1) & ~(page_size - 1)) + page_size;
+ alloc_size += 2 * page_size;
+
+ region = mmap (NULL, alloc_size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (region == MAP_FAILED) {
+ g_warning ("mmap failed");
+ return NULL;
+ }
+#if 0
+ munmap (region, page_size);
+ munmap (region + alloc_size - page_size, page_size);
+
+ fenced_buffer->region = region + page_size;
+ fenced_buffer->length = alloc_size - page_size;
+#else
+ mprotect (region, page_size, PROT_NONE);
+ mprotect ((char *) region + alloc_size - page_size, page_size, PROT_NONE);
+
+ fenced_buffer->region = region;
+ fenced_buffer->length = alloc_size;
+#endif
+
+ GST_DEBUG ("new region %p %d", fenced_buffer->region, fenced_buffer->length);
+
+ if (fence_top) {
+ int offset;
+
+ /* Align to top of region, but force alignment to 4 bytes */
+ offset = alloc_size - page_size - length;
+ offset &= ~0x3;
+ return (void *) ((char *) region + offset);
+ } else {
+ return (void *) ((char *) region + page_size);
+ }
+}
+
+static void
+gst_fenced_buffer_class_init (gpointer g_class, gpointer class_data)
+{
+ GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS (g_class);
+
+ fenced_buffer_parent_class = g_type_class_peek_parent (g_class);
+
+ mini_object_class->finalize =
+ (GstMiniObjectFinalizeFunction) gst_fenced_buffer_finalize;
+ mini_object_class->copy = (GstMiniObjectCopyFunction) gst_fenced_buffer_copy;
+}
+
+GType
+gst_fenced_buffer_get_type (void)
+{
+ static GType fenced_buf_type = 0;
+
+ if (G_UNLIKELY (!fenced_buf_type)) {
+ static const GTypeInfo fenced_buf_info = {
+ sizeof (GstBufferClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) gst_fenced_buffer_class_init,
+ NULL,
+ NULL,
+ sizeof (GstFencedBuffer),
+ 0,
+ NULL,
+ };
+
+ fenced_buf_type = g_type_register_static (GST_TYPE_BUFFER,
+ "GstFencedBuffer", &fenced_buf_info, 0);
+ }
+ return fenced_buf_type;
+}
diff --git a/gst/debugutils/efence.h b/gst/debugutils/efence.h
new file mode 100644
index 00000000..7c4acb5d
--- /dev/null
+++ b/gst/debugutils/efence.h
@@ -0,0 +1,50 @@
+/*
+ * efence.h
+ */
+
+#ifndef __GST_EFENCE_H__
+#define __GST_EFENCE_H__
+
+#include <gst/gst.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+/* #define's don't like whitespacey bits */
+#define GST_TYPE_EFENCE \
+ (gst_gst_efence_get_type())
+#define GST_EFENCE(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_EFENCE,GstEFence))
+#define GST_EFENCE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_EFENCE,GstEFenceClass))
+#define GST_IS_EFENCE(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_EFENCE))
+#define GST_IS_EFENCE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_EFENCE))
+
+ typedef struct _GstEFence GstEFence;
+ typedef struct _GstEFenceClass GstEFenceClass;
+
+ struct _GstEFence
+ {
+ GstElement element;
+
+ GstPad *sinkpad, *srcpad;
+
+ gboolean fence_top;
+ };
+
+ struct _GstEFenceClass
+ {
+ GstElementClass parent_class;
+ };
+
+ GType gst_gst_efence_get_type (void);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __GST_EFENCE_H__ */
diff --git a/gst/debugutils/efence.vcproj b/gst/debugutils/efence.vcproj
new file mode 100644
index 00000000..c01fcd28
--- /dev/null
+++ b/gst/debugutils/efence.vcproj
@@ -0,0 +1,148 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="7.10"
+ Name="efence"
+ ProjectGUID="{979C216F-0ACF-4956-AE00-055A42D678AE}"
+ Keyword="Win32Proj">
+ <Platforms>
+ <Platform
+ Name="Win32"/>
+ </Platforms>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="../../win32/Debug"
+ IntermediateDirectory="../../win32/Debug"
+ ConfigurationType="2"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="../../../gstreamer/win32;../../../gstreamer;../../../gstreamer/libs;../../../glib;../../../glib/glib;../../../glib/gmodule;&quot;../../gst-libs&quot;;../../../popt/include;../../../libxml2/include/libxml2"
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;efence_EXPORTS;HAVE_CONFIG_H;_USE_MATH_DEFINES"
+ MinimalRebuild="TRUE"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="TRUE"
+ DebugInformationFormat="4"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="glib-2.0.lib gmodule-2.0.lib gthread-2.0.lib gobject-2.0.lib libgstreamer.lib gstbytestream.lib iconv.lib intl.lib"
+ OutputFile="$(OutDir)/gstefence.dll"
+ LinkIncremental="2"
+ AdditionalLibraryDirectories="../../../gstreamer/win32/Debug;../../../glib/glib;../../../glib/gmodule;../../../glib/gthread;../../../glib/gobject;../../../gettext/lib;../../../libiconv/lib"
+ ModuleDefinitionFile=""
+ GenerateDebugInformation="TRUE"
+ ProgramDatabaseFile="$(OutDir)/efence.pdb"
+ SubSystem="2"
+ OptimizeReferences="2"
+ ImportLibrary="$(OutDir)/gstefence.lib"
+ TargetMachine="1"/>
+ <Tool
+ Name="VCMIDLTool"/>
+ <Tool
+ Name="VCPostBuildEventTool"
+ CommandLine="copy /Y $(TargetPath) c:\gstreamer\plugins"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="../../win32/Release"
+ IntermediateDirectory="../../win32/Release"
+ ConfigurationType="2"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="../../../gstreamer/win32;../../../gstreamer;../../../gstreamer/libs;../../../glib;../../../glib/glib;../../../glib/gmodule;&quot;../../gst-libs&quot;;../../../popt/include;../../../libxml2/include/libxml2"
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;efence_EXPORTS;HAVE_CONFIG_H;_USE_MATH_DEFINES"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="TRUE"
+ DebugInformationFormat="3"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="glib-2.0.lib gmodule-2.0.lib gthread-2.0.lib gobject-2.0.lib libgstreamer.lib gstbytestream.lib iconv.lib intl.lib"
+ OutputFile="$(OutDir)/gstefence.dll"
+ LinkIncremental="1"
+ AdditionalLibraryDirectories="../../../gstreamer/win32/Release;../../../glib/glib;../../../glib/gmodule;../../../glib/gthread;../../../glib/gobject;../../../gettext/lib;../../../libiconv/lib"
+ ModuleDefinitionFile=""
+ GenerateDebugInformation="TRUE"
+ SubSystem="2"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ ImportLibrary="$(OutDir)/gstefence.lib"
+ TargetMachine="1"/>
+ <Tool
+ Name="VCMIDLTool"/>
+ <Tool
+ Name="VCPostBuildEventTool"
+ CommandLine="copy /Y $(TargetPath) c:\gstreamer\plugins"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}">
+ <File
+ RelativePath=".\efence.c">
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}">
+ <File
+ RelativePath=".\efence.h">
+ </File>
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}">
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/gst/debugutils/gstdebug.c b/gst/debugutils/gstdebug.c
new file mode 100644
index 00000000..53f24d50
--- /dev/null
+++ b/gst/debugutils/gstdebug.c
@@ -0,0 +1,63 @@
+/* GStreamer
+ * Copyright (C) 2004 Benjamin Otte <otte@gnome.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.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <gst/gst.h>
+
+GType gst_break_my_data_get_type (void);
+GType gst_rnd_buffer_size_get_type (void);
+GType gst_navseek_get_type (void);
+GType gst_progress_report_get_type (void);
+GType gst_tag_inject_get_type (void);
+GType gst_test_get_type (void);
+/*
+GType gst_push_file_src_get_type (void);
+GType gst_gst_negotiation_get_type (void);
+*/
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+ if (!gst_element_register (plugin, "breakmydata", GST_RANK_NONE,
+ gst_break_my_data_get_type ())
+ || !gst_element_register (plugin, "rndbuffersize", GST_RANK_NONE,
+ gst_rnd_buffer_size_get_type ())
+ || !gst_element_register (plugin, "navseek", GST_RANK_NONE,
+ gst_navseek_get_type ()) ||
+/* !gst_element_register (plugin, "pushfilesrc", GST_RANK_NONE, gst_push_file_src_get_type ()) || */
+/* !gst_element_register (plugin, "negotiation", GST_RANK_NONE, gst_gst_negotiation_get_type ()) || */
+ !gst_element_register (plugin, "progressreport", GST_RANK_NONE,
+ gst_progress_report_get_type ())
+ || !gst_element_register (plugin, "taginject", GST_RANK_NONE,
+ gst_tag_inject_get_type ())
+ || !gst_element_register (plugin, "testsink", GST_RANK_NONE,
+ gst_test_get_type ()))
+ return FALSE;
+
+ return TRUE;
+}
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+ GST_VERSION_MINOR,
+ "debug",
+ "elements for testing and debugging",
+ plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
diff --git a/gst/debugutils/gstnavigationtest.c b/gst/debugutils/gstnavigationtest.c
new file mode 100644
index 00000000..5c339252
--- /dev/null
+++ b/gst/debugutils/gstnavigationtest.c
@@ -0,0 +1,357 @@
+/* GStreamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ * Copyright (C) <2003> David 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstnavigationtest.h"
+#include <string.h>
+#include <math.h>
+
+#include <gst/video/video.h>
+
+#ifdef _MSC_VER
+#define rint(x) (floor((x)+0.5))
+#endif
+
+GST_DEBUG_CATEGORY_STATIC (navigationtest_debug);
+#define GST_CAT_DEFAULT navigationtest_debug
+
+static const GstElementDetails navigationtest_details =
+GST_ELEMENT_DETAILS ("Video navigation test",
+ "Filter/Effect/Video",
+ "Handle navigation events showing a black square following mouse pointer",
+ "David Schleef <ds@schleef.org>");
+
+static GstStaticPadTemplate gst_navigationtest_src_template =
+GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("I420"))
+ );
+
+static GstStaticPadTemplate gst_navigationtest_sink_template =
+GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("I420"))
+ );
+
+static GstVideoFilterClass *parent_class = NULL;
+
+static gboolean
+gst_navigationtest_handle_src_event (GstPad * pad, GstEvent * event)
+{
+ GstNavigationtest *navtest;
+ const gchar *type;
+
+ navtest = GST_NAVIGATIONTEST (GST_PAD_PARENT (pad));
+
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_NAVIGATION:
+ {
+ const GstStructure *s = gst_event_get_structure (event);
+ gint fps_n, fps_d;
+
+ fps_n = gst_value_get_fraction_numerator ((&navtest->framerate));
+ fps_d = gst_value_get_fraction_denominator ((&navtest->framerate));
+
+ type = gst_structure_get_string (s, "event");
+ if (g_str_equal (type, "mouse-move")) {
+ gst_structure_get_double (s, "pointer_x", &navtest->x);
+ gst_structure_get_double (s, "pointer_y", &navtest->y);
+ } else if (g_str_equal (type, "mouse-button-press")) {
+ ButtonClick *click = g_new (ButtonClick, 1);
+
+ gst_structure_get_double (s, "pointer_x", &click->x);
+ gst_structure_get_double (s, "pointer_y", &click->y);
+ click->images_left = (fps_n + fps_d - 1) / fps_d;
+ /* green */
+ click->cy = 150;
+ click->cu = 46;
+ click->cv = 21;
+ navtest->clicks = g_slist_prepend (navtest->clicks, click);
+ } else if (g_str_equal (type, "mouse-button-release")) {
+ ButtonClick *click = g_new (ButtonClick, 1);
+
+ gst_structure_get_double (s, "pointer_x", &click->x);
+ gst_structure_get_double (s, "pointer_y", &click->y);
+ click->images_left = (fps_n + fps_d - 1) / fps_d;
+ /* red */
+ click->cy = 76;
+ click->cu = 85;
+ click->cv = 255;
+ navtest->clicks = g_slist_prepend (navtest->clicks, click);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ return gst_pad_event_default (pad, event);
+}
+
+/* Useful macros */
+#define GST_VIDEO_I420_Y_ROWSTRIDE(width) (GST_ROUND_UP_4(width))
+#define GST_VIDEO_I420_U_ROWSTRIDE(width) (GST_ROUND_UP_8(width)/2)
+#define GST_VIDEO_I420_V_ROWSTRIDE(width) ((GST_ROUND_UP_8(GST_VIDEO_I420_Y_ROWSTRIDE(width)))/2)
+
+#define GST_VIDEO_I420_Y_OFFSET(w,h) (0)
+#define GST_VIDEO_I420_U_OFFSET(w,h) (GST_VIDEO_I420_Y_OFFSET(w,h)+(GST_VIDEO_I420_Y_ROWSTRIDE(w)*GST_ROUND_UP_2(h)))
+#define GST_VIDEO_I420_V_OFFSET(w,h) (GST_VIDEO_I420_U_OFFSET(w,h)+(GST_VIDEO_I420_U_ROWSTRIDE(w)*GST_ROUND_UP_2(h)/2))
+
+#define GST_VIDEO_I420_SIZE(w,h) (GST_VIDEO_I420_V_OFFSET(w,h)+(GST_VIDEO_I420_V_ROWSTRIDE(w)*GST_ROUND_UP_2(h)/2))
+
+static gboolean
+gst_navigationtest_get_unit_size (GstBaseTransform * btrans, GstCaps * caps,
+ guint * size)
+{
+ GstNavigationtest *navtest;
+ GstStructure *structure;
+ gboolean ret = FALSE;
+ gint width, height;
+
+ navtest = GST_NAVIGATIONTEST (btrans);
+
+ structure = gst_caps_get_structure (caps, 0);
+
+ if (gst_structure_get_int (structure, "width", &width) &&
+ gst_structure_get_int (structure, "height", &height)) {
+ *size = GST_VIDEO_I420_SIZE (width, height);
+ ret = TRUE;
+ GST_DEBUG_OBJECT (navtest, "our frame size is %d bytes (%dx%d)", *size,
+ width, height);
+ }
+
+ return ret;
+}
+
+static gboolean
+gst_navigationtest_set_caps (GstBaseTransform * btrans, GstCaps * incaps,
+ GstCaps * outcaps)
+{
+ GstNavigationtest *navtest = GST_NAVIGATIONTEST (btrans);
+ gboolean ret = FALSE;
+ GstStructure *structure;
+
+ structure = gst_caps_get_structure (incaps, 0);
+
+ if (gst_structure_get_int (structure, "width", &navtest->width) &&
+ gst_structure_get_int (structure, "height", &navtest->height)) {
+ const GValue *framerate;
+
+ framerate = gst_structure_get_value (structure, "framerate");
+ if (framerate && GST_VALUE_HOLDS_FRACTION (framerate)) {
+ g_value_copy (framerate, &navtest->framerate);
+ ret = TRUE;
+ }
+ }
+
+ return ret;
+}
+
+static void
+draw_box_planar411 (guint8 * dest, int width, int height, int x, int y,
+ guint8 colory, guint8 coloru, guint8 colorv)
+{
+ int x1, x2, y1, y2;
+ guint8 *d = dest;
+
+ if (x < 0 || y < 0 || x >= width || y >= height)
+ return;
+
+ x1 = MAX (x - 5, 0);
+ x2 = MIN (x + 5, width);
+ y1 = MAX (y - 5, 0);
+ y2 = MIN (y + 5, height);
+
+ for (y = y1; y < y2; y++) {
+ for (x = x1; x < x2; x++) {
+ ((guint8 *) d)[y * GST_VIDEO_I420_Y_ROWSTRIDE (width) + x] = colory;
+ }
+ }
+
+ d = dest + GST_VIDEO_I420_U_OFFSET (width, height);
+ x1 /= 2;
+ x2 /= 2;
+ y1 /= 2;
+ y2 /= 2;
+ for (y = y1; y < y2; y++) {
+ for (x = x1; x < x2; x++) {
+ ((guint8 *) d)[y * GST_VIDEO_I420_U_ROWSTRIDE (width) + x] = coloru;
+ }
+ }
+
+ d = dest + GST_VIDEO_I420_V_OFFSET (width, height);
+ for (y = y1; y < y2; y++) {
+ for (x = x1; x < x2; x++) {
+ ((guint8 *) d)[y * GST_VIDEO_I420_V_ROWSTRIDE (width) + x] = colorv;
+ }
+ }
+}
+
+static GstFlowReturn
+gst_navigationtest_transform (GstBaseTransform * trans, GstBuffer * in,
+ GstBuffer * out)
+{
+ GstNavigationtest *navtest = GST_NAVIGATIONTEST (trans);
+ GSList *walk;
+ GstFlowReturn ret = GST_FLOW_OK;
+
+ /* do something interesting here. This simply copies the source
+ * to the destination. */
+ gst_buffer_copy_metadata (out, in, GST_BUFFER_COPY_TIMESTAMPS);
+
+ memcpy (GST_BUFFER_DATA (out), GST_BUFFER_DATA (in),
+ MIN (GST_BUFFER_SIZE (in), GST_BUFFER_SIZE (out)));
+
+ walk = navtest->clicks;
+ while (walk) {
+ ButtonClick *click = walk->data;
+
+ walk = g_slist_next (walk);
+ draw_box_planar411 (GST_BUFFER_DATA (out), navtest->width, navtest->height,
+ rint (click->x), rint (click->y), click->cy, click->cu, click->cv);
+ if (--click->images_left < 1) {
+ navtest->clicks = g_slist_remove (navtest->clicks, click);
+ g_free (click);
+ }
+ }
+ draw_box_planar411 (GST_BUFFER_DATA (out), navtest->width, navtest->height,
+ rint (navtest->x), rint (navtest->y), 0, 128, 128);
+
+ return ret;
+}
+
+static GstStateChangeReturn
+gst_navigationtest_change_state (GstElement * element,
+ GstStateChange transition)
+{
+ GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
+ GstNavigationtest *navtest = GST_NAVIGATIONTEST (element);
+
+ if (GST_ELEMENT_CLASS (parent_class)->change_state)
+ ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+
+ /* downwards state changes */
+ switch (transition) {
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ {
+ g_slist_foreach (navtest->clicks, (GFunc) g_free, NULL);
+ g_slist_free (navtest->clicks);
+ navtest->clicks = NULL;
+ break;
+ }
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static void
+gst_navigationtest_base_init (gpointer g_class)
+{
+ GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+
+ gst_element_class_set_details (element_class, &navigationtest_details);
+
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&gst_navigationtest_sink_template));
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&gst_navigationtest_src_template));
+}
+
+static void
+gst_navigationtest_class_init (gpointer klass, gpointer class_data)
+{
+ GObjectClass *gobject_class;
+ GstElementClass *element_class;
+ GstBaseTransformClass *trans_class;
+
+ gobject_class = (GObjectClass *) klass;
+ element_class = (GstElementClass *) klass;
+ trans_class = (GstBaseTransformClass *) klass;
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ element_class->change_state =
+ GST_DEBUG_FUNCPTR (gst_navigationtest_change_state);
+
+ trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_navigationtest_set_caps);
+ trans_class->get_unit_size =
+ GST_DEBUG_FUNCPTR (gst_navigationtest_get_unit_size);
+ trans_class->transform = GST_DEBUG_FUNCPTR (gst_navigationtest_transform);
+}
+
+static void
+gst_navigationtest_init (GTypeInstance * instance, gpointer g_class)
+{
+ GstNavigationtest *navtest = GST_NAVIGATIONTEST (instance);
+ GstBaseTransform *btrans = GST_BASE_TRANSFORM (instance);
+
+ gst_pad_set_event_function (btrans->srcpad,
+ GST_DEBUG_FUNCPTR (gst_navigationtest_handle_src_event));
+
+ navtest->x = -1;
+ navtest->y = -1;
+ g_value_init (&navtest->framerate, GST_TYPE_FRACTION);
+}
+
+GType
+gst_navigationtest_get_type (void)
+{
+ static GType navigationtest_type = 0;
+
+ if (!navigationtest_type) {
+ static const GTypeInfo navigationtest_info = {
+ sizeof (GstNavigationtestClass),
+ gst_navigationtest_base_init,
+ NULL,
+ gst_navigationtest_class_init,
+ NULL,
+ NULL,
+ sizeof (GstNavigationtest),
+ 0,
+ gst_navigationtest_init,
+ };
+
+ navigationtest_type = g_type_register_static (GST_TYPE_VIDEO_FILTER,
+ "GstNavigationtest", &navigationtest_info, 0);
+ }
+ return navigationtest_type;
+}
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+ GST_DEBUG_CATEGORY_INIT (navigationtest_debug, "navigationtest", 0,
+ "navigationtest");
+
+ return gst_element_register (plugin, "navigationtest", GST_RANK_NONE,
+ GST_TYPE_NAVIGATIONTEST);
+}
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+ GST_VERSION_MINOR,
+ "navigationtest",
+ "Template for a video filter",
+ plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
diff --git a/gst/debugutils/gstnavigationtest.h b/gst/debugutils/gstnavigationtest.h
new file mode 100644
index 00000000..efdbb228
--- /dev/null
+++ b/gst/debugutils/gstnavigationtest.h
@@ -0,0 +1,68 @@
+/* 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_NAVIGATIONTEST_H__
+#define __GST_NAVIGATIONTEST_H__
+
+#include <gst/video/gstvideofilter.h>
+
+G_BEGIN_DECLS
+#define GST_TYPE_NAVIGATIONTEST \
+ (gst_navigationtest_get_type())
+#define GST_NAVIGATIONTEST(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_NAVIGATIONTEST,GstNavigationtest))
+#define GST_NAVIGATIONTEST_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_NAVIGATIONTEST,GstNavigationtestClass))
+#define GST_IS_NAVIGATIONTEST(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_NAVIGATIONTEST))
+#define GST_IS_NAVIGATIONTEST_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_NAVIGATIONTEST))
+typedef struct _GstNavigationtest GstNavigationtest;
+typedef struct _GstNavigationtestClass GstNavigationtestClass;
+
+typedef struct
+{
+ gdouble x;
+ gdouble y;
+ gint images_left;
+ guint8 cy, cu, cv;
+} ButtonClick;
+
+struct _GstNavigationtest
+{
+ GstVideoFilter videofilter;
+
+ gint width, height;
+
+ GValue framerate;
+ gdouble x, y;
+
+ GSList *clicks;
+};
+
+struct _GstNavigationtestClass
+{
+ GstVideoFilterClass parent_class;
+};
+
+GType gst_navigationtest_get_type (void);
+
+G_END_DECLS
+#endif /* __GST_NAVIGATIONTEST_H__ */
diff --git a/gst/debugutils/gstnavseek.c b/gst/debugutils/gstnavseek.c
new file mode 100644
index 00000000..c47bf42f
--- /dev/null
+++ b/gst/debugutils/gstnavseek.c
@@ -0,0 +1,340 @@
+/* GStreamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ * Copyright (C) <2003> David 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.
+ */
+
+/*
+ * This file was (probably) generated from gstnavseek.c,
+ * gstnavseek.c,v 1.7 2003/11/08 02:48:59 dschleef Exp
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstnavseek.h"
+#include <string.h>
+#include <math.h>
+
+enum
+{
+ ARG_0,
+ ARG_SEEKOFFSET
+};
+
+GstStaticPadTemplate navseek_src_template = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS_ANY);
+
+GstStaticPadTemplate navseek_sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS_ANY);
+
+static const GstElementDetails navseek_details =
+GST_ELEMENT_DETAILS ("Seek based on left-right arrows",
+ "Filter/Video",
+ "Seek based on navigation keys left-right",
+ "Jan Schmidt <thaytan@mad.scientist.com>");
+
+static gboolean gst_navseek_event (GstBaseTransform * trans, GstEvent * event);
+static GstFlowReturn gst_navseek_transform_ip (GstBaseTransform * basetrans,
+ GstBuffer * buf);
+static gboolean gst_navseek_handle_src_event (GstPad * pad, GstEvent * event);
+static gboolean gst_navseek_stop (GstBaseTransform * trans);
+static gboolean gst_navseek_start (GstBaseTransform * trans);
+
+static void gst_navseek_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+static void gst_navseek_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
+
+GST_BOILERPLATE (GstNavSeek, gst_navseek, GstBaseTransform,
+ GST_TYPE_BASE_TRANSFORM);
+
+static void
+gst_navseek_base_init (gpointer g_class)
+{
+ GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&navseek_sink_template));
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&navseek_src_template));
+
+ gst_element_class_set_details (element_class, &navseek_details);
+}
+
+static void
+gst_navseek_class_init (GstNavSeekClass * klass)
+{
+ GstBaseTransformClass *gstbasetrans_class;
+ GObjectClass *gobject_class;
+
+ gobject_class = G_OBJECT_CLASS (klass);
+ gstbasetrans_class = GST_BASE_TRANSFORM_CLASS (klass);
+
+ gobject_class->set_property = gst_navseek_set_property;
+ gobject_class->get_property = gst_navseek_get_property;
+
+ g_object_class_install_property (gobject_class,
+ ARG_SEEKOFFSET, g_param_spec_double ("seek-offset", "Seek Offset",
+ "Time in seconds to seek by", 0.0, G_MAXDOUBLE, 5.0,
+ G_PARAM_READWRITE));
+
+ gstbasetrans_class->event = GST_DEBUG_FUNCPTR (gst_navseek_event);
+ gstbasetrans_class->transform_ip =
+ GST_DEBUG_FUNCPTR (gst_navseek_transform_ip);
+ gstbasetrans_class->start = GST_DEBUG_FUNCPTR (gst_navseek_start);
+ gstbasetrans_class->stop = GST_DEBUG_FUNCPTR (gst_navseek_stop);
+}
+
+static void
+gst_navseek_init (GstNavSeek * navseek, GstNavSeekClass * g_class)
+{
+ gst_pad_set_event_function (GST_BASE_TRANSFORM (navseek)->srcpad,
+ GST_DEBUG_FUNCPTR (gst_navseek_handle_src_event));
+
+ gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (navseek), TRUE);
+
+ navseek->seek_offset = 5.0;
+ navseek->loop = FALSE;
+ navseek->grab_seg_start = FALSE;
+ navseek->grab_seg_end = FALSE;
+ navseek->segment_start = GST_CLOCK_TIME_NONE;
+ navseek->segment_end = GST_CLOCK_TIME_NONE;
+}
+
+static void
+gst_navseek_seek (GstNavSeek * navseek, gint64 offset)
+{
+ GstFormat peer_format = GST_FORMAT_TIME;
+ gboolean ret;
+ GstPad *peer_pad;
+ gint64 peer_value;
+
+ /* Query for the current time then attempt to set to time + offset */
+ peer_pad = gst_pad_get_peer (GST_BASE_TRANSFORM (navseek)->sinkpad);
+ ret = gst_pad_query_position (peer_pad, &peer_format, &peer_value);
+
+ if (ret && peer_format == GST_FORMAT_TIME) {
+ GstEvent *event;
+
+ peer_value += offset;
+ if (peer_value < 0)
+ peer_value = 0;
+
+ event = gst_event_new_seek (1.0, GST_FORMAT_TIME,
+ GST_SEEK_FLAG_ACCURATE | GST_SEEK_FLAG_FLUSH,
+ GST_SEEK_TYPE_SET, peer_value, GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
+
+ gst_pad_send_event (peer_pad, event);
+ }
+
+ gst_object_unref (peer_pad);
+}
+
+static void
+gst_navseek_segseek (GstNavSeek * navseek)
+{
+ GstEvent *event;
+ GstPad *peer_pad;
+
+ if ((navseek->segment_start == GST_CLOCK_TIME_NONE) ||
+ (navseek->segment_end == GST_CLOCK_TIME_NONE) ||
+ (!GST_PAD_IS_LINKED (GST_BASE_TRANSFORM (navseek)->sinkpad))) {
+ return;
+ }
+
+ if (navseek->loop) {
+ event =
+ gst_event_new_seek (1.0, GST_FORMAT_TIME,
+ GST_SEEK_FLAG_ACCURATE | GST_SEEK_FLAG_SEGMENT,
+ GST_SEEK_TYPE_SET, navseek->segment_start, GST_SEEK_TYPE_SET,
+ navseek->segment_end);
+ } else {
+ event =
+ gst_event_new_seek (1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_ACCURATE,
+ GST_SEEK_TYPE_SET, navseek->segment_start, GST_SEEK_TYPE_SET,
+ navseek->segment_end);
+ }
+
+ peer_pad = gst_pad_get_peer (GST_BASE_TRANSFORM (navseek)->sinkpad);
+ gst_pad_send_event (peer_pad, event);
+ gst_object_unref (peer_pad);
+}
+
+static gboolean
+gst_navseek_handle_src_event (GstPad * pad, GstEvent * event)
+{
+ GstNavSeek *navseek;
+ gboolean ret = TRUE;
+
+ navseek = GST_NAVSEEK (GST_PAD_PARENT (pad));
+
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_NAVIGATION:
+ /* Check for a keyup and convert left/right to a seek event */
+ {
+ const GstStructure *structure;
+ const gchar *event_type;
+
+ structure = gst_event_get_structure (event);
+ g_return_val_if_fail (structure != NULL, FALSE);
+
+ event_type = gst_structure_get_string (structure, "event");
+ g_return_val_if_fail (event_type != NULL, FALSE);
+
+ if (strcmp (event_type, "key-press") == 0) {
+ const gchar *key;
+
+ key = gst_structure_get_string (structure, "key");
+ g_return_val_if_fail (key != NULL, FALSE);
+
+ if (strcmp (key, "Left") == 0) {
+ /* Seek backward by 5 secs */
+ gst_navseek_seek (navseek, -1.0 * navseek->seek_offset * GST_SECOND);
+ } else if (strcmp (key, "Right") == 0) {
+ /* Seek forward */
+ gst_navseek_seek (navseek, navseek->seek_offset * GST_SECOND);
+ } else if (strcmp (key, "s") == 0) {
+ /* Grab the next frame as the start frame of a segment */
+ navseek->grab_seg_start = TRUE;
+ } else if (strcmp (key, "e") == 0) {
+ /* Grab the next frame as the end frame of a segment */
+ navseek->grab_seg_end = TRUE;
+ } else if (strcmp (key, "l") == 0) {
+ /* Toggle the loop flag. If we have both start and end segment times send a seek */
+ navseek->loop = !navseek->loop;
+ gst_navseek_segseek (navseek);
+ }
+ } else {
+ break;
+ }
+ gst_event_unref (event);
+ event = NULL;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (event && GST_PAD_IS_LINKED (GST_BASE_TRANSFORM (navseek)->sinkpad)) {
+ GstPad *peer_pad = gst_pad_get_peer (GST_BASE_TRANSFORM (navseek)->sinkpad);
+
+ ret = gst_pad_send_event (peer_pad, event);
+ gst_object_unref (peer_pad);
+ }
+
+ return ret;
+}
+
+static void
+gst_navseek_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstNavSeek *navseek = GST_NAVSEEK (object);
+
+ switch (prop_id) {
+ case ARG_SEEKOFFSET:
+ GST_OBJECT_LOCK (navseek);
+ navseek->seek_offset = g_value_get_double (value);
+ GST_OBJECT_UNLOCK (navseek);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_navseek_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GstNavSeek *navseek = GST_NAVSEEK (object);
+
+ switch (prop_id) {
+ case ARG_SEEKOFFSET:
+ GST_OBJECT_LOCK (navseek);
+ g_value_set_double (value, navseek->seek_offset);
+ GST_OBJECT_UNLOCK (navseek);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static gboolean
+gst_navseek_event (GstBaseTransform * trans, GstEvent * event)
+{
+ GstNavSeek *navseek = GST_NAVSEEK (trans);
+
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_EOS:
+ GST_OBJECT_LOCK (navseek);
+ if (navseek->loop)
+ gst_navseek_segseek (navseek);
+ GST_OBJECT_UNLOCK (navseek);
+ break;
+ default:
+ break;
+ }
+ return GST_BASE_TRANSFORM_CLASS (parent_class)->event (trans, event);
+}
+
+static GstFlowReturn
+gst_navseek_transform_ip (GstBaseTransform * basetrans, GstBuffer * buf)
+{
+ GstNavSeek *navseek = GST_NAVSEEK (basetrans);
+
+ GST_OBJECT_LOCK (navseek);
+
+ if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
+ if (navseek->grab_seg_start) {
+ navseek->segment_start = GST_BUFFER_TIMESTAMP (buf);
+ navseek->segment_end = GST_CLOCK_TIME_NONE;
+ navseek->grab_seg_start = FALSE;
+ }
+
+ if (navseek->grab_seg_end) {
+ navseek->segment_end = GST_BUFFER_TIMESTAMP (buf);
+ navseek->grab_seg_end = FALSE;
+ gst_navseek_segseek (navseek);
+ }
+ }
+
+ GST_OBJECT_UNLOCK (navseek);
+
+ return GST_FLOW_OK;
+}
+
+static gboolean
+gst_navseek_start (GstBaseTransform * trans)
+{
+ /* anything we should be doing here? */
+ return TRUE;
+}
+
+static gboolean
+gst_navseek_stop (GstBaseTransform * trans)
+{
+ /* anything we should be doing here? */
+ return TRUE;
+}
diff --git a/gst/debugutils/gstnavseek.h b/gst/debugutils/gstnavseek.h
new file mode 100644
index 00000000..af7e9d21
--- /dev/null
+++ b/gst/debugutils/gstnavseek.h
@@ -0,0 +1,60 @@
+/* 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_NAVSEEK_H__
+#define __GST_NAVSEEK_H__
+
+
+#include <gst/gst.h>
+#include <gst/base/gstbasetransform.h>
+
+G_BEGIN_DECLS
+#define GST_TYPE_NAVSEEK \
+ (gst_navseek_get_type())
+#define GST_NAVSEEK(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_NAVSEEK,GstNavSeek))
+#define GST_NAVSEEK_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_NAVSEEK,GstNavSeekClass))
+#define GST_IS_NAVSEEK(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_NAVSEEK))
+#define GST_IS_NAVSEEK_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_NAVSEEK))
+typedef struct _GstNavSeek GstNavSeek;
+typedef struct _GstNavSeekClass GstNavSeekClass;
+
+struct _GstNavSeek
+{
+ GstBaseTransform basetransform;
+
+ gdouble seek_offset;
+ gboolean loop;
+ gboolean grab_seg_start;
+ gboolean grab_seg_end;
+ GstClockTime segment_start;
+ GstClockTime segment_end;
+};
+
+struct _GstNavSeekClass
+{
+ GstBaseTransformClass parent_class;
+};
+
+G_END_DECLS
+#endif /* __GST_NAVSEEK_H__ */
diff --git a/gst/debugutils/gstpushfilesrc.c b/gst/debugutils/gstpushfilesrc.c
new file mode 100644
index 00000000..47daa83e
--- /dev/null
+++ b/gst/debugutils/gstpushfilesrc.c
@@ -0,0 +1,195 @@
+/* GStreamer Push File Source
+ * 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:element-pushfilesrc
+ * @see_also: filesrc
+ *
+ * This element is only useful for debugging purposes. It implements an URI
+ * protocol handler for the 'pushfile' protocol and behaves like a file source
+ * element that cannot be activated in pull-mode. This makes it very easy to
+ * debug demuxers or decoders that can operate both pull and push-based in
+ * connection with the playbin element (which creates a source based on the
+ * URI passed).
+ *
+ * <refsect2>
+ * <title>Example launch line</title>
+ * |[
+ * gst-launch -m playbin uri=pushfile:///home/you/some/file.ogg
+ * ]| This plays back the given file using playbin, with the demuxer operating
+ * push-based.
+ * </refsect2>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstpushfilesrc.h"
+
+#include <gst/gst.h>
+
+GST_DEBUG_CATEGORY_STATIC (pushfilesrc_debug);
+#define GST_CAT_DEFAULT pushfilesrc_debug
+
+static const GstElementDetails pushfilesrc_details =
+GST_ELEMENT_DETAILS ("Push File Source",
+ "Testing",
+ "Implements pushfile:// URI-handler for push-based file access",
+ "Tim-Philipp Müller <tim centricular net>");
+
+static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS_ANY);
+
+static void gst_push_file_src_uri_handler_init (gpointer g_iface,
+ gpointer iface_data);
+static void gst_file_push_src_add_uri_handler (GType type);
+
+GST_BOILERPLATE_FULL (GstPushFileSrc, gst_push_file_src, GstBin, GST_TYPE_BIN,
+ gst_file_push_src_add_uri_handler);
+
+static void
+gst_file_push_src_add_uri_handler (GType type)
+{
+ static const GInterfaceInfo info = {
+ gst_push_file_src_uri_handler_init,
+ NULL,
+ NULL
+ };
+
+ g_type_add_interface_static (type, GST_TYPE_URI_HANDLER, &info);
+ GST_DEBUG_CATEGORY_INIT (pushfilesrc_debug, "pushfilesrc", 0,
+ "pushfilesrc element");
+}
+
+static void
+gst_push_file_src_base_init (gpointer g_class)
+{
+ GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&srctemplate));
+
+ gst_element_class_set_details (element_class, &pushfilesrc_details);
+}
+
+static void
+gst_push_file_src_dispose (GObject * obj)
+{
+ GstPushFileSrc *src = GST_PUSH_FILE_SRC (obj);
+
+ if (src->srcpad) {
+ gst_element_remove_pad (GST_ELEMENT (src), src->srcpad);
+ src->srcpad = NULL;
+ }
+ if (src->filesrc) {
+ gst_bin_remove (GST_BIN (src), src->filesrc);
+ src->filesrc = NULL;
+ }
+
+ G_OBJECT_CLASS (parent_class)->dispose (obj);
+}
+
+static void
+gst_push_file_src_class_init (GstPushFileSrcClass * g_class)
+{
+ GObjectClass *gobject_class;
+
+ gobject_class = G_OBJECT_CLASS (g_class);
+
+ gobject_class->dispose = gst_push_file_src_dispose;
+}
+
+static gboolean
+gst_push_file_src_ghostpad_checkgetrange (GstPad * pad)
+{
+ return FALSE;
+}
+
+static void
+gst_push_file_src_init (GstPushFileSrc * src, GstPushFileSrcClass * g_class)
+{
+ src->filesrc = gst_element_factory_make ("filesrc", "real-filesrc");
+ if (src->filesrc) {
+ GstPad *pad;
+
+ gst_bin_add (GST_BIN (src), src->filesrc);
+ pad = gst_element_get_static_pad (src->filesrc, "src");
+ g_assert (pad != NULL);
+ src->srcpad = gst_ghost_pad_new ("src", pad);
+ /* FIXME^H^HCORE: try pushfile:///foo/bar.ext ! typefind ! fakesink without
+ * this and watch core bugginess (some pad stays in flushing state) */
+ gst_pad_set_checkgetrange_function (src->srcpad,
+ GST_DEBUG_FUNCPTR (gst_push_file_src_ghostpad_checkgetrange));
+ gst_element_add_pad (GST_ELEMENT (src), src->srcpad);
+ gst_object_unref (pad);
+ }
+}
+
+/*** GSTURIHANDLER INTERFACE *************************************************/
+
+static GstURIType
+gst_push_file_src_uri_get_type (void)
+{
+ return GST_URI_SRC;
+}
+
+static gchar **
+gst_push_file_src_uri_get_protocols (void)
+{
+ static gchar *protocols[] = { "pushfile", NULL };
+
+ return protocols;
+}
+
+static const gchar *
+gst_push_file_src_uri_get_uri (GstURIHandler * handler)
+{
+ GstPushFileSrc *src = GST_PUSH_FILE_SRC (handler);
+
+ if (src->filesrc == NULL)
+ return NULL;
+
+ return gst_uri_handler_get_uri (GST_URI_HANDLER (src->filesrc));
+}
+
+static gboolean
+gst_push_file_src_uri_set_uri (GstURIHandler * handler, const gchar * uri)
+{
+ GstPushFileSrc *src = GST_PUSH_FILE_SRC (handler);
+
+ if (src->filesrc == NULL || !g_str_has_prefix (uri, "pushfile://"))
+ return FALSE;
+
+ /* skip 'push' bit */
+ return gst_uri_handler_set_uri (GST_URI_HANDLER (src->filesrc), uri + 4);
+}
+
+static void
+gst_push_file_src_uri_handler_init (gpointer g_iface, gpointer iface_data)
+{
+ GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
+
+ iface->get_type = gst_push_file_src_uri_get_type;
+ iface->get_protocols = gst_push_file_src_uri_get_protocols;
+ iface->get_uri = gst_push_file_src_uri_get_uri;
+ iface->set_uri = gst_push_file_src_uri_set_uri;
+}
diff --git a/gst/debugutils/gstpushfilesrc.h b/gst/debugutils/gstpushfilesrc.h
new file mode 100644
index 00000000..d9333444
--- /dev/null
+++ b/gst/debugutils/gstpushfilesrc.h
@@ -0,0 +1,56 @@
+/* GStreamer Push File Source
+ * 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_PUSH_FILE_SRC_H__
+#define __GST_PUSH_FILE_SRC_H__
+
+#include <gst/gstbin.h>
+
+G_BEGIN_DECLS
+#define GST_TYPE_PUSH_FILE_SRC \
+ (gst_push_file_src_get_type())
+#define GST_PUSH_FILE_SRC(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_PUSH_FILE_SRC,GstPushFileSrc))
+#define GST_PUSH_FILE_SRC_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_PUSH_FILE_SRC,GstPushFileSrcClass))
+#define GST_IS_PUSH_FILE_SRC(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_PUSH_FILE_SRC))
+#define GST_IS_PUSH_FILE_SRC_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_PUSH_FILE_SRC))
+typedef struct _GstPushFileSrc GstPushFileSrc;
+typedef struct _GstPushFileSrcClass GstPushFileSrcClass;
+
+struct _GstPushFileSrc
+{
+ GstBin parent;
+
+ /*< private > */
+ GstElement *filesrc;
+ GstPad *srcpad;
+};
+
+struct _GstPushFileSrcClass
+{
+ GstBinClass parent_class;
+};
+
+GType gst_push_file_src_get_type (void);
+
+G_END_DECLS
+#endif /* __GST_PUSH_FILE_SRC_H__ */
diff --git a/gst/debugutils/gsttaginject.c b/gst/debugutils/gsttaginject.c
new file mode 100644
index 00000000..3429fb14
--- /dev/null
+++ b/gst/debugutils/gsttaginject.c
@@ -0,0 +1,204 @@
+/* GStreamer
+ * Copyright (C) 2008 Stefan Kost <ensonic@users.sf.net>
+ *
+ * gsttaginject.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.
+ */
+/**
+ * SECTION:element-taginject
+ *
+ * Element that injects new metadata tags, but passes incomming data through
+ * unmodified.
+ *
+ * <refsect2>
+ * <title>Example launch lines</title>
+ * |[
+ * gst-launch audiotestsrc num-buffers=100 ! taginject tags="title=testsrc,artist=gstreamer" ! vorbisenc ! oggmux ! filesink location=test.ogg
+ * ]| set title and artist
+ * |[
+ * gst-launch audiotestsrc num-buffers=100 ! taginject tags="keywords=\"testone,audio\",title=\"audio testtone\"" ! vorbisenc ! oggmux ! filesink location=test.ogg
+ * ]| set keywords and title demonstrating quoting of special chars
+ * </refsect2>
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdlib.h>
+
+#include "gsttaginject.h"
+
+static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS_ANY);
+
+static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS_ANY);
+
+GST_DEBUG_CATEGORY_STATIC (gst_tag_inject_debug);
+#define GST_CAT_DEFAULT gst_tag_inject_debug
+
+enum
+{
+ PROP_TAGS = 1
+};
+
+
+#define DEBUG_INIT(bla) \
+ GST_DEBUG_CATEGORY_INIT (gst_tag_inject_debug, "taginject", 0, "tag inject element");
+
+GST_BOILERPLATE_FULL (GstTagInject, gst_tag_inject, GstBaseTransform,
+ GST_TYPE_BASE_TRANSFORM, DEBUG_INIT);
+
+static void gst_tag_inject_finalize (GObject * object);
+static void gst_tag_inject_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+static void gst_tag_inject_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
+
+static GstFlowReturn gst_tag_inject_transform_ip (GstBaseTransform * trans,
+ GstBuffer * buf);
+static gboolean gst_tag_inject_start (GstBaseTransform * trans);
+
+
+static void
+gst_tag_inject_base_init (gpointer g_class)
+{
+ GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
+
+ gst_element_class_set_details_simple (gstelement_class,
+ "TagInject",
+ "Generic", "inject metadata tags", "Stefan Kost <ensonic@users.sf.net>");
+ gst_element_class_add_pad_template (gstelement_class,
+ gst_static_pad_template_get (&srctemplate));
+ gst_element_class_add_pad_template (gstelement_class,
+ gst_static_pad_template_get (&sinktemplate));
+}
+
+static void
+gst_tag_inject_finalize (GObject * object)
+{
+ GstTagInject *self = GST_TAG_INJECT (object);
+
+ if (self->tags) {
+ gst_tag_list_free (self->tags);
+ self->tags = NULL;
+ }
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gst_tag_inject_class_init (GstTagInjectClass * klass)
+{
+ GObjectClass *gobject_class;
+ GstElementClass *gstelement_class;
+ GstBaseTransformClass *gstbasetrans_class;
+
+ gobject_class = G_OBJECT_CLASS (klass);
+ gstelement_class = GST_ELEMENT_CLASS (klass);
+ gstbasetrans_class = GST_BASE_TRANSFORM_CLASS (klass);
+
+ gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_tag_inject_set_property);
+ gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_tag_inject_get_property);
+
+ g_object_class_install_property (gobject_class, PROP_TAGS,
+ g_param_spec_string ("tags", "taglist",
+ "List of tags to inject into the target file",
+ NULL, G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
+
+ gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_tag_inject_finalize);
+
+ gstbasetrans_class->transform_ip =
+ GST_DEBUG_FUNCPTR (gst_tag_inject_transform_ip);
+
+ gstbasetrans_class->start = GST_DEBUG_FUNCPTR (gst_tag_inject_start);
+}
+
+static void
+gst_tag_inject_init (GstTagInject * self, GstTagInjectClass * g_class)
+{
+ self->tags = NULL;
+}
+
+static GstFlowReturn
+gst_tag_inject_transform_ip (GstBaseTransform * trans, GstBuffer * buf)
+{
+ GstTagInject *self = GST_TAG_INJECT (trans);
+
+ if (G_UNLIKELY (!self->tags_sent)) {
+ self->tags_sent = TRUE;
+ /* send tags */
+ if (self->tags && !gst_tag_list_is_empty (self->tags)) {
+ GST_DEBUG ("tag event :%" GST_PTR_FORMAT, self->tags);
+ gst_element_found_tags (GST_ELEMENT (trans),
+ gst_tag_list_copy (self->tags));
+ }
+ }
+
+ return GST_FLOW_OK;
+}
+
+static void
+gst_tag_inject_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstTagInject *self = GST_TAG_INJECT (object);
+
+ switch (prop_id) {
+ case PROP_TAGS:{
+ gchar *structure =
+ g_strdup_printf ("taglist,%s", g_value_get_string (value));
+ if (!(self->tags = gst_structure_from_string (structure, NULL))) {
+ GST_WARNING ("unparsable taglist = '%s'", structure);
+ }
+ g_free (structure);
+ break;
+ }
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_tag_inject_get_property (GObject * object, guint prop_id, GValue * value,
+ GParamSpec * pspec)
+{
+ /*GstTagInject *self = GST_TAG_INJECT (object); */
+
+ switch (prop_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static gboolean
+gst_tag_inject_start (GstBaseTransform * trans)
+{
+ GstTagInject *self = GST_TAG_INJECT (trans);
+
+ /* we need to sent tags _transform_ip() once */
+ self->tags_sent = FALSE;
+
+ return TRUE;
+}
diff --git a/gst/debugutils/gsttaginject.h b/gst/debugutils/gsttaginject.h
new file mode 100644
index 00000000..8e8de1ee
--- /dev/null
+++ b/gst/debugutils/gsttaginject.h
@@ -0,0 +1,66 @@
+/* GStreamer
+ * Copyright (C) 2008 Stefan Kost <ensonic@users.sf.net>
+ *
+ * gsttaginject.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.
+ */
+
+
+#ifndef __GST_TAG_INJECT_H__
+#define __GST_TAG_INJECT_H__
+
+
+#include <gst/gst.h>
+#include <gst/base/gstbasetransform.h>
+
+G_BEGIN_DECLS
+#define GST_TYPE_TAG_INJECT \
+ (gst_tag_inject_get_type())
+#define GST_TAG_INJECT(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_TAG_INJECT,GstTagInject))
+#define GST_TAG_INJECT_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_TAG_INJECT,GstTagInjectClass))
+#define GST_IS_TAG_INJECT(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_TAG_INJECT))
+#define GST_IS_TAG_INJECT_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_TAG_INJECT))
+typedef struct _GstTagInject GstTagInject;
+typedef struct _GstTagInjectClass GstTagInjectClass;
+
+/**
+ * GstTagInject:
+ *
+ * Opaque #GstTagInject data structure
+ */
+struct _GstTagInject
+{
+ GstBaseTransform element;
+
+ /*< private > */
+ GstTagList *tags;
+ gboolean tags_sent;
+};
+
+struct _GstTagInjectClass
+{
+ GstBaseTransformClass parent_class;
+};
+
+GType gst_tag_inject_get_type (void);
+
+G_END_DECLS
+#endif /* __GST_TAG_INJECT_H__ */
diff --git a/gst/debugutils/navigationtest.vcproj b/gst/debugutils/navigationtest.vcproj
new file mode 100644
index 00000000..0bcc0ff7
--- /dev/null
+++ b/gst/debugutils/navigationtest.vcproj
@@ -0,0 +1,148 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="7.10"
+ Name="navigationtest"
+ ProjectGUID="{979C216F-0ACF-4956-AE00-055A42D678AD}"
+ Keyword="Win32Proj">
+ <Platforms>
+ <Platform
+ Name="Win32"/>
+ </Platforms>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="../../win32/Debug"
+ IntermediateDirectory="../../win32/Debug"
+ ConfigurationType="2"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="../../../gstreamer/win32;../../../gstreamer;../../../gstreamer/libs;../../../glib;../../../glib/glib;../../../glib/gmodule;&quot;../../gst-libs&quot;;../../../popt/include;../../../libxml2/include/libxml2"
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;navigationtest_EXPORTS;HAVE_CONFIG_H;_USE_MATH_DEFINES"
+ MinimalRebuild="TRUE"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="TRUE"
+ DebugInformationFormat="4"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="glib-2.0.lib gmodule-2.0.lib gthread-2.0.lib gobject-2.0.lib libgstreamer.lib gstbytestream.lib iconv.lib intl.lib"
+ OutputFile="$(OutDir)/gstnavigationtest.dll"
+ LinkIncremental="2"
+ AdditionalLibraryDirectories="../../../gstreamer/win32/Debug;../../../glib/glib;../../../glib/gmodule;../../../glib/gthread;../../../glib/gobject;../../../gettext/lib;../../../libiconv/lib"
+ ModuleDefinitionFile=""
+ GenerateDebugInformation="TRUE"
+ ProgramDatabaseFile="$(OutDir)/navigationtest.pdb"
+ SubSystem="2"
+ OptimizeReferences="2"
+ ImportLibrary="$(OutDir)/gstnavigationtest.lib"
+ TargetMachine="1"/>
+ <Tool
+ Name="VCMIDLTool"/>
+ <Tool
+ Name="VCPostBuildEventTool"
+ CommandLine="copy /Y $(TargetPath) c:\gstreamer\plugins"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="../../win32/Release"
+ IntermediateDirectory="../../win32/Release"
+ ConfigurationType="2"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="../../../gstreamer/win32;../../../gstreamer;../../../gstreamer/libs;../../../glib;../../../glib/glib;../../../glib/gmodule;&quot;../../gst-libs&quot;;../../../popt/include;../../../libxml2/include/libxml2"
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;navigationtest_EXPORTS;HAVE_CONFIG_H;_USE_MATH_DEFINES"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="TRUE"
+ DebugInformationFormat="3"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="glib-2.0.lib gmodule-2.0.lib gthread-2.0.lib gobject-2.0.lib libgstreamer.lib gstbytestream.lib iconv.lib intl.lib"
+ OutputFile="$(OutDir)/gstnavigationtest.dll"
+ LinkIncremental="1"
+ AdditionalLibraryDirectories="../../../gstreamer/win32/Release;../../../glib/glib;../../../glib/gmodule;../../../glib/gthread;../../../glib/gobject;../../../gettext/lib;../../../libiconv/lib"
+ ModuleDefinitionFile=""
+ GenerateDebugInformation="TRUE"
+ SubSystem="2"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ ImportLibrary="$(OutDir)/gstnavigationtest.lib"
+ TargetMachine="1"/>
+ <Tool
+ Name="VCMIDLTool"/>
+ <Tool
+ Name="VCPostBuildEventTool"
+ CommandLine="copy /Y $(TargetPath) c:\gstreamer\plugins"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}">
+ <File
+ RelativePath=".\gstnavigationtest.c">
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}">
+ <File
+ RelativePath=".\navigationtest.h">
+ </File>
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}">
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/gst/debugutils/negotiation.c b/gst/debugutils/negotiation.c
new file mode 100644
index 00000000..12082273
--- /dev/null
+++ b/gst/debugutils/negotiation.c
@@ -0,0 +1,287 @@
+/*
+ * GStreamer
+ * Copyright (C) 1999-2001 Erik Walthinsen <omega@cse.ogi.edu>
+ * Copyright (C) 2002 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <gst/gst.h>
+
+#include <string.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+
+#define GST_TYPE_NEGOTIATION \
+ (gst_gst_negotiation_get_type())
+#define GST_NEGOTIATION(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_NEGOTIATION,GstNegotiation))
+#define GST_NEGOTIATION_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_NEGOTIATION,GstNegotiation))
+#define GST_IS_NEGOTIATION(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_NEGOTIATION))
+#define GST_IS_NEGOTIATION_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_NEGOTIATION))
+
+typedef struct _GstNegotiation GstNegotiation;
+typedef struct _GstNegotiationClass GstNegotiationClass;
+
+struct _GstNegotiation
+{
+ GstElement element;
+
+ GstPad *sinkpad, *srcpad;
+
+ GstCaps *caps;
+};
+
+struct _GstNegotiationClass
+{
+ GstElementClass parent_class;
+};
+
+GType gst_gst_negotiation_get_type (void);
+
+
+static const GstElementDetails plugin_details =
+GST_ELEMENT_DETAILS ("Negotiation",
+ "Testing",
+ "This element acts like identity, except that one can control how "
+ "negotiation works",
+ "David A. Schleef <ds@schleef.org>");
+
+/* Filter signals and args */
+enum
+{
+ /* FILL ME */
+ LAST_SIGNAL
+};
+
+enum
+{
+ ARG_0,
+ ARG_ALLOWED_CAPS
+};
+
+static GstStaticPadTemplate gst_negotiation_sink_factory =
+GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS_ANY);
+
+static GstStaticPadTemplate gst_negotiation_src_factory =
+GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS_ANY);
+
+static void gst_negotiation_base_init (gpointer g_class);
+static void gst_negotiation_class_init (GstNegotiationClass * klass);
+static void gst_negotiation_init (GstNegotiation * filter);
+
+static GstCaps *gst_negotiation_getcaps (GstPad * pad);
+static GstPadLinkReturn gst_negotiation_pad_link (GstPad * pad,
+ const GstCaps * caps);
+
+static void gst_negotiation_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+static void gst_negotiation_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
+
+static void gst_negotiation_update_caps (GstNegotiation * negotiation);
+static void gst_negotiation_chain (GstPad * pad, GstData * _data);
+
+static GstElementClass *parent_class = NULL;
+
+GType
+gst_gst_negotiation_get_type (void)
+{
+ static GType plugin_type = 0;
+
+ if (!plugin_type) {
+ static const GTypeInfo plugin_info = {
+ sizeof (GstNegotiationClass),
+ gst_negotiation_base_init,
+ NULL,
+ (GClassInitFunc) gst_negotiation_class_init,
+ NULL,
+ NULL,
+ sizeof (GstNegotiation),
+ 0,
+ (GInstanceInitFunc) gst_negotiation_init,
+ };
+
+ plugin_type = g_type_register_static (GST_TYPE_ELEMENT,
+ "GstNegotiation", &plugin_info, 0);
+ }
+ return plugin_type;
+}
+
+static void
+gst_negotiation_base_init (gpointer g_class)
+{
+ GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&gst_negotiation_sink_factory));
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&gst_negotiation_src_factory));
+ gst_element_class_set_details (element_class, &plugin_details);
+}
+
+static void
+gst_negotiation_class_init (GstNegotiationClass * klass)
+{
+ GObjectClass *gobject_class;
+ GstElementClass *gstelement_class;
+
+ gobject_class = (GObjectClass *) klass;
+ gstelement_class = (GstElementClass *) klass;
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ gobject_class->set_property = gst_negotiation_set_property;
+ gobject_class->get_property = gst_negotiation_get_property;
+
+ g_object_class_install_property (gobject_class, ARG_ALLOWED_CAPS,
+ g_param_spec_boxed ("allowed-caps", "Caps",
+ "The range of formats allowed by " "this element's peers",
+ GST_TYPE_CAPS, G_PARAM_READABLE));
+}
+
+static void
+gst_negotiation_init (GstNegotiation * filter)
+{
+ filter->sinkpad =
+ gst_pad_new_from_static_template (&gst_negotiation_sink_factory, "sink");
+ gst_pad_set_getcaps_function (filter->sinkpad, gst_negotiation_getcaps);
+ gst_pad_set_link_function (filter->sinkpad, gst_negotiation_pad_link);
+ filter->srcpad =
+ gst_pad_new_from_static_template (&gst_negotiation_src_factory, "src");
+ gst_pad_set_getcaps_function (filter->srcpad, gst_negotiation_getcaps);
+ gst_pad_set_link_function (filter->srcpad, gst_negotiation_pad_link);
+
+ gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad);
+ gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);
+ gst_pad_set_chain_function (filter->sinkpad, gst_negotiation_chain);
+}
+
+static GstCaps *
+gst_negotiation_getcaps (GstPad * pad)
+{
+ GstNegotiation *negotiation = GST_NEGOTIATION (gst_pad_get_parent (pad));
+ GstPad *otherpad;
+ GstCaps *caps;
+
+ otherpad = (pad == negotiation->sinkpad) ? negotiation->srcpad :
+ negotiation->sinkpad;
+
+ caps = gst_pad_get_allowed_caps (otherpad);
+
+ GST_ERROR ("getcaps called on %" GST_PTR_FORMAT ", returning %"
+ GST_PTR_FORMAT, pad, caps);
+
+ gst_negotiation_update_caps (negotiation);
+ gst_object_unref (negotiation);
+
+ return caps;
+}
+
+static GstPadLinkReturn
+gst_negotiation_pad_link (GstPad * pad, const GstCaps * caps)
+{
+ GstNegotiation *negotiation = GST_NEGOTIATION (gst_pad_get_parent (pad));
+ GstPad *otherpad;
+ GstPadLinkReturn ret;
+
+ otherpad = (pad == negotiation->sinkpad) ? negotiation->srcpad :
+ negotiation->sinkpad;
+
+ ret = gst_pad_try_set_caps (otherpad, caps);
+
+ GST_ERROR ("pad_link called on %" GST_PTR_FORMAT " with caps %"
+ GST_PTR_FORMAT ", returning %d", pad, caps, ret);
+ gst_object_unref (negotiation);
+
+ return ret;
+}
+
+static void
+gst_negotiation_update_caps (GstNegotiation * negotiation)
+{
+ GstCaps *srccaps;
+ GstCaps *sinkcaps;
+ GstCaps *icaps;
+
+ srccaps = gst_pad_get_allowed_caps (negotiation->srcpad);
+ sinkcaps = gst_pad_get_allowed_caps (negotiation->sinkpad);
+
+ icaps = gst_caps_intersect (srccaps, sinkcaps);
+ gst_caps_free (srccaps);
+ gst_caps_free (sinkcaps);
+
+ gst_caps_replace (&negotiation->caps, icaps);
+ g_object_notify (G_OBJECT (negotiation), "allowed-caps");
+ GST_DEBUG ("notify %" GST_PTR_FORMAT, icaps);
+}
+
+static void
+gst_negotiation_chain (GstPad * pad, GstData * _data)
+{
+ GstNegotiation *negotiation = GST_NEGOTIATION (gst_pad_get_parent (pad));
+
+ gst_pad_push (negotiation->srcpad, _data);
+ gst_object_unref (negotiation);
+}
+
+static void
+gst_negotiation_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstNegotiation *filter;
+
+ g_return_if_fail (GST_IS_NEGOTIATION (object));
+ filter = GST_NEGOTIATION (object);
+
+ switch (prop_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_negotiation_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GstNegotiation *filter;
+
+ g_return_if_fail (GST_IS_NEGOTIATION (object));
+ filter = GST_NEGOTIATION (object);
+
+ switch (prop_id) {
+ case ARG_ALLOWED_CAPS:
+ g_value_set_boxed (value, filter->caps);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
diff --git a/gst/debugutils/progressreport.c b/gst/debugutils/progressreport.c
new file mode 100644
index 00000000..8f2547b3
--- /dev/null
+++ b/gst/debugutils/progressreport.c
@@ -0,0 +1,478 @@
+/* GStreamer Progress Report Element
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ * Copyright (C) <2003> David Schleef <ds@schleef.org>
+ * Copyright (C) <2004> Jan Schmidt <thaytan@mad.scientist.com>
+ * Copyright (C) <2006> 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:element-progressreport
+ *
+ * The progressreport element can be put into a pipeline to report progress,
+ * which is done by doing upstream duration and position queries in regular
+ * (real-time) intervals. Both the interval and the prefered query format
+ * can be specified via the #GstProgressReport:update-freq and the
+ * #GstProgressReport:format property.
+ *
+ * Element messages containing a "progress" structure are posted on the bus
+ * whenever progress has been queried (since gst-plugins-good 0.10.6 only).
+ *
+ * Since the element was originally designed for debugging purposes, it will
+ * by default also print information about the current progress to the
+ * terminal. This can be prevented by setting the #GstProgressReport:silent
+ * property to %TRUE.
+ *
+ * This element is most useful in transcoding pipelines or other situations
+ * where just querying the pipeline might not lead to the wanted result. For
+ * progress in TIME format, the element is best placed in a 'raw stream'
+ * section of the pipeline (or after any demuxers/decoders/parsers).
+ *
+ * Three more things should be pointed out: firstly, the element will only
+ * query progress when data flow happens. If data flow is stalled for some
+ * reason, no progress messages will be posted. Secondly, there are other
+ * elements (like qtdemux, for example) that may also post "progress" element
+ * messages on the bus. Applications should check the source of any element
+ * messages they receive, if needed. Finally, applications should not take
+ * action on receiving notification of progress being 100%, they should only
+ * take action when they receive an EOS message (since the progress reported
+ * is in reference to an internal point of a pipeline and not the pipeline as
+ * a whole).
+ *
+ * <refsect2>
+ * <title>Example launch line</title>
+ * |[
+ * gst-launch -m filesrc location=foo.ogg ! decodebin ! progressreport update-freq=1 ! audioconvert ! audioresample ! autoaudiosink
+ * ]| This shows a progress query where a duration is available.
+ * |[
+ * gst-launch -m audiotestsrc ! progressreport update-freq=1 ! audioconvert ! autoaudiosink
+ * ]| This shows a progress query where no duration is available.
+ * </refsect2>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gst/gst.h>
+#include <string.h>
+#include <math.h>
+#include <time.h>
+
+#include "progressreport.h"
+
+
+enum
+{
+ ARG_0,
+ ARG_UPDATE_FREQ,
+ ARG_SILENT,
+ ARG_FORMAT
+};
+
+GstStaticPadTemplate progress_report_src_template =
+GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS_ANY);
+
+GstStaticPadTemplate progress_report_sink_template =
+GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS_ANY);
+
+static const GstElementDetails progress_report_details =
+GST_ELEMENT_DETAILS ("Progress report",
+ "Testing",
+ "Periodically query and report on processing progress",
+ "Jan Schmidt <thaytan@mad.scientist.com>");
+
+#define DEFAULT_UPDATE_FREQ 5
+#define DEFAULT_SILENT FALSE
+#define DEFAULT_FORMAT "auto"
+
+static void gst_progress_report_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+static void gst_progress_report_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
+
+static gboolean gst_progress_report_event (GstBaseTransform * trans,
+ GstEvent * event);
+static GstFlowReturn gst_progress_report_transform_ip (GstBaseTransform * trans,
+ GstBuffer * buf);
+
+static gboolean gst_progress_report_start (GstBaseTransform * trans);
+static gboolean gst_progress_report_stop (GstBaseTransform * trans);
+
+GST_BOILERPLATE (GstProgressReport, gst_progress_report, GstBaseTransform,
+ GST_TYPE_BASE_TRANSFORM);
+
+static void
+gst_progress_report_base_init (gpointer g_class)
+{
+ GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&progress_report_sink_template));
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&progress_report_src_template));
+
+ gst_element_class_set_details (element_class, &progress_report_details);
+}
+
+static void
+gst_progress_report_finalize (GObject * obj)
+{
+ GstProgressReport *filter = GST_PROGRESS_REPORT (obj);
+
+ g_free (filter->format);
+ filter->format = NULL;
+
+ G_OBJECT_CLASS (parent_class)->finalize (obj);
+}
+
+static void
+gst_progress_report_class_init (GstProgressReportClass * g_class)
+{
+ GstBaseTransformClass *gstbasetrans_class;
+ GObjectClass *gobject_class;
+
+ gobject_class = G_OBJECT_CLASS (g_class);
+ gstbasetrans_class = GST_BASE_TRANSFORM_CLASS (g_class);
+
+ gobject_class->finalize = gst_progress_report_finalize;
+ gobject_class->set_property = gst_progress_report_set_property;
+ gobject_class->get_property = gst_progress_report_get_property;
+
+ g_object_class_install_property (gobject_class,
+ ARG_UPDATE_FREQ, g_param_spec_int ("update-freq", "Update Frequency",
+ "Number of seconds between reports when data is flowing", 1, G_MAXINT,
+ DEFAULT_UPDATE_FREQ, G_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class,
+ ARG_SILENT, g_param_spec_boolean ("silent",
+ "Do not print output to stdout", "Do not print output to stdout",
+ DEFAULT_SILENT, G_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class,
+ ARG_FORMAT, g_param_spec_string ("format", "format",
+ "Format to use for the querying", DEFAULT_FORMAT, G_PARAM_READWRITE));
+
+ gstbasetrans_class->event = GST_DEBUG_FUNCPTR (gst_progress_report_event);
+ gstbasetrans_class->transform_ip =
+ GST_DEBUG_FUNCPTR (gst_progress_report_transform_ip);
+ gstbasetrans_class->start = GST_DEBUG_FUNCPTR (gst_progress_report_start);
+ gstbasetrans_class->stop = GST_DEBUG_FUNCPTR (gst_progress_report_stop);
+}
+
+static void
+gst_progress_report_init (GstProgressReport * report,
+ GstProgressReportClass * g_class)
+{
+ gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (report), TRUE);
+
+ report->update_freq = DEFAULT_UPDATE_FREQ;
+ report->silent = DEFAULT_SILENT;
+ report->format = g_strdup (DEFAULT_FORMAT);
+}
+
+static void
+gst_progress_report_post_progress (GstProgressReport * filter,
+ GstFormat format, gint64 current, gint64 total)
+{
+ GstStructure *s = NULL;
+
+ if (current >= 0 && total > 0) {
+ gdouble perc;
+
+ perc = gst_util_guint64_to_gdouble (current) * 100.0 /
+ gst_util_guint64_to_gdouble (total);
+ perc = CLAMP (perc, 0.0, 100.0);
+
+ /* we provide a "percent" field of integer type to stay compatible
+ * with qtdemux, but add a second "percent-double" field for those who
+ * want more precision and are too lazy to calculate it themselves */
+ s = gst_structure_new ("progress", "percent", G_TYPE_INT, (gint) perc,
+ "percent-double", G_TYPE_DOUBLE, perc, "current", G_TYPE_INT64, current,
+ "total", G_TYPE_INT64, total, NULL);
+ } else if (current >= 0) {
+ s = gst_structure_new ("progress", "current", G_TYPE_INT64, current, NULL);
+ }
+
+ if (s) {
+ GST_LOG_OBJECT (filter, "posting progress message: %" GST_PTR_FORMAT, s);
+ gst_structure_set (s, "format", GST_TYPE_FORMAT, format, NULL);
+ /* can't post it right here because we're holding the object lock */
+ filter->pending_msg = gst_message_new_element (GST_OBJECT_CAST (filter), s);
+ }
+}
+
+static gboolean
+gst_progress_report_do_query (GstProgressReport * filter, GstFormat format,
+ gint hh, gint mm, gint ss)
+{
+ const gchar *format_name = NULL;
+ GstPad *sink_pad;
+ gint64 cur, total;
+
+ sink_pad = GST_BASE_TRANSFORM (filter)->sinkpad;
+
+ GST_LOG_OBJECT (filter, "querying using format %d (%s)", format,
+ gst_format_get_name (format));
+
+ if (!gst_pad_query_peer_position (sink_pad, &format, &cur) ||
+ !gst_pad_query_peer_duration (sink_pad, &format, &total)) {
+ return FALSE;
+ }
+
+ switch (format) {
+ case GST_FORMAT_BYTES:
+ format_name = "bytes";
+ break;
+ case GST_FORMAT_BUFFERS:
+ format_name = "buffers";
+ break;
+ case GST_FORMAT_PERCENT:
+ format_name = "percent";
+ break;
+ case GST_FORMAT_TIME:
+ format_name = "seconds";
+ cur /= GST_SECOND;
+ total /= GST_SECOND;
+ break;
+ case GST_FORMAT_DEFAULT:{
+ GstCaps *caps;
+
+ format_name = "bogounits";
+ caps = GST_PAD_CAPS (GST_BASE_TRANSFORM (filter)->sinkpad);
+ if (caps && gst_caps_is_fixed (caps) && !gst_caps_is_any (caps)) {
+ GstStructure *s = gst_caps_get_structure (caps, 0);
+ const gchar *mime_type = gst_structure_get_name (s);
+
+ if (g_str_has_prefix (mime_type, "video/") ||
+ g_str_has_prefix (mime_type, "image/")) {
+ format_name = "frames";
+ } else if (g_str_has_prefix (mime_type, "audio/")) {
+ format_name = "samples";
+ }
+ }
+ break;
+ }
+ default:{
+ const GstFormatDefinition *details;
+
+ details = gst_format_get_details (format);
+ if (details) {
+ format_name = details->nick;
+ } else {
+ format_name = "unknown";
+ }
+ break;
+ }
+ }
+
+ if (!filter->silent) {
+ if (total > 0) {
+ g_print ("%s (%02d:%02d:%02d): %" G_GINT64_FORMAT " / %"
+ G_GINT64_FORMAT " %s (%4.1f %%)\n", GST_OBJECT_NAME (filter), hh,
+ mm, ss, cur, total, format_name, (gdouble) cur / total * 100.0);
+ } else {
+ g_print ("%s (%02d:%02d:%02d): %" G_GINT64_FORMAT " %s\n",
+ GST_OBJECT_NAME (filter), hh, mm, ss, cur, format_name);
+ }
+ }
+
+ gst_progress_report_post_progress (filter, format, cur, total);
+ return TRUE;
+}
+
+static void
+gst_progress_report_report (GstProgressReport * filter, GTimeVal cur_time)
+{
+ GstFormat try_formats[] = { GST_FORMAT_TIME, GST_FORMAT_BYTES,
+ GST_FORMAT_PERCENT, GST_FORMAT_BUFFERS,
+ GST_FORMAT_DEFAULT
+ };
+ GstMessage *msg;
+ GstFormat format = GST_FORMAT_UNDEFINED;
+ gboolean done = FALSE;
+ glong run_time;
+ gint hh, mm, ss;
+
+ run_time = cur_time.tv_sec - filter->start_time.tv_sec;
+
+ hh = (run_time / 3600) % 100;
+ mm = (run_time / 60) % 60;
+ ss = (run_time % 60);
+
+ GST_OBJECT_LOCK (filter);
+
+ if (filter->format != NULL && strcmp (filter->format, "auto") != 0) {
+ format = gst_format_get_by_nick (filter->format);
+ }
+
+ if (format != GST_FORMAT_UNDEFINED) {
+ done = gst_progress_report_do_query (filter, format, hh, mm, ss);
+ } else {
+ gint i;
+
+ for (i = 0; i < G_N_ELEMENTS (try_formats); ++i) {
+ done = gst_progress_report_do_query (filter, try_formats[i], hh, mm, ss);
+ if (done)
+ break;
+ }
+ }
+
+ if (!done && !filter->silent) {
+ g_print ("%s (%2d:%2d:%2d): Could not query position and/or duration\n",
+ GST_OBJECT_NAME (filter), hh, mm, ss);
+ }
+
+ msg = filter->pending_msg;
+ filter->pending_msg = NULL;
+ GST_OBJECT_UNLOCK (filter);
+
+ if (msg) {
+ gst_element_post_message (GST_ELEMENT_CAST (filter), msg);
+ }
+}
+
+static gboolean
+gst_progress_report_event (GstBaseTransform * trans, GstEvent * event)
+{
+ GstProgressReport *filter;
+
+ filter = GST_PROGRESS_REPORT (trans);
+
+ if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) {
+ GTimeVal cur_time;
+
+ g_get_current_time (&cur_time);
+ gst_progress_report_report (filter, cur_time);
+ }
+ return GST_BASE_TRANSFORM_CLASS (parent_class)->event (trans, event);
+}
+
+static GstFlowReturn
+gst_progress_report_transform_ip (GstBaseTransform * trans, GstBuffer * buf)
+{
+ GstProgressReport *filter;
+ gboolean need_update;
+ GTimeVal cur_time;
+
+ g_get_current_time (&cur_time);
+
+ filter = GST_PROGRESS_REPORT (trans);
+
+ /* Check if update_freq seconds have passed since the last update */
+ GST_OBJECT_LOCK (filter);
+ need_update =
+ ((cur_time.tv_sec - filter->last_report.tv_sec) >= filter->update_freq);
+ GST_OBJECT_UNLOCK (filter);
+
+ if (need_update) {
+ gst_progress_report_report (filter, cur_time);
+ GST_OBJECT_LOCK (filter);
+ filter->last_report = cur_time;
+ GST_OBJECT_UNLOCK (filter);
+ }
+
+ return GST_FLOW_OK;
+}
+
+static gboolean
+gst_progress_report_start (GstBaseTransform * trans)
+{
+ GstProgressReport *filter;
+
+ filter = GST_PROGRESS_REPORT (trans);
+
+ g_get_current_time (&filter->last_report);
+ filter->start_time = filter->last_report;
+
+ return TRUE;
+}
+
+static gboolean
+gst_progress_report_stop (GstBaseTransform * trans)
+{
+ /* anything we should be doing here? */
+ return TRUE;
+}
+
+static void
+gst_progress_report_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstProgressReport *filter;
+
+ filter = GST_PROGRESS_REPORT (object);
+
+ switch (prop_id) {
+ case ARG_UPDATE_FREQ:
+ GST_OBJECT_LOCK (filter);
+ filter->update_freq = g_value_get_int (value);
+ GST_OBJECT_UNLOCK (filter);
+ break;
+ case ARG_SILENT:
+ GST_OBJECT_LOCK (filter);
+ filter->silent = g_value_get_boolean (value);
+ GST_OBJECT_UNLOCK (filter);
+ break;
+ case ARG_FORMAT:
+ GST_OBJECT_LOCK (filter);
+ g_free (filter->format);
+ filter->format = g_value_dup_string (value);
+ if (filter->format == NULL)
+ filter->format = g_strdup ("auto");
+ GST_OBJECT_UNLOCK (filter);
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+gst_progress_report_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GstProgressReport *filter;
+
+ filter = GST_PROGRESS_REPORT (object);
+
+ switch (prop_id) {
+ case ARG_UPDATE_FREQ:
+ GST_OBJECT_LOCK (filter);
+ g_value_set_int (value, filter->update_freq);
+ GST_OBJECT_UNLOCK (filter);
+ break;
+ case ARG_SILENT:
+ GST_OBJECT_LOCK (filter);
+ g_value_set_boolean (value, filter->silent);
+ GST_OBJECT_UNLOCK (filter);
+ break;
+ case ARG_FORMAT:
+ GST_OBJECT_LOCK (filter);
+ g_value_set_string (value, filter->format);
+ GST_OBJECT_UNLOCK (filter);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
diff --git a/gst/debugutils/progressreport.h b/gst/debugutils/progressreport.h
new file mode 100644
index 00000000..92886b29
--- /dev/null
+++ b/gst/debugutils/progressreport.h
@@ -0,0 +1,66 @@
+/* GStreamer Progress Report Element
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ * Copyright (C) <2003> David Schleef <ds@schleef.org>
+ * Copyright (C) <2004> Jan Schmidt <thaytan@mad.scientist.com>
+ * Copyright (C) <2006> 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_PROGRESS_REPORT_H__
+#define __GST_PROGRESS_REPORT_H__
+
+#include <gst/base/gstbasetransform.h>
+
+G_BEGIN_DECLS
+#define GST_TYPE_PROGRESS_REPORT \
+ (gst_progress_report_get_type())
+#define GST_PROGRESS_REPORT(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_PROGRESS_REPORT,GstProgressReport))
+#define GST_PROGRESS_REPORT_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_PROGRESS_REPORT,GstProgressReportClass))
+#define GST_IS_PROGRESS_REPORT(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_PROGRESS_REPORT))
+#define GST_IS_PROGRESS_REPORT_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_PROGRESS_REPORT))
+typedef struct _GstProgressReport GstProgressReport;
+typedef struct _GstProgressReportClass GstProgressReportClass;
+
+struct _GstProgressReport
+{
+ GstBaseTransform basetransform;
+
+ GstMessage *pending_msg;
+
+ gint update_freq;
+ gboolean silent;
+ GTimeVal start_time;
+ GTimeVal last_report;
+
+ /* Format used for querying. Using a string here because the
+ * format might not be registered yet when the property is set */
+ gchar *format;
+};
+
+struct _GstProgressReportClass
+{
+ GstBaseTransformClass parent_class;
+};
+
+GType gst_progress_report_get_type (void);
+
+G_END_DECLS
+#endif /* __GST_PROGRESS_REPORT_H__ */
diff --git a/gst/debugutils/rndbuffersize.c b/gst/debugutils/rndbuffersize.c
new file mode 100644
index 00000000..7a8abd6b
--- /dev/null
+++ b/gst/debugutils/rndbuffersize.c
@@ -0,0 +1,371 @@
+/* GStreamer
+ * Copyright (C) 2007 Nokia Corporation (contact <stefan.kost@nokia.com>)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/**
+ * SECTION:element-rndbuffersize
+ *
+ * This element pulls buffers with random sizes from the source.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <gst/gst.h>
+
+GST_DEBUG_CATEGORY_STATIC (gst_rnd_buffer_size_debug);
+#define GST_CAT_DEFAULT gst_rnd_buffer_size_debug
+
+#define GST_TYPE_RND_BUFFER_SIZE (gst_rnd_buffer_size_get_type())
+#define GST_RND_BUFFER_SIZE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RND_BUFFER_SIZE,GstRndBufferSize))
+#define GST_RND_BUFFER_SIZE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RND_BUFFER_SIZE,GstRndBufferSizeClass))
+#define GST_IS_RND_BUFFER_SIZE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RND_BUFFER_SIZE))
+#define GST_IS_RND_BUFFER_SIZE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RND_BUFFER_SIZE))
+
+typedef struct _GstRndBufferSize GstRndBufferSize;
+typedef struct _GstRndBufferSizeClass GstRndBufferSizeClass;
+
+struct _GstRndBufferSize
+{
+ GstElement parent;
+
+ /*< private > */
+ GRand *rand;
+ gulong seed;
+ glong min, max;
+
+ GstPad *sinkpad, *srcpad;
+ guint64 offset;
+};
+
+struct _GstRndBufferSizeClass
+{
+ GstElementClass parent_class;
+};
+
+enum
+{
+ ARG_SEED = 1,
+ ARG_MINIMUM,
+ ARG_MAXIMUM
+};
+
+#define DEFAULT_SEED 0
+#define DEFAULT_MIN 1
+#define DEFAULT_MAX (8*1024)
+
+static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS_ANY);
+
+static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS_ANY);
+
+static void gst_rnd_buffer_size_finalize (GObject * object);
+static void gst_rnd_buffer_size_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+static void gst_rnd_buffer_size_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
+
+static gboolean gst_rnd_buffer_size_activate (GstPad * pad);
+static gboolean gst_rnd_buffer_size_activate_pull (GstPad * pad,
+ gboolean active);
+static void gst_rnd_buffer_size_loop (GstRndBufferSize * self);
+static GstStateChangeReturn gst_rnd_buffer_size_change_state (GstElement *
+ element, GstStateChange transition);
+
+#define DEBUG_INIT(bla) \
+ GST_DEBUG_CATEGORY_INIT (gst_rnd_buffer_size_debug, "rndbuffersize", 0, \
+ "rndbuffersize element");
+
+GST_BOILERPLATE_FULL (GstRndBufferSize, gst_rnd_buffer_size, GstElement,
+ GST_TYPE_ELEMENT, DEBUG_INIT);
+
+
+static void
+gst_rnd_buffer_size_base_init (gpointer g_class)
+{
+ GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
+
+ gst_element_class_add_pad_template (gstelement_class,
+ gst_static_pad_template_get (&sink_template));
+ gst_element_class_add_pad_template (gstelement_class,
+ gst_static_pad_template_get (&src_template));
+
+ gst_element_class_set_details_simple (gstelement_class, "Random buffer size",
+ "Testing", "pull random sized buffers",
+ "Stefan Kost <stefan.kost@nokia.com>)");
+}
+
+
+static void
+gst_rnd_buffer_size_class_init (GstRndBufferSizeClass * klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
+
+ gobject_class->set_property =
+ GST_DEBUG_FUNCPTR (gst_rnd_buffer_size_set_property);
+ gobject_class->get_property =
+ GST_DEBUG_FUNCPTR (gst_rnd_buffer_size_get_property);
+ gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_rnd_buffer_size_finalize);
+
+ gstelement_class->change_state =
+ GST_DEBUG_FUNCPTR (gst_rnd_buffer_size_change_state);
+
+ /* FIXME 0.11: these should all be int instead of long, to avoid bugs
+ * when passing these as varargs with g_object_set(), and there was no
+ * reason to use long in the first place here */
+ g_object_class_install_property (gobject_class, ARG_SEED,
+ g_param_spec_ulong ("seed", "random number seed",
+ "seed for randomness (initialized when going from READY to PAUSED)",
+ 0, G_MAXUINT32, DEFAULT_SEED, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ g_object_class_install_property (gobject_class, ARG_MINIMUM,
+ g_param_spec_long ("min", "mininum", "mininum buffer size",
+ 0, G_MAXINT32, DEFAULT_MIN, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ g_object_class_install_property (gobject_class, ARG_MAXIMUM,
+ g_param_spec_long ("max", "maximum", "maximum buffer size",
+ 1, G_MAXINT32, DEFAULT_MAX, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+}
+
+static void
+gst_rnd_buffer_size_init (GstRndBufferSize * self,
+ GstRndBufferSizeClass * g_class)
+{
+ self->sinkpad = gst_pad_new_from_static_template (&sink_template, "sink");
+ gst_pad_set_activate_function (self->sinkpad,
+ GST_DEBUG_FUNCPTR (gst_rnd_buffer_size_activate));
+ gst_pad_set_activatepull_function (self->sinkpad,
+ GST_DEBUG_FUNCPTR (gst_rnd_buffer_size_activate_pull));
+ gst_element_add_pad (GST_ELEMENT (self), self->sinkpad);
+
+ self->srcpad = gst_pad_new_from_static_template (&src_template, "src");
+ gst_element_add_pad (GST_ELEMENT (self), self->srcpad);
+}
+
+
+static void
+gst_rnd_buffer_size_finalize (GObject * object)
+{
+ GstRndBufferSize *self = GST_RND_BUFFER_SIZE (object);
+
+ if (self->rand) {
+ g_rand_free (self->rand);
+ self->rand = NULL;
+ }
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+
+static void
+gst_rnd_buffer_size_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstRndBufferSize *self = GST_RND_BUFFER_SIZE (object);
+
+ switch (prop_id) {
+ case ARG_SEED:
+ self->seed = g_value_get_ulong (value);
+ break;
+ case ARG_MINIMUM:
+ self->min = g_value_get_long (value);
+ break;
+ case ARG_MAXIMUM:
+ self->max = g_value_get_long (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+
+static void
+gst_rnd_buffer_size_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GstRndBufferSize *self = GST_RND_BUFFER_SIZE (object);
+
+ switch (prop_id) {
+ case ARG_SEED:
+ g_value_set_ulong (value, self->seed);
+ break;
+ case ARG_MINIMUM:
+ g_value_set_long (value, self->min);
+ break;
+ case ARG_MAXIMUM:
+ g_value_set_long (value, self->max);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+
+static gboolean
+gst_rnd_buffer_size_activate (GstPad * pad)
+{
+ if (gst_pad_check_pull_range (pad)) {
+ return gst_pad_activate_pull (pad, TRUE);
+ } else {
+ GST_INFO_OBJECT (pad, "push mode not supported");
+ return FALSE;
+ }
+}
+
+
+static gboolean
+gst_rnd_buffer_size_activate_pull (GstPad * pad, gboolean active)
+{
+ GstRndBufferSize *self = GST_RND_BUFFER_SIZE (GST_OBJECT_PARENT (pad));
+
+ if (active) {
+ GST_INFO_OBJECT (self, "starting pull");
+ return gst_pad_start_task (pad, (GstTaskFunction) gst_rnd_buffer_size_loop,
+ self);
+ } else {
+ GST_INFO_OBJECT (self, "stopping pull");
+ return gst_pad_stop_task (pad);
+ }
+}
+
+
+static void
+gst_rnd_buffer_size_loop (GstRndBufferSize * self)
+{
+ GstBuffer *buf = NULL;
+ GstFlowReturn ret;
+ guint num_bytes;
+
+ if (G_UNLIKELY (self->min > self->max))
+ goto bogus_minmax;
+
+ if (G_UNLIKELY (self->min != self->max)) {
+ num_bytes = g_rand_int_range (self->rand, self->min, self->max);
+ } else {
+ num_bytes = self->min;
+ }
+
+ GST_LOG_OBJECT (self, "pulling %u bytes at offset %" G_GUINT64_FORMAT,
+ num_bytes, self->offset);
+
+ ret = gst_pad_pull_range (self->sinkpad, self->offset, num_bytes, &buf);
+
+ if (ret != GST_FLOW_OK)
+ goto pull_failed;
+
+ if (GST_BUFFER_SIZE (buf) < num_bytes) {
+ GST_WARNING_OBJECT (self, "short buffer: %u bytes", GST_BUFFER_SIZE (buf));
+ }
+
+ self->offset += GST_BUFFER_SIZE (buf);
+
+ ret = gst_pad_push (self->srcpad, buf);
+
+ if (ret != GST_FLOW_OK)
+ goto push_failed;
+
+ return;
+
+pause_task:
+ {
+ GST_DEBUG_OBJECT (self, "pausing task");
+ gst_pad_pause_task (self->sinkpad);
+ return;
+ }
+
+pull_failed:
+ {
+ if (ret == GST_FLOW_UNEXPECTED) {
+ GST_DEBUG_OBJECT (self, "eos");
+ gst_pad_push_event (self->srcpad, gst_event_new_eos ());
+ } else {
+ GST_WARNING_OBJECT (self, "pull_range flow: %s", gst_flow_get_name (ret));
+ }
+ goto pause_task;
+ }
+
+push_failed:
+ {
+ GST_DEBUG_OBJECT (self, "push flow: %s", gst_flow_get_name (ret));
+ if (ret == GST_FLOW_UNEXPECTED) {
+ GST_DEBUG_OBJECT (self, "eos");
+ gst_pad_push_event (self->srcpad, gst_event_new_eos ());
+ } else if (GST_FLOW_IS_FATAL (ret) || ret == GST_FLOW_NOT_LINKED) {
+ GST_ELEMENT_ERROR (self, STREAM, FAILED,
+ ("Internal data stream error."),
+ ("streaming stopped, reason: %s", gst_flow_get_name (ret)));
+ }
+ goto pause_task;
+ }
+
+bogus_minmax:
+ {
+ GST_ELEMENT_ERROR (self, LIBRARY, SETTINGS,
+ ("The minimum buffer size is smaller than the maximum buffer size."),
+ ("buffer sizes: max=%ld, min=%ld", self->min, self->max));
+ goto pause_task;
+ }
+}
+
+static GstStateChangeReturn
+gst_rnd_buffer_size_change_state (GstElement * element,
+ GstStateChange transition)
+{
+ GstRndBufferSize *self = GST_RND_BUFFER_SIZE (element);
+ GstStateChangeReturn ret;
+
+ switch (transition) {
+ case GST_STATE_CHANGE_NULL_TO_READY:
+ break;
+ case GST_STATE_CHANGE_READY_TO_PAUSED:
+ self->offset = 0;
+ if (!self->rand) {
+ self->rand = g_rand_new_with_seed (self->seed);
+ }
+ break;
+ case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
+ break;
+ default:
+ break;
+ }
+
+ ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+
+ switch (transition) {
+ case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
+ break;
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ if (self->rand) {
+ g_rand_free (self->rand);
+ self->rand = NULL;
+ }
+ break;
+ case GST_STATE_CHANGE_READY_TO_NULL:
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
diff --git a/gst/debugutils/testplugin.c b/gst/debugutils/testplugin.c
new file mode 100644
index 00000000..dbb1e618
--- /dev/null
+++ b/gst/debugutils/testplugin.c
@@ -0,0 +1,318 @@
+/* GStreamer
+ * Copyright (C) 2004 Benjamin Otte <otte@gnome.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.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <gst/gst.h>
+#include <gst/base/gstbasesink.h>
+#include "tests.h"
+
+GST_DEBUG_CATEGORY_STATIC (gst_test_debug);
+#define GST_CAT_DEFAULT gst_test_debug
+
+/* This plugin does all the tests registered in the tests.h file
+ */
+
+#define GST_TYPE_TEST \
+ (gst_test_get_type())
+#define GST_TEST(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_TEST,GstTest))
+#define GST_TEST_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_TEST,GstTestClass))
+#define GST_TEST_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj),GST_TYPE_TEST,GstTestClass))
+#define GST_IS_TEST(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_TEST))
+#define GST_IS_TEST_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_TEST))
+
+typedef struct _GstTest GstTest;
+typedef struct _GstTestClass GstTestClass;
+
+struct _GstTest
+{
+ GstBaseSink basesink;
+
+ gpointer tests[TESTS_COUNT];
+ GValue values[TESTS_COUNT];
+};
+
+struct _GstTestClass
+{
+ GstBaseSinkClass parent_class;
+
+ gchar *param_names[2 * TESTS_COUNT];
+};
+
+static void gst_test_finalize (GstTest * test);
+
+static gboolean gst_test_start (GstBaseSink * trans);
+static gboolean gst_test_stop (GstBaseSink * trans);
+static gboolean gst_test_sink_event (GstBaseSink * basesink, GstEvent * event);
+static GstFlowReturn gst_test_render_buffer (GstBaseSink * basesink,
+ GstBuffer * buf);
+
+static void gst_test_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+static void gst_test_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
+
+static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS_ANY);
+
+
+static const GstElementDetails details = GST_ELEMENT_DETAILS ("Test plugin",
+ "Testing",
+ "perform a number of tests",
+ "Benjamin Otte <otte@gnome>");
+
+#define DEBUG_INIT(bla) \
+ GST_DEBUG_CATEGORY_INIT (gst_test_debug, "testsink", 0, \
+ "debugging category for testsink element");
+
+GST_BOILERPLATE_FULL (GstTest, gst_test, GstBaseSink, GST_TYPE_BASE_SINK,
+ DEBUG_INIT);
+
+
+static void
+gst_test_base_init (gpointer g_class)
+{
+ GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
+
+ gst_element_class_add_pad_template (gstelement_class,
+ gst_static_pad_template_get (&sinktemplate));
+
+ gst_element_class_set_details (gstelement_class, &details);
+}
+
+static void
+gst_test_class_init (GstTestClass * klass)
+{
+ GstBaseSinkClass *basesink_class = GST_BASE_SINK_CLASS (klass);
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ guint i;
+
+ object_class->set_property = GST_DEBUG_FUNCPTR (gst_test_set_property);
+ object_class->get_property = GST_DEBUG_FUNCPTR (gst_test_get_property);
+
+ object_class->finalize = (GObjectFinalizeFunc) gst_test_finalize;
+
+ for (i = 0; i < TESTS_COUNT; i++) {
+ GParamSpec *spec;
+
+ spec = tests[i].get_spec (&tests[i], FALSE);
+ klass->param_names[2 * i] = g_strdup (g_param_spec_get_name (spec));
+ g_object_class_install_property (object_class, 2 * i + 1, spec);
+ spec = tests[i].get_spec (&tests[i], TRUE);
+ klass->param_names[2 * i + 1] = g_strdup (g_param_spec_get_name (spec));
+ g_object_class_install_property (object_class, 2 * i + 2, spec);
+ }
+
+ basesink_class->preroll = GST_DEBUG_FUNCPTR (gst_test_render_buffer);
+ basesink_class->render = GST_DEBUG_FUNCPTR (gst_test_render_buffer);
+ basesink_class->event = GST_DEBUG_FUNCPTR (gst_test_sink_event);
+ basesink_class->start = GST_DEBUG_FUNCPTR (gst_test_start);
+ basesink_class->stop = GST_DEBUG_FUNCPTR (gst_test_stop);
+}
+
+static void
+gst_test_init (GstTest * test, GstTestClass * g_class)
+{
+ GstTestClass *klass;
+ guint i;
+
+ klass = GST_TEST_GET_CLASS (test);
+ for (i = 0; i < TESTS_COUNT; i++) {
+ GParamSpec *spec = g_object_class_find_property (G_OBJECT_CLASS (klass),
+ klass->param_names[2 * i + 1]);
+
+ g_value_init (&test->values[i], G_PARAM_SPEC_VALUE_TYPE (spec));
+ }
+}
+
+static void
+gst_test_finalize (GstTest * test)
+{
+ guint i;
+
+ for (i = 0; i < TESTS_COUNT; i++) {
+ g_value_unset (&test->values[i]);
+ }
+
+ G_OBJECT_CLASS (parent_class)->finalize ((GObject *) test);
+}
+
+static void
+tests_unset (GstTest * test)
+{
+ guint i;
+
+ for (i = 0; i < TESTS_COUNT; i++) {
+ if (test->tests[i]) {
+ tests[i].free (test->tests[i]);
+ test->tests[i] = NULL;
+ }
+ }
+}
+
+static void
+tests_set (GstTest * test)
+{
+ guint i;
+
+ for (i = 0; i < TESTS_COUNT; i++) {
+ g_assert (test->tests[i] == NULL);
+ test->tests[i] = tests[i].new (&tests[i]);
+ }
+}
+
+static gboolean
+gst_test_sink_event (GstBaseSink * basesink, GstEvent * event)
+{
+ GstTestClass *klass = GST_TEST_GET_CLASS (basesink);
+ GstTest *test = GST_TEST (basesink);
+ gboolean ret = FALSE;
+
+ switch (GST_EVENT_TYPE (event)) {
+/*
+ case GST_EVENT_NEWSEGMENT:
+ if (GST_EVENT_DISCONT_NEW_MEDIA (event)) {
+ tests_unset (test);
+ tests_set (test);
+ }
+ break;
+*/
+ case GST_EVENT_EOS:{
+ gint i;
+
+ g_object_freeze_notify (G_OBJECT (test));
+ for (i = 0; i < TESTS_COUNT; i++) {
+ if (test->tests[i]) {
+ if (!tests[i].finish (test->tests[i], &test->values[i])) {
+ GValue v = { 0, };
+ gchar *real, *expected;
+
+ expected = gst_value_serialize (&test->values[i]);
+ g_value_init (&v, G_VALUE_TYPE (&test->values[i]));
+ g_object_get_property (G_OBJECT (test), klass->param_names[2 * i],
+ &v);
+ real = gst_value_serialize (&v);
+ g_value_unset (&v);
+ GST_ELEMENT_ERROR (test, STREAM, FORMAT, (NULL),
+ ("test %s returned value \"%s\" and not expected value \"%s\"",
+ klass->param_names[2 * i], real, expected));
+ g_free (real);
+ g_free (expected);
+ }
+ g_object_notify (G_OBJECT (test), klass->param_names[2 * i]);
+ }
+ }
+ g_object_thaw_notify (G_OBJECT (test));
+ ret = TRUE;
+ break;
+ }
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static GstFlowReturn
+gst_test_render_buffer (GstBaseSink * basesink, GstBuffer * buf)
+{
+ GstTest *test = GST_TEST (basesink);
+ guint i;
+
+ for (i = 0; i < TESTS_COUNT; i++) {
+ if (test->tests[i]) {
+ tests[i].add (test->tests[i], buf);
+ }
+ }
+ return GST_FLOW_OK;
+}
+
+static gboolean
+gst_test_start (GstBaseSink * sink)
+{
+ GstTest *test = GST_TEST (sink);
+
+ tests_set (test);
+ return TRUE;
+}
+
+static gboolean
+gst_test_stop (GstBaseSink * sink)
+{
+ GstTest *test = GST_TEST (sink);
+
+ tests_unset (test);
+ return TRUE;
+}
+
+static void
+gst_test_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstTest *test = GST_TEST (object);
+
+ if (prop_id == 0 || prop_id > 2 * TESTS_COUNT) {
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ return;
+ }
+
+ if (prop_id % 2) {
+ /* real values can't be set */
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ } else {
+ /* expected values */
+ GST_OBJECT_LOCK (test);
+ g_value_copy (value, &test->values[prop_id / 2 - 1]);
+ GST_OBJECT_UNLOCK (test);
+ }
+}
+
+static void
+gst_test_get_property (GObject * object, guint prop_id, GValue * value,
+ GParamSpec * pspec)
+{
+ GstTest *test = GST_TEST (object);
+ guint id = (prop_id - 1) / 2;
+
+ if (prop_id == 0 || prop_id > 2 * TESTS_COUNT) {
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ return;
+ }
+
+ GST_OBJECT_LOCK (test);
+
+ if (prop_id % 2) {
+ /* real values */
+ tests[id].get_value (test->tests[id], value);
+ } else {
+ /* expected values */
+ g_value_copy (&test->values[id], value);
+ }
+
+ GST_OBJECT_UNLOCK (test);
+}
diff --git a/gst/debugutils/tests.c b/gst/debugutils/tests.c
new file mode 100644
index 00000000..cd382782
--- /dev/null
+++ b/gst/debugutils/tests.c
@@ -0,0 +1,568 @@
+/* GStreamer
+ * Copyright (C) 2004 Benjamin Otte <otte@gnome.org>
+ *
+ * includes code based on glibc 2.2.3's crypt/md5.c,
+ * Copyright (C) 1995, 1996, 1997, 1999, 2000 Free Software Foundation, Inc.
+ *
+ * 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 "tests.h"
+#include <stdlib.h>
+#include <string.h>
+
+
+/*
+ *** LENGTH ***
+ */
+
+typedef struct
+{
+ gint64 value;
+}
+LengthTest;
+
+static GParamSpec *
+length_get_spec (const GstTestInfo * info, gboolean compare_value)
+{
+ if (compare_value) {
+ return g_param_spec_int64 ("expected-length", "expected length",
+ "expected length of stream", -1, G_MAXINT64, -1,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+ } else {
+ return g_param_spec_int64 ("length", "length", "length of stream",
+ -1, G_MAXINT64, -1, G_PARAM_READABLE);
+ }
+}
+
+static gpointer
+length_new (const GstTestInfo * info)
+{
+ return g_new0 (LengthTest, 1);
+}
+
+static void
+length_add (gpointer test, GstBuffer * buffer)
+{
+ LengthTest *t = test;
+
+ t->value += GST_BUFFER_SIZE (buffer);
+}
+
+static gboolean
+length_finish (gpointer test, GValue * value)
+{
+ LengthTest *t = test;
+
+ if (g_value_get_int64 (value) == -1)
+ return TRUE;
+
+ return t->value == g_value_get_int64 (value);
+}
+
+static void
+length_get_value (gpointer test, GValue * value)
+{
+ LengthTest *t = test;
+
+ g_value_set_int64 (value, t ? t->value : -1);
+}
+
+/*
+ *** BUFFER COUNT ***
+ */
+
+static GParamSpec *
+buffer_count_get_spec (const GstTestInfo * info, gboolean compare_value)
+{
+ if (compare_value) {
+ return g_param_spec_int64 ("expected-buffer-count", "expected buffer count",
+ "expected number of buffers in stream",
+ -1, G_MAXINT64, -1, G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+ } else {
+ return g_param_spec_int64 ("buffer-count", "buffer count",
+ "number of buffers in stream", -1, G_MAXINT64, -1, G_PARAM_READABLE);
+ }
+}
+
+static void
+buffer_count_add (gpointer test, GstBuffer * buffer)
+{
+ LengthTest *t = test;
+
+ t->value++;
+}
+
+/*
+ *** TIMESTAMP / DURATION MATCHING ***
+ */
+
+typedef struct
+{
+ guint64 diff;
+ guint count;
+ GstClockTime expected;
+}
+TimeDurTest;
+
+static GParamSpec *
+timedur_get_spec (const GstTestInfo * info, gboolean compare_value)
+{
+ if (compare_value) {
+ return g_param_spec_int64 ("allowed-timestamp-deviation",
+ "allowed timestamp deviation",
+ "allowed average difference in usec between timestamp of next buffer "
+ "and expected timestamp from analyzing last buffer",
+ -1, G_MAXINT64, -1, G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+ } else {
+ return g_param_spec_int64 ("timestamp-deviation",
+ "timestamp deviation",
+ "average difference in usec between timestamp of next buffer "
+ "and expected timestamp from analyzing last buffer",
+ -1, G_MAXINT64, -1, G_PARAM_READABLE);
+ }
+}
+
+static gpointer
+timedur_new (const GstTestInfo * info)
+{
+ TimeDurTest *ret = g_new0 (TimeDurTest, 1);
+
+ ret->expected = GST_CLOCK_TIME_NONE;
+
+ return ret;
+}
+
+static void
+timedur_add (gpointer test, GstBuffer * buffer)
+{
+ TimeDurTest *t = test;
+
+ if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer) &&
+ GST_CLOCK_TIME_IS_VALID (t->expected)) {
+ t->diff += labs (GST_BUFFER_TIMESTAMP (buffer) - t->expected);
+ t->count++;
+ }
+ if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer) &&
+ GST_BUFFER_DURATION_IS_VALID (buffer)) {
+ t->expected = GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION (buffer);
+ } else {
+ t->expected = GST_CLOCK_TIME_NONE;
+ }
+}
+
+static gboolean
+timedur_finish (gpointer test, GValue * value)
+{
+ TimeDurTest *t = test;
+
+ if (g_value_get_int64 (value) == -1)
+ return TRUE;
+
+ return (t->diff / MAX (1, t->count)) <= g_value_get_int64 (value);
+}
+
+static void
+timedur_get_value (gpointer test, GValue * value)
+{
+ TimeDurTest *t = test;
+
+ g_value_set_int64 (value, t ? (t->diff / MAX (1, t->count)) : -1);
+}
+
+/*
+ *** MD5 ***
+ */
+
+typedef struct
+{
+ /* md5 information */
+ guint32 A;
+ guint32 B;
+ guint32 C;
+ guint32 D;
+
+ guint32 total[2];
+ guint32 buflen;
+ gchar buffer[128];
+
+ gchar result[33];
+}
+MD5Test;
+
+static void md5_process_block (const void *buffer, size_t len, MD5Test * ctx);
+static void md5_read_ctx (MD5Test * ctx, gchar * result);
+
+static GParamSpec *
+md5_get_spec (const GstTestInfo * info, gboolean compare_value)
+{
+ if (compare_value) {
+ return g_param_spec_string ("expected-md5", "expected md5",
+ "expected md5 of processing the whole data",
+ "---", G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+ } else {
+ return g_param_spec_string ("md5", "md5",
+ "md5 of processing the whole data", "---", G_PARAM_READABLE);
+ }
+}
+
+/* This array contains the bytes used to pad the buffer to the next
+ 64-byte boundary. (RFC 1321, 3.1: Step 1) */
+static const guchar fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ };
+
+/* MD5 functions */
+/* Initialize structure containing state of computation.
+ (RFC 1321, 3.3: Step 3) */
+static gpointer
+md5_new (const GstTestInfo * info)
+{
+ MD5Test *ctx = g_new (MD5Test, 1);
+
+ ctx->A = 0x67452301;
+ ctx->B = 0xefcdab89;
+ ctx->C = 0x98badcfe;
+ ctx->D = 0x10325476;
+
+ ctx->total[0] = ctx->total[1] = 0;
+ ctx->buflen = 0;
+
+ memset (ctx->result, 0, 33);
+
+ return ctx;
+}
+
+/* Process the remaining bytes in the internal buffer and the usual
+ prolog according to the standard and write the result to RESBUF.
+
+ IMPORTANT: On some systems it is required that RESBUF is correctly
+ aligned for a 32 bits value. */
+static gboolean
+md5_finish (gpointer test, GValue * value)
+{
+ MD5Test *ctx = test;
+ const gchar *str_val = g_value_get_string (value);
+
+ /* Take yet unprocessed bytes into account. */
+ guint32 bytes = ctx->buflen;
+ size_t pad;
+
+ /* Now count remaining bytes. */
+ ctx->total[0] += bytes;
+ if (ctx->total[0] < bytes)
+ ++ctx->total[1];
+
+ pad = bytes >= 56 ? 64 + 56 - bytes : 56 - bytes;
+ memcpy (&ctx->buffer[bytes], fillbuf, pad);
+
+ /* Put the 64-bit file length in *bits* at the end of the buffer. */
+ *(guint32 *) & ctx->buffer[bytes + pad] = GUINT32_TO_LE (ctx->total[0] << 3);
+ *(guint32 *) & ctx->buffer[bytes + pad + 4] =
+ GUINT32_TO_LE ((ctx->total[1] << 3) | (ctx->total[0] >> 29));
+
+ /* Process last bytes. */
+ md5_process_block (ctx->buffer, bytes + pad + 8, ctx);
+
+ md5_read_ctx (ctx, ctx->result);
+ if (g_str_equal (str_val, "---"))
+ return TRUE;
+ if (g_str_equal (str_val, ctx->result))
+ return TRUE;
+ return FALSE;
+}
+
+/* Put result from CTX in first 16 bytes following RESBUF. The result
+ must be in little endian byte order.
+
+ IMPORTANT: On some systems it is required that RESBUF is correctly
+ aligned for a 32 bits value. */
+static void
+md5_read_ctx (MD5Test * ctx, gchar * result)
+{
+ guint32 resbuf[4];
+ guint i;
+
+ resbuf[0] = GUINT32_TO_LE (ctx->A);
+ resbuf[1] = GUINT32_TO_LE (ctx->B);
+ resbuf[2] = GUINT32_TO_LE (ctx->C);
+ resbuf[3] = GUINT32_TO_LE (ctx->D);
+
+ for (i = 0; i < 16; i++)
+ sprintf (result + i * 2, "%02x", ((guchar *) resbuf)[i]);
+}
+
+static void
+md5_add (gpointer test, GstBuffer * gstbuffer)
+{
+ const void *buffer = GST_BUFFER_DATA (gstbuffer);
+ gsize len = GST_BUFFER_SIZE (gstbuffer);
+ MD5Test *ctx = test;
+
+ /*const void aligned_buffer = buffer; */
+
+ /* When we already have some bits in our internal buffer concatenate
+ both inputs first. */
+ if (ctx->buflen != 0) {
+ gsize left_over = ctx->buflen;
+ gsize add = 128 - left_over > len ? len : 128 - left_over;
+
+ /* Only put full words in the buffer. */
+ /* Forcing alignment here appears to be only an optimization.
+ * The glibc source uses __alignof__, which seems to be a
+ * gratuitous usage of a GCC extension, when sizeof() will
+ * work fine. (And don't question the sanity of using
+ * sizeof(guint32) instead of 4. */
+ /* add -= add % __alignof__ (guint32); */
+ add -= add % sizeof (guint32);
+
+ memcpy (&ctx->buffer[left_over], buffer, add);
+ ctx->buflen += add;
+
+ if (ctx->buflen > 64) {
+ md5_process_block (ctx->buffer, ctx->buflen & ~63, ctx);
+
+ ctx->buflen &= 63;
+ /* The regions in the following copy operation cannot overlap. */
+ memcpy (ctx->buffer, &ctx->buffer[(left_over + add) & ~63], ctx->buflen);
+ }
+
+ buffer = (const char *) buffer + add;
+ len -= add;
+ }
+
+ /* Process available complete blocks. */
+ if (len > 64) {
+ md5_process_block (buffer, len & ~63, ctx);
+ buffer = (const char *) buffer + (len & ~63);
+ len &= 63;
+ }
+
+ /* Move remaining bytes in internal buffer. */
+ if (len > 0) {
+ size_t left_over = ctx->buflen;
+
+ memcpy (&ctx->buffer[left_over], buffer, len);
+ left_over += len;
+ if (left_over >= 64) {
+ md5_process_block (ctx->buffer, 64, ctx);
+ left_over -= 64;
+ memcpy (ctx->buffer, &ctx->buffer[64], left_over);
+ }
+ ctx->buflen = left_over;
+ }
+}
+
+
+/* These are the four functions used in the four steps of the MD5 algorithm
+ and defined in the RFC 1321. The first function is a little bit optimized
+ (as found in Colin Plumbs public domain implementation). */
+/* #define FF(b, c, d) ((b & c) | (~b & d)) */
+#define FF(b, c, d) (d ^ (b & (c ^ d)))
+#define FG(b, c, d) FF (d, b, c)
+#define FH(b, c, d) (b ^ c ^ d)
+#define FI(b, c, d) (c ^ (b | ~d))
+
+static void
+md5_process_block (const void *buffer, size_t len, MD5Test * ctx)
+{
+/* Process LEN bytes of BUFFER, accumulating context into CTX.
+ It is assumed that LEN % 64 == 0. */
+ guint32 correct_words[16];
+ const guint32 *words = buffer;
+ size_t nwords = len / sizeof (guint32);
+ const guint32 *endp = words + nwords;
+ guint32 A = ctx->A;
+ guint32 B = ctx->B;
+ guint32 C = ctx->C;
+ guint32 D = ctx->D;
+
+ /* First increment the byte count. RFC 1321 specifies the possible
+ length of the file up to 2^64 bits. Here we only compute the
+ number of bytes. Do a double word increment. */
+ ctx->total[0] += len;
+ if (ctx->total[0] < len)
+ ++ctx->total[1];
+
+ /* Process all bytes in the buffer with 64 bytes in each round of
+ the loop. */
+ while (words < endp) {
+ guint32 *cwp = correct_words;
+ guint32 A_save = A;
+ guint32 B_save = B;
+ guint32 C_save = C;
+ guint32 D_save = D;
+
+ /* First round: using the given function, the context and a constant
+ the next context is computed. Because the algorithms processing
+ unit is a 32-bit word and it is determined to work on words in
+ little endian byte order we perhaps have to change the byte order
+ before the computation. To reduce the work for the next steps
+ we store the swapped words in the array CORRECT_WORDS. */
+
+#define OP(a, b, c, d, s, T) \
+ do \
+ { \
+ a += FF (b, c, d) + (*cwp++ = GUINT32_TO_LE (*words)) + T; \
+ ++words; \
+ CYCLIC (a, s); \
+ a += b; \
+ } \
+ while (0)
+
+ /* It is unfortunate that C does not provide an operator for
+ cyclic rotation. Hope the C compiler is smart enough. */
+#define CYCLIC(w, s) (w = (w << s) | (w >> (32 - s)))
+
+ /* Before we start, one word to the strange constants.
+ They are defined in RFC 1321 as
+
+ T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64
+ */
+
+ /* Round 1. */
+ OP (A, B, C, D, 7, 0xd76aa478);
+ OP (D, A, B, C, 12, 0xe8c7b756);
+ OP (C, D, A, B, 17, 0x242070db);
+ OP (B, C, D, A, 22, 0xc1bdceee);
+ OP (A, B, C, D, 7, 0xf57c0faf);
+ OP (D, A, B, C, 12, 0x4787c62a);
+ OP (C, D, A, B, 17, 0xa8304613);
+ OP (B, C, D, A, 22, 0xfd469501);
+ OP (A, B, C, D, 7, 0x698098d8);
+ OP (D, A, B, C, 12, 0x8b44f7af);
+ OP (C, D, A, B, 17, 0xffff5bb1);
+ OP (B, C, D, A, 22, 0x895cd7be);
+ OP (A, B, C, D, 7, 0x6b901122);
+ OP (D, A, B, C, 12, 0xfd987193);
+ OP (C, D, A, B, 17, 0xa679438e);
+ OP (B, C, D, A, 22, 0x49b40821);
+
+ /* For the second to fourth round we have the possibly swapped words
+ in CORRECT_WORDS. Redefine the macro to take an additional first
+ argument specifying the function to use. */
+#undef OP
+#define OP(f, a, b, c, d, k, s, T) \
+ do \
+ { \
+ a += f (b, c, d) + correct_words[k] + T; \
+ CYCLIC (a, s); \
+ a += b; \
+ } \
+ while (0)
+
+ /* Round 2. */
+ OP (FG, A, B, C, D, 1, 5, 0xf61e2562);
+ OP (FG, D, A, B, C, 6, 9, 0xc040b340);
+ OP (FG, C, D, A, B, 11, 14, 0x265e5a51);
+ OP (FG, B, C, D, A, 0, 20, 0xe9b6c7aa);
+ OP (FG, A, B, C, D, 5, 5, 0xd62f105d);
+ OP (FG, D, A, B, C, 10, 9, 0x02441453);
+ OP (FG, C, D, A, B, 15, 14, 0xd8a1e681);
+ OP (FG, B, C, D, A, 4, 20, 0xe7d3fbc8);
+ OP (FG, A, B, C, D, 9, 5, 0x21e1cde6);
+ OP (FG, D, A, B, C, 14, 9, 0xc33707d6);
+ OP (FG, C, D, A, B, 3, 14, 0xf4d50d87);
+ OP (FG, B, C, D, A, 8, 20, 0x455a14ed);
+ OP (FG, A, B, C, D, 13, 5, 0xa9e3e905);
+ OP (FG, D, A, B, C, 2, 9, 0xfcefa3f8);
+ OP (FG, C, D, A, B, 7, 14, 0x676f02d9);
+ OP (FG, B, C, D, A, 12, 20, 0x8d2a4c8a);
+
+ /* Round 3. */
+ OP (FH, A, B, C, D, 5, 4, 0xfffa3942);
+ OP (FH, D, A, B, C, 8, 11, 0x8771f681);
+ OP (FH, C, D, A, B, 11, 16, 0x6d9d6122);
+ OP (FH, B, C, D, A, 14, 23, 0xfde5380c);
+ OP (FH, A, B, C, D, 1, 4, 0xa4beea44);
+ OP (FH, D, A, B, C, 4, 11, 0x4bdecfa9);
+ OP (FH, C, D, A, B, 7, 16, 0xf6bb4b60);
+ OP (FH, B, C, D, A, 10, 23, 0xbebfbc70);
+ OP (FH, A, B, C, D, 13, 4, 0x289b7ec6);
+ OP (FH, D, A, B, C, 0, 11, 0xeaa127fa);
+ OP (FH, C, D, A, B, 3, 16, 0xd4ef3085);
+ OP (FH, B, C, D, A, 6, 23, 0x04881d05);
+ OP (FH, A, B, C, D, 9, 4, 0xd9d4d039);
+ OP (FH, D, A, B, C, 12, 11, 0xe6db99e5);
+ OP (FH, C, D, A, B, 15, 16, 0x1fa27cf8);
+ OP (FH, B, C, D, A, 2, 23, 0xc4ac5665);
+
+ /* Round 4. */
+ OP (FI, A, B, C, D, 0, 6, 0xf4292244);
+ OP (FI, D, A, B, C, 7, 10, 0x432aff97);
+ OP (FI, C, D, A, B, 14, 15, 0xab9423a7);
+ OP (FI, B, C, D, A, 5, 21, 0xfc93a039);
+ OP (FI, A, B, C, D, 12, 6, 0x655b59c3);
+ OP (FI, D, A, B, C, 3, 10, 0x8f0ccc92);
+ OP (FI, C, D, A, B, 10, 15, 0xffeff47d);
+ OP (FI, B, C, D, A, 1, 21, 0x85845dd1);
+ OP (FI, A, B, C, D, 8, 6, 0x6fa87e4f);
+ OP (FI, D, A, B, C, 15, 10, 0xfe2ce6e0);
+ OP (FI, C, D, A, B, 6, 15, 0xa3014314);
+ OP (FI, B, C, D, A, 13, 21, 0x4e0811a1);
+ OP (FI, A, B, C, D, 4, 6, 0xf7537e82);
+ OP (FI, D, A, B, C, 11, 10, 0xbd3af235);
+ OP (FI, C, D, A, B, 2, 15, 0x2ad7d2bb);
+ OP (FI, B, C, D, A, 9, 21, 0xeb86d391);
+
+ /* Add the starting values of the context. */
+ A += A_save;
+ B += B_save;
+ C += C_save;
+ D += D_save;
+ }
+
+ /* Put checksum in context given as argument. */
+ ctx->A = A;
+ ctx->B = B;
+ ctx->C = C;
+ ctx->D = D;
+}
+
+static void
+md5_get_value (gpointer test, GValue * value)
+{
+ MD5Test *ctx = test;
+
+ if (!ctx) {
+ g_value_set_string (value, "---");
+ } else if (ctx->result[0] == 0) {
+ gchar *str = g_new (gchar, 33);
+
+ str[32] = 0;
+ md5_read_ctx (ctx, str);
+ g_value_take_string (value, str);
+ } else {
+ g_value_set_string (value, ctx->result);
+ }
+}
+
+/*
+ *** TESTINFO ***
+ */
+
+const GstTestInfo tests[] = {
+ {length_get_spec, length_new, length_add,
+ length_finish, length_get_value, g_free},
+ {buffer_count_get_spec, length_new, buffer_count_add,
+ length_finish, length_get_value, g_free},
+ {timedur_get_spec, timedur_new, timedur_add,
+ timedur_finish, timedur_get_value, g_free},
+ {md5_get_spec, md5_new, md5_add,
+ md5_finish, md5_get_value, g_free}
+};
diff --git a/gst/debugutils/tests.h b/gst/debugutils/tests.h
new file mode 100644
index 00000000..9926af62
--- /dev/null
+++ b/gst/debugutils/tests.h
@@ -0,0 +1,43 @@
+/* GStreamer
+ * Copyright (C) 2004 Benjamin Otte <otte@gnome.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.
+ */
+
+#include <gst/gst.h>
+
+#ifndef __GST_TESTS_H__
+#define __GST_TESTS_H__
+
+
+typedef struct _GstTestInfo GstTestInfo;
+
+struct _GstTestInfo
+{
+ GParamSpec *(*get_spec) (const GstTestInfo * info, gboolean compare_value);
+ gpointer (*new) (const GstTestInfo * info);
+ void (*add) (gpointer test, GstBuffer * buffer);
+ gboolean (*finish) (gpointer test, GValue * value);
+ void (*get_value) (gpointer test, GValue * value);
+ void (*free) (gpointer test);
+};
+
+extern const GstTestInfo tests[];
+/* keep up to date! */
+#define TESTS_COUNT (4)
+
+
+#endif /* __GST_TESTS_H__ */