diff --git a/gst/debugutils/Makefile.am b/gst/debugutils/Makefile.am index 9d237a3251..eadb82e9b9 100644 --- a/gst/debugutils/Makefile.am +++ b/gst/debugutils/Makefile.am @@ -22,10 +22,14 @@ libgstdebugutilsbad_la_SOURCES = \ gstchecksumsink.h \ gstchopmydata.c \ gstchopmydata.h \ + gstcompare.c \ + gstcompare.h \ gstdebugspy.h + nodist_libgstdebugutilsbad_la_SOURCES = $(BUILT_SOURCES) libgstdebugutilsbad_la_CFLAGS = $(GST_CFLAGS) $(GST_BASE_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) libgstdebugutilsbad_la_LIBADD = $(GST_BASE_LIBS) $(GST_PLUGINS_BASE_LIBS) \ + -lgstvideo-$(GST_MAJORMINOR) \ -lgstinterfaces-$(GST_MAJORMINOR) $(GST_LIBS) libgstdebugutilsbad_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) libgstdebugutilsbad_la_LIBTOOLFLAGS = --tag=disable-static diff --git a/gst/debugutils/debugutilsbad.c b/gst/debugutils/debugutilsbad.c index 09082a72db..2ee7f93c06 100644 --- a/gst/debugutils/debugutilsbad.c +++ b/gst/debugutils/debugutilsbad.c @@ -26,6 +26,7 @@ GType gst_checksum_sink_get_type (void); GType fps_display_sink_get_type (void); GType gst_chop_my_data_get_type (void); +GType gst_compare_get_type (void); GType gst_debug_spy_get_type (void); static gboolean @@ -37,8 +38,11 @@ plugin_init (GstPlugin * plugin) fps_display_sink_get_type ()); gst_element_register (plugin, "chopmydata", GST_RANK_NONE, gst_chop_my_data_get_type ()); + gst_element_register (plugin, "compare", GST_RANK_NONE, + gst_compare_get_type ()); gst_element_register (plugin, "debugspy", GST_RANK_NONE, gst_debug_spy_get_type ()); + return TRUE; } diff --git a/gst/debugutils/gstcompare.c b/gst/debugutils/gstcompare.c new file mode 100644 index 0000000000..abf563c6c7 --- /dev/null +++ b/gst/debugutils/gstcompare.c @@ -0,0 +1,664 @@ +/* GStreamer Element + * + * Copyright 2011 Collabora Ltd. + * @author: Mark Nauwelaerts + * Copyright 2011 Nokia Corp. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include + +#include +#include +#include + +#include "gstcompare.h" + +GST_DEBUG_CATEGORY_STATIC (compare_debug); +#define GST_CAT_DEFAULT compare_debug + + +static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + +static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + +static GstStaticPadTemplate check_sink_factory = +GST_STATIC_PAD_TEMPLATE ("check", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + +enum GstCompareMethod +{ + GST_COMPARE_METHOD_MEM, + GST_COMPARE_METHOD_MAX, + GST_COMPARE_METHOD_SSIM +}; + +#define GST_COMPARE_METHOD_TYPE (gst_compare_method_get_type()) +static GType +gst_compare_method_get_type (void) +{ + static GType method_type = 0; + + static const GEnumValue method_types[] = { + {GST_COMPARE_METHOD_MEM, "Memory", "mem"}, + {GST_COMPARE_METHOD_MAX, "Maximum metric", "max"}, + {GST_COMPARE_METHOD_SSIM, "SSIM (raw video)", "ssim"}, + {0, NULL, NULL} + }; + + if (!method_type) { + method_type = g_enum_register_static ("GstCompareMethod", method_types); + } + return method_type; +} + +/* Filter signals and args */ +enum +{ + /* FILL ME */ + LAST_SIGNAL +}; + +enum +{ + PROP_0, + PROP_META, + PROP_OFFSET_TS, + PROP_METHOD, + PROP_THRESHOLD, + PROP_UPPER, + PROP_LAST +}; + +#define DEFAULT_META GST_BUFFER_COPY_ALL +#define DEFAULT_OFFSET_TS FALSE +#define DEFAULT_METHOD GST_COMPARE_METHOD_MEM +#define DEFAULT_THRESHOLD 0 +#define DEFAULT_UPPER TRUE + +static void gst_compare_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec); +static void gst_compare_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec); + +static void gst_compare_reset (GstCompare * overlay); + +static GstCaps *gst_compare_getcaps (GstPad * pad); +static GstFlowReturn gst_compare_collect_pads (GstCollectPads * cpads, + GstCompare * comp); + +static GstStateChangeReturn gst_compare_change_state (GstElement * element, + GstStateChange transition); + +GST_BOILERPLATE (GstCompare, gst_compare, GstElement, GST_TYPE_ELEMENT); + + +static void +gst_compare_base_init (gpointer g_class) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&src_factory)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&sink_factory)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&check_sink_factory)); + gst_element_class_set_details_simple (element_class, "Compare buffers", + "Filter/Debug", "Compares incoming buffers", + "Mark Nauwelaerts "); +} + +static void +gst_compare_finalize (GObject * object) +{ + GstCompare *comp = GST_COMPARE (object); + + gst_object_unref (comp->cpads); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gst_compare_class_init (GstCompareClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + + GST_DEBUG_CATEGORY_INIT (compare_debug, "compare", 0, "Compare buffers"); + + gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_compare_change_state); + + gobject_class->set_property = gst_compare_set_property; + gobject_class->get_property = gst_compare_get_property; + gobject_class->finalize = gst_compare_finalize; + + g_object_class_install_property (gobject_class, PROP_META, + g_param_spec_flags ("meta", "Compare Meta", + "Indicates which metadata should be compared", + gst_buffer_copy_flags_get_type (), DEFAULT_META, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_OFFSET_TS, + g_param_spec_boolean ("offset-ts", "Offsets Timestamps", + "Consider OFFSET and OFFSET_END part of timestamp metadata", + DEFAULT_OFFSET_TS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_METHOD, + g_param_spec_enum ("method", "Content Compare Method", + "Method to compare buffer content", + GST_COMPARE_METHOD_TYPE, DEFAULT_METHOD, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_THRESHOLD, + g_param_spec_double ("threshold", "Content Threshold", + "Threshold beyond which to consider content different as determined by content-method", + 0, G_MAXDOUBLE, DEFAULT_THRESHOLD, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_UPPER, + g_param_spec_boolean ("upper", "Threshold Upper Bound", + "Whether threshold value is upper bound or lower bound for difference measure", + DEFAULT_UPPER, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); +} + +static void +gst_compare_init (GstCompare * comp, GstCompareClass * klass) +{ + comp->cpads = gst_collect_pads_new (); + gst_collect_pads_set_function (comp->cpads, + (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (gst_compare_collect_pads), + comp); + + comp->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink"); + gst_pad_set_getcaps_function (comp->sinkpad, gst_compare_getcaps); + gst_element_add_pad (GST_ELEMENT (comp), comp->sinkpad); + + comp->checkpad = + gst_pad_new_from_static_template (&check_sink_factory, "check"); + gst_pad_set_getcaps_function (comp->checkpad, gst_compare_getcaps); + gst_element_add_pad (GST_ELEMENT (comp), comp->checkpad); + + gst_collect_pads_add_pad_full (comp->cpads, comp->sinkpad, + sizeof (GstCollectData), NULL); + gst_collect_pads_add_pad_full (comp->cpads, comp->checkpad, + sizeof (GstCollectData), NULL); + + comp->srcpad = gst_pad_new_from_static_template (&src_factory, "src"); + gst_pad_set_getcaps_function (comp->srcpad, gst_compare_getcaps); + gst_element_add_pad (GST_ELEMENT (comp), comp->srcpad); + + /* init properties */ + comp->meta = DEFAULT_META; + comp->offset_ts = DEFAULT_OFFSET_TS; + comp->method = DEFAULT_METHOD; + comp->threshold = DEFAULT_THRESHOLD; + comp->upper = DEFAULT_UPPER; + + gst_compare_reset (comp); +} + +static void +gst_compare_reset (GstCompare * comp) +{ +} + +static GstCaps * +gst_compare_getcaps (GstPad * pad) +{ + GstCompare *comp; + GstPad *otherpad; + GstCaps *result; + + comp = GST_COMPARE (gst_pad_get_parent (pad)); + if (G_UNLIKELY (comp == NULL)) + return gst_caps_new_any (); + + otherpad = (pad == comp->srcpad ? comp->sinkpad : comp->srcpad); + result = gst_pad_peer_get_caps (otherpad); + if (result == NULL) + result = gst_caps_new_any (); + + gst_object_unref (comp); + + return result; +} + +static void +gst_compare_meta (GstCompare * comp, GstBuffer * buf1, GstBuffer * buf2) +{ + gint flags = 0; + + if (comp->meta & GST_BUFFER_COPY_FLAGS) { + if (GST_BUFFER_FLAGS (buf1) != GST_BUFFER_FLAGS (buf2)) { + flags |= GST_BUFFER_COPY_FLAGS; + GST_DEBUG_OBJECT (comp, "flags %d != flags %d", GST_BUFFER_FLAGS (buf1), + GST_BUFFER_FLAGS (buf2)); + } + } + if (comp->meta & GST_BUFFER_COPY_TIMESTAMPS) { + if (GST_BUFFER_TIMESTAMP (buf1) != GST_BUFFER_TIMESTAMP (buf2)) { + flags |= GST_BUFFER_COPY_TIMESTAMPS; + GST_DEBUG_OBJECT (comp, + "ts %" GST_TIME_FORMAT " != ts %" GST_TIME_FORMAT, + GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf1)), + GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf2))); + } + if (GST_BUFFER_DURATION (buf1) != GST_BUFFER_DURATION (buf2)) { + flags |= GST_BUFFER_COPY_TIMESTAMPS; + GST_DEBUG_OBJECT (comp, + "dur %" GST_TIME_FORMAT " != dur %" GST_TIME_FORMAT, + GST_TIME_ARGS (GST_BUFFER_DURATION (buf1)), + GST_TIME_ARGS (GST_BUFFER_DURATION (buf2))); + } + if (comp->offset_ts) { + if (GST_BUFFER_OFFSET (buf1) != GST_BUFFER_OFFSET (buf2)) { + flags |= GST_BUFFER_COPY_TIMESTAMPS; + GST_DEBUG_OBJECT (comp, + "offset %" G_GINT64_FORMAT " != offset %" G_GINT64_FORMAT, + GST_BUFFER_OFFSET (buf1), GST_BUFFER_OFFSET (buf2)); + } + if (GST_BUFFER_OFFSET_END (buf1) != GST_BUFFER_OFFSET_END (buf2)) { + flags |= GST_BUFFER_COPY_TIMESTAMPS; + GST_DEBUG_OBJECT (comp, + "offset_end %" G_GINT64_FORMAT " != offset_end %" G_GINT64_FORMAT, + GST_BUFFER_OFFSET_END (buf1), GST_BUFFER_OFFSET_END (buf2)); + } + } + } + if (comp->meta & GST_BUFFER_COPY_CAPS) { + if (!gst_caps_is_equal (GST_BUFFER_CAPS (buf1), GST_BUFFER_CAPS (buf2))) { + flags |= GST_BUFFER_COPY_CAPS; + GST_DEBUG_OBJECT (comp, + "caps %" GST_PTR_FORMAT " != caps %" GST_PTR_FORMAT, + GST_BUFFER_CAPS (buf1), GST_BUFFER_CAPS (buf2)); + } + } + + /* signal mismatch by debug and message */ + if (flags) { + GST_WARNING_OBJECT (comp, "buffers %p and %p failed metadata match %d", + buf1, buf2, flags); + + gst_element_post_message (GST_ELEMENT (comp), + gst_message_new_element (GST_OBJECT (comp), + gst_structure_new ("delta", "meta", G_TYPE_INT, flags, NULL))); + } +} + +/* when comparing contents, it is already ensured sizes are equal */ + +static gint +gst_compare_mem (GstCompare * comp, GstBuffer * buf1, GstBuffer * buf2) +{ + return memcmp (GST_BUFFER_DATA (buf1), GST_BUFFER_DATA (buf2), + GST_BUFFER_SIZE (buf1)) ? 1 : 0; +} + +static gint +gst_compare_max (GstCompare * comp, GstBuffer * buf1, GstBuffer * buf2) +{ + gint i, delta = 0; + gint8 *data1, *data2; + + data1 = (gint8 *) GST_BUFFER_DATA (buf1); + data2 = (gint8 *) GST_BUFFER_DATA (buf2); + + /* primitive loop */ + for (i = 0; i < GST_BUFFER_SIZE (buf1); i++) { + gint diff = ABS (*data1 - *data2); + if (diff > 0) + GST_LOG_OBJECT (comp, "diff at %d = %d", i, diff); + delta = MAX (delta, ABS (*data1 - *data2)); + data1++; + data2++; + } + + return delta; +} + +static double +gst_compare_ssim_window (GstCompare * comp, guint8 * data1, guint8 * data2, + gint width, gint height, gint step, gint stride) +{ + gint count = 0, i, j; + gint sum1 = 0, sum2 = 0, ssum1 = 0, ssum2 = 0, acov = 0; + gdouble avg1, avg2, var1, var2, cov; + + const gdouble k1 = 0.01; + const gdouble k2 = 0.03; + const gdouble L = 255.0; + const gdouble c1 = (k1 * L) * (k1 * L); + const gdouble c2 = (k2 * L) * (k2 * L); + + /* plain and simple; no fancy optimizations */ + for (i = 0; i < height; i++) { + for (j = 0; j < width; j++) { + sum1 += *data1; + sum2 += *data2; + ssum1 += *data1 * *data1; + ssum2 += *data2 * *data2; + acov += *data1 * *data2; + count++; + data1 += step; + data2 += step; + } + data1 -= j * step; + data2 -= j * step; + data1 += stride; + data2 += stride; + } + + avg1 = sum1 / count; + avg2 = sum2 / count; + var1 = ssum1 / count - avg1 * avg1; + var2 = ssum2 / count - avg2 * avg2; + cov = acov / count - avg1 * avg2; + + return (2 * avg1 * avg2 + c1) * (2 * cov + c2) / + ((avg1 * avg1 + avg2 * avg2 + c1) * (var1 + var2 + c2)); +} + +/* @width etc are for the particular component */ +static gdouble +gst_compare_ssim_component (GstCompare * comp, guint8 * data1, guint8 * data2, + gint width, gint height, gint step, gint stride) +{ + const gint window = 16; + gdouble ssim_sum = 0; + gint count = 0, i, j; + + for (j = 0; j + (window / 2) < height; j += (window / 2)) { + for (i = 0; i + (window / 2) < width; i += (window / 2)) { + gdouble ssim; + + ssim = gst_compare_ssim_window (comp, data1 + step * i + j * stride, + data2 + step * i + j * stride, + MIN (window, width - i), MIN (window, height - j), step, stride); + GST_LOG_OBJECT (comp, "ssim for %dx%d at (%d, %d) = %f", window, window, + i, j, ssim); + ssim_sum += ssim; + count++; + } + } + + return (ssim_sum / count); +} + +static gdouble +gst_compare_ssim (GstCompare * comp, GstBuffer * buf1, GstBuffer * buf2) +{ + GstCaps *caps; + GstVideoFormat format, f; + gint width, height, w, h, i, comps; + gdouble cssim[4], ssim, c[4] = { 1.0, 0.0, 0.0, 0.0 }; + guint8 *data1, *data2; + + caps = GST_BUFFER_CAPS (buf1); + if (!caps) + goto invalid_input; + + if (!gst_video_format_parse_caps (caps, &format, &width, &height)) + goto invalid_input; + + caps = GST_BUFFER_CAPS (buf2); + if (!caps) + goto invalid_input; + + if (!gst_video_format_parse_caps (caps, &f, &w, &h)) + goto invalid_input; + + if (f != format || w != width || h != height) + return comp->threshold + 1; + + comps = gst_video_format_is_gray (format) ? 1 : 3; + if (gst_video_format_has_alpha (format)) + comps += 1; + + /* note that some are reported both yuv and gray */ + for (i = 0; i < comps; ++i) + c[i] = 1.0; + /* increase luma weight if yuv */ + if (gst_video_format_is_yuv (format) && (comps > 1)) + c[0] = comps - 1; + for (i = 0; i < comps; ++i) + c[i] /= (gst_video_format_is_yuv (format) && (comps > 1)) ? + 2 * (comps - 1) : comps; + + data1 = GST_BUFFER_DATA (buf1); + data2 = GST_BUFFER_DATA (buf2); + for (i = 0; i < comps; i++) { + gint offset, cw, ch, step, stride; + + /* only support most common formats */ + if (gst_video_format_get_component_depth (format, i) != 8) + goto unsupported_input; + offset = gst_video_format_get_component_offset (format, i, width, height); + cw = gst_video_format_get_component_width (format, i, width); + ch = gst_video_format_get_component_height (format, i, height); + step = gst_video_format_get_pixel_stride (format, i); + stride = gst_video_format_get_row_stride (format, i, width); + + GST_LOG_OBJECT (comp, "component %d", i); + cssim[i] = gst_compare_ssim_component (comp, data1 + offset, data2 + offset, + cw, ch, step, stride); + GST_LOG_OBJECT (comp, "ssim[%d] = %f", i, cssim[i]); + } + +#ifndef GST_DISABLE_GST_DEBUG + for (i = 0; i < 4; i++) { + GST_DEBUG_OBJECT (comp, "ssim[%d] = %f, c[%d] = %f", i, cssim[i], i, c[i]); + } +#endif + + ssim = cssim[0] * c[0] + cssim[1] * c[1] + cssim[2] * c[2] + cssim[3] * c[3]; + + return ssim; + + /* ERRORS */ +invalid_input: + { + GST_ERROR_OBJECT (comp, "ssim method needs raw video input"); + return 0; + } +unsupported_input: + { + GST_ERROR_OBJECT (comp, "raw video format not supported %" GST_PTR_FORMAT, + caps); + return 0; + } +} + +static void +gst_compare_buffers (GstCompare * comp, GstBuffer * buf1, GstBuffer * buf2) +{ + gdouble delta = 0; + + /* first check metadata */ + gst_compare_meta (comp, buf1, buf2); + + /* check content according to method */ + /* but at least size should match */ + if (GST_BUFFER_SIZE (buf1) != GST_BUFFER_SIZE (buf2)) { + delta = comp->threshold + 1; + } else { + GST_MEMDUMP_OBJECT (comp, "buffer 1", GST_BUFFER_DATA (buf1), + GST_BUFFER_SIZE (buf1)); + GST_MEMDUMP_OBJECT (comp, "buffer 2", GST_BUFFER_DATA (buf2), + GST_BUFFER_SIZE (buf2)); + switch (comp->method) { + case GST_COMPARE_METHOD_MEM: + delta = gst_compare_mem (comp, buf1, buf2); + break; + case GST_COMPARE_METHOD_MAX: + delta = gst_compare_max (comp, buf1, buf2); + break; + case GST_COMPARE_METHOD_SSIM: + delta = gst_compare_ssim (comp, buf1, buf2); + break; + default: + g_assert_not_reached (); + break; + } + } + + if ((comp->upper && delta > comp->threshold) || + (!comp->upper && delta < comp->threshold)) { + GST_WARNING_OBJECT (comp, "buffers %p and %p failed content match %f", + buf1, buf2, delta); + + gst_element_post_message (GST_ELEMENT (comp), + gst_message_new_element (GST_OBJECT (comp), + gst_structure_new ("delta", "content", G_TYPE_DOUBLE, delta, + NULL))); + } +} + +static GstFlowReturn +gst_compare_collect_pads (GstCollectPads * cpads, GstCompare * comp) +{ + GstBuffer *buf1, *buf2; + + buf1 = gst_collect_pads_pop (comp->cpads, + gst_pad_get_element_private (comp->sinkpad)); + + buf2 = gst_collect_pads_pop (comp->cpads, + gst_pad_get_element_private (comp->checkpad)); + + if (!buf1 && !buf2) { + gst_pad_push_event (comp->srcpad, gst_event_new_eos ()); + return GST_FLOW_UNEXPECTED; + } else if (buf1 && buf2) { + gst_compare_buffers (comp, buf1, buf2); + } else { + GST_WARNING_OBJECT (comp, "buffer %p != NULL", buf1 ? buf1 : buf2); + + comp->count++; + gst_element_post_message (GST_ELEMENT (comp), + gst_message_new_element (GST_OBJECT (comp), + gst_structure_new ("delta", "count", G_TYPE_INT, comp->count, + NULL))); + } + + if (buf1) + gst_pad_push (comp->srcpad, buf1); + + if (buf2) + gst_buffer_unref (buf2); + + return GST_FLOW_OK; +} + +static void +gst_compare_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstCompare *comp = GST_COMPARE (object); + + switch (prop_id) { + case PROP_META: + comp->meta = g_value_get_flags (value); + break; + case PROP_OFFSET_TS: + comp->offset_ts = g_value_get_boolean (value); + break; + case PROP_METHOD: + comp->method = g_value_get_enum (value); + break; + case PROP_THRESHOLD: + comp->threshold = g_value_get_double (value); + break; + case PROP_UPPER: + comp->upper = g_value_get_boolean (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_compare_get_property (GObject * object, guint prop_id, GValue * value, + GParamSpec * pspec) +{ + GstCompare *comp = GST_COMPARE (object); + + switch (prop_id) { + case PROP_META: + g_value_set_flags (value, comp->meta); + break; + case PROP_OFFSET_TS: + g_value_set_boolean (value, comp->offset_ts); + break; + case PROP_METHOD: + g_value_set_enum (value, comp->method); + break; + case PROP_THRESHOLD: + g_value_set_double (value, comp->threshold); + break; + case PROP_UPPER: + g_value_set_boolean (value, comp->upper); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static GstStateChangeReturn +gst_compare_change_state (GstElement * element, GstStateChange transition) +{ + GstCompare *comp = GST_COMPARE (element); + GstStateChangeReturn ret; + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + case GST_STATE_CHANGE_READY_TO_PAUSED: + gst_collect_pads_start (comp->cpads); + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_collect_pads_stop (comp->cpads); + break; + default: + break; + } + + ret = GST_CALL_PARENT_WITH_DEFAULT (GST_ELEMENT_CLASS, change_state, + (element, transition), GST_STATE_CHANGE_SUCCESS); + if (ret != GST_STATE_CHANGE_SUCCESS) + return ret; + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_compare_reset (comp); + break; + default: + break; + } + + return GST_STATE_CHANGE_SUCCESS; +} diff --git a/gst/debugutils/gstcompare.h b/gst/debugutils/gstcompare.h new file mode 100644 index 0000000000..bee5108e3e --- /dev/null +++ b/gst/debugutils/gstcompare.h @@ -0,0 +1,75 @@ +/* GStreamer Element + * + * Copyright 2011 Collabora Ltd. + * @author: Mark Nauwelaerts + * Copyright 2011 Nokia Corp. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + + +#ifndef __GST_COMPARE_H__ +#define __GST_COMPARE_H__ + + +#include + +G_BEGIN_DECLS + +#define GST_TYPE_COMPARE \ + (gst_compare_get_type()) +#define GST_COMPARE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_COMPARE, GstCompare)) +#define GST_COMPARE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_COMPARE, GstCompareClass)) +#define GST_COMPARE_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), GST_TYPE_COMPARE, GstCompareClass)) +#define GST_IS_COMPARE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_COMPARE)) +#define GST_IS_COMPARE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_COMPARE)) + +typedef struct _GstCompare GstCompare; +typedef struct _GstCompareClass GstCompareClass; + +struct _GstCompare { + GstElement element; + + GstPad *srcpad; + GstPad *sinkpad; + GstPad *checkpad; + + GstCollectPads *cpads; + + gint count; + + /* properties */ + GstBufferCopyFlags meta; + gboolean offset_ts; + gint method; + gdouble threshold; + gboolean upper; +}; + +struct _GstCompareClass { + GstElementClass parent_class; +}; + +GType gst_compare_get_type(void); + +G_END_DECLS + +#endif /* __GST_COMPARE_H__ */