2 * Copyright (C) 2014 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
3 * Copyright (C) 2014 Thibault Saunier <tsaunier@gnome.org>
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
23 * SECTION: gstaggregator
24 * @short_description: manages a set of pads with the purpose of
25 * aggregating their buffers.
26 * @see_also: gstcollectpads for historical reasons.
28 * Manages a set of pads with the purpose of aggregating their buffers.
29 * Control is given to the subclass when all pads have data.
32 * Base class for mixers and muxers. Implementers should at least implement
33 * the aggregate () vmethod.
36 * When data is queued on all pads, tha aggregate vmethod is called.
39 * One can peek at the data on any given GstAggregatorPad with the
40 * gst_aggregator_pad_get_buffer () method, and take ownership of it
41 * with the gst_aggregator_pad_steal_buffer () method. When a buffer
42 * has been taken with steal_buffer (), a new buffer can be queued
46 * If the subclass wishes to push a buffer downstream in its aggregate
47 * implementation, it should do so through the
48 * gst_aggregator_finish_buffer () method. This method will take care
49 * of sending and ordering mandatory events such as stream start, caps
53 * Same goes for EOS events, which should not be pushed directly by the
54 * subclass, it should instead return GST_FLOW_EOS in its aggregate
64 #include <string.h> /* strlen */
66 #include "gstaggregator.h"
69 /* Might become API */
70 static void gst_aggregator_merge_tags (GstAggregator * aggregator,
71 const GstTagList * tags, GstTagMergeMode mode);
73 GST_DEBUG_CATEGORY_STATIC (aggregator_debug);
74 #define GST_CAT_DEFAULT aggregator_debug
76 /* GstAggregatorPad definitions */
77 #define PAD_LOCK_EVENT(pad) G_STMT_START { \
78 GST_LOG_OBJECT (pad, "Taking EVENT lock from thread %p", \
80 g_mutex_lock(&pad->priv->event_lock); \
81 GST_LOG_OBJECT (pad, "Took EVENT lock from thread %p", \
85 #define PAD_UNLOCK_EVENT(pad) G_STMT_START { \
86 GST_LOG_OBJECT (pad, "Releasing EVENT lock from thread %p", \
88 g_mutex_unlock(&pad->priv->event_lock); \
89 GST_LOG_OBJECT (pad, "Release EVENT lock from thread %p", \
94 #define PAD_WAIT_EVENT(pad) G_STMT_START { \
95 GST_LOG_OBJECT (pad, "Waiting for EVENT on thread %p", \
97 g_cond_wait(&(((GstAggregatorPad* )pad)->priv->event_cond), \
98 &(pad->priv->event_lock)); \
99 GST_LOG_OBJECT (pad, "DONE Waiting for EVENT on thread %p", \
103 #define PAD_BROADCAST_EVENT(pad) { \
104 GST_LOG_OBJECT (pad, "Signaling EVENT from thread %p", \
106 g_cond_broadcast(&(((GstAggregatorPad* )pad)->priv->event_cond)); \
109 struct _GstAggregatorPadPrivate
111 gboolean pending_flush_start;
112 gboolean pending_flush_stop;
113 gboolean pending_eos;
121 _aggpad_flush (GstAggregatorPad * aggpad, GstAggregator * agg)
123 GstAggregatorPadClass *klass = GST_AGGREGATOR_PAD_GET_CLASS (aggpad);
126 aggpad->priv->flushing = FALSE;
129 return klass->flush (aggpad, agg);
134 /*************************************
135 * GstAggregator implementation *
136 *************************************/
137 static GstElementClass *aggregator_parent_class = NULL;
139 #define MAIN_CONTEXT_LOCK(self) G_STMT_START { \
140 GST_LOG_OBJECT (self, "Getting MAIN_CONTEXT_LOCK in thread %p", \
142 g_mutex_lock(&((GstAggregator*)self)->priv->mcontext_lock); \
143 GST_LOG_OBJECT (self, "Got MAIN_CONTEXT_LOCK in thread %p", \
147 #define MAIN_CONTEXT_UNLOCK(self) G_STMT_START { \
148 g_mutex_unlock(&((GstAggregator*)self)->priv->mcontext_lock); \
149 GST_LOG_OBJECT (self, "Unlocked MAIN_CONTEXT_LOCK in thread %p", \
153 struct _GstAggregatorPrivate
157 GMainContext *mcontext;
159 /* Our state is >= PAUSED */
162 /* Ensure that when we remove all sources from the maincontext
163 * we can not add any source, avoiding:
164 * "g_source_attach: assertion '!SOURCE_DESTROYED (source)' failed" */
165 GMutex mcontext_lock;
169 gboolean send_stream_start;
170 gboolean send_segment;
171 gboolean flush_seeking;
172 gboolean pending_flush_start;
174 GstFlowReturn flow_return;
179 gboolean tags_changed;
190 * gst_aggregator_iterate_sinkpads:
191 * @self: The #GstAggregator
192 * @func: The function to call.
193 * @user_data: The data to pass to @func.
195 * Iterate the sinkpads of aggregator to call a function on them.
197 * This method guarantees that @func will be called only once for each
201 gst_aggregator_iterate_sinkpads (GstAggregator * self,
202 GstAggregatorPadForeachFunc func, gpointer user_data)
204 gboolean result = FALSE;
206 gboolean done = FALSE;
207 GValue item = { 0, };
208 GList *seen_pads = NULL;
210 iter = gst_element_iterate_sink_pads (GST_ELEMENT (self));
216 switch (gst_iterator_next (iter, &item)) {
217 case GST_ITERATOR_OK:
221 pad = g_value_get_object (&item);
223 /* if already pushed, skip. FIXME, find something faster to tag pads */
224 if (pad == NULL || g_list_find (seen_pads, pad)) {
225 g_value_reset (&item);
229 GST_LOG_OBJECT (self, "calling function on pad %s:%s",
230 GST_DEBUG_PAD_NAME (pad));
231 result = func (self, pad, user_data);
235 seen_pads = g_list_prepend (seen_pads, pad);
237 g_value_reset (&item);
240 case GST_ITERATOR_RESYNC:
241 gst_iterator_resync (iter);
243 case GST_ITERATOR_ERROR:
244 GST_ERROR_OBJECT (self,
245 "Could not iterate over internally linked pads");
248 case GST_ITERATOR_DONE:
253 g_value_unset (&item);
254 gst_iterator_free (iter);
256 if (seen_pads == NULL) {
257 GST_DEBUG_OBJECT (self, "No pad seen");
261 g_list_free (seen_pads);
267 static inline gboolean
268 _check_all_pads_with_data_or_eos (GstAggregator * self,
269 GstAggregatorPad * aggpad)
271 if (aggpad->buffer || aggpad->eos) {
275 GST_LOG_OBJECT (aggpad, "Not ready to be aggregated");
281 * gst_aggregator_set_src_caps:
282 * @self: The #GstAggregator
283 * @caps: The #GstCaps to set later on the src pad.
285 * Sets the caps to be used on the src pad.
288 gst_aggregator_set_src_caps (GstAggregator * self, GstCaps * caps)
290 gst_caps_replace (&self->priv->srccaps, caps);
294 _reset_flow_values (GstAggregator * self)
296 self->priv->flow_return = GST_FLOW_FLUSHING;
297 self->priv->send_stream_start = TRUE;
298 self->priv->send_segment = TRUE;
299 gst_segment_init (&self->segment, GST_FORMAT_TIME);
303 _push_mandatory_events (GstAggregator * self)
305 GstAggregatorPrivate *priv = self->priv;
307 if (g_atomic_int_get (&self->priv->send_stream_start)) {
310 GST_INFO_OBJECT (self, "pushing stream start");
311 /* stream-start (FIXME: create id based on input ids) */
312 g_snprintf (s_id, sizeof (s_id), "agg-%08x", g_random_int ());
313 if (!gst_pad_push_event (self->srcpad, gst_event_new_stream_start (s_id))) {
314 GST_WARNING_OBJECT (self->srcpad, "Sending stream start event failed");
316 g_atomic_int_set (&self->priv->send_stream_start, FALSE);
319 if (self->priv->srccaps) {
321 GST_INFO_OBJECT (self, "pushing caps: %" GST_PTR_FORMAT,
322 self->priv->srccaps);
323 if (!gst_pad_push_event (self->srcpad,
324 gst_event_new_caps (self->priv->srccaps))) {
325 GST_WARNING_OBJECT (self->srcpad, "Sending caps event failed");
327 gst_caps_unref (self->priv->srccaps);
328 self->priv->srccaps = NULL;
331 if (g_atomic_int_get (&self->priv->send_segment)) {
332 if (!g_atomic_int_get (&self->priv->flush_seeking)) {
333 GstEvent *segev = gst_event_new_segment (&self->segment);
335 if (!self->priv->seqnum)
336 self->priv->seqnum = gst_event_get_seqnum (segev);
338 gst_event_set_seqnum (segev, self->priv->seqnum);
340 GST_DEBUG_OBJECT (self, "pushing segment %" GST_PTR_FORMAT, segev);
341 gst_pad_push_event (self->srcpad, segev);
342 g_atomic_int_set (&self->priv->send_segment, FALSE);
346 if (priv->tags && priv->tags_changed) {
347 gst_pad_push_event (self->srcpad,
348 gst_event_new_tag (gst_tag_list_ref (priv->tags)));
349 priv->tags_changed = FALSE;
354 * gst_aggregator_finish_buffer:
355 * @self: The #GstAggregator
356 * @buffer: the #GstBuffer to push.
358 * This method will take care of sending mandatory events before pushing
359 * the provided buffer.
362 gst_aggregator_finish_buffer (GstAggregator * self, GstBuffer * buffer)
364 _push_mandatory_events (self);
366 if (!g_atomic_int_get (&self->priv->flush_seeking) &&
367 gst_pad_is_active (self->srcpad)) {
368 GST_TRACE_OBJECT (self, "pushing buffer %" GST_PTR_FORMAT, buffer);
369 return gst_pad_push (self->srcpad, buffer);
371 GST_INFO_OBJECT (self, "Not pushing (active: %i, flushing: %i)",
372 g_atomic_int_get (&self->priv->flush_seeking),
373 gst_pad_is_active (self->srcpad));
374 gst_buffer_unref (buffer);
380 _push_eos (GstAggregator * self)
383 _push_mandatory_events (self);
385 self->priv->send_eos = FALSE;
386 event = gst_event_new_eos ();
387 gst_event_set_seqnum (event, self->priv->seqnum);
388 gst_pad_push_event (self->srcpad, event);
393 _destroy_gsource (GSource * source)
395 g_source_destroy (source);
396 g_source_unref (source);
400 _remove_all_sources (GstAggregator * self)
402 GstAggregatorPrivate *priv = self->priv;
404 MAIN_CONTEXT_LOCK (self);
405 g_list_free_full (priv->gsources, (GDestroyNotify) _destroy_gsource);
406 priv->gsources = NULL;
407 MAIN_CONTEXT_UNLOCK (self);
411 aggregate_func (GstAggregator * self)
413 GstAggregatorPrivate *priv = self->priv;
414 GstAggregatorClass *klass = GST_AGGREGATOR_GET_CLASS (self);
416 GST_LOG_OBJECT (self, "Checking aggregate");
417 while (priv->send_eos && gst_aggregator_iterate_sinkpads (self,
418 (GstAggregatorPadForeachFunc) _check_all_pads_with_data_or_eos,
419 NULL) && priv->running) {
420 GST_TRACE_OBJECT (self, "Actually aggregating!");
422 priv->flow_return = klass->aggregate (self);
424 if (priv->flow_return == GST_FLOW_EOS) {
425 g_main_context_wakeup (self->priv->mcontext);
426 _remove_all_sources (self);
430 if (priv->flow_return == GST_FLOW_FLUSHING &&
431 g_atomic_int_get (&priv->flush_seeking))
432 priv->flow_return = GST_FLOW_OK;
434 GST_LOG_OBJECT (self, "flow return is %s",
435 gst_flow_get_name (priv->flow_return));
437 if (priv->flow_return != GST_FLOW_OK)
441 return G_SOURCE_REMOVE;
445 iterate_main_context_func (GstAggregator * self)
447 if (self->priv->running == FALSE) {
448 GST_DEBUG_OBJECT (self, "Not running anymore");
453 g_main_context_iteration (self->priv->mcontext, TRUE);
457 _start (GstAggregator * self)
459 self->priv->running = TRUE;
460 self->priv->send_stream_start = TRUE;
461 self->priv->send_segment = TRUE;
462 self->priv->send_eos = TRUE;
463 self->priv->srccaps = NULL;
464 self->priv->flow_return = GST_FLOW_OK;
470 _check_pending_flush_stop (GstAggregatorPad * pad)
472 return (!pad->priv->pending_flush_stop && !pad->priv->pending_flush_start);
476 _stop_srcpad_task (GstAggregator * self, GstEvent * flush_start)
480 GST_INFO_OBJECT (self, "%s srcpad task",
481 flush_start ? "Pausing" : "Stopping");
483 self->priv->running = FALSE;
485 /* Clean the stack of GSource set on the MainContext */
486 g_main_context_wakeup (self->priv->mcontext);
487 _remove_all_sources (self);
489 res = gst_pad_push_event (self->srcpad, flush_start);
492 gst_pad_stop_task (self->srcpad);
498 _start_srcpad_task (GstAggregator * self)
500 GST_INFO_OBJECT (self, "Starting srcpad task");
502 self->priv->running = TRUE;
503 gst_pad_start_task (GST_PAD (self->srcpad),
504 (GstTaskFunction) iterate_main_context_func, self, NULL);
508 _add_aggregate_gsource (GstAggregator * self)
511 GstAggregatorPrivate *priv = self->priv;
513 MAIN_CONTEXT_LOCK (self);
514 source = g_idle_source_new ();
515 g_source_set_callback (source, (GSourceFunc) aggregate_func, self, NULL);
516 priv->gsources = g_list_prepend (priv->gsources, source);
517 g_source_attach (source, priv->mcontext);
518 MAIN_CONTEXT_UNLOCK (self);
522 _flush (GstAggregator * self)
524 GstFlowReturn ret = GST_FLOW_OK;
525 GstAggregatorPrivate *priv = self->priv;
526 GstAggregatorClass *klass = GST_AGGREGATOR_GET_CLASS (self);
528 GST_DEBUG_OBJECT (self, "Flushing everything");
529 g_atomic_int_set (&priv->send_segment, TRUE);
530 g_atomic_int_set (&priv->flush_seeking, FALSE);
531 g_atomic_int_set (&priv->tags_changed, FALSE);
533 ret = klass->flush (self);
539 _all_flush_stop_received (GstAggregator * self)
542 GstAggregatorPad *tmppad;
544 GST_OBJECT_LOCK (self);
545 for (tmp = GST_ELEMENT (self)->sinkpads; tmp; tmp = tmp->next) {
546 tmppad = (GstAggregatorPad *) tmp->data;
548 if (_check_pending_flush_stop (tmppad) == FALSE) {
549 GST_DEBUG_OBJECT (tmppad, "Is not last %i -- %i",
550 tmppad->priv->pending_flush_start, tmppad->priv->pending_flush_stop);
551 GST_OBJECT_UNLOCK (self);
555 GST_OBJECT_UNLOCK (self);
560 /* GstAggregator vmethods default implementations */
562 _sink_event (GstAggregator * self, GstAggregatorPad * aggpad, GstEvent * event)
565 GstPad *pad = GST_PAD (aggpad);
566 GstAggregatorPrivate *priv = self->priv;
567 GstAggregatorPadPrivate *padpriv = aggpad->priv;
569 switch (GST_EVENT_TYPE (event)) {
570 case GST_EVENT_FLUSH_START:
574 g_atomic_int_set (&aggpad->priv->flushing, TRUE);
575 /* Remove pad buffer and wake up the streaming thread */
576 tmpbuf = gst_aggregator_pad_steal_buffer (aggpad);
577 gst_buffer_replace (&tmpbuf, NULL);
578 if (g_atomic_int_compare_and_exchange (&padpriv->pending_flush_start,
579 TRUE, FALSE) == TRUE) {
580 GST_DEBUG_OBJECT (aggpad, "Expecting FLUSH_STOP now");
581 g_atomic_int_set (&padpriv->pending_flush_stop, TRUE);
584 if (g_atomic_int_get (&priv->flush_seeking)) {
585 /* If flush_seeking we forward the first FLUSH_START */
586 if (g_atomic_int_compare_and_exchange (&priv->pending_flush_start,
587 TRUE, FALSE) == TRUE) {
589 GST_DEBUG_OBJECT (self, "Flushing, pausing srcpad task");
590 _stop_srcpad_task (self, event);
591 priv->flow_return = GST_FLOW_OK;
593 GST_INFO_OBJECT (self, "Getting STREAM_LOCK while seeking");
594 GST_PAD_STREAM_LOCK (self->srcpad);
595 GST_LOG_OBJECT (self, "GOT STREAM_LOCK");
601 /* We forward only in one case: right after flush_seeking */
604 case GST_EVENT_FLUSH_STOP:
606 GST_DEBUG_OBJECT (aggpad, "Got FLUSH_STOP");
608 _aggpad_flush (aggpad, self);
609 if (g_atomic_int_get (&priv->flush_seeking)) {
610 g_atomic_int_set (&aggpad->priv->pending_flush_stop, FALSE);
612 if (g_atomic_int_get (&priv->flush_seeking)) {
613 if (_all_flush_stop_received (self)) {
614 /* That means we received FLUSH_STOP/FLUSH_STOP on
615 * all sinkpads -- Seeking is Done... sending FLUSH_STOP */
617 gst_pad_push_event (self->srcpad, event);
618 priv->send_eos = TRUE;
620 _add_aggregate_gsource (self);
622 GST_INFO_OBJECT (self, "Releasing source pad STREAM_LOCK");
623 GST_PAD_STREAM_UNLOCK (self->srcpad);
624 _start_srcpad_task (self);
629 /* We never forward the event */
634 GST_DEBUG_OBJECT (aggpad, "EOS");
636 /* We still have a buffer, and we don't want the subclass to have to
637 * check for it. Mark pending_eos, eos will be set when steal_buffer is
640 PAD_LOCK_EVENT (aggpad);
641 if (!aggpad->buffer) {
644 aggpad->priv->pending_eos = TRUE;
646 PAD_UNLOCK_EVENT (aggpad);
648 _add_aggregate_gsource (self);
651 case GST_EVENT_SEGMENT:
653 PAD_LOCK_EVENT (aggpad);
654 gst_event_copy_segment (event, &aggpad->segment);
655 PAD_UNLOCK_EVENT (aggpad);
658 case GST_EVENT_STREAM_START:
666 gst_event_parse_tag (event, &tags);
668 if (gst_tag_list_get_scope (tags) == GST_TAG_SCOPE_STREAM) {
669 gst_aggregator_merge_tags (self, tags, GST_TAG_MERGE_REPLACE);
670 gst_event_unref (event);
682 GST_DEBUG_OBJECT (pad, "Forwarding event: %" GST_PTR_FORMAT, event);
683 return gst_pad_event_default (pad, GST_OBJECT (self), event);
686 GST_DEBUG_OBJECT (pad, "Eating event: %" GST_PTR_FORMAT, event);
688 gst_event_unref (event);
694 _flush_pad (GstAggregator * self, GstAggregatorPad * pad, gpointer unused_udata)
696 _aggpad_flush (pad, self);
702 _stop (GstAggregator * agg)
704 _reset_flow_values (agg);
706 gst_aggregator_iterate_sinkpads (agg,
707 (GstAggregatorPadForeachFunc) _flush_pad, NULL);
712 /* GstElement vmethods implementations */
713 static GstStateChangeReturn
714 _change_state (GstElement * element, GstStateChange transition)
716 GstStateChangeReturn ret;
717 GstAggregator *self = GST_AGGREGATOR (element);
718 GstAggregatorClass *agg_class = GST_AGGREGATOR_GET_CLASS (self);
721 switch (transition) {
722 case GST_STATE_CHANGE_READY_TO_PAUSED:
723 agg_class->start (self);
730 GST_ELEMENT_CLASS (aggregator_parent_class)->change_state (element,
731 transition)) == GST_STATE_CHANGE_FAILURE)
735 switch (transition) {
736 case GST_STATE_CHANGE_PAUSED_TO_READY:
737 agg_class->stop (self);
747 GST_ERROR_OBJECT (element, "parent failed state change");
753 _release_pad (GstElement * element, GstPad * pad)
757 GstAggregator *self = GST_AGGREGATOR (element);
758 GstAggregatorPad *aggpad = GST_AGGREGATOR_PAD (pad);
760 GST_INFO_OBJECT (pad, "Removing pad");
762 g_atomic_int_set (&aggpad->priv->flushing, TRUE);
763 tmpbuf = gst_aggregator_pad_steal_buffer (aggpad);
764 gst_buffer_replace (&tmpbuf, NULL);
765 gst_element_remove_pad (element, pad);
767 /* Something changed make sure we try to aggregate */
768 _add_aggregate_gsource (self);
772 _request_new_pad (GstElement * element,
773 GstPadTemplate * templ, const gchar * req_name, const GstCaps * caps)
776 GstAggregatorPad *agg_pad;
778 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
779 GstAggregatorPrivate *priv = GST_AGGREGATOR (element)->priv;
781 self = GST_AGGREGATOR (element);
783 if (templ == gst_element_class_get_pad_template (klass, "sink_%u")) {
787 GST_OBJECT_LOCK (element);
788 if (req_name == NULL || strlen (req_name) < 6
789 || !g_str_has_prefix (req_name, "sink_")) {
790 /* no name given when requesting the pad, use next available int */
793 /* parse serial number from requested padname */
794 serial = g_ascii_strtoull (&req_name[5], NULL, 10);
795 if (serial >= priv->padcount)
796 priv->padcount = serial;
799 name = g_strdup_printf ("sink_%u", priv->padcount);
800 agg_pad = g_object_new (GST_AGGREGATOR_GET_CLASS (self)->sinkpads_type,
801 "name", name, "direction", GST_PAD_SINK, "template", templ, NULL);
803 GST_OBJECT_UNLOCK (element);
809 GST_DEBUG_OBJECT (element, "Adding pad %s", GST_PAD_NAME (agg_pad));
812 gst_pad_set_active (GST_PAD (agg_pad), TRUE);
814 /* add the pad to the element */
815 gst_element_add_pad (element, GST_PAD (agg_pad));
817 return GST_PAD (agg_pad);
821 _src_query (GstAggregator * self, GstQuery * query)
825 switch (GST_QUERY_TYPE (query)) {
826 case GST_QUERY_SEEKING:
830 /* don't pass it along as some (file)sink might claim it does
831 * whereas with a collectpads in between that will not likely work */
832 gst_query_parse_seeking (query, &format, NULL, NULL, NULL);
833 gst_query_set_seeking (query, format, FALSE, 0, -1);
842 return gst_pad_query_default (self->srcpad, GST_OBJECT (self), query);
849 event_forward_func (GstPad * pad, EventData * evdata)
852 GstPad *peer = gst_pad_get_peer (pad);
853 GstAggregatorPadPrivate *padpriv = GST_AGGREGATOR_PAD (pad)->priv;
856 ret = gst_pad_send_event (peer, gst_event_ref (evdata->event));
857 GST_DEBUG_OBJECT (pad, "return of event push is %d", ret);
858 gst_object_unref (peer);
861 evdata->result &= ret;
864 if (GST_EVENT_TYPE (evdata->event) == GST_EVENT_SEEK)
865 GST_ERROR_OBJECT (pad, "Event %" GST_PTR_FORMAT " failed", evdata->event);
867 GST_INFO_OBJECT (pad, "Event %" GST_PTR_FORMAT " failed", evdata->event);
870 padpriv->pending_flush_start = FALSE;
871 padpriv->pending_flush_stop = FALSE;
875 /* Always send to all pads */
880 _set_flush_pending (GstAggregator * self, GstAggregatorPad * pad,
883 pad->priv->pending_flush_start = TRUE;
884 pad->priv->pending_flush_stop = FALSE;
890 _forward_event_to_all_sinkpads (GstAggregator * self, GstEvent * event,
895 evdata.event = event;
896 evdata.result = TRUE;
897 evdata.flush = flush;
899 /* We first need to set all pads as flushing in a first pass
900 * as flush_start flush_stop is sometimes sent synchronously
901 * while we send the seek event */
903 gst_aggregator_iterate_sinkpads (self,
904 (GstAggregatorPadForeachFunc) _set_flush_pending, NULL);
905 gst_pad_forward (self->srcpad, (GstPadForwardFunction) event_forward_func,
908 gst_event_unref (event);
910 return evdata.result;
914 _do_seek (GstAggregator * self, GstEvent * event)
919 GstSeekType start_type, stop_type;
923 GstAggregatorPrivate *priv = self->priv;
925 gst_event_parse_seek (event, &rate, &fmt, &flags, &start_type,
926 &start, &stop_type, &stop);
928 GST_INFO_OBJECT (self, "starting SEEK");
930 flush = flags & GST_SEEK_FLAG_FLUSH;
933 g_atomic_int_set (&priv->pending_flush_start, TRUE);
934 g_atomic_int_set (&priv->flush_seeking, TRUE);
937 gst_segment_do_seek (&self->segment, rate, fmt, flags, start_type, start,
938 stop_type, stop, NULL);
940 /* forward the seek upstream */
941 res = _forward_event_to_all_sinkpads (self, event, flush);
945 g_atomic_int_set (&priv->flush_seeking, FALSE);
946 g_atomic_int_set (&priv->pending_flush_start, FALSE);
949 GST_INFO_OBJECT (self, "seek done, result: %d", res);
955 _src_event (GstAggregator * self, GstEvent * event)
959 switch (GST_EVENT_TYPE (event)) {
962 gst_event_ref (event);
963 res = _do_seek (self, event);
965 self->priv->seqnum = gst_event_get_seqnum (event);
966 gst_event_unref (event);
970 case GST_EVENT_NAVIGATION:
972 /* navigation is rather pointless. */
974 gst_event_unref (event);
983 return _forward_event_to_all_sinkpads (self, event, FALSE);
990 src_event_func (GstPad * pad, GstObject * parent, GstEvent * event)
992 GstAggregatorClass *klass = GST_AGGREGATOR_GET_CLASS (parent);
994 return klass->src_event (GST_AGGREGATOR (parent), event);
998 src_query_func (GstPad * pad, GstObject * parent, GstQuery * query)
1000 GstAggregatorClass *klass = GST_AGGREGATOR_GET_CLASS (parent);
1002 return klass->src_query (GST_AGGREGATOR (parent), query);
1006 src_activate_mode (GstPad * pad,
1007 GstObject * parent, GstPadMode mode, gboolean active)
1009 GstAggregator *self = GST_AGGREGATOR (parent);
1010 GstAggregatorClass *klass = GST_AGGREGATOR_GET_CLASS (parent);
1012 if (klass->src_activate) {
1013 if (klass->src_activate (self, mode, active) == FALSE) {
1018 if (active == TRUE) {
1020 case GST_PAD_MODE_PUSH:
1022 GST_INFO_OBJECT (pad, "Activating pad!");
1023 _start_srcpad_task (self);
1028 GST_ERROR_OBJECT (pad, "Only supported mode is PUSH");
1035 GST_INFO_OBJECT (self, "Deactivating srcpad");
1036 _stop_srcpad_task (self, FALSE);
1042 _sink_query (GstAggregator * self, GstAggregatorPad * aggpad, GstQuery * query)
1044 GstPad *pad = GST_PAD (aggpad);
1046 return gst_pad_query_default (pad, GST_OBJECT (self), query);
1050 gst_aggregator_finalize (GObject * object)
1052 GstAggregator *self = (GstAggregator *) object;
1054 g_mutex_clear (&self->priv->mcontext_lock);
1056 G_OBJECT_CLASS (aggregator_parent_class)->finalize (object);
1060 gst_aggregator_dispose (GObject * object)
1062 GstAggregator *self = (GstAggregator *) object;
1064 G_OBJECT_CLASS (aggregator_parent_class)->dispose (object);
1066 g_main_context_unref (self->priv->mcontext);
1067 _remove_all_sources (self);
1070 /* GObject vmethods implementations */
1072 gst_aggregator_class_init (GstAggregatorClass * klass)
1074 GObjectClass *gobject_class = (GObjectClass *) klass;
1075 GstElementClass *gstelement_class = (GstElementClass *) klass;
1077 aggregator_parent_class = g_type_class_peek_parent (klass);
1078 g_type_class_add_private (klass, sizeof (GstAggregatorPrivate));
1080 GST_DEBUG_CATEGORY_INIT (aggregator_debug, "aggregator",
1081 GST_DEBUG_FG_MAGENTA, "GstAggregator");
1083 klass->sinkpads_type = GST_TYPE_AGGREGATOR_PAD;
1084 klass->start = _start;
1085 klass->stop = _stop;
1087 klass->sink_event = _sink_event;
1088 klass->sink_query = _sink_query;
1090 klass->src_event = _src_event;
1091 klass->src_query = _src_query;
1093 gstelement_class->request_new_pad = GST_DEBUG_FUNCPTR (_request_new_pad);
1094 gstelement_class->release_pad = GST_DEBUG_FUNCPTR (_release_pad);
1095 gstelement_class->change_state = GST_DEBUG_FUNCPTR (_change_state);
1097 gobject_class->finalize = gst_aggregator_finalize;
1098 gobject_class->dispose = gst_aggregator_dispose;
1102 gst_aggregator_init (GstAggregator * self, GstAggregatorClass * klass)
1104 GstPadTemplate *pad_template;
1105 GstAggregatorPrivate *priv;
1107 g_return_if_fail (klass->aggregate != NULL);
1110 G_TYPE_INSTANCE_GET_PRIVATE (self, GST_TYPE_AGGREGATOR,
1111 GstAggregatorPrivate);
1116 gst_element_class_get_pad_template (GST_ELEMENT_CLASS (klass), "src");
1117 g_return_if_fail (pad_template != NULL);
1119 priv->padcount = -1;
1120 priv->tags_changed = FALSE;
1121 _reset_flow_values (self);
1123 priv->mcontext = g_main_context_new ();
1124 self->srcpad = gst_pad_new_from_template (pad_template, "src");
1126 gst_pad_set_event_function (self->srcpad,
1127 GST_DEBUG_FUNCPTR ((GstPadEventFunction) src_event_func));
1128 gst_pad_set_query_function (self->srcpad,
1129 GST_DEBUG_FUNCPTR ((GstPadQueryFunction) src_query_func));
1130 gst_pad_set_activatemode_function (self->srcpad,
1131 GST_DEBUG_FUNCPTR ((GstPadActivateModeFunction) src_activate_mode));
1133 gst_element_add_pad (GST_ELEMENT (self), self->srcpad);
1135 g_mutex_init (&self->priv->mcontext_lock);
1138 /* we can't use G_DEFINE_ABSTRACT_TYPE because we need the klass in the _init
1139 * method to get to the padtemplates */
1141 gst_aggregator_get_type (void)
1143 static volatile gsize type = 0;
1145 if (g_once_init_enter (&type)) {
1147 static const GTypeInfo info = {
1148 sizeof (GstAggregatorClass),
1151 (GClassInitFunc) gst_aggregator_class_init,
1154 sizeof (GstAggregator),
1156 (GInstanceInitFunc) gst_aggregator_init,
1159 _type = g_type_register_static (GST_TYPE_ELEMENT,
1160 "GstAggregator", &info, G_TYPE_FLAG_ABSTRACT);
1161 g_once_init_leave (&type, _type);
1166 static GstFlowReturn
1167 _chain (GstPad * pad, GstObject * object, GstBuffer * buffer)
1169 GstBuffer *actual_buf = buffer;
1170 GstAggregator *self = GST_AGGREGATOR (object);
1171 GstAggregatorPrivate *priv = self->priv;
1172 GstAggregatorPad *aggpad = GST_AGGREGATOR_PAD (pad);
1173 GstAggregatorClass *aggclass = GST_AGGREGATOR_GET_CLASS (object);
1175 GST_DEBUG_OBJECT (aggpad, "Start chaining a buffer %" GST_PTR_FORMAT, buffer);
1177 if (g_atomic_int_get (&aggpad->priv->flushing) == TRUE)
1180 if (g_atomic_int_get (&aggpad->priv->pending_eos) == TRUE)
1183 PAD_LOCK_EVENT (aggpad);
1184 if (aggpad->buffer) {
1185 GST_DEBUG_OBJECT (aggpad, "Waiting for buffer to be consumed");
1186 PAD_WAIT_EVENT (aggpad);
1188 PAD_UNLOCK_EVENT (aggpad);
1190 if (g_atomic_int_get (&aggpad->priv->flushing) == TRUE)
1194 if (aggclass->clip) {
1195 aggclass->clip (self, aggpad, buffer, &actual_buf);
1198 PAD_LOCK_EVENT (aggpad);
1200 gst_buffer_unref (aggpad->buffer);
1201 aggpad->buffer = actual_buf;
1202 PAD_UNLOCK_EVENT (aggpad);
1204 _add_aggregate_gsource (self);
1206 GST_DEBUG_OBJECT (aggpad, "Done chaining");
1208 return priv->flow_return;
1212 gst_buffer_unref (buffer);
1213 GST_DEBUG_OBJECT (aggpad, "We are flushing");
1215 return GST_FLOW_FLUSHING;
1219 gst_buffer_unref (buffer);
1220 GST_DEBUG_OBJECT (pad, "We are EOS already...");
1222 return GST_FLOW_EOS;
1226 pad_query_func (GstPad * pad, GstObject * parent, GstQuery * query)
1228 GstAggregatorClass *klass = GST_AGGREGATOR_GET_CLASS (parent);
1230 return klass->sink_query (GST_AGGREGATOR (parent),
1231 GST_AGGREGATOR_PAD (pad), query);
1235 pad_event_func (GstPad * pad, GstObject * parent, GstEvent * event)
1237 GstAggregatorClass *klass = GST_AGGREGATOR_GET_CLASS (parent);
1239 return klass->sink_event (GST_AGGREGATOR (parent),
1240 GST_AGGREGATOR_PAD (pad), event);
1244 pad_activate_mode_func (GstPad * pad,
1245 GstObject * parent, GstPadMode mode, gboolean active)
1247 GstAggregatorPad *aggpad = GST_AGGREGATOR_PAD (pad);
1249 if (active == FALSE) {
1250 PAD_LOCK_EVENT (aggpad);
1251 g_atomic_int_set (&aggpad->priv->flushing, TRUE);
1252 gst_buffer_replace (&aggpad->buffer, NULL);
1253 PAD_BROADCAST_EVENT (aggpad);
1254 PAD_UNLOCK_EVENT (aggpad);
1256 g_atomic_int_set (&aggpad->priv->flushing, FALSE);
1257 PAD_LOCK_EVENT (aggpad);
1258 PAD_BROADCAST_EVENT (aggpad);
1259 PAD_UNLOCK_EVENT (aggpad);
1265 /***********************************
1266 * GstAggregatorPad implementation *
1267 ************************************/
1268 static GstPadClass *aggregator_pad_parent_class = NULL;
1269 G_DEFINE_TYPE (GstAggregatorPad, gst_aggregator_pad, GST_TYPE_PAD);
1272 _pad_constructed (GObject * object)
1274 GstPad *pad = GST_PAD (object);
1276 gst_pad_set_chain_function (pad,
1277 GST_DEBUG_FUNCPTR ((GstPadChainFunction) _chain));
1278 gst_pad_set_event_function (pad,
1279 GST_DEBUG_FUNCPTR ((GstPadEventFunction) pad_event_func));
1280 gst_pad_set_query_function (pad,
1281 GST_DEBUG_FUNCPTR ((GstPadQueryFunction) pad_query_func));
1282 gst_pad_set_activatemode_function (pad,
1283 GST_DEBUG_FUNCPTR ((GstPadActivateModeFunction) pad_activate_mode_func));
1287 gst_aggregator_pad_finalize (GObject * object)
1289 GstAggregatorPad *pad = (GstAggregatorPad *) object;
1291 g_mutex_clear (&pad->priv->event_lock);
1292 g_cond_clear (&pad->priv->event_cond);
1294 G_OBJECT_CLASS (aggregator_pad_parent_class)->finalize (object);
1298 gst_aggregator_pad_dispose (GObject * object)
1300 GstAggregatorPad *pad = (GstAggregatorPad *) object;
1303 buf = gst_aggregator_pad_steal_buffer (pad);
1305 gst_buffer_unref (buf);
1307 G_OBJECT_CLASS (aggregator_pad_parent_class)->dispose (object);
1311 gst_aggregator_pad_class_init (GstAggregatorPadClass * klass)
1313 GObjectClass *gobject_class = (GObjectClass *) klass;
1315 aggregator_pad_parent_class = g_type_class_peek_parent (klass);
1316 g_type_class_add_private (klass, sizeof (GstAggregatorPadPrivate));
1318 gobject_class->constructed = GST_DEBUG_FUNCPTR (_pad_constructed);
1319 gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_aggregator_pad_finalize);
1320 gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_aggregator_pad_dispose);
1324 gst_aggregator_pad_init (GstAggregatorPad * pad)
1327 G_TYPE_INSTANCE_GET_PRIVATE (pad, GST_TYPE_AGGREGATOR_PAD,
1328 GstAggregatorPadPrivate);
1331 g_mutex_init (&pad->priv->event_lock);
1332 g_cond_init (&pad->priv->event_cond);
1337 * gst_aggregator_pad_steal_buffer:
1338 * @pad: the pad to get buffer from
1340 * Steal the ref to the buffer currently queued in @pad.
1342 * Returns: (transfer full): The buffer in @pad or NULL if no buffer was
1343 * queued. You should unref the buffer after usage.
1346 gst_aggregator_pad_steal_buffer (GstAggregatorPad * pad)
1348 GstBuffer *buffer = NULL;
1350 PAD_LOCK_EVENT (pad);
1352 GST_TRACE_OBJECT (pad, "Consuming buffer");
1353 buffer = pad->buffer;
1355 if (pad->priv->pending_eos) {
1356 pad->priv->pending_eos = FALSE;
1359 PAD_BROADCAST_EVENT (pad);
1360 GST_DEBUG_OBJECT (pad, "Consummed: %" GST_PTR_FORMAT, buffer);
1362 PAD_UNLOCK_EVENT (pad);
1368 * gst_aggregator_pad_get_buffer:
1369 * @pad: the pad to get buffer from
1371 * Returns: (transfer full): A reference to the buffer in @pad or
1372 * NULL if no buffer was queued. You should unref the buffer after
1376 gst_aggregator_pad_get_buffer (GstAggregatorPad * pad)
1378 GstBuffer *buffer = NULL;
1380 PAD_LOCK_EVENT (pad);
1382 buffer = gst_buffer_ref (pad->buffer);
1383 PAD_UNLOCK_EVENT (pad);
1389 * gst_aggregator_merge_tags:
1390 * @self: a #GstAggregator
1391 * @tags: a #GstTagList to merge
1392 * @mode: the #GstTagMergeMode to use
1394 * Adds tags to so-called pending tags, which will be processed
1395 * before pushing out data downstream.
1397 * Note that this is provided for convenience, and the subclass is
1398 * not required to use this and can still do tag handling on its own.
1403 gst_aggregator_merge_tags (GstAggregator * self,
1404 const GstTagList * tags, GstTagMergeMode mode)
1408 g_return_if_fail (GST_IS_AGGREGATOR (self));
1409 g_return_if_fail (tags == NULL || GST_IS_TAG_LIST (tags));
1411 /* FIXME Check if we can use OBJECT lock here! */
1412 GST_OBJECT_LOCK (self);
1414 GST_DEBUG_OBJECT (self, "merging tags %" GST_PTR_FORMAT, tags);
1415 otags = self->priv->tags;
1416 self->priv->tags = gst_tag_list_merge (self->priv->tags, tags, mode);
1418 gst_tag_list_unref (otags);
1419 self->priv->tags_changed = TRUE;
1420 GST_OBJECT_UNLOCK (self);