Branch data Line data Source code
1 : : /* GStreamer input selector
2 : : * Copyright (C) 2003 Julien Moutte <julien@moutte.net>
3 : : * Copyright (C) 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
4 : : * Copyright (C) 2005 Jan Schmidt <thaytan@mad.scientist.com>
5 : : * Copyright (C) 2007 Wim Taymans <wim.taymans@gmail.com>
6 : : * Copyright (C) 2007 Andy Wingo <wingo@pobox.com>
7 : : * Copyright (C) 2008 Nokia Corporation. (contact <stefan.kost@nokia.com>)
8 : : * Copyright (C) 2011 Sebastian Dröge <sebastian.droege@collabora.co.uk>
9 : : *
10 : : * This library is free software; you can redistribute it and/or
11 : : * modify it under the terms of the GNU Library General Public
12 : : * License as published by the Free Software Foundation; either
13 : : * version 2 of the License, or (at your option) any later version.
14 : : *
15 : : * This library is distributed in the hope that it will be useful,
16 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 : : * Library General Public License for more details.
19 : : *
20 : : * You should have received a copy of the GNU Library General Public
21 : : * License along with this library; if not, write to the
22 : : * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23 : : * Boston, MA 02111-1307, USA.
24 : : */
25 : :
26 : : /**
27 : : * SECTION:element-input-selector
28 : : * @see_also: #GstOutputSelector
29 : : *
30 : : * Direct one out of N input streams to the output pad.
31 : : *
32 : : * The input pads are from a GstPad subclass and have additional
33 : : * properties, which users may find useful, namely:
34 : : *
35 : : * <itemizedlist>
36 : : * <listitem>
37 : : * "running-time": Running time of stream on pad (#gint64)
38 : : * </listitem>
39 : : * <listitem>
40 : : * "tags": The currently active tags on the pad (#GstTagList, boxed type)
41 : : * </listitem>
42 : : * <listitem>
43 : : * "active": If the pad is currently active (#gboolean)
44 : : * </listitem>
45 : : * <listitem>
46 : : * "always-ok" : Make an inactive pads return #GST_FLOW_OK instead of
47 : : * #GST_FLOW_NOT_LINKED
48 : : * </listitem>
49 : : * </itemizedlist>
50 : : *
51 : : * Since: 0.10.32
52 : : */
53 : :
54 : : #ifdef HAVE_CONFIG_H
55 : : #include "config.h"
56 : : #endif
57 : :
58 : : #include <string.h>
59 : :
60 : : #include "gstinputselector.h"
61 : :
62 : : GST_DEBUG_CATEGORY_STATIC (input_selector_debug);
63 : : #define GST_CAT_DEFAULT input_selector_debug
64 : :
65 : : #if GLIB_CHECK_VERSION(2, 26, 0)
66 : : #define NOTIFY_MUTEX_LOCK()
67 : : #define NOTIFY_MUTEX_UNLOCK()
68 : : #else
69 : : static GStaticRecMutex notify_mutex = G_STATIC_REC_MUTEX_INIT;
70 : : #define NOTIFY_MUTEX_LOCK() g_static_rec_mutex_lock (¬ify_mutex)
71 : : #define NOTIFY_MUTEX_UNLOCK() g_static_rec_mutex_unlock (¬ify_mutex)
72 : : #endif
73 : :
74 : : #define GST_INPUT_SELECTOR_GET_LOCK(sel) (((GstInputSelector*)(sel))->lock)
75 : : #define GST_INPUT_SELECTOR_GET_COND(sel) (((GstInputSelector*)(sel))->cond)
76 : : #define GST_INPUT_SELECTOR_LOCK(sel) (g_mutex_lock (GST_INPUT_SELECTOR_GET_LOCK(sel)))
77 : : #define GST_INPUT_SELECTOR_UNLOCK(sel) (g_mutex_unlock (GST_INPUT_SELECTOR_GET_LOCK(sel)))
78 : : #define GST_INPUT_SELECTOR_WAIT(sel) (g_cond_wait (GST_INPUT_SELECTOR_GET_COND(sel), \
79 : : GST_INPUT_SELECTOR_GET_LOCK(sel)))
80 : : #define GST_INPUT_SELECTOR_BROADCAST(sel) (g_cond_broadcast (GST_INPUT_SELECTOR_GET_COND(sel)))
81 : :
82 : : static GstStaticPadTemplate gst_input_selector_sink_factory =
83 : : GST_STATIC_PAD_TEMPLATE ("sink%d",
84 : : GST_PAD_SINK,
85 : : GST_PAD_REQUEST,
86 : : GST_STATIC_CAPS_ANY);
87 : :
88 : : static GstStaticPadTemplate gst_input_selector_src_factory =
89 : : GST_STATIC_PAD_TEMPLATE ("src",
90 : : GST_PAD_SRC,
91 : : GST_PAD_ALWAYS,
92 : : GST_STATIC_CAPS_ANY);
93 : :
94 : : enum
95 : : {
96 : : PROP_0,
97 : : PROP_N_PADS,
98 : : PROP_ACTIVE_PAD
99 : : };
100 : :
101 : : #define DEFAULT_PAD_ALWAYS_OK TRUE
102 : :
103 : : enum
104 : : {
105 : : PROP_PAD_0,
106 : : PROP_PAD_RUNNING_TIME,
107 : : PROP_PAD_TAGS,
108 : : PROP_PAD_ACTIVE,
109 : : PROP_PAD_ALWAYS_OK
110 : : };
111 : :
112 : : enum
113 : : {
114 : : /* methods */
115 : : SIGNAL_BLOCK,
116 : : SIGNAL_SWITCH,
117 : : LAST_SIGNAL
118 : : };
119 : : static guint gst_input_selector_signals[LAST_SIGNAL] = { 0 };
120 : :
121 : : static inline gboolean gst_input_selector_is_active_sinkpad (GstInputSelector *
122 : : sel, GstPad * pad);
123 : : static GstPad *gst_input_selector_activate_sinkpad (GstInputSelector * sel,
124 : : GstPad * pad);
125 : : static GstPad *gst_input_selector_get_linked_pad (GstPad * pad,
126 : : gboolean strict);
127 : :
128 : : #define GST_TYPE_SELECTOR_PAD \
129 : : (gst_selector_pad_get_type())
130 : : #define GST_SELECTOR_PAD(obj) \
131 : : (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_SELECTOR_PAD, GstSelectorPad))
132 : : #define GST_SELECTOR_PAD_CLASS(klass) \
133 : : (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_SELECTOR_PAD, GstSelectorPadClass))
134 : : #define GST_IS_SELECTOR_PAD(obj) \
135 : : (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_SELECTOR_PAD))
136 : : #define GST_IS_SELECTOR_PAD_CLASS(klass) \
137 : : (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_SELECTOR_PAD))
138 : : #define GST_SELECTOR_PAD_CAST(obj) \
139 : : ((GstSelectorPad *)(obj))
140 : :
141 : : typedef struct _GstSelectorPad GstSelectorPad;
142 : : typedef struct _GstSelectorPadClass GstSelectorPadClass;
143 : :
144 : : struct _GstSelectorPad
145 : : {
146 : : GstPad parent;
147 : :
148 : : gboolean active; /* when buffer have passed the pad */
149 : : gboolean pushed; /* when buffer was pushed downstream since activation */
150 : : gboolean eos; /* when EOS has been received */
151 : : gboolean eos_sent; /* when EOS was sent downstream */
152 : : gboolean discont; /* after switching we create a discont */
153 : : gboolean flushing; /* set after flush-start and before flush-stop */
154 : : gboolean always_ok;
155 : : GstSegment segment; /* the current segment on the pad */
156 : : GstTagList *tags; /* last tags received on the pad */
157 : :
158 : : gboolean segment_pending;
159 : : };
160 : :
161 : : struct _GstSelectorPadClass
162 : : {
163 : : GstPadClass parent;
164 : : };
165 : :
166 : : static void gst_selector_pad_class_init (GstSelectorPadClass * klass);
167 : : static void gst_selector_pad_init (GstSelectorPad * pad);
168 : : static void gst_selector_pad_finalize (GObject * object);
169 : : static void gst_selector_pad_get_property (GObject * object,
170 : : guint prop_id, GValue * value, GParamSpec * pspec);
171 : : static void gst_selector_pad_set_property (GObject * object,
172 : : guint prop_id, const GValue * value, GParamSpec * pspec);
173 : :
174 : : static GstPadClass *selector_pad_parent_class = NULL;
175 : :
176 : : static gint64 gst_selector_pad_get_running_time (GstSelectorPad * pad);
177 : : static void gst_selector_pad_reset (GstSelectorPad * pad);
178 : : static gboolean gst_selector_pad_event (GstPad * pad, GstEvent * event);
179 : : static GstCaps *gst_selector_pad_getcaps (GstPad * pad);
180 : : static gboolean gst_selector_pad_acceptcaps (GstPad * pad, GstCaps * caps);
181 : : static GstIterator *gst_selector_pad_iterate_linked_pads (GstPad * pad);
182 : : static GstFlowReturn gst_selector_pad_chain (GstPad * pad, GstBuffer * buf);
183 : : static GstFlowReturn gst_selector_pad_bufferalloc (GstPad * pad,
184 : : guint64 offset, guint size, GstCaps * caps, GstBuffer ** buf);
185 : :
186 : : static GType
187 : 24 : gst_selector_pad_get_type (void)
188 : : {
189 : : static volatile gsize selector_pad_type = 0;
190 : : static const GTypeInfo selector_pad_info = {
191 : : sizeof (GstSelectorPadClass),
192 : : NULL,
193 : : NULL,
194 : : (GClassInitFunc) gst_selector_pad_class_init,
195 : : NULL,
196 : : NULL,
197 : : sizeof (GstSelectorPad),
198 : : 0,
199 : : (GInstanceInitFunc) gst_selector_pad_init,
200 : : };
201 : :
202 [ + + ]: 24 : if (g_once_init_enter (&selector_pad_type)) {
203 : 1 : GType tmp = g_type_register_static (GST_TYPE_PAD, "GstSelectorPad",
204 : : &selector_pad_info, 0);
205 : 1 : g_once_init_leave (&selector_pad_type, tmp);
206 : : }
207 : :
208 : 24 : return (GType) selector_pad_type;
209 : : }
210 : :
211 : : static void
212 : 1 : gst_selector_pad_class_init (GstSelectorPadClass * klass)
213 : : {
214 : : GObjectClass *gobject_class;
215 : :
216 : 1 : gobject_class = (GObjectClass *) klass;
217 : :
218 : 1 : selector_pad_parent_class = g_type_class_peek_parent (klass);
219 : :
220 : 1 : gobject_class->finalize = gst_selector_pad_finalize;
221 : :
222 : 1 : gobject_class->get_property = gst_selector_pad_get_property;
223 : 1 : gobject_class->set_property = gst_selector_pad_set_property;
224 : :
225 : 1 : g_object_class_install_property (gobject_class, PROP_PAD_RUNNING_TIME,
226 : : g_param_spec_int64 ("running-time", "Running time",
227 : : "Running time of stream on pad", 0, G_MAXINT64, 0,
228 : : G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
229 : 1 : g_object_class_install_property (gobject_class, PROP_PAD_TAGS,
230 : : g_param_spec_boxed ("tags", "Tags",
231 : : "The currently active tags on the pad", GST_TYPE_TAG_LIST,
232 : : G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
233 : 1 : g_object_class_install_property (gobject_class, PROP_PAD_ACTIVE,
234 : : g_param_spec_boolean ("active", "Active",
235 : : "If the pad is currently active", FALSE,
236 : : G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
237 : : /* FIXME: better property name? */
238 : 1 : g_object_class_install_property (gobject_class, PROP_PAD_ALWAYS_OK,
239 : : g_param_spec_boolean ("always-ok", "Always OK",
240 : : "Make an inactive pad return OK instead of NOT_LINKED",
241 : : DEFAULT_PAD_ALWAYS_OK, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
242 : 1 : }
243 : :
244 : : static void
245 : 24 : gst_selector_pad_init (GstSelectorPad * pad)
246 : : {
247 : 24 : pad->always_ok = DEFAULT_PAD_ALWAYS_OK;
248 : 24 : gst_selector_pad_reset (pad);
249 : 24 : }
250 : :
251 : : static void
252 : 24 : gst_selector_pad_finalize (GObject * object)
253 : : {
254 : : GstSelectorPad *pad;
255 : :
256 : 24 : pad = GST_SELECTOR_PAD_CAST (object);
257 : :
258 [ - + ]: 24 : if (pad->tags)
259 : 0 : gst_tag_list_free (pad->tags);
260 : :
261 : 24 : G_OBJECT_CLASS (selector_pad_parent_class)->finalize (object);
262 : 24 : }
263 : :
264 : : static void
265 : 0 : gst_selector_pad_set_property (GObject * object, guint prop_id,
266 : : const GValue * value, GParamSpec * pspec)
267 : : {
268 : 0 : GstSelectorPad *spad = GST_SELECTOR_PAD_CAST (object);
269 : :
270 [ # # ]: 0 : switch (prop_id) {
271 : : case PROP_PAD_ALWAYS_OK:
272 : 0 : GST_OBJECT_LOCK (object);
273 : 0 : spad->always_ok = g_value_get_boolean (value);
274 : 0 : GST_OBJECT_UNLOCK (object);
275 : 0 : break;
276 : : default:
277 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
278 : 0 : break;
279 : : }
280 : 0 : }
281 : :
282 : : static void
283 : 0 : gst_selector_pad_get_property (GObject * object, guint prop_id,
284 : : GValue * value, GParamSpec * pspec)
285 : : {
286 : 0 : GstSelectorPad *spad = GST_SELECTOR_PAD_CAST (object);
287 : :
288 [ # # # # : 0 : switch (prop_id) {
# ]
289 : : case PROP_PAD_RUNNING_TIME:
290 : 0 : g_value_set_int64 (value, gst_selector_pad_get_running_time (spad));
291 : 0 : break;
292 : : case PROP_PAD_TAGS:
293 : 0 : GST_OBJECT_LOCK (object);
294 : 0 : g_value_set_boxed (value, spad->tags);
295 : 0 : GST_OBJECT_UNLOCK (object);
296 : 0 : break;
297 : : case PROP_PAD_ACTIVE:
298 : : {
299 : : GstInputSelector *sel;
300 : :
301 : 0 : sel = GST_INPUT_SELECTOR (gst_pad_get_parent (spad));
302 : 0 : g_value_set_boolean (value, gst_input_selector_is_active_sinkpad (sel,
303 : : GST_PAD_CAST (spad)));
304 : 0 : gst_object_unref (sel);
305 : 0 : break;
306 : : }
307 : : case PROP_PAD_ALWAYS_OK:
308 : 0 : GST_OBJECT_LOCK (object);
309 : 0 : g_value_set_boolean (value, spad->always_ok);
310 : 0 : GST_OBJECT_UNLOCK (object);
311 : 0 : break;
312 : : default:
313 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
314 : 0 : break;
315 : : }
316 : 0 : }
317 : :
318 : : static gint64
319 : 12 : gst_selector_pad_get_running_time (GstSelectorPad * pad)
320 : : {
321 : 12 : gint64 ret = 0;
322 : :
323 : 12 : GST_OBJECT_LOCK (pad);
324 [ + + ]: 12 : if (pad->active) {
325 : 9 : gint64 last_stop = pad->segment.last_stop;
326 : :
327 [ + - ]: 9 : if (last_stop >= 0)
328 : 9 : ret = gst_segment_to_running_time (&pad->segment, GST_FORMAT_TIME,
329 : : last_stop);
330 : : }
331 : 12 : GST_OBJECT_UNLOCK (pad);
332 : :
333 [ - + ][ # # ]: 12 : GST_DEBUG_OBJECT (pad, "running time: %" GST_TIME_FORMAT,
[ # # ][ # # ]
[ # # ]
334 : : GST_TIME_ARGS (ret));
335 : :
336 : 12 : return ret;
337 : : }
338 : :
339 : : static void
340 : 48 : gst_selector_pad_reset (GstSelectorPad * pad)
341 : : {
342 : 48 : GST_OBJECT_LOCK (pad);
343 : 48 : pad->active = FALSE;
344 : 48 : pad->pushed = FALSE;
345 : 48 : pad->eos = FALSE;
346 : 48 : pad->eos_sent = FALSE;
347 : 48 : pad->segment_pending = FALSE;
348 : 48 : pad->discont = FALSE;
349 : 48 : pad->flushing = FALSE;
350 : 48 : gst_segment_init (&pad->segment, GST_FORMAT_UNDEFINED);
351 : 48 : GST_OBJECT_UNLOCK (pad);
352 : 48 : }
353 : :
354 : : /* strictly get the linked pad from the sinkpad. If the pad is active we return
355 : : * the srcpad else we return NULL */
356 : : static GstIterator *
357 : 0 : gst_selector_pad_iterate_linked_pads (GstPad * pad)
358 : : {
359 : 0 : GstInputSelector *sel = GST_INPUT_SELECTOR (gst_pad_get_parent (pad));
360 : : GstPad *otherpad;
361 : : GstIterator *it;
362 : :
363 : 0 : otherpad = gst_input_selector_get_linked_pad (pad, TRUE);
364 : 0 : it = gst_iterator_new_single (GST_TYPE_PAD, otherpad,
365 : : (GstCopyFunction) gst_object_ref, (GFreeFunc) gst_object_unref);
366 : :
367 [ # # ]: 0 : if (otherpad)
368 : 0 : gst_object_unref (otherpad);
369 : 0 : gst_object_unref (sel);
370 : :
371 : 0 : return it;
372 : : }
373 : :
374 : : static gboolean
375 : 0 : gst_selector_pad_event (GstPad * pad, GstEvent * event)
376 : : {
377 : 0 : gboolean res = TRUE;
378 : : gboolean forward;
379 : : GstInputSelector *sel;
380 : : GstSelectorPad *selpad;
381 : : GstPad *prev_active_sinkpad;
382 : : GstPad *active_sinkpad;
383 : :
384 : 0 : sel = GST_INPUT_SELECTOR (gst_pad_get_parent (pad));
385 : 0 : selpad = GST_SELECTOR_PAD_CAST (pad);
386 : :
387 : 0 : GST_INPUT_SELECTOR_LOCK (sel);
388 : 0 : prev_active_sinkpad = sel->active_sinkpad;
389 : 0 : active_sinkpad = gst_input_selector_activate_sinkpad (sel, pad);
390 : :
391 : : /* only forward if we are dealing with the active sinkpad */
392 : 0 : forward = (pad == active_sinkpad);
393 : 0 : GST_INPUT_SELECTOR_UNLOCK (sel);
394 : :
395 [ # # ][ # # ]: 0 : if (prev_active_sinkpad != active_sinkpad && pad == active_sinkpad) {
396 : : NOTIFY_MUTEX_LOCK ();
397 : 0 : g_object_notify (G_OBJECT (sel), "active-pad");
398 : : NOTIFY_MUTEX_UNLOCK ();
399 : : }
400 : :
401 [ # # # # : 0 : switch (GST_EVENT_TYPE (event)) {
# # ]
402 : : case GST_EVENT_FLUSH_START:
403 : : /* Unblock the pad if it's waiting */
404 : 0 : GST_INPUT_SELECTOR_LOCK (sel);
405 : 0 : selpad->flushing = TRUE;
406 : 0 : GST_INPUT_SELECTOR_BROADCAST (sel);
407 : 0 : GST_INPUT_SELECTOR_UNLOCK (sel);
408 : 0 : break;
409 : : case GST_EVENT_FLUSH_STOP:
410 : 0 : GST_INPUT_SELECTOR_LOCK (sel);
411 : 0 : gst_selector_pad_reset (selpad);
412 : 0 : sel->pending_close = FALSE;
413 : 0 : GST_INPUT_SELECTOR_UNLOCK (sel);
414 : 0 : break;
415 : : case GST_EVENT_NEWSEGMENT:
416 : : {
417 : : gboolean update;
418 : : GstFormat format;
419 : : gdouble rate, arate;
420 : : gint64 start, stop, time;
421 : :
422 : 0 : gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
423 : : &start, &stop, &time);
424 : :
425 [ # # ]: 0 : GST_DEBUG_OBJECT (pad,
426 : : "configured NEWSEGMENT update %d, rate %lf, applied rate %lf, "
427 : : "format %d, "
428 : : "%" G_GINT64_FORMAT " -- %" G_GINT64_FORMAT ", time %"
429 : : G_GINT64_FORMAT, update, rate, arate, format, start, stop, time);
430 : :
431 : 0 : GST_INPUT_SELECTOR_LOCK (sel);
432 : 0 : GST_OBJECT_LOCK (selpad);
433 : 0 : gst_segment_set_newsegment_full (&selpad->segment, update,
434 : : rate, arate, format, start, stop, time);
435 : 0 : GST_OBJECT_UNLOCK (selpad);
436 : :
437 : : /* If we aren't forwarding the event because the pad is not the
438 : : * active_sinkpad, then set the flag on the pad
439 : : * that says a segment needs sending if/when that pad is activated.
440 : : * For all other cases, we send the event immediately, which makes
441 : : * sparse streams and other segment updates work correctly downstream.
442 : : */
443 [ # # ]: 0 : if (!forward)
444 : 0 : selpad->segment_pending = TRUE;
445 : :
446 : 0 : GST_INPUT_SELECTOR_UNLOCK (sel);
447 : 0 : break;
448 : : }
449 : : case GST_EVENT_TAG:
450 : : {
451 : : GstTagList *tags, *oldtags, *newtags;
452 : :
453 : 0 : gst_event_parse_tag (event, &tags);
454 : :
455 : 0 : GST_OBJECT_LOCK (selpad);
456 : 0 : oldtags = selpad->tags;
457 : :
458 : 0 : newtags = gst_tag_list_merge (oldtags, tags, GST_TAG_MERGE_REPLACE);
459 : 0 : selpad->tags = newtags;
460 [ # # ]: 0 : if (oldtags)
461 : 0 : gst_tag_list_free (oldtags);
462 [ # # ]: 0 : GST_DEBUG_OBJECT (pad, "received tags %" GST_PTR_FORMAT, newtags);
463 : 0 : GST_OBJECT_UNLOCK (selpad);
464 : :
465 : 0 : g_object_notify (G_OBJECT (selpad), "tags");
466 : 0 : break;
467 : : }
468 : : case GST_EVENT_EOS:
469 : 0 : selpad->eos = TRUE;
470 : :
471 [ # # ]: 0 : if (forward) {
472 : 0 : selpad->eos_sent = TRUE;
473 : : } else {
474 : : GstSelectorPad *tmp;
475 : :
476 : : /* If the active sinkpad is in EOS state but EOS
477 : : * was not sent downstream this means that the pad
478 : : * got EOS before it was set as active pad and that
479 : : * the previously active pad got EOS after it was
480 : : * active
481 : : */
482 : 0 : GST_INPUT_SELECTOR_LOCK (sel);
483 : 0 : active_sinkpad = gst_input_selector_activate_sinkpad (sel, pad);
484 : 0 : tmp = GST_SELECTOR_PAD (active_sinkpad);
485 [ # # ][ # # ]: 0 : forward = (tmp->eos && !tmp->eos_sent);
486 : 0 : tmp->eos_sent = TRUE;
487 : 0 : GST_INPUT_SELECTOR_UNLOCK (sel);
488 : : }
489 [ # # ]: 0 : GST_DEBUG_OBJECT (pad, "received EOS");
490 : 0 : break;
491 : : default:
492 : 0 : break;
493 : : }
494 [ # # ]: 0 : if (forward) {
495 [ # # ]: 0 : GST_DEBUG_OBJECT (pad, "forwarding event");
496 : 0 : res = gst_pad_push_event (sel->srcpad, event);
497 : : } else
498 : 0 : gst_event_unref (event);
499 : :
500 : 0 : gst_object_unref (sel);
501 : :
502 : 0 : return res;
503 : : }
504 : :
505 : : static GstCaps *
506 : 24 : gst_selector_pad_getcaps (GstPad * pad)
507 : : {
508 : : GstInputSelector *sel;
509 : : GstCaps *caps;
510 : :
511 : 24 : sel = GST_INPUT_SELECTOR (gst_pad_get_parent (pad));
512 : :
513 [ - + ]: 24 : GST_DEBUG_OBJECT (sel, "Getting caps of srcpad peer");
514 : 24 : caps = gst_pad_peer_get_caps_reffed (sel->srcpad);
515 [ - + ]: 24 : if (caps == NULL)
516 : 0 : caps = gst_caps_new_any ();
517 : :
518 : 24 : gst_object_unref (sel);
519 : :
520 : 24 : return caps;
521 : : }
522 : :
523 : : static gboolean
524 : 0 : gst_selector_pad_acceptcaps (GstPad * pad, GstCaps * caps)
525 : : {
526 : : GstInputSelector *sel;
527 : : gboolean res;
528 : :
529 : 0 : sel = GST_INPUT_SELECTOR (gst_pad_get_parent (pad));
530 : :
531 [ # # ]: 0 : GST_DEBUG_OBJECT (sel, "Checking acceptcaps of srcpad peer");
532 : 0 : res = gst_pad_peer_accept_caps (sel->srcpad, caps);
533 : 0 : gst_object_unref (sel);
534 : :
535 : 0 : return res;
536 : : }
537 : :
538 : : static GstFlowReturn
539 : 0 : gst_selector_pad_bufferalloc (GstPad * pad, guint64 offset,
540 : : guint size, GstCaps * caps, GstBuffer ** buf)
541 : : {
542 : : GstInputSelector *sel;
543 : : GstFlowReturn result;
544 : : GstPad *active_sinkpad;
545 : : GstPad *prev_active_sinkpad;
546 : : GstSelectorPad *selpad;
547 : :
548 : 0 : sel = GST_INPUT_SELECTOR (gst_pad_get_parent (pad));
549 : 0 : selpad = GST_SELECTOR_PAD_CAST (pad);
550 : :
551 [ # # ]: 0 : GST_LOG_OBJECT (pad, "received alloc");
552 : :
553 : 0 : GST_INPUT_SELECTOR_LOCK (sel);
554 : 0 : prev_active_sinkpad = sel->active_sinkpad;
555 : 0 : active_sinkpad = gst_input_selector_activate_sinkpad (sel, pad);
556 : :
557 [ # # ]: 0 : if (pad != active_sinkpad)
558 : 0 : goto not_active;
559 : :
560 : 0 : GST_INPUT_SELECTOR_UNLOCK (sel);
561 : :
562 [ # # ][ # # ]: 0 : if (prev_active_sinkpad != active_sinkpad && pad == active_sinkpad) {
563 : : NOTIFY_MUTEX_LOCK ();
564 : 0 : g_object_notify (G_OBJECT (sel), "active-pad");
565 : : NOTIFY_MUTEX_UNLOCK ();
566 : : }
567 : :
568 : 0 : result = gst_pad_alloc_buffer (sel->srcpad, offset, size, caps, buf);
569 : :
570 : : done:
571 : 0 : gst_object_unref (sel);
572 : :
573 : 0 : return result;
574 : :
575 : : /* ERRORS */
576 : : not_active:
577 : : {
578 : 0 : gboolean active_pad_pushed = GST_SELECTOR_PAD_CAST (active_sinkpad)->pushed;
579 : :
580 : 0 : GST_INPUT_SELECTOR_UNLOCK (sel);
581 : :
582 : : /* unselected pad, perform fallback alloc or return unlinked when
583 : : * asked */
584 : 0 : GST_OBJECT_LOCK (selpad);
585 [ # # ][ # # ]: 0 : if (selpad->always_ok || !active_pad_pushed) {
586 [ # # ]: 0 : GST_DEBUG_OBJECT (pad, "Not selected, performing fallback allocation");
587 : 0 : *buf = NULL;
588 : 0 : result = GST_FLOW_OK;
589 : : } else {
590 [ # # ]: 0 : GST_DEBUG_OBJECT (pad, "Not selected, return NOT_LINKED");
591 : 0 : result = GST_FLOW_NOT_LINKED;
592 : : }
593 : 0 : GST_OBJECT_UNLOCK (selpad);
594 : :
595 : 0 : goto done;
596 : : }
597 : : }
598 : :
599 : : /* must be called with the SELECTOR_LOCK, will block while the pad is blocked
600 : : * or return TRUE when flushing */
601 : : static gboolean
602 : 84 : gst_input_selector_wait (GstInputSelector * self, GstSelectorPad * pad)
603 : : {
604 [ - + ][ # # ]: 84 : while (self->blocked && !self->flushing && !pad->flushing) {
[ # # ]
605 : : /* we can be unlocked here when we are shutting down (flushing) or when we
606 : : * get unblocked */
607 : 0 : GST_INPUT_SELECTOR_WAIT (self);
608 : : }
609 : 84 : return self->flushing;
610 : : }
611 : :
612 : : static GstFlowReturn
613 : 84 : gst_selector_pad_chain (GstPad * pad, GstBuffer * buf)
614 : : {
615 : : GstInputSelector *sel;
616 : : GstFlowReturn res;
617 : : GstPad *active_sinkpad;
618 : : GstPad *prev_active_sinkpad;
619 : : GstSelectorPad *selpad;
620 : : GstClockTime start_time;
621 : : GstSegment *seg;
622 : 84 : GstEvent *close_event = NULL, *start_event = NULL;
623 : : GstCaps *caps;
624 : :
625 : 84 : sel = GST_INPUT_SELECTOR (gst_pad_get_parent (pad));
626 : 84 : selpad = GST_SELECTOR_PAD_CAST (pad);
627 : 84 : seg = &selpad->segment;
628 : :
629 : 84 : GST_INPUT_SELECTOR_LOCK (sel);
630 : : /* wait or check for flushing */
631 [ - + ]: 84 : if (gst_input_selector_wait (sel, selpad))
632 : 0 : goto flushing;
633 : :
634 [ - + ]: 84 : GST_LOG_OBJECT (pad, "getting active pad");
635 : :
636 : 84 : prev_active_sinkpad = sel->active_sinkpad;
637 : 84 : active_sinkpad = gst_input_selector_activate_sinkpad (sel, pad);
638 : :
639 : : /* update the segment on the srcpad */
640 : 84 : start_time = GST_BUFFER_TIMESTAMP (buf);
641 [ - + ]: 84 : if (GST_CLOCK_TIME_IS_VALID (start_time)) {
642 [ # # ][ # # ]: 0 : GST_LOG_OBJECT (pad, "received start time %" GST_TIME_FORMAT,
[ # # ][ # # ]
[ # # ]
643 : : GST_TIME_ARGS (start_time));
644 [ # # ]: 0 : if (GST_BUFFER_DURATION_IS_VALID (buf))
645 [ # # ][ # # ]: 0 : GST_LOG_OBJECT (pad, "received end time %" GST_TIME_FORMAT,
[ # # ][ # # ]
[ # # ]
646 : : GST_TIME_ARGS (start_time + GST_BUFFER_DURATION (buf)));
647 : :
648 : 0 : GST_OBJECT_LOCK (pad);
649 : 0 : gst_segment_set_last_stop (seg, seg->format, start_time);
650 : 0 : GST_OBJECT_UNLOCK (pad);
651 : : }
652 : :
653 : : /* Ignore buffers from pads except the selected one */
654 [ + + ]: 84 : if (pad != active_sinkpad)
655 : 48 : goto ignore;
656 : :
657 [ + + ]: 36 : if (G_UNLIKELY (sel->pending_close)) {
658 : 9 : GstSegment *cseg = &sel->segment;
659 : :
660 [ - + ]: 9 : GST_DEBUG_OBJECT (sel,
661 : : "pushing close NEWSEGMENT update %d, rate %lf, applied rate %lf, "
662 : : "format %d, "
663 : : "%" G_GINT64_FORMAT " -- %" G_GINT64_FORMAT ", time %"
664 : : G_GINT64_FORMAT, TRUE, cseg->rate, cseg->applied_rate, cseg->format,
665 : : cseg->start, cseg->stop, cseg->time);
666 : :
667 : : /* create update segment */
668 : 9 : close_event = gst_event_new_new_segment_full (TRUE, cseg->rate,
669 : : cseg->applied_rate, cseg->format, cseg->start, cseg->stop, cseg->time);
670 : :
671 : 9 : sel->pending_close = FALSE;
672 : : }
673 : : /* if we have a pending segment, push it out now */
674 [ + + ]: 36 : if (G_UNLIKELY (selpad->segment_pending)) {
675 [ - + ]: 9 : GST_DEBUG_OBJECT (pad,
676 : : "pushing pending NEWSEGMENT update %d, rate %lf, applied rate %lf, "
677 : : "format %d, "
678 : : "%" G_GINT64_FORMAT " -- %" G_GINT64_FORMAT ", time %"
679 : : G_GINT64_FORMAT, FALSE, seg->rate, seg->applied_rate, seg->format,
680 : : seg->start, seg->stop, seg->time);
681 : :
682 : 9 : start_event = gst_event_new_new_segment_full (FALSE, seg->rate,
683 : : seg->applied_rate, seg->format, seg->start, seg->stop, seg->time);
684 : :
685 : 9 : selpad->segment_pending = FALSE;
686 : : }
687 : 36 : GST_INPUT_SELECTOR_UNLOCK (sel);
688 : :
689 [ - + ][ # # ]: 36 : if (prev_active_sinkpad != active_sinkpad && pad == active_sinkpad) {
690 : : NOTIFY_MUTEX_LOCK ();
691 : 0 : g_object_notify (G_OBJECT (sel), "active-pad");
692 : : NOTIFY_MUTEX_UNLOCK ();
693 : : }
694 : :
695 [ + + ]: 36 : if (close_event)
696 : 9 : gst_pad_push_event (sel->srcpad, close_event);
697 : :
698 [ + + ]: 36 : if (start_event)
699 : 9 : gst_pad_push_event (sel->srcpad, start_event);
700 : :
701 [ + + ]: 36 : if (selpad->discont) {
702 : 9 : buf = gst_buffer_make_metadata_writable (buf);
703 : :
704 [ - + ]: 9 : GST_DEBUG_OBJECT (pad, "Marking discont buffer %p", buf);
705 : 9 : GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
706 : 9 : selpad->discont = FALSE;
707 : : }
708 : :
709 : : /* forward */
710 [ - + ]: 36 : GST_LOG_OBJECT (pad, "Forwarding buffer %p", buf);
711 : :
712 [ + - ]: 36 : if ((caps = GST_BUFFER_CAPS (buf))) {
713 [ + + ]: 36 : if (GST_PAD_CAPS (sel->srcpad) != caps)
714 : 9 : gst_pad_set_caps (sel->srcpad, caps);
715 : : }
716 : :
717 : 36 : res = gst_pad_push (sel->srcpad, buf);
718 : 36 : selpad->pushed = TRUE;
719 : :
720 : : done:
721 : 84 : gst_object_unref (sel);
722 : 84 : return res;
723 : :
724 : : /* dropped buffers */
725 : : ignore:
726 : : {
727 : 48 : gboolean active_pad_pushed = GST_SELECTOR_PAD_CAST (active_sinkpad)->pushed;
728 : :
729 [ - + ]: 48 : GST_DEBUG_OBJECT (pad, "Pad not active, discard buffer %p", buf);
730 : : /* when we drop a buffer, we're creating a discont on this pad */
731 : 48 : selpad->discont = TRUE;
732 : 48 : GST_INPUT_SELECTOR_UNLOCK (sel);
733 : 48 : gst_buffer_unref (buf);
734 : :
735 : : /* figure out what to return upstream */
736 : 48 : GST_OBJECT_LOCK (selpad);
737 [ - + ][ # # ]: 48 : if (selpad->always_ok || !active_pad_pushed)
738 : 48 : res = GST_FLOW_OK;
739 : : else
740 : 0 : res = GST_FLOW_NOT_LINKED;
741 : 48 : GST_OBJECT_UNLOCK (selpad);
742 : :
743 : 48 : goto done;
744 : : }
745 : : flushing:
746 : : {
747 [ # # ]: 0 : GST_DEBUG_OBJECT (pad, "We are flushing, discard buffer %p", buf);
748 : 0 : GST_INPUT_SELECTOR_UNLOCK (sel);
749 : 0 : gst_buffer_unref (buf);
750 : 0 : res = GST_FLOW_WRONG_STATE;
751 : 0 : goto done;
752 : : }
753 : : }
754 : :
755 : : static void gst_input_selector_dispose (GObject * object);
756 : :
757 : : static void gst_input_selector_set_property (GObject * object,
758 : : guint prop_id, const GValue * value, GParamSpec * pspec);
759 : : static void gst_input_selector_get_property (GObject * object,
760 : : guint prop_id, GValue * value, GParamSpec * pspec);
761 : :
762 : : static GstPad *gst_input_selector_request_new_pad (GstElement * element,
763 : : GstPadTemplate * templ, const gchar * unused);
764 : : static void gst_input_selector_release_pad (GstElement * element, GstPad * pad);
765 : :
766 : : static GstStateChangeReturn gst_input_selector_change_state (GstElement *
767 : : element, GstStateChange transition);
768 : :
769 : : static GstCaps *gst_input_selector_getcaps (GstPad * pad);
770 : : static gboolean gst_input_selector_event (GstPad * pad, GstEvent * event);
771 : : static gboolean gst_input_selector_query (GstPad * pad, GstQuery * query);
772 : : static gint64 gst_input_selector_block (GstInputSelector * self);
773 : : static void gst_input_selector_switch (GstInputSelector * self,
774 : : GstPad * pad, gint64 stop_time, gint64 start_time);
775 : :
776 : : /* FIXME: create these marshallers using glib-genmarshal */
777 : : #define g_marshal_value_peek_object(v) (v)->data[0].v_pointer
778 : : #define g_marshal_value_peek_int64(v) (v)->data[0].v_int64
779 : :
780 : : static void
781 : 0 : gst_input_selector_marshal_INT64__VOID (GClosure * closure,
782 : : GValue * return_value G_GNUC_UNUSED,
783 : : guint n_param_values,
784 : : const GValue * param_values,
785 : : gpointer invocation_hint G_GNUC_UNUSED, gpointer marshal_data)
786 : : {
787 : : typedef gint64 (*GMarshalFunc_INT64__VOID) (gpointer data1, gpointer data2);
788 : : register GMarshalFunc_INT64__VOID callback;
789 : 0 : register GCClosure *cc = (GCClosure *) closure;
790 : : register gpointer data1, data2;
791 : : gint64 v_return;
792 : :
793 [ # # ]: 0 : g_return_if_fail (return_value != NULL);
794 [ # # ]: 0 : g_return_if_fail (n_param_values == 1);
795 : :
796 [ # # ]: 0 : if (G_CCLOSURE_SWAP_DATA (closure)) {
797 : 0 : data1 = closure->data;
798 : 0 : data2 = g_value_peek_pointer (param_values + 0);
799 : : } else {
800 : 0 : data1 = g_value_peek_pointer (param_values + 0);
801 : 0 : data2 = closure->data;
802 : : }
803 : 0 : callback =
804 [ # # ]: 0 : (GMarshalFunc_INT64__VOID) (marshal_data ? marshal_data : cc->callback);
805 : :
806 : 0 : v_return = callback (data1, data2);
807 : :
808 : 0 : g_value_set_int64 (return_value, v_return);
809 : : }
810 : :
811 : : static void
812 : 0 : gst_input_selector_marshal_VOID__OBJECT_INT64_INT64 (GClosure * closure,
813 : : GValue * return_value G_GNUC_UNUSED,
814 : : guint n_param_values,
815 : : const GValue * param_values,
816 : : gpointer invocation_hint G_GNUC_UNUSED, gpointer marshal_data)
817 : : {
818 : : typedef void (*GMarshalFunc_VOID__OBJECT_INT64_INT64) (gpointer data1,
819 : : gpointer arg_1, gint64 arg_2, gint64 arg_3, gpointer data2);
820 : : register GMarshalFunc_VOID__OBJECT_INT64_INT64 callback;
821 : 0 : register GCClosure *cc = (GCClosure *) closure;
822 : : register gpointer data1, data2;
823 : :
824 [ # # ]: 0 : g_return_if_fail (n_param_values == 4);
825 : :
826 [ # # ]: 0 : if (G_CCLOSURE_SWAP_DATA (closure)) {
827 : 0 : data1 = closure->data;
828 : 0 : data2 = g_value_peek_pointer (param_values + 0);
829 : : } else {
830 : 0 : data1 = g_value_peek_pointer (param_values + 0);
831 : 0 : data2 = closure->data;
832 : : }
833 : 0 : callback =
834 [ # # ]: 0 : (GMarshalFunc_VOID__OBJECT_INT64_INT64) (marshal_data ? marshal_data :
835 : : cc->callback);
836 : :
837 : 0 : callback (data1,
838 : 0 : g_marshal_value_peek_object (param_values + 1),
839 : 0 : g_marshal_value_peek_int64 (param_values + 2),
840 : 0 : g_marshal_value_peek_int64 (param_values + 3), data2);
841 : : }
842 : :
843 : : #define _do_init(bla) \
844 : : GST_DEBUG_CATEGORY_INIT (input_selector_debug, \
845 : : "input-selector", 0, "An input stream selector element");
846 : :
847 [ + + ][ + - ]: 539 : GST_BOILERPLATE_FULL (GstInputSelector, gst_input_selector, GstElement,
848 : 539 : GST_TYPE_ELEMENT, _do_init);
849 : :
850 : : static void
851 : 9 : gst_input_selector_base_init (gpointer g_class)
852 : : {
853 : 9 : GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
854 : :
855 : 9 : gst_element_class_set_details_simple (element_class, "Input selector",
856 : : "Generic", "N-to-1 input stream selector",
857 : : "Julien Moutte <julien@moutte.net>, "
858 : : "Jan Schmidt <thaytan@mad.scientist.com>, "
859 : : "Wim Taymans <wim.taymans@gmail.com>");
860 : 9 : gst_element_class_add_pad_template (element_class,
861 : : gst_static_pad_template_get (&gst_input_selector_sink_factory));
862 : 9 : gst_element_class_add_pad_template (element_class,
863 : : gst_static_pad_template_get (&gst_input_selector_src_factory));
864 : 9 : }
865 : :
866 : : static void
867 : 9 : gst_input_selector_class_init (GstInputSelectorClass * klass)
868 : : {
869 : 9 : GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
870 : 9 : GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
871 : :
872 : 9 : gobject_class->dispose = gst_input_selector_dispose;
873 : :
874 : 9 : gobject_class->set_property = gst_input_selector_set_property;
875 : 9 : gobject_class->get_property = gst_input_selector_get_property;
876 : :
877 : 9 : g_object_class_install_property (gobject_class, PROP_N_PADS,
878 : : g_param_spec_uint ("n-pads", "Number of Pads",
879 : : "The number of sink pads", 0, G_MAXUINT, 0,
880 : : G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
881 : :
882 : 9 : g_object_class_install_property (gobject_class, PROP_ACTIVE_PAD,
883 : : g_param_spec_object ("active-pad", "Active pad",
884 : : "The currently active sink pad", GST_TYPE_PAD,
885 : : G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
886 : :
887 : : /**
888 : : * GstInputSelector::block:
889 : : * @inputselector: the #GstInputSelector
890 : : *
891 : : * Block all sink pads in preparation for a switch. Returns the stop time of
892 : : * the current switch segment, as a running time, or 0 if there is no current
893 : : * active pad or the current active pad never received data.
894 : : */
895 : 9 : gst_input_selector_signals[SIGNAL_BLOCK] =
896 : 9 : g_signal_new ("block", G_TYPE_FROM_CLASS (klass),
897 : : G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
898 : : G_STRUCT_OFFSET (GstInputSelectorClass, block), NULL, NULL,
899 : : gst_input_selector_marshal_INT64__VOID, G_TYPE_INT64, 0);
900 : : /**
901 : : * GstInputSelector::switch:
902 : : * @inputselector: the #GstInputSelector
903 : : * @pad: the pad to switch to
904 : : * @stop_time: running time at which to close the previous segment, or -1
905 : : * to use the running time of the previously active sink pad
906 : : * @start_time: running time at which to start the new segment, or -1 to
907 : : * use the running time of the newly active sink pad
908 : : *
909 : : * Switch to a new feed. The segment opened by the previously active pad, if
910 : : * any, will be closed, and a new segment opened before data flows again.
911 : : *
912 : : * This signal must be emitted when the element has been blocked via the <link
913 : : * linkend="GstInputSelector-block">block</link> signal.
914 : : *
915 : : * If you have a stream with only one switch element, such as an audio-only
916 : : * stream, a stream switch should be performed by first emitting the block
917 : : * signal, and then emitting the switch signal with -1 for the stop and start
918 : : * time values.
919 : : *
920 : : * The intention of the @stop_time and @start_time arguments is to allow
921 : : * multiple switch elements to switch and maintain stream synchronization.
922 : : * When switching a stream with multiple feeds, you will need as many switch
923 : : * elements as you have feeds. For example, a feed with audio and video will
924 : : * have one switch element between the audio feeds and one for video.
925 : : *
926 : : * A switch over multiple switch elements should be performed as follows:
927 : : * First, emit the <link linkend="GstInputSelector-block">block</link>
928 : : * signal, collecting the returned values. The maximum running time returned
929 : : * by block should then be used as the time at which to close the previous
930 : : * segment.
931 : : *
932 : : * Then, query the running times of the new audio and video pads that you will
933 : : * switch to. Naturally, these pads are on separate switch elements. Take the
934 : : * minimum running time for those streams and use it for the time at which to
935 : : * open the new segment.
936 : : *
937 : : * If @pad is the same as the current active pad, the element will cancel any
938 : : * previous block without adjusting segments.
939 : : *
940 : : * <note><simpara>
941 : : * the signal changed from accepting the pad name to the pad object.
942 : : * </simpara></note>
943 : : *
944 : : * Since: 0.10.7
945 : : */
946 : 9 : gst_input_selector_signals[SIGNAL_SWITCH] =
947 : 9 : g_signal_new ("switch", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
948 : : G_STRUCT_OFFSET (GstInputSelectorClass, switch_),
949 : : NULL, NULL, gst_input_selector_marshal_VOID__OBJECT_INT64_INT64,
950 : : G_TYPE_NONE, 3, GST_TYPE_PAD, G_TYPE_INT64, G_TYPE_INT64);
951 : :
952 : 9 : gstelement_class->request_new_pad = gst_input_selector_request_new_pad;
953 : 9 : gstelement_class->release_pad = gst_input_selector_release_pad;
954 : 9 : gstelement_class->change_state = gst_input_selector_change_state;
955 : :
956 : 9 : klass->block = GST_DEBUG_FUNCPTR (gst_input_selector_block);
957 : : /* note the underscore because switch is a keyword otherwise */
958 : 9 : klass->switch_ = GST_DEBUG_FUNCPTR (gst_input_selector_switch);
959 : 9 : }
960 : :
961 : : static void
962 : 19 : gst_input_selector_init (GstInputSelector * sel,
963 : : GstInputSelectorClass * g_class)
964 : : {
965 : 19 : sel->srcpad = gst_pad_new ("src", GST_PAD_SRC);
966 : 19 : gst_pad_set_iterate_internal_links_function (sel->srcpad,
967 : 19 : GST_DEBUG_FUNCPTR (gst_selector_pad_iterate_linked_pads));
968 : 19 : gst_pad_set_getcaps_function (sel->srcpad,
969 : 19 : GST_DEBUG_FUNCPTR (gst_input_selector_getcaps));
970 : 19 : gst_pad_set_query_function (sel->srcpad,
971 : 19 : GST_DEBUG_FUNCPTR (gst_input_selector_query));
972 : 19 : gst_pad_set_event_function (sel->srcpad,
973 : 19 : GST_DEBUG_FUNCPTR (gst_input_selector_event));
974 : 19 : gst_element_add_pad (GST_ELEMENT (sel), sel->srcpad);
975 : : /* sinkpad management */
976 : 19 : sel->active_sinkpad = NULL;
977 : 19 : sel->padcount = 0;
978 : 19 : gst_segment_init (&sel->segment, GST_FORMAT_UNDEFINED);
979 : :
980 : 19 : sel->lock = g_mutex_new ();
981 : 19 : sel->cond = g_cond_new ();
982 : 19 : sel->blocked = FALSE;
983 : 19 : }
984 : :
985 : : static void
986 : 19 : gst_input_selector_dispose (GObject * object)
987 : : {
988 : 19 : GstInputSelector *sel = GST_INPUT_SELECTOR (object);
989 : :
990 [ - + ]: 19 : if (sel->active_sinkpad) {
991 : 0 : gst_object_unref (sel->active_sinkpad);
992 : 0 : sel->active_sinkpad = NULL;
993 : : }
994 [ + - ]: 19 : if (sel->lock) {
995 : 19 : g_mutex_free (sel->lock);
996 : 19 : sel->lock = NULL;
997 : : }
998 [ + - ]: 19 : if (sel->cond) {
999 : 19 : g_cond_free (sel->cond);
1000 : 19 : sel->cond = NULL;
1001 : : }
1002 : :
1003 : 19 : G_OBJECT_CLASS (parent_class)->dispose (object);
1004 : 19 : }
1005 : :
1006 : : /* Solve the following equation for B.timestamp, and set that as the segment
1007 : : * stop:
1008 : : * B.running_time = (B.timestamp - NS.start) / NS.abs_rate + NS.accum
1009 : : */
1010 : : static gint64
1011 : 18 : gst_segment_get_timestamp (GstSegment * segment, gint64 running_time)
1012 : : {
1013 [ + - ]: 18 : if (running_time <= segment->accum)
1014 : 18 : return segment->start;
1015 : : else
1016 : 18 : return (running_time - segment->accum) * segment->abs_rate + segment->start;
1017 : : }
1018 : :
1019 : : static void
1020 : 9 : gst_segment_set_stop (GstSegment * segment, gint64 running_time)
1021 : : {
1022 : 9 : segment->stop = gst_segment_get_timestamp (segment, running_time);
1023 : 9 : segment->last_stop = -1;
1024 : 9 : }
1025 : :
1026 : : static void
1027 : 9 : gst_segment_set_start (GstSegment * segment, gint64 running_time)
1028 : : {
1029 : : gint64 new_start, duration;
1030 : :
1031 : 9 : new_start = gst_segment_get_timestamp (segment, running_time);
1032 : :
1033 : : /* this is the duration we skipped */
1034 : 9 : duration = new_start - segment->start;
1035 : : /* add the duration to the accumulated segment time */
1036 : 9 : segment->accum += duration;
1037 : : /* move position in the segment */
1038 : 9 : segment->time += duration;
1039 : 9 : segment->start += duration;
1040 : 9 : }
1041 : :
1042 : : /* this function must be called with the SELECTOR_LOCK. It returns TRUE when the
1043 : : * active pad changed. */
1044 : : static gboolean
1045 : 40 : gst_input_selector_set_active_pad (GstInputSelector * self,
1046 : : GstPad * pad, gint64 stop_time, gint64 start_time)
1047 : : {
1048 : : GstSelectorPad *old, *new;
1049 : : GstPad **active_pad_p;
1050 : :
1051 [ + + ]: 40 : if (pad == self->active_sinkpad)
1052 : 16 : return FALSE;
1053 : :
1054 : 24 : old = GST_SELECTOR_PAD_CAST (self->active_sinkpad);
1055 : 24 : new = GST_SELECTOR_PAD_CAST (pad);
1056 : :
1057 [ - + ][ # # ]: 24 : GST_DEBUG_OBJECT (self, "setting active pad to %s:%s",
[ # # ][ # # ]
[ # # ][ # # ]
1058 : : GST_DEBUG_PAD_NAME (new));
1059 : :
1060 [ + - ][ + + ]: 24 : if (!GST_CLOCK_TIME_IS_VALID (stop_time) && old) {
1061 : : /* no stop time given, get the latest running_time on the active pad to
1062 : : * close and open the new segment */
1063 : 12 : stop_time = start_time = gst_selector_pad_get_running_time (old);
1064 [ - + ][ # # ]: 12 : GST_DEBUG_OBJECT (self, "using start/stop of %" GST_TIME_FORMAT,
[ # # ][ # # ]
[ # # ]
1065 : : GST_TIME_ARGS (start_time));
1066 : : }
1067 : :
1068 [ + + ][ + + ]: 24 : if (old && old->active && !self->pending_close && stop_time >= 0) {
[ + - ][ + - ]
1069 : : /* schedule a last_stop update if one isn't already scheduled, and a
1070 : : segment has been pushed before. */
1071 : 9 : memcpy (&self->segment, &old->segment, sizeof (self->segment));
1072 : :
1073 [ - + ][ # # ]: 9 : GST_DEBUG_OBJECT (self, "setting stop_time to %" GST_TIME_FORMAT,
[ # # ][ # # ]
[ # # ]
1074 : : GST_TIME_ARGS (stop_time));
1075 : 9 : gst_segment_set_stop (&self->segment, stop_time);
1076 : 9 : self->pending_close = TRUE;
1077 : : }
1078 [ + + ]: 24 : if (old)
1079 : 12 : old->pushed = FALSE;
1080 : :
1081 [ + - ][ + + ]: 24 : if (new && new->active && start_time >= 0) {
[ + - ]
1082 [ - + ][ # # ]: 9 : GST_DEBUG_OBJECT (self, "setting start_time to %" GST_TIME_FORMAT,
[ # # ][ # # ]
[ # # ]
1083 : : GST_TIME_ARGS (start_time));
1084 : : /* schedule a new segment push */
1085 : 9 : gst_segment_set_start (&new->segment, start_time);
1086 : 9 : new->segment_pending = TRUE;
1087 : : }
1088 [ + - ]: 24 : if (new)
1089 : 24 : new->pushed = FALSE;
1090 : :
1091 : 24 : active_pad_p = &self->active_sinkpad;
1092 : 24 : gst_object_replace ((GstObject **) active_pad_p, GST_OBJECT_CAST (pad));
1093 [ - + ]: 24 : GST_DEBUG_OBJECT (self, "New active pad is %" GST_PTR_FORMAT,
1094 : : self->active_sinkpad);
1095 : :
1096 : 40 : return TRUE;
1097 : : }
1098 : :
1099 : : static void
1100 : 40 : gst_input_selector_set_property (GObject * object, guint prop_id,
1101 : : const GValue * value, GParamSpec * pspec)
1102 : : {
1103 : 40 : GstInputSelector *sel = GST_INPUT_SELECTOR (object);
1104 : :
1105 [ + - ]: 40 : switch (prop_id) {
1106 : : case PROP_ACTIVE_PAD:
1107 : : {
1108 : : GstPad *pad;
1109 : :
1110 : 40 : pad = g_value_get_object (value);
1111 : :
1112 : 40 : GST_INPUT_SELECTOR_LOCK (sel);
1113 : 40 : gst_input_selector_set_active_pad (sel, pad,
1114 : : GST_CLOCK_TIME_NONE, GST_CLOCK_TIME_NONE);
1115 : 40 : GST_INPUT_SELECTOR_UNLOCK (sel);
1116 : 40 : break;
1117 : : }
1118 : : default:
1119 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1120 : 0 : break;
1121 : : }
1122 : 40 : }
1123 : :
1124 : : static void
1125 : 0 : gst_input_selector_get_property (GObject * object, guint prop_id,
1126 : : GValue * value, GParamSpec * pspec)
1127 : : {
1128 : 0 : GstInputSelector *sel = GST_INPUT_SELECTOR (object);
1129 : :
1130 [ # # # ]: 0 : switch (prop_id) {
1131 : : case PROP_N_PADS:
1132 : 0 : GST_INPUT_SELECTOR_LOCK (object);
1133 : 0 : g_value_set_uint (value, sel->n_pads);
1134 : 0 : GST_INPUT_SELECTOR_UNLOCK (object);
1135 : 0 : break;
1136 : : case PROP_ACTIVE_PAD:
1137 : 0 : GST_INPUT_SELECTOR_LOCK (object);
1138 : 0 : g_value_set_object (value, sel->active_sinkpad);
1139 : 0 : GST_INPUT_SELECTOR_UNLOCK (object);
1140 : 0 : break;
1141 : : default:
1142 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1143 : 0 : break;
1144 : : }
1145 : 0 : }
1146 : :
1147 : : static GstPad *
1148 : 16 : gst_input_selector_get_linked_pad (GstPad * pad, gboolean strict)
1149 : : {
1150 : : GstInputSelector *sel;
1151 : 16 : GstPad *otherpad = NULL;
1152 : :
1153 : 16 : sel = GST_INPUT_SELECTOR (gst_pad_get_parent (pad));
1154 : :
1155 : 16 : GST_INPUT_SELECTOR_LOCK (sel);
1156 [ + - ]: 16 : if (pad == sel->srcpad)
1157 : 16 : otherpad = sel->active_sinkpad;
1158 [ # # ][ # # ]: 0 : else if (pad == sel->active_sinkpad || !strict)
1159 : 0 : otherpad = sel->srcpad;
1160 [ - + ]: 16 : if (otherpad)
1161 : 0 : gst_object_ref (otherpad);
1162 : 16 : GST_INPUT_SELECTOR_UNLOCK (sel);
1163 : :
1164 : 16 : gst_object_unref (sel);
1165 : :
1166 : 16 : return otherpad;
1167 : : }
1168 : :
1169 : : static gboolean
1170 : 0 : gst_input_selector_event (GstPad * pad, GstEvent * event)
1171 : : {
1172 : 0 : gboolean res = FALSE;
1173 : : GstPad *otherpad;
1174 : :
1175 : 0 : otherpad = gst_input_selector_get_linked_pad (pad, TRUE);
1176 : :
1177 [ # # ]: 0 : if (otherpad) {
1178 : 0 : res = gst_pad_push_event (otherpad, event);
1179 : :
1180 : 0 : gst_object_unref (otherpad);
1181 : : } else
1182 : 0 : gst_event_unref (event);
1183 : 0 : return res;
1184 : : }
1185 : :
1186 : : /* query on the srcpad. We override this function because by default it will
1187 : : * only forward the query to one random sinkpad */
1188 : : static gboolean
1189 : 0 : gst_input_selector_query (GstPad * pad, GstQuery * query)
1190 : : {
1191 : 0 : gboolean res = TRUE;
1192 : : GstInputSelector *sel;
1193 : : GstPad *otherpad;
1194 : :
1195 : 0 : sel = GST_INPUT_SELECTOR (gst_pad_get_parent (pad));
1196 : :
1197 : 0 : otherpad = gst_input_selector_get_linked_pad (pad, TRUE);
1198 : :
1199 [ # # ]: 0 : switch (GST_QUERY_TYPE (query)) {
1200 : : case GST_QUERY_LATENCY:
1201 : : {
1202 : : GList *walk;
1203 : : GstClockTime resmin, resmax;
1204 : : gboolean reslive;
1205 : :
1206 : 0 : resmin = 0;
1207 : 0 : resmax = -1;
1208 : 0 : reslive = FALSE;
1209 : :
1210 : : /* assume FALSE, we become TRUE if one query succeeds */
1211 : 0 : res = FALSE;
1212 : :
1213 : : /* perform the query on all sinkpads and combine the results. We take the
1214 : : * max of min and the min of max for the result latency. */
1215 : 0 : GST_INPUT_SELECTOR_LOCK (sel);
1216 [ # # ]: 0 : for (walk = GST_ELEMENT_CAST (sel)->sinkpads; walk;
1217 [ # # ]: 0 : walk = g_list_next (walk)) {
1218 : 0 : GstPad *sinkpad = GST_PAD_CAST (walk->data);
1219 : :
1220 [ # # ]: 0 : if (gst_pad_peer_query (sinkpad, query)) {
1221 : : GstClockTime min, max;
1222 : : gboolean live;
1223 : :
1224 : : /* one query succeeded, we succeed too */
1225 : 0 : res = TRUE;
1226 : :
1227 : 0 : gst_query_parse_latency (query, &live, &min, &max);
1228 : :
1229 [ # # ][ # # ]: 0 : GST_DEBUG_OBJECT (sinkpad,
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
1230 : : "peer latency min %" GST_TIME_FORMAT ", max %" GST_TIME_FORMAT
1231 : : ", live %d", GST_TIME_ARGS (min), GST_TIME_ARGS (max), live);
1232 : :
1233 [ # # ]: 0 : if (live) {
1234 [ # # ]: 0 : if (min > resmin)
1235 : 0 : resmin = min;
1236 [ # # ]: 0 : if (resmax == -1)
1237 : 0 : resmax = max;
1238 [ # # ]: 0 : else if (max < resmax)
1239 : 0 : resmax = max;
1240 [ # # ]: 0 : if (reslive == FALSE)
1241 : 0 : reslive = live;
1242 : : }
1243 : : }
1244 : : }
1245 : 0 : GST_INPUT_SELECTOR_UNLOCK (sel);
1246 [ # # ]: 0 : if (res) {
1247 : 0 : gst_query_set_latency (query, reslive, resmin, resmax);
1248 : :
1249 [ # # ][ # # ]: 0 : GST_DEBUG_OBJECT (sel,
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
1250 : : "total latency min %" GST_TIME_FORMAT ", max %" GST_TIME_FORMAT
1251 : : ", live %d", GST_TIME_ARGS (resmin), GST_TIME_ARGS (resmax),
1252 : : reslive);
1253 : : }
1254 : :
1255 : 0 : break;
1256 : : }
1257 : : default:
1258 [ # # ]: 0 : if (otherpad)
1259 : 0 : res = gst_pad_peer_query (otherpad, query);
1260 : 0 : break;
1261 : : }
1262 [ # # ]: 0 : if (otherpad)
1263 : 0 : gst_object_unref (otherpad);
1264 : 0 : gst_object_unref (sel);
1265 : :
1266 : 0 : return res;
1267 : : }
1268 : :
1269 : : static GstCaps *
1270 : 16 : gst_input_selector_getcaps (GstPad * pad)
1271 : : {
1272 : : GstPad *otherpad;
1273 : : GstObject *parent;
1274 : : GstCaps *caps;
1275 : :
1276 : 16 : parent = gst_object_get_parent (GST_OBJECT (pad));
1277 : :
1278 : 16 : otherpad = gst_input_selector_get_linked_pad (pad, FALSE);
1279 : :
1280 [ + - ]: 16 : if (!otherpad) {
1281 [ - + ]: 16 : GST_DEBUG_OBJECT (pad, "Pad not linked, returning ANY");
1282 : 16 : caps = gst_caps_new_any ();
1283 : : } else {
1284 [ # # ][ # # ]: 0 : GST_DEBUG_OBJECT (pad, "Pad is linked (to %s:%s), returning peer caps",
[ # # ][ # # ]
[ # # ][ # # ]
1285 : : GST_DEBUG_PAD_NAME (otherpad));
1286 : : /* if the peer has caps, use those. If the pad is not linked, this function
1287 : : * returns NULL and we return ANY */
1288 [ # # ]: 0 : if (!(caps = gst_pad_peer_get_caps_reffed (otherpad)))
1289 : 0 : caps = gst_caps_new_any ();
1290 : 0 : gst_object_unref (otherpad);
1291 : : }
1292 : :
1293 : 16 : gst_object_unref (parent);
1294 : 16 : return caps;
1295 : : }
1296 : :
1297 : : /* check if the pad is the active sinkpad */
1298 : : static inline gboolean
1299 : 0 : gst_input_selector_is_active_sinkpad (GstInputSelector * sel, GstPad * pad)
1300 : : {
1301 : : gboolean res;
1302 : :
1303 : 0 : GST_INPUT_SELECTOR_LOCK (sel);
1304 : 0 : res = (pad == sel->active_sinkpad);
1305 : 0 : GST_INPUT_SELECTOR_UNLOCK (sel);
1306 : :
1307 : 0 : return res;
1308 : : }
1309 : :
1310 : : /* Get or create the active sinkpad, must be called with SELECTOR_LOCK */
1311 : : static GstPad *
1312 : 84 : gst_input_selector_activate_sinkpad (GstInputSelector * sel, GstPad * pad)
1313 : : {
1314 : : GstPad *active_sinkpad;
1315 : : GstSelectorPad *selpad;
1316 : :
1317 : 84 : selpad = GST_SELECTOR_PAD_CAST (pad);
1318 : :
1319 : 84 : selpad->active = TRUE;
1320 : 84 : active_sinkpad = sel->active_sinkpad;
1321 [ - + ]: 84 : if (active_sinkpad == NULL) {
1322 : : /* first pad we get activity on becomes the activated pad by default */
1323 [ # # ]: 0 : if (sel->active_sinkpad)
1324 : 0 : gst_object_unref (sel->active_sinkpad);
1325 : 0 : active_sinkpad = sel->active_sinkpad = gst_object_ref (pad);
1326 [ # # ][ # # ]: 0 : GST_DEBUG_OBJECT (sel, "Activating pad %s:%s", GST_DEBUG_PAD_NAME (pad));
[ # # ][ # # ]
[ # # ][ # # ]
1327 : : }
1328 : :
1329 : 84 : return active_sinkpad;
1330 : : }
1331 : :
1332 : : static GstPad *
1333 : 24 : gst_input_selector_request_new_pad (GstElement * element,
1334 : : GstPadTemplate * templ, const gchar * unused)
1335 : : {
1336 : : GstInputSelector *sel;
1337 : 24 : gchar *name = NULL;
1338 : 24 : GstPad *sinkpad = NULL;
1339 : :
1340 [ - + ]: 24 : g_return_val_if_fail (templ->direction == GST_PAD_SINK, NULL);
1341 : :
1342 : 24 : sel = GST_INPUT_SELECTOR (element);
1343 : :
1344 : 24 : GST_INPUT_SELECTOR_LOCK (sel);
1345 : :
1346 [ - + ]: 24 : GST_LOG_OBJECT (sel, "Creating new pad %d", sel->padcount);
1347 : 24 : name = g_strdup_printf ("sink%d", sel->padcount++);
1348 : 24 : sinkpad = g_object_new (GST_TYPE_SELECTOR_PAD,
1349 : 24 : "name", name, "direction", templ->direction, "template", templ, NULL);
1350 : 24 : g_free (name);
1351 : :
1352 : 24 : sel->n_pads++;
1353 : :
1354 : 24 : gst_pad_set_event_function (sinkpad,
1355 : 24 : GST_DEBUG_FUNCPTR (gst_selector_pad_event));
1356 : 24 : gst_pad_set_getcaps_function (sinkpad,
1357 : 24 : GST_DEBUG_FUNCPTR (gst_selector_pad_getcaps));
1358 : 24 : gst_pad_set_acceptcaps_function (sinkpad,
1359 : 24 : GST_DEBUG_FUNCPTR (gst_selector_pad_acceptcaps));
1360 : 24 : gst_pad_set_chain_function (sinkpad,
1361 : 24 : GST_DEBUG_FUNCPTR (gst_selector_pad_chain));
1362 : 24 : gst_pad_set_iterate_internal_links_function (sinkpad,
1363 : 24 : GST_DEBUG_FUNCPTR (gst_selector_pad_iterate_linked_pads));
1364 : 24 : gst_pad_set_bufferalloc_function (sinkpad,
1365 : 24 : GST_DEBUG_FUNCPTR (gst_selector_pad_bufferalloc));
1366 : :
1367 : 24 : gst_pad_set_active (sinkpad, TRUE);
1368 : 24 : gst_element_add_pad (GST_ELEMENT (sel), sinkpad);
1369 : 24 : GST_INPUT_SELECTOR_UNLOCK (sel);
1370 : :
1371 : 24 : return sinkpad;
1372 : : }
1373 : :
1374 : : static void
1375 : 24 : gst_input_selector_release_pad (GstElement * element, GstPad * pad)
1376 : : {
1377 : : GstInputSelector *sel;
1378 : :
1379 : 24 : sel = GST_INPUT_SELECTOR (element);
1380 [ - + ][ # # ]: 24 : GST_LOG_OBJECT (sel, "Releasing pad %s:%s", GST_DEBUG_PAD_NAME (pad));
[ # # ][ # # ]
[ # # ][ # # ]
1381 : :
1382 : 24 : GST_INPUT_SELECTOR_LOCK (sel);
1383 : : /* if the pad was the active pad, makes us select a new one */
1384 [ - + ]: 24 : if (sel->active_sinkpad == pad) {
1385 [ # # ][ # # ]: 0 : GST_DEBUG_OBJECT (sel, "Deactivating pad %s:%s", GST_DEBUG_PAD_NAME (pad));
[ # # ][ # # ]
[ # # ][ # # ]
1386 : 0 : gst_object_unref (sel->active_sinkpad);
1387 : 0 : sel->active_sinkpad = NULL;
1388 : : }
1389 : 24 : sel->n_pads--;
1390 : :
1391 : 24 : gst_pad_set_active (pad, FALSE);
1392 : 24 : gst_element_remove_pad (GST_ELEMENT (sel), pad);
1393 : 24 : GST_INPUT_SELECTOR_UNLOCK (sel);
1394 : 24 : }
1395 : :
1396 : : static void
1397 : 23 : gst_input_selector_reset (GstInputSelector * sel)
1398 : : {
1399 : : GList *walk;
1400 : :
1401 : 23 : GST_INPUT_SELECTOR_LOCK (sel);
1402 : : /* clear active pad */
1403 [ + + ]: 23 : if (sel->active_sinkpad) {
1404 : 12 : gst_object_unref (sel->active_sinkpad);
1405 : 12 : sel->active_sinkpad = NULL;
1406 : : }
1407 : : /* reset segment */
1408 : 23 : gst_segment_init (&sel->segment, GST_FORMAT_UNDEFINED);
1409 : 23 : sel->pending_close = FALSE;
1410 : : /* reset each of our sinkpads state */
1411 [ + - ][ + + ]: 47 : for (walk = GST_ELEMENT_CAST (sel)->sinkpads; walk; walk = g_list_next (walk)) {
1412 : 24 : GstSelectorPad *selpad = GST_SELECTOR_PAD_CAST (walk->data);
1413 : :
1414 : 24 : gst_selector_pad_reset (selpad);
1415 : :
1416 [ - + ]: 24 : if (selpad->tags) {
1417 : 0 : gst_tag_list_free (selpad->tags);
1418 : 0 : selpad->tags = NULL;
1419 : : }
1420 : : }
1421 : 23 : GST_INPUT_SELECTOR_UNLOCK (sel);
1422 : 23 : }
1423 : :
1424 : : static GstStateChangeReturn
1425 : 146 : gst_input_selector_change_state (GstElement * element,
1426 : : GstStateChange transition)
1427 : : {
1428 : 146 : GstInputSelector *self = GST_INPUT_SELECTOR (element);
1429 : : GstStateChangeReturn result;
1430 : :
1431 [ + + + ]: 146 : switch (transition) {
1432 : : case GST_STATE_CHANGE_READY_TO_PAUSED:
1433 : 23 : GST_INPUT_SELECTOR_LOCK (self);
1434 : 23 : self->blocked = FALSE;
1435 : 23 : self->flushing = FALSE;
1436 : 23 : GST_INPUT_SELECTOR_UNLOCK (self);
1437 : 23 : break;
1438 : : case GST_STATE_CHANGE_PAUSED_TO_READY:
1439 : : /* first unlock before we call the parent state change function, which
1440 : : * tries to acquire the stream lock when going to ready. */
1441 : 23 : GST_INPUT_SELECTOR_LOCK (self);
1442 : 23 : self->blocked = FALSE;
1443 : 23 : self->flushing = TRUE;
1444 : 23 : GST_INPUT_SELECTOR_BROADCAST (self);
1445 : 23 : GST_INPUT_SELECTOR_UNLOCK (self);
1446 : 23 : break;
1447 : : default:
1448 : 100 : break;
1449 : : }
1450 : :
1451 : 146 : result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1452 : :
1453 [ + + ]: 146 : switch (transition) {
1454 : : case GST_STATE_CHANGE_PAUSED_TO_READY:
1455 : 23 : gst_input_selector_reset (self);
1456 : 23 : break;
1457 : : default:
1458 : 123 : break;
1459 : : }
1460 : :
1461 : 146 : return result;
1462 : : }
1463 : :
1464 : : static gint64
1465 : 0 : gst_input_selector_block (GstInputSelector * self)
1466 : : {
1467 : 0 : gint64 ret = 0;
1468 : : GstSelectorPad *spad;
1469 : :
1470 : 0 : GST_INPUT_SELECTOR_LOCK (self);
1471 : :
1472 [ # # ]: 0 : if (self->blocked)
1473 [ # # ]: 0 : GST_WARNING_OBJECT (self, "switch already blocked");
1474 : :
1475 : 0 : self->blocked = TRUE;
1476 : 0 : spad = GST_SELECTOR_PAD_CAST (self->active_sinkpad);
1477 : :
1478 [ # # ]: 0 : if (spad)
1479 : 0 : ret = gst_selector_pad_get_running_time (spad);
1480 : : else
1481 [ # # ]: 0 : GST_DEBUG_OBJECT (self, "no active pad while blocking");
1482 : :
1483 : 0 : GST_INPUT_SELECTOR_UNLOCK (self);
1484 : :
1485 : 0 : return ret;
1486 : : }
1487 : :
1488 : : /* stop_time and start_time are running times */
1489 : : static void
1490 : 0 : gst_input_selector_switch (GstInputSelector * self, GstPad * pad,
1491 : : gint64 stop_time, gint64 start_time)
1492 : : {
1493 : : gboolean changed;
1494 : :
1495 [ # # ]: 0 : g_return_if_fail (self->blocked == TRUE);
1496 : :
1497 : 0 : GST_INPUT_SELECTOR_LOCK (self);
1498 : 0 : changed =
1499 : : gst_input_selector_set_active_pad (self, pad, stop_time, start_time);
1500 : :
1501 : 0 : self->blocked = FALSE;
1502 : 0 : GST_INPUT_SELECTOR_BROADCAST (self);
1503 : 0 : GST_INPUT_SELECTOR_UNLOCK (self);
1504 : :
1505 [ # # ]: 0 : if (changed) {
1506 : : NOTIFY_MUTEX_LOCK ();
1507 : 0 : g_object_notify (G_OBJECT (self), "active-pad");
1508 : : NOTIFY_MUTEX_UNLOCK ();
1509 : : }
1510 : : }
|