1 /* GStreamer
2  * Copyright (C) 2009 Sebastian Dröge <sebastian.droege@collabora.co.uk>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23 
24 #include <string.h>
25 
26 #include "gstfrei0r.h"
27 #include "gstfrei0rmixer.h"
28 
29 GST_DEBUG_CATEGORY_EXTERN (frei0r_debug);
30 #define GST_CAT_DEFAULT frei0r_debug
31 
32 typedef struct
33 {
34   f0r_plugin_info_t info;
35   GstFrei0rFuncTable ftable;
36 } GstFrei0rMixerClassData;
37 
38 static void
gst_frei0r_mixer_reset(GstFrei0rMixer * self)39 gst_frei0r_mixer_reset (GstFrei0rMixer * self)
40 {
41   GstFrei0rMixerClass *klass = GST_FREI0R_MIXER_GET_CLASS (self);
42   GstEvent **p_ev;
43 
44   if (self->f0r_instance) {
45     klass->ftable->destruct (self->f0r_instance);
46     self->f0r_instance = NULL;
47   }
48 
49   if (self->property_cache)
50     gst_frei0r_property_cache_free (klass->properties, self->property_cache,
51         klass->n_properties);
52   self->property_cache = NULL;
53 
54   gst_caps_replace (&self->caps, NULL);
55   p_ev = &self->segment_event;
56   gst_event_replace (p_ev, NULL);
57 
58   gst_video_info_init (&self->info);
59 }
60 
61 static void
gst_frei0r_mixer_finalize(GObject * object)62 gst_frei0r_mixer_finalize (GObject * object)
63 {
64   GstFrei0rMixer *self = GST_FREI0R_MIXER (object);
65   GstFrei0rMixerClass *klass = GST_FREI0R_MIXER_GET_CLASS (object);
66 
67   if (self->property_cache)
68     gst_frei0r_property_cache_free (klass->properties, self->property_cache,
69         klass->n_properties);
70   self->property_cache = NULL;
71 
72   if (self->collect)
73     gst_object_unref (self->collect);
74   self->collect = NULL;
75 
76   G_OBJECT_CLASS (g_type_class_peek_parent (klass))->finalize (object);
77 }
78 
79 static void
gst_frei0r_mixer_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)80 gst_frei0r_mixer_get_property (GObject * object, guint prop_id, GValue * value,
81     GParamSpec * pspec)
82 {
83   GstFrei0rMixer *self = GST_FREI0R_MIXER (object);
84   GstFrei0rMixerClass *klass = GST_FREI0R_MIXER_GET_CLASS (object);
85 
86   GST_OBJECT_LOCK (self);
87   if (!gst_frei0r_get_property (self->f0r_instance, klass->ftable,
88           klass->properties, klass->n_properties, self->property_cache, prop_id,
89           value))
90     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
91   GST_OBJECT_UNLOCK (self);
92 }
93 
94 static void
gst_frei0r_mixer_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)95 gst_frei0r_mixer_set_property (GObject * object, guint prop_id,
96     const GValue * value, GParamSpec * pspec)
97 {
98   GstFrei0rMixer *self = GST_FREI0R_MIXER (object);
99   GstFrei0rMixerClass *klass = GST_FREI0R_MIXER_GET_CLASS (object);
100 
101   GST_OBJECT_LOCK (self);
102   if (!gst_frei0r_set_property (self->f0r_instance, klass->ftable,
103           klass->properties, klass->n_properties, self->property_cache, prop_id,
104           value))
105     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
106   GST_OBJECT_UNLOCK (self);
107 }
108 
109 static GstStateChangeReturn
gst_frei0r_mixer_change_state(GstElement * element,GstStateChange transition)110 gst_frei0r_mixer_change_state (GstElement * element, GstStateChange transition)
111 {
112   GstFrei0rMixer *self = GST_FREI0R_MIXER (element);
113   GstFrei0rMixerClass *klass = GST_FREI0R_MIXER_GET_CLASS (self);
114   GstStateChangeReturn ret;
115 
116   switch (transition) {
117     case GST_STATE_CHANGE_NULL_TO_READY:
118       break;
119     case GST_STATE_CHANGE_READY_TO_PAUSED:
120       gst_collect_pads_start (self->collect);
121       break;
122     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
123       break;
124     default:
125       break;
126   }
127 
128   /* Stop before calling the parent's state change function as
129    * GstCollectPads might take locks and we would deadlock in that
130    * case
131    */
132   if (transition == GST_STATE_CHANGE_PAUSED_TO_READY)
133     gst_collect_pads_stop (self->collect);
134 
135   ret =
136       GST_ELEMENT_CLASS (g_type_class_peek_parent (klass))->change_state
137       (element, transition);
138 
139   switch (transition) {
140     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
141       break;
142     case GST_STATE_CHANGE_PAUSED_TO_READY:
143       gst_frei0r_mixer_reset (self);
144       break;
145     case GST_STATE_CHANGE_READY_TO_NULL:
146       break;
147     default:
148       break;
149   }
150 
151   return ret;
152 }
153 
154 static GstCaps *
gst_frei0r_mixer_query_pad_caps(GstPad * pad,GstPad * skip,GstCaps * filter)155 gst_frei0r_mixer_query_pad_caps (GstPad * pad, GstPad * skip, GstCaps * filter)
156 {
157   GstCaps *caps;
158 
159   if (pad == skip)
160     return filter;
161 
162   caps = gst_pad_peer_query_caps (pad, filter);
163 
164   if (caps)
165     gst_caps_unref (filter);
166   else
167     caps = filter;
168 
169   return caps;
170 }
171 
172 static GstCaps *
gst_frei0r_mixer_get_caps(GstFrei0rMixer * self,GstPad * pad,GstCaps * filter)173 gst_frei0r_mixer_get_caps (GstFrei0rMixer * self, GstPad * pad,
174     GstCaps * filter)
175 {
176   GstCaps *caps = NULL;
177 
178   if (self->caps) {
179     caps = gst_caps_ref (self->caps);
180   } else {
181     GstCaps *tmp;
182 
183     caps = gst_pad_get_pad_template_caps (self->src);
184     if (filter) {
185       tmp = caps;
186       caps = gst_caps_intersect_full (tmp, filter, GST_CAPS_INTERSECT_FIRST);
187       gst_caps_unref (tmp);
188     }
189 
190     caps = gst_frei0r_mixer_query_pad_caps (self->src, pad, caps);
191     caps = gst_frei0r_mixer_query_pad_caps (self->sink0, pad, caps);
192     caps = gst_frei0r_mixer_query_pad_caps (self->sink1, pad, caps);
193     if (self->sink2)
194       caps = gst_frei0r_mixer_query_pad_caps (self->sink2, pad, caps);
195   }
196 
197   return caps;
198 }
199 
200 static gboolean
gst_frei0r_mixer_set_caps(GstFrei0rMixer * self,GstPad * pad,GstCaps * caps)201 gst_frei0r_mixer_set_caps (GstFrei0rMixer * self, GstPad * pad, GstCaps * caps)
202 {
203   gboolean ret = TRUE;
204 
205   if (!self->caps) {
206     gst_caps_replace (&self->caps, caps);
207 
208     ret = gst_pad_set_caps (self->src, caps);
209 
210     if (ret) {
211       GstVideoInfo info;
212 
213       gst_video_info_init (&info);
214       if (!gst_video_info_from_caps (&self->info, caps)) {
215         ret = FALSE;
216       }
217     }
218   } else if (!gst_caps_is_equal (caps, self->caps)) {
219     GstCaps *upstream_caps;
220 
221     upstream_caps = gst_pad_peer_query_caps (pad, NULL);
222     if (gst_caps_can_intersect (self->caps, upstream_caps))
223       gst_pad_push_event (pad, gst_event_new_reconfigure ());
224     gst_caps_unref (upstream_caps);
225     ret = FALSE;
226   }
227 
228   return ret;
229 }
230 
231 static gboolean
gst_frei0r_mixer_src_query_duration(GstFrei0rMixer * self,GstQuery * query)232 gst_frei0r_mixer_src_query_duration (GstFrei0rMixer * self, GstQuery * query)
233 {
234   gint64 min;
235   gboolean res;
236   GstFormat format;
237   GstIterator *it;
238   gboolean done;
239 
240   /* parse format */
241   gst_query_parse_duration (query, &format, NULL);
242 
243   min = -1;
244   res = TRUE;
245   done = FALSE;
246 
247   /* Take minimum of all durations */
248   it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (self));
249   while (!done) {
250     GstIteratorResult ires;
251     GValue item = { 0 };
252 
253     ires = gst_iterator_next (it, &item);
254     switch (ires) {
255       case GST_ITERATOR_DONE:
256         done = TRUE;
257         break;
258       case GST_ITERATOR_OK:
259       {
260         GstPad *pad = g_value_get_object (&item);
261         gint64 duration;
262 
263         /* ask sink peer for duration */
264         res &= gst_pad_peer_query_duration (pad, format, &duration);
265         /* take min from all valid return values */
266         if (res) {
267           /* valid unknown length, stop searching */
268           if (duration == -1) {
269             min = duration;
270             done = TRUE;
271           }
272           /* else see if smaller than current min */
273           else if (duration < min)
274             min = duration;
275         }
276         g_value_reset (&item);
277         break;
278       }
279       case GST_ITERATOR_RESYNC:
280         min = -1;
281         res = TRUE;
282         gst_iterator_resync (it);
283         break;
284       default:
285         res = FALSE;
286         done = TRUE;
287         break;
288     }
289 
290     g_value_unset (&item);
291   }
292   gst_iterator_free (it);
293 
294   if (res) {
295     /* and store the min */
296     GST_DEBUG_OBJECT (self, "Total duration in format %s: %"
297         GST_TIME_FORMAT, gst_format_get_name (format), GST_TIME_ARGS (min));
298     gst_query_set_duration (query, format, min);
299   }
300 
301   return res;
302 }
303 
304 static gboolean
gst_frei0r_mixer_src_query_latency(GstFrei0rMixer * self,GstQuery * query)305 gst_frei0r_mixer_src_query_latency (GstFrei0rMixer * self, GstQuery * query)
306 {
307   GstClockTime min, max;
308   gboolean live;
309   gboolean res;
310   GstIterator *it;
311   gboolean done;
312 
313   res = TRUE;
314   done = FALSE;
315 
316   live = FALSE;
317   min = 0;
318   max = GST_CLOCK_TIME_NONE;
319 
320   /* Take maximum of all latency values */
321   it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (self));
322   while (!done) {
323     GstIteratorResult ires;
324     GValue item = { 0 };
325 
326     ires = gst_iterator_next (it, &item);
327     switch (ires) {
328       case GST_ITERATOR_DONE:
329         done = TRUE;
330         break;
331       case GST_ITERATOR_OK:
332       {
333         GstPad *pad = g_value_get_object (&item);
334         GstQuery *peerquery;
335         GstClockTime min_cur, max_cur;
336         gboolean live_cur;
337 
338         peerquery = gst_query_new_latency ();
339 
340         /* Ask peer for latency */
341         res &= gst_pad_peer_query (pad, peerquery);
342 
343         /* take max from all valid return values */
344         if (res) {
345           gst_query_parse_latency (peerquery, &live_cur, &min_cur, &max_cur);
346 
347           if (live_cur) {
348             if (min_cur > min)
349               min = min_cur;
350 
351             if (max == GST_CLOCK_TIME_NONE)
352               max = max_cur;
353             else if (max_cur < max)
354               max = max_cur;
355 
356             live = TRUE;
357           }
358         }
359 
360         gst_query_unref (peerquery);
361         g_value_reset (&item);
362         break;
363       }
364       case GST_ITERATOR_RESYNC:
365         live = FALSE;
366         min = 0;
367         max = GST_CLOCK_TIME_NONE;
368         res = TRUE;
369         gst_iterator_resync (it);
370         break;
371       default:
372         res = FALSE;
373         done = TRUE;
374         break;
375     }
376 
377     g_value_unset (&item);
378   }
379   gst_iterator_free (it);
380 
381   if (res) {
382     /* store the results */
383     GST_DEBUG_OBJECT (self, "Calculated total latency: live %s, min %"
384         GST_TIME_FORMAT ", max %" GST_TIME_FORMAT,
385         (live ? "yes" : "no"), GST_TIME_ARGS (min), GST_TIME_ARGS (max));
386     gst_query_set_latency (query, live, min, max);
387   }
388 
389   return res;
390 }
391 
392 static gboolean
gst_frei0r_mixer_src_query(GstPad * pad,GstObject * object,GstQuery * query)393 gst_frei0r_mixer_src_query (GstPad * pad, GstObject * object, GstQuery * query)
394 {
395   GstFrei0rMixer *self = GST_FREI0R_MIXER (object);
396   gboolean ret = FALSE;
397 
398   switch (GST_QUERY_TYPE (query)) {
399     case GST_QUERY_POSITION:
400       ret = gst_pad_query (self->sink0, query);
401       break;
402     case GST_QUERY_DURATION:
403       ret = gst_frei0r_mixer_src_query_duration (self, query);
404       break;
405     case GST_QUERY_LATENCY:
406       ret = gst_frei0r_mixer_src_query_latency (self, query);
407       break;
408     case GST_QUERY_CAPS:
409     {
410       GstCaps *filter, *caps;
411       gst_query_parse_caps (query, &filter);
412       caps = gst_frei0r_mixer_get_caps (self, pad, filter);
413       gst_query_set_caps_result (query, caps);
414       gst_caps_unref (caps);
415       break;
416     }
417     default:
418       ret = gst_pad_query_default (pad, GST_OBJECT (self), query);
419       break;
420   }
421 
422   return ret;
423 }
424 
425 static gboolean
gst_frei0r_mixer_sink_query(GstCollectPads * pads,GstCollectData * cdata,GstQuery * query,GstFrei0rMixer * self)426 gst_frei0r_mixer_sink_query (GstCollectPads * pads, GstCollectData * cdata,
427     GstQuery * query, GstFrei0rMixer * self)
428 {
429   gboolean ret = TRUE;
430 
431   switch (GST_QUERY_TYPE (query)) {
432     case GST_QUERY_CAPS:
433     {
434       GstCaps *filter, *caps;
435       gst_query_parse_caps (query, &filter);
436       caps = gst_frei0r_mixer_get_caps (self, cdata->pad, filter);
437       gst_query_set_caps_result (query, caps);
438       gst_caps_unref (caps);
439       break;
440     }
441     default:
442       ret = gst_collect_pads_query_default (pads, cdata, query, FALSE);
443       break;
444   }
445   return ret;
446 }
447 
448 static gboolean
gst_frei0r_mixer_src_event(GstPad * pad,GstObject * object,GstEvent * event)449 gst_frei0r_mixer_src_event (GstPad * pad, GstObject * object, GstEvent * event)
450 {
451   GstFrei0rMixer *self = GST_FREI0R_MIXER (object);
452   gboolean ret = FALSE;
453 
454   switch (GST_EVENT_TYPE (event)) {
455     case GST_EVENT_QOS:
456       /* QoS might be tricky */
457       ret = FALSE;
458       break;
459     case GST_EVENT_SEEK:
460     {
461       GstSeekFlags flags;
462 
463       /* parse the seek parameters */
464       gst_event_parse_seek (event, NULL, NULL, &flags, NULL, NULL, NULL, NULL);
465 
466       /* check if we are flushing */
467       if (flags & GST_SEEK_FLAG_FLUSH) {
468         /* make sure we accept nothing anymore and return WRONG_STATE */
469         gst_collect_pads_set_flushing (self->collect, TRUE);
470 
471         /* flushing seek, start flush downstream, the flush will be done
472          * when all pads received a FLUSH_STOP. */
473         gst_pad_push_event (self->src, gst_event_new_flush_start ());
474       }
475 
476       ret = gst_pad_event_default (pad, GST_OBJECT (self), event);
477       break;
478     }
479     default:
480       ret = gst_pad_event_default (pad, GST_OBJECT (self), event);
481       break;
482   }
483 
484   return ret;
485 }
486 
487 static gboolean
gst_frei0r_mixer_sink_event(GstCollectPads * pads,GstCollectData * cdata,GstEvent * event,GstFrei0rMixer * self)488 gst_frei0r_mixer_sink_event (GstCollectPads * pads, GstCollectData * cdata,
489     GstEvent * event, GstFrei0rMixer * self)
490 {
491   gboolean ret = TRUE;
492 
493   switch (GST_EVENT_TYPE (event)) {
494     case GST_EVENT_CAPS:
495     {
496       GstCaps *caps;
497       gst_event_parse_caps (event, &caps);
498       ret = gst_frei0r_mixer_set_caps (self, cdata->pad, caps);
499       gst_event_unref (event);
500       break;
501     }
502     default:
503       ret = gst_collect_pads_event_default (pads, cdata, event, FALSE);
504       break;
505   }
506   return ret;
507 }
508 
509 static GstFlowReturn
gst_frei0r_mixer_collected(GstCollectPads * pads,GstFrei0rMixer * self)510 gst_frei0r_mixer_collected (GstCollectPads * pads, GstFrei0rMixer * self)
511 {
512   GstBuffer *inbuf0 = NULL, *inbuf1 = NULL, *inbuf2 = NULL;
513   GstBuffer *outbuf = NULL;
514   GstFlowReturn ret = GST_FLOW_OK;
515   GSList *l;
516   GstFrei0rMixerClass *klass = GST_FREI0R_MIXER_GET_CLASS (self);
517   GstClockTime timestamp;
518   gdouble time;
519   GstSegment *segment = NULL;
520   GstAllocationParams alloc_params = { 0, 31, 0, 0 };
521   GstMapInfo outmap, inmap0, inmap1, inmap2;
522 
523   if (G_UNLIKELY (self->info.width <= 0 || self->info.height <= 0))
524     return GST_FLOW_NOT_NEGOTIATED;
525 
526   if (G_UNLIKELY (!self->f0r_instance)) {
527     self->f0r_instance = gst_frei0r_instance_construct (klass->ftable,
528         klass->properties, klass->n_properties, self->property_cache,
529         self->info.width, self->info.height);
530     if (G_UNLIKELY (!self->f0r_instance))
531       return GST_FLOW_ERROR;
532   }
533 
534   if (self->segment_event) {
535     gst_pad_push_event (self->src, self->segment_event);
536     self->segment_event = NULL;
537   }
538 
539   /* FIXME Request an allocator and/or pool */
540   outbuf = gst_buffer_new_allocate (NULL, self->info.size, &alloc_params);
541 
542   for (l = pads->data; l; l = l->next) {
543     GstCollectData *cdata = l->data;
544 
545     if (cdata->pad == self->sink0) {
546       inbuf0 = gst_collect_pads_pop (pads, cdata);
547       segment = &cdata->segment;
548     } else if (cdata->pad == self->sink1) {
549       inbuf1 = gst_collect_pads_pop (pads, cdata);
550     } else if (cdata->pad == self->sink2) {
551       inbuf2 = gst_collect_pads_pop (pads, cdata);
552     }
553   }
554 
555   if (!inbuf0 || !inbuf1 || (!inbuf2 && self->sink2))
556     goto eos;
557 
558   gst_buffer_map (outbuf, &outmap, GST_MAP_READWRITE);
559   gst_buffer_map (inbuf0, &inmap0, GST_MAP_READ);
560   gst_buffer_map (inbuf1, &inmap1, GST_MAP_READ);
561   if (inbuf2)
562     gst_buffer_map (inbuf2, &inmap2, GST_MAP_READ);
563 
564   g_assert (segment != NULL);
565   timestamp = GST_BUFFER_PTS (inbuf0);
566   timestamp = gst_segment_to_stream_time (segment, GST_FORMAT_TIME, timestamp);
567 
568   GST_DEBUG_OBJECT (self, "sync to %" GST_TIME_FORMAT,
569       GST_TIME_ARGS (timestamp));
570 
571   if (GST_CLOCK_TIME_IS_VALID (timestamp))
572     gst_object_sync_values (GST_OBJECT (self), timestamp);
573 
574   gst_buffer_copy_into (outbuf, inbuf0,
575       GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS, 0, -1);
576   time = ((gdouble) GST_BUFFER_PTS (outbuf)) / GST_SECOND;
577 
578   GST_OBJECT_LOCK (self);
579   klass->ftable->update2 (self->f0r_instance, time,
580       (const guint32 *) inmap0.data, (const guint32 *) inmap1.data,
581       (inbuf2) ? (const guint32 *) inmap2.data : NULL, (guint32 *) outmap.data);
582   GST_OBJECT_UNLOCK (self);
583 
584   gst_buffer_unmap (outbuf, &outmap);
585   gst_buffer_unref (inbuf0);
586   gst_buffer_unmap (inbuf0, &inmap0);
587   gst_buffer_unref (inbuf1);
588   gst_buffer_unmap (inbuf1, &inmap1);
589   if (inbuf2) {
590     gst_buffer_unmap (inbuf2, &inmap2);
591     gst_buffer_unref (inbuf2);
592   }
593 
594   ret = gst_pad_push (self->src, outbuf);
595 
596   return ret;
597 
598 eos:
599   {
600     GST_DEBUG_OBJECT (self, "no data available, must be EOS");
601     gst_buffer_unref (outbuf);
602 
603     if (inbuf0)
604       gst_buffer_unref (inbuf0);
605     if (inbuf1)
606       gst_buffer_unref (inbuf1);
607     if (inbuf2)
608       gst_buffer_unref (inbuf2);
609 
610     gst_pad_push_event (self->src, gst_event_new_eos ());
611     return GST_FLOW_EOS;
612   }
613 }
614 
615 static void
gst_frei0r_mixer_class_init(GstFrei0rMixerClass * klass,GstFrei0rMixerClassData * class_data)616 gst_frei0r_mixer_class_init (GstFrei0rMixerClass * klass,
617     GstFrei0rMixerClassData * class_data)
618 {
619   GObjectClass *gobject_class = (GObjectClass *) klass;
620   GstElementClass *gstelement_class = (GstElementClass *) klass;
621   GstPadTemplate *templ;
622   const gchar *desc;
623   GstCaps *caps;
624   gchar *author;
625 
626   klass->ftable = &class_data->ftable;
627   klass->info = &class_data->info;
628 
629   gobject_class->finalize = gst_frei0r_mixer_finalize;
630   gobject_class->set_property = gst_frei0r_mixer_set_property;
631   gobject_class->get_property = gst_frei0r_mixer_get_property;
632 
633   klass->n_properties = klass->info->num_params;
634   klass->properties = g_new0 (GstFrei0rProperty, klass->n_properties);
635 
636   gst_frei0r_klass_install_properties (gobject_class, klass->ftable,
637       klass->properties, klass->n_properties);
638 
639   author =
640       g_strdup_printf
641       ("Sebastian Dröge <sebastian.droege@collabora.co.uk>, %s",
642       class_data->info.author);
643   desc = class_data->info.explanation;
644   if (desc == NULL || *desc == '\0')
645     desc = "No details";
646   gst_element_class_set_metadata (gstelement_class, class_data->info.name,
647       "Filter/Editor/Video", desc, author);
648   g_free (author);
649 
650   caps = gst_frei0r_caps_from_color_model (class_data->info.color_model);
651 
652   templ =
653       gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
654       gst_caps_ref (caps));
655   gst_element_class_add_pad_template (gstelement_class, templ);
656 
657   templ =
658       gst_pad_template_new ("sink_0", GST_PAD_SINK, GST_PAD_ALWAYS,
659       gst_caps_ref (caps));
660   gst_element_class_add_pad_template (gstelement_class, templ);
661 
662   templ =
663       gst_pad_template_new ("sink_1", GST_PAD_SINK, GST_PAD_ALWAYS,
664       gst_caps_ref (caps));
665   gst_element_class_add_pad_template (gstelement_class, templ);
666 
667   if (klass->info->plugin_type == F0R_PLUGIN_TYPE_MIXER3) {
668     templ =
669         gst_pad_template_new ("sink_2", GST_PAD_SINK, GST_PAD_ALWAYS,
670         gst_caps_ref (caps));
671     gst_element_class_add_pad_template (gstelement_class, templ);
672   }
673   gst_caps_unref (caps);
674 
675   gstelement_class->change_state =
676       GST_DEBUG_FUNCPTR (gst_frei0r_mixer_change_state);
677 }
678 
679 static void
gst_frei0r_mixer_init(GstFrei0rMixer * self,GstFrei0rMixerClass * klass)680 gst_frei0r_mixer_init (GstFrei0rMixer * self, GstFrei0rMixerClass * klass)
681 {
682   self->property_cache =
683       gst_frei0r_property_cache_init (klass->properties, klass->n_properties);
684   gst_video_info_init (&self->info);
685 
686   self->collect = gst_collect_pads_new ();
687   gst_collect_pads_set_function (self->collect,
688       (GstCollectPadsFunction) gst_frei0r_mixer_collected, self);
689   gst_collect_pads_set_event_function (self->collect,
690       (GstCollectPadsEventFunction) gst_frei0r_mixer_sink_event, self);
691   gst_collect_pads_set_query_function (self->collect,
692       (GstCollectPadsQueryFunction) gst_frei0r_mixer_sink_query, self);
693 
694   self->src =
695       gst_pad_new_from_template (gst_element_class_get_pad_template
696       (GST_ELEMENT_CLASS (klass), "src"), "src");
697   gst_pad_set_query_function (self->src,
698       GST_DEBUG_FUNCPTR (gst_frei0r_mixer_src_query));
699   gst_pad_set_event_function (self->src,
700       GST_DEBUG_FUNCPTR (gst_frei0r_mixer_src_event));
701   gst_element_add_pad (GST_ELEMENT_CAST (self), self->src);
702 
703   self->sink0 =
704       gst_pad_new_from_template (gst_element_class_get_pad_template
705       (GST_ELEMENT_CLASS (klass), "sink_0"), "sink_0");
706   gst_collect_pads_add_pad (self->collect, self->sink0,
707       sizeof (GstCollectData), NULL, TRUE);
708   self->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (self->sink0);
709   gst_element_add_pad (GST_ELEMENT_CAST (self), self->sink0);
710 
711   self->sink1 =
712       gst_pad_new_from_template (gst_element_class_get_pad_template
713       (GST_ELEMENT_CLASS (klass), "sink_1"), "sink_1");
714   gst_collect_pads_add_pad (self->collect, self->sink1,
715       sizeof (GstCollectData), NULL, TRUE);
716   gst_element_add_pad (GST_ELEMENT_CAST (self), self->sink1);
717 
718   if (klass->info->plugin_type == F0R_PLUGIN_TYPE_MIXER3) {
719     self->sink2 =
720         gst_pad_new_from_template (gst_element_class_get_pad_template
721         (GST_ELEMENT_CLASS (klass), "sink_2"), "sink_2");
722     gst_collect_pads_add_pad (self->collect, self->sink2,
723         sizeof (GstCollectData), NULL, TRUE);
724     gst_element_add_pad (GST_ELEMENT_CAST (self), self->sink2);
725   }
726 
727 }
728 
729 GstFrei0rPluginRegisterReturn
gst_frei0r_mixer_register(GstPlugin * plugin,const gchar * vendor,const f0r_plugin_info_t * info,const GstFrei0rFuncTable * ftable)730 gst_frei0r_mixer_register (GstPlugin * plugin, const gchar * vendor,
731     const f0r_plugin_info_t * info, const GstFrei0rFuncTable * ftable)
732 {
733   GTypeInfo typeinfo = {
734     sizeof (GstFrei0rMixerClass),
735     NULL,
736     NULL,
737     (GClassInitFunc) gst_frei0r_mixer_class_init,
738     NULL,
739     NULL,
740     sizeof (GstFrei0rMixer),
741     0,
742     (GInstanceInitFunc) gst_frei0r_mixer_init
743   };
744   GType type;
745   gchar *type_name, *tmp;
746   GstFrei0rMixerClassData *class_data;
747   GstFrei0rPluginRegisterReturn ret = GST_FREI0R_PLUGIN_REGISTER_RETURN_FAILED;
748 
749   if (ftable->update2 == NULL)
750     return GST_FREI0R_PLUGIN_REGISTER_RETURN_FAILED;
751 
752   if (vendor)
753     tmp = g_strdup_printf ("frei0r-mixer-%s-%s", vendor, info->name);
754   else
755     tmp = g_strdup_printf ("frei0r-mixer-%s", info->name);
756   type_name = g_ascii_strdown (tmp, -1);
757   g_free (tmp);
758   g_strcanon (type_name, G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-+", '-');
759 
760   if (g_type_from_name (type_name)) {
761     GST_DEBUG ("Type '%s' already exists", type_name);
762     return GST_FREI0R_PLUGIN_REGISTER_RETURN_ALREADY_REGISTERED;
763   }
764 
765   class_data = g_new0 (GstFrei0rMixerClassData, 1);
766   memcpy (&class_data->info, info, sizeof (f0r_plugin_info_t));
767   memcpy (&class_data->ftable, ftable, sizeof (GstFrei0rFuncTable));
768   typeinfo.class_data = class_data;
769 
770   type = g_type_register_static (GST_TYPE_ELEMENT, type_name, &typeinfo, 0);
771   if (gst_element_register (plugin, type_name, GST_RANK_NONE, type))
772     ret = GST_FREI0R_PLUGIN_REGISTER_RETURN_OK;
773 
774   g_free (type_name);
775   return ret;
776 }
777