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;
220 /* colorbalance proxy interface */
221 GstColorBalance *colorbalance_element;
222 GList *colorbalance_channels; /* CONTRAST, BRIGHTNESS, HUE, SATURATION */
223 gint colorbalance_values[4];
226 struct _GstPlaySinkClass
228 GstBinClass parent_class;
230 gboolean (*reconfigure) (GstPlaySink * playsink);
232 GstBuffer *(*convert_frame) (GstPlaySink * playsink, GstCaps * caps);
236 static GstStaticPadTemplate audiotemplate =
237 GST_STATIC_PAD_TEMPLATE ("audio_sink",
240 GST_STATIC_CAPS_ANY);
241 static GstStaticPadTemplate videotemplate =
242 GST_STATIC_PAD_TEMPLATE ("video_sink",
245 GST_STATIC_CAPS_ANY);
246 static GstStaticPadTemplate texttemplate = GST_STATIC_PAD_TEMPLATE ("text_sink",
249 GST_STATIC_CAPS_ANY);
251 /* FIXME 0.11: Remove */
252 static GstStaticPadTemplate audiorawtemplate =
253 GST_STATIC_PAD_TEMPLATE ("audio_raw_sink",
256 GST_STATIC_CAPS_ANY);
257 static GstStaticPadTemplate videorawtemplate =
258 GST_STATIC_PAD_TEMPLATE ("video_raw_sink",
261 GST_STATIC_CAPS_ANY);
272 PROP_SUBTITLE_ENCODING,
288 static void gst_play_sink_dispose (GObject * object);
289 static void gst_play_sink_finalize (GObject * object);
290 static void gst_play_sink_set_property (GObject * object, guint prop_id,
291 const GValue * value, GParamSpec * spec);
292 static void gst_play_sink_get_property (GObject * object, guint prop_id,
293 GValue * value, GParamSpec * spec);
295 static GstPad *gst_play_sink_request_new_pad (GstElement * element,
296 GstPadTemplate * templ, const gchar * name);
297 static void gst_play_sink_release_request_pad (GstElement * element,
299 static gboolean gst_play_sink_send_event (GstElement * element,
301 static GstStateChangeReturn gst_play_sink_change_state (GstElement * element,
302 GstStateChange transition);
304 static void gst_play_sink_handle_message (GstBin * bin, GstMessage * message);
306 static void notify_volume_cb (GObject * object, GParamSpec * pspec,
307 GstPlaySink * playsink);
308 static void notify_mute_cb (GObject * object, GParamSpec * pspec,
309 GstPlaySink * playsink);
311 static void update_av_offset (GstPlaySink * playsink);
314 gst_play_marshal_BUFFER__BOXED (GClosure * closure,
315 GValue * return_value G_GNUC_UNUSED,
316 guint n_param_values,
317 const GValue * param_values,
318 gpointer invocation_hint G_GNUC_UNUSED, gpointer marshal_data)
320 typedef GstBuffer *(*GMarshalFunc_OBJECT__BOXED) (gpointer data1,
321 gpointer arg_1, gpointer data2);
322 register GMarshalFunc_OBJECT__BOXED callback;
323 register GCClosure *cc = (GCClosure *) closure;
324 register gpointer data1, data2;
326 g_return_if_fail (return_value != NULL);
327 g_return_if_fail (n_param_values == 2);
329 if (G_CCLOSURE_SWAP_DATA (closure)) {
330 data1 = closure->data;
331 data2 = g_value_peek_pointer (param_values + 0);
333 data1 = g_value_peek_pointer (param_values + 0);
334 data2 = closure->data;
337 (GMarshalFunc_OBJECT__BOXED) (marshal_data ? marshal_data : cc->callback);
339 v_return = callback (data1, g_value_get_boxed (param_values + 1), data2);
341 gst_value_take_buffer (return_value, v_return);
344 /* static guint gst_play_sink_signals[LAST_SIGNAL] = { 0 }; */
346 static void gst_play_sink_implements_interface_init (gpointer g_iface,
347 gpointer g_iface_data);
348 static void gst_play_sink_xoverlay_init (gpointer g_iface,
349 gpointer g_iface_data);
350 static void gst_play_sink_navigation_init (gpointer g_iface,
351 gpointer g_iface_data);
352 static void gst_play_sink_colorbalance_init (gpointer g_iface,
353 gpointer g_iface_data);
356 _do_init (GType type)
358 static const GInterfaceInfo impl_info = {
359 gst_play_sink_implements_interface_init,
362 static const GInterfaceInfo svol_info = {
365 static const GInterfaceInfo xov_info = {
366 gst_play_sink_xoverlay_init,
369 static const GInterfaceInfo nav_info = {
370 gst_play_sink_navigation_init,
373 static const GInterfaceInfo col_info = {
374 gst_play_sink_colorbalance_init,
378 g_type_add_interface_static (type, GST_TYPE_IMPLEMENTS_INTERFACE, &impl_info);
379 g_type_add_interface_static (type, GST_TYPE_STREAM_VOLUME, &svol_info);
380 g_type_add_interface_static (type, GST_TYPE_X_OVERLAY, &xov_info);
381 g_type_add_interface_static (type, GST_TYPE_NAVIGATION, &nav_info);
382 g_type_add_interface_static (type, GST_TYPE_COLOR_BALANCE, &col_info);
385 G_DEFINE_TYPE_WITH_CODE (GstPlaySink, gst_play_sink, GST_TYPE_BIN,
386 _do_init (g_define_type_id));
389 gst_play_sink_class_init (GstPlaySinkClass * klass)
391 GObjectClass *gobject_klass;
392 GstElementClass *gstelement_klass;
393 GstBinClass *gstbin_klass;
395 gobject_klass = (GObjectClass *) klass;
396 gstelement_klass = (GstElementClass *) klass;
397 gstbin_klass = (GstBinClass *) klass;
399 gobject_klass->dispose = gst_play_sink_dispose;
400 gobject_klass->finalize = gst_play_sink_finalize;
401 gobject_klass->set_property = gst_play_sink_set_property;
402 gobject_klass->get_property = gst_play_sink_get_property;
408 * Control the behaviour of playsink.
410 g_object_class_install_property (gobject_klass, PROP_FLAGS,
411 g_param_spec_flags ("flags", "Flags", "Flags to control behaviour",
412 GST_TYPE_PLAY_FLAGS, DEFAULT_FLAGS,
413 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
416 * GstPlaySink:volume:
418 * Get or set the current audio stream volume. 1.0 means 100%,
419 * 0.0 means mute. This uses a linear volume scale.
422 g_object_class_install_property (gobject_klass, PROP_VOLUME,
423 g_param_spec_double ("volume", "Volume", "The audio volume, 1.0=100%",
424 0.0, VOLUME_MAX_DOUBLE, 1.0,
425 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
426 g_object_class_install_property (gobject_klass, PROP_MUTE,
427 g_param_spec_boolean ("mute", "Mute",
428 "Mute the audio channel without changing the volume", FALSE,
429 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
430 g_object_class_install_property (gobject_klass, PROP_FONT_DESC,
431 g_param_spec_string ("subtitle-font-desc",
432 "Subtitle font description",
433 "Pango font description of font "
434 "to be used for subtitle rendering", NULL,
435 G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
436 g_object_class_install_property (gobject_klass, PROP_SUBTITLE_ENCODING,
437 g_param_spec_string ("subtitle-encoding", "subtitle encoding",
438 "Encoding to assume if input subtitles are not in UTF-8 encoding. "
439 "If not set, the GST_SUBTITLE_ENCODING environment variable will "
440 "be checked for an encoding to use. If that is not set either, "
441 "ISO-8859-15 will be assumed.", NULL,
442 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
443 g_object_class_install_property (gobject_klass, PROP_VIS_PLUGIN,
444 g_param_spec_object ("vis-plugin", "Vis plugin",
445 "the visualization element to use (NULL = default)",
446 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
450 * Get the currently rendered or prerolled frame in the video sink.
451 * The #GstCaps on the buffer will describe the format of the buffer.
455 g_object_class_install_property (gobject_klass, PROP_FRAME,
456 gst_param_spec_mini_object ("frame", "Frame",
457 "The last frame (NULL = no video available)",
458 GST_TYPE_BUFFER, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
460 * GstPlaySink:av-offset:
462 * Control the synchronisation offset between the audio and video streams.
463 * Positive values make the audio ahead of the video and negative values make
464 * the audio go behind the video.
468 g_object_class_install_property (gobject_klass, PROP_AV_OFFSET,
469 g_param_spec_int64 ("av-offset", "AV Offset",
470 "The synchronisation offset between audio and video in nanoseconds",
471 G_MININT64, G_MAXINT64, 0,
472 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
475 * GstPlaySink:video-sink:
477 * Set the used video sink element. NULL will use the default sink. playsink
478 * must be in %GST_STATE_NULL
482 g_object_class_install_property (gobject_klass, PROP_VIDEO_SINK,
483 g_param_spec_object ("video-sink", "Video Sink",
484 "the video output element to use (NULL = default sink)",
485 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
487 * GstPlaySink:audio-sink:
489 * Set the used audio 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_AUDIO_SINK,
495 g_param_spec_object ("audio-sink", "Audio Sink",
496 "the audio output element to use (NULL = default sink)",
497 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
500 * GstPlaySink:text-sink:
502 * Set the used text sink element. NULL will use the default sink. playsink
503 * must be in %GST_STATE_NULL
507 g_object_class_install_property (gobject_klass, PROP_TEXT_SINK,
508 g_param_spec_object ("text-sink", "Text sink",
509 "the text output element to use (NULL = default textoverlay)",
510 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
513 g_signal_new ("reconfigure", G_TYPE_FROM_CLASS (klass),
514 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstPlaySinkClass,
515 reconfigure), NULL, NULL, gst_marshal_BOOLEAN__VOID, G_TYPE_BOOLEAN,
518 * GstPlaySink::convert-frame
519 * @playsink: a #GstPlaySink
520 * @caps: the target format of the frame
522 * Action signal to retrieve the currently playing video frame in the format
523 * specified by @caps.
524 * If @caps is %NULL, no conversion will be performed and this function is
525 * equivalent to the #GstPlaySink::frame property.
527 * Returns: a #GstBuffer of the current video frame converted to #caps.
528 * The caps on the buffer will describe the final layout of the buffer data.
529 * %NULL is returned when no current buffer can be retrieved or when the
534 g_signal_new ("convert-frame", G_TYPE_FROM_CLASS (klass),
535 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
536 G_STRUCT_OFFSET (GstPlaySinkClass, convert_frame), NULL, NULL,
537 gst_play_marshal_BUFFER__BOXED, GST_TYPE_BUFFER, 1, GST_TYPE_CAPS);
539 gst_element_class_add_static_pad_template (gstelement_klass,
541 gst_element_class_add_static_pad_template (gstelement_klass, &audiotemplate);
542 gst_element_class_add_static_pad_template (gstelement_klass,
544 gst_element_class_add_static_pad_template (gstelement_klass, &videotemplate);
545 gst_element_class_add_static_pad_template (gstelement_klass, &texttemplate);
546 gst_element_class_set_details_simple (gstelement_klass, "Player Sink",
548 "Convenience sink for multiple streams",
549 "Wim Taymans <wim.taymans@gmail.com>");
551 gstelement_klass->change_state =
552 GST_DEBUG_FUNCPTR (gst_play_sink_change_state);
553 gstelement_klass->send_event = GST_DEBUG_FUNCPTR (gst_play_sink_send_event);
554 gstelement_klass->request_new_pad =
555 GST_DEBUG_FUNCPTR (gst_play_sink_request_new_pad);
556 gstelement_klass->release_pad =
557 GST_DEBUG_FUNCPTR (gst_play_sink_release_request_pad);
559 gstbin_klass->handle_message =
560 GST_DEBUG_FUNCPTR (gst_play_sink_handle_message);
562 klass->reconfigure = GST_DEBUG_FUNCPTR (gst_play_sink_reconfigure);
563 klass->convert_frame = GST_DEBUG_FUNCPTR (gst_play_sink_convert_frame);
567 gst_play_sink_init (GstPlaySink * playsink)
569 GstColorBalanceChannel *channel;
572 playsink->video_sink = NULL;
573 playsink->audio_sink = NULL;
574 playsink->visualisation = NULL;
575 playsink->text_sink = NULL;
576 playsink->volume = 1.0;
577 playsink->font_desc = NULL;
578 playsink->subtitle_encoding = NULL;
579 playsink->flags = DEFAULT_FLAGS;
581 playsink->stream_synchronizer =
582 g_object_new (GST_TYPE_STREAM_SYNCHRONIZER, NULL);
583 gst_bin_add (GST_BIN_CAST (playsink),
584 GST_ELEMENT_CAST (playsink->stream_synchronizer));
586 g_static_rec_mutex_init (&playsink->lock);
587 GST_OBJECT_FLAG_SET (playsink, GST_ELEMENT_IS_SINK);
590 GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
592 channel->label = g_strdup ("CONTRAST");
593 channel->min_value = -1000;
594 channel->max_value = 1000;
595 playsink->colorbalance_channels =
596 g_list_append (playsink->colorbalance_channels, channel);
597 playsink->colorbalance_values[0] = 0;
600 GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
602 channel->label = g_strdup ("BRIGHTNESS");
603 channel->min_value = -1000;
604 channel->max_value = 1000;
605 playsink->colorbalance_channels =
606 g_list_append (playsink->colorbalance_channels, channel);
607 playsink->colorbalance_values[1] = 0;
610 GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
612 channel->label = g_strdup ("HUE");
613 channel->min_value = -1000;
614 channel->max_value = 1000;
615 playsink->colorbalance_channels =
616 g_list_append (playsink->colorbalance_channels, channel);
617 playsink->colorbalance_values[2] = 0;
620 GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
622 channel->label = g_strdup ("SATURATION");
623 channel->min_value = -1000;
624 channel->max_value = 1000;
625 playsink->colorbalance_channels =
626 g_list_append (playsink->colorbalance_channels, channel);
627 playsink->colorbalance_values[3] = 0;
631 disconnect_chain (GstPlayAudioChain * chain, GstPlaySink * playsink)
635 g_signal_handlers_disconnect_by_func (chain->volume, notify_volume_cb,
638 g_signal_handlers_disconnect_by_func (chain->mute, notify_mute_cb,
644 free_chain (GstPlayChain * chain)
648 gst_object_unref (chain->bin);
654 gst_play_sink_dispose (GObject * object)
656 GstPlaySink *playsink;
658 playsink = GST_PLAY_SINK (object);
660 if (playsink->audio_sink != NULL) {
661 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
662 gst_object_unref (playsink->audio_sink);
663 playsink->audio_sink = NULL;
665 if (playsink->video_sink != NULL) {
666 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
667 gst_object_unref (playsink->video_sink);
668 playsink->video_sink = NULL;
670 if (playsink->visualisation != NULL) {
671 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
672 gst_object_unref (playsink->visualisation);
673 playsink->visualisation = NULL;
675 if (playsink->text_sink != NULL) {
676 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
677 gst_object_unref (playsink->text_sink);
678 playsink->text_sink = NULL;
681 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
682 playsink->videodeinterlacechain = NULL;
683 free_chain ((GstPlayChain *) playsink->videochain);
684 playsink->videochain = NULL;
685 free_chain ((GstPlayChain *) playsink->audiochain);
686 playsink->audiochain = NULL;
687 free_chain ((GstPlayChain *) playsink->vischain);
688 playsink->vischain = NULL;
689 free_chain ((GstPlayChain *) playsink->textchain);
690 playsink->textchain = NULL;
692 if (playsink->audio_tee_sink) {
693 gst_object_unref (playsink->audio_tee_sink);
694 playsink->audio_tee_sink = NULL;
697 if (playsink->audio_tee_vissrc) {
698 gst_element_release_request_pad (playsink->audio_tee,
699 playsink->audio_tee_vissrc);
700 gst_object_unref (playsink->audio_tee_vissrc);
701 playsink->audio_tee_vissrc = NULL;
704 if (playsink->audio_tee_asrc) {
705 gst_element_release_request_pad (playsink->audio_tee,
706 playsink->audio_tee_asrc);
707 gst_object_unref (playsink->audio_tee_asrc);
708 playsink->audio_tee_asrc = NULL;
711 g_free (playsink->font_desc);
712 playsink->font_desc = NULL;
714 g_free (playsink->subtitle_encoding);
715 playsink->subtitle_encoding = NULL;
717 playsink->stream_synchronizer = NULL;
719 g_list_foreach (playsink->colorbalance_channels, (GFunc) gst_object_unref,
721 g_list_free (playsink->colorbalance_channels);
722 playsink->colorbalance_channels = NULL;
724 G_OBJECT_CLASS (gst_play_sink_parent_class)->dispose (object);
728 gst_play_sink_finalize (GObject * object)
730 GstPlaySink *playsink;
732 playsink = GST_PLAY_SINK (object);
734 g_static_rec_mutex_free (&playsink->lock);
736 G_OBJECT_CLASS (gst_play_sink_parent_class)->finalize (object);
740 gst_play_sink_set_sink (GstPlaySink * playsink, GstPlaySinkType type,
743 GstElement **elem = NULL, *old = NULL;
745 GST_LOG ("Setting sink %" GST_PTR_FORMAT " as sink type %d", sink, type);
747 GST_PLAY_SINK_LOCK (playsink);
749 case GST_PLAY_SINK_TYPE_AUDIO:
750 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
751 elem = &playsink->audio_sink;
753 case GST_PLAY_SINK_TYPE_VIDEO:
754 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
755 elem = &playsink->video_sink;
757 case GST_PLAY_SINK_TYPE_TEXT:
758 elem = &playsink->text_sink;
766 gst_object_ref (sink);
769 GST_PLAY_SINK_UNLOCK (playsink);
773 gst_element_set_state (old, GST_STATE_NULL);
774 gst_object_unref (old);
779 gst_play_sink_get_sink (GstPlaySink * playsink, GstPlaySinkType type)
781 GstElement *result = NULL;
782 GstElement *elem = NULL, *chainp = NULL;
784 GST_PLAY_SINK_LOCK (playsink);
786 case GST_PLAY_SINK_TYPE_AUDIO:
787 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
789 GstPlayAudioChain *chain;
790 if ((chain = (GstPlayAudioChain *) playsink->audiochain))
791 chainp = chain->sink;
792 elem = playsink->audio_sink;
795 case GST_PLAY_SINK_TYPE_VIDEO:
796 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
798 GstPlayVideoChain *chain;
799 if ((chain = (GstPlayVideoChain *) playsink->videochain))
800 chainp = chain->sink;
801 elem = playsink->video_sink;
804 case GST_PLAY_SINK_TYPE_TEXT:
806 GstPlayTextChain *chain;
807 if ((chain = (GstPlayTextChain *) playsink->textchain))
808 chainp = chain->sink;
809 elem = playsink->text_sink;
816 /* we have an active chain with a sink, get the sink */
817 result = gst_object_ref (chainp);
819 /* nothing found, return last configured sink */
820 if (result == NULL && elem)
821 result = gst_object_ref (elem);
822 GST_PLAY_SINK_UNLOCK (playsink);
828 gst_play_sink_vis_unblocked (GstPad * tee_pad, gboolean blocked,
831 GstPlaySink *playsink;
833 playsink = GST_PLAY_SINK (user_data);
834 /* nothing to do here, we need a dummy callback here to make the async call
836 GST_DEBUG_OBJECT (playsink, "vis pad unblocked");
840 gst_play_sink_vis_blocked (GstPad * tee_pad, gboolean blocked,
843 GstPlaySink *playsink;
844 GstPlayVisChain *chain;
846 playsink = GST_PLAY_SINK (user_data);
848 GST_PLAY_SINK_LOCK (playsink);
849 GST_DEBUG_OBJECT (playsink, "vis pad blocked");
850 /* now try to change the plugin in the running vis chain */
851 if (!(chain = (GstPlayVisChain *) playsink->vischain))
854 /* unlink the old plugin and unghost the pad */
855 gst_pad_unlink (chain->blockpad, chain->vissinkpad);
856 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad), NULL);
858 /* set the old plugin to NULL and remove */
859 gst_element_set_state (chain->vis, GST_STATE_NULL);
860 gst_bin_remove (GST_BIN_CAST (chain->chain.bin), chain->vis);
862 /* add new plugin and set state to playing */
863 chain->vis = playsink->visualisation;
864 gst_bin_add (GST_BIN_CAST (chain->chain.bin), chain->vis);
865 gst_element_set_state (chain->vis, GST_STATE_PLAYING);
868 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
869 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
872 gst_pad_link_full (chain->blockpad, chain->vissinkpad,
873 GST_PAD_LINK_CHECK_NOTHING);
874 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad),
878 /* Unblock the pad */
879 gst_pad_set_blocked_async (tee_pad, FALSE, gst_play_sink_vis_unblocked,
881 GST_PLAY_SINK_UNLOCK (playsink);
885 gst_play_sink_set_vis_plugin (GstPlaySink * playsink, GstElement * vis)
887 GstPlayVisChain *chain;
889 /* setting NULL means creating the default vis plugin */
891 vis = gst_element_factory_make ("goom", "vis");
893 /* simply return if we don't have a vis plugin here */
897 GST_PLAY_SINK_LOCK (playsink);
898 /* first store the new vis */
899 if (playsink->visualisation)
900 gst_object_unref (playsink->visualisation);
902 gst_object_ref_sink (vis);
903 playsink->visualisation = vis;
905 /* now try to change the plugin in the running vis chain, if we have no chain,
906 * we don't bother, any future vis chain will be created with the new vis
908 if (!(chain = (GstPlayVisChain *) playsink->vischain))
911 /* block the pad, the next time the callback is called we can change the
912 * visualisation. It's possible that this never happens or that the pad was
913 * already blocked. If the callback never happens, we don't have new data so
914 * we don't need the new vis plugin. If the pad was already blocked, the
915 * function returns FALSE but the previous pad block will do the right thing
917 GST_DEBUG_OBJECT (playsink, "blocking vis pad");
918 gst_pad_set_blocked_async (chain->blockpad, TRUE, gst_play_sink_vis_blocked,
921 GST_PLAY_SINK_UNLOCK (playsink);
927 gst_play_sink_get_vis_plugin (GstPlaySink * playsink)
929 GstElement *result = NULL;
930 GstPlayVisChain *chain;
932 GST_PLAY_SINK_LOCK (playsink);
933 if ((chain = (GstPlayVisChain *) playsink->vischain)) {
934 /* we have an active chain, get the sink */
936 result = gst_object_ref (chain->vis);
938 /* nothing found, return last configured sink */
939 if (result == NULL && playsink->visualisation)
940 result = gst_object_ref (playsink->visualisation);
941 GST_PLAY_SINK_UNLOCK (playsink);
947 gst_play_sink_set_volume (GstPlaySink * playsink, gdouble volume)
949 GstPlayAudioChain *chain;
951 GST_PLAY_SINK_LOCK (playsink);
952 playsink->volume = volume;
953 chain = (GstPlayAudioChain *) playsink->audiochain;
954 if (chain && chain->volume) {
955 GST_LOG_OBJECT (playsink, "elements: volume=%" GST_PTR_FORMAT ", mute=%"
956 GST_PTR_FORMAT "; new volume=%.03f, mute=%d", chain->volume,
957 chain->mute, volume, playsink->mute);
958 /* if there is a mute element or we are not muted, set the volume */
959 if (chain->mute || !playsink->mute)
960 g_object_set (chain->volume, "volume", volume, NULL);
962 GST_LOG_OBJECT (playsink, "no volume element");
963 playsink->volume_changed = TRUE;
965 GST_PLAY_SINK_UNLOCK (playsink);
969 gst_play_sink_get_volume (GstPlaySink * playsink)
972 GstPlayAudioChain *chain;
974 GST_PLAY_SINK_LOCK (playsink);
975 chain = (GstPlayAudioChain *) playsink->audiochain;
976 result = playsink->volume;
977 if (chain && chain->volume) {
978 if (chain->mute || !playsink->mute) {
979 g_object_get (chain->volume, "volume", &result, NULL);
980 playsink->volume = result;
983 GST_PLAY_SINK_UNLOCK (playsink);
989 gst_play_sink_set_mute (GstPlaySink * playsink, gboolean mute)
991 GstPlayAudioChain *chain;
993 GST_PLAY_SINK_LOCK (playsink);
994 playsink->mute = mute;
995 chain = (GstPlayAudioChain *) playsink->audiochain;
998 g_object_set (chain->mute, "mute", mute, NULL);
999 } else if (chain->volume) {
1001 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
1003 g_object_set (chain->volume, "volume", (gdouble) playsink->volume,
1007 playsink->mute_changed = TRUE;
1009 GST_PLAY_SINK_UNLOCK (playsink);
1013 gst_play_sink_get_mute (GstPlaySink * playsink)
1016 GstPlayAudioChain *chain;
1018 GST_PLAY_SINK_LOCK (playsink);
1019 chain = (GstPlayAudioChain *) playsink->audiochain;
1020 if (chain && chain->mute) {
1021 g_object_get (chain->mute, "mute", &result, NULL);
1022 playsink->mute = result;
1024 result = playsink->mute;
1026 GST_PLAY_SINK_UNLOCK (playsink);
1032 post_missing_element_message (GstPlaySink * playsink, const gchar * name)
1036 msg = gst_missing_element_message_new (GST_ELEMENT_CAST (playsink), name);
1037 gst_element_post_message (GST_ELEMENT_CAST (playsink), msg);
1041 add_chain (GstPlayChain * chain, gboolean add)
1043 if (chain->added == add)
1047 gst_bin_add (GST_BIN_CAST (chain->playsink), chain->bin);
1049 gst_bin_remove (GST_BIN_CAST (chain->playsink), chain->bin);
1050 /* we don't want to lose our sink status */
1051 GST_OBJECT_FLAG_SET (chain->playsink, GST_ELEMENT_IS_SINK);
1060 activate_chain (GstPlayChain * chain, gboolean activate)
1064 if (chain->activated == activate)
1067 GST_OBJECT_LOCK (chain->playsink);
1068 state = GST_STATE_TARGET (chain->playsink);
1069 GST_OBJECT_UNLOCK (chain->playsink);
1072 gst_element_set_state (chain->bin, state);
1074 gst_element_set_state (chain->bin, GST_STATE_NULL);
1076 chain->activated = activate;
1082 element_is_sink (GstElement * element)
1086 GST_OBJECT_LOCK (element);
1087 is_sink = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_IS_SINK);
1088 GST_OBJECT_UNLOCK (element);
1090 GST_DEBUG_OBJECT (element, "is a sink: %s", (is_sink) ? "yes" : "no");
1095 element_has_property (GstElement * element, const gchar * pname, GType type)
1099 pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (element), pname);
1101 if (pspec == NULL) {
1102 GST_DEBUG_OBJECT (element, "no %s property", pname);
1106 if (type == G_TYPE_INVALID || type == pspec->value_type ||
1107 g_type_is_a (pspec->value_type, type)) {
1108 GST_DEBUG_OBJECT (element, "has %s property of type %s", pname,
1109 (type == G_TYPE_INVALID) ? "any type" : g_type_name (type));
1113 GST_WARNING_OBJECT (element, "has %s property, but property is of type %s "
1114 "and we expected it to be of type %s", pname,
1115 g_type_name (pspec->value_type), g_type_name (type));
1122 const gchar *prop_name;
1125 } FindPropertyHelper;
1128 find_property (GstElement * element, FindPropertyHelper * helper)
1130 if (helper->need_sink && !element_is_sink (element)) {
1131 gst_object_unref (element);
1135 if (!element_has_property (element, helper->prop_name, helper->prop_type)) {
1136 gst_object_unref (element);
1140 GST_INFO_OBJECT (element, "found %s with %s property", helper->prop_name,
1141 (helper->need_sink) ? "sink" : "element");
1142 return 0; /* keep it */
1145 /* FIXME: why not move these functions into core? */
1146 /* find a sink in the hierarchy with a property named @name. This function does
1147 * not increase the refcount of the returned object and thus remains valid as
1148 * long as the bin is valid. */
1150 gst_play_sink_find_property_sinks (GstPlaySink * playsink, GstElement * obj,
1151 const gchar * name, GType expected_type)
1153 GstElement *result = NULL;
1156 if (element_has_property (obj, name, expected_type)) {
1158 } else if (GST_IS_BIN (obj)) {
1159 FindPropertyHelper helper = { name, expected_type, TRUE };
1161 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1162 result = gst_iterator_find_custom (it,
1163 (GCompareFunc) find_property, &helper);
1164 gst_iterator_free (it);
1165 /* we don't need the extra ref */
1167 gst_object_unref (result);
1172 /* find an object in the hierarchy with a property named @name */
1174 gst_play_sink_find_property (GstPlaySink * playsink, GstElement * obj,
1175 const gchar * name, GType expected_type)
1177 GstElement *result = NULL;
1180 if (GST_IS_BIN (obj)) {
1181 FindPropertyHelper helper = { name, expected_type, FALSE };
1183 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1184 result = gst_iterator_find_custom (it,
1185 (GCompareFunc) find_property, &helper);
1186 gst_iterator_free (it);
1188 if (element_has_property (obj, name, expected_type)) {
1190 gst_object_ref (obj);
1197 do_async_start (GstPlaySink * playsink)
1199 GstMessage *message;
1201 if (!playsink->need_async_start) {
1202 GST_INFO_OBJECT (playsink, "no async_start needed");
1206 playsink->async_pending = TRUE;
1208 GST_INFO_OBJECT (playsink, "Sending async_start message");
1209 message = gst_message_new_async_start (GST_OBJECT_CAST (playsink), FALSE);
1210 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1211 (playsink), message);
1215 do_async_done (GstPlaySink * playsink)
1217 GstMessage *message;
1219 if (playsink->async_pending) {
1220 GST_INFO_OBJECT (playsink, "Sending async_done message");
1221 message = gst_message_new_async_done (GST_OBJECT_CAST (playsink));
1222 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1223 (playsink), message);
1225 playsink->async_pending = FALSE;
1228 playsink->need_async_start = FALSE;
1231 /* try to change the state of an element. This function returns the element when
1232 * the state change could be performed. When this function returns NULL an error
1233 * occured and the element is unreffed if @unref is TRUE. */
1235 try_element (GstPlaySink * playsink, GstElement * element, gboolean unref)
1237 GstStateChangeReturn ret;
1240 ret = gst_element_set_state (element, GST_STATE_READY);
1241 if (ret == GST_STATE_CHANGE_FAILURE) {
1242 GST_DEBUG_OBJECT (playsink, "failed state change..");
1243 gst_element_set_state (element, GST_STATE_NULL);
1245 gst_object_unref (element);
1252 /* make the element (bin) that contains the elements needed to perform
1253 * video display. Only used for *raw* video streams.
1255 * +------------------------------------------------------------+
1257 * | +-------+ +----------+ +----------+ +---------+ |
1258 * | | queue | |colorspace| |videoscale| |videosink| |
1259 * | +-sink src-sink src-sink src-sink | |
1260 * | | +-------+ +----------+ +----------+ +---------+ |
1262 * +------------------------------------------------------------+
1265 static GstPlayVideoDeinterlaceChain *
1266 gen_video_deinterlace_chain (GstPlaySink * playsink)
1268 GstPlayVideoDeinterlaceChain *chain;
1271 GstElement *head = NULL, *prev = NULL;
1273 chain = g_new0 (GstPlayVideoDeinterlaceChain, 1);
1274 chain->chain.playsink = playsink;
1276 GST_DEBUG_OBJECT (playsink, "making video deinterlace chain %p", chain);
1278 /* create a bin to hold objects, as we create them we add them to this bin so
1279 * that when something goes wrong we only need to unref the bin */
1280 chain->chain.bin = gst_bin_new ("vdbin");
1281 bin = GST_BIN_CAST (chain->chain.bin);
1282 gst_object_ref_sink (bin);
1284 GST_DEBUG_OBJECT (playsink, "creating " COLORSPACE);
1285 chain->conv = gst_element_factory_make (COLORSPACE, "vdconv");
1286 if (chain->conv == NULL) {
1287 post_missing_element_message (playsink, COLORSPACE);
1288 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1289 (_("Missing element '%s' - check your GStreamer installation."),
1290 COLORSPACE), ("video rendering might fail"));
1292 gst_bin_add (bin, chain->conv);
1297 GST_DEBUG_OBJECT (playsink, "creating deinterlace");
1298 chain->deinterlace = gst_element_factory_make ("deinterlace", "deinterlace");
1299 if (chain->deinterlace == NULL) {
1300 post_missing_element_message (playsink, "deinterlace");
1301 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1302 (_("Missing element '%s' - check your GStreamer installation."),
1303 "deinterlace"), ("deinterlacing won't work"));
1305 gst_bin_add (bin, chain->deinterlace);
1307 if (!gst_element_link_pads_full (prev, "src", chain->deinterlace, "sink",
1308 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1311 head = chain->deinterlace;
1313 prev = chain->deinterlace;
1317 pad = gst_element_get_static_pad (head, "sink");
1318 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1319 gst_object_unref (pad);
1321 chain->sinkpad = gst_ghost_pad_new_no_target ("sink", GST_PAD_SINK);
1325 pad = gst_element_get_static_pad (prev, "src");
1326 chain->srcpad = gst_ghost_pad_new ("src", pad);
1327 gst_object_unref (pad);
1329 chain->srcpad = gst_ghost_pad_new ("src", chain->sinkpad);
1332 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1333 gst_element_add_pad (chain->chain.bin, chain->srcpad);
1339 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1340 (NULL), ("Failed to configure the video deinterlace chain."));
1341 free_chain ((GstPlayChain *) chain);
1347 is_valid_color_balance_element (GstElement * element)
1349 GstColorBalance *bal = GST_COLOR_BALANCE (element);
1350 gboolean have_brightness = FALSE;
1351 gboolean have_contrast = FALSE;
1352 gboolean have_hue = FALSE;
1353 gboolean have_saturation = FALSE;
1354 const GList *channels, *l;
1356 channels = gst_color_balance_list_channels (bal);
1357 for (l = channels; l; l = l->next) {
1358 GstColorBalanceChannel *ch = l->data;
1360 if (g_strrstr (ch->label, "BRIGHTNESS"))
1361 have_brightness = TRUE;
1362 else if (g_strrstr (ch->label, "CONTRAST"))
1363 have_contrast = TRUE;
1364 else if (g_strrstr (ch->label, "HUE"))
1366 else if (g_strrstr (ch->label, "SATURATION"))
1367 have_saturation = TRUE;
1370 return have_brightness && have_contrast && have_hue && have_saturation;
1374 iterate_color_balance_elements (gpointer data, gpointer user_data)
1376 gboolean valid = is_valid_color_balance_element (data);
1377 GstColorBalance **cb_out = user_data;
1381 && gst_color_balance_get_balance_type (*cb_out) ==
1382 GST_COLOR_BALANCE_SOFTWARE) {
1383 gst_object_unref (*cb_out);
1384 *cb_out = GST_COLOR_BALANCE (gst_object_ref (data));
1385 } else if (!*cb_out) {
1386 *cb_out = GST_COLOR_BALANCE (gst_object_ref (data));
1390 gst_object_unref (data);
1393 static GstColorBalance *
1394 find_color_balance_element (GstElement * element)
1397 GstColorBalance *cb = NULL;
1399 if (GST_IS_COLOR_BALANCE (element)
1400 && is_valid_color_balance_element (element))
1401 return GST_COLOR_BALANCE (gst_object_ref (element));
1402 else if (!GST_IS_BIN (element))
1405 it = gst_bin_iterate_all_by_interface (GST_BIN (element),
1406 GST_TYPE_COLOR_BALANCE);
1407 while (gst_iterator_foreach (it, iterate_color_balance_elements,
1408 &cb) == GST_ITERATOR_RESYNC)
1409 gst_iterator_resync (it);
1410 gst_iterator_free (it);
1416 colorbalance_value_changed_cb (GstColorBalance * balance,
1417 GstColorBalanceChannel * channel, gint value, GstPlaySink * playsink)
1422 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
1423 GstColorBalanceChannel *proxy = l->data;
1425 if (g_strrstr (channel->label, proxy->label)) {
1428 /* Convert to [0, 1] range */
1431 (gdouble) channel->min_value) / ((gdouble) channel->max_value -
1432 (gdouble) channel->min_value);
1433 /* Convert to proxy range */
1435 proxy->min_value + new_val * ((gdouble) proxy->max_value -
1436 (gdouble) proxy->min_value);
1437 playsink->colorbalance_values[i] = (gint) (0.5 + new_val);
1439 gst_color_balance_value_changed (GST_COLOR_BALANCE (playsink), proxy,
1440 playsink->colorbalance_values[i]);
1447 update_colorbalance (GstPlaySink * playsink)
1449 GstColorBalance *balance = NULL;
1453 GST_OBJECT_LOCK (playsink);
1454 if (playsink->colorbalance_element) {
1456 GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
1458 GST_OBJECT_UNLOCK (playsink);
1462 g_signal_handlers_block_by_func (balance,
1463 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1465 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
1466 GstColorBalanceChannel *proxy = l->data;
1467 GstColorBalanceChannel *channel = NULL;
1468 const GList *channels, *k;
1470 channels = gst_color_balance_list_channels (balance);
1471 for (k = channels; k; k = k->next) {
1472 GstColorBalanceChannel *tmp = k->data;
1474 if (g_strrstr (tmp->label, proxy->label)) {
1482 gst_color_balance_set_value (balance, channel,
1483 playsink->colorbalance_values[i]);
1486 g_signal_handlers_unblock_by_func (balance,
1487 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1490 /* make the element (bin) that contains the elements needed to perform
1493 * +------------------------------------------------------------+
1495 * | +-------+ +----------+ +----------+ +---------+ |
1496 * | | queue | |colorspace| |videoscale| |videosink| |
1497 * | +-sink src-sink src-sink src-sink | |
1498 * | | +-------+ +----------+ +----------+ +---------+ |
1500 * +------------------------------------------------------------+
1503 static GstPlayVideoChain *
1504 gen_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1506 GstPlayVideoChain *chain;
1509 GstElement *head = NULL, *prev = NULL, *elem = NULL;
1511 chain = g_new0 (GstPlayVideoChain, 1);
1512 chain->chain.playsink = playsink;
1513 chain->chain.raw = raw;
1515 GST_DEBUG_OBJECT (playsink, "making video chain %p", chain);
1517 if (playsink->video_sink) {
1518 GST_DEBUG_OBJECT (playsink, "trying configured videosink");
1519 chain->sink = try_element (playsink, playsink->video_sink, FALSE);
1521 /* only try fallback if no specific sink was chosen */
1522 if (chain->sink == NULL) {
1523 GST_DEBUG_OBJECT (playsink, "trying autovideosink");
1524 elem = gst_element_factory_make ("autovideosink", "videosink");
1525 chain->sink = try_element (playsink, elem, TRUE);
1527 if (chain->sink == NULL) {
1528 /* if default sink from config.h is different then try it too */
1529 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1530 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_VIDEOSINK);
1531 elem = gst_element_factory_make (DEFAULT_VIDEOSINK, "videosink");
1532 chain->sink = try_element (playsink, elem, TRUE);
1536 playsink->video_sink = gst_object_ref (chain->sink);
1538 if (chain->sink == NULL)
1542 /* if we can disable async behaviour of the sink, we can avoid adding a
1543 * queue for the audio chain. */
1545 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1548 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1549 async, GST_ELEMENT_NAME (elem));
1550 g_object_set (elem, "async", async, NULL);
1551 chain->async = async;
1553 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1554 chain->async = TRUE;
1557 /* Make sure the aspect ratio is kept */
1559 gst_play_sink_find_property_sinks (playsink, chain->sink,
1560 "force-aspect-ratio", G_TYPE_BOOLEAN);
1562 g_object_set (elem, "force-aspect-ratio", TRUE, NULL);
1564 /* find ts-offset element */
1565 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1566 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1569 /* create a bin to hold objects, as we create them we add them to this bin so
1570 * that when something goes wrong we only need to unref the bin */
1571 chain->chain.bin = gst_bin_new ("vbin");
1572 bin = GST_BIN_CAST (chain->chain.bin);
1573 gst_object_ref_sink (bin);
1574 gst_bin_add (bin, chain->sink);
1576 /* Get the XOverlay element */
1578 GstXOverlay *xoverlay = NULL;
1580 GST_OBJECT_LOCK (playsink);
1581 if (playsink->xoverlay_element)
1582 gst_object_unref (playsink->xoverlay_element);
1583 playsink->xoverlay_element =
1584 GST_X_OVERLAY (gst_bin_get_by_interface (GST_BIN (chain->chain.bin),
1585 GST_TYPE_X_OVERLAY));
1586 if (playsink->xoverlay_element)
1587 xoverlay = GST_X_OVERLAY (gst_object_ref (playsink->xoverlay_element));
1588 GST_OBJECT_UNLOCK (playsink);
1591 if (playsink->xoverlay_handle_set)
1592 gst_x_overlay_set_window_handle (xoverlay, playsink->xoverlay_handle);
1593 if (playsink->xoverlay_handle_events_set)
1594 gst_x_overlay_handle_events (xoverlay,
1595 playsink->xoverlay_handle_events);
1596 if (playsink->xoverlay_render_rectangle_set)
1597 gst_x_overlay_set_render_rectangle (xoverlay,
1598 playsink->xoverlay_x, playsink->xoverlay_y,
1599 playsink->xoverlay_width, playsink->xoverlay_height);
1600 gst_object_unref (xoverlay);
1604 /* decouple decoder from sink, this improves playback quite a lot since the
1605 * decoder can continue while the sink blocks for synchronisation. We don't
1606 * need a lot of buffers as this consumes a lot of memory and we don't want
1607 * too little because else we would be context switching too quickly. */
1608 chain->queue = gst_element_factory_make ("queue", "vqueue");
1609 if (chain->queue == NULL) {
1610 post_missing_element_message (playsink, "queue");
1611 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1612 (_("Missing element '%s' - check your GStreamer installation."),
1613 "queue"), ("video rendering might be suboptimal"));
1617 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1618 "max-size-bytes", 0, "max-size-time", (gint64) 0, "silent", TRUE, NULL);
1619 gst_bin_add (bin, chain->queue);
1620 head = prev = chain->queue;
1623 GST_OBJECT_LOCK (playsink);
1624 if (playsink->colorbalance_element) {
1625 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
1626 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1627 gst_object_unref (playsink->colorbalance_element);
1629 playsink->colorbalance_element = find_color_balance_element (chain->sink);
1630 GST_OBJECT_UNLOCK (playsink);
1632 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)
1633 || (!playsink->colorbalance_element
1634 && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE))) {
1635 gboolean use_converters = !(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO);
1636 gboolean use_balance = !playsink->colorbalance_element
1637 && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE);
1639 GST_DEBUG_OBJECT (playsink, "creating videoconverter");
1641 g_object_new (GST_TYPE_PLAY_SINK_VIDEO_CONVERT, "name", "vconv",
1642 "use-converters", use_converters, "use-balance", use_balance, NULL);
1644 GST_OBJECT_LOCK (playsink);
1645 if (use_balance && GST_PLAY_SINK_VIDEO_CONVERT (chain->conv)->balance)
1646 playsink->colorbalance_element =
1647 GST_COLOR_BALANCE (gst_object_ref (GST_PLAY_SINK_VIDEO_CONVERT
1648 (chain->conv)->balance));
1649 GST_OBJECT_UNLOCK (playsink);
1651 gst_bin_add (bin, chain->conv);
1653 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
1654 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1662 update_colorbalance (playsink);
1665 GST_DEBUG_OBJECT (playsink, "linking to sink");
1666 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
1667 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1671 pad = gst_element_get_static_pad (head, "sink");
1672 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1673 gst_object_unref (pad);
1675 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1682 if (!elem && !playsink->video_sink) {
1683 post_missing_element_message (playsink, "autovideosink");
1684 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1685 post_missing_element_message (playsink, DEFAULT_VIDEOSINK);
1686 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1687 (_("Both autovideosink and %s elements are missing."),
1688 DEFAULT_VIDEOSINK), (NULL));
1690 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1691 (_("The autovideosink element is missing.")), (NULL));
1694 if (playsink->video_sink) {
1695 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1696 (_("Configured videosink %s is not working."),
1697 GST_ELEMENT_NAME (playsink->video_sink)), (NULL));
1698 } else if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1699 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1700 (_("Both autovideosink and %s elements are not working."),
1701 DEFAULT_VIDEOSINK), (NULL));
1703 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1704 (_("The autovideosink element is not working.")), (NULL));
1707 free_chain ((GstPlayChain *) chain);
1713 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1714 (NULL), ("Failed to configure the video sink."));
1715 /* checking sink made it READY */
1716 gst_element_set_state (chain->sink, GST_STATE_NULL);
1717 /* Remove chain from the bin to allow reuse later */
1718 gst_bin_remove (bin, chain->sink);
1719 free_chain ((GstPlayChain *) chain);
1725 setup_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1728 GstPlayVideoChain *chain;
1729 GstStateChangeReturn ret;
1731 chain = playsink->videochain;
1733 chain->chain.raw = raw;
1735 /* if the chain was active we don't do anything */
1736 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
1739 /* try to set the sink element to READY again */
1740 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
1741 if (ret == GST_STATE_CHANGE_FAILURE)
1744 /* Get the XOverlay element */
1746 GstXOverlay *xoverlay = NULL;
1748 GST_OBJECT_LOCK (playsink);
1749 if (playsink->xoverlay_element)
1750 gst_object_unref (playsink->xoverlay_element);
1751 playsink->xoverlay_element =
1752 GST_X_OVERLAY (gst_bin_get_by_interface (GST_BIN (chain->chain.bin),
1753 GST_TYPE_X_OVERLAY));
1754 if (playsink->xoverlay_element)
1755 xoverlay = GST_X_OVERLAY (gst_object_ref (playsink->xoverlay_element));
1756 GST_OBJECT_UNLOCK (playsink);
1759 if (playsink->xoverlay_handle_set)
1760 gst_x_overlay_set_window_handle (xoverlay, playsink->xoverlay_handle);
1761 if (playsink->xoverlay_handle_events_set)
1762 gst_x_overlay_handle_events (xoverlay,
1763 playsink->xoverlay_handle_events);
1764 if (playsink->xoverlay_render_rectangle_set)
1765 gst_x_overlay_set_render_rectangle (xoverlay,
1766 playsink->xoverlay_x, playsink->xoverlay_y,
1767 playsink->xoverlay_width, playsink->xoverlay_height);
1768 gst_object_unref (xoverlay);
1772 /* find ts-offset element */
1773 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1774 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1777 /* if we can disable async behaviour of the sink, we can avoid adding a
1778 * queue for the audio chain. */
1780 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1783 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1784 async, GST_ELEMENT_NAME (elem));
1785 g_object_set (elem, "async", async, NULL);
1786 chain->async = async;
1788 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1789 chain->async = TRUE;
1792 /* Make sure the aspect ratio is kept */
1794 gst_play_sink_find_property_sinks (playsink, chain->sink,
1795 "force-aspect-ratio", G_TYPE_BOOLEAN);
1797 g_object_set (elem, "force-aspect-ratio", TRUE, NULL);
1799 GST_OBJECT_LOCK (playsink);
1800 if (playsink->colorbalance_element) {
1801 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
1802 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1803 gst_object_unref (playsink->colorbalance_element);
1805 playsink->colorbalance_element = find_color_balance_element (chain->sink);
1806 GST_OBJECT_UNLOCK (playsink);
1809 gboolean use_balance = !playsink->colorbalance_element
1810 && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE);
1812 g_object_set (chain->conv, "use-balance", use_balance, NULL);
1814 GST_OBJECT_LOCK (playsink);
1815 if (use_balance && GST_PLAY_SINK_VIDEO_CONVERT (chain->conv)->balance)
1816 playsink->colorbalance_element =
1817 GST_COLOR_BALANCE (gst_object_ref (GST_PLAY_SINK_VIDEO_CONVERT
1818 (chain->conv)->balance));
1819 GST_OBJECT_UNLOCK (playsink);
1822 update_colorbalance (playsink);
1827 /* make an element for playback of video with subtitles embedded.
1828 * Only used for *raw* video streams.
1830 * +--------------------------------------------+
1832 * | +--------+ +-----------------+ |
1833 * | | queue | | subtitleoverlay | |
1834 * video--src sink---video_sink | |
1835 * | +--------+ | src--src
1836 * text------------------text_sink | |
1837 * | +-----------------+ |
1838 * +--------------------------------------------+
1841 static GstPlayTextChain *
1842 gen_text_chain (GstPlaySink * playsink)
1844 GstPlayTextChain *chain;
1847 GstPad *videosinkpad, *textsinkpad, *srcpad;
1849 chain = g_new0 (GstPlayTextChain, 1);
1850 chain->chain.playsink = playsink;
1852 GST_DEBUG_OBJECT (playsink, "making text chain %p", chain);
1854 chain->chain.bin = gst_bin_new ("tbin");
1855 bin = GST_BIN_CAST (chain->chain.bin);
1856 gst_object_ref_sink (bin);
1858 videosinkpad = textsinkpad = srcpad = NULL;
1860 /* first try to hook the text pad to the custom sink */
1861 if (playsink->text_sink) {
1862 GST_DEBUG_OBJECT (playsink, "trying configured textsink");
1863 chain->sink = try_element (playsink, playsink->text_sink, FALSE);
1866 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1869 /* make sure the sparse subtitles don't participate in the preroll */
1870 g_object_set (elem, "async", FALSE, NULL);
1871 GST_DEBUG_OBJECT (playsink, "adding custom text sink");
1872 gst_bin_add (bin, chain->sink);
1873 /* NOTE streamsynchronizer needs streams decoupled */
1874 /* make a little queue */
1875 chain->queue = gst_element_factory_make ("queue", "subqueue");
1876 if (chain->queue == NULL) {
1877 post_missing_element_message (playsink, "queue");
1878 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1879 (_("Missing element '%s' - check your GStreamer installation."),
1880 "queue"), ("rendering might be suboptimal"));
1882 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1883 "max-size-bytes", 0, "max-size-time", (gint64) 0,
1884 "silent", TRUE, NULL);
1885 gst_bin_add (bin, chain->queue);
1887 /* we have a custom sink, this will be our textsinkpad */
1888 if (gst_element_link_pads_full (chain->queue, "src", chain->sink,
1889 "sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
1890 /* we're all fine now and we can add the sink to the chain */
1891 GST_DEBUG_OBJECT (playsink, "using custom text sink");
1892 textsinkpad = gst_element_get_static_pad (chain->queue, "sink");
1894 GST_WARNING_OBJECT (playsink,
1895 "can't find a sink pad on custom text sink");
1896 gst_bin_remove (bin, chain->sink);
1897 gst_bin_remove (bin, chain->queue);
1899 chain->queue = NULL;
1901 /* try to set sync to true but it's no biggie when we can't */
1902 if (chain->sink && (elem =
1903 gst_play_sink_find_property_sinks (playsink, chain->sink,
1904 "sync", G_TYPE_BOOLEAN)))
1905 g_object_set (elem, "sync", TRUE, NULL);
1908 gst_bin_remove (bin, chain->sink);
1910 GST_WARNING_OBJECT (playsink,
1911 "can't find async property in custom text sink");
1914 if (textsinkpad == NULL) {
1915 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1916 (_("Custom text sink element is not usable.")),
1917 ("fallback to default textoverlay"));
1921 if (textsinkpad == NULL) {
1922 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
1923 /* make a little queue */
1924 chain->queue = gst_element_factory_make ("queue", "vqueue");
1925 if (chain->queue == NULL) {
1926 post_missing_element_message (playsink, "queue");
1927 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1928 (_("Missing element '%s' - check your GStreamer installation."),
1929 "queue"), ("video rendering might be suboptimal"));
1931 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1932 "max-size-bytes", 0, "max-size-time", (gint64) 0,
1933 "silent", TRUE, NULL);
1934 gst_bin_add (bin, chain->queue);
1935 videosinkpad = gst_element_get_static_pad (chain->queue, "sink");
1939 gst_element_factory_make ("subtitleoverlay", "suboverlay");
1940 if (chain->overlay == NULL) {
1941 post_missing_element_message (playsink, "subtitleoverlay");
1942 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1943 (_("Missing element '%s' - check your GStreamer installation."),
1944 "subtitleoverlay"), ("subtitle rendering disabled"));
1946 GstElement *element;
1948 gst_bin_add (bin, chain->overlay);
1950 g_object_set (G_OBJECT (chain->overlay), "silent", FALSE, NULL);
1951 if (playsink->font_desc) {
1952 g_object_set (G_OBJECT (chain->overlay), "font-desc",
1953 playsink->font_desc, NULL);
1955 if (playsink->subtitle_encoding) {
1956 g_object_set (G_OBJECT (chain->overlay), "subtitle-encoding",
1957 playsink->subtitle_encoding, NULL);
1960 gst_element_link_pads_full (chain->queue, "src", chain->overlay,
1961 "video_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS);
1963 /* make another little queue to decouple streams */
1964 element = gst_element_factory_make ("queue", "subqueue");
1965 if (element == NULL) {
1966 post_missing_element_message (playsink, "queue");
1967 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1968 (_("Missing element '%s' - check your GStreamer installation."),
1969 "queue"), ("rendering might be suboptimal"));
1971 g_object_set (G_OBJECT (element), "max-size-buffers", 3,
1972 "max-size-bytes", 0, "max-size-time", (gint64) 0,
1973 "silent", TRUE, NULL);
1974 gst_bin_add (bin, element);
1975 if (gst_element_link_pads_full (element, "src", chain->overlay,
1976 "subtitle_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
1977 textsinkpad = gst_element_get_static_pad (element, "sink");
1978 srcpad = gst_element_get_static_pad (chain->overlay, "src");
1980 gst_bin_remove (bin, chain->sink);
1981 gst_bin_remove (bin, chain->overlay);
1983 chain->overlay = NULL;
1984 gst_object_unref (videosinkpad);
1985 videosinkpad = NULL;
1992 if (videosinkpad == NULL) {
1993 /* if we still don't have a videosink, we don't have an overlay. the only
1994 * thing we can do is insert an identity and ghost the src
1996 chain->identity = gst_element_factory_make ("identity", "tidentity");
1997 if (chain->identity == NULL) {
1998 post_missing_element_message (playsink, "identity");
1999 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2000 (_("Missing element '%s' - check your GStreamer installation."),
2001 "identity"), (NULL));
2003 g_object_set (chain->identity, "signal-handoffs", FALSE, NULL);
2004 g_object_set (chain->identity, "silent", TRUE, NULL);
2005 gst_bin_add (bin, chain->identity);
2006 srcpad = gst_element_get_static_pad (chain->identity, "src");
2007 videosinkpad = gst_element_get_static_pad (chain->identity, "sink");
2011 /* expose the ghostpads */
2013 chain->videosinkpad = gst_ghost_pad_new ("sink", videosinkpad);
2014 gst_object_unref (videosinkpad);
2015 gst_element_add_pad (chain->chain.bin, chain->videosinkpad);
2018 chain->textsinkpad = gst_ghost_pad_new ("text_sink", textsinkpad);
2019 gst_object_unref (textsinkpad);
2020 gst_element_add_pad (chain->chain.bin, chain->textsinkpad);
2023 chain->srcpad = gst_ghost_pad_new ("src", srcpad);
2024 gst_object_unref (srcpad);
2025 gst_element_add_pad (chain->chain.bin, chain->srcpad);
2032 notify_volume_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
2036 g_object_get (object, "volume", &vol, NULL);
2037 playsink->volume = vol;
2039 g_object_notify (G_OBJECT (playsink), "volume");
2043 notify_mute_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
2047 g_object_get (object, "mute", &mute, NULL);
2048 playsink->mute = mute;
2050 g_object_notify (G_OBJECT (playsink), "mute");
2053 /* make the chain that contains the elements needed to perform
2056 * We add a tee as the first element so that we can link the visualisation chain
2057 * to it when requested.
2059 * +-------------------------------------------------------------+
2061 * | +---------+ +----------+ +---------+ +---------+ |
2062 * | |audioconv| |audioscale| | volume | |audiosink| |
2063 * | +-srck src-sink src-sink src-sink | |
2064 * | | +---------+ +----------+ +---------+ +---------+ |
2066 * +-------------------------------------------------------------+
2068 static GstPlayAudioChain *
2069 gen_audio_chain (GstPlaySink * playsink, gboolean raw)
2071 GstPlayAudioChain *chain;
2073 gboolean have_volume;
2075 GstElement *head, *prev, *elem = NULL;
2077 chain = g_new0 (GstPlayAudioChain, 1);
2078 chain->chain.playsink = playsink;
2079 chain->chain.raw = raw;
2081 GST_DEBUG_OBJECT (playsink, "making audio chain %p", chain);
2083 if (playsink->audio_sink) {
2084 GST_DEBUG_OBJECT (playsink, "trying configured audiosink %" GST_PTR_FORMAT,
2085 playsink->audio_sink);
2086 chain->sink = try_element (playsink, playsink->audio_sink, FALSE);
2088 /* only try fallback if no specific sink was chosen */
2089 if (chain->sink == NULL) {
2090 GST_DEBUG_OBJECT (playsink, "trying autoaudiosink");
2091 elem = gst_element_factory_make ("autoaudiosink", "audiosink");
2092 chain->sink = try_element (playsink, elem, TRUE);
2094 if (chain->sink == NULL) {
2095 /* if default sink from config.h is different then try it too */
2096 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
2097 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_AUDIOSINK);
2098 elem = gst_element_factory_make (DEFAULT_AUDIOSINK, "audiosink");
2099 chain->sink = try_element (playsink, elem, TRUE);
2103 playsink->audio_sink = gst_object_ref (chain->sink);
2105 if (chain->sink == NULL)
2108 chain->chain.bin = gst_bin_new ("abin");
2109 bin = GST_BIN_CAST (chain->chain.bin);
2110 gst_object_ref_sink (bin);
2111 gst_bin_add (bin, chain->sink);
2113 /* we have to add a queue when we need to decouple for the video sink in
2114 * visualisations and for streamsynchronizer */
2115 GST_DEBUG_OBJECT (playsink, "adding audio queue");
2116 chain->queue = gst_element_factory_make ("queue", "aqueue");
2117 if (chain->queue == NULL) {
2118 post_missing_element_message (playsink, "queue");
2119 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2120 (_("Missing element '%s' - check your GStreamer installation."),
2121 "queue"), ("audio playback and visualizations might not work"));
2125 g_object_set (chain->queue, "silent", TRUE, NULL);
2126 gst_bin_add (bin, chain->queue);
2127 prev = head = chain->queue;
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 g_signal_connect (chain->volume, "notify::volume",
2144 G_CALLBACK (notify_volume_cb), playsink);
2146 GST_DEBUG_OBJECT (playsink, "the sink has a volume property");
2148 chain->sink_volume = TRUE;
2149 /* if the sink also has a mute property we can use this as well. We'll only
2150 * use the mute property if there is a volume property. We can simulate the
2151 * mute with the volume otherwise. */
2153 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
2156 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
2157 g_signal_connect (chain->mute, "notify::mute",
2158 G_CALLBACK (notify_mute_cb), playsink);
2160 /* use the sink to control the volume and mute */
2161 if (playsink->volume_changed) {
2162 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2163 playsink->volume_changed = FALSE;
2165 if (playsink->mute_changed) {
2167 g_object_set (chain->mute, "mute", playsink->mute, NULL);
2170 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
2172 playsink->mute_changed = FALSE;
2175 /* no volume, we need to add a volume element when we can */
2176 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
2177 have_volume = FALSE;
2178 chain->sink_volume = FALSE;
2181 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO) || (!have_volume
2182 && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME))) {
2183 gboolean use_converters = !(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO);
2184 gboolean use_volume =
2185 !have_volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME);
2186 GST_DEBUG_OBJECT (playsink,
2187 "creating audioconvert with use-converters %d, use-volume %d",
2188 use_converters, use_volume);
2190 g_object_new (GST_TYPE_PLAY_SINK_AUDIO_CONVERT, "name", "aconv",
2191 "use-converters", use_converters, "use-volume", use_volume, NULL);
2192 gst_bin_add (bin, chain->conv);
2194 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
2195 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
2202 if (!have_volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
2203 GstPlaySinkAudioConvert *conv =
2204 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
2207 chain->volume = conv->volume;
2210 g_signal_connect (chain->volume, "notify::volume",
2211 G_CALLBACK (notify_volume_cb), playsink);
2213 /* volume also has the mute property */
2214 chain->mute = chain->volume;
2215 g_signal_connect (chain->mute, "notify::mute",
2216 G_CALLBACK (notify_mute_cb), playsink);
2218 /* configure with the latest volume and mute */
2219 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume,
2221 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
2227 /* we only have to link to the previous element if we have something in
2228 * front of the sink */
2229 GST_DEBUG_OBJECT (playsink, "linking to sink");
2230 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
2231 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
2235 /* post a warning if we have no way to configure the volume */
2237 GST_ELEMENT_WARNING (playsink, STREAM, NOT_IMPLEMENTED,
2238 (_("No volume control found")), ("Volume/mute is not available"));
2241 /* and ghost the sinkpad of the headmost element */
2242 GST_DEBUG_OBJECT (playsink, "ghosting sink pad");
2243 pad = gst_element_get_static_pad (head, "sink");
2244 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
2245 gst_object_unref (pad);
2246 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
2253 if (!elem && !playsink->audio_sink) {
2254 post_missing_element_message (playsink, "autoaudiosink");
2255 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
2256 post_missing_element_message (playsink, DEFAULT_AUDIOSINK);
2257 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2258 (_("Both autoaudiosink and %s elements are missing."),
2259 DEFAULT_AUDIOSINK), (NULL));
2261 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2262 (_("The autoaudiosink element is missing.")), (NULL));
2265 if (playsink->audio_sink) {
2266 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2267 (_("Configured audiosink %s is not working."),
2268 GST_ELEMENT_NAME (playsink->audio_sink)), (NULL));
2269 } else if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
2270 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2271 (_("Both autoaudiosink and %s elements are not working."),
2272 DEFAULT_AUDIOSINK), (NULL));
2274 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2275 (_("The autoaudiosink element is not working.")), (NULL));
2278 free_chain ((GstPlayChain *) chain);
2283 GST_ELEMENT_ERROR (playsink, CORE, PAD,
2284 (NULL), ("Failed to configure the audio sink."));
2285 /* checking sink made it READY */
2286 gst_element_set_state (chain->sink, GST_STATE_NULL);
2287 /* Remove chain from the bin to allow reuse later */
2288 gst_bin_remove (bin, chain->sink);
2289 free_chain ((GstPlayChain *) chain);
2295 setup_audio_chain (GstPlaySink * playsink, gboolean raw)
2298 GstPlayAudioChain *chain;
2299 GstStateChangeReturn ret;
2301 chain = playsink->audiochain;
2303 chain->chain.raw = raw;
2305 /* if the chain was active we don't do anything */
2306 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
2309 /* try to set the sink element to READY again */
2310 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
2311 if (ret == GST_STATE_CHANGE_FAILURE)
2314 /* find ts-offset element */
2315 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
2316 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
2319 /* check if the sink, or something within the sink, has the volume property.
2320 * If it does we don't need to add a volume element. */
2322 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
2325 chain->volume = elem;
2327 if (playsink->volume_changed) {
2328 GST_DEBUG_OBJECT (playsink, "the sink has a volume property, setting %f",
2330 /* use the sink to control the volume */
2331 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2332 playsink->volume_changed = FALSE;
2335 g_signal_connect (chain->volume, "notify::volume",
2336 G_CALLBACK (notify_volume_cb), playsink);
2337 /* if the sink also has a mute property we can use this as well. We'll only
2338 * use the mute property if there is a volume property. We can simulate the
2339 * mute with the volume otherwise. */
2341 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
2344 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
2345 g_signal_connect (chain->mute, "notify::mute",
2346 G_CALLBACK (notify_mute_cb), playsink);
2349 g_object_set (chain->conv, "use-volume", FALSE, NULL);
2351 GstPlaySinkAudioConvert *conv =
2352 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
2354 /* no volume, we need to add a volume element when we can */
2355 g_object_set (chain->conv, "use-volume",
2356 ! !(playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME), NULL);
2357 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
2359 /* Disconnect signals */
2360 disconnect_chain (chain, playsink);
2362 if (conv->volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
2363 chain->volume = conv->volume;
2364 chain->mute = chain->volume;
2366 g_signal_connect (chain->volume, "notify::volume",
2367 G_CALLBACK (notify_volume_cb), playsink);
2369 g_signal_connect (chain->mute, "notify::mute",
2370 G_CALLBACK (notify_mute_cb), playsink);
2372 /* configure with the latest volume and mute */
2373 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2374 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
2377 GST_DEBUG_OBJECT (playsink, "reusing existing volume element");
2383 * +-------------------------------------------------------------------+
2385 * | +----------+ +------------+ +----------+ +-------+ |
2386 * | | visqueue | | audioconv | | audiores | | vis | |
2387 * | +-sink src-sink + samp src-sink src-sink src-+ |
2388 * | | +----------+ +------------+ +----------+ +-------+ | |
2390 * +-------------------------------------------------------------------+
2393 static GstPlayVisChain *
2394 gen_vis_chain (GstPlaySink * playsink)
2396 GstPlayVisChain *chain;
2402 chain = g_new0 (GstPlayVisChain, 1);
2403 chain->chain.playsink = playsink;
2405 GST_DEBUG_OBJECT (playsink, "making vis chain %p", chain);
2407 chain->chain.bin = gst_bin_new ("visbin");
2408 bin = GST_BIN_CAST (chain->chain.bin);
2409 gst_object_ref_sink (bin);
2411 /* we're queuing raw audio here, we can remove this queue when we can disable
2412 * async behaviour in the video sink. */
2413 chain->queue = gst_element_factory_make ("queue", "visqueue");
2414 if (chain->queue == NULL)
2416 g_object_set (chain->queue, "silent", TRUE, NULL);
2417 gst_bin_add (bin, chain->queue);
2419 chain->conv = gst_element_factory_make ("audioconvert", "aconv");
2420 if (chain->conv == NULL)
2421 goto no_audioconvert;
2422 gst_bin_add (bin, chain->conv);
2424 chain->resample = gst_element_factory_make ("audioresample", "aresample");
2425 if (chain->resample == NULL)
2426 goto no_audioresample;
2427 gst_bin_add (bin, chain->resample);
2429 /* this pad will be used for blocking the dataflow and switching the vis
2431 chain->blockpad = gst_element_get_static_pad (chain->resample, "src");
2433 if (playsink->visualisation) {
2434 GST_DEBUG_OBJECT (playsink, "trying configure vis");
2435 chain->vis = try_element (playsink, playsink->visualisation, FALSE);
2437 if (chain->vis == NULL) {
2438 GST_DEBUG_OBJECT (playsink, "trying goom");
2439 elem = gst_element_factory_make ("goom", "vis");
2440 chain->vis = try_element (playsink, elem, TRUE);
2442 if (chain->vis == NULL)
2445 gst_bin_add (bin, chain->vis);
2447 res = gst_element_link_pads_full (chain->queue, "src", chain->conv, "sink",
2448 GST_PAD_LINK_CHECK_NOTHING);
2450 gst_element_link_pads_full (chain->conv, "src", chain->resample, "sink",
2451 GST_PAD_LINK_CHECK_NOTHING);
2453 gst_element_link_pads_full (chain->resample, "src", chain->vis, "sink",
2454 GST_PAD_LINK_CHECK_NOTHING);
2458 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
2459 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
2461 pad = gst_element_get_static_pad (chain->queue, "sink");
2462 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
2463 gst_object_unref (pad);
2464 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
2466 chain->srcpad = gst_ghost_pad_new ("src", chain->vissrcpad);
2467 gst_element_add_pad (chain->chain.bin, chain->srcpad);
2474 post_missing_element_message (playsink, "queue");
2475 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2476 (_("Missing element '%s' - check your GStreamer installation."),
2478 free_chain ((GstPlayChain *) chain);
2483 post_missing_element_message (playsink, "audioconvert");
2484 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2485 (_("Missing element '%s' - check your GStreamer installation."),
2486 "audioconvert"), ("possibly a liboil version mismatch?"));
2487 free_chain ((GstPlayChain *) chain);
2492 post_missing_element_message (playsink, "audioresample");
2493 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2494 (_("Missing element '%s' - check your GStreamer installation."),
2495 "audioresample"), (NULL));
2496 free_chain ((GstPlayChain *) chain);
2501 post_missing_element_message (playsink, "goom");
2502 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2503 (_("Missing element '%s' - check your GStreamer installation."),
2505 free_chain ((GstPlayChain *) chain);
2510 GST_ELEMENT_ERROR (playsink, CORE, PAD,
2511 (NULL), ("Failed to configure the visualisation element."));
2512 /* element made it to READY */
2513 gst_element_set_state (chain->vis, GST_STATE_NULL);
2514 free_chain ((GstPlayChain *) chain);
2519 /* this function is called when all the request pads are requested and when we
2520 * have to construct the final pipeline. Based on the flags we construct the
2521 * final output pipelines.
2524 gst_play_sink_reconfigure (GstPlaySink * playsink)
2527 gboolean need_audio, need_video, need_deinterlace, need_vis, need_text;
2529 GST_DEBUG_OBJECT (playsink, "reconfiguring");
2531 /* assume we need nothing */
2532 need_audio = need_video = need_deinterlace = need_vis = need_text = FALSE;
2534 GST_PLAY_SINK_LOCK (playsink);
2535 GST_OBJECT_LOCK (playsink);
2536 /* get flags, there are protected with the object lock */
2537 flags = playsink->flags;
2538 GST_OBJECT_UNLOCK (playsink);
2540 /* figure out which components we need */
2541 if (flags & GST_PLAY_FLAG_TEXT && playsink->text_pad) {
2542 /* we have subtitles and we are requested to show it */
2546 GST_OBJECT_LOCK (playsink);
2547 if (playsink->xoverlay_element)
2548 gst_object_unref (playsink->xoverlay_element);
2549 playsink->xoverlay_element = NULL;
2551 if (playsink->colorbalance_element) {
2552 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
2553 G_CALLBACK (colorbalance_value_changed_cb), playsink);
2554 gst_object_unref (playsink->colorbalance_element);
2556 playsink->colorbalance_element = NULL;
2557 GST_OBJECT_UNLOCK (playsink);
2559 if (((flags & GST_PLAY_FLAG_VIDEO)
2560 || (flags & GST_PLAY_FLAG_NATIVE_VIDEO)) && playsink->video_pad) {
2561 /* we have video and we are requested to show it */
2564 /* we only deinterlace if native video is not requested and
2565 * we have raw video */
2566 if ((flags & GST_PLAY_FLAG_DEINTERLACE)
2567 && !(flags & GST_PLAY_FLAG_NATIVE_VIDEO) && playsink->video_pad_raw)
2568 need_deinterlace = TRUE;
2571 if (playsink->audio_pad) {
2572 if ((flags & GST_PLAY_FLAG_AUDIO) || (flags & GST_PLAY_FLAG_NATIVE_AUDIO)) {
2575 if (playsink->audio_pad_raw) {
2576 /* only can do vis with raw uncompressed audio */
2577 if (flags & GST_PLAY_FLAG_VIS && !need_video) {
2578 /* also add video when we add visualisation */
2585 /* we have a text_pad and we need text rendering, in this case we need a
2586 * video_pad to combine the video with the text or visualizations */
2587 if (need_text && !need_video) {
2588 if (playsink->video_pad) {
2590 } else if (need_audio) {
2591 GST_ELEMENT_WARNING (playsink, STREAM, FORMAT,
2592 (_("Can't play a text file without video or visualizations.")),
2593 ("Have text pad but no video pad or visualizations"));
2596 GST_ELEMENT_ERROR (playsink, STREAM, FORMAT,
2597 (_("Can't play a text file without video or visualizations.")),
2598 ("Have text pad but no video pad or visualizations"));
2599 GST_PLAY_SINK_UNLOCK (playsink);
2604 GST_DEBUG_OBJECT (playsink, "audio:%d, video:%d, vis:%d, text:%d", need_audio,
2605 need_video, need_vis, need_text);
2607 /* set up video pipeline */
2609 gboolean raw, async;
2611 /* we need a raw sink when we do vis or when we have a raw pad */
2612 raw = need_vis ? TRUE : playsink->video_pad_raw;
2613 /* we try to set the sink async=FALSE when we need vis, this way we can
2614 * avoid a queue in the audio chain. */
2617 GST_DEBUG_OBJECT (playsink, "adding video, raw %d",
2618 playsink->video_pad_raw);
2620 if (playsink->videochain) {
2621 /* try to reactivate the chain */
2622 if (!setup_video_chain (playsink, raw, async)) {
2623 if (playsink->video_sinkpad_stream_synchronizer) {
2624 gst_element_release_request_pad (GST_ELEMENT_CAST
2625 (playsink->stream_synchronizer),
2626 playsink->video_sinkpad_stream_synchronizer);
2627 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2628 playsink->video_sinkpad_stream_synchronizer = NULL;
2629 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2630 playsink->video_srcpad_stream_synchronizer = NULL;
2633 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2635 /* Remove the sink from the bin to keep its state
2636 * and unparent it to allow reuse */
2637 if (playsink->videochain->sink)
2638 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
2639 playsink->videochain->sink);
2641 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2642 free_chain ((GstPlayChain *) playsink->videochain);
2643 playsink->videochain = NULL;
2647 if (!playsink->videochain)
2648 playsink->videochain = gen_video_chain (playsink, raw, async);
2649 if (!playsink->videochain)
2652 if (!playsink->video_sinkpad_stream_synchronizer) {
2655 playsink->video_sinkpad_stream_synchronizer =
2656 gst_element_get_request_pad (GST_ELEMENT_CAST
2657 (playsink->stream_synchronizer), "sink_%d");
2658 it = gst_pad_iterate_internal_links
2659 (playsink->video_sinkpad_stream_synchronizer);
2661 gst_iterator_next (it,
2662 (gpointer *) & playsink->video_srcpad_stream_synchronizer);
2663 g_assert (playsink->video_srcpad_stream_synchronizer);
2664 gst_iterator_free (it);
2667 if (playsink->video_pad)
2668 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad),
2669 playsink->video_sinkpad_stream_synchronizer);
2671 if (need_deinterlace) {
2672 if (!playsink->videodeinterlacechain)
2673 playsink->videodeinterlacechain =
2674 gen_video_deinterlace_chain (playsink);
2675 if (!playsink->videodeinterlacechain)
2678 GST_DEBUG_OBJECT (playsink, "adding video deinterlace chain");
2680 GST_DEBUG_OBJECT (playsink, "setting up deinterlacing chain");
2682 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
2683 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
2685 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2686 playsink->videodeinterlacechain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2688 if (playsink->videodeinterlacechain) {
2689 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2690 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
2695 GST_DEBUG_OBJECT (playsink, "adding video chain");
2696 add_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
2697 activate_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
2698 /* if we are not part of vis or subtitles, set the ghostpad target */
2699 if (!need_vis && !need_text && (!playsink->textchain
2700 || !playsink->text_pad)) {
2701 GST_DEBUG_OBJECT (playsink, "ghosting video sinkpad");
2702 if (need_deinterlace)
2703 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
2704 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2706 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2707 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2710 GST_DEBUG_OBJECT (playsink, "no video needed");
2711 if (playsink->videochain) {
2712 GST_DEBUG_OBJECT (playsink, "removing video chain");
2713 if (playsink->vischain) {
2716 GST_DEBUG_OBJECT (playsink, "unlinking vis chain");
2718 /* also had visualisation, release the tee srcpad before we then
2719 * unlink the video from it */
2720 if (playsink->audio_tee_vissrc) {
2721 gst_element_release_request_pad (playsink->audio_tee,
2722 playsink->audio_tee_vissrc);
2723 gst_object_unref (playsink->audio_tee_vissrc);
2724 playsink->audio_tee_vissrc = NULL;
2727 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2728 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
2731 if (playsink->video_sinkpad_stream_synchronizer) {
2732 gst_element_release_request_pad (GST_ELEMENT_CAST
2733 (playsink->stream_synchronizer),
2734 playsink->video_sinkpad_stream_synchronizer);
2735 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2736 playsink->video_sinkpad_stream_synchronizer = NULL;
2737 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2738 playsink->video_srcpad_stream_synchronizer = NULL;
2741 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2742 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2743 if (playsink->videochain->ts_offset)
2744 gst_object_unref (playsink->videochain->ts_offset);
2745 playsink->videochain->ts_offset = NULL;
2748 if (playsink->videodeinterlacechain) {
2749 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2750 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2753 if (playsink->video_pad)
2754 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
2760 GST_DEBUG_OBJECT (playsink, "adding audio");
2762 /* get a raw sink if we are asked for a raw pad */
2763 raw = playsink->audio_pad_raw;
2765 if (playsink->audiochain) {
2766 /* try to reactivate the chain */
2767 if (!setup_audio_chain (playsink, raw)) {
2768 GST_DEBUG_OBJECT (playsink, "removing current audio chain");
2769 if (playsink->audio_tee_asrc) {
2770 gst_element_release_request_pad (playsink->audio_tee,
2771 playsink->audio_tee_asrc);
2772 gst_object_unref (playsink->audio_tee_asrc);
2773 playsink->audio_tee_asrc = NULL;
2776 if (playsink->audio_sinkpad_stream_synchronizer) {
2777 gst_element_release_request_pad (GST_ELEMENT_CAST
2778 (playsink->stream_synchronizer),
2779 playsink->audio_sinkpad_stream_synchronizer);
2780 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
2781 playsink->audio_sinkpad_stream_synchronizer = NULL;
2782 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
2783 playsink->audio_srcpad_stream_synchronizer = NULL;
2786 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2788 /* Remove the sink from the bin to keep its state
2789 * and unparent it to allow reuse */
2790 if (playsink->audiochain->sink)
2791 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
2792 playsink->audiochain->sink);
2794 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2795 disconnect_chain (playsink->audiochain, playsink);
2796 playsink->audiochain->volume = NULL;
2797 playsink->audiochain->mute = NULL;
2798 if (playsink->audiochain->ts_offset)
2799 gst_object_unref (playsink->audiochain->ts_offset);
2800 playsink->audiochain->ts_offset = NULL;
2801 free_chain ((GstPlayChain *) playsink->audiochain);
2802 playsink->audiochain = NULL;
2803 playsink->volume_changed = playsink->mute_changed = FALSE;
2807 if (!playsink->audiochain) {
2808 GST_DEBUG_OBJECT (playsink, "creating new audio chain");
2809 playsink->audiochain = gen_audio_chain (playsink, raw);
2812 if (!playsink->audio_sinkpad_stream_synchronizer) {
2815 playsink->audio_sinkpad_stream_synchronizer =
2816 gst_element_get_request_pad (GST_ELEMENT_CAST
2817 (playsink->stream_synchronizer), "sink_%d");
2818 it = gst_pad_iterate_internal_links
2819 (playsink->audio_sinkpad_stream_synchronizer);
2821 gst_iterator_next (it,
2822 (gpointer *) & playsink->audio_srcpad_stream_synchronizer);
2823 g_assert (playsink->audio_srcpad_stream_synchronizer);
2824 gst_iterator_free (it);
2827 if (playsink->audiochain) {
2828 GST_DEBUG_OBJECT (playsink, "adding audio chain");
2829 if (playsink->audio_tee_asrc == NULL) {
2830 playsink->audio_tee_asrc =
2831 gst_element_get_request_pad (playsink->audio_tee, "src%d");
2833 add_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
2834 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
2835 gst_pad_link_full (playsink->audio_tee_asrc,
2836 playsink->audio_sinkpad_stream_synchronizer,
2837 GST_PAD_LINK_CHECK_NOTHING);
2838 gst_pad_link_full (playsink->audio_srcpad_stream_synchronizer,
2839 playsink->audiochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2842 GST_DEBUG_OBJECT (playsink, "no audio needed");
2843 /* we have no audio or we are requested to not play audio */
2844 if (playsink->audiochain) {
2845 GST_DEBUG_OBJECT (playsink, "removing audio chain");
2846 /* release the audio pad */
2847 if (playsink->audio_tee_asrc) {
2848 gst_element_release_request_pad (playsink->audio_tee,
2849 playsink->audio_tee_asrc);
2850 gst_object_unref (playsink->audio_tee_asrc);
2851 playsink->audio_tee_asrc = NULL;
2854 if (playsink->audio_sinkpad_stream_synchronizer) {
2855 gst_element_release_request_pad (GST_ELEMENT_CAST
2856 (playsink->stream_synchronizer),
2857 playsink->audio_sinkpad_stream_synchronizer);
2858 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
2859 playsink->audio_sinkpad_stream_synchronizer = NULL;
2860 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
2861 playsink->audio_srcpad_stream_synchronizer = NULL;
2864 if (playsink->audiochain->sink_volume) {
2865 disconnect_chain (playsink->audiochain, playsink);
2866 playsink->audiochain->volume = NULL;
2867 playsink->audiochain->mute = NULL;
2868 if (playsink->audiochain->ts_offset)
2869 gst_object_unref (playsink->audiochain->ts_offset);
2870 playsink->audiochain->ts_offset = NULL;
2872 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2873 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2880 if (!playsink->vischain)
2881 playsink->vischain = gen_vis_chain (playsink);
2883 GST_DEBUG_OBJECT (playsink, "adding visualisation");
2885 if (playsink->vischain) {
2886 GST_DEBUG_OBJECT (playsink, "setting up vis chain");
2888 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2889 add_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
2890 activate_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
2891 if (playsink->audio_tee_vissrc == NULL) {
2892 playsink->audio_tee_vissrc =
2893 gst_element_get_request_pad (playsink->audio_tee, "src%d");
2895 gst_pad_link_full (playsink->audio_tee_vissrc,
2896 playsink->vischain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2897 gst_pad_link_full (srcpad, playsink->video_sinkpad_stream_synchronizer,
2898 GST_PAD_LINK_CHECK_NOTHING);
2899 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2900 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2901 gst_object_unref (srcpad);
2904 GST_DEBUG_OBJECT (playsink, "no vis needed");
2905 if (playsink->vischain) {
2906 if (playsink->audio_tee_vissrc) {
2907 gst_element_release_request_pad (playsink->audio_tee,
2908 playsink->audio_tee_vissrc);
2909 gst_object_unref (playsink->audio_tee_vissrc);
2910 playsink->audio_tee_vissrc = NULL;
2912 GST_DEBUG_OBJECT (playsink, "removing vis chain");
2913 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
2914 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
2919 GST_DEBUG_OBJECT (playsink, "adding text");
2920 if (!playsink->textchain) {
2921 GST_DEBUG_OBJECT (playsink, "creating text chain");
2922 playsink->textchain = gen_text_chain (playsink);
2924 if (playsink->textchain) {
2927 GST_DEBUG_OBJECT (playsink, "adding text chain");
2928 if (playsink->textchain->overlay)
2929 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", FALSE,
2931 add_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
2933 if (!playsink->text_sinkpad_stream_synchronizer) {
2934 playsink->text_sinkpad_stream_synchronizer =
2935 gst_element_get_request_pad (GST_ELEMENT_CAST
2936 (playsink->stream_synchronizer), "sink_%d");
2937 it = gst_pad_iterate_internal_links
2938 (playsink->text_sinkpad_stream_synchronizer);
2940 gst_iterator_next (it,
2941 (gpointer *) & playsink->text_srcpad_stream_synchronizer);
2942 g_assert (playsink->text_srcpad_stream_synchronizer);
2943 gst_iterator_free (it);
2945 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad),
2946 playsink->text_sinkpad_stream_synchronizer);
2947 gst_pad_link_full (playsink->text_srcpad_stream_synchronizer,
2948 playsink->textchain->textsinkpad, GST_PAD_LINK_CHECK_NOTHING);
2955 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2956 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
2957 gst_pad_link_full (srcpad, playsink->textchain->videosinkpad,
2958 GST_PAD_LINK_CHECK_NOTHING);
2959 gst_object_unref (srcpad);
2961 if (need_deinterlace)
2962 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
2963 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
2965 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2966 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
2968 gst_pad_link_full (playsink->textchain->srcpad,
2969 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2971 activate_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
2974 GST_DEBUG_OBJECT (playsink, "no text needed");
2975 /* we have no subtitles/text or we are requested to not show them */
2977 if (playsink->text_sinkpad_stream_synchronizer) {
2978 gst_element_release_request_pad (GST_ELEMENT_CAST
2979 (playsink->stream_synchronizer),
2980 playsink->text_sinkpad_stream_synchronizer);
2981 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
2982 playsink->text_sinkpad_stream_synchronizer = NULL;
2983 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
2984 playsink->text_srcpad_stream_synchronizer = NULL;
2987 if (playsink->textchain) {
2988 if (playsink->text_pad == NULL) {
2989 /* no text pad, remove the chain entirely */
2990 GST_DEBUG_OBJECT (playsink, "removing text chain");
2991 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
2992 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
2994 /* we have a chain and a textpad, turn the subtitles off */
2995 GST_DEBUG_OBJECT (playsink, "turning off the text");
2996 if (playsink->textchain->overlay)
2997 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", TRUE,
3001 if (!need_video && playsink->video_pad) {
3002 if (playsink->video_sinkpad_stream_synchronizer) {
3003 gst_element_release_request_pad (GST_ELEMENT_CAST
3004 (playsink->stream_synchronizer),
3005 playsink->video_sinkpad_stream_synchronizer);
3006 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3007 playsink->video_sinkpad_stream_synchronizer = NULL;
3008 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3009 playsink->video_srcpad_stream_synchronizer = NULL;
3012 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
3015 if (playsink->text_pad && !playsink->textchain)
3016 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad), NULL);
3018 update_av_offset (playsink);
3019 do_async_done (playsink);
3020 GST_PLAY_SINK_UNLOCK (playsink);
3027 /* gen_ chain already posted error */
3028 GST_DEBUG_OBJECT (playsink, "failed to setup chain");
3029 GST_PLAY_SINK_UNLOCK (playsink);
3035 * gst_play_sink_set_flags:
3036 * @playsink: a #GstPlaySink
3037 * @flags: #GstPlayFlags
3039 * Configure @flags on @playsink. The flags control the behaviour of @playsink
3040 * when constructing the sink pipelins.
3042 * Returns: TRUE if the flags could be configured.
3045 gst_play_sink_set_flags (GstPlaySink * playsink, GstPlayFlags flags)
3047 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), FALSE);
3049 GST_OBJECT_LOCK (playsink);
3050 playsink->flags = flags;
3051 GST_OBJECT_UNLOCK (playsink);
3057 * gst_play_sink_get_flags:
3058 * @playsink: a #GstPlaySink
3060 * Get the flags of @playsink. That flags control the behaviour of the sink when
3061 * it constructs the sink pipelines.
3063 * Returns: the currently configured #GstPlayFlags.
3066 gst_play_sink_get_flags (GstPlaySink * playsink)
3070 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), 0);
3072 GST_OBJECT_LOCK (playsink);
3073 res = playsink->flags;
3074 GST_OBJECT_UNLOCK (playsink);
3080 gst_play_sink_set_font_desc (GstPlaySink * playsink, const gchar * desc)
3082 GstPlayTextChain *chain;
3084 GST_PLAY_SINK_LOCK (playsink);
3085 chain = (GstPlayTextChain *) playsink->textchain;
3086 g_free (playsink->font_desc);
3087 playsink->font_desc = g_strdup (desc);
3088 if (chain && chain->overlay) {
3089 g_object_set (chain->overlay, "font-desc", desc, NULL);
3091 GST_PLAY_SINK_UNLOCK (playsink);
3095 gst_play_sink_get_font_desc (GstPlaySink * playsink)
3097 gchar *result = NULL;
3098 GstPlayTextChain *chain;
3100 GST_PLAY_SINK_LOCK (playsink);
3101 chain = (GstPlayTextChain *) playsink->textchain;
3102 if (chain && chain->overlay) {
3103 g_object_get (chain->overlay, "font-desc", &result, NULL);
3104 playsink->font_desc = g_strdup (result);
3106 result = g_strdup (playsink->font_desc);
3108 GST_PLAY_SINK_UNLOCK (playsink);
3114 gst_play_sink_set_subtitle_encoding (GstPlaySink * playsink,
3115 const gchar * encoding)
3117 GstPlayTextChain *chain;
3119 GST_PLAY_SINK_LOCK (playsink);
3120 chain = (GstPlayTextChain *) playsink->textchain;
3121 g_free (playsink->subtitle_encoding);
3122 playsink->subtitle_encoding = g_strdup (encoding);
3123 if (chain && chain->overlay) {
3124 g_object_set (chain->overlay, "subtitle-encoding", encoding, NULL);
3126 GST_PLAY_SINK_UNLOCK (playsink);
3130 gst_play_sink_get_subtitle_encoding (GstPlaySink * playsink)
3132 gchar *result = NULL;
3133 GstPlayTextChain *chain;
3135 GST_PLAY_SINK_LOCK (playsink);
3136 chain = (GstPlayTextChain *) playsink->textchain;
3137 if (chain && chain->overlay) {
3138 g_object_get (chain->overlay, "subtitle-encoding", &result, NULL);
3139 playsink->subtitle_encoding = g_strdup (result);
3141 result = g_strdup (playsink->subtitle_encoding);
3143 GST_PLAY_SINK_UNLOCK (playsink);
3149 update_av_offset (GstPlaySink * playsink)
3152 GstPlayAudioChain *achain;
3153 GstPlayVideoChain *vchain;
3155 av_offset = playsink->av_offset;
3156 achain = (GstPlayAudioChain *) playsink->audiochain;
3157 vchain = (GstPlayVideoChain *) playsink->videochain;
3159 if (achain && vchain && achain->ts_offset && vchain->ts_offset) {
3160 g_object_set (achain->ts_offset, "ts-offset", MAX (0, -av_offset), NULL);
3161 g_object_set (vchain->ts_offset, "ts-offset", MAX (0, av_offset), NULL);
3163 GST_LOG_OBJECT (playsink, "no ts_offset elements");
3168 gst_play_sink_set_av_offset (GstPlaySink * playsink, gint64 av_offset)
3170 GST_PLAY_SINK_LOCK (playsink);
3171 playsink->av_offset = av_offset;
3172 update_av_offset (playsink);
3173 GST_PLAY_SINK_UNLOCK (playsink);
3177 gst_play_sink_get_av_offset (GstPlaySink * playsink)
3181 GST_PLAY_SINK_LOCK (playsink);
3182 result = playsink->av_offset;
3183 GST_PLAY_SINK_UNLOCK (playsink);
3189 * gst_play_sink_get_last_frame:
3190 * @playsink: a #GstPlaySink
3192 * Get the last displayed frame from @playsink. This frame is in the native
3193 * format of the sink element, the caps on the result buffer contain the format
3194 * of the frame data.
3196 * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
3200 gst_play_sink_get_last_frame (GstPlaySink * playsink)
3202 GstBuffer *result = NULL;
3203 GstPlayVideoChain *chain;
3205 GST_PLAY_SINK_LOCK (playsink);
3206 GST_DEBUG_OBJECT (playsink, "taking last frame");
3207 /* get the video chain if we can */
3208 if ((chain = (GstPlayVideoChain *) playsink->videochain)) {
3209 GST_DEBUG_OBJECT (playsink, "found video chain");
3210 /* see if the chain is active */
3211 if (chain->chain.activated && chain->sink) {
3214 GST_DEBUG_OBJECT (playsink, "video chain active and has a sink");
3216 /* find and get the last-buffer property now */
3218 gst_play_sink_find_property (playsink, chain->sink,
3219 "last-buffer", GST_TYPE_BUFFER))) {
3220 GST_DEBUG_OBJECT (playsink, "getting last-buffer property");
3221 g_object_get (elem, "last-buffer", &result, NULL);
3222 gst_object_unref (elem);
3226 GST_PLAY_SINK_UNLOCK (playsink);
3232 * gst_play_sink_convert_frame:
3233 * @playsink: a #GstPlaySink
3236 * Get the last displayed frame from @playsink. If caps is %NULL, the video will
3237 * be in the native format of the sink element and the caps on the buffer
3238 * describe the format of the frame. If @caps is not %NULL, the video
3239 * frame will be converted to the format of the caps.
3241 * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
3242 * available or when the conversion failed.
3245 gst_play_sink_convert_frame (GstPlaySink * playsink, GstCaps * caps)
3249 result = gst_play_sink_get_last_frame (playsink);
3250 if (result != NULL && caps != NULL) {
3254 temp = gst_video_convert_frame (result, caps, 25 * GST_SECOND, &err);
3255 gst_buffer_unref (result);
3256 if (temp == NULL && err) {
3257 /* I'm really uncertain whether we should make playsink post an error
3258 * on the bus or not. It's not like it's a critical issue regarding
3259 * playsink behaviour. */
3260 GST_ERROR ("Error converting frame: %s", err->message);
3269 is_raw_structure (GstStructure * s)
3273 name = gst_structure_get_name (s);
3275 if (g_str_has_prefix (name, "video/x-raw-") ||
3276 g_str_has_prefix (name, "audio/x-raw-"))
3282 is_raw_pad (GstPad * pad)
3284 GstPad *peer = gst_pad_get_peer (pad);
3286 gboolean raw = TRUE;
3291 caps = gst_pad_get_negotiated_caps (peer);
3295 caps = gst_pad_get_caps_reffed (peer);
3297 n = gst_caps_get_size (caps);
3298 for (i = 0; i < n; i++) {
3299 gboolean r = is_raw_structure (gst_caps_get_structure (caps, i));
3303 } else if (raw != r) {
3304 GST_ERROR_OBJECT (pad,
3305 "Caps contains raw and non-raw structures: %" GST_PTR_FORMAT, caps);
3311 raw = is_raw_structure (gst_caps_get_structure (caps, 0));
3313 gst_caps_unref (caps);
3314 gst_object_unref (peer);
3320 sinkpad_blocked_cb (GstPad * blockedpad, gboolean blocked, gpointer user_data)
3322 GstPlaySink *playsink = (GstPlaySink *) user_data;
3325 GST_PLAY_SINK_LOCK (playsink);
3327 pad = GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (blockedpad)));
3328 if (pad == playsink->video_pad) {
3329 playsink->video_pad_blocked = blocked;
3330 GST_DEBUG_OBJECT (pad, "Video pad blocked: %d", blocked);
3332 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO_RAW);
3333 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO);
3335 } else if (pad == playsink->audio_pad) {
3336 playsink->audio_pad_blocked = blocked;
3337 GST_DEBUG_OBJECT (pad, "Audio pad blocked: %d", blocked);
3339 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO_RAW);
3340 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO);
3342 } else if (pad == playsink->text_pad) {
3343 playsink->text_pad_blocked = blocked;
3344 GST_DEBUG_OBJECT (pad, "Text pad blocked: %d", blocked);
3346 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_TEXT);
3350 gst_object_unref (pad);
3351 GST_PLAY_SINK_UNLOCK (playsink);
3355 /* We reconfigure when for ALL streams:
3356 * * there isn't a pad
3357 * * OR the pad is blocked
3358 * * OR there are no pending blocks on that pad
3361 if ((!playsink->video_pad || playsink->video_pad_blocked
3362 || !PENDING_VIDEO_BLOCK (playsink)) && (!playsink->audio_pad
3363 || playsink->audio_pad_blocked || !PENDING_AUDIO_BLOCK (playsink))
3364 && (!playsink->text_pad || playsink->text_pad_blocked
3365 || !PENDING_TEXT_BLOCK (playsink))) {
3366 GST_DEBUG_OBJECT (playsink, "All pads blocked -- reconfiguring");
3368 if (playsink->video_pad) {
3369 playsink->video_pad_raw = is_raw_pad (playsink->video_pad);
3370 GST_DEBUG_OBJECT (playsink, "Video pad is raw: %d",
3371 playsink->video_pad_raw);
3374 if (playsink->audio_pad) {
3375 playsink->audio_pad_raw = is_raw_pad (playsink->audio_pad);
3376 GST_DEBUG_OBJECT (playsink, "Audio pad is raw: %d",
3377 playsink->audio_pad_raw);
3380 gst_play_sink_reconfigure (playsink);
3382 if (playsink->video_pad) {
3384 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3385 (playsink->video_pad)));
3386 gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3387 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3388 gst_object_unref (opad);
3391 if (playsink->audio_pad) {
3393 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3394 (playsink->audio_pad)));
3395 gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3396 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3397 gst_object_unref (opad);
3400 if (playsink->text_pad) {
3402 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3403 (playsink->text_pad)));
3404 gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3405 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3406 gst_object_unref (opad);
3410 gst_object_unref (pad);
3412 GST_PLAY_SINK_UNLOCK (playsink);
3416 caps_notify_cb (GstPad * pad, GParamSpec * unused, GstPlaySink * playsink)
3418 gboolean reconfigure = FALSE;
3422 g_object_get (pad, "caps", &caps, NULL);
3426 if (pad == playsink->audio_pad) {
3427 raw = is_raw_pad (pad);
3428 reconfigure = (! !playsink->audio_pad_raw != ! !raw)
3429 && playsink->audiochain;
3430 GST_DEBUG_OBJECT (pad,
3431 "Audio caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3433 } else if (pad == playsink->video_pad) {
3434 raw = is_raw_pad (pad);
3435 reconfigure = (! !playsink->video_pad_raw != ! !raw)
3436 && playsink->videochain;
3437 GST_DEBUG_OBJECT (pad,
3438 "Video caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3442 gst_caps_unref (caps);
3445 GST_PLAY_SINK_LOCK (playsink);
3446 if (playsink->video_pad) {
3448 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3449 (playsink->video_pad)));
3450 gst_pad_set_blocked_async_full (opad, TRUE, sinkpad_blocked_cb,
3451 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3452 gst_object_unref (opad);
3455 if (playsink->audio_pad) {
3457 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3458 (playsink->audio_pad)));
3459 gst_pad_set_blocked_async_full (opad, TRUE, sinkpad_blocked_cb,
3460 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3461 gst_object_unref (opad);
3464 if (playsink->text_pad) {
3466 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3467 (playsink->text_pad)));
3468 gst_pad_set_blocked_async_full (opad, TRUE, sinkpad_blocked_cb,
3469 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3470 gst_object_unref (opad);
3472 GST_PLAY_SINK_UNLOCK (playsink);
3477 * gst_play_sink_request_pad
3478 * @playsink: a #GstPlaySink
3479 * @type: a #GstPlaySinkType
3481 * Create or return a pad of @type.
3483 * Returns: a #GstPad of @type or %NULL when the pad could not be created.
3486 gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type)
3489 gboolean created = FALSE;
3490 gboolean activate = TRUE;
3491 const gchar *pad_name = NULL;
3493 GST_DEBUG_OBJECT (playsink, "request pad type %d", type);
3495 GST_PLAY_SINK_LOCK (playsink);
3497 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
3498 case GST_PLAY_SINK_TYPE_AUDIO:
3499 pad_name = "audio_sink";
3500 if (!playsink->audio_tee) {
3501 GST_LOG_OBJECT (playsink, "creating tee");
3502 /* create tee when needed. This element will feed the audio sink chain
3503 * and the vis chain. */
3504 playsink->audio_tee = gst_element_factory_make ("tee", "audiotee");
3505 if (playsink->audio_tee == NULL) {
3506 post_missing_element_message (playsink, "tee");
3507 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
3508 (_("Missing element '%s' - check your GStreamer installation."),
3513 playsink->audio_tee_sink =
3514 gst_element_get_static_pad (playsink->audio_tee, "sink");
3515 gst_bin_add (GST_BIN_CAST (playsink), playsink->audio_tee);
3516 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
3519 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
3521 if (!playsink->audio_pad) {
3522 GST_LOG_OBJECT (playsink, "ghosting tee sinkpad");
3523 playsink->audio_pad =
3524 gst_ghost_pad_new (pad_name, playsink->audio_tee_sink);
3525 g_signal_connect (G_OBJECT (playsink->audio_pad), "notify::caps",
3526 G_CALLBACK (caps_notify_cb), playsink);
3529 playsink->audio_pad_raw = FALSE;
3530 res = playsink->audio_pad;
3532 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
3533 case GST_PLAY_SINK_TYPE_VIDEO:
3534 pad_name = "video_sink";
3535 if (!playsink->video_pad) {
3536 GST_LOG_OBJECT (playsink, "ghosting videosink");
3537 playsink->video_pad =
3538 gst_ghost_pad_new_no_target (pad_name, GST_PAD_SINK);
3539 g_signal_connect (G_OBJECT (playsink->video_pad), "notify::caps",
3540 G_CALLBACK (caps_notify_cb), playsink);
3543 playsink->video_pad_raw = FALSE;
3544 res = playsink->video_pad;
3546 case GST_PLAY_SINK_TYPE_TEXT:
3547 GST_LOG_OBJECT (playsink, "ghosting text");
3548 if (!playsink->text_pad) {
3549 playsink->text_pad =
3550 gst_ghost_pad_new_no_target ("text_sink", GST_PAD_SINK);
3553 res = playsink->text_pad;
3555 case GST_PLAY_SINK_TYPE_FLUSHING:
3559 /* we need a unique padname for the flushing pad. */
3560 padname = g_strdup_printf ("flushing_%d", playsink->count);
3561 res = gst_ghost_pad_new_no_target (padname, GST_PAD_SINK);
3572 GST_PLAY_SINK_UNLOCK (playsink);
3574 if (created && res) {
3575 /* we have to add the pad when it's active or we get an error when the
3576 * element is 'running' */
3577 gst_pad_set_active (res, TRUE);
3578 gst_element_add_pad (GST_ELEMENT_CAST (playsink), res);
3579 if (type != GST_PLAY_SINK_TYPE_FLUSHING) {
3581 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (res)));
3583 gst_pad_set_blocked_async_full (blockpad, TRUE, sinkpad_blocked_cb,
3584 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3585 PENDING_FLAG_SET (playsink, type);
3586 gst_object_unref (blockpad);
3589 gst_pad_set_active (res, activate);
3596 gst_play_sink_request_new_pad (GstElement * element, GstPadTemplate * templ,
3601 GstPlaySinkType type;
3602 const gchar *tplname;
3604 g_return_val_if_fail (templ != NULL, NULL);
3606 GST_DEBUG_OBJECT (element, "name:%s", name);
3608 psink = GST_PLAY_SINK (element);
3609 tplname = GST_PAD_TEMPLATE_NAME_TEMPLATE (templ);
3611 /* Figure out the GstPlaySinkType based on the template */
3612 if (!strcmp (tplname, "audio_sink"))
3613 type = GST_PLAY_SINK_TYPE_AUDIO;
3614 else if (!strcmp (tplname, "audio_raw_sink"))
3615 type = GST_PLAY_SINK_TYPE_AUDIO_RAW;
3616 else if (!strcmp (tplname, "video_sink"))
3617 type = GST_PLAY_SINK_TYPE_VIDEO;
3618 else if (!strcmp (tplname, "video_raw_sink"))
3619 type = GST_PLAY_SINK_TYPE_VIDEO_RAW;
3620 else if (!strcmp (tplname, "text_sink"))
3621 type = GST_PLAY_SINK_TYPE_TEXT;
3623 goto unknown_template;
3625 pad = gst_play_sink_request_pad (psink, type);
3629 GST_WARNING_OBJECT (element, "Unknown pad template");
3634 gst_play_sink_release_pad (GstPlaySink * playsink, GstPad * pad)
3636 GstPad **res = NULL;
3637 gboolean untarget = TRUE;
3639 GST_DEBUG_OBJECT (playsink, "release pad %" GST_PTR_FORMAT, pad);
3641 GST_PLAY_SINK_LOCK (playsink);
3642 if (pad == playsink->video_pad) {
3643 res = &playsink->video_pad;
3644 g_signal_handlers_disconnect_by_func (playsink->video_pad, caps_notify_cb,
3646 } else if (pad == playsink->audio_pad) {
3647 res = &playsink->audio_pad;
3648 g_signal_handlers_disconnect_by_func (playsink->audio_pad, caps_notify_cb,
3650 } else if (pad == playsink->text_pad) {
3651 res = &playsink->text_pad;
3653 /* try to release the given pad anyway, these could be the FLUSHING pads. */
3657 GST_PLAY_SINK_UNLOCK (playsink);
3660 GST_DEBUG_OBJECT (playsink, "deactivate pad %" GST_PTR_FORMAT, *res);
3661 gst_pad_set_active (*res, FALSE);
3663 GST_DEBUG_OBJECT (playsink, "untargeting pad %" GST_PTR_FORMAT, *res);
3664 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (*res), NULL);
3666 GST_DEBUG_OBJECT (playsink, "remove pad %" GST_PTR_FORMAT, *res);
3667 gst_element_remove_pad (GST_ELEMENT_CAST (playsink), *res);
3673 gst_play_sink_release_request_pad (GstElement * element, GstPad * pad)
3675 GstPlaySink *psink = GST_PLAY_SINK (element);
3677 gst_play_sink_release_pad (psink, pad);
3681 gst_play_sink_handle_message (GstBin * bin, GstMessage * message)
3683 GstPlaySink *playsink;
3685 playsink = GST_PLAY_SINK_CAST (bin);
3687 switch (GST_MESSAGE_TYPE (message)) {
3688 case GST_MESSAGE_STEP_DONE:
3693 gboolean flush, intermediate, eos;
3696 GST_INFO_OBJECT (playsink, "Handling step-done message");
3697 gst_message_parse_step_done (message, &format, &amount, &rate, &flush,
3698 &intermediate, &duration, &eos);
3700 if (format == GST_FORMAT_BUFFERS) {
3701 /* for the buffer format, we align the other streams */
3702 if (playsink->audiochain) {
3706 gst_event_new_step (GST_FORMAT_TIME, duration, rate, flush,
3709 if (!gst_element_send_event (playsink->audiochain->chain.bin, event)) {
3710 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
3714 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
3717 case GST_MESSAGE_ELEMENT:{
3718 if (gst_structure_has_name (message->structure, "prepare-xwindow-id")) {
3719 GstXOverlay *xoverlay;
3721 GST_OBJECT_LOCK (playsink);
3722 if (playsink->xoverlay_element
3723 && GST_OBJECT_CAST (playsink->xoverlay_element) !=
3724 GST_MESSAGE_SRC (message)) {
3725 gst_object_unref (playsink->xoverlay_element);
3726 playsink->xoverlay_element = NULL;
3729 if (!playsink->xoverlay_element)
3730 playsink->xoverlay_element =
3731 GST_X_OVERLAY (gst_object_ref (GST_MESSAGE_SRC (message)));
3732 xoverlay = GST_X_OVERLAY (gst_object_ref (playsink->xoverlay_element));
3733 GST_OBJECT_UNLOCK (playsink);
3735 GST_DEBUG_OBJECT (playsink, "Got prepare-xwindow-id message");
3737 if (playsink->xoverlay_handle_set)
3738 gst_x_overlay_set_window_handle (playsink->xoverlay_element,
3739 playsink->xoverlay_handle);
3740 if (playsink->xoverlay_handle_events_set)
3741 gst_x_overlay_handle_events (playsink->xoverlay_element,
3742 playsink->xoverlay_handle_events);
3743 if (playsink->xoverlay_render_rectangle_set)
3744 gst_x_overlay_set_render_rectangle (playsink->xoverlay_element,
3745 playsink->xoverlay_x, playsink->xoverlay_y,
3746 playsink->xoverlay_width, playsink->xoverlay_height);
3748 gst_object_unref (xoverlay);
3749 gst_message_unref (message);
3750 gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (playsink));
3755 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
3760 /* Send an event to our sinks until one of them works; don't then send to the
3761 * remaining sinks (unlike GstBin)
3762 * Special case: If a text sink is set we need to send the event
3763 * to them in case it's source is different from the a/v stream's source.
3766 gst_play_sink_send_event_to_sink (GstPlaySink * playsink, GstEvent * event)
3768 gboolean res = TRUE;
3769 if (playsink->textchain && playsink->textchain->sink) {
3770 gst_event_ref (event);
3771 if ((res = gst_element_send_event (playsink->textchain->chain.bin, event))) {
3772 GST_DEBUG_OBJECT (playsink, "Sent event successfully to text sink");
3774 GST_DEBUG_OBJECT (playsink, "Event failed when sent to text sink");
3778 if (playsink->videochain) {
3779 gst_event_ref (event);
3780 if ((res = gst_element_send_event (playsink->videochain->chain.bin, event))) {
3781 GST_DEBUG_OBJECT (playsink, "Sent event successfully to video sink");
3784 GST_DEBUG_OBJECT (playsink, "Event failed when sent to video sink");
3786 if (playsink->audiochain) {
3787 gst_event_ref (event);
3788 if ((res = gst_element_send_event (playsink->audiochain->chain.bin, event))) {
3789 GST_DEBUG_OBJECT (playsink, "Sent event successfully to audio sink");
3792 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
3796 gst_event_unref (event);
3800 /* We only want to send the event to a single sink (overriding GstBin's
3801 * behaviour), but we want to keep GstPipeline's behaviour - wrapping seek
3802 * events appropriately. So, this is a messy duplication of code. */
3804 gst_play_sink_send_event (GstElement * element, GstEvent * event)
3806 gboolean res = FALSE;
3807 GstEventType event_type = GST_EVENT_TYPE (event);
3808 GstPlaySink *playsink;
3809 playsink = GST_PLAY_SINK_CAST (element);
3810 switch (event_type) {
3811 case GST_EVENT_SEEK:
3812 GST_DEBUG_OBJECT (element, "Sending event to a sink");
3813 res = gst_play_sink_send_event_to_sink (playsink, event);
3815 case GST_EVENT_STEP:
3820 gboolean flush, intermediate;
3821 gst_event_parse_step (event, &format, &amount, &rate, &flush,
3823 if (format == GST_FORMAT_BUFFERS) {
3824 /* for buffers, we will try to step video frames, for other formats we
3825 * send the step to all sinks */
3826 res = gst_play_sink_send_event_to_sink (playsink, event);
3829 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
3836 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
3843 static GstStateChangeReturn
3844 gst_play_sink_change_state (GstElement * element, GstStateChange transition)
3846 GstStateChangeReturn ret;
3847 GstStateChangeReturn bret;
3848 GstPlaySink *playsink;
3849 playsink = GST_PLAY_SINK (element);
3850 switch (transition) {
3851 case GST_STATE_CHANGE_READY_TO_PAUSED:
3852 playsink->need_async_start = TRUE;
3853 /* we want to go async to PAUSED until we managed to configure and add the
3855 do_async_start (playsink);
3856 ret = GST_STATE_CHANGE_ASYNC;
3858 case GST_STATE_CHANGE_PAUSED_TO_READY:
3859 /* unblock all pads here */
3860 GST_PLAY_SINK_LOCK (playsink);
3861 if (playsink->video_pad) {
3863 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3864 (playsink->video_pad)));
3865 if (gst_pad_is_blocked (opad)) {
3866 gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3867 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3869 gst_object_unref (opad);
3870 playsink->video_pad_blocked = FALSE;
3873 if (playsink->audio_pad) {
3875 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3876 (playsink->audio_pad)));
3877 if (gst_pad_is_blocked (opad)) {
3878 gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3879 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3881 gst_object_unref (opad);
3882 playsink->audio_pad_blocked = FALSE;
3885 if (playsink->text_pad) {
3887 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3888 (playsink->text_pad)));
3889 if (gst_pad_is_blocked (opad)) {
3890 gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3891 gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3893 gst_object_unref (opad);
3894 playsink->text_pad_blocked = FALSE;
3896 GST_PLAY_SINK_UNLOCK (playsink);
3898 case GST_STATE_CHANGE_READY_TO_NULL:
3899 if (playsink->audiochain && playsink->audiochain->sink_volume) {
3900 /* remove our links to the mute and volume elements when they were
3901 * provided by a sink */
3902 disconnect_chain (playsink->audiochain, playsink);
3903 playsink->audiochain->volume = NULL;
3904 playsink->audiochain->mute = NULL;
3907 if (playsink->audiochain && playsink->audiochain->ts_offset) {
3908 gst_object_unref (playsink->audiochain->ts_offset);
3909 playsink->audiochain->ts_offset = NULL;
3912 if (playsink->videochain && playsink->videochain->ts_offset) {
3913 gst_object_unref (playsink->videochain->ts_offset);
3914 playsink->videochain->ts_offset = NULL;
3917 GST_OBJECT_LOCK (playsink);
3918 if (playsink->xoverlay_element)
3919 gst_object_unref (playsink->xoverlay_element);
3920 playsink->xoverlay_element = NULL;
3922 if (playsink->colorbalance_element) {
3923 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
3924 G_CALLBACK (colorbalance_value_changed_cb), playsink);
3925 gst_object_unref (playsink->colorbalance_element);
3927 playsink->colorbalance_element = NULL;
3928 GST_OBJECT_UNLOCK (playsink);
3930 ret = GST_STATE_CHANGE_SUCCESS;
3933 /* all other state changes return SUCCESS by default, this value can be
3934 * overridden by the result of the children */
3935 ret = GST_STATE_CHANGE_SUCCESS;
3939 /* do the state change of the children */
3941 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->change_state (element,
3943 /* now look at the result of our children and adjust the return value */
3945 case GST_STATE_CHANGE_FAILURE:
3946 /* failure, we stop */
3947 goto activate_failed;
3948 case GST_STATE_CHANGE_NO_PREROLL:
3949 /* some child returned NO_PREROLL. This is strange but we never know. We
3950 * commit our async state change (if any) and return the NO_PREROLL */
3951 do_async_done (playsink);
3954 case GST_STATE_CHANGE_ASYNC:
3955 /* some child was async, return this */
3959 /* return our previously configured return value */
3963 switch (transition) {
3964 case GST_STATE_CHANGE_READY_TO_PAUSED:
3966 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
3967 /* FIXME Release audio device when we implement that */
3968 playsink->need_async_start = TRUE;
3970 case GST_STATE_CHANGE_PAUSED_TO_READY:{
3971 if (playsink->video_sinkpad_stream_synchronizer) {
3972 gst_element_release_request_pad (GST_ELEMENT_CAST
3973 (playsink->stream_synchronizer),
3974 playsink->video_sinkpad_stream_synchronizer);
3975 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3976 playsink->video_sinkpad_stream_synchronizer = NULL;
3977 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3978 playsink->video_srcpad_stream_synchronizer = NULL;
3980 if (playsink->audio_sinkpad_stream_synchronizer) {
3981 gst_element_release_request_pad (GST_ELEMENT_CAST
3982 (playsink->stream_synchronizer),
3983 playsink->audio_sinkpad_stream_synchronizer);
3984 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
3985 playsink->audio_sinkpad_stream_synchronizer = NULL;
3986 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
3987 playsink->audio_srcpad_stream_synchronizer = NULL;
3989 if (playsink->text_sinkpad_stream_synchronizer) {
3990 gst_element_release_request_pad (GST_ELEMENT_CAST
3991 (playsink->stream_synchronizer),
3992 playsink->text_sinkpad_stream_synchronizer);
3993 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
3994 playsink->text_sinkpad_stream_synchronizer = NULL;
3995 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
3996 playsink->text_srcpad_stream_synchronizer = NULL;
4000 case GST_STATE_CHANGE_READY_TO_NULL:
4001 /* remove sinks we added */
4002 if (playsink->videodeinterlacechain) {
4003 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
4005 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
4007 if (playsink->videochain) {
4008 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
4009 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
4011 if (playsink->audiochain) {
4012 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
4013 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
4015 if (playsink->vischain) {
4016 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
4017 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
4019 if (playsink->textchain) {
4020 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
4021 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
4023 do_async_done (playsink);
4024 /* when going to READY, keep elements around as long as possible,
4025 * so they may be re-used faster next time/url around.
4026 * when really going to NULL, clean up everything completely. */
4027 if (transition == GST_STATE_CHANGE_READY_TO_NULL) {
4029 /* Unparent the sinks to allow reuse */
4030 if (playsink->videochain && playsink->videochain->sink)
4031 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
4032 playsink->videochain->sink);
4033 if (playsink->audiochain && playsink->audiochain->sink)
4034 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
4035 playsink->audiochain->sink);
4036 if (playsink->textchain && playsink->textchain->sink)
4037 gst_bin_remove (GST_BIN_CAST (playsink->textchain->chain.bin),
4038 playsink->textchain->sink);
4039 if (playsink->audio_sink != NULL)
4040 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
4041 if (playsink->video_sink != NULL)
4042 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
4043 if (playsink->visualisation != NULL)
4044 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
4045 if (playsink->text_sink != NULL)
4046 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
4047 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
4048 playsink->videodeinterlacechain = NULL;
4049 free_chain ((GstPlayChain *) playsink->videochain);
4050 playsink->videochain = NULL;
4051 free_chain ((GstPlayChain *) playsink->audiochain);
4052 playsink->audiochain = NULL;
4053 free_chain ((GstPlayChain *) playsink->vischain);
4054 playsink->vischain = NULL;
4055 free_chain ((GstPlayChain *) playsink->textchain);
4056 playsink->textchain = NULL;
4066 GST_DEBUG_OBJECT (element,
4067 "element failed to change states -- activation problem?");
4068 return GST_STATE_CHANGE_FAILURE;
4073 gst_play_sink_set_property (GObject * object, guint prop_id,
4074 const GValue * value, GParamSpec * spec)
4076 GstPlaySink *playsink = GST_PLAY_SINK (object);
4079 gst_play_sink_set_flags (playsink, g_value_get_flags (value));
4082 gst_play_sink_set_volume (playsink, g_value_get_double (value));
4085 gst_play_sink_set_mute (playsink, g_value_get_boolean (value));
4087 case PROP_FONT_DESC:
4088 gst_play_sink_set_font_desc (playsink, g_value_get_string (value));
4090 case PROP_SUBTITLE_ENCODING:
4091 gst_play_sink_set_subtitle_encoding (playsink,
4092 g_value_get_string (value));
4094 case PROP_VIS_PLUGIN:
4095 gst_play_sink_set_vis_plugin (playsink, g_value_get_object (value));
4097 case PROP_AV_OFFSET:
4098 gst_play_sink_set_av_offset (playsink, g_value_get_int64 (value));
4100 case PROP_VIDEO_SINK:
4101 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_VIDEO,
4102 g_value_get_object (value));
4104 case PROP_AUDIO_SINK:
4105 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_AUDIO,
4106 g_value_get_object (value));
4108 case PROP_TEXT_SINK:
4109 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_TEXT,
4110 g_value_get_object (value));
4113 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
4119 gst_play_sink_get_property (GObject * object, guint prop_id,
4120 GValue * value, GParamSpec * spec)
4122 GstPlaySink *playsink = GST_PLAY_SINK (object);
4125 g_value_set_flags (value, gst_play_sink_get_flags (playsink));
4128 g_value_set_double (value, gst_play_sink_get_volume (playsink));
4131 g_value_set_boolean (value, gst_play_sink_get_mute (playsink));
4133 case PROP_FONT_DESC:
4134 g_value_take_string (value, gst_play_sink_get_font_desc (playsink));
4136 case PROP_SUBTITLE_ENCODING:
4137 g_value_take_string (value,
4138 gst_play_sink_get_subtitle_encoding (playsink));
4140 case PROP_VIS_PLUGIN:
4141 g_value_take_object (value, gst_play_sink_get_vis_plugin (playsink));
4144 gst_value_take_buffer (value, gst_play_sink_get_last_frame (playsink));
4146 case PROP_AV_OFFSET:
4147 g_value_set_int64 (value, gst_play_sink_get_av_offset (playsink));
4149 case PROP_VIDEO_SINK:
4150 g_value_take_object (value, gst_play_sink_get_sink (playsink,
4151 GST_PLAY_SINK_TYPE_VIDEO));
4153 case PROP_AUDIO_SINK:
4154 g_value_take_object (value, gst_play_sink_get_sink (playsink,
4155 GST_PLAY_SINK_TYPE_AUDIO));
4157 case PROP_TEXT_SINK:
4158 g_value_take_object (value, gst_play_sink_get_sink (playsink,
4159 GST_PLAY_SINK_TYPE_TEXT));
4162 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
4168 gst_play_sink_xoverlay_expose (GstXOverlay * overlay)
4170 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4171 GstXOverlay *xoverlay;
4173 GST_OBJECT_LOCK (playsink);
4174 if (playsink->xoverlay_element)
4175 xoverlay = GST_X_OVERLAY (gst_object_ref (playsink->xoverlay_element));
4178 GST_OBJECT_UNLOCK (playsink);
4181 gst_x_overlay_expose (xoverlay);
4182 gst_object_unref (xoverlay);
4187 gst_play_sink_xoverlay_handle_events (GstXOverlay * overlay,
4188 gboolean handle_events)
4190 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4191 GstXOverlay *xoverlay;
4193 GST_OBJECT_LOCK (playsink);
4194 if (playsink->xoverlay_element)
4195 xoverlay = GST_X_OVERLAY (gst_object_ref (playsink->xoverlay_element));
4198 GST_OBJECT_UNLOCK (playsink);
4200 playsink->xoverlay_handle_events_set = TRUE;
4201 playsink->xoverlay_handle_events = handle_events;
4204 gst_x_overlay_handle_events (xoverlay, handle_events);
4205 gst_object_unref (xoverlay);
4210 gst_play_sink_xoverlay_set_render_rectangle (GstXOverlay * overlay, gint x,
4211 gint y, gint width, gint height)
4213 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4214 GstXOverlay *xoverlay;
4216 GST_OBJECT_LOCK (playsink);
4217 if (playsink->xoverlay_element)
4218 xoverlay = GST_X_OVERLAY (gst_object_ref (playsink->xoverlay_element));
4221 GST_OBJECT_UNLOCK (playsink);
4223 playsink->xoverlay_render_rectangle_set = TRUE;
4224 playsink->xoverlay_x = x;
4225 playsink->xoverlay_y = y;
4226 playsink->xoverlay_width = width;
4227 playsink->xoverlay_height = height;
4230 gst_x_overlay_set_render_rectangle (xoverlay, x, y, width, height);
4231 gst_object_unref (xoverlay);
4236 gst_play_sink_xoverlay_set_window_handle (GstXOverlay * overlay,
4239 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4240 GstXOverlay *xoverlay;
4242 GST_OBJECT_LOCK (playsink);
4243 if (playsink->xoverlay_element)
4244 xoverlay = GST_X_OVERLAY (gst_object_ref (playsink->xoverlay_element));
4247 GST_OBJECT_UNLOCK (playsink);
4249 playsink->xoverlay_handle_set = TRUE;
4250 playsink->xoverlay_handle = handle;
4253 gst_x_overlay_set_window_handle (xoverlay, handle);
4254 gst_object_unref (xoverlay);
4259 gst_play_sink_xoverlay_init (gpointer g_iface, gpointer g_iface_data)
4261 GstXOverlayClass *iface = (GstXOverlayClass *) g_iface;
4262 iface->expose = gst_play_sink_xoverlay_expose;
4263 iface->handle_events = gst_play_sink_xoverlay_handle_events;
4264 iface->set_render_rectangle = gst_play_sink_xoverlay_set_render_rectangle;
4265 iface->set_window_handle = gst_play_sink_xoverlay_set_window_handle;
4269 gst_play_sink_implements_interface_supported (GstImplementsInterface * iface,
4272 if (type == GST_TYPE_X_OVERLAY || type == GST_TYPE_STREAM_VOLUME ||
4273 type == GST_TYPE_NAVIGATION || type == GST_TYPE_COLOR_BALANCE)
4280 gst_play_sink_implements_interface_init (gpointer g_iface,
4281 gpointer g_iface_data)
4283 GstImplementsInterfaceClass *iface = (GstImplementsInterfaceClass *) g_iface;
4284 iface->supported = gst_play_sink_implements_interface_supported;
4288 gst_play_sink_navigation_send_event (GstNavigation * navigation,
4289 GstStructure * structure)
4291 GstPlaySink *playsink = GST_PLAY_SINK (navigation);
4294 GST_PLAY_SINK_LOCK (playsink);
4295 if (playsink->videochain && playsink->videochain->chain.bin)
4296 bin = GST_BIN (gst_object_ref (playsink->videochain->chain.bin));
4297 GST_PLAY_SINK_UNLOCK (playsink);
4300 GstElement *nav = gst_bin_get_by_interface (bin, GST_TYPE_NAVIGATION);
4303 gst_navigation_send_event (GST_NAVIGATION (nav), structure);
4305 gst_object_unref (nav);
4308 gst_object_unref (bin);
4312 gst_structure_free (structure);
4316 gst_play_sink_navigation_init (gpointer g_iface, gpointer g_iface_data)
4318 GstNavigationInterface *iface = (GstNavigationInterface *) g_iface;
4320 iface->send_event = gst_play_sink_navigation_send_event;
4323 static const GList *
4324 gst_play_sink_colorbalance_list_channels (GstColorBalance * balance)
4326 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4328 return playsink->colorbalance_channels;
4332 gst_play_sink_colorbalance_set_value (GstColorBalance * balance,
4333 GstColorBalanceChannel * proxy, gint value)
4335 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4338 GstColorBalance *balance_element = NULL;
4340 GST_OBJECT_LOCK (playsink);
4341 if (playsink->colorbalance_element)
4343 GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
4344 GST_OBJECT_UNLOCK (playsink);
4346 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
4347 GstColorBalanceChannel *proxy_tmp = l->data;
4350 if (proxy_tmp != proxy)
4353 playsink->colorbalance_values[i] = value;
4355 if (balance_element) {
4356 GstColorBalanceChannel *channel = NULL;
4357 const GList *channels, *k;
4359 channels = gst_color_balance_list_channels (balance_element);
4360 for (k = channels; k; k = k->next) {
4361 GstColorBalanceChannel *tmp = l->data;
4363 if (g_strrstr (tmp->label, proxy->label)) {
4371 /* Convert to [0, 1] range */
4374 (gdouble) proxy->min_value) / ((gdouble) proxy->max_value -
4375 (gdouble) proxy->min_value);
4376 /* Convert to channel range */
4378 channel->min_value + new_val * ((gdouble) channel->max_value -
4379 (gdouble) channel->min_value);
4381 gst_color_balance_set_value (balance_element, channel,
4382 (gint) (new_val + 0.5));
4384 gst_object_unref (balance_element);
4387 gst_color_balance_value_changed (balance, proxy, value);
4393 gst_play_sink_colorbalance_get_value (GstColorBalance * balance,
4394 GstColorBalanceChannel * proxy)
4396 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4400 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
4401 GstColorBalanceChannel *proxy_tmp = l->data;
4403 if (proxy_tmp != proxy)
4406 return playsink->colorbalance_values[i];
4409 g_return_val_if_reached (0);
4412 static GstColorBalanceType
4413 gst_play_sink_colorbalance_get_balance_type (GstColorBalance * balance)
4415 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4416 GstColorBalance *balance_element = NULL;
4417 GstColorBalanceType t = GST_COLOR_BALANCE_SOFTWARE;
4419 GST_OBJECT_LOCK (playsink);
4420 if (playsink->colorbalance_element)
4422 GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
4423 GST_OBJECT_UNLOCK (playsink);
4425 if (balance_element) {
4426 t = gst_color_balance_get_balance_type (balance_element);
4427 gst_object_unref (balance_element);
4434 gst_play_sink_colorbalance_init (gpointer g_iface, gpointer g_iface_data)
4436 GstColorBalanceClass *iface = (GstColorBalanceClass *) g_iface;
4438 iface->list_channels = gst_play_sink_colorbalance_list_channels;
4439 iface->set_value = gst_play_sink_colorbalance_set_value;
4440 iface->get_value = gst_play_sink_colorbalance_get_value;
4441 iface->get_balance_type = gst_play_sink_colorbalance_get_balance_type;
4445 gst_play_sink_plugin_init (GstPlugin * plugin)
4447 GST_DEBUG_CATEGORY_INIT (gst_play_sink_debug, "playsink", 0, "play bin");
4448 return gst_element_register (plugin, "playsink", GST_RANK_NONE,
4449 GST_TYPE_PLAY_SINK);