videoaggregator: Remove custom get_next_time implementation
[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  * @title: GstVideoAggregator
24  * @short_description: Base class for video aggregators
25  *
26  * VideoAggregator can accept AYUV, ARGB and BGRA video streams. For each of the requested
27  * sink pads it will compare the incoming geometry and framerate to define the
28  * output parameters. Indeed output video frames will have the geometry of the
29  * biggest incoming video stream and the framerate of the fastest incoming one.
30  *
31  * VideoAggregator will do colorspace conversion.
32  *
33  * Zorder for each input stream can be configured on the
34  * #GstVideoAggregatorPad.
35  *
36  */
37
38 #ifdef HAVE_CONFIG_H
39 #include "config.h"
40 #endif
41
42 #include <string.h>
43
44 #include "gstvideoaggregator.h"
45
46 GST_DEBUG_CATEGORY_STATIC (gst_video_aggregator_debug);
47 #define GST_CAT_DEFAULT gst_video_aggregator_debug
48
49 /* Needed prototypes */
50 static void gst_video_aggregator_reset_qos (GstVideoAggregator * vagg);
51
52 /****************************************
53  * GstVideoAggregatorPad implementation *
54  ****************************************/
55
56 #define DEFAULT_PAD_ZORDER 0
57 #define DEFAULT_PAD_REPEAT_AFTER_EOS FALSE
58 enum
59 {
60   PROP_PAD_0,
61   PROP_PAD_ZORDER,
62   PROP_PAD_REPEAT_AFTER_EOS,
63 };
64
65
66 struct _GstVideoAggregatorPadPrivate
67 {
68   GstBuffer *buffer;
69   GstVideoFrame prepared_frame;
70
71   /* properties */
72   guint zorder;
73   gboolean repeat_after_eos;
74
75   /* Subclasses can force an alpha channel in the (input thus output)
76    * colorspace format */
77   gboolean needs_alpha;
78
79   GstClockTime start_time;
80   GstClockTime end_time;
81
82   GstVideoInfo pending_vinfo;
83 };
84
85
86 G_DEFINE_TYPE (GstVideoAggregatorPad, gst_video_aggregator_pad,
87     GST_TYPE_AGGREGATOR_PAD);
88
89 static void
90 gst_video_aggregator_pad_get_property (GObject * object, guint prop_id,
91     GValue * value, GParamSpec * pspec)
92 {
93   GstVideoAggregatorPad *pad = GST_VIDEO_AGGREGATOR_PAD (object);
94
95   switch (prop_id) {
96     case PROP_PAD_ZORDER:
97       g_value_set_uint (value, pad->priv->zorder);
98       break;
99     case PROP_PAD_REPEAT_AFTER_EOS:
100       g_value_set_boolean (value, pad->priv->repeat_after_eos);
101       break;
102     default:
103       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
104       break;
105   }
106 }
107
108 static int
109 pad_zorder_compare (const GstVideoAggregatorPad * pad1,
110     const GstVideoAggregatorPad * pad2)
111 {
112   return pad1->priv->zorder - pad2->priv->zorder;
113 }
114
115 static void
116 gst_video_aggregator_pad_set_property (GObject * object, guint prop_id,
117     const GValue * value, GParamSpec * pspec)
118 {
119   GstVideoAggregatorPad *pad = GST_VIDEO_AGGREGATOR_PAD (object);
120   GstVideoAggregator *vagg =
121       GST_VIDEO_AGGREGATOR (gst_pad_get_parent (GST_PAD (pad)));
122
123   switch (prop_id) {
124     case PROP_PAD_ZORDER:
125       GST_OBJECT_LOCK (vagg);
126       pad->priv->zorder = g_value_get_uint (value);
127       GST_ELEMENT (vagg)->sinkpads = g_list_sort (GST_ELEMENT (vagg)->sinkpads,
128           (GCompareFunc) pad_zorder_compare);
129       GST_OBJECT_UNLOCK (vagg);
130       break;
131     case PROP_PAD_REPEAT_AFTER_EOS:
132       pad->priv->repeat_after_eos = g_value_get_boolean (value);
133       break;
134     default:
135       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
136       break;
137   }
138
139   gst_object_unref (vagg);
140 }
141
142 static GstFlowReturn
143 _flush_pad (GstAggregatorPad * aggpad, GstAggregator * aggregator)
144 {
145   GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (aggregator);
146   GstVideoAggregatorPad *pad = GST_VIDEO_AGGREGATOR_PAD (aggpad);
147
148   gst_video_aggregator_reset_qos (vagg);
149   gst_buffer_replace (&pad->priv->buffer, NULL);
150   pad->priv->start_time = -1;
151   pad->priv->end_time = -1;
152
153   return GST_FLOW_OK;
154 }
155
156 static gboolean
157 gst_video_aggregator_pad_skip_buffer (GstAggregatorPad * aggpad,
158     GstAggregator * agg, GstBuffer * buffer)
159 {
160   gboolean ret = FALSE;
161   GstSegment *agg_segment = &GST_AGGREGATOR_PAD (agg->srcpad)->segment;
162
163   if (agg_segment->position != GST_CLOCK_TIME_NONE
164       && GST_BUFFER_DURATION (buffer) != GST_CLOCK_TIME_NONE) {
165     GstClockTime start_time =
166         gst_segment_to_running_time (agg_segment, GST_FORMAT_TIME,
167         GST_BUFFER_PTS (buffer));
168     GstClockTime end_time = start_time + GST_BUFFER_DURATION (buffer);
169     GstClockTime output_start_running_time =
170         gst_segment_to_running_time (agg_segment, GST_FORMAT_TIME,
171         agg_segment->position);
172
173     ret = end_time < output_start_running_time;
174   }
175
176   return ret;
177 }
178
179 static gboolean
180 gst_video_aggregator_pad_prepare_frame (GstVideoAggregatorPad * pad,
181     GstVideoAggregator * vagg, GstBuffer * buffer,
182     GstVideoFrame * prepared_frame)
183 {
184   if (!gst_video_frame_map (prepared_frame, &pad->info, buffer, GST_MAP_READ)) {
185     GST_WARNING_OBJECT (vagg, "Could not map input buffer");
186     return FALSE;
187   }
188
189   return TRUE;
190 }
191
192 static void
193 gst_video_aggregator_pad_clean_frame (GstVideoAggregatorPad * pad,
194     GstVideoAggregator * vagg, GstVideoFrame * prepared_frame)
195 {
196   if (prepared_frame->buffer) {
197     gst_video_frame_unmap (prepared_frame);
198     memset (prepared_frame, 0, sizeof (GstVideoFrame));
199   }
200 }
201
202 static void
203 gst_video_aggregator_pad_class_init (GstVideoAggregatorPadClass * klass)
204 {
205   GObjectClass *gobject_class = (GObjectClass *) klass;
206   GstAggregatorPadClass *aggpadclass = (GstAggregatorPadClass *) klass;
207
208   gobject_class->set_property = gst_video_aggregator_pad_set_property;
209   gobject_class->get_property = gst_video_aggregator_pad_get_property;
210
211   g_object_class_install_property (gobject_class, PROP_PAD_ZORDER,
212       g_param_spec_uint ("zorder", "Z-Order", "Z Order of the picture",
213           0, G_MAXUINT, DEFAULT_PAD_ZORDER,
214           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
215   g_object_class_install_property (gobject_class, PROP_PAD_REPEAT_AFTER_EOS,
216       g_param_spec_boolean ("repeat-after-eos", "Repeat After EOS",
217           "Repeat the " "last frame after EOS until all pads are EOS",
218           DEFAULT_PAD_REPEAT_AFTER_EOS,
219           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
220
221   g_type_class_add_private (klass, sizeof (GstVideoAggregatorPadPrivate));
222
223   aggpadclass->flush = GST_DEBUG_FUNCPTR (_flush_pad);
224   aggpadclass->skip_buffer =
225       GST_DEBUG_FUNCPTR (gst_video_aggregator_pad_skip_buffer);
226   klass->prepare_frame =
227       GST_DEBUG_FUNCPTR (gst_video_aggregator_pad_prepare_frame);
228   klass->clean_frame = GST_DEBUG_FUNCPTR (gst_video_aggregator_pad_clean_frame);
229 }
230
231 static void
232 gst_video_aggregator_pad_init (GstVideoAggregatorPad * vaggpad)
233 {
234   vaggpad->priv =
235       G_TYPE_INSTANCE_GET_PRIVATE (vaggpad, GST_TYPE_VIDEO_AGGREGATOR_PAD,
236       GstVideoAggregatorPadPrivate);
237
238   vaggpad->priv->zorder = DEFAULT_PAD_ZORDER;
239   vaggpad->priv->repeat_after_eos = DEFAULT_PAD_REPEAT_AFTER_EOS;
240   memset (&vaggpad->priv->prepared_frame, 0, sizeof (GstVideoFrame));
241 }
242
243 /**
244  * gst_video_aggregator_pad_has_current_buffer:
245  * @pad: a #GstVideoAggregatorPad
246  *
247  * Checks if the pad currently has a buffer queued that is going to be used
248  * for the current output frame.
249  *
250  * This must only be called from the aggregate_frames() virtual method,
251  * or from the prepare_frame() virtual method of the aggregator pads.
252  *
253  * Returns: %TRUE if the pad has currently a buffer queued
254  */
255 gboolean
256 gst_video_aggregator_pad_has_current_buffer (GstVideoAggregatorPad * pad)
257 {
258   g_return_val_if_fail (GST_IS_VIDEO_AGGREGATOR_PAD (pad), FALSE);
259
260   return pad->priv->buffer != NULL;
261 }
262
263 /**
264  * gst_video_aggregator_pad_get_current_buffer:
265  * @pad: a #GstVideoAggregatorPad
266  *
267  * Returns the currently queued buffer that is going to be used
268  * for the current output frame.
269  *
270  * This must only be called from the aggregate_frames() virtual method,
271  * or from the prepare_frame() virtual method of the aggregator pads.
272  *
273  * The return value is only valid until aggregate_frames() or prepare_frames()
274  * returns.
275  *
276  * Returns: (transfer none): The currently queued buffer
277  */
278 GstBuffer *
279 gst_video_aggregator_pad_get_current_buffer (GstVideoAggregatorPad * pad)
280 {
281   g_return_val_if_fail (GST_IS_VIDEO_AGGREGATOR_PAD (pad), NULL);
282
283   return pad->priv->buffer;
284 }
285
286 /**
287  * gst_video_aggregator_pad_get_prepared_frame:
288  * @pad: a #GstVideoAggregatorPad
289  *
290  * Returns the currently prepared video frame that has to be aggregated into
291  * the current output frame.
292  *
293  * This must only be called from the aggregate_frames() virtual method,
294  * or from the prepare_frame() virtual method of the aggregator pads.
295  *
296  * The return value is only valid until aggregate_frames() or prepare_frames()
297  * returns.
298  *
299  * Returns: (transfer none): The currently prepared video frame
300  */
301 GstVideoFrame *
302 gst_video_aggregator_pad_get_prepared_frame (GstVideoAggregatorPad * pad)
303 {
304   g_return_val_if_fail (GST_IS_VIDEO_AGGREGATOR_PAD (pad), NULL);
305
306   return pad->priv->prepared_frame.buffer ? &pad->priv->prepared_frame : NULL;
307 }
308
309 /**
310  * gst_video_aggregator_pad_set_needs_alpha:
311  * @pad: a #GstVideoAggregatorPad
312  * @needs_alpha: %TRUE if this pad requires alpha output
313  *
314  * Allows selecting that this pad requires an output format with alpha
315  *
316  */
317 void
318 gst_video_aggregator_pad_set_needs_alpha (GstVideoAggregatorPad * pad,
319     gboolean needs_alpha)
320 {
321   g_return_if_fail (GST_IS_VIDEO_AGGREGATOR_PAD (pad));
322
323   if (needs_alpha != pad->priv->needs_alpha) {
324     GstAggregator *agg =
325         GST_AGGREGATOR (gst_object_get_parent (GST_OBJECT (pad)));
326     pad->priv->needs_alpha = needs_alpha;
327     if (agg) {
328       gst_pad_mark_reconfigure (GST_AGGREGATOR_SRC_PAD (agg));
329       gst_object_unref (agg);
330     }
331   }
332 }
333
334 /****************************************
335  * GstVideoAggregatorConvertPad implementation *
336  ****************************************/
337
338 enum
339 {
340   PROP_CONVERT_PAD_0,
341   PROP_CONVERT_PAD_CONVERTER_CONFIG,
342 };
343
344 struct _GstVideoAggregatorConvertPadPrivate
345 {
346   /* Converter, if NULL no conversion is done */
347   GstVideoConverter *convert;
348
349   /* caps used for conversion if needed */
350   GstVideoInfo conversion_info;
351   GstBuffer *converted_buffer;
352
353   GstStructure *converter_config;
354   gboolean converter_config_changed;
355 };
356
357 G_DEFINE_TYPE (GstVideoAggregatorConvertPad, gst_video_aggregator_convert_pad,
358     GST_TYPE_VIDEO_AGGREGATOR_PAD);
359
360 static void
361 gst_video_aggregator_convert_pad_finalize (GObject * o)
362 {
363   GstVideoAggregatorConvertPad *vaggpad = GST_VIDEO_AGGREGATOR_CONVERT_PAD (o);
364
365   if (vaggpad->priv->convert)
366     gst_video_converter_free (vaggpad->priv->convert);
367   vaggpad->priv->convert = NULL;
368
369   if (vaggpad->priv->converter_config)
370     gst_structure_free (vaggpad->priv->converter_config);
371   vaggpad->priv->converter_config = NULL;
372
373   G_OBJECT_CLASS (gst_video_aggregator_pad_parent_class)->finalize (o);
374 }
375
376 static void
377     gst_video_aggregator_convert_pad_update_conversion_info_internal
378     (GstVideoAggregatorPad * vpad)
379 {
380   GstVideoAggregatorConvertPad *pad = GST_VIDEO_AGGREGATOR_CONVERT_PAD (vpad);
381
382   pad->priv->converter_config_changed = TRUE;
383 }
384
385 static gboolean
386 gst_video_aggregator_convert_pad_prepare_frame (GstVideoAggregatorPad * vpad,
387     GstVideoAggregator * vagg, GstBuffer * buffer,
388     GstVideoFrame * prepared_frame)
389 {
390   GstVideoAggregatorConvertPad *pad = GST_VIDEO_AGGREGATOR_CONVERT_PAD (vpad);
391   GstVideoFrame frame;
392
393   /* Update/create converter as needed */
394   if (pad->priv->converter_config_changed) {
395     GstVideoAggregatorConvertPadClass *klass =
396         GST_VIDEO_AGGREGATOR_CONVERT_PAD_GET_CLASS (pad);
397     GstVideoInfo conversion_info;
398
399     gst_video_info_init (&conversion_info);
400     klass->create_conversion_info (pad, vagg, &conversion_info);
401     if (conversion_info.finfo == NULL)
402       return FALSE;
403     pad->priv->converter_config_changed = FALSE;
404
405     if (!pad->priv->conversion_info.finfo
406         || !gst_video_info_is_equal (&conversion_info,
407             &pad->priv->conversion_info)) {
408       pad->priv->conversion_info = conversion_info;
409
410       if (pad->priv->convert)
411         gst_video_converter_free (pad->priv->convert);
412       pad->priv->convert = NULL;
413
414       if (!gst_video_info_is_equal (&vpad->info, &pad->priv->conversion_info)) {
415         pad->priv->convert =
416             gst_video_converter_new (&vpad->info, &pad->priv->conversion_info,
417             pad->priv->converter_config ? gst_structure_copy (pad->
418                 priv->converter_config) : NULL);
419         if (!pad->priv->convert) {
420           GST_WARNING_OBJECT (pad, "No path found for conversion");
421           return FALSE;
422         }
423
424         GST_DEBUG_OBJECT (pad, "This pad will be converted from %d to %d",
425             GST_VIDEO_INFO_FORMAT (&vpad->info),
426             GST_VIDEO_INFO_FORMAT (&pad->priv->conversion_info));
427       } else {
428         GST_DEBUG_OBJECT (pad, "This pad will not need conversion");
429       }
430     }
431   }
432
433   if (!gst_video_frame_map (&frame, &vpad->info, buffer, GST_MAP_READ)) {
434     GST_WARNING_OBJECT (vagg, "Could not map input buffer");
435     return FALSE;
436   }
437
438   if (pad->priv->convert) {
439     GstVideoFrame converted_frame;
440     GstBuffer *converted_buf = NULL;
441     static GstAllocationParams params = { 0, 15, 0, 0, };
442     gint converted_size;
443     guint outsize;
444
445     /* We wait until here to set the conversion infos, in case vagg->info changed */
446     converted_size = pad->priv->conversion_info.size;
447     outsize = GST_VIDEO_INFO_SIZE (&vagg->info);
448     converted_size = converted_size > outsize ? converted_size : outsize;
449     converted_buf = gst_buffer_new_allocate (NULL, converted_size, &params);
450
451     if (!gst_video_frame_map (&converted_frame, &(pad->priv->conversion_info),
452             converted_buf, GST_MAP_READWRITE)) {
453       GST_WARNING_OBJECT (vagg, "Could not map converted frame");
454
455       gst_video_frame_unmap (&frame);
456       return FALSE;
457     }
458
459     gst_video_converter_frame (pad->priv->convert, &frame, &converted_frame);
460     pad->priv->converted_buffer = converted_buf;
461     gst_video_frame_unmap (&frame);
462     *prepared_frame = converted_frame;
463   } else {
464     *prepared_frame = frame;
465   }
466
467   return TRUE;
468 }
469
470 static void
471 gst_video_aggregator_convert_pad_clean_frame (GstVideoAggregatorPad * vpad,
472     GstVideoAggregator * vagg, GstVideoFrame * prepared_frame)
473 {
474   GstVideoAggregatorConvertPad *pad = GST_VIDEO_AGGREGATOR_CONVERT_PAD (vpad);
475
476   if (prepared_frame->buffer) {
477     gst_video_frame_unmap (prepared_frame);
478     memset (prepared_frame, 0, sizeof (GstVideoFrame));
479   }
480
481   if (pad->priv->converted_buffer) {
482     gst_buffer_unref (pad->priv->converted_buffer);
483     pad->priv->converted_buffer = NULL;
484   }
485 }
486
487 static void
488     gst_video_aggregator_convert_pad_create_conversion_info
489     (GstVideoAggregatorConvertPad * pad, GstVideoAggregator * agg,
490     GstVideoInfo * convert_info)
491 {
492   GstVideoAggregatorPad *vpad = GST_VIDEO_AGGREGATOR_PAD (pad);
493   gchar *colorimetry, *best_colorimetry;
494   const gchar *chroma, *best_chroma;
495
496   g_return_if_fail (GST_IS_VIDEO_AGGREGATOR_CONVERT_PAD (pad));
497   g_return_if_fail (convert_info != NULL);
498
499   if (!vpad->info.finfo
500       || GST_VIDEO_INFO_FORMAT (&vpad->info) == GST_VIDEO_FORMAT_UNKNOWN) {
501     return;
502   }
503
504   if (!agg->info.finfo
505       || GST_VIDEO_INFO_FORMAT (&agg->info) == GST_VIDEO_FORMAT_UNKNOWN) {
506     return;
507   }
508
509   colorimetry = gst_video_colorimetry_to_string (&vpad->info.colorimetry);
510   chroma = gst_video_chroma_to_string (vpad->info.chroma_site);
511
512   best_colorimetry = gst_video_colorimetry_to_string (&agg->info.colorimetry);
513   best_chroma = gst_video_chroma_to_string (agg->info.chroma_site);
514
515   if (GST_VIDEO_INFO_FORMAT (&agg->info) != GST_VIDEO_INFO_FORMAT (&vpad->info)
516       || g_strcmp0 (colorimetry, best_colorimetry)
517       || g_strcmp0 (chroma, best_chroma)) {
518     GstVideoInfo tmp_info;
519
520     /* Initialize with the wanted video format and our original width and
521      * height as we don't want to rescale. Then copy over the wanted
522      * colorimetry, and chroma-site and our current pixel-aspect-ratio
523      * and other relevant fields.
524      */
525     gst_video_info_set_format (&tmp_info, GST_VIDEO_INFO_FORMAT (&agg->info),
526         vpad->info.width, vpad->info.height);
527     tmp_info.chroma_site = agg->info.chroma_site;
528     tmp_info.colorimetry = agg->info.colorimetry;
529     tmp_info.par_n = vpad->info.par_n;
530     tmp_info.par_d = vpad->info.par_d;
531     tmp_info.fps_n = vpad->info.fps_n;
532     tmp_info.fps_d = vpad->info.fps_d;
533     tmp_info.flags = vpad->info.flags;
534     tmp_info.interlace_mode = vpad->info.interlace_mode;
535
536     *convert_info = tmp_info;
537   } else {
538     *convert_info = vpad->info;
539   }
540
541   g_free (colorimetry);
542   g_free (best_colorimetry);
543 }
544
545 static void
546 gst_video_aggregator_convert_pad_get_property (GObject * object, guint prop_id,
547     GValue * value, GParamSpec * pspec)
548 {
549   GstVideoAggregatorConvertPad *pad = GST_VIDEO_AGGREGATOR_CONVERT_PAD (object);
550
551   switch (prop_id) {
552     case PROP_CONVERT_PAD_CONVERTER_CONFIG:
553       GST_OBJECT_LOCK (pad);
554       if (pad->priv->converter_config)
555         g_value_set_boxed (value, pad->priv->converter_config);
556       GST_OBJECT_UNLOCK (pad);
557       break;
558     default:
559       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
560       break;
561   }
562 }
563
564 static void
565 gst_video_aggregator_convert_pad_set_property (GObject * object, guint prop_id,
566     const GValue * value, GParamSpec * pspec)
567 {
568   GstVideoAggregatorConvertPad *pad = GST_VIDEO_AGGREGATOR_CONVERT_PAD (object);
569
570   switch (prop_id) {
571     case PROP_CONVERT_PAD_CONVERTER_CONFIG:
572       GST_OBJECT_LOCK (pad);
573       if (pad->priv->converter_config)
574         gst_structure_free (pad->priv->converter_config);
575       pad->priv->converter_config = g_value_dup_boxed (value);
576       pad->priv->converter_config_changed = TRUE;
577       GST_OBJECT_UNLOCK (pad);
578       break;
579     default:
580       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
581       break;
582   }
583 }
584
585 static void
586 gst_video_aggregator_convert_pad_class_init (GstVideoAggregatorConvertPadClass *
587     klass)
588 {
589   GObjectClass *gobject_class = (GObjectClass *) klass;
590   GstVideoAggregatorPadClass *vaggpadclass =
591       (GstVideoAggregatorPadClass *) klass;
592
593   gobject_class->finalize = gst_video_aggregator_convert_pad_finalize;
594   gobject_class->get_property =
595       GST_DEBUG_FUNCPTR (gst_video_aggregator_convert_pad_get_property);
596   gobject_class->set_property =
597       GST_DEBUG_FUNCPTR (gst_video_aggregator_convert_pad_set_property);
598
599   g_type_class_add_private (klass,
600       sizeof (GstVideoAggregatorConvertPadPrivate));
601
602   g_object_class_install_property (gobject_class,
603       PROP_CONVERT_PAD_CONVERTER_CONFIG, g_param_spec_boxed ("converter-config",
604           "Converter configuration",
605           "A GstStructure describing the configuration that should be used "
606           "when scaling and converting this pad's video frames",
607           GST_TYPE_STRUCTURE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
608
609   vaggpadclass->update_conversion_info =
610       GST_DEBUG_FUNCPTR
611       (gst_video_aggregator_convert_pad_update_conversion_info_internal);
612   vaggpadclass->prepare_frame =
613       GST_DEBUG_FUNCPTR (gst_video_aggregator_convert_pad_prepare_frame);
614   vaggpadclass->clean_frame =
615       GST_DEBUG_FUNCPTR (gst_video_aggregator_convert_pad_clean_frame);
616
617   klass->create_conversion_info =
618       gst_video_aggregator_convert_pad_create_conversion_info;
619 }
620
621 static void
622 gst_video_aggregator_convert_pad_init (GstVideoAggregatorConvertPad * vaggpad)
623 {
624   vaggpad->priv =
625       G_TYPE_INSTANCE_GET_PRIVATE (vaggpad,
626       GST_TYPE_VIDEO_AGGREGATOR_CONVERT_PAD,
627       GstVideoAggregatorConvertPadPrivate);
628
629   vaggpad->priv->converted_buffer = NULL;
630   vaggpad->priv->convert = NULL;
631   vaggpad->priv->converter_config = NULL;
632   vaggpad->priv->converter_config_changed = FALSE;
633 }
634
635
636 /**
637  * gst_video_aggregator_convert_pad_update_conversion_info:
638  * @pad: a #GstVideoAggregatorPad
639  *
640  * Requests the pad to check and update the converter before the next usage to
641  * update for any changes that have happened.
642  *
643  */
644 void gst_video_aggregator_convert_pad_update_conversion_info
645     (GstVideoAggregatorConvertPad * pad)
646 {
647   g_return_if_fail (GST_IS_VIDEO_AGGREGATOR_CONVERT_PAD (pad));
648
649   pad->priv->converter_config_changed = TRUE;
650 }
651
652 /**************************************
653  * GstVideoAggregator implementation  *
654  **************************************/
655
656 #define GST_VIDEO_AGGREGATOR_GET_LOCK(vagg) (&GST_VIDEO_AGGREGATOR(vagg)->priv->lock)
657
658 #define GST_VIDEO_AGGREGATOR_LOCK(vagg)   G_STMT_START {       \
659   GST_LOG_OBJECT (vagg, "Taking EVENT lock from thread %p",    \
660         g_thread_self());                                      \
661   g_mutex_lock(GST_VIDEO_AGGREGATOR_GET_LOCK(vagg));           \
662   GST_LOG_OBJECT (vagg, "Took EVENT lock from thread %p",      \
663         g_thread_self());                                      \
664   } G_STMT_END
665
666 #define GST_VIDEO_AGGREGATOR_UNLOCK(vagg)   G_STMT_START {     \
667   GST_LOG_OBJECT (vagg, "Releasing EVENT lock from thread %p", \
668         g_thread_self());                                      \
669   g_mutex_unlock(GST_VIDEO_AGGREGATOR_GET_LOCK(vagg));         \
670   GST_LOG_OBJECT (vagg, "Took EVENT lock from thread %p",      \
671         g_thread_self());                                      \
672   } G_STMT_END
673
674
675 struct _GstVideoAggregatorPrivate
676 {
677   /* Lock to prevent the state to change while aggregating */
678   GMutex lock;
679
680   /* Current downstream segment */
681   GstClockTime ts_offset;
682   guint64 nframes;
683
684   /* QoS stuff */
685   gdouble proportion;
686   GstClockTime earliest_time;
687   guint64 qos_processed, qos_dropped;
688
689   /* current caps */
690   GstCaps *current_caps;
691
692   gboolean live;
693 };
694
695 /* Can't use the G_DEFINE_TYPE macros because we need the
696  * videoaggregator class in the _init to be able to set
697  * the sink pad non-alpha caps. Using the G_DEFINE_TYPE there
698  * seems to be no way of getting the real class being initialized */
699 static void gst_video_aggregator_init (GstVideoAggregator * self,
700     GstVideoAggregatorClass * klass);
701 static void gst_video_aggregator_class_init (GstVideoAggregatorClass * klass);
702 static gpointer gst_video_aggregator_parent_class = NULL;
703
704 GType
705 gst_video_aggregator_get_type (void)
706 {
707   static volatile gsize g_define_type_id_volatile = 0;
708
709   if (g_once_init_enter (&g_define_type_id_volatile)) {
710     GType g_define_type_id = g_type_register_static_simple (GST_TYPE_AGGREGATOR,
711         g_intern_static_string ("GstVideoAggregator"),
712         sizeof (GstVideoAggregatorClass),
713         (GClassInitFunc) gst_video_aggregator_class_init,
714         sizeof (GstVideoAggregator),
715         (GInstanceInitFunc) gst_video_aggregator_init,
716         (GTypeFlags) G_TYPE_FLAG_ABSTRACT);
717     g_once_init_leave (&g_define_type_id_volatile, g_define_type_id);
718   }
719   return g_define_type_id_volatile;
720 }
721
722 static void
723 gst_video_aggregator_find_best_format (GstVideoAggregator * vagg,
724     GstCaps * downstream_caps, GstVideoInfo * best_info,
725     gboolean * at_least_one_alpha)
726 {
727   GList *tmp;
728   GstCaps *possible_caps;
729   GstVideoAggregatorPad *pad;
730   gboolean need_alpha = FALSE;
731   gint best_format_number = 0;
732   GHashTable *formats_table = g_hash_table_new (g_direct_hash, g_direct_equal);
733
734   GST_OBJECT_LOCK (vagg);
735   for (tmp = GST_ELEMENT (vagg)->sinkpads; tmp; tmp = tmp->next) {
736     GstStructure *s;
737     gint format_number;
738
739     pad = tmp->data;
740
741     if (!pad->info.finfo)
742       continue;
743
744     if (pad->info.finfo->flags & GST_VIDEO_FORMAT_FLAG_ALPHA)
745       *at_least_one_alpha = TRUE;
746
747     /* If we want alpha, disregard all the other formats */
748     if (need_alpha && !(pad->info.finfo->flags & GST_VIDEO_FORMAT_FLAG_ALPHA))
749       continue;
750
751     /* This can happen if we release a pad and another pad hasn't been negotiated_caps yet */
752     if (GST_VIDEO_INFO_FORMAT (&pad->info) == GST_VIDEO_FORMAT_UNKNOWN)
753       continue;
754
755     possible_caps = gst_video_info_to_caps (&pad->info);
756
757     s = gst_caps_get_structure (possible_caps, 0);
758     gst_structure_remove_fields (s, "width", "height", "framerate",
759         "pixel-aspect-ratio", "interlace-mode", NULL);
760
761     /* Can downstream accept this format ? */
762     if (!gst_caps_can_intersect (downstream_caps, possible_caps)) {
763       gst_caps_unref (possible_caps);
764       continue;
765     }
766
767     gst_caps_unref (possible_caps);
768
769     format_number =
770         GPOINTER_TO_INT (g_hash_table_lookup (formats_table,
771             GINT_TO_POINTER (GST_VIDEO_INFO_FORMAT (&pad->info))));
772     format_number += pad->info.width * pad->info.height;
773
774     g_hash_table_replace (formats_table,
775         GINT_TO_POINTER (GST_VIDEO_INFO_FORMAT (&pad->info)),
776         GINT_TO_POINTER (format_number));
777
778     /* If that pad is the first with alpha, set it as the new best format */
779     if (!need_alpha && (pad->priv->needs_alpha
780             && (!GST_VIDEO_FORMAT_INFO_HAS_ALPHA (pad->info.finfo)))) {
781       need_alpha = TRUE;
782       /* Just fallback to ARGB in case we require alpha but the input pad
783        * does not have alpha.
784        * Do not increment best_format_number in that case. */
785       gst_video_info_set_format (best_info,
786           GST_VIDEO_FORMAT_ARGB,
787           GST_VIDEO_INFO_WIDTH (&pad->info),
788           GST_VIDEO_INFO_HEIGHT (&pad->info));
789     } else if (!need_alpha
790         && (pad->info.finfo->flags & GST_VIDEO_FORMAT_FLAG_ALPHA)) {
791       need_alpha = TRUE;
792       *best_info = pad->info;
793       best_format_number = format_number;
794     } else if (format_number > best_format_number) {
795       *best_info = pad->info;
796       best_format_number = format_number;
797     }
798   }
799   GST_OBJECT_UNLOCK (vagg);
800
801   g_hash_table_unref (formats_table);
802 }
803
804 static GstCaps *
805 gst_video_aggregator_default_fixate_src_caps (GstAggregator * agg,
806     GstCaps * caps)
807 {
808   GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (agg);
809   gint best_width = -1, best_height = -1;
810   gint best_fps_n = -1, best_fps_d = -1;
811   gdouble best_fps = -1.;
812   GstStructure *s;
813   GList *l;
814
815   GST_OBJECT_LOCK (vagg);
816   for (l = GST_ELEMENT (vagg)->sinkpads; l; l = l->next) {
817     GstVideoAggregatorPad *mpad = l->data;
818     gint fps_n, fps_d;
819     gint width, height;
820     gdouble cur_fps;
821
822     fps_n = GST_VIDEO_INFO_FPS_N (&mpad->info);
823     fps_d = GST_VIDEO_INFO_FPS_D (&mpad->info);
824     width = GST_VIDEO_INFO_WIDTH (&mpad->info);
825     height = GST_VIDEO_INFO_HEIGHT (&mpad->info);
826
827     if (width == 0 || height == 0)
828       continue;
829
830     if (best_width < width)
831       best_width = width;
832     if (best_height < height)
833       best_height = height;
834
835     if (fps_d == 0)
836       cur_fps = 0.0;
837     else
838       gst_util_fraction_to_double (fps_n, fps_d, &cur_fps);
839
840     if (best_fps < cur_fps) {
841       best_fps = cur_fps;
842       best_fps_n = fps_n;
843       best_fps_d = fps_d;
844     }
845   }
846   GST_OBJECT_UNLOCK (vagg);
847
848   if (best_fps_n <= 0 || best_fps_d <= 0 || best_fps == 0.0) {
849     best_fps_n = 25;
850     best_fps_d = 1;
851     best_fps = 25.0;
852   }
853
854   caps = gst_caps_make_writable (caps);
855   s = gst_caps_get_structure (caps, 0);
856   gst_structure_fixate_field_nearest_int (s, "width", best_width);
857   gst_structure_fixate_field_nearest_int (s, "height", best_height);
858   gst_structure_fixate_field_nearest_fraction (s, "framerate", best_fps_n,
859       best_fps_d);
860   if (gst_structure_has_field (s, "pixel-aspect-ratio"))
861     gst_structure_fixate_field_nearest_fraction (s, "pixel-aspect-ratio", 1, 1);
862   caps = gst_caps_fixate (caps);
863
864   return caps;
865 }
866
867 static GstCaps *
868 gst_video_aggregator_default_update_caps (GstVideoAggregator * vagg,
869     GstCaps * caps)
870 {
871   GstVideoAggregatorClass *vagg_klass = GST_VIDEO_AGGREGATOR_GET_CLASS (vagg);
872   GstCaps *ret, *best_format_caps;
873   gboolean at_least_one_alpha = FALSE;
874   GstVideoFormat best_format;
875   GstVideoInfo best_info;
876   gchar *color_name;
877
878   best_format = GST_VIDEO_FORMAT_UNKNOWN;
879   gst_video_info_init (&best_info);
880
881   if (vagg_klass->find_best_format) {
882     vagg_klass->find_best_format (vagg, caps, &best_info, &at_least_one_alpha);
883
884     best_format = GST_VIDEO_INFO_FORMAT (&best_info);
885   }
886
887   if (best_format == GST_VIDEO_FORMAT_UNKNOWN) {
888     GstCaps *tmp = gst_caps_fixate (gst_caps_ref (caps));
889     gst_video_info_from_caps (&best_info, tmp);
890     best_format = GST_VIDEO_INFO_FORMAT (&best_info);
891     gst_caps_unref (tmp);
892   }
893
894   GST_DEBUG_OBJECT (vagg,
895       "The output format will now be : %d with chroma : %s and colorimetry %s",
896       best_format, gst_video_chroma_to_string (best_info.chroma_site),
897       gst_video_colorimetry_to_string (&best_info.colorimetry));
898
899   best_format_caps = gst_caps_copy (caps);
900   color_name = gst_video_colorimetry_to_string (&best_info.colorimetry);
901   gst_caps_set_simple (best_format_caps, "format", G_TYPE_STRING,
902       gst_video_format_to_string (best_format), "chroma-site", G_TYPE_STRING,
903       gst_video_chroma_to_string (best_info.chroma_site), "colorimetry",
904       G_TYPE_STRING, color_name, NULL);
905   g_free (color_name);
906   ret = gst_caps_merge (best_format_caps, gst_caps_ref (caps));
907
908   return ret;
909 }
910
911 static GstFlowReturn
912 gst_video_aggregator_default_update_src_caps (GstAggregator * agg,
913     GstCaps * caps, GstCaps ** ret)
914 {
915   GstVideoAggregatorClass *vagg_klass = GST_VIDEO_AGGREGATOR_GET_CLASS (agg);
916   GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (agg);
917   gboolean at_least_one_pad_configured = FALSE;
918   GList *l;
919
920   GST_OBJECT_LOCK (vagg);
921   for (l = GST_ELEMENT (vagg)->sinkpads; l; l = l->next) {
922     GstVideoAggregatorPad *mpad = l->data;
923
924     if (GST_VIDEO_INFO_WIDTH (&mpad->info) == 0
925         || GST_VIDEO_INFO_HEIGHT (&mpad->info) == 0)
926       continue;
927
928     at_least_one_pad_configured = TRUE;
929   }
930   GST_OBJECT_UNLOCK (vagg);
931
932   if (!at_least_one_pad_configured) {
933     /* We couldn't decide the output video info because the sinkpads don't have
934      * all the caps yet, so we mark the pad as needing a reconfigure. This
935      * allows aggregate() to skip ahead a bit and try again later. */
936     GST_DEBUG_OBJECT (vagg, "Couldn't decide output video info");
937     gst_pad_mark_reconfigure (agg->srcpad);
938     return GST_AGGREGATOR_FLOW_NEED_DATA;
939   }
940
941   g_assert (vagg_klass->update_caps);
942
943   *ret = vagg_klass->update_caps (vagg, caps);
944
945   return GST_FLOW_OK;
946 }
947
948 static gboolean
949 gst_video_aggregator_default_negotiated_src_caps (GstAggregator * agg,
950     GstCaps * caps)
951 {
952   GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (agg);
953   gboolean at_least_one_alpha = FALSE;
954   const GstVideoFormatInfo *finfo;
955   GstVideoInfo info;
956   GList *l;
957
958   GST_INFO_OBJECT (agg->srcpad, "set src caps: %" GST_PTR_FORMAT, caps);
959
960   GST_OBJECT_LOCK (vagg);
961   for (l = GST_ELEMENT (vagg)->sinkpads; l; l = l->next) {
962     GstVideoAggregatorPad *mpad = l->data;
963
964     if (GST_VIDEO_INFO_WIDTH (&mpad->info) == 0
965         || GST_VIDEO_INFO_HEIGHT (&mpad->info) == 0)
966       continue;
967
968     if (mpad->info.finfo->flags & GST_VIDEO_FORMAT_FLAG_ALPHA)
969       at_least_one_alpha = TRUE;
970   }
971   GST_OBJECT_UNLOCK (vagg);
972
973   if (!gst_video_info_from_caps (&info, caps))
974     return FALSE;
975
976   if (GST_VIDEO_INFO_FPS_N (&vagg->info) != GST_VIDEO_INFO_FPS_N (&info) ||
977       GST_VIDEO_INFO_FPS_D (&vagg->info) != GST_VIDEO_INFO_FPS_D (&info)) {
978     if (GST_AGGREGATOR_PAD (agg->srcpad)->segment.position != -1) {
979       vagg->priv->nframes = 0;
980       /* The timestamp offset will be updated based on the
981        * segment position the next time we aggregate */
982       GST_DEBUG_OBJECT (vagg,
983           "Resetting frame counter because of framerate change");
984     }
985     gst_video_aggregator_reset_qos (vagg);
986   }
987
988   vagg->info = info;
989
990   finfo = vagg->info.finfo;
991
992   if (at_least_one_alpha && !(finfo->flags & GST_VIDEO_FORMAT_FLAG_ALPHA)) {
993     GST_ELEMENT_ERROR (vagg, CORE, NEGOTIATION,
994         ("At least one of the input pads contains alpha, but configured caps don't support alpha."),
995         ("Either convert your inputs to not contain alpha or add a videoconvert after the aggregator"));
996     return FALSE;
997   }
998
999   /* Then browse the sinks once more, setting or unsetting conversion if needed */
1000   for (l = GST_ELEMENT (vagg)->sinkpads; l; l = l->next) {
1001     GstVideoAggregatorPad *pad = GST_VIDEO_AGGREGATOR_PAD (l->data);
1002     GstVideoAggregatorPadClass *vaggpad_klass =
1003         GST_VIDEO_AGGREGATOR_PAD_GET_CLASS (pad);
1004
1005     if (vaggpad_klass->update_conversion_info) {
1006       vaggpad_klass->update_conversion_info (pad);
1007     }
1008   }
1009
1010   if (vagg->priv->current_caps == NULL ||
1011       gst_caps_is_equal (caps, vagg->priv->current_caps) == FALSE) {
1012     GstClockTime latency;
1013
1014     gst_caps_replace (&vagg->priv->current_caps, caps);
1015
1016     gst_aggregator_set_src_caps (agg, caps);
1017     latency = gst_util_uint64_scale (GST_SECOND,
1018         GST_VIDEO_INFO_FPS_D (&vagg->info), GST_VIDEO_INFO_FPS_N (&vagg->info));
1019     gst_aggregator_set_latency (agg, latency, latency);
1020   }
1021
1022   return TRUE;
1023 }
1024
1025 static gboolean
1026 gst_video_aggregator_get_sinkpads_interlace_mode (GstVideoAggregator * vagg,
1027     GstVideoAggregatorPad * skip_pad, GstVideoInterlaceMode * mode)
1028 {
1029   GList *walk;
1030
1031   for (walk = GST_ELEMENT (vagg)->sinkpads; walk; walk = g_list_next (walk)) {
1032     GstVideoAggregatorPad *vaggpad = walk->data;
1033
1034     if (skip_pad && vaggpad == skip_pad)
1035       continue;
1036     if (vaggpad->info.finfo
1037         && GST_VIDEO_INFO_FORMAT (&vaggpad->info) != GST_VIDEO_FORMAT_UNKNOWN) {
1038       *mode = GST_VIDEO_INFO_INTERLACE_MODE (&vaggpad->info);
1039       return TRUE;
1040     }
1041   }
1042   return FALSE;
1043 }
1044
1045 static gboolean
1046 gst_video_aggregator_pad_sink_setcaps (GstPad * pad, GstObject * parent,
1047     GstCaps * caps)
1048 {
1049   GstVideoAggregator *vagg;
1050   GstVideoAggregatorPad *vaggpad;
1051   GstVideoInfo info;
1052   gboolean ret = FALSE;
1053
1054   GST_INFO_OBJECT (pad, "Setting caps %" GST_PTR_FORMAT, caps);
1055
1056   vagg = GST_VIDEO_AGGREGATOR (parent);
1057   vaggpad = GST_VIDEO_AGGREGATOR_PAD (pad);
1058
1059   if (!gst_video_info_from_caps (&info, caps)) {
1060     GST_DEBUG_OBJECT (pad, "Failed to parse caps");
1061     goto beach;
1062   }
1063
1064   GST_VIDEO_AGGREGATOR_LOCK (vagg);
1065   {
1066     GstVideoInterlaceMode pads_mode = GST_VIDEO_INTERLACE_MODE_PROGRESSIVE;
1067     gboolean has_mode = FALSE;
1068
1069     /* get the current output setting or fallback to other pads settings */
1070     if (GST_VIDEO_INFO_FORMAT (&vagg->info) != GST_VIDEO_FORMAT_UNKNOWN) {
1071       pads_mode = GST_VIDEO_INFO_INTERLACE_MODE (&vagg->info);
1072       has_mode = TRUE;
1073     } else {
1074       has_mode =
1075           gst_video_aggregator_get_sinkpads_interlace_mode (vagg, vaggpad,
1076           &pads_mode);
1077     }
1078
1079     if (has_mode) {
1080       if (pads_mode != GST_VIDEO_INFO_INTERLACE_MODE (&info)) {
1081         GST_ERROR_OBJECT (pad,
1082             "got input caps %" GST_PTR_FORMAT ", but current caps are %"
1083             GST_PTR_FORMAT, caps, vagg->priv->current_caps);
1084         GST_VIDEO_AGGREGATOR_UNLOCK (vagg);
1085         return FALSE;
1086       }
1087     }
1088   }
1089
1090   if (!vaggpad->info.finfo ||
1091       GST_VIDEO_INFO_FORMAT (&vaggpad->info) == GST_VIDEO_FORMAT_UNKNOWN) {
1092     /* no video info was already set, so this is the first time
1093      * that this pad is getting configured; configure immediately to avoid
1094      * problems with the initial negotiation */
1095     vaggpad->info = info;
1096     gst_pad_mark_reconfigure (GST_AGGREGATOR_SRC_PAD (vagg));
1097   } else {
1098     /* this pad already had caps but received new ones; keep the new caps
1099      * pending until we pick the next buffer from the queue, otherwise we
1100      * might use an old buffer with the new caps and crash */
1101     vaggpad->priv->pending_vinfo = info;
1102     GST_DEBUG_OBJECT (pad, "delaying caps change");
1103   }
1104   ret = TRUE;
1105
1106   GST_VIDEO_AGGREGATOR_UNLOCK (vagg);
1107
1108 beach:
1109   return ret;
1110 }
1111
1112 static gboolean
1113 gst_video_aggregator_caps_has_alpha (GstCaps * caps)
1114 {
1115   guint size = gst_caps_get_size (caps);
1116   guint i;
1117
1118   for (i = 0; i < size; i++) {
1119     GstStructure *s = gst_caps_get_structure (caps, i);
1120     const GValue *formats = gst_structure_get_value (s, "format");
1121
1122     if (formats) {
1123       const GstVideoFormatInfo *info;
1124
1125       if (GST_VALUE_HOLDS_LIST (formats)) {
1126         guint list_size = gst_value_list_get_size (formats);
1127         guint index;
1128
1129         for (index = 0; index < list_size; index++) {
1130           const GValue *list_item = gst_value_list_get_value (formats, index);
1131           info =
1132               gst_video_format_get_info (gst_video_format_from_string
1133               (g_value_get_string (list_item)));
1134           if (GST_VIDEO_FORMAT_INFO_HAS_ALPHA (info))
1135             return TRUE;
1136         }
1137
1138       } else if (G_VALUE_HOLDS_STRING (formats)) {
1139         info =
1140             gst_video_format_get_info (gst_video_format_from_string
1141             (g_value_get_string (formats)));
1142         if (GST_VIDEO_FORMAT_INFO_HAS_ALPHA (info))
1143           return TRUE;
1144
1145       } else {
1146         g_assert_not_reached ();
1147         GST_WARNING ("Unexpected type for video 'format' field: %s",
1148             G_VALUE_TYPE_NAME (formats));
1149       }
1150
1151     } else {
1152       return TRUE;
1153     }
1154   }
1155   return FALSE;
1156 }
1157
1158 static GstCaps *
1159 _get_non_alpha_caps (GstCaps * caps)
1160 {
1161   GstCaps *result;
1162   guint i, size;
1163
1164   size = gst_caps_get_size (caps);
1165   result = gst_caps_new_empty ();
1166   for (i = 0; i < size; i++) {
1167     GstStructure *s = gst_caps_get_structure (caps, i);
1168     const GValue *formats = gst_structure_get_value (s, "format");
1169     GValue new_formats = { 0, };
1170     gboolean has_format = FALSE;
1171
1172     /* FIXME what to do if formats are missing? */
1173     if (formats) {
1174       const GstVideoFormatInfo *info;
1175
1176       if (GST_VALUE_HOLDS_LIST (formats)) {
1177         guint list_size = gst_value_list_get_size (formats);
1178         guint index;
1179
1180         g_value_init (&new_formats, GST_TYPE_LIST);
1181
1182         for (index = 0; index < list_size; index++) {
1183           const GValue *list_item = gst_value_list_get_value (formats, index);
1184
1185           info =
1186               gst_video_format_get_info (gst_video_format_from_string
1187               (g_value_get_string (list_item)));
1188           if (!GST_VIDEO_FORMAT_INFO_HAS_ALPHA (info)) {
1189             has_format = TRUE;
1190             gst_value_list_append_value (&new_formats, list_item);
1191           }
1192         }
1193
1194       } else if (G_VALUE_HOLDS_STRING (formats)) {
1195         info =
1196             gst_video_format_get_info (gst_video_format_from_string
1197             (g_value_get_string (formats)));
1198         if (!GST_VIDEO_FORMAT_INFO_HAS_ALPHA (info)) {
1199           has_format = TRUE;
1200           gst_value_init_and_copy (&new_formats, formats);
1201         }
1202
1203       } else {
1204         g_assert_not_reached ();
1205         GST_WARNING ("Unexpected type for video 'format' field: %s",
1206             G_VALUE_TYPE_NAME (formats));
1207       }
1208
1209       if (has_format) {
1210         s = gst_structure_copy (s);
1211         gst_structure_take_value (s, "format", &new_formats);
1212         gst_caps_append_structure (result, s);
1213       }
1214
1215     }
1216   }
1217
1218   return result;
1219 }
1220
1221 static GstCaps *
1222 gst_video_aggregator_pad_sink_getcaps (GstPad * pad, GstVideoAggregator * vagg,
1223     GstCaps * filter)
1224 {
1225   GstCaps *srccaps;
1226   GstCaps *template_caps, *sink_template_caps;
1227   GstCaps *returned_caps;
1228   GstStructure *s;
1229   gint i, n;
1230   GstAggregator *agg = GST_AGGREGATOR (vagg);
1231   GstPad *srcpad = GST_PAD (agg->srcpad);
1232   gboolean has_alpha;
1233   GstVideoInterlaceMode interlace_mode;
1234   gboolean has_interlace_mode;
1235
1236   template_caps = gst_pad_get_pad_template_caps (srcpad);
1237
1238   GST_DEBUG_OBJECT (pad, "Get caps with filter: %" GST_PTR_FORMAT, filter);
1239
1240   srccaps = gst_pad_peer_query_caps (srcpad, template_caps);
1241   srccaps = gst_caps_make_writable (srccaps);
1242   has_alpha = gst_video_aggregator_caps_has_alpha (srccaps);
1243
1244   has_interlace_mode =
1245       gst_video_aggregator_get_sinkpads_interlace_mode (vagg, NULL,
1246       &interlace_mode);
1247
1248   n = gst_caps_get_size (srccaps);
1249   for (i = 0; i < n; i++) {
1250     s = gst_caps_get_structure (srccaps, i);
1251     gst_structure_set (s, "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
1252         "height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
1253         "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
1254
1255     gst_structure_remove_fields (s, "colorimetry", "chroma-site", "format",
1256         "pixel-aspect-ratio", NULL);
1257     if (has_interlace_mode)
1258       gst_structure_set (s, "interlace-mode", G_TYPE_STRING,
1259           gst_video_interlace_mode_to_string (interlace_mode), NULL);
1260   }
1261
1262   if (filter) {
1263     returned_caps = gst_caps_intersect (srccaps, filter);
1264     gst_caps_unref (srccaps);
1265   } else {
1266     returned_caps = srccaps;
1267   }
1268
1269   sink_template_caps = gst_pad_get_pad_template_caps (pad);
1270   if (!has_alpha) {
1271     GstCaps *tmp = _get_non_alpha_caps (sink_template_caps);
1272     gst_caps_unref (sink_template_caps);
1273     sink_template_caps = tmp;
1274   }
1275
1276   {
1277     GstCaps *intersect = gst_caps_intersect (returned_caps, sink_template_caps);
1278     gst_caps_unref (returned_caps);
1279     returned_caps = intersect;
1280   }
1281
1282   gst_caps_unref (template_caps);
1283   gst_caps_unref (sink_template_caps);
1284
1285   GST_DEBUG_OBJECT (pad, "Returning caps: %" GST_PTR_FORMAT, returned_caps);
1286
1287   return returned_caps;
1288 }
1289
1290 static void
1291 gst_video_aggregator_update_qos (GstVideoAggregator * vagg, gdouble proportion,
1292     GstClockTimeDiff diff, GstClockTime timestamp)
1293 {
1294   gboolean live;
1295
1296   GST_DEBUG_OBJECT (vagg,
1297       "Updating QoS: proportion %lf, diff %" GST_STIME_FORMAT ", timestamp %"
1298       GST_TIME_FORMAT, proportion, GST_STIME_ARGS (diff),
1299       GST_TIME_ARGS (timestamp));
1300
1301   live =
1302       GST_CLOCK_TIME_IS_VALID (gst_aggregator_get_latency (GST_AGGREGATOR
1303           (vagg)));
1304
1305   GST_OBJECT_LOCK (vagg);
1306
1307   vagg->priv->proportion = proportion;
1308   if (G_LIKELY (timestamp != GST_CLOCK_TIME_NONE)) {
1309     if (!live && G_UNLIKELY (diff > 0))
1310       vagg->priv->earliest_time =
1311           timestamp + 2 * diff + gst_util_uint64_scale_int_round (GST_SECOND,
1312           GST_VIDEO_INFO_FPS_D (&vagg->info),
1313           GST_VIDEO_INFO_FPS_N (&vagg->info));
1314     else
1315       vagg->priv->earliest_time = timestamp + diff;
1316   } else {
1317     vagg->priv->earliest_time = GST_CLOCK_TIME_NONE;
1318   }
1319   GST_OBJECT_UNLOCK (vagg);
1320 }
1321
1322 static void
1323 gst_video_aggregator_reset_qos (GstVideoAggregator * vagg)
1324 {
1325   gst_video_aggregator_update_qos (vagg, 0.5, 0, GST_CLOCK_TIME_NONE);
1326   vagg->priv->qos_processed = vagg->priv->qos_dropped = 0;
1327 }
1328
1329 static void
1330 gst_video_aggregator_read_qos (GstVideoAggregator * vagg, gdouble * proportion,
1331     GstClockTime * time)
1332 {
1333   GST_OBJECT_LOCK (vagg);
1334   *proportion = vagg->priv->proportion;
1335   *time = vagg->priv->earliest_time;
1336   GST_OBJECT_UNLOCK (vagg);
1337 }
1338
1339 static void
1340 gst_video_aggregator_reset (GstVideoAggregator * vagg)
1341 {
1342   GstAggregator *agg = GST_AGGREGATOR (vagg);
1343   GList *l;
1344
1345   gst_video_info_init (&vagg->info);
1346   vagg->priv->ts_offset = 0;
1347   vagg->priv->nframes = 0;
1348   vagg->priv->live = FALSE;
1349
1350   GST_AGGREGATOR_PAD (agg->srcpad)->segment.position = -1;
1351
1352   gst_video_aggregator_reset_qos (vagg);
1353
1354   GST_OBJECT_LOCK (vagg);
1355   for (l = GST_ELEMENT (vagg)->sinkpads; l; l = l->next) {
1356     GstVideoAggregatorPad *p = l->data;
1357
1358     gst_buffer_replace (&p->priv->buffer, NULL);
1359     p->priv->start_time = -1;
1360     p->priv->end_time = -1;
1361
1362     gst_video_info_init (&p->info);
1363   }
1364   GST_OBJECT_UNLOCK (vagg);
1365 }
1366
1367 static GstFlowReturn
1368 gst_video_aggregator_fill_queues (GstVideoAggregator * vagg,
1369     GstClockTime output_start_running_time,
1370     GstClockTime output_end_running_time)
1371 {
1372   GstAggregator *agg = GST_AGGREGATOR (vagg);
1373   GList *l;
1374   gboolean eos = TRUE;
1375   gboolean need_more_data = FALSE;
1376   gboolean need_reconfigure = FALSE;
1377   GstSegment *agg_segment = &GST_AGGREGATOR_PAD (agg->srcpad)->segment;
1378
1379   /* get a set of buffers into pad->priv->buffer that are within output_start_running_time
1380    * and output_end_running_time taking into account finished and unresponsive pads */
1381
1382   GST_OBJECT_LOCK (vagg);
1383   for (l = GST_ELEMENT (vagg)->sinkpads; l; l = l->next) {
1384     GstVideoAggregatorPad *pad = l->data;
1385     GstSegment segment;
1386     GstAggregatorPad *bpad;
1387     GstBuffer *buf;
1388     gboolean is_eos;
1389
1390     bpad = GST_AGGREGATOR_PAD (pad);
1391     GST_OBJECT_LOCK (bpad);
1392     segment = bpad->segment;
1393     GST_OBJECT_UNLOCK (bpad);
1394     is_eos = gst_aggregator_pad_is_eos (bpad);
1395
1396     if (!is_eos)
1397       eos = FALSE;
1398     buf = gst_aggregator_pad_peek_buffer (bpad);
1399     if (buf) {
1400       GstClockTime start_time, end_time;
1401
1402       start_time = GST_BUFFER_TIMESTAMP (buf);
1403       if (start_time == -1) {
1404         gst_buffer_unref (buf);
1405         GST_DEBUG_OBJECT (pad, "Need timestamped buffers!");
1406         GST_OBJECT_UNLOCK (vagg);
1407         return GST_FLOW_ERROR;
1408       }
1409
1410       /* FIXME: Make all this work with negative rates */
1411       end_time = GST_BUFFER_DURATION (buf);
1412
1413       if (end_time == -1) {
1414         start_time = MAX (start_time, segment.start);
1415         start_time =
1416             gst_segment_to_running_time (&segment, GST_FORMAT_TIME, start_time);
1417
1418         if (start_time >= output_end_running_time) {
1419           if (pad->priv->buffer) {
1420             GST_DEBUG_OBJECT (pad, "buffer duration is -1, start_time >= "
1421                 "output_end_running_time. Keeping previous buffer");
1422           } else {
1423             GST_DEBUG_OBJECT (pad, "buffer duration is -1, start_time >= "
1424                 "output_end_running_time. No previous buffer.");
1425           }
1426           gst_buffer_unref (buf);
1427           continue;
1428         } else if (start_time < output_start_running_time) {
1429           GST_DEBUG_OBJECT (pad, "buffer duration is -1, start_time < "
1430               "output_start_running_time.  Discarding old buffer");
1431           gst_buffer_replace (&pad->priv->buffer, buf);
1432           if (pad->priv->pending_vinfo.finfo) {
1433             pad->info = pad->priv->pending_vinfo;
1434             need_reconfigure = TRUE;
1435             pad->priv->pending_vinfo.finfo = NULL;
1436           }
1437           gst_buffer_unref (buf);
1438           gst_aggregator_pad_drop_buffer (bpad);
1439           need_more_data = TRUE;
1440           continue;
1441         }
1442         gst_buffer_unref (buf);
1443         buf = gst_aggregator_pad_pop_buffer (bpad);
1444         gst_buffer_replace (&pad->priv->buffer, buf);
1445         if (pad->priv->pending_vinfo.finfo) {
1446           pad->info = pad->priv->pending_vinfo;
1447           need_reconfigure = TRUE;
1448           pad->priv->pending_vinfo.finfo = NULL;
1449         }
1450         /* FIXME: Set start_time and end_time to something here? */
1451         gst_buffer_unref (buf);
1452         GST_DEBUG_OBJECT (pad, "buffer duration is -1");
1453         continue;
1454       }
1455
1456       g_assert (start_time != -1 && end_time != -1);
1457       end_time += start_time;   /* convert from duration to position */
1458
1459       /* Check if it's inside the segment */
1460       if (start_time >= segment.stop || end_time < segment.start) {
1461         GST_DEBUG_OBJECT (pad,
1462             "Buffer outside the segment : segment: [%" GST_TIME_FORMAT " -- %"
1463             GST_TIME_FORMAT "]" " Buffer [%" GST_TIME_FORMAT " -- %"
1464             GST_TIME_FORMAT "]", GST_TIME_ARGS (segment.stop),
1465             GST_TIME_ARGS (segment.start), GST_TIME_ARGS (start_time),
1466             GST_TIME_ARGS (end_time));
1467
1468         gst_buffer_unref (buf);
1469         gst_aggregator_pad_drop_buffer (bpad);
1470
1471         need_more_data = TRUE;
1472         continue;
1473       }
1474
1475       /* Clip to segment and convert to running time */
1476       start_time = MAX (start_time, segment.start);
1477       if (segment.stop != -1)
1478         end_time = MIN (end_time, segment.stop);
1479       start_time =
1480           gst_segment_to_running_time (&segment, GST_FORMAT_TIME, start_time);
1481       end_time =
1482           gst_segment_to_running_time (&segment, GST_FORMAT_TIME, end_time);
1483       g_assert (start_time != -1 && end_time != -1);
1484
1485       /* Convert to the output segment rate */
1486       if (ABS (agg_segment->rate) != 1.0) {
1487         start_time *= ABS (agg_segment->rate);
1488         end_time *= ABS (agg_segment->rate);
1489       }
1490
1491       GST_TRACE_OBJECT (pad, "dealing with buffer %p start %" GST_TIME_FORMAT
1492           " end %" GST_TIME_FORMAT " out start %" GST_TIME_FORMAT
1493           " out end %" GST_TIME_FORMAT, buf, GST_TIME_ARGS (start_time),
1494           GST_TIME_ARGS (end_time), GST_TIME_ARGS (output_start_running_time),
1495           GST_TIME_ARGS (output_end_running_time));
1496
1497       if (pad->priv->end_time != -1 && pad->priv->end_time > end_time) {
1498         GST_DEBUG_OBJECT (pad, "Buffer from the past, dropping");
1499         gst_buffer_unref (buf);
1500         gst_aggregator_pad_drop_buffer (bpad);
1501         continue;
1502       }
1503
1504       if (end_time >= output_start_running_time
1505           && start_time < output_end_running_time) {
1506         GST_DEBUG_OBJECT (pad,
1507             "Taking new buffer with start time %" GST_TIME_FORMAT,
1508             GST_TIME_ARGS (start_time));
1509         gst_buffer_replace (&pad->priv->buffer, buf);
1510         if (pad->priv->pending_vinfo.finfo) {
1511           pad->info = pad->priv->pending_vinfo;
1512           need_reconfigure = TRUE;
1513           pad->priv->pending_vinfo.finfo = NULL;
1514         }
1515         pad->priv->start_time = start_time;
1516         pad->priv->end_time = end_time;
1517
1518         gst_buffer_unref (buf);
1519         gst_aggregator_pad_drop_buffer (bpad);
1520         eos = FALSE;
1521       } else if (start_time >= output_end_running_time) {
1522         GST_DEBUG_OBJECT (pad, "Keeping buffer until %" GST_TIME_FORMAT,
1523             GST_TIME_ARGS (start_time));
1524         gst_buffer_unref (buf);
1525         eos = FALSE;
1526       } else {
1527         gst_buffer_replace (&pad->priv->buffer, buf);
1528         if (pad->priv->pending_vinfo.finfo) {
1529           pad->info = pad->priv->pending_vinfo;
1530           need_reconfigure = TRUE;
1531           pad->priv->pending_vinfo.finfo = NULL;
1532         }
1533         pad->priv->start_time = start_time;
1534         pad->priv->end_time = end_time;
1535         GST_DEBUG_OBJECT (pad,
1536             "replacing old buffer with a newer buffer, start %" GST_TIME_FORMAT
1537             " out end %" GST_TIME_FORMAT, GST_TIME_ARGS (start_time),
1538             GST_TIME_ARGS (output_end_running_time));
1539         gst_buffer_unref (buf);
1540         gst_aggregator_pad_drop_buffer (bpad);
1541
1542         need_more_data = TRUE;
1543         continue;
1544       }
1545     } else {
1546       if (is_eos && pad->priv->repeat_after_eos) {
1547         eos = FALSE;
1548         GST_DEBUG_OBJECT (pad, "ignoring EOS and re-using previous buffer");
1549         continue;
1550       }
1551
1552       if (pad->priv->end_time != -1) {
1553         if (pad->priv->end_time <= output_start_running_time) {
1554           pad->priv->start_time = pad->priv->end_time = -1;
1555           if (!is_eos) {
1556             GST_DEBUG ("I just need more data");
1557             need_more_data = TRUE;
1558           } else {
1559             gst_buffer_replace (&pad->priv->buffer, NULL);
1560           }
1561         } else if (is_eos) {
1562           eos = FALSE;
1563         }
1564       } else if (is_eos) {
1565         gst_buffer_replace (&pad->priv->buffer, NULL);
1566       }
1567     }
1568   }
1569   GST_OBJECT_UNLOCK (vagg);
1570
1571   if (need_reconfigure)
1572     gst_pad_mark_reconfigure (GST_AGGREGATOR_SRC_PAD (vagg));
1573
1574   if (need_more_data)
1575     return GST_AGGREGATOR_FLOW_NEED_DATA;
1576   if (eos)
1577     return GST_FLOW_EOS;
1578
1579   return GST_FLOW_OK;
1580 }
1581
1582 static gboolean
1583 sync_pad_values (GstElement * vagg, GstPad * pad, gpointer user_data)
1584 {
1585   GstVideoAggregatorPad *vpad = GST_VIDEO_AGGREGATOR_PAD (pad);
1586   GstAggregatorPad *bpad = GST_AGGREGATOR_PAD_CAST (pad);
1587   GstClockTime timestamp;
1588   gint64 stream_time;
1589
1590   if (vpad->priv->buffer == NULL)
1591     return TRUE;
1592
1593   timestamp = GST_BUFFER_TIMESTAMP (vpad->priv->buffer);
1594   GST_OBJECT_LOCK (bpad);
1595   stream_time = gst_segment_to_stream_time (&bpad->segment, GST_FORMAT_TIME,
1596       timestamp);
1597   GST_OBJECT_UNLOCK (bpad);
1598
1599   /* sync object properties on stream time */
1600   if (GST_CLOCK_TIME_IS_VALID (stream_time))
1601     gst_object_sync_values (GST_OBJECT_CAST (pad), stream_time);
1602
1603   return TRUE;
1604 }
1605
1606 static gboolean
1607 prepare_frames (GstElement * agg, GstPad * pad, gpointer user_data)
1608 {
1609   GstVideoAggregatorPad *vpad = GST_VIDEO_AGGREGATOR_PAD_CAST (pad);
1610   GstVideoAggregatorPadClass *vaggpad_class =
1611       GST_VIDEO_AGGREGATOR_PAD_GET_CLASS (pad);
1612
1613   memset (&vpad->priv->prepared_frame, 0, sizeof (GstVideoFrame));
1614
1615   if (vpad->priv->buffer == NULL || !vaggpad_class->prepare_frame)
1616     return TRUE;
1617
1618   return vaggpad_class->prepare_frame (vpad, GST_VIDEO_AGGREGATOR_CAST (agg),
1619       vpad->priv->buffer, &vpad->priv->prepared_frame);
1620 }
1621
1622 static gboolean
1623 clean_pad (GstElement * agg, GstPad * pad, gpointer user_data)
1624 {
1625   GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR_CAST (agg);
1626   GstVideoAggregatorPad *vpad = GST_VIDEO_AGGREGATOR_PAD_CAST (pad);
1627   GstVideoAggregatorPadClass *vaggpad_class =
1628       GST_VIDEO_AGGREGATOR_PAD_GET_CLASS (pad);
1629
1630   if (vaggpad_class->clean_frame)
1631     vaggpad_class->clean_frame (vpad, vagg, &vpad->priv->prepared_frame);
1632
1633   memset (&vpad->priv->prepared_frame, 0, sizeof (GstVideoFrame));
1634
1635   return TRUE;
1636 }
1637
1638 static GstFlowReturn
1639 gst_video_aggregator_do_aggregate (GstVideoAggregator * vagg,
1640     GstClockTime output_start_time, GstClockTime output_end_time,
1641     GstBuffer ** outbuf)
1642 {
1643   GstFlowReturn ret = GST_FLOW_OK;
1644   GstElementClass *klass = GST_ELEMENT_GET_CLASS (vagg);
1645   GstVideoAggregatorClass *vagg_klass = (GstVideoAggregatorClass *) klass;
1646
1647   g_assert (vagg_klass->aggregate_frames != NULL);
1648   g_assert (vagg_klass->create_output_buffer != NULL);
1649
1650   if ((ret = vagg_klass->create_output_buffer (vagg, outbuf)) != GST_FLOW_OK) {
1651     GST_WARNING_OBJECT (vagg, "Could not get an output buffer, reason: %s",
1652         gst_flow_get_name (ret));
1653     return ret;
1654   }
1655   if (*outbuf == NULL) {
1656     /* sub-class doesn't want to generate output right now */
1657     return GST_FLOW_OK;
1658   }
1659
1660   GST_BUFFER_TIMESTAMP (*outbuf) = output_start_time;
1661   GST_BUFFER_DURATION (*outbuf) = output_end_time - output_start_time;
1662
1663   /* Sync pad properties to the stream time */
1664   gst_element_foreach_sink_pad (GST_ELEMENT_CAST (vagg), sync_pad_values, NULL);
1665
1666   /* Convert all the frames the subclass has before aggregating */
1667   gst_element_foreach_sink_pad (GST_ELEMENT_CAST (vagg), prepare_frames, NULL);
1668
1669   ret = vagg_klass->aggregate_frames (vagg, *outbuf);
1670
1671   gst_element_foreach_sink_pad (GST_ELEMENT_CAST (vagg), clean_pad, NULL);
1672
1673   return ret;
1674 }
1675
1676 /* Perform qos calculations before processing the next frame. Returns TRUE if
1677  * the frame should be processed, FALSE if the frame can be dropped entirely */
1678 static gint64
1679 gst_video_aggregator_do_qos (GstVideoAggregator * vagg, GstClockTime timestamp)
1680 {
1681   GstAggregator *agg = GST_AGGREGATOR (vagg);
1682   GstClockTime qostime, earliest_time;
1683   gdouble proportion;
1684   gint64 jitter;
1685
1686   /* no timestamp, can't do QoS => process frame */
1687   if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (timestamp))) {
1688     GST_LOG_OBJECT (vagg, "invalid timestamp, can't do QoS, process frame");
1689     return -1;
1690   }
1691
1692   /* get latest QoS observation values */
1693   gst_video_aggregator_read_qos (vagg, &proportion, &earliest_time);
1694
1695   /* skip qos if we have no observation (yet) => process frame */
1696   if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (earliest_time))) {
1697     GST_LOG_OBJECT (vagg, "no observation yet, process frame");
1698     return -1;
1699   }
1700
1701   /* qos is done on running time */
1702   qostime =
1703       gst_segment_to_running_time (&GST_AGGREGATOR_PAD (agg->srcpad)->segment,
1704       GST_FORMAT_TIME, timestamp);
1705
1706   /* see how our next timestamp relates to the latest qos timestamp */
1707   GST_LOG_OBJECT (vagg, "qostime %" GST_TIME_FORMAT ", earliest %"
1708       GST_TIME_FORMAT, GST_TIME_ARGS (qostime), GST_TIME_ARGS (earliest_time));
1709
1710   jitter = GST_CLOCK_DIFF (qostime, earliest_time);
1711   if (qostime != GST_CLOCK_TIME_NONE && jitter > 0) {
1712     GST_DEBUG_OBJECT (vagg, "we are late, drop frame");
1713     return jitter;
1714   }
1715
1716   GST_LOG_OBJECT (vagg, "process frame");
1717   return jitter;
1718 }
1719
1720 static void
1721 gst_video_aggregator_advance_on_timeout (GstVideoAggregator * vagg)
1722 {
1723   GstAggregator *agg = GST_AGGREGATOR (vagg);
1724   guint64 frame_duration;
1725   gint fps_d, fps_n;
1726   GstSegment *agg_segment = &GST_AGGREGATOR_PAD (agg->srcpad)->segment;
1727
1728   GST_OBJECT_LOCK (agg);
1729   if (agg_segment->position == -1) {
1730     if (agg_segment->rate > 0.0)
1731       agg_segment->position = agg_segment->start;
1732     else
1733       agg_segment->position = agg_segment->stop;
1734   }
1735
1736   /* Advance position */
1737   fps_d = GST_VIDEO_INFO_FPS_D (&vagg->info) ?
1738       GST_VIDEO_INFO_FPS_D (&vagg->info) : 1;
1739   fps_n = GST_VIDEO_INFO_FPS_N (&vagg->info) ?
1740       GST_VIDEO_INFO_FPS_N (&vagg->info) : 25;
1741   /* Default to 25/1 if no "best fps" is known */
1742   frame_duration = gst_util_uint64_scale (GST_SECOND, fps_d, fps_n);
1743   if (agg_segment->rate > 0.0)
1744     agg_segment->position += frame_duration;
1745   else if (agg_segment->position > frame_duration)
1746     agg_segment->position -= frame_duration;
1747   else
1748     agg_segment->position = 0;
1749   vagg->priv->nframes++;
1750   GST_OBJECT_UNLOCK (agg);
1751 }
1752
1753 static GstFlowReturn
1754 gst_video_aggregator_aggregate (GstAggregator * agg, gboolean timeout)
1755 {
1756   GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (agg);
1757   GstClockTime output_start_time, output_end_time;
1758   GstClockTime output_start_running_time, output_end_running_time;
1759   GstBuffer *outbuf = NULL;
1760   GstFlowReturn flow_ret;
1761   gint64 jitter;
1762   GstSegment *agg_segment = &GST_AGGREGATOR_PAD (agg->srcpad)->segment;
1763
1764   GST_VIDEO_AGGREGATOR_LOCK (vagg);
1765
1766   if (GST_VIDEO_INFO_FORMAT (&vagg->info) == GST_VIDEO_FORMAT_UNKNOWN) {
1767     if (timeout)
1768       gst_video_aggregator_advance_on_timeout (vagg);
1769     flow_ret = GST_AGGREGATOR_FLOW_NEED_DATA;
1770     goto unlock_and_return;
1771   }
1772
1773   output_start_time = agg_segment->position;
1774   if (agg_segment->position == -1 || agg_segment->position < agg_segment->start)
1775     output_start_time = agg_segment->start;
1776
1777   if (vagg->priv->nframes == 0) {
1778     vagg->priv->ts_offset = output_start_time;
1779     GST_DEBUG_OBJECT (vagg, "New ts offset %" GST_TIME_FORMAT,
1780         GST_TIME_ARGS (output_start_time));
1781   }
1782
1783   if (GST_VIDEO_INFO_FPS_N (&vagg->info) == 0) {
1784     output_end_time = -1;
1785   } else {
1786     output_end_time =
1787         vagg->priv->ts_offset +
1788         gst_util_uint64_scale_round (vagg->priv->nframes + 1,
1789         GST_SECOND * GST_VIDEO_INFO_FPS_D (&vagg->info),
1790         GST_VIDEO_INFO_FPS_N (&vagg->info));
1791   }
1792
1793   if (agg_segment->stop != -1)
1794     output_end_time = MIN (output_end_time, agg_segment->stop);
1795
1796   output_start_running_time =
1797       gst_segment_to_running_time (agg_segment, GST_FORMAT_TIME,
1798       output_start_time);
1799   output_end_running_time =
1800       gst_segment_to_running_time (agg_segment, GST_FORMAT_TIME,
1801       output_end_time);
1802
1803   if (output_end_time == output_start_time) {
1804     flow_ret = GST_FLOW_EOS;
1805   } else {
1806     flow_ret =
1807         gst_video_aggregator_fill_queues (vagg, output_start_running_time,
1808         output_end_running_time);
1809   }
1810
1811   if (flow_ret == GST_AGGREGATOR_FLOW_NEED_DATA && !timeout) {
1812     GST_DEBUG_OBJECT (vagg, "Need more data for decisions");
1813     goto unlock_and_return;
1814   } else if (flow_ret == GST_FLOW_EOS) {
1815     GST_DEBUG_OBJECT (vagg, "All sinkpads are EOS -- forwarding");
1816     goto unlock_and_return;
1817   } else if (flow_ret == GST_FLOW_ERROR) {
1818     GST_WARNING_OBJECT (vagg, "Error collecting buffers");
1819     goto unlock_and_return;
1820   }
1821
1822   /* It is possible that gst_video_aggregator_fill_queues() marked the pad
1823    * for reconfiguration. In this case we have to reconfigure before continuing
1824    * because we have picked a new buffer with different caps than before from
1825    * one one of the sink pads and continuing here may lead to a crash.
1826    * https://bugzilla.gnome.org/show_bug.cgi?id=780682
1827    */
1828   if (gst_pad_needs_reconfigure (GST_AGGREGATOR_SRC_PAD (vagg))) {
1829     GST_DEBUG_OBJECT (vagg, "Need reconfigure");
1830     flow_ret = GST_AGGREGATOR_FLOW_NEED_DATA;
1831     goto unlock_and_return;
1832   }
1833
1834   GST_DEBUG_OBJECT (vagg,
1835       "Producing buffer for %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT
1836       ", running time start %" GST_TIME_FORMAT ", running time end %"
1837       GST_TIME_FORMAT, GST_TIME_ARGS (output_start_time),
1838       GST_TIME_ARGS (output_end_time),
1839       GST_TIME_ARGS (output_start_running_time),
1840       GST_TIME_ARGS (output_end_running_time));
1841
1842   jitter = gst_video_aggregator_do_qos (vagg, output_start_time);
1843   if (jitter <= 0) {
1844     flow_ret = gst_video_aggregator_do_aggregate (vagg, output_start_time,
1845         output_end_time, &outbuf);
1846     if (flow_ret != GST_FLOW_OK)
1847       goto done;
1848     vagg->priv->qos_processed++;
1849   } else {
1850     GstMessage *msg;
1851
1852     vagg->priv->qos_dropped++;
1853
1854     msg =
1855         gst_message_new_qos (GST_OBJECT_CAST (vagg), vagg->priv->live,
1856         output_start_running_time, gst_segment_to_stream_time (agg_segment,
1857             GST_FORMAT_TIME, output_start_time), output_start_time,
1858         output_end_time - output_start_time);
1859     gst_message_set_qos_values (msg, jitter, vagg->priv->proportion, 1000000);
1860     gst_message_set_qos_stats (msg, GST_FORMAT_BUFFERS,
1861         vagg->priv->qos_processed, vagg->priv->qos_dropped);
1862     gst_element_post_message (GST_ELEMENT_CAST (vagg), msg);
1863
1864     flow_ret = GST_FLOW_OK;
1865   }
1866
1867   GST_VIDEO_AGGREGATOR_UNLOCK (vagg);
1868   if (outbuf) {
1869     GST_DEBUG_OBJECT (vagg,
1870         "Pushing buffer with ts %" GST_TIME_FORMAT " and duration %"
1871         GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)),
1872         GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)));
1873
1874     flow_ret = gst_aggregator_finish_buffer (agg, outbuf);
1875   }
1876
1877   GST_VIDEO_AGGREGATOR_LOCK (vagg);
1878   vagg->priv->nframes++;
1879   agg_segment->position = output_end_time;
1880   GST_VIDEO_AGGREGATOR_UNLOCK (vagg);
1881
1882   return flow_ret;
1883
1884 done:
1885   if (outbuf)
1886     gst_buffer_unref (outbuf);
1887 unlock_and_return:
1888   GST_VIDEO_AGGREGATOR_UNLOCK (vagg);
1889   return flow_ret;
1890 }
1891
1892 /* FIXME, the duration query should reflect how long you will produce
1893  * data, that is the amount of stream time until you will emit EOS.
1894  *
1895  * For synchronized aggregating this is always the max of all the durations
1896  * of upstream since we emit EOS when all of them finished.
1897  *
1898  * We don't do synchronized aggregating so this really depends on where the
1899  * streams where punched in and what their relative offsets are against
1900  * each other which we can get from the first timestamps we see.
1901  *
1902  * When we add a new stream (or remove a stream) the duration might
1903  * also become invalid again and we need to post a new DURATION
1904  * message to notify this fact to the parent.
1905  * For now we take the max of all the upstream elements so the simple
1906  * cases work at least somewhat.
1907  */
1908 static gboolean
1909 gst_video_aggregator_query_duration (GstVideoAggregator * vagg,
1910     GstQuery * query)
1911 {
1912   GValue item = { 0 };
1913   gint64 max;
1914   gboolean res;
1915   GstFormat format;
1916   GstIterator *it;
1917   gboolean done;
1918
1919   /* parse format */
1920   gst_query_parse_duration (query, &format, NULL);
1921
1922   max = -1;
1923   res = TRUE;
1924   done = FALSE;
1925
1926   /* Take maximum of all durations */
1927   it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (vagg));
1928   while (!done) {
1929     switch (gst_iterator_next (it, &item)) {
1930       case GST_ITERATOR_DONE:
1931         done = TRUE;
1932         break;
1933       case GST_ITERATOR_OK:
1934       {
1935         GstPad *pad;
1936         gint64 duration;
1937
1938         pad = g_value_get_object (&item);
1939
1940         /* ask sink peer for duration */
1941         res &= gst_pad_peer_query_duration (pad, format, &duration);
1942         /* take max from all valid return values */
1943         if (res) {
1944           /* valid unknown length, stop searching */
1945           if (duration == -1) {
1946             max = duration;
1947             done = TRUE;
1948           }
1949           /* else see if bigger than current max */
1950           else if (duration > max)
1951             max = duration;
1952         }
1953         g_value_reset (&item);
1954         break;
1955       }
1956       case GST_ITERATOR_RESYNC:
1957         max = -1;
1958         res = TRUE;
1959         gst_iterator_resync (it);
1960         break;
1961       default:
1962         res = FALSE;
1963         done = TRUE;
1964         break;
1965     }
1966   }
1967   g_value_unset (&item);
1968   gst_iterator_free (it);
1969
1970   if (res) {
1971     /* and store the max */
1972     GST_DEBUG_OBJECT (vagg, "Total duration in format %s: %"
1973         GST_TIME_FORMAT, gst_format_get_name (format), GST_TIME_ARGS (max));
1974     gst_query_set_duration (query, format, max);
1975   }
1976
1977   return res;
1978 }
1979
1980 static gboolean
1981 gst_video_aggregator_src_query (GstAggregator * agg, GstQuery * query)
1982 {
1983   GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (agg);
1984   gboolean res = FALSE;
1985   GstSegment *agg_segment = &GST_AGGREGATOR_PAD (agg->srcpad)->segment;
1986
1987   switch (GST_QUERY_TYPE (query)) {
1988     case GST_QUERY_POSITION:
1989     {
1990       GstFormat format;
1991
1992       gst_query_parse_position (query, &format, NULL);
1993
1994       switch (format) {
1995         case GST_FORMAT_TIME:
1996           gst_query_set_position (query, format,
1997               gst_segment_to_stream_time (agg_segment, GST_FORMAT_TIME,
1998                   agg_segment->position));
1999           res = TRUE;
2000           break;
2001         default:
2002           break;
2003       }
2004       break;
2005     }
2006     case GST_QUERY_DURATION:
2007       res = gst_video_aggregator_query_duration (vagg, query);
2008       break;
2009     case GST_QUERY_LATENCY:
2010       res =
2011           GST_AGGREGATOR_CLASS (gst_video_aggregator_parent_class)->src_query
2012           (agg, query);
2013
2014       if (res) {
2015         gst_query_parse_latency (query, &vagg->priv->live, NULL, NULL);
2016       }
2017       break;
2018     default:
2019       res =
2020           GST_AGGREGATOR_CLASS (gst_video_aggregator_parent_class)->src_query
2021           (agg, query);
2022       break;
2023   }
2024   return res;
2025 }
2026
2027 static gboolean
2028 gst_video_aggregator_src_event (GstAggregator * agg, GstEvent * event)
2029 {
2030   GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (agg);
2031
2032   switch (GST_EVENT_TYPE (event)) {
2033     case GST_EVENT_QOS:
2034     {
2035       GstQOSType type;
2036       GstClockTimeDiff diff;
2037       GstClockTime timestamp;
2038       gdouble proportion;
2039
2040       gst_event_parse_qos (event, &type, &proportion, &diff, &timestamp);
2041       gst_video_aggregator_update_qos (vagg, proportion, diff, timestamp);
2042       break;
2043     }
2044     case GST_EVENT_SEEK:
2045     {
2046       GST_DEBUG_OBJECT (vagg, "Handling SEEK event");
2047     }
2048     default:
2049       break;
2050   }
2051
2052   return
2053       GST_AGGREGATOR_CLASS (gst_video_aggregator_parent_class)->src_event (agg,
2054       event);
2055 }
2056
2057 static GstFlowReturn
2058 gst_video_aggregator_flush (GstAggregator * agg)
2059 {
2060   GList *l;
2061   gdouble abs_rate;
2062   GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (agg);
2063   GstSegment *agg_segment = &GST_AGGREGATOR_PAD (agg->srcpad)->segment;
2064
2065   GST_INFO_OBJECT (agg, "Flushing");
2066   GST_OBJECT_LOCK (vagg);
2067   abs_rate = ABS (agg_segment->rate);
2068   for (l = GST_ELEMENT (vagg)->sinkpads; l; l = l->next) {
2069     GstVideoAggregatorPad *p = l->data;
2070
2071     /* Convert to the output segment rate */
2072     if (ABS (agg_segment->rate) != abs_rate) {
2073       if (ABS (agg_segment->rate) != 1.0 && p->priv->buffer) {
2074         p->priv->start_time /= ABS (agg_segment->rate);
2075         p->priv->end_time /= ABS (agg_segment->rate);
2076       }
2077       if (abs_rate != 1.0 && p->priv->buffer) {
2078         p->priv->start_time *= abs_rate;
2079         p->priv->end_time *= abs_rate;
2080       }
2081     }
2082   }
2083   GST_OBJECT_UNLOCK (vagg);
2084
2085   agg_segment->position = -1;
2086   vagg->priv->ts_offset = 0;
2087   vagg->priv->nframes = 0;
2088
2089   gst_video_aggregator_reset_qos (vagg);
2090   return GST_FLOW_OK;
2091 }
2092
2093 static gboolean
2094 gst_video_aggregator_sink_event (GstAggregator * agg, GstAggregatorPad * bpad,
2095     GstEvent * event)
2096 {
2097   GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (agg);
2098   GstVideoAggregatorPad *pad = GST_VIDEO_AGGREGATOR_PAD (bpad);
2099   gboolean ret = TRUE;
2100
2101   GST_DEBUG_OBJECT (pad, "Got %s event on pad %s:%s",
2102       GST_EVENT_TYPE_NAME (event), GST_DEBUG_PAD_NAME (pad));
2103
2104   switch (GST_EVENT_TYPE (event)) {
2105     case GST_EVENT_CAPS:
2106     {
2107       GstCaps *caps;
2108
2109       gst_event_parse_caps (event, &caps);
2110       ret =
2111           gst_video_aggregator_pad_sink_setcaps (GST_PAD (pad),
2112           GST_OBJECT (vagg), caps);
2113       gst_event_unref (event);
2114       event = NULL;
2115       break;
2116     }
2117     case GST_EVENT_SEGMENT:{
2118       GstSegment seg;
2119       gst_event_copy_segment (event, &seg);
2120
2121       g_assert (seg.format == GST_FORMAT_TIME);
2122       gst_video_aggregator_reset_qos (vagg);
2123       break;
2124     }
2125     default:
2126       break;
2127   }
2128
2129   if (event != NULL)
2130     return GST_AGGREGATOR_CLASS (gst_video_aggregator_parent_class)->sink_event
2131         (agg, bpad, event);
2132
2133   return ret;
2134 }
2135
2136 static gboolean
2137 gst_video_aggregator_start (GstAggregator * agg)
2138 {
2139   GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (agg);
2140
2141   gst_caps_replace (&vagg->priv->current_caps, NULL);
2142
2143   return TRUE;
2144 }
2145
2146 static gboolean
2147 gst_video_aggregator_stop (GstAggregator * agg)
2148 {
2149   GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (agg);
2150
2151   gst_video_aggregator_reset (vagg);
2152
2153   return TRUE;
2154 }
2155
2156 /* GstElement vmethods */
2157 static GstPad *
2158 gst_video_aggregator_request_new_pad (GstElement * element,
2159     GstPadTemplate * templ, const gchar * req_name, const GstCaps * caps)
2160 {
2161   GstVideoAggregator *vagg;
2162   GstVideoAggregatorPad *vaggpad;
2163
2164   vagg = GST_VIDEO_AGGREGATOR (element);
2165
2166   vaggpad = (GstVideoAggregatorPad *)
2167       GST_ELEMENT_CLASS (gst_video_aggregator_parent_class)->request_new_pad
2168       (element, templ, req_name, caps);
2169
2170   if (vaggpad == NULL)
2171     return NULL;
2172
2173   GST_OBJECT_LOCK (vagg);
2174   vaggpad->priv->zorder = GST_ELEMENT (vagg)->numsinkpads;
2175   vaggpad->priv->start_time = -1;
2176   vaggpad->priv->end_time = -1;
2177   element->sinkpads = g_list_sort (element->sinkpads,
2178       (GCompareFunc) pad_zorder_compare);
2179   GST_OBJECT_UNLOCK (vagg);
2180
2181   return GST_PAD (vaggpad);
2182 }
2183
2184 static void
2185 gst_video_aggregator_release_pad (GstElement * element, GstPad * pad)
2186 {
2187   GstVideoAggregator *vagg = NULL;
2188   GstVideoAggregatorPad *vaggpad;
2189   gboolean last_pad;
2190
2191   vagg = GST_VIDEO_AGGREGATOR (element);
2192   vaggpad = GST_VIDEO_AGGREGATOR_PAD (pad);
2193
2194   GST_VIDEO_AGGREGATOR_LOCK (vagg);
2195
2196   GST_OBJECT_LOCK (vagg);
2197   last_pad = (GST_ELEMENT (vagg)->numsinkpads - 1 == 0);
2198   GST_OBJECT_UNLOCK (vagg);
2199
2200   if (last_pad)
2201     gst_video_aggregator_reset (vagg);
2202
2203   gst_buffer_replace (&vaggpad->priv->buffer, NULL);
2204
2205   GST_ELEMENT_CLASS (gst_video_aggregator_parent_class)->release_pad
2206       (GST_ELEMENT (vagg), pad);
2207
2208   gst_pad_mark_reconfigure (GST_AGGREGATOR_SRC_PAD (vagg));
2209
2210   GST_VIDEO_AGGREGATOR_UNLOCK (vagg);
2211   return;
2212 }
2213
2214 static gboolean
2215 gst_video_aggregator_propose_allocation (GstAggregator * agg,
2216     GstAggregatorPad * pad, GstQuery * decide_query, GstQuery * query)
2217 {
2218   gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
2219
2220   return TRUE;
2221 }
2222
2223 static gboolean
2224 gst_video_aggregator_decide_allocation (GstAggregator * agg, GstQuery * query)
2225 {
2226   GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (agg);
2227   GstAllocationParams params = { 0, 15, 0, 0 };
2228   guint i;
2229   GstBufferPool *pool;
2230   GstAllocator *allocator;
2231   guint size, min, max;
2232   gboolean update = FALSE;
2233   GstStructure *config = NULL;
2234   GstCaps *caps = NULL;
2235
2236   if (gst_query_get_n_allocation_params (query) == 0) {
2237     gst_query_add_allocation_param (query, NULL, &params);
2238   } else {
2239     for (i = 0; i < gst_query_get_n_allocation_params (query); i++) {
2240       GstAllocator *allocator;
2241
2242       gst_query_parse_nth_allocation_param (query, i, &allocator, &params);
2243       params.align = MAX (params.align, 15);
2244       gst_query_set_nth_allocation_param (query, i, allocator, &params);
2245     }
2246   }
2247
2248   gst_query_parse_nth_allocation_param (query, 0, &allocator, &params);
2249
2250   if (gst_query_get_n_allocation_pools (query) > 0) {
2251     gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
2252
2253     /* adjust size */
2254     size = MAX (size, vagg->info.size);
2255     update = TRUE;
2256   } else {
2257     pool = NULL;
2258     size = vagg->info.size;
2259     min = max = 0;
2260     update = FALSE;
2261   }
2262
2263   gst_query_parse_allocation (query, &caps, NULL);
2264
2265   /* no downstream pool, make our own */
2266   if (pool == NULL)
2267     pool = gst_video_buffer_pool_new ();
2268
2269   config = gst_buffer_pool_get_config (pool);
2270
2271   gst_buffer_pool_config_set_params (config, caps, size, min, max);
2272   gst_buffer_pool_config_set_allocator (config, allocator, &params);
2273   if (gst_query_find_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL)) {
2274     gst_buffer_pool_config_add_option (config,
2275         GST_BUFFER_POOL_OPTION_VIDEO_META);
2276   }
2277
2278   /* buffer pool may have to do some changes */
2279   if (!gst_buffer_pool_set_config (pool, config)) {
2280     config = gst_buffer_pool_get_config (pool);
2281
2282     /* If change are not acceptable, fallback to generic pool */
2283     if (!gst_buffer_pool_config_validate_params (config, caps, size, min, max)) {
2284       GST_DEBUG_OBJECT (agg, "unsupported pool, making new pool");
2285
2286       gst_object_unref (pool);
2287       pool = gst_video_buffer_pool_new ();
2288       gst_buffer_pool_config_set_params (config, caps, size, min, max);
2289       gst_buffer_pool_config_set_allocator (config, allocator, &params);
2290
2291       if (gst_query_find_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL)) {
2292         gst_buffer_pool_config_add_option (config,
2293             GST_BUFFER_POOL_OPTION_VIDEO_META);
2294       }
2295     }
2296
2297     if (!gst_buffer_pool_set_config (pool, config))
2298       goto config_failed;
2299   }
2300
2301   if (update)
2302     gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max);
2303   else
2304     gst_query_add_allocation_pool (query, pool, size, min, max);
2305
2306   if (pool)
2307     gst_object_unref (pool);
2308   if (allocator)
2309     gst_object_unref (allocator);
2310
2311   return TRUE;
2312
2313 config_failed:
2314   if (pool)
2315     gst_object_unref (pool);
2316   if (allocator)
2317     gst_object_unref (allocator);
2318
2319   GST_ELEMENT_ERROR (agg, RESOURCE, SETTINGS,
2320       ("Failed to configure the buffer pool"),
2321       ("Configuration is most likely invalid, please report this issue."));
2322   return FALSE;
2323 }
2324
2325 static GstFlowReturn
2326 gst_video_aggregator_create_output_buffer (GstVideoAggregator * videoaggregator,
2327     GstBuffer ** outbuf)
2328 {
2329   GstAggregator *aggregator = GST_AGGREGATOR (videoaggregator);
2330   GstBufferPool *pool;
2331   GstFlowReturn ret = GST_FLOW_OK;
2332
2333   pool = gst_aggregator_get_buffer_pool (aggregator);
2334
2335   if (pool) {
2336     if (!gst_buffer_pool_is_active (pool)) {
2337       if (!gst_buffer_pool_set_active (pool, TRUE)) {
2338         GST_ELEMENT_ERROR (videoaggregator, RESOURCE, SETTINGS,
2339             ("failed to activate bufferpool"),
2340             ("failed to activate bufferpool"));
2341         return GST_FLOW_ERROR;
2342       }
2343     }
2344
2345     ret = gst_buffer_pool_acquire_buffer (pool, outbuf, NULL);
2346     gst_object_unref (pool);
2347   } else {
2348     guint outsize;
2349     GstAllocator *allocator;
2350     GstAllocationParams params;
2351
2352     gst_aggregator_get_allocator (aggregator, &allocator, &params);
2353
2354     outsize = GST_VIDEO_INFO_SIZE (&videoaggregator->info);
2355     *outbuf = gst_buffer_new_allocate (allocator, outsize, &params);
2356
2357     if (allocator)
2358       gst_object_unref (allocator);
2359
2360     if (*outbuf == NULL) {
2361       GST_ELEMENT_ERROR (videoaggregator, RESOURCE, NO_SPACE_LEFT,
2362           (NULL), ("Could not acquire buffer of size: %d", outsize));
2363       ret = GST_FLOW_ERROR;
2364     }
2365   }
2366   return ret;
2367 }
2368
2369 static gboolean
2370 gst_video_aggregator_pad_sink_acceptcaps (GstPad * pad,
2371     GstVideoAggregator * vagg, GstCaps * caps)
2372 {
2373   gboolean ret;
2374   GstCaps *modified_caps;
2375   GstCaps *accepted_caps;
2376   GstCaps *template_caps;
2377   gboolean had_current_caps = TRUE;
2378   gint i, n;
2379   GstStructure *s;
2380   GstAggregator *agg = GST_AGGREGATOR (vagg);
2381
2382   GST_DEBUG_OBJECT (pad, "%" GST_PTR_FORMAT, caps);
2383
2384   accepted_caps = gst_pad_get_current_caps (GST_PAD (agg->srcpad));
2385
2386   template_caps = gst_pad_get_pad_template_caps (GST_PAD (agg->srcpad));
2387
2388   if (accepted_caps == NULL) {
2389     accepted_caps = template_caps;
2390     had_current_caps = FALSE;
2391   }
2392
2393   accepted_caps = gst_caps_make_writable (accepted_caps);
2394
2395   GST_LOG_OBJECT (pad, "src caps %" GST_PTR_FORMAT, accepted_caps);
2396
2397   n = gst_caps_get_size (accepted_caps);
2398   for (i = 0; i < n; i++) {
2399     s = gst_caps_get_structure (accepted_caps, i);
2400     gst_structure_set (s, "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
2401         "height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
2402         "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
2403
2404     gst_structure_remove_fields (s, "colorimetry", "chroma-site", "format",
2405         "pixel-aspect-ratio", NULL);
2406   }
2407
2408   modified_caps = gst_caps_intersect (accepted_caps, template_caps);
2409
2410   ret = gst_caps_can_intersect (caps, accepted_caps);
2411   GST_DEBUG_OBJECT (pad, "%saccepted caps %" GST_PTR_FORMAT,
2412       (ret ? "" : "not "), caps);
2413   gst_caps_unref (accepted_caps);
2414   gst_caps_unref (modified_caps);
2415   if (had_current_caps)
2416     gst_caps_unref (template_caps);
2417   return ret;
2418 }
2419
2420 static gboolean
2421 gst_video_aggregator_sink_query (GstAggregator * agg, GstAggregatorPad * bpad,
2422     GstQuery * query)
2423 {
2424   GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (agg);
2425   GstVideoAggregatorPad *pad = GST_VIDEO_AGGREGATOR_PAD (bpad);
2426   gboolean ret = FALSE;
2427
2428   switch (GST_QUERY_TYPE (query)) {
2429     case GST_QUERY_CAPS:
2430     {
2431       GstCaps *filter, *caps;
2432
2433       gst_query_parse_caps (query, &filter);
2434       caps =
2435           gst_video_aggregator_pad_sink_getcaps (GST_PAD (pad), vagg, filter);
2436       gst_query_set_caps_result (query, caps);
2437       gst_caps_unref (caps);
2438       ret = TRUE;
2439       break;
2440     }
2441     case GST_QUERY_ACCEPT_CAPS:
2442     {
2443       GstCaps *caps;
2444
2445       gst_query_parse_accept_caps (query, &caps);
2446       ret =
2447           gst_video_aggregator_pad_sink_acceptcaps (GST_PAD (pad), vagg, caps);
2448       gst_query_set_accept_caps_result (query, ret);
2449       ret = TRUE;
2450       break;
2451     }
2452     default:
2453       ret =
2454           GST_AGGREGATOR_CLASS (gst_video_aggregator_parent_class)->sink_query
2455           (agg, bpad, query);
2456       break;
2457   }
2458   return ret;
2459 }
2460
2461 /* GObject vmethods */
2462 static void
2463 gst_video_aggregator_finalize (GObject * o)
2464 {
2465   GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (o);
2466
2467   g_mutex_clear (&vagg->priv->lock);
2468
2469   G_OBJECT_CLASS (gst_video_aggregator_parent_class)->finalize (o);
2470 }
2471
2472 static void
2473 gst_video_aggregator_dispose (GObject * o)
2474 {
2475   GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (o);
2476
2477   gst_caps_replace (&vagg->priv->current_caps, NULL);
2478
2479   G_OBJECT_CLASS (gst_video_aggregator_parent_class)->dispose (o);
2480 }
2481
2482 static void
2483 gst_video_aggregator_get_property (GObject * object,
2484     guint prop_id, GValue * value, GParamSpec * pspec)
2485 {
2486   switch (prop_id) {
2487     default:
2488       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2489       break;
2490   }
2491 }
2492
2493 static void
2494 gst_video_aggregator_set_property (GObject * object,
2495     guint prop_id, const GValue * value, GParamSpec * pspec)
2496 {
2497   switch (prop_id) {
2498     default:
2499       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2500       break;
2501   }
2502 }
2503
2504 /* GObject boilerplate */
2505 static void
2506 gst_video_aggregator_class_init (GstVideoAggregatorClass * klass)
2507 {
2508   GObjectClass *gobject_class = (GObjectClass *) klass;
2509   GstElementClass *gstelement_class = (GstElementClass *) klass;
2510   GstAggregatorClass *agg_class = (GstAggregatorClass *) klass;
2511
2512   GST_DEBUG_CATEGORY_INIT (gst_video_aggregator_debug, "videoaggregator", 0,
2513       "base video aggregator");
2514
2515   gst_video_aggregator_parent_class = g_type_class_peek_parent (klass);
2516
2517   g_type_class_add_private (klass, sizeof (GstVideoAggregatorPrivate));
2518
2519   gobject_class->finalize = gst_video_aggregator_finalize;
2520   gobject_class->dispose = gst_video_aggregator_dispose;
2521
2522   gobject_class->get_property = gst_video_aggregator_get_property;
2523   gobject_class->set_property = gst_video_aggregator_set_property;
2524
2525   gstelement_class->request_new_pad =
2526       GST_DEBUG_FUNCPTR (gst_video_aggregator_request_new_pad);
2527   gstelement_class->release_pad =
2528       GST_DEBUG_FUNCPTR (gst_video_aggregator_release_pad);
2529
2530   agg_class->start = gst_video_aggregator_start;
2531   agg_class->stop = gst_video_aggregator_stop;
2532   agg_class->sink_query = gst_video_aggregator_sink_query;
2533   agg_class->sink_event = gst_video_aggregator_sink_event;
2534   agg_class->flush = gst_video_aggregator_flush;
2535   agg_class->aggregate = gst_video_aggregator_aggregate;
2536   agg_class->src_event = gst_video_aggregator_src_event;
2537   agg_class->src_query = gst_video_aggregator_src_query;
2538   agg_class->get_next_time = gst_aggregator_simple_get_next_time;
2539   agg_class->update_src_caps = gst_video_aggregator_default_update_src_caps;
2540   agg_class->fixate_src_caps = gst_video_aggregator_default_fixate_src_caps;
2541   agg_class->negotiated_src_caps =
2542       gst_video_aggregator_default_negotiated_src_caps;
2543   agg_class->decide_allocation = gst_video_aggregator_decide_allocation;
2544   agg_class->propose_allocation = gst_video_aggregator_propose_allocation;
2545
2546   klass->find_best_format = gst_video_aggregator_find_best_format;
2547   klass->create_output_buffer = gst_video_aggregator_create_output_buffer;
2548   klass->update_caps = gst_video_aggregator_default_update_caps;
2549
2550   /* Register the pad class */
2551   g_type_class_ref (GST_TYPE_VIDEO_AGGREGATOR_PAD);
2552 }
2553
2554 static void
2555 gst_video_aggregator_init (GstVideoAggregator * vagg,
2556     GstVideoAggregatorClass * klass)
2557 {
2558   vagg->priv =
2559       G_TYPE_INSTANCE_GET_PRIVATE (vagg, GST_TYPE_VIDEO_AGGREGATOR,
2560       GstVideoAggregatorPrivate);
2561
2562   vagg->priv->current_caps = NULL;
2563
2564   g_mutex_init (&vagg->priv->lock);
2565
2566   /* initialize variables */
2567   gst_video_aggregator_reset (vagg);
2568 }