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