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
gst_compare_method_get_type(void)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
gst_compare_finalize(GObject * object)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
gst_compare_class_init(GstCompareClass * klass)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
gst_compare_init(GstCompare * comp)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
gst_compare_reset(GstCompare * comp)223 gst_compare_reset (GstCompare * comp)
224 {
225 }
226
227 static gboolean
gst_compare_query(GstPad * pad,GstObject * parent,GstQuery * query)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
gst_compare_meta(GstCompare * comp,GstBuffer * buf1,GstCaps * caps1,GstBuffer * buf2,GstCaps * caps2)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
gst_compare_mem(GstCompare * comp,GstBuffer * buf1,GstCaps * caps1,GstBuffer * buf2,GstCaps * caps2)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
gst_compare_max(GstCompare * comp,GstBuffer * buf1,GstCaps * caps1,GstBuffer * buf2,GstCaps * caps2)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
gst_compare_ssim_window(GstCompare * comp,guint8 * data1,guint8 * data2,gint width,gint height,gint step,gint stride)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
gst_compare_ssim_component(GstCompare * comp,guint8 * data1,guint8 * data2,gint width,gint height,gint step,gint stride)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
gst_compare_ssim(GstCompare * comp,GstBuffer * buf1,GstCaps * caps1,GstBuffer * buf2,GstCaps * caps2)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
gst_compare_buffers(GstCompare * comp,GstBuffer * buf1,GstCaps * caps1,GstBuffer * buf2,GstCaps * caps2)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
gst_compare_collect_pads(GstCollectPads * cpads,GstCompare * comp)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
gst_compare_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)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
gst_compare_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)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
gst_compare_change_state(GstElement * element,GstStateChange transition)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 }
702