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