2 * Copyright (C) 2010 Sebastian Dröge <sebastian.droege@collabora.co.uk>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
24 #include "gststreamsynchronizer.h"
26 GST_DEBUG_CATEGORY_STATIC (stream_synchronizer_debug);
27 #define GST_CAT_DEFAULT stream_synchronizer_debug
29 #define GST_STREAM_SYNCHRONIZER_LOCK(obj) G_STMT_START { \
30 GST_LOG_OBJECT (obj, \
31 "locking from thread %p", \
33 g_mutex_lock (&GST_STREAM_SYNCHRONIZER_CAST(obj)->lock); \
34 GST_LOG_OBJECT (obj, \
35 "locked from thread %p", \
39 #define GST_STREAM_SYNCHRONIZER_UNLOCK(obj) G_STMT_START { \
40 GST_LOG_OBJECT (obj, \
41 "unlocking from thread %p", \
43 g_mutex_unlock (&GST_STREAM_SYNCHRONIZER_CAST(obj)->lock); \
46 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src_%u",
50 static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink_%u",
55 #define gst_stream_synchronizer_parent_class parent_class
56 G_DEFINE_TYPE (GstStreamSynchronizer, gst_stream_synchronizer,
61 GstStreamSynchronizer *transform;
67 gboolean wait; /* TRUE if waiting/blocking */
69 gboolean drop_discont;
70 gboolean is_eos; /* TRUE if EOS was received */
73 GCond stream_finish_cond;
75 /* seqnum of the previously received STREAM_START
76 * default: G_MAXUINT32 */
77 guint32 stream_start_seqnum;
78 guint32 segment_seqnum;
81 /* Must be called with lock! */
82 static inline GstPad *
83 gst_stream_get_other_pad (GstStream * stream, GstPad * pad)
85 if (stream->sinkpad == pad)
86 return gst_object_ref (stream->srcpad);
87 if (stream->srcpad == pad)
88 return gst_object_ref (stream->sinkpad);
94 gst_stream_get_other_pad_from_pad (GstStreamSynchronizer * self, GstPad * pad)
99 GST_STREAM_SYNCHRONIZER_LOCK (self);
100 stream = gst_pad_get_element_private (pad);
104 opad = gst_stream_get_other_pad (stream, pad);
107 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
110 GST_WARNING_OBJECT (pad, "Trying to get other pad after releasing");
115 /* Generic pad functions */
117 gst_stream_synchronizer_iterate_internal_links (GstPad * pad,
120 GstIterator *it = NULL;
124 gst_stream_get_other_pad_from_pad (GST_STREAM_SYNCHRONIZER (parent), pad);
126 GValue value = { 0, };
128 g_value_init (&value, GST_TYPE_PAD);
129 g_value_set_object (&value, opad);
130 it = gst_iterator_new_single (GST_TYPE_PAD, &value);
131 g_value_unset (&value);
132 gst_object_unref (opad);
139 gst_stream_synchronizer_query (GstPad * pad, GstObject * parent,
143 gboolean ret = FALSE;
145 GST_LOG_OBJECT (pad, "Handling query %s", GST_QUERY_TYPE_NAME (query));
148 gst_stream_get_other_pad_from_pad (GST_STREAM_SYNCHRONIZER (parent), pad);
150 ret = gst_pad_peer_query (opad, query);
151 gst_object_unref (opad);
157 /* srcpad functions */
159 gst_stream_synchronizer_src_event (GstPad * pad, GstObject * parent,
162 GstStreamSynchronizer *self = GST_STREAM_SYNCHRONIZER (parent);
164 gboolean ret = FALSE;
166 GST_LOG_OBJECT (pad, "Handling event %s: %" GST_PTR_FORMAT,
167 GST_EVENT_TYPE_NAME (event), event);
169 switch (GST_EVENT_TYPE (event)) {
172 GstClockTimeDiff diff;
173 GstClockTime timestamp;
174 gint64 running_time_diff = -1;
177 gst_event_parse_qos (event, NULL, &proportion, &diff, ×tamp);
178 gst_event_unref (event);
180 GST_STREAM_SYNCHRONIZER_LOCK (self);
181 stream = gst_pad_get_element_private (pad);
183 running_time_diff = stream->segment.base;
184 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
186 if (running_time_diff == -1) {
187 GST_WARNING_OBJECT (pad, "QOS event before group start");
190 if (timestamp < running_time_diff) {
191 GST_DEBUG_OBJECT (pad, "QOS event from previous group");
196 "Adjusting QOS event: %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT " = %"
197 GST_TIME_FORMAT, GST_TIME_ARGS (timestamp),
198 GST_TIME_ARGS (running_time_diff),
199 GST_TIME_ARGS (timestamp - running_time_diff));
201 timestamp -= running_time_diff;
203 /* That case is invalid for QoS events */
204 if (diff < 0 && -diff > timestamp) {
205 GST_DEBUG_OBJECT (pad, "QOS event from previous group");
211 gst_event_new_qos (GST_QOS_TYPE_UNDERFLOW, proportion, diff,
219 opad = gst_stream_get_other_pad_from_pad (self, pad);
221 ret = gst_pad_push_event (opad, event);
222 gst_object_unref (opad);
229 /* sinkpad functions */
231 gst_stream_synchronizer_sink_event (GstPad * pad, GstObject * parent,
234 GstStreamSynchronizer *self = GST_STREAM_SYNCHRONIZER (parent);
236 gboolean ret = FALSE;
238 GST_LOG_OBJECT (pad, "Handling event %s: %" GST_PTR_FORMAT,
239 GST_EVENT_TYPE_NAME (event), event);
241 switch (GST_EVENT_TYPE (event)) {
242 case GST_EVENT_STREAM_START:
245 guint32 seqnum = gst_event_get_seqnum (event);
247 gboolean all_wait = TRUE;
249 GST_STREAM_SYNCHRONIZER_LOCK (self);
250 stream = gst_pad_get_element_private (pad);
251 if (stream && stream->stream_start_seqnum != seqnum) {
253 GST_DEBUG_OBJECT (pad, "Stream %d changed", stream->stream_number);
255 stream->is_eos = FALSE;
257 stream->new_stream = TRUE;
259 for (l = self->streams; l; l = l->next) {
260 GstStream *ostream = l->data;
262 all_wait = all_wait && ostream->wait;
269 GST_DEBUG_OBJECT (self, "All streams have changed -- unblocking");
271 for (l = self->streams; l; l = l->next) {
272 GstStream *ostream = l->data;
273 gint64 stop_running_time;
274 gint64 position_running_time;
276 ostream->wait = FALSE;
278 if (ostream->segment.format == GST_FORMAT_TIME) {
280 gst_segment_to_running_time (&ostream->segment,
281 GST_FORMAT_TIME, ostream->segment.stop);
282 position_running_time =
283 gst_segment_to_running_time (&ostream->segment,
284 GST_FORMAT_TIME, ostream->segment.position);
286 MAX (position, MAX (stop_running_time,
287 position_running_time));
290 position = MAX (0, position);
291 self->group_start_time = MAX (self->group_start_time, position);
293 GST_DEBUG_OBJECT (self, "New group start time: %" GST_TIME_FORMAT,
294 GST_TIME_ARGS (self->group_start_time));
296 for (l = self->streams; l; l = l->next) {
297 GstStream *ostream = l->data;
298 g_cond_broadcast (&ostream->stream_finish_cond);
302 GST_DEBUG_OBJECT (self, "No stream or STREAM_START from same source");
303 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
306 case GST_EVENT_SEGMENT:{
310 gst_event_copy_segment (event, &segment);
312 GST_STREAM_SYNCHRONIZER_LOCK (self);
313 stream = gst_pad_get_element_private (pad);
316 GST_DEBUG_OBJECT (pad, "Stream %d is waiting", stream->stream_number);
317 g_cond_wait (&stream->stream_finish_cond, &self->lock);
318 stream = gst_pad_get_element_private (pad);
320 stream->wait = FALSE;
324 if (self->shutdown) {
325 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
326 gst_event_unref (event);
330 if (stream && segment.format == GST_FORMAT_TIME) {
331 if (stream->new_stream) {
332 stream->new_stream = FALSE;
333 stream->drop_discont = TRUE;
334 segment.base = self->group_start_time;
337 GST_DEBUG_OBJECT (pad, "Segment was: %" GST_SEGMENT_FORMAT,
339 gst_segment_copy_into (&segment, &stream->segment);
340 GST_DEBUG_OBJECT (pad, "Segment now is: %" GST_SEGMENT_FORMAT,
342 stream->segment_seqnum = gst_event_get_seqnum (event);
344 GST_DEBUG_OBJECT (pad, "Stream start running time: %" GST_TIME_FORMAT,
345 GST_TIME_ARGS (stream->segment.base));
349 tmpev = gst_event_new_segment (&stream->segment);
350 gst_event_set_seqnum (tmpev, stream->segment_seqnum);
351 gst_event_unref (event);
356 GST_WARNING_OBJECT (pad, "Non-TIME segment: %s",
357 gst_format_get_name (segment.format));
358 gst_segment_init (&stream->segment, GST_FORMAT_UNDEFINED);
360 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
363 case GST_EVENT_FLUSH_START:{
366 GST_STREAM_SYNCHRONIZER_LOCK (self);
367 stream = gst_pad_get_element_private (pad);
369 GST_DEBUG_OBJECT (pad, "Flushing streams");
370 g_cond_broadcast (&stream->stream_finish_cond);
372 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
375 case GST_EVENT_FLUSH_STOP:{
378 GST_STREAM_SYNCHRONIZER_LOCK (self);
379 stream = gst_pad_get_element_private (pad);
381 GST_DEBUG_OBJECT (pad, "Resetting segment for stream %d",
382 stream->stream_number);
383 gst_segment_init (&stream->segment, GST_FORMAT_UNDEFINED);
385 stream->is_eos = FALSE;
386 stream->wait = FALSE;
387 stream->new_stream = FALSE;
388 stream->drop_discont = FALSE;
389 stream->seen_data = FALSE;
390 g_cond_broadcast (&stream->stream_finish_cond);
392 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
398 gboolean all_eos = TRUE;
403 GST_STREAM_SYNCHRONIZER_LOCK (self);
404 stream = gst_pad_get_element_private (pad);
406 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
407 GST_WARNING_OBJECT (pad, "EOS for unknown stream");
411 GST_DEBUG_OBJECT (pad, "Have EOS for stream %d", stream->stream_number);
412 stream->is_eos = TRUE;
414 seen_data = stream->seen_data;
415 srcpad = gst_object_ref (stream->srcpad);
417 for (l = self->streams; l; l = l->next) {
418 GstStream *ostream = l->data;
420 all_eos = all_eos && ostream->is_eos;
426 GST_DEBUG_OBJECT (self, "All streams are EOS -- forwarding");
427 for (l = self->streams; l; l = l->next) {
428 GstStream *ostream = l->data;
429 /* local snapshot of current pads */
430 gst_object_ref (ostream->srcpad);
431 pads = g_slist_prepend (pads, ostream->srcpad);
434 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
435 /* drop lock when sending eos, which may block in e.g. preroll */
444 GST_DEBUG_OBJECT (pad, "Pushing EOS");
445 ret = ret && gst_pad_push_event (pad, gst_event_new_eos ());
446 gst_object_unref (pad);
447 epad = g_slist_next (epad);
451 /* if EOS, but no data has passed, then send something to replace EOS
452 * for preroll purposes */
456 event = gst_event_new_gap (0, 0);
457 gst_pad_push_event (srcpad, event);
460 gst_object_unref (srcpad);
461 gst_event_unref (event);
469 opad = gst_stream_get_other_pad_from_pad (self, pad);
471 ret = gst_pad_push_event (opad, event);
472 gst_object_unref (opad);
481 gst_stream_synchronizer_sink_chain (GstPad * pad, GstObject * parent,
484 GstStreamSynchronizer *self = GST_STREAM_SYNCHRONIZER (parent);
486 GstFlowReturn ret = GST_FLOW_ERROR;
488 GstClockTime timestamp = GST_CLOCK_TIME_NONE;
489 GstClockTime timestamp_end = GST_CLOCK_TIME_NONE;
491 GST_LOG_OBJECT (pad, "Handling buffer %p: size=%" G_GSIZE_FORMAT
492 ", timestamp=%" GST_TIME_FORMAT " duration=%" GST_TIME_FORMAT
493 " offset=%" G_GUINT64_FORMAT " offset_end=%" G_GUINT64_FORMAT,
494 buffer, gst_buffer_get_size (buffer),
495 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
496 GST_TIME_ARGS (GST_BUFFER_DURATION (buffer)),
497 GST_BUFFER_OFFSET (buffer), GST_BUFFER_OFFSET_END (buffer));
499 timestamp = GST_BUFFER_TIMESTAMP (buffer);
500 if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)
501 && GST_BUFFER_DURATION_IS_VALID (buffer))
502 timestamp_end = timestamp + GST_BUFFER_DURATION (buffer);
504 GST_STREAM_SYNCHRONIZER_LOCK (self);
505 stream = gst_pad_get_element_private (pad);
508 stream->seen_data = TRUE;
509 if (stream->drop_discont) {
510 if (GST_BUFFER_IS_DISCONT (buffer)) {
511 GST_DEBUG_OBJECT (pad, "removing DISCONT from buffer %p", buffer);
512 buffer = gst_buffer_make_writable (buffer);
513 GST_BUFFER_FLAG_UNSET (buffer, GST_BUFFER_FLAG_DISCONT);
515 stream->drop_discont = FALSE;
518 if (stream->segment.format == GST_FORMAT_TIME
519 && GST_CLOCK_TIME_IS_VALID (timestamp)) {
521 "Updating position from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
522 GST_TIME_ARGS (stream->segment.position), GST_TIME_ARGS (timestamp));
523 stream->segment.position = timestamp;
526 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
528 opad = gst_stream_get_other_pad_from_pad (self, pad);
530 ret = gst_pad_push (opad, buffer);
531 gst_object_unref (opad);
534 GST_LOG_OBJECT (pad, "Push returned: %s", gst_flow_get_name (ret));
535 if (ret == GST_FLOW_OK) {
538 GST_STREAM_SYNCHRONIZER_LOCK (self);
539 stream = gst_pad_get_element_private (pad);
540 if (stream && stream->segment.format == GST_FORMAT_TIME
541 && GST_CLOCK_TIME_IS_VALID (timestamp_end)) {
543 "Updating position from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
544 GST_TIME_ARGS (stream->segment.position),
545 GST_TIME_ARGS (timestamp_end));
546 stream->segment.position = timestamp_end;
549 /* Advance EOS streams if necessary. For non-EOS
550 * streams the demuxers should already do this! */
551 for (l = self->streams; l; l = l->next) {
552 GstStream *ostream = l->data;
555 if (!ostream->is_eos || ostream->segment.format != GST_FORMAT_TIME)
558 if (ostream->segment.position != -1)
559 position = ostream->segment.position;
561 position = ostream->segment.start;
563 /* Is there a 1 second lag? */
564 if (position != -1 && position + GST_SECOND < timestamp_end) {
565 gint64 new_start, new_stop;
567 new_start = timestamp_end - GST_SECOND;
568 if (ostream->segment.stop == -1)
571 new_stop = MAX (new_start, ostream->segment.stop);
573 GST_DEBUG_OBJECT (ostream->sinkpad,
574 "Advancing stream %u from %" GST_TIME_FORMAT " to %"
575 GST_TIME_FORMAT, ostream->stream_number, GST_TIME_ARGS (position),
576 GST_TIME_ARGS (new_start));
578 ostream->segment.start = new_start;
579 ostream->segment.stop = new_stop;
580 ostream->segment.time = new_start;
581 ostream->segment.position = new_start;
583 gst_pad_push_event (ostream->srcpad,
584 gst_event_new_segment (&ostream->segment));
587 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
593 /* GstElement vfuncs */
595 gst_stream_synchronizer_request_new_pad (GstElement * element,
596 GstPadTemplate * temp, const gchar * name, const GstCaps * caps)
598 GstStreamSynchronizer *self = GST_STREAM_SYNCHRONIZER (element);
602 GST_STREAM_SYNCHRONIZER_LOCK (self);
603 GST_DEBUG_OBJECT (self, "Requesting new pad for stream %d",
604 self->current_stream_number);
606 stream = g_slice_new0 (GstStream);
607 stream->transform = self;
608 stream->stream_number = self->current_stream_number;
609 g_cond_init (&stream->stream_finish_cond);
610 stream->stream_start_seqnum = G_MAXUINT32;
611 stream->segment_seqnum = G_MAXUINT32;
613 tmp = g_strdup_printf ("sink_%u", self->current_stream_number);
614 stream->sinkpad = gst_pad_new_from_static_template (&sinktemplate, tmp);
616 gst_pad_set_element_private (stream->sinkpad, stream);
617 gst_pad_set_iterate_internal_links_function (stream->sinkpad,
618 GST_DEBUG_FUNCPTR (gst_stream_synchronizer_iterate_internal_links));
619 gst_pad_set_query_function (stream->sinkpad,
620 GST_DEBUG_FUNCPTR (gst_stream_synchronizer_query));
621 gst_pad_set_event_function (stream->sinkpad,
622 GST_DEBUG_FUNCPTR (gst_stream_synchronizer_sink_event));
623 gst_pad_set_chain_function (stream->sinkpad,
624 GST_DEBUG_FUNCPTR (gst_stream_synchronizer_sink_chain));
626 tmp = g_strdup_printf ("src_%u", self->current_stream_number);
627 stream->srcpad = gst_pad_new_from_static_template (&srctemplate, tmp);
629 gst_pad_set_element_private (stream->srcpad, stream);
630 gst_pad_set_iterate_internal_links_function (stream->srcpad,
631 GST_DEBUG_FUNCPTR (gst_stream_synchronizer_iterate_internal_links));
632 gst_pad_set_query_function (stream->srcpad,
633 GST_DEBUG_FUNCPTR (gst_stream_synchronizer_query));
634 gst_pad_set_event_function (stream->srcpad,
635 GST_DEBUG_FUNCPTR (gst_stream_synchronizer_src_event));
637 gst_segment_init (&stream->segment, GST_FORMAT_UNDEFINED);
639 self->streams = g_list_prepend (self->streams, stream);
640 self->current_stream_number++;
641 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
643 /* Add pads and activate unless we're going to NULL */
644 g_rec_mutex_lock (GST_STATE_GET_LOCK (self));
645 if (GST_STATE_TARGET (self) != GST_STATE_NULL) {
646 gst_pad_set_active (stream->srcpad, TRUE);
647 gst_pad_set_active (stream->sinkpad, TRUE);
649 gst_element_add_pad (GST_ELEMENT_CAST (self), stream->srcpad);
650 gst_element_add_pad (GST_ELEMENT_CAST (self), stream->sinkpad);
651 g_rec_mutex_unlock (GST_STATE_GET_LOCK (self));
653 return stream->sinkpad;
656 /* Must be called with lock! */
658 gst_stream_synchronizer_release_stream (GstStreamSynchronizer * self,
663 GST_DEBUG_OBJECT (self, "Releasing stream %d", stream->stream_number);
665 for (l = self->streams; l; l = l->next) {
666 if (l->data == stream) {
667 self->streams = g_list_delete_link (self->streams, l);
671 g_assert (l != NULL);
673 /* we can drop the lock, since stream exists now only local.
674 * Moreover, we should drop, to prevent deadlock with STREAM_LOCK
675 * (due to reverse lock order) when deactivating pads */
676 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
678 gst_pad_set_element_private (stream->srcpad, NULL);
679 gst_pad_set_element_private (stream->sinkpad, NULL);
680 gst_pad_set_active (stream->srcpad, FALSE);
681 gst_element_remove_pad (GST_ELEMENT_CAST (self), stream->srcpad);
682 gst_pad_set_active (stream->sinkpad, FALSE);
683 gst_element_remove_pad (GST_ELEMENT_CAST (self), stream->sinkpad);
685 if (stream->segment.format == GST_FORMAT_TIME) {
686 gint64 stop_running_time;
687 gint64 position_running_time;
690 gst_segment_to_running_time (&stream->segment, GST_FORMAT_TIME,
691 stream->segment.stop);
692 position_running_time =
693 gst_segment_to_running_time (&stream->segment, GST_FORMAT_TIME,
694 stream->segment.position);
695 stop_running_time = MAX (stop_running_time, position_running_time);
697 if (stop_running_time > self->group_start_time) {
698 GST_DEBUG_OBJECT (stream->sinkpad,
699 "Updating global start running time from %" GST_TIME_FORMAT " to %"
700 GST_TIME_FORMAT, GST_TIME_ARGS (self->group_start_time),
701 GST_TIME_ARGS (stop_running_time));
703 self->group_start_time = stop_running_time;
707 g_cond_clear (&stream->stream_finish_cond);
708 g_slice_free (GstStream, stream);
710 /* NOTE: In theory we have to check here if all streams
711 * are EOS but the one that was removed wasn't and then
712 * send EOS downstream. But due to the way how playsink
713 * works this is not necessary and will only cause problems
714 * for gapless playback. playsink will only add/remove pads
715 * when it's reconfigured, which happens when the streams
719 /* lock for good measure, since the caller had it */
720 GST_STREAM_SYNCHRONIZER_LOCK (self);
724 gst_stream_synchronizer_release_pad (GstElement * element, GstPad * pad)
726 GstStreamSynchronizer *self = GST_STREAM_SYNCHRONIZER (element);
729 GST_STREAM_SYNCHRONIZER_LOCK (self);
730 stream = gst_pad_get_element_private (pad);
732 g_assert (stream->sinkpad == pad);
734 gst_stream_synchronizer_release_stream (self, stream);
736 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
739 static GstStateChangeReturn
740 gst_stream_synchronizer_change_state (GstElement * element,
741 GstStateChange transition)
743 GstStreamSynchronizer *self = GST_STREAM_SYNCHRONIZER (element);
744 GstStateChangeReturn ret;
746 switch (transition) {
747 case GST_STATE_CHANGE_NULL_TO_READY:
748 GST_DEBUG_OBJECT (self, "State change NULL->READY");
749 self->shutdown = FALSE;
751 case GST_STATE_CHANGE_READY_TO_PAUSED:
752 GST_DEBUG_OBJECT (self, "State change READY->PAUSED");
753 self->group_start_time = 0;
754 self->shutdown = FALSE;
756 case GST_STATE_CHANGE_PAUSED_TO_READY:{
759 GST_DEBUG_OBJECT (self, "State change READY->NULL");
761 GST_STREAM_SYNCHRONIZER_LOCK (self);
762 for (l = self->streams; l; l = l->next) {
763 GstStream *ostream = l->data;
764 g_cond_broadcast (&ostream->stream_finish_cond);
766 self->shutdown = TRUE;
767 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
773 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
774 GST_DEBUG_OBJECT (self, "Base class state changed returned: %d", ret);
775 if (G_UNLIKELY (ret != GST_STATE_CHANGE_SUCCESS))
778 switch (transition) {
779 case GST_STATE_CHANGE_PAUSED_TO_READY:{
782 GST_DEBUG_OBJECT (self, "State change PAUSED->READY");
783 self->group_start_time = 0;
785 GST_STREAM_SYNCHRONIZER_LOCK (self);
786 for (l = self->streams; l; l = l->next) {
787 GstStream *stream = l->data;
789 gst_segment_init (&stream->segment, GST_FORMAT_UNDEFINED);
790 stream->wait = FALSE;
791 stream->new_stream = FALSE;
792 stream->drop_discont = FALSE;
793 stream->is_eos = FALSE;
795 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
798 case GST_STATE_CHANGE_READY_TO_NULL:{
799 GST_DEBUG_OBJECT (self, "State change READY->NULL");
801 GST_STREAM_SYNCHRONIZER_LOCK (self);
802 self->current_stream_number = 0;
803 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
815 gst_stream_synchronizer_finalize (GObject * object)
817 GstStreamSynchronizer *self = GST_STREAM_SYNCHRONIZER (object);
819 g_mutex_clear (&self->lock);
821 G_OBJECT_CLASS (parent_class)->finalize (object);
824 /* GObject type initialization */
826 gst_stream_synchronizer_init (GstStreamSynchronizer * self)
828 g_mutex_init (&self->lock);
832 gst_stream_synchronizer_class_init (GstStreamSynchronizerClass * klass)
834 GObjectClass *gobject_class = (GObjectClass *) klass;
835 GstElementClass *element_class = (GstElementClass *) klass;
837 GST_DEBUG_CATEGORY_INIT (stream_synchronizer_debug,
838 "streamsynchronizer", 0, "Stream Synchronizer");
840 gobject_class->finalize = gst_stream_synchronizer_finalize;
842 gst_element_class_add_pad_template (element_class,
843 gst_static_pad_template_get (&srctemplate));
844 gst_element_class_add_pad_template (element_class,
845 gst_static_pad_template_get (&sinktemplate));
847 gst_element_class_set_static_metadata (element_class,
848 "Stream Synchronizer", "Generic",
849 "Synchronizes a group of streams to have equal durations and starting points",
850 "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
852 element_class->change_state =
853 GST_DEBUG_FUNCPTR (gst_stream_synchronizer_change_state);
854 element_class->request_new_pad =
855 GST_DEBUG_FUNCPTR (gst_stream_synchronizer_request_new_pad);
856 element_class->release_pad =
857 GST_DEBUG_FUNCPTR (gst_stream_synchronizer_release_pad);