Line data Source code
1 : /* SPDX-License-Identifier: LGPL-2.1-only */
2 : /**
3 : * Copyright (C) 2020 Gichan Jang <gichan2.jang@samsung.com>
4 : */
5 : /**
6 : * @file gstjoin.c
7 : * @date 10 Nov 2020
8 : * @brief Select the out that arrived first among the input streams
9 : * @see https://github.com/nnstreamer/nnstreamer
10 : * @author Gichan Jang <gichan2.jang@samsung.com>
11 : * @bug No known bugs except for NYI items
12 : */
13 : /**
14 : * SECTION:element-join
15 : * @see_also #GstInputSelector
16 : * @note A join has reduced and changed input-selector's function.
17 : *
18 : * Connect recently arrived buffer from N input streams to the output pad.
19 : * N streams should not operate at the same time.
20 : * All capabilities (input stream i and output stream) should be the same.
21 : * For example, If one sinkpad is receiving buffer, the others should be stopped.
22 : * <refsect2>
23 : * <title>Example launch line</title>
24 : * gst-launch-1.0 ... (input stream 0) ! join.sink_0 \
25 : * ... (input stream 1) ! join.sink_1 \
26 : * ... \
27 : ... (input stream N) ! join.sink_n \
28 : join name=join ! (arrived input stream) ...
29 : * </refsect2>
30 : */
31 :
32 : #include "gstjoin.h"
33 :
34 : GST_DEBUG_CATEGORY_STATIC (join_debug);
35 : #define GST_CAT_DEFAULT join_debug
36 :
37 : #define GST_JOIN_GET_LOCK(sel) (&((GstJoin*)(sel))->lock)
38 : #define GST_JOIN_GET_COND(sel) (&((GstJoin*)(sel))->cond)
39 : #define GST_JOIN_LOCK(sel) (g_mutex_lock (GST_JOIN_GET_LOCK(sel)))
40 : #define GST_JOIN_UNLOCK(sel) (g_mutex_unlock (GST_JOIN_GET_LOCK(sel)))
41 : #define GST_JOIN_WAIT(sel) (g_cond_wait (GST_JOIN_GET_COND(sel), \
42 : GST_JOIN_GET_LOCK(sel)))
43 :
44 : /**
45 : * @brief The capabilities of the inputs
46 : */
47 : static GstStaticPadTemplate gst_join_sink_factory =
48 : GST_STATIC_PAD_TEMPLATE ("sink_%u",
49 : GST_PAD_SINK,
50 : GST_PAD_REQUEST,
51 : GST_STATIC_CAPS_ANY);
52 :
53 : /**
54 : * @brief The capabilities of the outputs
55 : */
56 : static GstStaticPadTemplate gst_join_src_factory =
57 : GST_STATIC_PAD_TEMPLATE ("src",
58 : GST_PAD_SRC,
59 : GST_PAD_ALWAYS,
60 : GST_STATIC_CAPS_ANY);
61 :
62 : enum
63 : {
64 : PROP_0,
65 : PROP_N_PADS,
66 : PROP_ACTIVE_PAD,
67 : };
68 :
69 : static GstPad *gst_join_get_active_sinkpad (GstJoin * sel);
70 : static GstPad *gst_join_get_linked_pad (GstJoin * sel,
71 : GstPad * pad, gboolean strict);
72 : static gboolean gst_join_set_active_pad (GstJoin * self, GstPad * pad);
73 :
74 : #define GST_TYPE_JOIN_PAD \
75 : (gst_join_pad_get_type())
76 : #define GST_JOIN_PAD(obj) \
77 : (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_JOIN_PAD, GstJoinPad))
78 : #define GST_JOIN_PAD_CLASS(klass) \
79 : (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_JOIN_PAD, GstJoinPadClass))
80 : #define GST_IS_JOIN_PAD(obj) \
81 : (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_JOIN_PAD))
82 : #define GST_IS_JOIN_PAD_CLASS(klass) \
83 : (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_JOIN_PAD))
84 : #define GST_JOIN_PAD_CAST(obj) \
85 : ((GstJoinPad *)(obj))
86 :
87 : typedef struct _GstJoinPad GstJoinPad;
88 : typedef struct _GstJoinPadClass GstJoinPadClass;
89 :
90 : /**
91 : * @brief GstJoinPad data structure.
92 : */
93 : struct _GstJoinPad
94 : {
95 : GstPad parent;
96 :
97 : guint group_id; /* Group ID from the last stream-start */
98 :
99 : GstSegment segment; /* the current segment on the pad */
100 : guint32 segment_seqnum; /* sequence number of the current segment */
101 : };
102 :
103 : /**
104 : * @brief _GstJoinPadClass data structure
105 : */
106 : struct _GstJoinPadClass
107 : {
108 : GstPadClass parent;
109 : };
110 :
111 : GType gst_join_pad_get_type (void);
112 : static void gst_join_pad_finalize (GObject * object);
113 : static void gst_join_pad_reset (GstJoinPad * pad);
114 : static gboolean gst_join_pad_event (GstPad * pad, GstObject * parent,
115 : GstEvent * event);
116 : static gboolean gst_join_pad_query (GstPad * pad, GstObject * parent,
117 : GstQuery * query);
118 : static GstIterator *gst_join_pad_iterate_linked_pads (GstPad * pad,
119 : GstObject * parent);
120 : static GstFlowReturn gst_join_pad_chain (GstPad * pad, GstObject * parent,
121 : GstBuffer * buf);
122 :
123 300 : G_DEFINE_TYPE (GstJoinPad, gst_join_pad, GST_TYPE_PAD);
124 :
125 : /**
126 : * @brief initialize the join's pad class
127 : */
128 : static void
129 7 : gst_join_pad_class_init (GstJoinPadClass * klass)
130 : {
131 : GObjectClass *gobject_class;
132 :
133 7 : gobject_class = (GObjectClass *) klass;
134 :
135 7 : gobject_class->finalize = gst_join_pad_finalize;
136 7 : }
137 :
138 : /**
139 : * @brief initialize the join pad
140 : */
141 : static void
142 36 : gst_join_pad_init (GstJoinPad * pad)
143 : {
144 36 : gst_join_pad_reset (pad);
145 36 : }
146 :
147 : /**
148 : * @brief finalize the join pad
149 : */
150 : static void
151 24 : gst_join_pad_finalize (GObject * object)
152 : {
153 24 : G_OBJECT_CLASS (gst_join_pad_parent_class)->finalize (object);
154 24 : }
155 :
156 : /**
157 : * @brief Clear and reset join pad.
158 : * @note must be called with the JOIN_LOCK
159 : */
160 : static void
161 36 : gst_join_pad_reset (GstJoinPad * pad)
162 : {
163 36 : GST_OBJECT_LOCK (pad);
164 36 : gst_segment_init (&pad->segment, GST_FORMAT_UNDEFINED);
165 36 : GST_OBJECT_UNLOCK (pad);
166 36 : }
167 :
168 : /**
169 : * @brief strictly get the linked pad from the sinkpad.
170 : * @return If the pad is active, return the srcpad else return NULL.
171 : */
172 : static GstIterator *
173 142 : gst_join_pad_iterate_linked_pads (GstPad * pad, GstObject * parent)
174 : {
175 : GstJoin *sel;
176 : GstPad *otherpad;
177 142 : GstIterator *it = NULL;
178 142 : GValue val = { 0, };
179 :
180 142 : sel = GST_JOIN (parent);
181 :
182 142 : otherpad = gst_join_get_linked_pad (sel, pad, TRUE);
183 142 : if (otherpad) {
184 37 : g_value_init (&val, GST_TYPE_PAD);
185 37 : g_value_set_object (&val, otherpad);
186 37 : it = gst_iterator_new_single (GST_TYPE_PAD, &val);
187 37 : g_value_unset (&val);
188 37 : gst_object_unref (otherpad);
189 : }
190 :
191 142 : return it;
192 : }
193 :
194 : /**
195 : * @brief forward sticky event
196 : */
197 : static gboolean
198 367 : forward_sticky_events (GstPad * sinkpad, GstEvent ** event, gpointer user_data)
199 : {
200 367 : GstJoin *sel = GST_JOIN (user_data);
201 :
202 367 : GST_DEBUG_OBJECT (sinkpad, "forward sticky event %" GST_PTR_FORMAT, *event);
203 :
204 367 : if (GST_EVENT_TYPE (*event) == GST_EVENT_SEGMENT) {
205 125 : GstSegment *seg = &GST_JOIN_PAD (sinkpad)->segment;
206 : GstEvent *e;
207 :
208 125 : e = gst_event_new_segment (seg);
209 125 : gst_event_set_seqnum (e, GST_JOIN_PAD_CAST (sinkpad)->segment_seqnum);
210 :
211 125 : gst_pad_push_event (sel->srcpad, e);
212 242 : } else if (GST_EVENT_TYPE (*event) == GST_EVENT_STREAM_START
213 117 : && !sel->have_group_id) {
214 : GstEvent *tmp =
215 0 : gst_pad_get_sticky_event (sel->srcpad, GST_EVENT_STREAM_START, 0);
216 :
217 : /* Only push stream-start once if not all our streams have a stream-id */
218 0 : if (!tmp) {
219 0 : gst_pad_push_event (sel->srcpad, gst_event_ref (*event));
220 : } else {
221 0 : gst_event_unref (tmp);
222 : }
223 : } else {
224 242 : gst_pad_push_event (sel->srcpad, gst_event_ref (*event));
225 : }
226 367 : return TRUE;
227 : }
228 :
229 : /**
230 : * @brief event function for sink pad
231 : */
232 : static gboolean
233 116 : gst_join_pad_event (GstPad * pad, GstObject * parent, GstEvent * event)
234 : {
235 116 : gboolean res = TRUE;
236 : gboolean forward;
237 : GstJoin *sel;
238 : GstJoinPad *selpad;
239 : GstPad *active_sinkpad;
240 :
241 116 : sel = GST_JOIN (parent);
242 116 : selpad = GST_JOIN_PAD_CAST (pad);
243 116 : GST_DEBUG_OBJECT (selpad, "received event %" GST_PTR_FORMAT, event);
244 :
245 116 : GST_JOIN_LOCK (sel);
246 :
247 116 : active_sinkpad = gst_join_get_active_sinkpad (sel);
248 :
249 : /* only forward if we are dealing with the active sinkpad */
250 116 : forward = (pad == active_sinkpad);
251 :
252 116 : switch (GST_EVENT_TYPE (event)) {
253 34 : case GST_EVENT_CAPS:
254 : {
255 : GstCaps *prev_caps, *new_caps;
256 :
257 34 : if (!(prev_caps = gst_pad_get_current_caps (active_sinkpad)))
258 34 : break;
259 :
260 15 : gst_event_parse_caps (event, &new_caps);
261 :
262 15 : if (!gst_caps_is_equal (prev_caps, new_caps)) {
263 3 : GST_ERROR_OBJECT (sel, "Capabilities of the sinks should be the same.");
264 3 : res = FALSE;
265 : }
266 :
267 15 : gst_caps_unref (prev_caps);
268 :
269 15 : break;
270 : }
271 26 : case GST_EVENT_STREAM_START:{
272 26 : if (!gst_event_parse_group_id (event, &selpad->group_id)) {
273 0 : sel->have_group_id = FALSE;
274 0 : selpad->group_id = 0;
275 : }
276 26 : break;
277 : }
278 31 : case GST_EVENT_SEGMENT:
279 : {
280 31 : gst_event_copy_segment (event, &selpad->segment);
281 31 : selpad->segment_seqnum = gst_event_get_seqnum (event);
282 :
283 31 : GST_DEBUG_OBJECT (pad, "configured SEGMENT %" GST_SEGMENT_FORMAT,
284 : &selpad->segment);
285 31 : break;
286 : }
287 25 : default:
288 25 : break;
289 : }
290 116 : GST_JOIN_UNLOCK (sel);
291 :
292 116 : if (forward) {
293 39 : GST_DEBUG_OBJECT (pad, "forwarding event");
294 39 : res = gst_pad_push_event (sel->srcpad, event);
295 : } else {
296 77 : gst_event_unref (event);
297 : }
298 :
299 115 : return res;
300 : }
301 :
302 : /**
303 : * @brief handlesink sink pad query
304 : * @return TRUE if the query was performed successfully.
305 : */
306 : static gboolean
307 573 : gst_join_pad_query (GstPad * pad, GstObject * parent, GstQuery * query)
308 : {
309 573 : gboolean res = FALSE;
310 573 : GstJoin *self = (GstJoin *) parent;
311 :
312 573 : switch (GST_QUERY_TYPE (query)) {
313 512 : case GST_QUERY_CAPS:
314 : case GST_QUERY_POSITION:
315 : case GST_QUERY_DURATION:
316 : case GST_QUERY_CONTEXT:
317 : /**
318 : * always proxy caps/position/duration/context queries, regardless of active pad or not
319 : * See https://bugzilla.gnome.org/show_bug.cgi?id=775445
320 : */
321 512 : res = gst_pad_peer_query (self->srcpad, query);
322 512 : break;
323 4 : case GST_QUERY_ALLOCATION:{
324 : GstPad *active_sinkpad;
325 4 : GstJoin *sel = GST_JOIN (parent);
326 :
327 : /**
328 : * Only do the allocation query for the active sinkpad,
329 : * after switching a reconfigure event is sent and upstream
330 : * should reconfigure and do a new allocation query
331 : */
332 4 : if (GST_PAD_DIRECTION (pad) == GST_PAD_SINK) {
333 4 : GST_JOIN_LOCK (sel);
334 4 : active_sinkpad = gst_join_get_active_sinkpad (sel);
335 4 : GST_JOIN_UNLOCK (sel);
336 :
337 4 : if (pad != active_sinkpad) {
338 1 : res = FALSE;
339 1 : goto done;
340 : }
341 : }
342 : }
343 : /* fall through */
344 : default:
345 60 : res = gst_pad_query_default (pad, parent, query);
346 60 : break;
347 : }
348 :
349 573 : done:
350 573 : return res;
351 : }
352 :
353 : /**
354 : * @brief Chain function, this function does the actual processing.
355 : */
356 : static GstFlowReturn
357 139 : gst_join_pad_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
358 : {
359 : GstJoin *sel;
360 : GstFlowReturn res;
361 : GstPad *active_sinkpad;
362 139 : GstPad *prev_active_sinkpad = NULL;
363 : GstJoinPad *selpad;
364 :
365 139 : sel = GST_JOIN (parent);
366 139 : selpad = GST_JOIN_PAD_CAST (pad);
367 139 : GST_DEBUG_OBJECT (selpad,
368 : "entering chain for buf %p with timestamp %" GST_TIME_FORMAT, buf,
369 : GST_TIME_ARGS (GST_BUFFER_PTS (buf)));
370 :
371 139 : GST_JOIN_LOCK (sel);
372 :
373 139 : GST_LOG_OBJECT (pad, "getting active pad");
374 :
375 139 : prev_active_sinkpad =
376 139 : sel->active_sinkpad ? gst_object_ref (sel->active_sinkpad) : NULL;
377 :
378 139 : if (sel->active_sinkpad != pad) {
379 125 : gst_join_set_active_pad (sel, pad);
380 : }
381 139 : active_sinkpad = pad;
382 :
383 : /* update the segment on the srcpad */
384 139 : if (GST_BUFFER_PTS_IS_VALID (buf)) {
385 137 : GstClockTime start_time = GST_BUFFER_PTS (buf);
386 :
387 137 : GST_LOG_OBJECT (pad, "received start time %" GST_TIME_FORMAT,
388 : GST_TIME_ARGS (start_time));
389 137 : if (GST_BUFFER_DURATION_IS_VALID (buf))
390 125 : GST_LOG_OBJECT (pad, "received end time %" GST_TIME_FORMAT,
391 : GST_TIME_ARGS (start_time + GST_BUFFER_DURATION (buf)));
392 :
393 137 : GST_OBJECT_LOCK (pad);
394 137 : selpad->segment.position = start_time;
395 137 : GST_OBJECT_UNLOCK (pad);
396 : }
397 :
398 139 : GST_JOIN_UNLOCK (sel);
399 :
400 : /* if we have a pending events, push them now */
401 139 : if (G_UNLIKELY (prev_active_sinkpad != active_sinkpad)) {
402 125 : gst_pad_sticky_events_foreach (GST_PAD_CAST (selpad), forward_sticky_events,
403 : sel);
404 : }
405 :
406 : /* forward */
407 139 : GST_LOG_OBJECT (pad, "Forwarding buffer %p with timestamp %" GST_TIME_FORMAT,
408 : buf, GST_TIME_ARGS (GST_BUFFER_PTS (buf)));
409 :
410 139 : res = gst_pad_push (sel->srcpad, buf);
411 139 : GST_LOG_OBJECT (pad, "Buffer %p forwarded result=%d", buf, res);
412 :
413 139 : if (prev_active_sinkpad)
414 139 : gst_object_unref (prev_active_sinkpad);
415 139 : prev_active_sinkpad = NULL;
416 :
417 139 : return res;
418 : }
419 :
420 : static void gst_join_dispose (GObject * object);
421 : static void gst_join_finalize (GObject * object);
422 :
423 : static void gst_join_get_property (GObject * object,
424 : guint prop_id, GValue * value, GParamSpec * pspec);
425 : static GstPad *gst_join_request_new_pad (GstElement * element,
426 : GstPadTemplate * templ, const gchar * unused, const GstCaps * caps);
427 :
428 : #define gst_join_parent_class parent_class
429 908 : G_DEFINE_TYPE_WITH_CODE (GstJoin, gst_join, GST_TYPE_ELEMENT,
430 : GST_DEBUG_CATEGORY_INIT (join_debug,
431 : "join", 0, "An input stream join element"));
432 :
433 : /**
434 : * @brief initialize the join's class
435 : */
436 : static void
437 27 : gst_join_class_init (GstJoinClass * klass)
438 : {
439 27 : GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
440 27 : GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
441 :
442 27 : gobject_class->dispose = gst_join_dispose;
443 27 : gobject_class->finalize = gst_join_finalize;
444 :
445 27 : gobject_class->get_property = gst_join_get_property;
446 :
447 27 : g_object_class_install_property (gobject_class, PROP_N_PADS,
448 : g_param_spec_uint ("n-pads", "Number of Pads",
449 : "The number of sink pads", 0, G_MAXUINT, 0,
450 : G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
451 :
452 27 : g_object_class_install_property (gobject_class, PROP_ACTIVE_PAD,
453 : g_param_spec_object ("active-pad", "Active pad",
454 : "The currently active sink pad", GST_TYPE_PAD,
455 : G_PARAM_READABLE | GST_PARAM_MUTABLE_PLAYING |
456 : G_PARAM_STATIC_STRINGS));
457 :
458 27 : gst_element_class_set_static_metadata (gstelement_class, "Input join",
459 : "Generic", "N-to-1 input stream join",
460 : "Gichan Jang <gichan2.jang@samsung.com>, ");
461 :
462 27 : gst_element_class_add_static_pad_template (gstelement_class,
463 : &gst_join_sink_factory);
464 27 : gst_element_class_add_static_pad_template (gstelement_class,
465 : &gst_join_src_factory);
466 :
467 27 : gstelement_class->request_new_pad = gst_join_request_new_pad;
468 27 : }
469 :
470 : /**
471 : * @brief initialize the join element
472 : */
473 : static void
474 14 : gst_join_init (GstJoin * sel)
475 : {
476 14 : sel->srcpad = gst_pad_new ("src", GST_PAD_SRC);
477 14 : gst_pad_set_iterate_internal_links_function (sel->srcpad,
478 : GST_DEBUG_FUNCPTR (gst_join_pad_iterate_linked_pads));
479 14 : GST_OBJECT_FLAG_SET (sel->srcpad, GST_PAD_FLAG_PROXY_CAPS);
480 14 : gst_element_add_pad (GST_ELEMENT (sel), sel->srcpad);
481 : /* sinkpad management */
482 14 : sel->active_sinkpad = NULL;
483 14 : sel->padcount = 0;
484 14 : sel->have_group_id = TRUE;
485 :
486 14 : g_mutex_init (&sel->lock);
487 14 : g_cond_init (&sel->cond);
488 14 : }
489 :
490 : /**
491 : * @brief dispose function for join element
492 : */
493 : static void
494 10 : gst_join_dispose (GObject * object)
495 : {
496 10 : GstJoin *sel = GST_JOIN (object);
497 :
498 10 : if (sel->active_sinkpad) {
499 8 : gst_object_unref (sel->active_sinkpad);
500 8 : sel->active_sinkpad = NULL;
501 : }
502 10 : G_OBJECT_CLASS (parent_class)->dispose (object);
503 10 : }
504 :
505 : /**
506 : * @brief finalize join element.
507 : */
508 : static void
509 10 : gst_join_finalize (GObject * object)
510 : {
511 10 : GstJoin *sel = GST_JOIN (object);
512 :
513 10 : g_mutex_clear (&sel->lock);
514 10 : g_cond_clear (&sel->cond);
515 :
516 10 : G_OBJECT_CLASS (parent_class)->finalize (object);
517 10 : }
518 :
519 : /**
520 : * @brief set active sink pad.
521 : * @return TRUE when the active pad changed.
522 : * @note this function must be called with the JOIN_LOCK.
523 : */
524 : static gboolean
525 125 : gst_join_set_active_pad (GstJoin * self, GstPad * pad)
526 : {
527 : GstJoinPad *old, *new;
528 : GstPad **active_pad_p;
529 :
530 125 : if (pad == self->active_sinkpad)
531 0 : return FALSE;
532 :
533 : /* guard against users setting a src pad or foreign pad as active pad */
534 125 : if (pad != NULL) {
535 125 : g_return_val_if_fail (GST_PAD_IS_SINK (pad), FALSE);
536 125 : g_return_val_if_fail (GST_IS_JOIN_PAD (pad), FALSE);
537 125 : g_return_val_if_fail (GST_PAD_PARENT (pad) == GST_ELEMENT_CAST (self),
538 : FALSE);
539 : }
540 :
541 125 : old = GST_JOIN_PAD_CAST (self->active_sinkpad);
542 125 : new = GST_JOIN_PAD_CAST (pad);
543 :
544 125 : GST_DEBUG_OBJECT (self, "setting active pad to %s:%s",
545 : GST_DEBUG_PAD_NAME (new));
546 :
547 125 : active_pad_p = &self->active_sinkpad;
548 125 : gst_object_replace ((GstObject **) active_pad_p, GST_OBJECT_CAST (pad));
549 :
550 125 : if (old && old != new)
551 125 : gst_pad_push_event (GST_PAD_CAST (old), gst_event_new_reconfigure ());
552 125 : if (new)
553 125 : gst_pad_push_event (GST_PAD_CAST (new), gst_event_new_reconfigure ());
554 :
555 125 : GST_DEBUG_OBJECT (self, "New active pad is %" GST_PTR_FORMAT,
556 : self->active_sinkpad);
557 :
558 125 : return TRUE;
559 : }
560 :
561 : /**
562 : * @brief Getter for join properties.
563 : */
564 : static void
565 3 : gst_join_get_property (GObject * object, guint prop_id,
566 : GValue * value, GParamSpec * pspec)
567 : {
568 3 : GstJoin *sel = GST_JOIN (object);
569 :
570 3 : switch (prop_id) {
571 1 : case PROP_N_PADS:
572 1 : GST_JOIN_LOCK (object);
573 1 : g_value_set_uint (value, sel->n_pads);
574 1 : GST_JOIN_UNLOCK (object);
575 1 : break;
576 2 : case PROP_ACTIVE_PAD:
577 2 : GST_JOIN_LOCK (object);
578 2 : g_value_set_object (value, sel->active_sinkpad);
579 2 : GST_JOIN_UNLOCK (object);
580 2 : break;
581 0 : default:
582 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
583 0 : break;
584 : }
585 3 : }
586 :
587 : /**
588 : * @brief Get linked pad
589 : */
590 : static GstPad *
591 142 : gst_join_get_linked_pad (GstJoin * sel, GstPad * pad, gboolean strict)
592 : {
593 142 : GstPad *otherpad = NULL;
594 :
595 142 : GST_JOIN_LOCK (sel);
596 142 : if (pad == sel->srcpad)
597 82 : otherpad = sel->active_sinkpad;
598 60 : else if (pad == sel->active_sinkpad || !strict)
599 15 : otherpad = sel->srcpad;
600 142 : if (otherpad)
601 37 : gst_object_ref (otherpad);
602 142 : GST_JOIN_UNLOCK (sel);
603 :
604 142 : return otherpad;
605 : }
606 :
607 : /**
608 : * @brief Get or create the active sinkpad.
609 : * @note must be called with JOIN_LOCK.
610 : */
611 : static GstPad *
612 120 : gst_join_get_active_sinkpad (GstJoin * sel)
613 : {
614 : GstPad *active_sinkpad;
615 :
616 120 : active_sinkpad = sel->active_sinkpad;
617 120 : if (active_sinkpad == NULL) {
618 12 : GValue item = G_VALUE_INIT;
619 12 : GstIterator *iter = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (sel));
620 : GstIteratorResult ires;
621 :
622 12 : while ((ires = gst_iterator_next (iter, &item)) == GST_ITERATOR_RESYNC)
623 0 : gst_iterator_resync (iter);
624 12 : if (ires == GST_ITERATOR_OK) {
625 : /**
626 : * If no pad is currently selected, we return the first usable pad to
627 : * guarantee consistency
628 : */
629 :
630 12 : active_sinkpad = sel->active_sinkpad = g_value_dup_object (&item);
631 12 : g_value_reset (&item);
632 12 : GST_DEBUG_OBJECT (sel, "Activating pad %s:%s",
633 : GST_DEBUG_PAD_NAME (active_sinkpad));
634 : } else
635 0 : GST_WARNING_OBJECT (sel, "Couldn't find a default sink pad");
636 12 : gst_iterator_free (iter);
637 : }
638 :
639 120 : return active_sinkpad;
640 : }
641 :
642 : /**
643 : * @brief request new sink pad
644 : */
645 : static GstPad *
646 36 : gst_join_request_new_pad (GstElement * element, GstPadTemplate * templ,
647 : const gchar * unused, const GstCaps * caps)
648 : {
649 : GstJoin *sel;
650 36 : gchar *name = NULL;
651 36 : GstPad *sinkpad = NULL;
652 : (void) unused;
653 : (void) caps;
654 :
655 36 : g_return_val_if_fail (templ->direction == GST_PAD_SINK, NULL);
656 :
657 36 : sel = GST_JOIN (element);
658 :
659 36 : GST_JOIN_LOCK (sel);
660 :
661 36 : GST_LOG_OBJECT (sel, "Creating new pad sink_%u", sel->padcount);
662 36 : name = g_strdup_printf ("sink_%u", sel->padcount++);
663 36 : sinkpad = g_object_new (GST_TYPE_JOIN_PAD,
664 36 : "name", name, "direction", templ->direction, "template", templ, NULL);
665 36 : g_free (name);
666 :
667 36 : sel->n_pads++;
668 :
669 36 : gst_pad_set_event_function (sinkpad, GST_DEBUG_FUNCPTR (gst_join_pad_event));
670 36 : gst_pad_set_query_function (sinkpad, GST_DEBUG_FUNCPTR (gst_join_pad_query));
671 36 : gst_pad_set_chain_function (sinkpad, GST_DEBUG_FUNCPTR (gst_join_pad_chain));
672 36 : gst_pad_set_iterate_internal_links_function (sinkpad,
673 : GST_DEBUG_FUNCPTR (gst_join_pad_iterate_linked_pads));
674 :
675 36 : GST_OBJECT_FLAG_SET (sinkpad, GST_PAD_FLAG_PROXY_CAPS);
676 36 : GST_OBJECT_FLAG_SET (sinkpad, GST_PAD_FLAG_PROXY_ALLOCATION);
677 36 : gst_pad_set_active (sinkpad, TRUE);
678 36 : gst_element_add_pad (GST_ELEMENT (sel), sinkpad);
679 36 : GST_JOIN_UNLOCK (sel);
680 :
681 36 : return sinkpad;
682 : }
683 :
684 : /**
685 : * @brief register this element
686 : */
687 : static gboolean
688 27 : plugin_init (GstPlugin * plugin)
689 : {
690 27 : GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "join", 0,
691 : "gstreamer join element");
692 :
693 27 : if (!gst_element_register (plugin, "join", GST_RANK_NONE, GST_TYPE_JOIN)) {
694 0 : return FALSE;
695 : }
696 :
697 27 : return TRUE;
698 : }
699 :
700 : #ifndef PACKAGE
701 : #define PACKAGE "join"
702 : #endif
703 :
704 27 : GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
705 : GST_VERSION_MINOR,
706 : join,
707 : "Select the out that arrived first among the input streams",
708 : plugin_init, VERSION, "LGPL", PACKAGE,
709 : "https://github.com/nnstreamer/nnstreamer")
|