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., 51 Franklin St, Fifth Floor,
17 * Boston, MA 02110-1301, 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);
359 /* Since this stream is not time-based, we mark it so that
360 * other streams don't wait forever on it */
363 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
366 case GST_EVENT_FLUSH_START:{
369 GST_STREAM_SYNCHRONIZER_LOCK (self);
370 stream = gst_pad_get_element_private (pad);
372 GST_DEBUG_OBJECT (pad, "Flushing streams");
373 g_cond_broadcast (&stream->stream_finish_cond);
375 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
378 case GST_EVENT_FLUSH_STOP:{
381 GST_STREAM_SYNCHRONIZER_LOCK (self);
382 stream = gst_pad_get_element_private (pad);
384 GST_DEBUG_OBJECT (pad, "Resetting segment for stream %d",
385 stream->stream_number);
386 gst_segment_init (&stream->segment, GST_FORMAT_UNDEFINED);
388 stream->is_eos = FALSE;
389 stream->wait = FALSE;
390 stream->new_stream = FALSE;
391 stream->drop_discont = FALSE;
392 stream->seen_data = FALSE;
393 g_cond_broadcast (&stream->stream_finish_cond);
395 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
401 gboolean all_eos = TRUE;
405 GstClockTime timestamp;
407 GST_STREAM_SYNCHRONIZER_LOCK (self);
408 stream = gst_pad_get_element_private (pad);
410 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
411 GST_WARNING_OBJECT (pad, "EOS for unknown stream");
415 GST_DEBUG_OBJECT (pad, "Have EOS for stream %d", stream->stream_number);
416 stream->is_eos = TRUE;
418 seen_data = stream->seen_data;
419 srcpad = gst_object_ref (stream->srcpad);
421 if (seen_data && stream->segment.position != -1)
422 timestamp = stream->segment.position;
423 else if (stream->segment.rate < 0.0 || stream->segment.stop == -1)
424 timestamp = stream->segment.start;
426 timestamp = stream->segment.stop;
428 for (l = self->streams; l; l = l->next) {
429 GstStream *ostream = l->data;
431 all_eos = all_eos && ostream->is_eos;
437 GST_DEBUG_OBJECT (self, "All streams are EOS -- forwarding");
438 for (l = self->streams; l; l = l->next) {
439 GstStream *ostream = l->data;
440 /* local snapshot of current pads */
441 gst_object_ref (ostream->srcpad);
442 pads = g_slist_prepend (pads, ostream->srcpad);
445 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
446 /* drop lock when sending eos, which may block in e.g. preroll */
455 GST_DEBUG_OBJECT (pad, "Pushing EOS");
456 ret = ret && gst_pad_push_event (pad, gst_event_new_eos ());
457 gst_object_unref (pad);
458 epad = g_slist_next (epad);
462 /* if EOS, but no data has passed, then send something to replace EOS
463 * for preroll purposes */
467 gap_event = gst_event_new_gap (timestamp, GST_CLOCK_TIME_NONE);
468 ret = gst_pad_push_event (srcpad, gap_event);
472 /* FIXME: Also send a GAP event to let audio sinks start their
473 * clock in case they did not have enough data yet */
474 gap_event = gst_event_new_gap (timestamp, GST_CLOCK_TIME_NONE);
475 ret = gst_pad_push_event (srcpad, gap_event);
478 gst_object_unref (srcpad);
479 gst_event_unref (event);
486 opad = gst_stream_get_other_pad_from_pad (self, pad);
488 ret = gst_pad_push_event (opad, event);
489 gst_object_unref (opad);
498 gst_stream_synchronizer_sink_chain (GstPad * pad, GstObject * parent,
501 GstStreamSynchronizer *self = GST_STREAM_SYNCHRONIZER (parent);
503 GstFlowReturn ret = GST_FLOW_ERROR;
505 GstClockTime timestamp = GST_CLOCK_TIME_NONE;
506 GstClockTime timestamp_end = GST_CLOCK_TIME_NONE;
508 GST_LOG_OBJECT (pad, "Handling buffer %p: size=%" G_GSIZE_FORMAT
509 ", timestamp=%" GST_TIME_FORMAT " duration=%" GST_TIME_FORMAT
510 " offset=%" G_GUINT64_FORMAT " offset_end=%" G_GUINT64_FORMAT,
511 buffer, gst_buffer_get_size (buffer),
512 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
513 GST_TIME_ARGS (GST_BUFFER_DURATION (buffer)),
514 GST_BUFFER_OFFSET (buffer), GST_BUFFER_OFFSET_END (buffer));
516 timestamp = GST_BUFFER_TIMESTAMP (buffer);
517 if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)
518 && GST_BUFFER_DURATION_IS_VALID (buffer))
519 timestamp_end = timestamp + GST_BUFFER_DURATION (buffer);
521 GST_STREAM_SYNCHRONIZER_LOCK (self);
522 stream = gst_pad_get_element_private (pad);
525 stream->seen_data = TRUE;
526 if (stream->drop_discont) {
527 if (GST_BUFFER_IS_DISCONT (buffer)) {
528 GST_DEBUG_OBJECT (pad, "removing DISCONT from buffer %p", buffer);
529 buffer = gst_buffer_make_writable (buffer);
530 GST_BUFFER_FLAG_UNSET (buffer, GST_BUFFER_FLAG_DISCONT);
532 stream->drop_discont = FALSE;
535 if (stream->segment.format == GST_FORMAT_TIME
536 && GST_CLOCK_TIME_IS_VALID (timestamp)) {
538 "Updating position from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
539 GST_TIME_ARGS (stream->segment.position), GST_TIME_ARGS (timestamp));
540 stream->segment.position = timestamp;
543 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
545 opad = gst_stream_get_other_pad_from_pad (self, pad);
547 ret = gst_pad_push (opad, buffer);
548 gst_object_unref (opad);
551 GST_LOG_OBJECT (pad, "Push returned: %s", gst_flow_get_name (ret));
552 if (ret == GST_FLOW_OK) {
555 GST_STREAM_SYNCHRONIZER_LOCK (self);
556 stream = gst_pad_get_element_private (pad);
557 if (stream && stream->segment.format == GST_FORMAT_TIME
558 && GST_CLOCK_TIME_IS_VALID (timestamp_end)) {
560 "Updating position from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
561 GST_TIME_ARGS (stream->segment.position),
562 GST_TIME_ARGS (timestamp_end));
563 stream->segment.position = timestamp_end;
566 /* Advance EOS streams if necessary. For non-EOS
567 * streams the demuxers should already do this! */
568 for (l = self->streams; l; l = l->next) {
569 GstStream *ostream = l->data;
572 if (!ostream->is_eos || ostream->segment.format != GST_FORMAT_TIME)
575 if (ostream->segment.position != -1)
576 position = ostream->segment.position;
578 position = ostream->segment.start;
580 /* Is there a 1 second lag? */
581 if (position != -1 && position + GST_SECOND < timestamp_end) {
584 new_start = timestamp_end - GST_SECOND;
586 GST_DEBUG_OBJECT (ostream->sinkpad,
587 "Advancing stream %u from %" GST_TIME_FORMAT " to %"
588 GST_TIME_FORMAT, ostream->stream_number, GST_TIME_ARGS (position),
589 GST_TIME_ARGS (new_start));
591 ostream->segment.position = new_start;
593 gst_pad_push_event (ostream->srcpad,
594 gst_event_new_gap (position, new_start - position));
597 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
603 /* GstElement vfuncs */
605 gst_stream_synchronizer_request_new_pad (GstElement * element,
606 GstPadTemplate * temp, const gchar * name, const GstCaps * caps)
608 GstStreamSynchronizer *self = GST_STREAM_SYNCHRONIZER (element);
612 GST_STREAM_SYNCHRONIZER_LOCK (self);
613 GST_DEBUG_OBJECT (self, "Requesting new pad for stream %d",
614 self->current_stream_number);
616 stream = g_slice_new0 (GstStream);
617 stream->transform = self;
618 stream->stream_number = self->current_stream_number;
619 g_cond_init (&stream->stream_finish_cond);
620 stream->stream_start_seqnum = G_MAXUINT32;
621 stream->segment_seqnum = G_MAXUINT32;
623 tmp = g_strdup_printf ("sink_%u", self->current_stream_number);
624 stream->sinkpad = gst_pad_new_from_static_template (&sinktemplate, tmp);
626 gst_pad_set_element_private (stream->sinkpad, stream);
627 gst_pad_set_iterate_internal_links_function (stream->sinkpad,
628 GST_DEBUG_FUNCPTR (gst_stream_synchronizer_iterate_internal_links));
629 gst_pad_set_query_function (stream->sinkpad,
630 GST_DEBUG_FUNCPTR (gst_stream_synchronizer_query));
631 gst_pad_set_event_function (stream->sinkpad,
632 GST_DEBUG_FUNCPTR (gst_stream_synchronizer_sink_event));
633 gst_pad_set_chain_function (stream->sinkpad,
634 GST_DEBUG_FUNCPTR (gst_stream_synchronizer_sink_chain));
636 tmp = g_strdup_printf ("src_%u", self->current_stream_number);
637 stream->srcpad = gst_pad_new_from_static_template (&srctemplate, tmp);
639 gst_pad_set_element_private (stream->srcpad, stream);
640 gst_pad_set_iterate_internal_links_function (stream->srcpad,
641 GST_DEBUG_FUNCPTR (gst_stream_synchronizer_iterate_internal_links));
642 gst_pad_set_query_function (stream->srcpad,
643 GST_DEBUG_FUNCPTR (gst_stream_synchronizer_query));
644 gst_pad_set_event_function (stream->srcpad,
645 GST_DEBUG_FUNCPTR (gst_stream_synchronizer_src_event));
647 gst_segment_init (&stream->segment, GST_FORMAT_UNDEFINED);
649 self->streams = g_list_prepend (self->streams, stream);
650 self->current_stream_number++;
651 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
653 /* Add pads and activate unless we're going to NULL */
654 g_rec_mutex_lock (GST_STATE_GET_LOCK (self));
655 if (GST_STATE_TARGET (self) != GST_STATE_NULL) {
656 gst_pad_set_active (stream->srcpad, TRUE);
657 gst_pad_set_active (stream->sinkpad, TRUE);
659 gst_element_add_pad (GST_ELEMENT_CAST (self), stream->srcpad);
660 gst_element_add_pad (GST_ELEMENT_CAST (self), stream->sinkpad);
661 g_rec_mutex_unlock (GST_STATE_GET_LOCK (self));
663 return stream->sinkpad;
666 /* Must be called with lock! */
668 gst_stream_synchronizer_release_stream (GstStreamSynchronizer * self,
673 GST_DEBUG_OBJECT (self, "Releasing stream %d", stream->stream_number);
675 for (l = self->streams; l; l = l->next) {
676 if (l->data == stream) {
677 self->streams = g_list_delete_link (self->streams, l);
681 g_assert (l != NULL);
683 /* we can drop the lock, since stream exists now only local.
684 * Moreover, we should drop, to prevent deadlock with STREAM_LOCK
685 * (due to reverse lock order) when deactivating pads */
686 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
688 gst_pad_set_element_private (stream->srcpad, NULL);
689 gst_pad_set_element_private (stream->sinkpad, NULL);
690 gst_pad_set_active (stream->srcpad, FALSE);
691 gst_element_remove_pad (GST_ELEMENT_CAST (self), stream->srcpad);
692 gst_pad_set_active (stream->sinkpad, FALSE);
693 gst_element_remove_pad (GST_ELEMENT_CAST (self), stream->sinkpad);
695 if (stream->segment.format == GST_FORMAT_TIME) {
696 gint64 stop_running_time;
697 gint64 position_running_time;
700 gst_segment_to_running_time (&stream->segment, GST_FORMAT_TIME,
701 stream->segment.stop);
702 position_running_time =
703 gst_segment_to_running_time (&stream->segment, GST_FORMAT_TIME,
704 stream->segment.position);
705 stop_running_time = MAX (stop_running_time, position_running_time);
707 if (stop_running_time > self->group_start_time) {
708 GST_DEBUG_OBJECT (stream->sinkpad,
709 "Updating global start running time from %" GST_TIME_FORMAT " to %"
710 GST_TIME_FORMAT, GST_TIME_ARGS (self->group_start_time),
711 GST_TIME_ARGS (stop_running_time));
713 self->group_start_time = stop_running_time;
717 g_cond_clear (&stream->stream_finish_cond);
718 g_slice_free (GstStream, stream);
720 /* NOTE: In theory we have to check here if all streams
721 * are EOS but the one that was removed wasn't and then
722 * send EOS downstream. But due to the way how playsink
723 * works this is not necessary and will only cause problems
724 * for gapless playback. playsink will only add/remove pads
725 * when it's reconfigured, which happens when the streams
729 /* lock for good measure, since the caller had it */
730 GST_STREAM_SYNCHRONIZER_LOCK (self);
734 gst_stream_synchronizer_release_pad (GstElement * element, GstPad * pad)
736 GstStreamSynchronizer *self = GST_STREAM_SYNCHRONIZER (element);
739 GST_STREAM_SYNCHRONIZER_LOCK (self);
740 stream = gst_pad_get_element_private (pad);
742 g_assert (stream->sinkpad == pad);
744 gst_stream_synchronizer_release_stream (self, stream);
746 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
749 static GstStateChangeReturn
750 gst_stream_synchronizer_change_state (GstElement * element,
751 GstStateChange transition)
753 GstStreamSynchronizer *self = GST_STREAM_SYNCHRONIZER (element);
754 GstStateChangeReturn ret;
756 switch (transition) {
757 case GST_STATE_CHANGE_NULL_TO_READY:
758 GST_DEBUG_OBJECT (self, "State change NULL->READY");
759 self->shutdown = FALSE;
761 case GST_STATE_CHANGE_READY_TO_PAUSED:
762 GST_DEBUG_OBJECT (self, "State change READY->PAUSED");
763 self->group_start_time = 0;
764 self->shutdown = FALSE;
766 case GST_STATE_CHANGE_PAUSED_TO_READY:{
769 GST_DEBUG_OBJECT (self, "State change READY->NULL");
771 GST_STREAM_SYNCHRONIZER_LOCK (self);
772 for (l = self->streams; l; l = l->next) {
773 GstStream *ostream = l->data;
774 g_cond_broadcast (&ostream->stream_finish_cond);
776 self->shutdown = TRUE;
777 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
783 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
784 GST_DEBUG_OBJECT (self, "Base class state changed returned: %d", ret);
785 if (G_UNLIKELY (ret != GST_STATE_CHANGE_SUCCESS))
788 switch (transition) {
789 case GST_STATE_CHANGE_PAUSED_TO_READY:{
792 GST_DEBUG_OBJECT (self, "State change PAUSED->READY");
793 self->group_start_time = 0;
795 GST_STREAM_SYNCHRONIZER_LOCK (self);
796 for (l = self->streams; l; l = l->next) {
797 GstStream *stream = l->data;
799 gst_segment_init (&stream->segment, GST_FORMAT_UNDEFINED);
800 stream->wait = FALSE;
801 stream->new_stream = FALSE;
802 stream->drop_discont = FALSE;
803 stream->is_eos = FALSE;
805 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
808 case GST_STATE_CHANGE_READY_TO_NULL:{
809 GST_DEBUG_OBJECT (self, "State change READY->NULL");
811 GST_STREAM_SYNCHRONIZER_LOCK (self);
812 self->current_stream_number = 0;
813 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
825 gst_stream_synchronizer_finalize (GObject * object)
827 GstStreamSynchronizer *self = GST_STREAM_SYNCHRONIZER (object);
829 g_mutex_clear (&self->lock);
831 G_OBJECT_CLASS (parent_class)->finalize (object);
834 /* GObject type initialization */
836 gst_stream_synchronizer_init (GstStreamSynchronizer * self)
838 g_mutex_init (&self->lock);
842 gst_stream_synchronizer_class_init (GstStreamSynchronizerClass * klass)
844 GObjectClass *gobject_class = (GObjectClass *) klass;
845 GstElementClass *element_class = (GstElementClass *) klass;
847 gobject_class->finalize = gst_stream_synchronizer_finalize;
849 gst_element_class_add_pad_template (element_class,
850 gst_static_pad_template_get (&srctemplate));
851 gst_element_class_add_pad_template (element_class,
852 gst_static_pad_template_get (&sinktemplate));
854 gst_element_class_set_static_metadata (element_class,
855 "Stream Synchronizer", "Generic",
856 "Synchronizes a group of streams to have equal durations and starting points",
857 "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
859 element_class->change_state =
860 GST_DEBUG_FUNCPTR (gst_stream_synchronizer_change_state);
861 element_class->request_new_pad =
862 GST_DEBUG_FUNCPTR (gst_stream_synchronizer_request_new_pad);
863 element_class->release_pad =
864 GST_DEBUG_FUNCPTR (gst_stream_synchronizer_release_pad);
868 gst_stream_synchronizer_plugin_init (GstPlugin * plugin)
870 GST_DEBUG_CATEGORY_INIT (stream_synchronizer_debug,
871 "streamsynchronizer", 0, "Stream Synchronizer");
873 return gst_element_register (plugin, "streamsynchronizer", GST_RANK_NONE,
874 GST_TYPE_STREAM_SYNCHRONIZER);