1 /* Generic video mixer plugin
2 * Copyright (C) 2004 Wim Taymans <wim@fluendo.com>
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.
21 * SECTION:element-videomixer
23 * Videomixer can accept AYUV, ARGB and BGRA video streams. For each of the requested
24 * sink pads it will compare the incoming geometry and framerate to define the
25 * output parameters. Indeed output video frames will have the geometry of the
26 * biggest incoming video stream and the framerate of the fastest incoming one.
28 * All sink pads must be either AYUV, ARGB or BGRA, but a mixture of them is not
29 * supported. The src pad will have the same colorspace as the sinks.
30 * No colorspace conversion is done.
32 * Individual parameters for each input stream can be configured on the
36 * <title>Sample pipelines</title>
39 * videotestsrc pattern=1 ! \
40 * video/x-raw-yuv,format=\(fourcc\)AYUV,framerate=\(fraction\)10/1,width=100,height=100 ! \
41 * videobox border-alpha=0 top=-70 bottom=-70 right=-220 ! \
42 * videomixer name=mix sink_0::alpha=0.7 sink_1::alpha=0.5 ! \
43 * ffmpegcolorspace ! xvimagesink \
45 * video/x-raw-yuv,format=\(fourcc\)AYUV,framerate=\(fraction\)5/1,width=320,height=240 ! mix.
46 * ]| A pipeline to demonstrate videomixer used together with videobox.
47 * This should show a 320x240 pixels video test source with some transparency
48 * showing the background checker pattern. Another video test source with just
49 * the snow pattern of 100x100 pixels is overlayed on top of the first one on
50 * the left vertically centered with a small transparency showing the first
51 * video test source behind and the checker pattern under it. Note that the
52 * framerate of the output video is 10 frames per second.
54 * gst-launch videotestsrc pattern=1 ! \
55 * video/x-raw-rgb, framerate=\(fraction\)10/1, width=100, height=100 ! \
56 * videomixer name=mix ! ffmpegcolorspace ! ximagesink \
58 * video/x-raw-rgb, framerate=\(fraction\)5/1, width=320, height=240 ! mix.
59 * ]| A pipeline to demostrate bgra mixing. (This does not demonstrate alpha blending).
61 * gst-launch videotestsrc pattern=1 ! \
62 * video/x-raw-yuv,format =\(fourcc\)I420, framerate=\(fraction\)10/1, width=100, height=100 ! \
63 * videomixer name=mix ! ffmpegcolorspace ! ximagesink \
65 * video/x-raw-yuv,format=\(fourcc\)I420, framerate=\(fraction\)5/1, width=320, height=240 ! mix.
66 * ]| A pipeline to test I420
68 * gst-launch videotestsrc pattern="snow" ! video/x-raw-yuv, framerate=\(fraction\)10/1, width=200, height=150 ! videomixer name=mix sink_1::xpos=20 sink_1::ypos=20 sink_1::alpha=0.5 ! ffmpegcolorspace ! xvimagesink videotestsrc ! video/x-raw-yuv, framerate=\(fraction\)10/1, width=640, height=360 ! mix.
69 * ]| Set position and alpha on the mixer using #GstVideoMixerPad properties.
78 #include <gst/base/gstcollectpads.h>
79 #include <gst/controller/gstcontroller.h>
80 #include <gst/video/video.h>
89 #include "videomixer.h"
90 #include "videomixer2.h"
92 #include "gst/glib-compat-private.h"
95 #define orc_memset memset
97 #include <orc/orcfunctions.h>
100 GST_DEBUG_CATEGORY_STATIC (gst_videomixer_debug);
101 #define GST_CAT_DEFAULT gst_videomixer_debug
103 #define GST_VIDEO_MIXER_GET_STATE_LOCK(mix) \
104 (GST_VIDEO_MIXER(mix)->state_lock)
105 #define GST_VIDEO_MIXER_STATE_LOCK(mix) \
106 (g_mutex_lock(GST_VIDEO_MIXER_GET_STATE_LOCK (mix)))
107 #define GST_VIDEO_MIXER_STATE_UNLOCK(mix) \
108 (g_mutex_unlock(GST_VIDEO_MIXER_GET_STATE_LOCK (mix)))
110 static GType gst_videomixer_get_type (void);
112 static void gst_videomixer_pad_get_property (GObject * object, guint prop_id,
113 GValue * value, GParamSpec * pspec);
114 static void gst_videomixer_pad_set_property (GObject * object, guint prop_id,
115 const GValue * value, GParamSpec * pspec);
117 static gboolean gst_videomixer_src_event (GstPad * pad, GstEvent * event);
118 static gboolean gst_videomixer_sink_event (GstPad * pad, GstEvent * event);
120 static void gst_videomixer_sort_pads (GstVideoMixer * mix);
122 #define DEFAULT_PAD_ZORDER 0
123 #define DEFAULT_PAD_XPOS 0
124 #define DEFAULT_PAD_YPOS 0
125 #define DEFAULT_PAD_ALPHA 1.0
135 GType gst_videomixer_pad_get_type (void);
136 G_DEFINE_TYPE (GstVideoMixerPad, gst_videomixer_pad, GST_TYPE_PAD);
139 gst_videomixer_pad_class_init (GstVideoMixerPadClass * klass)
141 GObjectClass *gobject_class = (GObjectClass *) klass;
143 gobject_class->set_property = gst_videomixer_pad_set_property;
144 gobject_class->get_property = gst_videomixer_pad_get_property;
146 g_object_class_install_property (gobject_class, PROP_PAD_ZORDER,
147 g_param_spec_uint ("zorder", "Z-Order", "Z Order of the picture",
148 0, 10000, DEFAULT_PAD_ZORDER,
149 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
150 g_object_class_install_property (gobject_class, PROP_PAD_XPOS,
151 g_param_spec_int ("xpos", "X Position", "X Position of the picture",
152 G_MININT, G_MAXINT, DEFAULT_PAD_XPOS,
153 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
154 g_object_class_install_property (gobject_class, PROP_PAD_YPOS,
155 g_param_spec_int ("ypos", "Y Position", "Y Position of the picture",
156 G_MININT, G_MAXINT, DEFAULT_PAD_YPOS,
157 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
158 g_object_class_install_property (gobject_class, PROP_PAD_ALPHA,
159 g_param_spec_double ("alpha", "Alpha", "Alpha of the picture", 0.0, 1.0,
161 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
165 gst_videomixer_pad_get_property (GObject * object, guint prop_id,
166 GValue * value, GParamSpec * pspec)
168 GstVideoMixerPad *pad = GST_VIDEO_MIXER_PAD (object);
171 case PROP_PAD_ZORDER:
172 g_value_set_uint (value, pad->zorder);
175 g_value_set_int (value, pad->xpos);
178 g_value_set_int (value, pad->ypos);
181 g_value_set_double (value, pad->alpha);
184 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
190 gst_videomixer_pad_set_property (GObject * object, guint prop_id,
191 const GValue * value, GParamSpec * pspec)
193 GstVideoMixerPad *pad = GST_VIDEO_MIXER_PAD (object);
194 GstVideoMixer *mix = GST_VIDEO_MIXER (gst_pad_get_parent (GST_PAD (pad)));
197 case PROP_PAD_ZORDER:
198 GST_VIDEO_MIXER_STATE_LOCK (mix);
199 pad->zorder = g_value_get_uint (value);
200 gst_videomixer_sort_pads (mix);
201 GST_VIDEO_MIXER_STATE_UNLOCK (mix);
204 pad->xpos = g_value_get_int (value);
207 pad->ypos = g_value_get_int (value);
210 pad->alpha = g_value_get_double (value);
213 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
217 gst_object_unref (mix);
221 gst_videomixer_update_qos (GstVideoMixer * mix, gdouble proportion,
222 GstClockTimeDiff diff, GstClockTime timestamp)
224 GST_DEBUG_OBJECT (mix,
225 "Updating QoS: proportion %lf, diff %s%" GST_TIME_FORMAT ", timestamp %"
226 GST_TIME_FORMAT, proportion, (diff < 0) ? "-" : "",
227 GST_TIME_ARGS (ABS (diff)), GST_TIME_ARGS (timestamp));
229 GST_OBJECT_LOCK (mix);
230 mix->proportion = proportion;
231 if (G_LIKELY (timestamp != GST_CLOCK_TIME_NONE)) {
232 if (G_UNLIKELY (diff > 0))
234 timestamp + 2 * diff + gst_util_uint64_scale_int (GST_SECOND,
235 mix->fps_d, mix->fps_n);
237 mix->earliest_time = timestamp + diff;
239 mix->earliest_time = GST_CLOCK_TIME_NONE;
241 GST_OBJECT_UNLOCK (mix);
245 gst_videomixer_reset_qos (GstVideoMixer * mix)
247 gst_videomixer_update_qos (mix, 0.5, 0, GST_CLOCK_TIME_NONE);
251 gst_videomixer_read_qos (GstVideoMixer * mix, gdouble * proportion,
254 GST_OBJECT_LOCK (mix);
255 *proportion = mix->proportion;
256 *time = mix->earliest_time;
257 GST_OBJECT_UNLOCK (mix);
260 /* Perform qos calculations before processing the next frame. Returns TRUE if
261 * the frame should be processed, FALSE if the frame can be dropped entirely */
263 gst_videomixer_do_qos (GstVideoMixer * mix, GstClockTime timestamp)
265 GstClockTime qostime, earliest_time;
268 /* no timestamp, can't do QoS => process frame */
269 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (timestamp))) {
270 GST_LOG_OBJECT (mix, "invalid timestamp, can't do QoS, process frame");
274 /* get latest QoS observation values */
275 gst_videomixer_read_qos (mix, &proportion, &earliest_time);
277 /* skip qos if we have no observation (yet) => process frame */
278 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (earliest_time))) {
279 GST_LOG_OBJECT (mix, "no observation yet, process frame");
283 /* qos is done on running time */
285 gst_segment_to_running_time (&mix->segment, GST_FORMAT_TIME, timestamp);
287 /* see how our next timestamp relates to the latest qos timestamp */
288 GST_LOG_OBJECT (mix, "qostime %" GST_TIME_FORMAT ", earliest %"
289 GST_TIME_FORMAT, GST_TIME_ARGS (qostime), GST_TIME_ARGS (earliest_time));
291 if (qostime != GST_CLOCK_TIME_NONE && qostime <= earliest_time) {
292 GST_DEBUG_OBJECT (mix, "we are late, drop frame");
296 GST_LOG_OBJECT (mix, "process frame");
301 gst_videomixer_set_master_geometry (GstVideoMixer * mix)
304 gint width = 0, height = 0, fps_n = 0, fps_d = 0, par_n = 0, par_d = 0;
305 GstVideoMixerPad *master = NULL;
307 walk = mix->sinkpads;
309 GstVideoMixerPad *mixpad = GST_VIDEO_MIXER_PAD (walk->data);
311 walk = g_slist_next (walk);
313 /* Biggest input geometry will be our output geometry */
314 width = MAX (width, mixpad->in_width);
315 height = MAX (height, mixpad->in_height);
317 /* If mix framerate < mixpad framerate, using fractions */
318 GST_DEBUG_OBJECT (mixpad, "comparing framerate %d/%d to mixpad's %d/%d",
319 fps_n, fps_d, mixpad->fps_n, mixpad->fps_d);
320 if ((!fps_n && !fps_d) ||
321 ((gint64) fps_n * mixpad->fps_d < (gint64) mixpad->fps_n * fps_d)) {
322 fps_n = mixpad->fps_n;
323 fps_d = mixpad->fps_d;
324 par_n = mixpad->par_n;
325 par_d = mixpad->par_d;
326 GST_DEBUG_OBJECT (mixpad, "becomes the master pad");
332 if (mix->master != master || mix->in_width != width
333 || mix->in_height != height || mix->fps_n != fps_n
334 || mix->fps_d != fps_d || mix->par_n != par_n || mix->par_d != par_d) {
337 gst_videomixer_reset_qos (mix);
338 mix->master = master;
339 mix->in_width = width;
340 mix->in_height = height;
349 gst_videomixer_pad_sink_setcaps (GstPad * pad, GstCaps * vscaps)
352 GstVideoMixerPad *mixpad;
353 GstStructure *structure;
354 gint in_width, in_height;
355 gboolean ret = FALSE;
356 const GValue *framerate, *par;
358 GST_INFO_OBJECT (pad, "Setting caps %" GST_PTR_FORMAT, vscaps);
360 mix = GST_VIDEO_MIXER (gst_pad_get_parent (pad));
361 mixpad = GST_VIDEO_MIXER_PAD (pad);
366 structure = gst_caps_get_structure (vscaps, 0);
368 if (!gst_structure_get_int (structure, "width", &in_width)
369 || !gst_structure_get_int (structure, "height", &in_height)
370 || (framerate = gst_structure_get_value (structure, "framerate")) == NULL)
372 par = gst_structure_get_value (structure, "pixel-aspect-ratio");
374 GST_VIDEO_MIXER_STATE_LOCK (mix);
375 mixpad->fps_n = gst_value_get_fraction_numerator (framerate);
376 mixpad->fps_d = gst_value_get_fraction_denominator (framerate);
378 mixpad->par_n = gst_value_get_fraction_numerator (par);
379 mixpad->par_d = gst_value_get_fraction_denominator (par);
381 mixpad->par_n = mixpad->par_d = 1;
384 mixpad->in_width = in_width;
385 mixpad->in_height = in_height;
387 gst_videomixer_set_master_geometry (mix);
388 GST_VIDEO_MIXER_STATE_UNLOCK (mix);
393 gst_object_unref (mix);
399 gst_videomixer_pad_sink_getcaps (GstPad * pad)
402 GstVideoMixerPad *mixpad;
407 mix = GST_VIDEO_MIXER (gst_pad_get_parent (pad));
408 mixpad = GST_VIDEO_MIXER_PAD (pad);
413 /* Get downstream allowed caps */
414 res = gst_pad_get_allowed_caps (mix->srcpad);
415 if (G_UNLIKELY (res == NULL)) {
416 res = gst_caps_copy (gst_pad_get_pad_template_caps (pad));
420 GST_VIDEO_MIXER_STATE_LOCK (mix);
422 /* Return as-is if not other sinkpad set as master */
423 if (mix->master == NULL) {
424 GST_VIDEO_MIXER_STATE_UNLOCK (mix);
428 mastercaps = gst_pad_get_fixed_caps_func (GST_PAD (mix->master));
430 /* If master pad caps aren't negotiated yet, return downstream
432 if (!GST_CAPS_IS_SIMPLE (mastercaps)) {
433 GST_VIDEO_MIXER_STATE_UNLOCK (mix);
434 gst_caps_unref (mastercaps);
438 gst_caps_unref (res);
439 res = gst_caps_make_writable (mastercaps);
440 st = gst_caps_get_structure (res, 0);
441 gst_structure_set (st, "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
442 "height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
443 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
444 if (!gst_structure_has_field (st, "pixel-aspect-ratio"))
445 gst_structure_set (st, "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1, NULL);
447 GST_VIDEO_MIXER_STATE_UNLOCK (mix);
451 GST_DEBUG_OBJECT (pad, "Returning %" GST_PTR_FORMAT, res);
457 * We accept the caps if it has the same format as other sink pads in
461 gst_videomixer_pad_sink_acceptcaps (GstPad * pad, GstCaps * vscaps)
465 GstCaps *acceptedCaps;
467 mix = GST_VIDEO_MIXER (gst_pad_get_parent (pad));
468 GST_DEBUG_OBJECT (pad, "%" GST_PTR_FORMAT, vscaps);
469 GST_VIDEO_MIXER_STATE_LOCK (mix);
472 acceptedCaps = gst_pad_get_fixed_caps_func (GST_PAD (mix->master));
473 acceptedCaps = gst_caps_make_writable (acceptedCaps);
474 GST_LOG_OBJECT (pad, "master's caps %" GST_PTR_FORMAT, acceptedCaps);
475 if (GST_CAPS_IS_SIMPLE (acceptedCaps)) {
477 s = gst_caps_get_structure (acceptedCaps, 0);
478 gst_structure_set (s, "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
479 "height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
480 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
481 if (!gst_structure_has_field (s, "pixel-aspect-ratio"))
482 gst_structure_set (s, "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1,
486 acceptedCaps = gst_pad_get_fixed_caps_func (pad);
489 GST_INFO_OBJECT (pad, "vscaps: %" GST_PTR_FORMAT, vscaps);
490 GST_INFO_OBJECT (pad, "acceptedCaps: %" GST_PTR_FORMAT, acceptedCaps);
492 ret = gst_caps_can_intersect (vscaps, acceptedCaps);
493 GST_INFO_OBJECT (pad, "%saccepted caps %" GST_PTR_FORMAT, (ret ? "" : "not "),
495 gst_caps_unref (acceptedCaps);
496 GST_VIDEO_MIXER_STATE_UNLOCK (mix);
497 gst_object_unref (mix);
504 gst_videomixer_pad_init (GstVideoMixerPad * mixerpad)
506 /* setup some pad functions */
507 gst_pad_set_setcaps_function (GST_PAD (mixerpad),
508 gst_videomixer_pad_sink_setcaps);
509 gst_pad_set_acceptcaps_function (GST_PAD (mixerpad),
510 GST_DEBUG_FUNCPTR (gst_videomixer_pad_sink_acceptcaps));
511 gst_pad_set_getcaps_function (GST_PAD (mixerpad),
512 gst_videomixer_pad_sink_getcaps);
514 mixerpad->zorder = DEFAULT_PAD_ZORDER;
515 mixerpad->xpos = DEFAULT_PAD_XPOS;
516 mixerpad->ypos = DEFAULT_PAD_YPOS;
517 mixerpad->alpha = DEFAULT_PAD_ALPHA;
520 /* VideoMixer signals and args */
527 #define DEFAULT_BACKGROUND VIDEO_MIXER_BACKGROUND_CHECKER
534 #define GST_TYPE_VIDEO_MIXER_BACKGROUND (gst_video_mixer_background_get_type())
536 gst_video_mixer_background_get_type (void)
538 static GType video_mixer_background_type = 0;
540 static const GEnumValue video_mixer_background[] = {
541 {VIDEO_MIXER_BACKGROUND_CHECKER, "Checker pattern", "checker"},
542 {VIDEO_MIXER_BACKGROUND_BLACK, "Black", "black"},
543 {VIDEO_MIXER_BACKGROUND_WHITE, "White", "white"},
544 {VIDEO_MIXER_BACKGROUND_TRANSPARENT,
545 "Transparent Background to enable further mixing", "transparent"},
549 if (!video_mixer_background_type) {
550 video_mixer_background_type =
551 g_enum_register_static ("GstVideoMixerBackground",
552 video_mixer_background);
554 return video_mixer_background_type;
557 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
560 GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV") ";" GST_VIDEO_CAPS_BGRA ";"
561 GST_VIDEO_CAPS_ARGB ";" GST_VIDEO_CAPS_RGBA ";" GST_VIDEO_CAPS_ABGR ";"
562 GST_VIDEO_CAPS_YUV ("Y444") ";" GST_VIDEO_CAPS_YUV ("Y42B") ";"
563 GST_VIDEO_CAPS_YUV ("YUY2") ";" GST_VIDEO_CAPS_YUV ("UYVY") ";"
564 GST_VIDEO_CAPS_YUV ("YVYU") ";"
565 GST_VIDEO_CAPS_YUV ("I420") ";" GST_VIDEO_CAPS_YUV ("YV12") ";"
566 GST_VIDEO_CAPS_YUV ("Y41B") ";" GST_VIDEO_CAPS_RGB ";"
567 GST_VIDEO_CAPS_BGR ";" GST_VIDEO_CAPS_xRGB ";" GST_VIDEO_CAPS_xBGR ";"
568 GST_VIDEO_CAPS_RGBx ";" GST_VIDEO_CAPS_BGRx)
571 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink_%d",
574 GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV") ";" GST_VIDEO_CAPS_BGRA ";"
575 GST_VIDEO_CAPS_ARGB ";" GST_VIDEO_CAPS_RGBA ";" GST_VIDEO_CAPS_ABGR ";"
576 GST_VIDEO_CAPS_YUV ("Y444") ";" GST_VIDEO_CAPS_YUV ("Y42B") ";"
577 GST_VIDEO_CAPS_YUV ("YUY2") ";" GST_VIDEO_CAPS_YUV ("UYVY") ";"
578 GST_VIDEO_CAPS_YUV ("YVYU") ";"
579 GST_VIDEO_CAPS_YUV ("I420") ";" GST_VIDEO_CAPS_YUV ("YV12") ";"
580 GST_VIDEO_CAPS_YUV ("Y41B") ";" GST_VIDEO_CAPS_RGB ";"
581 GST_VIDEO_CAPS_BGR ";" GST_VIDEO_CAPS_xRGB ";" GST_VIDEO_CAPS_xBGR ";"
582 GST_VIDEO_CAPS_RGBx ";" GST_VIDEO_CAPS_BGRx)
585 static void gst_videomixer_finalize (GObject * object);
587 static GstCaps *gst_videomixer_getcaps (GstPad * pad);
588 static gboolean gst_videomixer_setcaps (GstPad * pad, GstCaps * caps);
589 static gboolean gst_videomixer_query (GstPad * pad, GstQuery * query);
591 static GstFlowReturn gst_videomixer_collected (GstCollectPads * pads,
592 GstVideoMixer * mix);
593 static GstPad *gst_videomixer_request_new_pad (GstElement * element,
594 GstPadTemplate * templ, const gchar * name);
595 static void gst_videomixer_release_pad (GstElement * element, GstPad * pad);
597 static void gst_videomixer_set_property (GObject * object, guint prop_id,
598 const GValue * value, GParamSpec * pspec);
599 static void gst_videomixer_get_property (GObject * object, guint prop_id,
600 GValue * value, GParamSpec * pspec);
601 static GstStateChangeReturn gst_videomixer_change_state (GstElement * element,
602 GstStateChange transition);
604 /*static guint gst_videomixer_signals[LAST_SIGNAL] = { 0 }; */
606 static void gst_videomixer_child_proxy_init (gpointer g_iface,
607 gpointer iface_data);
608 static void _do_init (GType object_type);
610 GST_BOILERPLATE_FULL (GstVideoMixer, gst_videomixer, GstElement,
611 GST_TYPE_ELEMENT, _do_init);
614 _do_init (GType object_type)
616 static const GInterfaceInfo child_proxy_info = {
617 (GInterfaceInitFunc) gst_videomixer_child_proxy_init,
622 g_type_add_interface_static (object_type, GST_TYPE_CHILD_PROXY,
624 GST_INFO ("GstChildProxy interface registered");
628 gst_videomixer_child_proxy_get_child_by_index (GstChildProxy * child_proxy,
631 GstVideoMixer *mix = GST_VIDEO_MIXER (child_proxy);
634 GST_VIDEO_MIXER_STATE_LOCK (mix);
635 if ((obj = g_slist_nth_data (mix->sinkpads, index)))
636 gst_object_ref (obj);
637 GST_VIDEO_MIXER_STATE_UNLOCK (mix);
642 gst_videomixer_child_proxy_get_children_count (GstChildProxy * child_proxy)
645 GstVideoMixer *mix = GST_VIDEO_MIXER (child_proxy);
647 GST_VIDEO_MIXER_STATE_LOCK (mix);
648 count = mix->numpads;
649 GST_VIDEO_MIXER_STATE_UNLOCK (mix);
650 GST_INFO_OBJECT (mix, "Children Count: %d", count);
655 gst_videomixer_child_proxy_init (gpointer g_iface, gpointer iface_data)
657 GstChildProxyInterface *iface = g_iface;
659 GST_INFO ("intializing child proxy interface");
660 iface->get_child_by_index = gst_videomixer_child_proxy_get_child_by_index;
661 iface->get_children_count = gst_videomixer_child_proxy_get_children_count;
665 gst_videomixer_base_init (gpointer g_class)
667 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
669 gst_element_class_add_static_pad_template (element_class, &src_factory);
670 gst_element_class_add_static_pad_template (element_class, &sink_factory);
672 gst_element_class_set_details_simple (element_class, "Video mixer",
673 "Filter/Editor/Video",
674 "Mix multiple video streams", "Wim Taymans <wim@fluendo.com>");
678 gst_videomixer_class_init (GstVideoMixerClass * klass)
680 GObjectClass *gobject_class = (GObjectClass *) klass;
681 GstElementClass *gstelement_class = (GstElementClass *) klass;
683 gobject_class->finalize = gst_videomixer_finalize;
685 gobject_class->get_property = gst_videomixer_get_property;
686 gobject_class->set_property = gst_videomixer_set_property;
688 g_object_class_install_property (gobject_class, PROP_BACKGROUND,
689 g_param_spec_enum ("background", "Background", "Background type",
690 GST_TYPE_VIDEO_MIXER_BACKGROUND,
691 DEFAULT_BACKGROUND, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
693 gstelement_class->request_new_pad =
694 GST_DEBUG_FUNCPTR (gst_videomixer_request_new_pad);
695 gstelement_class->release_pad =
696 GST_DEBUG_FUNCPTR (gst_videomixer_release_pad);
697 gstelement_class->change_state =
698 GST_DEBUG_FUNCPTR (gst_videomixer_change_state);
700 /* Register the pad class */
701 (void) (GST_TYPE_VIDEO_MIXER_PAD);
702 /* Register the background enum */
703 (void) (GST_TYPE_VIDEO_MIXER_BACKGROUND);
707 gst_videomixer_collect_free (GstVideoMixerCollect * mixcol)
709 if (mixcol->buffer) {
710 gst_buffer_unref (mixcol->buffer);
711 mixcol->buffer = NULL;
716 gst_videomixer_reset (GstVideoMixer * mix)
724 mix->fps_n = mix->fps_d = 0;
725 mix->par_n = mix->par_d = 1;
726 mix->setcaps = FALSE;
727 mix->sendseg = FALSE;
729 mix->segment_position = 0;
730 gst_segment_init (&mix->segment, GST_FORMAT_TIME);
732 gst_videomixer_reset_qos (mix);
734 mix->fmt = GST_VIDEO_FORMAT_UNKNOWN;
737 mix->last_duration = -1;
739 /* clean up collect data */
740 walk = mix->collect->data;
742 GstVideoMixerCollect *data = (GstVideoMixerCollect *) walk->data;
744 gst_videomixer_collect_free (data);
745 walk = g_slist_next (walk);
748 mix->next_sinkpad = 0;
749 mix->flush_stop_pending = FALSE;
753 gst_videomixer_init (GstVideoMixer * mix, GstVideoMixerClass * g_class)
755 GstElementClass *klass = GST_ELEMENT_GET_CLASS (mix);
758 gst_pad_new_from_template (gst_element_class_get_pad_template (klass,
760 gst_pad_set_getcaps_function (GST_PAD (mix->srcpad),
761 GST_DEBUG_FUNCPTR (gst_videomixer_getcaps));
762 gst_pad_set_setcaps_function (GST_PAD (mix->srcpad),
763 GST_DEBUG_FUNCPTR (gst_videomixer_setcaps));
764 gst_pad_set_query_function (GST_PAD (mix->srcpad),
765 GST_DEBUG_FUNCPTR (gst_videomixer_query));
766 gst_pad_set_event_function (GST_PAD (mix->srcpad),
767 GST_DEBUG_FUNCPTR (gst_videomixer_src_event));
768 gst_element_add_pad (GST_ELEMENT (mix), mix->srcpad);
770 mix->collect = gst_collect_pads_new ();
771 mix->background = DEFAULT_BACKGROUND;
773 gst_collect_pads_set_function (mix->collect,
774 (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (gst_videomixer_collected),
777 mix->state_lock = g_mutex_new ();
778 /* initialize variables */
779 gst_videomixer_reset (mix);
783 gst_videomixer_finalize (GObject * object)
785 GstVideoMixer *mix = GST_VIDEO_MIXER (object);
787 gst_object_unref (mix->collect);
788 g_mutex_free (mix->state_lock);
790 G_OBJECT_CLASS (parent_class)->finalize (object);
794 gst_videomixer_query_duration (GstVideoMixer * mix, GstQuery * query)
803 gst_query_parse_duration (query, &format, NULL);
809 /* Take maximum of all durations */
810 it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (mix));
812 GstIteratorResult ires;
815 ires = gst_iterator_next (it, &item);
817 case GST_ITERATOR_DONE:
820 case GST_ITERATOR_OK:
822 GstPad *pad = GST_PAD_CAST (item);
825 /* ask sink peer for duration */
826 res &= gst_pad_query_peer_duration (pad, &format, &duration);
827 /* take max from all valid return values */
829 /* valid unknown length, stop searching */
830 if (duration == -1) {
834 /* else see if bigger than current max */
835 else if (duration > max)
838 gst_object_unref (pad);
841 case GST_ITERATOR_RESYNC:
844 gst_iterator_resync (it);
852 gst_iterator_free (it);
855 /* and store the max */
856 GST_DEBUG_OBJECT (mix, "Total duration in format %s: %"
857 GST_TIME_FORMAT, gst_format_get_name (format), GST_TIME_ARGS (max));
858 gst_query_set_duration (query, format, max);
865 gst_videomixer_query_latency (GstVideoMixer * mix, GstQuery * query)
867 GstClockTime min, max;
877 max = GST_CLOCK_TIME_NONE;
879 /* Take maximum of all latency values */
880 it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (mix));
882 GstIteratorResult ires;
885 ires = gst_iterator_next (it, &item);
887 case GST_ITERATOR_DONE:
890 case GST_ITERATOR_OK:
892 GstPad *pad = GST_PAD_CAST (item);
896 GstClockTime min_cur, max_cur;
900 peerquery = gst_query_new_latency ();
902 /* Ask peer for latency */
903 res &= gst_pad_peer_query (pad, peerquery);
905 /* take max from all valid return values */
907 gst_query_parse_latency (peerquery, &live_cur, &min_cur, &max_cur);
912 if (max_cur != GST_CLOCK_TIME_NONE &&
913 ((max != GST_CLOCK_TIME_NONE && max_cur > max) ||
914 (max == GST_CLOCK_TIME_NONE)))
917 live = live || live_cur;
920 gst_query_unref (peerquery);
921 gst_object_unref (pad);
924 case GST_ITERATOR_RESYNC:
927 max = GST_CLOCK_TIME_NONE;
929 gst_iterator_resync (it);
937 gst_iterator_free (it);
940 /* store the results */
941 GST_DEBUG_OBJECT (mix, "Calculated total latency: live %s, min %"
942 GST_TIME_FORMAT ", max %" GST_TIME_FORMAT,
943 (live ? "yes" : "no"), GST_TIME_ARGS (min), GST_TIME_ARGS (max));
944 gst_query_set_latency (query, live, min, max);
951 gst_videomixer_query (GstPad * pad, GstQuery * query)
953 GstVideoMixer *mix = GST_VIDEO_MIXER (gst_pad_get_parent (pad));
954 gboolean res = FALSE;
956 switch (GST_QUERY_TYPE (query)) {
957 case GST_QUERY_POSITION:
961 gst_query_parse_position (query, &format, NULL);
964 case GST_FORMAT_TIME:
965 /* FIXME, bring to stream time, might be tricky */
966 gst_query_set_position (query, format, mix->last_ts);
974 case GST_QUERY_DURATION:
975 res = gst_videomixer_query_duration (mix, query);
977 case GST_QUERY_LATENCY:
978 res = gst_videomixer_query_latency (mix, query);
981 /* FIXME, needs a custom query handler because we have multiple
982 * sinkpads, send to the master pad until then */
983 res = gst_pad_query (GST_PAD_CAST (mix->master), query);
987 gst_object_unref (mix);
992 gst_videomixer_getcaps (GstPad * pad)
994 GstVideoMixer *mix = GST_VIDEO_MIXER (gst_pad_get_parent (pad));
996 GstStructure *structure;
1001 gst_caps_copy (gst_pad_get_pad_template_caps (GST_PAD (mix->master)));
1003 caps = gst_caps_copy (gst_pad_get_pad_template_caps (mix->srcpad));
1006 numCaps = gst_caps_get_size (caps) - 1;
1007 for (; numCaps >= 0; numCaps--) {
1008 structure = gst_caps_get_structure (caps, numCaps);
1009 if (mix->out_width != 0) {
1010 gst_structure_set (structure, "width", G_TYPE_INT, mix->out_width, NULL);
1012 if (mix->out_height != 0) {
1013 gst_structure_set (structure, "height", G_TYPE_INT, mix->out_height,
1016 if (mix->fps_d != 0) {
1017 gst_structure_set (structure,
1018 "framerate", GST_TYPE_FRACTION, mix->fps_n, mix->fps_d, NULL);
1022 gst_object_unref (mix);
1028 gst_videomixer_setcaps (GstPad * pad, GstCaps * caps)
1030 GstVideoMixer *mixer = GST_VIDEO_MIXER (gst_pad_get_parent_element (pad));
1031 gboolean ret = FALSE;
1033 GST_INFO_OBJECT (mixer, "set src caps: %" GST_PTR_FORMAT, caps);
1035 mixer->blend = NULL;
1036 mixer->overlay = NULL;
1037 mixer->fill_checker = NULL;
1038 mixer->fill_color = NULL;
1040 if (!gst_video_format_parse_caps (caps, &mixer->fmt, NULL, NULL))
1043 switch (mixer->fmt) {
1044 case GST_VIDEO_FORMAT_AYUV:
1045 mixer->blend = gst_video_mixer_blend_ayuv;
1046 mixer->overlay = gst_video_mixer_overlay_ayuv;
1047 mixer->fill_checker = gst_video_mixer_fill_checker_ayuv;
1048 mixer->fill_color = gst_video_mixer_fill_color_ayuv;
1051 case GST_VIDEO_FORMAT_ARGB:
1052 mixer->blend = gst_video_mixer_blend_argb;
1053 mixer->overlay = gst_video_mixer_overlay_argb;
1054 mixer->fill_checker = gst_video_mixer_fill_checker_argb;
1055 mixer->fill_color = gst_video_mixer_fill_color_argb;
1058 case GST_VIDEO_FORMAT_BGRA:
1059 mixer->blend = gst_video_mixer_blend_bgra;
1060 mixer->overlay = gst_video_mixer_overlay_bgra;
1061 mixer->fill_checker = gst_video_mixer_fill_checker_bgra;
1062 mixer->fill_color = gst_video_mixer_fill_color_bgra;
1065 case GST_VIDEO_FORMAT_ABGR:
1066 mixer->blend = gst_video_mixer_blend_abgr;
1067 mixer->overlay = gst_video_mixer_overlay_abgr;
1068 mixer->fill_checker = gst_video_mixer_fill_checker_abgr;
1069 mixer->fill_color = gst_video_mixer_fill_color_abgr;
1072 case GST_VIDEO_FORMAT_RGBA:
1073 mixer->blend = gst_video_mixer_blend_rgba;
1074 mixer->overlay = gst_video_mixer_overlay_rgba;
1075 mixer->fill_checker = gst_video_mixer_fill_checker_rgba;
1076 mixer->fill_color = gst_video_mixer_fill_color_rgba;
1079 case GST_VIDEO_FORMAT_Y444:
1080 mixer->blend = gst_video_mixer_blend_y444;
1081 mixer->overlay = mixer->blend;
1082 mixer->fill_checker = gst_video_mixer_fill_checker_y444;
1083 mixer->fill_color = gst_video_mixer_fill_color_y444;
1086 case GST_VIDEO_FORMAT_Y42B:
1087 mixer->blend = gst_video_mixer_blend_y42b;
1088 mixer->overlay = mixer->blend;
1089 mixer->fill_checker = gst_video_mixer_fill_checker_y42b;
1090 mixer->fill_color = gst_video_mixer_fill_color_y42b;
1093 case GST_VIDEO_FORMAT_YUY2:
1094 mixer->blend = gst_video_mixer_blend_yuy2;
1095 mixer->overlay = mixer->blend;
1096 mixer->fill_checker = gst_video_mixer_fill_checker_yuy2;
1097 mixer->fill_color = gst_video_mixer_fill_color_yuy2;
1100 case GST_VIDEO_FORMAT_UYVY:
1101 mixer->blend = gst_video_mixer_blend_uyvy;
1102 mixer->overlay = mixer->blend;
1103 mixer->fill_checker = gst_video_mixer_fill_checker_uyvy;
1104 mixer->fill_color = gst_video_mixer_fill_color_uyvy;
1107 case GST_VIDEO_FORMAT_YVYU:
1108 mixer->blend = gst_video_mixer_blend_yvyu;
1109 mixer->overlay = mixer->blend;
1110 mixer->fill_checker = gst_video_mixer_fill_checker_yvyu;
1111 mixer->fill_color = gst_video_mixer_fill_color_yvyu;
1114 case GST_VIDEO_FORMAT_I420:
1115 mixer->blend = gst_video_mixer_blend_i420;
1116 mixer->overlay = mixer->blend;
1117 mixer->fill_checker = gst_video_mixer_fill_checker_i420;
1118 mixer->fill_color = gst_video_mixer_fill_color_i420;
1121 case GST_VIDEO_FORMAT_YV12:
1122 mixer->blend = gst_video_mixer_blend_yv12;
1123 mixer->overlay = mixer->blend;
1124 mixer->fill_checker = gst_video_mixer_fill_checker_yv12;
1125 mixer->fill_color = gst_video_mixer_fill_color_yv12;
1128 case GST_VIDEO_FORMAT_Y41B:
1129 mixer->blend = gst_video_mixer_blend_y41b;
1130 mixer->overlay = mixer->blend;
1131 mixer->fill_checker = gst_video_mixer_fill_checker_y41b;
1132 mixer->fill_color = gst_video_mixer_fill_color_y41b;
1135 case GST_VIDEO_FORMAT_RGB:
1136 mixer->blend = gst_video_mixer_blend_rgb;
1137 mixer->overlay = mixer->blend;
1138 mixer->fill_checker = gst_video_mixer_fill_checker_rgb;
1139 mixer->fill_color = gst_video_mixer_fill_color_rgb;
1142 case GST_VIDEO_FORMAT_BGR:
1143 mixer->blend = gst_video_mixer_blend_bgr;
1144 mixer->overlay = mixer->blend;
1145 mixer->fill_checker = gst_video_mixer_fill_checker_bgr;
1146 mixer->fill_color = gst_video_mixer_fill_color_bgr;
1149 case GST_VIDEO_FORMAT_xRGB:
1150 mixer->blend = gst_video_mixer_blend_xrgb;
1151 mixer->overlay = mixer->blend;
1152 mixer->fill_checker = gst_video_mixer_fill_checker_xrgb;
1153 mixer->fill_color = gst_video_mixer_fill_color_xrgb;
1156 case GST_VIDEO_FORMAT_xBGR:
1157 mixer->blend = gst_video_mixer_blend_xbgr;
1158 mixer->overlay = mixer->blend;
1159 mixer->fill_checker = gst_video_mixer_fill_checker_xbgr;
1160 mixer->fill_color = gst_video_mixer_fill_color_xbgr;
1163 case GST_VIDEO_FORMAT_RGBx:
1164 mixer->blend = gst_video_mixer_blend_rgbx;
1165 mixer->overlay = mixer->blend;
1166 mixer->fill_checker = gst_video_mixer_fill_checker_rgbx;
1167 mixer->fill_color = gst_video_mixer_fill_color_rgbx;
1170 case GST_VIDEO_FORMAT_BGRx:
1171 mixer->blend = gst_video_mixer_blend_bgrx;
1172 mixer->overlay = mixer->blend;
1173 mixer->fill_checker = gst_video_mixer_fill_checker_bgrx;
1174 mixer->fill_color = gst_video_mixer_fill_color_bgrx;
1182 gst_object_unref (mixer);
1188 gst_videomixer_request_new_pad (GstElement * element,
1189 GstPadTemplate * templ, const gchar * req_name)
1191 GstVideoMixer *mix = NULL;
1192 GstVideoMixerPad *mixpad = NULL;
1193 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
1195 g_return_val_if_fail (templ != NULL, NULL);
1197 if (G_UNLIKELY (templ->direction != GST_PAD_SINK)) {
1198 g_warning ("videomixer: request pad that is not a SINK pad");
1202 g_return_val_if_fail (GST_IS_VIDEO_MIXER (element), NULL);
1204 mix = GST_VIDEO_MIXER (element);
1206 if (templ == gst_element_class_get_pad_template (klass, "sink_%d")) {
1209 GstVideoMixerCollect *mixcol = NULL;
1211 GST_VIDEO_MIXER_STATE_LOCK (mix);
1212 if (req_name == NULL || strlen (req_name) < 6
1213 || !g_str_has_prefix (req_name, "sink_")) {
1214 /* no name given when requesting the pad, use next available int */
1215 serial = mix->next_sinkpad++;
1217 /* parse serial number from requested padname */
1218 serial = atoi (&req_name[5]);
1219 if (serial >= mix->next_sinkpad)
1220 mix->next_sinkpad = serial + 1;
1222 /* create new pad with the name */
1223 name = g_strdup_printf ("sink_%d", serial);
1224 mixpad = g_object_new (GST_TYPE_VIDEO_MIXER_PAD, "name", name, "direction",
1225 templ->direction, "template", templ, NULL);
1228 mixpad->zorder = mix->numpads;
1229 mixpad->xpos = DEFAULT_PAD_XPOS;
1230 mixpad->ypos = DEFAULT_PAD_YPOS;
1231 mixpad->alpha = DEFAULT_PAD_ALPHA;
1233 mixcol = (GstVideoMixerCollect *)
1234 gst_collect_pads_add_pad (mix->collect, GST_PAD (mixpad),
1235 sizeof (GstVideoMixerCollect));
1237 /* FIXME: hacked way to override/extend the event function of
1238 * GstCollectPads; because it sets its own event function giving the
1239 * element no access to events */
1240 mix->collect_event =
1241 (GstPadEventFunction) GST_PAD_EVENTFUNC (GST_PAD (mixpad));
1242 gst_pad_set_event_function (GST_PAD (mixpad),
1243 GST_DEBUG_FUNCPTR (gst_videomixer_sink_event));
1245 /* Keep track of each other */
1246 mixcol->mixpad = mixpad;
1247 mixpad->mixcol = mixcol;
1249 /* Keep an internal list of mixpads for zordering */
1250 mix->sinkpads = g_slist_append (mix->sinkpads, mixpad);
1252 GST_VIDEO_MIXER_STATE_UNLOCK (mix);
1254 g_warning ("videomixer: this is not our template!");
1258 /* add the pad to the element */
1259 gst_element_add_pad (element, GST_PAD (mixpad));
1260 gst_child_proxy_child_added (GST_OBJECT (mix), GST_OBJECT (mixpad));
1262 return GST_PAD (mixpad);
1266 gst_videomixer_release_pad (GstElement * element, GstPad * pad)
1268 GstVideoMixer *mix = NULL;
1269 GstVideoMixerPad *mixpad;
1271 mix = GST_VIDEO_MIXER (element);
1272 GST_VIDEO_MIXER_STATE_LOCK (mix);
1273 if (G_UNLIKELY (g_slist_find (mix->sinkpads, pad) == NULL)) {
1274 g_warning ("Unknown pad %s", GST_PAD_NAME (pad));
1278 mixpad = GST_VIDEO_MIXER_PAD (pad);
1280 mix->sinkpads = g_slist_remove (mix->sinkpads, pad);
1281 gst_videomixer_collect_free (mixpad->mixcol);
1282 gst_collect_pads_remove_pad (mix->collect, pad);
1283 gst_child_proxy_child_removed (GST_OBJECT (mix), GST_OBJECT (mixpad));
1284 /* determine possibly new geometry and master */
1285 gst_videomixer_set_master_geometry (mix);
1287 GST_VIDEO_MIXER_STATE_UNLOCK (mix);
1289 gst_element_remove_pad (element, pad);
1292 GST_VIDEO_MIXER_STATE_UNLOCK (mix);
1296 pad_zorder_compare (const GstVideoMixerPad * pad1,
1297 const GstVideoMixerPad * pad2)
1299 return pad1->zorder - pad2->zorder;
1303 gst_videomixer_sort_pads (GstVideoMixer * mix)
1305 mix->sinkpads = g_slist_sort (mix->sinkpads,
1306 (GCompareFunc) pad_zorder_compare);
1309 /* try to get a buffer on all pads. As long as the queued value is
1310 * negative, we skip buffers */
1312 gst_videomixer_fill_queues (GstVideoMixer * mix)
1314 GSList *walk = NULL;
1315 gboolean eos = TRUE;
1317 g_return_val_if_fail (GST_IS_VIDEO_MIXER (mix), FALSE);
1319 /* try to make sure we have a buffer from each usable pad first */
1320 walk = mix->collect->data;
1322 GstCollectData *data = (GstCollectData *) walk->data;
1323 GstVideoMixerCollect *mixcol = (GstVideoMixerCollect *) data;
1324 GstVideoMixerPad *mixpad = mixcol->mixpad;
1326 walk = g_slist_next (walk);
1328 if (mixcol->buffer == NULL) {
1329 GstBuffer *buf = NULL;
1331 GST_LOG_OBJECT (mix, "we need a new buffer");
1333 buf = gst_collect_pads_peek (mix->collect, data);
1338 mixcol->buffer = buf;
1339 duration = GST_BUFFER_DURATION (mixcol->buffer);
1341 GST_LOG_OBJECT (mix, "we have a buffer with duration %" GST_TIME_FORMAT
1342 ", queued %" GST_TIME_FORMAT, GST_TIME_ARGS (duration),
1343 GST_TIME_ARGS (mixpad->queued));
1345 /* no duration on the buffer, use the framerate */
1346 if (!GST_CLOCK_TIME_IS_VALID (duration)) {
1347 if (mixpad->fps_n == 0) {
1348 duration = GST_CLOCK_TIME_NONE;
1351 gst_util_uint64_scale_int (GST_SECOND, mixpad->fps_d,
1355 if (GST_CLOCK_TIME_IS_VALID (duration))
1356 mixpad->queued += duration;
1357 else if (!mixpad->queued)
1358 mixpad->queued = GST_CLOCK_TIME_NONE;
1360 GST_LOG_OBJECT (mix, "now queued: %" GST_TIME_FORMAT,
1361 GST_TIME_ARGS (mixpad->queued));
1363 GST_LOG_OBJECT (mix, "pop returned a NULL buffer");
1366 if (mix->sendseg && (mixpad == mix->master)) {
1369 GstSegment *segment = &data->segment;
1371 /* FIXME, use rate/applied_rate as set on all sinkpads.
1372 * - currently we just set rate as received from last seek-event
1373 * We could potentially figure out the duration as well using
1374 * the current segment positions and the stated stop positions.
1375 * Also we just start from stream time 0 which is rather
1376 * weird. For non-synchronized mixing, the time should be
1377 * the min of the stream times of all received segments,
1378 * rationale being that the duration is at least going to
1379 * be as long as the earliest stream we start mixing. This
1380 * would also be correct for synchronized mixing but then
1381 * the later streams would be delayed until the stream times
1384 GST_INFO_OBJECT (mix, "_sending play segment");
1386 start = segment->accum;
1388 /* get the duration of the segment if we can and add it to the accumulated
1389 * time on the segment. */
1390 if (segment->stop != -1 && segment->start != -1)
1391 stop = start + (segment->stop - segment->start);
1395 gst_segment_set_newsegment (&mix->segment, FALSE, segment->rate,
1396 segment->format, start, stop, start + mix->segment_position);
1398 gst_event_new_new_segment_full (FALSE, segment->rate, 1.0,
1399 segment->format, start, stop, start + mix->segment_position);
1400 gst_pad_push_event (mix->srcpad, event);
1401 mix->sendseg = FALSE;
1404 if (mixcol->buffer != NULL && GST_CLOCK_TIME_IS_VALID (mixpad->queued)) {
1405 /* got a buffer somewhere so we're not eos */
1413 /* blend all buffers present on the pads */
1415 gst_videomixer_blend_buffers (GstVideoMixer * mix, GstBuffer * outbuf)
1418 BlendFunction blend;
1419 if (mix->background == VIDEO_MIXER_BACKGROUND_TRANSPARENT) {
1420 blend = mix->overlay;
1426 walk = mix->sinkpads;
1427 while (walk) { /* We walk with this list because it's ordered */
1428 GstVideoMixerPad *pad = GST_VIDEO_MIXER_PAD (walk->data);
1429 GstVideoMixerCollect *mixcol = pad->mixcol;
1431 walk = g_slist_next (walk);
1433 if (mixcol->buffer != NULL) {
1434 GstClockTime timestamp;
1438 seg = &mixcol->collect.segment;
1440 timestamp = GST_BUFFER_TIMESTAMP (mixcol->buffer);
1443 gst_segment_to_stream_time (seg, GST_FORMAT_TIME, timestamp);
1445 /* sync object properties on stream time */
1446 if (GST_CLOCK_TIME_IS_VALID (stream_time))
1447 gst_object_sync_values (G_OBJECT (pad), stream_time);
1449 blend (GST_BUFFER_DATA (mixcol->buffer),
1450 pad->xpos, pad->ypos, pad->in_width, pad->in_height, pad->alpha,
1451 GST_BUFFER_DATA (outbuf), mix->out_width, mix->out_height);
1456 /* remove buffers from the queue that were expired in the
1457 * interval of the master, we also prepare the queued value
1458 * in the pad so that we can skip and fill buffers later on */
1460 gst_videomixer_update_queues (GstVideoMixer * mix)
1465 interval = mix->master->queued;
1466 if (interval <= 0) {
1467 if (mix->fps_n == 0) {
1468 interval = G_MAXINT64;
1470 interval = gst_util_uint64_scale_int (GST_SECOND, mix->fps_d, mix->fps_n);
1472 GST_LOG_OBJECT (mix, "set interval to %" G_GINT64_FORMAT " nanoseconds",
1476 walk = mix->sinkpads;
1478 GstVideoMixerPad *pad = GST_VIDEO_MIXER_PAD (walk->data);
1479 GstVideoMixerCollect *mixcol = pad->mixcol;
1481 walk = g_slist_next (walk);
1483 if (mixcol->buffer != NULL) {
1484 pad->queued -= interval;
1485 GST_LOG_OBJECT (pad, "queued now %" G_GINT64_FORMAT, pad->queued);
1486 if (pad->queued <= 0) {
1488 gst_collect_pads_pop (mix->collect, &mixcol->collect);
1490 GST_LOG_OBJECT (pad, "unreffing buffer");
1492 gst_buffer_unref (buffer);
1494 GST_WARNING_OBJECT (pad,
1495 "Buffer was removed by GstCollectPads in the meantime");
1497 gst_buffer_unref (mixcol->buffer);
1498 mixcol->buffer = NULL;
1504 static GstFlowReturn
1505 gst_videomixer_collected (GstCollectPads * pads, GstVideoMixer * mix)
1507 GstFlowReturn ret = GST_FLOW_OK;
1508 GstBuffer *outbuf = NULL;
1510 gboolean eos = FALSE;
1511 GstClockTime timestamp = GST_CLOCK_TIME_NONE;
1512 GstClockTime duration = GST_CLOCK_TIME_NONE;
1514 g_return_val_if_fail (GST_IS_VIDEO_MIXER (mix), GST_FLOW_ERROR);
1516 /* This must be set, otherwise we have no caps */
1517 if (G_UNLIKELY (mix->in_width == 0))
1518 return GST_FLOW_NOT_NEGOTIATED;
1520 if (g_atomic_int_compare_and_exchange (&mix->flush_stop_pending, TRUE, FALSE)) {
1521 GST_DEBUG_OBJECT (mix, "pending flush stop");
1522 gst_pad_push_event (mix->srcpad, gst_event_new_flush_stop ());
1525 GST_LOG_OBJECT (mix, "all pads are collected");
1526 GST_VIDEO_MIXER_STATE_LOCK (mix);
1528 eos = gst_videomixer_fill_queues (mix);
1531 /* Push EOS downstream */
1532 GST_LOG_OBJECT (mix, "all our sinkpads are EOS, pushing downstream");
1533 gst_pad_push_event (mix->srcpad, gst_event_new_eos ());
1534 ret = GST_FLOW_WRONG_STATE;
1538 /* If geometry has changed we need to set new caps on the buffer */
1539 if (mix->in_width != mix->out_width || mix->in_height != mix->out_height
1541 GstCaps *newcaps = NULL;
1543 newcaps = gst_caps_make_writable
1544 (gst_pad_get_negotiated_caps (GST_PAD (mix->master)));
1545 gst_caps_set_simple (newcaps,
1546 "width", G_TYPE_INT, mix->in_width,
1547 "height", G_TYPE_INT, mix->in_height,
1548 "pixel-aspect-ratio", GST_TYPE_FRACTION, mix->par_n, mix->par_d, NULL);
1550 mix->out_width = mix->in_width;
1551 mix->out_height = mix->in_height;
1552 mix->setcaps = FALSE;
1554 /* Calculating out buffer size from input size */
1555 gst_pad_set_caps (mix->srcpad, newcaps);
1556 gst_caps_unref (newcaps);
1559 /* Get timestamp & duration */
1560 if (mix->master->mixcol->buffer != NULL) {
1563 GstVideoMixerCollect *mixcol = mix->master->mixcol;
1565 seg = &mixcol->collect.segment;
1566 in_ts = GST_BUFFER_TIMESTAMP (mixcol->buffer);
1568 timestamp = gst_segment_to_running_time (seg, GST_FORMAT_TIME, in_ts);
1569 duration = GST_BUFFER_DURATION (mixcol->buffer);
1571 mix->last_ts = timestamp;
1572 mix->last_duration = duration;
1574 timestamp = mix->last_ts;
1575 duration = mix->last_duration;
1578 if (GST_CLOCK_TIME_IS_VALID (duration))
1579 mix->last_ts += duration;
1581 if (!gst_videomixer_do_qos (mix, timestamp)) {
1582 gst_videomixer_update_queues (mix);
1583 GST_VIDEO_MIXER_STATE_UNLOCK (mix);
1588 /* allocate an output buffer */
1590 gst_video_format_get_size (mix->fmt, mix->out_width, mix->out_height);
1592 gst_pad_alloc_buffer_and_set_caps (mix->srcpad, GST_BUFFER_OFFSET_NONE,
1593 outsize, GST_PAD_CAPS (mix->srcpad), &outbuf);
1595 /* This must be set at this point, otherwise we have no src caps */
1596 g_assert (mix->blend != NULL);
1598 if (ret != GST_FLOW_OK) {
1602 GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
1603 GST_BUFFER_DURATION (outbuf) = duration;
1605 switch (mix->background) {
1606 case VIDEO_MIXER_BACKGROUND_CHECKER:
1607 mix->fill_checker (GST_BUFFER_DATA (outbuf), mix->out_width,
1610 case VIDEO_MIXER_BACKGROUND_BLACK:
1611 mix->fill_color (GST_BUFFER_DATA (outbuf), mix->out_width,
1612 mix->out_height, 16, 128, 128);
1614 case VIDEO_MIXER_BACKGROUND_WHITE:
1615 mix->fill_color (GST_BUFFER_DATA (outbuf), mix->out_width,
1616 mix->out_height, 240, 128, 128);
1618 case VIDEO_MIXER_BACKGROUND_TRANSPARENT:
1619 orc_memset (GST_BUFFER_DATA (outbuf), 0,
1620 gst_video_format_get_row_stride (mix->fmt, 0,
1621 mix->out_width) * mix->out_height);
1625 gst_videomixer_blend_buffers (mix, outbuf);
1627 gst_videomixer_update_queues (mix);
1628 GST_VIDEO_MIXER_STATE_UNLOCK (mix);
1630 ret = gst_pad_push (mix->srcpad, outbuf);
1639 gst_buffer_unref (outbuf);
1641 GST_VIDEO_MIXER_STATE_UNLOCK (mix);
1647 forward_event_func (GstPad * pad, GValue * ret, GstEvent * event)
1649 gst_event_ref (event);
1650 GST_LOG_OBJECT (pad, "About to send event %s", GST_EVENT_TYPE_NAME (event));
1651 if (!gst_pad_push_event (pad, event)) {
1652 g_value_set_boolean (ret, FALSE);
1653 GST_WARNING_OBJECT (pad, "Sending event %p (%s) failed.",
1654 event, GST_EVENT_TYPE_NAME (event));
1656 GST_LOG_OBJECT (pad, "Sent event %p (%s).",
1657 event, GST_EVENT_TYPE_NAME (event));
1659 gst_object_unref (pad);
1663 /* forwards the event to all sinkpads, takes ownership of the
1666 * Returns: TRUE if the event could be forwarded on all
1670 forward_event (GstVideoMixer * mix, GstEvent * event)
1673 GValue vret = { 0 };
1675 GST_LOG_OBJECT (mix, "Forwarding event %p (%s)", event,
1676 GST_EVENT_TYPE_NAME (event));
1678 g_value_init (&vret, G_TYPE_BOOLEAN);
1679 g_value_set_boolean (&vret, TRUE);
1680 it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (mix));
1681 gst_iterator_fold (it, (GstIteratorFoldFunction) forward_event_func, &vret,
1683 gst_iterator_free (it);
1684 gst_event_unref (event);
1686 return g_value_get_boolean (&vret);
1690 gst_videomixer_src_event (GstPad * pad, GstEvent * event)
1692 GstVideoMixer *mix = GST_VIDEO_MIXER (gst_pad_get_parent (pad));
1695 switch (GST_EVENT_TYPE (event)) {
1696 case GST_EVENT_QOS:{
1697 GstClockTimeDiff diff;
1698 GstClockTime timestamp;
1701 gst_event_parse_qos (event, &proportion, &diff, ×tamp);
1703 gst_videomixer_update_qos (mix, proportion, diff, timestamp);
1704 gst_event_unref (event);
1706 /* TODO: The QoS event should be transformed and send upstream */
1710 case GST_EVENT_SEEK:
1713 GstSeekType curtype;
1716 /* parse the seek parameters */
1717 gst_event_parse_seek (event, NULL, NULL, &flags, &curtype,
1720 /* check if we are flushing */
1721 if (flags & GST_SEEK_FLAG_FLUSH) {
1722 /* make sure we accept nothing anymore and return WRONG_STATE */
1723 gst_collect_pads_set_flushing (mix->collect, TRUE);
1725 /* flushing seek, start flush downstream, the flush will be done
1726 * when all pads received a FLUSH_STOP. */
1727 gst_pad_push_event (mix->srcpad, gst_event_new_flush_start ());
1730 /* now wait for the collected to be finished and mark a new
1732 GST_OBJECT_LOCK (mix->collect);
1733 if (curtype == GST_SEEK_TYPE_SET)
1734 mix->segment_position = cur;
1736 mix->segment_position = 0;
1737 mix->sendseg = TRUE;
1739 if (flags & GST_SEEK_FLAG_FLUSH) {
1740 gst_collect_pads_set_flushing (mix->collect, FALSE);
1742 /* we can't send FLUSH_STOP here since upstream could start pushing data
1743 * after we unlock mix->collect.
1744 * We set flush_stop_pending to TRUE instead and send FLUSH_STOP after
1745 * forwarding the seek upstream or from gst_videomixer_collected,
1746 * whichever happens first.
1748 mix->flush_stop_pending = TRUE;
1751 GST_OBJECT_UNLOCK (mix->collect);
1752 gst_videomixer_reset_qos (mix);
1754 result = forward_event (mix, event);
1756 if (g_atomic_int_compare_and_exchange (&mix->flush_stop_pending,
1758 GST_DEBUG_OBJECT (mix, "pending flush stop");
1759 gst_pad_push_event (mix->srcpad, gst_event_new_flush_stop ());
1764 case GST_EVENT_NAVIGATION:
1765 /* navigation is rather pointless. */
1769 /* just forward the rest for now */
1770 result = forward_event (mix, event);
1773 gst_object_unref (mix);
1779 gst_videomixer_sink_event (GstPad * pad, GstEvent * event)
1781 GstVideoMixerPad *vpad = GST_VIDEO_MIXER_PAD (pad);
1782 GstVideoMixer *videomixer = GST_VIDEO_MIXER (gst_pad_get_parent (pad));
1785 GST_DEBUG_OBJECT (pad, "Got %s event on pad %s:%s",
1786 GST_EVENT_TYPE_NAME (event), GST_DEBUG_PAD_NAME (pad));
1788 switch (GST_EVENT_TYPE (event)) {
1789 case GST_EVENT_FLUSH_STOP:
1790 /* mark a pending new segment. This event is synchronized
1791 * with the streaming thread so we can safely update the
1792 * variable without races. It's somewhat weird because we
1793 * assume the collectpads forwarded the FLUSH_STOP past us
1794 * and downstream (using our source pad, the bastard!).
1796 videomixer->sendseg = TRUE;
1797 videomixer->flush_stop_pending = FALSE;
1798 gst_videomixer_reset_qos (videomixer);
1800 /* Reset pad state after FLUSH_STOP */
1801 if (vpad->mixcol->buffer)
1802 gst_buffer_unref (vpad->mixcol->buffer);
1803 vpad->mixcol->buffer = NULL;
1806 case GST_EVENT_NEWSEGMENT:
1807 if (!videomixer->master || vpad == videomixer->master) {
1808 videomixer->sendseg = TRUE;
1809 gst_videomixer_reset_qos (videomixer);
1816 /* now GstCollectPads can take care of the rest, e.g. EOS */
1817 ret = videomixer->collect_event (pad, event);
1819 gst_object_unref (videomixer);
1825 gst_videomixer_get_property (GObject * object,
1826 guint prop_id, GValue * value, GParamSpec * pspec)
1828 GstVideoMixer *mix = GST_VIDEO_MIXER (object);
1831 case PROP_BACKGROUND:
1832 g_value_set_enum (value, mix->background);
1835 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1841 gst_videomixer_set_property (GObject * object,
1842 guint prop_id, const GValue * value, GParamSpec * pspec)
1844 GstVideoMixer *mix = GST_VIDEO_MIXER (object);
1847 case PROP_BACKGROUND:
1848 mix->background = g_value_get_enum (value);
1851 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1856 static GstStateChangeReturn
1857 gst_videomixer_change_state (GstElement * element, GstStateChange transition)
1860 GstStateChangeReturn ret;
1862 g_return_val_if_fail (GST_IS_VIDEO_MIXER (element), GST_STATE_CHANGE_FAILURE);
1864 mix = GST_VIDEO_MIXER (element);
1866 switch (transition) {
1867 case GST_STATE_CHANGE_READY_TO_PAUSED:
1868 GST_LOG_OBJECT (mix, "starting collectpads");
1869 gst_collect_pads_start (mix->collect);
1871 case GST_STATE_CHANGE_PAUSED_TO_READY:
1872 GST_LOG_OBJECT (mix, "stopping collectpads");
1873 gst_collect_pads_stop (mix->collect);
1879 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1881 switch (transition) {
1882 case GST_STATE_CHANGE_PAUSED_TO_READY:
1883 gst_videomixer_reset (mix);
1893 plugin_init (GstPlugin * plugin)
1895 GST_DEBUG_CATEGORY_INIT (gst_videomixer_debug, "videomixer", 0,
1898 gst_video_mixer_init_blend ();
1900 return gst_element_register (plugin, "videomixer", GST_RANK_PRIMARY,
1901 GST_TYPE_VIDEO_MIXER) && gst_videomixer2_register (plugin);
1904 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1907 "Video mixer", plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME,