2 * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
3 * Copyright (C) <2011> Sebastian Dröge <sebastian.droege@collabora.co.uk>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
25 /* FIXME 0.11: suppress warnings for deprecated API such as GStaticRecMutex
26 * with newer GLib versions (>= 2.31.0) */
27 #define GLIB_DISABLE_DEPRECATION_WARNINGS
32 #include <gst/gst-i18n-plugin.h>
33 #include <gst/pbutils/pbutils.h>
34 #include <gst/video/video.h>
35 #include <gst/interfaces/streamvolume.h>
36 #include <gst/interfaces/colorbalance.h>
38 #include "gstplaysink.h"
39 #include "gststreamsynchronizer.h"
40 #include "gstplaysinkvideoconvert.h"
41 #include "gstplaysinkaudioconvert.h"
43 GST_DEBUG_CATEGORY_STATIC (gst_play_sink_debug);
44 #define GST_CAT_DEFAULT gst_play_sink_debug
46 #define VOLUME_MAX_DOUBLE 10.0
48 #define DEFAULT_FLAGS GST_PLAY_FLAG_AUDIO | GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_TEXT | \
49 GST_PLAY_FLAG_SOFT_VOLUME | GST_PLAY_FLAG_SOFT_COLORBALANCE
51 #define GST_PLAY_CHAIN(c) ((GstPlayChain *)(c))
53 /* holds the common data fields for the audio and video pipelines. We keep them
54 * in a structure to more easily have all the info available. */
57 GstPlaySink *playsink;
70 GstElement *volume; /* element with the volume property */
71 gboolean sink_volume; /* if the volume was provided by the sink */
72 GstElement *mute; /* element with the mute property */
74 GstElement *ts_offset;
80 GstPad *sinkpad, *srcpad;
82 GstElement *deinterlace;
83 } GstPlayVideoDeinterlaceChain;
93 GstElement *ts_offset;
102 GstElement *resample;
103 GstPad *blockpad; /* srcpad of resample, used for switching the vis */
104 GstPad *vissinkpad; /* visualisation sinkpad, */
106 GstPad *vissrcpad; /* visualisation srcpad, */
107 GstPad *srcpad; /* outgoing srcpad, used to connect to the next
116 GstElement *identity;
118 GstPad *videosinkpad;
120 GstPad *srcpad; /* outgoing srcpad, used to connect to the next
122 GstElement *sink; /* custom sink to receive subtitle buffers */
125 #define GST_PLAY_SINK_GET_LOCK(playsink) (&((GstPlaySink *)playsink)->lock)
126 #define GST_PLAY_SINK_LOCK(playsink) G_STMT_START { \
127 GST_LOG_OBJECT (playsink, "locking from thread %p", g_thread_self ()); \
128 g_static_rec_mutex_lock (GST_PLAY_SINK_GET_LOCK (playsink)); \
129 GST_LOG_OBJECT (playsink, "locked from thread %p", g_thread_self ()); \
131 #define GST_PLAY_SINK_UNLOCK(playsink) G_STMT_START { \
132 GST_LOG_OBJECT (playsink, "unlocking from thread %p", g_thread_self ()); \
133 g_static_rec_mutex_unlock (GST_PLAY_SINK_GET_LOCK (playsink)); \
136 #define PENDING_FLAG_SET(playsink, flagtype) \
137 ((playsink->pending_blocked_pads) |= (1 << flagtype))
138 #define PENDING_FLAG_UNSET(playsink, flagtype) \
139 ((playsink->pending_blocked_pads) &= ~(1 << flagtype))
140 #define PENDING_FLAG_IS_SET(playsink, flagtype) \
141 ((playsink->pending_blocked_pads) & (1 << flagtype))
142 #define PENDING_VIDEO_BLOCK(playsink) \
143 ((playsink->pending_blocked_pads) & (1 << GST_PLAY_SINK_TYPE_VIDEO_RAW | 1 << GST_PLAY_SINK_TYPE_VIDEO))
144 #define PENDING_AUDIO_BLOCK(playsink) \
145 ((playsink->pending_blocked_pads) & (1 << GST_PLAY_SINK_TYPE_AUDIO_RAW | 1 << GST_PLAY_SINK_TYPE_AUDIO))
146 #define PENDING_TEXT_BLOCK(playsink) \
147 PENDING_FLAG_IS_SET(playsink, GST_PLAY_SINK_TYPE_TEXT)
153 GStaticRecMutex lock;
155 gboolean async_pending;
156 gboolean need_async_start;
160 GstStreamSynchronizer *stream_synchronizer;
163 GstPlayAudioChain *audiochain;
164 GstPlayVideoDeinterlaceChain *videodeinterlacechain;
165 GstPlayVideoChain *videochain;
166 GstPlayVisChain *vischain;
167 GstPlayTextChain *textchain;
171 gboolean audio_pad_raw;
172 gboolean audio_pad_blocked;
173 GstPad *audio_srcpad_stream_synchronizer;
174 GstPad *audio_sinkpad_stream_synchronizer;
176 GstElement *audio_tee;
177 GstPad *audio_tee_sink;
178 GstPad *audio_tee_asrc;
179 GstPad *audio_tee_vissrc;
182 gboolean video_pad_raw;
183 gboolean video_pad_blocked;
184 GstPad *video_srcpad_stream_synchronizer;
185 GstPad *video_sinkpad_stream_synchronizer;
188 gboolean text_pad_blocked;
189 GstPad *text_srcpad_stream_synchronizer;
190 GstPad *text_sinkpad_stream_synchronizer;
192 guint32 pending_blocked_pads;
195 GstElement *audio_sink;
196 GstElement *video_sink;
197 GstElement *visualisation;
198 GstElement *text_sink;
201 gchar *font_desc; /* font description */
202 gchar *subtitle_encoding; /* subtitle encoding */
203 guint connection_speed; /* connection speed in bits/sec (0 = unknown) */
205 gboolean volume_changed; /* volume/mute changed while no audiochain */
206 gboolean mute_changed; /* ... has been created yet */
210 struct _GstPlaySinkClass
212 GstBinClass parent_class;
214 gboolean (*reconfigure) (GstPlaySink * playsink);
216 GstBuffer *(*convert_frame) (GstPlaySink * playsink, GstCaps * caps);
220 static GstStaticPadTemplate audiotemplate =
221 GST_STATIC_PAD_TEMPLATE ("audio_sink",
224 GST_STATIC_CAPS_ANY);
225 static GstStaticPadTemplate videotemplate =
226 GST_STATIC_PAD_TEMPLATE ("video_sink",
229 GST_STATIC_CAPS_ANY);
230 static GstStaticPadTemplate texttemplate = GST_STATIC_PAD_TEMPLATE ("text_sink",
233 GST_STATIC_CAPS_ANY);
235 /* FIXME 0.11: Remove */
236 static GstStaticPadTemplate audiorawtemplate =
237 GST_STATIC_PAD_TEMPLATE ("audio_raw_sink",
240 GST_STATIC_CAPS_ANY);
241 static GstStaticPadTemplate videorawtemplate =
242 GST_STATIC_PAD_TEMPLATE ("video_raw_sink",
245 GST_STATIC_CAPS_ANY);
256 PROP_SUBTITLE_ENCODING,
272 static void gst_play_sink_dispose (GObject * object);
273 static void gst_play_sink_finalize (GObject * object);
274 static void gst_play_sink_set_property (GObject * object, guint prop_id,
275 const GValue * value, GParamSpec * spec);
276 static void gst_play_sink_get_property (GObject * object, guint prop_id,
277 GValue * value, GParamSpec * spec);
279 static GstPad *gst_play_sink_request_new_pad (GstElement * element,
280 GstPadTemplate * templ, const gchar * name);
281 static void gst_play_sink_release_request_pad (GstElement * element,
283 static gboolean gst_play_sink_send_event (GstElement * element,
285 static GstStateChangeReturn gst_play_sink_change_state (GstElement * element,
286 GstStateChange transition);
288 static void gst_play_sink_handle_message (GstBin * bin, GstMessage * message);
290 static void notify_volume_cb (GObject * object, GParamSpec * pspec,
291 GstPlaySink * playsink);
292 static void notify_mute_cb (GObject * object, GParamSpec * pspec,
293 GstPlaySink * playsink);
295 static void update_av_offset (GstPlaySink * playsink);
298 gst_play_marshal_BUFFER__BOXED (GClosure * closure,
299 GValue * return_value G_GNUC_UNUSED,
300 guint n_param_values,
301 const GValue * param_values,
302 gpointer invocation_hint G_GNUC_UNUSED, gpointer marshal_data)
304 typedef GstBuffer *(*GMarshalFunc_OBJECT__BOXED) (gpointer data1,
305 gpointer arg_1, gpointer data2);
306 register GMarshalFunc_OBJECT__BOXED callback;
307 register GCClosure *cc = (GCClosure *) closure;
308 register gpointer data1, data2;
310 g_return_if_fail (return_value != NULL);
311 g_return_if_fail (n_param_values == 2);
313 if (G_CCLOSURE_SWAP_DATA (closure)) {
314 data1 = closure->data;
315 data2 = g_value_peek_pointer (param_values + 0);
317 data1 = g_value_peek_pointer (param_values + 0);
318 data2 = closure->data;
321 (GMarshalFunc_OBJECT__BOXED) (marshal_data ? marshal_data : cc->callback);
323 v_return = callback (data1, g_value_get_boxed (param_values + 1), data2);
325 gst_value_take_buffer (return_value, v_return);
328 /* static guint gst_play_sink_signals[LAST_SIGNAL] = { 0 }; */
331 _do_init (GType type)
333 static const GInterfaceInfo svol_info = {
337 g_type_add_interface_static (type, GST_TYPE_STREAM_VOLUME, &svol_info);
340 G_DEFINE_TYPE_WITH_CODE (GstPlaySink, gst_play_sink, GST_TYPE_BIN,
341 _do_init (g_define_type_id));
344 gst_play_sink_class_init (GstPlaySinkClass * klass)
346 GObjectClass *gobject_klass;
347 GstElementClass *gstelement_klass;
348 GstBinClass *gstbin_klass;
350 gobject_klass = (GObjectClass *) klass;
351 gstelement_klass = (GstElementClass *) klass;
352 gstbin_klass = (GstBinClass *) klass;
354 gobject_klass->dispose = gst_play_sink_dispose;
355 gobject_klass->finalize = gst_play_sink_finalize;
356 gobject_klass->set_property = gst_play_sink_set_property;
357 gobject_klass->get_property = gst_play_sink_get_property;
363 * Control the behaviour of playsink.
365 g_object_class_install_property (gobject_klass, PROP_FLAGS,
366 g_param_spec_flags ("flags", "Flags", "Flags to control behaviour",
367 GST_TYPE_PLAY_FLAGS, DEFAULT_FLAGS,
368 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
371 * GstPlaySink:volume:
373 * Get or set the current audio stream volume. 1.0 means 100%,
374 * 0.0 means mute. This uses a linear volume scale.
377 g_object_class_install_property (gobject_klass, PROP_VOLUME,
378 g_param_spec_double ("volume", "Volume", "The audio volume, 1.0=100%",
379 0.0, VOLUME_MAX_DOUBLE, 1.0,
380 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
381 g_object_class_install_property (gobject_klass, PROP_MUTE,
382 g_param_spec_boolean ("mute", "Mute",
383 "Mute the audio channel without changing the volume", FALSE,
384 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
385 g_object_class_install_property (gobject_klass, PROP_FONT_DESC,
386 g_param_spec_string ("subtitle-font-desc",
387 "Subtitle font description",
388 "Pango font description of font "
389 "to be used for subtitle rendering", NULL,
390 G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
391 g_object_class_install_property (gobject_klass, PROP_SUBTITLE_ENCODING,
392 g_param_spec_string ("subtitle-encoding", "subtitle encoding",
393 "Encoding to assume if input subtitles are not in UTF-8 encoding. "
394 "If not set, the GST_SUBTITLE_ENCODING environment variable will "
395 "be checked for an encoding to use. If that is not set either, "
396 "ISO-8859-15 will be assumed.", NULL,
397 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
398 g_object_class_install_property (gobject_klass, PROP_VIS_PLUGIN,
399 g_param_spec_object ("vis-plugin", "Vis plugin",
400 "the visualization element to use (NULL = default)",
401 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
405 * Get the currently rendered or prerolled frame in the video sink.
406 * The #GstCaps on the buffer will describe the format of the buffer.
410 g_object_class_install_property (gobject_klass, PROP_FRAME,
411 gst_param_spec_mini_object ("frame", "Frame",
412 "The last frame (NULL = no video available)",
413 GST_TYPE_BUFFER, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
415 * GstPlaySink:av-offset:
417 * Control the synchronisation offset between the audio and video streams.
418 * Positive values make the audio ahead of the video and negative values make
419 * the audio go behind the video.
423 g_object_class_install_property (gobject_klass, PROP_AV_OFFSET,
424 g_param_spec_int64 ("av-offset", "AV Offset",
425 "The synchronisation offset between audio and video in nanoseconds",
426 G_MININT64, G_MAXINT64, 0,
427 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
430 * GstPlaySink:video-sink:
432 * Set the used video sink element. NULL will use the default sink. playsink
433 * must be in %GST_STATE_NULL
437 g_object_class_install_property (gobject_klass, PROP_VIDEO_SINK,
438 g_param_spec_object ("video-sink", "Video Sink",
439 "the video output element to use (NULL = default sink)",
440 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
442 * GstPlaySink:audio-sink:
444 * Set the used audio sink element. NULL will use the default sink. playsink
445 * must be in %GST_STATE_NULL
449 g_object_class_install_property (gobject_klass, PROP_AUDIO_SINK,
450 g_param_spec_object ("audio-sink", "Audio Sink",
451 "the audio output element to use (NULL = default sink)",
452 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
454 * GstPlaySink:text-sink:
456 * Set the used text sink element. NULL will use the default sink. playsink
457 * must be in %GST_STATE_NULL
461 g_object_class_install_property (gobject_klass, PROP_TEXT_SINK,
462 g_param_spec_object ("text-sink", "Text sink",
463 "the text output element to use (NULL = default textoverlay)",
464 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
467 g_signal_new ("reconfigure", G_TYPE_FROM_CLASS (klass),
468 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstPlaySinkClass,
469 reconfigure), NULL, NULL, gst_marshal_BOOLEAN__VOID, G_TYPE_BOOLEAN,
472 * GstPlaySink::convert-frame
473 * @playsink: a #GstPlaySink
474 * @caps: the target format of the frame
476 * Action signal to retrieve the currently playing video frame in the format
477 * specified by @caps.
478 * If @caps is %NULL, no conversion will be performed and this function is
479 * equivalent to the #GstPlaySink::frame property.
481 * Returns: a #GstBuffer of the current video frame converted to #caps.
482 * The caps on the buffer will describe the final layout of the buffer data.
483 * %NULL is returned when no current buffer can be retrieved or when the
488 g_signal_new ("convert-frame", G_TYPE_FROM_CLASS (klass),
489 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
490 G_STRUCT_OFFSET (GstPlaySinkClass, convert_frame), NULL, NULL,
491 gst_play_marshal_BUFFER__BOXED, GST_TYPE_BUFFER, 1, GST_TYPE_CAPS);
493 gst_element_class_add_static_pad_template (gstelement_klass,
495 gst_element_class_add_static_pad_template (gstelement_klass, &audiotemplate);
496 gst_element_class_add_static_pad_template (gstelement_klass,
498 gst_element_class_add_static_pad_template (gstelement_klass, &videotemplate);
499 gst_element_class_add_static_pad_template (gstelement_klass, &texttemplate);
500 gst_element_class_set_details_simple (gstelement_klass, "Player Sink",
502 "Convenience sink for multiple streams",
503 "Wim Taymans <wim.taymans@gmail.com>");
505 gstelement_klass->change_state =
506 GST_DEBUG_FUNCPTR (gst_play_sink_change_state);
507 gstelement_klass->send_event = GST_DEBUG_FUNCPTR (gst_play_sink_send_event);
508 gstelement_klass->request_new_pad =
509 GST_DEBUG_FUNCPTR (gst_play_sink_request_new_pad);
510 gstelement_klass->release_pad =
511 GST_DEBUG_FUNCPTR (gst_play_sink_release_request_pad);
513 gstbin_klass->handle_message =
514 GST_DEBUG_FUNCPTR (gst_play_sink_handle_message);
516 klass->reconfigure = GST_DEBUG_FUNCPTR (gst_play_sink_reconfigure);
517 klass->convert_frame = GST_DEBUG_FUNCPTR (gst_play_sink_convert_frame);
521 gst_play_sink_init (GstPlaySink * playsink)
524 playsink->video_sink = NULL;
525 playsink->audio_sink = NULL;
526 playsink->visualisation = NULL;
527 playsink->text_sink = NULL;
528 playsink->volume = 1.0;
529 playsink->font_desc = NULL;
530 playsink->subtitle_encoding = NULL;
531 playsink->flags = DEFAULT_FLAGS;
533 playsink->stream_synchronizer =
534 g_object_new (GST_TYPE_STREAM_SYNCHRONIZER, NULL);
535 gst_bin_add (GST_BIN_CAST (playsink),
536 GST_ELEMENT_CAST (playsink->stream_synchronizer));
538 g_static_rec_mutex_init (&playsink->lock);
539 GST_OBJECT_FLAG_SET (playsink, GST_ELEMENT_IS_SINK);
543 disconnect_chain (GstPlayAudioChain * chain, GstPlaySink * playsink)
547 g_signal_handlers_disconnect_by_func (chain->volume, notify_volume_cb,
550 g_signal_handlers_disconnect_by_func (chain->mute, notify_mute_cb,
556 free_chain (GstPlayChain * chain)
560 gst_object_unref (chain->bin);
566 gst_play_sink_dispose (GObject * object)
568 GstPlaySink *playsink;
570 playsink = GST_PLAY_SINK (object);
572 if (playsink->audio_sink != NULL) {
573 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
574 gst_object_unref (playsink->audio_sink);
575 playsink->audio_sink = NULL;
577 if (playsink->video_sink != NULL) {
578 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
579 gst_object_unref (playsink->video_sink);
580 playsink->video_sink = NULL;
582 if (playsink->visualisation != NULL) {
583 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
584 gst_object_unref (playsink->visualisation);
585 playsink->visualisation = NULL;
587 if (playsink->text_sink != NULL) {
588 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
589 gst_object_unref (playsink->text_sink);
590 playsink->text_sink = NULL;
593 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
594 playsink->videodeinterlacechain = NULL;
595 free_chain ((GstPlayChain *) playsink->videochain);
596 playsink->videochain = NULL;
597 free_chain ((GstPlayChain *) playsink->audiochain);
598 playsink->audiochain = NULL;
599 free_chain ((GstPlayChain *) playsink->vischain);
600 playsink->vischain = NULL;
601 free_chain ((GstPlayChain *) playsink->textchain);
602 playsink->textchain = NULL;
604 if (playsink->audio_tee_sink) {
605 gst_object_unref (playsink->audio_tee_sink);
606 playsink->audio_tee_sink = NULL;
609 if (playsink->audio_tee_vissrc) {
610 gst_element_release_request_pad (playsink->audio_tee,
611 playsink->audio_tee_vissrc);
612 gst_object_unref (playsink->audio_tee_vissrc);
613 playsink->audio_tee_vissrc = NULL;
616 if (playsink->audio_tee_asrc) {
617 gst_element_release_request_pad (playsink->audio_tee,
618 playsink->audio_tee_asrc);
619 gst_object_unref (playsink->audio_tee_asrc);
620 playsink->audio_tee_asrc = NULL;
623 g_free (playsink->font_desc);
624 playsink->font_desc = NULL;
626 g_free (playsink->subtitle_encoding);
627 playsink->subtitle_encoding = NULL;
629 playsink->stream_synchronizer = NULL;
631 G_OBJECT_CLASS (gst_play_sink_parent_class)->dispose (object);
635 gst_play_sink_finalize (GObject * object)
637 GstPlaySink *playsink;
639 playsink = GST_PLAY_SINK (object);
641 g_static_rec_mutex_free (&playsink->lock);
643 G_OBJECT_CLASS (gst_play_sink_parent_class)->finalize (object);
647 gst_play_sink_set_sink (GstPlaySink * playsink, GstPlaySinkType type,
650 GstElement **elem = NULL, *old = NULL;
652 GST_LOG ("Setting sink %" GST_PTR_FORMAT " as sink type %d", sink, type);
654 GST_PLAY_SINK_LOCK (playsink);
656 case GST_PLAY_SINK_TYPE_AUDIO:
657 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
658 elem = &playsink->audio_sink;
660 case GST_PLAY_SINK_TYPE_VIDEO:
661 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
662 elem = &playsink->video_sink;
664 case GST_PLAY_SINK_TYPE_TEXT:
665 elem = &playsink->text_sink;
673 gst_object_ref (sink);
676 GST_PLAY_SINK_UNLOCK (playsink);
680 gst_element_set_state (old, GST_STATE_NULL);
681 gst_object_unref (old);
686 gst_play_sink_get_sink (GstPlaySink * playsink, GstPlaySinkType type)
688 GstElement *result = NULL;
689 GstElement *elem = NULL, *chainp = NULL;
691 GST_PLAY_SINK_LOCK (playsink);
693 case GST_PLAY_SINK_TYPE_AUDIO:
694 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
696 GstPlayAudioChain *chain;
697 if ((chain = (GstPlayAudioChain *) playsink->audiochain))
698 chainp = chain->sink;
699 elem = playsink->audio_sink;
702 case GST_PLAY_SINK_TYPE_VIDEO:
703 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
705 GstPlayVideoChain *chain;
706 if ((chain = (GstPlayVideoChain *) playsink->videochain))
707 chainp = chain->sink;
708 elem = playsink->video_sink;
711 case GST_PLAY_SINK_TYPE_TEXT:
713 GstPlayTextChain *chain;
714 if ((chain = (GstPlayTextChain *) playsink->textchain))
715 chainp = chain->sink;
716 elem = playsink->text_sink;
723 /* we have an active chain with a sink, get the sink */
724 result = gst_object_ref (chainp);
726 /* nothing found, return last configured sink */
727 if (result == NULL && elem)
728 result = gst_object_ref (elem);
729 GST_PLAY_SINK_UNLOCK (playsink);
735 gst_play_sink_vis_unblocked (GstPad * tee_pad, gboolean blocked,
738 GstPlaySink *playsink;
740 playsink = GST_PLAY_SINK (user_data);
741 /* nothing to do here, we need a dummy callback here to make the async call
743 GST_DEBUG_OBJECT (playsink, "vis pad unblocked");
747 gst_play_sink_vis_blocked (GstPad * tee_pad, gboolean blocked,
750 GstPlaySink *playsink;
751 GstPlayVisChain *chain;
753 playsink = GST_PLAY_SINK (user_data);
755 GST_PLAY_SINK_LOCK (playsink);
756 GST_DEBUG_OBJECT (playsink, "vis pad blocked");
757 /* now try to change the plugin in the running vis chain */
758 if (!(chain = (GstPlayVisChain *) playsink->vischain))
761 /* unlink the old plugin and unghost the pad */
762 gst_pad_unlink (chain->blockpad, chain->vissinkpad);
763 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad), NULL);
765 /* set the old plugin to NULL and remove */
766 gst_element_set_state (chain->vis, GST_STATE_NULL);
767 gst_bin_remove (GST_BIN_CAST (chain->chain.bin), chain->vis);
769 /* add new plugin and set state to playing */
770 chain->vis = playsink->visualisation;
771 gst_bin_add (GST_BIN_CAST (chain->chain.bin), chain->vis);
772 gst_element_set_state (chain->vis, GST_STATE_PLAYING);
775 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
776 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
779 gst_pad_link_full (chain->blockpad, chain->vissinkpad,
780 GST_PAD_LINK_CHECK_NOTHING);
781 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad),
785 /* Unblock the pad */
786 gst_pad_set_blocked_async (tee_pad, FALSE, gst_play_sink_vis_unblocked,
788 GST_PLAY_SINK_UNLOCK (playsink);
792 gst_play_sink_set_vis_plugin (GstPlaySink * playsink, GstElement * vis)
794 GstPlayVisChain *chain;
796 /* setting NULL means creating the default vis plugin */
798 vis = gst_element_factory_make ("goom", "vis");
800 /* simply return if we don't have a vis plugin here */
804 GST_PLAY_SINK_LOCK (playsink);
805 /* first store the new vis */
806 if (playsink->visualisation)
807 gst_object_unref (playsink->visualisation);
809 gst_object_ref_sink (vis);
810 playsink->visualisation = vis;
812 /* now try to change the plugin in the running vis chain, if we have no chain,
813 * we don't bother, any future vis chain will be created with the new vis
815 if (!(chain = (GstPlayVisChain *) playsink->vischain))
818 /* block the pad, the next time the callback is called we can change the
819 * visualisation. It's possible that this never happens or that the pad was
820 * already blocked. If the callback never happens, we don't have new data so
821 * we don't need the new vis plugin. If the pad was already blocked, the
822 * function returns FALSE but the previous pad block will do the right thing
824 GST_DEBUG_OBJECT (playsink, "blocking vis pad");
825 gst_pad_set_blocked_async (chain->blockpad, TRUE, gst_play_sink_vis_blocked,
828 GST_PLAY_SINK_UNLOCK (playsink);
834 gst_play_sink_get_vis_plugin (GstPlaySink * playsink)
836 GstElement *result = NULL;
837 GstPlayVisChain *chain;
839 GST_PLAY_SINK_LOCK (playsink);
840 if ((chain = (GstPlayVisChain *) playsink->vischain)) {
841 /* we have an active chain, get the sink */
843 result = gst_object_ref (chain->vis);
845 /* nothing found, return last configured sink */
846 if (result == NULL && playsink->visualisation)
847 result = gst_object_ref (playsink->visualisation);
848 GST_PLAY_SINK_UNLOCK (playsink);
854 gst_play_sink_set_volume (GstPlaySink * playsink, gdouble volume)
856 GstPlayAudioChain *chain;
858 GST_PLAY_SINK_LOCK (playsink);
859 playsink->volume = volume;
860 chain = (GstPlayAudioChain *) playsink->audiochain;
861 if (chain && chain->volume) {
862 GST_LOG_OBJECT (playsink, "elements: volume=%" GST_PTR_FORMAT ", mute=%"
863 GST_PTR_FORMAT "; new volume=%.03f, mute=%d", chain->volume,
864 chain->mute, volume, playsink->mute);
865 /* if there is a mute element or we are not muted, set the volume */
866 if (chain->mute || !playsink->mute)
867 g_object_set (chain->volume, "volume", volume, NULL);
869 GST_LOG_OBJECT (playsink, "no volume element");
870 playsink->volume_changed = TRUE;
872 GST_PLAY_SINK_UNLOCK (playsink);
876 gst_play_sink_get_volume (GstPlaySink * playsink)
879 GstPlayAudioChain *chain;
881 GST_PLAY_SINK_LOCK (playsink);
882 chain = (GstPlayAudioChain *) playsink->audiochain;
883 result = playsink->volume;
884 if (chain && chain->volume) {
885 if (chain->mute || !playsink->mute) {
886 g_object_get (chain->volume, "volume", &result, NULL);
887 playsink->volume = result;
890 GST_PLAY_SINK_UNLOCK (playsink);
896 gst_play_sink_set_mute (GstPlaySink * playsink, gboolean mute)
898 GstPlayAudioChain *chain;
900 GST_PLAY_SINK_LOCK (playsink);
901 playsink->mute = mute;
902 chain = (GstPlayAudioChain *) playsink->audiochain;
905 g_object_set (chain->mute, "mute", mute, NULL);
906 } else if (chain->volume) {
908 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
910 g_object_set (chain->volume, "volume", (gdouble) playsink->volume,
914 playsink->mute_changed = TRUE;
916 GST_PLAY_SINK_UNLOCK (playsink);
920 gst_play_sink_get_mute (GstPlaySink * playsink)
923 GstPlayAudioChain *chain;
925 GST_PLAY_SINK_LOCK (playsink);
926 chain = (GstPlayAudioChain *) playsink->audiochain;
927 if (chain && chain->mute) {
928 g_object_get (chain->mute, "mute", &result, NULL);
929 playsink->mute = result;
931 result = playsink->mute;
933 GST_PLAY_SINK_UNLOCK (playsink);
939 post_missing_element_message (GstPlaySink * playsink, const gchar * name)
943 msg = gst_missing_element_message_new (GST_ELEMENT_CAST (playsink), name);
944 gst_element_post_message (GST_ELEMENT_CAST (playsink), msg);
948 add_chain (GstPlayChain * chain, gboolean add)
950 if (chain->added == add)
954 gst_bin_add (GST_BIN_CAST (chain->playsink), chain->bin);
956 gst_bin_remove (GST_BIN_CAST (chain->playsink), chain->bin);
957 /* we don't want to lose our sink status */
958 GST_OBJECT_FLAG_SET (chain->playsink, GST_ELEMENT_IS_SINK);
967 activate_chain (GstPlayChain * chain, gboolean activate)
971 if (chain->activated == activate)
974 GST_OBJECT_LOCK (chain->playsink);
975 state = GST_STATE_TARGET (chain->playsink);
976 GST_OBJECT_UNLOCK (chain->playsink);
979 gst_element_set_state (chain->bin, state);
981 gst_element_set_state (chain->bin, GST_STATE_NULL);
983 chain->activated = activate;
989 element_is_sink (GstElement * element)
993 GST_OBJECT_LOCK (element);
994 is_sink = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_IS_SINK);
995 GST_OBJECT_UNLOCK (element);
997 GST_DEBUG_OBJECT (element, "is a sink: %s", (is_sink) ? "yes" : "no");
1002 element_has_property (GstElement * element, const gchar * pname, GType type)
1006 pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (element), pname);
1008 if (pspec == NULL) {
1009 GST_DEBUG_OBJECT (element, "no %s property", pname);
1013 if (type == G_TYPE_INVALID || type == pspec->value_type ||
1014 g_type_is_a (pspec->value_type, type)) {
1015 GST_DEBUG_OBJECT (element, "has %s property of type %s", pname,
1016 (type == G_TYPE_INVALID) ? "any type" : g_type_name (type));
1020 GST_WARNING_OBJECT (element, "has %s property, but property is of type %s "
1021 "and we expected it to be of type %s", pname,
1022 g_type_name (pspec->value_type), g_type_name (type));
1029 const gchar *prop_name;
1032 } FindPropertyHelper;
1035 find_property (GstElement * element, FindPropertyHelper * helper)
1037 if (helper->need_sink && !element_is_sink (element)) {
1038 gst_object_unref (element);
1042 if (!element_has_property (element, helper->prop_name, helper->prop_type)) {
1043 gst_object_unref (element);
1047 GST_INFO_OBJECT (element, "found %s with %s property", helper->prop_name,
1048 (helper->need_sink) ? "sink" : "element");
1049 return 0; /* keep it */
1052 /* FIXME: why not move these functions into core? */
1053 /* find a sink in the hierarchy with a property named @name. This function does
1054 * not increase the refcount of the returned object and thus remains valid as
1055 * long as the bin is valid. */
1057 gst_play_sink_find_property_sinks (GstPlaySink * playsink, GstElement * obj,
1058 const gchar * name, GType expected_type)
1060 GstElement *result = NULL;
1063 if (element_has_property (obj, name, expected_type)) {
1065 } else if (GST_IS_BIN (obj)) {
1066 FindPropertyHelper helper = { name, expected_type, TRUE };
1068 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1069 result = gst_iterator_find_custom (it,
1070 (GCompareFunc) find_property, &helper);
1071 gst_iterator_free (it);
1072 /* we don't need the extra ref */
1074 gst_object_unref (result);
1079 /* find an object in the hierarchy with a property named @name */
1081 gst_play_sink_find_property (GstPlaySink * playsink, GstElement * obj,
1082 const gchar * name, GType expected_type)
1084 GstElement *result = NULL;
1087 if (GST_IS_BIN (obj)) {
1088 FindPropertyHelper helper = { name, expected_type, FALSE };
1090 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1091 result = gst_iterator_find_custom (it,
1092 (GCompareFunc) find_property, &helper);
1093 gst_iterator_free (it);
1095 if (element_has_property (obj, name, expected_type)) {
1097 gst_object_ref (obj);
1104 do_async_start (GstPlaySink * playsink)
1106 GstMessage *message;
1108 if (!playsink->need_async_start) {
1109 GST_INFO_OBJECT (playsink, "no async_start needed");
1113 playsink->async_pending = TRUE;
1115 GST_INFO_OBJECT (playsink, "Sending async_start message");
1116 message = gst_message_new_async_start (GST_OBJECT_CAST (playsink), FALSE);
1117 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1118 (playsink), message);
1122 do_async_done (GstPlaySink * playsink)
1124 GstMessage *message;
1126 if (playsink->async_pending) {
1127 GST_INFO_OBJECT (playsink, "Sending async_done message");
1128 message = gst_message_new_async_done (GST_OBJECT_CAST (playsink));
1129 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1130 (playsink), message);
1132 playsink->async_pending = FALSE;
1135 playsink->need_async_start = FALSE;
1138 /* try to change the state of an element. This function returns the element when
1139 * the state change could be performed. When this function returns NULL an error
1140 * occured and the element is unreffed if @unref is TRUE. */
1142 try_element (GstPlaySink * playsink, GstElement * element, gboolean unref)
1144 GstStateChangeReturn ret;
1147 ret = gst_element_set_state (element, GST_STATE_READY);
1148 if (ret == GST_STATE_CHANGE_FAILURE) {
1149 GST_DEBUG_OBJECT (playsink, "failed state change..");
1150 gst_element_set_state (element, GST_STATE_NULL);
1152 gst_object_unref (element);
1159 /* make the element (bin) that contains the elements needed to perform
1160 * video display. Only used for *raw* video streams.
1162 * +------------------------------------------------------------+
1164 * | +-------+ +----------+ +----------+ +---------+ |
1165 * | | queue | |colorspace| |videoscale| |videosink| |
1166 * | +-sink src-sink src-sink src-sink | |
1167 * | | +-------+ +----------+ +----------+ +---------+ |
1169 * +------------------------------------------------------------+
1172 static GstPlayVideoDeinterlaceChain *
1173 gen_video_deinterlace_chain (GstPlaySink * playsink)
1175 GstPlayVideoDeinterlaceChain *chain;
1178 GstElement *head = NULL, *prev = NULL;
1180 chain = g_new0 (GstPlayVideoDeinterlaceChain, 1);
1181 chain->chain.playsink = playsink;
1183 GST_DEBUG_OBJECT (playsink, "making video deinterlace chain %p", chain);
1185 /* create a bin to hold objects, as we create them we add them to this bin so
1186 * that when something goes wrong we only need to unref the bin */
1187 chain->chain.bin = gst_bin_new ("vdbin");
1188 bin = GST_BIN_CAST (chain->chain.bin);
1189 gst_object_ref_sink (bin);
1191 GST_DEBUG_OBJECT (playsink, "creating " COLORSPACE);
1192 chain->conv = gst_element_factory_make (COLORSPACE, "vdconv");
1193 if (chain->conv == NULL) {
1194 post_missing_element_message (playsink, COLORSPACE);
1195 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1196 (_("Missing element '%s' - check your GStreamer installation."),
1197 COLORSPACE), ("video rendering might fail"));
1199 gst_bin_add (bin, chain->conv);
1204 GST_DEBUG_OBJECT (playsink, "creating deinterlace");
1205 chain->deinterlace = gst_element_factory_make ("deinterlace", "deinterlace");
1206 if (chain->deinterlace == NULL) {
1207 post_missing_element_message (playsink, "deinterlace");
1208 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1209 (_("Missing element '%s' - check your GStreamer installation."),
1210 "deinterlace"), ("deinterlacing won't work"));
1212 gst_bin_add (bin, chain->deinterlace);
1214 if (!gst_element_link_pads_full (prev, "src", chain->deinterlace, "sink",
1215 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1218 head = chain->deinterlace;
1220 prev = chain->deinterlace;
1224 pad = gst_element_get_static_pad (head, "sink");
1225 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1226 gst_object_unref (pad);
1228 chain->sinkpad = gst_ghost_pad_new_no_target ("sink", GST_PAD_SINK);
1232 pad = gst_element_get_static_pad (prev, "src");
1233 chain->srcpad = gst_ghost_pad_new ("src", pad);
1234 gst_object_unref (pad);
1236 chain->srcpad = gst_ghost_pad_new ("src", chain->sinkpad);
1239 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1240 gst_element_add_pad (chain->chain.bin, chain->srcpad);
1246 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1247 (NULL), ("Failed to configure the video deinterlace chain."));
1248 free_chain ((GstPlayChain *) chain);
1254 is_valid_color_balance_element (GstElement * element)
1256 GstColorBalance *bal = GST_COLOR_BALANCE (element);
1257 gboolean have_brightness = FALSE;
1258 gboolean have_contrast = FALSE;
1259 gboolean have_hue = FALSE;
1260 gboolean have_saturation = FALSE;
1261 const GList *channels, *l;
1263 channels = gst_color_balance_list_channels (bal);
1264 for (l = channels; l; l = l->next) {
1265 GstColorBalanceChannel *ch = l->data;
1267 if (g_strrstr (ch->label, "BRIGHTNESS"))
1268 have_brightness = TRUE;
1269 else if (g_strrstr (ch->label, "CONTRAST"))
1270 have_contrast = TRUE;
1271 else if (g_strrstr (ch->label, "HUE"))
1273 else if (g_strrstr (ch->label, "SATURATION"))
1274 have_saturation = TRUE;
1277 return have_brightness && have_contrast && have_hue && have_saturation;
1281 iterate_color_balance_elements (gpointer data, gpointer user_data)
1283 gboolean valid = is_valid_color_balance_element (data);
1284 gboolean *valid_out = user_data;
1286 *valid_out = *valid_out && valid;
1288 gst_object_unref (data);
1292 has_color_balance_element (GstElement * element)
1295 gboolean valid = FALSE;
1297 if (GST_IS_COLOR_BALANCE (element))
1298 return is_valid_color_balance_element (element);
1299 else if (!GST_IS_BIN (element))
1302 it = gst_bin_iterate_all_by_interface (GST_BIN (element),
1303 GST_TYPE_COLOR_BALANCE);
1304 while (gst_iterator_foreach (it, iterate_color_balance_elements,
1305 &valid) == GST_ITERATOR_RESYNC)
1306 gst_iterator_resync (it);
1307 gst_iterator_free (it);
1312 /* make the element (bin) that contains the elements needed to perform
1315 * +------------------------------------------------------------+
1317 * | +-------+ +----------+ +----------+ +---------+ |
1318 * | | queue | |colorspace| |videoscale| |videosink| |
1319 * | +-sink src-sink src-sink src-sink | |
1320 * | | +-------+ +----------+ +----------+ +---------+ |
1322 * +------------------------------------------------------------+
1325 static GstPlayVideoChain *
1326 gen_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1328 GstPlayVideoChain *chain;
1331 GstElement *head = NULL, *prev = NULL, *elem = NULL;
1333 chain = g_new0 (GstPlayVideoChain, 1);
1334 chain->chain.playsink = playsink;
1335 chain->chain.raw = raw;
1337 GST_DEBUG_OBJECT (playsink, "making video chain %p", chain);
1339 if (playsink->video_sink) {
1340 GST_DEBUG_OBJECT (playsink, "trying configured videosink");
1341 chain->sink = try_element (playsink, playsink->video_sink, FALSE);
1343 /* only try fallback if no specific sink was chosen */
1344 if (chain->sink == NULL) {
1345 GST_DEBUG_OBJECT (playsink, "trying autovideosink");
1346 elem = gst_element_factory_make ("autovideosink", "videosink");
1347 chain->sink = try_element (playsink, elem, TRUE);
1349 if (chain->sink == NULL) {
1350 /* if default sink from config.h is different then try it too */
1351 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1352 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_VIDEOSINK);
1353 elem = gst_element_factory_make (DEFAULT_VIDEOSINK, "videosink");
1354 chain->sink = try_element (playsink, elem, TRUE);
1358 playsink->video_sink = gst_object_ref (chain->sink);
1360 if (chain->sink == NULL)
1364 /* if we can disable async behaviour of the sink, we can avoid adding a
1365 * queue for the audio chain. */
1367 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1370 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1371 async, GST_ELEMENT_NAME (elem));
1372 g_object_set (elem, "async", async, NULL);
1373 chain->async = async;
1375 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1376 chain->async = TRUE;
1379 /* find ts-offset element */
1380 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1381 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1384 /* create a bin to hold objects, as we create them we add them to this bin so
1385 * that when something goes wrong we only need to unref the bin */
1386 chain->chain.bin = gst_bin_new ("vbin");
1387 bin = GST_BIN_CAST (chain->chain.bin);
1388 gst_object_ref_sink (bin);
1389 gst_bin_add (bin, chain->sink);
1391 /* decouple decoder from sink, this improves playback quite a lot since the
1392 * decoder can continue while the sink blocks for synchronisation. We don't
1393 * need a lot of buffers as this consumes a lot of memory and we don't want
1394 * too little because else we would be context switching too quickly. */
1395 chain->queue = gst_element_factory_make ("queue", "vqueue");
1396 if (chain->queue == NULL) {
1397 post_missing_element_message (playsink, "queue");
1398 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1399 (_("Missing element '%s' - check your GStreamer installation."),
1400 "queue"), ("video rendering might be suboptimal"));
1404 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1405 "max-size-bytes", 0, "max-size-time", (gint64) 0, "silent", TRUE, NULL);
1406 gst_bin_add (bin, chain->queue);
1407 head = prev = chain->queue;
1410 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)
1411 || (!has_color_balance_element (chain->sink)
1412 && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE))) {
1413 gboolean use_converters = !(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO);
1414 gboolean use_balance = !has_color_balance_element (chain->sink)
1415 && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE);
1417 GST_DEBUG_OBJECT (playsink, "creating videoconverter");
1419 g_object_new (GST_TYPE_PLAY_SINK_VIDEO_CONVERT, "name", "vconv",
1420 "use-converters", use_converters, "use-balance", use_balance, NULL);
1421 gst_bin_add (bin, chain->conv);
1423 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
1424 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1433 GST_DEBUG_OBJECT (playsink, "linking to sink");
1434 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
1435 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1439 pad = gst_element_get_static_pad (head, "sink");
1440 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1441 gst_object_unref (pad);
1443 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1450 if (!elem && !playsink->video_sink) {
1451 post_missing_element_message (playsink, "autovideosink");
1452 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1453 post_missing_element_message (playsink, DEFAULT_VIDEOSINK);
1454 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1455 (_("Both autovideosink and %s elements are missing."),
1456 DEFAULT_VIDEOSINK), (NULL));
1458 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1459 (_("The autovideosink element is missing.")), (NULL));
1462 if (playsink->video_sink) {
1463 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1464 (_("Configured videosink %s is not working."),
1465 GST_ELEMENT_NAME (playsink->video_sink)), (NULL));
1466 } else if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1467 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1468 (_("Both autovideosink and %s elements are not working."),
1469 DEFAULT_VIDEOSINK), (NULL));
1471 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1472 (_("The autovideosink element is not working.")), (NULL));
1475 free_chain ((GstPlayChain *) chain);
1480 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1481 (NULL), ("Failed to configure the video sink."));
1482 /* checking sink made it READY */
1483 gst_element_set_state (chain->sink, GST_STATE_NULL);
1484 /* Remove chain from the bin to allow reuse later */
1485 gst_bin_remove (bin, chain->sink);
1486 free_chain ((GstPlayChain *) chain);
1492 setup_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1495 GstPlayVideoChain *chain;
1496 GstStateChangeReturn ret;
1498 chain = playsink->videochain;
1500 chain->chain.raw = raw;
1502 /* if the chain was active we don't do anything */
1503 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
1506 /* try to set the sink element to READY again */
1507 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
1508 if (ret == GST_STATE_CHANGE_FAILURE)
1511 /* find ts-offset element */
1513 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1514 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1517 /* if we can disable async behaviour of the sink, we can avoid adding a
1518 * queue for the audio chain. */
1520 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1523 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1524 async, GST_ELEMENT_NAME (elem));
1525 g_object_set (elem, "async", async, NULL);
1526 chain->async = async;
1528 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1529 chain->async = TRUE;
1533 g_object_set (chain->conv, "use-balance",
1534 !has_color_balance_element (chain->sink)
1535 && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE), NULL);
1540 /* make an element for playback of video with subtitles embedded.
1541 * Only used for *raw* video streams.
1543 * +--------------------------------------------+
1545 * | +--------+ +-----------------+ |
1546 * | | queue | | subtitleoverlay | |
1547 * video--src sink---video_sink | |
1548 * | +--------+ | src--src
1549 * text------------------text_sink | |
1550 * | +-----------------+ |
1551 * +--------------------------------------------+
1554 static GstPlayTextChain *
1555 gen_text_chain (GstPlaySink * playsink)
1557 GstPlayTextChain *chain;
1560 GstPad *videosinkpad, *textsinkpad, *srcpad;
1562 chain = g_new0 (GstPlayTextChain, 1);
1563 chain->chain.playsink = playsink;
1565 GST_DEBUG_OBJECT (playsink, "making text chain %p", chain);
1567 chain->chain.bin = gst_bin_new ("tbin");
1568 bin = GST_BIN_CAST (chain->chain.bin);
1569 gst_object_ref_sink (bin);
1571 videosinkpad = textsinkpad = srcpad = NULL;
1573 /* first try to hook the text pad to the custom sink */
1574 if (playsink->text_sink) {
1575 GST_DEBUG_OBJECT (playsink, "trying configured textsink");
1576 chain->sink = try_element (playsink, playsink->text_sink, FALSE);
1579 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1582 /* make sure the sparse subtitles don't participate in the preroll */
1583 g_object_set (elem, "async", FALSE, NULL);
1584 GST_DEBUG_OBJECT (playsink, "adding custom text sink");
1585 gst_bin_add (bin, chain->sink);
1586 /* NOTE streamsynchronizer needs streams decoupled */
1587 /* make a little queue */
1588 chain->queue = gst_element_factory_make ("queue", "subqueue");
1589 if (chain->queue == NULL) {
1590 post_missing_element_message (playsink, "queue");
1591 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1592 (_("Missing element '%s' - check your GStreamer installation."),
1593 "queue"), ("rendering might be suboptimal"));
1595 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1596 "max-size-bytes", 0, "max-size-time", (gint64) 0,
1597 "silent", TRUE, NULL);
1598 gst_bin_add (bin, chain->queue);
1600 /* we have a custom sink, this will be our textsinkpad */
1601 if (gst_element_link_pads_full (chain->queue, "src", chain->sink,
1602 "sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
1603 /* we're all fine now and we can add the sink to the chain */
1604 GST_DEBUG_OBJECT (playsink, "using custom text sink");
1605 textsinkpad = gst_element_get_static_pad (chain->queue, "sink");
1607 GST_WARNING_OBJECT (playsink,
1608 "can't find a sink pad on custom text sink");
1609 gst_bin_remove (bin, chain->sink);
1610 gst_bin_remove (bin, chain->queue);
1612 chain->queue = NULL;
1614 /* try to set sync to true but it's no biggie when we can't */
1615 if (chain->sink && (elem =
1616 gst_play_sink_find_property_sinks (playsink, chain->sink,
1617 "sync", G_TYPE_BOOLEAN)))
1618 g_object_set (elem, "sync", TRUE, NULL);
1621 gst_bin_remove (bin, chain->sink);
1623 GST_WARNING_OBJECT (playsink,
1624 "can't find async property in custom text sink");
1627 if (textsinkpad == NULL) {
1628 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1629 (_("Custom text sink element is not usable.")),
1630 ("fallback to default textoverlay"));
1634 if (textsinkpad == NULL) {
1635 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
1636 /* make a little queue */
1637 chain->queue = gst_element_factory_make ("queue", "vqueue");
1638 if (chain->queue == NULL) {
1639 post_missing_element_message (playsink, "queue");
1640 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1641 (_("Missing element '%s' - check your GStreamer installation."),
1642 "queue"), ("video rendering might be suboptimal"));
1644 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1645 "max-size-bytes", 0, "max-size-time", (gint64) 0,
1646 "silent", TRUE, NULL);
1647 gst_bin_add (bin, chain->queue);
1648 videosinkpad = gst_element_get_static_pad (chain->queue, "sink");
1652 gst_element_factory_make ("subtitleoverlay", "suboverlay");
1653 if (chain->overlay == NULL) {
1654 post_missing_element_message (playsink, "subtitleoverlay");
1655 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1656 (_("Missing element '%s' - check your GStreamer installation."),
1657 "subtitleoverlay"), ("subtitle rendering disabled"));
1659 GstElement *element;
1661 gst_bin_add (bin, chain->overlay);
1663 g_object_set (G_OBJECT (chain->overlay), "silent", FALSE, NULL);
1664 if (playsink->font_desc) {
1665 g_object_set (G_OBJECT (chain->overlay), "font-desc",
1666 playsink->font_desc, NULL);
1668 if (playsink->subtitle_encoding) {
1669 g_object_set (G_OBJECT (chain->overlay), "subtitle-encoding",
1670 playsink->subtitle_encoding, NULL);
1673 gst_element_link_pads_full (chain->queue, "src", chain->overlay,
1674 "video_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS);
1676 /* make another little queue to decouple streams */
1677 element = gst_element_factory_make ("queue", "subqueue");
1678 if (element == NULL) {
1679 post_missing_element_message (playsink, "queue");
1680 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1681 (_("Missing element '%s' - check your GStreamer installation."),
1682 "queue"), ("rendering might be suboptimal"));
1684 g_object_set (G_OBJECT (element), "max-size-buffers", 3,
1685 "max-size-bytes", 0, "max-size-time", (gint64) 0,
1686 "silent", TRUE, NULL);
1687 gst_bin_add (bin, element);
1688 if (gst_element_link_pads_full (element, "src", chain->overlay,
1689 "subtitle_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
1690 textsinkpad = gst_element_get_static_pad (element, "sink");
1691 srcpad = gst_element_get_static_pad (chain->overlay, "src");
1693 gst_bin_remove (bin, chain->sink);
1694 gst_bin_remove (bin, chain->overlay);
1696 chain->overlay = NULL;
1697 gst_object_unref (videosinkpad);
1698 videosinkpad = NULL;
1705 if (videosinkpad == NULL) {
1706 /* if we still don't have a videosink, we don't have an overlay. the only
1707 * thing we can do is insert an identity and ghost the src
1709 chain->identity = gst_element_factory_make ("identity", "tidentity");
1710 if (chain->identity == NULL) {
1711 post_missing_element_message (playsink, "identity");
1712 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1713 (_("Missing element '%s' - check your GStreamer installation."),
1714 "identity"), (NULL));
1716 g_object_set (chain->identity, "signal-handoffs", FALSE, NULL);
1717 g_object_set (chain->identity, "silent", TRUE, NULL);
1718 gst_bin_add (bin, chain->identity);
1719 srcpad = gst_element_get_static_pad (chain->identity, "src");
1720 videosinkpad = gst_element_get_static_pad (chain->identity, "sink");
1724 /* expose the ghostpads */
1726 chain->videosinkpad = gst_ghost_pad_new ("sink", videosinkpad);
1727 gst_object_unref (videosinkpad);
1728 gst_element_add_pad (chain->chain.bin, chain->videosinkpad);
1731 chain->textsinkpad = gst_ghost_pad_new ("text_sink", textsinkpad);
1732 gst_object_unref (textsinkpad);
1733 gst_element_add_pad (chain->chain.bin, chain->textsinkpad);
1736 chain->srcpad = gst_ghost_pad_new ("src", srcpad);
1737 gst_object_unref (srcpad);
1738 gst_element_add_pad (chain->chain.bin, chain->srcpad);
1745 notify_volume_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
1749 g_object_get (object, "volume", &vol, NULL);
1750 playsink->volume = vol;
1752 g_object_notify (G_OBJECT (playsink), "volume");
1756 notify_mute_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
1760 g_object_get (object, "mute", &mute, NULL);
1761 playsink->mute = mute;
1763 g_object_notify (G_OBJECT (playsink), "mute");
1766 /* make the chain that contains the elements needed to perform
1769 * We add a tee as the first element so that we can link the visualisation chain
1770 * to it when requested.
1772 * +-------------------------------------------------------------+
1774 * | +---------+ +----------+ +---------+ +---------+ |
1775 * | |audioconv| |audioscale| | volume | |audiosink| |
1776 * | +-srck src-sink src-sink src-sink | |
1777 * | | +---------+ +----------+ +---------+ +---------+ |
1779 * +-------------------------------------------------------------+
1781 static GstPlayAudioChain *
1782 gen_audio_chain (GstPlaySink * playsink, gboolean raw)
1784 GstPlayAudioChain *chain;
1786 gboolean have_volume;
1788 GstElement *head, *prev, *elem = NULL;
1790 chain = g_new0 (GstPlayAudioChain, 1);
1791 chain->chain.playsink = playsink;
1792 chain->chain.raw = raw;
1794 GST_DEBUG_OBJECT (playsink, "making audio chain %p", chain);
1796 if (playsink->audio_sink) {
1797 GST_DEBUG_OBJECT (playsink, "trying configured audiosink %" GST_PTR_FORMAT,
1798 playsink->audio_sink);
1799 chain->sink = try_element (playsink, playsink->audio_sink, FALSE);
1801 /* only try fallback if no specific sink was chosen */
1802 if (chain->sink == NULL) {
1803 GST_DEBUG_OBJECT (playsink, "trying autoaudiosink");
1804 elem = gst_element_factory_make ("autoaudiosink", "audiosink");
1805 chain->sink = try_element (playsink, elem, TRUE);
1807 if (chain->sink == NULL) {
1808 /* if default sink from config.h is different then try it too */
1809 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1810 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_AUDIOSINK);
1811 elem = gst_element_factory_make (DEFAULT_AUDIOSINK, "audiosink");
1812 chain->sink = try_element (playsink, elem, TRUE);
1816 playsink->audio_sink = gst_object_ref (chain->sink);
1818 if (chain->sink == NULL)
1821 chain->chain.bin = gst_bin_new ("abin");
1822 bin = GST_BIN_CAST (chain->chain.bin);
1823 gst_object_ref_sink (bin);
1824 gst_bin_add (bin, chain->sink);
1826 /* we have to add a queue when we need to decouple for the video sink in
1827 * visualisations and for streamsynchronizer */
1828 GST_DEBUG_OBJECT (playsink, "adding audio queue");
1829 chain->queue = gst_element_factory_make ("queue", "aqueue");
1830 if (chain->queue == NULL) {
1831 post_missing_element_message (playsink, "queue");
1832 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1833 (_("Missing element '%s' - check your GStreamer installation."),
1834 "queue"), ("audio playback and visualizations might not work"));
1838 g_object_set (chain->queue, "silent", TRUE, NULL);
1839 gst_bin_add (bin, chain->queue);
1840 prev = head = chain->queue;
1843 /* find ts-offset element */
1844 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1845 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1848 /* check if the sink, or something within the sink, has the volume property.
1849 * If it does we don't need to add a volume element. */
1851 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
1854 chain->volume = elem;
1856 g_signal_connect (chain->volume, "notify::volume",
1857 G_CALLBACK (notify_volume_cb), playsink);
1859 GST_DEBUG_OBJECT (playsink, "the sink has a volume property");
1861 chain->sink_volume = TRUE;
1862 /* if the sink also has a mute property we can use this as well. We'll only
1863 * use the mute property if there is a volume property. We can simulate the
1864 * mute with the volume otherwise. */
1866 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
1869 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
1870 g_signal_connect (chain->mute, "notify::mute",
1871 G_CALLBACK (notify_mute_cb), playsink);
1873 /* use the sink to control the volume and mute */
1874 if (playsink->volume_changed) {
1875 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
1876 playsink->volume_changed = FALSE;
1878 if (playsink->mute_changed) {
1880 g_object_set (chain->mute, "mute", playsink->mute, NULL);
1883 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
1885 playsink->mute_changed = FALSE;
1888 /* no volume, we need to add a volume element when we can */
1889 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
1890 have_volume = FALSE;
1891 chain->sink_volume = FALSE;
1894 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO) || (!have_volume
1895 && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME))) {
1896 gboolean use_converters = !(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO);
1897 gboolean use_volume =
1898 !have_volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME);
1899 GST_DEBUG_OBJECT (playsink,
1900 "creating audioconvert with use-converters %d, use-volume %d",
1901 use_converters, use_volume);
1903 g_object_new (GST_TYPE_PLAY_SINK_AUDIO_CONVERT, "name", "aconv",
1904 "use-converters", use_converters, "use-volume", use_volume, NULL);
1905 gst_bin_add (bin, chain->conv);
1907 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
1908 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1915 if (!have_volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
1916 GstPlaySinkAudioConvert *conv =
1917 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
1920 chain->volume = conv->volume;
1923 g_signal_connect (chain->volume, "notify::volume",
1924 G_CALLBACK (notify_volume_cb), playsink);
1926 /* volume also has the mute property */
1927 chain->mute = chain->volume;
1928 g_signal_connect (chain->mute, "notify::mute",
1929 G_CALLBACK (notify_mute_cb), playsink);
1931 /* configure with the latest volume and mute */
1932 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume,
1934 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
1940 /* we only have to link to the previous element if we have something in
1941 * front of the sink */
1942 GST_DEBUG_OBJECT (playsink, "linking to sink");
1943 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
1944 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1948 /* post a warning if we have no way to configure the volume */
1950 GST_ELEMENT_WARNING (playsink, STREAM, NOT_IMPLEMENTED,
1951 (_("No volume control found")), ("Volume/mute is not available"));
1954 /* and ghost the sinkpad of the headmost element */
1955 GST_DEBUG_OBJECT (playsink, "ghosting sink pad");
1956 pad = gst_element_get_static_pad (head, "sink");
1957 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1958 gst_object_unref (pad);
1959 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1966 if (!elem && !playsink->audio_sink) {
1967 post_missing_element_message (playsink, "autoaudiosink");
1968 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1969 post_missing_element_message (playsink, DEFAULT_AUDIOSINK);
1970 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1971 (_("Both autoaudiosink and %s elements are missing."),
1972 DEFAULT_AUDIOSINK), (NULL));
1974 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1975 (_("The autoaudiosink element is missing.")), (NULL));
1978 if (playsink->audio_sink) {
1979 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1980 (_("Configured audiosink %s is not working."),
1981 GST_ELEMENT_NAME (playsink->audio_sink)), (NULL));
1982 } else if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1983 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1984 (_("Both autoaudiosink and %s elements are not working."),
1985 DEFAULT_AUDIOSINK), (NULL));
1987 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1988 (_("The autoaudiosink element is not working.")), (NULL));
1991 free_chain ((GstPlayChain *) chain);
1996 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1997 (NULL), ("Failed to configure the audio sink."));
1998 /* checking sink made it READY */
1999 gst_element_set_state (chain->sink, GST_STATE_NULL);
2000 /* Remove chain from the bin to allow reuse later */
2001 gst_bin_remove (bin, chain->sink);
2002 free_chain ((GstPlayChain *) chain);
2008 setup_audio_chain (GstPlaySink * playsink, gboolean raw)
2011 GstPlayAudioChain *chain;
2012 GstStateChangeReturn ret;
2014 chain = playsink->audiochain;
2016 chain->chain.raw = raw;
2018 /* if the chain was active we don't do anything */
2019 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
2022 /* try to set the sink element to READY again */
2023 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
2024 if (ret == GST_STATE_CHANGE_FAILURE)
2027 /* find ts-offset element */
2028 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
2029 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
2032 /* check if the sink, or something within the sink, has the volume property.
2033 * If it does we don't need to add a volume element. */
2035 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
2038 chain->volume = elem;
2040 if (playsink->volume_changed) {
2041 GST_DEBUG_OBJECT (playsink, "the sink has a volume property, setting %f",
2043 /* use the sink to control the volume */
2044 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2045 playsink->volume_changed = FALSE;
2048 g_signal_connect (chain->volume, "notify::volume",
2049 G_CALLBACK (notify_volume_cb), playsink);
2050 /* if the sink also has a mute property we can use this as well. We'll only
2051 * use the mute property if there is a volume property. We can simulate the
2052 * mute with the volume otherwise. */
2054 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
2057 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
2058 g_signal_connect (chain->mute, "notify::mute",
2059 G_CALLBACK (notify_mute_cb), playsink);
2062 g_object_set (chain->conv, "use-volume", FALSE, NULL);
2064 GstPlaySinkAudioConvert *conv =
2065 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
2067 /* no volume, we need to add a volume element when we can */
2068 g_object_set (chain->conv, "use-volume",
2069 ! !(playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME), NULL);
2070 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
2072 /* Disconnect signals */
2073 disconnect_chain (chain, playsink);
2075 if (conv->volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
2076 chain->volume = conv->volume;
2077 chain->mute = chain->volume;
2079 g_signal_connect (chain->volume, "notify::volume",
2080 G_CALLBACK (notify_volume_cb), playsink);
2082 g_signal_connect (chain->mute, "notify::mute",
2083 G_CALLBACK (notify_mute_cb), playsink);
2085 /* configure with the latest volume and mute */
2086 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2087 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
2090 GST_DEBUG_OBJECT (playsink, "reusing existing volume element");
2096 * +-------------------------------------------------------------------+
2098 * | +----------+ +------------+ +----------+ +-------+ |
2099 * | | visqueue | | audioconv | | audiores | | vis | |
2100 * | +-sink src-sink + samp src-sink src-sink src-+ |
2101 * | | +----------+ +------------+ +----------+ +-------+ | |
2103 * +-------------------------------------------------------------------+
2106 static GstPlayVisChain *
2107 gen_vis_chain (GstPlaySink * playsink)
2109 GstPlayVisChain *chain;
2115 chain = g_new0 (GstPlayVisChain, 1);
2116 chain->chain.playsink = playsink;
2118 GST_DEBUG_OBJECT (playsink, "making vis chain %p", chain);
2120 chain->chain.bin = gst_bin_new ("visbin");
2121 bin = GST_BIN_CAST (chain->chain.bin);
2122 gst_object_ref_sink (bin);
2124 /* we're queuing raw audio here, we can remove this queue when we can disable
2125 * async behaviour in the video sink. */
2126 chain->queue = gst_element_factory_make ("queue", "visqueue");
2127 if (chain->queue == NULL)
2129 g_object_set (chain->queue, "silent", TRUE, NULL);
2130 gst_bin_add (bin, chain->queue);
2132 chain->conv = gst_element_factory_make ("audioconvert", "aconv");
2133 if (chain->conv == NULL)
2134 goto no_audioconvert;
2135 gst_bin_add (bin, chain->conv);
2137 chain->resample = gst_element_factory_make ("audioresample", "aresample");
2138 if (chain->resample == NULL)
2139 goto no_audioresample;
2140 gst_bin_add (bin, chain->resample);
2142 /* this pad will be used for blocking the dataflow and switching the vis
2144 chain->blockpad = gst_element_get_static_pad (chain->resample, "src");
2146 if (playsink->visualisation) {
2147 GST_DEBUG_OBJECT (playsink, "trying configure vis");
2148 chain->vis = try_element (playsink, playsink->visualisation, FALSE);
2150 if (chain->vis == NULL) {
2151 GST_DEBUG_OBJECT (playsink, "trying goom");
2152 elem = gst_element_factory_make ("goom", "vis");
2153 chain->vis = try_element (playsink, elem, TRUE);
2155 if (chain->vis == NULL)
2158 gst_bin_add (bin, chain->vis);
2160 res = gst_element_link_pads_full (chain->queue, "src", chain->conv, "sink",
2161 GST_PAD_LINK_CHECK_NOTHING);
2163 gst_element_link_pads_full (chain->conv, "src", chain->resample, "sink",
2164 GST_PAD_LINK_CHECK_NOTHING);
2166 gst_element_link_pads_full (chain->resample, "src", chain->vis, "sink",
2167 GST_PAD_LINK_CHECK_NOTHING);
2171 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
2172 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
2174 pad = gst_element_get_static_pad (chain->queue, "sink");
2175 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
2176 gst_object_unref (pad);
2177 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
2179 chain->srcpad = gst_ghost_pad_new ("src", chain->vissrcpad);
2180 gst_element_add_pad (chain->chain.bin, chain->srcpad);
2187 post_missing_element_message (playsink, "queue");
2188 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2189 (_("Missing element '%s' - check your GStreamer installation."),
2191 free_chain ((GstPlayChain *) chain);
2196 post_missing_element_message (playsink, "audioconvert");
2197 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2198 (_("Missing element '%s' - check your GStreamer installation."),
2199 "audioconvert"), ("possibly a liboil version mismatch?"));
2200 free_chain ((GstPlayChain *) chain);
2205 post_missing_element_message (playsink, "audioresample");
2206 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2207 (_("Missing element '%s' - check your GStreamer installation."),
2208 "audioresample"), (NULL));
2209 free_chain ((GstPlayChain *) chain);
2214 post_missing_element_message (playsink, "goom");
2215 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2216 (_("Missing element '%s' - check your GStreamer installation."),
2218 free_chain ((GstPlayChain *) chain);
2223 GST_ELEMENT_ERROR (playsink, CORE, PAD,
2224 (NULL), ("Failed to configure the visualisation element."));
2225 /* element made it to READY */
2226 gst_element_set_state (chain->vis, GST_STATE_NULL);
2227 free_chain ((GstPlayChain *) chain);
2232 /* this function is called when all the request pads are requested and when we
2233 * have to construct the final pipeline. Based on the flags we construct the
2234 * final output pipelines.
2237 gst_play_sink_reconfigure (GstPlaySink * playsink)
2240 gboolean need_audio, need_video, need_deinterlace, need_vis, need_text;
2242 GST_DEBUG_OBJECT (playsink, "reconfiguring");
2244 /* assume we need nothing */
2245 need_audio = need_video = need_deinterlace = need_vis = need_text = FALSE;
2247 GST_PLAY_SINK_LOCK (playsink);
2248 GST_OBJECT_LOCK (playsink);
2249 /* get flags, there are protected with the object lock */
2250 flags = playsink->flags;
2251 GST_OBJECT_UNLOCK (playsink);
2253 /* figure out which components we need */
2254 if (flags & GST_PLAY_FLAG_TEXT && playsink->text_pad) {
2255 /* we have subtitles and we are requested to show it */
2259 if (((flags & GST_PLAY_FLAG_VIDEO)
2260 || (flags & GST_PLAY_FLAG_NATIVE_VIDEO)) && playsink->video_pad) {
2261 /* we have video and we are requested to show it */
2264 /* we only deinterlace if native video is not requested and
2265 * we have raw video */
2266 if ((flags & GST_PLAY_FLAG_DEINTERLACE)
2267 && !(flags & GST_PLAY_FLAG_NATIVE_VIDEO) && playsink->video_pad_raw)
2268 need_deinterlace = TRUE;
2271 if (playsink->audio_pad) {
2272 if ((flags & GST_PLAY_FLAG_AUDIO) || (flags & GST_PLAY_FLAG_NATIVE_AUDIO)) {
2275 if (playsink->audio_pad_raw) {
2276 /* only can do vis with raw uncompressed audio */
2277 if (flags & GST_PLAY_FLAG_VIS && !need_video) {
2278 /* also add video when we add visualisation */
2285 /* we have a text_pad and we need text rendering, in this case we need a
2286 * video_pad to combine the video with the text or visualizations */
2287 if (need_text && !need_video) {
2288 if (playsink->video_pad) {
2290 } else if (need_audio) {
2291 GST_ELEMENT_WARNING (playsink, STREAM, FORMAT,
2292 (_("Can't play a text file without video or visualizations.")),
2293 ("Have text pad but no video pad or visualizations"));
2296 GST_ELEMENT_ERROR (playsink, STREAM, FORMAT,
2297 (_("Can't play a text file without video or visualizations.")),
2298 ("Have text pad but no video pad or visualizations"));
2299 GST_PLAY_SINK_UNLOCK (playsink);
2304 GST_DEBUG_OBJECT (playsink, "audio:%d, video:%d, vis:%d, text:%d", need_audio,
2305 need_video, need_vis, need_text);
2307 /* set up video pipeline */
2309 gboolean raw, async;
2311 /* we need a raw sink when we do vis or when we have a raw pad */
2312 raw = need_vis ? TRUE : playsink->video_pad_raw;
2313 /* we try to set the sink async=FALSE when we need vis, this way we can
2314 * avoid a queue in the audio chain. */
2317 GST_DEBUG_OBJECT (playsink, "adding video, raw %d",
2318 playsink->video_pad_raw);
2320 if (playsink->videochain) {
2321 /* try to reactivate the chain */
2322 if (!setup_video_chain (playsink, raw, async)) {
2323 if (playsink->video_sinkpad_stream_synchronizer) {
2324 gst_element_release_request_pad (GST_ELEMENT_CAST
2325 (playsink->stream_synchronizer),
2326 playsink->video_sinkpad_stream_synchronizer);
2327 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2328 playsink->video_sinkpad_stream_synchronizer = NULL;
2329 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2330 playsink->video_srcpad_stream_synchronizer = NULL;
2333 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2335 /* Remove the sink from the bin to keep its state
2336 * and unparent it to allow reuse */
2337 if (playsink->videochain->sink)
2338 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
2339 playsink->videochain->sink);
2341 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2342 free_chain ((GstPlayChain *) playsink->videochain);
2343 playsink->videochain = NULL;
2347 if (!playsink->videochain)
2348 playsink->videochain = gen_video_chain (playsink, raw, async);
2349 if (!playsink->videochain)
2352 if (!playsink->video_sinkpad_stream_synchronizer) {
2355 playsink->video_sinkpad_stream_synchronizer =
2356 gst_element_get_request_pad (GST_ELEMENT_CAST
2357 (playsink->stream_synchronizer), "sink_%d");
2358 it = gst_pad_iterate_internal_links
2359 (playsink->video_sinkpad_stream_synchronizer);
2361 gst_iterator_next (it,
2362 (gpointer *) & playsink->video_srcpad_stream_synchronizer);
2363 g_assert (playsink->video_srcpad_stream_synchronizer);
2364 gst_iterator_free (it);
2367 if (playsink->video_pad)
2368 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad),
2369 playsink->video_sinkpad_stream_synchronizer);
2371 if (need_deinterlace) {
2372 if (!playsink->videodeinterlacechain)
2373 playsink->videodeinterlacechain =
2374 gen_video_deinterlace_chain (playsink);
2375 if (!playsink->videodeinterlacechain)
2378 GST_DEBUG_OBJECT (playsink, "adding video deinterlace chain");
2380 GST_DEBUG_OBJECT (playsink, "setting up deinterlacing chain");
2382 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
2383 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
2385 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2386 playsink->videodeinterlacechain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2388 if (playsink->videodeinterlacechain) {
2389 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2390 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
2395 GST_DEBUG_OBJECT (playsink, "adding video chain");
2396 add_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
2397 activate_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
2398 /* if we are not part of vis or subtitles, set the ghostpad target */
2399 if (!need_vis && !need_text && (!playsink->textchain
2400 || !playsink->text_pad)) {
2401 GST_DEBUG_OBJECT (playsink, "ghosting video sinkpad");
2402 if (need_deinterlace)
2403 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
2404 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2406 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2407 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2410 GST_DEBUG_OBJECT (playsink, "no video needed");
2411 if (playsink->videochain) {
2412 GST_DEBUG_OBJECT (playsink, "removing video chain");
2413 if (playsink->vischain) {
2416 GST_DEBUG_OBJECT (playsink, "unlinking vis chain");
2418 /* also had visualisation, release the tee srcpad before we then
2419 * unlink the video from it */
2420 if (playsink->audio_tee_vissrc) {
2421 gst_element_release_request_pad (playsink->audio_tee,
2422 playsink->audio_tee_vissrc);
2423 gst_object_unref (playsink->audio_tee_vissrc);
2424 playsink->audio_tee_vissrc = NULL;
2427 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2428 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
2431 if (playsink->video_sinkpad_stream_synchronizer) {
2432 gst_element_release_request_pad (GST_ELEMENT_CAST
2433 (playsink->stream_synchronizer),
2434 playsink->video_sinkpad_stream_synchronizer);
2435 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2436 playsink->video_sinkpad_stream_synchronizer = NULL;
2437 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2438 playsink->video_srcpad_stream_synchronizer = NULL;
2441 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2442 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2443 if (playsink->videochain->ts_offset)
2444 gst_object_unref (playsink->videochain->ts_offset);
2445 playsink->videochain->ts_offset = NULL;
2448 if (playsink->videodeinterlacechain) {
2449 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2450 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2453 if (playsink->video_pad)
2454 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
2460 GST_DEBUG_OBJECT (playsink, "adding audio");
2462 /* get a raw sink if we are asked for a raw pad */
2463 raw = playsink->audio_pad_raw;
2465 if (playsink->audiochain) {
2466 /* try to reactivate the chain */
2467 if (!setup_audio_chain (playsink, raw)) {
2468 GST_DEBUG_OBJECT (playsink, "removing current audio chain");
2469 if (playsink->audio_tee_asrc) {
2470 gst_element_release_request_pad (playsink->audio_tee,
2471 playsink->audio_tee_asrc);
2472 gst_object_unref (playsink->audio_tee_asrc);
2473 playsink->audio_tee_asrc = NULL;
2476 if (playsink->audio_sinkpad_stream_synchronizer) {
2477 gst_element_release_request_pad (GST_ELEMENT_CAST
2478 (playsink->stream_synchronizer),
2479 playsink->audio_sinkpad_stream_synchronizer);
2480 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
2481 playsink->audio_sinkpad_stream_synchronizer = NULL;
2482 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
2483 playsink->audio_srcpad_stream_synchronizer = NULL;
2486 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2488 /* Remove the sink from the bin to keep its state
2489 * and unparent it to allow reuse */
2490 if (playsink->audiochain->sink)
2491 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
2492 playsink->audiochain->sink);
2494 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2495 disconnect_chain (playsink->audiochain, playsink);
2496 playsink->audiochain->volume = NULL;
2497 playsink->audiochain->mute = NULL;
2498 if (playsink->audiochain->ts_offset)
2499 gst_object_unref (playsink->audiochain->ts_offset);
2500 playsink->audiochain->ts_offset = NULL;
2501 free_chain ((GstPlayChain *) playsink->audiochain);
2502 playsink->audiochain = NULL;
2503 playsink->volume_changed = playsink->mute_changed = FALSE;
2507 if (!playsink->audiochain) {
2508 GST_DEBUG_OBJECT (playsink, "creating new audio chain");
2509 playsink->audiochain = gen_audio_chain (playsink, raw);
2512 if (!playsink->audio_sinkpad_stream_synchronizer) {
2515 playsink->audio_sinkpad_stream_synchronizer =
2516 gst_element_get_request_pad (GST_ELEMENT_CAST
2517 (playsink->stream_synchronizer), "sink_%d");
2518 it = gst_pad_iterate_internal_links
2519 (playsink->audio_sinkpad_stream_synchronizer);
2521 gst_iterator_next (it,
2522 (gpointer *) & playsink->audio_srcpad_stream_synchronizer);
2523 g_assert (playsink->audio_srcpad_stream_synchronizer);
2524 gst_iterator_free (it);
2527 if (playsink->audiochain) {
2528 GST_DEBUG_OBJECT (playsink, "adding audio chain");
2529 if (playsink->audio_tee_asrc == NULL) {
2530 playsink->audio_tee_asrc =
2531 gst_element_get_request_pad (playsink->audio_tee, "src%d");
2533 add_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
2534 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
2535 gst_pad_link_full (playsink->audio_tee_asrc,
2536 playsink->audio_sinkpad_stream_synchronizer,
2537 GST_PAD_LINK_CHECK_NOTHING);
2538 gst_pad_link_full (playsink->audio_srcpad_stream_synchronizer,
2539 playsink->audiochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2542 GST_DEBUG_OBJECT (playsink, "no audio needed");
2543 /* we have no audio or we are requested to not play audio */
2544 if (playsink->audiochain) {
2545 GST_DEBUG_OBJECT (playsink, "removing audio chain");
2546 /* release the audio pad */
2547 if (playsink->audio_tee_asrc) {
2548 gst_element_release_request_pad (playsink->audio_tee,
2549 playsink->audio_tee_asrc);
2550 gst_object_unref (playsink->audio_tee_asrc);
2551 playsink->audio_tee_asrc = NULL;
2554 if (playsink->audio_sinkpad_stream_synchronizer) {
2555 gst_element_release_request_pad (GST_ELEMENT_CAST
2556 (playsink->stream_synchronizer),
2557 playsink->audio_sinkpad_stream_synchronizer);
2558 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
2559 playsink->audio_sinkpad_stream_synchronizer = NULL;
2560 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
2561 playsink->audio_srcpad_stream_synchronizer = NULL;
2564 if (playsink->audiochain->sink_volume) {
2565 disconnect_chain (playsink->audiochain, playsink);
2566 playsink->audiochain->volume = NULL;
2567 playsink->audiochain->mute = NULL;
2568 if (playsink->audiochain->ts_offset)
2569 gst_object_unref (playsink->audiochain->ts_offset);
2570 playsink->audiochain->ts_offset = NULL;
2572 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2573 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2580 if (!playsink->vischain)
2581 playsink->vischain = gen_vis_chain (playsink);
2583 GST_DEBUG_OBJECT (playsink, "adding visualisation");
2585 if (playsink->vischain) {
2586 GST_DEBUG_OBJECT (playsink, "setting up vis chain");
2588 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2589 add_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
2590 activate_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
2591 if (playsink->audio_tee_vissrc == NULL) {
2592 playsink->audio_tee_vissrc =
2593 gst_element_get_request_pad (playsink->audio_tee, "src%d");
2595 gst_pad_link_full (playsink->audio_tee_vissrc,
2596 playsink->vischain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2597 gst_pad_link_full (srcpad, playsink->video_sinkpad_stream_synchronizer,
2598 GST_PAD_LINK_CHECK_NOTHING);
2599 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2600 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2601 gst_object_unref (srcpad);
2604 GST_DEBUG_OBJECT (playsink, "no vis needed");
2605 if (playsink->vischain) {
2606 if (playsink->audio_tee_vissrc) {
2607 gst_element_release_request_pad (playsink->audio_tee,
2608 playsink->audio_tee_vissrc);
2609 gst_object_unref (playsink->audio_tee_vissrc);
2610 playsink->audio_tee_vissrc = NULL;
2612 GST_DEBUG_OBJECT (playsink, "removing vis chain");
2613 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
2614 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
2619 GST_DEBUG_OBJECT (playsink, "adding text");
2620 if (!playsink->textchain) {
2621 GST_DEBUG_OBJECT (playsink, "creating text chain");
2622 playsink->textchain = gen_text_chain (playsink);
2624 if (playsink->textchain) {
2627 GST_DEBUG_OBJECT (playsink, "adding text chain");
2628 if (playsink->textchain->overlay)
2629 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", FALSE,
2631 add_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
2633 if (!playsink->text_sinkpad_stream_synchronizer) {
2634 playsink->text_sinkpad_stream_synchronizer =
2635 gst_element_get_request_pad (GST_ELEMENT_CAST
2636 (playsink->stream_synchronizer), "sink_%d");
2637 it = gst_pad_iterate_internal_links
2638 (playsink->text_sinkpad_stream_synchronizer);
2640 gst_iterator_next (it,
2641 (gpointer *) & playsink->text_srcpad_stream_synchronizer);
2642 g_assert (playsink->text_srcpad_stream_synchronizer);
2643 gst_iterator_free (it);
2645 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad),
2646 playsink->text_sinkpad_stream_synchronizer);
2647 gst_pad_link_full (playsink->text_srcpad_stream_synchronizer,
2648 playsink->textchain->textsinkpad, GST_PAD_LINK_CHECK_NOTHING);
2655 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2656 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
2657 gst_pad_link_full (srcpad, playsink->textchain->videosinkpad,
2658 GST_PAD_LINK_CHECK_NOTHING);
2659 gst_object_unref (srcpad);
2661 if (need_deinterlace)
2662 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
2663 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
2665 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2666 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
2668 gst_pad_link_full (playsink->textchain->srcpad,
2669 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2671 activate_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
2674 GST_DEBUG_OBJECT (playsink, "no text needed");
2675 /* we have no subtitles/text or we are requested to not show them */
2677 if (playsink->text_sinkpad_stream_synchronizer) {
2678 gst_element_release_request_pad (GST_ELEMENT_CAST
2679 (playsink->stream_synchronizer),
2680 playsink->text_sinkpad_stream_synchronizer);
2681 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
2682 playsink->text_sinkpad_stream_synchronizer = NULL;
2683 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
2684 playsink->text_srcpad_stream_synchronizer = NULL;
2687 if (playsink->textchain) {
2688 if (playsink->text_pad == NULL) {
2689 /* no text pad, remove the chain entirely */
2690 GST_DEBUG_OBJECT (playsink, "removing text chain");
2691 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
2692 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
2694 /* we have a chain and a textpad, turn the subtitles off */
2695 GST_DEBUG_OBJECT (playsink, "turning off the text");
2696 if (playsink->textchain->overlay)
2697 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", TRUE,
2701 if (!need_video && playsink->video_pad) {
2702 if (playsink->video_sinkpad_stream_synchronizer) {
2703 gst_element_release_request_pad (GST_ELEMENT_CAST
2704 (playsink->stream_synchronizer),
2705 playsink->video_sinkpad_stream_synchronizer);
2706 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2707 playsink->video_sinkpad_stream_synchronizer = NULL;
2708 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2709 playsink->video_srcpad_stream_synchronizer = NULL;
2712 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
2715 if (playsink->text_pad && !playsink->textchain)
2716 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad), NULL);
2718 update_av_offset (playsink);
2719 do_async_done (playsink);
2720 GST_PLAY_SINK_UNLOCK (playsink);
2727 /* gen_ chain already posted error */
2728 GST_DEBUG_OBJECT (playsink, "failed to setup chain");
2729 GST_PLAY_SINK_UNLOCK (playsink);
2735 * gst_play_sink_set_flags:
2736 * @playsink: a #GstPlaySink
2737 * @flags: #GstPlayFlags
2739 * Configure @flags on @playsink. The flags control the behaviour of @playsink
2740 * when constructing the sink pipelins.
2742 * Returns: TRUE if the flags could be configured.
2745 gst_play_sink_set_flags (GstPlaySink * playsink, GstPlayFlags flags)
2747 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), FALSE);
2749 GST_OBJECT_LOCK (playsink);
2750 playsink->flags = flags;
2751 GST_OBJECT_UNLOCK (playsink);
2757 * gst_play_sink_get_flags:
2758 * @playsink: a #GstPlaySink
2760 * Get the flags of @playsink. That flags control the behaviour of the sink when
2761 * it constructs the sink pipelines.
2763 * Returns: the currently configured #GstPlayFlags.
2766 gst_play_sink_get_flags (GstPlaySink * playsink)
2770 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), 0);
2772 GST_OBJECT_LOCK (playsink);
2773 res = playsink->flags;
2774 GST_OBJECT_UNLOCK (playsink);
2780 gst_play_sink_set_font_desc (GstPlaySink * playsink, const gchar * desc)
2782 GstPlayTextChain *chain;
2784 GST_PLAY_SINK_LOCK (playsink);
2785 chain = (GstPlayTextChain *) playsink->textchain;
2786 g_free (playsink->font_desc);
2787 playsink->font_desc = g_strdup (desc);
2788 if (chain && chain->overlay) {
2789 g_object_set (chain->overlay, "font-desc", desc, NULL);
2791 GST_PLAY_SINK_UNLOCK (playsink);
2795 gst_play_sink_get_font_desc (GstPlaySink * playsink)
2797 gchar *result = NULL;
2798 GstPlayTextChain *chain;
2800 GST_PLAY_SINK_LOCK (playsink);
2801 chain = (GstPlayTextChain *) playsink->textchain;
2802 if (chain && chain->overlay) {
2803 g_object_get (chain->overlay, "font-desc", &result, NULL);
2804 playsink->font_desc = g_strdup (result);
2806 result = g_strdup (playsink->font_desc);
2808 GST_PLAY_SINK_UNLOCK (playsink);
2814 gst_play_sink_set_subtitle_encoding (GstPlaySink * playsink,
2815 const gchar * encoding)
2817 GstPlayTextChain *chain;
2819 GST_PLAY_SINK_LOCK (playsink);
2820 chain = (GstPlayTextChain *) playsink->textchain;
2821 g_free (playsink->subtitle_encoding);
2822 playsink->subtitle_encoding = g_strdup (encoding);
2823 if (chain && chain->overlay) {
2824 g_object_set (chain->overlay, "subtitle-encoding", encoding, NULL);
2826 GST_PLAY_SINK_UNLOCK (playsink);
2830 gst_play_sink_get_subtitle_encoding (GstPlaySink * playsink)
2832 gchar *result = NULL;
2833 GstPlayTextChain *chain;
2835 GST_PLAY_SINK_LOCK (playsink);
2836 chain = (GstPlayTextChain *) playsink->textchain;
2837 if (chain && chain->overlay) {
2838 g_object_get (chain->overlay, "subtitle-encoding", &result, NULL);
2839 playsink->subtitle_encoding = g_strdup (result);
2841 result = g_strdup (playsink->subtitle_encoding);
2843 GST_PLAY_SINK_UNLOCK (playsink);
2849 update_av_offset (GstPlaySink * playsink)
2852 GstPlayAudioChain *achain;
2853 GstPlayVideoChain *vchain;
2855 av_offset = playsink->av_offset;
2856 achain = (GstPlayAudioChain *) playsink->audiochain;
2857 vchain = (GstPlayVideoChain *) playsink->videochain;
2859 if (achain && vchain && achain->ts_offset && vchain->ts_offset) {
2860 g_object_set (achain->ts_offset, "ts-offset", MAX (0, -av_offset), NULL);
2861 g_object_set (vchain->ts_offset, "ts-offset", MAX (0, av_offset), NULL);
2863 GST_LOG_OBJECT (playsink, "no ts_offset elements");
2868 gst_play_sink_set_av_offset (GstPlaySink * playsink, gint64 av_offset)
2870 GST_PLAY_SINK_LOCK (playsink);
2871 playsink->av_offset = av_offset;
2872 update_av_offset (playsink);
2873 GST_PLAY_SINK_UNLOCK (playsink);
2877 gst_play_sink_get_av_offset (GstPlaySink * playsink)
2881 GST_PLAY_SINK_LOCK (playsink);
2882 result = playsink->av_offset;
2883 GST_PLAY_SINK_UNLOCK (playsink);
2889 * gst_play_sink_get_last_frame:
2890 * @playsink: a #GstPlaySink
2892 * Get the last displayed frame from @playsink. This frame is in the native
2893 * format of the sink element, the caps on the result buffer contain the format
2894 * of the frame data.
2896 * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
2900 gst_play_sink_get_last_frame (GstPlaySink * playsink)
2902 GstBuffer *result = NULL;
2903 GstPlayVideoChain *chain;
2905 GST_PLAY_SINK_LOCK (playsink);
2906 GST_DEBUG_OBJECT (playsink, "taking last frame");
2907 /* get the video chain if we can */
2908 if ((chain = (GstPlayVideoChain *) playsink->videochain)) {
2909 GST_DEBUG_OBJECT (playsink, "found video chain");
2910 /* see if the chain is active */
2911 if (chain->chain.activated && chain->sink) {
2914 GST_DEBUG_OBJECT (playsink, "video chain active and has a sink");
2916 /* find and get the last-buffer property now */
2918 gst_play_sink_find_property (playsink, chain->sink,
2919 "last-buffer", GST_TYPE_BUFFER))) {
2920 GST_DEBUG_OBJECT (playsink, "getting last-buffer property");
2921 g_object_get (elem, "last-buffer", &result, NULL);
2922 gst_object_unref (elem);
2926 GST_PLAY_SINK_UNLOCK (playsink);
2932 * gst_play_sink_convert_frame:
2933 * @playsink: a #GstPlaySink
2936 * Get the last displayed frame from @playsink. If caps is %NULL, the video will
2937 * be in the native format of the sink element and the caps on the buffer
2938 * describe the format of the frame. If @caps is not %NULL, the video
2939 * frame will be converted to the format of the caps.
2941 * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
2942 * available or when the conversion failed.
2945 gst_play_sink_convert_frame (GstPlaySink * playsink, GstCaps * caps)
2949 result = gst_play_sink_get_last_frame (playsink);
2950 if (result != NULL && caps != NULL) {
2954 temp = gst_video_convert_frame (result, caps, 25 * GST_SECOND, &err);
2955 gst_buffer_unref (result);
2956 if (temp == NULL && err) {
2957 /* I'm really uncertain whether we should make playsink post an error
2958 * on the bus or not. It's not like it's a critical issue regarding
2959 * playsink behaviour. */
2960 GST_ERROR ("Error converting frame: %s", err->message);
2969 is_raw_structure (GstStructure * s)
2973 name = gst_structure_get_name (s);
2975 if (g_str_has_prefix (name, "video/x-raw-") ||
2976 g_str_has_prefix (name, "audio/x-raw-"))
2982 is_raw_pad (GstPad * pad)
2984 GstPad *peer = gst_pad_get_peer (pad);
2986 gboolean raw = TRUE;
2991 caps = gst_pad_get_negotiated_caps (peer);
2995 caps = gst_pad_get_caps_reffed (peer);
2997 n = gst_caps_get_size (caps);
2998 for (i = 0; i < n; i++) {
2999 gboolean r = is_raw_structure (gst_caps_get_structure (caps, i));
3003 } else if (raw != r) {
3004 GST_ERROR_OBJECT (pad,
3005 "Caps contains raw and non-raw structures: %" GST_PTR_FORMAT, caps);
3011 raw = is_raw_structure (gst_caps_get_structure (caps, 0));
3013 gst_caps_unref (caps);
3014 gst_object_unref (peer);
3020 sinkpad_blocked_cb (GstPad * blockedpad, gboolean blocked, gpointer user_data)
3022 GstPlaySink *playsink = (GstPlaySink *) user_data;
3025 GST_PLAY_SINK_LOCK (playsink);
3027 pad = GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (blockedpad)));
3028 if (pad == playsink->video_pad) {
3029 playsink->video_pad_blocked = blocked;
3030 GST_DEBUG_OBJECT (pad, "Video pad blocked: %d", blocked);
3032 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO_RAW);
3033 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO);
3035 } else if (pad == playsink->audio_pad) {
3036 playsink->audio_pad_blocked = blocked;
3037 GST_DEBUG_OBJECT (pad, "Audio pad blocked: %d", blocked);
3039 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO_RAW);
3040 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO);
3042 } else if (pad == playsink->text_pad) {
3043 playsink->text_pad_blocked = blocked;
3044 GST_DEBUG_OBJECT (pad, "Text pad blocked: %d", blocked);
3046 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_TEXT);
3050 gst_object_unref (pad);
3051 GST_PLAY_SINK_UNLOCK (playsink);
3055 /* We reconfigure when for ALL streams:
3056 * * there isn't a pad
3057 * * OR the pad is blocked
3058 * * OR there are no pending blocks on that pad
3061 if ((!playsink->video_pad || playsink->video_pad_blocked
3062 || !PENDING_VIDEO_BLOCK (playsink)) && (!playsink->audio_pad
3063 || playsink->audio_pad_blocked || !PENDING_AUDIO_BLOCK (playsink))
3064 && (!playsink->text_pad || playsink->text_pad_blocked
3065 || !PENDING_TEXT_BLOCK (playsink))) {
3066 GST_DEBUG_OBJECT (playsink, "All pads blocked -- reconfiguring");
3068 if (playsink->video_pad) {
3069 playsink->video_pad_raw = is_raw_pad (playsink->video_pad);
3070 GST_DEBUG_OBJECT (playsink, "Video pad is raw: %d",
3071 playsink->video_pad_raw);
3074 if (playsink->audio_pad) {
3075 playsink->audio_pad_raw = is_raw_pad (playsink->audio_pad);
3076 GST_DEBUG_OBJECT (playsink, "Audio pad is raw: %d",
3077 playsink->audio_pad_raw);
3080 gst_play_sink_reconfigure (playsink);
3082 if (playsink->video_pad) {
3084 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3085 (playsink->video_pad)));
3086 gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3087 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3088 gst_object_unref (opad);
3091 if (playsink->audio_pad) {
3093 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3094 (playsink->audio_pad)));
3095 gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3096 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3097 gst_object_unref (opad);
3100 if (playsink->text_pad) {
3102 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3103 (playsink->text_pad)));
3104 gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3105 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3106 gst_object_unref (opad);
3110 gst_object_unref (pad);
3112 GST_PLAY_SINK_UNLOCK (playsink);
3116 caps_notify_cb (GstPad * pad, GParamSpec * unused, GstPlaySink * playsink)
3118 gboolean reconfigure = FALSE;
3122 g_object_get (pad, "caps", &caps, NULL);
3126 if (pad == playsink->audio_pad) {
3127 raw = is_raw_pad (pad);
3128 reconfigure = (! !playsink->audio_pad_raw != ! !raw)
3129 && playsink->audiochain;
3130 GST_DEBUG_OBJECT (pad,
3131 "Audio caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3133 } else if (pad == playsink->video_pad) {
3134 raw = is_raw_pad (pad);
3135 reconfigure = (! !playsink->video_pad_raw != ! !raw)
3136 && playsink->videochain;
3137 GST_DEBUG_OBJECT (pad,
3138 "Video caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3142 gst_caps_unref (caps);
3145 GST_PLAY_SINK_LOCK (playsink);
3146 if (playsink->video_pad) {
3148 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3149 (playsink->video_pad)));
3150 gst_pad_set_blocked_async_full (opad, TRUE, sinkpad_blocked_cb,
3151 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3152 gst_object_unref (opad);
3155 if (playsink->audio_pad) {
3157 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3158 (playsink->audio_pad)));
3159 gst_pad_set_blocked_async_full (opad, TRUE, sinkpad_blocked_cb,
3160 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3161 gst_object_unref (opad);
3164 if (playsink->text_pad) {
3166 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3167 (playsink->text_pad)));
3168 gst_pad_set_blocked_async_full (opad, TRUE, sinkpad_blocked_cb,
3169 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3170 gst_object_unref (opad);
3172 GST_PLAY_SINK_UNLOCK (playsink);
3177 * gst_play_sink_request_pad
3178 * @playsink: a #GstPlaySink
3179 * @type: a #GstPlaySinkType
3181 * Create or return a pad of @type.
3183 * Returns: a #GstPad of @type or %NULL when the pad could not be created.
3186 gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type)
3189 gboolean created = FALSE;
3190 gboolean activate = TRUE;
3191 const gchar *pad_name = NULL;
3193 GST_DEBUG_OBJECT (playsink, "request pad type %d", type);
3195 GST_PLAY_SINK_LOCK (playsink);
3197 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
3198 case GST_PLAY_SINK_TYPE_AUDIO:
3199 pad_name = "audio_sink";
3200 if (!playsink->audio_tee) {
3201 GST_LOG_OBJECT (playsink, "creating tee");
3202 /* create tee when needed. This element will feed the audio sink chain
3203 * and the vis chain. */
3204 playsink->audio_tee = gst_element_factory_make ("tee", "audiotee");
3205 if (playsink->audio_tee == NULL) {
3206 post_missing_element_message (playsink, "tee");
3207 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
3208 (_("Missing element '%s' - check your GStreamer installation."),
3213 playsink->audio_tee_sink =
3214 gst_element_get_static_pad (playsink->audio_tee, "sink");
3215 gst_bin_add (GST_BIN_CAST (playsink), playsink->audio_tee);
3216 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
3219 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
3221 if (!playsink->audio_pad) {
3222 GST_LOG_OBJECT (playsink, "ghosting tee sinkpad");
3223 playsink->audio_pad =
3224 gst_ghost_pad_new (pad_name, playsink->audio_tee_sink);
3225 g_signal_connect (G_OBJECT (playsink->audio_pad), "notify::caps",
3226 G_CALLBACK (caps_notify_cb), playsink);
3229 playsink->audio_pad_raw = FALSE;
3230 res = playsink->audio_pad;
3232 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
3233 case GST_PLAY_SINK_TYPE_VIDEO:
3234 pad_name = "video_sink";
3235 if (!playsink->video_pad) {
3236 GST_LOG_OBJECT (playsink, "ghosting videosink");
3237 playsink->video_pad =
3238 gst_ghost_pad_new_no_target (pad_name, GST_PAD_SINK);
3239 g_signal_connect (G_OBJECT (playsink->video_pad), "notify::caps",
3240 G_CALLBACK (caps_notify_cb), playsink);
3243 playsink->video_pad_raw = FALSE;
3244 res = playsink->video_pad;
3246 case GST_PLAY_SINK_TYPE_TEXT:
3247 GST_LOG_OBJECT (playsink, "ghosting text");
3248 if (!playsink->text_pad) {
3249 playsink->text_pad =
3250 gst_ghost_pad_new_no_target ("text_sink", GST_PAD_SINK);
3253 res = playsink->text_pad;
3255 case GST_PLAY_SINK_TYPE_FLUSHING:
3259 /* we need a unique padname for the flushing pad. */
3260 padname = g_strdup_printf ("flushing_%d", playsink->count);
3261 res = gst_ghost_pad_new_no_target (padname, GST_PAD_SINK);
3272 GST_PLAY_SINK_UNLOCK (playsink);
3274 if (created && res) {
3275 /* we have to add the pad when it's active or we get an error when the
3276 * element is 'running' */
3277 gst_pad_set_active (res, TRUE);
3278 gst_element_add_pad (GST_ELEMENT_CAST (playsink), res);
3279 if (type != GST_PLAY_SINK_TYPE_FLUSHING) {
3281 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (res)));
3283 gst_pad_set_blocked_async_full (blockpad, TRUE, sinkpad_blocked_cb,
3284 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3285 PENDING_FLAG_SET (playsink, type);
3286 gst_object_unref (blockpad);
3289 gst_pad_set_active (res, activate);
3296 gst_play_sink_request_new_pad (GstElement * element, GstPadTemplate * templ,
3301 GstPlaySinkType type;
3302 const gchar *tplname;
3304 g_return_val_if_fail (templ != NULL, NULL);
3306 GST_DEBUG_OBJECT (element, "name:%s", name);
3308 psink = GST_PLAY_SINK (element);
3309 tplname = GST_PAD_TEMPLATE_NAME_TEMPLATE (templ);
3311 /* Figure out the GstPlaySinkType based on the template */
3312 if (!strcmp (tplname, "audio_sink"))
3313 type = GST_PLAY_SINK_TYPE_AUDIO;
3314 else if (!strcmp (tplname, "audio_raw_sink"))
3315 type = GST_PLAY_SINK_TYPE_AUDIO_RAW;
3316 else if (!strcmp (tplname, "video_sink"))
3317 type = GST_PLAY_SINK_TYPE_VIDEO;
3318 else if (!strcmp (tplname, "video_raw_sink"))
3319 type = GST_PLAY_SINK_TYPE_VIDEO_RAW;
3320 else if (!strcmp (tplname, "text_sink"))
3321 type = GST_PLAY_SINK_TYPE_TEXT;
3323 goto unknown_template;
3325 pad = gst_play_sink_request_pad (psink, type);
3329 GST_WARNING_OBJECT (element, "Unknown pad template");
3334 gst_play_sink_release_pad (GstPlaySink * playsink, GstPad * pad)
3336 GstPad **res = NULL;
3337 gboolean untarget = TRUE;
3339 GST_DEBUG_OBJECT (playsink, "release pad %" GST_PTR_FORMAT, pad);
3341 GST_PLAY_SINK_LOCK (playsink);
3342 if (pad == playsink->video_pad) {
3343 res = &playsink->video_pad;
3344 g_signal_handlers_disconnect_by_func (playsink->video_pad, caps_notify_cb,
3346 } else if (pad == playsink->audio_pad) {
3347 res = &playsink->audio_pad;
3348 g_signal_handlers_disconnect_by_func (playsink->audio_pad, caps_notify_cb,
3350 } else if (pad == playsink->text_pad) {
3351 res = &playsink->text_pad;
3353 /* try to release the given pad anyway, these could be the FLUSHING pads. */
3357 GST_PLAY_SINK_UNLOCK (playsink);
3360 GST_DEBUG_OBJECT (playsink, "deactivate pad %" GST_PTR_FORMAT, *res);
3361 gst_pad_set_active (*res, FALSE);
3363 GST_DEBUG_OBJECT (playsink, "untargeting pad %" GST_PTR_FORMAT, *res);
3364 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (*res), NULL);
3366 GST_DEBUG_OBJECT (playsink, "remove pad %" GST_PTR_FORMAT, *res);
3367 gst_element_remove_pad (GST_ELEMENT_CAST (playsink), *res);
3373 gst_play_sink_release_request_pad (GstElement * element, GstPad * pad)
3375 GstPlaySink *psink = GST_PLAY_SINK (element);
3377 gst_play_sink_release_pad (psink, pad);
3381 gst_play_sink_handle_message (GstBin * bin, GstMessage * message)
3383 GstPlaySink *playsink;
3385 playsink = GST_PLAY_SINK_CAST (bin);
3387 switch (GST_MESSAGE_TYPE (message)) {
3388 case GST_MESSAGE_STEP_DONE:
3393 gboolean flush, intermediate, eos;
3396 GST_INFO_OBJECT (playsink, "Handling step-done message");
3397 gst_message_parse_step_done (message, &format, &amount, &rate, &flush,
3398 &intermediate, &duration, &eos);
3400 if (format == GST_FORMAT_BUFFERS) {
3401 /* for the buffer format, we align the other streams */
3402 if (playsink->audiochain) {
3406 gst_event_new_step (GST_FORMAT_TIME, duration, rate, flush,
3409 if (!gst_element_send_event (playsink->audiochain->chain.bin, event)) {
3410 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
3414 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
3418 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
3423 /* Send an event to our sinks until one of them works; don't then send to the
3424 * remaining sinks (unlike GstBin)
3425 * Special case: If a text sink is set we need to send the event
3426 * to them in case it's source is different from the a/v stream's source.
3429 gst_play_sink_send_event_to_sink (GstPlaySink * playsink, GstEvent * event)
3431 gboolean res = TRUE;
3433 if (playsink->textchain && playsink->textchain->sink) {
3434 gst_event_ref (event);
3435 if ((res = gst_element_send_event (playsink->textchain->chain.bin, event))) {
3436 GST_DEBUG_OBJECT (playsink, "Sent event successfully to text sink");
3438 GST_DEBUG_OBJECT (playsink, "Event failed when sent to text sink");
3442 if (playsink->videochain) {
3443 gst_event_ref (event);
3444 if ((res = gst_element_send_event (playsink->videochain->chain.bin, event))) {
3445 GST_DEBUG_OBJECT (playsink, "Sent event successfully to video sink");
3448 GST_DEBUG_OBJECT (playsink, "Event failed when sent to video sink");
3450 if (playsink->audiochain) {
3451 gst_event_ref (event);
3452 if ((res = gst_element_send_event (playsink->audiochain->chain.bin, event))) {
3453 GST_DEBUG_OBJECT (playsink, "Sent event successfully to audio sink");
3456 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
3460 gst_event_unref (event);
3464 /* We only want to send the event to a single sink (overriding GstBin's
3465 * behaviour), but we want to keep GstPipeline's behaviour - wrapping seek
3466 * events appropriately. So, this is a messy duplication of code. */
3468 gst_play_sink_send_event (GstElement * element, GstEvent * event)
3470 gboolean res = FALSE;
3471 GstEventType event_type = GST_EVENT_TYPE (event);
3472 GstPlaySink *playsink;
3474 playsink = GST_PLAY_SINK_CAST (element);
3476 switch (event_type) {
3477 case GST_EVENT_SEEK:
3478 GST_DEBUG_OBJECT (element, "Sending event to a sink");
3479 res = gst_play_sink_send_event_to_sink (playsink, event);
3481 case GST_EVENT_STEP:
3486 gboolean flush, intermediate;
3488 gst_event_parse_step (event, &format, &amount, &rate, &flush,
3491 if (format == GST_FORMAT_BUFFERS) {
3492 /* for buffers, we will try to step video frames, for other formats we
3493 * send the step to all sinks */
3494 res = gst_play_sink_send_event_to_sink (playsink, event);
3497 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
3504 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
3511 static GstStateChangeReturn
3512 gst_play_sink_change_state (GstElement * element, GstStateChange transition)
3514 GstStateChangeReturn ret;
3515 GstStateChangeReturn bret;
3517 GstPlaySink *playsink;
3519 playsink = GST_PLAY_SINK (element);
3521 switch (transition) {
3522 case GST_STATE_CHANGE_READY_TO_PAUSED:
3523 playsink->need_async_start = TRUE;
3524 /* we want to go async to PAUSED until we managed to configure and add the
3526 do_async_start (playsink);
3527 ret = GST_STATE_CHANGE_ASYNC;
3529 case GST_STATE_CHANGE_PAUSED_TO_READY:
3530 /* unblock all pads here */
3531 GST_PLAY_SINK_LOCK (playsink);
3532 if (playsink->video_pad) {
3534 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3535 (playsink->video_pad)));
3536 if (gst_pad_is_blocked (opad)) {
3537 gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3538 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3540 gst_object_unref (opad);
3541 playsink->video_pad_blocked = FALSE;
3544 if (playsink->audio_pad) {
3546 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3547 (playsink->audio_pad)));
3549 if (gst_pad_is_blocked (opad)) {
3550 gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3551 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3553 gst_object_unref (opad);
3554 playsink->audio_pad_blocked = FALSE;
3557 if (playsink->text_pad) {
3559 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3560 (playsink->text_pad)));
3561 if (gst_pad_is_blocked (opad)) {
3562 gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3563 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3565 gst_object_unref (opad);
3566 playsink->text_pad_blocked = FALSE;
3568 GST_PLAY_SINK_UNLOCK (playsink);
3570 case GST_STATE_CHANGE_READY_TO_NULL:
3571 if (playsink->audiochain && playsink->audiochain->sink_volume) {
3572 /* remove our links to the mute and volume elements when they were
3573 * provided by a sink */
3574 disconnect_chain (playsink->audiochain, playsink);
3575 playsink->audiochain->volume = NULL;
3576 playsink->audiochain->mute = NULL;
3579 if (playsink->audiochain && playsink->audiochain->ts_offset) {
3580 gst_object_unref (playsink->audiochain->ts_offset);
3581 playsink->audiochain->ts_offset = NULL;
3584 if (playsink->videochain && playsink->videochain->ts_offset) {
3585 gst_object_unref (playsink->videochain->ts_offset);
3586 playsink->videochain->ts_offset = NULL;
3588 ret = GST_STATE_CHANGE_SUCCESS;
3591 /* all other state changes return SUCCESS by default, this value can be
3592 * overridden by the result of the children */
3593 ret = GST_STATE_CHANGE_SUCCESS;
3597 /* do the state change of the children */
3599 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->change_state (element,
3601 /* now look at the result of our children and adjust the return value */
3603 case GST_STATE_CHANGE_FAILURE:
3604 /* failure, we stop */
3605 goto activate_failed;
3606 case GST_STATE_CHANGE_NO_PREROLL:
3607 /* some child returned NO_PREROLL. This is strange but we never know. We
3608 * commit our async state change (if any) and return the NO_PREROLL */
3609 do_async_done (playsink);
3612 case GST_STATE_CHANGE_ASYNC:
3613 /* some child was async, return this */
3617 /* return our previously configured return value */
3621 switch (transition) {
3622 case GST_STATE_CHANGE_READY_TO_PAUSED:
3624 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
3625 /* FIXME Release audio device when we implement that */
3626 playsink->need_async_start = TRUE;
3628 case GST_STATE_CHANGE_PAUSED_TO_READY:{
3629 if (playsink->video_sinkpad_stream_synchronizer) {
3630 gst_element_release_request_pad (GST_ELEMENT_CAST
3631 (playsink->stream_synchronizer),
3632 playsink->video_sinkpad_stream_synchronizer);
3633 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3634 playsink->video_sinkpad_stream_synchronizer = NULL;
3635 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3636 playsink->video_srcpad_stream_synchronizer = NULL;
3638 if (playsink->audio_sinkpad_stream_synchronizer) {
3639 gst_element_release_request_pad (GST_ELEMENT_CAST
3640 (playsink->stream_synchronizer),
3641 playsink->audio_sinkpad_stream_synchronizer);
3642 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
3643 playsink->audio_sinkpad_stream_synchronizer = NULL;
3644 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
3645 playsink->audio_srcpad_stream_synchronizer = NULL;
3647 if (playsink->text_sinkpad_stream_synchronizer) {
3648 gst_element_release_request_pad (GST_ELEMENT_CAST
3649 (playsink->stream_synchronizer),
3650 playsink->text_sinkpad_stream_synchronizer);
3651 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
3652 playsink->text_sinkpad_stream_synchronizer = NULL;
3653 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
3654 playsink->text_srcpad_stream_synchronizer = NULL;
3658 case GST_STATE_CHANGE_READY_TO_NULL:
3659 /* remove sinks we added */
3660 if (playsink->videodeinterlacechain) {
3661 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
3663 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3665 if (playsink->videochain) {
3666 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3667 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3669 if (playsink->audiochain) {
3670 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3671 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3673 if (playsink->vischain) {
3674 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3675 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3677 if (playsink->textchain) {
3678 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3679 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3681 do_async_done (playsink);
3682 /* when going to READY, keep elements around as long as possible,
3683 * so they may be re-used faster next time/url around.
3684 * when really going to NULL, clean up everything completely. */
3685 if (transition == GST_STATE_CHANGE_READY_TO_NULL) {
3687 /* Unparent the sinks to allow reuse */
3688 if (playsink->videochain && playsink->videochain->sink)
3689 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
3690 playsink->videochain->sink);
3691 if (playsink->audiochain && playsink->audiochain->sink)
3692 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
3693 playsink->audiochain->sink);
3694 if (playsink->textchain && playsink->textchain->sink)
3695 gst_bin_remove (GST_BIN_CAST (playsink->textchain->chain.bin),
3696 playsink->textchain->sink);
3698 if (playsink->audio_sink != NULL)
3699 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
3700 if (playsink->video_sink != NULL)
3701 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
3702 if (playsink->visualisation != NULL)
3703 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
3704 if (playsink->text_sink != NULL)
3705 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
3707 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
3708 playsink->videodeinterlacechain = NULL;
3709 free_chain ((GstPlayChain *) playsink->videochain);
3710 playsink->videochain = NULL;
3711 free_chain ((GstPlayChain *) playsink->audiochain);
3712 playsink->audiochain = NULL;
3713 free_chain ((GstPlayChain *) playsink->vischain);
3714 playsink->vischain = NULL;
3715 free_chain ((GstPlayChain *) playsink->textchain);
3716 playsink->textchain = NULL;
3727 GST_DEBUG_OBJECT (element,
3728 "element failed to change states -- activation problem?");
3729 return GST_STATE_CHANGE_FAILURE;
3734 gst_play_sink_set_property (GObject * object, guint prop_id,
3735 const GValue * value, GParamSpec * spec)
3737 GstPlaySink *playsink = GST_PLAY_SINK (object);
3741 gst_play_sink_set_flags (playsink, g_value_get_flags (value));
3744 gst_play_sink_set_volume (playsink, g_value_get_double (value));
3747 gst_play_sink_set_mute (playsink, g_value_get_boolean (value));
3749 case PROP_FONT_DESC:
3750 gst_play_sink_set_font_desc (playsink, g_value_get_string (value));
3752 case PROP_SUBTITLE_ENCODING:
3753 gst_play_sink_set_subtitle_encoding (playsink,
3754 g_value_get_string (value));
3756 case PROP_VIS_PLUGIN:
3757 gst_play_sink_set_vis_plugin (playsink, g_value_get_object (value));
3759 case PROP_AV_OFFSET:
3760 gst_play_sink_set_av_offset (playsink, g_value_get_int64 (value));
3762 case PROP_VIDEO_SINK:
3763 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_VIDEO,
3764 g_value_get_object (value));
3766 case PROP_AUDIO_SINK:
3767 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_AUDIO,
3768 g_value_get_object (value));
3770 case PROP_TEXT_SINK:
3771 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_TEXT,
3772 g_value_get_object (value));
3775 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
3781 gst_play_sink_get_property (GObject * object, guint prop_id,
3782 GValue * value, GParamSpec * spec)
3784 GstPlaySink *playsink = GST_PLAY_SINK (object);
3788 g_value_set_flags (value, gst_play_sink_get_flags (playsink));
3791 g_value_set_double (value, gst_play_sink_get_volume (playsink));
3794 g_value_set_boolean (value, gst_play_sink_get_mute (playsink));
3796 case PROP_FONT_DESC:
3797 g_value_take_string (value, gst_play_sink_get_font_desc (playsink));
3799 case PROP_SUBTITLE_ENCODING:
3800 g_value_take_string (value,
3801 gst_play_sink_get_subtitle_encoding (playsink));
3803 case PROP_VIS_PLUGIN:
3804 g_value_take_object (value, gst_play_sink_get_vis_plugin (playsink));
3807 gst_value_take_buffer (value, gst_play_sink_get_last_frame (playsink));
3809 case PROP_AV_OFFSET:
3810 g_value_set_int64 (value, gst_play_sink_get_av_offset (playsink));
3812 case PROP_VIDEO_SINK:
3813 g_value_take_object (value, gst_play_sink_get_sink (playsink,
3814 GST_PLAY_SINK_TYPE_VIDEO));
3816 case PROP_AUDIO_SINK:
3817 g_value_take_object (value, gst_play_sink_get_sink (playsink,
3818 GST_PLAY_SINK_TYPE_AUDIO));
3820 case PROP_TEXT_SINK:
3821 g_value_take_object (value, gst_play_sink_get_sink (playsink,
3822 GST_PLAY_SINK_TYPE_TEXT));
3825 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
3831 gst_play_sink_plugin_init (GstPlugin * plugin)
3833 GST_DEBUG_CATEGORY_INIT (gst_play_sink_debug, "playsink", 0, "play bin");
3835 return gst_element_register (plugin, "playsink", GST_RANK_NONE,
3836 GST_TYPE_PLAY_SINK);