2 * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
27 #include <gst/gst-i18n-plugin.h>
28 #include <gst/pbutils/pbutils.h>
29 #include <gst/video/video.h>
31 #include "gstplaysink.h"
32 #include "gststreamsynchronizer.h"
34 GST_DEBUG_CATEGORY_STATIC (gst_play_sink_debug);
35 #define GST_CAT_DEFAULT gst_play_sink_debug
37 #define VOLUME_MAX_DOUBLE 10.0
39 #define DEFAULT_FLAGS GST_PLAY_FLAG_AUDIO | GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_TEXT | \
40 GST_PLAY_FLAG_SOFT_VOLUME
42 #define GST_PLAY_CHAIN(c) ((GstPlayChain *)(c))
44 /* holds the common data fields for the audio and video pipelines. We keep them
45 * in a structure to more easily have all the info available. */
48 GstPlaySink *playsink;
62 GstElement *volume; /* element with the volume property */
63 gboolean sink_volume; /* if the volume was provided by the sink */
64 GstElement *mute; /* element with the mute property */
66 GstElement *ts_offset;
72 GstPad *sinkpad, *srcpad;
74 GstElement *deinterlace;
75 } GstPlayVideoDeinterlaceChain;
86 GstElement *ts_offset;
96 GstPad *blockpad; /* srcpad of resample, used for switching the vis */
97 GstPad *vissinkpad; /* visualisation sinkpad, */
99 GstPad *vissrcpad; /* visualisation srcpad, */
100 GstPad *srcpad; /* outgoing srcpad, used to connect to the next
109 GstElement *identity;
111 GstPad *videosinkpad;
113 GstPad *srcpad; /* outgoing srcpad, used to connect to the next
115 GstElement *sink; /* custom sink to receive subtitle buffers */
118 #define GST_PLAY_SINK_GET_LOCK(playsink) (&((GstPlaySink *)playsink)->lock)
119 #define GST_PLAY_SINK_LOCK(playsink) G_STMT_START { \
120 GST_LOG_OBJECT (playsink, "locking from thread %p", g_thread_self ()); \
121 g_static_rec_mutex_lock (GST_PLAY_SINK_GET_LOCK (playsink)); \
122 GST_LOG_OBJECT (playsink, "locked from thread %p", g_thread_self ()); \
124 #define GST_PLAY_SINK_UNLOCK(playsink) G_STMT_START { \
125 GST_LOG_OBJECT (playsink, "unlocking from thread %p", g_thread_self ()); \
126 g_static_rec_mutex_unlock (GST_PLAY_SINK_GET_LOCK (playsink)); \
133 GStaticRecMutex lock;
135 gboolean async_pending;
136 gboolean need_async_start;
140 GstStreamSynchronizer *stream_synchronizer;
143 GstPlayAudioChain *audiochain;
144 GstPlayVideoDeinterlaceChain *videodeinterlacechain;
145 GstPlayVideoChain *videochain;
146 GstPlayVisChain *vischain;
147 GstPlayTextChain *textchain;
151 gboolean audio_pad_raw;
152 GstPad *audio_srcpad_stream_synchronizer;
153 GstPad *audio_sinkpad_stream_synchronizer;
155 GstElement *audio_tee;
156 GstPad *audio_tee_sink;
157 GstPad *audio_tee_asrc;
158 GstPad *audio_tee_vissrc;
161 gboolean video_pad_raw;
162 GstPad *video_srcpad_stream_synchronizer;
163 GstPad *video_sinkpad_stream_synchronizer;
166 GstPad *text_srcpad_stream_synchronizer;
167 GstPad *text_sinkpad_stream_synchronizer;
170 GstElement *audio_sink;
171 GstElement *video_sink;
172 GstElement *visualisation;
173 GstElement *text_sink;
176 gchar *font_desc; /* font description */
177 gchar *subtitle_encoding; /* subtitle encoding */
178 guint connection_speed; /* connection speed in bits/sec (0 = unknown) */
180 gboolean volume_changed; /* volume/mute changed while no audiochain */
181 gboolean mute_changed; /* ... has been created yet */
185 struct _GstPlaySinkClass
187 GstBinClass parent_class;
189 gboolean (*reconfigure) (GstPlaySink * playsink);
191 GstBuffer *(*convert_frame) (GstPlaySink * playsink, GstCaps * caps);
194 static GstStaticPadTemplate audiorawtemplate =
195 GST_STATIC_PAD_TEMPLATE ("audio_raw_sink",
198 GST_STATIC_CAPS_ANY);
199 static GstStaticPadTemplate audiotemplate =
200 GST_STATIC_PAD_TEMPLATE ("audio_sink",
203 GST_STATIC_CAPS_ANY);
204 static GstStaticPadTemplate videorawtemplate =
205 GST_STATIC_PAD_TEMPLATE ("video_raw_sink",
208 GST_STATIC_CAPS_ANY);
209 static GstStaticPadTemplate videotemplate =
210 GST_STATIC_PAD_TEMPLATE ("video_sink",
213 GST_STATIC_CAPS_ANY);
214 static GstStaticPadTemplate texttemplate = GST_STATIC_PAD_TEMPLATE ("text_sink",
217 GST_STATIC_CAPS_ANY);
227 PROP_SUBTITLE_ENCODING,
240 static void gst_play_sink_dispose (GObject * object);
241 static void gst_play_sink_finalize (GObject * object);
242 static void gst_play_sink_set_property (GObject * object, guint prop_id,
243 const GValue * value, GParamSpec * spec);
244 static void gst_play_sink_get_property (GObject * object, guint prop_id,
245 GValue * value, GParamSpec * spec);
247 static GstPad *gst_play_sink_request_new_pad (GstElement * element,
248 GstPadTemplate * templ, const gchar * name);
249 static void gst_play_sink_release_request_pad (GstElement * element,
251 static gboolean gst_play_sink_send_event (GstElement * element,
253 static GstStateChangeReturn gst_play_sink_change_state (GstElement * element,
254 GstStateChange transition);
256 static void gst_play_sink_handle_message (GstBin * bin, GstMessage * message);
258 static void notify_volume_cb (GObject * object, GParamSpec * pspec,
259 GstPlaySink * playsink);
260 static void notify_mute_cb (GObject * object, GParamSpec * pspec,
261 GstPlaySink * playsink);
263 static void update_av_offset (GstPlaySink * playsink);
266 gst_play_marshal_BUFFER__BOXED (GClosure * closure,
267 GValue * return_value G_GNUC_UNUSED,
268 guint n_param_values,
269 const GValue * param_values,
270 gpointer invocation_hint G_GNUC_UNUSED, gpointer marshal_data)
272 typedef GstBuffer *(*GMarshalFunc_OBJECT__BOXED) (gpointer data1,
273 gpointer arg_1, gpointer data2);
274 register GMarshalFunc_OBJECT__BOXED callback;
275 register GCClosure *cc = (GCClosure *) closure;
276 register gpointer data1, data2;
278 g_return_if_fail (return_value != NULL);
279 g_return_if_fail (n_param_values == 2);
281 if (G_CCLOSURE_SWAP_DATA (closure)) {
282 data1 = closure->data;
283 data2 = g_value_peek_pointer (param_values + 0);
285 data1 = g_value_peek_pointer (param_values + 0);
286 data2 = closure->data;
289 (GMarshalFunc_OBJECT__BOXED) (marshal_data ? marshal_data : cc->callback);
291 v_return = callback (data1, g_value_get_boxed (param_values + 1), data2);
293 gst_value_take_buffer (return_value, v_return);
296 /* static guint gst_play_sink_signals[LAST_SIGNAL] = { 0 }; */
298 G_DEFINE_TYPE (GstPlaySink, gst_play_sink, GST_TYPE_BIN);
301 gst_play_sink_class_init (GstPlaySinkClass * klass)
303 GObjectClass *gobject_klass;
304 GstElementClass *gstelement_klass;
305 GstBinClass *gstbin_klass;
307 gobject_klass = (GObjectClass *) klass;
308 gstelement_klass = (GstElementClass *) klass;
309 gstbin_klass = (GstBinClass *) klass;
311 gobject_klass->dispose = gst_play_sink_dispose;
312 gobject_klass->finalize = gst_play_sink_finalize;
313 gobject_klass->set_property = gst_play_sink_set_property;
314 gobject_klass->get_property = gst_play_sink_get_property;
320 * Control the behaviour of playsink.
322 g_object_class_install_property (gobject_klass, PROP_FLAGS,
323 g_param_spec_flags ("flags", "Flags", "Flags to control behaviour",
324 GST_TYPE_PLAY_FLAGS, DEFAULT_FLAGS,
325 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
328 * GstPlaySink:volume:
330 * Get or set the current audio stream volume. 1.0 means 100%,
331 * 0.0 means mute. This uses a linear volume scale.
334 g_object_class_install_property (gobject_klass, PROP_VOLUME,
335 g_param_spec_double ("volume", "Volume", "The audio volume, 1.0=100%",
336 0.0, VOLUME_MAX_DOUBLE, 1.0,
337 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
338 g_object_class_install_property (gobject_klass, PROP_MUTE,
339 g_param_spec_boolean ("mute", "Mute",
340 "Mute the audio channel without changing the volume", FALSE,
341 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
342 g_object_class_install_property (gobject_klass, PROP_FONT_DESC,
343 g_param_spec_string ("subtitle-font-desc",
344 "Subtitle font description",
345 "Pango font description of font "
346 "to be used for subtitle rendering", NULL,
347 G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
348 g_object_class_install_property (gobject_klass, PROP_SUBTITLE_ENCODING,
349 g_param_spec_string ("subtitle-encoding", "subtitle encoding",
350 "Encoding to assume if input subtitles are not in UTF-8 encoding. "
351 "If not set, the GST_SUBTITLE_ENCODING environment variable will "
352 "be checked for an encoding to use. If that is not set either, "
353 "ISO-8859-15 will be assumed.", NULL,
354 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
355 g_object_class_install_property (gobject_klass, PROP_VIS_PLUGIN,
356 g_param_spec_object ("vis-plugin", "Vis plugin",
357 "the visualization element to use (NULL = default)",
358 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
362 * Get the currently rendered or prerolled frame in the video sink.
363 * The #GstCaps on the buffer will describe the format of the buffer.
367 g_object_class_install_property (gobject_klass, PROP_FRAME,
368 gst_param_spec_mini_object ("frame", "Frame",
369 "The last frame (NULL = no video available)",
370 GST_TYPE_BUFFER, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
372 * GstPlaySink:av-offset:
374 * Control the synchronisation offset between the audio and video streams.
375 * Positive values make the audio ahead of the video and negative values make
376 * the audio go behind the video.
380 g_object_class_install_property (gobject_klass, PROP_AV_OFFSET,
381 g_param_spec_int64 ("av-offset", "AV Offset",
382 "The synchronisation offset between audio and video in nanoseconds",
383 G_MININT64, G_MAXINT64, 0,
384 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
386 g_signal_new ("reconfigure", G_TYPE_FROM_CLASS (klass),
387 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstPlaySinkClass,
388 reconfigure), NULL, NULL, gst_marshal_BOOLEAN__VOID, G_TYPE_BOOLEAN,
391 * GstPlaySink::convert-frame
392 * @playsink: a #GstPlaySink
393 * @caps: the target format of the frame
395 * Action signal to retrieve the currently playing video frame in the format
396 * specified by @caps.
397 * If @caps is %NULL, no conversion will be performed and this function is
398 * equivalent to the #GstPlaySink::frame property.
400 * Returns: a #GstBuffer of the current video frame converted to #caps.
401 * The caps on the buffer will describe the final layout of the buffer data.
402 * %NULL is returned when no current buffer can be retrieved or when the
407 g_signal_new ("convert-frame", G_TYPE_FROM_CLASS (klass),
408 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
409 G_STRUCT_OFFSET (GstPlaySinkClass, convert_frame), NULL, NULL,
410 gst_play_marshal_BUFFER__BOXED, GST_TYPE_BUFFER, 1, GST_TYPE_CAPS);
412 gst_element_class_add_pad_template (gstelement_klass,
413 gst_static_pad_template_get (&audiorawtemplate));
414 gst_element_class_add_pad_template (gstelement_klass,
415 gst_static_pad_template_get (&audiotemplate));
416 gst_element_class_add_pad_template (gstelement_klass,
417 gst_static_pad_template_get (&videorawtemplate));
418 gst_element_class_add_pad_template (gstelement_klass,
419 gst_static_pad_template_get (&videotemplate));
420 gst_element_class_add_pad_template (gstelement_klass,
421 gst_static_pad_template_get (&texttemplate));
422 gst_element_class_set_details_simple (gstelement_klass, "Player Sink",
424 "Convenience sink for multiple streams",
425 "Wim Taymans <wim.taymans@gmail.com>");
427 gstelement_klass->change_state =
428 GST_DEBUG_FUNCPTR (gst_play_sink_change_state);
429 gstelement_klass->send_event = GST_DEBUG_FUNCPTR (gst_play_sink_send_event);
430 gstelement_klass->request_new_pad =
431 GST_DEBUG_FUNCPTR (gst_play_sink_request_new_pad);
432 gstelement_klass->release_pad =
433 GST_DEBUG_FUNCPTR (gst_play_sink_release_request_pad);
435 gstbin_klass->handle_message =
436 GST_DEBUG_FUNCPTR (gst_play_sink_handle_message);
438 klass->reconfigure = GST_DEBUG_FUNCPTR (gst_play_sink_reconfigure);
439 klass->convert_frame = GST_DEBUG_FUNCPTR (gst_play_sink_convert_frame);
443 gst_play_sink_init (GstPlaySink * playsink)
446 playsink->video_sink = NULL;
447 playsink->audio_sink = NULL;
448 playsink->visualisation = NULL;
449 playsink->text_sink = NULL;
450 playsink->volume = 1.0;
451 playsink->font_desc = NULL;
452 playsink->subtitle_encoding = NULL;
453 playsink->flags = DEFAULT_FLAGS;
455 playsink->stream_synchronizer =
456 g_object_new (GST_TYPE_STREAM_SYNCHRONIZER, NULL);
457 gst_bin_add (GST_BIN_CAST (playsink),
458 GST_ELEMENT_CAST (playsink->stream_synchronizer));
460 g_static_rec_mutex_init (&playsink->lock);
461 GST_OBJECT_FLAG_SET (playsink, GST_ELEMENT_IS_SINK);
465 disconnect_chain (GstPlayAudioChain * chain, GstPlaySink * playsink)
469 g_signal_handlers_disconnect_by_func (chain->volume, notify_volume_cb,
472 g_signal_handlers_disconnect_by_func (chain->mute, notify_mute_cb,
478 free_chain (GstPlayChain * chain)
482 gst_object_unref (chain->bin);
488 gst_play_sink_dispose (GObject * object)
490 GstPlaySink *playsink;
492 playsink = GST_PLAY_SINK (object);
494 if (playsink->audio_sink != NULL) {
495 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
496 gst_object_unref (playsink->audio_sink);
497 playsink->audio_sink = NULL;
499 if (playsink->video_sink != NULL) {
500 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
501 gst_object_unref (playsink->video_sink);
502 playsink->video_sink = NULL;
504 if (playsink->visualisation != NULL) {
505 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
506 gst_object_unref (playsink->visualisation);
507 playsink->visualisation = NULL;
509 if (playsink->text_sink != NULL) {
510 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
511 gst_object_unref (playsink->text_sink);
512 playsink->text_sink = NULL;
515 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
516 playsink->videodeinterlacechain = NULL;
517 free_chain ((GstPlayChain *) playsink->videochain);
518 playsink->videochain = NULL;
519 free_chain ((GstPlayChain *) playsink->audiochain);
520 playsink->audiochain = NULL;
521 free_chain ((GstPlayChain *) playsink->vischain);
522 playsink->vischain = NULL;
523 free_chain ((GstPlayChain *) playsink->textchain);
524 playsink->textchain = NULL;
526 if (playsink->audio_tee_sink) {
527 gst_object_unref (playsink->audio_tee_sink);
528 playsink->audio_tee_sink = NULL;
531 if (playsink->audio_tee_vissrc) {
532 gst_element_release_request_pad (playsink->audio_tee,
533 playsink->audio_tee_vissrc);
534 gst_object_unref (playsink->audio_tee_vissrc);
535 playsink->audio_tee_vissrc = NULL;
538 if (playsink->audio_tee_asrc) {
539 gst_element_release_request_pad (playsink->audio_tee,
540 playsink->audio_tee_asrc);
541 gst_object_unref (playsink->audio_tee_asrc);
542 playsink->audio_tee_asrc = NULL;
545 g_free (playsink->font_desc);
546 playsink->font_desc = NULL;
548 g_free (playsink->subtitle_encoding);
549 playsink->subtitle_encoding = NULL;
551 playsink->stream_synchronizer = NULL;
553 G_OBJECT_CLASS (gst_play_sink_parent_class)->dispose (object);
557 gst_play_sink_finalize (GObject * object)
559 GstPlaySink *playsink;
561 playsink = GST_PLAY_SINK (object);
563 g_static_rec_mutex_free (&playsink->lock);
565 G_OBJECT_CLASS (gst_play_sink_parent_class)->finalize (object);
569 gst_play_sink_set_sink (GstPlaySink * playsink, GstPlaySinkType type,
572 GstElement **elem = NULL, *old = NULL;
574 GST_LOG ("Setting sink %" GST_PTR_FORMAT " as sink type %d", sink, type);
576 GST_PLAY_SINK_LOCK (playsink);
578 case GST_PLAY_SINK_TYPE_AUDIO:
579 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
580 elem = &playsink->audio_sink;
582 case GST_PLAY_SINK_TYPE_VIDEO:
583 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
584 elem = &playsink->video_sink;
586 case GST_PLAY_SINK_TYPE_TEXT:
587 elem = &playsink->text_sink;
595 gst_object_ref (sink);
598 GST_PLAY_SINK_UNLOCK (playsink);
602 gst_element_set_state (old, GST_STATE_NULL);
603 gst_object_unref (old);
608 gst_play_sink_get_sink (GstPlaySink * playsink, GstPlaySinkType type)
610 GstElement *result = NULL;
611 GstElement *elem = NULL, *chainp = NULL;
613 GST_PLAY_SINK_LOCK (playsink);
615 case GST_PLAY_SINK_TYPE_AUDIO:
617 GstPlayAudioChain *chain;
618 if ((chain = (GstPlayAudioChain *) playsink->audiochain))
619 chainp = chain->sink;
620 elem = playsink->audio_sink;
623 case GST_PLAY_SINK_TYPE_VIDEO:
625 GstPlayVideoChain *chain;
626 if ((chain = (GstPlayVideoChain *) playsink->videochain))
627 chainp = chain->sink;
628 elem = playsink->video_sink;
631 case GST_PLAY_SINK_TYPE_TEXT:
633 GstPlayTextChain *chain;
634 if ((chain = (GstPlayTextChain *) playsink->textchain))
635 chainp = chain->sink;
636 elem = playsink->text_sink;
643 /* we have an active chain with a sink, get the sink */
644 result = gst_object_ref (chainp);
646 /* nothing found, return last configured sink */
647 if (result == NULL && elem)
648 result = gst_object_ref (elem);
649 GST_PLAY_SINK_UNLOCK (playsink);
655 gst_play_sink_vis_unblocked (GstPad * tee_pad, gboolean blocked,
658 GstPlaySink *playsink;
660 playsink = GST_PLAY_SINK (user_data);
661 /* nothing to do here, we need a dummy callback here to make the async call
663 GST_DEBUG_OBJECT (playsink, "vis pad unblocked");
667 gst_play_sink_vis_blocked (GstPad * tee_pad, gboolean blocked,
670 GstPlaySink *playsink;
671 GstPlayVisChain *chain;
673 playsink = GST_PLAY_SINK (user_data);
675 GST_PLAY_SINK_LOCK (playsink);
676 GST_DEBUG_OBJECT (playsink, "vis pad blocked");
677 /* now try to change the plugin in the running vis chain */
678 if (!(chain = (GstPlayVisChain *) playsink->vischain))
681 /* unlink the old plugin and unghost the pad */
682 gst_pad_unlink (chain->blockpad, chain->vissinkpad);
683 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad), NULL);
685 /* set the old plugin to NULL and remove */
686 gst_element_set_state (chain->vis, GST_STATE_NULL);
687 gst_bin_remove (GST_BIN_CAST (chain->chain.bin), chain->vis);
689 /* add new plugin and set state to playing */
690 chain->vis = playsink->visualisation;
691 gst_bin_add (GST_BIN_CAST (chain->chain.bin), chain->vis);
692 gst_element_set_state (chain->vis, GST_STATE_PLAYING);
695 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
696 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
699 gst_pad_link_full (chain->blockpad, chain->vissinkpad,
700 GST_PAD_LINK_CHECK_NOTHING);
701 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad),
705 /* Unblock the pad */
706 gst_pad_set_blocked_async (tee_pad, FALSE, gst_play_sink_vis_unblocked,
708 GST_PLAY_SINK_UNLOCK (playsink);
712 gst_play_sink_set_vis_plugin (GstPlaySink * playsink, GstElement * vis)
714 GstPlayVisChain *chain;
716 /* setting NULL means creating the default vis plugin */
718 vis = gst_element_factory_make ("goom", "vis");
720 /* simply return if we don't have a vis plugin here */
724 GST_PLAY_SINK_LOCK (playsink);
725 /* first store the new vis */
726 if (playsink->visualisation)
727 gst_object_unref (playsink->visualisation);
729 gst_object_ref_sink (vis);
730 playsink->visualisation = vis;
732 /* now try to change the plugin in the running vis chain, if we have no chain,
733 * we don't bother, any future vis chain will be created with the new vis
735 if (!(chain = (GstPlayVisChain *) playsink->vischain))
738 /* block the pad, the next time the callback is called we can change the
739 * visualisation. It's possible that this never happens or that the pad was
740 * already blocked. If the callback never happens, we don't have new data so
741 * we don't need the new vis plugin. If the pad was already blocked, the
742 * function returns FALSE but the previous pad block will do the right thing
744 GST_DEBUG_OBJECT (playsink, "blocking vis pad");
745 gst_pad_set_blocked_async (chain->blockpad, TRUE, gst_play_sink_vis_blocked,
748 GST_PLAY_SINK_UNLOCK (playsink);
754 gst_play_sink_get_vis_plugin (GstPlaySink * playsink)
756 GstElement *result = NULL;
757 GstPlayVisChain *chain;
759 GST_PLAY_SINK_LOCK (playsink);
760 if ((chain = (GstPlayVisChain *) playsink->vischain)) {
761 /* we have an active chain, get the sink */
763 result = gst_object_ref (chain->vis);
765 /* nothing found, return last configured sink */
766 if (result == NULL && playsink->visualisation)
767 result = gst_object_ref (playsink->visualisation);
768 GST_PLAY_SINK_UNLOCK (playsink);
774 gst_play_sink_set_volume (GstPlaySink * playsink, gdouble volume)
776 GstPlayAudioChain *chain;
778 GST_PLAY_SINK_LOCK (playsink);
779 playsink->volume = volume;
780 chain = (GstPlayAudioChain *) playsink->audiochain;
781 if (chain && chain->volume) {
782 GST_LOG_OBJECT (playsink, "elements: volume=%" GST_PTR_FORMAT ", mute=%"
783 GST_PTR_FORMAT "; new volume=%.03f, mute=%d", chain->volume,
784 chain->mute, volume, playsink->mute);
785 /* if there is a mute element or we are not muted, set the volume */
786 if (chain->mute || !playsink->mute)
787 g_object_set (chain->volume, "volume", volume, NULL);
789 GST_LOG_OBJECT (playsink, "no volume element");
790 playsink->volume_changed = TRUE;
792 GST_PLAY_SINK_UNLOCK (playsink);
796 gst_play_sink_get_volume (GstPlaySink * playsink)
799 GstPlayAudioChain *chain;
801 GST_PLAY_SINK_LOCK (playsink);
802 chain = (GstPlayAudioChain *) playsink->audiochain;
803 result = playsink->volume;
804 if (chain && chain->volume) {
805 if (chain->mute || !playsink->mute) {
806 g_object_get (chain->volume, "volume", &result, NULL);
807 playsink->volume = result;
810 GST_PLAY_SINK_UNLOCK (playsink);
816 gst_play_sink_set_mute (GstPlaySink * playsink, gboolean mute)
818 GstPlayAudioChain *chain;
820 GST_PLAY_SINK_LOCK (playsink);
821 playsink->mute = mute;
822 chain = (GstPlayAudioChain *) playsink->audiochain;
825 g_object_set (chain->mute, "mute", mute, NULL);
826 } else if (chain->volume) {
828 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
830 g_object_set (chain->volume, "volume", (gdouble) playsink->volume,
834 playsink->mute_changed = TRUE;
836 GST_PLAY_SINK_UNLOCK (playsink);
840 gst_play_sink_get_mute (GstPlaySink * playsink)
843 GstPlayAudioChain *chain;
845 GST_PLAY_SINK_LOCK (playsink);
846 chain = (GstPlayAudioChain *) playsink->audiochain;
847 if (chain && chain->mute) {
848 g_object_get (chain->mute, "mute", &result, NULL);
849 playsink->mute = result;
851 result = playsink->mute;
853 GST_PLAY_SINK_UNLOCK (playsink);
859 post_missing_element_message (GstPlaySink * playsink, const gchar * name)
863 msg = gst_missing_element_message_new (GST_ELEMENT_CAST (playsink), name);
864 gst_element_post_message (GST_ELEMENT_CAST (playsink), msg);
868 add_chain (GstPlayChain * chain, gboolean add)
870 if (chain->added == add)
874 gst_bin_add (GST_BIN_CAST (chain->playsink), chain->bin);
876 gst_bin_remove (GST_BIN_CAST (chain->playsink), chain->bin);
877 /* we don't want to lose our sink status */
878 GST_OBJECT_FLAG_SET (chain->playsink, GST_ELEMENT_IS_SINK);
887 activate_chain (GstPlayChain * chain, gboolean activate)
891 if (chain->activated == activate)
894 GST_OBJECT_LOCK (chain->playsink);
895 state = GST_STATE_TARGET (chain->playsink);
896 GST_OBJECT_UNLOCK (chain->playsink);
899 gst_element_set_state (chain->bin, state);
901 gst_element_set_state (chain->bin, GST_STATE_NULL);
903 chain->activated = activate;
909 element_is_sink (GstElement * element)
913 GST_OBJECT_LOCK (element);
914 is_sink = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_IS_SINK);
915 GST_OBJECT_UNLOCK (element);
917 GST_DEBUG_OBJECT (element, "is a sink: %s", (is_sink) ? "yes" : "no");
922 element_has_property (GstElement * element, const gchar * pname, GType type)
926 pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (element), pname);
929 GST_DEBUG_OBJECT (element, "no %s property", pname);
933 if (type == G_TYPE_INVALID || type == pspec->value_type ||
934 g_type_is_a (pspec->value_type, type)) {
935 GST_DEBUG_OBJECT (element, "has %s property of type %s", pname,
936 (type == G_TYPE_INVALID) ? "any type" : g_type_name (type));
940 GST_WARNING_OBJECT (element, "has %s property, but property is of type %s "
941 "and we expected it to be of type %s", pname,
942 g_type_name (pspec->value_type), g_type_name (type));
949 const gchar *prop_name;
952 } FindPropertyHelper;
955 find_property (GstElement * element, FindPropertyHelper * helper)
957 if (helper->need_sink && !element_is_sink (element)) {
958 gst_object_unref (element);
962 if (!element_has_property (element, helper->prop_name, helper->prop_type)) {
963 gst_object_unref (element);
967 GST_INFO_OBJECT (element, "found %s with %s property", helper->prop_name,
968 (helper->need_sink) ? "sink" : "element");
969 return 0; /* keep it */
972 /* FIXME: why not move these functions into core? */
973 /* find a sink in the hierarchy with a property named @name. This function does
974 * not increase the refcount of the returned object and thus remains valid as
975 * long as the bin is valid. */
977 gst_play_sink_find_property_sinks (GstPlaySink * playsink, GstElement * obj,
978 const gchar * name, GType expected_type)
980 GstElement *result = NULL;
983 if (element_has_property (obj, name, expected_type)) {
985 } else if (GST_IS_BIN (obj)) {
986 FindPropertyHelper helper = { name, expected_type, TRUE };
988 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
989 result = gst_iterator_find_custom (it,
990 (GCompareFunc) find_property, &helper);
991 gst_iterator_free (it);
992 /* we don't need the extra ref */
994 gst_object_unref (result);
999 /* find an object in the hierarchy with a property named @name */
1001 gst_play_sink_find_property (GstPlaySink * playsink, GstElement * obj,
1002 const gchar * name, GType expected_type)
1004 GstElement *result = NULL;
1007 if (GST_IS_BIN (obj)) {
1008 FindPropertyHelper helper = { name, expected_type, FALSE };
1010 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1011 result = gst_iterator_find_custom (it,
1012 (GCompareFunc) find_property, &helper);
1013 gst_iterator_free (it);
1015 if (element_has_property (obj, name, expected_type)) {
1017 gst_object_ref (obj);
1024 do_async_start (GstPlaySink * playsink)
1026 GstMessage *message;
1028 if (!playsink->need_async_start) {
1029 GST_INFO_OBJECT (playsink, "no async_start needed");
1033 playsink->async_pending = TRUE;
1035 GST_INFO_OBJECT (playsink, "Sending async_start message");
1036 message = gst_message_new_async_start (GST_OBJECT_CAST (playsink), FALSE);
1037 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1038 (playsink), message);
1042 do_async_done (GstPlaySink * playsink)
1044 GstMessage *message;
1046 if (playsink->async_pending) {
1047 GST_INFO_OBJECT (playsink, "Sending async_done message");
1048 message = gst_message_new_async_done (GST_OBJECT_CAST (playsink));
1049 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1050 (playsink), message);
1052 playsink->async_pending = FALSE;
1055 playsink->need_async_start = FALSE;
1058 /* try to change the state of an element. This function returns the element when
1059 * the state change could be performed. When this function returns NULL an error
1060 * occured and the element is unreffed if @unref is TRUE. */
1062 try_element (GstPlaySink * playsink, GstElement * element, gboolean unref)
1064 GstStateChangeReturn ret;
1067 ret = gst_element_set_state (element, GST_STATE_READY);
1068 if (ret == GST_STATE_CHANGE_FAILURE) {
1069 GST_DEBUG_OBJECT (playsink, "failed state change..");
1070 gst_element_set_state (element, GST_STATE_NULL);
1072 gst_object_unref (element);
1079 /* make the element (bin) that contains the elements needed to perform
1082 * +------------------------------------------------------------+
1084 * | +-------+ +----------+ +----------+ +---------+ |
1085 * | | queue | |colorspace| |videoscale| |videosink| |
1086 * | +-sink src-sink src-sink src-sink | |
1087 * | | +-------+ +----------+ +----------+ +---------+ |
1089 * +------------------------------------------------------------+
1092 static GstPlayVideoDeinterlaceChain *
1093 gen_video_deinterlace_chain (GstPlaySink * playsink)
1095 GstPlayVideoDeinterlaceChain *chain;
1098 GstElement *head = NULL, *prev = NULL;
1100 chain = g_new0 (GstPlayVideoDeinterlaceChain, 1);
1101 chain->chain.playsink = playsink;
1103 GST_DEBUG_OBJECT (playsink, "making video deinterlace chain %p", chain);
1105 /* create a bin to hold objects, as we create them we add them to this bin so
1106 * that when something goes wrong we only need to unref the bin */
1107 chain->chain.bin = gst_bin_new ("vdbin");
1108 bin = GST_BIN_CAST (chain->chain.bin);
1109 gst_object_ref_sink (bin);
1111 GST_DEBUG_OBJECT (playsink, "creating ffmpegcolorspace");
1112 chain->conv = gst_element_factory_make ("ffmpegcolorspace", "vdconv");
1113 if (chain->conv == NULL) {
1114 post_missing_element_message (playsink, "ffmpegcolorspace");
1115 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1116 (_("Missing element '%s' - check your GStreamer installation."),
1117 "ffmpegcolorspace"), ("video rendering might fail"));
1119 gst_bin_add (bin, chain->conv);
1124 GST_DEBUG_OBJECT (playsink, "creating deinterlace");
1125 chain->deinterlace = gst_element_factory_make ("deinterlace", "deinterlace");
1126 if (chain->deinterlace == NULL) {
1127 post_missing_element_message (playsink, "deinterlace");
1128 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1129 (_("Missing element '%s' - check your GStreamer installation."),
1130 "deinterlace"), ("deinterlacing won't work"));
1132 gst_bin_add (bin, chain->deinterlace);
1134 if (!gst_element_link_pads_full (prev, "src", chain->deinterlace, "sink",
1135 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1138 head = chain->deinterlace;
1140 prev = chain->deinterlace;
1144 pad = gst_element_get_static_pad (head, "sink");
1145 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1146 gst_object_unref (pad);
1148 chain->sinkpad = gst_ghost_pad_new_no_target ("sink", GST_PAD_SINK);
1152 pad = gst_element_get_static_pad (prev, "src");
1153 chain->srcpad = gst_ghost_pad_new ("src", pad);
1154 gst_object_unref (pad);
1156 chain->srcpad = gst_ghost_pad_new ("src", chain->sinkpad);
1159 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1160 gst_element_add_pad (chain->chain.bin, chain->srcpad);
1166 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1167 (NULL), ("Failed to configure the video deinterlace chain."));
1168 free_chain ((GstPlayChain *) chain);
1173 /* make the element (bin) that contains the elements needed to perform
1176 * +------------------------------------------------------------+
1178 * | +-------+ +----------+ +----------+ +---------+ |
1179 * | | queue | |colorspace| |videoscale| |videosink| |
1180 * | +-sink src-sink src-sink src-sink | |
1181 * | | +-------+ +----------+ +----------+ +---------+ |
1183 * +------------------------------------------------------------+
1186 static GstPlayVideoChain *
1187 gen_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1189 GstPlayVideoChain *chain;
1192 GstElement *head = NULL, *prev = NULL, *elem = NULL;
1194 chain = g_new0 (GstPlayVideoChain, 1);
1195 chain->chain.playsink = playsink;
1196 chain->chain.raw = raw;
1198 GST_DEBUG_OBJECT (playsink, "making video chain %p", chain);
1200 if (playsink->video_sink) {
1201 GST_DEBUG_OBJECT (playsink, "trying configured videosink");
1202 chain->sink = try_element (playsink, playsink->video_sink, FALSE);
1204 /* only try fallback if no specific sink was chosen */
1205 if (chain->sink == NULL) {
1206 GST_DEBUG_OBJECT (playsink, "trying autovideosink");
1207 elem = gst_element_factory_make ("autovideosink", "videosink");
1208 chain->sink = try_element (playsink, elem, TRUE);
1210 if (chain->sink == NULL) {
1211 /* if default sink from config.h is different then try it too */
1212 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1213 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_VIDEOSINK);
1214 elem = gst_element_factory_make (DEFAULT_VIDEOSINK, "videosink");
1215 chain->sink = try_element (playsink, elem, TRUE);
1219 playsink->video_sink = gst_object_ref (chain->sink);
1221 if (chain->sink == NULL)
1225 /* if we can disable async behaviour of the sink, we can avoid adding a
1226 * queue for the audio chain. */
1228 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1231 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1232 async, GST_ELEMENT_NAME (elem));
1233 g_object_set (elem, "async", async, NULL);
1234 chain->async = async;
1236 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1237 chain->async = TRUE;
1240 /* find ts-offset element */
1242 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1245 /* create a bin to hold objects, as we create them we add them to this bin so
1246 * that when something goes wrong we only need to unref the bin */
1247 chain->chain.bin = gst_bin_new ("vbin");
1248 bin = GST_BIN_CAST (chain->chain.bin);
1249 gst_object_ref_sink (bin);
1250 gst_bin_add (bin, chain->sink);
1252 /* decouple decoder from sink, this improves playback quite a lot since the
1253 * decoder can continue while the sink blocks for synchronisation. We don't
1254 * need a lot of buffers as this consumes a lot of memory and we don't want
1255 * too little because else we would be context switching too quickly. */
1256 chain->queue = gst_element_factory_make ("queue", "vqueue");
1257 if (chain->queue == NULL) {
1258 post_missing_element_message (playsink, "queue");
1259 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1260 (_("Missing element '%s' - check your GStreamer installation."),
1261 "queue"), ("video rendering might be suboptimal"));
1265 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1266 "max-size-bytes", 0, "max-size-time", (gint64) 0, "silent", TRUE, NULL);
1267 gst_bin_add (bin, chain->queue);
1268 head = prev = chain->queue;
1271 if (raw && !(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
1272 GST_DEBUG_OBJECT (playsink, "creating ffmpegcolorspace");
1273 chain->conv = gst_element_factory_make ("ffmpegcolorspace", "vconv");
1274 if (chain->conv == NULL) {
1275 post_missing_element_message (playsink, "ffmpegcolorspace");
1276 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1277 (_("Missing element '%s' - check your GStreamer installation."),
1278 "ffmpegcolorspace"), ("video rendering might fail"));
1280 gst_bin_add (bin, chain->conv);
1282 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
1283 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1291 GST_DEBUG_OBJECT (playsink, "creating videoscale");
1292 chain->scale = gst_element_factory_make ("videoscale", "vscale");
1293 if (chain->scale == NULL) {
1294 post_missing_element_message (playsink, "videoscale");
1295 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1296 (_("Missing element '%s' - check your GStreamer installation."),
1297 "videoscale"), ("possibly a liboil version mismatch?"));
1299 /* Add black borders if necessary to keep the DAR */
1300 g_object_set (chain->scale, "add-borders", TRUE, NULL);
1301 gst_bin_add (bin, chain->scale);
1303 if (!gst_element_link_pads_full (prev, "src", chain->scale, "sink",
1304 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1307 head = chain->scale;
1309 prev = chain->scale;
1314 GST_DEBUG_OBJECT (playsink, "linking to sink");
1315 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
1316 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1320 pad = gst_element_get_static_pad (head, "sink");
1321 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1322 gst_object_unref (pad);
1324 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1331 if (!elem && !playsink->video_sink) {
1332 post_missing_element_message (playsink, "autovideosink");
1333 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1334 post_missing_element_message (playsink, DEFAULT_VIDEOSINK);
1335 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1336 (_("Both autovideosink and %s elements are missing."),
1337 DEFAULT_VIDEOSINK), (NULL));
1339 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1340 (_("The autovideosink element is missing.")), (NULL));
1343 if (playsink->video_sink) {
1344 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1345 (_("Configured videosink %s is not working."),
1346 GST_ELEMENT_NAME (playsink->video_sink)), (NULL));
1347 } else if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1348 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1349 (_("Both autovideosink and %s elements are not working."),
1350 DEFAULT_VIDEOSINK), (NULL));
1352 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1353 (_("The autovideosink element is not working.")), (NULL));
1356 free_chain ((GstPlayChain *) chain);
1361 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1362 (NULL), ("Failed to configure the video sink."));
1363 /* checking sink made it READY */
1364 gst_element_set_state (chain->sink, GST_STATE_NULL);
1365 /* Remove chain from the bin to allow reuse later */
1366 gst_bin_remove (bin, chain->sink);
1367 free_chain ((GstPlayChain *) chain);
1373 setup_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1376 GstPlayVideoChain *chain;
1377 GstStateChangeReturn ret;
1379 chain = playsink->videochain;
1381 /* if the chain was active we don't do anything */
1382 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
1385 if (chain->chain.raw != raw)
1388 /* try to set the sink element to READY again */
1389 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
1390 if (ret == GST_STATE_CHANGE_FAILURE)
1393 /* find ts-offset element */
1395 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1398 /* if we can disable async behaviour of the sink, we can avoid adding a
1399 * queue for the audio chain. */
1401 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1404 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1405 async, GST_ELEMENT_NAME (elem));
1406 g_object_set (elem, "async", async, NULL);
1407 chain->async = async;
1409 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1410 chain->async = TRUE;
1415 /* make an element for playback of video with subtitles embedded.
1417 * +--------------------------------------------+
1419 * | +--------+ +-----------------+ |
1420 * | | queue | | subtitleoverlay | |
1421 * video--src sink---video_sink | |
1422 * | +--------+ | src--src
1423 * text------------------text_sink | |
1424 * | +-----------------+ |
1425 * +--------------------------------------------+
1428 static GstPlayTextChain *
1429 gen_text_chain (GstPlaySink * playsink)
1431 GstPlayTextChain *chain;
1434 GstPad *videosinkpad, *textsinkpad, *srcpad;
1436 chain = g_new0 (GstPlayTextChain, 1);
1437 chain->chain.playsink = playsink;
1439 GST_DEBUG_OBJECT (playsink, "making text chain %p", chain);
1441 chain->chain.bin = gst_bin_new ("tbin");
1442 bin = GST_BIN_CAST (chain->chain.bin);
1443 gst_object_ref_sink (bin);
1445 videosinkpad = textsinkpad = srcpad = NULL;
1447 /* first try to hook the text pad to the custom sink */
1448 if (playsink->text_sink) {
1449 GST_DEBUG_OBJECT (playsink, "trying configured textsink");
1450 chain->sink = try_element (playsink, playsink->text_sink, FALSE);
1453 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1456 /* make sure the sparse subtitles don't participate in the preroll */
1457 g_object_set (elem, "async", FALSE, NULL);
1458 GST_DEBUG_OBJECT (playsink, "adding custom text sink");
1459 gst_bin_add (bin, chain->sink);
1460 /* NOTE streamsynchronizer needs streams decoupled */
1461 /* make a little queue */
1462 chain->queue = gst_element_factory_make ("queue", "subqueue");
1463 if (chain->queue == NULL) {
1464 post_missing_element_message (playsink, "queue");
1465 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1466 (_("Missing element '%s' - check your GStreamer installation."),
1467 "queue"), ("rendering might be suboptimal"));
1469 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1470 "max-size-bytes", 0, "max-size-time", (gint64) 0,
1471 "silent", TRUE, NULL);
1472 gst_bin_add (bin, chain->queue);
1474 /* we have a custom sink, this will be our textsinkpad */
1475 if (gst_element_link_pads_full (chain->queue, "src", chain->sink,
1476 "sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
1477 /* we're all fine now and we can add the sink to the chain */
1478 GST_DEBUG_OBJECT (playsink, "using custom text sink");
1479 textsinkpad = gst_element_get_static_pad (chain->queue, "sink");
1481 GST_WARNING_OBJECT (playsink,
1482 "can't find a sink pad on custom text sink");
1483 gst_bin_remove (bin, chain->sink);
1484 gst_bin_remove (bin, chain->queue);
1486 chain->queue = NULL;
1488 /* try to set sync to true but it's no biggie when we can't */
1490 gst_play_sink_find_property_sinks (playsink, chain->sink,
1491 "sync", G_TYPE_BOOLEAN)))
1492 g_object_set (elem, "sync", TRUE, NULL);
1495 gst_bin_remove (bin, chain->sink);
1497 GST_WARNING_OBJECT (playsink,
1498 "can't find async property in custom text sink");
1501 if (textsinkpad == NULL) {
1502 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1503 (_("Custom text sink element is not usable.")),
1504 ("fallback to default textoverlay"));
1508 if (textsinkpad == NULL) {
1509 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
1510 /* make a little queue */
1511 chain->queue = gst_element_factory_make ("queue", "vqueue");
1512 if (chain->queue == NULL) {
1513 post_missing_element_message (playsink, "queue");
1514 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1515 (_("Missing element '%s' - check your GStreamer installation."),
1516 "queue"), ("video rendering might be suboptimal"));
1518 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1519 "max-size-bytes", 0, "max-size-time", (gint64) 0,
1520 "silent", TRUE, NULL);
1521 gst_bin_add (bin, chain->queue);
1522 videosinkpad = gst_element_get_static_pad (chain->queue, "sink");
1526 gst_element_factory_make ("subtitleoverlay", "suboverlay");
1527 if (chain->overlay == NULL) {
1528 post_missing_element_message (playsink, "subtitleoverlay");
1529 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1530 (_("Missing element '%s' - check your GStreamer installation."),
1531 "subtitleoverlay"), ("subtitle rendering disabled"));
1533 GstElement *element;
1535 gst_bin_add (bin, chain->overlay);
1537 g_object_set (G_OBJECT (chain->overlay), "silent", FALSE, NULL);
1538 if (playsink->font_desc) {
1539 g_object_set (G_OBJECT (chain->overlay), "font-desc",
1540 playsink->font_desc, NULL);
1542 if (playsink->subtitle_encoding) {
1543 g_object_set (G_OBJECT (chain->overlay), "subtitle-encoding",
1544 playsink->subtitle_encoding, NULL);
1547 gst_element_link_pads_full (chain->queue, "src", chain->overlay,
1548 "video_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS);
1550 /* make another little queue to decouple streams */
1551 element = gst_element_factory_make ("queue", "subqueue");
1552 if (element == NULL) {
1553 post_missing_element_message (playsink, "queue");
1554 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1555 (_("Missing element '%s' - check your GStreamer installation."),
1556 "queue"), ("rendering might be suboptimal"));
1558 g_object_set (G_OBJECT (element), "max-size-buffers", 3,
1559 "max-size-bytes", 0, "max-size-time", (gint64) 0,
1560 "silent", TRUE, NULL);
1561 gst_bin_add (bin, element);
1562 gst_element_link_pads_full (element, "src", chain->overlay,
1563 "subtitle_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS);
1564 textsinkpad = gst_element_get_static_pad (element, "sink");
1565 srcpad = gst_element_get_static_pad (chain->overlay, "src");
1571 if (videosinkpad == NULL) {
1572 /* if we still don't have a videosink, we don't have an overlay. the only
1573 * thing we can do is insert an identity and ghost the src
1575 chain->identity = gst_element_factory_make ("identity", "tidentity");
1576 if (chain->identity == NULL) {
1577 post_missing_element_message (playsink, "identity");
1578 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1579 (_("Missing element '%s' - check your GStreamer installation."),
1580 "identity"), (NULL));
1582 g_object_set (chain->identity, "signal-handoffs", FALSE, NULL);
1583 g_object_set (chain->identity, "silent", TRUE, NULL);
1584 gst_bin_add (bin, chain->identity);
1585 srcpad = gst_element_get_static_pad (chain->identity, "src");
1586 videosinkpad = gst_element_get_static_pad (chain->identity, "sink");
1590 /* expose the ghostpads */
1592 chain->videosinkpad = gst_ghost_pad_new ("sink", videosinkpad);
1593 gst_object_unref (videosinkpad);
1594 gst_element_add_pad (chain->chain.bin, chain->videosinkpad);
1597 chain->textsinkpad = gst_ghost_pad_new ("text_sink", textsinkpad);
1598 gst_object_unref (textsinkpad);
1599 gst_element_add_pad (chain->chain.bin, chain->textsinkpad);
1602 chain->srcpad = gst_ghost_pad_new ("src", srcpad);
1603 gst_object_unref (srcpad);
1604 gst_element_add_pad (chain->chain.bin, chain->srcpad);
1611 notify_volume_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
1615 g_object_get (object, "volume", &vol, NULL);
1616 playsink->volume = vol;
1618 g_object_notify (G_OBJECT (playsink), "volume");
1622 notify_mute_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
1626 g_object_get (object, "mute", &mute, NULL);
1627 playsink->mute = mute;
1629 g_object_notify (G_OBJECT (playsink), "mute");
1632 /* make the chain that contains the elements needed to perform
1635 * We add a tee as the first element so that we can link the visualisation chain
1636 * to it when requested.
1638 * +-------------------------------------------------------------+
1640 * | +---------+ +----------+ +---------+ +---------+ |
1641 * | |audioconv| |audioscale| | volume | |audiosink| |
1642 * | +-srck src-sink src-sink src-sink | |
1643 * | | +---------+ +----------+ +---------+ +---------+ |
1645 * +-------------------------------------------------------------+
1647 static GstPlayAudioChain *
1648 gen_audio_chain (GstPlaySink * playsink, gboolean raw)
1650 GstPlayAudioChain *chain;
1652 gboolean have_volume;
1654 GstElement *head, *prev, *elem = NULL;
1656 chain = g_new0 (GstPlayAudioChain, 1);
1657 chain->chain.playsink = playsink;
1658 chain->chain.raw = raw;
1660 GST_DEBUG_OBJECT (playsink, "making audio chain %p", chain);
1662 if (playsink->audio_sink) {
1663 GST_DEBUG_OBJECT (playsink, "trying configured audiosink %" GST_PTR_FORMAT,
1664 playsink->audio_sink);
1665 chain->sink = try_element (playsink, playsink->audio_sink, FALSE);
1667 /* only try fallback if no specific sink was chosen */
1668 if (chain->sink == NULL) {
1669 GST_DEBUG_OBJECT (playsink, "trying autoaudiosink");
1670 elem = gst_element_factory_make ("autoaudiosink", "audiosink");
1671 chain->sink = try_element (playsink, elem, TRUE);
1673 if (chain->sink == NULL) {
1674 /* if default sink from config.h is different then try it too */
1675 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1676 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_AUDIOSINK);
1677 elem = gst_element_factory_make (DEFAULT_AUDIOSINK, "audiosink");
1678 chain->sink = try_element (playsink, elem, TRUE);
1682 playsink->audio_sink = gst_object_ref (chain->sink);
1684 if (chain->sink == NULL)
1687 chain->chain.bin = gst_bin_new ("abin");
1688 bin = GST_BIN_CAST (chain->chain.bin);
1689 gst_object_ref_sink (bin);
1690 gst_bin_add (bin, chain->sink);
1692 /* we have to add a queue when we need to decouple for the video sink in
1693 * visualisations and for streamsynchronizer */
1694 GST_DEBUG_OBJECT (playsink, "adding audio queue");
1695 chain->queue = gst_element_factory_make ("queue", "aqueue");
1696 if (chain->queue == NULL) {
1697 post_missing_element_message (playsink, "queue");
1698 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1699 (_("Missing element '%s' - check your GStreamer installation."),
1700 "queue"), ("audio playback and visualizations might not work"));
1704 g_object_set (chain->queue, "silent", TRUE, NULL);
1705 gst_bin_add (bin, chain->queue);
1706 prev = head = chain->queue;
1709 /* find ts-offset element */
1711 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1714 /* check if the sink, or something within the sink, has the volume property.
1715 * If it does we don't need to add a volume element. */
1717 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
1720 chain->volume = elem;
1722 g_signal_connect (chain->volume, "notify::volume",
1723 G_CALLBACK (notify_volume_cb), playsink);
1725 GST_DEBUG_OBJECT (playsink, "the sink has a volume property");
1727 chain->sink_volume = TRUE;
1728 /* if the sink also has a mute property we can use this as well. We'll only
1729 * use the mute property if there is a volume property. We can simulate the
1730 * mute with the volume otherwise. */
1732 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
1735 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
1736 g_signal_connect (chain->mute, "notify::mute",
1737 G_CALLBACK (notify_mute_cb), playsink);
1739 /* use the sink to control the volume and mute */
1740 if (playsink->volume_changed) {
1741 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
1742 playsink->volume_changed = FALSE;
1744 if (playsink->mute_changed) {
1746 g_object_set (chain->mute, "mute", playsink->mute, NULL);
1749 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
1751 playsink->mute_changed = FALSE;
1754 /* no volume, we need to add a volume element when we can */
1755 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
1756 have_volume = FALSE;
1757 chain->sink_volume = FALSE;
1760 if (raw && !(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO)) {
1761 GST_DEBUG_OBJECT (playsink, "creating audioconvert");
1762 chain->conv = gst_element_factory_make ("audioconvert", "aconv");
1763 if (chain->conv == NULL) {
1764 post_missing_element_message (playsink, "audioconvert");
1765 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1766 (_("Missing element '%s' - check your GStreamer installation."),
1767 "audioconvert"), ("possibly a liboil version mismatch?"));
1769 gst_bin_add (bin, chain->conv);
1771 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
1772 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1780 GST_DEBUG_OBJECT (playsink, "creating audioresample");
1781 chain->resample = gst_element_factory_make ("audioresample", "aresample");
1782 if (chain->resample == NULL) {
1783 post_missing_element_message (playsink, "audioresample");
1784 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1785 (_("Missing element '%s' - check your GStreamer installation."),
1786 "audioresample"), ("possibly a liboil version mismatch?"));
1788 gst_bin_add (bin, chain->resample);
1790 if (!gst_element_link_pads_full (prev, "src", chain->resample, "sink",
1791 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1794 head = chain->resample;
1796 prev = chain->resample;
1799 if (!have_volume && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME) {
1800 GST_DEBUG_OBJECT (playsink, "creating volume");
1801 chain->volume = gst_element_factory_make ("volume", "volume");
1802 if (chain->volume == NULL) {
1803 post_missing_element_message (playsink, "volume");
1804 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1805 (_("Missing element '%s' - check your GStreamer installation."),
1806 "volume"), ("possibly a liboil version mismatch?"));
1810 g_signal_connect (chain->volume, "notify::volume",
1811 G_CALLBACK (notify_volume_cb), playsink);
1813 /* volume also has the mute property */
1814 chain->mute = chain->volume;
1815 g_signal_connect (chain->mute, "notify::mute",
1816 G_CALLBACK (notify_mute_cb), playsink);
1818 /* configure with the latest volume and mute */
1819 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume,
1821 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
1822 gst_bin_add (bin, chain->volume);
1825 if (!gst_element_link_pads_full (prev, "src", chain->volume, "sink",
1826 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1829 head = chain->volume;
1831 prev = chain->volume;
1837 /* we only have to link to the previous element if we have something in
1838 * front of the sink */
1839 GST_DEBUG_OBJECT (playsink, "linking to sink");
1840 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
1841 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1845 /* post a warning if we have no way to configure the volume */
1847 GST_ELEMENT_WARNING (playsink, STREAM, NOT_IMPLEMENTED,
1848 (_("No volume control found")), ("Volume/mute is not available"));
1851 /* and ghost the sinkpad of the headmost element */
1852 GST_DEBUG_OBJECT (playsink, "ghosting sink pad");
1853 pad = gst_element_get_static_pad (head, "sink");
1854 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1855 gst_object_unref (pad);
1856 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1863 if (!elem && !playsink->audio_sink) {
1864 post_missing_element_message (playsink, "autoaudiosink");
1865 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1866 post_missing_element_message (playsink, DEFAULT_AUDIOSINK);
1867 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1868 (_("Both autoaudiosink and %s elements are missing."),
1869 DEFAULT_AUDIOSINK), (NULL));
1871 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1872 (_("The autoaudiosink element is missing.")), (NULL));
1875 if (playsink->audio_sink) {
1876 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1877 (_("Configured audiosink %s is not working."),
1878 GST_ELEMENT_NAME (playsink->audio_sink)), (NULL));
1879 } else if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1880 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1881 (_("Both autoaudiosink and %s elements are not working."),
1882 DEFAULT_AUDIOSINK), (NULL));
1884 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1885 (_("The autoaudiosink element is not working.")), (NULL));
1888 free_chain ((GstPlayChain *) chain);
1893 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1894 (NULL), ("Failed to configure the audio sink."));
1895 /* checking sink made it READY */
1896 gst_element_set_state (chain->sink, GST_STATE_NULL);
1897 /* Remove chain from the bin to allow reuse later */
1898 gst_bin_remove (bin, chain->sink);
1899 free_chain ((GstPlayChain *) chain);
1905 setup_audio_chain (GstPlaySink * playsink, gboolean raw)
1908 GstPlayAudioChain *chain;
1909 GstStateChangeReturn ret;
1911 chain = playsink->audiochain;
1913 /* if the chain was active we don't do anything */
1914 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
1917 if (chain->chain.raw != raw)
1920 /* try to set the sink element to READY again */
1921 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
1922 if (ret == GST_STATE_CHANGE_FAILURE)
1925 /* find ts-offset element */
1927 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1930 /* check if the sink, or something within the sink, has the volume property.
1931 * If it does we don't need to add a volume element. */
1933 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
1936 chain->volume = elem;
1938 if (playsink->volume_changed) {
1939 GST_DEBUG_OBJECT (playsink, "the sink has a volume property, setting %f",
1941 /* use the sink to control the volume */
1942 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
1943 playsink->volume_changed = FALSE;
1946 g_signal_connect (chain->volume, "notify::volume",
1947 G_CALLBACK (notify_volume_cb), playsink);
1948 /* if the sink also has a mute property we can use this as well. We'll only
1949 * use the mute property if there is a volume property. We can simulate the
1950 * mute with the volume otherwise. */
1952 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
1955 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
1956 g_signal_connect (chain->mute, "notify::mute",
1957 G_CALLBACK (notify_mute_cb), playsink);
1960 /* no volume, we need to add a volume element when we can */
1961 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
1963 GST_LOG_OBJECT (playsink, "non-raw format, can't do soft volume control");
1965 disconnect_chain (chain, playsink);
1966 chain->volume = NULL;
1969 /* both last and current chain are raw audio, there should be a volume
1970 * element already, unless the sink changed from one with a volume
1971 * property to one that hasn't got a volume property, in which case we
1972 * re-generate the chain */
1973 if (chain->volume == NULL) {
1974 GST_DEBUG_OBJECT (playsink, "no existing volume element to re-use");
1975 /* undo background state change done earlier */
1976 gst_element_set_state (chain->sink, GST_STATE_NULL);
1980 GST_DEBUG_OBJECT (playsink, "reusing existing volume element");
1987 * +-------------------------------------------------------------------+
1989 * | +----------+ +------------+ +----------+ +-------+ |
1990 * | | visqueue | | audioconv | | audiores | | vis | |
1991 * | +-sink src-sink + samp src-sink src-sink src-+ |
1992 * | | +----------+ +------------+ +----------+ +-------+ | |
1994 * +-------------------------------------------------------------------+
1997 static GstPlayVisChain *
1998 gen_vis_chain (GstPlaySink * playsink)
2000 GstPlayVisChain *chain;
2006 chain = g_new0 (GstPlayVisChain, 1);
2007 chain->chain.playsink = playsink;
2009 GST_DEBUG_OBJECT (playsink, "making vis chain %p", chain);
2011 chain->chain.bin = gst_bin_new ("visbin");
2012 bin = GST_BIN_CAST (chain->chain.bin);
2013 gst_object_ref_sink (bin);
2015 /* we're queuing raw audio here, we can remove this queue when we can disable
2016 * async behaviour in the video sink. */
2017 chain->queue = gst_element_factory_make ("queue", "visqueue");
2018 if (chain->queue == NULL)
2020 g_object_set (chain->queue, "silent", TRUE, NULL);
2021 gst_bin_add (bin, chain->queue);
2023 chain->conv = gst_element_factory_make ("audioconvert", "aconv");
2024 if (chain->conv == NULL)
2025 goto no_audioconvert;
2026 gst_bin_add (bin, chain->conv);
2028 chain->resample = gst_element_factory_make ("audioresample", "aresample");
2029 if (chain->resample == NULL)
2030 goto no_audioresample;
2031 gst_bin_add (bin, chain->resample);
2033 /* this pad will be used for blocking the dataflow and switching the vis
2035 chain->blockpad = gst_element_get_static_pad (chain->resample, "src");
2037 if (playsink->visualisation) {
2038 GST_DEBUG_OBJECT (playsink, "trying configure vis");
2039 chain->vis = try_element (playsink, playsink->visualisation, FALSE);
2041 if (chain->vis == NULL) {
2042 GST_DEBUG_OBJECT (playsink, "trying goom");
2043 elem = gst_element_factory_make ("goom", "vis");
2044 chain->vis = try_element (playsink, elem, TRUE);
2046 if (chain->vis == NULL)
2049 gst_bin_add (bin, chain->vis);
2051 res = gst_element_link_pads_full (chain->queue, "src", chain->conv, "sink",
2052 GST_PAD_LINK_CHECK_NOTHING);
2054 gst_element_link_pads_full (chain->conv, "src", chain->resample, "sink",
2055 GST_PAD_LINK_CHECK_NOTHING);
2057 gst_element_link_pads_full (chain->resample, "src", chain->vis, "sink",
2058 GST_PAD_LINK_CHECK_NOTHING);
2062 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
2063 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
2065 pad = gst_element_get_static_pad (chain->queue, "sink");
2066 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
2067 gst_object_unref (pad);
2068 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
2070 chain->srcpad = gst_ghost_pad_new ("src", chain->vissrcpad);
2071 gst_element_add_pad (chain->chain.bin, chain->srcpad);
2078 post_missing_element_message (playsink, "queue");
2079 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2080 (_("Missing element '%s' - check your GStreamer installation."),
2082 free_chain ((GstPlayChain *) chain);
2087 post_missing_element_message (playsink, "audioconvert");
2088 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2089 (_("Missing element '%s' - check your GStreamer installation."),
2090 "audioconvert"), ("possibly a liboil version mismatch?"));
2091 free_chain ((GstPlayChain *) chain);
2096 post_missing_element_message (playsink, "audioresample");
2097 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2098 (_("Missing element '%s' - check your GStreamer installation."),
2099 "audioresample"), (NULL));
2100 free_chain ((GstPlayChain *) chain);
2105 post_missing_element_message (playsink, "goom");
2106 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2107 (_("Missing element '%s' - check your GStreamer installation."),
2109 free_chain ((GstPlayChain *) chain);
2114 GST_ELEMENT_ERROR (playsink, CORE, PAD,
2115 (NULL), ("Failed to configure the visualisation element."));
2116 /* element made it to READY */
2117 gst_element_set_state (chain->vis, GST_STATE_NULL);
2118 free_chain ((GstPlayChain *) chain);
2123 /* this function is called when all the request pads are requested and when we
2124 * have to construct the final pipeline. Based on the flags we construct the
2125 * final output pipelines.
2128 gst_play_sink_reconfigure (GstPlaySink * playsink)
2131 gboolean need_audio, need_video, need_deinterlace, need_vis, need_text;
2133 GST_DEBUG_OBJECT (playsink, "reconfiguring");
2135 /* assume we need nothing */
2136 need_audio = need_video = need_deinterlace = need_vis = need_text = FALSE;
2138 GST_PLAY_SINK_LOCK (playsink);
2139 GST_OBJECT_LOCK (playsink);
2140 /* get flags, there are protected with the object lock */
2141 flags = playsink->flags;
2142 GST_OBJECT_UNLOCK (playsink);
2144 /* figure out which components we need */
2145 if (flags & GST_PLAY_FLAG_TEXT && playsink->text_pad) {
2146 /* we have subtitles and we are requested to show it */
2150 if (((flags & GST_PLAY_FLAG_VIDEO)
2151 || (flags & GST_PLAY_FLAG_NATIVE_VIDEO)) && playsink->video_pad) {
2152 /* we have video and we are requested to show it */
2155 /* we only deinterlace if native video is not requested and
2156 * we have raw video */
2157 if ((flags & GST_PLAY_FLAG_DEINTERLACE)
2158 && !(flags & GST_PLAY_FLAG_NATIVE_VIDEO) && playsink->video_pad_raw)
2159 need_deinterlace = TRUE;
2162 if (playsink->audio_pad) {
2163 if ((flags & GST_PLAY_FLAG_AUDIO) || (flags & GST_PLAY_FLAG_NATIVE_AUDIO)) {
2166 if (playsink->audio_pad_raw) {
2167 /* only can do vis with raw uncompressed audio */
2168 if (flags & GST_PLAY_FLAG_VIS && !need_video) {
2169 /* also add video when we add visualisation */
2176 /* we have a text_pad and we need text rendering, in this case we need a
2177 * video_pad to combine the video with the text or visualizations */
2178 if (need_text && !need_video) {
2179 if (playsink->video_pad) {
2181 } else if (need_audio) {
2182 GST_ELEMENT_WARNING (playsink, STREAM, FORMAT,
2183 (_("Can't play a text file without video or visualizations.")),
2184 ("Have text pad but no video pad or visualizations"));
2187 GST_ELEMENT_ERROR (playsink, STREAM, FORMAT,
2188 (_("Can't play a text file without video or visualizations.")),
2189 ("Have text pad but no video pad or visualizations"));
2190 GST_PLAY_SINK_UNLOCK (playsink);
2195 GST_DEBUG_OBJECT (playsink, "audio:%d, video:%d, vis:%d, text:%d", need_audio,
2196 need_video, need_vis, need_text);
2198 /* set up video pipeline */
2200 gboolean raw, async;
2202 /* we need a raw sink when we do vis or when we have a raw pad */
2203 raw = need_vis ? TRUE : playsink->video_pad_raw;
2204 /* we try to set the sink async=FALSE when we need vis, this way we can
2205 * avoid a queue in the audio chain. */
2208 GST_DEBUG_OBJECT (playsink, "adding video, raw %d",
2209 playsink->video_pad_raw);
2211 if (playsink->videochain) {
2212 /* try to reactivate the chain */
2213 if (!setup_video_chain (playsink, raw, async)) {
2214 if (playsink->video_sinkpad_stream_synchronizer) {
2215 gst_element_release_request_pad (GST_ELEMENT_CAST
2216 (playsink->stream_synchronizer),
2217 playsink->video_sinkpad_stream_synchronizer);
2218 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2219 playsink->video_sinkpad_stream_synchronizer = NULL;
2220 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2221 playsink->video_srcpad_stream_synchronizer = NULL;
2224 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2226 /* Remove the sink from the bin to keep its state
2227 * and unparent it to allow reuse */
2228 if (playsink->videochain->sink)
2229 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
2230 playsink->videochain->sink);
2232 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2233 free_chain ((GstPlayChain *) playsink->videochain);
2234 playsink->videochain = NULL;
2238 if (!playsink->videochain)
2239 playsink->videochain = gen_video_chain (playsink, raw, async);
2240 if (!playsink->videochain)
2243 if (!playsink->video_sinkpad_stream_synchronizer) {
2246 playsink->video_sinkpad_stream_synchronizer =
2247 gst_element_get_request_pad (GST_ELEMENT_CAST
2248 (playsink->stream_synchronizer), "sink_%d");
2249 it = gst_pad_iterate_internal_links
2250 (playsink->video_sinkpad_stream_synchronizer);
2252 gst_iterator_next (it,
2253 (gpointer *) & playsink->video_srcpad_stream_synchronizer);
2254 g_assert (playsink->video_srcpad_stream_synchronizer);
2255 gst_iterator_free (it);
2258 if (playsink->video_pad)
2259 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad),
2260 playsink->video_sinkpad_stream_synchronizer);
2262 if (need_deinterlace) {
2263 if (!playsink->videodeinterlacechain)
2264 playsink->videodeinterlacechain =
2265 gen_video_deinterlace_chain (playsink);
2266 if (!playsink->videodeinterlacechain)
2269 GST_DEBUG_OBJECT (playsink, "adding video deinterlace chain");
2271 GST_DEBUG_OBJECT (playsink, "setting up deinterlacing chain");
2273 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
2274 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
2276 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2277 playsink->videodeinterlacechain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2279 if (playsink->videodeinterlacechain) {
2280 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2281 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
2286 GST_DEBUG_OBJECT (playsink, "adding video chain");
2287 add_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
2288 activate_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
2289 /* if we are not part of vis or subtitles, set the ghostpad target */
2290 if (!need_vis && !need_text && (!playsink->textchain
2291 || !playsink->text_pad)) {
2292 GST_DEBUG_OBJECT (playsink, "ghosting video sinkpad");
2293 if (need_deinterlace)
2294 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
2295 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2297 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2298 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2301 GST_DEBUG_OBJECT (playsink, "no video needed");
2302 if (playsink->videochain) {
2303 GST_DEBUG_OBJECT (playsink, "removing video chain");
2304 if (playsink->vischain) {
2307 GST_DEBUG_OBJECT (playsink, "unlinking vis chain");
2309 /* also had visualisation, release the tee srcpad before we then
2310 * unlink the video from it */
2311 if (playsink->audio_tee_vissrc) {
2312 gst_element_release_request_pad (playsink->audio_tee,
2313 playsink->audio_tee_vissrc);
2314 gst_object_unref (playsink->audio_tee_vissrc);
2315 playsink->audio_tee_vissrc = NULL;
2318 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2319 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
2322 if (playsink->video_sinkpad_stream_synchronizer) {
2323 gst_element_release_request_pad (GST_ELEMENT_CAST
2324 (playsink->stream_synchronizer),
2325 playsink->video_sinkpad_stream_synchronizer);
2326 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2327 playsink->video_sinkpad_stream_synchronizer = NULL;
2328 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2329 playsink->video_srcpad_stream_synchronizer = NULL;
2332 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2333 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2334 playsink->videochain->ts_offset = NULL;
2337 if (playsink->videodeinterlacechain) {
2338 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2339 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2342 if (playsink->video_pad)
2343 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
2349 GST_DEBUG_OBJECT (playsink, "adding audio");
2351 /* get a raw sink if we are asked for a raw pad */
2352 raw = playsink->audio_pad_raw;
2354 if (playsink->audiochain) {
2355 /* try to reactivate the chain */
2356 if (!setup_audio_chain (playsink, raw)) {
2357 GST_DEBUG_OBJECT (playsink, "removing current audio chain");
2358 if (playsink->audio_tee_asrc) {
2359 gst_element_release_request_pad (playsink->audio_tee,
2360 playsink->audio_tee_asrc);
2361 gst_object_unref (playsink->audio_tee_asrc);
2362 playsink->audio_tee_asrc = NULL;
2365 if (playsink->audio_sinkpad_stream_synchronizer) {
2366 gst_element_release_request_pad (GST_ELEMENT_CAST
2367 (playsink->stream_synchronizer),
2368 playsink->audio_sinkpad_stream_synchronizer);
2369 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
2370 playsink->audio_sinkpad_stream_synchronizer = NULL;
2371 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
2372 playsink->audio_srcpad_stream_synchronizer = NULL;
2375 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2377 /* Remove the sink from the bin to keep its state
2378 * and unparent it to allow reuse */
2379 if (playsink->audiochain->sink)
2380 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
2381 playsink->audiochain->sink);
2383 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2384 disconnect_chain (playsink->audiochain, playsink);
2385 playsink->audiochain->volume = NULL;
2386 playsink->audiochain->mute = NULL;
2387 playsink->audiochain->ts_offset = NULL;
2388 free_chain ((GstPlayChain *) playsink->audiochain);
2389 playsink->audiochain = NULL;
2390 playsink->volume_changed = playsink->mute_changed = FALSE;
2394 if (!playsink->audiochain) {
2395 GST_DEBUG_OBJECT (playsink, "creating new audio chain");
2396 playsink->audiochain = gen_audio_chain (playsink, raw);
2399 if (!playsink->audio_sinkpad_stream_synchronizer) {
2402 playsink->audio_sinkpad_stream_synchronizer =
2403 gst_element_get_request_pad (GST_ELEMENT_CAST
2404 (playsink->stream_synchronizer), "sink_%d");
2405 it = gst_pad_iterate_internal_links
2406 (playsink->audio_sinkpad_stream_synchronizer);
2408 gst_iterator_next (it,
2409 (gpointer *) & playsink->audio_srcpad_stream_synchronizer);
2410 g_assert (playsink->audio_srcpad_stream_synchronizer);
2411 gst_iterator_free (it);
2414 if (playsink->audiochain) {
2415 GST_DEBUG_OBJECT (playsink, "adding audio chain");
2416 if (playsink->audio_tee_asrc == NULL) {
2417 playsink->audio_tee_asrc =
2418 gst_element_get_request_pad (playsink->audio_tee, "src%d");
2420 add_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
2421 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
2422 gst_pad_link_full (playsink->audio_tee_asrc,
2423 playsink->audio_sinkpad_stream_synchronizer,
2424 GST_PAD_LINK_CHECK_NOTHING);
2425 gst_pad_link_full (playsink->audio_srcpad_stream_synchronizer,
2426 playsink->audiochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2429 GST_DEBUG_OBJECT (playsink, "no audio needed");
2430 /* we have no audio or we are requested to not play audio */
2431 if (playsink->audiochain) {
2432 GST_DEBUG_OBJECT (playsink, "removing audio chain");
2433 /* release the audio pad */
2434 if (playsink->audio_tee_asrc) {
2435 gst_element_release_request_pad (playsink->audio_tee,
2436 playsink->audio_tee_asrc);
2437 gst_object_unref (playsink->audio_tee_asrc);
2438 playsink->audio_tee_asrc = NULL;
2441 if (playsink->audio_sinkpad_stream_synchronizer) {
2442 gst_element_release_request_pad (GST_ELEMENT_CAST
2443 (playsink->stream_synchronizer),
2444 playsink->audio_sinkpad_stream_synchronizer);
2445 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
2446 playsink->audio_sinkpad_stream_synchronizer = NULL;
2447 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
2448 playsink->audio_srcpad_stream_synchronizer = NULL;
2451 if (playsink->audiochain->sink_volume) {
2452 disconnect_chain (playsink->audiochain, playsink);
2453 playsink->audiochain->volume = NULL;
2454 playsink->audiochain->mute = NULL;
2455 playsink->audiochain->ts_offset = NULL;
2457 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2458 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2465 if (!playsink->vischain)
2466 playsink->vischain = gen_vis_chain (playsink);
2468 GST_DEBUG_OBJECT (playsink, "adding visualisation");
2470 if (playsink->vischain) {
2471 GST_DEBUG_OBJECT (playsink, "setting up vis chain");
2473 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2474 add_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
2475 activate_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
2476 if (playsink->audio_tee_vissrc == NULL) {
2477 playsink->audio_tee_vissrc =
2478 gst_element_get_request_pad (playsink->audio_tee, "src%d");
2480 gst_pad_link_full (playsink->audio_tee_vissrc,
2481 playsink->vischain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2482 gst_pad_link_full (srcpad, playsink->video_sinkpad_stream_synchronizer,
2483 GST_PAD_LINK_CHECK_NOTHING);
2484 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2485 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2486 gst_object_unref (srcpad);
2489 GST_DEBUG_OBJECT (playsink, "no vis needed");
2490 if (playsink->vischain) {
2491 if (playsink->audio_tee_vissrc) {
2492 gst_element_release_request_pad (playsink->audio_tee,
2493 playsink->audio_tee_vissrc);
2494 gst_object_unref (playsink->audio_tee_vissrc);
2495 playsink->audio_tee_vissrc = NULL;
2497 GST_DEBUG_OBJECT (playsink, "removing vis chain");
2498 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
2499 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
2504 GST_DEBUG_OBJECT (playsink, "adding text");
2505 if (!playsink->textchain) {
2506 GST_DEBUG_OBJECT (playsink, "creating text chain");
2507 playsink->textchain = gen_text_chain (playsink);
2509 if (playsink->textchain) {
2512 GST_DEBUG_OBJECT (playsink, "adding text chain");
2513 if (playsink->textchain->overlay)
2514 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", FALSE,
2516 add_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
2518 if (!playsink->text_sinkpad_stream_synchronizer) {
2519 playsink->text_sinkpad_stream_synchronizer =
2520 gst_element_get_request_pad (GST_ELEMENT_CAST
2521 (playsink->stream_synchronizer), "sink_%d");
2522 it = gst_pad_iterate_internal_links
2523 (playsink->text_sinkpad_stream_synchronizer);
2525 gst_iterator_next (it,
2526 (gpointer *) & playsink->text_srcpad_stream_synchronizer);
2527 g_assert (playsink->text_srcpad_stream_synchronizer);
2528 gst_iterator_free (it);
2530 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad),
2531 playsink->text_sinkpad_stream_synchronizer);
2532 gst_pad_link_full (playsink->text_srcpad_stream_synchronizer,
2533 playsink->textchain->textsinkpad, GST_PAD_LINK_CHECK_NOTHING);
2540 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2541 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
2542 gst_pad_link_full (srcpad, playsink->textchain->videosinkpad,
2543 GST_PAD_LINK_CHECK_NOTHING);
2544 gst_object_unref (srcpad);
2546 if (need_deinterlace)
2547 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
2548 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
2550 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2551 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
2553 gst_pad_link_full (playsink->textchain->srcpad,
2554 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2556 activate_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
2559 GST_DEBUG_OBJECT (playsink, "no text needed");
2560 /* we have no subtitles/text or we are requested to not show them */
2562 if (playsink->text_sinkpad_stream_synchronizer) {
2563 gst_element_release_request_pad (GST_ELEMENT_CAST
2564 (playsink->stream_synchronizer),
2565 playsink->text_sinkpad_stream_synchronizer);
2566 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
2567 playsink->text_sinkpad_stream_synchronizer = NULL;
2568 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
2569 playsink->text_srcpad_stream_synchronizer = NULL;
2572 if (playsink->textchain) {
2573 if (playsink->text_pad == NULL) {
2574 /* no text pad, remove the chain entirely */
2575 GST_DEBUG_OBJECT (playsink, "removing text chain");
2576 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
2577 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
2579 /* we have a chain and a textpad, turn the subtitles off */
2580 GST_DEBUG_OBJECT (playsink, "turning off the text");
2581 if (playsink->textchain->overlay)
2582 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", TRUE,
2586 if (!need_video && playsink->video_pad) {
2587 if (playsink->video_sinkpad_stream_synchronizer) {
2588 gst_element_release_request_pad (GST_ELEMENT_CAST
2589 (playsink->stream_synchronizer),
2590 playsink->video_sinkpad_stream_synchronizer);
2591 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2592 playsink->video_sinkpad_stream_synchronizer = NULL;
2593 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2594 playsink->video_srcpad_stream_synchronizer = NULL;
2597 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
2600 if (playsink->text_pad && !playsink->textchain)
2601 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad), NULL);
2603 update_av_offset (playsink);
2604 do_async_done (playsink);
2605 GST_PLAY_SINK_UNLOCK (playsink);
2612 /* gen_ chain already posted error */
2613 GST_DEBUG_OBJECT (playsink, "failed to setup chain");
2614 GST_PLAY_SINK_UNLOCK (playsink);
2620 * gst_play_sink_set_flags:
2621 * @playsink: a #GstPlaySink
2622 * @flags: #GstPlayFlags
2624 * Configure @flags on @playsink. The flags control the behaviour of @playsink
2625 * when constructing the sink pipelins.
2627 * Returns: TRUE if the flags could be configured.
2630 gst_play_sink_set_flags (GstPlaySink * playsink, GstPlayFlags flags)
2632 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), FALSE);
2634 GST_OBJECT_LOCK (playsink);
2635 playsink->flags = flags;
2636 GST_OBJECT_UNLOCK (playsink);
2642 * gst_play_sink_get_flags:
2643 * @playsink: a #GstPlaySink
2645 * Get the flags of @playsink. That flags control the behaviour of the sink when
2646 * it constructs the sink pipelines.
2648 * Returns: the currently configured #GstPlayFlags.
2651 gst_play_sink_get_flags (GstPlaySink * playsink)
2655 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), 0);
2657 GST_OBJECT_LOCK (playsink);
2658 res = playsink->flags;
2659 GST_OBJECT_UNLOCK (playsink);
2665 gst_play_sink_set_font_desc (GstPlaySink * playsink, const gchar * desc)
2667 GstPlayTextChain *chain;
2669 GST_PLAY_SINK_LOCK (playsink);
2670 chain = (GstPlayTextChain *) playsink->textchain;
2671 g_free (playsink->font_desc);
2672 playsink->font_desc = g_strdup (desc);
2673 if (chain && chain->overlay) {
2674 g_object_set (chain->overlay, "font-desc", desc, NULL);
2676 GST_PLAY_SINK_UNLOCK (playsink);
2680 gst_play_sink_get_font_desc (GstPlaySink * playsink)
2682 gchar *result = NULL;
2683 GstPlayTextChain *chain;
2685 GST_PLAY_SINK_LOCK (playsink);
2686 chain = (GstPlayTextChain *) playsink->textchain;
2687 if (chain && chain->overlay) {
2688 g_object_get (chain->overlay, "font-desc", &result, NULL);
2689 playsink->font_desc = g_strdup (result);
2691 result = g_strdup (playsink->font_desc);
2693 GST_PLAY_SINK_UNLOCK (playsink);
2699 gst_play_sink_set_subtitle_encoding (GstPlaySink * playsink,
2700 const gchar * encoding)
2702 GstPlayTextChain *chain;
2704 GST_PLAY_SINK_LOCK (playsink);
2705 chain = (GstPlayTextChain *) playsink->textchain;
2706 g_free (playsink->subtitle_encoding);
2707 playsink->subtitle_encoding = g_strdup (encoding);
2708 if (chain && chain->overlay) {
2709 g_object_set (chain->overlay, "subtitle-encoding", encoding, NULL);
2711 GST_PLAY_SINK_UNLOCK (playsink);
2715 gst_play_sink_get_subtitle_encoding (GstPlaySink * playsink)
2717 gchar *result = NULL;
2718 GstPlayTextChain *chain;
2720 GST_PLAY_SINK_LOCK (playsink);
2721 chain = (GstPlayTextChain *) playsink->textchain;
2722 if (chain && chain->overlay) {
2723 g_object_get (chain->overlay, "subtitle-encoding", &result, NULL);
2724 playsink->subtitle_encoding = g_strdup (result);
2726 result = g_strdup (playsink->subtitle_encoding);
2728 GST_PLAY_SINK_UNLOCK (playsink);
2734 update_av_offset (GstPlaySink * playsink)
2737 GstPlayAudioChain *achain;
2738 GstPlayVideoChain *vchain;
2740 av_offset = playsink->av_offset;
2741 achain = (GstPlayAudioChain *) playsink->audiochain;
2742 vchain = (GstPlayVideoChain *) playsink->videochain;
2744 if (achain && vchain && achain->ts_offset && vchain->ts_offset) {
2745 g_object_set (achain->ts_offset, "ts-offset", MAX (0, -av_offset), NULL);
2746 g_object_set (vchain->ts_offset, "ts-offset", MAX (0, av_offset), NULL);
2748 GST_LOG_OBJECT (playsink, "no ts_offset elements");
2753 gst_play_sink_set_av_offset (GstPlaySink * playsink, gint64 av_offset)
2755 GST_PLAY_SINK_LOCK (playsink);
2756 playsink->av_offset = av_offset;
2757 update_av_offset (playsink);
2758 GST_PLAY_SINK_UNLOCK (playsink);
2762 gst_play_sink_get_av_offset (GstPlaySink * playsink)
2766 GST_PLAY_SINK_LOCK (playsink);
2767 result = playsink->av_offset;
2768 GST_PLAY_SINK_UNLOCK (playsink);
2774 * gst_play_sink_get_last_frame:
2775 * @playsink: a #GstPlaySink
2777 * Get the last displayed frame from @playsink. This frame is in the native
2778 * format of the sink element, the caps on the result buffer contain the format
2779 * of the frame data.
2781 * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
2785 gst_play_sink_get_last_frame (GstPlaySink * playsink)
2787 GstBuffer *result = NULL;
2788 GstPlayVideoChain *chain;
2790 GST_PLAY_SINK_LOCK (playsink);
2791 GST_DEBUG_OBJECT (playsink, "taking last frame");
2792 /* get the video chain if we can */
2793 if ((chain = (GstPlayVideoChain *) playsink->videochain)) {
2794 GST_DEBUG_OBJECT (playsink, "found video chain");
2795 /* see if the chain is active */
2796 if (chain->chain.activated && chain->sink) {
2799 GST_DEBUG_OBJECT (playsink, "video chain active and has a sink");
2801 /* find and get the last-buffer property now */
2803 gst_play_sink_find_property (playsink, chain->sink,
2804 "last-buffer", GST_TYPE_BUFFER))) {
2805 GST_DEBUG_OBJECT (playsink, "getting last-buffer property");
2806 g_object_get (elem, "last-buffer", &result, NULL);
2807 gst_object_unref (elem);
2811 GST_PLAY_SINK_UNLOCK (playsink);
2817 * gst_play_sink_convert_frame:
2818 * @playsink: a #GstPlaySink
2821 * Get the last displayed frame from @playsink. If caps is %NULL, the video will
2822 * be in the native format of the sink element and the caps on the buffer
2823 * describe the format of the frame. If @caps is not %NULL, the video
2824 * frame will be converted to the format of the caps.
2826 * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
2827 * available or when the conversion failed.
2830 gst_play_sink_convert_frame (GstPlaySink * playsink, GstCaps * caps)
2834 result = gst_play_sink_get_last_frame (playsink);
2835 if (result != NULL && caps != NULL) {
2839 temp = gst_video_convert_frame (result, caps, 25 * GST_SECOND, &err);
2840 gst_buffer_unref (result);
2841 if (temp == NULL && err) {
2842 /* I'm really uncertain whether we should make playsink post an error
2843 * on the bus or not. It's not like it's a critical issue regarding
2844 * playsink behaviour. */
2845 GST_ERROR ("Error converting frame: %s", err->message);
2853 * gst_play_sink_request_pad
2854 * @playsink: a #GstPlaySink
2855 * @type: a #GstPlaySinkType
2857 * Create or return a pad of @type.
2859 * Returns: a #GstPad of @type or %NULL when the pad could not be created.
2862 gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type)
2865 gboolean created = FALSE;
2866 gboolean raw = FALSE;
2867 gboolean activate = TRUE;
2868 const gchar *pad_name = NULL;
2870 GST_DEBUG_OBJECT (playsink, "request pad type %d", type);
2872 GST_PLAY_SINK_LOCK (playsink);
2874 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
2875 pad_name = "audio_raw_sink";
2877 case GST_PLAY_SINK_TYPE_AUDIO:
2878 if (pad_name == NULL)
2879 pad_name = "audio_sink";
2880 if (!playsink->audio_tee) {
2881 GST_LOG_OBJECT (playsink, "creating tee");
2882 /* create tee when needed. This element will feed the audio sink chain
2883 * and the vis chain. */
2884 playsink->audio_tee = gst_element_factory_make ("tee", "audiotee");
2885 if (playsink->audio_tee == NULL) {
2886 post_missing_element_message (playsink, "tee");
2887 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2888 (_("Missing element '%s' - check your GStreamer installation."),
2893 playsink->audio_tee_sink =
2894 gst_element_get_static_pad (playsink->audio_tee, "sink");
2895 gst_bin_add (GST_BIN_CAST (playsink), playsink->audio_tee);
2896 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
2899 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
2901 if (!playsink->audio_pad) {
2902 GST_LOG_OBJECT (playsink, "ghosting tee sinkpad");
2903 playsink->audio_pad =
2904 gst_ghost_pad_new (pad_name, playsink->audio_tee_sink);
2907 playsink->audio_pad_raw = raw;
2908 res = playsink->audio_pad;
2910 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
2911 pad_name = "video_raw_sink";
2913 case GST_PLAY_SINK_TYPE_VIDEO:
2914 if (pad_name == NULL)
2915 pad_name = "video_sink";
2916 if (!playsink->video_pad) {
2917 GST_LOG_OBJECT (playsink, "ghosting videosink");
2918 playsink->video_pad =
2919 gst_ghost_pad_new_no_target (pad_name, GST_PAD_SINK);
2922 playsink->video_pad_raw = raw;
2923 res = playsink->video_pad;
2925 case GST_PLAY_SINK_TYPE_TEXT:
2926 GST_LOG_OBJECT (playsink, "ghosting text");
2927 if (!playsink->text_pad) {
2928 playsink->text_pad =
2929 gst_ghost_pad_new_no_target ("text_sink", GST_PAD_SINK);
2932 res = playsink->text_pad;
2934 case GST_PLAY_SINK_TYPE_FLUSHING:
2938 /* we need a unique padname for the flushing pad. */
2939 padname = g_strdup_printf ("flushing_%d", playsink->count);
2940 res = gst_ghost_pad_new_no_target (padname, GST_PAD_SINK);
2951 GST_PLAY_SINK_UNLOCK (playsink);
2953 if (created && res) {
2954 /* we have to add the pad when it's active or we get an error when the
2955 * element is 'running' */
2956 gst_pad_set_active (res, TRUE);
2957 gst_element_add_pad (GST_ELEMENT_CAST (playsink), res);
2959 gst_pad_set_active (res, activate);
2966 gst_play_sink_request_new_pad (GstElement * element, GstPadTemplate * templ,
2971 GstPlaySinkType type;
2972 const gchar *tplname;
2974 g_return_val_if_fail (templ != NULL, NULL);
2976 GST_DEBUG_OBJECT (element, "name:%s", name);
2978 psink = GST_PLAY_SINK (element);
2979 tplname = GST_PAD_TEMPLATE_NAME_TEMPLATE (templ);
2981 /* Figure out the GstPlaySinkType based on the template */
2982 if (!strcmp (tplname, "audio_sink"))
2983 type = GST_PLAY_SINK_TYPE_AUDIO;
2984 else if (!strcmp (tplname, "audio_raw_sink"))
2985 type = GST_PLAY_SINK_TYPE_AUDIO_RAW;
2986 else if (!strcmp (tplname, "video_sink"))
2987 type = GST_PLAY_SINK_TYPE_VIDEO;
2988 else if (!strcmp (tplname, "video_raw_sink"))
2989 type = GST_PLAY_SINK_TYPE_VIDEO_RAW;
2990 else if (!strcmp (tplname, "text_sink"))
2991 type = GST_PLAY_SINK_TYPE_TEXT;
2993 goto unknown_template;
2995 pad = gst_play_sink_request_pad (psink, type);
2999 GST_WARNING_OBJECT (element, "Unknown pad template");
3004 gst_play_sink_release_pad (GstPlaySink * playsink, GstPad * pad)
3006 GstPad **res = NULL;
3007 gboolean untarget = TRUE;
3009 GST_DEBUG_OBJECT (playsink, "release pad %" GST_PTR_FORMAT, pad);
3011 GST_PLAY_SINK_LOCK (playsink);
3012 if (pad == playsink->video_pad) {
3013 res = &playsink->video_pad;
3014 } else if (pad == playsink->audio_pad) {
3015 res = &playsink->audio_pad;
3016 } else if (pad == playsink->text_pad) {
3017 res = &playsink->text_pad;
3019 /* try to release the given pad anyway, these could be the FLUSHING pads. */
3023 GST_PLAY_SINK_UNLOCK (playsink);
3026 GST_DEBUG_OBJECT (playsink, "deactivate pad %" GST_PTR_FORMAT, *res);
3027 gst_pad_set_active (*res, FALSE);
3029 GST_DEBUG_OBJECT (playsink, "untargeting pad %" GST_PTR_FORMAT, *res);
3030 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (*res), NULL);
3032 GST_DEBUG_OBJECT (playsink, "remove pad %" GST_PTR_FORMAT, *res);
3033 gst_element_remove_pad (GST_ELEMENT_CAST (playsink), *res);
3039 gst_play_sink_release_request_pad (GstElement * element, GstPad * pad)
3041 GstPlaySink *psink = GST_PLAY_SINK (element);
3043 gst_play_sink_release_pad (psink, pad);
3047 gst_play_sink_handle_message (GstBin * bin, GstMessage * message)
3049 GstPlaySink *playsink;
3051 playsink = GST_PLAY_SINK_CAST (bin);
3053 switch (GST_MESSAGE_TYPE (message)) {
3054 case GST_MESSAGE_STEP_DONE:
3059 gboolean flush, intermediate, eos;
3062 GST_INFO_OBJECT (playsink, "Handling step-done message");
3063 gst_message_parse_step_done (message, &format, &amount, &rate, &flush,
3064 &intermediate, &duration, &eos);
3066 if (format == GST_FORMAT_BUFFERS) {
3067 /* for the buffer format, we align the other streams */
3068 if (playsink->audiochain) {
3072 gst_event_new_step (GST_FORMAT_TIME, duration, rate, flush,
3075 if (!gst_element_send_event (playsink->audiochain->chain.bin, event)) {
3076 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
3080 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
3084 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
3089 /* Send an event to our sinks until one of them works; don't then send to the
3090 * remaining sinks (unlike GstBin)
3091 * Special case: If a text sink is set we need to send the event
3092 * to them in case it's source is different from the a/v stream's source.
3095 gst_play_sink_send_event_to_sink (GstPlaySink * playsink, GstEvent * event)
3097 gboolean res = TRUE;
3099 if (playsink->textchain && playsink->textchain->sink) {
3100 gst_event_ref (event);
3101 if ((res = gst_element_send_event (playsink->textchain->chain.bin, event))) {
3102 GST_DEBUG_OBJECT (playsink, "Sent event succesfully to text sink");
3104 GST_DEBUG_OBJECT (playsink, "Event failed when sent to text sink");
3108 if (playsink->videochain) {
3109 gst_event_ref (event);
3110 if ((res = gst_element_send_event (playsink->videochain->chain.bin, event))) {
3111 GST_DEBUG_OBJECT (playsink, "Sent event succesfully to video sink");
3114 GST_DEBUG_OBJECT (playsink, "Event failed when sent to video sink");
3116 if (playsink->audiochain) {
3117 gst_event_ref (event);
3118 if ((res = gst_element_send_event (playsink->audiochain->chain.bin, event))) {
3119 GST_DEBUG_OBJECT (playsink, "Sent event succesfully to audio sink");
3122 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
3126 gst_event_unref (event);
3130 /* We only want to send the event to a single sink (overriding GstBin's
3131 * behaviour), but we want to keep GstPipeline's behaviour - wrapping seek
3132 * events appropriately. So, this is a messy duplication of code. */
3134 gst_play_sink_send_event (GstElement * element, GstEvent * event)
3136 gboolean res = FALSE;
3137 GstEventType event_type = GST_EVENT_TYPE (event);
3138 GstPlaySink *playsink;
3140 playsink = GST_PLAY_SINK_CAST (element);
3142 switch (event_type) {
3143 case GST_EVENT_SEEK:
3144 GST_DEBUG_OBJECT (element, "Sending event to a sink");
3145 res = gst_play_sink_send_event_to_sink (playsink, event);
3147 case GST_EVENT_STEP:
3152 gboolean flush, intermediate;
3154 gst_event_parse_step (event, &format, &amount, &rate, &flush,
3157 if (format == GST_FORMAT_BUFFERS) {
3158 /* for buffers, we will try to step video frames, for other formats we
3159 * send the step to all sinks */
3160 res = gst_play_sink_send_event_to_sink (playsink, event);
3163 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
3170 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
3177 static GstStateChangeReturn
3178 gst_play_sink_change_state (GstElement * element, GstStateChange transition)
3180 GstStateChangeReturn ret;
3181 GstStateChangeReturn bret;
3183 GstPlaySink *playsink;
3185 playsink = GST_PLAY_SINK (element);
3187 switch (transition) {
3188 case GST_STATE_CHANGE_READY_TO_PAUSED:
3189 playsink->need_async_start = TRUE;
3190 /* we want to go async to PAUSED until we managed to configure and add the
3192 do_async_start (playsink);
3193 ret = GST_STATE_CHANGE_ASYNC;
3195 case GST_STATE_CHANGE_PAUSED_TO_READY:
3196 case GST_STATE_CHANGE_READY_TO_NULL:
3197 if (playsink->audiochain && playsink->audiochain->sink_volume) {
3198 /* remove our links to the mute and volume elements when they were
3199 * provided by a sink */
3200 disconnect_chain (playsink->audiochain, playsink);
3201 playsink->audiochain->volume = NULL;
3202 playsink->audiochain->mute = NULL;
3203 playsink->audiochain->ts_offset = NULL;
3205 ret = GST_STATE_CHANGE_SUCCESS;
3208 /* all other state changes return SUCCESS by default, this value can be
3209 * overridden by the result of the children */
3210 ret = GST_STATE_CHANGE_SUCCESS;
3214 /* do the state change of the children */
3216 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->change_state (element,
3218 /* now look at the result of our children and adjust the return value */
3220 case GST_STATE_CHANGE_FAILURE:
3221 /* failure, we stop */
3222 goto activate_failed;
3223 case GST_STATE_CHANGE_NO_PREROLL:
3224 /* some child returned NO_PREROLL. This is strange but we never know. We
3225 * commit our async state change (if any) and return the NO_PREROLL */
3226 do_async_done (playsink);
3229 case GST_STATE_CHANGE_ASYNC:
3230 /* some child was async, return this */
3234 /* return our previously configured return value */
3238 switch (transition) {
3239 case GST_STATE_CHANGE_READY_TO_PAUSED:
3241 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
3242 /* FIXME Release audio device when we implement that */
3243 playsink->need_async_start = TRUE;
3245 case GST_STATE_CHANGE_PAUSED_TO_READY:{
3246 if (playsink->video_sinkpad_stream_synchronizer) {
3247 gst_element_release_request_pad (GST_ELEMENT_CAST
3248 (playsink->stream_synchronizer),
3249 playsink->video_sinkpad_stream_synchronizer);
3250 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3251 playsink->video_sinkpad_stream_synchronizer = NULL;
3252 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3253 playsink->video_srcpad_stream_synchronizer = NULL;
3255 if (playsink->audio_sinkpad_stream_synchronizer) {
3256 gst_element_release_request_pad (GST_ELEMENT_CAST
3257 (playsink->stream_synchronizer),
3258 playsink->audio_sinkpad_stream_synchronizer);
3259 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
3260 playsink->audio_sinkpad_stream_synchronizer = NULL;
3261 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
3262 playsink->audio_srcpad_stream_synchronizer = NULL;
3264 if (playsink->text_sinkpad_stream_synchronizer) {
3265 gst_element_release_request_pad (GST_ELEMENT_CAST
3266 (playsink->stream_synchronizer),
3267 playsink->text_sinkpad_stream_synchronizer);
3268 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
3269 playsink->text_sinkpad_stream_synchronizer = NULL;
3270 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
3271 playsink->text_srcpad_stream_synchronizer = NULL;
3275 case GST_STATE_CHANGE_READY_TO_NULL:
3276 /* remove sinks we added */
3277 if (playsink->videodeinterlacechain) {
3278 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
3280 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3282 if (playsink->videochain) {
3283 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3284 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3286 if (playsink->audiochain) {
3287 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3288 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3290 if (playsink->vischain) {
3291 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3292 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3294 if (playsink->textchain) {
3295 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3296 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3298 do_async_done (playsink);
3299 /* when going to READY, keep elements around as long as possible,
3300 * so they may be re-used faster next time/url around.
3301 * when really going to NULL, clean up everything completely. */
3302 if (transition == GST_STATE_CHANGE_READY_TO_NULL) {
3304 /* Unparent the sinks to allow reuse */
3305 if (playsink->videochain && playsink->videochain->sink)
3306 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
3307 playsink->videochain->sink);
3308 if (playsink->audiochain && playsink->audiochain->sink)
3309 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
3310 playsink->audiochain->sink);
3311 if (playsink->textchain && playsink->textchain->sink)
3312 gst_bin_remove (GST_BIN_CAST (playsink->textchain->chain.bin),
3313 playsink->textchain->sink);
3315 if (playsink->audio_sink != NULL)
3316 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
3317 if (playsink->video_sink != NULL)
3318 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
3319 if (playsink->visualisation != NULL)
3320 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
3321 if (playsink->text_sink != NULL)
3322 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
3324 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
3325 playsink->videodeinterlacechain = NULL;
3326 free_chain ((GstPlayChain *) playsink->videochain);
3327 playsink->videochain = NULL;
3328 free_chain ((GstPlayChain *) playsink->audiochain);
3329 playsink->audiochain = NULL;
3330 free_chain ((GstPlayChain *) playsink->vischain);
3331 playsink->vischain = NULL;
3332 free_chain ((GstPlayChain *) playsink->textchain);
3333 playsink->textchain = NULL;
3344 GST_DEBUG_OBJECT (element,
3345 "element failed to change states -- activation problem?");
3346 return GST_STATE_CHANGE_FAILURE;
3351 gst_play_sink_set_property (GObject * object, guint prop_id,
3352 const GValue * value, GParamSpec * spec)
3354 GstPlaySink *playsink = GST_PLAY_SINK (object);
3358 gst_play_sink_set_flags (playsink, g_value_get_flags (value));
3361 gst_play_sink_set_volume (playsink, g_value_get_double (value));
3364 gst_play_sink_set_mute (playsink, g_value_get_boolean (value));
3366 case PROP_FONT_DESC:
3367 gst_play_sink_set_font_desc (playsink, g_value_get_string (value));
3369 case PROP_SUBTITLE_ENCODING:
3370 gst_play_sink_set_subtitle_encoding (playsink,
3371 g_value_get_string (value));
3373 case PROP_VIS_PLUGIN:
3374 gst_play_sink_set_vis_plugin (playsink, g_value_get_object (value));
3376 case PROP_AV_OFFSET:
3377 gst_play_sink_set_av_offset (playsink, g_value_get_int64 (value));
3380 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
3386 gst_play_sink_get_property (GObject * object, guint prop_id,
3387 GValue * value, GParamSpec * spec)
3389 GstPlaySink *playsink = GST_PLAY_SINK (object);
3393 g_value_set_flags (value, gst_play_sink_get_flags (playsink));
3396 g_value_set_double (value, gst_play_sink_get_volume (playsink));
3399 g_value_set_boolean (value, gst_play_sink_get_mute (playsink));
3401 case PROP_FONT_DESC:
3402 g_value_take_string (value, gst_play_sink_get_font_desc (playsink));
3404 case PROP_SUBTITLE_ENCODING:
3405 g_value_take_string (value,
3406 gst_play_sink_get_subtitle_encoding (playsink));
3408 case PROP_VIS_PLUGIN:
3409 g_value_take_object (value, gst_play_sink_get_vis_plugin (playsink));
3412 gst_value_take_buffer (value, gst_play_sink_get_last_frame (playsink));
3414 case PROP_AV_OFFSET:
3415 g_value_set_int64 (value, gst_play_sink_get_av_offset (playsink));
3418 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
3425 gst_play_sink_plugin_init (GstPlugin * plugin)
3427 GST_DEBUG_CATEGORY_INIT (gst_play_sink_debug, "playsink", 0, "play bin");
3429 return gst_element_register (plugin, "playsink", GST_RANK_NONE,
3430 GST_TYPE_PLAY_SINK);