2 * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
3 * Copyright (C) <2011> Sebastian Dröge <sebastian.droege@collabora.co.uk>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
25 /* FIXME 0.11: suppress warnings for deprecated API such as GStaticRecMutex
26 * with newer GLib versions (>= 2.31.0) */
27 #define GLIB_DISABLE_DEPRECATION_WARNINGS
32 #include <gst/gst-i18n-plugin.h>
33 #include <gst/pbutils/pbutils.h>
34 #include <gst/video/video.h>
35 #include <gst/interfaces/streamvolume.h>
36 #include <gst/interfaces/colorbalance.h>
37 #include <gst/interfaces/xoverlay.h>
38 #include <gst/interfaces/navigation.h>
40 #include "gstplaysink.h"
41 #include "gststreamsynchronizer.h"
42 #include "gstplaysinkvideoconvert.h"
43 #include "gstplaysinkaudioconvert.h"
45 GST_DEBUG_CATEGORY_STATIC (gst_play_sink_debug);
46 #define GST_CAT_DEFAULT gst_play_sink_debug
48 #define VOLUME_MAX_DOUBLE 10.0
50 #define DEFAULT_FLAGS GST_PLAY_FLAG_AUDIO | GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_TEXT | \
51 GST_PLAY_FLAG_SOFT_VOLUME | GST_PLAY_FLAG_SOFT_COLORBALANCE
53 #define GST_PLAY_CHAIN(c) ((GstPlayChain *)(c))
55 /* holds the common data fields for the audio and video pipelines. We keep them
56 * in a structure to more easily have all the info available. */
59 GstPlaySink *playsink;
72 GstElement *volume; /* element with the volume property */
73 gboolean sink_volume; /* if the volume was provided by the sink */
74 GstElement *mute; /* element with the mute property */
76 GstElement *ts_offset;
82 GstPad *sinkpad, *srcpad;
84 GstElement *deinterlace;
85 } GstPlayVideoDeinterlaceChain;
95 GstElement *ts_offset;
104 GstElement *resample;
105 GstPad *blockpad; /* srcpad of resample, used for switching the vis */
106 GstPad *vissinkpad; /* visualisation sinkpad, */
108 GstPad *vissrcpad; /* visualisation srcpad, */
109 GstPad *srcpad; /* outgoing srcpad, used to connect to the next
118 GstElement *identity;
120 GstPad *videosinkpad;
122 GstPad *srcpad; /* outgoing srcpad, used to connect to the next
124 GstElement *sink; /* custom sink to receive subtitle buffers */
127 #define GST_PLAY_SINK_GET_LOCK(playsink) (&((GstPlaySink *)playsink)->lock)
128 #define GST_PLAY_SINK_LOCK(playsink) G_STMT_START { \
129 GST_LOG_OBJECT (playsink, "locking from thread %p", g_thread_self ()); \
130 g_static_rec_mutex_lock (GST_PLAY_SINK_GET_LOCK (playsink)); \
131 GST_LOG_OBJECT (playsink, "locked from thread %p", g_thread_self ()); \
133 #define GST_PLAY_SINK_UNLOCK(playsink) G_STMT_START { \
134 GST_LOG_OBJECT (playsink, "unlocking from thread %p", g_thread_self ()); \
135 g_static_rec_mutex_unlock (GST_PLAY_SINK_GET_LOCK (playsink)); \
138 #define PENDING_FLAG_SET(playsink, flagtype) \
139 ((playsink->pending_blocked_pads) |= (1 << flagtype))
140 #define PENDING_FLAG_UNSET(playsink, flagtype) \
141 ((playsink->pending_blocked_pads) &= ~(1 << flagtype))
142 #define PENDING_FLAG_IS_SET(playsink, flagtype) \
143 ((playsink->pending_blocked_pads) & (1 << flagtype))
144 #define PENDING_VIDEO_BLOCK(playsink) \
145 ((playsink->pending_blocked_pads) & (1 << GST_PLAY_SINK_TYPE_VIDEO_RAW | 1 << GST_PLAY_SINK_TYPE_VIDEO))
146 #define PENDING_AUDIO_BLOCK(playsink) \
147 ((playsink->pending_blocked_pads) & (1 << GST_PLAY_SINK_TYPE_AUDIO_RAW | 1 << GST_PLAY_SINK_TYPE_AUDIO))
148 #define PENDING_TEXT_BLOCK(playsink) \
149 PENDING_FLAG_IS_SET(playsink, GST_PLAY_SINK_TYPE_TEXT)
155 GStaticRecMutex lock;
157 gboolean async_pending;
158 gboolean need_async_start;
162 GstStreamSynchronizer *stream_synchronizer;
165 GstPlayAudioChain *audiochain;
166 GstPlayVideoDeinterlaceChain *videodeinterlacechain;
167 GstPlayVideoChain *videochain;
168 GstPlayVisChain *vischain;
169 GstPlayTextChain *textchain;
173 gboolean audio_pad_raw;
174 gboolean audio_pad_blocked;
175 GstPad *audio_srcpad_stream_synchronizer;
176 GstPad *audio_sinkpad_stream_synchronizer;
178 GstElement *audio_tee;
179 GstPad *audio_tee_sink;
180 GstPad *audio_tee_asrc;
181 GstPad *audio_tee_vissrc;
184 gboolean video_pad_raw;
185 gboolean video_pad_blocked;
186 GstPad *video_srcpad_stream_synchronizer;
187 GstPad *video_sinkpad_stream_synchronizer;
190 gboolean text_pad_blocked;
191 GstPad *text_srcpad_stream_synchronizer;
192 GstPad *text_sinkpad_stream_synchronizer;
194 guint32 pending_blocked_pads;
197 GstElement *audio_sink;
198 GstElement *video_sink;
199 GstElement *visualisation;
200 GstElement *text_sink;
203 gchar *font_desc; /* font description */
204 gchar *subtitle_encoding; /* subtitle encoding */
205 guint connection_speed; /* connection speed in bits/sec (0 = unknown) */
207 gboolean volume_changed; /* volume/mute changed while no audiochain */
208 gboolean mute_changed; /* ... has been created yet */
211 /* xoverlay proxy interface */
212 GstXOverlay *xoverlay_element; /* protected with LOCK */
213 gboolean xoverlay_handle_set;
214 guintptr xoverlay_handle;
215 gboolean xoverlay_render_rectangle_set;
216 gint xoverlay_x, xoverlay_y, xoverlay_width, xoverlay_height;
217 gboolean xoverlay_handle_events_set;
218 gboolean xoverlay_handle_events;
221 struct _GstPlaySinkClass
223 GstBinClass parent_class;
225 gboolean (*reconfigure) (GstPlaySink * playsink);
227 GstBuffer *(*convert_frame) (GstPlaySink * playsink, GstCaps * caps);
231 static GstStaticPadTemplate audiotemplate =
232 GST_STATIC_PAD_TEMPLATE ("audio_sink",
235 GST_STATIC_CAPS_ANY);
236 static GstStaticPadTemplate videotemplate =
237 GST_STATIC_PAD_TEMPLATE ("video_sink",
240 GST_STATIC_CAPS_ANY);
241 static GstStaticPadTemplate texttemplate = GST_STATIC_PAD_TEMPLATE ("text_sink",
244 GST_STATIC_CAPS_ANY);
246 /* FIXME 0.11: Remove */
247 static GstStaticPadTemplate audiorawtemplate =
248 GST_STATIC_PAD_TEMPLATE ("audio_raw_sink",
251 GST_STATIC_CAPS_ANY);
252 static GstStaticPadTemplate videorawtemplate =
253 GST_STATIC_PAD_TEMPLATE ("video_raw_sink",
256 GST_STATIC_CAPS_ANY);
267 PROP_SUBTITLE_ENCODING,
283 static void gst_play_sink_dispose (GObject * object);
284 static void gst_play_sink_finalize (GObject * object);
285 static void gst_play_sink_set_property (GObject * object, guint prop_id,
286 const GValue * value, GParamSpec * spec);
287 static void gst_play_sink_get_property (GObject * object, guint prop_id,
288 GValue * value, GParamSpec * spec);
290 static GstPad *gst_play_sink_request_new_pad (GstElement * element,
291 GstPadTemplate * templ, const gchar * name);
292 static void gst_play_sink_release_request_pad (GstElement * element,
294 static gboolean gst_play_sink_send_event (GstElement * element,
296 static GstStateChangeReturn gst_play_sink_change_state (GstElement * element,
297 GstStateChange transition);
299 static void gst_play_sink_handle_message (GstBin * bin, GstMessage * message);
301 static void notify_volume_cb (GObject * object, GParamSpec * pspec,
302 GstPlaySink * playsink);
303 static void notify_mute_cb (GObject * object, GParamSpec * pspec,
304 GstPlaySink * playsink);
306 static void update_av_offset (GstPlaySink * playsink);
309 gst_play_marshal_BUFFER__BOXED (GClosure * closure,
310 GValue * return_value G_GNUC_UNUSED,
311 guint n_param_values,
312 const GValue * param_values,
313 gpointer invocation_hint G_GNUC_UNUSED, gpointer marshal_data)
315 typedef GstBuffer *(*GMarshalFunc_OBJECT__BOXED) (gpointer data1,
316 gpointer arg_1, gpointer data2);
317 register GMarshalFunc_OBJECT__BOXED callback;
318 register GCClosure *cc = (GCClosure *) closure;
319 register gpointer data1, data2;
321 g_return_if_fail (return_value != NULL);
322 g_return_if_fail (n_param_values == 2);
324 if (G_CCLOSURE_SWAP_DATA (closure)) {
325 data1 = closure->data;
326 data2 = g_value_peek_pointer (param_values + 0);
328 data1 = g_value_peek_pointer (param_values + 0);
329 data2 = closure->data;
332 (GMarshalFunc_OBJECT__BOXED) (marshal_data ? marshal_data : cc->callback);
334 v_return = callback (data1, g_value_get_boxed (param_values + 1), data2);
336 gst_value_take_buffer (return_value, v_return);
339 /* static guint gst_play_sink_signals[LAST_SIGNAL] = { 0 }; */
341 static void gst_play_sink_implements_interface_init (gpointer g_iface,
342 gpointer g_iface_data);
343 static void gst_play_sink_xoverlay_init (gpointer g_iface,
344 gpointer g_iface_data);
345 static void gst_play_sink_navigation_init (gpointer g_iface,
346 gpointer g_iface_data);
348 _do_init (GType type)
350 static const GInterfaceInfo impl_info = {
351 gst_play_sink_implements_interface_init,
354 static const GInterfaceInfo svol_info = {
357 static const GInterfaceInfo xov_info = {
358 gst_play_sink_xoverlay_init,
361 static const GInterfaceInfo nav_info = {
362 gst_play_sink_navigation_init,
366 g_type_add_interface_static (type, GST_TYPE_IMPLEMENTS_INTERFACE, &impl_info);
367 g_type_add_interface_static (type, GST_TYPE_STREAM_VOLUME, &svol_info);
368 g_type_add_interface_static (type, GST_TYPE_X_OVERLAY, &xov_info);
369 g_type_add_interface_static (type, GST_TYPE_NAVIGATION, &nav_info);
372 G_DEFINE_TYPE_WITH_CODE (GstPlaySink, gst_play_sink, GST_TYPE_BIN,
373 _do_init (g_define_type_id));
376 gst_play_sink_class_init (GstPlaySinkClass * klass)
378 GObjectClass *gobject_klass;
379 GstElementClass *gstelement_klass;
380 GstBinClass *gstbin_klass;
382 gobject_klass = (GObjectClass *) klass;
383 gstelement_klass = (GstElementClass *) klass;
384 gstbin_klass = (GstBinClass *) klass;
386 gobject_klass->dispose = gst_play_sink_dispose;
387 gobject_klass->finalize = gst_play_sink_finalize;
388 gobject_klass->set_property = gst_play_sink_set_property;
389 gobject_klass->get_property = gst_play_sink_get_property;
395 * Control the behaviour of playsink.
397 g_object_class_install_property (gobject_klass, PROP_FLAGS,
398 g_param_spec_flags ("flags", "Flags", "Flags to control behaviour",
399 GST_TYPE_PLAY_FLAGS, DEFAULT_FLAGS,
400 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
403 * GstPlaySink:volume:
405 * Get or set the current audio stream volume. 1.0 means 100%,
406 * 0.0 means mute. This uses a linear volume scale.
409 g_object_class_install_property (gobject_klass, PROP_VOLUME,
410 g_param_spec_double ("volume", "Volume", "The audio volume, 1.0=100%",
411 0.0, VOLUME_MAX_DOUBLE, 1.0,
412 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
413 g_object_class_install_property (gobject_klass, PROP_MUTE,
414 g_param_spec_boolean ("mute", "Mute",
415 "Mute the audio channel without changing the volume", FALSE,
416 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
417 g_object_class_install_property (gobject_klass, PROP_FONT_DESC,
418 g_param_spec_string ("subtitle-font-desc",
419 "Subtitle font description",
420 "Pango font description of font "
421 "to be used for subtitle rendering", NULL,
422 G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
423 g_object_class_install_property (gobject_klass, PROP_SUBTITLE_ENCODING,
424 g_param_spec_string ("subtitle-encoding", "subtitle encoding",
425 "Encoding to assume if input subtitles are not in UTF-8 encoding. "
426 "If not set, the GST_SUBTITLE_ENCODING environment variable will "
427 "be checked for an encoding to use. If that is not set either, "
428 "ISO-8859-15 will be assumed.", NULL,
429 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
430 g_object_class_install_property (gobject_klass, PROP_VIS_PLUGIN,
431 g_param_spec_object ("vis-plugin", "Vis plugin",
432 "the visualization element to use (NULL = default)",
433 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
437 * Get the currently rendered or prerolled frame in the video sink.
438 * The #GstCaps on the buffer will describe the format of the buffer.
442 g_object_class_install_property (gobject_klass, PROP_FRAME,
443 gst_param_spec_mini_object ("frame", "Frame",
444 "The last frame (NULL = no video available)",
445 GST_TYPE_BUFFER, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
447 * GstPlaySink:av-offset:
449 * Control the synchronisation offset between the audio and video streams.
450 * Positive values make the audio ahead of the video and negative values make
451 * the audio go behind the video.
455 g_object_class_install_property (gobject_klass, PROP_AV_OFFSET,
456 g_param_spec_int64 ("av-offset", "AV Offset",
457 "The synchronisation offset between audio and video in nanoseconds",
458 G_MININT64, G_MAXINT64, 0,
459 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
462 * GstPlaySink:video-sink:
464 * Set the used video sink element. NULL will use the default sink. playsink
465 * must be in %GST_STATE_NULL
469 g_object_class_install_property (gobject_klass, PROP_VIDEO_SINK,
470 g_param_spec_object ("video-sink", "Video Sink",
471 "the video output element to use (NULL = default sink)",
472 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
474 * GstPlaySink:audio-sink:
476 * Set the used audio sink element. NULL will use the default sink. playsink
477 * must be in %GST_STATE_NULL
481 g_object_class_install_property (gobject_klass, PROP_AUDIO_SINK,
482 g_param_spec_object ("audio-sink", "Audio Sink",
483 "the audio output element to use (NULL = default sink)",
484 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
487 * GstPlaySink:text-sink:
489 * Set the used text sink element. NULL will use the default sink. playsink
490 * must be in %GST_STATE_NULL
494 g_object_class_install_property (gobject_klass, PROP_TEXT_SINK,
495 g_param_spec_object ("text-sink", "Text sink",
496 "the text output element to use (NULL = default textoverlay)",
497 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
500 g_signal_new ("reconfigure", G_TYPE_FROM_CLASS (klass),
501 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstPlaySinkClass,
502 reconfigure), NULL, NULL, gst_marshal_BOOLEAN__VOID, G_TYPE_BOOLEAN,
505 * GstPlaySink::convert-frame
506 * @playsink: a #GstPlaySink
507 * @caps: the target format of the frame
509 * Action signal to retrieve the currently playing video frame in the format
510 * specified by @caps.
511 * If @caps is %NULL, no conversion will be performed and this function is
512 * equivalent to the #GstPlaySink::frame property.
514 * Returns: a #GstBuffer of the current video frame converted to #caps.
515 * The caps on the buffer will describe the final layout of the buffer data.
516 * %NULL is returned when no current buffer can be retrieved or when the
521 g_signal_new ("convert-frame", G_TYPE_FROM_CLASS (klass),
522 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
523 G_STRUCT_OFFSET (GstPlaySinkClass, convert_frame), NULL, NULL,
524 gst_play_marshal_BUFFER__BOXED, GST_TYPE_BUFFER, 1, GST_TYPE_CAPS);
526 gst_element_class_add_static_pad_template (gstelement_klass,
528 gst_element_class_add_static_pad_template (gstelement_klass, &audiotemplate);
529 gst_element_class_add_static_pad_template (gstelement_klass,
531 gst_element_class_add_static_pad_template (gstelement_klass, &videotemplate);
532 gst_element_class_add_static_pad_template (gstelement_klass, &texttemplate);
533 gst_element_class_set_details_simple (gstelement_klass, "Player Sink",
535 "Convenience sink for multiple streams",
536 "Wim Taymans <wim.taymans@gmail.com>");
538 gstelement_klass->change_state =
539 GST_DEBUG_FUNCPTR (gst_play_sink_change_state);
540 gstelement_klass->send_event = GST_DEBUG_FUNCPTR (gst_play_sink_send_event);
541 gstelement_klass->request_new_pad =
542 GST_DEBUG_FUNCPTR (gst_play_sink_request_new_pad);
543 gstelement_klass->release_pad =
544 GST_DEBUG_FUNCPTR (gst_play_sink_release_request_pad);
546 gstbin_klass->handle_message =
547 GST_DEBUG_FUNCPTR (gst_play_sink_handle_message);
549 klass->reconfigure = GST_DEBUG_FUNCPTR (gst_play_sink_reconfigure);
550 klass->convert_frame = GST_DEBUG_FUNCPTR (gst_play_sink_convert_frame);
554 gst_play_sink_init (GstPlaySink * playsink)
557 playsink->video_sink = NULL;
558 playsink->audio_sink = NULL;
559 playsink->visualisation = NULL;
560 playsink->text_sink = NULL;
561 playsink->volume = 1.0;
562 playsink->font_desc = NULL;
563 playsink->subtitle_encoding = NULL;
564 playsink->flags = DEFAULT_FLAGS;
566 playsink->stream_synchronizer =
567 g_object_new (GST_TYPE_STREAM_SYNCHRONIZER, NULL);
568 gst_bin_add (GST_BIN_CAST (playsink),
569 GST_ELEMENT_CAST (playsink->stream_synchronizer));
571 g_static_rec_mutex_init (&playsink->lock);
572 GST_OBJECT_FLAG_SET (playsink, GST_ELEMENT_IS_SINK);
576 disconnect_chain (GstPlayAudioChain * chain, GstPlaySink * playsink)
580 g_signal_handlers_disconnect_by_func (chain->volume, notify_volume_cb,
583 g_signal_handlers_disconnect_by_func (chain->mute, notify_mute_cb,
589 free_chain (GstPlayChain * chain)
593 gst_object_unref (chain->bin);
599 gst_play_sink_dispose (GObject * object)
601 GstPlaySink *playsink;
603 playsink = GST_PLAY_SINK (object);
605 if (playsink->audio_sink != NULL) {
606 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
607 gst_object_unref (playsink->audio_sink);
608 playsink->audio_sink = NULL;
610 if (playsink->video_sink != NULL) {
611 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
612 gst_object_unref (playsink->video_sink);
613 playsink->video_sink = NULL;
615 if (playsink->visualisation != NULL) {
616 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
617 gst_object_unref (playsink->visualisation);
618 playsink->visualisation = NULL;
620 if (playsink->text_sink != NULL) {
621 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
622 gst_object_unref (playsink->text_sink);
623 playsink->text_sink = NULL;
626 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
627 playsink->videodeinterlacechain = NULL;
628 free_chain ((GstPlayChain *) playsink->videochain);
629 playsink->videochain = NULL;
630 free_chain ((GstPlayChain *) playsink->audiochain);
631 playsink->audiochain = NULL;
632 free_chain ((GstPlayChain *) playsink->vischain);
633 playsink->vischain = NULL;
634 free_chain ((GstPlayChain *) playsink->textchain);
635 playsink->textchain = NULL;
637 if (playsink->audio_tee_sink) {
638 gst_object_unref (playsink->audio_tee_sink);
639 playsink->audio_tee_sink = NULL;
642 if (playsink->audio_tee_vissrc) {
643 gst_element_release_request_pad (playsink->audio_tee,
644 playsink->audio_tee_vissrc);
645 gst_object_unref (playsink->audio_tee_vissrc);
646 playsink->audio_tee_vissrc = NULL;
649 if (playsink->audio_tee_asrc) {
650 gst_element_release_request_pad (playsink->audio_tee,
651 playsink->audio_tee_asrc);
652 gst_object_unref (playsink->audio_tee_asrc);
653 playsink->audio_tee_asrc = NULL;
656 g_free (playsink->font_desc);
657 playsink->font_desc = NULL;
659 g_free (playsink->subtitle_encoding);
660 playsink->subtitle_encoding = NULL;
662 playsink->stream_synchronizer = NULL;
664 G_OBJECT_CLASS (gst_play_sink_parent_class)->dispose (object);
668 gst_play_sink_finalize (GObject * object)
670 GstPlaySink *playsink;
672 playsink = GST_PLAY_SINK (object);
674 g_static_rec_mutex_free (&playsink->lock);
676 G_OBJECT_CLASS (gst_play_sink_parent_class)->finalize (object);
680 gst_play_sink_set_sink (GstPlaySink * playsink, GstPlaySinkType type,
683 GstElement **elem = NULL, *old = NULL;
685 GST_LOG ("Setting sink %" GST_PTR_FORMAT " as sink type %d", sink, type);
687 GST_PLAY_SINK_LOCK (playsink);
689 case GST_PLAY_SINK_TYPE_AUDIO:
690 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
691 elem = &playsink->audio_sink;
693 case GST_PLAY_SINK_TYPE_VIDEO:
694 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
695 elem = &playsink->video_sink;
697 case GST_PLAY_SINK_TYPE_TEXT:
698 elem = &playsink->text_sink;
706 gst_object_ref (sink);
709 GST_PLAY_SINK_UNLOCK (playsink);
713 gst_element_set_state (old, GST_STATE_NULL);
714 gst_object_unref (old);
719 gst_play_sink_get_sink (GstPlaySink * playsink, GstPlaySinkType type)
721 GstElement *result = NULL;
722 GstElement *elem = NULL, *chainp = NULL;
724 GST_PLAY_SINK_LOCK (playsink);
726 case GST_PLAY_SINK_TYPE_AUDIO:
727 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
729 GstPlayAudioChain *chain;
730 if ((chain = (GstPlayAudioChain *) playsink->audiochain))
731 chainp = chain->sink;
732 elem = playsink->audio_sink;
735 case GST_PLAY_SINK_TYPE_VIDEO:
736 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
738 GstPlayVideoChain *chain;
739 if ((chain = (GstPlayVideoChain *) playsink->videochain))
740 chainp = chain->sink;
741 elem = playsink->video_sink;
744 case GST_PLAY_SINK_TYPE_TEXT:
746 GstPlayTextChain *chain;
747 if ((chain = (GstPlayTextChain *) playsink->textchain))
748 chainp = chain->sink;
749 elem = playsink->text_sink;
756 /* we have an active chain with a sink, get the sink */
757 result = gst_object_ref (chainp);
759 /* nothing found, return last configured sink */
760 if (result == NULL && elem)
761 result = gst_object_ref (elem);
762 GST_PLAY_SINK_UNLOCK (playsink);
768 gst_play_sink_vis_unblocked (GstPad * tee_pad, gboolean blocked,
771 GstPlaySink *playsink;
773 playsink = GST_PLAY_SINK (user_data);
774 /* nothing to do here, we need a dummy callback here to make the async call
776 GST_DEBUG_OBJECT (playsink, "vis pad unblocked");
780 gst_play_sink_vis_blocked (GstPad * tee_pad, gboolean blocked,
783 GstPlaySink *playsink;
784 GstPlayVisChain *chain;
786 playsink = GST_PLAY_SINK (user_data);
788 GST_PLAY_SINK_LOCK (playsink);
789 GST_DEBUG_OBJECT (playsink, "vis pad blocked");
790 /* now try to change the plugin in the running vis chain */
791 if (!(chain = (GstPlayVisChain *) playsink->vischain))
794 /* unlink the old plugin and unghost the pad */
795 gst_pad_unlink (chain->blockpad, chain->vissinkpad);
796 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad), NULL);
798 /* set the old plugin to NULL and remove */
799 gst_element_set_state (chain->vis, GST_STATE_NULL);
800 gst_bin_remove (GST_BIN_CAST (chain->chain.bin), chain->vis);
802 /* add new plugin and set state to playing */
803 chain->vis = playsink->visualisation;
804 gst_bin_add (GST_BIN_CAST (chain->chain.bin), chain->vis);
805 gst_element_set_state (chain->vis, GST_STATE_PLAYING);
808 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
809 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
812 gst_pad_link_full (chain->blockpad, chain->vissinkpad,
813 GST_PAD_LINK_CHECK_NOTHING);
814 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad),
818 /* Unblock the pad */
819 gst_pad_set_blocked_async (tee_pad, FALSE, gst_play_sink_vis_unblocked,
821 GST_PLAY_SINK_UNLOCK (playsink);
825 gst_play_sink_set_vis_plugin (GstPlaySink * playsink, GstElement * vis)
827 GstPlayVisChain *chain;
829 /* setting NULL means creating the default vis plugin */
831 vis = gst_element_factory_make ("goom", "vis");
833 /* simply return if we don't have a vis plugin here */
837 GST_PLAY_SINK_LOCK (playsink);
838 /* first store the new vis */
839 if (playsink->visualisation)
840 gst_object_unref (playsink->visualisation);
842 gst_object_ref_sink (vis);
843 playsink->visualisation = vis;
845 /* now try to change the plugin in the running vis chain, if we have no chain,
846 * we don't bother, any future vis chain will be created with the new vis
848 if (!(chain = (GstPlayVisChain *) playsink->vischain))
851 /* block the pad, the next time the callback is called we can change the
852 * visualisation. It's possible that this never happens or that the pad was
853 * already blocked. If the callback never happens, we don't have new data so
854 * we don't need the new vis plugin. If the pad was already blocked, the
855 * function returns FALSE but the previous pad block will do the right thing
857 GST_DEBUG_OBJECT (playsink, "blocking vis pad");
858 gst_pad_set_blocked_async (chain->blockpad, TRUE, gst_play_sink_vis_blocked,
861 GST_PLAY_SINK_UNLOCK (playsink);
867 gst_play_sink_get_vis_plugin (GstPlaySink * playsink)
869 GstElement *result = NULL;
870 GstPlayVisChain *chain;
872 GST_PLAY_SINK_LOCK (playsink);
873 if ((chain = (GstPlayVisChain *) playsink->vischain)) {
874 /* we have an active chain, get the sink */
876 result = gst_object_ref (chain->vis);
878 /* nothing found, return last configured sink */
879 if (result == NULL && playsink->visualisation)
880 result = gst_object_ref (playsink->visualisation);
881 GST_PLAY_SINK_UNLOCK (playsink);
887 gst_play_sink_set_volume (GstPlaySink * playsink, gdouble volume)
889 GstPlayAudioChain *chain;
891 GST_PLAY_SINK_LOCK (playsink);
892 playsink->volume = volume;
893 chain = (GstPlayAudioChain *) playsink->audiochain;
894 if (chain && chain->volume) {
895 GST_LOG_OBJECT (playsink, "elements: volume=%" GST_PTR_FORMAT ", mute=%"
896 GST_PTR_FORMAT "; new volume=%.03f, mute=%d", chain->volume,
897 chain->mute, volume, playsink->mute);
898 /* if there is a mute element or we are not muted, set the volume */
899 if (chain->mute || !playsink->mute)
900 g_object_set (chain->volume, "volume", volume, NULL);
902 GST_LOG_OBJECT (playsink, "no volume element");
903 playsink->volume_changed = TRUE;
905 GST_PLAY_SINK_UNLOCK (playsink);
909 gst_play_sink_get_volume (GstPlaySink * playsink)
912 GstPlayAudioChain *chain;
914 GST_PLAY_SINK_LOCK (playsink);
915 chain = (GstPlayAudioChain *) playsink->audiochain;
916 result = playsink->volume;
917 if (chain && chain->volume) {
918 if (chain->mute || !playsink->mute) {
919 g_object_get (chain->volume, "volume", &result, NULL);
920 playsink->volume = result;
923 GST_PLAY_SINK_UNLOCK (playsink);
929 gst_play_sink_set_mute (GstPlaySink * playsink, gboolean mute)
931 GstPlayAudioChain *chain;
933 GST_PLAY_SINK_LOCK (playsink);
934 playsink->mute = mute;
935 chain = (GstPlayAudioChain *) playsink->audiochain;
938 g_object_set (chain->mute, "mute", mute, NULL);
939 } else if (chain->volume) {
941 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
943 g_object_set (chain->volume, "volume", (gdouble) playsink->volume,
947 playsink->mute_changed = TRUE;
949 GST_PLAY_SINK_UNLOCK (playsink);
953 gst_play_sink_get_mute (GstPlaySink * playsink)
956 GstPlayAudioChain *chain;
958 GST_PLAY_SINK_LOCK (playsink);
959 chain = (GstPlayAudioChain *) playsink->audiochain;
960 if (chain && chain->mute) {
961 g_object_get (chain->mute, "mute", &result, NULL);
962 playsink->mute = result;
964 result = playsink->mute;
966 GST_PLAY_SINK_UNLOCK (playsink);
972 post_missing_element_message (GstPlaySink * playsink, const gchar * name)
976 msg = gst_missing_element_message_new (GST_ELEMENT_CAST (playsink), name);
977 gst_element_post_message (GST_ELEMENT_CAST (playsink), msg);
981 add_chain (GstPlayChain * chain, gboolean add)
983 if (chain->added == add)
987 gst_bin_add (GST_BIN_CAST (chain->playsink), chain->bin);
989 gst_bin_remove (GST_BIN_CAST (chain->playsink), chain->bin);
990 /* we don't want to lose our sink status */
991 GST_OBJECT_FLAG_SET (chain->playsink, GST_ELEMENT_IS_SINK);
1000 activate_chain (GstPlayChain * chain, gboolean activate)
1004 if (chain->activated == activate)
1007 GST_OBJECT_LOCK (chain->playsink);
1008 state = GST_STATE_TARGET (chain->playsink);
1009 GST_OBJECT_UNLOCK (chain->playsink);
1012 gst_element_set_state (chain->bin, state);
1014 gst_element_set_state (chain->bin, GST_STATE_NULL);
1016 chain->activated = activate;
1022 element_is_sink (GstElement * element)
1026 GST_OBJECT_LOCK (element);
1027 is_sink = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_IS_SINK);
1028 GST_OBJECT_UNLOCK (element);
1030 GST_DEBUG_OBJECT (element, "is a sink: %s", (is_sink) ? "yes" : "no");
1035 element_has_property (GstElement * element, const gchar * pname, GType type)
1039 pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (element), pname);
1041 if (pspec == NULL) {
1042 GST_DEBUG_OBJECT (element, "no %s property", pname);
1046 if (type == G_TYPE_INVALID || type == pspec->value_type ||
1047 g_type_is_a (pspec->value_type, type)) {
1048 GST_DEBUG_OBJECT (element, "has %s property of type %s", pname,
1049 (type == G_TYPE_INVALID) ? "any type" : g_type_name (type));
1053 GST_WARNING_OBJECT (element, "has %s property, but property is of type %s "
1054 "and we expected it to be of type %s", pname,
1055 g_type_name (pspec->value_type), g_type_name (type));
1062 const gchar *prop_name;
1065 } FindPropertyHelper;
1068 find_property (GstElement * element, FindPropertyHelper * helper)
1070 if (helper->need_sink && !element_is_sink (element)) {
1071 gst_object_unref (element);
1075 if (!element_has_property (element, helper->prop_name, helper->prop_type)) {
1076 gst_object_unref (element);
1080 GST_INFO_OBJECT (element, "found %s with %s property", helper->prop_name,
1081 (helper->need_sink) ? "sink" : "element");
1082 return 0; /* keep it */
1085 /* FIXME: why not move these functions into core? */
1086 /* find a sink in the hierarchy with a property named @name. This function does
1087 * not increase the refcount of the returned object and thus remains valid as
1088 * long as the bin is valid. */
1090 gst_play_sink_find_property_sinks (GstPlaySink * playsink, GstElement * obj,
1091 const gchar * name, GType expected_type)
1093 GstElement *result = NULL;
1096 if (element_has_property (obj, name, expected_type)) {
1098 } else if (GST_IS_BIN (obj)) {
1099 FindPropertyHelper helper = { name, expected_type, TRUE };
1101 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1102 result = gst_iterator_find_custom (it,
1103 (GCompareFunc) find_property, &helper);
1104 gst_iterator_free (it);
1105 /* we don't need the extra ref */
1107 gst_object_unref (result);
1112 /* find an object in the hierarchy with a property named @name */
1114 gst_play_sink_find_property (GstPlaySink * playsink, GstElement * obj,
1115 const gchar * name, GType expected_type)
1117 GstElement *result = NULL;
1120 if (GST_IS_BIN (obj)) {
1121 FindPropertyHelper helper = { name, expected_type, FALSE };
1123 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1124 result = gst_iterator_find_custom (it,
1125 (GCompareFunc) find_property, &helper);
1126 gst_iterator_free (it);
1128 if (element_has_property (obj, name, expected_type)) {
1130 gst_object_ref (obj);
1137 do_async_start (GstPlaySink * playsink)
1139 GstMessage *message;
1141 if (!playsink->need_async_start) {
1142 GST_INFO_OBJECT (playsink, "no async_start needed");
1146 playsink->async_pending = TRUE;
1148 GST_INFO_OBJECT (playsink, "Sending async_start message");
1149 message = gst_message_new_async_start (GST_OBJECT_CAST (playsink), FALSE);
1150 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1151 (playsink), message);
1155 do_async_done (GstPlaySink * playsink)
1157 GstMessage *message;
1159 if (playsink->async_pending) {
1160 GST_INFO_OBJECT (playsink, "Sending async_done message");
1161 message = gst_message_new_async_done (GST_OBJECT_CAST (playsink));
1162 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1163 (playsink), message);
1165 playsink->async_pending = FALSE;
1168 playsink->need_async_start = FALSE;
1171 /* try to change the state of an element. This function returns the element when
1172 * the state change could be performed. When this function returns NULL an error
1173 * occured and the element is unreffed if @unref is TRUE. */
1175 try_element (GstPlaySink * playsink, GstElement * element, gboolean unref)
1177 GstStateChangeReturn ret;
1180 ret = gst_element_set_state (element, GST_STATE_READY);
1181 if (ret == GST_STATE_CHANGE_FAILURE) {
1182 GST_DEBUG_OBJECT (playsink, "failed state change..");
1183 gst_element_set_state (element, GST_STATE_NULL);
1185 gst_object_unref (element);
1192 /* make the element (bin) that contains the elements needed to perform
1193 * video display. Only used for *raw* video streams.
1195 * +------------------------------------------------------------+
1197 * | +-------+ +----------+ +----------+ +---------+ |
1198 * | | queue | |colorspace| |videoscale| |videosink| |
1199 * | +-sink src-sink src-sink src-sink | |
1200 * | | +-------+ +----------+ +----------+ +---------+ |
1202 * +------------------------------------------------------------+
1205 static GstPlayVideoDeinterlaceChain *
1206 gen_video_deinterlace_chain (GstPlaySink * playsink)
1208 GstPlayVideoDeinterlaceChain *chain;
1211 GstElement *head = NULL, *prev = NULL;
1213 chain = g_new0 (GstPlayVideoDeinterlaceChain, 1);
1214 chain->chain.playsink = playsink;
1216 GST_DEBUG_OBJECT (playsink, "making video deinterlace chain %p", chain);
1218 /* create a bin to hold objects, as we create them we add them to this bin so
1219 * that when something goes wrong we only need to unref the bin */
1220 chain->chain.bin = gst_bin_new ("vdbin");
1221 bin = GST_BIN_CAST (chain->chain.bin);
1222 gst_object_ref_sink (bin);
1224 GST_DEBUG_OBJECT (playsink, "creating " COLORSPACE);
1225 chain->conv = gst_element_factory_make (COLORSPACE, "vdconv");
1226 if (chain->conv == NULL) {
1227 post_missing_element_message (playsink, COLORSPACE);
1228 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1229 (_("Missing element '%s' - check your GStreamer installation."),
1230 COLORSPACE), ("video rendering might fail"));
1232 gst_bin_add (bin, chain->conv);
1237 GST_DEBUG_OBJECT (playsink, "creating deinterlace");
1238 chain->deinterlace = gst_element_factory_make ("deinterlace", "deinterlace");
1239 if (chain->deinterlace == NULL) {
1240 post_missing_element_message (playsink, "deinterlace");
1241 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1242 (_("Missing element '%s' - check your GStreamer installation."),
1243 "deinterlace"), ("deinterlacing won't work"));
1245 gst_bin_add (bin, chain->deinterlace);
1247 if (!gst_element_link_pads_full (prev, "src", chain->deinterlace, "sink",
1248 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1251 head = chain->deinterlace;
1253 prev = chain->deinterlace;
1257 pad = gst_element_get_static_pad (head, "sink");
1258 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1259 gst_object_unref (pad);
1261 chain->sinkpad = gst_ghost_pad_new_no_target ("sink", GST_PAD_SINK);
1265 pad = gst_element_get_static_pad (prev, "src");
1266 chain->srcpad = gst_ghost_pad_new ("src", pad);
1267 gst_object_unref (pad);
1269 chain->srcpad = gst_ghost_pad_new ("src", chain->sinkpad);
1272 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1273 gst_element_add_pad (chain->chain.bin, chain->srcpad);
1279 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1280 (NULL), ("Failed to configure the video deinterlace chain."));
1281 free_chain ((GstPlayChain *) chain);
1287 is_valid_color_balance_element (GstElement * element)
1289 GstColorBalance *bal = GST_COLOR_BALANCE (element);
1290 gboolean have_brightness = FALSE;
1291 gboolean have_contrast = FALSE;
1292 gboolean have_hue = FALSE;
1293 gboolean have_saturation = FALSE;
1294 const GList *channels, *l;
1296 channels = gst_color_balance_list_channels (bal);
1297 for (l = channels; l; l = l->next) {
1298 GstColorBalanceChannel *ch = l->data;
1300 if (g_strrstr (ch->label, "BRIGHTNESS"))
1301 have_brightness = TRUE;
1302 else if (g_strrstr (ch->label, "CONTRAST"))
1303 have_contrast = TRUE;
1304 else if (g_strrstr (ch->label, "HUE"))
1306 else if (g_strrstr (ch->label, "SATURATION"))
1307 have_saturation = TRUE;
1310 return have_brightness && have_contrast && have_hue && have_saturation;
1314 iterate_color_balance_elements (gpointer data, gpointer user_data)
1316 gboolean valid = is_valid_color_balance_element (data);
1317 gboolean *valid_out = user_data;
1319 *valid_out = *valid_out && valid;
1321 gst_object_unref (data);
1325 has_color_balance_element (GstElement * element)
1328 gboolean valid = FALSE;
1330 if (GST_IS_COLOR_BALANCE (element))
1331 return is_valid_color_balance_element (element);
1332 else if (!GST_IS_BIN (element))
1335 it = gst_bin_iterate_all_by_interface (GST_BIN (element),
1336 GST_TYPE_COLOR_BALANCE);
1337 while (gst_iterator_foreach (it, iterate_color_balance_elements,
1338 &valid) == GST_ITERATOR_RESYNC)
1339 gst_iterator_resync (it);
1340 gst_iterator_free (it);
1345 /* make the element (bin) that contains the elements needed to perform
1348 * +------------------------------------------------------------+
1350 * | +-------+ +----------+ +----------+ +---------+ |
1351 * | | queue | |colorspace| |videoscale| |videosink| |
1352 * | +-sink src-sink src-sink src-sink | |
1353 * | | +-------+ +----------+ +----------+ +---------+ |
1355 * +------------------------------------------------------------+
1358 static GstPlayVideoChain *
1359 gen_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1361 GstPlayVideoChain *chain;
1364 GstElement *head = NULL, *prev = NULL, *elem = NULL;
1366 chain = g_new0 (GstPlayVideoChain, 1);
1367 chain->chain.playsink = playsink;
1368 chain->chain.raw = raw;
1370 GST_DEBUG_OBJECT (playsink, "making video chain %p", chain);
1372 if (playsink->video_sink) {
1373 GST_DEBUG_OBJECT (playsink, "trying configured videosink");
1374 chain->sink = try_element (playsink, playsink->video_sink, FALSE);
1376 /* only try fallback if no specific sink was chosen */
1377 if (chain->sink == NULL) {
1378 GST_DEBUG_OBJECT (playsink, "trying autovideosink");
1379 elem = gst_element_factory_make ("autovideosink", "videosink");
1380 chain->sink = try_element (playsink, elem, TRUE);
1382 if (chain->sink == NULL) {
1383 /* if default sink from config.h is different then try it too */
1384 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1385 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_VIDEOSINK);
1386 elem = gst_element_factory_make (DEFAULT_VIDEOSINK, "videosink");
1387 chain->sink = try_element (playsink, elem, TRUE);
1391 playsink->video_sink = gst_object_ref (chain->sink);
1393 if (chain->sink == NULL)
1397 /* if we can disable async behaviour of the sink, we can avoid adding a
1398 * queue for the audio chain. */
1400 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1403 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1404 async, GST_ELEMENT_NAME (elem));
1405 g_object_set (elem, "async", async, NULL);
1406 chain->async = async;
1408 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1409 chain->async = TRUE;
1412 /* Make sure the aspect ratio is kept */
1414 gst_play_sink_find_property_sinks (playsink, chain->sink,
1415 "force-aspect-ratio", G_TYPE_BOOLEAN);
1417 g_object_set (elem, "force-aspect-ratio", TRUE, NULL);
1419 /* find ts-offset element */
1420 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1421 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1424 /* create a bin to hold objects, as we create them we add them to this bin so
1425 * that when something goes wrong we only need to unref the bin */
1426 chain->chain.bin = gst_bin_new ("vbin");
1427 bin = GST_BIN_CAST (chain->chain.bin);
1428 gst_object_ref_sink (bin);
1429 gst_bin_add (bin, chain->sink);
1431 /* Get the XOverlay element */
1433 GstXOverlay *xoverlay = NULL;
1435 GST_OBJECT_LOCK (playsink);
1436 if (playsink->xoverlay_element)
1437 gst_object_unref (playsink->xoverlay_element);
1438 playsink->xoverlay_element =
1439 GST_X_OVERLAY (gst_bin_get_by_interface (GST_BIN (chain->chain.bin),
1440 GST_TYPE_X_OVERLAY));
1441 if (playsink->xoverlay_element)
1442 xoverlay = GST_X_OVERLAY (gst_object_ref (playsink->xoverlay_element));
1443 GST_OBJECT_UNLOCK (playsink);
1446 if (playsink->xoverlay_handle_set)
1447 gst_x_overlay_set_window_handle (xoverlay, playsink->xoverlay_handle);
1448 if (playsink->xoverlay_handle_events_set)
1449 gst_x_overlay_handle_events (xoverlay,
1450 playsink->xoverlay_handle_events);
1451 if (playsink->xoverlay_render_rectangle_set)
1452 gst_x_overlay_set_render_rectangle (xoverlay,
1453 playsink->xoverlay_x, playsink->xoverlay_y,
1454 playsink->xoverlay_width, playsink->xoverlay_height);
1455 gst_object_unref (xoverlay);
1459 /* decouple decoder from sink, this improves playback quite a lot since the
1460 * decoder can continue while the sink blocks for synchronisation. We don't
1461 * need a lot of buffers as this consumes a lot of memory and we don't want
1462 * too little because else we would be context switching too quickly. */
1463 chain->queue = gst_element_factory_make ("queue", "vqueue");
1464 if (chain->queue == NULL) {
1465 post_missing_element_message (playsink, "queue");
1466 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1467 (_("Missing element '%s' - check your GStreamer installation."),
1468 "queue"), ("video rendering might be suboptimal"));
1472 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1473 "max-size-bytes", 0, "max-size-time", (gint64) 0, "silent", TRUE, NULL);
1474 gst_bin_add (bin, chain->queue);
1475 head = prev = chain->queue;
1478 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)
1479 || (!has_color_balance_element (chain->sink)
1480 && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE))) {
1481 gboolean use_converters = !(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO);
1482 gboolean use_balance = !has_color_balance_element (chain->sink)
1483 && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE);
1485 GST_DEBUG_OBJECT (playsink, "creating videoconverter");
1487 g_object_new (GST_TYPE_PLAY_SINK_VIDEO_CONVERT, "name", "vconv",
1488 "use-converters", use_converters, "use-balance", use_balance, NULL);
1489 gst_bin_add (bin, chain->conv);
1491 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
1492 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1501 GST_DEBUG_OBJECT (playsink, "linking to sink");
1502 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
1503 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1507 pad = gst_element_get_static_pad (head, "sink");
1508 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1509 gst_object_unref (pad);
1511 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1518 if (!elem && !playsink->video_sink) {
1519 post_missing_element_message (playsink, "autovideosink");
1520 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1521 post_missing_element_message (playsink, DEFAULT_VIDEOSINK);
1522 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1523 (_("Both autovideosink and %s elements are missing."),
1524 DEFAULT_VIDEOSINK), (NULL));
1526 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1527 (_("The autovideosink element is missing.")), (NULL));
1530 if (playsink->video_sink) {
1531 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1532 (_("Configured videosink %s is not working."),
1533 GST_ELEMENT_NAME (playsink->video_sink)), (NULL));
1534 } else if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1535 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1536 (_("Both autovideosink and %s elements are not working."),
1537 DEFAULT_VIDEOSINK), (NULL));
1539 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1540 (_("The autovideosink element is not working.")), (NULL));
1543 free_chain ((GstPlayChain *) chain);
1549 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1550 (NULL), ("Failed to configure the video sink."));
1551 /* checking sink made it READY */
1552 gst_element_set_state (chain->sink, GST_STATE_NULL);
1553 /* Remove chain from the bin to allow reuse later */
1554 gst_bin_remove (bin, chain->sink);
1555 free_chain ((GstPlayChain *) chain);
1561 setup_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1564 GstPlayVideoChain *chain;
1565 GstStateChangeReturn ret;
1567 chain = playsink->videochain;
1569 chain->chain.raw = raw;
1571 /* if the chain was active we don't do anything */
1572 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
1575 /* try to set the sink element to READY again */
1576 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
1577 if (ret == GST_STATE_CHANGE_FAILURE)
1580 /* Get the XOverlay element */
1582 GstXOverlay *xoverlay = NULL;
1584 GST_OBJECT_LOCK (playsink);
1585 if (playsink->xoverlay_element)
1586 gst_object_unref (playsink->xoverlay_element);
1587 playsink->xoverlay_element =
1588 GST_X_OVERLAY (gst_bin_get_by_interface (GST_BIN (chain->chain.bin),
1589 GST_TYPE_X_OVERLAY));
1590 if (playsink->xoverlay_element)
1591 xoverlay = GST_X_OVERLAY (gst_object_ref (playsink->xoverlay_element));
1592 GST_OBJECT_UNLOCK (playsink);
1595 if (playsink->xoverlay_handle_set)
1596 gst_x_overlay_set_window_handle (xoverlay, playsink->xoverlay_handle);
1597 if (playsink->xoverlay_handle_events_set)
1598 gst_x_overlay_handle_events (xoverlay,
1599 playsink->xoverlay_handle_events);
1600 if (playsink->xoverlay_render_rectangle_set)
1601 gst_x_overlay_set_render_rectangle (xoverlay,
1602 playsink->xoverlay_x, playsink->xoverlay_y,
1603 playsink->xoverlay_width, playsink->xoverlay_height);
1604 gst_object_unref (xoverlay);
1608 /* find ts-offset element */
1609 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1610 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1613 /* if we can disable async behaviour of the sink, we can avoid adding a
1614 * queue for the audio chain. */
1616 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1619 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1620 async, GST_ELEMENT_NAME (elem));
1621 g_object_set (elem, "async", async, NULL);
1622 chain->async = async;
1624 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1625 chain->async = TRUE;
1628 /* Make sure the aspect ratio is kept */
1630 gst_play_sink_find_property_sinks (playsink, chain->sink,
1631 "force-aspect-ratio", G_TYPE_BOOLEAN);
1633 g_object_set (elem, "force-aspect-ratio", TRUE, NULL);
1636 g_object_set (chain->conv, "use-balance",
1637 !has_color_balance_element (chain->sink)
1638 && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE), NULL);
1643 /* make an element for playback of video with subtitles embedded.
1644 * Only used for *raw* video streams.
1646 * +--------------------------------------------+
1648 * | +--------+ +-----------------+ |
1649 * | | queue | | subtitleoverlay | |
1650 * video--src sink---video_sink | |
1651 * | +--------+ | src--src
1652 * text------------------text_sink | |
1653 * | +-----------------+ |
1654 * +--------------------------------------------+
1657 static GstPlayTextChain *
1658 gen_text_chain (GstPlaySink * playsink)
1660 GstPlayTextChain *chain;
1663 GstPad *videosinkpad, *textsinkpad, *srcpad;
1665 chain = g_new0 (GstPlayTextChain, 1);
1666 chain->chain.playsink = playsink;
1668 GST_DEBUG_OBJECT (playsink, "making text chain %p", chain);
1670 chain->chain.bin = gst_bin_new ("tbin");
1671 bin = GST_BIN_CAST (chain->chain.bin);
1672 gst_object_ref_sink (bin);
1674 videosinkpad = textsinkpad = srcpad = NULL;
1676 /* first try to hook the text pad to the custom sink */
1677 if (playsink->text_sink) {
1678 GST_DEBUG_OBJECT (playsink, "trying configured textsink");
1679 chain->sink = try_element (playsink, playsink->text_sink, FALSE);
1682 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1685 /* make sure the sparse subtitles don't participate in the preroll */
1686 g_object_set (elem, "async", FALSE, NULL);
1687 GST_DEBUG_OBJECT (playsink, "adding custom text sink");
1688 gst_bin_add (bin, chain->sink);
1689 /* NOTE streamsynchronizer needs streams decoupled */
1690 /* make a little queue */
1691 chain->queue = gst_element_factory_make ("queue", "subqueue");
1692 if (chain->queue == NULL) {
1693 post_missing_element_message (playsink, "queue");
1694 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1695 (_("Missing element '%s' - check your GStreamer installation."),
1696 "queue"), ("rendering might be suboptimal"));
1698 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1699 "max-size-bytes", 0, "max-size-time", (gint64) 0,
1700 "silent", TRUE, NULL);
1701 gst_bin_add (bin, chain->queue);
1703 /* we have a custom sink, this will be our textsinkpad */
1704 if (gst_element_link_pads_full (chain->queue, "src", chain->sink,
1705 "sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
1706 /* we're all fine now and we can add the sink to the chain */
1707 GST_DEBUG_OBJECT (playsink, "using custom text sink");
1708 textsinkpad = gst_element_get_static_pad (chain->queue, "sink");
1710 GST_WARNING_OBJECT (playsink,
1711 "can't find a sink pad on custom text sink");
1712 gst_bin_remove (bin, chain->sink);
1713 gst_bin_remove (bin, chain->queue);
1715 chain->queue = NULL;
1717 /* try to set sync to true but it's no biggie when we can't */
1718 if (chain->sink && (elem =
1719 gst_play_sink_find_property_sinks (playsink, chain->sink,
1720 "sync", G_TYPE_BOOLEAN)))
1721 g_object_set (elem, "sync", TRUE, NULL);
1724 gst_bin_remove (bin, chain->sink);
1726 GST_WARNING_OBJECT (playsink,
1727 "can't find async property in custom text sink");
1730 if (textsinkpad == NULL) {
1731 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1732 (_("Custom text sink element is not usable.")),
1733 ("fallback to default textoverlay"));
1737 if (textsinkpad == NULL) {
1738 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
1739 /* make a little queue */
1740 chain->queue = gst_element_factory_make ("queue", "vqueue");
1741 if (chain->queue == NULL) {
1742 post_missing_element_message (playsink, "queue");
1743 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1744 (_("Missing element '%s' - check your GStreamer installation."),
1745 "queue"), ("video rendering might be suboptimal"));
1747 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1748 "max-size-bytes", 0, "max-size-time", (gint64) 0,
1749 "silent", TRUE, NULL);
1750 gst_bin_add (bin, chain->queue);
1751 videosinkpad = gst_element_get_static_pad (chain->queue, "sink");
1755 gst_element_factory_make ("subtitleoverlay", "suboverlay");
1756 if (chain->overlay == NULL) {
1757 post_missing_element_message (playsink, "subtitleoverlay");
1758 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1759 (_("Missing element '%s' - check your GStreamer installation."),
1760 "subtitleoverlay"), ("subtitle rendering disabled"));
1762 GstElement *element;
1764 gst_bin_add (bin, chain->overlay);
1766 g_object_set (G_OBJECT (chain->overlay), "silent", FALSE, NULL);
1767 if (playsink->font_desc) {
1768 g_object_set (G_OBJECT (chain->overlay), "font-desc",
1769 playsink->font_desc, NULL);
1771 if (playsink->subtitle_encoding) {
1772 g_object_set (G_OBJECT (chain->overlay), "subtitle-encoding",
1773 playsink->subtitle_encoding, NULL);
1776 gst_element_link_pads_full (chain->queue, "src", chain->overlay,
1777 "video_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS);
1779 /* make another little queue to decouple streams */
1780 element = gst_element_factory_make ("queue", "subqueue");
1781 if (element == NULL) {
1782 post_missing_element_message (playsink, "queue");
1783 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1784 (_("Missing element '%s' - check your GStreamer installation."),
1785 "queue"), ("rendering might be suboptimal"));
1787 g_object_set (G_OBJECT (element), "max-size-buffers", 3,
1788 "max-size-bytes", 0, "max-size-time", (gint64) 0,
1789 "silent", TRUE, NULL);
1790 gst_bin_add (bin, element);
1791 if (gst_element_link_pads_full (element, "src", chain->overlay,
1792 "subtitle_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
1793 textsinkpad = gst_element_get_static_pad (element, "sink");
1794 srcpad = gst_element_get_static_pad (chain->overlay, "src");
1796 gst_bin_remove (bin, chain->sink);
1797 gst_bin_remove (bin, chain->overlay);
1799 chain->overlay = NULL;
1800 gst_object_unref (videosinkpad);
1801 videosinkpad = NULL;
1808 if (videosinkpad == NULL) {
1809 /* if we still don't have a videosink, we don't have an overlay. the only
1810 * thing we can do is insert an identity and ghost the src
1812 chain->identity = gst_element_factory_make ("identity", "tidentity");
1813 if (chain->identity == NULL) {
1814 post_missing_element_message (playsink, "identity");
1815 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1816 (_("Missing element '%s' - check your GStreamer installation."),
1817 "identity"), (NULL));
1819 g_object_set (chain->identity, "signal-handoffs", FALSE, NULL);
1820 g_object_set (chain->identity, "silent", TRUE, NULL);
1821 gst_bin_add (bin, chain->identity);
1822 srcpad = gst_element_get_static_pad (chain->identity, "src");
1823 videosinkpad = gst_element_get_static_pad (chain->identity, "sink");
1827 /* expose the ghostpads */
1829 chain->videosinkpad = gst_ghost_pad_new ("sink", videosinkpad);
1830 gst_object_unref (videosinkpad);
1831 gst_element_add_pad (chain->chain.bin, chain->videosinkpad);
1834 chain->textsinkpad = gst_ghost_pad_new ("text_sink", textsinkpad);
1835 gst_object_unref (textsinkpad);
1836 gst_element_add_pad (chain->chain.bin, chain->textsinkpad);
1839 chain->srcpad = gst_ghost_pad_new ("src", srcpad);
1840 gst_object_unref (srcpad);
1841 gst_element_add_pad (chain->chain.bin, chain->srcpad);
1848 notify_volume_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
1852 g_object_get (object, "volume", &vol, NULL);
1853 playsink->volume = vol;
1855 g_object_notify (G_OBJECT (playsink), "volume");
1859 notify_mute_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
1863 g_object_get (object, "mute", &mute, NULL);
1864 playsink->mute = mute;
1866 g_object_notify (G_OBJECT (playsink), "mute");
1869 /* make the chain that contains the elements needed to perform
1872 * We add a tee as the first element so that we can link the visualisation chain
1873 * to it when requested.
1875 * +-------------------------------------------------------------+
1877 * | +---------+ +----------+ +---------+ +---------+ |
1878 * | |audioconv| |audioscale| | volume | |audiosink| |
1879 * | +-srck src-sink src-sink src-sink | |
1880 * | | +---------+ +----------+ +---------+ +---------+ |
1882 * +-------------------------------------------------------------+
1884 static GstPlayAudioChain *
1885 gen_audio_chain (GstPlaySink * playsink, gboolean raw)
1887 GstPlayAudioChain *chain;
1889 gboolean have_volume;
1891 GstElement *head, *prev, *elem = NULL;
1893 chain = g_new0 (GstPlayAudioChain, 1);
1894 chain->chain.playsink = playsink;
1895 chain->chain.raw = raw;
1897 GST_DEBUG_OBJECT (playsink, "making audio chain %p", chain);
1899 if (playsink->audio_sink) {
1900 GST_DEBUG_OBJECT (playsink, "trying configured audiosink %" GST_PTR_FORMAT,
1901 playsink->audio_sink);
1902 chain->sink = try_element (playsink, playsink->audio_sink, FALSE);
1904 /* only try fallback if no specific sink was chosen */
1905 if (chain->sink == NULL) {
1906 GST_DEBUG_OBJECT (playsink, "trying autoaudiosink");
1907 elem = gst_element_factory_make ("autoaudiosink", "audiosink");
1908 chain->sink = try_element (playsink, elem, TRUE);
1910 if (chain->sink == NULL) {
1911 /* if default sink from config.h is different then try it too */
1912 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1913 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_AUDIOSINK);
1914 elem = gst_element_factory_make (DEFAULT_AUDIOSINK, "audiosink");
1915 chain->sink = try_element (playsink, elem, TRUE);
1919 playsink->audio_sink = gst_object_ref (chain->sink);
1921 if (chain->sink == NULL)
1924 chain->chain.bin = gst_bin_new ("abin");
1925 bin = GST_BIN_CAST (chain->chain.bin);
1926 gst_object_ref_sink (bin);
1927 gst_bin_add (bin, chain->sink);
1929 /* we have to add a queue when we need to decouple for the video sink in
1930 * visualisations and for streamsynchronizer */
1931 GST_DEBUG_OBJECT (playsink, "adding audio queue");
1932 chain->queue = gst_element_factory_make ("queue", "aqueue");
1933 if (chain->queue == NULL) {
1934 post_missing_element_message (playsink, "queue");
1935 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1936 (_("Missing element '%s' - check your GStreamer installation."),
1937 "queue"), ("audio playback and visualizations might not work"));
1941 g_object_set (chain->queue, "silent", TRUE, NULL);
1942 gst_bin_add (bin, chain->queue);
1943 prev = head = chain->queue;
1946 /* find ts-offset element */
1947 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1948 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1951 /* check if the sink, or something within the sink, has the volume property.
1952 * If it does we don't need to add a volume element. */
1954 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
1957 chain->volume = elem;
1959 g_signal_connect (chain->volume, "notify::volume",
1960 G_CALLBACK (notify_volume_cb), playsink);
1962 GST_DEBUG_OBJECT (playsink, "the sink has a volume property");
1964 chain->sink_volume = TRUE;
1965 /* if the sink also has a mute property we can use this as well. We'll only
1966 * use the mute property if there is a volume property. We can simulate the
1967 * mute with the volume otherwise. */
1969 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
1972 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
1973 g_signal_connect (chain->mute, "notify::mute",
1974 G_CALLBACK (notify_mute_cb), playsink);
1976 /* use the sink to control the volume and mute */
1977 if (playsink->volume_changed) {
1978 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
1979 playsink->volume_changed = FALSE;
1981 if (playsink->mute_changed) {
1983 g_object_set (chain->mute, "mute", playsink->mute, NULL);
1986 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
1988 playsink->mute_changed = FALSE;
1991 /* no volume, we need to add a volume element when we can */
1992 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
1993 have_volume = FALSE;
1994 chain->sink_volume = FALSE;
1997 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO) || (!have_volume
1998 && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME))) {
1999 gboolean use_converters = !(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO);
2000 gboolean use_volume =
2001 !have_volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME);
2002 GST_DEBUG_OBJECT (playsink,
2003 "creating audioconvert with use-converters %d, use-volume %d",
2004 use_converters, use_volume);
2006 g_object_new (GST_TYPE_PLAY_SINK_AUDIO_CONVERT, "name", "aconv",
2007 "use-converters", use_converters, "use-volume", use_volume, NULL);
2008 gst_bin_add (bin, chain->conv);
2010 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
2011 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
2018 if (!have_volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
2019 GstPlaySinkAudioConvert *conv =
2020 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
2023 chain->volume = conv->volume;
2026 g_signal_connect (chain->volume, "notify::volume",
2027 G_CALLBACK (notify_volume_cb), playsink);
2029 /* volume also has the mute property */
2030 chain->mute = chain->volume;
2031 g_signal_connect (chain->mute, "notify::mute",
2032 G_CALLBACK (notify_mute_cb), playsink);
2034 /* configure with the latest volume and mute */
2035 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume,
2037 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
2043 /* we only have to link to the previous element if we have something in
2044 * front of the sink */
2045 GST_DEBUG_OBJECT (playsink, "linking to sink");
2046 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
2047 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
2051 /* post a warning if we have no way to configure the volume */
2053 GST_ELEMENT_WARNING (playsink, STREAM, NOT_IMPLEMENTED,
2054 (_("No volume control found")), ("Volume/mute is not available"));
2057 /* and ghost the sinkpad of the headmost element */
2058 GST_DEBUG_OBJECT (playsink, "ghosting sink pad");
2059 pad = gst_element_get_static_pad (head, "sink");
2060 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
2061 gst_object_unref (pad);
2062 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
2069 if (!elem && !playsink->audio_sink) {
2070 post_missing_element_message (playsink, "autoaudiosink");
2071 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
2072 post_missing_element_message (playsink, DEFAULT_AUDIOSINK);
2073 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2074 (_("Both autoaudiosink and %s elements are missing."),
2075 DEFAULT_AUDIOSINK), (NULL));
2077 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2078 (_("The autoaudiosink element is missing.")), (NULL));
2081 if (playsink->audio_sink) {
2082 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2083 (_("Configured audiosink %s is not working."),
2084 GST_ELEMENT_NAME (playsink->audio_sink)), (NULL));
2085 } else if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
2086 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2087 (_("Both autoaudiosink and %s elements are not working."),
2088 DEFAULT_AUDIOSINK), (NULL));
2090 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2091 (_("The autoaudiosink element is not working.")), (NULL));
2094 free_chain ((GstPlayChain *) chain);
2099 GST_ELEMENT_ERROR (playsink, CORE, PAD,
2100 (NULL), ("Failed to configure the audio sink."));
2101 /* checking sink made it READY */
2102 gst_element_set_state (chain->sink, GST_STATE_NULL);
2103 /* Remove chain from the bin to allow reuse later */
2104 gst_bin_remove (bin, chain->sink);
2105 free_chain ((GstPlayChain *) chain);
2111 setup_audio_chain (GstPlaySink * playsink, gboolean raw)
2114 GstPlayAudioChain *chain;
2115 GstStateChangeReturn ret;
2117 chain = playsink->audiochain;
2119 chain->chain.raw = raw;
2121 /* if the chain was active we don't do anything */
2122 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
2125 /* try to set the sink element to READY again */
2126 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
2127 if (ret == GST_STATE_CHANGE_FAILURE)
2130 /* find ts-offset element */
2131 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
2132 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
2135 /* check if the sink, or something within the sink, has the volume property.
2136 * If it does we don't need to add a volume element. */
2138 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
2141 chain->volume = elem;
2143 if (playsink->volume_changed) {
2144 GST_DEBUG_OBJECT (playsink, "the sink has a volume property, setting %f",
2146 /* use the sink to control the volume */
2147 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2148 playsink->volume_changed = FALSE;
2151 g_signal_connect (chain->volume, "notify::volume",
2152 G_CALLBACK (notify_volume_cb), playsink);
2153 /* if the sink also has a mute property we can use this as well. We'll only
2154 * use the mute property if there is a volume property. We can simulate the
2155 * mute with the volume otherwise. */
2157 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
2160 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
2161 g_signal_connect (chain->mute, "notify::mute",
2162 G_CALLBACK (notify_mute_cb), playsink);
2165 g_object_set (chain->conv, "use-volume", FALSE, NULL);
2167 GstPlaySinkAudioConvert *conv =
2168 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
2170 /* no volume, we need to add a volume element when we can */
2171 g_object_set (chain->conv, "use-volume",
2172 ! !(playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME), NULL);
2173 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
2175 /* Disconnect signals */
2176 disconnect_chain (chain, playsink);
2178 if (conv->volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
2179 chain->volume = conv->volume;
2180 chain->mute = chain->volume;
2182 g_signal_connect (chain->volume, "notify::volume",
2183 G_CALLBACK (notify_volume_cb), playsink);
2185 g_signal_connect (chain->mute, "notify::mute",
2186 G_CALLBACK (notify_mute_cb), playsink);
2188 /* configure with the latest volume and mute */
2189 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2190 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
2193 GST_DEBUG_OBJECT (playsink, "reusing existing volume element");
2199 * +-------------------------------------------------------------------+
2201 * | +----------+ +------------+ +----------+ +-------+ |
2202 * | | visqueue | | audioconv | | audiores | | vis | |
2203 * | +-sink src-sink + samp src-sink src-sink src-+ |
2204 * | | +----------+ +------------+ +----------+ +-------+ | |
2206 * +-------------------------------------------------------------------+
2209 static GstPlayVisChain *
2210 gen_vis_chain (GstPlaySink * playsink)
2212 GstPlayVisChain *chain;
2218 chain = g_new0 (GstPlayVisChain, 1);
2219 chain->chain.playsink = playsink;
2221 GST_DEBUG_OBJECT (playsink, "making vis chain %p", chain);
2223 chain->chain.bin = gst_bin_new ("visbin");
2224 bin = GST_BIN_CAST (chain->chain.bin);
2225 gst_object_ref_sink (bin);
2227 /* we're queuing raw audio here, we can remove this queue when we can disable
2228 * async behaviour in the video sink. */
2229 chain->queue = gst_element_factory_make ("queue", "visqueue");
2230 if (chain->queue == NULL)
2232 g_object_set (chain->queue, "silent", TRUE, NULL);
2233 gst_bin_add (bin, chain->queue);
2235 chain->conv = gst_element_factory_make ("audioconvert", "aconv");
2236 if (chain->conv == NULL)
2237 goto no_audioconvert;
2238 gst_bin_add (bin, chain->conv);
2240 chain->resample = gst_element_factory_make ("audioresample", "aresample");
2241 if (chain->resample == NULL)
2242 goto no_audioresample;
2243 gst_bin_add (bin, chain->resample);
2245 /* this pad will be used for blocking the dataflow and switching the vis
2247 chain->blockpad = gst_element_get_static_pad (chain->resample, "src");
2249 if (playsink->visualisation) {
2250 GST_DEBUG_OBJECT (playsink, "trying configure vis");
2251 chain->vis = try_element (playsink, playsink->visualisation, FALSE);
2253 if (chain->vis == NULL) {
2254 GST_DEBUG_OBJECT (playsink, "trying goom");
2255 elem = gst_element_factory_make ("goom", "vis");
2256 chain->vis = try_element (playsink, elem, TRUE);
2258 if (chain->vis == NULL)
2261 gst_bin_add (bin, chain->vis);
2263 res = gst_element_link_pads_full (chain->queue, "src", chain->conv, "sink",
2264 GST_PAD_LINK_CHECK_NOTHING);
2266 gst_element_link_pads_full (chain->conv, "src", chain->resample, "sink",
2267 GST_PAD_LINK_CHECK_NOTHING);
2269 gst_element_link_pads_full (chain->resample, "src", chain->vis, "sink",
2270 GST_PAD_LINK_CHECK_NOTHING);
2274 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
2275 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
2277 pad = gst_element_get_static_pad (chain->queue, "sink");
2278 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
2279 gst_object_unref (pad);
2280 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
2282 chain->srcpad = gst_ghost_pad_new ("src", chain->vissrcpad);
2283 gst_element_add_pad (chain->chain.bin, chain->srcpad);
2290 post_missing_element_message (playsink, "queue");
2291 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2292 (_("Missing element '%s' - check your GStreamer installation."),
2294 free_chain ((GstPlayChain *) chain);
2299 post_missing_element_message (playsink, "audioconvert");
2300 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2301 (_("Missing element '%s' - check your GStreamer installation."),
2302 "audioconvert"), ("possibly a liboil version mismatch?"));
2303 free_chain ((GstPlayChain *) chain);
2308 post_missing_element_message (playsink, "audioresample");
2309 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2310 (_("Missing element '%s' - check your GStreamer installation."),
2311 "audioresample"), (NULL));
2312 free_chain ((GstPlayChain *) chain);
2317 post_missing_element_message (playsink, "goom");
2318 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2319 (_("Missing element '%s' - check your GStreamer installation."),
2321 free_chain ((GstPlayChain *) chain);
2326 GST_ELEMENT_ERROR (playsink, CORE, PAD,
2327 (NULL), ("Failed to configure the visualisation element."));
2328 /* element made it to READY */
2329 gst_element_set_state (chain->vis, GST_STATE_NULL);
2330 free_chain ((GstPlayChain *) chain);
2335 /* this function is called when all the request pads are requested and when we
2336 * have to construct the final pipeline. Based on the flags we construct the
2337 * final output pipelines.
2340 gst_play_sink_reconfigure (GstPlaySink * playsink)
2343 gboolean need_audio, need_video, need_deinterlace, need_vis, need_text;
2345 GST_DEBUG_OBJECT (playsink, "reconfiguring");
2347 /* assume we need nothing */
2348 need_audio = need_video = need_deinterlace = need_vis = need_text = FALSE;
2350 GST_PLAY_SINK_LOCK (playsink);
2351 GST_OBJECT_LOCK (playsink);
2352 /* get flags, there are protected with the object lock */
2353 flags = playsink->flags;
2354 GST_OBJECT_UNLOCK (playsink);
2356 /* figure out which components we need */
2357 if (flags & GST_PLAY_FLAG_TEXT && playsink->text_pad) {
2358 /* we have subtitles and we are requested to show it */
2362 GST_OBJECT_LOCK (playsink);
2363 if (playsink->xoverlay_element)
2364 gst_object_unref (playsink->xoverlay_element);
2365 playsink->xoverlay_element = NULL;
2366 GST_OBJECT_UNLOCK (playsink);
2368 if (((flags & GST_PLAY_FLAG_VIDEO)
2369 || (flags & GST_PLAY_FLAG_NATIVE_VIDEO)) && playsink->video_pad) {
2370 /* we have video and we are requested to show it */
2373 /* we only deinterlace if native video is not requested and
2374 * we have raw video */
2375 if ((flags & GST_PLAY_FLAG_DEINTERLACE)
2376 && !(flags & GST_PLAY_FLAG_NATIVE_VIDEO) && playsink->video_pad_raw)
2377 need_deinterlace = TRUE;
2380 if (playsink->audio_pad) {
2381 if ((flags & GST_PLAY_FLAG_AUDIO) || (flags & GST_PLAY_FLAG_NATIVE_AUDIO)) {
2384 if (playsink->audio_pad_raw) {
2385 /* only can do vis with raw uncompressed audio */
2386 if (flags & GST_PLAY_FLAG_VIS && !need_video) {
2387 /* also add video when we add visualisation */
2394 /* we have a text_pad and we need text rendering, in this case we need a
2395 * video_pad to combine the video with the text or visualizations */
2396 if (need_text && !need_video) {
2397 if (playsink->video_pad) {
2399 } else if (need_audio) {
2400 GST_ELEMENT_WARNING (playsink, STREAM, FORMAT,
2401 (_("Can't play a text file without video or visualizations.")),
2402 ("Have text pad but no video pad or visualizations"));
2405 GST_ELEMENT_ERROR (playsink, STREAM, FORMAT,
2406 (_("Can't play a text file without video or visualizations.")),
2407 ("Have text pad but no video pad or visualizations"));
2408 GST_PLAY_SINK_UNLOCK (playsink);
2413 GST_DEBUG_OBJECT (playsink, "audio:%d, video:%d, vis:%d, text:%d", need_audio,
2414 need_video, need_vis, need_text);
2416 /* set up video pipeline */
2418 gboolean raw, async;
2420 /* we need a raw sink when we do vis or when we have a raw pad */
2421 raw = need_vis ? TRUE : playsink->video_pad_raw;
2422 /* we try to set the sink async=FALSE when we need vis, this way we can
2423 * avoid a queue in the audio chain. */
2426 GST_DEBUG_OBJECT (playsink, "adding video, raw %d",
2427 playsink->video_pad_raw);
2429 if (playsink->videochain) {
2430 /* try to reactivate the chain */
2431 if (!setup_video_chain (playsink, raw, async)) {
2432 if (playsink->video_sinkpad_stream_synchronizer) {
2433 gst_element_release_request_pad (GST_ELEMENT_CAST
2434 (playsink->stream_synchronizer),
2435 playsink->video_sinkpad_stream_synchronizer);
2436 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2437 playsink->video_sinkpad_stream_synchronizer = NULL;
2438 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2439 playsink->video_srcpad_stream_synchronizer = NULL;
2442 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2444 /* Remove the sink from the bin to keep its state
2445 * and unparent it to allow reuse */
2446 if (playsink->videochain->sink)
2447 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
2448 playsink->videochain->sink);
2450 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2451 free_chain ((GstPlayChain *) playsink->videochain);
2452 playsink->videochain = NULL;
2456 if (!playsink->videochain)
2457 playsink->videochain = gen_video_chain (playsink, raw, async);
2458 if (!playsink->videochain)
2461 if (!playsink->video_sinkpad_stream_synchronizer) {
2464 playsink->video_sinkpad_stream_synchronizer =
2465 gst_element_get_request_pad (GST_ELEMENT_CAST
2466 (playsink->stream_synchronizer), "sink_%d");
2467 it = gst_pad_iterate_internal_links
2468 (playsink->video_sinkpad_stream_synchronizer);
2470 gst_iterator_next (it,
2471 (gpointer *) & playsink->video_srcpad_stream_synchronizer);
2472 g_assert (playsink->video_srcpad_stream_synchronizer);
2473 gst_iterator_free (it);
2476 if (playsink->video_pad)
2477 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad),
2478 playsink->video_sinkpad_stream_synchronizer);
2480 if (need_deinterlace) {
2481 if (!playsink->videodeinterlacechain)
2482 playsink->videodeinterlacechain =
2483 gen_video_deinterlace_chain (playsink);
2484 if (!playsink->videodeinterlacechain)
2487 GST_DEBUG_OBJECT (playsink, "adding video deinterlace chain");
2489 GST_DEBUG_OBJECT (playsink, "setting up deinterlacing chain");
2491 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
2492 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
2494 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2495 playsink->videodeinterlacechain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2497 if (playsink->videodeinterlacechain) {
2498 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2499 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
2504 GST_DEBUG_OBJECT (playsink, "adding video chain");
2505 add_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
2506 activate_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
2507 /* if we are not part of vis or subtitles, set the ghostpad target */
2508 if (!need_vis && !need_text && (!playsink->textchain
2509 || !playsink->text_pad)) {
2510 GST_DEBUG_OBJECT (playsink, "ghosting video sinkpad");
2511 if (need_deinterlace)
2512 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
2513 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2515 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2516 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2519 GST_DEBUG_OBJECT (playsink, "no video needed");
2520 if (playsink->videochain) {
2521 GST_DEBUG_OBJECT (playsink, "removing video chain");
2522 if (playsink->vischain) {
2525 GST_DEBUG_OBJECT (playsink, "unlinking vis chain");
2527 /* also had visualisation, release the tee srcpad before we then
2528 * unlink the video from it */
2529 if (playsink->audio_tee_vissrc) {
2530 gst_element_release_request_pad (playsink->audio_tee,
2531 playsink->audio_tee_vissrc);
2532 gst_object_unref (playsink->audio_tee_vissrc);
2533 playsink->audio_tee_vissrc = NULL;
2536 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2537 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
2540 if (playsink->video_sinkpad_stream_synchronizer) {
2541 gst_element_release_request_pad (GST_ELEMENT_CAST
2542 (playsink->stream_synchronizer),
2543 playsink->video_sinkpad_stream_synchronizer);
2544 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2545 playsink->video_sinkpad_stream_synchronizer = NULL;
2546 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2547 playsink->video_srcpad_stream_synchronizer = NULL;
2550 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2551 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2552 if (playsink->videochain->ts_offset)
2553 gst_object_unref (playsink->videochain->ts_offset);
2554 playsink->videochain->ts_offset = NULL;
2557 if (playsink->videodeinterlacechain) {
2558 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2559 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2562 if (playsink->video_pad)
2563 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
2569 GST_DEBUG_OBJECT (playsink, "adding audio");
2571 /* get a raw sink if we are asked for a raw pad */
2572 raw = playsink->audio_pad_raw;
2574 if (playsink->audiochain) {
2575 /* try to reactivate the chain */
2576 if (!setup_audio_chain (playsink, raw)) {
2577 GST_DEBUG_OBJECT (playsink, "removing current audio chain");
2578 if (playsink->audio_tee_asrc) {
2579 gst_element_release_request_pad (playsink->audio_tee,
2580 playsink->audio_tee_asrc);
2581 gst_object_unref (playsink->audio_tee_asrc);
2582 playsink->audio_tee_asrc = NULL;
2585 if (playsink->audio_sinkpad_stream_synchronizer) {
2586 gst_element_release_request_pad (GST_ELEMENT_CAST
2587 (playsink->stream_synchronizer),
2588 playsink->audio_sinkpad_stream_synchronizer);
2589 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
2590 playsink->audio_sinkpad_stream_synchronizer = NULL;
2591 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
2592 playsink->audio_srcpad_stream_synchronizer = NULL;
2595 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2597 /* Remove the sink from the bin to keep its state
2598 * and unparent it to allow reuse */
2599 if (playsink->audiochain->sink)
2600 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
2601 playsink->audiochain->sink);
2603 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2604 disconnect_chain (playsink->audiochain, playsink);
2605 playsink->audiochain->volume = NULL;
2606 playsink->audiochain->mute = NULL;
2607 if (playsink->audiochain->ts_offset)
2608 gst_object_unref (playsink->audiochain->ts_offset);
2609 playsink->audiochain->ts_offset = NULL;
2610 free_chain ((GstPlayChain *) playsink->audiochain);
2611 playsink->audiochain = NULL;
2612 playsink->volume_changed = playsink->mute_changed = FALSE;
2616 if (!playsink->audiochain) {
2617 GST_DEBUG_OBJECT (playsink, "creating new audio chain");
2618 playsink->audiochain = gen_audio_chain (playsink, raw);
2621 if (!playsink->audio_sinkpad_stream_synchronizer) {
2624 playsink->audio_sinkpad_stream_synchronizer =
2625 gst_element_get_request_pad (GST_ELEMENT_CAST
2626 (playsink->stream_synchronizer), "sink_%d");
2627 it = gst_pad_iterate_internal_links
2628 (playsink->audio_sinkpad_stream_synchronizer);
2630 gst_iterator_next (it,
2631 (gpointer *) & playsink->audio_srcpad_stream_synchronizer);
2632 g_assert (playsink->audio_srcpad_stream_synchronizer);
2633 gst_iterator_free (it);
2636 if (playsink->audiochain) {
2637 GST_DEBUG_OBJECT (playsink, "adding audio chain");
2638 if (playsink->audio_tee_asrc == NULL) {
2639 playsink->audio_tee_asrc =
2640 gst_element_get_request_pad (playsink->audio_tee, "src%d");
2642 add_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
2643 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
2644 gst_pad_link_full (playsink->audio_tee_asrc,
2645 playsink->audio_sinkpad_stream_synchronizer,
2646 GST_PAD_LINK_CHECK_NOTHING);
2647 gst_pad_link_full (playsink->audio_srcpad_stream_synchronizer,
2648 playsink->audiochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2651 GST_DEBUG_OBJECT (playsink, "no audio needed");
2652 /* we have no audio or we are requested to not play audio */
2653 if (playsink->audiochain) {
2654 GST_DEBUG_OBJECT (playsink, "removing audio chain");
2655 /* release the audio pad */
2656 if (playsink->audio_tee_asrc) {
2657 gst_element_release_request_pad (playsink->audio_tee,
2658 playsink->audio_tee_asrc);
2659 gst_object_unref (playsink->audio_tee_asrc);
2660 playsink->audio_tee_asrc = NULL;
2663 if (playsink->audio_sinkpad_stream_synchronizer) {
2664 gst_element_release_request_pad (GST_ELEMENT_CAST
2665 (playsink->stream_synchronizer),
2666 playsink->audio_sinkpad_stream_synchronizer);
2667 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
2668 playsink->audio_sinkpad_stream_synchronizer = NULL;
2669 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
2670 playsink->audio_srcpad_stream_synchronizer = NULL;
2673 if (playsink->audiochain->sink_volume) {
2674 disconnect_chain (playsink->audiochain, playsink);
2675 playsink->audiochain->volume = NULL;
2676 playsink->audiochain->mute = NULL;
2677 if (playsink->audiochain->ts_offset)
2678 gst_object_unref (playsink->audiochain->ts_offset);
2679 playsink->audiochain->ts_offset = NULL;
2681 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2682 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2689 if (!playsink->vischain)
2690 playsink->vischain = gen_vis_chain (playsink);
2692 GST_DEBUG_OBJECT (playsink, "adding visualisation");
2694 if (playsink->vischain) {
2695 GST_DEBUG_OBJECT (playsink, "setting up vis chain");
2697 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2698 add_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
2699 activate_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
2700 if (playsink->audio_tee_vissrc == NULL) {
2701 playsink->audio_tee_vissrc =
2702 gst_element_get_request_pad (playsink->audio_tee, "src%d");
2704 gst_pad_link_full (playsink->audio_tee_vissrc,
2705 playsink->vischain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2706 gst_pad_link_full (srcpad, playsink->video_sinkpad_stream_synchronizer,
2707 GST_PAD_LINK_CHECK_NOTHING);
2708 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2709 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2710 gst_object_unref (srcpad);
2713 GST_DEBUG_OBJECT (playsink, "no vis needed");
2714 if (playsink->vischain) {
2715 if (playsink->audio_tee_vissrc) {
2716 gst_element_release_request_pad (playsink->audio_tee,
2717 playsink->audio_tee_vissrc);
2718 gst_object_unref (playsink->audio_tee_vissrc);
2719 playsink->audio_tee_vissrc = NULL;
2721 GST_DEBUG_OBJECT (playsink, "removing vis chain");
2722 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
2723 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
2728 GST_DEBUG_OBJECT (playsink, "adding text");
2729 if (!playsink->textchain) {
2730 GST_DEBUG_OBJECT (playsink, "creating text chain");
2731 playsink->textchain = gen_text_chain (playsink);
2733 if (playsink->textchain) {
2736 GST_DEBUG_OBJECT (playsink, "adding text chain");
2737 if (playsink->textchain->overlay)
2738 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", FALSE,
2740 add_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
2742 if (!playsink->text_sinkpad_stream_synchronizer) {
2743 playsink->text_sinkpad_stream_synchronizer =
2744 gst_element_get_request_pad (GST_ELEMENT_CAST
2745 (playsink->stream_synchronizer), "sink_%d");
2746 it = gst_pad_iterate_internal_links
2747 (playsink->text_sinkpad_stream_synchronizer);
2749 gst_iterator_next (it,
2750 (gpointer *) & playsink->text_srcpad_stream_synchronizer);
2751 g_assert (playsink->text_srcpad_stream_synchronizer);
2752 gst_iterator_free (it);
2754 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad),
2755 playsink->text_sinkpad_stream_synchronizer);
2756 gst_pad_link_full (playsink->text_srcpad_stream_synchronizer,
2757 playsink->textchain->textsinkpad, GST_PAD_LINK_CHECK_NOTHING);
2764 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2765 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
2766 gst_pad_link_full (srcpad, playsink->textchain->videosinkpad,
2767 GST_PAD_LINK_CHECK_NOTHING);
2768 gst_object_unref (srcpad);
2770 if (need_deinterlace)
2771 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
2772 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
2774 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2775 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
2777 gst_pad_link_full (playsink->textchain->srcpad,
2778 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2780 activate_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
2783 GST_DEBUG_OBJECT (playsink, "no text needed");
2784 /* we have no subtitles/text or we are requested to not show them */
2786 if (playsink->text_sinkpad_stream_synchronizer) {
2787 gst_element_release_request_pad (GST_ELEMENT_CAST
2788 (playsink->stream_synchronizer),
2789 playsink->text_sinkpad_stream_synchronizer);
2790 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
2791 playsink->text_sinkpad_stream_synchronizer = NULL;
2792 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
2793 playsink->text_srcpad_stream_synchronizer = NULL;
2796 if (playsink->textchain) {
2797 if (playsink->text_pad == NULL) {
2798 /* no text pad, remove the chain entirely */
2799 GST_DEBUG_OBJECT (playsink, "removing text chain");
2800 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
2801 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
2803 /* we have a chain and a textpad, turn the subtitles off */
2804 GST_DEBUG_OBJECT (playsink, "turning off the text");
2805 if (playsink->textchain->overlay)
2806 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", TRUE,
2810 if (!need_video && playsink->video_pad) {
2811 if (playsink->video_sinkpad_stream_synchronizer) {
2812 gst_element_release_request_pad (GST_ELEMENT_CAST
2813 (playsink->stream_synchronizer),
2814 playsink->video_sinkpad_stream_synchronizer);
2815 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2816 playsink->video_sinkpad_stream_synchronizer = NULL;
2817 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2818 playsink->video_srcpad_stream_synchronizer = NULL;
2821 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
2824 if (playsink->text_pad && !playsink->textchain)
2825 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad), NULL);
2827 update_av_offset (playsink);
2828 do_async_done (playsink);
2829 GST_PLAY_SINK_UNLOCK (playsink);
2836 /* gen_ chain already posted error */
2837 GST_DEBUG_OBJECT (playsink, "failed to setup chain");
2838 GST_PLAY_SINK_UNLOCK (playsink);
2844 * gst_play_sink_set_flags:
2845 * @playsink: a #GstPlaySink
2846 * @flags: #GstPlayFlags
2848 * Configure @flags on @playsink. The flags control the behaviour of @playsink
2849 * when constructing the sink pipelins.
2851 * Returns: TRUE if the flags could be configured.
2854 gst_play_sink_set_flags (GstPlaySink * playsink, GstPlayFlags flags)
2856 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), FALSE);
2858 GST_OBJECT_LOCK (playsink);
2859 playsink->flags = flags;
2860 GST_OBJECT_UNLOCK (playsink);
2866 * gst_play_sink_get_flags:
2867 * @playsink: a #GstPlaySink
2869 * Get the flags of @playsink. That flags control the behaviour of the sink when
2870 * it constructs the sink pipelines.
2872 * Returns: the currently configured #GstPlayFlags.
2875 gst_play_sink_get_flags (GstPlaySink * playsink)
2879 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), 0);
2881 GST_OBJECT_LOCK (playsink);
2882 res = playsink->flags;
2883 GST_OBJECT_UNLOCK (playsink);
2889 gst_play_sink_set_font_desc (GstPlaySink * playsink, const gchar * desc)
2891 GstPlayTextChain *chain;
2893 GST_PLAY_SINK_LOCK (playsink);
2894 chain = (GstPlayTextChain *) playsink->textchain;
2895 g_free (playsink->font_desc);
2896 playsink->font_desc = g_strdup (desc);
2897 if (chain && chain->overlay) {
2898 g_object_set (chain->overlay, "font-desc", desc, NULL);
2900 GST_PLAY_SINK_UNLOCK (playsink);
2904 gst_play_sink_get_font_desc (GstPlaySink * playsink)
2906 gchar *result = NULL;
2907 GstPlayTextChain *chain;
2909 GST_PLAY_SINK_LOCK (playsink);
2910 chain = (GstPlayTextChain *) playsink->textchain;
2911 if (chain && chain->overlay) {
2912 g_object_get (chain->overlay, "font-desc", &result, NULL);
2913 playsink->font_desc = g_strdup (result);
2915 result = g_strdup (playsink->font_desc);
2917 GST_PLAY_SINK_UNLOCK (playsink);
2923 gst_play_sink_set_subtitle_encoding (GstPlaySink * playsink,
2924 const gchar * encoding)
2926 GstPlayTextChain *chain;
2928 GST_PLAY_SINK_LOCK (playsink);
2929 chain = (GstPlayTextChain *) playsink->textchain;
2930 g_free (playsink->subtitle_encoding);
2931 playsink->subtitle_encoding = g_strdup (encoding);
2932 if (chain && chain->overlay) {
2933 g_object_set (chain->overlay, "subtitle-encoding", encoding, NULL);
2935 GST_PLAY_SINK_UNLOCK (playsink);
2939 gst_play_sink_get_subtitle_encoding (GstPlaySink * playsink)
2941 gchar *result = NULL;
2942 GstPlayTextChain *chain;
2944 GST_PLAY_SINK_LOCK (playsink);
2945 chain = (GstPlayTextChain *) playsink->textchain;
2946 if (chain && chain->overlay) {
2947 g_object_get (chain->overlay, "subtitle-encoding", &result, NULL);
2948 playsink->subtitle_encoding = g_strdup (result);
2950 result = g_strdup (playsink->subtitle_encoding);
2952 GST_PLAY_SINK_UNLOCK (playsink);
2958 update_av_offset (GstPlaySink * playsink)
2961 GstPlayAudioChain *achain;
2962 GstPlayVideoChain *vchain;
2964 av_offset = playsink->av_offset;
2965 achain = (GstPlayAudioChain *) playsink->audiochain;
2966 vchain = (GstPlayVideoChain *) playsink->videochain;
2968 if (achain && vchain && achain->ts_offset && vchain->ts_offset) {
2969 g_object_set (achain->ts_offset, "ts-offset", MAX (0, -av_offset), NULL);
2970 g_object_set (vchain->ts_offset, "ts-offset", MAX (0, av_offset), NULL);
2972 GST_LOG_OBJECT (playsink, "no ts_offset elements");
2977 gst_play_sink_set_av_offset (GstPlaySink * playsink, gint64 av_offset)
2979 GST_PLAY_SINK_LOCK (playsink);
2980 playsink->av_offset = av_offset;
2981 update_av_offset (playsink);
2982 GST_PLAY_SINK_UNLOCK (playsink);
2986 gst_play_sink_get_av_offset (GstPlaySink * playsink)
2990 GST_PLAY_SINK_LOCK (playsink);
2991 result = playsink->av_offset;
2992 GST_PLAY_SINK_UNLOCK (playsink);
2998 * gst_play_sink_get_last_frame:
2999 * @playsink: a #GstPlaySink
3001 * Get the last displayed frame from @playsink. This frame is in the native
3002 * format of the sink element, the caps on the result buffer contain the format
3003 * of the frame data.
3005 * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
3009 gst_play_sink_get_last_frame (GstPlaySink * playsink)
3011 GstBuffer *result = NULL;
3012 GstPlayVideoChain *chain;
3014 GST_PLAY_SINK_LOCK (playsink);
3015 GST_DEBUG_OBJECT (playsink, "taking last frame");
3016 /* get the video chain if we can */
3017 if ((chain = (GstPlayVideoChain *) playsink->videochain)) {
3018 GST_DEBUG_OBJECT (playsink, "found video chain");
3019 /* see if the chain is active */
3020 if (chain->chain.activated && chain->sink) {
3023 GST_DEBUG_OBJECT (playsink, "video chain active and has a sink");
3025 /* find and get the last-buffer property now */
3027 gst_play_sink_find_property (playsink, chain->sink,
3028 "last-buffer", GST_TYPE_BUFFER))) {
3029 GST_DEBUG_OBJECT (playsink, "getting last-buffer property");
3030 g_object_get (elem, "last-buffer", &result, NULL);
3031 gst_object_unref (elem);
3035 GST_PLAY_SINK_UNLOCK (playsink);
3041 * gst_play_sink_convert_frame:
3042 * @playsink: a #GstPlaySink
3045 * Get the last displayed frame from @playsink. If caps is %NULL, the video will
3046 * be in the native format of the sink element and the caps on the buffer
3047 * describe the format of the frame. If @caps is not %NULL, the video
3048 * frame will be converted to the format of the caps.
3050 * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
3051 * available or when the conversion failed.
3054 gst_play_sink_convert_frame (GstPlaySink * playsink, GstCaps * caps)
3058 result = gst_play_sink_get_last_frame (playsink);
3059 if (result != NULL && caps != NULL) {
3063 temp = gst_video_convert_frame (result, caps, 25 * GST_SECOND, &err);
3064 gst_buffer_unref (result);
3065 if (temp == NULL && err) {
3066 /* I'm really uncertain whether we should make playsink post an error
3067 * on the bus or not. It's not like it's a critical issue regarding
3068 * playsink behaviour. */
3069 GST_ERROR ("Error converting frame: %s", err->message);
3078 is_raw_structure (GstStructure * s)
3082 name = gst_structure_get_name (s);
3084 if (g_str_has_prefix (name, "video/x-raw-") ||
3085 g_str_has_prefix (name, "audio/x-raw-"))
3091 is_raw_pad (GstPad * pad)
3093 GstPad *peer = gst_pad_get_peer (pad);
3095 gboolean raw = TRUE;
3100 caps = gst_pad_get_negotiated_caps (peer);
3104 caps = gst_pad_get_caps_reffed (peer);
3106 n = gst_caps_get_size (caps);
3107 for (i = 0; i < n; i++) {
3108 gboolean r = is_raw_structure (gst_caps_get_structure (caps, i));
3112 } else if (raw != r) {
3113 GST_ERROR_OBJECT (pad,
3114 "Caps contains raw and non-raw structures: %" GST_PTR_FORMAT, caps);
3120 raw = is_raw_structure (gst_caps_get_structure (caps, 0));
3122 gst_caps_unref (caps);
3123 gst_object_unref (peer);
3129 sinkpad_blocked_cb (GstPad * blockedpad, gboolean blocked, gpointer user_data)
3131 GstPlaySink *playsink = (GstPlaySink *) user_data;
3134 GST_PLAY_SINK_LOCK (playsink);
3136 pad = GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (blockedpad)));
3137 if (pad == playsink->video_pad) {
3138 playsink->video_pad_blocked = blocked;
3139 GST_DEBUG_OBJECT (pad, "Video pad blocked: %d", blocked);
3141 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO_RAW);
3142 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO);
3144 } else if (pad == playsink->audio_pad) {
3145 playsink->audio_pad_blocked = blocked;
3146 GST_DEBUG_OBJECT (pad, "Audio pad blocked: %d", blocked);
3148 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO_RAW);
3149 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO);
3151 } else if (pad == playsink->text_pad) {
3152 playsink->text_pad_blocked = blocked;
3153 GST_DEBUG_OBJECT (pad, "Text pad blocked: %d", blocked);
3155 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_TEXT);
3159 gst_object_unref (pad);
3160 GST_PLAY_SINK_UNLOCK (playsink);
3164 /* We reconfigure when for ALL streams:
3165 * * there isn't a pad
3166 * * OR the pad is blocked
3167 * * OR there are no pending blocks on that pad
3170 if ((!playsink->video_pad || playsink->video_pad_blocked
3171 || !PENDING_VIDEO_BLOCK (playsink)) && (!playsink->audio_pad
3172 || playsink->audio_pad_blocked || !PENDING_AUDIO_BLOCK (playsink))
3173 && (!playsink->text_pad || playsink->text_pad_blocked
3174 || !PENDING_TEXT_BLOCK (playsink))) {
3175 GST_DEBUG_OBJECT (playsink, "All pads blocked -- reconfiguring");
3177 if (playsink->video_pad) {
3178 playsink->video_pad_raw = is_raw_pad (playsink->video_pad);
3179 GST_DEBUG_OBJECT (playsink, "Video pad is raw: %d",
3180 playsink->video_pad_raw);
3183 if (playsink->audio_pad) {
3184 playsink->audio_pad_raw = is_raw_pad (playsink->audio_pad);
3185 GST_DEBUG_OBJECT (playsink, "Audio pad is raw: %d",
3186 playsink->audio_pad_raw);
3189 gst_play_sink_reconfigure (playsink);
3191 if (playsink->video_pad) {
3193 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3194 (playsink->video_pad)));
3195 gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3196 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3197 gst_object_unref (opad);
3200 if (playsink->audio_pad) {
3202 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3203 (playsink->audio_pad)));
3204 gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3205 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3206 gst_object_unref (opad);
3209 if (playsink->text_pad) {
3211 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3212 (playsink->text_pad)));
3213 gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3214 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3215 gst_object_unref (opad);
3219 gst_object_unref (pad);
3221 GST_PLAY_SINK_UNLOCK (playsink);
3225 caps_notify_cb (GstPad * pad, GParamSpec * unused, GstPlaySink * playsink)
3227 gboolean reconfigure = FALSE;
3231 g_object_get (pad, "caps", &caps, NULL);
3235 if (pad == playsink->audio_pad) {
3236 raw = is_raw_pad (pad);
3237 reconfigure = (! !playsink->audio_pad_raw != ! !raw)
3238 && playsink->audiochain;
3239 GST_DEBUG_OBJECT (pad,
3240 "Audio caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3242 } else if (pad == playsink->video_pad) {
3243 raw = is_raw_pad (pad);
3244 reconfigure = (! !playsink->video_pad_raw != ! !raw)
3245 && playsink->videochain;
3246 GST_DEBUG_OBJECT (pad,
3247 "Video caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3251 gst_caps_unref (caps);
3254 GST_PLAY_SINK_LOCK (playsink);
3255 if (playsink->video_pad) {
3257 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3258 (playsink->video_pad)));
3259 gst_pad_set_blocked_async_full (opad, TRUE, sinkpad_blocked_cb,
3260 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3261 gst_object_unref (opad);
3264 if (playsink->audio_pad) {
3266 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3267 (playsink->audio_pad)));
3268 gst_pad_set_blocked_async_full (opad, TRUE, sinkpad_blocked_cb,
3269 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3270 gst_object_unref (opad);
3273 if (playsink->text_pad) {
3275 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3276 (playsink->text_pad)));
3277 gst_pad_set_blocked_async_full (opad, TRUE, sinkpad_blocked_cb,
3278 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3279 gst_object_unref (opad);
3281 GST_PLAY_SINK_UNLOCK (playsink);
3286 * gst_play_sink_request_pad
3287 * @playsink: a #GstPlaySink
3288 * @type: a #GstPlaySinkType
3290 * Create or return a pad of @type.
3292 * Returns: a #GstPad of @type or %NULL when the pad could not be created.
3295 gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type)
3298 gboolean created = FALSE;
3299 gboolean activate = TRUE;
3300 const gchar *pad_name = NULL;
3302 GST_DEBUG_OBJECT (playsink, "request pad type %d", type);
3304 GST_PLAY_SINK_LOCK (playsink);
3306 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
3307 case GST_PLAY_SINK_TYPE_AUDIO:
3308 pad_name = "audio_sink";
3309 if (!playsink->audio_tee) {
3310 GST_LOG_OBJECT (playsink, "creating tee");
3311 /* create tee when needed. This element will feed the audio sink chain
3312 * and the vis chain. */
3313 playsink->audio_tee = gst_element_factory_make ("tee", "audiotee");
3314 if (playsink->audio_tee == NULL) {
3315 post_missing_element_message (playsink, "tee");
3316 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
3317 (_("Missing element '%s' - check your GStreamer installation."),
3322 playsink->audio_tee_sink =
3323 gst_element_get_static_pad (playsink->audio_tee, "sink");
3324 gst_bin_add (GST_BIN_CAST (playsink), playsink->audio_tee);
3325 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
3328 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
3330 if (!playsink->audio_pad) {
3331 GST_LOG_OBJECT (playsink, "ghosting tee sinkpad");
3332 playsink->audio_pad =
3333 gst_ghost_pad_new (pad_name, playsink->audio_tee_sink);
3334 g_signal_connect (G_OBJECT (playsink->audio_pad), "notify::caps",
3335 G_CALLBACK (caps_notify_cb), playsink);
3338 playsink->audio_pad_raw = FALSE;
3339 res = playsink->audio_pad;
3341 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
3342 case GST_PLAY_SINK_TYPE_VIDEO:
3343 pad_name = "video_sink";
3344 if (!playsink->video_pad) {
3345 GST_LOG_OBJECT (playsink, "ghosting videosink");
3346 playsink->video_pad =
3347 gst_ghost_pad_new_no_target (pad_name, GST_PAD_SINK);
3348 g_signal_connect (G_OBJECT (playsink->video_pad), "notify::caps",
3349 G_CALLBACK (caps_notify_cb), playsink);
3352 playsink->video_pad_raw = FALSE;
3353 res = playsink->video_pad;
3355 case GST_PLAY_SINK_TYPE_TEXT:
3356 GST_LOG_OBJECT (playsink, "ghosting text");
3357 if (!playsink->text_pad) {
3358 playsink->text_pad =
3359 gst_ghost_pad_new_no_target ("text_sink", GST_PAD_SINK);
3362 res = playsink->text_pad;
3364 case GST_PLAY_SINK_TYPE_FLUSHING:
3368 /* we need a unique padname for the flushing pad. */
3369 padname = g_strdup_printf ("flushing_%d", playsink->count);
3370 res = gst_ghost_pad_new_no_target (padname, GST_PAD_SINK);
3381 GST_PLAY_SINK_UNLOCK (playsink);
3383 if (created && res) {
3384 /* we have to add the pad when it's active or we get an error when the
3385 * element is 'running' */
3386 gst_pad_set_active (res, TRUE);
3387 gst_element_add_pad (GST_ELEMENT_CAST (playsink), res);
3388 if (type != GST_PLAY_SINK_TYPE_FLUSHING) {
3390 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (res)));
3392 gst_pad_set_blocked_async_full (blockpad, TRUE, sinkpad_blocked_cb,
3393 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3394 PENDING_FLAG_SET (playsink, type);
3395 gst_object_unref (blockpad);
3398 gst_pad_set_active (res, activate);
3405 gst_play_sink_request_new_pad (GstElement * element, GstPadTemplate * templ,
3410 GstPlaySinkType type;
3411 const gchar *tplname;
3413 g_return_val_if_fail (templ != NULL, NULL);
3415 GST_DEBUG_OBJECT (element, "name:%s", name);
3417 psink = GST_PLAY_SINK (element);
3418 tplname = GST_PAD_TEMPLATE_NAME_TEMPLATE (templ);
3420 /* Figure out the GstPlaySinkType based on the template */
3421 if (!strcmp (tplname, "audio_sink"))
3422 type = GST_PLAY_SINK_TYPE_AUDIO;
3423 else if (!strcmp (tplname, "audio_raw_sink"))
3424 type = GST_PLAY_SINK_TYPE_AUDIO_RAW;
3425 else if (!strcmp (tplname, "video_sink"))
3426 type = GST_PLAY_SINK_TYPE_VIDEO;
3427 else if (!strcmp (tplname, "video_raw_sink"))
3428 type = GST_PLAY_SINK_TYPE_VIDEO_RAW;
3429 else if (!strcmp (tplname, "text_sink"))
3430 type = GST_PLAY_SINK_TYPE_TEXT;
3432 goto unknown_template;
3434 pad = gst_play_sink_request_pad (psink, type);
3438 GST_WARNING_OBJECT (element, "Unknown pad template");
3443 gst_play_sink_release_pad (GstPlaySink * playsink, GstPad * pad)
3445 GstPad **res = NULL;
3446 gboolean untarget = TRUE;
3448 GST_DEBUG_OBJECT (playsink, "release pad %" GST_PTR_FORMAT, pad);
3450 GST_PLAY_SINK_LOCK (playsink);
3451 if (pad == playsink->video_pad) {
3452 res = &playsink->video_pad;
3453 g_signal_handlers_disconnect_by_func (playsink->video_pad, caps_notify_cb,
3455 } else if (pad == playsink->audio_pad) {
3456 res = &playsink->audio_pad;
3457 g_signal_handlers_disconnect_by_func (playsink->audio_pad, caps_notify_cb,
3459 } else if (pad == playsink->text_pad) {
3460 res = &playsink->text_pad;
3462 /* try to release the given pad anyway, these could be the FLUSHING pads. */
3466 GST_PLAY_SINK_UNLOCK (playsink);
3469 GST_DEBUG_OBJECT (playsink, "deactivate pad %" GST_PTR_FORMAT, *res);
3470 gst_pad_set_active (*res, FALSE);
3472 GST_DEBUG_OBJECT (playsink, "untargeting pad %" GST_PTR_FORMAT, *res);
3473 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (*res), NULL);
3475 GST_DEBUG_OBJECT (playsink, "remove pad %" GST_PTR_FORMAT, *res);
3476 gst_element_remove_pad (GST_ELEMENT_CAST (playsink), *res);
3482 gst_play_sink_release_request_pad (GstElement * element, GstPad * pad)
3484 GstPlaySink *psink = GST_PLAY_SINK (element);
3486 gst_play_sink_release_pad (psink, pad);
3490 gst_play_sink_handle_message (GstBin * bin, GstMessage * message)
3492 GstPlaySink *playsink;
3494 playsink = GST_PLAY_SINK_CAST (bin);
3496 switch (GST_MESSAGE_TYPE (message)) {
3497 case GST_MESSAGE_STEP_DONE:
3502 gboolean flush, intermediate, eos;
3505 GST_INFO_OBJECT (playsink, "Handling step-done message");
3506 gst_message_parse_step_done (message, &format, &amount, &rate, &flush,
3507 &intermediate, &duration, &eos);
3509 if (format == GST_FORMAT_BUFFERS) {
3510 /* for the buffer format, we align the other streams */
3511 if (playsink->audiochain) {
3515 gst_event_new_step (GST_FORMAT_TIME, duration, rate, flush,
3518 if (!gst_element_send_event (playsink->audiochain->chain.bin, event)) {
3519 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
3523 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
3526 case GST_MESSAGE_ELEMENT:{
3527 if (gst_structure_has_name (message->structure, "prepare-xwindow-id")) {
3528 GstXOverlay *xoverlay;
3530 GST_OBJECT_LOCK (playsink);
3531 if (playsink->xoverlay_element
3532 && GST_OBJECT_CAST (playsink->xoverlay_element) !=
3533 GST_MESSAGE_SRC (message)) {
3534 gst_object_unref (playsink->xoverlay_element);
3535 playsink->xoverlay_element = NULL;
3538 if (!playsink->xoverlay_element)
3539 playsink->xoverlay_element =
3540 GST_X_OVERLAY (gst_object_ref (GST_MESSAGE_SRC (message)));
3541 xoverlay = GST_X_OVERLAY (gst_object_ref (playsink->xoverlay_element));
3542 GST_OBJECT_UNLOCK (playsink);
3544 GST_DEBUG_OBJECT (playsink, "Got prepare-xwindow-id message");
3546 if (playsink->xoverlay_handle_set)
3547 gst_x_overlay_set_window_handle (playsink->xoverlay_element,
3548 playsink->xoverlay_handle);
3549 if (playsink->xoverlay_handle_events_set)
3550 gst_x_overlay_handle_events (playsink->xoverlay_element,
3551 playsink->xoverlay_handle_events);
3552 if (playsink->xoverlay_render_rectangle_set)
3553 gst_x_overlay_set_render_rectangle (playsink->xoverlay_element,
3554 playsink->xoverlay_x, playsink->xoverlay_y,
3555 playsink->xoverlay_width, playsink->xoverlay_height);
3557 gst_object_unref (xoverlay);
3558 gst_message_unref (message);
3559 gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (playsink));
3564 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
3569 /* Send an event to our sinks until one of them works; don't then send to the
3570 * remaining sinks (unlike GstBin)
3571 * Special case: If a text sink is set we need to send the event
3572 * to them in case it's source is different from the a/v stream's source.
3575 gst_play_sink_send_event_to_sink (GstPlaySink * playsink, GstEvent * event)
3577 gboolean res = TRUE;
3578 if (playsink->textchain && playsink->textchain->sink) {
3579 gst_event_ref (event);
3580 if ((res = gst_element_send_event (playsink->textchain->chain.bin, event))) {
3581 GST_DEBUG_OBJECT (playsink, "Sent event successfully to text sink");
3583 GST_DEBUG_OBJECT (playsink, "Event failed when sent to text sink");
3587 if (playsink->videochain) {
3588 gst_event_ref (event);
3589 if ((res = gst_element_send_event (playsink->videochain->chain.bin, event))) {
3590 GST_DEBUG_OBJECT (playsink, "Sent event successfully to video sink");
3593 GST_DEBUG_OBJECT (playsink, "Event failed when sent to video sink");
3595 if (playsink->audiochain) {
3596 gst_event_ref (event);
3597 if ((res = gst_element_send_event (playsink->audiochain->chain.bin, event))) {
3598 GST_DEBUG_OBJECT (playsink, "Sent event successfully to audio sink");
3601 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
3605 gst_event_unref (event);
3609 /* We only want to send the event to a single sink (overriding GstBin's
3610 * behaviour), but we want to keep GstPipeline's behaviour - wrapping seek
3611 * events appropriately. So, this is a messy duplication of code. */
3613 gst_play_sink_send_event (GstElement * element, GstEvent * event)
3615 gboolean res = FALSE;
3616 GstEventType event_type = GST_EVENT_TYPE (event);
3617 GstPlaySink *playsink;
3618 playsink = GST_PLAY_SINK_CAST (element);
3619 switch (event_type) {
3620 case GST_EVENT_SEEK:
3621 GST_DEBUG_OBJECT (element, "Sending event to a sink");
3622 res = gst_play_sink_send_event_to_sink (playsink, event);
3624 case GST_EVENT_STEP:
3629 gboolean flush, intermediate;
3630 gst_event_parse_step (event, &format, &amount, &rate, &flush,
3632 if (format == GST_FORMAT_BUFFERS) {
3633 /* for buffers, we will try to step video frames, for other formats we
3634 * send the step to all sinks */
3635 res = gst_play_sink_send_event_to_sink (playsink, event);
3638 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
3645 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
3652 static GstStateChangeReturn
3653 gst_play_sink_change_state (GstElement * element, GstStateChange transition)
3655 GstStateChangeReturn ret;
3656 GstStateChangeReturn bret;
3657 GstPlaySink *playsink;
3658 playsink = GST_PLAY_SINK (element);
3659 switch (transition) {
3660 case GST_STATE_CHANGE_READY_TO_PAUSED:
3661 playsink->need_async_start = TRUE;
3662 /* we want to go async to PAUSED until we managed to configure and add the
3664 do_async_start (playsink);
3665 ret = GST_STATE_CHANGE_ASYNC;
3667 case GST_STATE_CHANGE_PAUSED_TO_READY:
3668 /* unblock all pads here */
3669 GST_PLAY_SINK_LOCK (playsink);
3670 if (playsink->video_pad) {
3672 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3673 (playsink->video_pad)));
3674 if (gst_pad_is_blocked (opad)) {
3675 gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3676 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3678 gst_object_unref (opad);
3679 playsink->video_pad_blocked = FALSE;
3682 if (playsink->audio_pad) {
3684 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3685 (playsink->audio_pad)));
3686 if (gst_pad_is_blocked (opad)) {
3687 gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3688 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3690 gst_object_unref (opad);
3691 playsink->audio_pad_blocked = FALSE;
3694 if (playsink->text_pad) {
3696 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3697 (playsink->text_pad)));
3698 if (gst_pad_is_blocked (opad)) {
3699 gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3700 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3702 gst_object_unref (opad);
3703 playsink->text_pad_blocked = FALSE;
3705 GST_PLAY_SINK_UNLOCK (playsink);
3707 case GST_STATE_CHANGE_READY_TO_NULL:
3708 if (playsink->audiochain && playsink->audiochain->sink_volume) {
3709 /* remove our links to the mute and volume elements when they were
3710 * provided by a sink */
3711 disconnect_chain (playsink->audiochain, playsink);
3712 playsink->audiochain->volume = NULL;
3713 playsink->audiochain->mute = NULL;
3716 if (playsink->audiochain && playsink->audiochain->ts_offset) {
3717 gst_object_unref (playsink->audiochain->ts_offset);
3718 playsink->audiochain->ts_offset = NULL;
3721 if (playsink->videochain && playsink->videochain->ts_offset) {
3722 gst_object_unref (playsink->videochain->ts_offset);
3723 playsink->videochain->ts_offset = NULL;
3726 GST_OBJECT_LOCK (playsink);
3727 if (playsink->xoverlay_element)
3728 gst_object_unref (playsink->xoverlay_element);
3729 playsink->xoverlay_element = NULL;
3730 GST_OBJECT_UNLOCK (playsink);
3732 ret = GST_STATE_CHANGE_SUCCESS;
3735 /* all other state changes return SUCCESS by default, this value can be
3736 * overridden by the result of the children */
3737 ret = GST_STATE_CHANGE_SUCCESS;
3741 /* do the state change of the children */
3743 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->change_state (element,
3745 /* now look at the result of our children and adjust the return value */
3747 case GST_STATE_CHANGE_FAILURE:
3748 /* failure, we stop */
3749 goto activate_failed;
3750 case GST_STATE_CHANGE_NO_PREROLL:
3751 /* some child returned NO_PREROLL. This is strange but we never know. We
3752 * commit our async state change (if any) and return the NO_PREROLL */
3753 do_async_done (playsink);
3756 case GST_STATE_CHANGE_ASYNC:
3757 /* some child was async, return this */
3761 /* return our previously configured return value */
3765 switch (transition) {
3766 case GST_STATE_CHANGE_READY_TO_PAUSED:
3768 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
3769 /* FIXME Release audio device when we implement that */
3770 playsink->need_async_start = TRUE;
3772 case GST_STATE_CHANGE_PAUSED_TO_READY:{
3773 if (playsink->video_sinkpad_stream_synchronizer) {
3774 gst_element_release_request_pad (GST_ELEMENT_CAST
3775 (playsink->stream_synchronizer),
3776 playsink->video_sinkpad_stream_synchronizer);
3777 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3778 playsink->video_sinkpad_stream_synchronizer = NULL;
3779 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3780 playsink->video_srcpad_stream_synchronizer = NULL;
3782 if (playsink->audio_sinkpad_stream_synchronizer) {
3783 gst_element_release_request_pad (GST_ELEMENT_CAST
3784 (playsink->stream_synchronizer),
3785 playsink->audio_sinkpad_stream_synchronizer);
3786 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
3787 playsink->audio_sinkpad_stream_synchronizer = NULL;
3788 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
3789 playsink->audio_srcpad_stream_synchronizer = NULL;
3791 if (playsink->text_sinkpad_stream_synchronizer) {
3792 gst_element_release_request_pad (GST_ELEMENT_CAST
3793 (playsink->stream_synchronizer),
3794 playsink->text_sinkpad_stream_synchronizer);
3795 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
3796 playsink->text_sinkpad_stream_synchronizer = NULL;
3797 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
3798 playsink->text_srcpad_stream_synchronizer = NULL;
3802 case GST_STATE_CHANGE_READY_TO_NULL:
3803 /* remove sinks we added */
3804 if (playsink->videodeinterlacechain) {
3805 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
3807 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3809 if (playsink->videochain) {
3810 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3811 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3813 if (playsink->audiochain) {
3814 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3815 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3817 if (playsink->vischain) {
3818 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3819 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3821 if (playsink->textchain) {
3822 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3823 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3825 do_async_done (playsink);
3826 /* when going to READY, keep elements around as long as possible,
3827 * so they may be re-used faster next time/url around.
3828 * when really going to NULL, clean up everything completely. */
3829 if (transition == GST_STATE_CHANGE_READY_TO_NULL) {
3831 /* Unparent the sinks to allow reuse */
3832 if (playsink->videochain && playsink->videochain->sink)
3833 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
3834 playsink->videochain->sink);
3835 if (playsink->audiochain && playsink->audiochain->sink)
3836 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
3837 playsink->audiochain->sink);
3838 if (playsink->textchain && playsink->textchain->sink)
3839 gst_bin_remove (GST_BIN_CAST (playsink->textchain->chain.bin),
3840 playsink->textchain->sink);
3841 if (playsink->audio_sink != NULL)
3842 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
3843 if (playsink->video_sink != NULL)
3844 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
3845 if (playsink->visualisation != NULL)
3846 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
3847 if (playsink->text_sink != NULL)
3848 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
3849 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
3850 playsink->videodeinterlacechain = NULL;
3851 free_chain ((GstPlayChain *) playsink->videochain);
3852 playsink->videochain = NULL;
3853 free_chain ((GstPlayChain *) playsink->audiochain);
3854 playsink->audiochain = NULL;
3855 free_chain ((GstPlayChain *) playsink->vischain);
3856 playsink->vischain = NULL;
3857 free_chain ((GstPlayChain *) playsink->textchain);
3858 playsink->textchain = NULL;
3868 GST_DEBUG_OBJECT (element,
3869 "element failed to change states -- activation problem?");
3870 return GST_STATE_CHANGE_FAILURE;
3875 gst_play_sink_set_property (GObject * object, guint prop_id,
3876 const GValue * value, GParamSpec * spec)
3878 GstPlaySink *playsink = GST_PLAY_SINK (object);
3881 gst_play_sink_set_flags (playsink, g_value_get_flags (value));
3884 gst_play_sink_set_volume (playsink, g_value_get_double (value));
3887 gst_play_sink_set_mute (playsink, g_value_get_boolean (value));
3889 case PROP_FONT_DESC:
3890 gst_play_sink_set_font_desc (playsink, g_value_get_string (value));
3892 case PROP_SUBTITLE_ENCODING:
3893 gst_play_sink_set_subtitle_encoding (playsink,
3894 g_value_get_string (value));
3896 case PROP_VIS_PLUGIN:
3897 gst_play_sink_set_vis_plugin (playsink, g_value_get_object (value));
3899 case PROP_AV_OFFSET:
3900 gst_play_sink_set_av_offset (playsink, g_value_get_int64 (value));
3902 case PROP_VIDEO_SINK:
3903 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_VIDEO,
3904 g_value_get_object (value));
3906 case PROP_AUDIO_SINK:
3907 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_AUDIO,
3908 g_value_get_object (value));
3910 case PROP_TEXT_SINK:
3911 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_TEXT,
3912 g_value_get_object (value));
3915 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
3921 gst_play_sink_get_property (GObject * object, guint prop_id,
3922 GValue * value, GParamSpec * spec)
3924 GstPlaySink *playsink = GST_PLAY_SINK (object);
3927 g_value_set_flags (value, gst_play_sink_get_flags (playsink));
3930 g_value_set_double (value, gst_play_sink_get_volume (playsink));
3933 g_value_set_boolean (value, gst_play_sink_get_mute (playsink));
3935 case PROP_FONT_DESC:
3936 g_value_take_string (value, gst_play_sink_get_font_desc (playsink));
3938 case PROP_SUBTITLE_ENCODING:
3939 g_value_take_string (value,
3940 gst_play_sink_get_subtitle_encoding (playsink));
3942 case PROP_VIS_PLUGIN:
3943 g_value_take_object (value, gst_play_sink_get_vis_plugin (playsink));
3946 gst_value_take_buffer (value, gst_play_sink_get_last_frame (playsink));
3948 case PROP_AV_OFFSET:
3949 g_value_set_int64 (value, gst_play_sink_get_av_offset (playsink));
3951 case PROP_VIDEO_SINK:
3952 g_value_take_object (value, gst_play_sink_get_sink (playsink,
3953 GST_PLAY_SINK_TYPE_VIDEO));
3955 case PROP_AUDIO_SINK:
3956 g_value_take_object (value, gst_play_sink_get_sink (playsink,
3957 GST_PLAY_SINK_TYPE_AUDIO));
3959 case PROP_TEXT_SINK:
3960 g_value_take_object (value, gst_play_sink_get_sink (playsink,
3961 GST_PLAY_SINK_TYPE_TEXT));
3964 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
3970 gst_play_sink_xoverlay_expose (GstXOverlay * overlay)
3972 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
3973 GstXOverlay *xoverlay;
3975 GST_OBJECT_LOCK (playsink);
3976 if (playsink->xoverlay_element)
3977 xoverlay = GST_X_OVERLAY (gst_object_ref (playsink->xoverlay_element));
3980 GST_OBJECT_UNLOCK (playsink);
3983 gst_x_overlay_expose (xoverlay);
3984 gst_object_unref (xoverlay);
3989 gst_play_sink_xoverlay_handle_events (GstXOverlay * overlay,
3990 gboolean handle_events)
3992 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
3993 GstXOverlay *xoverlay;
3995 GST_OBJECT_LOCK (playsink);
3996 if (playsink->xoverlay_element)
3997 xoverlay = GST_X_OVERLAY (gst_object_ref (playsink->xoverlay_element));
4000 GST_OBJECT_UNLOCK (playsink);
4002 playsink->xoverlay_handle_events_set = TRUE;
4003 playsink->xoverlay_handle_events = handle_events;
4006 gst_x_overlay_handle_events (xoverlay, handle_events);
4007 gst_object_unref (xoverlay);
4012 gst_play_sink_xoverlay_set_render_rectangle (GstXOverlay * overlay, gint x,
4013 gint y, gint width, gint height)
4015 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4016 GstXOverlay *xoverlay;
4018 GST_OBJECT_LOCK (playsink);
4019 if (playsink->xoverlay_element)
4020 xoverlay = GST_X_OVERLAY (gst_object_ref (playsink->xoverlay_element));
4023 GST_OBJECT_UNLOCK (playsink);
4025 playsink->xoverlay_render_rectangle_set = TRUE;
4026 playsink->xoverlay_x = x;
4027 playsink->xoverlay_y = y;
4028 playsink->xoverlay_width = width;
4029 playsink->xoverlay_height = height;
4032 gst_x_overlay_set_render_rectangle (xoverlay, x, y, width, height);
4033 gst_object_unref (xoverlay);
4038 gst_play_sink_xoverlay_set_window_handle (GstXOverlay * overlay,
4041 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4042 GstXOverlay *xoverlay;
4044 GST_OBJECT_LOCK (playsink);
4045 if (playsink->xoverlay_element)
4046 xoverlay = GST_X_OVERLAY (gst_object_ref (playsink->xoverlay_element));
4049 GST_OBJECT_UNLOCK (playsink);
4051 playsink->xoverlay_handle_set = TRUE;
4052 playsink->xoverlay_handle = handle;
4055 gst_x_overlay_set_window_handle (xoverlay, handle);
4056 gst_object_unref (xoverlay);
4061 gst_play_sink_xoverlay_init (gpointer g_iface, gpointer g_iface_data)
4063 GstXOverlayClass *iface = (GstXOverlayClass *) g_iface;
4064 iface->expose = gst_play_sink_xoverlay_expose;
4065 iface->handle_events = gst_play_sink_xoverlay_handle_events;
4066 iface->set_render_rectangle = gst_play_sink_xoverlay_set_render_rectangle;
4067 iface->set_window_handle = gst_play_sink_xoverlay_set_window_handle;
4071 gst_play_sink_implements_interface_supported (GstImplementsInterface * iface,
4074 if (type == GST_TYPE_X_OVERLAY || type == GST_TYPE_STREAM_VOLUME ||
4075 type == GST_TYPE_NAVIGATION)
4082 gst_play_sink_implements_interface_init (gpointer g_iface,
4083 gpointer g_iface_data)
4085 GstImplementsInterfaceClass *iface = (GstImplementsInterfaceClass *) g_iface;
4086 iface->supported = gst_play_sink_implements_interface_supported;
4090 gst_play_sink_navigation_send_event (GstNavigation * navigation,
4091 GstStructure * structure)
4093 GstPlaySink *playsink = GST_PLAY_SINK (navigation);
4096 GST_PLAY_SINK_LOCK (playsink);
4097 if (playsink->videochain && playsink->videochain->chain.bin)
4098 bin = GST_BIN (gst_object_ref (playsink->videochain->chain.bin));
4099 GST_PLAY_SINK_UNLOCK (playsink);
4102 GstElement *nav = gst_bin_get_by_interface (bin, GST_TYPE_NAVIGATION);
4105 gst_navigation_send_event (GST_NAVIGATION (nav), structure);
4107 gst_object_unref (nav);
4110 gst_object_unref (bin);
4114 gst_structure_free (structure);
4118 gst_play_sink_navigation_init (gpointer g_iface, gpointer g_iface_data)
4120 GstNavigationInterface *iface = (GstNavigationInterface *) g_iface;
4122 iface->send_event = gst_play_sink_navigation_send_event;
4126 gst_play_sink_plugin_init (GstPlugin * plugin)
4128 GST_DEBUG_CATEGORY_INIT (gst_play_sink_debug, "playsink", 0, "play bin");
4129 return gst_element_register (plugin, "playsink", GST_RANK_NONE,
4130 GST_TYPE_PLAY_SINK);