videoaggregator: Create a new GstVideoAggregator baseclass
[platform/upstream/gstreamer.git] / gst-libs / gst / video / gstvideoaggregator.c
1 /* Generic video aggregator plugin
2  * Copyright (C) 2004, 2008 Wim Taymans <wim@fluendo.com>
3  * Copyright (C) 2010 Sebastian Dröge <sebastian.droege@collabora.co.uk>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20
21 /**
22  * SECTION:gstvideoaggregator
23  * @short_description: Base class for video aggregators
24  *
25  * VideoAggregator can accept AYUV, ARGB and BGRA video streams. For each of the requested
26  * sink pads it will compare the incoming geometry and framerate to define the
27  * output parameters. Indeed output video frames will have the geometry of the
28  * biggest incoming video stream and the framerate of the fastest incoming one.
29  *
30  * VideoAggregator will do colorspace conversion.
31  * 
32  * Zorder for each input stream can be configured on the
33  * #GstVideoAggregatorPad.
34  *
35  */
36
37 #ifdef HAVE_CONFIG_H
38 #include "config.h"
39 #endif
40
41 #include <string.h>
42
43 #include "gstvideoaggregator.h"
44 #include "gstvideoaggregatorpad.h"
45 #include "videoconvert.h"
46
47 GST_DEBUG_CATEGORY_STATIC (gst_videoaggregator_debug);
48 #define GST_CAT_DEFAULT gst_videoaggregator_debug
49
50 /* Needed prototypes */
51 static void gst_videoaggregator_reset_qos (GstVideoAggregator * vagg);
52
53 /****************************************
54  * GstVideoAggregatorPad implementation *
55  ****************************************/
56
57 #define DEFAULT_PAD_ZORDER 0
58 enum
59 {
60   PROP_PAD_0,
61   PROP_PAD_ZORDER,
62 };
63
64 G_DEFINE_TYPE (GstVideoAggregatorPad, gst_videoaggregator_pad,
65     GST_TYPE_AGGREGATOR_PAD);
66
67 static void
68 gst_videoaggregator_pad_get_property (GObject * object, guint prop_id,
69     GValue * value, GParamSpec * pspec)
70 {
71   GstVideoAggregatorPad *pad = GST_VIDEO_AGGREGATOR_PAD (object);
72
73   switch (prop_id) {
74     case PROP_PAD_ZORDER:
75       g_value_set_uint (value, pad->zorder);
76       break;
77     default:
78       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
79       break;
80   }
81 }
82
83 static int
84 pad_zorder_compare (const GstVideoAggregatorPad * pad1,
85     const GstVideoAggregatorPad * pad2)
86 {
87   return pad1->zorder - pad2->zorder;
88 }
89
90 static void
91 gst_videoaggregator_pad_set_property (GObject * object, guint prop_id,
92     const GValue * value, GParamSpec * pspec)
93 {
94   GstVideoAggregatorPad *pad = GST_VIDEO_AGGREGATOR_PAD (object);
95   GstVideoAggregator *vagg =
96       GST_VIDEO_AGGREGATOR (gst_pad_get_parent (GST_PAD (pad)));
97
98   switch (prop_id) {
99     case PROP_PAD_ZORDER:
100       GST_OBJECT_LOCK (vagg);
101       pad->zorder = g_value_get_uint (value);
102       GST_ELEMENT (vagg)->sinkpads = g_list_sort (GST_ELEMENT (vagg)->sinkpads,
103           (GCompareFunc) pad_zorder_compare);
104       GST_OBJECT_UNLOCK (vagg);
105       break;
106     default:
107       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
108       break;
109   }
110
111   gst_object_unref (vagg);
112 }
113
114 static gboolean
115 _flush_pad (GstAggregatorPad * aggpad, GstAggregator * aggregator)
116 {
117   GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (aggregator);
118   GstVideoAggregatorPad *pad = GST_VIDEO_AGGREGATOR_PAD (aggpad);
119
120   gst_videoaggregator_reset_qos (vagg);
121   gst_buffer_replace (&pad->buffer, NULL);
122   pad->start_time = -1;
123   pad->end_time = -1;
124
125   return TRUE;
126 }
127
128 static void
129 gst_videoaggregator_pad_finalize (GObject * o)
130 {
131   GstVideoAggregatorPad *vaggpad = GST_VIDEO_AGGREGATOR_PAD (o);
132
133   if (vaggpad->convert)
134     videoconvert_convert_free (vaggpad->convert);
135   vaggpad->convert = NULL;
136
137   G_OBJECT_CLASS (gst_videoaggregator_pad_parent_class)->dispose (o);
138 }
139
140 static void
141 gst_videoaggregator_pad_class_init (GstVideoAggregatorPadClass * klass)
142 {
143   GObjectClass *gobject_class = (GObjectClass *) klass;
144   GstAggregatorPadClass *aggpadclass = (GstAggregatorPadClass *) klass;
145
146   gobject_class->set_property = gst_videoaggregator_pad_set_property;
147   gobject_class->get_property = gst_videoaggregator_pad_get_property;
148   gobject_class->finalize = gst_videoaggregator_pad_finalize;
149
150   g_object_class_install_property (gobject_class, PROP_PAD_ZORDER,
151       g_param_spec_uint ("zorder", "Z-Order", "Z Order of the picture",
152           0, 10000, DEFAULT_PAD_ZORDER,
153           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
154
155   aggpadclass->flush = GST_DEBUG_FUNCPTR (_flush_pad);
156 }
157
158 static void
159 gst_videoaggregator_pad_init (GstVideoAggregatorPad * vaggpad)
160 {
161   vaggpad->zorder = DEFAULT_PAD_ZORDER;
162   vaggpad->convert = NULL;
163   vaggpad->need_conversion_update = FALSE;
164   vaggpad->aggregated_frame = NULL;
165   vaggpad->converted_buffer = NULL;
166 }
167
168 /*********************************
169  * GstChildProxy implementation  *
170  *********************************/
171 static GObject *
172 gst_videoaggregator_child_proxy_get_child_by_index (GstChildProxy * child_proxy,
173     guint index)
174 {
175   GObject *obj;
176   GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (child_proxy);
177
178   GST_OBJECT_LOCK (vagg);
179   if ((obj = g_list_nth_data (GST_ELEMENT (vagg)->sinkpads, index)))
180     g_object_ref (obj);
181   GST_OBJECT_UNLOCK (vagg);
182
183   return obj;
184 }
185
186 static guint
187 gst_videoaggregator_child_proxy_get_children_count (GstChildProxy * child_proxy)
188 {
189   guint count = 0;
190
191   GST_OBJECT_LOCK (child_proxy);
192   count = GST_ELEMENT (child_proxy)->numsinkpads;
193   GST_OBJECT_UNLOCK (child_proxy);
194
195   GST_INFO_OBJECT (child_proxy, "Children Count: %d", count);
196
197   return count;
198 }
199
200 static void
201 gst_videoaggregator_child_proxy_init (gpointer g_iface, gpointer iface_data)
202 {
203   GstChildProxyInterface *iface = g_iface;
204
205   GST_INFO ("intializing child proxy interface");
206   iface->get_child_by_index =
207       gst_videoaggregator_child_proxy_get_child_by_index;
208   iface->get_children_count =
209       gst_videoaggregator_child_proxy_get_children_count;
210 }
211
212 /**************************************
213  * GstVideoAggregator implementation  *
214  **************************************/
215
216 #define GST_VIDEO_AGGREGATOR_GET_LOCK(vagg) (&GST_VIDEO_AGGREGATOR(vagg)->priv->lock)
217
218 #define GST_VIDEO_AGGREGATOR_LOCK(vagg)   G_STMT_START {       \
219   GST_LOG_OBJECT (vagg, "Taking EVENT lock from thread %p",    \
220         g_thread_self());                                      \
221   g_mutex_lock(GST_VIDEO_AGGREGATOR_GET_LOCK(vagg));           \
222   GST_LOG_OBJECT (vagg, "Took EVENT lock from thread %p",      \
223         g_thread_self());                                      \
224   } G_STMT_END
225
226 #define GST_VIDEO_AGGREGATOR_UNLOCK(vagg)   G_STMT_START {     \
227   GST_LOG_OBJECT (vagg, "Releasing EVENT lock from thread %p", \
228         g_thread_self());                                      \
229   g_mutex_unlock(GST_VIDEO_AGGREGATOR_GET_LOCK(vagg));         \
230   GST_LOG_OBJECT (vagg, "Took EVENT lock from thread %p",      \
231         g_thread_self());                                      \
232   } G_STMT_END
233
234
235 #define GST_VIDEO_AGGREGATOR_GET_SETCAPS_LOCK(vagg) (&GST_VIDEO_AGGREGATOR(vagg)->priv->setcaps_lock)
236 #define GST_VIDEO_AGGREGATOR_SETCAPS_LOCK(vagg)   G_STMT_START {  \
237   GST_LOG_OBJECT (vagg, "Taking SETCAPS lock from thread %p",     \
238         g_thread_self());                                         \
239   g_mutex_lock(GST_VIDEO_AGGREGATOR_GET_SETCAPS_LOCK(vagg));      \
240   GST_LOG_OBJECT (vagg, "Took SETCAPS lock from thread %p",       \
241         g_thread_self());                                         \
242   } G_STMT_END
243
244 #define GST_VIDEO_AGGREGATOR_SETCAPS_UNLOCK(vagg)   G_STMT_START {  \
245   GST_LOG_OBJECT (vagg, "Releasing SETCAPS lock from thread %p",    \
246         g_thread_self());                                           \
247   g_mutex_unlock(GST_VIDEO_AGGREGATOR_GET_SETCAPS_LOCK(vagg));      \
248   GST_LOG_OBJECT (vagg, "Took SETCAPS lock from thread %p",         \
249         g_thread_self());                                           \
250   } G_STMT_END
251
252 struct _GstVideoAggregatorPrivate
253 {
254   /* Lock to prevent the state to change while aggregating */
255   GMutex lock;
256
257   /* Lock to prevent two src setcaps from happening at the same time  */
258   GMutex setcaps_lock;
259
260   /* Current downstream segment */
261   GstClockTime ts_offset;
262   guint64 nframes;
263
264   /* QoS stuff */
265   gdouble proportion;
266   GstClockTime earliest_time;
267   guint64 qos_processed, qos_dropped;
268
269   /* current caps */
270   GstCaps *current_caps;
271   gboolean send_caps;
272 };
273
274 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstVideoAggregator, gst_videoaggregator,
275     GST_TYPE_AGGREGATOR, G_IMPLEMENT_INTERFACE (GST_TYPE_CHILD_PROXY,
276         gst_videoaggregator_child_proxy_init));
277
278 static void
279 _find_best_video_format (GstVideoAggregator * vagg, GstCaps * downstream_caps,
280     GstVideoInfo * best_info, GstVideoFormat * best_format,
281     gboolean * at_least_one_alpha)
282 {
283   GList *tmp;
284   GstCaps *possible_caps;
285   GstVideoAggregatorPad *pad;
286   gboolean need_alpha = FALSE;
287   gint best_format_number = 0;
288   GHashTable *formats_table = g_hash_table_new (g_direct_hash, g_direct_equal);
289
290   GST_OBJECT_LOCK (vagg);
291   for (tmp = GST_ELEMENT (vagg)->sinkpads; tmp; tmp = tmp->next) {
292     GstStructure *s;
293     gint format_number;
294
295     pad = tmp->data;
296
297     if (!pad->info.finfo)
298       continue;
299
300     if (pad->info.finfo->flags & GST_VIDEO_FORMAT_FLAG_ALPHA)
301       *at_least_one_alpha = TRUE;
302
303     /* If we want alpha, disregard all the other formats */
304     if (need_alpha && !(pad->info.finfo->flags & GST_VIDEO_FORMAT_FLAG_ALPHA))
305       continue;
306
307     /* This can happen if we release a pad and another pad hasn't been negotiated_caps yet */
308     if (GST_VIDEO_INFO_FORMAT (&pad->info) == GST_VIDEO_FORMAT_UNKNOWN)
309       continue;
310
311     possible_caps = gst_video_info_to_caps (&pad->info);
312
313     s = gst_caps_get_structure (possible_caps, 0);
314     gst_structure_remove_fields (s, "width", "height", "framerate",
315         "pixel-aspect-ratio", "interlace-mode", NULL);
316
317     /* Can downstream accept this format ? */
318     if (!gst_caps_can_intersect (downstream_caps, possible_caps)) {
319       gst_caps_unref (possible_caps);
320       continue;
321     }
322
323     gst_caps_unref (possible_caps);
324
325     format_number =
326         GPOINTER_TO_INT (g_hash_table_lookup (formats_table,
327             GINT_TO_POINTER (GST_VIDEO_INFO_FORMAT (&pad->info))));
328     format_number += 1;
329
330     g_hash_table_replace (formats_table,
331         GINT_TO_POINTER (GST_VIDEO_INFO_FORMAT (&pad->info)),
332         GINT_TO_POINTER (format_number));
333
334     /* If that pad is the first with alpha, set it as the new best format */
335     if (!need_alpha && (pad->info.finfo->flags & GST_VIDEO_FORMAT_FLAG_ALPHA)) {
336       need_alpha = TRUE;
337       *best_format = GST_VIDEO_INFO_FORMAT (&pad->info);
338       *best_info = pad->info;
339       best_format_number = format_number;
340     } else if (format_number > best_format_number) {
341       *best_format = GST_VIDEO_INFO_FORMAT (&pad->info);
342       *best_info = pad->info;
343       best_format_number = format_number;
344     }
345   }
346   GST_OBJECT_UNLOCK (vagg);
347
348   g_hash_table_unref (formats_table);
349 }
350
351 static gboolean
352 gst_videoaggregator_update_converters (GstVideoAggregator * vagg)
353 {
354   GList *tmp;
355   GstVideoAggregatorPad *pad;
356   GstVideoFormat best_format;
357   GstVideoInfo best_info;
358   gboolean at_least_one_alpha = FALSE;
359   GstCaps *downstream_caps;
360   gchar *best_colorimetry;
361   const gchar *best_chroma;
362   GstElementClass *klass = GST_ELEMENT_GET_CLASS (vagg);
363   GstVideoAggregatorClass *vagg_klass = (GstVideoAggregatorClass *) klass;
364   GstAggregator *agg = GST_AGGREGATOR (vagg);
365
366   best_format = GST_VIDEO_FORMAT_UNKNOWN;
367   gst_video_info_init (&best_info);
368
369   downstream_caps = gst_pad_get_allowed_caps (agg->srcpad);
370
371   if (!downstream_caps || gst_caps_is_empty (downstream_caps)) {
372     GST_INFO_OBJECT (vagg, "No downstream caps found %"
373         GST_PTR_FORMAT, downstream_caps);
374     return FALSE;
375   }
376
377
378   if (vagg_klass->disable_frame_conversion == FALSE)
379     _find_best_video_format (vagg, downstream_caps, &best_info, &best_format,
380         &at_least_one_alpha);
381
382   if (best_format == GST_VIDEO_FORMAT_UNKNOWN) {
383     downstream_caps = gst_caps_fixate (downstream_caps);
384     gst_video_info_from_caps (&best_info, downstream_caps);
385     best_format = GST_VIDEO_INFO_FORMAT (&best_info);
386   }
387
388   gst_caps_unref (downstream_caps);
389
390   if (at_least_one_alpha
391       && !(best_info.finfo->flags & GST_VIDEO_FORMAT_FLAG_ALPHA)) {
392     GST_ELEMENT_ERROR (vagg, CORE, NEGOTIATION,
393         ("At least one of the input pads contains alpha, but downstream can't support alpha."),
394         ("Either convert your inputs to not contain alpha or add a videoconvert after the aggregator"));
395     return FALSE;
396   }
397
398   best_colorimetry = gst_video_colorimetry_to_string (&(best_info.colorimetry));
399   best_chroma = gst_video_chroma_to_string (best_info.chroma_site);
400   vagg->info = best_info;
401
402   GST_DEBUG_OBJECT (vagg,
403       "The output format will now be : %d with colorimetry : %s and chroma : %s",
404       best_format, best_colorimetry, best_chroma);
405
406   /* Then browse the sinks once more, setting or unsetting conversion if needed */
407   GST_OBJECT_LOCK (vagg);
408   for (tmp = GST_ELEMENT (vagg)->sinkpads; tmp; tmp = tmp->next) {
409     gchar *colorimetry;
410     const gchar *chroma;
411
412     pad = tmp->data;
413
414     if (!pad->info.finfo)
415       continue;
416
417     if (GST_VIDEO_INFO_FORMAT (&pad->info) == GST_VIDEO_FORMAT_UNKNOWN)
418       continue;
419
420     if (pad->convert)
421       videoconvert_convert_free (pad->convert);
422
423     pad->convert = NULL;
424
425     colorimetry = gst_video_colorimetry_to_string (&(pad->info.colorimetry));
426     chroma = gst_video_chroma_to_string (pad->info.chroma_site);
427
428     if (best_format != GST_VIDEO_INFO_FORMAT (&pad->info) ||
429         g_strcmp0 (colorimetry, best_colorimetry) ||
430         g_strcmp0 (chroma, best_chroma)) {
431       GST_DEBUG_OBJECT (pad, "This pad will be converted from %d to %d",
432           GST_VIDEO_INFO_FORMAT (&pad->info),
433           GST_VIDEO_INFO_FORMAT (&best_info));
434       pad->convert = videoconvert_convert_new (&pad->info, &best_info);
435       pad->need_conversion_update = TRUE;
436       if (!pad->convert) {
437         g_free (colorimetry);
438         g_free (best_colorimetry);
439         GST_WARNING ("No path found for conversion");
440         GST_OBJECT_UNLOCK (vagg);
441         return FALSE;
442       }
443     } else {
444       GST_DEBUG_OBJECT (pad, "This pad will not need conversion");
445     }
446     g_free (colorimetry);
447   }
448   GST_OBJECT_UNLOCK (vagg);
449
450   g_free (best_colorimetry);
451   return TRUE;
452 }
453
454 static gboolean
455 gst_videoaggregator_src_setcaps (GstVideoAggregator * vagg, GstCaps * caps)
456 {
457   GstAggregator *agg = GST_AGGREGATOR (vagg);
458   gboolean ret = FALSE;
459   GstVideoInfo info;
460
461   GstPad *pad = GST_AGGREGATOR (vagg)->srcpad;
462
463   GST_INFO_OBJECT (pad, "set src caps: %" GST_PTR_FORMAT, caps);
464
465   if (!gst_video_info_from_caps (&info, caps))
466     goto done;
467
468   ret = TRUE;
469
470   GST_VIDEO_AGGREGATOR_LOCK (vagg);
471
472   if (GST_VIDEO_INFO_FPS_N (&vagg->info) != GST_VIDEO_INFO_FPS_N (&info) ||
473       GST_VIDEO_INFO_FPS_D (&vagg->info) != GST_VIDEO_INFO_FPS_D (&info)) {
474     if (agg->segment.position != -1) {
475       vagg->priv->ts_offset = agg->segment.position - agg->segment.start;
476       vagg->priv->nframes = 0;
477     }
478     gst_videoaggregator_reset_qos (vagg);
479   }
480
481   vagg->info = info;
482
483   GST_VIDEO_AGGREGATOR_UNLOCK (vagg);
484
485   if (vagg->priv->current_caps == NULL ||
486       gst_caps_is_equal (caps, vagg->priv->current_caps) == FALSE) {
487     gst_caps_replace (&vagg->priv->current_caps, caps);
488     vagg->priv->send_caps = TRUE;
489   }
490
491 done:
492   return ret;
493 }
494
495 static gboolean
496 gst_videoaggregator_update_src_caps (GstVideoAggregator * vagg)
497 {
498   GList *l;
499   gint best_width = -1, best_height = -1;
500   gdouble best_fps = -1, cur_fps;
501   gint best_fps_n = -1, best_fps_d = -1;
502   gboolean ret = TRUE;
503   GstElementClass *klass = GST_ELEMENT_GET_CLASS (vagg);
504   GstVideoAggregatorClass *vagg_klass = (GstVideoAggregatorClass *) klass;
505   GstAggregator *agg = GST_AGGREGATOR (vagg);
506
507   GST_VIDEO_AGGREGATOR_SETCAPS_LOCK (vagg);
508   GST_VIDEO_AGGREGATOR_LOCK (vagg);
509   GST_OBJECT_LOCK (vagg);
510   for (l = GST_ELEMENT (vagg)->sinkpads; l; l = l->next) {
511     GstVideoAggregatorPad *mpad = l->data;
512     gint this_width, this_height;
513     gint fps_n, fps_d;
514     gint width, height;
515
516     fps_n = GST_VIDEO_INFO_FPS_N (&mpad->info);
517     fps_d = GST_VIDEO_INFO_FPS_D (&mpad->info);
518     width = GST_VIDEO_INFO_WIDTH (&mpad->info);
519     height = GST_VIDEO_INFO_HEIGHT (&mpad->info);
520
521     if (width == 0 || height == 0)
522       continue;
523
524     this_width = width;
525     this_height = height;
526
527     if (best_width < this_width)
528       best_width = this_width;
529     if (best_height < this_height)
530       best_height = this_height;
531
532     if (fps_d == 0)
533       cur_fps = 0.0;
534     else
535       gst_util_fraction_to_double (fps_n, fps_d, &cur_fps);
536
537     if (best_fps < cur_fps) {
538       best_fps = cur_fps;
539       best_fps_n = fps_n;
540       best_fps_d = fps_d;
541     }
542   }
543   GST_OBJECT_UNLOCK (vagg);
544
545   if (best_fps_n <= 0 || best_fps_d <= 0 || best_fps == 0.0) {
546     best_fps_n = 25;
547     best_fps_d = 1;
548     best_fps = 25.0;
549   }
550
551   if (best_width > 0 && best_height > 0 && best_fps > 0) {
552     GstCaps *caps, *peercaps;
553     GstStructure *s;
554     GstVideoInfo info;
555
556     if (GST_VIDEO_INFO_FPS_N (&vagg->info) != best_fps_n ||
557         GST_VIDEO_INFO_FPS_D (&vagg->info) != best_fps_d) {
558       if (agg->segment.position != -1) {
559         vagg->priv->ts_offset = agg->segment.position - agg->segment.start;
560         vagg->priv->nframes = 0;
561       }
562     }
563     gst_video_info_init (&info);
564     gst_video_info_set_format (&info, GST_VIDEO_INFO_FORMAT (&vagg->info),
565         best_width, best_height);
566     info.fps_n = best_fps_n;
567     info.fps_d = best_fps_d;
568     info.par_n = GST_VIDEO_INFO_PAR_N (&vagg->info);
569     info.par_d = GST_VIDEO_INFO_PAR_D (&vagg->info);
570
571     if (vagg_klass->update_info) {
572       if (!vagg_klass->update_info (vagg, &info)) {
573         ret = FALSE;
574         GST_VIDEO_AGGREGATOR_UNLOCK (vagg);
575         goto done;
576       }
577     }
578
579     caps = gst_video_info_to_caps (&info);
580
581     peercaps = gst_pad_peer_query_caps (agg->srcpad, NULL);
582     if (peercaps) {
583       GstCaps *tmp;
584
585       s = gst_caps_get_structure (caps, 0);
586       gst_structure_set (s, "width", GST_TYPE_INT_RANGE, 1, G_MAXINT, "height",
587           GST_TYPE_INT_RANGE, 1, G_MAXINT, "framerate", GST_TYPE_FRACTION_RANGE,
588           0, 1, G_MAXINT, 1, NULL);
589
590       tmp = gst_caps_intersect (caps, peercaps);
591       gst_caps_unref (caps);
592       gst_caps_unref (peercaps);
593       caps = tmp;
594       if (gst_caps_is_empty (caps)) {
595         GST_DEBUG_OBJECT (vagg, "empty caps");
596         ret = FALSE;
597         GST_VIDEO_AGGREGATOR_UNLOCK (vagg);
598         GST_OBJECT_UNLOCK (vagg);
599         goto done;
600       }
601
602       caps = gst_caps_truncate (caps);
603       s = gst_caps_get_structure (caps, 0);
604       gst_structure_fixate_field_nearest_int (s, "width", info.width);
605       gst_structure_fixate_field_nearest_int (s, "height", info.height);
606       gst_structure_fixate_field_nearest_fraction (s, "framerate", best_fps_n,
607           best_fps_d);
608
609       gst_structure_get_int (s, "width", &info.width);
610       gst_structure_get_int (s, "height", &info.height);
611       gst_structure_get_fraction (s, "framerate", &info.fps_n, &info.fps_d);
612     }
613
614     gst_caps_unref (caps);
615     caps = gst_video_info_to_caps (&info);
616
617     GST_VIDEO_AGGREGATOR_UNLOCK (vagg);
618     GST_OBJECT_UNLOCK (vagg);
619
620     if (gst_videoaggregator_src_setcaps (vagg, caps)) {
621       if (vagg_klass->negotiated_caps)
622         ret =
623             GST_VIDEO_AGGREGATOR_GET_CLASS (vagg)->negotiated_caps (vagg, caps);
624     }
625     gst_caps_unref (caps);
626   } else {
627     GST_VIDEO_AGGREGATOR_UNLOCK (vagg);
628   }
629
630 done:
631   GST_VIDEO_AGGREGATOR_SETCAPS_UNLOCK (vagg);
632
633   return ret;
634 }
635
636 static gboolean
637 gst_videoaggregator_pad_sink_setcaps (GstPad * pad, GstObject * parent,
638     GstCaps * caps)
639 {
640   GstVideoAggregator *vagg;
641   GstVideoAggregatorPad *vaggpad;
642   GstVideoInfo info;
643   gboolean ret = FALSE;
644
645   GST_INFO_OBJECT (pad, "Setting caps %" GST_PTR_FORMAT, caps);
646
647   vagg = GST_VIDEO_AGGREGATOR (parent);
648   vaggpad = GST_VIDEO_AGGREGATOR_PAD (pad);
649
650   if (!gst_video_info_from_caps (&info, caps)) {
651     GST_DEBUG_OBJECT (pad, "Failed to parse caps");
652     goto beach;
653   }
654
655   GST_VIDEO_AGGREGATOR_LOCK (vagg);
656   if (GST_VIDEO_INFO_FORMAT (&vagg->info) != GST_VIDEO_FORMAT_UNKNOWN) {
657     if (GST_VIDEO_INFO_PAR_N (&vagg->info) != GST_VIDEO_INFO_PAR_N (&info)
658         || GST_VIDEO_INFO_PAR_D (&vagg->info) != GST_VIDEO_INFO_PAR_D (&info) ||
659         GST_VIDEO_INFO_INTERLACE_MODE (&vagg->info) !=
660         GST_VIDEO_INFO_INTERLACE_MODE (&info)) {
661       GST_ERROR_OBJECT (pad,
662           "got input caps %" GST_PTR_FORMAT ", but " "current caps are %"
663           GST_PTR_FORMAT, caps, vagg->priv->current_caps);
664       GST_VIDEO_AGGREGATOR_UNLOCK (vagg);
665       return FALSE;
666     }
667   }
668
669   vaggpad->info = info;
670
671   ret = gst_videoaggregator_update_converters (vagg);
672   GST_VIDEO_AGGREGATOR_UNLOCK (vagg);
673
674   if (ret)
675     ret = gst_videoaggregator_update_src_caps (vagg);
676
677 beach:
678   return ret;
679 }
680
681 static GstCaps *
682 gst_videoaggregator_pad_sink_getcaps (GstPad * pad, GstVideoAggregator * vagg,
683     GstCaps * filter)
684 {
685   GstCaps *srccaps;
686   GstCaps *template_caps;
687   GstCaps *filtered_caps;
688   GstCaps *returned_caps;
689   GstStructure *s;
690   gboolean had_current_caps = TRUE;
691   gint i, n;
692   GstAggregator *agg = GST_AGGREGATOR (vagg);
693
694   template_caps = gst_pad_get_pad_template_caps (GST_PAD (agg->srcpad));
695
696   srccaps = gst_pad_get_current_caps (GST_PAD (agg->srcpad));
697   if (srccaps == NULL) {
698     had_current_caps = FALSE;
699     srccaps = template_caps;
700   }
701
702   srccaps = gst_caps_make_writable (srccaps);
703
704   n = gst_caps_get_size (srccaps);
705   for (i = 0; i < n; i++) {
706     s = gst_caps_get_structure (srccaps, i);
707     gst_structure_set (s, "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
708         "height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
709         "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
710     if (!gst_structure_has_field (s, "pixel-aspect-ratio"))
711       gst_structure_set (s, "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1,
712           NULL);
713
714     gst_structure_remove_fields (s, "colorimetry", "chroma-site", "format",
715         NULL);
716   }
717
718   filtered_caps = srccaps;
719   if (filter)
720     filtered_caps = gst_caps_intersect (srccaps, filter);
721   returned_caps = gst_caps_intersect (filtered_caps, template_caps);
722
723   gst_caps_unref (srccaps);
724   if (filter)
725     gst_caps_unref (filtered_caps);
726   if (had_current_caps)
727     gst_caps_unref (template_caps);
728
729   return returned_caps;
730 }
731
732 static void
733 gst_videoaggregator_update_qos (GstVideoAggregator * vagg, gdouble proportion,
734     GstClockTimeDiff diff, GstClockTime timestamp)
735 {
736   GST_DEBUG_OBJECT (vagg,
737       "Updating QoS: proportion %lf, diff %s%" GST_TIME_FORMAT ", timestamp %"
738       GST_TIME_FORMAT, proportion, (diff < 0) ? "-" : "",
739       GST_TIME_ARGS (ABS (diff)), GST_TIME_ARGS (timestamp));
740
741   GST_OBJECT_LOCK (vagg);
742   vagg->priv->proportion = proportion;
743   if (G_LIKELY (timestamp != GST_CLOCK_TIME_NONE)) {
744     if (G_UNLIKELY (diff > 0))
745       vagg->priv->earliest_time =
746           timestamp + 2 * diff + gst_util_uint64_scale_int_round (GST_SECOND,
747           GST_VIDEO_INFO_FPS_D (&vagg->info),
748           GST_VIDEO_INFO_FPS_N (&vagg->info));
749     else
750       vagg->priv->earliest_time = timestamp + diff;
751   } else {
752     vagg->priv->earliest_time = GST_CLOCK_TIME_NONE;
753   }
754   GST_OBJECT_UNLOCK (vagg);
755 }
756
757 static void
758 gst_videoaggregator_reset_qos (GstVideoAggregator * vagg)
759 {
760   gst_videoaggregator_update_qos (vagg, 0.5, 0, GST_CLOCK_TIME_NONE);
761   vagg->priv->qos_processed = vagg->priv->qos_dropped = 0;
762 }
763
764 static void
765 gst_videoaggregator_read_qos (GstVideoAggregator * vagg, gdouble * proportion,
766     GstClockTime * time)
767 {
768   GST_OBJECT_LOCK (vagg);
769   *proportion = vagg->priv->proportion;
770   *time = vagg->priv->earliest_time;
771   GST_OBJECT_UNLOCK (vagg);
772 }
773
774 static void
775 gst_videoaggregator_reset (GstVideoAggregator * vagg)
776 {
777   GstAggregator *agg = GST_AGGREGATOR (vagg);
778   GList *l;
779
780   gst_video_info_init (&vagg->info);
781   vagg->priv->ts_offset = 0;
782   vagg->priv->nframes = 0;
783
784   gst_segment_init (&agg->segment, GST_FORMAT_TIME);
785   agg->segment.position = -1;
786
787   gst_videoaggregator_reset_qos (vagg);
788
789   GST_OBJECT_LOCK (vagg);
790   for (l = GST_ELEMENT (vagg)->sinkpads; l; l = l->next) {
791     GstVideoAggregatorPad *p = l->data;
792
793     gst_buffer_replace (&p->buffer, NULL);
794     p->start_time = -1;
795     p->end_time = -1;
796
797     gst_video_info_init (&p->info);
798   }
799   GST_OBJECT_UNLOCK (vagg);
800 }
801
802 #define GST_FLOW_NEEDS_DATA GST_FLOW_CUSTOM_ERROR
803 static gint
804 gst_videoaggregator_fill_queues (GstVideoAggregator * vagg,
805     GstClockTime output_start_time, GstClockTime output_end_time)
806 {
807   GstAggregator *agg = GST_AGGREGATOR (vagg);
808   GList *l;
809   gboolean eos = TRUE;
810   gboolean need_more_data = FALSE;
811
812   GST_OBJECT_LOCK (vagg);
813   for (l = GST_ELEMENT (vagg)->sinkpads; l; l = l->next) {
814     GstVideoAggregatorPad *pad = l->data;
815     GstSegment *segment;
816     GstAggregatorPad *bpad;
817     GstBuffer *buf;
818     GstVideoInfo *vinfo;
819     gboolean is_eos;
820
821     bpad = GST_AGGREGATOR_PAD (pad);
822     segment = &bpad->segment;
823     is_eos = bpad->eos;
824     buf = gst_aggregator_pad_get_buffer (bpad);
825     if (buf) {
826       GstClockTime start_time, end_time;
827
828       start_time = GST_BUFFER_TIMESTAMP (buf);
829       if (start_time == -1) {
830         gst_buffer_unref (buf);
831         GST_DEBUG_OBJECT (pad, "Need timestamped buffers!");
832         GST_OBJECT_UNLOCK (vagg);
833         return GST_FLOW_ERROR;
834       }
835
836       vinfo = &pad->info;
837
838       /* FIXME: Make all this work with negative rates */
839
840       if ((pad->buffer && start_time < GST_BUFFER_TIMESTAMP (pad->buffer))
841           || (pad->queued && start_time < GST_BUFFER_TIMESTAMP (pad->queued))) {
842         GST_DEBUG_OBJECT (pad, "Buffer from the past, dropping");
843         gst_buffer_unref (buf);
844         buf = gst_aggregator_pad_steal_buffer (bpad);
845         gst_buffer_unref (buf);
846         need_more_data = TRUE;
847         continue;
848       }
849
850       if (pad->queued) {
851         end_time = start_time - GST_BUFFER_TIMESTAMP (pad->queued);
852         start_time = GST_BUFFER_TIMESTAMP (pad->queued);
853         gst_buffer_unref (buf);
854         buf = gst_buffer_ref (pad->queued);
855         vinfo = &pad->queued_vinfo;
856       } else {
857         end_time = GST_BUFFER_DURATION (buf);
858
859         if (end_time == -1) {
860           pad->queued = buf;
861           buf = gst_aggregator_pad_steal_buffer (bpad);
862           gst_buffer_unref (buf);
863           pad->queued_vinfo = pad->info;
864           GST_DEBUG ("end time is -1 and nothing queued");
865           need_more_data = TRUE;
866           continue;
867         }
868       }
869
870       g_assert (start_time != -1 && end_time != -1);
871       end_time += start_time;   /* convert from duration to position */
872
873       /* Check if it's inside the segment */
874       if (start_time >= segment->stop || end_time < segment->start) {
875         GST_DEBUG_OBJECT (pad,
876             "Buffer outside the segment : segment: [%" GST_TIME_FORMAT " -- %"
877             GST_TIME_FORMAT "]" " Buffer [%" GST_TIME_FORMAT " -- %"
878             GST_TIME_FORMAT "]", GST_TIME_ARGS (segment->stop),
879             GST_TIME_ARGS (segment->start), GST_TIME_ARGS (start_time),
880             GST_TIME_ARGS (end_time));
881
882         if (buf == pad->queued) {
883           gst_buffer_unref (buf);
884           gst_buffer_replace (&pad->queued, NULL);
885         } else {
886           gst_buffer_unref (buf);
887           buf = gst_aggregator_pad_steal_buffer (bpad);
888           gst_buffer_unref (buf);
889         }
890
891         need_more_data = TRUE;
892         continue;
893       }
894
895       /* Clip to segment and convert to running time */
896       start_time = MAX (start_time, segment->start);
897       if (segment->stop != -1)
898         end_time = MIN (end_time, segment->stop);
899       start_time =
900           gst_segment_to_running_time (segment, GST_FORMAT_TIME, start_time);
901       end_time =
902           gst_segment_to_running_time (segment, GST_FORMAT_TIME, end_time);
903       g_assert (start_time != -1 && end_time != -1);
904
905       /* Convert to the output segment rate */
906       if (ABS (agg->segment.rate) != 1.0) {
907         start_time *= ABS (agg->segment.rate);
908         end_time *= ABS (agg->segment.rate);
909       }
910
911       if (pad->end_time != -1 && pad->end_time > end_time) {
912         GST_DEBUG_OBJECT (pad, "Buffer from the past, dropping");
913         if (buf == pad->queued) {
914           gst_buffer_unref (buf);
915           gst_buffer_replace (&pad->queued, NULL);
916         } else {
917           gst_buffer_unref (buf);
918           buf = gst_aggregator_pad_steal_buffer (bpad);
919           gst_buffer_unref (buf);
920         }
921
922         need_more_data = TRUE;
923         continue;
924       }
925
926       if (end_time >= output_start_time && start_time < output_end_time) {
927         GST_DEBUG_OBJECT (pad,
928             "Taking new buffer with start time %" GST_TIME_FORMAT,
929             GST_TIME_ARGS (start_time));
930         gst_buffer_replace (&pad->buffer, buf);
931         pad->buffer_vinfo = *vinfo;
932         pad->start_time = start_time;
933         pad->end_time = end_time;
934
935         if (buf == pad->queued) {
936           gst_buffer_unref (buf);
937           gst_buffer_replace (&pad->queued, NULL);
938         } else {
939           gst_buffer_unref (buf);
940           buf = gst_aggregator_pad_steal_buffer (bpad);
941           gst_buffer_unref (buf);
942         }
943         eos = FALSE;
944       } else if (start_time >= output_end_time) {
945         GST_DEBUG_OBJECT (pad, "Keeping buffer until %" GST_TIME_FORMAT,
946             GST_TIME_ARGS (start_time));
947         eos = FALSE;
948       } else {
949         GST_DEBUG_OBJECT (pad, "Too old buffer -- dropping");
950         if (buf == pad->queued) {
951           gst_buffer_unref (buf);
952           gst_buffer_replace (&pad->queued, NULL);
953         } else {
954           gst_buffer_unref (buf);
955           buf = gst_aggregator_pad_steal_buffer (bpad);
956           gst_buffer_unref (buf);
957         }
958
959         need_more_data = TRUE;
960         continue;
961       }
962     } else {
963       if (pad->end_time != -1) {
964         if (pad->end_time <= output_start_time) {
965           gst_buffer_replace (&pad->buffer, NULL);
966           pad->start_time = pad->end_time = -1;
967           if (is_eos) {
968             GST_DEBUG ("I just need more data");
969             need_more_data = TRUE;
970           }
971         } else if (is_eos) {
972           eos = FALSE;
973         }
974       }
975     }
976   }
977   GST_OBJECT_UNLOCK (vagg);
978
979   if (need_more_data)
980     return GST_FLOW_NEEDS_DATA;
981   if (eos)
982     return GST_FLOW_EOS;
983
984   return GST_FLOW_OK;
985 }
986
987 static gboolean
988 prepare_frames (GstVideoAggregator * vagg, GstVideoAggregatorPad * pad)
989 {
990   GstAggregatorPad *bpad = GST_AGGREGATOR_PAD (pad);
991
992   static GstAllocationParams params = { 0, 15, 0, 0, };
993
994   if (pad->buffer != NULL) {
995     guint outsize;
996     GstClockTime timestamp;
997     gint64 stream_time;
998     GstSegment *seg;
999     GstVideoFrame *converted_frame = g_slice_new0 (GstVideoFrame);
1000     GstBuffer *converted_buf = NULL;
1001     GstVideoFrame *frame = g_slice_new0 (GstVideoFrame);
1002
1003     seg = &bpad->segment;
1004
1005     timestamp = GST_BUFFER_TIMESTAMP (pad->buffer);
1006
1007     stream_time = gst_segment_to_stream_time (seg, GST_FORMAT_TIME, timestamp);
1008
1009     /* sync object properties on stream time */
1010     if (GST_CLOCK_TIME_IS_VALID (stream_time))
1011       gst_object_sync_values (GST_OBJECT (pad), stream_time);
1012
1013
1014     if (!gst_video_frame_map (frame, &pad->buffer_vinfo, pad->buffer,
1015             GST_MAP_READ)) {
1016       GST_WARNING_OBJECT (vagg, "Could not map input buffer");
1017     }
1018
1019     if (pad->convert) {
1020       gint converted_size;
1021
1022       /* We wait until here to set the conversion infos, in case vagg->info changed */
1023       if (pad->need_conversion_update) {
1024         pad->conversion_info = vagg->info;
1025         gst_video_info_set_format (&(pad->conversion_info),
1026             GST_VIDEO_INFO_FORMAT (&vagg->info), pad->info.width,
1027             pad->info.height);
1028         pad->need_conversion_update = FALSE;
1029       }
1030
1031       converted_size = pad->conversion_info.size;
1032       outsize = GST_VIDEO_INFO_SIZE (&vagg->info);
1033       converted_size = converted_size > outsize ? converted_size : outsize;
1034       converted_buf = gst_buffer_new_allocate (NULL, converted_size, &params);
1035
1036       if (!gst_video_frame_map (converted_frame, &(pad->conversion_info),
1037               converted_buf, GST_MAP_READWRITE)) {
1038         GST_WARNING_OBJECT (vagg, "Could not map converted frame");
1039
1040         return FALSE;
1041       }
1042
1043       videoconvert_convert_convert (pad->convert, converted_frame, frame);
1044       pad->converted_buffer = converted_buf;
1045       gst_video_frame_unmap (frame);
1046     } else {
1047       converted_frame = frame;
1048       converted_buf = pad->buffer;
1049     }
1050
1051     pad->aggregated_frame = converted_frame;
1052   }
1053
1054   return TRUE;
1055 }
1056
1057 static gboolean
1058 clean_pad (GstVideoAggregator * vagg, GstVideoAggregatorPad * pad)
1059 {
1060   if (pad->aggregated_frame) {
1061     gst_video_frame_unmap (pad->aggregated_frame);
1062     g_slice_free (GstVideoFrame, pad->aggregated_frame);
1063     pad->aggregated_frame = NULL;
1064   }
1065
1066   if (pad->converted_buffer) {
1067     gst_buffer_unref (pad->converted_buffer);
1068     pad->converted_buffer = NULL;
1069   }
1070
1071   return TRUE;
1072 }
1073
1074 static GstFlowReturn
1075 gst_videoaggregator_do_aggregate (GstVideoAggregator * vagg,
1076     GstClockTime output_start_time, GstClockTime output_end_time,
1077     GstBuffer ** outbuf)
1078 {
1079   GstFlowReturn ret = GST_FLOW_OK;
1080   GstElementClass *klass = GST_ELEMENT_GET_CLASS (vagg);
1081   GstVideoAggregatorClass *vagg_klass = (GstVideoAggregatorClass *) klass;
1082
1083   g_assert (vagg_klass->aggregate_frames != NULL);
1084   g_assert (vagg_klass->get_output_buffer != NULL);
1085
1086   if ((ret = vagg_klass->get_output_buffer (vagg, outbuf)) != GST_FLOW_OK) {
1087     GST_WARNING_OBJECT (vagg, "Could not get an output buffer, reason: %s",
1088         gst_flow_get_name (ret));
1089     return ret;
1090   }
1091
1092   GST_BUFFER_TIMESTAMP (*outbuf) = output_start_time;
1093   GST_BUFFER_DURATION (*outbuf) = output_end_time - output_start_time;
1094
1095   if (vagg_klass->disable_frame_conversion == FALSE) {
1096     /* Here we convert all the frames the subclass will have to aggregate */
1097     gst_aggregator_iterate_sinkpads (GST_AGGREGATOR (vagg),
1098         (GstAggregatorPadForeachFunc) prepare_frames, NULL);
1099   }
1100
1101   ret = vagg_klass->aggregate_frames (vagg, *outbuf);
1102
1103   gst_aggregator_iterate_sinkpads (GST_AGGREGATOR (vagg),
1104       (GstAggregatorPadForeachFunc) clean_pad, NULL);
1105
1106   return ret;
1107 }
1108
1109 /* Perform qos calculations before processing the next frame. Returns TRUE if
1110  * the frame should be processed, FALSE if the frame can be dropped entirely */
1111 static gint64
1112 gst_videoaggregator_do_qos (GstVideoAggregator * vagg, GstClockTime timestamp)
1113 {
1114   GstAggregator *agg = GST_AGGREGATOR (vagg);
1115   GstClockTime qostime, earliest_time;
1116   gdouble proportion;
1117   gint64 jitter;
1118
1119   /* no timestamp, can't do QoS => process frame */
1120   if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (timestamp))) {
1121     GST_LOG_OBJECT (vagg, "invalid timestamp, can't do QoS, process frame");
1122     return -1;
1123   }
1124
1125   /* get latest QoS observation values */
1126   gst_videoaggregator_read_qos (vagg, &proportion, &earliest_time);
1127
1128   /* skip qos if we have no observation (yet) => process frame */
1129   if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (earliest_time))) {
1130     GST_LOG_OBJECT (vagg, "no observation yet, process frame");
1131     return -1;
1132   }
1133
1134   /* qos is done on running time */
1135   qostime =
1136       gst_segment_to_running_time (&agg->segment, GST_FORMAT_TIME, timestamp);
1137
1138   /* see how our next timestamp relates to the latest qos timestamp */
1139   GST_LOG_OBJECT (vagg, "qostime %" GST_TIME_FORMAT ", earliest %"
1140       GST_TIME_FORMAT, GST_TIME_ARGS (qostime), GST_TIME_ARGS (earliest_time));
1141
1142   jitter = GST_CLOCK_DIFF (qostime, earliest_time);
1143   if (qostime != GST_CLOCK_TIME_NONE && jitter > 0) {
1144     GST_DEBUG_OBJECT (vagg, "we are late, drop frame");
1145     return jitter;
1146   }
1147
1148   GST_LOG_OBJECT (vagg, "process frame");
1149   return jitter;
1150 }
1151
1152 static GstFlowReturn
1153 gst_videoaggregator_aggregate (GstAggregator * agg)
1154 {
1155   GstFlowReturn ret;
1156   GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (agg);
1157   GstClockTime output_start_time, output_end_time;
1158   GstBuffer *outbuf = NULL;
1159   gint res;
1160   gint64 jitter;
1161
1162   /* If we're not negotiated_caps yet... */
1163   if (GST_VIDEO_INFO_FORMAT (&vagg->info) == GST_VIDEO_FORMAT_UNKNOWN) {
1164     GST_INFO_OBJECT (agg, "Not negotiated yet!");
1165     return GST_FLOW_NOT_NEGOTIATED;
1166   }
1167
1168   if (gst_pad_check_reconfigure (agg->srcpad))
1169     gst_videoaggregator_update_src_caps (vagg);
1170
1171   if (vagg->priv->send_caps) {
1172     gst_aggregator_set_src_caps (agg, vagg->priv->current_caps);
1173     vagg->priv->send_caps = FALSE;
1174   }
1175
1176   GST_VIDEO_AGGREGATOR_LOCK (vagg);
1177
1178   if (agg->segment.position == -1)
1179     output_start_time = agg->segment.start;
1180   else
1181     output_start_time = agg->segment.position;
1182
1183   output_end_time =
1184       vagg->priv->ts_offset + gst_util_uint64_scale_round (vagg->priv->nframes +
1185       1, GST_SECOND * GST_VIDEO_INFO_FPS_D (&vagg->info),
1186       GST_VIDEO_INFO_FPS_N (&vagg->info)) + agg->segment.start;
1187
1188   if (agg->segment.stop != -1)
1189     output_end_time = MIN (output_end_time, agg->segment.stop);
1190
1191   res =
1192       gst_videoaggregator_fill_queues (vagg, output_start_time,
1193       output_end_time);
1194
1195   if (res == GST_FLOW_NEEDS_DATA) {
1196     GST_DEBUG_OBJECT (vagg, "Need more data for decisions");
1197     ret = GST_FLOW_OK;
1198     goto done;
1199   } else if (res == GST_FLOW_EOS) {
1200     GST_VIDEO_AGGREGATOR_UNLOCK (vagg);
1201     GST_DEBUG_OBJECT (vagg, "All sinkpads are EOS -- forwarding");
1202     ret = GST_FLOW_EOS;
1203     goto done_unlocked;
1204   } else if (res == GST_FLOW_ERROR) {
1205     GST_WARNING_OBJECT (vagg, "Error collecting buffers");
1206     ret = GST_FLOW_ERROR;
1207     goto done;
1208   }
1209
1210   jitter = gst_videoaggregator_do_qos (vagg, output_start_time);
1211   if (jitter <= 0) {
1212     ret = gst_videoaggregator_do_aggregate (vagg, output_start_time,
1213         output_end_time, &outbuf);
1214     vagg->priv->qos_processed++;
1215   } else {
1216     GstMessage *msg;
1217
1218     vagg->priv->qos_dropped++;
1219
1220     /* TODO: live */
1221     msg =
1222         gst_message_new_qos (GST_OBJECT_CAST (vagg), FALSE,
1223         gst_segment_to_running_time (&agg->segment, GST_FORMAT_TIME,
1224             output_start_time), gst_segment_to_stream_time (&agg->segment,
1225             GST_FORMAT_TIME, output_start_time), output_start_time,
1226         output_end_time - output_start_time);
1227     gst_message_set_qos_values (msg, jitter, vagg->priv->proportion, 1000000);
1228     gst_message_set_qos_stats (msg, GST_FORMAT_BUFFERS,
1229         vagg->priv->qos_processed, vagg->priv->qos_dropped);
1230     gst_element_post_message (GST_ELEMENT_CAST (vagg), msg);
1231
1232     ret = GST_FLOW_OK;
1233   }
1234
1235   agg->segment.position = output_end_time;
1236   vagg->priv->nframes++;
1237
1238   GST_VIDEO_AGGREGATOR_UNLOCK (vagg);
1239   if (outbuf) {
1240     GST_DEBUG_OBJECT (vagg,
1241         "Pushing buffer with ts %" GST_TIME_FORMAT " and duration %"
1242         GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)),
1243         GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)));
1244
1245     ret = gst_aggregator_finish_buffer (agg, outbuf);
1246   }
1247   goto done_unlocked;
1248
1249 done:
1250   GST_VIDEO_AGGREGATOR_UNLOCK (vagg);
1251
1252 done_unlocked:
1253   return ret;
1254 }
1255
1256 /* FIXME, the duration query should reflect how long you will produce
1257  * data, that is the amount of stream time until you will emit EOS.
1258  *
1259  * For synchronized aggregating this is always the max of all the durations
1260  * of upstream since we emit EOS when all of them finished.
1261  *
1262  * We don't do synchronized aggregating so this really depends on where the
1263  * streams where punched in and what their relative offsets are against
1264  * each other which we can get from the first timestamps we see.
1265  *
1266  * When we add a new stream (or remove a stream) the duration might
1267  * also become invalid again and we need to post a new DURATION
1268  * message to notify this fact to the parent.
1269  * For now we take the max of all the upstream elements so the simple
1270  * cases work at least somewhat.
1271  */
1272 static gboolean
1273 gst_videoaggregator_query_duration (GstVideoAggregator * vagg, GstQuery * query)
1274 {
1275   GValue item = { 0 };
1276   gint64 max;
1277   gboolean res;
1278   GstFormat format;
1279   GstIterator *it;
1280   gboolean done;
1281
1282   /* parse format */
1283   gst_query_parse_duration (query, &format, NULL);
1284
1285   max = -1;
1286   res = TRUE;
1287   done = FALSE;
1288
1289   /* Take maximum of all durations */
1290   it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (vagg));
1291   while (!done) {
1292     switch (gst_iterator_next (it, &item)) {
1293       case GST_ITERATOR_DONE:
1294         done = TRUE;
1295         break;
1296       case GST_ITERATOR_OK:
1297       {
1298         GstPad *pad;
1299         gint64 duration;
1300
1301         pad = g_value_get_object (&item);
1302
1303         /* ask sink peer for duration */
1304         res &= gst_pad_peer_query_duration (pad, format, &duration);
1305         /* take max from all valid return values */
1306         if (res) {
1307           /* valid unknown length, stop searching */
1308           if (duration == -1) {
1309             max = duration;
1310             done = TRUE;
1311           }
1312           /* else see if bigger than current max */
1313           else if (duration > max)
1314             max = duration;
1315         }
1316         g_value_reset (&item);
1317         break;
1318       }
1319       case GST_ITERATOR_RESYNC:
1320         max = -1;
1321         res = TRUE;
1322         gst_iterator_resync (it);
1323         break;
1324       default:
1325         res = FALSE;
1326         done = TRUE;
1327         break;
1328     }
1329   }
1330   g_value_unset (&item);
1331   gst_iterator_free (it);
1332
1333   if (res) {
1334     /* and store the max */
1335     GST_DEBUG_OBJECT (vagg, "Total duration in format %s: %"
1336         GST_TIME_FORMAT, gst_format_get_name (format), GST_TIME_ARGS (max));
1337     gst_query_set_duration (query, format, max);
1338   }
1339
1340   return res;
1341 }
1342
1343 static gboolean
1344 gst_videoaggregator_query_latency (GstVideoAggregator * vagg, GstQuery * query)
1345 {
1346   GstClockTime min, max;
1347   gboolean live;
1348   gboolean res;
1349   GstIterator *it;
1350   gboolean done;
1351   GValue item = { 0 };
1352
1353   res = TRUE;
1354   done = FALSE;
1355   live = FALSE;
1356   min = 0;
1357   max = GST_CLOCK_TIME_NONE;
1358
1359   /* Take maximum of all latency values */
1360   it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (vagg));
1361   while (!done) {
1362     switch (gst_iterator_next (it, &item)) {
1363       case GST_ITERATOR_DONE:
1364         done = TRUE;
1365         break;
1366       case GST_ITERATOR_OK:
1367       {
1368         GstPad *pad = g_value_get_object (&item);
1369         GstQuery *peerquery;
1370         GstClockTime min_cur, max_cur;
1371         gboolean live_cur;
1372
1373         peerquery = gst_query_new_latency ();
1374
1375         /* Ask peer for latency */
1376         res &= gst_pad_peer_query (pad, peerquery);
1377
1378         /* take max from all valid return values */
1379         if (res) {
1380           gst_query_parse_latency (peerquery, &live_cur, &min_cur, &max_cur);
1381
1382           if (min_cur > min)
1383             min = min_cur;
1384
1385           if (max_cur != GST_CLOCK_TIME_NONE &&
1386               ((max != GST_CLOCK_TIME_NONE && max_cur > max) ||
1387                   (max == GST_CLOCK_TIME_NONE)))
1388             max = max_cur;
1389
1390           live = live || live_cur;
1391         }
1392
1393         gst_query_unref (peerquery);
1394         g_value_reset (&item);
1395         break;
1396       }
1397       case GST_ITERATOR_RESYNC:
1398         live = FALSE;
1399         min = 0;
1400         max = GST_CLOCK_TIME_NONE;
1401         res = TRUE;
1402         gst_iterator_resync (it);
1403         break;
1404       default:
1405         res = FALSE;
1406         done = TRUE;
1407         break;
1408     }
1409   }
1410   g_value_unset (&item);
1411   gst_iterator_free (it);
1412
1413   if (res) {
1414     /* store the results */
1415     GST_DEBUG_OBJECT (vagg, "Calculated total latency: live %s, min %"
1416         GST_TIME_FORMAT ", max %" GST_TIME_FORMAT,
1417         (live ? "yes" : "no"), GST_TIME_ARGS (min), GST_TIME_ARGS (max));
1418     gst_query_set_latency (query, live, min, max);
1419   }
1420
1421   return res;
1422 }
1423
1424 static gboolean
1425 gst_videoaggregator_src_query (GstAggregator * agg, GstQuery * query)
1426 {
1427   GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (agg);
1428   gboolean res = FALSE;
1429
1430   switch (GST_QUERY_TYPE (query)) {
1431     case GST_QUERY_POSITION:
1432     {
1433       GstFormat format;
1434
1435       gst_query_parse_position (query, &format, NULL);
1436
1437       switch (format) {
1438         case GST_FORMAT_TIME:
1439           gst_query_set_position (query, format,
1440               gst_segment_to_stream_time (&agg->segment, GST_FORMAT_TIME,
1441                   agg->segment.position));
1442           res = TRUE;
1443           break;
1444         default:
1445           break;
1446       }
1447       break;
1448     }
1449     case GST_QUERY_DURATION:
1450       res = gst_videoaggregator_query_duration (vagg, query);
1451       break;
1452     case GST_QUERY_LATENCY:
1453       res = gst_videoaggregator_query_latency (vagg, query);
1454       break;
1455     case GST_QUERY_CAPS:
1456       res =
1457           GST_AGGREGATOR_CLASS (gst_videoaggregator_parent_class)->src_query
1458           (agg, query);
1459       break;
1460     default:
1461       /* FIXME, needs a custom query handler because we have multiple
1462        * sinkpads */
1463       res = FALSE;
1464       break;
1465   }
1466   return res;
1467 }
1468
1469 static gboolean
1470 gst_videoaggregator_src_event (GstAggregator * agg, GstEvent * event)
1471 {
1472   GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (agg);
1473
1474   switch (GST_EVENT_TYPE (event)) {
1475     case GST_EVENT_QOS:
1476     {
1477       GstQOSType type;
1478       GstClockTimeDiff diff;
1479       GstClockTime timestamp;
1480       gdouble proportion;
1481
1482       gst_event_parse_qos (event, &type, &proportion, &diff, &timestamp);
1483       gst_videoaggregator_update_qos (vagg, proportion, diff, timestamp);
1484       break;
1485     }
1486     case GST_EVENT_SEEK:
1487     {
1488       GST_DEBUG_OBJECT (vagg, "Handling SEEK event");
1489     }
1490     default:
1491       break;
1492   }
1493
1494   return
1495       GST_AGGREGATOR_CLASS (gst_videoaggregator_parent_class)->src_event (agg,
1496       event);
1497 }
1498
1499 static GstFlowReturn
1500 gst_videoaggregator_sink_clip (GstAggregator * agg,
1501     GstAggregatorPad * bpad, GstBuffer * buf, GstBuffer ** outbuf)
1502 {
1503   GstVideoAggregatorPad *pad = GST_VIDEO_AGGREGATOR_PAD (bpad);
1504   GstClockTime start_time, end_time;
1505
1506   start_time = GST_BUFFER_TIMESTAMP (buf);
1507   if (start_time == -1) {
1508     GST_DEBUG_OBJECT (pad, "Timestamped buffers required!");
1509     gst_buffer_unref (buf);
1510     return GST_FLOW_ERROR;
1511   }
1512
1513   end_time = GST_BUFFER_DURATION (buf);
1514   if (end_time == -1 && GST_VIDEO_INFO_FPS_N (&pad->info) != 0)
1515     end_time =
1516         gst_util_uint64_scale_int_round (GST_SECOND,
1517         GST_VIDEO_INFO_FPS_D (&pad->info), GST_VIDEO_INFO_FPS_N (&pad->info));
1518   if (end_time == -1) {
1519     *outbuf = buf;
1520     return GST_FLOW_OK;
1521   }
1522
1523   start_time = MAX (start_time, bpad->segment.start);
1524   start_time =
1525       gst_segment_to_running_time (&bpad->segment, GST_FORMAT_TIME, start_time);
1526
1527   end_time += GST_BUFFER_TIMESTAMP (buf);
1528   if (bpad->segment.stop != -1)
1529     end_time = MIN (end_time, bpad->segment.stop);
1530   end_time =
1531       gst_segment_to_running_time (&bpad->segment, GST_FORMAT_TIME, end_time);
1532
1533   /* Convert to the output segment rate */
1534   if (ABS (agg->segment.rate) != 1.0) {
1535     start_time *= ABS (agg->segment.rate);
1536     end_time *= ABS (agg->segment.rate);
1537   }
1538
1539   if (bpad->buffer != NULL && end_time < pad->end_time) {
1540     gst_buffer_unref (buf);
1541     *outbuf = NULL;
1542     return GST_FLOW_OK;
1543   }
1544
1545   *outbuf = buf;
1546   return GST_FLOW_OK;
1547 }
1548
1549 static GstFlowReturn
1550 gst_videoaggregator_flush (GstAggregator * agg)
1551 {
1552   GList *l;
1553   gdouble abs_rate;
1554   GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (agg);
1555
1556   GST_INFO_OBJECT (agg, "Flushing");
1557   abs_rate = ABS (agg->segment.rate);
1558   GST_OBJECT_LOCK (vagg);
1559   for (l = GST_ELEMENT (vagg)->sinkpads; l; l = l->next) {
1560     GstVideoAggregatorPad *p = l->data;
1561
1562     /* Convert to the output segment rate */
1563     if (ABS (agg->segment.rate) != abs_rate) {
1564       if (ABS (agg->segment.rate) != 1.0 && p->buffer) {
1565         p->start_time /= ABS (agg->segment.rate);
1566         p->end_time /= ABS (agg->segment.rate);
1567       }
1568       if (abs_rate != 1.0 && p->buffer) {
1569         p->start_time *= abs_rate;
1570         p->end_time *= abs_rate;
1571       }
1572     }
1573   }
1574   GST_OBJECT_UNLOCK (vagg);
1575
1576   agg->segment.position = -1;
1577   vagg->priv->ts_offset = 0;
1578   vagg->priv->nframes = 0;
1579
1580   gst_videoaggregator_reset_qos (vagg);
1581   return GST_FLOW_OK;
1582 }
1583
1584 static gboolean
1585 gst_videoaggregator_sink_event (GstAggregator * agg, GstAggregatorPad * bpad,
1586     GstEvent * event)
1587 {
1588   GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (agg);
1589   GstVideoAggregatorPad *pad = GST_VIDEO_AGGREGATOR_PAD (bpad);
1590   gboolean ret = TRUE;
1591
1592   GST_DEBUG_OBJECT (pad, "Got %s event on pad %s:%s",
1593       GST_EVENT_TYPE_NAME (event), GST_DEBUG_PAD_NAME (pad));
1594
1595   switch (GST_EVENT_TYPE (event)) {
1596     case GST_EVENT_CAPS:
1597     {
1598       GstCaps *caps;
1599
1600       gst_event_parse_caps (event, &caps);
1601       ret =
1602           gst_videoaggregator_pad_sink_setcaps (GST_PAD (pad),
1603           GST_OBJECT (vagg), caps);
1604       gst_event_unref (event);
1605       event = NULL;
1606       break;
1607     }
1608     case GST_EVENT_SEGMENT:{
1609       GstSegment seg;
1610       gst_event_copy_segment (event, &seg);
1611
1612       g_assert (seg.format == GST_FORMAT_TIME);
1613       break;
1614     }
1615     default:
1616       break;
1617   }
1618
1619   if (event != NULL)
1620     return GST_AGGREGATOR_CLASS (gst_videoaggregator_parent_class)->sink_event
1621         (agg, bpad, event);
1622
1623   return ret;
1624 }
1625
1626 static gboolean
1627 gst_videoaggregator_start (GstAggregator * agg)
1628 {
1629   GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (agg);
1630
1631   if (!GST_AGGREGATOR_CLASS (gst_videoaggregator_parent_class)->start (agg))
1632     return FALSE;
1633
1634   vagg->priv->send_caps = TRUE;
1635   gst_segment_init (&agg->segment, GST_FORMAT_TIME);
1636   gst_caps_replace (&vagg->priv->current_caps, NULL);
1637
1638   return TRUE;
1639 }
1640
1641 static gboolean
1642 gst_videoaggregator_stop (GstAggregator * agg)
1643 {
1644   GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (agg);
1645
1646   if (!GST_AGGREGATOR_CLASS (gst_videoaggregator_parent_class)->stop (agg))
1647     return FALSE;
1648
1649   gst_videoaggregator_reset (vagg);
1650
1651   return TRUE;
1652 }
1653
1654 /* GstElement vmethods */
1655 static GstPad *
1656 gst_videoaggregator_request_new_pad (GstElement * element,
1657     GstPadTemplate * templ, const gchar * req_name, const GstCaps * caps)
1658 {
1659   GstVideoAggregator *vagg;
1660   GstVideoAggregatorPad *vaggpad;
1661
1662   vagg = GST_VIDEO_AGGREGATOR (element);
1663
1664   vaggpad = (GstVideoAggregatorPad *)
1665       GST_ELEMENT_CLASS (gst_videoaggregator_parent_class)->request_new_pad
1666       (element, templ, req_name, caps);
1667
1668   if (vaggpad == NULL)
1669     return NULL;
1670
1671   GST_OBJECT_LOCK (vagg);
1672   vaggpad->zorder = GST_ELEMENT (vagg)->numsinkpads;
1673   vaggpad->start_time = -1;
1674   vaggpad->end_time = -1;
1675   element->sinkpads = g_list_sort (element->sinkpads,
1676       (GCompareFunc) pad_zorder_compare);
1677   GST_OBJECT_UNLOCK (vagg);
1678
1679   gst_child_proxy_child_added (GST_CHILD_PROXY (vagg), G_OBJECT (vaggpad),
1680       GST_OBJECT_NAME (vaggpad));
1681
1682   return GST_PAD (vaggpad);
1683 }
1684
1685 static void
1686 gst_videoaggregator_release_pad (GstElement * element, GstPad * pad)
1687 {
1688   GstVideoAggregator *vagg = NULL;
1689   GstVideoAggregatorPad *vaggpad;
1690   gboolean update_caps;
1691
1692   vagg = GST_VIDEO_AGGREGATOR (element);
1693   vaggpad = GST_VIDEO_AGGREGATOR_PAD (pad);
1694
1695   GST_VIDEO_AGGREGATOR_LOCK (vagg);
1696   gst_videoaggregator_update_converters (vagg);
1697   gst_buffer_replace (&vaggpad->buffer, NULL);
1698   update_caps = GST_VIDEO_INFO_FORMAT (&vagg->info) != GST_VIDEO_FORMAT_UNKNOWN;
1699   GST_VIDEO_AGGREGATOR_UNLOCK (vagg);
1700
1701   gst_child_proxy_child_removed (GST_CHILD_PROXY (vagg), G_OBJECT (vaggpad),
1702       GST_OBJECT_NAME (vaggpad));
1703
1704   GST_ELEMENT_CLASS (gst_videoaggregator_parent_class)->release_pad (GST_ELEMENT
1705       (vagg), pad);
1706
1707   if (update_caps)
1708     gst_videoaggregator_update_src_caps (vagg);
1709
1710   return;
1711 }
1712
1713 static GstFlowReturn
1714 gst_videoaggregator_get_output_buffer (GstVideoAggregator * videoaggregator,
1715     GstBuffer ** outbuf)
1716 {
1717   guint outsize;
1718   static GstAllocationParams params = { 0, 15, 0, 0, };
1719
1720   outsize = GST_VIDEO_INFO_SIZE (&videoaggregator->info);
1721   *outbuf = gst_buffer_new_allocate (NULL, outsize, &params);
1722
1723   if (*outbuf == NULL) {
1724     GST_ERROR_OBJECT (videoaggregator,
1725         "Could not instantiate buffer of size: %d", outsize);
1726   }
1727
1728   return GST_FLOW_OK;
1729 }
1730
1731 static gboolean
1732 gst_videoaggregator_pad_sink_acceptcaps (GstPad * pad,
1733     GstVideoAggregator * vagg, GstCaps * caps)
1734 {
1735   gboolean ret;
1736   GstCaps *modified_caps;
1737   GstCaps *accepted_caps;
1738   GstCaps *template_caps;
1739   gboolean had_current_caps = TRUE;
1740   gint i, n;
1741   GstStructure *s;
1742   GstAggregator *agg = GST_AGGREGATOR (vagg);
1743
1744   GST_DEBUG_OBJECT (pad, "%" GST_PTR_FORMAT, caps);
1745
1746   accepted_caps = gst_pad_get_current_caps (GST_PAD (agg->srcpad));
1747
1748   template_caps = gst_pad_get_pad_template_caps (GST_PAD (agg->srcpad));
1749
1750   if (accepted_caps == NULL) {
1751     accepted_caps = template_caps;
1752     had_current_caps = FALSE;
1753   }
1754
1755   accepted_caps = gst_caps_make_writable (accepted_caps);
1756
1757   GST_LOG_OBJECT (pad, "src caps %" GST_PTR_FORMAT, accepted_caps);
1758
1759   n = gst_caps_get_size (accepted_caps);
1760   for (i = 0; i < n; i++) {
1761     s = gst_caps_get_structure (accepted_caps, i);
1762     gst_structure_set (s, "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
1763         "height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
1764         "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
1765     if (!gst_structure_has_field (s, "pixel-aspect-ratio"))
1766       gst_structure_set (s, "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1,
1767           NULL);
1768
1769     gst_structure_remove_fields (s, "colorimetry", "chroma-site", "format",
1770         NULL);
1771   }
1772
1773   modified_caps = gst_caps_intersect (accepted_caps, template_caps);
1774
1775   ret = gst_caps_can_intersect (caps, accepted_caps);
1776   GST_DEBUG_OBJECT (pad, "%saccepted caps %" GST_PTR_FORMAT,
1777       (ret ? "" : "not "), caps);
1778   gst_caps_unref (accepted_caps);
1779   gst_caps_unref (modified_caps);
1780   if (had_current_caps)
1781     gst_caps_unref (template_caps);
1782   return ret;
1783 }
1784
1785 static gboolean
1786 gst_videoaggregator_sink_query (GstAggregator * agg, GstAggregatorPad * bpad,
1787     GstQuery * query)
1788 {
1789   GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (agg);
1790   GstVideoAggregatorPad *pad = GST_VIDEO_AGGREGATOR_PAD (bpad);
1791   gboolean ret = FALSE;
1792
1793   switch (GST_QUERY_TYPE (query)) {
1794     case GST_QUERY_CAPS:
1795     {
1796       GstCaps *filter, *caps;
1797
1798       gst_query_parse_caps (query, &filter);
1799       caps = gst_videoaggregator_pad_sink_getcaps (GST_PAD (pad), vagg, filter);
1800       gst_query_set_caps_result (query, caps);
1801       gst_caps_unref (caps);
1802       ret = TRUE;
1803       break;
1804     }
1805     case GST_QUERY_ACCEPT_CAPS:
1806     {
1807       GstCaps *caps;
1808
1809       gst_query_parse_accept_caps (query, &caps);
1810       ret = gst_videoaggregator_pad_sink_acceptcaps (GST_PAD (pad), vagg, caps);
1811       gst_query_set_accept_caps_result (query, ret);
1812       ret = TRUE;
1813       break;
1814     }
1815     default:
1816       ret =
1817           GST_AGGREGATOR_CLASS (gst_videoaggregator_parent_class)->sink_query
1818           (agg, bpad, query);
1819       break;
1820   }
1821   return ret;
1822 }
1823
1824 /* GObject vmethods */
1825 static void
1826 gst_videoaggregator_finalize (GObject * o)
1827 {
1828   GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (o);
1829
1830   g_mutex_clear (&vagg->priv->lock);
1831   g_mutex_clear (&vagg->priv->setcaps_lock);
1832
1833   G_OBJECT_CLASS (gst_videoaggregator_parent_class)->finalize (o);
1834 }
1835
1836 static void
1837 gst_videoaggregator_dispose (GObject * o)
1838 {
1839   GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (o);
1840
1841   gst_caps_replace (&vagg->priv->current_caps, NULL);
1842 }
1843
1844 static void
1845 gst_videoaggregator_get_property (GObject * object,
1846     guint prop_id, GValue * value, GParamSpec * pspec)
1847 {
1848   switch (prop_id) {
1849     default:
1850       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1851       break;
1852   }
1853 }
1854
1855 static void
1856 gst_videoaggregator_set_property (GObject * object,
1857     guint prop_id, const GValue * value, GParamSpec * pspec)
1858 {
1859   switch (prop_id) {
1860     default:
1861       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1862       break;
1863   }
1864 }
1865
1866 /* GObject boilerplate */
1867 static void
1868 gst_videoaggregator_class_init (GstVideoAggregatorClass * klass)
1869 {
1870   GObjectClass *gobject_class = (GObjectClass *) klass;
1871   GstElementClass *gstelement_class = (GstElementClass *) klass;
1872   GstAggregatorClass *agg_class = (GstAggregatorClass *) klass;
1873
1874   GST_DEBUG_CATEGORY_INIT (gst_videoaggregator_debug, "videoaggregator", 0,
1875       "base video aggregator");
1876
1877   g_type_class_add_private (klass, sizeof (GstVideoAggregatorPrivate));
1878
1879   gobject_class->finalize = gst_videoaggregator_finalize;
1880   gobject_class->dispose = gst_videoaggregator_dispose;
1881
1882   gobject_class->get_property = gst_videoaggregator_get_property;
1883   gobject_class->set_property = gst_videoaggregator_set_property;
1884
1885   gstelement_class->request_new_pad =
1886       GST_DEBUG_FUNCPTR (gst_videoaggregator_request_new_pad);
1887   gstelement_class->release_pad =
1888       GST_DEBUG_FUNCPTR (gst_videoaggregator_release_pad);
1889
1890   gst_element_class_set_static_metadata (gstelement_class,
1891       "Video aggregator base class", "Filter/Editor/Video",
1892       "Aggregate multiple video streams",
1893       "Wim Taymans <wim@fluendo.com>, "
1894       "Sebastian Dröge <sebastian.droege@collabora.co.uk>, "
1895       "Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>, "
1896       "Thibault Saunier <tsaunier@gnome.org>");
1897
1898   agg_class->sinkpads_type = GST_TYPE_VIDEO_AGGREGATOR_PAD;
1899   agg_class->start = gst_videoaggregator_start;
1900   agg_class->stop = gst_videoaggregator_stop;
1901   agg_class->sink_query = gst_videoaggregator_sink_query;
1902   agg_class->sink_event = gst_videoaggregator_sink_event;
1903   agg_class->flush = gst_videoaggregator_flush;
1904   agg_class->clip = gst_videoaggregator_sink_clip;
1905   agg_class->aggregate = gst_videoaggregator_aggregate;
1906   agg_class->src_event = gst_videoaggregator_src_event;
1907   agg_class->src_query = gst_videoaggregator_src_query;
1908
1909   klass->get_output_buffer = gst_videoaggregator_get_output_buffer;
1910
1911   /* Register the pad class */
1912   g_type_class_ref (GST_TYPE_VIDEO_AGGREGATOR_PAD);
1913 }
1914
1915 static void
1916 gst_videoaggregator_init (GstVideoAggregator * vagg)
1917 {
1918   vagg->priv =
1919       G_TYPE_INSTANCE_GET_PRIVATE (vagg, GST_TYPE_VIDEO_AGGREGATOR,
1920       GstVideoAggregatorPrivate);
1921
1922   vagg->priv->current_caps = NULL;
1923
1924   g_mutex_init (&vagg->priv->lock);
1925   g_mutex_init (&vagg->priv->setcaps_lock);
1926   /* initialize variables */
1927   gst_videoaggregator_reset (vagg);
1928 }