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 static const gboolean passthrough = TRUE;
57 #define gst_stream_synchronizer_parent_class parent_class
58 G_DEFINE_TYPE (GstStreamSynchronizer, gst_stream_synchronizer,
63 GstStreamSynchronizer *transform;
71 gboolean drop_discont;
75 gint64 running_time_diff;
78 /* Must be called with lock! */
80 gst_stream_get_other_pad (GstStream * stream, GstPad * pad)
82 if (stream->sinkpad == pad)
83 return gst_object_ref (stream->srcpad);
84 else if (stream->srcpad == pad)
85 return gst_object_ref (stream->sinkpad);
91 gst_stream_get_other_pad_from_pad (GstPad * pad)
93 GstStreamSynchronizer *self =
94 GST_STREAM_SYNCHRONIZER (gst_pad_get_parent (pad));
98 GST_STREAM_SYNCHRONIZER_LOCK (self);
99 stream = gst_pad_get_element_private (pad);
103 opad = gst_stream_get_other_pad (stream, pad);
106 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
107 gst_object_unref (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;
123 opad = gst_stream_get_other_pad_from_pad (pad);
125 GValue value = { 0, };
127 g_value_init (&value, GST_TYPE_PAD);
128 g_value_set_object (&value, opad);
129 it = gst_iterator_new_single (GST_TYPE_PAD, &value);
130 g_value_unset (&value);
131 gst_object_unref (opad);
138 gst_stream_synchronizer_query (GstPad * pad, GstObject * parent,
142 gboolean ret = FALSE;
144 GST_LOG_OBJECT (pad, "Handling query %s", GST_QUERY_TYPE_NAME (query));
146 opad = gst_stream_get_other_pad_from_pad (pad);
148 ret = gst_pad_peer_query (opad, query);
149 gst_object_unref (opad);
155 /* srcpad functions */
157 gst_stream_synchronizer_src_event (GstPad * pad, GstObject * parent,
160 GstStreamSynchronizer *self = GST_STREAM_SYNCHRONIZER (parent);
162 gboolean ret = FALSE;
165 goto skip_adjustments;
167 GST_LOG_OBJECT (pad, "Handling event %s: %" GST_PTR_FORMAT,
168 GST_EVENT_TYPE_NAME (event), event);
170 switch (GST_EVENT_TYPE (event)) {
173 GstClockTimeDiff diff;
174 GstClockTime timestamp;
175 gint64 running_time_diff;
178 gst_event_parse_qos (event, NULL, &proportion, &diff, ×tamp);
179 gst_event_unref (event);
181 GST_STREAM_SYNCHRONIZER_LOCK (self);
182 stream = gst_pad_get_element_private (pad);
184 running_time_diff = stream->running_time_diff;
186 running_time_diff = -1;
187 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
189 if (running_time_diff == -1) {
190 GST_WARNING_OBJECT (pad, "QOS event before group start");
192 } else if (timestamp < running_time_diff) {
193 GST_DEBUG_OBJECT (pad, "QOS event from previous group");
198 "Adjusting QOS event: %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT " = %"
199 GST_TIME_FORMAT, GST_TIME_ARGS (timestamp),
200 GST_TIME_ARGS (running_time_diff),
201 GST_TIME_ARGS (timestamp - running_time_diff));
203 timestamp -= running_time_diff;
205 /* That case is invalid for QoS events */
206 if (diff < 0 && -diff > timestamp) {
207 GST_DEBUG_OBJECT (pad, "QOS event from previous group");
213 gst_event_new_qos (GST_QOS_TYPE_UNDERFLOW, proportion, diff,
223 opad = gst_stream_get_other_pad_from_pad (pad);
225 ret = gst_pad_push_event (opad, event);
226 gst_object_unref (opad);
233 /* sinkpad functions */
235 gst_stream_synchronizer_sink_event (GstPad * pad, GstObject * parent,
238 GstStreamSynchronizer *self = GST_STREAM_SYNCHRONIZER (parent);
240 gboolean ret = FALSE;
243 goto skip_adjustments;
245 GST_LOG_OBJECT (pad, "Handling event %s: %" GST_PTR_FORMAT,
246 GST_EVENT_TYPE_NAME (event), event);
248 switch (GST_EVENT_TYPE (event)) {
249 case GST_EVENT_SINK_MESSAGE:{
252 gst_event_parse_sink_message (event, &message);
253 if (gst_message_has_name (message, "playbin-stream-changed")) {
256 GST_STREAM_SYNCHRONIZER_LOCK (self);
257 stream = gst_pad_get_element_private (pad);
260 gboolean all_wait = TRUE;
262 GST_DEBUG_OBJECT (pad, "Stream %d changed", stream->stream_number);
264 stream->is_eos = FALSE;
266 stream->new_stream = TRUE;
268 for (l = self->streams; l; l = l->next) {
269 GstStream *ostream = l->data;
271 all_wait = all_wait && ostream->wait;
278 GST_DEBUG_OBJECT (self, "All streams have changed -- unblocking");
280 for (l = self->streams; l; l = l->next) {
281 GstStream *ostream = l->data;
282 gint64 stop_running_time;
283 gint64 position_running_time;
285 ostream->wait = FALSE;
288 gst_segment_to_running_time (&ostream->segment,
289 GST_FORMAT_TIME, ostream->segment.stop);
290 position_running_time =
291 gst_segment_to_running_time (&ostream->segment,
292 GST_FORMAT_TIME, ostream->segment.position);
294 MAX (position, MAX (stop_running_time,
295 position_running_time));
297 position = MAX (0, position);
298 self->group_start_time = MAX (self->group_start_time, position);
300 GST_DEBUG_OBJECT (self, "New group start time: %" GST_TIME_FORMAT,
301 GST_TIME_ARGS (self->group_start_time));
303 g_cond_broadcast (self->stream_finish_cond);
306 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
308 gst_message_unref (message);
311 case GST_EVENT_SEGMENT:{
315 gst_event_copy_segment (event, &segment);
317 GST_STREAM_SYNCHRONIZER_LOCK (self);
318 stream = gst_pad_get_element_private (pad);
321 GST_DEBUG_OBJECT (pad, "Stream %d is waiting", stream->stream_number);
322 g_cond_wait (self->stream_finish_cond, self->lock);
323 stream = gst_pad_get_element_private (pad);
325 stream->wait = FALSE;
329 if (self->shutdown) {
330 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
331 gst_event_unref (event);
335 if (stream && segment.format == GST_FORMAT_TIME) {
336 if (stream->new_stream) {
337 gint64 position_running_time = 0;
338 gint64 stop_running_time = 0;
340 if (stream->segment.format == GST_FORMAT_TIME) {
341 position_running_time =
342 gst_segment_to_running_time (&stream->segment, GST_FORMAT_TIME,
343 stream->segment.position);
344 position_running_time = MAX (position_running_time, 0);
346 gst_segment_to_running_time (&stream->segment, GST_FORMAT_TIME,
347 stream->segment.stop);
348 stop_running_time = MAX (position_running_time, 0);
350 if (stop_running_time != position_running_time) {
351 GST_WARNING_OBJECT (pad,
352 "Gap between position and segment stop: %" GST_TIME_FORMAT
353 " != %" GST_TIME_FORMAT, GST_TIME_ARGS (stop_running_time),
354 GST_TIME_ARGS (position_running_time));
357 if (stop_running_time < position_running_time) {
358 GST_DEBUG_OBJECT (pad, "Updating stop position");
359 stream->segment.stop = stream->segment.position;
360 gst_pad_push_event (stream->srcpad,
361 gst_event_new_segment (&stream->segment));
363 stop_running_time = MAX (stop_running_time, position_running_time);
364 GST_DEBUG_OBJECT (pad,
365 "Stop running time of last group: %" GST_TIME_FORMAT,
366 GST_TIME_ARGS (stop_running_time));
368 stream->new_stream = FALSE;
369 stream->drop_discont = TRUE;
371 if (stop_running_time < self->group_start_time) {
372 gint64 diff = self->group_start_time - stop_running_time;
374 GST_DEBUG_OBJECT (pad,
375 "Advancing running time for other streams by: %"
376 GST_TIME_FORMAT, GST_TIME_ARGS (diff));
378 segment.base += diff;
382 GST_DEBUG_OBJECT (pad, "Segment was: %" GST_SEGMENT_FORMAT,
384 gst_segment_copy_into (&segment, &stream->segment);
385 GST_DEBUG_OBJECT (pad, "Segment now is: %" GST_SEGMENT_FORMAT,
388 GST_DEBUG_OBJECT (pad, "Stream start running time: %" GST_TIME_FORMAT,
389 GST_TIME_ARGS (stream->segment.base));
390 stream->running_time_diff = stream->segment.base;
392 GST_WARNING_OBJECT (pad, "Non-TIME segment: %s",
393 gst_format_get_name (segment.format));
394 gst_segment_init (&stream->segment, GST_FORMAT_UNDEFINED);
396 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
399 case GST_EVENT_FLUSH_STOP:{
402 GST_STREAM_SYNCHRONIZER_LOCK (self);
403 stream = gst_pad_get_element_private (pad);
405 GST_DEBUG_OBJECT (pad, "Resetting segment for stream %d",
406 stream->stream_number);
407 gst_segment_init (&stream->segment, GST_FORMAT_UNDEFINED);
409 stream->is_eos = FALSE;
410 stream->wait = FALSE;
411 stream->new_stream = FALSE;
412 stream->drop_discont = FALSE;
413 stream->seen_data = FALSE;
415 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
421 gboolean all_eos = TRUE;
426 GST_STREAM_SYNCHRONIZER_LOCK (self);
427 stream = gst_pad_get_element_private (pad);
429 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
430 GST_WARNING_OBJECT (pad, "EOS for unknown stream");
434 GST_DEBUG_OBJECT (pad, "Have EOS for stream %d", stream->stream_number);
435 stream->is_eos = TRUE;
437 seen_data = stream->seen_data;
438 srcpad = gst_object_ref (stream->srcpad);
440 for (l = self->streams; l; l = l->next) {
441 GstStream *ostream = l->data;
443 all_eos = all_eos && ostream->is_eos;
449 GST_DEBUG_OBJECT (self, "All streams are EOS -- forwarding");
450 for (l = self->streams; l; l = l->next) {
451 GstStream *ostream = l->data;
452 /* local snapshot of current pads */
453 gst_object_ref (ostream->srcpad);
454 pads = g_slist_prepend (pads, ostream->srcpad);
457 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
458 /* drop lock when sending eos, which may block in e.g. preroll */
467 GST_DEBUG_OBJECT (pad, "Pushing EOS");
468 ret = ret && gst_pad_push_event (pad, gst_event_new_eos ());
469 gst_object_unref (pad);
470 epad = g_slist_next (epad);
474 /* if EOS, but no data has passed, then send something to replace EOS
475 * for preroll purposes */
477 GstBuffer *buf = gst_buffer_new ();
479 gst_pad_push (srcpad, buf);
482 gst_object_unref (srcpad);
492 opad = gst_stream_get_other_pad_from_pad (pad);
494 ret = gst_pad_push_event (opad, event);
495 gst_object_unref (opad);
503 gst_stream_synchronizer_sink_chain (GstPad * pad, GstObject * parent,
506 GstStreamSynchronizer *self = GST_STREAM_SYNCHRONIZER (parent);
508 GstFlowReturn ret = GST_FLOW_ERROR;
510 GstClockTime timestamp = GST_CLOCK_TIME_NONE;
511 GstClockTime timestamp_end = GST_CLOCK_TIME_NONE;
514 opad = gst_stream_get_other_pad_from_pad (pad);
516 ret = gst_pad_push (opad, buffer);
517 gst_object_unref (opad);
522 GST_LOG_OBJECT (pad, "Handling buffer %p: size=%" G_GSIZE_FORMAT
523 ", timestamp=%" GST_TIME_FORMAT " duration=%" GST_TIME_FORMAT
524 " offset=%" G_GUINT64_FORMAT " offset_end=%" G_GUINT64_FORMAT,
525 buffer, gst_buffer_get_size (buffer),
526 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
527 GST_TIME_ARGS (GST_BUFFER_DURATION (buffer)),
528 GST_BUFFER_OFFSET (buffer), GST_BUFFER_OFFSET_END (buffer));
530 timestamp = GST_BUFFER_TIMESTAMP (buffer);
531 if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)
532 && GST_BUFFER_DURATION_IS_VALID (buffer))
533 timestamp_end = timestamp + GST_BUFFER_DURATION (buffer);
535 GST_STREAM_SYNCHRONIZER_LOCK (self);
536 stream = gst_pad_get_element_private (pad);
538 stream->seen_data = TRUE;
539 if (stream && stream->drop_discont) {
540 buffer = gst_buffer_make_writable (buffer);
541 GST_BUFFER_FLAG_UNSET (buffer, GST_BUFFER_FLAG_DISCONT);
542 stream->drop_discont = FALSE;
545 if (stream && stream->segment.format == GST_FORMAT_TIME
546 && GST_CLOCK_TIME_IS_VALID (timestamp)) {
548 "Updating last-stop from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
549 GST_TIME_ARGS (stream->segment.position), GST_TIME_ARGS (timestamp));
550 stream->segment.position = timestamp;
552 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
554 opad = gst_stream_get_other_pad_from_pad (pad);
556 ret = gst_pad_push (opad, buffer);
557 gst_object_unref (opad);
560 GST_LOG_OBJECT (pad, "Push returned: %s", gst_flow_get_name (ret));
561 if (ret == GST_FLOW_OK) {
564 GST_STREAM_SYNCHRONIZER_LOCK (self);
565 stream = gst_pad_get_element_private (pad);
566 if (stream && stream->segment.format == GST_FORMAT_TIME
567 && GST_CLOCK_TIME_IS_VALID (timestamp_end)) {
569 "Updating last-stop from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
570 GST_TIME_ARGS (stream->segment.position),
571 GST_TIME_ARGS (timestamp_end));
572 stream->segment.position = timestamp_end;
575 /* Advance EOS streams if necessary. For non-EOS
576 * streams the demuxers should already do this! */
577 for (l = self->streams; l; l = l->next) {
578 GstStream *ostream = l->data;
581 if (!ostream->is_eos || ostream->segment.format != GST_FORMAT_TIME)
584 if (ostream->segment.position != -1)
585 position = ostream->segment.position;
587 position = ostream->segment.start;
589 /* Is there a 1 second lag? */
590 if (position != -1 && position + GST_SECOND < timestamp_end) {
591 gint64 new_start, new_stop;
593 new_start = timestamp_end - GST_SECOND;
594 if (ostream->segment.stop == -1)
597 new_stop = MAX (new_start, ostream->segment.stop);
599 GST_DEBUG_OBJECT (ostream->sinkpad,
600 "Advancing stream %u from %" GST_TIME_FORMAT " to %"
601 GST_TIME_FORMAT, ostream->stream_number, GST_TIME_ARGS (position),
602 GST_TIME_ARGS (new_start));
604 ostream->segment.start = new_start;
605 ostream->segment.stop = new_stop;
606 ostream->segment.time = new_start;
607 ostream->segment.position = new_start;
609 gst_pad_push_event (ostream->srcpad,
610 gst_event_new_segment (&ostream->segment));
613 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
620 /* GstElement vfuncs */
622 gst_stream_synchronizer_request_new_pad (GstElement * element,
623 GstPadTemplate * temp, const gchar * name, const GstCaps * caps)
625 GstStreamSynchronizer *self = GST_STREAM_SYNCHRONIZER (element);
629 GST_STREAM_SYNCHRONIZER_LOCK (self);
630 GST_DEBUG_OBJECT (self, "Requesting new pad for stream %d",
631 self->current_stream_number);
633 stream = g_slice_new0 (GstStream);
634 stream->transform = self;
635 stream->stream_number = self->current_stream_number;
637 tmp = g_strdup_printf ("sink_%u", self->current_stream_number);
638 stream->sinkpad = gst_pad_new_from_static_template (&sinktemplate, tmp);
640 gst_pad_set_element_private (stream->sinkpad, stream);
641 gst_pad_set_iterate_internal_links_function (stream->sinkpad,
642 GST_DEBUG_FUNCPTR (gst_stream_synchronizer_iterate_internal_links));
643 gst_pad_set_query_function (stream->sinkpad,
644 GST_DEBUG_FUNCPTR (gst_stream_synchronizer_query));
645 gst_pad_set_event_function (stream->sinkpad,
646 GST_DEBUG_FUNCPTR (gst_stream_synchronizer_sink_event));
647 gst_pad_set_chain_function (stream->sinkpad,
648 GST_DEBUG_FUNCPTR (gst_stream_synchronizer_sink_chain));
650 tmp = g_strdup_printf ("src_%u", self->current_stream_number);
651 stream->srcpad = gst_pad_new_from_static_template (&srctemplate, tmp);
653 gst_pad_set_element_private (stream->srcpad, stream);
654 gst_pad_set_iterate_internal_links_function (stream->srcpad,
655 GST_DEBUG_FUNCPTR (gst_stream_synchronizer_iterate_internal_links));
656 gst_pad_set_query_function (stream->srcpad,
657 GST_DEBUG_FUNCPTR (gst_stream_synchronizer_query));
658 gst_pad_set_event_function (stream->srcpad,
659 GST_DEBUG_FUNCPTR (gst_stream_synchronizer_src_event));
661 gst_segment_init (&stream->segment, GST_FORMAT_UNDEFINED);
663 self->streams = g_list_prepend (self->streams, stream);
664 self->current_stream_number++;
665 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
667 /* Add pads and activate unless we're going to NULL */
668 g_static_rec_mutex_lock (GST_STATE_GET_LOCK (self));
669 if (GST_STATE_TARGET (self) != GST_STATE_NULL) {
670 gst_pad_set_active (stream->srcpad, TRUE);
671 gst_pad_set_active (stream->sinkpad, TRUE);
673 gst_element_add_pad (GST_ELEMENT_CAST (self), stream->srcpad);
674 gst_element_add_pad (GST_ELEMENT_CAST (self), stream->sinkpad);
675 g_static_rec_mutex_unlock (GST_STATE_GET_LOCK (self));
677 return stream->sinkpad;
680 /* Must be called with lock! */
682 gst_stream_synchronizer_release_stream (GstStreamSynchronizer * self,
687 GST_DEBUG_OBJECT (self, "Releasing stream %d", stream->stream_number);
689 for (l = self->streams; l; l = l->next) {
690 if (l->data == stream) {
691 self->streams = g_list_delete_link (self->streams, l);
695 g_assert (l != NULL);
697 /* we can drop the lock, since stream exists now only local.
698 * Moreover, we should drop, to prevent deadlock with STREAM_LOCK
699 * (due to reverse lock order) when deactivating pads */
700 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
702 gst_pad_set_element_private (stream->srcpad, NULL);
703 gst_pad_set_element_private (stream->sinkpad, NULL);
704 gst_pad_set_active (stream->srcpad, FALSE);
705 gst_element_remove_pad (GST_ELEMENT_CAST (self), stream->srcpad);
706 gst_pad_set_active (stream->sinkpad, FALSE);
707 gst_element_remove_pad (GST_ELEMENT_CAST (self), stream->sinkpad);
709 if (stream->segment.format == GST_FORMAT_TIME) {
710 gint64 stop_running_time;
711 gint64 position_running_time;
714 gst_segment_to_running_time (&stream->segment, GST_FORMAT_TIME,
715 stream->segment.stop);
716 position_running_time =
717 gst_segment_to_running_time (&stream->segment, GST_FORMAT_TIME,
718 stream->segment.position);
719 stop_running_time = MAX (stop_running_time, position_running_time);
721 GST_DEBUG_OBJECT (stream->sinkpad,
722 "Stop running time was: %" GST_TIME_FORMAT,
723 GST_TIME_ARGS (stop_running_time));
725 self->group_start_time = MAX (self->group_start_time, stop_running_time);
728 g_slice_free (GstStream, stream);
730 /* NOTE: In theory we have to check here if all streams
731 * are EOS but the one that was removed wasn't and then
732 * send EOS downstream. But due to the way how playsink
733 * works this is not necessary and will only cause problems
734 * for gapless playback. playsink will only add/remove pads
735 * when it's reconfigured, which happens when the streams
739 /* lock for good measure, since the caller had it */
740 GST_STREAM_SYNCHRONIZER_LOCK (self);
744 gst_stream_synchronizer_release_pad (GstElement * element, GstPad * pad)
746 GstStreamSynchronizer *self = GST_STREAM_SYNCHRONIZER (element);
749 GST_STREAM_SYNCHRONIZER_LOCK (self);
750 stream = gst_pad_get_element_private (pad);
752 g_assert (stream->sinkpad == pad);
754 gst_stream_synchronizer_release_stream (self, stream);
756 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
759 static GstStateChangeReturn
760 gst_stream_synchronizer_change_state (GstElement * element,
761 GstStateChange transition)
763 GstStreamSynchronizer *self = GST_STREAM_SYNCHRONIZER (element);
764 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
766 switch (transition) {
767 case GST_STATE_CHANGE_NULL_TO_READY:
768 GST_DEBUG_OBJECT (self, "State change NULL->READY");
769 self->shutdown = FALSE;
771 case GST_STATE_CHANGE_READY_TO_PAUSED:
772 GST_DEBUG_OBJECT (self, "State change READY->PAUSED");
773 self->group_start_time = 0;
774 self->shutdown = FALSE;
776 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
777 GST_DEBUG_OBJECT (self, "State change PAUSED->PLAYING");
779 case GST_STATE_CHANGE_PAUSED_TO_READY:
780 GST_DEBUG_OBJECT (self, "State change READY->NULL");
782 GST_STREAM_SYNCHRONIZER_LOCK (self);
783 g_cond_broadcast (self->stream_finish_cond);
784 self->shutdown = TRUE;
785 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
791 GstStateChangeReturn bret;
793 bret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
794 GST_DEBUG_OBJECT (self, "Base class state changed returned: %d", bret);
795 if (G_UNLIKELY (bret == GST_STATE_CHANGE_FAILURE))
799 switch (transition) {
800 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
801 GST_DEBUG_OBJECT (self, "State change PLAYING->PAUSED");
803 case GST_STATE_CHANGE_PAUSED_TO_READY:{
806 GST_DEBUG_OBJECT (self, "State change PAUSED->READY");
807 self->group_start_time = 0;
809 GST_STREAM_SYNCHRONIZER_LOCK (self);
810 for (l = self->streams; l; l = l->next) {
811 GstStream *stream = l->data;
813 gst_segment_init (&stream->segment, GST_FORMAT_UNDEFINED);
814 stream->wait = FALSE;
815 stream->new_stream = FALSE;
816 stream->drop_discont = FALSE;
817 stream->is_eos = FALSE;
819 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
822 case GST_STATE_CHANGE_READY_TO_NULL:{
823 GST_DEBUG_OBJECT (self, "State change READY->NULL");
825 GST_STREAM_SYNCHRONIZER_LOCK (self);
826 while (self->streams)
827 gst_stream_synchronizer_release_stream (self, self->streams->data);
828 self->current_stream_number = 0;
829 GST_STREAM_SYNCHRONIZER_UNLOCK (self);
841 gst_stream_synchronizer_finalize (GObject * object)
843 GstStreamSynchronizer *self = GST_STREAM_SYNCHRONIZER (object);
846 g_mutex_free (self->lock);
850 if (self->stream_finish_cond) {
851 g_cond_free (self->stream_finish_cond);
852 self->stream_finish_cond = NULL;
855 G_OBJECT_CLASS (parent_class)->finalize (object);
858 /* GObject type initialization */
860 gst_stream_synchronizer_init (GstStreamSynchronizer * self)
862 self->lock = g_mutex_new ();
863 self->stream_finish_cond = g_cond_new ();
867 gst_stream_synchronizer_class_init (GstStreamSynchronizerClass * klass)
869 GObjectClass *gobject_class = (GObjectClass *) klass;
870 GstElementClass *element_class = (GstElementClass *) klass;
872 GST_DEBUG_CATEGORY_INIT (stream_synchronizer_debug,
873 "streamsynchronizer", 0, "Stream Synchronizer");
875 gobject_class->finalize = gst_stream_synchronizer_finalize;
877 gst_element_class_add_pad_template (element_class,
878 gst_static_pad_template_get (&srctemplate));
879 gst_element_class_add_pad_template (element_class,
880 gst_static_pad_template_get (&sinktemplate));
882 gst_element_class_set_details_simple (element_class,
883 "Stream Synchronizer", "Generic",
884 "Synchronizes a group of streams to have equal durations and starting points",
885 "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
887 element_class->change_state =
888 GST_DEBUG_FUNCPTR (gst_stream_synchronizer_change_state);
889 element_class->request_new_pad =
890 GST_DEBUG_FUNCPTR (gst_stream_synchronizer_request_new_pad);
891 element_class->release_pad =
892 GST_DEBUG_FUNCPTR (gst_stream_synchronizer_release_pad);