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.
28 #include <gst/gst-i18n-plugin.h>
29 #include <gst/pbutils/pbutils.h>
30 #include <gst/video/video.h>
32 #include "gstplaysink.h"
33 #include "gststreamsynchronizer.h"
34 #include "gstplaysinkvideoconvert.h"
35 #include "gstplaysinkaudioconvert.h"
37 GST_DEBUG_CATEGORY_STATIC (gst_play_sink_debug);
38 #define GST_CAT_DEFAULT gst_play_sink_debug
40 #define VOLUME_MAX_DOUBLE 10.0
42 #define DEFAULT_FLAGS GST_PLAY_FLAG_AUDIO | GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_TEXT | \
43 GST_PLAY_FLAG_SOFT_VOLUME
45 #define GST_PLAY_CHAIN(c) ((GstPlayChain *)(c))
47 /* holds the common data fields for the audio and video pipelines. We keep them
48 * in a structure to more easily have all the info available. */
51 GstPlaySink *playsink;
64 GstElement *volume; /* element with the volume property */
65 gboolean sink_volume; /* if the volume was provided by the sink */
66 GstElement *mute; /* element with the mute property */
68 GstElement *ts_offset;
74 GstPad *sinkpad, *srcpad;
76 GstElement *deinterlace;
77 } GstPlayVideoDeinterlaceChain;
87 GstElement *ts_offset;
97 GstPad *blockpad; /* srcpad of resample, used for switching the vis */
98 GstPad *vissinkpad; /* visualisation sinkpad, */
100 GstPad *vissrcpad; /* visualisation srcpad, */
101 GstPad *srcpad; /* outgoing srcpad, used to connect to the next
110 GstElement *identity;
112 GstPad *videosinkpad;
114 GstPad *srcpad; /* outgoing srcpad, used to connect to the next
116 GstElement *sink; /* custom sink to receive subtitle buffers */
119 #define GST_PLAY_SINK_GET_LOCK(playsink) (&((GstPlaySink *)playsink)->lock)
120 #define GST_PLAY_SINK_LOCK(playsink) G_STMT_START { \
121 GST_LOG_OBJECT (playsink, "locking from thread %p", g_thread_self ()); \
122 g_static_rec_mutex_lock (GST_PLAY_SINK_GET_LOCK (playsink)); \
123 GST_LOG_OBJECT (playsink, "locked from thread %p", g_thread_self ()); \
125 #define GST_PLAY_SINK_UNLOCK(playsink) G_STMT_START { \
126 GST_LOG_OBJECT (playsink, "unlocking from thread %p", g_thread_self ()); \
127 g_static_rec_mutex_unlock (GST_PLAY_SINK_GET_LOCK (playsink)); \
130 #define PENDING_FLAG_SET(playsink, flagtype) \
131 ((playsink->pending_blocked_pads) |= (1 << flagtype))
132 #define PENDING_FLAG_UNSET(playsink, flagtype) \
133 ((playsink->pending_blocked_pads) &= ~(1 << flagtype))
134 #define PENDING_FLAG_IS_SET(playsink, flagtype) \
135 ((playsink->pending_blocked_pads) & (1 << flagtype))
136 #define PENDING_VIDEO_BLOCK(playsink) \
137 ((playsink->pending_blocked_pads) & (1 << GST_PLAY_SINK_TYPE_VIDEO_RAW | 1 << GST_PLAY_SINK_TYPE_VIDEO))
138 #define PENDING_AUDIO_BLOCK(playsink) \
139 ((playsink->pending_blocked_pads) & (1 << GST_PLAY_SINK_TYPE_AUDIO_RAW | 1 << GST_PLAY_SINK_TYPE_AUDIO))
140 #define PENDING_TEXT_BLOCK(playsink) \
141 PENDING_FLAG_IS_SET(playsink, GST_PLAY_SINK_TYPE_TEXT)
147 GStaticRecMutex lock;
149 gboolean async_pending;
150 gboolean need_async_start;
154 GstStreamSynchronizer *stream_synchronizer;
157 GstPlayAudioChain *audiochain;
158 GstPlayVideoDeinterlaceChain *videodeinterlacechain;
159 GstPlayVideoChain *videochain;
160 GstPlayVisChain *vischain;
161 GstPlayTextChain *textchain;
165 gboolean audio_pad_raw;
166 gboolean audio_pad_blocked;
167 GstPad *audio_srcpad_stream_synchronizer;
168 GstPad *audio_sinkpad_stream_synchronizer;
169 gulong audio_block_id;
171 GstElement *audio_tee;
172 GstPad *audio_tee_sink;
173 GstPad *audio_tee_asrc;
174 GstPad *audio_tee_vissrc;
177 gboolean video_pad_raw;
178 gboolean video_pad_blocked;
179 GstPad *video_srcpad_stream_synchronizer;
180 GstPad *video_sinkpad_stream_synchronizer;
181 gulong video_block_id;
184 gboolean text_pad_blocked;
185 GstPad *text_srcpad_stream_synchronizer;
186 GstPad *text_sinkpad_stream_synchronizer;
187 gulong text_block_id;
189 guint32 pending_blocked_pads;
192 GstElement *audio_sink;
193 GstElement *video_sink;
194 GstElement *visualisation;
195 GstElement *text_sink;
198 gchar *font_desc; /* font description */
199 gchar *subtitle_encoding; /* subtitle encoding */
200 guint connection_speed; /* connection speed in bits/sec (0 = unknown) */
202 gboolean volume_changed; /* volume/mute changed while no audiochain */
203 gboolean mute_changed; /* ... has been created yet */
207 struct _GstPlaySinkClass
209 GstBinClass parent_class;
211 gboolean (*reconfigure) (GstPlaySink * playsink);
213 GstBuffer *(*convert_frame) (GstPlaySink * playsink, GstCaps * caps);
217 static GstStaticPadTemplate audiotemplate =
218 GST_STATIC_PAD_TEMPLATE ("audio_sink",
221 GST_STATIC_CAPS_ANY);
222 static GstStaticPadTemplate videotemplate =
223 GST_STATIC_PAD_TEMPLATE ("video_sink",
226 GST_STATIC_CAPS_ANY);
227 static GstStaticPadTemplate texttemplate = GST_STATIC_PAD_TEMPLATE ("text_sink",
230 GST_STATIC_CAPS_ANY);
232 /* FIXME 0.11: Remove */
233 static GstStaticPadTemplate audiorawtemplate =
234 GST_STATIC_PAD_TEMPLATE ("audio_raw_sink",
237 GST_STATIC_CAPS_ANY);
238 static GstStaticPadTemplate videorawtemplate =
239 GST_STATIC_PAD_TEMPLATE ("video_raw_sink",
242 GST_STATIC_CAPS_ANY);
253 PROP_SUBTITLE_ENCODING,
266 static void gst_play_sink_dispose (GObject * object);
267 static void gst_play_sink_finalize (GObject * object);
268 static void gst_play_sink_set_property (GObject * object, guint prop_id,
269 const GValue * value, GParamSpec * spec);
270 static void gst_play_sink_get_property (GObject * object, guint prop_id,
271 GValue * value, GParamSpec * spec);
273 static GstPad *gst_play_sink_request_new_pad (GstElement * element,
274 GstPadTemplate * templ, const gchar * name, const GstCaps * caps);
275 static void gst_play_sink_release_request_pad (GstElement * element,
277 static gboolean gst_play_sink_send_event (GstElement * element,
279 static GstStateChangeReturn gst_play_sink_change_state (GstElement * element,
280 GstStateChange transition);
282 static void gst_play_sink_handle_message (GstBin * bin, GstMessage * message);
284 static void notify_volume_cb (GObject * object, GParamSpec * pspec,
285 GstPlaySink * playsink);
286 static void notify_mute_cb (GObject * object, GParamSpec * pspec,
287 GstPlaySink * playsink);
289 static void update_av_offset (GstPlaySink * playsink);
292 gst_play_marshal_BUFFER__BOXED (GClosure * closure,
293 GValue * return_value G_GNUC_UNUSED,
294 guint n_param_values,
295 const GValue * param_values,
296 gpointer invocation_hint G_GNUC_UNUSED, gpointer marshal_data)
298 typedef GstBuffer *(*GMarshalFunc_OBJECT__BOXED) (gpointer data1,
299 gpointer arg_1, gpointer data2);
300 register GMarshalFunc_OBJECT__BOXED callback;
301 register GCClosure *cc = (GCClosure *) closure;
302 register gpointer data1, data2;
304 g_return_if_fail (return_value != NULL);
305 g_return_if_fail (n_param_values == 2);
307 if (G_CCLOSURE_SWAP_DATA (closure)) {
308 data1 = closure->data;
309 data2 = g_value_peek_pointer (param_values + 0);
311 data1 = g_value_peek_pointer (param_values + 0);
312 data2 = closure->data;
315 (GMarshalFunc_OBJECT__BOXED) (marshal_data ? marshal_data : cc->callback);
317 v_return = callback (data1, g_value_get_boxed (param_values + 1), data2);
319 gst_value_take_buffer (return_value, v_return);
322 /* static guint gst_play_sink_signals[LAST_SIGNAL] = { 0 }; */
324 G_DEFINE_TYPE (GstPlaySink, gst_play_sink, GST_TYPE_BIN);
327 gst_play_sink_class_init (GstPlaySinkClass * klass)
329 GObjectClass *gobject_klass;
330 GstElementClass *gstelement_klass;
331 GstBinClass *gstbin_klass;
333 gobject_klass = (GObjectClass *) klass;
334 gstelement_klass = (GstElementClass *) klass;
335 gstbin_klass = (GstBinClass *) klass;
337 gobject_klass->dispose = gst_play_sink_dispose;
338 gobject_klass->finalize = gst_play_sink_finalize;
339 gobject_klass->set_property = gst_play_sink_set_property;
340 gobject_klass->get_property = gst_play_sink_get_property;
346 * Control the behaviour of playsink.
348 g_object_class_install_property (gobject_klass, PROP_FLAGS,
349 g_param_spec_flags ("flags", "Flags", "Flags to control behaviour",
350 GST_TYPE_PLAY_FLAGS, DEFAULT_FLAGS,
351 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
354 * GstPlaySink:volume:
356 * Get or set the current audio stream volume. 1.0 means 100%,
357 * 0.0 means mute. This uses a linear volume scale.
360 g_object_class_install_property (gobject_klass, PROP_VOLUME,
361 g_param_spec_double ("volume", "Volume", "The audio volume, 1.0=100%",
362 0.0, VOLUME_MAX_DOUBLE, 1.0,
363 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
364 g_object_class_install_property (gobject_klass, PROP_MUTE,
365 g_param_spec_boolean ("mute", "Mute",
366 "Mute the audio channel without changing the volume", FALSE,
367 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
368 g_object_class_install_property (gobject_klass, PROP_FONT_DESC,
369 g_param_spec_string ("subtitle-font-desc",
370 "Subtitle font description",
371 "Pango font description of font "
372 "to be used for subtitle rendering", NULL,
373 G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
374 g_object_class_install_property (gobject_klass, PROP_SUBTITLE_ENCODING,
375 g_param_spec_string ("subtitle-encoding", "subtitle encoding",
376 "Encoding to assume if input subtitles are not in UTF-8 encoding. "
377 "If not set, the GST_SUBTITLE_ENCODING environment variable will "
378 "be checked for an encoding to use. If that is not set either, "
379 "ISO-8859-15 will be assumed.", NULL,
380 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
381 g_object_class_install_property (gobject_klass, PROP_VIS_PLUGIN,
382 g_param_spec_object ("vis-plugin", "Vis plugin",
383 "the visualization element to use (NULL = default)",
384 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
388 * Get the currently rendered or prerolled frame in the video sink.
389 * The #GstCaps on the buffer will describe the format of the buffer.
393 g_object_class_install_property (gobject_klass, PROP_FRAME,
394 g_param_spec_boxed ("frame", "Frame",
395 "The last frame (NULL = no video available)",
396 GST_TYPE_BUFFER, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
398 * GstPlaySink:av-offset:
400 * Control the synchronisation offset between the audio and video streams.
401 * Positive values make the audio ahead of the video and negative values make
402 * the audio go behind the video.
406 g_object_class_install_property (gobject_klass, PROP_AV_OFFSET,
407 g_param_spec_int64 ("av-offset", "AV Offset",
408 "The synchronisation offset between audio and video in nanoseconds",
409 G_MININT64, G_MAXINT64, 0,
410 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
412 g_signal_new ("reconfigure", G_TYPE_FROM_CLASS (klass),
413 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstPlaySinkClass,
414 reconfigure), NULL, NULL, gst_marshal_BOOLEAN__VOID, G_TYPE_BOOLEAN,
417 * GstPlaySink::convert-frame
418 * @playsink: a #GstPlaySink
419 * @caps: the target format of the frame
421 * Action signal to retrieve the currently playing video frame in the format
422 * specified by @caps.
423 * If @caps is %NULL, no conversion will be performed and this function is
424 * equivalent to the #GstPlaySink::frame property.
426 * Returns: a #GstBuffer of the current video frame converted to #caps.
427 * The caps on the buffer will describe the final layout of the buffer data.
428 * %NULL is returned when no current buffer can be retrieved or when the
433 g_signal_new ("convert-frame", G_TYPE_FROM_CLASS (klass),
434 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
435 G_STRUCT_OFFSET (GstPlaySinkClass, convert_frame), NULL, NULL,
436 gst_play_marshal_BUFFER__BOXED, GST_TYPE_BUFFER, 1, GST_TYPE_CAPS);
438 gst_element_class_add_pad_template (gstelement_klass,
439 gst_static_pad_template_get (&audiorawtemplate));
440 gst_element_class_add_pad_template (gstelement_klass,
441 gst_static_pad_template_get (&audiotemplate));
442 gst_element_class_add_pad_template (gstelement_klass,
443 gst_static_pad_template_get (&videorawtemplate));
444 gst_element_class_add_pad_template (gstelement_klass,
445 gst_static_pad_template_get (&videotemplate));
446 gst_element_class_add_pad_template (gstelement_klass,
447 gst_static_pad_template_get (&texttemplate));
448 gst_element_class_set_details_simple (gstelement_klass, "Player Sink",
450 "Convenience sink for multiple streams",
451 "Wim Taymans <wim.taymans@gmail.com>");
453 gstelement_klass->change_state =
454 GST_DEBUG_FUNCPTR (gst_play_sink_change_state);
455 gstelement_klass->send_event = GST_DEBUG_FUNCPTR (gst_play_sink_send_event);
456 gstelement_klass->request_new_pad =
457 GST_DEBUG_FUNCPTR (gst_play_sink_request_new_pad);
458 gstelement_klass->release_pad =
459 GST_DEBUG_FUNCPTR (gst_play_sink_release_request_pad);
461 gstbin_klass->handle_message =
462 GST_DEBUG_FUNCPTR (gst_play_sink_handle_message);
464 klass->reconfigure = GST_DEBUG_FUNCPTR (gst_play_sink_reconfigure);
465 klass->convert_frame = GST_DEBUG_FUNCPTR (gst_play_sink_convert_frame);
469 gst_play_sink_init (GstPlaySink * playsink)
472 playsink->video_sink = NULL;
473 playsink->audio_sink = NULL;
474 playsink->visualisation = NULL;
475 playsink->text_sink = NULL;
476 playsink->volume = 1.0;
477 playsink->font_desc = NULL;
478 playsink->subtitle_encoding = NULL;
479 playsink->flags = DEFAULT_FLAGS;
481 playsink->stream_synchronizer =
482 g_object_new (GST_TYPE_STREAM_SYNCHRONIZER, NULL);
483 gst_bin_add (GST_BIN_CAST (playsink),
484 GST_ELEMENT_CAST (playsink->stream_synchronizer));
486 g_static_rec_mutex_init (&playsink->lock);
487 GST_OBJECT_FLAG_SET (playsink, GST_ELEMENT_IS_SINK);
491 disconnect_chain (GstPlayAudioChain * chain, GstPlaySink * playsink)
495 g_signal_handlers_disconnect_by_func (chain->volume, notify_volume_cb,
498 g_signal_handlers_disconnect_by_func (chain->mute, notify_mute_cb,
504 free_chain (GstPlayChain * chain)
508 gst_object_unref (chain->bin);
514 gst_play_sink_dispose (GObject * object)
516 GstPlaySink *playsink;
518 playsink = GST_PLAY_SINK (object);
520 if (playsink->audio_sink != NULL) {
521 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
522 gst_object_unref (playsink->audio_sink);
523 playsink->audio_sink = NULL;
525 if (playsink->video_sink != NULL) {
526 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
527 gst_object_unref (playsink->video_sink);
528 playsink->video_sink = NULL;
530 if (playsink->visualisation != NULL) {
531 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
532 gst_object_unref (playsink->visualisation);
533 playsink->visualisation = NULL;
535 if (playsink->text_sink != NULL) {
536 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
537 gst_object_unref (playsink->text_sink);
538 playsink->text_sink = NULL;
541 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
542 playsink->videodeinterlacechain = NULL;
543 free_chain ((GstPlayChain *) playsink->videochain);
544 playsink->videochain = NULL;
545 free_chain ((GstPlayChain *) playsink->audiochain);
546 playsink->audiochain = NULL;
547 free_chain ((GstPlayChain *) playsink->vischain);
548 playsink->vischain = NULL;
549 free_chain ((GstPlayChain *) playsink->textchain);
550 playsink->textchain = NULL;
552 if (playsink->audio_tee_sink) {
553 gst_object_unref (playsink->audio_tee_sink);
554 playsink->audio_tee_sink = NULL;
557 if (playsink->audio_tee_vissrc) {
558 gst_element_release_request_pad (playsink->audio_tee,
559 playsink->audio_tee_vissrc);
560 gst_object_unref (playsink->audio_tee_vissrc);
561 playsink->audio_tee_vissrc = NULL;
564 if (playsink->audio_tee_asrc) {
565 gst_element_release_request_pad (playsink->audio_tee,
566 playsink->audio_tee_asrc);
567 gst_object_unref (playsink->audio_tee_asrc);
568 playsink->audio_tee_asrc = NULL;
571 g_free (playsink->font_desc);
572 playsink->font_desc = NULL;
574 g_free (playsink->subtitle_encoding);
575 playsink->subtitle_encoding = NULL;
577 playsink->stream_synchronizer = NULL;
579 G_OBJECT_CLASS (gst_play_sink_parent_class)->dispose (object);
583 gst_play_sink_finalize (GObject * object)
585 GstPlaySink *playsink;
587 playsink = GST_PLAY_SINK (object);
589 g_static_rec_mutex_free (&playsink->lock);
591 G_OBJECT_CLASS (gst_play_sink_parent_class)->finalize (object);
595 gst_play_sink_set_sink (GstPlaySink * playsink, GstPlaySinkType type,
598 GstElement **elem = NULL, *old = NULL;
600 GST_LOG ("Setting sink %" GST_PTR_FORMAT " as sink type %d", sink, type);
602 GST_PLAY_SINK_LOCK (playsink);
604 case GST_PLAY_SINK_TYPE_AUDIO:
605 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
606 elem = &playsink->audio_sink;
608 case GST_PLAY_SINK_TYPE_VIDEO:
609 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
610 elem = &playsink->video_sink;
612 case GST_PLAY_SINK_TYPE_TEXT:
613 elem = &playsink->text_sink;
621 gst_object_ref (sink);
624 GST_PLAY_SINK_UNLOCK (playsink);
628 gst_element_set_state (old, GST_STATE_NULL);
629 gst_object_unref (old);
634 gst_play_sink_get_sink (GstPlaySink * playsink, GstPlaySinkType type)
636 GstElement *result = NULL;
637 GstElement *elem = NULL, *chainp = NULL;
639 GST_PLAY_SINK_LOCK (playsink);
641 case GST_PLAY_SINK_TYPE_AUDIO:
642 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
644 GstPlayAudioChain *chain;
645 if ((chain = (GstPlayAudioChain *) playsink->audiochain))
646 chainp = chain->sink;
647 elem = playsink->audio_sink;
650 case GST_PLAY_SINK_TYPE_VIDEO:
651 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
653 GstPlayVideoChain *chain;
654 if ((chain = (GstPlayVideoChain *) playsink->videochain))
655 chainp = chain->sink;
656 elem = playsink->video_sink;
659 case GST_PLAY_SINK_TYPE_TEXT:
661 GstPlayTextChain *chain;
662 if ((chain = (GstPlayTextChain *) playsink->textchain))
663 chainp = chain->sink;
664 elem = playsink->text_sink;
671 /* we have an active chain with a sink, get the sink */
672 result = gst_object_ref (chainp);
674 /* nothing found, return last configured sink */
675 if (result == NULL && elem)
676 result = gst_object_ref (elem);
677 GST_PLAY_SINK_UNLOCK (playsink);
682 static GstProbeReturn
683 gst_play_sink_vis_blocked (GstPad * tee_pad, GstProbeType type,
684 gpointer type_data, gpointer user_data)
686 GstPlaySink *playsink;
687 GstPlayVisChain *chain;
689 playsink = GST_PLAY_SINK (user_data);
691 GST_PLAY_SINK_LOCK (playsink);
692 GST_DEBUG_OBJECT (playsink, "vis pad blocked");
693 /* now try to change the plugin in the running vis chain */
694 if (!(chain = (GstPlayVisChain *) playsink->vischain))
697 /* unlink the old plugin and unghost the pad */
698 gst_pad_unlink (chain->blockpad, chain->vissinkpad);
699 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad), NULL);
701 /* set the old plugin to NULL and remove */
702 gst_element_set_state (chain->vis, GST_STATE_NULL);
703 gst_bin_remove (GST_BIN_CAST (chain->chain.bin), chain->vis);
705 /* add new plugin and set state to playing */
706 chain->vis = playsink->visualisation;
707 gst_bin_add (GST_BIN_CAST (chain->chain.bin), chain->vis);
708 gst_element_set_state (chain->vis, GST_STATE_PLAYING);
711 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
712 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
715 gst_pad_link_full (chain->blockpad, chain->vissinkpad,
716 GST_PAD_LINK_CHECK_NOTHING);
717 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad),
721 GST_PLAY_SINK_UNLOCK (playsink);
723 /* remove the probe and unblock the pad */
724 return GST_PROBE_REMOVE;
728 gst_play_sink_set_vis_plugin (GstPlaySink * playsink, GstElement * vis)
730 GstPlayVisChain *chain;
732 /* setting NULL means creating the default vis plugin */
734 vis = gst_element_factory_make ("goom", "vis");
736 /* simply return if we don't have a vis plugin here */
740 GST_PLAY_SINK_LOCK (playsink);
741 /* first store the new vis */
742 if (playsink->visualisation)
743 gst_object_unref (playsink->visualisation);
745 gst_object_ref_sink (vis);
746 playsink->visualisation = vis;
748 /* now try to change the plugin in the running vis chain, if we have no chain,
749 * we don't bother, any future vis chain will be created with the new vis
751 if (!(chain = (GstPlayVisChain *) playsink->vischain))
754 /* block the pad, the next time the callback is called we can change the
755 * visualisation. It's possible that this never happens or that the pad was
756 * already blocked. If the callback never happens, we don't have new data so
757 * we don't need the new vis plugin. If the pad was already blocked, the
758 * function returns FALSE but the previous pad block will do the right thing
760 GST_DEBUG_OBJECT (playsink, "blocking vis pad");
761 gst_pad_add_probe (chain->blockpad, GST_PROBE_TYPE_BLOCK,
762 gst_play_sink_vis_blocked, playsink, NULL);
764 GST_PLAY_SINK_UNLOCK (playsink);
770 gst_play_sink_get_vis_plugin (GstPlaySink * playsink)
772 GstElement *result = NULL;
773 GstPlayVisChain *chain;
775 GST_PLAY_SINK_LOCK (playsink);
776 if ((chain = (GstPlayVisChain *) playsink->vischain)) {
777 /* we have an active chain, get the sink */
779 result = gst_object_ref (chain->vis);
781 /* nothing found, return last configured sink */
782 if (result == NULL && playsink->visualisation)
783 result = gst_object_ref (playsink->visualisation);
784 GST_PLAY_SINK_UNLOCK (playsink);
790 gst_play_sink_set_volume (GstPlaySink * playsink, gdouble volume)
792 GstPlayAudioChain *chain;
794 GST_PLAY_SINK_LOCK (playsink);
795 playsink->volume = volume;
796 chain = (GstPlayAudioChain *) playsink->audiochain;
797 if (chain && chain->volume) {
798 GST_LOG_OBJECT (playsink, "elements: volume=%" GST_PTR_FORMAT ", mute=%"
799 GST_PTR_FORMAT "; new volume=%.03f, mute=%d", chain->volume,
800 chain->mute, volume, playsink->mute);
801 /* if there is a mute element or we are not muted, set the volume */
802 if (chain->mute || !playsink->mute)
803 g_object_set (chain->volume, "volume", volume, NULL);
805 GST_LOG_OBJECT (playsink, "no volume element");
806 playsink->volume_changed = TRUE;
808 GST_PLAY_SINK_UNLOCK (playsink);
812 gst_play_sink_get_volume (GstPlaySink * playsink)
815 GstPlayAudioChain *chain;
817 GST_PLAY_SINK_LOCK (playsink);
818 chain = (GstPlayAudioChain *) playsink->audiochain;
819 result = playsink->volume;
820 if (chain && chain->volume) {
821 if (chain->mute || !playsink->mute) {
822 g_object_get (chain->volume, "volume", &result, NULL);
823 playsink->volume = result;
826 GST_PLAY_SINK_UNLOCK (playsink);
832 gst_play_sink_set_mute (GstPlaySink * playsink, gboolean mute)
834 GstPlayAudioChain *chain;
836 GST_PLAY_SINK_LOCK (playsink);
837 playsink->mute = mute;
838 chain = (GstPlayAudioChain *) playsink->audiochain;
841 g_object_set (chain->mute, "mute", mute, NULL);
842 } else if (chain->volume) {
844 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
846 g_object_set (chain->volume, "volume", (gdouble) playsink->volume,
850 playsink->mute_changed = TRUE;
852 GST_PLAY_SINK_UNLOCK (playsink);
856 gst_play_sink_get_mute (GstPlaySink * playsink)
859 GstPlayAudioChain *chain;
861 GST_PLAY_SINK_LOCK (playsink);
862 chain = (GstPlayAudioChain *) playsink->audiochain;
863 if (chain && chain->mute) {
864 g_object_get (chain->mute, "mute", &result, NULL);
865 playsink->mute = result;
867 result = playsink->mute;
869 GST_PLAY_SINK_UNLOCK (playsink);
875 post_missing_element_message (GstPlaySink * playsink, const gchar * name)
879 msg = gst_missing_element_message_new (GST_ELEMENT_CAST (playsink), name);
880 gst_element_post_message (GST_ELEMENT_CAST (playsink), msg);
884 add_chain (GstPlayChain * chain, gboolean add)
886 if (chain->added == add)
890 gst_bin_add (GST_BIN_CAST (chain->playsink), chain->bin);
892 gst_bin_remove (GST_BIN_CAST (chain->playsink), chain->bin);
893 /* we don't want to lose our sink status */
894 GST_OBJECT_FLAG_SET (chain->playsink, GST_ELEMENT_IS_SINK);
903 activate_chain (GstPlayChain * chain, gboolean activate)
907 if (chain->activated == activate)
910 GST_OBJECT_LOCK (chain->playsink);
911 state = GST_STATE_TARGET (chain->playsink);
912 GST_OBJECT_UNLOCK (chain->playsink);
915 gst_element_set_state (chain->bin, state);
917 gst_element_set_state (chain->bin, GST_STATE_NULL);
919 chain->activated = activate;
925 element_is_sink (GstElement * element)
929 GST_OBJECT_LOCK (element);
930 is_sink = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_IS_SINK);
931 GST_OBJECT_UNLOCK (element);
933 GST_DEBUG_OBJECT (element, "is a sink: %s", (is_sink) ? "yes" : "no");
938 element_has_property (GstElement * element, const gchar * pname, GType type)
942 pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (element), pname);
945 GST_DEBUG_OBJECT (element, "no %s property", pname);
949 if (type == G_TYPE_INVALID || type == pspec->value_type ||
950 g_type_is_a (pspec->value_type, type)) {
951 GST_DEBUG_OBJECT (element, "has %s property of type %s", pname,
952 (type == G_TYPE_INVALID) ? "any type" : g_type_name (type));
956 GST_WARNING_OBJECT (element, "has %s property, but property is of type %s "
957 "and we expected it to be of type %s", pname,
958 g_type_name (pspec->value_type), g_type_name (type));
965 const gchar *prop_name;
968 } FindPropertyHelper;
971 find_property (const GValue * item, FindPropertyHelper * helper)
973 GstElement *element = g_value_get_object (item);
974 if (helper->need_sink && !element_is_sink (element)) {
978 if (!element_has_property (element, helper->prop_name, helper->prop_type)) {
982 GST_INFO_OBJECT (element, "found %s with %s property", helper->prop_name,
983 (helper->need_sink) ? "sink" : "element");
984 return 0; /* keep it */
987 /* FIXME: why not move these functions into core? */
988 /* find a sink in the hierarchy with a property named @name. This function does
989 * not increase the refcount of the returned object and thus remains valid as
990 * long as the bin is valid. */
992 gst_play_sink_find_property_sinks (GstPlaySink * playsink, GstElement * obj,
993 const gchar * name, GType expected_type)
995 GstElement *result = NULL;
998 if (element_has_property (obj, name, expected_type)) {
1000 } else if (GST_IS_BIN (obj)) {
1002 GValue item = { 0, };
1003 FindPropertyHelper helper = { name, expected_type, TRUE };
1005 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1006 found = gst_iterator_find_custom (it,
1007 (GCompareFunc) find_property, &item, &helper);
1008 gst_iterator_free (it);
1010 result = g_value_get_object (&item);
1011 /* we don't need the extra ref */
1012 g_value_unset (&item);
1018 /* find an object in the hierarchy with a property named @name */
1020 gst_play_sink_find_property (GstPlaySink * playsink, GstElement * obj,
1021 const gchar * name, GType expected_type)
1023 GstElement *result = NULL;
1026 if (GST_IS_BIN (obj)) {
1028 GValue item = { 0, };
1029 FindPropertyHelper helper = { name, expected_type, FALSE };
1031 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1032 found = gst_iterator_find_custom (it,
1033 (GCompareFunc) find_property, &item, &helper);
1034 gst_iterator_free (it);
1036 result = g_value_dup_object (&item);
1037 g_value_unset (&item);
1039 if (element_has_property (obj, name, expected_type)) {
1041 gst_object_ref (obj);
1048 do_async_start (GstPlaySink * playsink)
1050 GstMessage *message;
1052 if (!playsink->need_async_start) {
1053 GST_INFO_OBJECT (playsink, "no async_start needed");
1057 playsink->async_pending = TRUE;
1059 GST_INFO_OBJECT (playsink, "Sending async_start message");
1060 message = gst_message_new_async_start (GST_OBJECT_CAST (playsink));
1061 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1062 (playsink), message);
1066 do_async_done (GstPlaySink * playsink)
1068 GstMessage *message;
1070 if (playsink->async_pending) {
1071 GST_INFO_OBJECT (playsink, "Sending async_done message");
1072 message = gst_message_new_async_done (GST_OBJECT_CAST (playsink), FALSE);
1073 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1074 (playsink), message);
1076 playsink->async_pending = FALSE;
1079 playsink->need_async_start = FALSE;
1082 /* try to change the state of an element. This function returns the element when
1083 * the state change could be performed. When this function returns NULL an error
1084 * occured and the element is unreffed if @unref is TRUE. */
1086 try_element (GstPlaySink * playsink, GstElement * element, gboolean unref)
1088 GstStateChangeReturn ret;
1091 ret = gst_element_set_state (element, GST_STATE_READY);
1092 if (ret == GST_STATE_CHANGE_FAILURE) {
1093 GST_DEBUG_OBJECT (playsink, "failed state change..");
1094 gst_element_set_state (element, GST_STATE_NULL);
1096 gst_object_unref (element);
1103 /* make the element (bin) that contains the elements needed to perform
1104 * video display. Only used for *raw* video streams.
1106 * +------------------------------------------------------------+
1108 * | +-------+ +----------+ +----------+ +---------+ |
1109 * | | queue | |colorspace| |videoscale| |videosink| |
1110 * | +-sink src-sink src-sink src-sink | |
1111 * | | +-------+ +----------+ +----------+ +---------+ |
1113 * +------------------------------------------------------------+
1116 static GstPlayVideoDeinterlaceChain *
1117 gen_video_deinterlace_chain (GstPlaySink * playsink)
1119 GstPlayVideoDeinterlaceChain *chain;
1122 GstElement *head = NULL, *prev = NULL;
1124 chain = g_new0 (GstPlayVideoDeinterlaceChain, 1);
1125 chain->chain.playsink = playsink;
1127 GST_DEBUG_OBJECT (playsink, "making video deinterlace chain %p", chain);
1129 /* create a bin to hold objects, as we create them we add them to this bin so
1130 * that when something goes wrong we only need to unref the bin */
1131 chain->chain.bin = gst_bin_new ("vdbin");
1132 bin = GST_BIN_CAST (chain->chain.bin);
1133 gst_object_ref_sink (bin);
1135 GST_DEBUG_OBJECT (playsink, "creating " COLORSPACE);
1136 chain->conv = gst_element_factory_make (COLORSPACE, "vdconv");
1137 if (chain->conv == NULL) {
1138 post_missing_element_message (playsink, COLORSPACE);
1139 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1140 (_("Missing element '%s' - check your GStreamer installation."),
1141 COLORSPACE), ("video rendering might fail"));
1143 gst_bin_add (bin, chain->conv);
1148 GST_DEBUG_OBJECT (playsink, "creating deinterlace");
1149 chain->deinterlace = gst_element_factory_make ("deinterlace", "deinterlace");
1150 if (chain->deinterlace == NULL) {
1151 post_missing_element_message (playsink, "deinterlace");
1152 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1153 (_("Missing element '%s' - check your GStreamer installation."),
1154 "deinterlace"), ("deinterlacing won't work"));
1156 gst_bin_add (bin, chain->deinterlace);
1158 if (!gst_element_link_pads_full (prev, "src", chain->deinterlace, "sink",
1159 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1162 head = chain->deinterlace;
1164 prev = chain->deinterlace;
1168 pad = gst_element_get_static_pad (head, "sink");
1169 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1170 gst_object_unref (pad);
1172 chain->sinkpad = gst_ghost_pad_new_no_target ("sink", GST_PAD_SINK);
1176 pad = gst_element_get_static_pad (prev, "src");
1177 chain->srcpad = gst_ghost_pad_new ("src", pad);
1178 gst_object_unref (pad);
1180 chain->srcpad = gst_ghost_pad_new ("src", chain->sinkpad);
1183 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1184 gst_element_add_pad (chain->chain.bin, chain->srcpad);
1190 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1191 (NULL), ("Failed to configure the video deinterlace chain."));
1192 free_chain ((GstPlayChain *) chain);
1197 /* make the element (bin) that contains the elements needed to perform
1200 * +------------------------------------------------------------+
1202 * | +-------+ +----------+ +----------+ +---------+ |
1203 * | | queue | |colorspace| |videoscale| |videosink| |
1204 * | +-sink src-sink src-sink src-sink | |
1205 * | | +-------+ +----------+ +----------+ +---------+ |
1207 * +------------------------------------------------------------+
1210 static GstPlayVideoChain *
1211 gen_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1213 GstPlayVideoChain *chain;
1216 GstElement *head = NULL, *prev = NULL, *elem = NULL;
1218 chain = g_new0 (GstPlayVideoChain, 1);
1219 chain->chain.playsink = playsink;
1220 chain->chain.raw = raw;
1222 GST_DEBUG_OBJECT (playsink, "making video chain %p", chain);
1224 if (playsink->video_sink) {
1225 GST_DEBUG_OBJECT (playsink, "trying configured videosink");
1226 chain->sink = try_element (playsink, playsink->video_sink, FALSE);
1228 /* only try fallback if no specific sink was chosen */
1229 if (chain->sink == NULL) {
1230 GST_DEBUG_OBJECT (playsink, "trying autovideosink");
1231 elem = gst_element_factory_make ("autovideosink", "videosink");
1232 chain->sink = try_element (playsink, elem, TRUE);
1234 if (chain->sink == NULL) {
1235 /* if default sink from config.h is different then try it too */
1236 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1237 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_VIDEOSINK);
1238 elem = gst_element_factory_make (DEFAULT_VIDEOSINK, "videosink");
1239 chain->sink = try_element (playsink, elem, TRUE);
1243 playsink->video_sink = gst_object_ref (chain->sink);
1245 if (chain->sink == NULL)
1249 /* if we can disable async behaviour of the sink, we can avoid adding a
1250 * queue for the audio chain. */
1252 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1255 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1256 async, GST_ELEMENT_NAME (elem));
1257 g_object_set (elem, "async", async, NULL);
1258 chain->async = async;
1260 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1261 chain->async = TRUE;
1264 /* find ts-offset element */
1265 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1266 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1269 /* create a bin to hold objects, as we create them we add them to this bin so
1270 * that when something goes wrong we only need to unref the bin */
1271 chain->chain.bin = gst_bin_new ("vbin");
1272 bin = GST_BIN_CAST (chain->chain.bin);
1273 gst_object_ref_sink (bin);
1274 gst_bin_add (bin, chain->sink);
1276 /* decouple decoder from sink, this improves playback quite a lot since the
1277 * decoder can continue while the sink blocks for synchronisation. We don't
1278 * need a lot of buffers as this consumes a lot of memory and we don't want
1279 * too little because else we would be context switching too quickly. */
1280 chain->queue = gst_element_factory_make ("queue", "vqueue");
1281 if (chain->queue == NULL) {
1282 post_missing_element_message (playsink, "queue");
1283 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1284 (_("Missing element '%s' - check your GStreamer installation."),
1285 "queue"), ("video rendering might be suboptimal"));
1289 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1290 "max-size-bytes", 0, "max-size-time", (gint64) 0, "silent", TRUE, NULL);
1291 gst_bin_add (bin, chain->queue);
1292 head = prev = chain->queue;
1295 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
1296 GST_DEBUG_OBJECT (playsink, "creating videoconverter");
1298 g_object_new (GST_TYPE_PLAY_SINK_VIDEO_CONVERT, "name", "vconv", NULL);
1299 gst_bin_add (bin, chain->conv);
1301 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
1302 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1311 GST_DEBUG_OBJECT (playsink, "linking to sink");
1312 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
1313 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1317 pad = gst_element_get_static_pad (head, "sink");
1318 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1319 gst_object_unref (pad);
1321 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1328 if (!elem && !playsink->video_sink) {
1329 post_missing_element_message (playsink, "autovideosink");
1330 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1331 post_missing_element_message (playsink, DEFAULT_VIDEOSINK);
1332 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1333 (_("Both autovideosink and %s elements are missing."),
1334 DEFAULT_VIDEOSINK), (NULL));
1336 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1337 (_("The autovideosink element is missing.")), (NULL));
1340 if (playsink->video_sink) {
1341 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1342 (_("Configured videosink %s is not working."),
1343 GST_ELEMENT_NAME (playsink->video_sink)), (NULL));
1344 } else if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1345 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1346 (_("Both autovideosink and %s elements are not working."),
1347 DEFAULT_VIDEOSINK), (NULL));
1349 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1350 (_("The autovideosink element is not working.")), (NULL));
1353 free_chain ((GstPlayChain *) chain);
1358 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1359 (NULL), ("Failed to configure the video sink."));
1360 /* checking sink made it READY */
1361 gst_element_set_state (chain->sink, GST_STATE_NULL);
1362 /* Remove chain from the bin to allow reuse later */
1363 gst_bin_remove (bin, chain->sink);
1364 free_chain ((GstPlayChain *) chain);
1370 setup_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1373 GstPlayVideoChain *chain;
1374 GstStateChangeReturn ret;
1376 chain = playsink->videochain;
1378 chain->chain.raw = raw;
1380 /* if the chain was active we don't do anything */
1381 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
1384 /* try to set the sink element to READY again */
1385 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
1386 if (ret == GST_STATE_CHANGE_FAILURE)
1389 /* find ts-offset element */
1391 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1392 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1395 /* if we can disable async behaviour of the sink, we can avoid adding a
1396 * queue for the audio chain. */
1398 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1401 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1402 async, GST_ELEMENT_NAME (elem));
1403 g_object_set (elem, "async", async, NULL);
1404 chain->async = async;
1406 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1407 chain->async = TRUE;
1412 /* make an element for playback of video with subtitles embedded.
1413 * Only used for *raw* video streams.
1415 * +--------------------------------------------+
1417 * | +--------+ +-----------------+ |
1418 * | | queue | | subtitleoverlay | |
1419 * video--src sink---video_sink | |
1420 * | +--------+ | src--src
1421 * text------------------text_sink | |
1422 * | +-----------------+ |
1423 * +--------------------------------------------+
1426 static GstPlayTextChain *
1427 gen_text_chain (GstPlaySink * playsink)
1429 GstPlayTextChain *chain;
1432 GstPad *videosinkpad, *textsinkpad, *srcpad;
1434 chain = g_new0 (GstPlayTextChain, 1);
1435 chain->chain.playsink = playsink;
1437 GST_DEBUG_OBJECT (playsink, "making text chain %p", chain);
1439 chain->chain.bin = gst_bin_new ("tbin");
1440 bin = GST_BIN_CAST (chain->chain.bin);
1441 gst_object_ref_sink (bin);
1443 videosinkpad = textsinkpad = srcpad = NULL;
1445 /* first try to hook the text pad to the custom sink */
1446 if (playsink->text_sink) {
1447 GST_DEBUG_OBJECT (playsink, "trying configured textsink");
1448 chain->sink = try_element (playsink, playsink->text_sink, FALSE);
1451 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1454 /* make sure the sparse subtitles don't participate in the preroll */
1455 g_object_set (elem, "async", FALSE, NULL);
1456 GST_DEBUG_OBJECT (playsink, "adding custom text sink");
1457 gst_bin_add (bin, chain->sink);
1458 /* NOTE streamsynchronizer needs streams decoupled */
1459 /* make a little queue */
1460 chain->queue = gst_element_factory_make ("queue", "subqueue");
1461 if (chain->queue == NULL) {
1462 post_missing_element_message (playsink, "queue");
1463 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1464 (_("Missing element '%s' - check your GStreamer installation."),
1465 "queue"), ("rendering might be suboptimal"));
1467 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1468 "max-size-bytes", 0, "max-size-time", (gint64) 0,
1469 "silent", TRUE, NULL);
1470 gst_bin_add (bin, chain->queue);
1472 /* we have a custom sink, this will be our textsinkpad */
1473 if (gst_element_link_pads_full (chain->queue, "src", chain->sink,
1474 "sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
1475 /* we're all fine now and we can add the sink to the chain */
1476 GST_DEBUG_OBJECT (playsink, "using custom text sink");
1477 textsinkpad = gst_element_get_static_pad (chain->queue, "sink");
1479 GST_WARNING_OBJECT (playsink,
1480 "can't find a sink pad on custom text sink");
1481 gst_bin_remove (bin, chain->sink);
1482 gst_bin_remove (bin, chain->queue);
1484 chain->queue = NULL;
1486 /* try to set sync to true but it's no biggie when we can't */
1488 gst_play_sink_find_property_sinks (playsink, chain->sink,
1489 "sync", G_TYPE_BOOLEAN)))
1490 g_object_set (elem, "sync", TRUE, NULL);
1493 gst_bin_remove (bin, chain->sink);
1495 GST_WARNING_OBJECT (playsink,
1496 "can't find async property in custom text sink");
1499 if (textsinkpad == NULL) {
1500 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1501 (_("Custom text sink element is not usable.")),
1502 ("fallback to default textoverlay"));
1506 if (textsinkpad == NULL) {
1507 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
1508 /* make a little queue */
1509 chain->queue = gst_element_factory_make ("queue", "vqueue");
1510 if (chain->queue == NULL) {
1511 post_missing_element_message (playsink, "queue");
1512 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1513 (_("Missing element '%s' - check your GStreamer installation."),
1514 "queue"), ("video rendering might be suboptimal"));
1516 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1517 "max-size-bytes", 0, "max-size-time", (gint64) 0,
1518 "silent", TRUE, NULL);
1519 gst_bin_add (bin, chain->queue);
1520 videosinkpad = gst_element_get_static_pad (chain->queue, "sink");
1524 gst_element_factory_make ("subtitleoverlay", "suboverlay");
1525 if (chain->overlay == NULL) {
1526 post_missing_element_message (playsink, "subtitleoverlay");
1527 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1528 (_("Missing element '%s' - check your GStreamer installation."),
1529 "subtitleoverlay"), ("subtitle rendering disabled"));
1531 GstElement *element;
1533 gst_bin_add (bin, chain->overlay);
1535 g_object_set (G_OBJECT (chain->overlay), "silent", FALSE, NULL);
1536 if (playsink->font_desc) {
1537 g_object_set (G_OBJECT (chain->overlay), "font-desc",
1538 playsink->font_desc, NULL);
1540 if (playsink->subtitle_encoding) {
1541 g_object_set (G_OBJECT (chain->overlay), "subtitle-encoding",
1542 playsink->subtitle_encoding, NULL);
1545 gst_element_link_pads_full (chain->queue, "src", chain->overlay,
1546 "video_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS);
1548 /* make another little queue to decouple streams */
1549 element = gst_element_factory_make ("queue", "subqueue");
1550 if (element == NULL) {
1551 post_missing_element_message (playsink, "queue");
1552 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1553 (_("Missing element '%s' - check your GStreamer installation."),
1554 "queue"), ("rendering might be suboptimal"));
1556 g_object_set (G_OBJECT (element), "max-size-buffers", 3,
1557 "max-size-bytes", 0, "max-size-time", (gint64) 0,
1558 "silent", TRUE, NULL);
1559 gst_bin_add (bin, element);
1560 gst_element_link_pads_full (element, "src", chain->overlay,
1561 "subtitle_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS);
1562 textsinkpad = gst_element_get_static_pad (element, "sink");
1563 srcpad = gst_element_get_static_pad (chain->overlay, "src");
1569 if (videosinkpad == NULL) {
1570 /* if we still don't have a videosink, we don't have an overlay. the only
1571 * thing we can do is insert an identity and ghost the src
1573 chain->identity = gst_element_factory_make ("identity", "tidentity");
1574 if (chain->identity == NULL) {
1575 post_missing_element_message (playsink, "identity");
1576 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1577 (_("Missing element '%s' - check your GStreamer installation."),
1578 "identity"), (NULL));
1580 g_object_set (chain->identity, "signal-handoffs", FALSE, NULL);
1581 g_object_set (chain->identity, "silent", TRUE, NULL);
1582 gst_bin_add (bin, chain->identity);
1583 srcpad = gst_element_get_static_pad (chain->identity, "src");
1584 videosinkpad = gst_element_get_static_pad (chain->identity, "sink");
1588 /* expose the ghostpads */
1590 chain->videosinkpad = gst_ghost_pad_new ("sink", videosinkpad);
1591 gst_object_unref (videosinkpad);
1592 gst_element_add_pad (chain->chain.bin, chain->videosinkpad);
1595 chain->textsinkpad = gst_ghost_pad_new ("text_sink", textsinkpad);
1596 gst_object_unref (textsinkpad);
1597 gst_element_add_pad (chain->chain.bin, chain->textsinkpad);
1600 chain->srcpad = gst_ghost_pad_new ("src", srcpad);
1601 gst_object_unref (srcpad);
1602 gst_element_add_pad (chain->chain.bin, chain->srcpad);
1609 notify_volume_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
1613 g_object_get (object, "volume", &vol, NULL);
1614 playsink->volume = vol;
1616 g_object_notify (G_OBJECT (playsink), "volume");
1620 notify_mute_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
1624 g_object_get (object, "mute", &mute, NULL);
1625 playsink->mute = mute;
1627 g_object_notify (G_OBJECT (playsink), "mute");
1630 /* make the chain that contains the elements needed to perform
1633 * We add a tee as the first element so that we can link the visualisation chain
1634 * to it when requested.
1636 * +-------------------------------------------------------------+
1638 * | +---------+ +----------+ +---------+ +---------+ |
1639 * | |audioconv| |audioscale| | volume | |audiosink| |
1640 * | +-srck src-sink src-sink src-sink | |
1641 * | | +---------+ +----------+ +---------+ +---------+ |
1643 * +-------------------------------------------------------------+
1645 static GstPlayAudioChain *
1646 gen_audio_chain (GstPlaySink * playsink, gboolean raw)
1648 GstPlayAudioChain *chain;
1650 gboolean have_volume;
1652 GstElement *head, *prev, *elem = NULL;
1654 chain = g_new0 (GstPlayAudioChain, 1);
1655 chain->chain.playsink = playsink;
1656 chain->chain.raw = raw;
1658 GST_DEBUG_OBJECT (playsink, "making audio chain %p", chain);
1660 if (playsink->audio_sink) {
1661 GST_DEBUG_OBJECT (playsink, "trying configured audiosink %" GST_PTR_FORMAT,
1662 playsink->audio_sink);
1663 chain->sink = try_element (playsink, playsink->audio_sink, FALSE);
1665 /* only try fallback if no specific sink was chosen */
1666 if (chain->sink == NULL) {
1667 GST_DEBUG_OBJECT (playsink, "trying autoaudiosink");
1668 elem = gst_element_factory_make ("autoaudiosink", "audiosink");
1669 chain->sink = try_element (playsink, elem, TRUE);
1671 if (chain->sink == NULL) {
1672 /* if default sink from config.h is different then try it too */
1673 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1674 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_AUDIOSINK);
1675 elem = gst_element_factory_make (DEFAULT_AUDIOSINK, "audiosink");
1676 chain->sink = try_element (playsink, elem, TRUE);
1680 playsink->audio_sink = gst_object_ref (chain->sink);
1682 if (chain->sink == NULL)
1685 chain->chain.bin = gst_bin_new ("abin");
1686 bin = GST_BIN_CAST (chain->chain.bin);
1687 gst_object_ref_sink (bin);
1688 gst_bin_add (bin, chain->sink);
1690 /* we have to add a queue when we need to decouple for the video sink in
1691 * visualisations and for streamsynchronizer */
1692 GST_DEBUG_OBJECT (playsink, "adding audio queue");
1693 chain->queue = gst_element_factory_make ("queue", "aqueue");
1694 if (chain->queue == NULL) {
1695 post_missing_element_message (playsink, "queue");
1696 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1697 (_("Missing element '%s' - check your GStreamer installation."),
1698 "queue"), ("audio playback and visualizations might not work"));
1702 g_object_set (chain->queue, "silent", TRUE, NULL);
1703 gst_bin_add (bin, chain->queue);
1704 prev = head = chain->queue;
1707 /* find ts-offset element */
1708 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1709 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1712 /* check if the sink, or something within the sink, has the volume property.
1713 * If it does we don't need to add a volume element. */
1715 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
1718 chain->volume = elem;
1720 g_signal_connect (chain->volume, "notify::volume",
1721 G_CALLBACK (notify_volume_cb), playsink);
1723 GST_DEBUG_OBJECT (playsink, "the sink has a volume property");
1725 chain->sink_volume = TRUE;
1726 /* if the sink also has a mute property we can use this as well. We'll only
1727 * use the mute property if there is a volume property. We can simulate the
1728 * mute with the volume otherwise. */
1730 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
1733 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
1734 g_signal_connect (chain->mute, "notify::mute",
1735 G_CALLBACK (notify_mute_cb), playsink);
1737 /* use the sink to control the volume and mute */
1738 if (playsink->volume_changed) {
1739 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
1740 playsink->volume_changed = FALSE;
1742 if (playsink->mute_changed) {
1744 g_object_set (chain->mute, "mute", playsink->mute, NULL);
1747 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
1749 playsink->mute_changed = FALSE;
1752 /* no volume, we need to add a volume element when we can */
1753 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
1754 have_volume = FALSE;
1755 chain->sink_volume = FALSE;
1758 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO) || (!have_volume
1759 && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
1760 GST_DEBUG_OBJECT (playsink, "creating audioconvert");
1762 g_object_new (GST_TYPE_PLAY_SINK_AUDIO_CONVERT, "name", "aconv", NULL);
1763 gst_bin_add (bin, chain->conv);
1765 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
1766 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1773 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv)->use_converters =
1774 !(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO);
1775 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv)->use_volume = (!have_volume
1776 && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME);
1778 if (!have_volume && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME) {
1779 GstPlaySinkAudioConvert *conv =
1780 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
1783 chain->volume = conv->volume;
1786 g_signal_connect (chain->volume, "notify::volume",
1787 G_CALLBACK (notify_volume_cb), playsink);
1789 /* volume also has the mute property */
1790 chain->mute = chain->volume;
1791 g_signal_connect (chain->mute, "notify::mute",
1792 G_CALLBACK (notify_mute_cb), playsink);
1794 /* configure with the latest volume and mute */
1795 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume,
1797 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
1803 /* we only have to link to the previous element if we have something in
1804 * front of the sink */
1805 GST_DEBUG_OBJECT (playsink, "linking to sink");
1806 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
1807 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1811 /* post a warning if we have no way to configure the volume */
1813 GST_ELEMENT_WARNING (playsink, STREAM, NOT_IMPLEMENTED,
1814 (_("No volume control found")), ("Volume/mute is not available"));
1817 /* and ghost the sinkpad of the headmost element */
1818 GST_DEBUG_OBJECT (playsink, "ghosting sink pad");
1819 pad = gst_element_get_static_pad (head, "sink");
1820 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1821 gst_object_unref (pad);
1822 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1829 if (!elem && !playsink->audio_sink) {
1830 post_missing_element_message (playsink, "autoaudiosink");
1831 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1832 post_missing_element_message (playsink, DEFAULT_AUDIOSINK);
1833 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1834 (_("Both autoaudiosink and %s elements are missing."),
1835 DEFAULT_AUDIOSINK), (NULL));
1837 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1838 (_("The autoaudiosink element is missing.")), (NULL));
1841 if (playsink->audio_sink) {
1842 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1843 (_("Configured audiosink %s is not working."),
1844 GST_ELEMENT_NAME (playsink->audio_sink)), (NULL));
1845 } else if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1846 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1847 (_("Both autoaudiosink and %s elements are not working."),
1848 DEFAULT_AUDIOSINK), (NULL));
1850 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1851 (_("The autoaudiosink element is not working.")), (NULL));
1854 free_chain ((GstPlayChain *) chain);
1859 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1860 (NULL), ("Failed to configure the audio sink."));
1861 /* checking sink made it READY */
1862 gst_element_set_state (chain->sink, GST_STATE_NULL);
1863 /* Remove chain from the bin to allow reuse later */
1864 gst_bin_remove (bin, chain->sink);
1865 free_chain ((GstPlayChain *) chain);
1871 setup_audio_chain (GstPlaySink * playsink, gboolean raw)
1874 GstPlayAudioChain *chain;
1875 GstStateChangeReturn ret;
1877 chain = playsink->audiochain;
1879 chain->chain.raw = raw;
1881 /* if the chain was active we don't do anything */
1882 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
1885 /* try to set the sink element to READY again */
1886 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
1887 if (ret == GST_STATE_CHANGE_FAILURE)
1890 /* find ts-offset element */
1891 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1892 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1895 /* check if the sink, or something within the sink, has the volume property.
1896 * If it does we don't need to add a volume element. */
1898 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
1901 chain->volume = elem;
1903 if (playsink->volume_changed) {
1904 GST_DEBUG_OBJECT (playsink, "the sink has a volume property, setting %f",
1906 /* use the sink to control the volume */
1907 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
1908 playsink->volume_changed = FALSE;
1911 g_signal_connect (chain->volume, "notify::volume",
1912 G_CALLBACK (notify_volume_cb), playsink);
1913 /* if the sink also has a mute property we can use this as well. We'll only
1914 * use the mute property if there is a volume property. We can simulate the
1915 * mute with the volume otherwise. */
1917 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
1920 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
1921 g_signal_connect (chain->mute, "notify::mute",
1922 G_CALLBACK (notify_mute_cb), playsink);
1925 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv)->use_volume = FALSE;
1927 GstPlaySinkAudioConvert *conv =
1928 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
1930 /* no volume, we need to add a volume element when we can */
1931 conv->use_volume = TRUE;
1932 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
1934 /* Disconnect signals */
1935 disconnect_chain (chain, playsink);
1938 chain->volume = conv->volume;
1939 chain->mute = chain->volume;
1941 g_signal_connect (chain->volume, "notify::volume",
1942 G_CALLBACK (notify_volume_cb), playsink);
1944 g_signal_connect (chain->mute, "notify::mute",
1945 G_CALLBACK (notify_mute_cb), playsink);
1947 /* configure with the latest volume and mute */
1948 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
1949 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
1952 GST_DEBUG_OBJECT (playsink, "reusing existing volume element");
1958 * +-------------------------------------------------------------------+
1960 * | +----------+ +------------+ +----------+ +-------+ |
1961 * | | visqueue | | audioconv | | audiores | | vis | |
1962 * | +-sink src-sink + samp src-sink src-sink src-+ |
1963 * | | +----------+ +------------+ +----------+ +-------+ | |
1965 * +-------------------------------------------------------------------+
1968 static GstPlayVisChain *
1969 gen_vis_chain (GstPlaySink * playsink)
1971 GstPlayVisChain *chain;
1977 chain = g_new0 (GstPlayVisChain, 1);
1978 chain->chain.playsink = playsink;
1980 GST_DEBUG_OBJECT (playsink, "making vis chain %p", chain);
1982 chain->chain.bin = gst_bin_new ("visbin");
1983 bin = GST_BIN_CAST (chain->chain.bin);
1984 gst_object_ref_sink (bin);
1986 /* we're queuing raw audio here, we can remove this queue when we can disable
1987 * async behaviour in the video sink. */
1988 chain->queue = gst_element_factory_make ("queue", "visqueue");
1989 if (chain->queue == NULL)
1991 g_object_set (chain->queue, "silent", TRUE, NULL);
1992 gst_bin_add (bin, chain->queue);
1994 chain->conv = gst_element_factory_make ("audioconvert", "aconv");
1995 if (chain->conv == NULL)
1996 goto no_audioconvert;
1997 gst_bin_add (bin, chain->conv);
1999 chain->resample = gst_element_factory_make ("audioresample", "aresample");
2000 if (chain->resample == NULL)
2001 goto no_audioresample;
2002 gst_bin_add (bin, chain->resample);
2004 /* this pad will be used for blocking the dataflow and switching the vis
2006 chain->blockpad = gst_element_get_static_pad (chain->resample, "src");
2008 if (playsink->visualisation) {
2009 GST_DEBUG_OBJECT (playsink, "trying configure vis");
2010 chain->vis = try_element (playsink, playsink->visualisation, FALSE);
2012 if (chain->vis == NULL) {
2013 GST_DEBUG_OBJECT (playsink, "trying goom");
2014 elem = gst_element_factory_make ("goom", "vis");
2015 chain->vis = try_element (playsink, elem, TRUE);
2017 if (chain->vis == NULL)
2020 gst_bin_add (bin, chain->vis);
2022 res = gst_element_link_pads_full (chain->queue, "src", chain->conv, "sink",
2023 GST_PAD_LINK_CHECK_NOTHING);
2025 gst_element_link_pads_full (chain->conv, "src", chain->resample, "sink",
2026 GST_PAD_LINK_CHECK_NOTHING);
2028 gst_element_link_pads_full (chain->resample, "src", chain->vis, "sink",
2029 GST_PAD_LINK_CHECK_NOTHING);
2033 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
2034 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
2036 pad = gst_element_get_static_pad (chain->queue, "sink");
2037 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
2038 gst_object_unref (pad);
2039 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
2041 chain->srcpad = gst_ghost_pad_new ("src", chain->vissrcpad);
2042 gst_element_add_pad (chain->chain.bin, chain->srcpad);
2049 post_missing_element_message (playsink, "queue");
2050 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2051 (_("Missing element '%s' - check your GStreamer installation."),
2053 free_chain ((GstPlayChain *) chain);
2058 post_missing_element_message (playsink, "audioconvert");
2059 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2060 (_("Missing element '%s' - check your GStreamer installation."),
2061 "audioconvert"), ("possibly a liboil version mismatch?"));
2062 free_chain ((GstPlayChain *) chain);
2067 post_missing_element_message (playsink, "audioresample");
2068 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2069 (_("Missing element '%s' - check your GStreamer installation."),
2070 "audioresample"), (NULL));
2071 free_chain ((GstPlayChain *) chain);
2076 post_missing_element_message (playsink, "goom");
2077 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2078 (_("Missing element '%s' - check your GStreamer installation."),
2080 free_chain ((GstPlayChain *) chain);
2085 GST_ELEMENT_ERROR (playsink, CORE, PAD,
2086 (NULL), ("Failed to configure the visualisation element."));
2087 /* element made it to READY */
2088 gst_element_set_state (chain->vis, GST_STATE_NULL);
2089 free_chain ((GstPlayChain *) chain);
2094 /* this function is called when all the request pads are requested and when we
2095 * have to construct the final pipeline. Based on the flags we construct the
2096 * final output pipelines.
2099 gst_play_sink_reconfigure (GstPlaySink * playsink)
2102 gboolean need_audio, need_video, need_deinterlace, need_vis, need_text;
2104 GST_DEBUG_OBJECT (playsink, "reconfiguring");
2106 /* assume we need nothing */
2107 need_audio = need_video = need_deinterlace = need_vis = need_text = FALSE;
2109 GST_PLAY_SINK_LOCK (playsink);
2110 GST_OBJECT_LOCK (playsink);
2111 /* get flags, there are protected with the object lock */
2112 flags = playsink->flags;
2113 GST_OBJECT_UNLOCK (playsink);
2115 /* figure out which components we need */
2116 if (flags & GST_PLAY_FLAG_TEXT && playsink->text_pad) {
2117 /* we have subtitles and we are requested to show it */
2121 if (((flags & GST_PLAY_FLAG_VIDEO)
2122 || (flags & GST_PLAY_FLAG_NATIVE_VIDEO)) && playsink->video_pad) {
2123 /* we have video and we are requested to show it */
2126 /* we only deinterlace if native video is not requested and
2127 * we have raw video */
2128 if ((flags & GST_PLAY_FLAG_DEINTERLACE)
2129 && !(flags & GST_PLAY_FLAG_NATIVE_VIDEO) && playsink->video_pad_raw)
2130 need_deinterlace = TRUE;
2133 if (playsink->audio_pad) {
2134 if ((flags & GST_PLAY_FLAG_AUDIO) || (flags & GST_PLAY_FLAG_NATIVE_AUDIO)) {
2137 if (playsink->audio_pad_raw) {
2138 /* only can do vis with raw uncompressed audio */
2139 if (flags & GST_PLAY_FLAG_VIS && !need_video) {
2140 /* also add video when we add visualisation */
2147 /* we have a text_pad and we need text rendering, in this case we need a
2148 * video_pad to combine the video with the text or visualizations */
2149 if (need_text && !need_video) {
2150 if (playsink->video_pad) {
2152 } else if (need_audio) {
2153 GST_ELEMENT_WARNING (playsink, STREAM, FORMAT,
2154 (_("Can't play a text file without video or visualizations.")),
2155 ("Have text pad but no video pad or visualizations"));
2158 GST_ELEMENT_ERROR (playsink, STREAM, FORMAT,
2159 (_("Can't play a text file without video or visualizations.")),
2160 ("Have text pad but no video pad or visualizations"));
2161 GST_PLAY_SINK_UNLOCK (playsink);
2166 GST_DEBUG_OBJECT (playsink, "audio:%d, video:%d, vis:%d, text:%d", need_audio,
2167 need_video, need_vis, need_text);
2169 /* set up video pipeline */
2171 gboolean raw, async;
2173 /* we need a raw sink when we do vis or when we have a raw pad */
2174 raw = need_vis ? TRUE : playsink->video_pad_raw;
2175 /* we try to set the sink async=FALSE when we need vis, this way we can
2176 * avoid a queue in the audio chain. */
2179 GST_DEBUG_OBJECT (playsink, "adding video, raw %d",
2180 playsink->video_pad_raw);
2182 if (playsink->videochain) {
2183 /* try to reactivate the chain */
2184 if (!setup_video_chain (playsink, raw, async)) {
2185 if (playsink->video_sinkpad_stream_synchronizer) {
2186 gst_element_release_request_pad (GST_ELEMENT_CAST
2187 (playsink->stream_synchronizer),
2188 playsink->video_sinkpad_stream_synchronizer);
2189 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2190 playsink->video_sinkpad_stream_synchronizer = NULL;
2191 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2192 playsink->video_srcpad_stream_synchronizer = NULL;
2195 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2197 /* Remove the sink from the bin to keep its state
2198 * and unparent it to allow reuse */
2199 if (playsink->videochain->sink)
2200 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
2201 playsink->videochain->sink);
2203 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2204 free_chain ((GstPlayChain *) playsink->videochain);
2205 playsink->videochain = NULL;
2209 if (!playsink->videochain)
2210 playsink->videochain = gen_video_chain (playsink, raw, async);
2211 if (!playsink->videochain)
2214 if (!playsink->video_sinkpad_stream_synchronizer) {
2215 GValue item = { 0, };
2218 playsink->video_sinkpad_stream_synchronizer =
2219 gst_element_get_request_pad (GST_ELEMENT_CAST
2220 (playsink->stream_synchronizer), "sink_%d");
2221 it = gst_pad_iterate_internal_links
2222 (playsink->video_sinkpad_stream_synchronizer);
2224 gst_iterator_next (it, &item);
2225 playsink->video_srcpad_stream_synchronizer = g_value_dup_object (&item);
2226 g_value_unset (&item);
2227 g_assert (playsink->video_srcpad_stream_synchronizer);
2228 gst_iterator_free (it);
2231 if (playsink->video_pad)
2232 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad),
2233 playsink->video_sinkpad_stream_synchronizer);
2235 if (need_deinterlace) {
2236 if (!playsink->videodeinterlacechain)
2237 playsink->videodeinterlacechain =
2238 gen_video_deinterlace_chain (playsink);
2239 if (!playsink->videodeinterlacechain)
2242 GST_DEBUG_OBJECT (playsink, "adding video deinterlace chain");
2244 GST_DEBUG_OBJECT (playsink, "setting up deinterlacing chain");
2246 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
2247 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
2249 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2250 playsink->videodeinterlacechain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2252 if (playsink->videodeinterlacechain) {
2253 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2254 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
2259 GST_DEBUG_OBJECT (playsink, "adding video chain");
2260 add_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
2261 activate_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
2262 /* if we are not part of vis or subtitles, set the ghostpad target */
2263 if (!need_vis && !need_text && (!playsink->textchain
2264 || !playsink->text_pad)) {
2265 GST_DEBUG_OBJECT (playsink, "ghosting video sinkpad");
2266 if (need_deinterlace)
2267 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
2268 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2270 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2271 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2274 GST_DEBUG_OBJECT (playsink, "no video needed");
2275 if (playsink->videochain) {
2276 GST_DEBUG_OBJECT (playsink, "removing video chain");
2277 if (playsink->vischain) {
2280 GST_DEBUG_OBJECT (playsink, "unlinking vis chain");
2282 /* also had visualisation, release the tee srcpad before we then
2283 * unlink the video from it */
2284 if (playsink->audio_tee_vissrc) {
2285 gst_element_release_request_pad (playsink->audio_tee,
2286 playsink->audio_tee_vissrc);
2287 gst_object_unref (playsink->audio_tee_vissrc);
2288 playsink->audio_tee_vissrc = NULL;
2291 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2292 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
2295 if (playsink->video_sinkpad_stream_synchronizer) {
2296 gst_element_release_request_pad (GST_ELEMENT_CAST
2297 (playsink->stream_synchronizer),
2298 playsink->video_sinkpad_stream_synchronizer);
2299 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2300 playsink->video_sinkpad_stream_synchronizer = NULL;
2301 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2302 playsink->video_srcpad_stream_synchronizer = NULL;
2305 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2306 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2307 if (playsink->videochain->ts_offset)
2308 gst_object_unref (playsink->videochain->ts_offset);
2309 playsink->videochain->ts_offset = NULL;
2312 if (playsink->videodeinterlacechain) {
2313 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2314 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2317 if (playsink->video_pad)
2318 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
2324 GST_DEBUG_OBJECT (playsink, "adding audio");
2326 /* get a raw sink if we are asked for a raw pad */
2327 raw = playsink->audio_pad_raw;
2329 if (playsink->audiochain) {
2330 /* try to reactivate the chain */
2331 if (!setup_audio_chain (playsink, raw)) {
2332 GST_DEBUG_OBJECT (playsink, "removing current audio chain");
2333 if (playsink->audio_tee_asrc) {
2334 gst_element_release_request_pad (playsink->audio_tee,
2335 playsink->audio_tee_asrc);
2336 gst_object_unref (playsink->audio_tee_asrc);
2337 playsink->audio_tee_asrc = NULL;
2340 if (playsink->audio_sinkpad_stream_synchronizer) {
2341 gst_element_release_request_pad (GST_ELEMENT_CAST
2342 (playsink->stream_synchronizer),
2343 playsink->audio_sinkpad_stream_synchronizer);
2344 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
2345 playsink->audio_sinkpad_stream_synchronizer = NULL;
2346 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
2347 playsink->audio_srcpad_stream_synchronizer = NULL;
2350 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2352 /* Remove the sink from the bin to keep its state
2353 * and unparent it to allow reuse */
2354 if (playsink->audiochain->sink)
2355 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
2356 playsink->audiochain->sink);
2358 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2359 disconnect_chain (playsink->audiochain, playsink);
2360 playsink->audiochain->volume = NULL;
2361 playsink->audiochain->mute = NULL;
2362 if (playsink->audiochain->ts_offset)
2363 gst_object_unref (playsink->audiochain->ts_offset);
2364 playsink->audiochain->ts_offset = NULL;
2365 free_chain ((GstPlayChain *) playsink->audiochain);
2366 playsink->audiochain = NULL;
2367 playsink->volume_changed = playsink->mute_changed = FALSE;
2371 if (!playsink->audiochain) {
2372 GST_DEBUG_OBJECT (playsink, "creating new audio chain");
2373 playsink->audiochain = gen_audio_chain (playsink, raw);
2376 if (!playsink->audio_sinkpad_stream_synchronizer) {
2377 GValue item = { 0, };
2380 playsink->audio_sinkpad_stream_synchronizer =
2381 gst_element_get_request_pad (GST_ELEMENT_CAST
2382 (playsink->stream_synchronizer), "sink_%d");
2383 it = gst_pad_iterate_internal_links
2384 (playsink->audio_sinkpad_stream_synchronizer);
2386 gst_iterator_next (it, &item);
2387 playsink->audio_srcpad_stream_synchronizer = g_value_dup_object (&item);
2388 g_value_unset (&item);
2389 g_assert (playsink->audio_srcpad_stream_synchronizer);
2390 gst_iterator_free (it);
2393 if (playsink->audiochain) {
2394 GST_DEBUG_OBJECT (playsink, "adding audio chain");
2395 if (playsink->audio_tee_asrc == NULL) {
2396 playsink->audio_tee_asrc =
2397 gst_element_get_request_pad (playsink->audio_tee, "src%d");
2399 add_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
2400 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
2401 gst_pad_link_full (playsink->audio_tee_asrc,
2402 playsink->audio_sinkpad_stream_synchronizer,
2403 GST_PAD_LINK_CHECK_NOTHING);
2404 gst_pad_link_full (playsink->audio_srcpad_stream_synchronizer,
2405 playsink->audiochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2408 GST_DEBUG_OBJECT (playsink, "no audio needed");
2409 /* we have no audio or we are requested to not play audio */
2410 if (playsink->audiochain) {
2411 GST_DEBUG_OBJECT (playsink, "removing audio chain");
2412 /* release the audio pad */
2413 if (playsink->audio_tee_asrc) {
2414 gst_element_release_request_pad (playsink->audio_tee,
2415 playsink->audio_tee_asrc);
2416 gst_object_unref (playsink->audio_tee_asrc);
2417 playsink->audio_tee_asrc = NULL;
2420 if (playsink->audio_sinkpad_stream_synchronizer) {
2421 gst_element_release_request_pad (GST_ELEMENT_CAST
2422 (playsink->stream_synchronizer),
2423 playsink->audio_sinkpad_stream_synchronizer);
2424 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
2425 playsink->audio_sinkpad_stream_synchronizer = NULL;
2426 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
2427 playsink->audio_srcpad_stream_synchronizer = NULL;
2430 if (playsink->audiochain->sink_volume) {
2431 disconnect_chain (playsink->audiochain, playsink);
2432 playsink->audiochain->volume = NULL;
2433 playsink->audiochain->mute = NULL;
2434 if (playsink->audiochain->ts_offset)
2435 gst_object_unref (playsink->audiochain->ts_offset);
2436 playsink->audiochain->ts_offset = NULL;
2438 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2439 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2446 if (!playsink->vischain)
2447 playsink->vischain = gen_vis_chain (playsink);
2449 GST_DEBUG_OBJECT (playsink, "adding visualisation");
2451 if (playsink->vischain) {
2452 GST_DEBUG_OBJECT (playsink, "setting up vis chain");
2454 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2455 add_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
2456 activate_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
2457 if (playsink->audio_tee_vissrc == NULL) {
2458 playsink->audio_tee_vissrc =
2459 gst_element_get_request_pad (playsink->audio_tee, "src%d");
2461 gst_pad_link_full (playsink->audio_tee_vissrc,
2462 playsink->vischain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2463 gst_pad_link_full (srcpad, playsink->video_sinkpad_stream_synchronizer,
2464 GST_PAD_LINK_CHECK_NOTHING);
2465 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2466 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2467 gst_object_unref (srcpad);
2470 GST_DEBUG_OBJECT (playsink, "no vis needed");
2471 if (playsink->vischain) {
2472 if (playsink->audio_tee_vissrc) {
2473 gst_element_release_request_pad (playsink->audio_tee,
2474 playsink->audio_tee_vissrc);
2475 gst_object_unref (playsink->audio_tee_vissrc);
2476 playsink->audio_tee_vissrc = NULL;
2478 GST_DEBUG_OBJECT (playsink, "removing vis chain");
2479 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
2480 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
2485 GST_DEBUG_OBJECT (playsink, "adding text");
2486 if (!playsink->textchain) {
2487 GST_DEBUG_OBJECT (playsink, "creating text chain");
2488 playsink->textchain = gen_text_chain (playsink);
2490 if (playsink->textchain) {
2493 GST_DEBUG_OBJECT (playsink, "adding text chain");
2494 if (playsink->textchain->overlay)
2495 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", FALSE,
2497 add_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
2499 if (!playsink->text_sinkpad_stream_synchronizer) {
2500 GValue item = { 0, };
2502 playsink->text_sinkpad_stream_synchronizer =
2503 gst_element_get_request_pad (GST_ELEMENT_CAST
2504 (playsink->stream_synchronizer), "sink_%d");
2505 it = gst_pad_iterate_internal_links
2506 (playsink->text_sinkpad_stream_synchronizer);
2508 gst_iterator_next (it, &item);
2509 playsink->text_srcpad_stream_synchronizer = g_value_dup_object (&item);
2510 g_value_unset (&item);
2511 g_assert (playsink->text_srcpad_stream_synchronizer);
2512 gst_iterator_free (it);
2514 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad),
2515 playsink->text_sinkpad_stream_synchronizer);
2516 gst_pad_link_full (playsink->text_srcpad_stream_synchronizer,
2517 playsink->textchain->textsinkpad, GST_PAD_LINK_CHECK_NOTHING);
2524 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2525 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
2526 gst_pad_link_full (srcpad, playsink->textchain->videosinkpad,
2527 GST_PAD_LINK_CHECK_NOTHING);
2528 gst_object_unref (srcpad);
2530 if (need_deinterlace)
2531 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
2532 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
2534 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2535 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
2537 gst_pad_link_full (playsink->textchain->srcpad,
2538 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2540 activate_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
2543 GST_DEBUG_OBJECT (playsink, "no text needed");
2544 /* we have no subtitles/text or we are requested to not show them */
2546 if (playsink->text_sinkpad_stream_synchronizer) {
2547 gst_element_release_request_pad (GST_ELEMENT_CAST
2548 (playsink->stream_synchronizer),
2549 playsink->text_sinkpad_stream_synchronizer);
2550 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
2551 playsink->text_sinkpad_stream_synchronizer = NULL;
2552 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
2553 playsink->text_srcpad_stream_synchronizer = NULL;
2556 if (playsink->textchain) {
2557 if (playsink->text_pad == NULL) {
2558 /* no text pad, remove the chain entirely */
2559 GST_DEBUG_OBJECT (playsink, "removing text chain");
2560 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
2561 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
2563 /* we have a chain and a textpad, turn the subtitles off */
2564 GST_DEBUG_OBJECT (playsink, "turning off the text");
2565 if (playsink->textchain->overlay)
2566 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", TRUE,
2570 if (!need_video && playsink->video_pad) {
2571 if (playsink->video_sinkpad_stream_synchronizer) {
2572 gst_element_release_request_pad (GST_ELEMENT_CAST
2573 (playsink->stream_synchronizer),
2574 playsink->video_sinkpad_stream_synchronizer);
2575 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2576 playsink->video_sinkpad_stream_synchronizer = NULL;
2577 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2578 playsink->video_srcpad_stream_synchronizer = NULL;
2581 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
2584 if (playsink->text_pad && !playsink->textchain)
2585 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad), NULL);
2587 update_av_offset (playsink);
2588 do_async_done (playsink);
2589 GST_PLAY_SINK_UNLOCK (playsink);
2596 /* gen_ chain already posted error */
2597 GST_DEBUG_OBJECT (playsink, "failed to setup chain");
2598 GST_PLAY_SINK_UNLOCK (playsink);
2604 * gst_play_sink_set_flags:
2605 * @playsink: a #GstPlaySink
2606 * @flags: #GstPlayFlags
2608 * Configure @flags on @playsink. The flags control the behaviour of @playsink
2609 * when constructing the sink pipelins.
2611 * Returns: TRUE if the flags could be configured.
2614 gst_play_sink_set_flags (GstPlaySink * playsink, GstPlayFlags flags)
2616 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), FALSE);
2618 GST_OBJECT_LOCK (playsink);
2619 playsink->flags = flags;
2620 GST_OBJECT_UNLOCK (playsink);
2626 * gst_play_sink_get_flags:
2627 * @playsink: a #GstPlaySink
2629 * Get the flags of @playsink. That flags control the behaviour of the sink when
2630 * it constructs the sink pipelines.
2632 * Returns: the currently configured #GstPlayFlags.
2635 gst_play_sink_get_flags (GstPlaySink * playsink)
2639 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), 0);
2641 GST_OBJECT_LOCK (playsink);
2642 res = playsink->flags;
2643 GST_OBJECT_UNLOCK (playsink);
2649 gst_play_sink_set_font_desc (GstPlaySink * playsink, const gchar * desc)
2651 GstPlayTextChain *chain;
2653 GST_PLAY_SINK_LOCK (playsink);
2654 chain = (GstPlayTextChain *) playsink->textchain;
2655 g_free (playsink->font_desc);
2656 playsink->font_desc = g_strdup (desc);
2657 if (chain && chain->overlay) {
2658 g_object_set (chain->overlay, "font-desc", desc, NULL);
2660 GST_PLAY_SINK_UNLOCK (playsink);
2664 gst_play_sink_get_font_desc (GstPlaySink * playsink)
2666 gchar *result = NULL;
2667 GstPlayTextChain *chain;
2669 GST_PLAY_SINK_LOCK (playsink);
2670 chain = (GstPlayTextChain *) playsink->textchain;
2671 if (chain && chain->overlay) {
2672 g_object_get (chain->overlay, "font-desc", &result, NULL);
2673 playsink->font_desc = g_strdup (result);
2675 result = g_strdup (playsink->font_desc);
2677 GST_PLAY_SINK_UNLOCK (playsink);
2683 gst_play_sink_set_subtitle_encoding (GstPlaySink * playsink,
2684 const gchar * encoding)
2686 GstPlayTextChain *chain;
2688 GST_PLAY_SINK_LOCK (playsink);
2689 chain = (GstPlayTextChain *) playsink->textchain;
2690 g_free (playsink->subtitle_encoding);
2691 playsink->subtitle_encoding = g_strdup (encoding);
2692 if (chain && chain->overlay) {
2693 g_object_set (chain->overlay, "subtitle-encoding", encoding, NULL);
2695 GST_PLAY_SINK_UNLOCK (playsink);
2699 gst_play_sink_get_subtitle_encoding (GstPlaySink * playsink)
2701 gchar *result = NULL;
2702 GstPlayTextChain *chain;
2704 GST_PLAY_SINK_LOCK (playsink);
2705 chain = (GstPlayTextChain *) playsink->textchain;
2706 if (chain && chain->overlay) {
2707 g_object_get (chain->overlay, "subtitle-encoding", &result, NULL);
2708 playsink->subtitle_encoding = g_strdup (result);
2710 result = g_strdup (playsink->subtitle_encoding);
2712 GST_PLAY_SINK_UNLOCK (playsink);
2718 update_av_offset (GstPlaySink * playsink)
2721 GstPlayAudioChain *achain;
2722 GstPlayVideoChain *vchain;
2724 av_offset = playsink->av_offset;
2725 achain = (GstPlayAudioChain *) playsink->audiochain;
2726 vchain = (GstPlayVideoChain *) playsink->videochain;
2728 if (achain && vchain && achain->ts_offset && vchain->ts_offset) {
2729 g_object_set (achain->ts_offset, "ts-offset", MAX (0, -av_offset), NULL);
2730 g_object_set (vchain->ts_offset, "ts-offset", MAX (0, av_offset), NULL);
2732 GST_LOG_OBJECT (playsink, "no ts_offset elements");
2737 gst_play_sink_set_av_offset (GstPlaySink * playsink, gint64 av_offset)
2739 GST_PLAY_SINK_LOCK (playsink);
2740 playsink->av_offset = av_offset;
2741 update_av_offset (playsink);
2742 GST_PLAY_SINK_UNLOCK (playsink);
2746 gst_play_sink_get_av_offset (GstPlaySink * playsink)
2750 GST_PLAY_SINK_LOCK (playsink);
2751 result = playsink->av_offset;
2752 GST_PLAY_SINK_UNLOCK (playsink);
2758 * gst_play_sink_get_last_frame:
2759 * @playsink: a #GstPlaySink
2761 * Get the last displayed frame from @playsink. This frame is in the native
2762 * format of the sink element, the caps on the result buffer contain the format
2763 * of the frame data.
2765 * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
2769 gst_play_sink_get_last_frame (GstPlaySink * playsink)
2771 GstBuffer *result = NULL;
2772 GstPlayVideoChain *chain;
2774 GST_PLAY_SINK_LOCK (playsink);
2775 GST_DEBUG_OBJECT (playsink, "taking last frame");
2776 /* get the video chain if we can */
2777 if ((chain = (GstPlayVideoChain *) playsink->videochain)) {
2778 GST_DEBUG_OBJECT (playsink, "found video chain");
2779 /* see if the chain is active */
2780 if (chain->chain.activated && chain->sink) {
2783 GST_DEBUG_OBJECT (playsink, "video chain active and has a sink");
2785 /* find and get the last-buffer property now */
2787 gst_play_sink_find_property (playsink, chain->sink,
2788 "last-buffer", GST_TYPE_BUFFER))) {
2789 GST_DEBUG_OBJECT (playsink, "getting last-buffer property");
2790 g_object_get (elem, "last-buffer", &result, NULL);
2791 gst_object_unref (elem);
2795 GST_PLAY_SINK_UNLOCK (playsink);
2801 * gst_play_sink_convert_frame:
2802 * @playsink: a #GstPlaySink
2805 * Get the last displayed frame from @playsink. If caps is %NULL, the video will
2806 * be in the native format of the sink element and the caps on the buffer
2807 * describe the format of the frame. If @caps is not %NULL, the video
2808 * frame will be converted to the format of the caps.
2810 * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
2811 * available or when the conversion failed.
2814 gst_play_sink_convert_frame (GstPlaySink * playsink, GstCaps * caps)
2818 result = gst_play_sink_get_last_frame (playsink);
2819 if (result != NULL && caps != NULL) {
2823 /* FIXME, need to get the input buffer caps */
2824 temp = gst_video_convert_frame (result, NULL, caps, 25 * GST_SECOND, &err);
2825 gst_buffer_unref (result);
2826 if (temp == NULL && err) {
2827 /* I'm really uncertain whether we should make playsink post an error
2828 * on the bus or not. It's not like it's a critical issue regarding
2829 * playsink behaviour. */
2830 GST_ERROR ("Error converting frame: %s", err->message);
2838 is_raw_structure (GstStructure * s)
2842 name = gst_structure_get_name (s);
2844 if (g_str_has_prefix (name, "video/x-raw") ||
2845 g_str_has_prefix (name, "audio/x-raw"))
2851 is_raw_pad (GstPad * pad)
2853 GstPad *peer = gst_pad_get_peer (pad);
2855 gboolean raw = TRUE;
2860 caps = gst_pad_get_current_caps (peer);
2864 caps = gst_pad_get_caps (peer, NULL);
2866 n = gst_caps_get_size (caps);
2867 for (i = 0; i < n; i++) {
2868 gboolean r = is_raw_structure (gst_caps_get_structure (caps, i));
2872 } else if (raw != r) {
2873 GST_ERROR_OBJECT (pad,
2874 "Caps contains raw and non-raw structures: %" GST_PTR_FORMAT, caps);
2880 raw = is_raw_structure (gst_caps_get_structure (caps, 0));
2882 gst_caps_unref (caps);
2883 gst_object_unref (peer);
2888 static GstProbeReturn
2889 sinkpad_blocked_cb (GstPad * blockedpad, GstProbeType type, gpointer type_data,
2890 gpointer user_data);
2893 video_set_blocked (GstPlaySink * playsink, gboolean blocked)
2895 if (playsink->video_pad) {
2897 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
2898 (playsink->video_pad)));
2899 if (blocked && playsink->video_block_id == 0) {
2900 playsink->video_block_id =
2901 gst_pad_add_probe (opad, GST_PROBE_TYPE_BLOCK, sinkpad_blocked_cb,
2902 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
2903 } else if (!blocked && playsink->video_block_id) {
2904 gst_pad_remove_probe (opad, playsink->video_block_id);
2905 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO_RAW);
2906 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO);
2907 playsink->video_block_id = 0;
2908 playsink->video_pad_blocked = FALSE;
2910 gst_object_unref (opad);
2915 audio_set_blocked (GstPlaySink * playsink, gboolean blocked)
2917 if (playsink->audio_pad) {
2919 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
2920 (playsink->audio_pad)));
2921 if (blocked && playsink->audio_block_id == 0) {
2922 playsink->audio_block_id =
2923 gst_pad_add_probe (opad, GST_PROBE_TYPE_BLOCK, sinkpad_blocked_cb,
2924 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
2925 } else if (!blocked && playsink->audio_block_id) {
2926 gst_pad_remove_probe (opad, playsink->audio_block_id);
2927 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO_RAW);
2928 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO);
2929 playsink->audio_block_id = 0;
2930 playsink->audio_pad_blocked = FALSE;
2932 gst_object_unref (opad);
2937 text_set_blocked (GstPlaySink * playsink, gboolean blocked)
2939 if (playsink->text_pad) {
2941 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
2942 (playsink->text_pad)));
2943 if (blocked && playsink->text_block_id == 0) {
2944 playsink->text_block_id =
2945 gst_pad_add_probe (opad, GST_PROBE_TYPE_BLOCK, sinkpad_blocked_cb,
2946 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
2947 } else if (!blocked && playsink->text_block_id) {
2948 gst_pad_remove_probe (opad, playsink->text_block_id);
2949 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_TEXT);
2950 playsink->text_block_id = 0;
2951 playsink->text_pad_blocked = FALSE;
2953 gst_object_unref (opad);
2957 static GstProbeReturn
2958 sinkpad_blocked_cb (GstPad * blockedpad, GstProbeType type, gpointer type_data,
2961 GstPlaySink *playsink = (GstPlaySink *) user_data;
2964 GST_PLAY_SINK_LOCK (playsink);
2966 pad = GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (blockedpad)));
2967 if (pad == playsink->video_pad) {
2968 playsink->video_pad_blocked = TRUE;
2969 GST_DEBUG_OBJECT (pad, "Video pad blocked");
2970 } else if (pad == playsink->audio_pad) {
2971 playsink->audio_pad_blocked = TRUE;
2972 GST_DEBUG_OBJECT (pad, "Audio pad blocked");
2973 } else if (pad == playsink->text_pad) {
2974 playsink->text_pad_blocked = TRUE;
2975 GST_DEBUG_OBJECT (pad, "Text pad blocked");
2978 /* We reconfigure when for ALL streams:
2979 * * there isn't a pad
2980 * * OR the pad is blocked
2981 * * OR there are no pending blocks on that pad
2984 if ((!playsink->video_pad || playsink->video_pad_blocked
2985 || !PENDING_VIDEO_BLOCK (playsink)) && (!playsink->audio_pad
2986 || playsink->audio_pad_blocked || !PENDING_AUDIO_BLOCK (playsink))
2987 && (!playsink->text_pad || playsink->text_pad_blocked
2988 || !PENDING_TEXT_BLOCK (playsink))) {
2989 GST_DEBUG_OBJECT (playsink, "All pads blocked -- reconfiguring");
2991 if (playsink->video_pad) {
2992 playsink->video_pad_raw = is_raw_pad (playsink->video_pad);
2993 GST_DEBUG_OBJECT (playsink, "Video pad is raw: %d",
2994 playsink->video_pad_raw);
2997 if (playsink->audio_pad) {
2998 playsink->audio_pad_raw = is_raw_pad (playsink->audio_pad);
2999 GST_DEBUG_OBJECT (playsink, "Audio pad is raw: %d",
3000 playsink->audio_pad_raw);
3003 gst_play_sink_reconfigure (playsink);
3005 video_set_blocked (playsink, FALSE);
3006 audio_set_blocked (playsink, FALSE);
3007 text_set_blocked (playsink, FALSE);
3010 gst_object_unref (pad);
3012 GST_PLAY_SINK_UNLOCK (playsink);
3014 return GST_PROBE_OK;
3018 caps_notify_cb (GstPad * pad, GParamSpec * unused, GstPlaySink * playsink)
3020 gboolean reconfigure = FALSE;
3024 g_object_get (pad, "caps", &caps, NULL);
3028 if (pad == playsink->audio_pad) {
3029 raw = is_raw_pad (pad);
3030 reconfigure = (!!playsink->audio_pad_raw != !!raw)
3031 && playsink->audiochain;
3032 GST_DEBUG_OBJECT (pad,
3033 "Audio caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3035 } else if (pad == playsink->video_pad) {
3036 raw = is_raw_pad (pad);
3037 reconfigure = (!!playsink->video_pad_raw != !!raw)
3038 && playsink->videochain;
3039 GST_DEBUG_OBJECT (pad,
3040 "Video caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3044 gst_caps_unref (caps);
3047 GST_PLAY_SINK_LOCK (playsink);
3048 video_set_blocked (playsink, TRUE);
3049 audio_set_blocked (playsink, TRUE);
3050 text_set_blocked (playsink, TRUE);
3051 GST_PLAY_SINK_UNLOCK (playsink);
3056 * gst_play_sink_request_pad
3057 * @playsink: a #GstPlaySink
3058 * @type: a #GstPlaySinkType
3060 * Create or return a pad of @type.
3062 * Returns: a #GstPad of @type or %NULL when the pad could not be created.
3065 gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type)
3068 gboolean created = FALSE;
3069 gboolean activate = TRUE;
3070 const gchar *pad_name = NULL;
3071 gulong *block_id = NULL;
3073 GST_DEBUG_OBJECT (playsink, "request pad type %d", type);
3075 GST_PLAY_SINK_LOCK (playsink);
3077 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
3078 case GST_PLAY_SINK_TYPE_AUDIO:
3079 pad_name = "audio_sink";
3080 if (!playsink->audio_tee) {
3081 GST_LOG_OBJECT (playsink, "creating tee");
3082 /* create tee when needed. This element will feed the audio sink chain
3083 * and the vis chain. */
3084 playsink->audio_tee = gst_element_factory_make ("tee", "audiotee");
3085 if (playsink->audio_tee == NULL) {
3086 post_missing_element_message (playsink, "tee");
3087 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
3088 (_("Missing element '%s' - check your GStreamer installation."),
3093 playsink->audio_tee_sink =
3094 gst_element_get_static_pad (playsink->audio_tee, "sink");
3095 gst_bin_add (GST_BIN_CAST (playsink), playsink->audio_tee);
3096 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
3099 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
3101 if (!playsink->audio_pad) {
3102 GST_LOG_OBJECT (playsink, "ghosting tee sinkpad");
3103 playsink->audio_pad =
3104 gst_ghost_pad_new (pad_name, playsink->audio_tee_sink);
3105 g_signal_connect (G_OBJECT (playsink->audio_pad), "notify::caps",
3106 G_CALLBACK (caps_notify_cb), playsink);
3109 playsink->audio_pad_raw = FALSE;
3110 res = playsink->audio_pad;
3111 block_id = &playsink->audio_block_id;
3113 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
3114 case GST_PLAY_SINK_TYPE_VIDEO:
3115 pad_name = "video_sink";
3116 if (!playsink->video_pad) {
3117 GST_LOG_OBJECT (playsink, "ghosting videosink");
3118 playsink->video_pad =
3119 gst_ghost_pad_new_no_target (pad_name, GST_PAD_SINK);
3120 g_signal_connect (G_OBJECT (playsink->video_pad), "notify::caps",
3121 G_CALLBACK (caps_notify_cb), playsink);
3124 playsink->video_pad_raw = FALSE;
3125 res = playsink->video_pad;
3126 block_id = &playsink->video_block_id;
3128 case GST_PLAY_SINK_TYPE_TEXT:
3129 GST_LOG_OBJECT (playsink, "ghosting text");
3130 if (!playsink->text_pad) {
3131 playsink->text_pad =
3132 gst_ghost_pad_new_no_target ("text_sink", GST_PAD_SINK);
3135 res = playsink->text_pad;
3136 block_id = &playsink->text_block_id;
3138 case GST_PLAY_SINK_TYPE_FLUSHING:
3142 /* we need a unique padname for the flushing pad. */
3143 padname = g_strdup_printf ("flushing_%d", playsink->count);
3144 res = gst_ghost_pad_new_no_target (padname, GST_PAD_SINK);
3155 GST_PLAY_SINK_UNLOCK (playsink);
3157 if (created && res) {
3158 /* we have to add the pad when it's active or we get an error when the
3159 * element is 'running' */
3160 gst_pad_set_active (res, TRUE);
3161 gst_element_add_pad (GST_ELEMENT_CAST (playsink), res);
3162 if (block_id && *block_id == 0) {
3164 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (res)));
3167 gst_pad_add_probe (blockpad, GST_PROBE_TYPE_BLOCK, sinkpad_blocked_cb,
3168 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3169 PENDING_FLAG_SET (playsink, type);
3170 gst_object_unref (blockpad);
3173 gst_pad_set_active (res, activate);
3180 gst_play_sink_request_new_pad (GstElement * element, GstPadTemplate * templ,
3181 const gchar * name, const GstCaps * caps)
3185 GstPlaySinkType type;
3186 const gchar *tplname;
3188 g_return_val_if_fail (templ != NULL, NULL);
3190 GST_DEBUG_OBJECT (element, "name:%s", name);
3192 psink = GST_PLAY_SINK (element);
3193 tplname = GST_PAD_TEMPLATE_NAME_TEMPLATE (templ);
3195 /* Figure out the GstPlaySinkType based on the template */
3196 if (!strcmp (tplname, "audio_sink"))
3197 type = GST_PLAY_SINK_TYPE_AUDIO;
3198 else if (!strcmp (tplname, "audio_raw_sink"))
3199 type = GST_PLAY_SINK_TYPE_AUDIO_RAW;
3200 else if (!strcmp (tplname, "video_sink"))
3201 type = GST_PLAY_SINK_TYPE_VIDEO;
3202 else if (!strcmp (tplname, "video_raw_sink"))
3203 type = GST_PLAY_SINK_TYPE_VIDEO_RAW;
3204 else if (!strcmp (tplname, "text_sink"))
3205 type = GST_PLAY_SINK_TYPE_TEXT;
3207 goto unknown_template;
3209 pad = gst_play_sink_request_pad (psink, type);
3213 GST_WARNING_OBJECT (element, "Unknown pad template");
3218 gst_play_sink_release_pad (GstPlaySink * playsink, GstPad * pad)
3220 GstPad **res = NULL;
3221 gboolean untarget = TRUE;
3223 GST_DEBUG_OBJECT (playsink, "release pad %" GST_PTR_FORMAT, pad);
3225 GST_PLAY_SINK_LOCK (playsink);
3226 if (pad == playsink->video_pad) {
3227 res = &playsink->video_pad;
3228 g_signal_handlers_disconnect_by_func (playsink->video_pad, caps_notify_cb,
3230 } else if (pad == playsink->audio_pad) {
3231 res = &playsink->audio_pad;
3232 g_signal_handlers_disconnect_by_func (playsink->audio_pad, caps_notify_cb,
3234 } else if (pad == playsink->text_pad) {
3235 res = &playsink->text_pad;
3237 /* try to release the given pad anyway, these could be the FLUSHING pads. */
3241 GST_PLAY_SINK_UNLOCK (playsink);
3244 GST_DEBUG_OBJECT (playsink, "deactivate pad %" GST_PTR_FORMAT, *res);
3245 gst_pad_set_active (*res, FALSE);
3247 GST_DEBUG_OBJECT (playsink, "untargeting pad %" GST_PTR_FORMAT, *res);
3248 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (*res), NULL);
3250 GST_DEBUG_OBJECT (playsink, "remove pad %" GST_PTR_FORMAT, *res);
3251 gst_element_remove_pad (GST_ELEMENT_CAST (playsink), *res);
3257 gst_play_sink_release_request_pad (GstElement * element, GstPad * pad)
3259 GstPlaySink *psink = GST_PLAY_SINK (element);
3261 gst_play_sink_release_pad (psink, pad);
3265 gst_play_sink_handle_message (GstBin * bin, GstMessage * message)
3267 GstPlaySink *playsink;
3269 playsink = GST_PLAY_SINK_CAST (bin);
3271 switch (GST_MESSAGE_TYPE (message)) {
3272 case GST_MESSAGE_STEP_DONE:
3277 gboolean flush, intermediate, eos;
3280 GST_INFO_OBJECT (playsink, "Handling step-done message");
3281 gst_message_parse_step_done (message, &format, &amount, &rate, &flush,
3282 &intermediate, &duration, &eos);
3284 if (format == GST_FORMAT_BUFFERS) {
3285 /* for the buffer format, we align the other streams */
3286 if (playsink->audiochain) {
3290 gst_event_new_step (GST_FORMAT_TIME, duration, rate, flush,
3293 if (!gst_element_send_event (playsink->audiochain->chain.bin, event)) {
3294 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
3298 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
3302 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
3307 /* Send an event to our sinks until one of them works; don't then send to the
3308 * remaining sinks (unlike GstBin)
3309 * Special case: If a text sink is set we need to send the event
3310 * to them in case it's source is different from the a/v stream's source.
3313 gst_play_sink_send_event_to_sink (GstPlaySink * playsink, GstEvent * event)
3315 gboolean res = TRUE;
3317 if (playsink->textchain && playsink->textchain->sink) {
3318 gst_event_ref (event);
3319 if ((res = gst_element_send_event (playsink->textchain->chain.bin, event))) {
3320 GST_DEBUG_OBJECT (playsink, "Sent event succesfully to text sink");
3322 GST_DEBUG_OBJECT (playsink, "Event failed when sent to text sink");
3326 if (playsink->videochain) {
3327 gst_event_ref (event);
3328 if ((res = gst_element_send_event (playsink->videochain->chain.bin, event))) {
3329 GST_DEBUG_OBJECT (playsink, "Sent event succesfully to video sink");
3332 GST_DEBUG_OBJECT (playsink, "Event failed when sent to video sink");
3334 if (playsink->audiochain) {
3335 gst_event_ref (event);
3336 if ((res = gst_element_send_event (playsink->audiochain->chain.bin, event))) {
3337 GST_DEBUG_OBJECT (playsink, "Sent event succesfully to audio sink");
3340 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
3344 gst_event_unref (event);
3348 /* We only want to send the event to a single sink (overriding GstBin's
3349 * behaviour), but we want to keep GstPipeline's behaviour - wrapping seek
3350 * events appropriately. So, this is a messy duplication of code. */
3352 gst_play_sink_send_event (GstElement * element, GstEvent * event)
3354 gboolean res = FALSE;
3355 GstEventType event_type = GST_EVENT_TYPE (event);
3356 GstPlaySink *playsink;
3358 playsink = GST_PLAY_SINK_CAST (element);
3360 switch (event_type) {
3361 case GST_EVENT_SEEK:
3362 GST_DEBUG_OBJECT (element, "Sending event to a sink");
3363 res = gst_play_sink_send_event_to_sink (playsink, event);
3365 case GST_EVENT_STEP:
3370 gboolean flush, intermediate;
3372 gst_event_parse_step (event, &format, &amount, &rate, &flush,
3375 if (format == GST_FORMAT_BUFFERS) {
3376 /* for buffers, we will try to step video frames, for other formats we
3377 * send the step to all sinks */
3378 res = gst_play_sink_send_event_to_sink (playsink, event);
3381 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
3388 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
3395 static GstStateChangeReturn
3396 gst_play_sink_change_state (GstElement * element, GstStateChange transition)
3398 GstStateChangeReturn ret;
3399 GstStateChangeReturn bret;
3401 GstPlaySink *playsink;
3403 playsink = GST_PLAY_SINK (element);
3405 switch (transition) {
3406 case GST_STATE_CHANGE_READY_TO_PAUSED:
3407 playsink->need_async_start = TRUE;
3408 /* we want to go async to PAUSED until we managed to configure and add the
3410 do_async_start (playsink);
3411 ret = GST_STATE_CHANGE_ASYNC;
3413 case GST_STATE_CHANGE_PAUSED_TO_READY:
3414 /* unblock all pads here */
3415 GST_PLAY_SINK_LOCK (playsink);
3416 video_set_blocked (playsink, FALSE);
3417 audio_set_blocked (playsink, FALSE);
3418 text_set_blocked (playsink, FALSE);
3419 GST_PLAY_SINK_UNLOCK (playsink);
3421 case GST_STATE_CHANGE_READY_TO_NULL:
3422 if (playsink->audiochain && playsink->audiochain->sink_volume) {
3423 /* remove our links to the mute and volume elements when they were
3424 * provided by a sink */
3425 disconnect_chain (playsink->audiochain, playsink);
3426 playsink->audiochain->volume = NULL;
3427 playsink->audiochain->mute = NULL;
3430 if (playsink->audiochain && playsink->audiochain->ts_offset) {
3431 gst_object_unref (playsink->audiochain->ts_offset);
3432 playsink->audiochain->ts_offset = NULL;
3435 if (playsink->videochain && playsink->videochain->ts_offset) {
3436 gst_object_unref (playsink->videochain->ts_offset);
3437 playsink->videochain->ts_offset = NULL;
3439 ret = GST_STATE_CHANGE_SUCCESS;
3442 /* all other state changes return SUCCESS by default, this value can be
3443 * overridden by the result of the children */
3444 ret = GST_STATE_CHANGE_SUCCESS;
3448 /* do the state change of the children */
3450 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->change_state (element,
3452 /* now look at the result of our children and adjust the return value */
3454 case GST_STATE_CHANGE_FAILURE:
3455 /* failure, we stop */
3456 goto activate_failed;
3457 case GST_STATE_CHANGE_NO_PREROLL:
3458 /* some child returned NO_PREROLL. This is strange but we never know. We
3459 * commit our async state change (if any) and return the NO_PREROLL */
3460 do_async_done (playsink);
3463 case GST_STATE_CHANGE_ASYNC:
3464 /* some child was async, return this */
3468 /* return our previously configured return value */
3472 switch (transition) {
3473 case GST_STATE_CHANGE_READY_TO_PAUSED:
3475 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
3476 /* FIXME Release audio device when we implement that */
3477 playsink->need_async_start = TRUE;
3479 case GST_STATE_CHANGE_PAUSED_TO_READY:{
3480 if (playsink->video_sinkpad_stream_synchronizer) {
3481 gst_element_release_request_pad (GST_ELEMENT_CAST
3482 (playsink->stream_synchronizer),
3483 playsink->video_sinkpad_stream_synchronizer);
3484 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3485 playsink->video_sinkpad_stream_synchronizer = NULL;
3486 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3487 playsink->video_srcpad_stream_synchronizer = NULL;
3489 if (playsink->audio_sinkpad_stream_synchronizer) {
3490 gst_element_release_request_pad (GST_ELEMENT_CAST
3491 (playsink->stream_synchronizer),
3492 playsink->audio_sinkpad_stream_synchronizer);
3493 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
3494 playsink->audio_sinkpad_stream_synchronizer = NULL;
3495 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
3496 playsink->audio_srcpad_stream_synchronizer = NULL;
3498 if (playsink->text_sinkpad_stream_synchronizer) {
3499 gst_element_release_request_pad (GST_ELEMENT_CAST
3500 (playsink->stream_synchronizer),
3501 playsink->text_sinkpad_stream_synchronizer);
3502 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
3503 playsink->text_sinkpad_stream_synchronizer = NULL;
3504 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
3505 playsink->text_srcpad_stream_synchronizer = NULL;
3509 case GST_STATE_CHANGE_READY_TO_NULL:
3510 /* remove sinks we added */
3511 if (playsink->videodeinterlacechain) {
3512 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
3514 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3516 if (playsink->videochain) {
3517 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3518 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3520 if (playsink->audiochain) {
3521 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3522 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3524 if (playsink->vischain) {
3525 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3526 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3528 if (playsink->textchain) {
3529 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3530 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3532 do_async_done (playsink);
3533 /* when going to READY, keep elements around as long as possible,
3534 * so they may be re-used faster next time/url around.
3535 * when really going to NULL, clean up everything completely. */
3536 if (transition == GST_STATE_CHANGE_READY_TO_NULL) {
3538 /* Unparent the sinks to allow reuse */
3539 if (playsink->videochain && playsink->videochain->sink)
3540 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
3541 playsink->videochain->sink);
3542 if (playsink->audiochain && playsink->audiochain->sink)
3543 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
3544 playsink->audiochain->sink);
3545 if (playsink->textchain && playsink->textchain->sink)
3546 gst_bin_remove (GST_BIN_CAST (playsink->textchain->chain.bin),
3547 playsink->textchain->sink);
3549 if (playsink->audio_sink != NULL)
3550 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
3551 if (playsink->video_sink != NULL)
3552 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
3553 if (playsink->visualisation != NULL)
3554 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
3555 if (playsink->text_sink != NULL)
3556 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
3558 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
3559 playsink->videodeinterlacechain = NULL;
3560 free_chain ((GstPlayChain *) playsink->videochain);
3561 playsink->videochain = NULL;
3562 free_chain ((GstPlayChain *) playsink->audiochain);
3563 playsink->audiochain = NULL;
3564 free_chain ((GstPlayChain *) playsink->vischain);
3565 playsink->vischain = NULL;
3566 free_chain ((GstPlayChain *) playsink->textchain);
3567 playsink->textchain = NULL;
3578 GST_DEBUG_OBJECT (element,
3579 "element failed to change states -- activation problem?");
3580 return GST_STATE_CHANGE_FAILURE;
3585 gst_play_sink_set_property (GObject * object, guint prop_id,
3586 const GValue * value, GParamSpec * spec)
3588 GstPlaySink *playsink = GST_PLAY_SINK (object);
3592 gst_play_sink_set_flags (playsink, g_value_get_flags (value));
3595 gst_play_sink_set_volume (playsink, g_value_get_double (value));
3598 gst_play_sink_set_mute (playsink, g_value_get_boolean (value));
3600 case PROP_FONT_DESC:
3601 gst_play_sink_set_font_desc (playsink, g_value_get_string (value));
3603 case PROP_SUBTITLE_ENCODING:
3604 gst_play_sink_set_subtitle_encoding (playsink,
3605 g_value_get_string (value));
3607 case PROP_VIS_PLUGIN:
3608 gst_play_sink_set_vis_plugin (playsink, g_value_get_object (value));
3610 case PROP_AV_OFFSET:
3611 gst_play_sink_set_av_offset (playsink, g_value_get_int64 (value));
3614 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
3620 gst_play_sink_get_property (GObject * object, guint prop_id,
3621 GValue * value, GParamSpec * spec)
3623 GstPlaySink *playsink = GST_PLAY_SINK (object);
3627 g_value_set_flags (value, gst_play_sink_get_flags (playsink));
3630 g_value_set_double (value, gst_play_sink_get_volume (playsink));
3633 g_value_set_boolean (value, gst_play_sink_get_mute (playsink));
3635 case PROP_FONT_DESC:
3636 g_value_take_string (value, gst_play_sink_get_font_desc (playsink));
3638 case PROP_SUBTITLE_ENCODING:
3639 g_value_take_string (value,
3640 gst_play_sink_get_subtitle_encoding (playsink));
3642 case PROP_VIS_PLUGIN:
3643 g_value_take_object (value, gst_play_sink_get_vis_plugin (playsink));
3646 gst_value_take_buffer (value, gst_play_sink_get_last_frame (playsink));
3648 case PROP_AV_OFFSET:
3649 g_value_set_int64 (value, gst_play_sink_get_av_offset (playsink));
3652 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
3659 gst_play_sink_plugin_init (GstPlugin * plugin)
3661 GST_DEBUG_CATEGORY_INIT (gst_play_sink_debug, "playsink", 0, "play bin");
3663 return gst_element_register (plugin, "playsink", GST_RANK_NONE,
3664 GST_TYPE_PLAY_SINK);