videocodectestsink: Add YUV422 support
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-bad / gst / debugutils / gstcompare.c
1 /* GStreamer Element
2  *
3  * Copyright 2011 Collabora Ltd.
4  *  @author: Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>
5  * Copyright 2011 Nokia Corp.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26 #include <string.h>
27
28 #include <gst/gst.h>
29 #include <gst/base/gstcollectpads.h>
30 #include <gst/video/video.h>
31
32 #include "gstdebugutilsbadelements.h"
33 #include "gstcompare.h"
34
35 GST_DEBUG_CATEGORY_STATIC (compare_debug);
36 #define GST_CAT_DEFAULT   compare_debug
37
38
39 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
40     GST_PAD_SRC,
41     GST_PAD_ALWAYS,
42     GST_STATIC_CAPS_ANY);
43
44 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
45     GST_PAD_SINK,
46     GST_PAD_ALWAYS,
47     GST_STATIC_CAPS_ANY);
48
49 static GstStaticPadTemplate check_sink_factory =
50 GST_STATIC_PAD_TEMPLATE ("check",
51     GST_PAD_SINK,
52     GST_PAD_ALWAYS,
53     GST_STATIC_CAPS_ANY);
54
55 enum GstCompareMethod
56 {
57   GST_COMPARE_METHOD_MEM,
58   GST_COMPARE_METHOD_MAX,
59   GST_COMPARE_METHOD_SSIM
60 };
61
62 #define GST_COMPARE_METHOD_TYPE (gst_compare_method_get_type())
63 static GType
64 gst_compare_method_get_type (void)
65 {
66   static GType method_type = 0;
67
68   static const GEnumValue method_types[] = {
69     {GST_COMPARE_METHOD_MEM, "Memory", "mem"},
70     {GST_COMPARE_METHOD_MAX, "Maximum metric", "max"},
71     {GST_COMPARE_METHOD_SSIM, "SSIM (raw video)", "ssim"},
72     {0, NULL, NULL}
73   };
74
75   if (!method_type) {
76     method_type = g_enum_register_static ("GstCompareMethod", method_types);
77   }
78   return method_type;
79 }
80
81 /* Filter signals and args */
82 enum
83 {
84   /* FILL ME */
85   LAST_SIGNAL
86 };
87
88 enum
89 {
90   PROP_0,
91   PROP_META,
92   PROP_OFFSET_TS,
93   PROP_METHOD,
94   PROP_THRESHOLD,
95   PROP_UPPER
96 };
97
98 #define DEFAULT_META             GST_BUFFER_COPY_ALL
99 #define DEFAULT_OFFSET_TS        FALSE
100 #define DEFAULT_METHOD           GST_COMPARE_METHOD_MEM
101 #define DEFAULT_THRESHOLD        0
102 #define DEFAULT_UPPER            TRUE
103
104 static void gst_compare_set_property (GObject * object,
105     guint prop_id, const GValue * value, GParamSpec * pspec);
106 static void gst_compare_get_property (GObject * object,
107     guint prop_id, GValue * value, GParamSpec * pspec);
108
109 static void gst_compare_reset (GstCompare * overlay);
110
111 static gboolean gst_compare_query (GstPad * pad, GstObject * parent,
112     GstQuery * query);
113 static GstFlowReturn gst_compare_collect_pads (GstCollectPads * cpads,
114     GstCompare * comp);
115
116 static GstStateChangeReturn gst_compare_change_state (GstElement * element,
117     GstStateChange transition);
118
119 #define gst_compare_parent_class parent_class
120 G_DEFINE_TYPE (GstCompare, gst_compare, GST_TYPE_ELEMENT);
121 GST_ELEMENT_REGISTER_DEFINE (compare, "compare",
122     GST_RANK_NONE, gst_compare_get_type ());
123
124 static void
125 gst_compare_finalize (GObject * object)
126 {
127   GstCompare *comp = GST_COMPARE (object);
128
129   gst_object_unref (comp->cpads);
130
131   G_OBJECT_CLASS (parent_class)->finalize (object);
132 }
133
134 static void
135 gst_compare_class_init (GstCompareClass * klass)
136 {
137   GObjectClass *gobject_class;
138   GstElementClass *gstelement_class;
139
140   gobject_class = (GObjectClass *) klass;
141   gstelement_class = (GstElementClass *) klass;
142
143   GST_DEBUG_CATEGORY_INIT (compare_debug, "compare", 0, "Compare buffers");
144
145   gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_compare_change_state);
146
147   gobject_class->set_property = gst_compare_set_property;
148   gobject_class->get_property = gst_compare_get_property;
149   gobject_class->finalize = gst_compare_finalize;
150
151   g_object_class_install_property (gobject_class, PROP_META,
152       g_param_spec_flags ("meta", "Compare Meta",
153           "Indicates which metadata should be compared",
154           gst_buffer_copy_flags_get_type (), DEFAULT_META,
155           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
156   g_object_class_install_property (gobject_class, PROP_OFFSET_TS,
157       g_param_spec_boolean ("offset-ts", "Offsets Timestamps",
158           "Consider OFFSET and OFFSET_END part of timestamp metadata",
159           DEFAULT_OFFSET_TS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
160   g_object_class_install_property (gobject_class, PROP_METHOD,
161       g_param_spec_enum ("method", "Content Compare Method",
162           "Method to compare buffer content",
163           GST_COMPARE_METHOD_TYPE, DEFAULT_METHOD,
164           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
165   g_object_class_install_property (gobject_class, PROP_THRESHOLD,
166       g_param_spec_double ("threshold", "Content Threshold",
167           "Threshold beyond which to consider content different as determined by content-method",
168           0, G_MAXDOUBLE, DEFAULT_THRESHOLD,
169           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
170   g_object_class_install_property (gobject_class, PROP_UPPER,
171       g_param_spec_boolean ("upper", "Threshold Upper Bound",
172           "Whether threshold value is upper bound or lower bound for difference measure",
173           DEFAULT_UPPER, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
174
175   gst_element_class_add_static_pad_template (gstelement_class, &src_factory);
176   gst_element_class_add_static_pad_template (gstelement_class, &sink_factory);
177   gst_element_class_add_static_pad_template (gstelement_class,
178       &check_sink_factory);
179   gst_element_class_set_static_metadata (gstelement_class, "Compare buffers",
180       "Filter/Debug", "Compares incoming buffers",
181       "Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>");
182
183   gst_type_mark_as_plugin_api (GST_COMPARE_METHOD_TYPE, 0);
184 }
185
186 static void
187 gst_compare_init (GstCompare * comp)
188 {
189   comp->cpads = gst_collect_pads_new ();
190   gst_collect_pads_set_function (comp->cpads,
191       (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (gst_compare_collect_pads),
192       comp);
193
194   comp->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink");
195   GST_PAD_SET_PROXY_CAPS (comp->sinkpad);
196   gst_element_add_pad (GST_ELEMENT (comp), comp->sinkpad);
197
198   comp->checkpad =
199       gst_pad_new_from_static_template (&check_sink_factory, "check");
200   gst_pad_set_query_function (comp->checkpad, gst_compare_query);
201   gst_element_add_pad (GST_ELEMENT (comp), comp->checkpad);
202
203   gst_collect_pads_add_pad (comp->cpads, comp->sinkpad,
204       sizeof (GstCollectData), NULL, TRUE);
205   gst_collect_pads_add_pad (comp->cpads, comp->checkpad,
206       sizeof (GstCollectData), NULL, TRUE);
207
208   comp->srcpad = gst_pad_new_from_static_template (&src_factory, "src");
209   gst_pad_set_query_function (comp->srcpad, gst_compare_query);
210   gst_element_add_pad (GST_ELEMENT (comp), comp->srcpad);
211
212   /* init properties */
213   comp->meta = DEFAULT_META;
214   comp->offset_ts = DEFAULT_OFFSET_TS;
215   comp->method = DEFAULT_METHOD;
216   comp->threshold = DEFAULT_THRESHOLD;
217   comp->upper = DEFAULT_UPPER;
218
219   gst_compare_reset (comp);
220 }
221
222 static void
223 gst_compare_reset (GstCompare * comp)
224 {
225 }
226
227 static gboolean
228 gst_compare_query (GstPad * pad, GstObject * parent, GstQuery * query)
229 {
230   GstCompare *comp;
231   GstPad *otherpad;
232
233   comp = GST_COMPARE (parent);
234   otherpad = (pad == comp->srcpad ? comp->sinkpad : comp->srcpad);
235
236   return gst_pad_peer_query (otherpad, query);
237 }
238
239 static void
240 gst_compare_meta (GstCompare * comp, GstBuffer * buf1, GstCaps * caps1,
241     GstBuffer * buf2, GstCaps * caps2)
242 {
243   gint flags = 0;
244
245   if (comp->meta & GST_BUFFER_COPY_FLAGS) {
246     if (GST_BUFFER_FLAGS (buf1) != GST_BUFFER_FLAGS (buf2)) {
247       flags |= GST_BUFFER_COPY_FLAGS;
248       GST_DEBUG_OBJECT (comp, "flags %d != flags %d", GST_BUFFER_FLAGS (buf1),
249           GST_BUFFER_FLAGS (buf2));
250     }
251   }
252   if (comp->meta & GST_BUFFER_COPY_TIMESTAMPS) {
253     if (GST_BUFFER_TIMESTAMP (buf1) != GST_BUFFER_TIMESTAMP (buf2)) {
254       flags |= GST_BUFFER_COPY_TIMESTAMPS;
255       GST_DEBUG_OBJECT (comp,
256           "ts %" GST_TIME_FORMAT " != ts %" GST_TIME_FORMAT,
257           GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf1)),
258           GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf2)));
259     }
260     if (GST_BUFFER_DURATION (buf1) != GST_BUFFER_DURATION (buf2)) {
261       flags |= GST_BUFFER_COPY_TIMESTAMPS;
262       GST_DEBUG_OBJECT (comp,
263           "dur %" GST_TIME_FORMAT " != dur %" GST_TIME_FORMAT,
264           GST_TIME_ARGS (GST_BUFFER_DURATION (buf1)),
265           GST_TIME_ARGS (GST_BUFFER_DURATION (buf2)));
266     }
267     if (comp->offset_ts) {
268       if (GST_BUFFER_OFFSET (buf1) != GST_BUFFER_OFFSET (buf2)) {
269         flags |= GST_BUFFER_COPY_TIMESTAMPS;
270         GST_DEBUG_OBJECT (comp,
271             "offset %" G_GINT64_FORMAT " != offset %" G_GINT64_FORMAT,
272             GST_BUFFER_OFFSET (buf1), GST_BUFFER_OFFSET (buf2));
273       }
274       if (GST_BUFFER_OFFSET_END (buf1) != GST_BUFFER_OFFSET_END (buf2)) {
275         flags |= GST_BUFFER_COPY_TIMESTAMPS;
276         GST_DEBUG_OBJECT (comp,
277             "offset_end %" G_GINT64_FORMAT " != offset_end %" G_GINT64_FORMAT,
278             GST_BUFFER_OFFSET_END (buf1), GST_BUFFER_OFFSET_END (buf2));
279       }
280     }
281   }
282 #if 0
283   /* FIXME ?? */
284   if (comp->meta & GST_BUFFER_COPY_CAPS) {
285     if (!gst_caps_is_equal (caps1, caps2)) {
286       flags |= GST_BUFFER_COPY_CAPS;
287       GST_DEBUG_OBJECT (comp,
288           "caps %" GST_PTR_FORMAT " != caps %" GST_PTR_FORMAT, caps1, caps2);
289     }
290   }
291 #endif
292
293   /* signal mismatch by debug and message */
294   if (flags) {
295     GST_WARNING_OBJECT (comp, "buffers %p and %p failed metadata match %d",
296         buf1, buf2, flags);
297
298     gst_element_post_message (GST_ELEMENT (comp),
299         gst_message_new_element (GST_OBJECT (comp),
300             gst_structure_new ("delta", "meta", G_TYPE_INT, flags, NULL)));
301   }
302 }
303
304 /* when comparing contents, it is already ensured sizes are equal */
305
306 static gint
307 gst_compare_mem (GstCompare * comp, GstBuffer * buf1, GstCaps * caps1,
308     GstBuffer * buf2, GstCaps * caps2)
309 {
310   GstMapInfo map1, map2;
311   gint c;
312
313   gst_buffer_map (buf1, &map1, GST_MAP_READ);
314   gst_buffer_map (buf2, &map2, GST_MAP_READ);
315
316   c = memcmp (map1.data, map2.data, map1.size);
317
318   gst_buffer_unmap (buf1, &map1);
319   gst_buffer_unmap (buf2, &map2);
320
321   return c ? 1 : 0;
322 }
323
324 static gint
325 gst_compare_max (GstCompare * comp, GstBuffer * buf1, GstCaps * caps1,
326     GstBuffer * buf2, GstCaps * caps2)
327 {
328   gint i, delta = 0;
329   gint8 *data1, *data2;
330   GstMapInfo map1, map2;
331
332   gst_buffer_map (buf1, &map1, GST_MAP_READ);
333   gst_buffer_map (buf2, &map2, GST_MAP_READ);
334
335   data1 = (gint8 *) map1.data;
336   data2 = (gint8 *) map2.data;
337
338   /* primitive loop */
339   for (i = 0; i < map1.size; i++) {
340     gint diff = ABS (*data1 - *data2);
341     if (diff > 0)
342       GST_LOG_OBJECT (comp, "diff at %d = %d", i, diff);
343     delta = MAX (delta, ABS (*data1 - *data2));
344     data1++;
345     data2++;
346   }
347
348   gst_buffer_unmap (buf1, &map1);
349   gst_buffer_unmap (buf2, &map2);
350
351   return delta;
352 }
353
354 static double
355 gst_compare_ssim_window (GstCompare * comp, guint8 * data1, guint8 * data2,
356     gint width, gint height, gint step, gint stride)
357 {
358   gint count = 0, i, j;
359   gint sum1 = 0, sum2 = 0, ssum1 = 0, ssum2 = 0, acov = 0;
360   gdouble avg1, avg2, var1, var2, cov;
361
362   const gdouble k1 = 0.01;
363   const gdouble k2 = 0.03;
364   const gdouble L = 255.0;
365   const gdouble c1 = (k1 * L) * (k1 * L);
366   const gdouble c2 = (k2 * L) * (k2 * L);
367
368   /* For empty images, return maximum similarity */
369   if (height <= 0 || width <= 0)
370     return 1.0;
371
372   /* plain and simple; no fancy optimizations */
373   for (i = 0; i < height; i++) {
374     for (j = 0; j < width; j++) {
375       sum1 += *data1;
376       sum2 += *data2;
377       ssum1 += *data1 * *data1;
378       ssum2 += *data2 * *data2;
379       acov += *data1 * *data2;
380       count++;
381       data1 += step;
382       data2 += step;
383     }
384     data1 -= j * step;
385     data2 -= j * step;
386     data1 += stride;
387     data2 += stride;
388   }
389
390   avg1 = sum1 / count;
391   avg2 = sum2 / count;
392   var1 = ssum1 / count - avg1 * avg1;
393   var2 = ssum2 / count - avg2 * avg2;
394   cov = acov / count - avg1 * avg2;
395
396   return (2 * avg1 * avg2 + c1) * (2 * cov + c2) /
397       ((avg1 * avg1 + avg2 * avg2 + c1) * (var1 + var2 + c2));
398 }
399
400 /* @width etc are for the particular component */
401 static gdouble
402 gst_compare_ssim_component (GstCompare * comp, guint8 * data1, guint8 * data2,
403     gint width, gint height, gint step, gint stride)
404 {
405   const gint window = 16;
406   gdouble ssim_sum = 0;
407   gint count = 0, i, j;
408
409   for (j = 0; j + (window / 2) < height; j += (window / 2)) {
410     for (i = 0; i + (window / 2) < width; i += (window / 2)) {
411       gdouble ssim;
412
413       ssim = gst_compare_ssim_window (comp, data1 + step * i + j * stride,
414           data2 + step * i + j * stride,
415           MIN (window, width - i), MIN (window, height - j), step, stride);
416       GST_LOG_OBJECT (comp, "ssim for %dx%d at (%d, %d) = %f", window, window,
417           i, j, ssim);
418       ssim_sum += ssim;
419       count++;
420     }
421   }
422
423   /* For empty images, return maximum similarity */
424   if (count == 0)
425     return 1.0;
426
427   return (ssim_sum / count);
428 }
429
430 static gdouble
431 gst_compare_ssim (GstCompare * comp, GstBuffer * buf1, GstCaps * caps1,
432     GstBuffer * buf2, GstCaps * caps2)
433 {
434   GstVideoInfo info1, info2;
435   GstVideoFrame frame1, frame2;
436   gint i, comps;
437   gdouble cssim[4], ssim, c[4] = { 1.0, 0.0, 0.0, 0.0 };
438
439   if (!caps1)
440     goto invalid_input;
441
442   if (!gst_video_info_from_caps (&info1, caps1))
443     goto invalid_input;
444
445   if (!caps2)
446     goto invalid_input;
447
448   if (!gst_video_info_from_caps (&info2, caps1))
449     goto invalid_input;
450
451   if (GST_VIDEO_INFO_FORMAT (&info1) != GST_VIDEO_INFO_FORMAT (&info2) ||
452       GST_VIDEO_INFO_WIDTH (&info1) != GST_VIDEO_INFO_WIDTH (&info2) ||
453       GST_VIDEO_INFO_HEIGHT (&info1) != GST_VIDEO_INFO_HEIGHT (&info2))
454     return comp->threshold + 1;
455
456   comps = GST_VIDEO_INFO_N_COMPONENTS (&info1);
457   /* note that some are reported both yuv and gray */
458   for (i = 0; i < comps; ++i)
459     c[i] = 1.0;
460   /* increase luma weight if yuv */
461   if (GST_VIDEO_INFO_IS_YUV (&info1) && (comps > 1))
462     c[0] = comps - 1;
463   for (i = 0; i < comps; ++i)
464     c[i] /= (GST_VIDEO_INFO_IS_YUV (&info1) && (comps > 1)) ?
465         2 * (comps - 1) : comps;
466
467   gst_video_frame_map (&frame1, &info1, buf1, GST_MAP_READ);
468   gst_video_frame_map (&frame2, &info2, buf2, GST_MAP_READ);
469
470   for (i = 0; i < comps; i++) {
471     gint cw, ch, step, stride;
472
473     /* only support most common formats */
474     if (GST_VIDEO_INFO_COMP_DEPTH (&info1, i) != 8)
475       goto unsupported_input;
476     cw = GST_VIDEO_FRAME_COMP_WIDTH (&frame1, i);
477     ch = GST_VIDEO_FRAME_COMP_HEIGHT (&frame1, i);
478     step = GST_VIDEO_FRAME_COMP_PSTRIDE (&frame1, i);
479     stride = GST_VIDEO_FRAME_COMP_STRIDE (&frame1, i);
480
481     GST_LOG_OBJECT (comp, "component %d", i);
482     cssim[i] = gst_compare_ssim_component (comp,
483         GST_VIDEO_FRAME_COMP_DATA (&frame1, i),
484         GST_VIDEO_FRAME_COMP_DATA (&frame2, i), cw, ch, step, stride);
485     GST_LOG_OBJECT (comp, "ssim[%d] = %f", i, cssim[i]);
486   }
487
488   gst_video_frame_unmap (&frame1);
489   gst_video_frame_unmap (&frame2);
490
491 #ifndef GST_DISABLE_GST_DEBUG
492   for (i = 0; i < 4; i++) {
493     GST_DEBUG_OBJECT (comp, "ssim[%d] = %f, c[%d] = %f", i, cssim[i], i, c[i]);
494   }
495 #endif
496
497   ssim = cssim[0] * c[0] + cssim[1] * c[1] + cssim[2] * c[2] + cssim[3] * c[3];
498
499   return ssim;
500
501   /* ERRORS */
502 invalid_input:
503   {
504     GST_ERROR_OBJECT (comp, "ssim method needs raw video input");
505     return 0;
506   }
507 unsupported_input:
508   {
509     GST_ERROR_OBJECT (comp, "raw video format not supported %" GST_PTR_FORMAT,
510         caps1);
511     return 0;
512   }
513 }
514
515 static void
516 gst_compare_buffers (GstCompare * comp, GstBuffer * buf1, GstCaps * caps1,
517     GstBuffer * buf2, GstCaps * caps2)
518 {
519   gdouble delta = 0;
520   gsize size1, size2;
521
522   /* first check metadata */
523   gst_compare_meta (comp, buf1, caps1, buf2, caps2);
524
525   size1 = gst_buffer_get_size (buf1);
526   size2 = gst_buffer_get_size (buf1);
527
528   /* check content according to method */
529   /* but at least size should match */
530   if (size1 != size2) {
531     delta = comp->threshold + 1;
532   } else {
533     GstMapInfo map1, map2;
534
535     gst_buffer_map (buf1, &map1, GST_MAP_READ);
536     gst_buffer_map (buf2, &map2, GST_MAP_READ);
537     GST_MEMDUMP_OBJECT (comp, "buffer 1", map1.data, map2.size);
538     GST_MEMDUMP_OBJECT (comp, "buffer 2", map2.data, map2.size);
539     gst_buffer_unmap (buf1, &map1);
540     gst_buffer_unmap (buf2, &map2);
541     switch (comp->method) {
542       case GST_COMPARE_METHOD_MEM:
543         delta = gst_compare_mem (comp, buf1, caps1, buf2, caps2);
544         break;
545       case GST_COMPARE_METHOD_MAX:
546         delta = gst_compare_max (comp, buf1, caps1, buf2, caps2);
547         break;
548       case GST_COMPARE_METHOD_SSIM:
549         delta = gst_compare_ssim (comp, buf1, caps1, buf2, caps2);
550         break;
551       default:
552         g_assert_not_reached ();
553         break;
554     }
555   }
556
557   if ((comp->upper && delta > comp->threshold) ||
558       (!comp->upper && delta < comp->threshold)) {
559     GST_WARNING_OBJECT (comp, "buffers %p and %p failed content match %f",
560         buf1, buf2, delta);
561
562     gst_element_post_message (GST_ELEMENT (comp),
563         gst_message_new_element (GST_OBJECT (comp),
564             gst_structure_new ("delta", "content", G_TYPE_DOUBLE, delta,
565                 NULL)));
566   }
567 }
568
569 static GstFlowReturn
570 gst_compare_collect_pads (GstCollectPads * cpads, GstCompare * comp)
571 {
572   GstBuffer *buf1, *buf2;
573   GstCaps *caps1, *caps2;
574
575   buf1 = gst_collect_pads_pop (comp->cpads,
576       gst_pad_get_element_private (comp->sinkpad));
577   caps1 = gst_pad_get_current_caps (comp->sinkpad);
578
579   buf2 = gst_collect_pads_pop (comp->cpads,
580       gst_pad_get_element_private (comp->checkpad));
581   caps2 = gst_pad_get_current_caps (comp->checkpad);
582
583   if (!buf1 && !buf2) {
584     gst_pad_push_event (comp->srcpad, gst_event_new_eos ());
585     return GST_FLOW_EOS;
586   } else if (buf1 && buf2) {
587     gst_compare_buffers (comp, buf1, caps1, buf2, caps2);
588   } else {
589     GST_WARNING_OBJECT (comp, "buffer %p != NULL", buf1 ? buf1 : buf2);
590
591     comp->count++;
592     gst_element_post_message (GST_ELEMENT (comp),
593         gst_message_new_element (GST_OBJECT (comp),
594             gst_structure_new ("delta", "count", G_TYPE_INT, comp->count,
595                 NULL)));
596   }
597
598   if (buf1)
599     gst_pad_push (comp->srcpad, buf1);
600
601   if (buf2)
602     gst_buffer_unref (buf2);
603
604   if (caps1)
605     gst_caps_unref (caps1);
606
607   if (caps2)
608     gst_caps_unref (caps2);
609
610   return GST_FLOW_OK;
611 }
612
613 static void
614 gst_compare_set_property (GObject * object, guint prop_id,
615     const GValue * value, GParamSpec * pspec)
616 {
617   GstCompare *comp = GST_COMPARE (object);
618
619   switch (prop_id) {
620     case PROP_META:
621       comp->meta = g_value_get_flags (value);
622       break;
623     case PROP_OFFSET_TS:
624       comp->offset_ts = g_value_get_boolean (value);
625       break;
626     case PROP_METHOD:
627       comp->method = g_value_get_enum (value);
628       break;
629     case PROP_THRESHOLD:
630       comp->threshold = g_value_get_double (value);
631       break;
632     case PROP_UPPER:
633       comp->upper = g_value_get_boolean (value);
634       break;
635     default:
636       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
637       break;
638   }
639 }
640
641 static void
642 gst_compare_get_property (GObject * object, guint prop_id, GValue * value,
643     GParamSpec * pspec)
644 {
645   GstCompare *comp = GST_COMPARE (object);
646
647   switch (prop_id) {
648     case PROP_META:
649       g_value_set_flags (value, comp->meta);
650       break;
651     case PROP_OFFSET_TS:
652       g_value_set_boolean (value, comp->offset_ts);
653       break;
654     case PROP_METHOD:
655       g_value_set_enum (value, comp->method);
656       break;
657     case PROP_THRESHOLD:
658       g_value_set_double (value, comp->threshold);
659       break;
660     case PROP_UPPER:
661       g_value_set_boolean (value, comp->upper);
662       break;
663     default:
664       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
665       break;
666   }
667 }
668
669 static GstStateChangeReturn
670 gst_compare_change_state (GstElement * element, GstStateChange transition)
671 {
672   GstCompare *comp = GST_COMPARE (element);
673   GstStateChangeReturn ret;
674
675   switch (transition) {
676     case GST_STATE_CHANGE_NULL_TO_READY:
677     case GST_STATE_CHANGE_READY_TO_PAUSED:
678       gst_collect_pads_start (comp->cpads);
679       break;
680     case GST_STATE_CHANGE_PAUSED_TO_READY:
681       gst_collect_pads_stop (comp->cpads);
682       break;
683     default:
684       break;
685   }
686
687   ret = GST_CALL_PARENT_WITH_DEFAULT (GST_ELEMENT_CLASS, change_state,
688       (element, transition), GST_STATE_CHANGE_SUCCESS);
689   if (ret != GST_STATE_CHANGE_SUCCESS)
690     return ret;
691
692   switch (transition) {
693     case GST_STATE_CHANGE_PAUSED_TO_READY:
694       gst_compare_reset (comp);
695       break;
696     default:
697       break;
698   }
699
700   return GST_STATE_CHANGE_SUCCESS;
701 }