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 self->priv->srccaps = NULL;
327 if (g_atomic_int_get (&self->priv->send_segment)) {
328 if (!g_atomic_int_get (&self->priv->flush_seeking)) {
329 GST_INFO_OBJECT (self, "pushing segment");
330 gst_pad_push_event (self->srcpad, gst_event_new_segment (&self->segment));
331 g_atomic_int_set (&self->priv->send_segment, FALSE);
335 if (priv->tags && priv->tags_changed) {
336 gst_pad_push_event (self->srcpad,
337 gst_event_new_tag (gst_tag_list_ref (priv->tags)));
338 priv->tags_changed = FALSE;
343 * gst_aggregator_finish_buffer:
344 * @self: The #GstAggregator
345 * @buffer: the #GstBuffer to push.
347 * This method will take care of sending mandatory events before pushing
348 * the provided buffer.
351 gst_aggregator_finish_buffer (GstAggregator * self, GstBuffer * buffer)
353 _push_mandatory_events (self);
355 if (!g_atomic_int_get (&self->priv->flush_seeking) &&
356 gst_pad_is_active (self->srcpad)) {
357 GST_TRACE_OBJECT (self, "pushing buffer %" GST_PTR_FORMAT, buffer);
358 return gst_pad_push (self->srcpad, buffer);
360 GST_INFO_OBJECT (self, "Not pushing (active: %i, flushing: %i)",
361 g_atomic_int_get (&self->priv->flush_seeking),
362 gst_pad_is_active (self->srcpad));
368 _push_eos (GstAggregator * self)
370 _push_mandatory_events (self);
372 self->priv->send_eos = FALSE;
373 gst_pad_push_event (self->srcpad, gst_event_new_eos ());
377 _remove_all_sources (GstAggregator * self)
381 MAIN_CONTEXT_LOCK (self);
383 g_main_context_find_source_by_user_data (self->priv->mcontext,
385 g_source_destroy (source);
387 MAIN_CONTEXT_UNLOCK (self);
391 aggregate_func (GstAggregator * self)
393 GstAggregatorPrivate *priv = self->priv;
394 GstAggregatorClass *klass = GST_AGGREGATOR_GET_CLASS (self);
396 GST_LOG_OBJECT (self, "Checking aggregate");
397 while (priv->send_eos && gst_aggregator_iterate_sinkpads (self,
398 (GstAggregatorPadForeachFunc) _check_all_pads_with_data_or_eos,
399 NULL) && priv->running) {
400 GST_TRACE_OBJECT (self, "Actually aggregating!");
402 priv->flow_return = klass->aggregate (self);
404 if (priv->flow_return == GST_FLOW_EOS) {
405 g_main_context_wakeup (self->priv->mcontext);
406 _remove_all_sources (self);
410 if (priv->flow_return == GST_FLOW_FLUSHING &&
411 g_atomic_int_get (&priv->flush_seeking))
412 priv->flow_return = GST_FLOW_OK;
414 GST_LOG_OBJECT (self, "flow return is %s",
415 gst_flow_get_name (priv->flow_return));
417 if (priv->flow_return != GST_FLOW_OK)
421 return G_SOURCE_REMOVE;
425 iterate_main_context_func (GstAggregator * self)
427 if (self->priv->running == FALSE) {
428 GST_DEBUG_OBJECT (self, "Not running anymore");
433 g_main_context_iteration (self->priv->mcontext, TRUE);
437 _start (GstAggregator * self)
439 self->priv->running = TRUE;
440 self->priv->send_stream_start = TRUE;
441 self->priv->send_segment = TRUE;
442 self->priv->send_eos = TRUE;
443 self->priv->srccaps = NULL;
449 _check_pending_flush_stop (GstAggregatorPad * pad)
451 return (!pad->priv->pending_flush_stop && !pad->priv->pending_flush_start);
455 _stop_srcpad_task (GstAggregator * self, GstEvent * flush_start)
459 GST_INFO_OBJECT (self, "%s srcpad task",
460 flush_start ? "Pausing" : "Stopping");
462 self->priv->running = FALSE;
464 /* Clean the stack of GSource set on the MainContext */
465 g_main_context_wakeup (self->priv->mcontext);
466 _remove_all_sources (self);
468 res = gst_pad_push_event (self->srcpad, flush_start);
471 gst_pad_stop_task (self->srcpad);
477 _start_srcpad_task (GstAggregator * self)
479 GST_INFO_OBJECT (self, "Starting srcpad task");
481 self->priv->running = TRUE;
482 gst_pad_start_task (GST_PAD (self->srcpad),
483 (GstTaskFunction) iterate_main_context_func, self, NULL);
487 _add_aggregate_gsource (GstAggregator * self)
489 MAIN_CONTEXT_LOCK (self);
490 g_main_context_invoke (self->priv->mcontext, (GSourceFunc) aggregate_func,
492 MAIN_CONTEXT_UNLOCK (self);
496 _flush (GstAggregator * self)
498 GstFlowReturn ret = GST_FLOW_OK;
499 GstAggregatorPrivate *priv = self->priv;
500 GstAggregatorClass *klass = GST_AGGREGATOR_GET_CLASS (self);
502 GST_DEBUG_OBJECT (self, "Flushing everything");
503 g_atomic_int_set (&priv->send_segment, TRUE);
504 g_atomic_int_set (&priv->flush_seeking, FALSE);
505 g_atomic_int_set (&priv->tags_changed, FALSE);
507 ret = klass->flush (self);
513 _all_flush_stop_received (GstAggregator * self)
516 GstAggregatorPad *tmppad;
518 GST_OBJECT_LOCK (self);
519 for (tmp = GST_ELEMENT (self)->sinkpads; tmp; tmp = tmp->next) {
520 tmppad = (GstAggregatorPad *) tmp->data;
522 if (_check_pending_flush_stop (tmppad) == FALSE) {
523 GST_DEBUG_OBJECT (tmppad, "Is not last %i -- %i",
524 tmppad->priv->pending_flush_start, tmppad->priv->pending_flush_stop);
525 GST_OBJECT_UNLOCK (self);
529 GST_OBJECT_UNLOCK (self);
534 /* GstAggregator vmethods default implementations */
536 _sink_event (GstAggregator * self, GstAggregatorPad * aggpad, GstEvent * event)
539 GstPad *pad = GST_PAD (aggpad);
540 GstAggregatorPrivate *priv = self->priv;
541 GstAggregatorPadPrivate *padpriv = aggpad->priv;
543 switch (GST_EVENT_TYPE (event)) {
544 case GST_EVENT_FLUSH_START:
548 g_atomic_int_set (&aggpad->priv->flushing, TRUE);
549 /* Remove pad buffer and wake up the streaming thread */
550 tmpbuf = gst_aggregator_pad_steal_buffer (aggpad);
551 gst_buffer_replace (&tmpbuf, NULL);
552 if (g_atomic_int_compare_and_exchange (&padpriv->pending_flush_start,
553 TRUE, FALSE) == TRUE) {
554 GST_DEBUG_OBJECT (aggpad, "Expecting FLUSH_STOP now");
555 g_atomic_int_set (&padpriv->pending_flush_stop, TRUE);
558 if (g_atomic_int_get (&priv->flush_seeking)) {
559 /* If flush_seeking we forward the first FLUSH_START */
560 if (g_atomic_int_compare_and_exchange (&priv->pending_flush_start,
561 TRUE, FALSE) == TRUE) {
563 GST_DEBUG_OBJECT (self, "Flushing, pausing srcpad task");
564 priv->flow_return = GST_FLOW_OK;
565 _stop_srcpad_task (self, event);
567 GST_INFO_OBJECT (self, "Getting STREAM_LOCK while seeking");
568 GST_PAD_STREAM_LOCK (self->srcpad);
569 GST_LOG_OBJECT (self, "GOT STREAM_LOCK");
575 /* We forward only in one case: right after flush_seeking */
578 case GST_EVENT_FLUSH_STOP:
580 GST_DEBUG_OBJECT (aggpad, "Got FLUSH_STOP");
582 _aggpad_flush (aggpad, self);
583 if (g_atomic_int_get (&priv->flush_seeking)) {
584 g_atomic_int_set (&aggpad->priv->pending_flush_stop, FALSE);
586 if (g_atomic_int_get (&priv->flush_seeking)) {
587 if (_all_flush_stop_received (self)) {
588 /* That means we received FLUSH_STOP/FLUSH_STOP on
589 * all sinkpads -- Seeking is Done... sending FLUSH_STOP */
591 gst_pad_push_event (self->srcpad, event);
592 priv->send_eos = TRUE;
594 _add_aggregate_gsource (self);
596 GST_INFO_OBJECT (self, "Releasing source pad STREAM_LOCK");
597 GST_PAD_STREAM_UNLOCK (self->srcpad);
598 _start_srcpad_task (self);
603 /* We never forward the event */
608 GST_DEBUG_OBJECT (aggpad, "EOS");
610 /* We still have a buffer, and we don't want the subclass to have to
611 * check for it. Mark pending_eos, eos will be set when steal_buffer is
614 PAD_LOCK_EVENT (aggpad);
615 if (!aggpad->buffer) {
618 aggpad->priv->pending_eos = TRUE;
620 PAD_UNLOCK_EVENT (aggpad);
622 _add_aggregate_gsource (self);
625 case GST_EVENT_SEGMENT:
627 gst_event_copy_segment (event, &aggpad->segment);
628 PAD_UNLOCK_EVENT (aggpad);
632 case GST_EVENT_STREAM_START:
640 gst_event_parse_tag (event, &tags);
642 if (gst_tag_list_get_scope (tags) == GST_TAG_SCOPE_STREAM) {
643 gst_aggregator_merge_tags (self, tags, GST_TAG_MERGE_REPLACE);
644 gst_event_unref (event);
656 GST_DEBUG_OBJECT (pad, "Forwarding event: %" GST_PTR_FORMAT, event);
657 return gst_pad_event_default (pad, GST_OBJECT (self), event);
660 GST_DEBUG_OBJECT (pad, "Eating event: %" GST_PTR_FORMAT, event);
662 gst_event_unref (event);
668 _stop (GstAggregator * agg)
670 _reset_flow_values (agg);
675 /* GstElement vmethods implementations */
676 static GstStateChangeReturn
677 _change_state (GstElement * element, GstStateChange transition)
679 GstStateChangeReturn ret;
680 GstAggregator *self = GST_AGGREGATOR (element);
681 GstAggregatorClass *agg_class = GST_AGGREGATOR_GET_CLASS (self);
684 switch (transition) {
685 case GST_STATE_CHANGE_READY_TO_PAUSED:
686 agg_class->start (self);
693 GST_ELEMENT_CLASS (aggregator_parent_class)->change_state (element,
694 transition)) == GST_STATE_CHANGE_FAILURE)
698 switch (transition) {
699 case GST_STATE_CHANGE_PAUSED_TO_READY:
700 agg_class->stop (self);
710 GST_ERROR_OBJECT (element, "parent failed state change");
716 _release_pad (GstElement * element, GstPad * pad)
720 GstAggregator *self = GST_AGGREGATOR (element);
721 GstAggregatorPad *aggpad = GST_AGGREGATOR_PAD (pad);
723 GST_INFO_OBJECT (pad, "Removing pad");
725 g_atomic_int_set (&aggpad->priv->flushing, TRUE);
726 tmpbuf = gst_aggregator_pad_steal_buffer (aggpad);
727 gst_buffer_replace (&tmpbuf, NULL);
728 gst_element_remove_pad (element, pad);
730 /* Something changed make sure we try to aggregate */
731 _add_aggregate_gsource (self);
735 _request_new_pad (GstElement * element,
736 GstPadTemplate * templ, const gchar * req_name, const GstCaps * caps)
739 GstAggregatorPad *agg_pad;
741 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
742 GstAggregatorPrivate *priv = GST_AGGREGATOR (element)->priv;
744 self = GST_AGGREGATOR (element);
746 if (templ == gst_element_class_get_pad_template (klass, "sink_%u")) {
750 GST_OBJECT_LOCK (element);
751 if (req_name == NULL || strlen (req_name) < 6
752 || !g_str_has_prefix (req_name, "sink_")) {
753 /* no name given when requesting the pad, use next available int */
756 /* parse serial number from requested padname */
757 serial = g_ascii_strtoull (&req_name[5], NULL, 10);
758 if (serial >= priv->padcount)
759 priv->padcount = serial + 1;
762 name = g_strdup_printf ("sink_%u", priv->padcount);
763 agg_pad = g_object_new (GST_AGGREGATOR_GET_CLASS (self)->sinkpads_type,
764 "name", name, "direction", GST_PAD_SINK, "template", templ, NULL);
766 GST_OBJECT_UNLOCK (element);
772 GST_DEBUG_OBJECT (element, "Adding pad %s", GST_PAD_NAME (agg_pad));
775 gst_pad_set_active (GST_PAD (agg_pad), TRUE);
777 /* add the pad to the element */
778 gst_element_add_pad (element, GST_PAD (agg_pad));
780 return GST_PAD (agg_pad);
784 _src_query (GstAggregator * self, GstQuery * query)
788 switch (GST_QUERY_TYPE (query)) {
789 case GST_QUERY_SEEKING:
793 /* don't pass it along as some (file)sink might claim it does
794 * whereas with a collectpads in between that will not likely work */
795 gst_query_parse_seeking (query, &format, NULL, NULL, NULL);
796 gst_query_set_seeking (query, format, FALSE, 0, -1);
805 return gst_pad_query_default (self->srcpad, GST_OBJECT (self), query);
812 event_forward_func (GstPad * pad, EventData * evdata)
815 GstPad *peer = gst_pad_get_peer (pad);
816 GstAggregatorPadPrivate *padpriv = GST_AGGREGATOR_PAD (pad)->priv;
819 ret = gst_pad_send_event (peer, gst_event_ref (evdata->event));
820 GST_DEBUG_OBJECT (pad, "return of event push is %d", ret);
821 gst_object_unref (peer);
824 evdata->result &= ret;
827 if (GST_EVENT_TYPE (evdata->event) == GST_EVENT_SEEK)
828 GST_ERROR_OBJECT (pad, "Event %" GST_PTR_FORMAT " failed", evdata->event);
830 GST_INFO_OBJECT (pad, "Event %" GST_PTR_FORMAT " failed", evdata->event);
833 padpriv->pending_flush_start = FALSE;
834 padpriv->pending_flush_stop = FALSE;
838 /* Always send to all pads */
843 _set_flush_pending (GstAggregator * self, GstAggregatorPad * pad,
846 pad->priv->pending_flush_start = TRUE;
847 pad->priv->pending_flush_stop = FALSE;
853 _forward_event_to_all_sinkpads (GstAggregator * self, GstEvent * event,
858 evdata.event = event;
859 evdata.result = TRUE;
860 evdata.flush = flush;
862 /* We first need to set all pads as flushing in a first pass
863 * as flush_start flush_stop is sometimes sent synchronously
864 * while we send the seek event */
866 gst_aggregator_iterate_sinkpads (self,
867 (GstAggregatorPadForeachFunc) _set_flush_pending, NULL);
868 gst_pad_forward (self->srcpad, (GstPadForwardFunction) event_forward_func,
871 gst_event_unref (event);
873 return evdata.result;
877 _do_seek (GstAggregator * self, GstEvent * event)
882 GstSeekType start_type, stop_type;
886 GstAggregatorPrivate *priv = self->priv;
888 gst_event_parse_seek (event, &rate, &fmt, &flags, &start_type,
889 &start, &stop_type, &stop);
891 GST_INFO_OBJECT (self, "starting SEEK");
893 flush = flags & GST_SEEK_FLAG_FLUSH;
896 g_atomic_int_set (&priv->pending_flush_start, TRUE);
897 g_atomic_int_set (&priv->flush_seeking, TRUE);
900 gst_segment_do_seek (&self->segment, rate, fmt, flags, start_type, start,
901 stop_type, stop, NULL);
903 /* forward the seek upstream */
904 res = _forward_event_to_all_sinkpads (self, event, flush);
908 g_atomic_int_set (&priv->flush_seeking, FALSE);
909 g_atomic_int_set (&priv->pending_flush_start, FALSE);
912 GST_INFO_OBJECT (self, "seek done, result: %d", res);
918 _src_event (GstAggregator * self, GstEvent * event)
922 switch (GST_EVENT_TYPE (event)) {
925 res = _do_seek (self, event);
929 case GST_EVENT_NAVIGATION:
931 /* navigation is rather pointless. */
933 gst_event_unref (event);
942 return _forward_event_to_all_sinkpads (self, event, FALSE);
949 src_event_func (GstPad * pad, GstObject * parent, GstEvent * event)
951 GstAggregatorClass *klass = GST_AGGREGATOR_GET_CLASS (parent);
953 return klass->src_event (GST_AGGREGATOR (parent), event);
957 src_query_func (GstPad * pad, GstObject * parent, GstQuery * query)
959 GstAggregatorClass *klass = GST_AGGREGATOR_GET_CLASS (parent);
961 return klass->src_query (GST_AGGREGATOR (parent), query);
965 src_activate_mode (GstPad * pad,
966 GstObject * parent, GstPadMode mode, gboolean active)
968 GstAggregator *self = GST_AGGREGATOR (parent);
969 GstAggregatorClass *klass = GST_AGGREGATOR_GET_CLASS (parent);
971 if (klass->src_activate) {
972 if (klass->src_activate (self, mode, active) == FALSE) {
977 if (active == TRUE) {
979 case GST_PAD_MODE_PUSH:
981 GST_INFO_OBJECT (pad, "Activating pad!");
982 _start_srcpad_task (self);
987 GST_ERROR_OBJECT (pad, "Only supported mode is PUSH");
994 GST_INFO_OBJECT (self, "Deactivating srcpad");
995 _stop_srcpad_task (self, FALSE);
1001 _sink_query (GstAggregator * self, GstAggregatorPad * aggpad, GstQuery * query)
1003 GstPad *pad = GST_PAD (aggpad);
1005 return gst_pad_query_default (pad, GST_OBJECT (self), query);
1008 /* GObject vmethods implementations */
1010 gst_aggregator_class_init (GstAggregatorClass * klass)
1012 GstElementClass *gstelement_class = (GstElementClass *) klass;
1014 aggregator_parent_class = g_type_class_peek_parent (klass);
1015 g_type_class_add_private (klass, sizeof (GstAggregatorPrivate));
1017 GST_DEBUG_CATEGORY_INIT (aggregator_debug, "aggregator",
1018 GST_DEBUG_FG_MAGENTA, "GstAggregator");
1020 klass->sinkpads_type = GST_TYPE_AGGREGATOR_PAD;
1021 klass->start = _start;
1022 klass->stop = _stop;
1024 klass->sink_event = _sink_event;
1025 klass->sink_query = _sink_query;
1027 klass->src_event = _src_event;
1028 klass->src_query = _src_query;
1030 gstelement_class->request_new_pad = GST_DEBUG_FUNCPTR (_request_new_pad);
1031 gstelement_class->release_pad = GST_DEBUG_FUNCPTR (_release_pad);
1032 gstelement_class->change_state = GST_DEBUG_FUNCPTR (_change_state);
1036 gst_aggregator_init (GstAggregator * self, GstAggregatorClass * klass)
1038 GstPadTemplate *pad_template;
1039 GstAggregatorPrivate *priv;
1041 g_return_if_fail (klass->aggregate != NULL);
1044 G_TYPE_INSTANCE_GET_PRIVATE (self, GST_TYPE_AGGREGATOR,
1045 GstAggregatorPrivate);
1050 gst_element_class_get_pad_template (GST_ELEMENT_CLASS (klass), "src");
1051 g_return_if_fail (pad_template != NULL);
1053 priv->padcount = -1;
1054 priv->tags_changed = FALSE;
1055 _reset_flow_values (self);
1057 priv->mcontext = g_main_context_new ();
1058 self->srcpad = gst_pad_new_from_template (pad_template, "src");
1060 gst_pad_set_event_function (self->srcpad,
1061 GST_DEBUG_FUNCPTR ((GstPadEventFunction) src_event_func));
1062 gst_pad_set_query_function (self->srcpad,
1063 GST_DEBUG_FUNCPTR ((GstPadQueryFunction) src_query_func));
1064 gst_pad_set_activatemode_function (self->srcpad,
1065 GST_DEBUG_FUNCPTR ((GstPadActivateModeFunction) src_activate_mode));
1067 gst_element_add_pad (GST_ELEMENT (self), self->srcpad);
1070 /* we can't use G_DEFINE_ABSTRACT_TYPE because we need the klass in the _init
1071 * method to get to the padtemplates */
1073 gst_aggregator_get_type (void)
1075 static volatile gsize type = 0;
1077 if (g_once_init_enter (&type)) {
1079 static const GTypeInfo info = {
1080 sizeof (GstAggregatorClass),
1083 (GClassInitFunc) gst_aggregator_class_init,
1086 sizeof (GstAggregator),
1088 (GInstanceInitFunc) gst_aggregator_init,
1091 _type = g_type_register_static (GST_TYPE_ELEMENT,
1092 "GstAggregator", &info, G_TYPE_FLAG_ABSTRACT);
1093 g_once_init_leave (&type, _type);
1098 static GstFlowReturn
1099 _chain (GstPad * pad, GstObject * object, GstBuffer * buffer)
1101 GstBuffer *actual_buf = buffer;
1102 GstAggregator *self = GST_AGGREGATOR (object);
1103 GstAggregatorPrivate *priv = self->priv;
1104 GstAggregatorPad *aggpad = GST_AGGREGATOR_PAD (pad);
1105 GstAggregatorClass *aggclass = GST_AGGREGATOR_GET_CLASS (object);
1107 GST_DEBUG_OBJECT (aggpad, "Start chaining a buffer %" GST_PTR_FORMAT, buffer);
1109 if (g_atomic_int_get (&aggpad->priv->flushing) == TRUE)
1112 if (g_atomic_int_get (&aggpad->priv->pending_eos) == TRUE)
1115 PAD_LOCK_EVENT (aggpad);
1116 if (aggpad->buffer) {
1117 GST_DEBUG_OBJECT (aggpad, "Waiting for buffer to be consumed");
1118 PAD_WAIT_EVENT (aggpad);
1120 PAD_UNLOCK_EVENT (aggpad);
1122 if (g_atomic_int_get (&aggpad->priv->flushing) == TRUE)
1126 if (aggclass->clip) {
1127 aggclass->clip (self, aggpad, buffer, &actual_buf);
1130 PAD_LOCK_EVENT (aggpad);
1132 gst_buffer_unref (aggpad->buffer);
1133 aggpad->buffer = actual_buf;
1134 PAD_UNLOCK_EVENT (aggpad);
1136 _add_aggregate_gsource (self);
1138 GST_DEBUG_OBJECT (aggpad, "Done chaining");
1140 return priv->flow_return;
1144 GST_DEBUG_OBJECT (aggpad, "We are flushing");
1146 return GST_FLOW_FLUSHING;
1150 GST_DEBUG_OBJECT (pad, "We are EOS already...");
1152 return GST_FLOW_EOS;
1156 pad_query_func (GstPad * pad, GstObject * parent, GstQuery * query)
1158 GstAggregatorClass *klass = GST_AGGREGATOR_GET_CLASS (parent);
1160 return klass->sink_query (GST_AGGREGATOR (parent),
1161 GST_AGGREGATOR_PAD (pad), query);
1165 pad_event_func (GstPad * pad, GstObject * parent, GstEvent * event)
1167 GstAggregatorClass *klass = GST_AGGREGATOR_GET_CLASS (parent);
1169 return klass->sink_event (GST_AGGREGATOR (parent),
1170 GST_AGGREGATOR_PAD (pad), event);
1174 pad_activate_mode_func (GstPad * pad,
1175 GstObject * parent, GstPadMode mode, gboolean active)
1177 GstAggregatorPad *aggpad = GST_AGGREGATOR_PAD (pad);
1179 if (active == FALSE) {
1180 PAD_LOCK_EVENT (aggpad);
1181 g_atomic_int_set (&aggpad->priv->flushing, TRUE);
1182 gst_buffer_replace (&aggpad->buffer, NULL);
1183 PAD_BROADCAST_EVENT (aggpad);
1184 PAD_UNLOCK_EVENT (aggpad);
1186 g_atomic_int_set (&aggpad->priv->flushing, FALSE);
1187 PAD_LOCK_EVENT (aggpad);
1188 PAD_BROADCAST_EVENT (aggpad);
1189 PAD_UNLOCK_EVENT (aggpad);
1195 /***********************************
1196 * GstAggregatorPad implementation *
1197 ************************************/
1198 G_DEFINE_TYPE (GstAggregatorPad, gst_aggregator_pad, GST_TYPE_PAD);
1201 _pad_constructed (GObject * object)
1203 GstPad *pad = GST_PAD (object);
1205 gst_pad_set_chain_function (pad,
1206 GST_DEBUG_FUNCPTR ((GstPadChainFunction) _chain));
1207 gst_pad_set_event_function (pad,
1208 GST_DEBUG_FUNCPTR ((GstPadEventFunction) pad_event_func));
1209 gst_pad_set_query_function (pad,
1210 GST_DEBUG_FUNCPTR ((GstPadQueryFunction) pad_query_func));
1211 gst_pad_set_activatemode_function (pad,
1212 GST_DEBUG_FUNCPTR ((GstPadActivateModeFunction) pad_activate_mode_func));
1216 gst_aggregator_pad_class_init (GstAggregatorPadClass * klass)
1218 GObjectClass *gobject_class = (GObjectClass *) klass;
1220 g_type_class_add_private (klass, sizeof (GstAggregatorPadPrivate));
1222 gobject_class->constructed = GST_DEBUG_FUNCPTR (_pad_constructed);
1226 gst_aggregator_pad_init (GstAggregatorPad * pad)
1229 G_TYPE_INSTANCE_GET_PRIVATE (pad, GST_TYPE_AGGREGATOR_PAD,
1230 GstAggregatorPadPrivate);
1233 g_mutex_init (&pad->priv->event_lock);
1234 g_cond_init (&pad->priv->event_cond);
1239 * gst_aggregator_pad_steal_buffer:
1240 * @pad: the pad to get buffer from
1242 * Steal the ref to the buffer currently queued in @pad.
1244 * Returns: (transfer full): The buffer in @pad or NULL if no buffer was
1245 * queued. You should unref the buffer after usage.
1248 gst_aggregator_pad_steal_buffer (GstAggregatorPad * pad)
1250 GstBuffer *buffer = NULL;
1252 PAD_LOCK_EVENT (pad);
1254 GST_TRACE_OBJECT (pad, "Consuming buffer");
1255 buffer = pad->buffer;
1257 if (pad->priv->pending_eos) {
1258 pad->priv->pending_eos = FALSE;
1261 PAD_BROADCAST_EVENT (pad);
1262 GST_DEBUG_OBJECT (pad, "Consummed: %" GST_PTR_FORMAT, buffer);
1264 PAD_UNLOCK_EVENT (pad);
1270 * gst_aggregator_pad_get_buffer:
1271 * @pad: the pad to get buffer from
1273 * Returns: (transfer full): A reference to the buffer in @pad or
1274 * NULL if no buffer was queued. You should unref the buffer after
1278 gst_aggregator_pad_get_buffer (GstAggregatorPad * pad)
1280 GstBuffer *buffer = NULL;
1282 PAD_LOCK_EVENT (pad);
1284 buffer = gst_buffer_ref (pad->buffer);
1285 PAD_UNLOCK_EVENT (pad);
1291 * gst_aggregator_merge_tags:
1292 * @self: a #GstAggregator
1293 * @tags: a #GstTagList to merge
1294 * @mode: the #GstTagMergeMode to use
1296 * Adds tags to so-called pending tags, which will be processed
1297 * before pushing out data downstream.
1299 * Note that this is provided for convenience, and the subclass is
1300 * not required to use this and can still do tag handling on its own.
1305 gst_aggregator_merge_tags (GstAggregator * self,
1306 const GstTagList * tags, GstTagMergeMode mode)
1310 g_return_if_fail (GST_IS_AGGREGATOR (self));
1311 g_return_if_fail (tags == NULL || GST_IS_TAG_LIST (tags));
1313 /* FIXME Check if we can use OBJECT lock here! */
1314 GST_OBJECT_LOCK (self);
1316 GST_DEBUG_OBJECT (self, "merging tags %" GST_PTR_FORMAT, tags);
1317 otags = self->priv->tags;
1318 self->priv->tags = gst_tag_list_merge (self->priv->tags, tags, mode);
1320 gst_tag_list_unref (otags);
1321 self->priv->tags_changed = TRUE;
1322 GST_OBJECT_UNLOCK (self);