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;
167 gboolean send_stream_start;
168 gboolean send_segment;
169 gboolean flush_seeking;
170 gboolean pending_flush_start;
172 GstFlowReturn flow_return;
177 gboolean tags_changed;
188 * gst_aggregator_iterate_sinkpads:
189 * @self: The #GstAggregator
190 * @func: The function to call.
191 * @user_data: The data to pass to @func.
193 * Iterate the sinkpads of aggregator to call a function on them.
195 * This method guarantees that @func will be called only once for each
199 gst_aggregator_iterate_sinkpads (GstAggregator * self,
200 GstAggregatorPadForeachFunc func, gpointer user_data)
202 gboolean result = FALSE;
204 gboolean done = FALSE;
205 GValue item = { 0, };
206 GList *seen_pads = NULL;
208 iter = gst_element_iterate_sink_pads (GST_ELEMENT (self));
214 switch (gst_iterator_next (iter, &item)) {
215 case GST_ITERATOR_OK:
219 pad = g_value_get_object (&item);
221 /* if already pushed, skip. FIXME, find something faster to tag pads */
222 if (pad == NULL || g_list_find (seen_pads, pad)) {
223 g_value_reset (&item);
227 GST_LOG_OBJECT (self, "calling function on pad %s:%s",
228 GST_DEBUG_PAD_NAME (pad));
229 result = func (self, pad, user_data);
233 seen_pads = g_list_prepend (seen_pads, pad);
235 g_value_reset (&item);
238 case GST_ITERATOR_RESYNC:
239 gst_iterator_resync (iter);
241 case GST_ITERATOR_ERROR:
242 GST_ERROR_OBJECT (self,
243 "Could not iterate over internally linked pads");
246 case GST_ITERATOR_DONE:
251 g_value_unset (&item);
252 gst_iterator_free (iter);
254 if (seen_pads == NULL) {
255 GST_DEBUG_OBJECT (self, "No pad seen");
259 g_list_free (seen_pads);
265 static inline gboolean
266 _check_all_pads_with_data_or_eos (GstAggregator * self,
267 GstAggregatorPad * aggpad)
269 if (aggpad->buffer || aggpad->eos) {
273 GST_LOG_OBJECT (aggpad, "Not ready to be aggregated");
279 * gst_aggregator_set_src_caps:
280 * @self: The #GstAggregator
281 * @caps: The #GstCaps to set later on the src pad.
283 * Sets the caps to be used on the src pad.
286 gst_aggregator_set_src_caps (GstAggregator * self, GstCaps * caps)
288 gst_caps_replace (&self->priv->srccaps, caps);
292 _reset_flow_values (GstAggregator * self)
294 self->priv->send_stream_start = TRUE;
295 self->priv->send_segment = TRUE;
296 gst_segment_init (&self->segment, GST_FORMAT_TIME);
300 _push_mandatory_events (GstAggregator * self)
302 GstAggregatorPrivate *priv = self->priv;
304 if (g_atomic_int_get (&self->priv->send_stream_start)) {
307 GST_INFO_OBJECT (self, "pushing stream start");
308 /* stream-start (FIXME: create id based on input ids) */
309 g_snprintf (s_id, sizeof (s_id), "agg-%08x", g_random_int ());
310 if (!gst_pad_push_event (self->srcpad, gst_event_new_stream_start (s_id))) {
311 GST_WARNING_OBJECT (self->srcpad, "Sending stream start event failed");
313 g_atomic_int_set (&self->priv->send_stream_start, FALSE);
316 if (self->priv->srccaps) {
318 GST_INFO_OBJECT (self, "pushing caps: %" GST_PTR_FORMAT,
319 self->priv->srccaps);
320 if (!gst_pad_push_event (self->srcpad,
321 gst_event_new_caps (self->priv->srccaps))) {
322 GST_WARNING_OBJECT (self->srcpad, "Sending caps event failed");
324 gst_caps_unref (self->priv->srccaps);
325 self->priv->srccaps = NULL;
328 if (g_atomic_int_get (&self->priv->send_segment)) {
329 if (!g_atomic_int_get (&self->priv->flush_seeking)) {
330 GST_INFO_OBJECT (self, "pushing segment");
331 gst_pad_push_event (self->srcpad, gst_event_new_segment (&self->segment));
332 g_atomic_int_set (&self->priv->send_segment, FALSE);
336 if (priv->tags && priv->tags_changed) {
337 gst_pad_push_event (self->srcpad,
338 gst_event_new_tag (gst_tag_list_ref (priv->tags)));
339 priv->tags_changed = FALSE;
344 * gst_aggregator_finish_buffer:
345 * @self: The #GstAggregator
346 * @buffer: the #GstBuffer to push.
348 * This method will take care of sending mandatory events before pushing
349 * the provided buffer.
352 gst_aggregator_finish_buffer (GstAggregator * self, GstBuffer * buffer)
354 _push_mandatory_events (self);
356 if (!g_atomic_int_get (&self->priv->flush_seeking) &&
357 gst_pad_is_active (self->srcpad)) {
358 GST_TRACE_OBJECT (self, "pushing buffer %" GST_PTR_FORMAT, buffer);
359 return gst_pad_push (self->srcpad, buffer);
361 GST_INFO_OBJECT (self, "Not pushing (active: %i, flushing: %i)",
362 g_atomic_int_get (&self->priv->flush_seeking),
363 gst_pad_is_active (self->srcpad));
369 _push_eos (GstAggregator * self)
371 _push_mandatory_events (self);
373 self->priv->send_eos = FALSE;
374 gst_pad_push_event (self->srcpad, gst_event_new_eos ());
378 _remove_all_sources (GstAggregator * self)
382 MAIN_CONTEXT_LOCK (self);
384 g_main_context_find_source_by_user_data (self->priv->mcontext,
386 g_source_destroy (source);
388 MAIN_CONTEXT_UNLOCK (self);
392 aggregate_func (GstAggregator * self)
394 GstAggregatorPrivate *priv = self->priv;
395 GstAggregatorClass *klass = GST_AGGREGATOR_GET_CLASS (self);
397 GST_LOG_OBJECT (self, "Checking aggregate");
398 while (priv->send_eos && gst_aggregator_iterate_sinkpads (self,
399 (GstAggregatorPadForeachFunc) _check_all_pads_with_data_or_eos,
400 NULL) && priv->running) {
401 GST_TRACE_OBJECT (self, "Actually aggregating!");
403 priv->flow_return = klass->aggregate (self);
405 if (priv->flow_return == GST_FLOW_EOS) {
406 g_main_context_wakeup (self->priv->mcontext);
407 _remove_all_sources (self);
411 if (priv->flow_return == GST_FLOW_FLUSHING &&
412 g_atomic_int_get (&priv->flush_seeking))
413 priv->flow_return = GST_FLOW_OK;
415 GST_LOG_OBJECT (self, "flow return is %s",
416 gst_flow_get_name (priv->flow_return));
418 if (priv->flow_return != GST_FLOW_OK)
422 return G_SOURCE_REMOVE;
426 iterate_main_context_func (GstAggregator * self)
428 if (self->priv->running == FALSE) {
429 GST_DEBUG_OBJECT (self, "Not running anymore");
434 g_main_context_iteration (self->priv->mcontext, TRUE);
438 _start (GstAggregator * self)
440 self->priv->running = TRUE;
441 self->priv->send_stream_start = TRUE;
442 self->priv->send_segment = TRUE;
443 self->priv->send_eos = TRUE;
444 self->priv->srccaps = NULL;
450 _check_pending_flush_stop (GstAggregatorPad * pad)
452 return (!pad->priv->pending_flush_stop && !pad->priv->pending_flush_start);
456 _stop_srcpad_task (GstAggregator * self, GstEvent * flush_start)
460 GST_INFO_OBJECT (self, "%s srcpad task",
461 flush_start ? "Pausing" : "Stopping");
463 self->priv->running = FALSE;
465 /* Clean the stack of GSource set on the MainContext */
466 g_main_context_wakeup (self->priv->mcontext);
467 _remove_all_sources (self);
469 res = gst_pad_push_event (self->srcpad, flush_start);
472 gst_pad_stop_task (self->srcpad);
478 _start_srcpad_task (GstAggregator * self)
480 GST_INFO_OBJECT (self, "Starting srcpad task");
482 self->priv->running = TRUE;
483 gst_pad_start_task (GST_PAD (self->srcpad),
484 (GstTaskFunction) iterate_main_context_func, self, NULL);
488 _add_aggregate_gsource (GstAggregator * self)
490 MAIN_CONTEXT_LOCK (self);
491 g_main_context_invoke (self->priv->mcontext, (GSourceFunc) aggregate_func,
493 MAIN_CONTEXT_UNLOCK (self);
497 _flush (GstAggregator * self)
499 GstFlowReturn ret = GST_FLOW_OK;
500 GstAggregatorPrivate *priv = self->priv;
501 GstAggregatorClass *klass = GST_AGGREGATOR_GET_CLASS (self);
503 GST_DEBUG_OBJECT (self, "Flushing everything");
504 g_atomic_int_set (&priv->send_segment, TRUE);
505 g_atomic_int_set (&priv->flush_seeking, FALSE);
506 g_atomic_int_set (&priv->tags_changed, FALSE);
508 ret = klass->flush (self);
514 _all_flush_stop_received (GstAggregator * self)
517 GstAggregatorPad *tmppad;
519 GST_OBJECT_LOCK (self);
520 for (tmp = GST_ELEMENT (self)->sinkpads; tmp; tmp = tmp->next) {
521 tmppad = (GstAggregatorPad *) tmp->data;
523 if (_check_pending_flush_stop (tmppad) == FALSE) {
524 GST_DEBUG_OBJECT (tmppad, "Is not last %i -- %i",
525 tmppad->priv->pending_flush_start, tmppad->priv->pending_flush_stop);
526 GST_OBJECT_UNLOCK (self);
530 GST_OBJECT_UNLOCK (self);
535 /* GstAggregator vmethods default implementations */
537 _sink_event (GstAggregator * self, GstAggregatorPad * aggpad, GstEvent * event)
540 GstPad *pad = GST_PAD (aggpad);
541 GstAggregatorPrivate *priv = self->priv;
542 GstAggregatorPadPrivate *padpriv = aggpad->priv;
544 switch (GST_EVENT_TYPE (event)) {
545 case GST_EVENT_FLUSH_START:
549 g_atomic_int_set (&aggpad->priv->flushing, TRUE);
550 /* Remove pad buffer and wake up the streaming thread */
551 tmpbuf = gst_aggregator_pad_steal_buffer (aggpad);
552 gst_buffer_replace (&tmpbuf, NULL);
553 if (g_atomic_int_compare_and_exchange (&padpriv->pending_flush_start,
554 TRUE, FALSE) == TRUE) {
555 GST_DEBUG_OBJECT (aggpad, "Expecting FLUSH_STOP now");
556 g_atomic_int_set (&padpriv->pending_flush_stop, TRUE);
559 if (g_atomic_int_get (&priv->flush_seeking)) {
560 /* If flush_seeking we forward the first FLUSH_START */
561 if (g_atomic_int_compare_and_exchange (&priv->pending_flush_start,
562 TRUE, FALSE) == TRUE) {
564 GST_DEBUG_OBJECT (self, "Flushing, pausing srcpad task");
565 priv->flow_return = GST_FLOW_OK;
566 _stop_srcpad_task (self, event);
568 GST_INFO_OBJECT (self, "Getting STREAM_LOCK while seeking");
569 GST_PAD_STREAM_LOCK (self->srcpad);
570 GST_LOG_OBJECT (self, "GOT STREAM_LOCK");
576 /* We forward only in one case: right after flush_seeking */
579 case GST_EVENT_FLUSH_STOP:
581 GST_DEBUG_OBJECT (aggpad, "Got FLUSH_STOP");
583 _aggpad_flush (aggpad, self);
584 if (g_atomic_int_get (&priv->flush_seeking)) {
585 g_atomic_int_set (&aggpad->priv->pending_flush_stop, FALSE);
587 if (g_atomic_int_get (&priv->flush_seeking)) {
588 if (_all_flush_stop_received (self)) {
589 /* That means we received FLUSH_STOP/FLUSH_STOP on
590 * all sinkpads -- Seeking is Done... sending FLUSH_STOP */
592 gst_pad_push_event (self->srcpad, event);
593 priv->send_eos = TRUE;
595 _add_aggregate_gsource (self);
597 GST_INFO_OBJECT (self, "Releasing source pad STREAM_LOCK");
598 GST_PAD_STREAM_UNLOCK (self->srcpad);
599 _start_srcpad_task (self);
604 /* We never forward the event */
609 GST_DEBUG_OBJECT (aggpad, "EOS");
611 /* We still have a buffer, and we don't want the subclass to have to
612 * check for it. Mark pending_eos, eos will be set when steal_buffer is
615 PAD_LOCK_EVENT (aggpad);
616 if (!aggpad->buffer) {
619 aggpad->priv->pending_eos = TRUE;
621 PAD_UNLOCK_EVENT (aggpad);
623 _add_aggregate_gsource (self);
626 case GST_EVENT_SEGMENT:
628 gst_event_copy_segment (event, &aggpad->segment);
629 PAD_UNLOCK_EVENT (aggpad);
633 case GST_EVENT_STREAM_START:
641 gst_event_parse_tag (event, &tags);
643 if (gst_tag_list_get_scope (tags) == GST_TAG_SCOPE_STREAM) {
644 gst_aggregator_merge_tags (self, tags, GST_TAG_MERGE_REPLACE);
645 gst_event_unref (event);
657 GST_DEBUG_OBJECT (pad, "Forwarding event: %" GST_PTR_FORMAT, event);
658 return gst_pad_event_default (pad, GST_OBJECT (self), event);
661 GST_DEBUG_OBJECT (pad, "Eating event: %" GST_PTR_FORMAT, event);
663 gst_event_unref (event);
669 _stop (GstAggregator * agg)
671 _reset_flow_values (agg);
676 /* GstElement vmethods implementations */
677 static GstStateChangeReturn
678 _change_state (GstElement * element, GstStateChange transition)
680 GstStateChangeReturn ret;
681 GstAggregator *self = GST_AGGREGATOR (element);
682 GstAggregatorClass *agg_class = GST_AGGREGATOR_GET_CLASS (self);
685 switch (transition) {
686 case GST_STATE_CHANGE_READY_TO_PAUSED:
687 agg_class->start (self);
694 GST_ELEMENT_CLASS (aggregator_parent_class)->change_state (element,
695 transition)) == GST_STATE_CHANGE_FAILURE)
699 switch (transition) {
700 case GST_STATE_CHANGE_PAUSED_TO_READY:
701 agg_class->stop (self);
711 GST_ERROR_OBJECT (element, "parent failed state change");
717 _release_pad (GstElement * element, GstPad * pad)
721 GstAggregator *self = GST_AGGREGATOR (element);
722 GstAggregatorPad *aggpad = GST_AGGREGATOR_PAD (pad);
724 GST_INFO_OBJECT (pad, "Removing pad");
726 g_atomic_int_set (&aggpad->priv->flushing, TRUE);
727 tmpbuf = gst_aggregator_pad_steal_buffer (aggpad);
728 gst_buffer_replace (&tmpbuf, NULL);
729 gst_element_remove_pad (element, pad);
731 /* Something changed make sure we try to aggregate */
732 _add_aggregate_gsource (self);
736 _request_new_pad (GstElement * element,
737 GstPadTemplate * templ, const gchar * req_name, const GstCaps * caps)
740 GstAggregatorPad *agg_pad;
742 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
743 GstAggregatorPrivate *priv = GST_AGGREGATOR (element)->priv;
745 self = GST_AGGREGATOR (element);
747 if (templ == gst_element_class_get_pad_template (klass, "sink_%u")) {
751 GST_OBJECT_LOCK (element);
752 if (req_name == NULL || strlen (req_name) < 6
753 || !g_str_has_prefix (req_name, "sink_")) {
754 /* no name given when requesting the pad, use next available int */
757 /* parse serial number from requested padname */
758 serial = g_ascii_strtoull (&req_name[5], NULL, 10);
759 if (serial >= priv->padcount)
760 priv->padcount = serial;
763 name = g_strdup_printf ("sink_%u", priv->padcount);
764 agg_pad = g_object_new (GST_AGGREGATOR_GET_CLASS (self)->sinkpads_type,
765 "name", name, "direction", GST_PAD_SINK, "template", templ, NULL);
767 GST_OBJECT_UNLOCK (element);
773 GST_DEBUG_OBJECT (element, "Adding pad %s", GST_PAD_NAME (agg_pad));
776 gst_pad_set_active (GST_PAD (agg_pad), TRUE);
778 /* add the pad to the element */
779 gst_element_add_pad (element, GST_PAD (agg_pad));
781 return GST_PAD (agg_pad);
785 _src_query (GstAggregator * self, GstQuery * query)
789 switch (GST_QUERY_TYPE (query)) {
790 case GST_QUERY_SEEKING:
794 /* don't pass it along as some (file)sink might claim it does
795 * whereas with a collectpads in between that will not likely work */
796 gst_query_parse_seeking (query, &format, NULL, NULL, NULL);
797 gst_query_set_seeking (query, format, FALSE, 0, -1);
806 return gst_pad_query_default (self->srcpad, GST_OBJECT (self), query);
813 event_forward_func (GstPad * pad, EventData * evdata)
816 GstPad *peer = gst_pad_get_peer (pad);
817 GstAggregatorPadPrivate *padpriv = GST_AGGREGATOR_PAD (pad)->priv;
820 ret = gst_pad_send_event (peer, gst_event_ref (evdata->event));
821 GST_DEBUG_OBJECT (pad, "return of event push is %d", ret);
822 gst_object_unref (peer);
825 evdata->result &= ret;
828 if (GST_EVENT_TYPE (evdata->event) == GST_EVENT_SEEK)
829 GST_ERROR_OBJECT (pad, "Event %" GST_PTR_FORMAT " failed", evdata->event);
831 GST_INFO_OBJECT (pad, "Event %" GST_PTR_FORMAT " failed", evdata->event);
834 padpriv->pending_flush_start = FALSE;
835 padpriv->pending_flush_stop = FALSE;
839 /* Always send to all pads */
844 _set_flush_pending (GstAggregator * self, GstAggregatorPad * pad,
847 pad->priv->pending_flush_start = TRUE;
848 pad->priv->pending_flush_stop = FALSE;
854 _forward_event_to_all_sinkpads (GstAggregator * self, GstEvent * event,
859 evdata.event = event;
860 evdata.result = TRUE;
861 evdata.flush = flush;
863 /* We first need to set all pads as flushing in a first pass
864 * as flush_start flush_stop is sometimes sent synchronously
865 * while we send the seek event */
867 gst_aggregator_iterate_sinkpads (self,
868 (GstAggregatorPadForeachFunc) _set_flush_pending, NULL);
869 gst_pad_forward (self->srcpad, (GstPadForwardFunction) event_forward_func,
872 gst_event_unref (event);
874 return evdata.result;
878 _do_seek (GstAggregator * self, GstEvent * event)
883 GstSeekType start_type, stop_type;
887 GstAggregatorPrivate *priv = self->priv;
889 gst_event_parse_seek (event, &rate, &fmt, &flags, &start_type,
890 &start, &stop_type, &stop);
892 GST_INFO_OBJECT (self, "starting SEEK");
894 flush = flags & GST_SEEK_FLAG_FLUSH;
897 g_atomic_int_set (&priv->pending_flush_start, TRUE);
898 g_atomic_int_set (&priv->flush_seeking, TRUE);
901 gst_segment_do_seek (&self->segment, rate, fmt, flags, start_type, start,
902 stop_type, stop, NULL);
904 /* forward the seek upstream */
905 res = _forward_event_to_all_sinkpads (self, event, flush);
909 g_atomic_int_set (&priv->flush_seeking, FALSE);
910 g_atomic_int_set (&priv->pending_flush_start, FALSE);
913 GST_INFO_OBJECT (self, "seek done, result: %d", res);
919 _src_event (GstAggregator * self, GstEvent * event)
923 switch (GST_EVENT_TYPE (event)) {
926 res = _do_seek (self, event);
930 case GST_EVENT_NAVIGATION:
932 /* navigation is rather pointless. */
934 gst_event_unref (event);
943 return _forward_event_to_all_sinkpads (self, event, FALSE);
950 src_event_func (GstPad * pad, GstObject * parent, GstEvent * event)
952 GstAggregatorClass *klass = GST_AGGREGATOR_GET_CLASS (parent);
954 return klass->src_event (GST_AGGREGATOR (parent), event);
958 src_query_func (GstPad * pad, GstObject * parent, GstQuery * query)
960 GstAggregatorClass *klass = GST_AGGREGATOR_GET_CLASS (parent);
962 return klass->src_query (GST_AGGREGATOR (parent), query);
966 src_activate_mode (GstPad * pad,
967 GstObject * parent, GstPadMode mode, gboolean active)
969 GstAggregator *self = GST_AGGREGATOR (parent);
970 GstAggregatorClass *klass = GST_AGGREGATOR_GET_CLASS (parent);
972 if (klass->src_activate) {
973 if (klass->src_activate (self, mode, active) == FALSE) {
978 if (active == TRUE) {
980 case GST_PAD_MODE_PUSH:
982 GST_INFO_OBJECT (pad, "Activating pad!");
983 _start_srcpad_task (self);
988 GST_ERROR_OBJECT (pad, "Only supported mode is PUSH");
995 GST_INFO_OBJECT (self, "Deactivating srcpad");
996 _stop_srcpad_task (self, FALSE);
1002 _sink_query (GstAggregator * self, GstAggregatorPad * aggpad, GstQuery * query)
1004 GstPad *pad = GST_PAD (aggpad);
1006 return gst_pad_query_default (pad, GST_OBJECT (self), query);
1010 gst_aggregator_finalize (GObject * object)
1012 GstAggregator *self = (GstAggregator *) object;
1014 g_mutex_clear (&self->priv->mcontext_lock);
1016 G_OBJECT_CLASS (aggregator_parent_class)->finalize (object);
1019 /* GObject vmethods implementations */
1021 gst_aggregator_class_init (GstAggregatorClass * klass)
1023 GObjectClass *gobject_class = (GObjectClass *) klass;
1024 GstElementClass *gstelement_class = (GstElementClass *) klass;
1026 aggregator_parent_class = g_type_class_peek_parent (klass);
1027 g_type_class_add_private (klass, sizeof (GstAggregatorPrivate));
1029 GST_DEBUG_CATEGORY_INIT (aggregator_debug, "aggregator",
1030 GST_DEBUG_FG_MAGENTA, "GstAggregator");
1032 klass->sinkpads_type = GST_TYPE_AGGREGATOR_PAD;
1033 klass->start = _start;
1034 klass->stop = _stop;
1036 klass->sink_event = _sink_event;
1037 klass->sink_query = _sink_query;
1039 klass->src_event = _src_event;
1040 klass->src_query = _src_query;
1042 gstelement_class->request_new_pad = GST_DEBUG_FUNCPTR (_request_new_pad);
1043 gstelement_class->release_pad = GST_DEBUG_FUNCPTR (_release_pad);
1044 gstelement_class->change_state = GST_DEBUG_FUNCPTR (_change_state);
1046 gobject_class->finalize = gst_aggregator_finalize;
1050 gst_aggregator_init (GstAggregator * self, GstAggregatorClass * klass)
1052 GstPadTemplate *pad_template;
1053 GstAggregatorPrivate *priv;
1055 g_return_if_fail (klass->aggregate != NULL);
1058 G_TYPE_INSTANCE_GET_PRIVATE (self, GST_TYPE_AGGREGATOR,
1059 GstAggregatorPrivate);
1064 gst_element_class_get_pad_template (GST_ELEMENT_CLASS (klass), "src");
1065 g_return_if_fail (pad_template != NULL);
1067 priv->padcount = -1;
1068 priv->tags_changed = FALSE;
1069 _reset_flow_values (self);
1071 priv->mcontext = g_main_context_new ();
1072 self->srcpad = gst_pad_new_from_template (pad_template, "src");
1074 gst_pad_set_event_function (self->srcpad,
1075 GST_DEBUG_FUNCPTR ((GstPadEventFunction) src_event_func));
1076 gst_pad_set_query_function (self->srcpad,
1077 GST_DEBUG_FUNCPTR ((GstPadQueryFunction) src_query_func));
1078 gst_pad_set_activatemode_function (self->srcpad,
1079 GST_DEBUG_FUNCPTR ((GstPadActivateModeFunction) src_activate_mode));
1081 gst_element_add_pad (GST_ELEMENT (self), self->srcpad);
1083 g_mutex_init (&self->priv->mcontext_lock);
1086 /* we can't use G_DEFINE_ABSTRACT_TYPE because we need the klass in the _init
1087 * method to get to the padtemplates */
1089 gst_aggregator_get_type (void)
1091 static volatile gsize type = 0;
1093 if (g_once_init_enter (&type)) {
1095 static const GTypeInfo info = {
1096 sizeof (GstAggregatorClass),
1099 (GClassInitFunc) gst_aggregator_class_init,
1102 sizeof (GstAggregator),
1104 (GInstanceInitFunc) gst_aggregator_init,
1107 _type = g_type_register_static (GST_TYPE_ELEMENT,
1108 "GstAggregator", &info, G_TYPE_FLAG_ABSTRACT);
1109 g_once_init_leave (&type, _type);
1114 static GstFlowReturn
1115 _chain (GstPad * pad, GstObject * object, GstBuffer * buffer)
1117 GstBuffer *actual_buf = buffer;
1118 GstAggregator *self = GST_AGGREGATOR (object);
1119 GstAggregatorPrivate *priv = self->priv;
1120 GstAggregatorPad *aggpad = GST_AGGREGATOR_PAD (pad);
1121 GstAggregatorClass *aggclass = GST_AGGREGATOR_GET_CLASS (object);
1123 GST_DEBUG_OBJECT (aggpad, "Start chaining a buffer %" GST_PTR_FORMAT, buffer);
1125 if (g_atomic_int_get (&aggpad->priv->flushing) == TRUE)
1128 if (g_atomic_int_get (&aggpad->priv->pending_eos) == TRUE)
1131 PAD_LOCK_EVENT (aggpad);
1132 if (aggpad->buffer) {
1133 GST_DEBUG_OBJECT (aggpad, "Waiting for buffer to be consumed");
1134 PAD_WAIT_EVENT (aggpad);
1136 PAD_UNLOCK_EVENT (aggpad);
1138 if (g_atomic_int_get (&aggpad->priv->flushing) == TRUE)
1142 if (aggclass->clip) {
1143 aggclass->clip (self, aggpad, buffer, &actual_buf);
1146 PAD_LOCK_EVENT (aggpad);
1148 gst_buffer_unref (aggpad->buffer);
1149 aggpad->buffer = actual_buf;
1150 PAD_UNLOCK_EVENT (aggpad);
1152 _add_aggregate_gsource (self);
1154 GST_DEBUG_OBJECT (aggpad, "Done chaining");
1156 return priv->flow_return;
1160 GST_DEBUG_OBJECT (aggpad, "We are flushing");
1162 return GST_FLOW_FLUSHING;
1166 GST_DEBUG_OBJECT (pad, "We are EOS already...");
1168 return GST_FLOW_EOS;
1172 pad_query_func (GstPad * pad, GstObject * parent, GstQuery * query)
1174 GstAggregatorClass *klass = GST_AGGREGATOR_GET_CLASS (parent);
1176 return klass->sink_query (GST_AGGREGATOR (parent),
1177 GST_AGGREGATOR_PAD (pad), query);
1181 pad_event_func (GstPad * pad, GstObject * parent, GstEvent * event)
1183 GstAggregatorClass *klass = GST_AGGREGATOR_GET_CLASS (parent);
1185 return klass->sink_event (GST_AGGREGATOR (parent),
1186 GST_AGGREGATOR_PAD (pad), event);
1190 pad_activate_mode_func (GstPad * pad,
1191 GstObject * parent, GstPadMode mode, gboolean active)
1193 GstAggregatorPad *aggpad = GST_AGGREGATOR_PAD (pad);
1195 if (active == FALSE) {
1196 PAD_LOCK_EVENT (aggpad);
1197 g_atomic_int_set (&aggpad->priv->flushing, TRUE);
1198 gst_buffer_replace (&aggpad->buffer, NULL);
1199 PAD_BROADCAST_EVENT (aggpad);
1200 PAD_UNLOCK_EVENT (aggpad);
1202 g_atomic_int_set (&aggpad->priv->flushing, FALSE);
1203 PAD_LOCK_EVENT (aggpad);
1204 PAD_BROADCAST_EVENT (aggpad);
1205 PAD_UNLOCK_EVENT (aggpad);
1211 /***********************************
1212 * GstAggregatorPad implementation *
1213 ************************************/
1214 static GstPadClass *aggregator_pad_parent_class = NULL;
1215 G_DEFINE_TYPE (GstAggregatorPad, gst_aggregator_pad, GST_TYPE_PAD);
1218 _pad_constructed (GObject * object)
1220 GstPad *pad = GST_PAD (object);
1222 gst_pad_set_chain_function (pad,
1223 GST_DEBUG_FUNCPTR ((GstPadChainFunction) _chain));
1224 gst_pad_set_event_function (pad,
1225 GST_DEBUG_FUNCPTR ((GstPadEventFunction) pad_event_func));
1226 gst_pad_set_query_function (pad,
1227 GST_DEBUG_FUNCPTR ((GstPadQueryFunction) pad_query_func));
1228 gst_pad_set_activatemode_function (pad,
1229 GST_DEBUG_FUNCPTR ((GstPadActivateModeFunction) pad_activate_mode_func));
1233 gst_aggregator_pad_finalize (GObject * object)
1235 GstAggregatorPad *pad = (GstAggregatorPad *) object;
1237 g_mutex_clear (&pad->priv->event_lock);
1238 g_cond_clear (&pad->priv->event_cond);
1240 G_OBJECT_CLASS (aggregator_pad_parent_class)->finalize (object);
1244 gst_aggregator_pad_dispose (GObject * object)
1246 GstAggregatorPad *pad = (GstAggregatorPad *) object;
1249 buf = gst_aggregator_pad_steal_buffer (pad);
1251 gst_buffer_unref (buf);
1253 G_OBJECT_CLASS (aggregator_pad_parent_class)->dispose (object);
1257 gst_aggregator_pad_class_init (GstAggregatorPadClass * klass)
1259 GObjectClass *gobject_class = (GObjectClass *) klass;
1261 aggregator_pad_parent_class = g_type_class_peek_parent (klass);
1262 g_type_class_add_private (klass, sizeof (GstAggregatorPadPrivate));
1264 gobject_class->constructed = GST_DEBUG_FUNCPTR (_pad_constructed);
1265 gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_aggregator_pad_finalize);
1266 gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_aggregator_pad_dispose);
1270 gst_aggregator_pad_init (GstAggregatorPad * pad)
1273 G_TYPE_INSTANCE_GET_PRIVATE (pad, GST_TYPE_AGGREGATOR_PAD,
1274 GstAggregatorPadPrivate);
1277 g_mutex_init (&pad->priv->event_lock);
1278 g_cond_init (&pad->priv->event_cond);
1283 * gst_aggregator_pad_steal_buffer:
1284 * @pad: the pad to get buffer from
1286 * Steal the ref to the buffer currently queued in @pad.
1288 * Returns: (transfer full): The buffer in @pad or NULL if no buffer was
1289 * queued. You should unref the buffer after usage.
1292 gst_aggregator_pad_steal_buffer (GstAggregatorPad * pad)
1294 GstBuffer *buffer = NULL;
1296 PAD_LOCK_EVENT (pad);
1298 GST_TRACE_OBJECT (pad, "Consuming buffer");
1299 buffer = pad->buffer;
1301 if (pad->priv->pending_eos) {
1302 pad->priv->pending_eos = FALSE;
1305 PAD_BROADCAST_EVENT (pad);
1306 GST_DEBUG_OBJECT (pad, "Consummed: %" GST_PTR_FORMAT, buffer);
1308 PAD_UNLOCK_EVENT (pad);
1314 * gst_aggregator_pad_get_buffer:
1315 * @pad: the pad to get buffer from
1317 * Returns: (transfer full): A reference to the buffer in @pad or
1318 * NULL if no buffer was queued. You should unref the buffer after
1322 gst_aggregator_pad_get_buffer (GstAggregatorPad * pad)
1324 GstBuffer *buffer = NULL;
1326 PAD_LOCK_EVENT (pad);
1328 buffer = gst_buffer_ref (pad->buffer);
1329 PAD_UNLOCK_EVENT (pad);
1335 * gst_aggregator_merge_tags:
1336 * @self: a #GstAggregator
1337 * @tags: a #GstTagList to merge
1338 * @mode: the #GstTagMergeMode to use
1340 * Adds tags to so-called pending tags, which will be processed
1341 * before pushing out data downstream.
1343 * Note that this is provided for convenience, and the subclass is
1344 * not required to use this and can still do tag handling on its own.
1349 gst_aggregator_merge_tags (GstAggregator * self,
1350 const GstTagList * tags, GstTagMergeMode mode)
1354 g_return_if_fail (GST_IS_AGGREGATOR (self));
1355 g_return_if_fail (tags == NULL || GST_IS_TAG_LIST (tags));
1357 /* FIXME Check if we can use OBJECT lock here! */
1358 GST_OBJECT_LOCK (self);
1360 GST_DEBUG_OBJECT (self, "merging tags %" GST_PTR_FORMAT, tags);
1361 otags = self->priv->tags;
1362 self->priv->tags = gst_tag_list_merge (self->priv->tags, tags, mode);
1364 gst_tag_list_unref (otags);
1365 self->priv->tags_changed = TRUE;
1366 GST_OBJECT_UNLOCK (self);