diff options
Diffstat (limited to 'gst/debugutils')
-rw-r--r-- | gst/debugutils/Makefile.am | 49 | ||||
-rw-r--r-- | gst/debugutils/breakmydata.c | 297 | ||||
-rw-r--r-- | gst/debugutils/debug.vcproj | 166 | ||||
-rw-r--r-- | gst/debugutils/efence.c | 537 | ||||
-rw-r--r-- | gst/debugutils/efence.h | 50 | ||||
-rw-r--r-- | gst/debugutils/efence.vcproj | 148 | ||||
-rw-r--r-- | gst/debugutils/gstdebug.c | 63 | ||||
-rw-r--r-- | gst/debugutils/gstnavigationtest.c | 357 | ||||
-rw-r--r-- | gst/debugutils/gstnavigationtest.h | 68 | ||||
-rw-r--r-- | gst/debugutils/gstnavseek.c | 340 | ||||
-rw-r--r-- | gst/debugutils/gstnavseek.h | 60 | ||||
-rw-r--r-- | gst/debugutils/gstpushfilesrc.c | 195 | ||||
-rw-r--r-- | gst/debugutils/gstpushfilesrc.h | 56 | ||||
-rw-r--r-- | gst/debugutils/gsttaginject.c | 204 | ||||
-rw-r--r-- | gst/debugutils/gsttaginject.h | 66 | ||||
-rw-r--r-- | gst/debugutils/navigationtest.vcproj | 148 | ||||
-rw-r--r-- | gst/debugutils/negotiation.c | 287 | ||||
-rw-r--r-- | gst/debugutils/progressreport.c | 478 | ||||
-rw-r--r-- | gst/debugutils/progressreport.h | 66 | ||||
-rw-r--r-- | gst/debugutils/rndbuffersize.c | 371 | ||||
-rw-r--r-- | gst/debugutils/testplugin.c | 318 | ||||
-rw-r--r-- | gst/debugutils/tests.c | 568 | ||||
-rw-r--r-- | gst/debugutils/tests.h | 43 |
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;"../../gst-libs";../../../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;"../../gst-libs";../../../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;"../../gst-libs";../../../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;"../../gst-libs";../../../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;"../../gst-libs";../../../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;"../../gst-libs";../../../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__ */ |