diff options
Diffstat (limited to 'gst/debugutils')
24 files changed, 4739 insertions, 0 deletions
diff --git a/gst/debugutils/breakmydata.c b/gst/debugutils/breakmydata.c new file mode 100644 index 0000000000..558d25f85c --- /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., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +/** + * SECTION:element-breakmydata + * @title: 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> + +#include "gstdebugutilselements.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)) + +GType gst_break_my_data_get_type (void); + +enum +{ + PROP_0, + PROP_SEED, + PROP_SET_TO, + PROP_SKIP, + PROP_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); + +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 gst_break_my_data_parent_class parent_class +G_DEFINE_TYPE (GstBreakMyData, gst_break_my_data, GST_TYPE_BASE_TRANSFORM); +GST_ELEMENT_REGISTER_DEFINE (breakmydata, "breakmydata", + GST_RANK_NONE, gst_break_my_data_get_type ()); + +static void +gst_break_my_data_class_init (GstBreakMyDataClass * klass) +{ + GstBaseTransformClass *gstbasetrans_class; + GstElementClass *gstelement_class; + GObjectClass *gobject_class; + + gobject_class = G_OBJECT_CLASS (klass); + gstelement_class = GST_ELEMENT_CLASS (klass); + gstbasetrans_class = GST_BASE_TRANSFORM_CLASS (klass); + + GST_DEBUG_CATEGORY_INIT (gst_break_my_data_debug, "breakmydata", 0, + "debugging category for breakmydata element"); + + gobject_class->set_property = gst_break_my_data_set_property; + gobject_class->get_property = gst_break_my_data_get_property; + + g_object_class_install_property (gobject_class, PROP_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_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_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_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_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_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_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 | G_PARAM_STATIC_STRINGS)); + + gst_element_class_add_static_pad_template (gstelement_class, + &bmd_sink_template); + gst_element_class_add_static_pad_template (gstelement_class, + &bmd_src_template); + + gst_element_class_set_static_metadata (gstelement_class, "Break my data", + "Testing", + "randomly change data in the stream", "Benjamin Otte <otte@gnome>"); + + 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) +{ + gst_base_transform_set_in_place (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 PROP_SEED: + bmd->seed = g_value_get_uint (value); + break; + case PROP_SET_TO: + bmd->set = g_value_get_int (value); + break; + case PROP_SKIP: + bmd->skip = g_value_get_uint (value); + break; + case PROP_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 PROP_SEED: + g_value_set_uint (value, bmd->seed); + break; + case PROP_SET_TO: + g_value_set_int (value, bmd->set); + break; + case PROP_SKIP: + g_value_set_uint (value, bmd->skip); + break; + case PROP_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); + GstMapInfo map; + gsize i; + + 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; + } + + gst_buffer_map (buf, &map, GST_MAP_READWRITE); + + GST_LOG_OBJECT (bmd, + "got buffer %p (size %" G_GSIZE_FORMAT ", timestamp %" G_GUINT64_FORMAT + ", offset %" G_GUINT64_FORMAT "", buf, map.size, + GST_BUFFER_TIMESTAMP (buf), GST_BUFFER_OFFSET (buf)); + + for (; i < map.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 %" G_GSIZE_FORMAT " from 0x%02X to 0x%02X", i, + (guint) GST_READ_UINT8 (map.data + i), (guint) ((guint8) new)); + map.data[i] = new; + } + } + /* don't overflow */ + bmd->skipped += MIN (G_MAXUINT - bmd->skipped, map.size); + + gst_buffer_unmap (buf, &map); + + 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/cpureport.c b/gst/debugutils/cpureport.c new file mode 100644 index 0000000000..058fc7c5e6 --- /dev/null +++ b/gst/debugutils/cpureport.c @@ -0,0 +1,149 @@ +/* GStreamer Cpu Report Element + * Copyright (C) <2010> Zaheer Abbas Merali <zaheerabbas merali 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., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gst/gst.h> +#include <string.h> +#include <math.h> +#include <time.h> + +#include "gstdebugutilselements.h" +#include "cpureport.h" + + +enum +{ + PROP_0, +}; + +GstStaticPadTemplate cpu_report_src_template = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + +GstStaticPadTemplate cpu_report_sink_template = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + +static GstFlowReturn gst_cpu_report_transform_ip (GstBaseTransform * trans, + GstBuffer * buf); + +static gboolean gst_cpu_report_start (GstBaseTransform * trans); +static gboolean gst_cpu_report_stop (GstBaseTransform * trans); + +#define gst_cpu_report_parent_class parent_class +G_DEFINE_TYPE (GstCpuReport, gst_cpu_report, GST_TYPE_BASE_TRANSFORM); +GST_ELEMENT_REGISTER_DEFINE (cpureport, "cpureport", + GST_RANK_NONE, gst_cpu_report_get_type ()); + +static void +gst_cpu_report_finalize (GObject * obj) +{ + G_OBJECT_CLASS (parent_class)->finalize (obj); +} + +static void +gst_cpu_report_class_init (GstCpuReportClass * g_class) +{ + GstBaseTransformClass *gstbasetrans_class; + GstElementClass *element_class; + GObjectClass *gobject_class; + + gobject_class = G_OBJECT_CLASS (g_class); + element_class = GST_ELEMENT_CLASS (g_class); + gstbasetrans_class = GST_BASE_TRANSFORM_CLASS (g_class); + + gobject_class->finalize = gst_cpu_report_finalize; + + gst_element_class_add_static_pad_template (element_class, + &cpu_report_sink_template); + gst_element_class_add_static_pad_template (element_class, + &cpu_report_src_template); + + gst_element_class_set_static_metadata (element_class, "CPU report", + "Testing", + "Post cpu usage information every buffer", + "Zaheer Abbas Merali <zaheerabbas at merali dot org>"); + + gstbasetrans_class->transform_ip = + GST_DEBUG_FUNCPTR (gst_cpu_report_transform_ip); + gstbasetrans_class->start = GST_DEBUG_FUNCPTR (gst_cpu_report_start); + gstbasetrans_class->stop = GST_DEBUG_FUNCPTR (gst_cpu_report_stop); +} + +static void +gst_cpu_report_init (GstCpuReport * report) +{ + gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (report), TRUE); + +} + +static GstFlowReturn +gst_cpu_report_transform_ip (GstBaseTransform * trans, GstBuffer * buf) +{ + GstCpuReport *filter; + GstClockTime cur_time; + clock_t cur_cpu_time; + GstMessage *msg; + GstStructure *s; + GstClockTimeDiff time_taken; + + cur_time = g_get_real_time () * GST_USECOND; + cur_cpu_time = clock (); + + filter = GST_CPU_REPORT (trans); + + + time_taken = cur_time - filter->last_time; + + s = gst_structure_new ("cpu-report", "cpu-time", G_TYPE_DOUBLE, + ((gdouble) (cur_cpu_time - filter->last_cpu_time)), + "actual-time", G_TYPE_INT64, time_taken, "buffer-time", G_TYPE_INT64, + GST_BUFFER_TIMESTAMP (buf), NULL); + msg = gst_message_new_element (GST_OBJECT_CAST (filter), s); + gst_element_post_message (GST_ELEMENT_CAST (filter), msg); + filter->last_time = cur_time; + filter->last_cpu_time = cur_cpu_time; + + + return GST_FLOW_OK; +} + +static gboolean +gst_cpu_report_start (GstBaseTransform * trans) +{ + GstCpuReport *filter; + + filter = GST_CPU_REPORT (trans); + + filter->start_time = filter->last_time = g_get_real_time () * GST_USECOND; + filter->last_cpu_time = clock (); + return TRUE; +} + +static gboolean +gst_cpu_report_stop (GstBaseTransform * trans) +{ + /* anything we should be doing here? */ + return TRUE; +} diff --git a/gst/debugutils/cpureport.h b/gst/debugutils/cpureport.h new file mode 100644 index 0000000000..1735e733cc --- /dev/null +++ b/gst/debugutils/cpureport.h @@ -0,0 +1,58 @@ +/* GStreamer CPU Report Element + * Copyright (C) <2010> Zaheer Abbas Merali <zaheerabbas merali 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., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_CPU_REPORT_H__ +#define __GST_CPU_REPORT_H__ + +#include <time.h> + +#include <gst/base/gstbasetransform.h> + +G_BEGIN_DECLS +#define GST_TYPE_CPU_REPORT \ + (gst_cpu_report_get_type()) +#define GST_CPU_REPORT(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_CPU_REPORT,GstCpuReport)) +#define GST_CPU_REPORT_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_CPU_REPORT,GstCpuReportClass)) +#define GST_IS_CPU_REPORT(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_CPU_REPORT)) +#define GST_IS_CPU_REPORT_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_CPU_REPORT)) +typedef struct _GstCpuReport GstCpuReport; +typedef struct _GstCpuReportClass GstCpuReportClass; + +struct _GstCpuReport +{ + GstBaseTransform basetransform; + + GstClockTime start_time; + GstClockTime last_time; + clock_t last_cpu_time; +}; + +struct _GstCpuReportClass +{ + GstBaseTransformClass parent_class; +}; + +GType gst_cpu_report_get_type (void); + +G_END_DECLS +#endif /* __GST_CPU_REPORT_H__ */ diff --git a/gst/debugutils/gstcapsdebug.c b/gst/debugutils/gstcapsdebug.c new file mode 100644 index 0000000000..84d75caf6a --- /dev/null +++ b/gst/debugutils/gstcapsdebug.c @@ -0,0 +1,261 @@ +/* GStreamer + * Copyright (C) 2010 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., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gst/gst.h> +#include <gst/gst.h> +#include "gstdebugutilselements.h" +#include "gstcapsdebug.h" + +GST_DEBUG_CATEGORY_STATIC (gst_caps_debug_debug); +#define GST_CAT_DEFAULT gst_caps_debug_debug + +/* prototypes */ + + +static void gst_caps_debug_dispose (GObject * object); +static void gst_caps_debug_finalize (GObject * object); + +static GstFlowReturn gst_caps_debug_sink_chain (GstPad * pad, + GstBuffer * buffer); +static GstCaps *gst_caps_debug_getcaps (GstPad * pad); +static gboolean gst_caps_debug_acceptcaps (GstPad * pad, GstCaps * caps); +static GstFlowReturn gst_caps_debug_bufferalloc (GstPad * pad, + guint64 offset, guint size, GstCaps * caps, GstBuffer ** buf); + +static GstStateChangeReturn +gst_caps_debug_change_state (GstElement * element, GstStateChange transition); + +/* pad templates */ + +static GstStaticPadTemplate gst_caps_debug_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + +static GstStaticPadTemplate gst_caps_debug_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + +/* class initialization */ + +#define gst_caps_debug_parent_class parent_class +G_DEFINE_TYPE (GstCapsDebug, gst_caps_debug, GST_TYPE_ELEMENT); +GST_ELEMENT_REGISTER_DEFINE (capsdebug, "capsdebug", + GST_RANK_PRIMARY, gst_caps_debug_get_type ()); + +static void +gst_caps_debug_class_init (GstCapsDebugClass * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + + gobject_class->dispose = gst_caps_debug_dispose; + gobject_class->finalize = gst_caps_debug_finalize; + element_class->change_state = GST_DEBUG_FUNCPTR (gst_caps_debug_change_state); + + GST_DEBUG_CATEGORY_INIT (gst_caps_debug_debug, "capsdebug", 0, + "debug category for capsdebug element"); + + gst_element_class_add_static_pad_template (element_class, + &gst_caps_debug_src_template); + gst_element_class_add_static_pad_template (element_class, + &gst_caps_debug_sink_template); + + gst_element_class_set_static_metadata (element_class, "Caps debug", + "Generic", "Debug caps negotiation", "David Schleef <ds@schleef.org>"); +} + +static void +gst_caps_debug_init (GstCapsDebug * capsdebug) +{ + + capsdebug->srcpad = + gst_pad_new_from_static_template (&gst_caps_debug_src_template, "src"); + gst_pad_set_getcaps_function (capsdebug->srcpad, + GST_DEBUG_FUNCPTR (gst_caps_debug_getcaps)); + gst_pad_set_acceptcaps_function (capsdebug->srcpad, + GST_DEBUG_FUNCPTR (gst_caps_debug_acceptcaps)); + gst_element_add_pad (GST_ELEMENT (capsdebug), capsdebug->srcpad); + + capsdebug->sinkpad = + gst_pad_new_from_static_template (&gst_caps_debug_sink_template, "sink"); + gst_pad_set_chain_function (capsdebug->sinkpad, + GST_DEBUG_FUNCPTR (gst_caps_debug_sink_chain)); + gst_pad_set_bufferalloc_function (capsdebug->sinkpad, + GST_DEBUG_FUNCPTR (gst_caps_debug_bufferalloc)); + gst_pad_set_getcaps_function (capsdebug->sinkpad, + GST_DEBUG_FUNCPTR (gst_caps_debug_getcaps)); + gst_pad_set_acceptcaps_function (capsdebug->sinkpad, + GST_DEBUG_FUNCPTR (gst_caps_debug_acceptcaps)); + gst_element_add_pad (GST_ELEMENT (capsdebug), capsdebug->sinkpad); + +} + +void +gst_caps_debug_dispose (GObject * object) +{ + /* clean up as possible. may be called multiple times */ + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +void +gst_caps_debug_finalize (GObject * object) +{ + /* clean up object here */ + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + + + +static GstStateChangeReturn +gst_caps_debug_change_state (GstElement * element, GstStateChange transition) +{ + GstStateChangeReturn ret; + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + return ret; +} + + +static GstFlowReturn +gst_caps_debug_sink_chain (GstPad * pad, GstBuffer * buffer) +{ + GstFlowReturn ret; + GstCapsDebug *capsdebug; + + capsdebug = GST_CAPS_DEBUG (gst_pad_get_parent (pad)); + + ret = gst_pad_push (capsdebug->srcpad, buffer); + + gst_object_unref (capsdebug); + + return ret; +} + +#define THISPAD ((pad == capsdebug->srcpad) ? "downstream" : "upstream") +#define OTHERPAD ((pad == capsdebug->srcpad) ? "upstream" : "downstream") + +static GstCaps * +gst_caps_debug_getcaps (GstPad * pad) +{ + GstCaps *caps; + GstCapsDebug *capsdebug; + gchar *s; + GstPad *otherpad; + + capsdebug = GST_CAPS_DEBUG (gst_pad_get_parent (pad)); + otherpad = + (pad == capsdebug->srcpad) ? capsdebug->sinkpad : capsdebug->srcpad; + + GST_INFO ("%s called getcaps", THISPAD); + + caps = gst_pad_peer_get_caps (otherpad); + + s = gst_caps_to_string (caps); + GST_INFO ("%s returned %s", OTHERPAD, s); + g_free (s); + + if (caps == NULL) + caps = gst_caps_new_any (); + + gst_object_unref (capsdebug); + + return caps; +} + + +static gboolean +gst_caps_debug_acceptcaps (GstPad * pad, GstCaps * caps) +{ + GstCapsDebug *capsdebug; + gchar *s; + gboolean ret; + GstPad *otherpad; + + capsdebug = GST_CAPS_DEBUG (gst_pad_get_parent (pad)); + otherpad = + (pad == capsdebug->srcpad) ? capsdebug->sinkpad : capsdebug->srcpad; + + s = gst_caps_to_string (caps); + GST_INFO ("%s called acceptcaps with %s", THISPAD, s); + g_free (s); + + ret = gst_pad_peer_accept_caps (otherpad, caps); + + GST_INFO ("%s returned %s", OTHERPAD, ret ? "TRUE" : "FALSE"); + + gst_object_unref (capsdebug); + + return ret; +} + +static GstFlowReturn +gst_caps_debug_bufferalloc (GstPad * pad, guint64 offset, guint size, + GstCaps * caps, GstBuffer ** buf) +{ + GstCapsDebug *capsdebug; + gchar *s; + gchar *t; + GstFlowReturn ret; + GstPad *otherpad; + gboolean newcaps; + + capsdebug = GST_CAPS_DEBUG (gst_pad_get_parent (pad)); + otherpad = + (pad == capsdebug->srcpad) ? capsdebug->sinkpad : capsdebug->srcpad; + + newcaps = (caps != GST_PAD_CAPS (pad)); + + if (newcaps) { + s = gst_caps_to_string (caps); + GST_INFO ("%s called bufferalloc with new caps, offset=%" G_GUINT64_FORMAT + " size=%d caps=%s", THISPAD, offset, size, s); + g_free (s); + } + + ret = gst_pad_alloc_buffer_and_set_caps (otherpad, offset, size, caps, buf); + + if (newcaps) { + GST_INFO ("%s returned %s", OTHERPAD, gst_flow_get_name (ret)); + } + if (caps != GST_BUFFER_CAPS (*buf)) { + s = gst_caps_to_string (caps); + t = gst_caps_to_string (GST_BUFFER_CAPS (*buf)); + GST_INFO + ("%s returned from bufferalloc with different caps, requested=%s returned=%s", + OTHERPAD, s, t); + g_free (s); + g_free (t); + } + + gst_object_unref (capsdebug); + + return ret; +} diff --git a/gst/debugutils/gstcapsdebug.h b/gst/debugutils/gstcapsdebug.h new file mode 100644 index 0000000000..9d0930dcc6 --- /dev/null +++ b/gst/debugutils/gstcapsdebug.h @@ -0,0 +1,55 @@ +/* GStreamer + * Copyright (C) 2010 FIXME <fixme@example.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., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _GST_CAPS_DEBUG_H_ +#define _GST_CAPS_DEBUG_H_ + +#include <gst/gst.h> +#include <gst/gst.h> + +G_BEGIN_DECLS + +#define GST_TYPE_CAPS_DEBUG (gst_caps_debug_get_type()) +#define GST_CAPS_DEBUG(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_CAPS_DEBUG,GstCapsDebug)) +#define GST_CAPS_DEBUG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_CAPS_DEBUG,GstCapsDebugClass)) +#define GST_IS_CAPS_DEBUG(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_CAPS_DEBUG)) +#define GST_IS_CAPS_DEBUG_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_CAPS_DEBUG)) + +typedef struct _GstCapsDebug GstCapsDebug; +typedef struct _GstCapsDebugClass GstCapsDebugClass; + +struct _GstCapsDebug +{ + GstElement base_capsdebug; + + GstPad *srcpad; + GstPad *sinkpad; + +}; + +struct _GstCapsDebugClass +{ + GstElementClass base_capsdebug_class; +}; + +GType gst_caps_debug_get_type (void); + +G_END_DECLS + +#endif diff --git a/gst/debugutils/gstcapssetter.c b/gst/debugutils/gstcapssetter.c new file mode 100644 index 0000000000..f6dad048d8 --- /dev/null +++ b/gst/debugutils/gstcapssetter.c @@ -0,0 +1,335 @@ +/* GStreamer Element + * Copyright (C) 2006-2009 Mark Nauwelaerts <mnauw@users.sourceforge.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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1307, USA. + */ + +/** + * SECTION:element-capssetter + * @title: capssetter + * + * Sets or merges caps on a stream's buffers. That is, a buffer's caps are + * updated using (fields of) #GstCapsSetter:caps. Note that this may contain + * multiple structures (though not likely recommended), but each of these must + * be fixed (or will otherwise be rejected). + * + * If #GstCapsSetter:join is %TRUE, then the incoming caps' mime-type is + * compared to the mime-type(s) of provided caps and only matching structure(s) + * are considered for updating. + * + * If #GstCapsSetter:replace is %TRUE, then any caps update is preceded by + * clearing existing fields, making provided fields (as a whole) replace + * incoming ones. Otherwise, no clearing is performed, in which case provided + * fields are added/merged onto incoming caps + * + * Although this element might mainly serve as debug helper, + * it can also practically be used to correct a faulty pixel-aspect-ratio, + * or to modify a yuv fourcc value to effectively swap chroma components or such + * alike. + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstdebugutilselements.h" +#include "gstcapssetter.h" + +#include <string.h> + + +GST_DEBUG_CATEGORY_STATIC (caps_setter_debug); +#define GST_CAT_DEFAULT caps_setter_debug + + +/* signals and args */ +enum +{ + /* FILL ME */ + LAST_SIGNAL +}; + +enum +{ + PROP_0, + PROP_CAPS, + PROP_JOIN, + PROP_REPLACE + /* FILL ME */ +}; + +#define DEFAULT_JOIN TRUE +#define DEFAULT_REPLACE FALSE + +static GstStaticPadTemplate gst_caps_setter_src_template = +GST_STATIC_PAD_TEMPLATE (GST_BASE_TRANSFORM_SRC_NAME, + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + +static GstStaticPadTemplate gst_caps_setter_sink_template = +GST_STATIC_PAD_TEMPLATE (GST_BASE_TRANSFORM_SINK_NAME, + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + + +static gboolean gst_caps_setter_transform_size (GstBaseTransform * trans, + GstPadDirection direction, GstCaps * caps, gsize size, + GstCaps * othercaps, gsize * othersize); +static GstCaps *gst_caps_setter_transform_caps (GstBaseTransform * trans, + GstPadDirection direction, GstCaps * caps, GstCaps * cfilter); +static GstFlowReturn gst_caps_setter_transform_ip (GstBaseTransform * btrans, + GstBuffer * in); + +static void gst_caps_setter_finalize (GObject * object); + +static void gst_caps_setter_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_caps_setter_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +#define gst_caps_setter_parent_class parent_class +G_DEFINE_TYPE (GstCapsSetter, gst_caps_setter, GST_TYPE_BASE_TRANSFORM); +GST_ELEMENT_REGISTER_DEFINE (capssetter, "capssetter", + GST_RANK_NONE, gst_caps_setter_get_type ()); + +static void +gst_caps_setter_class_init (GstCapsSetterClass * g_class) +{ + GObjectClass *gobject_class = (GObjectClass *) g_class; + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + GstBaseTransformClass *trans_class = (GstBaseTransformClass *) g_class; + + GST_DEBUG_CATEGORY_INIT (caps_setter_debug, "capssetter", 0, "capssetter"); + + gobject_class->set_property = gst_caps_setter_set_property; + gobject_class->get_property = gst_caps_setter_get_property; + + gobject_class->finalize = gst_caps_setter_finalize; + + g_object_class_install_property (gobject_class, PROP_CAPS, + g_param_spec_boxed ("caps", "Merge caps", + "Merge these caps (thereby overwriting) in the stream", + GST_TYPE_CAPS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_JOIN, + g_param_spec_boolean ("join", "Join", + "Match incoming caps' mime-type to mime-type of provided caps", + DEFAULT_JOIN, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_REPLACE, + g_param_spec_boolean ("replace", "Replace", + "Drop fields of incoming caps", DEFAULT_REPLACE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + gst_element_class_set_static_metadata (element_class, "CapsSetter", + "Generic", + "Set/merge caps on stream", + "Mark Nauwelaerts <mnauw@users.sourceforge.net>"); + + gst_element_class_add_static_pad_template (element_class, + &gst_caps_setter_sink_template); + gst_element_class_add_static_pad_template (element_class, + &gst_caps_setter_src_template); + + trans_class->transform_size = + GST_DEBUG_FUNCPTR (gst_caps_setter_transform_size); + trans_class->transform_caps = + GST_DEBUG_FUNCPTR (gst_caps_setter_transform_caps); + /* dummy seems needed */ + trans_class->transform_ip = GST_DEBUG_FUNCPTR (gst_caps_setter_transform_ip); +} + +static void +gst_caps_setter_init (GstCapsSetter * filter) +{ + filter->caps = gst_caps_new_any (); + filter->join = DEFAULT_JOIN; + filter->replace = DEFAULT_REPLACE; +} + +static void +gst_caps_setter_finalize (GObject * object) +{ + GstCapsSetter *filter = GST_CAPS_SETTER (object); + + gst_caps_replace (&filter->caps, NULL); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gboolean +gst_caps_setter_transform_size (GstBaseTransform * trans, + GstPadDirection direction, GstCaps * caps, gsize size, + GstCaps * othercaps, gsize * othersize) +{ + *othersize = size; + + return TRUE; +} + +static GstCaps * +gst_caps_setter_transform_caps (GstBaseTransform * trans, + GstPadDirection direction, GstCaps * caps, GstCaps * cfilter) +{ + GstCapsSetter *filter = GST_CAPS_SETTER (trans); + GstCaps *ret = NULL, *filter_caps = NULL; + GstStructure *structure, *merge; + const gchar *name; + gint i, j, k; + + GST_DEBUG_OBJECT (trans, + "receiving caps: %" GST_PTR_FORMAT ", with filter: %" GST_PTR_FORMAT, + caps, cfilter); + + /* pass filter caps upstream, or any if no filter */ + if (direction != GST_PAD_SINK) { + if (!cfilter || gst_caps_is_empty (cfilter)) { + return gst_caps_ref (GST_CAPS_ANY); + } else { + return gst_caps_ref (cfilter); + } + } + + ret = gst_caps_copy (caps); + + GST_OBJECT_LOCK (filter); + filter_caps = gst_caps_ref (filter->caps); + GST_OBJECT_UNLOCK (filter); + + for (k = 0; k < gst_caps_get_size (ret); k++) { + structure = gst_caps_get_structure (ret, k); + name = gst_structure_get_name (structure); + + for (i = 0; i < gst_caps_get_size (filter_caps); ++i) { + merge = gst_caps_get_structure (filter_caps, i); + if (gst_structure_has_name (merge, name) || !filter->join) { + + if (!filter->join) + gst_structure_set_name (structure, gst_structure_get_name (merge)); + + if (filter->replace) + gst_structure_remove_all_fields (structure); + + for (j = 0; j < gst_structure_n_fields (merge); ++j) { + const gchar *fname; + + fname = gst_structure_nth_field_name (merge, j); + gst_structure_set_value (structure, fname, + gst_structure_get_value (merge, fname)); + } + } + } + } + + GST_DEBUG_OBJECT (trans, "returning caps: %" GST_PTR_FORMAT, ret); + + gst_caps_unref (filter_caps); + + return ret; +} + +static GstFlowReturn +gst_caps_setter_transform_ip (GstBaseTransform * btrans, GstBuffer * in) +{ + return GST_FLOW_OK; +} + +static gboolean +gst_caps_is_fixed_foreach (GQuark field_id, const GValue * value, + gpointer unused) +{ + return gst_value_is_fixed (value); +} + +static void +gst_caps_setter_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstCapsSetter *filter = GST_CAPS_SETTER (object); + + switch (prop_id) { + case PROP_CAPS:{ + GstCaps *new_caps; + const GstCaps *new_caps_val = gst_value_get_caps (value); + gint i; + + if (new_caps_val == NULL) { + new_caps = gst_caps_new_any (); + } else { + new_caps = gst_caps_copy (new_caps_val); + } + + for (i = 0; new_caps && (i < gst_caps_get_size (new_caps)); ++i) { + GstStructure *s; + + s = gst_caps_get_structure (new_caps, i); + if (!gst_structure_foreach (s, gst_caps_is_fixed_foreach, NULL)) { + GST_ERROR_OBJECT (filter, "rejected unfixed caps: %" GST_PTR_FORMAT, + new_caps); + gst_caps_unref (new_caps); + new_caps = NULL; + break; + } + } + + if (new_caps) { + GST_OBJECT_LOCK (filter); + gst_caps_replace (&filter->caps, new_caps); + /* drop extra ref */ + gst_caps_unref (new_caps); + GST_OBJECT_UNLOCK (filter); + + GST_DEBUG_OBJECT (filter, "set new caps %" GST_PTR_FORMAT, new_caps); + } + + /* try to activate these new caps next time around */ + gst_base_transform_reconfigure_src (GST_BASE_TRANSFORM (filter)); + break; + } + case PROP_JOIN: + filter->join = g_value_get_boolean (value); + break; + case PROP_REPLACE: + filter->replace = g_value_get_boolean (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_caps_setter_get_property (GObject * object, guint prop_id, GValue * value, + GParamSpec * pspec) +{ + GstCapsSetter *filter = GST_CAPS_SETTER (object); + + switch (prop_id) { + case PROP_CAPS: + gst_value_set_caps (value, filter->caps); + break; + case PROP_JOIN: + g_value_set_boolean (value, filter->join); + break; + case PROP_REPLACE: + g_value_set_boolean (value, filter->replace); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} diff --git a/gst/debugutils/gstcapssetter.h b/gst/debugutils/gstcapssetter.h new file mode 100644 index 0000000000..246bf19b5f --- /dev/null +++ b/gst/debugutils/gstcapssetter.h @@ -0,0 +1,63 @@ +/* GStreamer Element + * Copyright (C) 2006-2009 Mark Nauwelaerts <mnauw@users.sourceforge.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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1307, USA. + */ + + +#ifndef __GST_CAPS_SETTER_H__ +#define __GST_CAPS_SETTER_H__ + +#include <gst/base/gstbasetransform.h> + +G_BEGIN_DECLS + +#define GST_TYPE_CAPS_SETTER \ + (gst_caps_setter_get_type()) +#define GST_CAPS_SETTER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_CAPS_SETTER,GstCapsSetter)) +#define GST_CAPS_SETTER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_CAPS_SETTER,GstCapsSetterClass)) +#define GST_IS_CAPS_SETTER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_CAPS_SETTER)) +#define GST_IS_CAPS_SETTER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_CAPS_SETTER)) + +GType gst_caps_setter_get_type (void); + +typedef struct _GstCapsSetter GstCapsSetter; +typedef struct _GstCapsSetterClass GstCapsSetterClass; + +struct _GstCapsSetter +{ + GstBaseTransform parent; + + /* < private > */ + /* properties */ + GstCaps *caps; + gboolean join; + gboolean replace; +}; + + +struct _GstCapsSetterClass +{ + GstBaseTransformClass parent_class; +}; + +G_END_DECLS + +#endif /* __GST_CAPS_SETTER_H__ */ diff --git a/gst/debugutils/gstdebug.c b/gst/debugutils/gstdebug.c new file mode 100644 index 0000000000..7e2446d508 --- /dev/null +++ b/gst/debugutils/gstdebug.c @@ -0,0 +1,55 @@ +/* GStreamer + * Copyright (C) 2004 Benjamin Otte <otte@gnome.org> + * Copyright (C) 2020 Huawei Technologies Co., Ltd. + * @Author: Stéphane Cerveau <stephane.cerveau@collabora.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <gst/gst.h> + +#include "gstdebugutilselements.h" + +static gboolean +plugin_init (GstPlugin * plugin) +{ + gboolean ret = FALSE; + + ret |= GST_ELEMENT_REGISTER (breakmydata, plugin); + ret |= GST_ELEMENT_REGISTER (capssetter, plugin); + ret |= GST_ELEMENT_REGISTER (rndbuffersize, plugin); + ret |= GST_ELEMENT_REGISTER (navseek, plugin); + ret |= GST_ELEMENT_REGISTER (pushfilesrc, plugin); + ret |= GST_ELEMENT_REGISTER (progressreport, plugin); + ret |= GST_ELEMENT_REGISTER (taginject, plugin); + ret |= GST_ELEMENT_REGISTER (testsink, plugin); +#if 0 + ret |= GST_ELEMENT_REGISTER (capsdebug, plugin); +#endif + ret |= GST_ELEMENT_REGISTER (cpureport, plugin); + + return ret; +} + +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/gstdebugutilselements.h b/gst/debugutils/gstdebugutilselements.h new file mode 100644 index 0000000000..ad6522cc6f --- /dev/null +++ b/gst/debugutils/gstdebugutilselements.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2004 Benjamin Otte <otte@gnome.org> + * Copyright (C) 2020 Huawei Technologies Co., Ltd. + * @Author: Stéphane Cerveau <stephane.cerveau@collabora.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_DEBUGUTILS_ELEMENTS_H__ +#define __GST_DEBUGUTILS_ELEMENTS_H__ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gst/gst.h> + +G_BEGIN_DECLS + +GST_ELEMENT_REGISTER_DECLARE (breakmydata); +GST_ELEMENT_REGISTER_DECLARE (capssetter); +GST_ELEMENT_REGISTER_DECLARE (rndbuffersize); +GST_ELEMENT_REGISTER_DECLARE (navseek); +GST_ELEMENT_REGISTER_DECLARE (pushfilesrc); +GST_ELEMENT_REGISTER_DECLARE (progressreport); +GST_ELEMENT_REGISTER_DECLARE (taginject); +GST_ELEMENT_REGISTER_DECLARE (testsink); +#if 0 +GST_ELEMENT_REGISTER_DECLARE (capsdebug); +#endif +GST_ELEMENT_REGISTER_DECLARE (cpureport); + +G_END_DECLS + +#endif /* __GST_DEBUGUTILS_ELEMENTS_H__ */ diff --git a/gst/debugutils/gstnavigationtest.c b/gst/debugutils/gstnavigationtest.c new file mode 100644 index 0000000000..eb2361fb6e --- /dev/null +++ b/gst/debugutils/gstnavigationtest.c @@ -0,0 +1,278 @@ +/* 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., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, 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 GstStaticPadTemplate gst_navigationtest_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("I420")) + ); + +static GstStaticPadTemplate gst_navigationtest_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("I420")) + ); + +#define gst_navigationtest_parent_class parent_class +G_DEFINE_TYPE (GstNavigationtest, gst_navigationtest, GST_TYPE_VIDEO_FILTER); +GST_ELEMENT_REGISTER_DEFINE (navigationtest, "navigationtest", GST_RANK_NONE, + GST_TYPE_NAVIGATIONTEST); + +static gboolean +gst_navigationtest_src_event (GstBaseTransform * trans, GstEvent * event) +{ + GstVideoInfo *info; + GstNavigationtest *navtest; + const gchar *type; + + navtest = GST_NAVIGATIONTEST (trans); + + info = &GST_VIDEO_FILTER (trans)->in_info; + + 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_VIDEO_INFO_FPS_N (info); + fps_d = GST_VIDEO_INFO_FPS_D (info); + + 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_BASE_TRANSFORM_CLASS (parent_class)->src_event (trans, 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 void +draw_box_planar411 (GstVideoFrame * frame, int x, int y, + guint8 colory, guint8 coloru, guint8 colorv) +{ + gint width, height; + int x1, x2, y1, y2; + guint8 *d; + gint stride; + + width = GST_VIDEO_FRAME_WIDTH (frame); + height = GST_VIDEO_FRAME_HEIGHT (frame); + + 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); + + d = GST_VIDEO_FRAME_PLANE_DATA (frame, 0); + stride = GST_VIDEO_FRAME_PLANE_STRIDE (frame, 0); + + for (y = y1; y < y2; y++) { + for (x = x1; x < x2; x++) { + d[y * stride + x] = colory; + } + } + + d = GST_VIDEO_FRAME_PLANE_DATA (frame, 1); + stride = GST_VIDEO_FRAME_PLANE_STRIDE (frame, 1); + + x1 /= 2; + x2 /= 2; + y1 /= 2; + y2 /= 2; + for (y = y1; y < y2; y++) { + for (x = x1; x < x2; x++) { + d[y * stride + x] = coloru; + } + } + + d = GST_VIDEO_FRAME_PLANE_DATA (frame, 2); + stride = GST_VIDEO_FRAME_PLANE_STRIDE (frame, 2); + + for (y = y1; y < y2; y++) { + for (x = x1; x < x2; x++) { + d[y * stride + x] = colorv; + } + } +} + +static GstFlowReturn +gst_navigationtest_transform_frame (GstVideoFilter * filter, + GstVideoFrame * in_frame, GstVideoFrame * out_frame) +{ + GstNavigationtest *navtest = GST_NAVIGATIONTEST (filter); + GSList *walk; + + gst_video_frame_copy (out_frame, in_frame); + + walk = navtest->clicks; + while (walk) { + ButtonClick *click = walk->data; + + walk = g_slist_next (walk); + draw_box_planar411 (out_frame, + 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 (out_frame, + rint (navtest->x), rint (navtest->y), 0, 128, 128); + + return GST_FLOW_OK; +} + +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_class_init (GstNavigationtestClass * klass) +{ + GstElementClass *element_class; + GstBaseTransformClass *trans_class; + GstVideoFilterClass *vfilter_class; + + element_class = (GstElementClass *) klass; + trans_class = (GstBaseTransformClass *) klass; + vfilter_class = (GstVideoFilterClass *) klass; + + element_class->change_state = + GST_DEBUG_FUNCPTR (gst_navigationtest_change_state); + + gst_element_class_set_static_metadata (element_class, "Video navigation test", + "Filter/Effect/Video", + "Handle navigation events showing a black square following mouse pointer", + "David Schleef <ds@schleef.org>"); + + gst_element_class_add_static_pad_template (element_class, + &gst_navigationtest_sink_template); + gst_element_class_add_static_pad_template (element_class, + &gst_navigationtest_src_template); + + trans_class->src_event = GST_DEBUG_FUNCPTR (gst_navigationtest_src_event); + + vfilter_class->transform_frame = + GST_DEBUG_FUNCPTR (gst_navigationtest_transform_frame); +} + +static void +gst_navigationtest_init (GstNavigationtest * navtest) +{ + navtest->x = -1; + navtest->y = -1; +} + +static gboolean +plugin_init (GstPlugin * plugin) +{ + GST_DEBUG_CATEGORY_INIT (navigationtest_debug, "navigationtest", 0, + "navigationtest"); + + return GST_ELEMENT_REGISTER (navigationtest, plugin); +} + +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 0000000000..c412359329 --- /dev/null +++ b/gst/debugutils/gstnavigationtest.h @@ -0,0 +1,67 @@ +/* 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., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + + +#ifndef __GST_NAVIGATIONTEST_H__ +#define __GST_NAVIGATIONTEST_H__ + +#include <gst/video/video.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; + + gdouble x, y; + GSList *clicks; +}; + +struct _GstNavigationtestClass +{ + GstVideoFilterClass parent_class; +}; + +GType gst_navigationtest_get_type (void); + +GST_ELEMENT_REGISTER_DECLARE (navigationtest); + +G_END_DECLS +#endif /* __GST_NAVIGATIONTEST_H__ */ diff --git a/gst/debugutils/gstnavseek.c b/gst/debugutils/gstnavseek.c new file mode 100644 index 0000000000..cfd854f386 --- /dev/null +++ b/gst/debugutils/gstnavseek.c @@ -0,0 +1,432 @@ +/* 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., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, 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 "gstdebugutilselements.h" +#include "gstnavseek.h" +#include <string.h> +#include <math.h> + +enum +{ + PROP_0, + PROP_SEEKOFFSET, + PROP_HOLD_EOS, +}; + +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 gboolean gst_navseek_sink_event (GstBaseTransform * trans, + GstEvent * event); +static GstFlowReturn gst_navseek_transform_ip (GstBaseTransform * basetrans, + GstBuffer * buf); +static gboolean gst_navseek_src_event (GstBaseTransform * trans, + 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); + +GType gst_navseek_get_type (void); +#define gst_navseek_parent_class parent_class +G_DEFINE_TYPE (GstNavSeek, gst_navseek, GST_TYPE_BASE_TRANSFORM); +GST_ELEMENT_REGISTER_DEFINE (navseek, "navseek", + GST_RANK_NONE, gst_navseek_get_type ()); + +static void +gst_navseek_class_init (GstNavSeekClass * klass) +{ + GstBaseTransformClass *gstbasetrans_class; + GstElementClass *element_class; + GObjectClass *gobject_class; + + gobject_class = G_OBJECT_CLASS (klass); + element_class = GST_ELEMENT_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, + PROP_SEEKOFFSET, g_param_spec_double ("seek-offset", "Seek Offset", + "Time in seconds to seek by", 0.0, G_MAXDOUBLE, 5.0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * navseek:hold-eos: + * + * Hold eos until the next 'Return' keystroke. + * + * Since: 1.20 + */ + g_object_class_install_property (gobject_class, + PROP_HOLD_EOS, g_param_spec_boolean ("hold-eos", "Hold EOS", + "Hold eos until the next 'Return' keystroke", FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + gst_element_class_add_static_pad_template (element_class, + &navseek_sink_template); + gst_element_class_add_static_pad_template (element_class, + &navseek_src_template); + + gst_element_class_set_static_metadata (element_class, + "Seek based on left-right arrows", "Filter/Video", + "Seek based on navigation keys left-right", + "Jan Schmidt <thaytan@mad.scientist.com>"); + + gstbasetrans_class->src_event = GST_DEBUG_FUNCPTR (gst_navseek_src_event); + gstbasetrans_class->sink_event = GST_DEBUG_FUNCPTR (gst_navseek_sink_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) +{ + gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (navseek), TRUE); + + navseek->seek_offset = 5.0; + navseek->loop = FALSE; + navseek->hold_eos = FALSE; + navseek->eos = NULL; + 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) +{ + 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, GST_FORMAT_TIME, &peer_value); + + if (ret) { + 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_change_playback_rate (GstNavSeek * navseek, gdouble rate) +{ + gboolean ret; + GstPad *peer_pad; + gint64 current_position; + + peer_pad = gst_pad_get_peer (GST_BASE_TRANSFORM (navseek)->sinkpad); + ret = gst_pad_query_position (peer_pad, GST_FORMAT_TIME, ¤t_position); + + if (ret) { + GstEvent *event; + gint64 start; + gint64 stop; + + if (rate > 0.0) { + start = current_position; + stop = -1; + } else { + /* negative rate: we play from stop to start */ + start = 0; + stop = current_position; + } + + event = gst_event_new_seek (rate, GST_FORMAT_TIME, + GST_SEEK_FLAG_ACCURATE | GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_SKIP, + GST_SEEK_TYPE_SET, start, GST_SEEK_TYPE_SET, stop); + + 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 void +gst_navseek_toggle_play_pause (GstNavSeek * navseek) +{ + GstStateChangeReturn sret; + GstState current, pending, state; + + sret = gst_element_get_state (GST_ELEMENT (navseek), ¤t, &pending, 0); + if (sret == GST_STATE_CHANGE_FAILURE) + return; + + state = (pending != GST_STATE_VOID_PENDING) ? pending : current; + + gst_element_post_message (GST_ELEMENT (navseek), + gst_message_new_request_state (GST_OBJECT (navseek), + (state == GST_STATE_PLAYING) ? GST_STATE_PAUSED : GST_STATE_PLAYING)); +} + +static gboolean +gst_navseek_src_event (GstBaseTransform * trans, GstEvent * event) +{ + GstNavSeek *navseek; + gboolean ret = TRUE; + + navseek = GST_NAVSEEK (trans); + + 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 if (strcmp (key, "f") == 0) { + /* fast forward */ + gst_navseek_change_playback_rate (navseek, 2.0); + } else if (strcmp (key, "r") == 0) { + /* rewind */ + gst_navseek_change_playback_rate (navseek, -2.0); + } else if (strcmp (key, "n") == 0) { + /* normal speed */ + gst_navseek_change_playback_rate (navseek, 1.0); + } else if (strcmp (key, "space") == 0) { + gst_navseek_toggle_play_pause (navseek); + } else if (strcmp (key, "Return") == 0) { + if (navseek->eos) { + gst_pad_push_event (GST_BASE_TRANSFORM (navseek)->srcpad, + navseek->eos); + navseek->eos = NULL; + } + } + } else { + break; + } + gst_event_unref (event); + event = NULL; + break; + } + default: + break; + } + + if (event) + ret = GST_BASE_TRANSFORM_CLASS (parent_class)->src_event (trans, event); + + 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 PROP_SEEKOFFSET: + GST_OBJECT_LOCK (navseek); + navseek->seek_offset = g_value_get_double (value); + GST_OBJECT_UNLOCK (navseek); + break; + case PROP_HOLD_EOS: + GST_OBJECT_LOCK (navseek); + navseek->hold_eos = g_value_get_boolean (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 PROP_SEEKOFFSET: + GST_OBJECT_LOCK (navseek); + g_value_set_double (value, navseek->seek_offset); + GST_OBJECT_UNLOCK (navseek); + break; + case PROP_HOLD_EOS: + GST_OBJECT_LOCK (navseek); + g_value_set_boolean (value, navseek->hold_eos); + GST_OBJECT_UNLOCK (navseek); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +gst_navseek_sink_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); + if (navseek->hold_eos) + navseek->eos = event; + GST_OBJECT_UNLOCK (navseek); + if (navseek->eos) + return TRUE; + break; + default: + break; + } + return GST_BASE_TRANSFORM_CLASS (parent_class)->sink_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) +{ + GstNavSeek *navseek = GST_NAVSEEK (trans); + + if (navseek->eos) { + gst_event_unref (navseek->eos); + navseek->eos = NULL; + } + return TRUE; +} diff --git a/gst/debugutils/gstnavseek.h b/gst/debugutils/gstnavseek.h new file mode 100644 index 0000000000..6b596b21e0 --- /dev/null +++ b/gst/debugutils/gstnavseek.h @@ -0,0 +1,62 @@ +/* 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., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, 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 hold_eos; + GstEvent* eos; + 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 0000000000..d9f3b36060 --- /dev/null +++ b/gst/debugutils/gstpushfilesrc.c @@ -0,0 +1,424 @@ +/* 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., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION:element-pushfilesrc + * @title: 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). + * + * ## Example launch line + * |[ + * gst-launch-1.0 -m playbin uri=pushfile:///home/you/some/file.ogg + * ]| This plays back the given file using playbin, with the demuxer operating + * push-based. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstdebugutilselements.h" +#include "gstpushfilesrc.h" + +#include <gst/gst.h> + +GST_DEBUG_CATEGORY_STATIC (pushfilesrc_debug); +#define GST_CAT_DEFAULT pushfilesrc_debug + +enum +{ + PROP_0, + PROP_LOCATION, + PROP_TIME_SEGMENT, + PROP_STREAM_TIME, + PROP_START_TIME, + PROP_INITIAL_TIMESTAMP, + PROP_RATE, + PROP_APPLIED_RATE +}; + +#define DEFAULT_TIME_SEGMENT FALSE +#define DEFAULT_STREAM_TIME 0 +#define DEFAULT_START_TIME 0 +#define DEFAULT_INITIAL_TIMESTAMP GST_CLOCK_TIME_NONE +#define DEFAULT_RATE 1.0 +#define DEFAULT_APPLIED_RATE 1.0 + +static void gst_push_file_src_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec); +static void gst_push_file_src_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec); + +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); + +#define gst_push_file_src_parent_class parent_class +G_DEFINE_TYPE_WITH_CODE (GstPushFileSrc, gst_push_file_src, GST_TYPE_BIN, + G_IMPLEMENT_INTERFACE (GST_TYPE_URI_HANDLER, + gst_push_file_src_uri_handler_init)); +GST_ELEMENT_REGISTER_DEFINE (pushfilesrc, "pushfilesrc", + GST_RANK_NONE, gst_push_file_src_get_type ()); + +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; + GstElementClass *element_class; + + gobject_class = G_OBJECT_CLASS (g_class); + element_class = GST_ELEMENT_CLASS (g_class); + + GST_DEBUG_CATEGORY_INIT (pushfilesrc_debug, "pushfilesrc", 0, + "pushfilesrc element"); + + gobject_class->dispose = gst_push_file_src_dispose; + gobject_class->set_property = gst_push_file_src_set_property; + gobject_class->get_property = gst_push_file_src_get_property; + + g_object_class_install_property (gobject_class, PROP_LOCATION, + g_param_spec_string ("location", "File Location", + "Location of the file to read", NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_READY)); + + g_object_class_install_property (gobject_class, PROP_TIME_SEGMENT, + g_param_spec_boolean ("time-segment", "Time Segment", + "Emit TIME SEGMENTS", DEFAULT_TIME_SEGMENT, G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, PROP_STREAM_TIME, + g_param_spec_int64 ("stream-time", "Stream Time", + "Initial Stream Time (if time-segment TRUE)", 0, G_MAXINT64, + DEFAULT_STREAM_TIME, G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, PROP_START_TIME, + g_param_spec_int64 ("start-time", "Start Time", + "Initial Start Time (if time-segment TRUE)", 0, G_MAXINT64, + DEFAULT_START_TIME, G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, PROP_INITIAL_TIMESTAMP, + g_param_spec_uint64 ("initial-timestamp", "Initial Timestamp", + "Initial Buffer Timestamp (if time-segment TRUE)", 0, G_MAXUINT64, + DEFAULT_INITIAL_TIMESTAMP, G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, PROP_RATE, + g_param_spec_double ("rate", "Rate", "Rate to use in TIME SEGMENT", + G_MINDOUBLE, G_MAXDOUBLE, DEFAULT_RATE, G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, PROP_APPLIED_RATE, + g_param_spec_double ("applied-rate", "Applied Rate", + "Applied rate to use in TIME SEGMENT", G_MINDOUBLE, G_MAXDOUBLE, + DEFAULT_APPLIED_RATE, G_PARAM_READWRITE)); + + gst_element_class_add_static_pad_template (element_class, &srctemplate); + + gst_element_class_set_static_metadata (element_class, "Push File Source", + "Testing", + "Implements pushfile:// URI-handler for push-based file access", + "Tim-Philipp Müller <tim centricular net>"); +} + +static void +gst_push_file_src_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstPushFileSrc *src = (GstPushFileSrc *) object; + + switch (prop_id) { + case PROP_LOCATION: + g_object_set_property (G_OBJECT (src->filesrc), "location", value); + break; + case PROP_TIME_SEGMENT: + src->time_segment = g_value_get_boolean (value); + break; + case PROP_STREAM_TIME: + src->stream_time = g_value_get_int64 (value); + break; + case PROP_START_TIME: + src->start_time = g_value_get_int64 (value); + break; + case PROP_INITIAL_TIMESTAMP: + src->initial_timestamp = g_value_get_uint64 (value); + break; + case PROP_RATE: + src->rate = g_value_get_double (value); + break; + case PROP_APPLIED_RATE: + src->applied_rate = g_value_get_double (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_push_file_src_get_property (GObject * object, guint prop_id, GValue * value, + GParamSpec * pspec) +{ + GstPushFileSrc *src = (GstPushFileSrc *) object; + + switch (prop_id) { + case PROP_LOCATION: + g_object_get_property (G_OBJECT (src->filesrc), "location", value); + break; + case PROP_TIME_SEGMENT: + g_value_set_boolean (value, src->time_segment); + break; + case PROP_STREAM_TIME: + g_value_set_int64 (value, src->stream_time); + break; + case PROP_START_TIME: + g_value_set_int64 (value, src->start_time); + break; + case PROP_INITIAL_TIMESTAMP: + g_value_set_uint64 (value, src->initial_timestamp); + break; + case PROP_RATE: + g_value_set_double (value, src->rate); + break; + case PROP_APPLIED_RATE: + g_value_set_double (value, src->applied_rate); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static GstPadProbeReturn +gst_push_file_src_ghostpad_buffer_probe (GstPad * pad, GstPadProbeInfo * info, + GstPushFileSrc * src) +{ + GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER (info); + + if (src->time_segment && !src->seen_first_buffer) { + GST_BUFFER_TIMESTAMP (buffer) = src->initial_timestamp; + src->seen_first_buffer = TRUE; + } + return GST_PAD_PROBE_OK; +} + +static GstPadProbeReturn +gst_push_file_src_ghostpad_event_probe (GstPad * pad, GstPadProbeInfo * info, + GstPushFileSrc * src) +{ + GstEvent *event = GST_PAD_PROBE_INFO_EVENT (info); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_SEGMENT: + { + if (src->time_segment) { + GstSegment segment; + GstEvent *replacement; + GST_DEBUG_OBJECT (src, "Replacing outgoing segment with TIME SEGMENT"); + gst_segment_init (&segment, GST_FORMAT_TIME); + segment.start = src->start_time; + segment.time = src->stream_time; + segment.rate = src->rate; + segment.applied_rate = src->applied_rate; + replacement = gst_event_new_segment (&segment); + gst_event_unref (event); + GST_PAD_PROBE_INFO_DATA (info) = replacement; + } + } + default: + break; + } + return GST_PAD_PROBE_OK; +} + +static gboolean +gst_push_file_src_ghostpad_event (GstPad * pad, GstObject * parent, + GstEvent * event) +{ + GstPushFileSrc *src = (GstPushFileSrc *) parent; + gboolean ret; + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_SEEK: + if (src->time_segment) { + /* When working in time we don't allow seeks */ + GST_DEBUG_OBJECT (src, "Refusing seek event in TIME mode"); + gst_event_unref (event); + ret = FALSE; + break; + } + /* PASSTHROUGH */ + default: + ret = gst_pad_event_default (pad, parent, event); + break; + } + + return ret; +} + +static gboolean +gst_push_file_src_ghostpad_query (GstPad * pad, GstObject * parent, + GstQuery * query) +{ + GstPushFileSrc *src = (GstPushFileSrc *) parent; + gboolean res; + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_SCHEDULING: + /* When working in time we don't allow seeks */ + if (src->time_segment) + gst_query_set_scheduling (query, GST_SCHEDULING_FLAG_SEQUENTIAL, 1, -1, + 0); + else + gst_query_set_scheduling (query, GST_SCHEDULING_FLAG_SEEKABLE, 1, -1, + 0); + gst_query_add_scheduling_mode (query, GST_PAD_MODE_PUSH); + res = TRUE; + break; + default: + res = gst_pad_query_default (pad, parent, query); + break; + } + return res; +} + +static void +gst_push_file_src_init (GstPushFileSrc * src) +{ + src->time_segment = DEFAULT_TIME_SEGMENT; + src->stream_time = DEFAULT_STREAM_TIME; + src->start_time = DEFAULT_START_TIME; + src->initial_timestamp = DEFAULT_INITIAL_TIMESTAMP; + src->rate = DEFAULT_RATE; + src->applied_rate = DEFAULT_APPLIED_RATE; + src->seen_first_buffer = FALSE; + + 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_query_function (src->srcpad, + GST_DEBUG_FUNCPTR (gst_push_file_src_ghostpad_query)); + gst_pad_set_event_function (src->srcpad, + GST_DEBUG_FUNCPTR (gst_push_file_src_ghostpad_event)); + /* Add outgoing event probe to replace segment and buffer timestamp */ + gst_pad_add_probe (src->srcpad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, + (GstPadProbeCallback) gst_push_file_src_ghostpad_event_probe, + src, NULL); + gst_pad_add_probe (src->srcpad, GST_PAD_PROBE_TYPE_BUFFER, + (GstPadProbeCallback) gst_push_file_src_ghostpad_buffer_probe, + src, NULL); + gst_element_add_pad (GST_ELEMENT (src), src->srcpad); + gst_object_unref (pad); + } +} + +/*** GSTURIHANDLER INTERFACE *************************************************/ + +static GstURIType +gst_push_file_src_uri_get_type (GType type) +{ + return GST_URI_SRC; +} + +static const gchar *const * +gst_push_file_src_uri_get_protocols (GType type) +{ + static const gchar *protocols[] = { "pushfile", NULL }; + + return protocols; +} + +static gchar * +gst_push_file_src_uri_get_uri (GstURIHandler * handler) +{ + GstPushFileSrc *src = GST_PUSH_FILE_SRC (handler); + gchar *fileuri, *pushfileuri; + + if (src->filesrc == NULL) + return NULL; + + fileuri = gst_uri_handler_get_uri (GST_URI_HANDLER (src->filesrc)); + if (fileuri == NULL) + return NULL; + pushfileuri = g_strconcat ("push", fileuri, NULL); + g_free (fileuri); + + return pushfileuri; +} + +static gboolean +gst_push_file_src_uri_set_uri (GstURIHandler * handler, const gchar * uri, + GError ** error) +{ + GstPushFileSrc *src = GST_PUSH_FILE_SRC (handler); + + if (src->filesrc == NULL) { + g_set_error_literal (error, GST_URI_ERROR, GST_URI_ERROR_BAD_STATE, + "Could not create file source element"); + return FALSE; + } + + /* skip 'push' bit */ + return gst_uri_handler_set_uri (GST_URI_HANDLER (src->filesrc), uri + 4, + error); +} + +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 0000000000..482ae13cf0 --- /dev/null +++ b/gst/debugutils/gstpushfilesrc.h @@ -0,0 +1,64 @@ +/* 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., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, 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; + + gboolean time_segment; + gboolean seen_first_buffer; + gint64 stream_time; + gint64 start_time; + guint64 initial_timestamp; + gdouble rate; + gdouble applied_rate; +}; + +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 0000000000..223a0da388 --- /dev/null +++ b/gst/debugutils/gsttaginject.c @@ -0,0 +1,206 @@ +/* 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., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +/** + * SECTION:element-taginject + * @title: taginject + * + * Element that injects new metadata tags, but passes incoming data through + * unmodified. + * + * ## Example launch lines + * |[ + * gst-launch-1.0 audiotestsrc num-buffers=100 ! taginject tags="title=testsrc,artist=gstreamer" ! vorbisenc ! oggmux ! filesink location=test.ogg + * ]| set title and artist + * |[ + * gst-launch-1.0 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 and handling lists + * + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdlib.h> + +#include "gstdebugutilselements.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 gst_tag_inject_parent_class parent_class +G_DEFINE_TYPE (GstTagInject, gst_tag_inject, GST_TYPE_BASE_TRANSFORM); +GST_ELEMENT_REGISTER_DEFINE (taginject, "taginject", + GST_RANK_NONE, gst_tag_inject_get_type ()); + +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_finalize (GObject * object) +{ + GstTagInject *self = GST_TAG_INJECT (object); + + if (self->tags) { + gst_tag_list_unref (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); + + GST_DEBUG_CATEGORY_INIT (gst_tag_inject_debug, "taginject", 0, + "tag inject element"); + + gobject_class->set_property = gst_tag_inject_set_property; + gobject_class->get_property = 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_tag_inject_finalize; + + gst_element_class_set_static_metadata (gstelement_class, + "TagInject", + "Generic", "inject metadata tags", "Stefan Kost <ensonic@users.sf.net>"); + gst_element_class_add_static_pad_template (gstelement_class, &srctemplate); + gst_element_class_add_static_pad_template (gstelement_class, &sinktemplate); + + 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) +{ + GstBaseTransform *trans = GST_BASE_TRANSFORM (self); + + gst_base_transform_set_gap_aware (trans, TRUE); + + 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_pad_push_event (GST_BASE_TRANSFORM_SRC_PAD (trans), + gst_event_new_tag (gst_tag_list_ref (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_tag_list_new_from_string (structure))) { + GST_WARNING ("unparsable taglist = '%s'", structure); + } + + /* make sure that tags will be send */ + self->tags_sent = FALSE; + 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 0000000000..a5459275f0 --- /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., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, 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/meson.build b/gst/debugutils/meson.build new file mode 100644 index 0000000000..1342d5334a --- /dev/null +++ b/gst/debugutils/meson.build @@ -0,0 +1,31 @@ +gstnavigationtest = library('gstnavigationtest', + 'gstnavigationtest.c', + c_args : gst_plugins_good_args, + include_directories : [configinc], + dependencies : [gstbase_dep, gstvideo_dep, libm], + install : true, + install_dir : plugins_install_dir, +) +pkgconfig.generate(gstnavigationtest, install_dir : plugins_pkgconfig_install_dir) +plugins += [gstnavigationtest] + +gstdebug = library('gstdebug', + 'gstdebug.c', + 'breakmydata.c', + 'gstcapssetter.c', + 'gstnavseek.c', + 'gstpushfilesrc.c', + 'gsttaginject.c', + 'rndbuffersize.c', + 'progressreport.c', + 'tests.c', + 'cpureport.c', + 'testplugin.c', + c_args: gst_plugins_good_args, + include_directories : [configinc], + dependencies : [gst_dep, gstbase_dep, gstvideo_dep], + install : true, + install_dir : plugins_install_dir, +) +pkgconfig.generate(gstdebug, install_dir : plugins_pkgconfig_install_dir) +plugins += [gstdebug] diff --git a/gst/debugutils/progressreport.c b/gst/debugutils/progressreport.c new file mode 100644 index 0000000000..8b2d2cfbc5 --- /dev/null +++ b/gst/debugutils/progressreport.c @@ -0,0 +1,522 @@ +/* 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., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION:element-progressreport + * @title: 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 preferred 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). + * + * ## Example launch line + * |[ + * gst-launch-1.0 -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-1.0 -m audiotestsrc ! progressreport update-freq=1 ! audioconvert ! autoaudiosink + * ]| This shows a progress query where no duration is available. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gst/gst.h> +#include <string.h> +#include <math.h> +#include <time.h> + +#include "gstdebugutilselements.h" +#include "progressreport.h" + + +enum +{ + PROP_0, + PROP_UPDATE_FREQ, + PROP_SILENT, + PROP_DO_QUERY, + PROP_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); + +#define DEFAULT_UPDATE_FREQ 5 +#define DEFAULT_SILENT FALSE +#define DEFAULT_DO_QUERY TRUE +#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_sink_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); + +#define gst_progress_report_parent_class parent_class +G_DEFINE_TYPE (GstProgressReport, gst_progress_report, GST_TYPE_BASE_TRANSFORM); +GST_ELEMENT_REGISTER_DEFINE (progressreport, "progressreport", + GST_RANK_NONE, gst_progress_report_get_type ()); + +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; + GstElementClass *element_class; + GObjectClass *gobject_class; + + gobject_class = G_OBJECT_CLASS (g_class); + element_class = GST_ELEMENT_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, + PROP_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_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, + PROP_SILENT, g_param_spec_boolean ("silent", + "Do not print output to stdout", "Do not print output to stdout", + DEFAULT_SILENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, + PROP_DO_QUERY, g_param_spec_boolean ("do-query", + "Use a query instead of buffer metadata to determine stream position", + "Use a query instead of buffer metadata to determine stream position", + DEFAULT_DO_QUERY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, + PROP_FORMAT, g_param_spec_string ("format", "format", + "Format to use for the querying", DEFAULT_FORMAT, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + gst_element_class_add_static_pad_template (element_class, + &progress_report_sink_template); + gst_element_class_add_static_pad_template (element_class, + &progress_report_src_template); + + gst_element_class_set_static_metadata (element_class, "Progress report", + "Testing", + "Periodically query and report on processing progress", + "Jan Schmidt <thaytan@mad.scientist.com>"); + + gstbasetrans_class->sink_event = + GST_DEBUG_FUNCPTR (gst_progress_report_sink_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) +{ + gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (report), TRUE); + + report->update_freq = DEFAULT_UPDATE_FREQ; + report->silent = DEFAULT_SILENT; + report->do_query = DEFAULT_DO_QUERY; + 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, GstBuffer * buf) +{ + 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 (filter->do_query || !buf) { + GST_LOG_OBJECT (filter, "using upstream query"); + if (!gst_pad_peer_query_position (sink_pad, format, &cur) || + !gst_pad_peer_query_duration (sink_pad, format, &total)) { + return FALSE; + } + } else { + GstBaseTransform *base = GST_BASE_TRANSFORM (filter); + + GST_LOG_OBJECT (filter, "using buffer metadata"); + if (format == GST_FORMAT_TIME && base->segment.format == GST_FORMAT_TIME) { + cur = gst_segment_to_stream_time (&base->segment, format, + GST_BUFFER_TIMESTAMP (buf)); + total = base->segment.duration; + } else if (format == GST_FORMAT_BUFFERS) { + cur = filter->buffer_count; + total = -1; + } else { + 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_get_current_caps (GST_BASE_TRANSFORM (filter)->sinkpad); + if (caps) { + if (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"; + } + } + gst_caps_unref (caps); + } + 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, gint64 cur_time_s, + GstBuffer * buf) +{ + 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_s - filter->start_time_s; + + 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, buf); + } 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, + buf); + 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_sink_event (GstBaseTransform * trans, GstEvent * event) +{ + GstProgressReport *filter; + + filter = GST_PROGRESS_REPORT (trans); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_EOS: + { + gint64 cur_time_s = g_get_real_time () / G_USEC_PER_SEC; + + gst_progress_report_report (filter, cur_time_s, NULL); + break; + } + default: + break; + } + return GST_BASE_TRANSFORM_CLASS (parent_class)->sink_event (trans, event); +} + +static GstFlowReturn +gst_progress_report_transform_ip (GstBaseTransform * trans, GstBuffer * buf) +{ + GstProgressReport *filter; + gboolean need_update; + gint64 cur_time; + + cur_time = g_get_real_time () / G_USEC_PER_SEC; + + filter = GST_PROGRESS_REPORT (trans); + + /* Check if update_freq seconds have passed since the last update */ + GST_OBJECT_LOCK (filter); + need_update = (cur_time - filter->last_report_s) >= filter->update_freq; + filter->buffer_count++; + GST_OBJECT_UNLOCK (filter); + + if (need_update) { + gst_progress_report_report (filter, cur_time, buf); + GST_OBJECT_LOCK (filter); + filter->last_report_s = 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); + + filter->start_time_s = filter->last_report_s = + g_get_real_time () / G_USEC_PER_SEC; + filter->buffer_count = 0; + + 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 PROP_UPDATE_FREQ: + GST_OBJECT_LOCK (filter); + filter->update_freq = g_value_get_int (value); + GST_OBJECT_UNLOCK (filter); + break; + case PROP_SILENT: + GST_OBJECT_LOCK (filter); + filter->silent = g_value_get_boolean (value); + GST_OBJECT_UNLOCK (filter); + break; + case PROP_DO_QUERY: + GST_OBJECT_LOCK (filter); + filter->do_query = g_value_get_boolean (value); + GST_OBJECT_UNLOCK (filter); + break; + case PROP_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 PROP_UPDATE_FREQ: + GST_OBJECT_LOCK (filter); + g_value_set_int (value, filter->update_freq); + GST_OBJECT_UNLOCK (filter); + break; + case PROP_SILENT: + GST_OBJECT_LOCK (filter); + g_value_set_boolean (value, filter->silent); + GST_OBJECT_UNLOCK (filter); + break; + case PROP_DO_QUERY: + GST_OBJECT_LOCK (filter); + g_value_set_boolean (value, filter->do_query); + GST_OBJECT_UNLOCK (filter); + break; + case PROP_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 0000000000..904ba38699 --- /dev/null +++ b/gst/debugutils/progressreport.h @@ -0,0 +1,68 @@ +/* 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., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, 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; + gboolean do_query; + gint64 start_time_s; + gint64 last_report_s; + gint64 buffer_count; + + /* 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 0000000000..1fee416fbb --- /dev/null +++ b/gst/debugutils/rndbuffersize.c @@ -0,0 +1,581 @@ +/* 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., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +/** + * SECTION:element-rndbuffersize + * @title: rndbuffersize + * + * This element pulls buffers with random sizes from the source. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gst/gst.h> +#include <gst/base/gstadapter.h> + +#include "gstdebugutilselements.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; + guint seed; + gint min, max; + + GstPad *sinkpad, *srcpad; + guint64 offset; + + gboolean need_newsegment; + + GstAdapter *adapter; +}; + +struct _GstRndBufferSizeClass +{ + GstElementClass parent_class; +}; + +enum +{ + PROP_SEED = 1, + PROP_MINIMUM, + PROP_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, GstObject * parent); +static gboolean gst_rnd_buffer_size_activate_mode (GstPad * pad, + GstObject * parent, GstPadMode mode, gboolean active); +static void gst_rnd_buffer_size_loop (GstRndBufferSize * self); +static GstStateChangeReturn gst_rnd_buffer_size_change_state (GstElement * + element, GstStateChange transition); +static gboolean gst_rnd_buffer_size_src_event (GstPad * pad, + GstObject * parent, GstEvent * event); +static gboolean gst_rnd_buffer_size_sink_event (GstPad * pad, + GstObject * parent, GstEvent * event); +static GstFlowReturn gst_rnd_buffer_size_chain (GstPad * pad, + GstObject * parent, GstBuffer * buffer); + +GType gst_rnd_buffer_size_get_type (void); +#define gst_rnd_buffer_size_parent_class parent_class +G_DEFINE_TYPE (GstRndBufferSize, gst_rnd_buffer_size, GST_TYPE_ELEMENT); +GST_ELEMENT_REGISTER_DEFINE (rndbuffersize, "rndbuffersize", + GST_RANK_NONE, gst_rnd_buffer_size_get_type ()); + +static void +gst_rnd_buffer_size_class_init (GstRndBufferSizeClass * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass); + + GST_DEBUG_CATEGORY_INIT (gst_rnd_buffer_size_debug, "rndbuffersize", 0, + "rndbuffersize element"); + + gobject_class->set_property = gst_rnd_buffer_size_set_property; + gobject_class->get_property = gst_rnd_buffer_size_get_property; + gobject_class->finalize = gst_rnd_buffer_size_finalize; + + gst_element_class_add_static_pad_template (gstelement_class, &sink_template); + gst_element_class_add_static_pad_template (gstelement_class, &src_template); + + gst_element_class_set_static_metadata (gstelement_class, "Random buffer size", + "Testing", "pull random sized buffers", + "Stefan Kost <stefan.kost@nokia.com>"); + + gstelement_class->change_state = + GST_DEBUG_FUNCPTR (gst_rnd_buffer_size_change_state); + + g_object_class_install_property (gobject_class, PROP_SEED, + g_param_spec_uint ("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_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_MINIMUM, + g_param_spec_int ("min", "minimum", "minimum buffer size", + 0, G_MAXINT32, DEFAULT_MIN, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_MAXIMUM, + g_param_spec_int ("max", "maximum", "maximum buffer size", + 1, G_MAXINT32, DEFAULT_MAX, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); +} + +static void +gst_rnd_buffer_size_init (GstRndBufferSize * self) +{ + 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_activatemode_function (self->sinkpad, + GST_DEBUG_FUNCPTR (gst_rnd_buffer_size_activate_mode)); + gst_pad_set_event_function (self->sinkpad, + GST_DEBUG_FUNCPTR (gst_rnd_buffer_size_sink_event)); + gst_pad_set_chain_function (self->sinkpad, + GST_DEBUG_FUNCPTR (gst_rnd_buffer_size_chain)); + GST_OBJECT_FLAG_SET (self->sinkpad, GST_PAD_FLAG_PROXY_CAPS); + GST_OBJECT_FLAG_SET (self->sinkpad, GST_PAD_FLAG_PROXY_ALLOCATION); + GST_OBJECT_FLAG_SET (self->sinkpad, GST_PAD_FLAG_PROXY_SCHEDULING); + gst_element_add_pad (GST_ELEMENT (self), self->sinkpad); + + self->srcpad = gst_pad_new_from_static_template (&src_template, "src"); + gst_pad_set_event_function (self->srcpad, + GST_DEBUG_FUNCPTR (gst_rnd_buffer_size_src_event)); + GST_OBJECT_FLAG_SET (self->srcpad, GST_PAD_FLAG_PROXY_CAPS); + GST_OBJECT_FLAG_SET (self->srcpad, GST_PAD_FLAG_PROXY_ALLOCATION); + GST_OBJECT_FLAG_SET (self->srcpad, GST_PAD_FLAG_PROXY_SCHEDULING); + 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 PROP_SEED: + self->seed = g_value_get_uint (value); + break; + case PROP_MINIMUM: + self->min = g_value_get_int (value); + break; + case PROP_MAXIMUM: + self->max = g_value_get_int (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 PROP_SEED: + g_value_set_uint (value, self->seed); + break; + case PROP_MINIMUM: + g_value_set_int (value, self->min); + break; + case PROP_MAXIMUM: + g_value_set_int (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, GstObject * parent) +{ + GstQuery *query; + gboolean pull_mode; + + query = gst_query_new_scheduling (); + + if (gst_pad_peer_query (pad, query)) + pull_mode = gst_query_has_scheduling_mode_with_flags (query, + GST_PAD_MODE_PULL, GST_SCHEDULING_FLAG_SEEKABLE); + else + pull_mode = FALSE; + + gst_query_unref (query); + + if (pull_mode) { + GST_DEBUG_OBJECT (pad, "activating pull"); + return gst_pad_activate_mode (pad, GST_PAD_MODE_PULL, TRUE); + } else { + GST_DEBUG_OBJECT (pad, "activating push"); + return gst_pad_activate_mode (pad, GST_PAD_MODE_PUSH, TRUE); + } +} + + +static gboolean +gst_rnd_buffer_size_activate_mode (GstPad * pad, GstObject * parent, + GstPadMode mode, gboolean active) +{ + gboolean res; + GstRndBufferSize *self = GST_RND_BUFFER_SIZE (parent); + + switch (mode) { + case GST_PAD_MODE_PULL: + if (active) { + GST_INFO_OBJECT (self, "starting pull"); + res = + gst_pad_start_task (pad, (GstTaskFunction) gst_rnd_buffer_size_loop, + self, NULL); + self->need_newsegment = TRUE; + } else { + GST_INFO_OBJECT (self, "stopping pull"); + res = gst_pad_stop_task (pad); + } + break; + case GST_PAD_MODE_PUSH: + GST_INFO_OBJECT (self, "%sactivating in push mode", (active) ? "" : "de"); + res = TRUE; + break; + default: + res = FALSE; + break; + } + return res; +} + +static gboolean +gst_rnd_buffer_size_src_event (GstPad * pad, GstObject * parent, + GstEvent * event) +{ + GstRndBufferSize *self; + GstSeekType start_type; + GstSeekFlags flags; + GstFormat format; + gint64 start; + + if (GST_EVENT_TYPE (event) != GST_EVENT_SEEK) { + return gst_pad_event_default (pad, parent, event); + } + + self = GST_RND_BUFFER_SIZE (parent); + gst_event_parse_seek (event, NULL, &format, &flags, &start_type, &start, + NULL, NULL); + + if (format != GST_FORMAT_BYTES) { + GST_WARNING_OBJECT (pad, "only BYTE format supported"); + return FALSE; + } + if (start_type != GST_SEEK_TYPE_SET) { + GST_WARNING_OBJECT (pad, "only SEEK_TYPE_SET supported"); + return FALSE; + } + + if ((flags & GST_SEEK_FLAG_FLUSH)) { + gst_pad_push_event (self->srcpad, gst_event_new_flush_start ()); + gst_pad_push_event (self->sinkpad, gst_event_new_flush_start ()); + } else { + gst_pad_pause_task (self->sinkpad); + } + + GST_PAD_STREAM_LOCK (self->sinkpad); + + if ((flags & GST_SEEK_FLAG_FLUSH)) { + gst_pad_push_event (self->srcpad, gst_event_new_flush_stop (TRUE)); + gst_pad_push_event (self->sinkpad, gst_event_new_flush_stop (TRUE)); + } + + GST_INFO_OBJECT (pad, "seeking to offset %" G_GINT64_FORMAT, start); + + self->offset = start; + self->need_newsegment = TRUE; + + gst_pad_start_task (self->sinkpad, (GstTaskFunction) gst_rnd_buffer_size_loop, + self, NULL); + + GST_PAD_STREAM_UNLOCK (self->sinkpad); + return TRUE; +} + +static GstFlowReturn +gst_rnd_buffer_size_drain_adapter (GstRndBufferSize * self, gboolean eos) +{ + GstFlowReturn flow; + GstBuffer *buf; + guint num_bytes, avail; + + flow = GST_FLOW_OK; + + if (G_UNLIKELY (self->min > self->max)) + goto bogus_minmax; + + do { + if (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 out of adapter", num_bytes); + + buf = gst_adapter_take_buffer (self->adapter, num_bytes); + + if (buf == NULL) { + if (!eos) { + GST_LOG_OBJECT (self, "not enough bytes in adapter"); + break; + } + + avail = gst_adapter_available (self->adapter); + + if (avail == 0) + break; + + if (avail < self->min) { + GST_WARNING_OBJECT (self, "discarding %u bytes at end (min=%u)", + avail, self->min); + gst_adapter_clear (self->adapter); + break; + } + buf = gst_adapter_take_buffer (self->adapter, avail); + g_assert (buf != NULL); + } + + flow = gst_pad_push (self->srcpad, buf); + } + while (flow == GST_FLOW_OK); + + return flow; + +/* ERRORS */ +bogus_minmax: + { + GST_ELEMENT_ERROR (self, LIBRARY, SETTINGS, + ("The minimum buffer size is smaller than the maximum buffer size."), + ("buffer sizes: max=%d, min=%d", self->min, self->max)); + return GST_FLOW_ERROR; + } +} + +static gboolean +gst_rnd_buffer_size_sink_event (GstPad * pad, GstObject * parent, + GstEvent * event) +{ + GstRndBufferSize *rnd = GST_RND_BUFFER_SIZE (parent); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_EOS: + gst_rnd_buffer_size_drain_adapter (rnd, TRUE); + break; + case GST_EVENT_FLUSH_STOP: + if (rnd->adapter != NULL) + gst_adapter_clear (rnd->adapter); + break; + default: + break; + } + + return gst_pad_event_default (pad, parent, event); +} + +static GstFlowReturn +gst_rnd_buffer_size_chain (GstPad * pad, GstObject * parent, GstBuffer * buf) +{ + GstRndBufferSize *rnd = GST_RND_BUFFER_SIZE (parent); + GstFlowReturn flow; + + if (rnd->adapter == NULL) + rnd->adapter = gst_adapter_new (); + + gst_adapter_push (rnd->adapter, buf); + + flow = gst_rnd_buffer_size_drain_adapter (rnd, FALSE); + + if (flow != GST_FLOW_OK) + GST_INFO_OBJECT (rnd, "flow: %s", gst_flow_get_name (flow)); + + return flow; +} + +static void +gst_rnd_buffer_size_loop (GstRndBufferSize * self) +{ + GstBuffer *buf = NULL; + GstFlowReturn ret; + guint num_bytes, size; + + 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; + + size = gst_buffer_get_size (buf); + + if (size < num_bytes) { + GST_WARNING_OBJECT (self, "short buffer: %u bytes", size); + } + + if (self->need_newsegment) { + GstSegment segment; + + gst_segment_init (&segment, GST_FORMAT_BYTES); + segment.start = self->offset; + gst_pad_push_event (self->srcpad, gst_event_new_segment (&segment)); + self->need_newsegment = FALSE; + } + + self->offset += size; + + 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_EOS) { + 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_EOS) { + GST_DEBUG_OBJECT (self, "eos"); + gst_pad_push_event (self->srcpad, gst_event_new_eos ()); + } else if (ret < GST_FLOW_EOS || ret == GST_FLOW_NOT_LINKED) { + GST_ELEMENT_FLOW_ERROR (self, 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=%d, min=%d", 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: + if (self->adapter) { + g_object_unref (self->adapter); + self->adapter = NULL; + } + break; + default: + break; + } + + return ret; +} diff --git a/gst/debugutils/testplugin.c b/gst/debugutils/testplugin.c new file mode 100644 index 0000000000..8fdb4f9aa4 --- /dev/null +++ b/gst/debugutils/testplugin.c @@ -0,0 +1,306 @@ +/* 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., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <gst/gst.h> +#include <gst/base/gstbasesink.h> +#include "gstdebugutilselements.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); + +GType gst_test_get_type (void); +#define gst_test_parent_class parent_class +G_DEFINE_TYPE (GstTest, gst_test, GST_TYPE_BASE_SINK); +GST_ELEMENT_REGISTER_DEFINE (testsink, "testsink", + GST_RANK_NONE, gst_test_get_type ()); + +static void +gst_test_class_init (GstTestClass * klass) +{ + GstBaseSinkClass *basesink_class = GST_BASE_SINK_CLASS (klass); + GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass); + GObjectClass *object_class = G_OBJECT_CLASS (klass); + guint i; + + GST_DEBUG_CATEGORY_INIT (gst_test_debug, "testsink", 0, + "debugging category for testsink element"); + + object_class->set_property = gst_test_set_property; + object_class->get_property = 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); + } + + gst_element_class_add_static_pad_template (gstelement_class, &sinktemplate); + + gst_element_class_set_static_metadata (gstelement_class, "Test plugin", + "Testing", "perform a number of tests", "Benjamin Otte <otte@gnome>"); + + 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 *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); + + 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)); + break; + } + default: + break; + } + + return GST_BASE_SINK_CLASS (parent_class)->event (basesink, event); +} + +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 0000000000..9ca2af90c9 --- /dev/null +++ b/gst/debugutils/tests.c @@ -0,0 +1,269 @@ +/* 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., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "tests.h" +#include <stdio.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_get_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 += + ABS (GST_CLOCK_DIFF (t->expected, GST_BUFFER_TIMESTAMP (buffer))); + 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 *** + */ + +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); + } +} + +static gpointer +md5_new (const GstTestInfo * info) +{ + return g_checksum_new (G_CHECKSUM_MD5); +} + +static void +md5_add (gpointer checksum, GstBuffer * buffer) +{ + GstMapInfo map; + + gst_buffer_map (buffer, &map, GST_MAP_READ); + g_checksum_update (checksum, map.data, map.size); + gst_buffer_unmap (buffer, &map); +} + +static gboolean +md5_finish (gpointer checksum, GValue * value) +{ + const gchar *expected, *result; + + expected = g_value_get_string (value); + result = g_checksum_get_string (checksum); + + if (g_str_equal (expected, "---")) + return TRUE; + if (g_str_equal (expected, result)) + return TRUE; + return FALSE; +} + +static void +md5_get_value (gpointer checksum, GValue * value) +{ + if (!checksum) { + g_value_set_string (value, "---"); + } else { + g_value_set_string (value, g_checksum_get_string (checksum)); + } +} + +static void +md5_free (gpointer checksum) +{ + g_checksum_free (checksum); +} + +/* + *** 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, md5_free} +}; diff --git a/gst/debugutils/tests.h b/gst/debugutils/tests.h new file mode 100644 index 0000000000..c8fa088cbd --- /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., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, 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__ */ |