2
* Copyright (C) 2009 Sebastian Dröge <sebastian.droege@collabora.co.uk>
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.
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.
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., 59 Temple Place - Suite 330,
17
* Boston, MA 02111-1307, USA.
26
#include "gstfrei0r.h"
27
#include "gstfrei0rmixer.h"
29
GST_DEBUG_CATEGORY_EXTERN (frei0r_debug);
30
#define GST_CAT_DEFAULT frei0r_debug
34
f0r_plugin_info_t info;
35
GstFrei0rFuncTable ftable;
36
} GstFrei0rMixerClassData;
39
gst_frei0r_mixer_reset (GstFrei0rMixer * self)
41
GstFrei0rMixerClass *klass = GST_FREI0R_MIXER_GET_CLASS (self);
43
if (self->f0r_instance) {
44
klass->ftable->destruct (self->f0r_instance);
45
self->f0r_instance = NULL;
48
if (self->property_cache)
49
gst_frei0r_property_cache_free (klass->properties, self->property_cache,
51
self->property_cache = NULL;
53
gst_caps_replace (&self->caps, NULL);
54
gst_event_replace (&self->newseg_event, NULL);
56
self->fmt = GST_VIDEO_FORMAT_UNKNOWN;
57
self->width = self->height = 0;
61
gst_frei0r_mixer_finalize (GObject * object)
63
GstFrei0rMixer *self = GST_FREI0R_MIXER (object);
64
GstFrei0rMixerClass *klass = GST_FREI0R_MIXER_GET_CLASS (object);
66
if (self->property_cache)
67
gst_frei0r_property_cache_free (klass->properties, self->property_cache,
69
self->property_cache = NULL;
72
gst_object_unref (self->collect);
75
G_OBJECT_CLASS (g_type_class_peek_parent (klass))->finalize (object);
79
gst_frei0r_mixer_get_property (GObject * object, guint prop_id, GValue * value,
82
GstFrei0rMixer *self = GST_FREI0R_MIXER (object);
83
GstFrei0rMixerClass *klass = GST_FREI0R_MIXER_GET_CLASS (object);
85
if (!gst_frei0r_get_property (self->f0r_instance, klass->ftable,
86
klass->properties, klass->n_properties, self->property_cache, prop_id,
88
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
92
gst_frei0r_mixer_set_property (GObject * object, guint prop_id,
93
const GValue * value, GParamSpec * pspec)
95
GstFrei0rMixer *self = GST_FREI0R_MIXER (object);
96
GstFrei0rMixerClass *klass = GST_FREI0R_MIXER_GET_CLASS (object);
98
if (!gst_frei0r_set_property (self->f0r_instance, klass->ftable,
99
klass->properties, klass->n_properties, self->property_cache, prop_id,
101
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
104
static GstStateChangeReturn
105
gst_frei0r_mixer_change_state (GstElement * element, GstStateChange transition)
107
GstFrei0rMixer *self = GST_FREI0R_MIXER (element);
108
GstFrei0rMixerClass *klass = GST_FREI0R_MIXER_GET_CLASS (self);
109
GstStateChangeReturn ret;
111
switch (transition) {
112
case GST_STATE_CHANGE_NULL_TO_READY:
114
case GST_STATE_CHANGE_READY_TO_PAUSED:
115
gst_collect_pads_start (self->collect);
117
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
123
/* Stop before calling the parent's state change function as
124
* GstCollectPads might take locks and we would deadlock in that
127
if (transition == GST_STATE_CHANGE_PAUSED_TO_READY)
128
gst_collect_pads_stop (self->collect);
131
GST_ELEMENT_CLASS (g_type_class_peek_parent (klass))->change_state
132
(element, transition);
134
switch (transition) {
135
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
137
case GST_STATE_CHANGE_PAUSED_TO_READY:
138
gst_frei0r_mixer_reset (self);
140
case GST_STATE_CHANGE_READY_TO_NULL:
150
gst_frei0r_mixer_get_caps (GstPad * pad)
152
GstFrei0rMixer *self = GST_FREI0R_MIXER (gst_pad_get_parent (pad));
153
GstCaps *caps = NULL;
156
caps = gst_caps_ref (self->caps);
160
tmp = gst_caps_copy (gst_pad_get_pad_template_caps (self->src));
161
tmp1 = gst_pad_peer_get_caps (pad);
163
caps = gst_caps_intersect (tmp, tmp1);
164
gst_caps_unref (tmp1);
165
gst_caps_unref (tmp);
171
tmp1 = gst_pad_peer_get_caps (self->sink0);
173
caps = gst_caps_intersect (tmp, tmp1);
174
gst_caps_unref (tmp);
175
gst_caps_unref (tmp1);
179
tmp1 = gst_pad_peer_get_caps (self->sink1);
181
caps = gst_caps_intersect (tmp, tmp1);
182
gst_caps_unref (tmp);
183
gst_caps_unref (tmp1);
188
tmp1 = gst_pad_peer_get_caps (self->sink2);
190
caps = gst_caps_intersect (tmp, tmp1);
191
gst_caps_unref (tmp);
192
gst_caps_unref (tmp1);
197
gst_object_unref (self);
203
gst_frei0r_mixer_set_caps (GstPad * pad, GstCaps * caps)
205
GstFrei0rMixer *self = GST_FREI0R_MIXER (gst_pad_get_parent (pad));
208
gst_caps_replace (&self->caps, caps);
210
if (pad != self->src)
211
ret &= gst_pad_set_caps (self->src, caps);
212
if (pad != self->sink0)
213
ret &= gst_pad_set_caps (self->sink0, caps);
214
if (pad != self->sink1)
215
ret &= gst_pad_set_caps (self->sink1, caps);
216
if (pad != self->sink2 && self->sink2)
217
ret &= gst_pad_set_caps (self->sink2, caps);
220
if (!gst_video_format_parse_caps (caps, &self->fmt, &self->width,
228
gst_object_unref (self);
234
gst_frei0r_mixer_src_query_duration (GstFrei0rMixer * self, GstQuery * query)
243
gst_query_parse_duration (query, &format, NULL);
249
/* Take minimum of all durations */
250
it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (self));
252
GstIteratorResult ires;
255
ires = gst_iterator_next (it, &item);
257
case GST_ITERATOR_DONE:
260
case GST_ITERATOR_OK:
262
GstPad *pad = GST_PAD_CAST (item);
265
/* ask sink peer for duration */
266
res &= gst_pad_query_peer_duration (pad, &format, &duration);
267
/* take min from all valid return values */
269
/* valid unknown length, stop searching */
270
if (duration == -1) {
274
/* else see if smaller than current min */
275
else if (duration < min)
278
gst_object_unref (pad);
281
case GST_ITERATOR_RESYNC:
284
gst_iterator_resync (it);
292
gst_iterator_free (it);
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);
305
gst_frei0r_mixer_src_query_latency (GstFrei0rMixer * self, GstQuery * query)
307
GstClockTime min, max;
318
max = GST_CLOCK_TIME_NONE;
320
/* Take maximum of all latency values */
321
it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (self));
323
GstIteratorResult ires;
326
ires = gst_iterator_next (it, &item);
328
case GST_ITERATOR_DONE:
331
case GST_ITERATOR_OK:
333
GstPad *pad = GST_PAD_CAST (item);
335
GstClockTime min_cur, max_cur;
338
peerquery = gst_query_new_latency ();
340
/* Ask peer for latency */
341
res &= gst_pad_peer_query (pad, peerquery);
343
/* take max from all valid return values */
345
gst_query_parse_latency (peerquery, &live_cur, &min_cur, &max_cur);
350
if (max_cur != GST_CLOCK_TIME_NONE &&
351
((max != GST_CLOCK_TIME_NONE && max_cur > max) ||
352
(max == GST_CLOCK_TIME_NONE)))
355
live = live || live_cur;
358
gst_query_unref (peerquery);
359
gst_object_unref (pad);
362
case GST_ITERATOR_RESYNC:
365
max = GST_CLOCK_TIME_NONE;
367
gst_iterator_resync (it);
375
gst_iterator_free (it);
378
/* store the results */
379
GST_DEBUG_OBJECT (self, "Calculated total latency: live %s, min %"
380
GST_TIME_FORMAT ", max %" GST_TIME_FORMAT,
381
(live ? "yes" : "no"), GST_TIME_ARGS (min), GST_TIME_ARGS (max));
382
gst_query_set_latency (query, live, min, max);
389
gst_frei0r_mixer_src_query (GstPad * pad, GstQuery * query)
391
GstFrei0rMixer *self = GST_FREI0R_MIXER (gst_pad_get_parent (pad));
392
gboolean ret = FALSE;
394
switch (GST_QUERY_TYPE (query)) {
395
case GST_QUERY_POSITION:
396
ret = gst_pad_query (self->sink0, query);
398
case GST_QUERY_DURATION:
399
ret = gst_frei0r_mixer_src_query_duration (self, query);
401
case GST_QUERY_LATENCY:
402
ret = gst_frei0r_mixer_src_query_latency (self, query);
405
ret = gst_pad_query_default (pad, query);
409
gst_object_unref (self);
415
gst_frei0r_mixer_sink_query (GstPad * pad, GstQuery * query)
417
GstFrei0rMixer *self = GST_FREI0R_MIXER (gst_pad_get_parent (pad));
418
gboolean ret = gst_pad_query (self->src, query);
420
gst_object_unref (self);
426
forward_event_func (GstPad * pad, GValue * ret, GstEvent * event)
428
gst_event_ref (event);
429
GST_LOG_OBJECT (pad, "About to send event %s", GST_EVENT_TYPE_NAME (event));
430
if (!gst_pad_push_event (pad, event)) {
431
g_value_set_boolean (ret, FALSE);
432
GST_WARNING_OBJECT (pad, "Sending event %p (%s) failed.",
433
event, GST_EVENT_TYPE_NAME (event));
435
GST_LOG_OBJECT (pad, "Sent event %p (%s).",
436
event, GST_EVENT_TYPE_NAME (event));
438
gst_object_unref (pad);
443
forward_event (GstFrei0rMixer * self, GstEvent * event)
448
GST_LOG_OBJECT (self, "Forwarding event %p (%s)", event,
449
GST_EVENT_TYPE_NAME (event));
451
g_value_init (&vret, G_TYPE_BOOLEAN);
452
g_value_set_boolean (&vret, TRUE);
453
it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (self));
454
gst_iterator_fold (it, (GstIteratorFoldFunction) forward_event_func, &vret,
456
gst_iterator_free (it);
457
gst_event_unref (event);
459
return g_value_get_boolean (&vret);
463
gst_frei0r_mixer_src_event (GstPad * pad, GstEvent * event)
465
GstFrei0rMixer *self = GST_FREI0R_MIXER (gst_pad_get_parent (pad));
466
gboolean ret = FALSE;
468
switch (GST_EVENT_TYPE (event)) {
470
/* QoS might be tricky */
477
/* parse the seek parameters */
478
gst_event_parse_seek (event, NULL, NULL, &flags, NULL, NULL, NULL, NULL);
480
/* check if we are flushing */
481
if (flags & GST_SEEK_FLAG_FLUSH) {
482
/* make sure we accept nothing anymore and return WRONG_STATE */
483
gst_collect_pads_set_flushing (self->collect, TRUE);
485
/* flushing seek, start flush downstream, the flush will be done
486
* when all pads received a FLUSH_STOP. */
487
gst_pad_push_event (self->src, gst_event_new_flush_start ());
490
ret = forward_event (self, event);
493
case GST_EVENT_NAVIGATION:
494
/* navigation is rather pointless. */
498
/* just forward the rest for now */
499
ret = forward_event (self, event);
503
gst_object_unref (self);
509
gst_frei0r_mixer_sink0_event (GstPad * pad, GstEvent * event)
511
GstFrei0rMixer *self = GST_FREI0R_MIXER (gst_pad_get_parent (pad));
512
gboolean ret = FALSE;
514
GST_DEBUG ("Got %s event on pad %s:%s", GST_EVENT_TYPE_NAME (event),
515
GST_DEBUG_PAD_NAME (pad));
517
switch (GST_EVENT_TYPE (event)) {
518
case GST_EVENT_NEWSEGMENT:
519
gst_event_replace (&self->newseg_event, event);
525
/* now GstCollectPads can take care of the rest, e.g. EOS */
526
ret = self->collect_event (pad, event);
528
gst_object_unref (self);
534
gst_frei0r_mixer_collected (GstCollectPads * pads, GstFrei0rMixer * self)
536
GstBuffer *inbuf0 = NULL, *inbuf1 = NULL, *inbuf2 = NULL;
537
GstBuffer *outbuf = NULL;
538
GstFlowReturn ret = GST_FLOW_OK;
540
GstFrei0rMixerClass *klass = GST_FREI0R_MIXER_GET_CLASS (self);
543
if (G_UNLIKELY (self->width <= 0 || self->height <= 0))
544
return GST_FLOW_NOT_NEGOTIATED;
546
if (G_UNLIKELY (!self->f0r_instance)) {
548
gst_frei0r_instance_construct (klass->ftable, klass->properties,
549
klass->n_properties, self->property_cache, self->width, self->height);
550
if (G_UNLIKELY (!self->f0r_instance))
551
return GST_FLOW_ERROR;
554
if (self->newseg_event) {
555
gst_pad_push_event (self->src, self->newseg_event);
556
self->newseg_event = NULL;
560
gst_pad_alloc_buffer_and_set_caps (self->src, GST_BUFFER_OFFSET_NONE,
561
gst_video_format_get_size (self->fmt, self->width, self->height),
562
GST_PAD_CAPS (self->src), &outbuf)) != GST_FLOW_OK)
565
for (l = pads->data; l; l = l->next) {
566
GstCollectData *cdata = l->data;
568
if (cdata->pad == self->sink0)
569
inbuf0 = gst_collect_pads_pop (pads, cdata);
570
else if (cdata->pad == self->sink1)
571
inbuf1 = gst_collect_pads_pop (pads, cdata);
572
else if (cdata->pad == self->sink2)
573
inbuf2 = gst_collect_pads_pop (pads, cdata);
576
if (!inbuf0 || !inbuf1 || (!inbuf2 && self->sink2))
579
gst_buffer_copy_metadata (outbuf, inbuf0,
580
GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS);
581
time = ((gdouble) GST_BUFFER_TIMESTAMP (outbuf)) / GST_SECOND;
583
klass->ftable->update2 (self->f0r_instance, time,
584
(const guint32 *) GST_BUFFER_DATA (inbuf0),
585
(const guint32 *) GST_BUFFER_DATA (inbuf1),
586
(inbuf2) ? (const guint32 *) GST_BUFFER_DATA (inbuf2) : NULL,
587
(guint32 *) GST_BUFFER_DATA (outbuf));
589
gst_buffer_unref (inbuf0);
590
gst_buffer_unref (inbuf1);
592
gst_buffer_unref (inbuf2);
594
ret = gst_pad_push (self->src, outbuf);
600
GST_DEBUG_OBJECT (self, "no data available, must be EOS");
601
gst_buffer_unref (outbuf);
604
gst_buffer_unref (inbuf0);
606
gst_buffer_unref (inbuf1);
608
gst_buffer_unref (inbuf2);
610
gst_pad_push_event (self->src, gst_event_new_eos ());
611
return GST_FLOW_UNEXPECTED;
616
gst_frei0r_mixer_class_init (GstFrei0rMixerClass * klass,
617
GstFrei0rMixerClassData * class_data)
619
GObjectClass *gobject_class = (GObjectClass *) klass;
620
GstElementClass *gstelement_class = (GstElementClass *) klass;
621
GstPadTemplate *templ;
625
klass->ftable = &class_data->ftable;
626
klass->info = &class_data->info;
628
gobject_class->finalize = gst_frei0r_mixer_finalize;
629
gobject_class->set_property = gst_frei0r_mixer_set_property;
630
gobject_class->get_property = gst_frei0r_mixer_get_property;
632
klass->n_properties = klass->info->num_params;
633
klass->properties = g_new0 (GstFrei0rProperty, klass->n_properties);
635
gst_frei0r_klass_install_properties (gobject_class, klass->ftable,
636
klass->properties, klass->n_properties);
640
("Sebastian Dröge <sebastian.droege@collabora.co.uk>, %s",
641
class_data->info.author);
642
gst_element_class_set_details_simple (gstelement_class, class_data->info.name,
643
"Filter/Editor/Video", class_data->info.explanation, author);
646
caps = gst_frei0r_caps_from_color_model (class_data->info.color_model);
649
gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
650
gst_caps_ref (caps));
651
gst_element_class_add_pad_template (gstelement_class, templ);
654
gst_pad_template_new ("sink_0", GST_PAD_SINK, GST_PAD_ALWAYS,
655
gst_caps_ref (caps));
656
gst_element_class_add_pad_template (gstelement_class, templ);
659
gst_pad_template_new ("sink_1", GST_PAD_SINK, GST_PAD_ALWAYS,
660
gst_caps_ref (caps));
661
gst_element_class_add_pad_template (gstelement_class, templ);
663
if (klass->info->plugin_type == F0R_PLUGIN_TYPE_MIXER3) {
665
gst_pad_template_new ("sink_2", GST_PAD_SINK, GST_PAD_ALWAYS,
666
gst_caps_ref (caps));
667
gst_element_class_add_pad_template (gstelement_class, templ);
669
gst_caps_unref (caps);
671
gstelement_class->change_state =
672
GST_DEBUG_FUNCPTR (gst_frei0r_mixer_change_state);
676
gst_frei0r_mixer_init (GstFrei0rMixer * self, GstFrei0rMixerClass * klass)
678
self->property_cache =
679
gst_frei0r_property_cache_init (klass->properties, klass->n_properties);
681
self->collect = gst_collect_pads_new ();
682
gst_collect_pads_set_function (self->collect,
683
(GstCollectPadsFunction) gst_frei0r_mixer_collected, self);
686
gst_pad_new_from_template (gst_element_class_get_pad_template
687
(GST_ELEMENT_CLASS (klass), "src"), "src");
688
gst_pad_set_getcaps_function (self->src,
689
GST_DEBUG_FUNCPTR (gst_frei0r_mixer_get_caps));
690
gst_pad_set_setcaps_function (self->src,
691
GST_DEBUG_FUNCPTR (gst_frei0r_mixer_set_caps));
692
gst_pad_set_query_function (self->src,
693
GST_DEBUG_FUNCPTR (gst_frei0r_mixer_src_query));
694
gst_pad_set_event_function (self->src,
695
GST_DEBUG_FUNCPTR (gst_frei0r_mixer_src_event));
696
gst_element_add_pad (GST_ELEMENT_CAST (self), self->src);
699
gst_pad_new_from_template (gst_element_class_get_pad_template
700
(GST_ELEMENT_CLASS (klass), "sink_0"), "sink_0");
701
gst_pad_set_getcaps_function (self->sink0,
702
GST_DEBUG_FUNCPTR (gst_frei0r_mixer_get_caps));
703
gst_pad_set_setcaps_function (self->sink0,
704
GST_DEBUG_FUNCPTR (gst_frei0r_mixer_set_caps));
705
gst_pad_set_query_function (self->sink0,
706
GST_DEBUG_FUNCPTR (gst_frei0r_mixer_sink_query));
707
gst_collect_pads_add_pad (self->collect, self->sink0,
708
sizeof (GstCollectData));
709
self->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (self->sink0);
710
gst_pad_set_event_function (self->sink0,
711
GST_DEBUG_FUNCPTR (gst_frei0r_mixer_sink0_event));
712
gst_element_add_pad (GST_ELEMENT_CAST (self), self->sink0);
715
gst_pad_new_from_template (gst_element_class_get_pad_template
716
(GST_ELEMENT_CLASS (klass), "sink_1"), "sink_1");
717
gst_pad_set_getcaps_function (self->sink1,
718
GST_DEBUG_FUNCPTR (gst_frei0r_mixer_get_caps));
719
gst_pad_set_setcaps_function (self->sink1,
720
GST_DEBUG_FUNCPTR (gst_frei0r_mixer_set_caps));
721
gst_pad_set_query_function (self->sink0,
722
GST_DEBUG_FUNCPTR (gst_frei0r_mixer_sink_query));
723
gst_collect_pads_add_pad (self->collect, self->sink1,
724
sizeof (GstCollectData));
725
gst_element_add_pad (GST_ELEMENT_CAST (self), self->sink1);
727
if (klass->info->plugin_type == F0R_PLUGIN_TYPE_MIXER3) {
729
gst_pad_new_from_template (gst_element_class_get_pad_template
730
(GST_ELEMENT_CLASS (klass), "sink_2"), "sink_2");
731
gst_pad_set_getcaps_function (self->sink2,
732
GST_DEBUG_FUNCPTR (gst_frei0r_mixer_get_caps));
733
gst_pad_set_setcaps_function (self->sink2,
734
GST_DEBUG_FUNCPTR (gst_frei0r_mixer_set_caps));
735
gst_pad_set_query_function (self->sink0,
736
GST_DEBUG_FUNCPTR (gst_frei0r_mixer_sink_query));
737
gst_collect_pads_add_pad (self->collect, self->sink2,
738
sizeof (GstCollectData));
739
gst_element_add_pad (GST_ELEMENT_CAST (self), self->sink2);
745
gst_frei0r_mixer_register (GstPlugin * plugin, const f0r_plugin_info_t * info,
746
const GstFrei0rFuncTable * ftable)
748
GTypeInfo typeinfo = {
749
sizeof (GstFrei0rMixerClass),
752
(GClassInitFunc) gst_frei0r_mixer_class_init,
755
sizeof (GstFrei0rMixer),
757
(GInstanceInitFunc) gst_frei0r_mixer_init
760
gchar *type_name, *tmp;
761
GstFrei0rMixerClassData *class_data;
762
gboolean ret = FALSE;
764
if (ftable->update2 == NULL)
767
tmp = g_strdup_printf ("frei0r-mixer-%s", info->name);
768
type_name = g_ascii_strdown (tmp, -1);
770
g_strcanon (type_name, G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-+", '-');
772
if (g_type_from_name (type_name)) {
773
GST_WARNING ("Type '%s' already exists", type_name);
777
class_data = g_new0 (GstFrei0rMixerClassData, 1);
778
memcpy (&class_data->info, info, sizeof (f0r_plugin_info_t));
779
memcpy (&class_data->ftable, ftable, sizeof (GstFrei0rFuncTable));
780
typeinfo.class_data = class_data;
782
type = g_type_register_static (GST_TYPE_ELEMENT, type_name, &typeinfo, 0);
783
ret = gst_element_register (plugin, type_name, GST_RANK_NONE, type);