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"
25 #include "gst/glib-compat-private.h"
27 GST_DEBUG_CATEGORY_STATIC (stream_synchronizer_debug);
28 #define GST_CAT_DEFAULT stream_synchronizer_debug
30 #define GST_STREAM_SYNCHRONIZER_LOCK(obj) G_STMT_START { \
31 GST_LOG_OBJECT (obj, \
32 "locking from thread %p", \
34 g_mutex_lock (GST_STREAM_SYNCHRONIZER_CAST(obj)->lock); \
35 GST_LOG_OBJECT (obj, \
36 "locked from thread %p", \
40 #define GST_STREAM_SYNCHRONIZER_UNLOCK(obj) G_STMT_START { \
41 GST_LOG_OBJECT (obj, \
42 "unlocking from thread %p", \
44 g_mutex_unlock (GST_STREAM_SYNCHRONIZER_CAST(obj)->lock); \
47 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src_%u",
51 static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink_%u",
56 static const gboolean passthrough = TRUE;
58 #define gst_stream_synchronizer_parent_class parent_class
59 G_DEFINE_TYPE (GstStreamSynchronizer, gst_stream_synchronizer,
64 GstStreamSynchronizer *transform;
72 gboolean drop_discont;
76 gint64 running_time_diff;
79 /* Must be called with lock! */
81 gst_stream_get_other_pad (GstStream * stream, GstPad * pad)
83 if (stream->sinkpad == pad)
84 return gst_object_ref (stream->srcpad);
85 else if (stream->srcpad == pad)
86 return gst_object_ref (stream->sinkpad);
92 gst_stream_get_other_pad_from_pad (GstPad * pad)
94 GstObject *parent = gst_pad_get_parent (pad);
95 GstStreamSynchronizer *self;
99 /* released pad does not have parent anymore */
100 if (!G_LIKELY (parent))
103 self = GST_STREAM_SYNCHRONIZER (parent);
104 GST_STREAM_SYNCHRONIZER_LOCK (self);
105 stream = gst_pad_get_element_private (pad);
109 opad = gst_stream_get_other_pad (stream, pad);
112 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
113 gst_object_unref (self);
117 GST_WARNING_OBJECT (pad, "Trying to get other pad after releasing");
122 /* Generic pad functions */
124 gst_stream_synchronizer_iterate_internal_links (GstPad * pad,
127 GstIterator *it = NULL;
130 opad = gst_stream_get_other_pad_from_pad (pad);
132 GValue value = { 0, };
134 g_value_init (&value, GST_TYPE_PAD);
135 g_value_set_object (&value, opad);
136 it = gst_iterator_new_single (GST_TYPE_PAD, &value);
137 g_value_unset (&value);
138 gst_object_unref (opad);
145 gst_stream_synchronizer_query (GstPad * pad, GstObject * parent,
149 gboolean ret = FALSE;
151 GST_LOG_OBJECT (pad, "Handling query %s", GST_QUERY_TYPE_NAME (query));
153 opad = gst_stream_get_other_pad_from_pad (pad);
155 ret = gst_pad_peer_query (opad, query);
156 gst_object_unref (opad);
162 /* srcpad functions */
164 gst_stream_synchronizer_src_event (GstPad * pad, GstObject * parent,
167 GstStreamSynchronizer *self = GST_STREAM_SYNCHRONIZER (parent);
169 gboolean ret = FALSE;
172 goto skip_adjustments;
174 GST_LOG_OBJECT (pad, "Handling event %s: %" GST_PTR_FORMAT,
175 GST_EVENT_TYPE_NAME (event), event);
177 switch (GST_EVENT_TYPE (event)) {
180 GstClockTimeDiff diff;
181 GstClockTime timestamp;
182 gint64 running_time_diff;
185 gst_event_parse_qos (event, NULL, &proportion, &diff, ×tamp);
186 gst_event_unref (event);
188 GST_STREAM_SYNCHRONIZER_LOCK (self);
189 stream = gst_pad_get_element_private (pad);
191 running_time_diff = stream->running_time_diff;
193 running_time_diff = -1;
194 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
196 if (running_time_diff == -1) {
197 GST_WARNING_OBJECT (pad, "QOS event before group start");
199 } else if (timestamp < running_time_diff) {
200 GST_DEBUG_OBJECT (pad, "QOS event from previous group");
205 "Adjusting QOS event: %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT " = %"
206 GST_TIME_FORMAT, GST_TIME_ARGS (timestamp),
207 GST_TIME_ARGS (running_time_diff),
208 GST_TIME_ARGS (timestamp - running_time_diff));
210 timestamp -= running_time_diff;
212 /* That case is invalid for QoS events */
213 if (diff < 0 && -diff > timestamp) {
214 GST_DEBUG_OBJECT (pad, "QOS event from previous group");
220 gst_event_new_qos (GST_QOS_TYPE_UNDERFLOW, proportion, diff,
230 opad = gst_stream_get_other_pad_from_pad (pad);
232 ret = gst_pad_push_event (opad, event);
233 gst_object_unref (opad);
240 /* sinkpad functions */
242 gst_stream_synchronizer_sink_event (GstPad * pad, GstObject * parent,
245 GstStreamSynchronizer *self = GST_STREAM_SYNCHRONIZER (parent);
247 gboolean ret = FALSE;
250 goto skip_adjustments;
252 GST_LOG_OBJECT (pad, "Handling event %s: %" GST_PTR_FORMAT,
253 GST_EVENT_TYPE_NAME (event), event);
255 switch (GST_EVENT_TYPE (event)) {
256 case GST_EVENT_SINK_MESSAGE:{
259 gst_event_parse_sink_message (event, &message);
260 if (gst_message_has_name (message, "playbin-stream-changed")) {
263 GST_STREAM_SYNCHRONIZER_LOCK (self);
264 stream = gst_pad_get_element_private (pad);
267 gboolean all_wait = TRUE;
269 GST_DEBUG_OBJECT (pad, "Stream %d changed", stream->stream_number);
271 stream->is_eos = FALSE;
273 stream->new_stream = TRUE;
275 for (l = self->streams; l; l = l->next) {
276 GstStream *ostream = l->data;
278 all_wait = all_wait && ostream->wait;
285 GST_DEBUG_OBJECT (self, "All streams have changed -- unblocking");
287 for (l = self->streams; l; l = l->next) {
288 GstStream *ostream = l->data;
289 gint64 stop_running_time;
290 gint64 position_running_time;
292 ostream->wait = FALSE;
295 gst_segment_to_running_time (&ostream->segment,
296 GST_FORMAT_TIME, ostream->segment.stop);
297 position_running_time =
298 gst_segment_to_running_time (&ostream->segment,
299 GST_FORMAT_TIME, ostream->segment.position);
301 MAX (position, MAX (stop_running_time,
302 position_running_time));
304 position = MAX (0, position);
305 self->group_start_time = MAX (self->group_start_time, position);
307 GST_DEBUG_OBJECT (self, "New group start time: %" GST_TIME_FORMAT,
308 GST_TIME_ARGS (self->group_start_time));
310 g_cond_broadcast (self->stream_finish_cond);
313 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
315 gst_message_unref (message);
318 case GST_EVENT_SEGMENT:{
322 gst_event_copy_segment (event, &segment);
324 GST_STREAM_SYNCHRONIZER_LOCK (self);
325 stream = gst_pad_get_element_private (pad);
328 GST_DEBUG_OBJECT (pad, "Stream %d is waiting", stream->stream_number);
329 g_cond_wait (self->stream_finish_cond, self->lock);
330 stream = gst_pad_get_element_private (pad);
332 stream->wait = FALSE;
336 if (self->shutdown) {
337 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
338 gst_event_unref (event);
342 if (stream && segment.format == GST_FORMAT_TIME) {
343 if (stream->new_stream) {
344 gint64 position_running_time = 0;
345 gint64 stop_running_time = 0;
347 if (stream->segment.format == GST_FORMAT_TIME) {
348 position_running_time =
349 gst_segment_to_running_time (&stream->segment, GST_FORMAT_TIME,
350 stream->segment.position);
351 position_running_time = MAX (position_running_time, 0);
353 gst_segment_to_running_time (&stream->segment, GST_FORMAT_TIME,
354 stream->segment.stop);
355 stop_running_time = MAX (position_running_time, 0);
357 if (stop_running_time != position_running_time) {
358 GST_WARNING_OBJECT (pad,
359 "Gap between position and segment stop: %" GST_TIME_FORMAT
360 " != %" GST_TIME_FORMAT, GST_TIME_ARGS (stop_running_time),
361 GST_TIME_ARGS (position_running_time));
364 if (stop_running_time < position_running_time) {
365 GST_DEBUG_OBJECT (pad, "Updating stop position");
366 stream->segment.stop = stream->segment.position;
367 gst_pad_push_event (stream->srcpad,
368 gst_event_new_segment (&stream->segment));
370 stop_running_time = MAX (stop_running_time, position_running_time);
371 GST_DEBUG_OBJECT (pad,
372 "Stop running time of last group: %" GST_TIME_FORMAT,
373 GST_TIME_ARGS (stop_running_time));
375 stream->new_stream = FALSE;
376 stream->drop_discont = TRUE;
378 if (stop_running_time < self->group_start_time) {
379 gint64 diff = self->group_start_time - stop_running_time;
381 GST_DEBUG_OBJECT (pad,
382 "Advancing running time for other streams by: %"
383 GST_TIME_FORMAT, GST_TIME_ARGS (diff));
385 segment.base += diff;
389 GST_DEBUG_OBJECT (pad, "Segment was: %" GST_SEGMENT_FORMAT,
391 gst_segment_copy_into (&segment, &stream->segment);
392 GST_DEBUG_OBJECT (pad, "Segment now is: %" GST_SEGMENT_FORMAT,
395 GST_DEBUG_OBJECT (pad, "Stream start running time: %" GST_TIME_FORMAT,
396 GST_TIME_ARGS (stream->segment.base));
397 stream->running_time_diff = stream->segment.base;
399 GST_WARNING_OBJECT (pad, "Non-TIME segment: %s",
400 gst_format_get_name (segment.format));
401 gst_segment_init (&stream->segment, GST_FORMAT_UNDEFINED);
403 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
406 case GST_EVENT_FLUSH_STOP:{
409 GST_STREAM_SYNCHRONIZER_LOCK (self);
410 stream = gst_pad_get_element_private (pad);
412 GST_DEBUG_OBJECT (pad, "Resetting segment for stream %d",
413 stream->stream_number);
414 gst_segment_init (&stream->segment, GST_FORMAT_UNDEFINED);
416 stream->is_eos = FALSE;
417 stream->wait = FALSE;
418 stream->new_stream = FALSE;
419 stream->drop_discont = FALSE;
420 stream->seen_data = FALSE;
422 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
428 gboolean all_eos = TRUE;
433 GST_STREAM_SYNCHRONIZER_LOCK (self);
434 stream = gst_pad_get_element_private (pad);
436 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
437 GST_WARNING_OBJECT (pad, "EOS for unknown stream");
441 GST_DEBUG_OBJECT (pad, "Have EOS for stream %d", stream->stream_number);
442 stream->is_eos = TRUE;
444 seen_data = stream->seen_data;
445 srcpad = gst_object_ref (stream->srcpad);
447 for (l = self->streams; l; l = l->next) {
448 GstStream *ostream = l->data;
450 all_eos = all_eos && ostream->is_eos;
456 GST_DEBUG_OBJECT (self, "All streams are EOS -- forwarding");
457 for (l = self->streams; l; l = l->next) {
458 GstStream *ostream = l->data;
459 /* local snapshot of current pads */
460 gst_object_ref (ostream->srcpad);
461 pads = g_slist_prepend (pads, ostream->srcpad);
464 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
465 /* drop lock when sending eos, which may block in e.g. preroll */
474 GST_DEBUG_OBJECT (pad, "Pushing EOS");
475 ret = ret && gst_pad_push_event (pad, gst_event_new_eos ());
476 gst_object_unref (pad);
477 epad = g_slist_next (epad);
481 /* if EOS, but no data has passed, then send something to replace EOS
482 * for preroll purposes */
484 GstBuffer *buf = gst_buffer_new ();
486 gst_pad_push (srcpad, buf);
489 gst_object_unref (srcpad);
499 opad = gst_stream_get_other_pad_from_pad (pad);
501 ret = gst_pad_push_event (opad, event);
502 gst_object_unref (opad);
511 gst_stream_synchronizer_sink_chain (GstPad * pad, GstObject * parent,
514 GstStreamSynchronizer *self = GST_STREAM_SYNCHRONIZER (parent);
516 GstFlowReturn ret = GST_FLOW_ERROR;
518 GstClockTime timestamp = GST_CLOCK_TIME_NONE;
519 GstClockTime timestamp_end = GST_CLOCK_TIME_NONE;
522 opad = gst_stream_get_other_pad_from_pad (pad);
524 ret = gst_pad_push (opad, buffer);
525 gst_object_unref (opad);
530 GST_LOG_OBJECT (pad, "Handling buffer %p: size=%" G_GSIZE_FORMAT
531 ", timestamp=%" GST_TIME_FORMAT " duration=%" GST_TIME_FORMAT
532 " offset=%" G_GUINT64_FORMAT " offset_end=%" G_GUINT64_FORMAT,
533 buffer, gst_buffer_get_size (buffer),
534 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
535 GST_TIME_ARGS (GST_BUFFER_DURATION (buffer)),
536 GST_BUFFER_OFFSET (buffer), GST_BUFFER_OFFSET_END (buffer));
538 timestamp = GST_BUFFER_TIMESTAMP (buffer);
539 if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)
540 && GST_BUFFER_DURATION_IS_VALID (buffer))
541 timestamp_end = timestamp + GST_BUFFER_DURATION (buffer);
543 GST_STREAM_SYNCHRONIZER_LOCK (self);
544 stream = gst_pad_get_element_private (pad);
547 stream->seen_data = TRUE;
548 if (stream && stream->drop_discont) {
549 buffer = gst_buffer_make_writable (buffer);
550 GST_BUFFER_FLAG_UNSET (buffer, GST_BUFFER_FLAG_DISCONT);
551 stream->drop_discont = FALSE;
554 if (stream && stream->segment.format == GST_FORMAT_TIME
555 && GST_CLOCK_TIME_IS_VALID (timestamp)) {
557 "Updating last-stop from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
558 GST_TIME_ARGS (stream->segment.position), GST_TIME_ARGS (timestamp));
559 stream->segment.position = timestamp;
561 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
563 opad = gst_stream_get_other_pad_from_pad (pad);
565 ret = gst_pad_push (opad, buffer);
566 gst_object_unref (opad);
569 GST_LOG_OBJECT (pad, "Push returned: %s", gst_flow_get_name (ret));
570 if (ret == GST_FLOW_OK) {
573 GST_STREAM_SYNCHRONIZER_LOCK (self);
574 stream = gst_pad_get_element_private (pad);
575 if (stream && stream->segment.format == GST_FORMAT_TIME
576 && GST_CLOCK_TIME_IS_VALID (timestamp_end)) {
578 "Updating last-stop from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
579 GST_TIME_ARGS (stream->segment.position),
580 GST_TIME_ARGS (timestamp_end));
581 stream->segment.position = timestamp_end;
584 /* Advance EOS streams if necessary. For non-EOS
585 * streams the demuxers should already do this! */
586 for (l = self->streams; l; l = l->next) {
587 GstStream *ostream = l->data;
590 if (!ostream->is_eos || ostream->segment.format != GST_FORMAT_TIME)
593 if (ostream->segment.position != -1)
594 position = ostream->segment.position;
596 position = ostream->segment.start;
598 /* Is there a 1 second lag? */
599 if (position != -1 && position + GST_SECOND < timestamp_end) {
600 gint64 new_start, new_stop;
602 new_start = timestamp_end - GST_SECOND;
603 if (ostream->segment.stop == -1)
606 new_stop = MAX (new_start, ostream->segment.stop);
608 GST_DEBUG_OBJECT (ostream->sinkpad,
609 "Advancing stream %u from %" GST_TIME_FORMAT " to %"
610 GST_TIME_FORMAT, ostream->stream_number, GST_TIME_ARGS (position),
611 GST_TIME_ARGS (new_start));
613 ostream->segment.start = new_start;
614 ostream->segment.stop = new_stop;
615 ostream->segment.time = new_start;
616 ostream->segment.position = new_start;
618 gst_pad_push_event (ostream->srcpad,
619 gst_event_new_segment (&ostream->segment));
622 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
629 /* GstElement vfuncs */
631 gst_stream_synchronizer_request_new_pad (GstElement * element,
632 GstPadTemplate * temp, const gchar * name, const GstCaps * caps)
634 GstStreamSynchronizer *self = GST_STREAM_SYNCHRONIZER (element);
638 GST_STREAM_SYNCHRONIZER_LOCK (self);
639 GST_DEBUG_OBJECT (self, "Requesting new pad for stream %d",
640 self->current_stream_number);
642 stream = g_slice_new0 (GstStream);
643 stream->transform = self;
644 stream->stream_number = self->current_stream_number;
646 tmp = g_strdup_printf ("sink_%u", self->current_stream_number);
647 stream->sinkpad = gst_pad_new_from_static_template (&sinktemplate, tmp);
649 gst_pad_set_element_private (stream->sinkpad, stream);
650 gst_pad_set_iterate_internal_links_function (stream->sinkpad,
651 GST_DEBUG_FUNCPTR (gst_stream_synchronizer_iterate_internal_links));
652 gst_pad_set_query_function (stream->sinkpad,
653 GST_DEBUG_FUNCPTR (gst_stream_synchronizer_query));
654 gst_pad_set_event_function (stream->sinkpad,
655 GST_DEBUG_FUNCPTR (gst_stream_synchronizer_sink_event));
656 gst_pad_set_chain_function (stream->sinkpad,
657 GST_DEBUG_FUNCPTR (gst_stream_synchronizer_sink_chain));
659 tmp = g_strdup_printf ("src_%u", self->current_stream_number);
660 stream->srcpad = gst_pad_new_from_static_template (&srctemplate, tmp);
662 gst_pad_set_element_private (stream->srcpad, stream);
663 gst_pad_set_iterate_internal_links_function (stream->srcpad,
664 GST_DEBUG_FUNCPTR (gst_stream_synchronizer_iterate_internal_links));
665 gst_pad_set_query_function (stream->srcpad,
666 GST_DEBUG_FUNCPTR (gst_stream_synchronizer_query));
667 gst_pad_set_event_function (stream->srcpad,
668 GST_DEBUG_FUNCPTR (gst_stream_synchronizer_src_event));
670 gst_segment_init (&stream->segment, GST_FORMAT_UNDEFINED);
672 self->streams = g_list_prepend (self->streams, stream);
673 self->current_stream_number++;
674 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
676 /* Add pads and activate unless we're going to NULL */
677 g_rec_mutex_lock (GST_STATE_GET_LOCK (self));
678 if (GST_STATE_TARGET (self) != GST_STATE_NULL) {
679 gst_pad_set_active (stream->srcpad, TRUE);
680 gst_pad_set_active (stream->sinkpad, TRUE);
682 gst_element_add_pad (GST_ELEMENT_CAST (self), stream->srcpad);
683 gst_element_add_pad (GST_ELEMENT_CAST (self), stream->sinkpad);
684 g_rec_mutex_unlock (GST_STATE_GET_LOCK (self));
686 return stream->sinkpad;
689 /* Must be called with lock! */
691 gst_stream_synchronizer_release_stream (GstStreamSynchronizer * self,
696 GST_DEBUG_OBJECT (self, "Releasing stream %d", stream->stream_number);
698 for (l = self->streams; l; l = l->next) {
699 if (l->data == stream) {
700 self->streams = g_list_delete_link (self->streams, l);
704 g_assert (l != NULL);
706 /* we can drop the lock, since stream exists now only local.
707 * Moreover, we should drop, to prevent deadlock with STREAM_LOCK
708 * (due to reverse lock order) when deactivating pads */
709 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
711 gst_pad_set_element_private (stream->srcpad, NULL);
712 gst_pad_set_element_private (stream->sinkpad, NULL);
713 gst_pad_set_active (stream->srcpad, FALSE);
714 gst_element_remove_pad (GST_ELEMENT_CAST (self), stream->srcpad);
715 gst_pad_set_active (stream->sinkpad, FALSE);
716 gst_element_remove_pad (GST_ELEMENT_CAST (self), stream->sinkpad);
718 if (stream->segment.format == GST_FORMAT_TIME) {
719 gint64 stop_running_time;
720 gint64 position_running_time;
723 gst_segment_to_running_time (&stream->segment, GST_FORMAT_TIME,
724 stream->segment.stop);
725 position_running_time =
726 gst_segment_to_running_time (&stream->segment, GST_FORMAT_TIME,
727 stream->segment.position);
728 stop_running_time = MAX (stop_running_time, position_running_time);
730 GST_DEBUG_OBJECT (stream->sinkpad,
731 "Stop running time was: %" GST_TIME_FORMAT,
732 GST_TIME_ARGS (stop_running_time));
734 self->group_start_time = MAX (self->group_start_time, stop_running_time);
737 g_slice_free (GstStream, stream);
739 /* NOTE: In theory we have to check here if all streams
740 * are EOS but the one that was removed wasn't and then
741 * send EOS downstream. But due to the way how playsink
742 * works this is not necessary and will only cause problems
743 * for gapless playback. playsink will only add/remove pads
744 * when it's reconfigured, which happens when the streams
748 /* lock for good measure, since the caller had it */
749 GST_STREAM_SYNCHRONIZER_LOCK (self);
753 gst_stream_synchronizer_release_pad (GstElement * element, GstPad * pad)
755 GstStreamSynchronizer *self = GST_STREAM_SYNCHRONIZER (element);
758 GST_STREAM_SYNCHRONIZER_LOCK (self);
759 stream = gst_pad_get_element_private (pad);
761 g_assert (stream->sinkpad == pad);
763 gst_stream_synchronizer_release_stream (self, stream);
765 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
768 static GstStateChangeReturn
769 gst_stream_synchronizer_change_state (GstElement * element,
770 GstStateChange transition)
772 GstStreamSynchronizer *self = GST_STREAM_SYNCHRONIZER (element);
773 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
775 switch (transition) {
776 case GST_STATE_CHANGE_NULL_TO_READY:
777 GST_DEBUG_OBJECT (self, "State change NULL->READY");
778 self->shutdown = FALSE;
780 case GST_STATE_CHANGE_READY_TO_PAUSED:
781 GST_DEBUG_OBJECT (self, "State change READY->PAUSED");
782 self->group_start_time = 0;
783 self->shutdown = FALSE;
785 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
786 GST_DEBUG_OBJECT (self, "State change PAUSED->PLAYING");
788 case GST_STATE_CHANGE_PAUSED_TO_READY:
789 GST_DEBUG_OBJECT (self, "State change READY->NULL");
791 GST_STREAM_SYNCHRONIZER_LOCK (self);
792 g_cond_broadcast (self->stream_finish_cond);
793 self->shutdown = TRUE;
794 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
800 GstStateChangeReturn bret;
802 bret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
803 GST_DEBUG_OBJECT (self, "Base class state changed returned: %d", bret);
804 if (G_UNLIKELY (bret == GST_STATE_CHANGE_FAILURE))
808 switch (transition) {
809 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
810 GST_DEBUG_OBJECT (self, "State change PLAYING->PAUSED");
812 case GST_STATE_CHANGE_PAUSED_TO_READY:{
815 GST_DEBUG_OBJECT (self, "State change PAUSED->READY");
816 self->group_start_time = 0;
818 GST_STREAM_SYNCHRONIZER_LOCK (self);
819 for (l = self->streams; l; l = l->next) {
820 GstStream *stream = l->data;
822 gst_segment_init (&stream->segment, GST_FORMAT_UNDEFINED);
823 stream->wait = FALSE;
824 stream->new_stream = FALSE;
825 stream->drop_discont = FALSE;
826 stream->is_eos = FALSE;
828 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
831 case GST_STATE_CHANGE_READY_TO_NULL:{
832 GST_DEBUG_OBJECT (self, "State change READY->NULL");
834 GST_STREAM_SYNCHRONIZER_LOCK (self);
835 while (self->streams)
836 gst_stream_synchronizer_release_stream (self, self->streams->data);
837 self->current_stream_number = 0;
838 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
850 gst_stream_synchronizer_finalize (GObject * object)
852 GstStreamSynchronizer *self = GST_STREAM_SYNCHRONIZER (object);
855 g_mutex_free (self->lock);
859 if (self->stream_finish_cond) {
860 g_cond_free (self->stream_finish_cond);
861 self->stream_finish_cond = NULL;
864 G_OBJECT_CLASS (parent_class)->finalize (object);
867 /* GObject type initialization */
869 gst_stream_synchronizer_init (GstStreamSynchronizer * self)
871 self->lock = g_mutex_new ();
872 self->stream_finish_cond = g_cond_new ();
876 gst_stream_synchronizer_class_init (GstStreamSynchronizerClass * klass)
878 GObjectClass *gobject_class = (GObjectClass *) klass;
879 GstElementClass *element_class = (GstElementClass *) klass;
881 GST_DEBUG_CATEGORY_INIT (stream_synchronizer_debug,
882 "streamsynchronizer", 0, "Stream Synchronizer");
884 gobject_class->finalize = gst_stream_synchronizer_finalize;
886 gst_element_class_add_pad_template (element_class,
887 gst_static_pad_template_get (&srctemplate));
888 gst_element_class_add_pad_template (element_class,
889 gst_static_pad_template_get (&sinktemplate));
891 gst_element_class_set_details_simple (element_class,
892 "Stream Synchronizer", "Generic",
893 "Synchronizes a group of streams to have equal durations and starting points",
894 "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
896 element_class->change_state =
897 GST_DEBUG_FUNCPTR (gst_stream_synchronizer_change_state);
898 element_class->request_new_pad =
899 GST_DEBUG_FUNCPTR (gst_stream_synchronizer_request_new_pad);
900 element_class->release_pad =
901 GST_DEBUG_FUNCPTR (gst_stream_synchronizer_release_pad);