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