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., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
28 #include <gst/gst-i18n-plugin.h>
29 #include <gst/pbutils/pbutils.h>
30 #include <gst/video/video.h>
31 #include <gst/audio/streamvolume.h>
32 #include <gst/video/colorbalance.h>
33 #include <gst/video/videooverlay.h>
34 #include <gst/video/navigation.h>
36 #include "gstplaysink.h"
37 #include "gststreamsynchronizer.h"
38 #include "gstplaysinkvideoconvert.h"
39 #include "gstplaysinkaudioconvert.h"
41 GST_DEBUG_CATEGORY_STATIC (gst_play_sink_debug);
42 #define GST_CAT_DEFAULT gst_play_sink_debug
44 #define VOLUME_MAX_DOUBLE 10.0
46 #define DEFAULT_FLAGS GST_PLAY_FLAG_AUDIO | GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_TEXT | \
47 GST_PLAY_FLAG_SOFT_VOLUME | GST_PLAY_FLAG_SOFT_COLORBALANCE
49 #define GST_PLAY_CHAIN(c) ((GstPlayChain *)(c))
53 * GstPlaySinkSendEventMode:
54 * @MODE_DEFAULT: default GstBin's send_event handling
55 * @MODE_FIRST: send event only to the first sink that return true
57 * Send event handling to use
63 } GstPlaySinkSendEventMode;
66 #define GST_TYPE_PLAY_SINK_SEND_EVENT_MODE (gst_play_sink_send_event_mode_get_type ())
68 gst_play_sink_send_event_mode_get_type (void)
70 static GType gtype = 0;
73 static const GEnumValue values[] = {
74 {MODE_DEFAULT, "Default GstBin's send_event handling (default)",
76 {MODE_FIRST, "Sends the event to sinks until the first one handles it",
81 gtype = g_enum_register_static ("GstPlaySinkSendEventMode", values);
86 /* holds the common data fields for the audio and video pipelines. We keep them
87 * in a structure to more easily have all the info available. */
90 GstPlaySink *playsink;
103 GstElement *volume; /* element with the volume property */
104 gboolean sink_volume; /* if the volume was provided by the sink */
105 gulong notify_volume_id;
106 GstElement *mute; /* element with the mute property */
107 gulong notify_mute_id;
109 GstElement *ts_offset;
115 GstPad *sinkpad, *srcpad;
117 GstElement *deinterlace;
118 } GstPlayVideoDeinterlaceChain;
128 GstElement *ts_offset;
137 GstElement *resample;
138 GstPad *blockpad; /* srcpad of queue, used for blocking the vis */
139 GstPad *vispeerpad; /* srcpad of resample, used for unlinking the vis */
140 GstPad *vissinkpad; /* visualisation sinkpad, */
142 GstPad *vissrcpad; /* visualisation srcpad, */
143 GstPad *srcpad; /* outgoing srcpad, used to connect to the next
152 GstElement *identity;
154 GstPad *videosinkpad;
156 GstPad *srcpad; /* outgoing srcpad, used to connect to the next
158 GstElement *sink; /* custom sink to receive subtitle buffers */
161 #define GST_PLAY_SINK_GET_LOCK(playsink) (&((GstPlaySink *)playsink)->lock)
162 #define GST_PLAY_SINK_LOCK(playsink) G_STMT_START { \
163 GST_LOG_OBJECT (playsink, "locking from thread %p", g_thread_self ()); \
164 g_rec_mutex_lock (GST_PLAY_SINK_GET_LOCK (playsink)); \
165 GST_LOG_OBJECT (playsink, "locked from thread %p", g_thread_self ()); \
167 #define GST_PLAY_SINK_UNLOCK(playsink) G_STMT_START { \
168 GST_LOG_OBJECT (playsink, "unlocking from thread %p", g_thread_self ()); \
169 g_rec_mutex_unlock (GST_PLAY_SINK_GET_LOCK (playsink)); \
172 #define PENDING_FLAG_SET(playsink, flagtype) \
173 ((playsink->pending_blocked_pads) |= (1 << flagtype))
174 #define PENDING_FLAG_UNSET(playsink, flagtype) \
175 ((playsink->pending_blocked_pads) &= ~(1 << flagtype))
176 #define PENDING_FLAG_IS_SET(playsink, flagtype) \
177 ((playsink->pending_blocked_pads) & (1 << flagtype))
178 #define PENDING_VIDEO_BLOCK(playsink) \
179 ((playsink->pending_blocked_pads) & (1 << GST_PLAY_SINK_TYPE_VIDEO_RAW | 1 << GST_PLAY_SINK_TYPE_VIDEO))
180 #define PENDING_AUDIO_BLOCK(playsink) \
181 ((playsink->pending_blocked_pads) & (1 << GST_PLAY_SINK_TYPE_AUDIO_RAW | 1 << GST_PLAY_SINK_TYPE_AUDIO))
182 #define PENDING_TEXT_BLOCK(playsink) \
183 PENDING_FLAG_IS_SET(playsink, GST_PLAY_SINK_TYPE_TEXT)
191 gboolean async_pending;
192 gboolean need_async_start;
196 GstStreamSynchronizer *stream_synchronizer;
199 GstPlayAudioChain *audiochain;
200 GstPlayVideoDeinterlaceChain *videodeinterlacechain;
201 GstPlayVideoChain *videochain;
202 GstPlayVisChain *vischain;
203 GstPlayTextChain *textchain;
207 gboolean audio_pad_raw;
208 gboolean audio_pad_blocked;
209 GstPad *audio_srcpad_stream_synchronizer;
210 GstPad *audio_sinkpad_stream_synchronizer;
211 gulong audio_block_id;
212 gulong audio_notify_caps_id;
214 GstElement *audio_tee;
215 GstPad *audio_tee_sink;
216 GstPad *audio_tee_asrc;
217 GstPad *audio_tee_vissrc;
220 gboolean video_pad_raw;
221 gboolean video_pad_blocked;
222 GstPad *video_srcpad_stream_synchronizer;
223 GstPad *video_sinkpad_stream_synchronizer;
224 gulong video_block_id;
225 gulong video_notify_caps_id;
228 gboolean text_pad_blocked;
229 GstPad *text_srcpad_stream_synchronizer;
230 GstPad *text_sinkpad_stream_synchronizer;
231 gulong text_block_id;
233 gulong vis_pad_block_id;
235 guint32 pending_blocked_pads;
238 GstElement *audio_sink;
239 GstElement *video_sink;
240 GstElement *visualisation;
241 GstElement *text_sink;
244 gchar *font_desc; /* font description */
245 gchar *subtitle_encoding; /* subtitle encoding */
246 guint connection_speed; /* connection speed in bits/sec (0 = unknown) */
248 gboolean volume_changed; /* volume/mute changed while no audiochain */
249 gboolean mute_changed; /* ... has been created yet */
251 GstPlaySinkSendEventMode send_event_mode;
252 gboolean force_aspect_ratio;
254 /* videooverlay proxy interface */
255 GstVideoOverlay *overlay_element; /* protected with LOCK */
256 gboolean overlay_handle_set;
257 guintptr overlay_handle;
258 gboolean overlay_render_rectangle_set;
259 gint overlay_x, overlay_y, overlay_width, overlay_height;
260 gboolean overlay_handle_events_set;
261 gboolean overlay_handle_events;
263 /* colorbalance proxy interface */
264 GstColorBalance *colorbalance_element;
265 GList *colorbalance_channels; /* CONTRAST, BRIGHTNESS, HUE, SATURATION */
266 gint colorbalance_values[4];
267 gulong colorbalance_value_changed_id;
269 /* sending audio/video flushes break stream changes when the pipeline
270 * is paused and played again in 0.10 */
272 gboolean video_custom_flush_finished;
273 gboolean video_ignore_wrong_state;
274 gboolean video_pending_flush;
276 gboolean audio_custom_flush_finished;
277 gboolean audio_ignore_wrong_state;
278 gboolean audio_pending_flush;
281 gboolean text_custom_flush_finished;
282 gboolean text_ignore_wrong_state;
283 gboolean text_pending_flush;
286 struct _GstPlaySinkClass
288 GstBinClass parent_class;
290 gboolean (*reconfigure) (GstPlaySink * playsink);
292 GstSample *(*convert_sample) (GstPlaySink * playsink, GstCaps * caps);
296 static GstStaticPadTemplate audiotemplate =
297 GST_STATIC_PAD_TEMPLATE ("audio_sink",
300 GST_STATIC_CAPS_ANY);
301 static GstStaticPadTemplate videotemplate =
302 GST_STATIC_PAD_TEMPLATE ("video_sink",
305 GST_STATIC_CAPS_ANY);
306 static GstStaticPadTemplate texttemplate = GST_STATIC_PAD_TEMPLATE ("text_sink",
309 GST_STATIC_CAPS_ANY);
311 /* FIXME 0.11: Remove */
312 static GstStaticPadTemplate audiorawtemplate =
313 GST_STATIC_PAD_TEMPLATE ("audio_raw_sink",
316 GST_STATIC_CAPS_ANY);
317 static GstStaticPadTemplate videorawtemplate =
318 GST_STATIC_PAD_TEMPLATE ("video_raw_sink",
321 GST_STATIC_CAPS_ANY);
332 PROP_SUBTITLE_ENCODING,
339 PROP_SEND_EVENT_MODE,
340 PROP_FORCE_ASPECT_RATIO,
350 static void gst_play_sink_dispose (GObject * object);
351 static void gst_play_sink_finalize (GObject * object);
352 static void gst_play_sink_set_property (GObject * object, guint prop_id,
353 const GValue * value, GParamSpec * spec);
354 static void gst_play_sink_get_property (GObject * object, guint prop_id,
355 GValue * value, GParamSpec * spec);
357 static GstPad *gst_play_sink_request_new_pad (GstElement * element,
358 GstPadTemplate * templ, const gchar * name, const GstCaps * caps);
359 static void gst_play_sink_release_request_pad (GstElement * element,
361 static gboolean gst_play_sink_send_event (GstElement * element,
363 static GstStateChangeReturn gst_play_sink_change_state (GstElement * element,
364 GstStateChange transition);
366 static void gst_play_sink_handle_message (GstBin * bin, GstMessage * message);
368 /* sending audio/video flushes break stream changes when the pipeline
369 * is paused and played again in 0.10 */
371 static gboolean gst_play_sink_video_sink_event (GstPad * pad, GstEvent * event);
372 static GstFlowReturn gst_play_sink_video_sink_chain (GstPad * pad,
374 static gboolean gst_play_sink_audio_sink_event (GstPad * pad, GstEvent * event);
375 static GstFlowReturn gst_play_sink_audio_sink_chain (GstPad * pad,
378 static gboolean gst_play_sink_text_sink_event (GstPad * pad, GstObject * parent,
380 static GstFlowReturn gst_play_sink_text_sink_chain (GstPad * pad,
381 GstObject * parent, GstBuffer * buffer);
383 static void notify_volume_cb (GObject * object, GParamSpec * pspec,
384 GstPlaySink * playsink);
385 static void notify_mute_cb (GObject * object, GParamSpec * pspec,
386 GstPlaySink * playsink);
388 static void update_av_offset (GstPlaySink * playsink);
390 static gboolean gst_play_sink_do_reconfigure (GstPlaySink * playsink);
392 static GQuark _playsink_reset_segment_event_marker_id = 0;
394 /* static guint gst_play_sink_signals[LAST_SIGNAL] = { 0 }; */
396 static void gst_play_sink_overlay_init (gpointer g_iface,
397 gpointer g_iface_data);
398 static void gst_play_sink_navigation_init (gpointer g_iface,
399 gpointer g_iface_data);
400 static void gst_play_sink_colorbalance_init (gpointer g_iface,
401 gpointer g_iface_data);
404 _do_init (GType type)
406 static const GInterfaceInfo svol_info = {
409 static const GInterfaceInfo ov_info = {
410 gst_play_sink_overlay_init,
413 static const GInterfaceInfo nav_info = {
414 gst_play_sink_navigation_init,
417 static const GInterfaceInfo col_info = {
418 gst_play_sink_colorbalance_init,
422 g_type_add_interface_static (type, GST_TYPE_STREAM_VOLUME, &svol_info);
423 g_type_add_interface_static (type, GST_TYPE_VIDEO_OVERLAY, &ov_info);
424 g_type_add_interface_static (type, GST_TYPE_NAVIGATION, &nav_info);
425 g_type_add_interface_static (type, GST_TYPE_COLOR_BALANCE, &col_info);
428 G_DEFINE_TYPE_WITH_CODE (GstPlaySink, gst_play_sink, GST_TYPE_BIN,
429 _do_init (g_define_type_id));
432 gst_play_sink_class_init (GstPlaySinkClass * klass)
434 GObjectClass *gobject_klass;
435 GstElementClass *gstelement_klass;
436 GstBinClass *gstbin_klass;
438 gobject_klass = (GObjectClass *) klass;
439 gstelement_klass = (GstElementClass *) klass;
440 gstbin_klass = (GstBinClass *) klass;
442 gobject_klass->dispose = gst_play_sink_dispose;
443 gobject_klass->finalize = gst_play_sink_finalize;
444 gobject_klass->set_property = gst_play_sink_set_property;
445 gobject_klass->get_property = gst_play_sink_get_property;
451 * Control the behaviour of playsink.
453 g_object_class_install_property (gobject_klass, PROP_FLAGS,
454 g_param_spec_flags ("flags", "Flags", "Flags to control behaviour",
455 GST_TYPE_PLAY_FLAGS, DEFAULT_FLAGS,
456 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
459 * GstPlaySink:volume:
461 * Get or set the current audio stream volume. 1.0 means 100%,
462 * 0.0 means mute. This uses a linear volume scale.
465 g_object_class_install_property (gobject_klass, PROP_VOLUME,
466 g_param_spec_double ("volume", "Volume", "The audio volume, 1.0=100%",
467 0.0, VOLUME_MAX_DOUBLE, 1.0,
468 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
469 g_object_class_install_property (gobject_klass, PROP_MUTE,
470 g_param_spec_boolean ("mute", "Mute",
471 "Mute the audio channel without changing the volume", FALSE,
472 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
473 g_object_class_install_property (gobject_klass, PROP_FONT_DESC,
474 g_param_spec_string ("subtitle-font-desc",
475 "Subtitle font description",
476 "Pango font description of font "
477 "to be used for subtitle rendering", NULL,
478 G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
479 g_object_class_install_property (gobject_klass, PROP_SUBTITLE_ENCODING,
480 g_param_spec_string ("subtitle-encoding", "subtitle encoding",
481 "Encoding to assume if input subtitles are not in UTF-8 encoding. "
482 "If not set, the GST_SUBTITLE_ENCODING environment variable will "
483 "be checked for an encoding to use. If that is not set either, "
484 "ISO-8859-15 will be assumed.", NULL,
485 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
486 g_object_class_install_property (gobject_klass, PROP_VIS_PLUGIN,
487 g_param_spec_object ("vis-plugin", "Vis plugin",
488 "the visualization element to use (NULL = default)",
489 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
491 * GstPlaySink:sample:
493 * Get the currently rendered or prerolled sample in the video sink.
494 * The #GstCaps in the sample will describe the format of the buffer.
496 g_object_class_install_property (gobject_klass, PROP_SAMPLE,
497 g_param_spec_boxed ("sample", "Sample",
498 "The last sample (NULL = no video available)",
499 GST_TYPE_SAMPLE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
501 * GstPlaySink:av-offset:
503 * Control the synchronisation offset between the audio and video streams.
504 * Positive values make the audio ahead of the video and negative values make
505 * the audio go behind the video.
509 g_object_class_install_property (gobject_klass, PROP_AV_OFFSET,
510 g_param_spec_int64 ("av-offset", "AV Offset",
511 "The synchronisation offset between audio and video in nanoseconds",
512 G_MININT64, G_MAXINT64, 0,
513 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
516 * GstPlaySink:video-sink:
518 * Set the used video sink element. NULL will use the default sink. playsink
519 * must be in %GST_STATE_NULL
523 g_object_class_install_property (gobject_klass, PROP_VIDEO_SINK,
524 g_param_spec_object ("video-sink", "Video Sink",
525 "the video output element to use (NULL = default sink)",
526 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
528 * GstPlaySink:audio-sink:
530 * Set the used audio sink element. NULL will use the default sink. playsink
531 * must be in %GST_STATE_NULL
535 g_object_class_install_property (gobject_klass, PROP_AUDIO_SINK,
536 g_param_spec_object ("audio-sink", "Audio Sink",
537 "the audio output element to use (NULL = default sink)",
538 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
541 * GstPlaySink:text-sink:
543 * Set the used text sink element. NULL will use the default sink. playsink
544 * must be in %GST_STATE_NULL
548 g_object_class_install_property (gobject_klass, PROP_TEXT_SINK,
549 g_param_spec_object ("text-sink", "Text sink",
550 "the text output element to use (NULL = default subtitleoverlay)",
551 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
554 * GstPlaySink::send-event-mode:
556 * Sets the handling method used for events received from send_event
557 * function. The default is %MODE_DEFAULT, that uses %GstBin's default
558 * handling (push the event to all internal sinks).
562 g_object_class_install_property (gobject_klass, PROP_SEND_EVENT_MODE,
563 g_param_spec_enum ("send-event-mode", "Send event mode",
564 "How to send events received in send_event function",
565 GST_TYPE_PLAY_SINK_SEND_EVENT_MODE, MODE_DEFAULT,
566 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
569 * GstPlaySink::force-aspect-ratio:
571 * Requests the video sink to enforce the video display aspect ratio.
575 g_object_class_install_property (gobject_klass, PROP_FORCE_ASPECT_RATIO,
576 g_param_spec_boolean ("force-aspect-ratio", "Force Aspect Ratio",
577 "When enabled, scaling will respect original aspect ratio", TRUE,
578 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
580 g_signal_new ("reconfigure", G_TYPE_FROM_CLASS (klass),
581 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstPlaySinkClass,
582 reconfigure), NULL, NULL, g_cclosure_marshal_generic, G_TYPE_BOOLEAN,
585 * GstPlaySink::convert-sample
586 * @playsink: a #GstPlaySink
587 * @caps: the target format of the sample
589 * Action signal to retrieve the currently playing video sample in the format
590 * specified by @caps.
591 * If @caps is %NULL, no conversion will be performed and this function is
592 * equivalent to the #GstPlaySink::sample property.
594 * Returns: a #GstSample of the current video sample converted to #caps.
595 * The caps in the sample will describe the final layout of the buffer data.
596 * %NULL is returned when no current sample can be retrieved or when the
599 g_signal_new ("convert-sample", G_TYPE_FROM_CLASS (klass),
600 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
601 G_STRUCT_OFFSET (GstPlaySinkClass, convert_sample), NULL, NULL,
602 g_cclosure_marshal_generic, GST_TYPE_SAMPLE, 1, GST_TYPE_CAPS);
604 gst_element_class_add_pad_template (gstelement_klass,
605 gst_static_pad_template_get (&audiorawtemplate));
606 gst_element_class_add_pad_template (gstelement_klass,
607 gst_static_pad_template_get (&audiotemplate));
608 gst_element_class_add_pad_template (gstelement_klass,
609 gst_static_pad_template_get (&videorawtemplate));
610 gst_element_class_add_pad_template (gstelement_klass,
611 gst_static_pad_template_get (&videotemplate));
612 gst_element_class_add_pad_template (gstelement_klass,
613 gst_static_pad_template_get (&texttemplate));
614 gst_element_class_set_static_metadata (gstelement_klass, "Player Sink",
616 "Convenience sink for multiple streams",
617 "Wim Taymans <wim.taymans@gmail.com>");
619 gstelement_klass->change_state =
620 GST_DEBUG_FUNCPTR (gst_play_sink_change_state);
621 gstelement_klass->send_event = GST_DEBUG_FUNCPTR (gst_play_sink_send_event);
622 gstelement_klass->request_new_pad =
623 GST_DEBUG_FUNCPTR (gst_play_sink_request_new_pad);
624 gstelement_klass->release_pad =
625 GST_DEBUG_FUNCPTR (gst_play_sink_release_request_pad);
627 gstbin_klass->handle_message =
628 GST_DEBUG_FUNCPTR (gst_play_sink_handle_message);
630 klass->reconfigure = GST_DEBUG_FUNCPTR (gst_play_sink_reconfigure);
631 klass->convert_sample = GST_DEBUG_FUNCPTR (gst_play_sink_convert_sample);
633 _playsink_reset_segment_event_marker_id =
634 g_quark_from_static_string ("gst-playsink-reset-segment-event-marker");
636 g_type_class_ref (GST_TYPE_STREAM_SYNCHRONIZER);
637 g_type_class_ref (GST_TYPE_COLOR_BALANCE_CHANNEL);
641 gst_play_sink_init (GstPlaySink * playsink)
643 GstColorBalanceChannel *channel;
646 playsink->video_sink = NULL;
647 playsink->audio_sink = NULL;
648 playsink->visualisation = NULL;
649 playsink->text_sink = NULL;
650 playsink->volume = 1.0;
651 playsink->font_desc = NULL;
652 playsink->subtitle_encoding = NULL;
653 playsink->flags = DEFAULT_FLAGS;
654 playsink->send_event_mode = MODE_DEFAULT;
655 playsink->force_aspect_ratio = TRUE;
657 playsink->stream_synchronizer =
658 g_object_new (GST_TYPE_STREAM_SYNCHRONIZER, NULL);
659 gst_bin_add (GST_BIN_CAST (playsink),
660 GST_ELEMENT_CAST (playsink->stream_synchronizer));
662 g_rec_mutex_init (&playsink->lock);
663 GST_OBJECT_FLAG_SET (playsink, GST_ELEMENT_FLAG_SINK);
666 GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
668 channel->label = g_strdup ("CONTRAST");
669 channel->min_value = -1000;
670 channel->max_value = 1000;
671 playsink->colorbalance_channels =
672 g_list_append (playsink->colorbalance_channels, channel);
673 playsink->colorbalance_values[0] = 0;
676 GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
678 channel->label = g_strdup ("BRIGHTNESS");
679 channel->min_value = -1000;
680 channel->max_value = 1000;
681 playsink->colorbalance_channels =
682 g_list_append (playsink->colorbalance_channels, channel);
683 playsink->colorbalance_values[1] = 0;
686 GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
688 channel->label = g_strdup ("HUE");
689 channel->min_value = -1000;
690 channel->max_value = 1000;
691 playsink->colorbalance_channels =
692 g_list_append (playsink->colorbalance_channels, channel);
693 playsink->colorbalance_values[2] = 0;
696 GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
698 channel->label = g_strdup ("SATURATION");
699 channel->min_value = -1000;
700 channel->max_value = 1000;
701 playsink->colorbalance_channels =
702 g_list_append (playsink->colorbalance_channels, channel);
703 playsink->colorbalance_values[3] = 0;
707 disconnect_audio_chain (GstPlayAudioChain * chain, GstPlaySink * playsink)
710 if (chain->notify_volume_id)
711 g_signal_handler_disconnect (chain->volume, chain->notify_volume_id);
712 if (chain->notify_mute_id)
713 g_signal_handler_disconnect (chain->mute, chain->notify_mute_id);
714 chain->notify_volume_id = chain->notify_mute_id = 0;
719 free_chain (GstPlayChain * chain)
723 gst_object_unref (chain->bin);
729 gst_play_sink_dispose (GObject * object)
731 GstPlaySink *playsink;
733 playsink = GST_PLAY_SINK (object);
735 if (playsink->audio_sink != NULL) {
736 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
737 gst_object_unref (playsink->audio_sink);
738 playsink->audio_sink = NULL;
740 if (playsink->video_sink != NULL) {
741 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
742 gst_object_unref (playsink->video_sink);
743 playsink->video_sink = NULL;
745 if (playsink->visualisation != NULL) {
746 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
747 gst_object_unref (playsink->visualisation);
748 playsink->visualisation = NULL;
750 if (playsink->text_sink != NULL) {
751 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
752 gst_object_unref (playsink->text_sink);
753 playsink->text_sink = NULL;
756 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
757 playsink->videodeinterlacechain = NULL;
758 free_chain ((GstPlayChain *) playsink->videochain);
759 playsink->videochain = NULL;
760 free_chain ((GstPlayChain *) playsink->audiochain);
761 playsink->audiochain = NULL;
762 free_chain ((GstPlayChain *) playsink->vischain);
763 playsink->vischain = NULL;
764 free_chain ((GstPlayChain *) playsink->textchain);
765 playsink->textchain = NULL;
767 if (playsink->audio_tee_sink) {
768 gst_object_unref (playsink->audio_tee_sink);
769 playsink->audio_tee_sink = NULL;
772 if (playsink->audio_tee_vissrc) {
773 gst_element_release_request_pad (playsink->audio_tee,
774 playsink->audio_tee_vissrc);
775 gst_object_unref (playsink->audio_tee_vissrc);
776 playsink->audio_tee_vissrc = NULL;
779 if (playsink->audio_tee_asrc) {
780 gst_element_release_request_pad (playsink->audio_tee,
781 playsink->audio_tee_asrc);
782 gst_object_unref (playsink->audio_tee_asrc);
783 playsink->audio_tee_asrc = NULL;
786 g_free (playsink->font_desc);
787 playsink->font_desc = NULL;
789 g_free (playsink->subtitle_encoding);
790 playsink->subtitle_encoding = NULL;
792 playsink->stream_synchronizer = NULL;
794 g_list_foreach (playsink->colorbalance_channels, (GFunc) gst_object_unref,
796 g_list_free (playsink->colorbalance_channels);
797 playsink->colorbalance_channels = NULL;
799 G_OBJECT_CLASS (gst_play_sink_parent_class)->dispose (object);
803 gst_play_sink_finalize (GObject * object)
805 GstPlaySink *playsink;
807 playsink = GST_PLAY_SINK (object);
809 g_rec_mutex_clear (&playsink->lock);
811 G_OBJECT_CLASS (gst_play_sink_parent_class)->finalize (object);
815 gst_play_sink_set_sink (GstPlaySink * playsink, GstPlaySinkType type,
818 GstElement **elem = NULL, *old = NULL;
820 GST_LOG ("Setting sink %" GST_PTR_FORMAT " as sink type %d", sink, type);
822 GST_PLAY_SINK_LOCK (playsink);
824 case GST_PLAY_SINK_TYPE_AUDIO:
825 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
826 elem = &playsink->audio_sink;
828 case GST_PLAY_SINK_TYPE_VIDEO:
829 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
830 elem = &playsink->video_sink;
832 case GST_PLAY_SINK_TYPE_TEXT:
833 elem = &playsink->text_sink;
841 gst_object_ref_sink (sink);
844 GST_PLAY_SINK_UNLOCK (playsink);
847 /* Set the old sink to NULL if it is not used any longer */
848 if (old != sink && !GST_OBJECT_PARENT (old))
849 gst_element_set_state (old, GST_STATE_NULL);
850 gst_object_unref (old);
855 gst_play_sink_get_sink (GstPlaySink * playsink, GstPlaySinkType type)
857 GstElement *result = NULL;
858 GstElement *elem = NULL, *chainp = NULL;
860 GST_PLAY_SINK_LOCK (playsink);
862 case GST_PLAY_SINK_TYPE_AUDIO:
863 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
865 GstPlayAudioChain *chain;
866 if ((chain = (GstPlayAudioChain *) playsink->audiochain))
867 chainp = chain->sink;
868 elem = playsink->audio_sink;
871 case GST_PLAY_SINK_TYPE_VIDEO:
872 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
874 GstPlayVideoChain *chain;
875 if ((chain = (GstPlayVideoChain *) playsink->videochain))
876 chainp = chain->sink;
877 elem = playsink->video_sink;
880 case GST_PLAY_SINK_TYPE_TEXT:
882 GstPlayTextChain *chain;
883 if ((chain = (GstPlayTextChain *) playsink->textchain))
884 chainp = chain->sink;
885 elem = playsink->text_sink;
892 /* we have an active chain with a sink, get the sink */
893 result = gst_object_ref (chainp);
895 /* nothing found, return last configured sink */
896 if (result == NULL && elem)
897 result = gst_object_ref (elem);
898 GST_PLAY_SINK_UNLOCK (playsink);
903 static GstPadProbeReturn
904 gst_play_sink_vis_blocked (GstPad * tee_pad, GstPadProbeInfo * info,
907 GstPlaySink *playsink;
908 GstPlayVisChain *chain;
910 playsink = GST_PLAY_SINK (user_data);
912 GST_PLAY_SINK_LOCK (playsink);
913 GST_DEBUG_OBJECT (playsink, "vis pad blocked");
914 /* now try to change the plugin in the running vis chain */
915 if (!(chain = (GstPlayVisChain *) playsink->vischain))
918 /* unlink the old plugin and unghost the pad */
919 gst_pad_unlink (chain->vispeerpad, chain->vissinkpad);
920 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad), NULL);
922 /* set the old plugin to NULL and remove */
923 gst_element_set_state (chain->vis, GST_STATE_NULL);
924 gst_bin_remove (GST_BIN_CAST (chain->chain.bin), chain->vis);
926 /* add new plugin and set state to playing */
927 chain->vis = playsink->visualisation;
928 gst_bin_add (GST_BIN_CAST (chain->chain.bin), chain->vis);
929 gst_element_set_state (chain->vis, GST_STATE_PLAYING);
932 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
933 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
936 gst_pad_link_full (chain->vispeerpad, chain->vissinkpad,
937 GST_PAD_LINK_CHECK_NOTHING);
938 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad),
942 playsink->vis_pad_block_id = 0;
944 GST_PLAY_SINK_UNLOCK (playsink);
946 /* remove the probe and unblock the pad */
947 return GST_PAD_PROBE_REMOVE;
951 gst_play_sink_set_vis_plugin (GstPlaySink * playsink, GstElement * vis)
953 GstPlayVisChain *chain;
955 /* setting NULL means creating the default vis plugin */
957 vis = gst_element_factory_make ("goom", "vis");
959 /* simply return if we don't have a vis plugin here */
963 GST_PLAY_SINK_LOCK (playsink);
964 /* first store the new vis */
965 if (playsink->visualisation)
966 gst_object_unref (playsink->visualisation);
968 gst_object_ref_sink (vis);
969 playsink->visualisation = vis;
971 /* now try to change the plugin in the running vis chain, if we have no chain,
972 * we don't bother, any future vis chain will be created with the new vis
974 if (!(chain = (GstPlayVisChain *) playsink->vischain))
977 /* block the pad, the next time the callback is called we can change the
978 * visualisation. It's possible that this never happens or that the pad was
979 * already blocked. If the callback never happens, we don't have new data so
980 * we don't need the new vis plugin. If the pad was already blocked, the
981 * function returns FALSE but the previous pad block will do the right thing
983 GST_DEBUG_OBJECT (playsink, "blocking vis pad");
984 if (!playsink->vis_pad_block_id && !playsink->audio_block_id
985 && !playsink->video_block_id && !playsink->text_block_id)
986 playsink->vis_pad_block_id =
987 gst_pad_add_probe (chain->blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
988 gst_play_sink_vis_blocked, playsink, NULL);
990 GST_PLAY_SINK_UNLOCK (playsink);
996 gst_play_sink_get_vis_plugin (GstPlaySink * playsink)
998 GstElement *result = NULL;
999 GstPlayVisChain *chain;
1001 GST_PLAY_SINK_LOCK (playsink);
1002 if ((chain = (GstPlayVisChain *) playsink->vischain)) {
1003 /* we have an active chain, get the sink */
1005 result = gst_object_ref (chain->vis);
1007 /* nothing found, return last configured sink */
1008 if (result == NULL && playsink->visualisation)
1009 result = gst_object_ref (playsink->visualisation);
1010 GST_PLAY_SINK_UNLOCK (playsink);
1016 gst_play_sink_set_volume (GstPlaySink * playsink, gdouble volume)
1018 GstPlayAudioChain *chain;
1020 GST_PLAY_SINK_LOCK (playsink);
1021 playsink->volume = volume;
1022 chain = (GstPlayAudioChain *) playsink->audiochain;
1023 if (chain && chain->volume) {
1024 GST_LOG_OBJECT (playsink, "elements: volume=%" GST_PTR_FORMAT ", mute=%"
1025 GST_PTR_FORMAT "; new volume=%.03f, mute=%d", chain->volume,
1026 chain->mute, volume, playsink->mute);
1027 /* if there is a mute element or we are not muted, set the volume */
1028 if (chain->mute || !playsink->mute)
1029 g_object_set (chain->volume, "volume", volume, NULL);
1031 GST_LOG_OBJECT (playsink, "no volume element");
1032 playsink->volume_changed = TRUE;
1034 GST_PLAY_SINK_UNLOCK (playsink);
1038 gst_play_sink_get_volume (GstPlaySink * playsink)
1041 GstPlayAudioChain *chain;
1043 GST_PLAY_SINK_LOCK (playsink);
1044 chain = (GstPlayAudioChain *) playsink->audiochain;
1045 result = playsink->volume;
1046 if (chain && chain->volume) {
1047 if (chain->mute || !playsink->mute) {
1048 g_object_get (chain->volume, "volume", &result, NULL);
1049 playsink->volume = result;
1052 GST_PLAY_SINK_UNLOCK (playsink);
1058 gst_play_sink_set_mute (GstPlaySink * playsink, gboolean mute)
1060 GstPlayAudioChain *chain;
1062 GST_PLAY_SINK_LOCK (playsink);
1063 playsink->mute = mute;
1064 chain = (GstPlayAudioChain *) playsink->audiochain;
1067 g_object_set (chain->mute, "mute", mute, NULL);
1068 } else if (chain->volume) {
1070 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
1072 g_object_set (chain->volume, "volume", (gdouble) playsink->volume,
1076 playsink->mute_changed = TRUE;
1078 GST_PLAY_SINK_UNLOCK (playsink);
1082 gst_play_sink_get_mute (GstPlaySink * playsink)
1085 GstPlayAudioChain *chain;
1087 GST_PLAY_SINK_LOCK (playsink);
1088 chain = (GstPlayAudioChain *) playsink->audiochain;
1089 if (chain && chain->mute) {
1090 g_object_get (chain->mute, "mute", &result, NULL);
1091 playsink->mute = result;
1093 result = playsink->mute;
1095 GST_PLAY_SINK_UNLOCK (playsink);
1101 post_missing_element_message (GstPlaySink * playsink, const gchar * name)
1105 msg = gst_missing_element_message_new (GST_ELEMENT_CAST (playsink), name);
1106 gst_element_post_message (GST_ELEMENT_CAST (playsink), msg);
1110 add_chain (GstPlayChain * chain, gboolean add)
1112 if (chain->added == add)
1116 gst_bin_add (GST_BIN_CAST (chain->playsink), chain->bin);
1118 gst_bin_remove (GST_BIN_CAST (chain->playsink), chain->bin);
1119 /* we don't want to lose our sink status */
1120 GST_OBJECT_FLAG_SET (chain->playsink, GST_ELEMENT_FLAG_SINK);
1129 activate_chain (GstPlayChain * chain, gboolean activate)
1133 if (chain->activated == activate)
1136 GST_OBJECT_LOCK (chain->playsink);
1137 state = GST_STATE_TARGET (chain->playsink);
1138 GST_OBJECT_UNLOCK (chain->playsink);
1141 gst_element_set_state (chain->bin, state);
1143 gst_element_set_state (chain->bin, GST_STATE_NULL);
1145 chain->activated = activate;
1151 element_is_sink (GstElement * element)
1155 GST_OBJECT_LOCK (element);
1156 is_sink = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_FLAG_SINK);
1157 GST_OBJECT_UNLOCK (element);
1159 GST_DEBUG_OBJECT (element, "is a sink: %s", (is_sink) ? "yes" : "no");
1164 element_has_property (GstElement * element, const gchar * pname, GType type)
1168 pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (element), pname);
1170 if (pspec == NULL) {
1171 GST_DEBUG_OBJECT (element, "no %s property", pname);
1175 if (type == G_TYPE_INVALID || type == pspec->value_type ||
1176 g_type_is_a (pspec->value_type, type)) {
1177 GST_DEBUG_OBJECT (element, "has %s property of type %s", pname,
1178 (type == G_TYPE_INVALID) ? "any type" : g_type_name (type));
1182 GST_WARNING_OBJECT (element, "has %s property, but property is of type %s "
1183 "and we expected it to be of type %s", pname,
1184 g_type_name (pspec->value_type), g_type_name (type));
1191 const gchar *prop_name;
1194 } FindPropertyHelper;
1197 find_property (const GValue * item, FindPropertyHelper * helper)
1199 GstElement *element = g_value_get_object (item);
1200 if (helper->need_sink && !element_is_sink (element)) {
1204 if (!element_has_property (element, helper->prop_name, helper->prop_type)) {
1208 GST_INFO_OBJECT (element, "found %s with %s property", helper->prop_name,
1209 (helper->need_sink) ? "sink" : "element");
1210 return 0; /* keep it */
1213 /* FIXME: why not move these functions into core? */
1214 /* find a sink in the hierarchy with a property named @name. This function does
1215 * not increase the refcount of the returned object and thus remains valid as
1216 * long as the bin is valid. */
1218 gst_play_sink_find_property_sinks (GstPlaySink * playsink, GstElement * obj,
1219 const gchar * name, GType expected_type)
1221 GstElement *result = NULL;
1224 if (element_has_property (obj, name, expected_type)) {
1226 } else if (GST_IS_BIN (obj)) {
1228 GValue item = { 0, };
1229 FindPropertyHelper helper = { name, expected_type, TRUE };
1231 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1232 found = gst_iterator_find_custom (it,
1233 (GCompareFunc) find_property, &item, &helper);
1234 gst_iterator_free (it);
1236 result = g_value_get_object (&item);
1237 /* we don't need the extra ref */
1238 g_value_unset (&item);
1244 /* find an object in the hierarchy with a property named @name */
1246 gst_play_sink_find_property (GstPlaySink * playsink, GstElement * obj,
1247 const gchar * name, GType expected_type)
1249 GstElement *result = NULL;
1252 if (GST_IS_BIN (obj)) {
1254 GValue item = { 0, };
1255 FindPropertyHelper helper = { name, expected_type, FALSE };
1257 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1258 found = gst_iterator_find_custom (it,
1259 (GCompareFunc) find_property, &item, &helper);
1260 gst_iterator_free (it);
1262 result = g_value_dup_object (&item);
1263 g_value_unset (&item);
1266 if (element_has_property (obj, name, expected_type)) {
1268 gst_object_ref (obj);
1275 do_async_start (GstPlaySink * playsink)
1277 GstMessage *message;
1279 if (!playsink->need_async_start) {
1280 GST_INFO_OBJECT (playsink, "no async_start needed");
1284 playsink->async_pending = TRUE;
1286 GST_INFO_OBJECT (playsink, "Sending async_start message");
1287 message = gst_message_new_async_start (GST_OBJECT_CAST (playsink));
1288 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1289 (playsink), message);
1293 do_async_done (GstPlaySink * playsink)
1295 GstMessage *message;
1297 if (playsink->async_pending) {
1298 GST_INFO_OBJECT (playsink, "Sending async_done message");
1300 gst_message_new_async_done (GST_OBJECT_CAST (playsink),
1301 GST_CLOCK_TIME_NONE);
1302 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1303 (playsink), message);
1305 playsink->async_pending = FALSE;
1308 playsink->need_async_start = FALSE;
1311 /* try to change the state of an element. This function returns the element when
1312 * the state change could be performed. When this function returns NULL an error
1313 * occured and the element is unreffed if @unref is TRUE. */
1315 try_element (GstPlaySink * playsink, GstElement * element, gboolean unref)
1317 GstStateChangeReturn ret;
1320 ret = gst_element_set_state (element, GST_STATE_READY);
1321 if (ret == GST_STATE_CHANGE_FAILURE) {
1322 GST_DEBUG_OBJECT (playsink, "failed state change..");
1323 gst_element_set_state (element, GST_STATE_NULL);
1325 gst_object_unref (element);
1332 /* make the element (bin) that contains the elements needed to perform
1333 * video deinterlacing. Only used for *raw* video streams.
1335 * +---------------------------------------+
1337 * | +----------+ +-----------+ |
1338 * | |colorspace| |deinterlace| |
1339 * | +-sink src-sink src-+ |
1340 * | | +----------+ +-----------+ | |
1342 * +---------------------------------------+
1345 static GstPlayVideoDeinterlaceChain *
1346 gen_video_deinterlace_chain (GstPlaySink * playsink)
1348 GstPlayVideoDeinterlaceChain *chain;
1351 GstElement *head = NULL, *prev = NULL;
1353 chain = g_new0 (GstPlayVideoDeinterlaceChain, 1);
1354 chain->chain.playsink = playsink;
1356 GST_DEBUG_OBJECT (playsink, "making video deinterlace chain %p", chain);
1358 /* create a bin to hold objects, as we create them we add them to this bin so
1359 * that when something goes wrong we only need to unref the bin */
1360 chain->chain.bin = gst_bin_new ("vdbin");
1361 bin = GST_BIN_CAST (chain->chain.bin);
1362 gst_object_ref_sink (bin);
1364 GST_DEBUG_OBJECT (playsink, "creating " COLORSPACE);
1365 chain->conv = gst_element_factory_make (COLORSPACE, "vdconv");
1366 if (chain->conv == NULL) {
1367 post_missing_element_message (playsink, COLORSPACE);
1368 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1369 (_("Missing element '%s' - check your GStreamer installation."),
1370 COLORSPACE), ("video rendering might fail"));
1372 gst_bin_add (bin, chain->conv);
1377 GST_DEBUG_OBJECT (playsink, "creating deinterlace");
1378 chain->deinterlace = gst_element_factory_make ("deinterlace", "deinterlace");
1379 if (chain->deinterlace == NULL) {
1380 chain->deinterlace =
1381 gst_element_factory_make ("avdeinterlace", "deinterlace");
1383 if (chain->deinterlace == NULL) {
1384 post_missing_element_message (playsink, "deinterlace");
1385 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1386 (_("Missing element '%s' - check your GStreamer installation."),
1387 "deinterlace"), ("deinterlacing won't work"));
1389 gst_bin_add (bin, chain->deinterlace);
1391 if (!gst_element_link_pads_full (prev, "src", chain->deinterlace, "sink",
1392 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1395 head = chain->deinterlace;
1397 prev = chain->deinterlace;
1401 pad = gst_element_get_static_pad (head, "sink");
1402 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1403 gst_object_unref (pad);
1405 chain->sinkpad = gst_ghost_pad_new_no_target ("sink", GST_PAD_SINK);
1409 pad = gst_element_get_static_pad (prev, "src");
1410 chain->srcpad = gst_ghost_pad_new ("src", pad);
1411 gst_object_unref (pad);
1413 chain->srcpad = gst_ghost_pad_new ("src", chain->sinkpad);
1416 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1417 gst_element_add_pad (chain->chain.bin, chain->srcpad);
1423 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1424 (NULL), ("Failed to configure the video deinterlace chain."));
1425 free_chain ((GstPlayChain *) chain);
1431 is_valid_color_balance_element (GstColorBalance * bal)
1433 gboolean have_brightness = FALSE;
1434 gboolean have_contrast = FALSE;
1435 gboolean have_hue = FALSE;
1436 gboolean have_saturation = FALSE;
1437 const GList *channels, *l;
1439 channels = gst_color_balance_list_channels (bal);
1440 for (l = channels; l; l = l->next) {
1441 GstColorBalanceChannel *ch = l->data;
1443 if (g_strrstr (ch->label, "BRIGHTNESS"))
1444 have_brightness = TRUE;
1445 else if (g_strrstr (ch->label, "CONTRAST"))
1446 have_contrast = TRUE;
1447 else if (g_strrstr (ch->label, "HUE"))
1449 else if (g_strrstr (ch->label, "SATURATION"))
1450 have_saturation = TRUE;
1453 return have_brightness && have_contrast && have_hue && have_saturation;
1457 iterate_color_balance_elements (const GValue * item, gpointer user_data)
1460 GstColorBalance *cb, **cb_out = user_data;
1462 cb = GST_COLOR_BALANCE (g_value_get_object (item));
1463 valid = is_valid_color_balance_element (cb);
1466 && gst_color_balance_get_balance_type (*cb_out) ==
1467 GST_COLOR_BALANCE_SOFTWARE) {
1468 gst_object_unref (*cb_out);
1469 *cb_out = GST_COLOR_BALANCE (gst_object_ref (cb));
1470 } else if (!*cb_out) {
1471 *cb_out = GST_COLOR_BALANCE (gst_object_ref (cb));
1476 static GstColorBalance *
1477 find_color_balance_element (GstElement * element)
1480 GstColorBalance *cb = NULL;
1482 if (GST_IS_COLOR_BALANCE (element)
1483 && is_valid_color_balance_element (GST_COLOR_BALANCE (element)))
1484 return GST_COLOR_BALANCE (gst_object_ref (element));
1485 else if (!GST_IS_BIN (element))
1488 it = gst_bin_iterate_all_by_interface (GST_BIN (element),
1489 GST_TYPE_COLOR_BALANCE);
1490 while (gst_iterator_foreach (it, iterate_color_balance_elements,
1491 &cb) == GST_ITERATOR_RESYNC)
1492 gst_iterator_resync (it);
1493 gst_iterator_free (it);
1499 colorbalance_value_changed_cb (GstColorBalance * balance,
1500 GstColorBalanceChannel * channel, gint value, GstPlaySink * playsink)
1505 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
1506 GstColorBalanceChannel *proxy = l->data;
1508 if (g_strrstr (channel->label, proxy->label)) {
1511 /* Convert to [0, 1] range */
1514 (gdouble) channel->min_value) / ((gdouble) channel->max_value -
1515 (gdouble) channel->min_value);
1516 /* Convert to proxy range */
1518 proxy->min_value + new_val * ((gdouble) proxy->max_value -
1519 (gdouble) proxy->min_value);
1520 playsink->colorbalance_values[i] = (gint) (0.5 + new_val);
1522 gst_color_balance_value_changed (GST_COLOR_BALANCE (playsink), proxy,
1523 playsink->colorbalance_values[i]);
1530 update_colorbalance (GstPlaySink * playsink)
1532 GstColorBalance *balance = NULL;
1536 GST_OBJECT_LOCK (playsink);
1537 if (playsink->colorbalance_element) {
1539 GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
1541 GST_OBJECT_UNLOCK (playsink);
1545 g_signal_handler_block (balance, playsink->colorbalance_value_changed_id);
1547 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
1548 GstColorBalanceChannel *proxy = l->data;
1549 GstColorBalanceChannel *channel = NULL;
1550 const GList *channels, *k;
1553 channels = gst_color_balance_list_channels (balance);
1554 for (k = channels; k; k = k->next) {
1555 GstColorBalanceChannel *tmp = k->data;
1557 if (g_strrstr (tmp->label, proxy->label)) {
1565 /* Convert to [0, 1] range */
1567 ((gdouble) playsink->colorbalance_values[i] -
1568 (gdouble) proxy->min_value) / ((gdouble) proxy->max_value -
1569 (gdouble) proxy->min_value);
1570 /* Convert to channel range */
1572 channel->min_value + new_val * ((gdouble) channel->max_value -
1573 (gdouble) channel->min_value);
1575 gst_color_balance_set_value (balance, channel, (gint) (new_val + 0.5));
1578 g_signal_handler_unblock (balance, playsink->colorbalance_value_changed_id);
1580 gst_object_unref (balance);
1583 /* make the element (bin) that contains the elements needed to perform
1586 * +------------------------------------------------------------+
1588 * | +-------+ +----------+ +----------+ +---------+ |
1589 * | | queue | |colorspace| |videoscale| |videosink| |
1590 * | +-sink src-sink src-sink src-sink | |
1591 * | | +-------+ +----------+ +----------+ +---------+ |
1593 * +------------------------------------------------------------+
1596 static GstPlayVideoChain *
1597 gen_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1599 GstPlayVideoChain *chain;
1602 GstElement *head = NULL, *prev = NULL, *elem = NULL;
1604 chain = g_new0 (GstPlayVideoChain, 1);
1605 chain->chain.playsink = playsink;
1606 chain->chain.raw = raw;
1608 GST_DEBUG_OBJECT (playsink, "making video chain %p", chain);
1610 if (playsink->video_sink) {
1611 GST_DEBUG_OBJECT (playsink, "trying configured videosink");
1612 chain->sink = try_element (playsink, playsink->video_sink, FALSE);
1614 /* only try fallback if no specific sink was chosen */
1615 if (chain->sink == NULL) {
1616 GST_DEBUG_OBJECT (playsink, "trying autovideosink");
1617 elem = gst_element_factory_make ("autovideosink", "videosink");
1618 chain->sink = try_element (playsink, elem, TRUE);
1620 if (chain->sink == NULL) {
1621 /* if default sink from config.h is different then try it too */
1622 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1623 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_VIDEOSINK);
1624 elem = gst_element_factory_make (DEFAULT_VIDEOSINK, "videosink");
1625 chain->sink = try_element (playsink, elem, TRUE);
1629 playsink->video_sink = gst_object_ref (chain->sink);
1631 if (chain->sink == NULL)
1635 /* if we can disable async behaviour of the sink, we can avoid adding a
1636 * queue for the audio chain. */
1638 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1641 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1642 async, GST_ELEMENT_NAME (elem));
1643 g_object_set (elem, "async", async, NULL);
1644 chain->async = async;
1646 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1647 chain->async = TRUE;
1650 /* Make sure the aspect ratio is kept */
1652 gst_play_sink_find_property_sinks (playsink, chain->sink,
1653 "force-aspect-ratio", G_TYPE_BOOLEAN);
1655 g_object_set (elem, "force-aspect-ratio", playsink->force_aspect_ratio,
1658 /* find ts-offset element */
1659 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1660 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1663 /* create a bin to hold objects, as we create them we add them to this bin so
1664 * that when something goes wrong we only need to unref the bin */
1665 chain->chain.bin = gst_bin_new ("vbin");
1666 bin = GST_BIN_CAST (chain->chain.bin);
1667 gst_object_ref_sink (bin);
1668 gst_bin_add (bin, chain->sink);
1670 /* Get the VideoOverlay element */
1672 GstVideoOverlay *overlay = NULL;
1674 GST_OBJECT_LOCK (playsink);
1675 if (playsink->overlay_element)
1676 gst_object_unref (playsink->overlay_element);
1677 playsink->overlay_element =
1678 GST_VIDEO_OVERLAY (gst_bin_get_by_interface (GST_BIN (chain->chain.bin),
1679 GST_TYPE_VIDEO_OVERLAY));
1680 if (playsink->overlay_element)
1681 overlay = GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
1682 GST_OBJECT_UNLOCK (playsink);
1685 if (playsink->overlay_handle_set)
1686 gst_video_overlay_set_window_handle (overlay, playsink->overlay_handle);
1687 if (playsink->overlay_handle_events_set)
1688 gst_video_overlay_handle_events (overlay,
1689 playsink->overlay_handle_events);
1690 if (playsink->overlay_render_rectangle_set)
1691 gst_video_overlay_set_render_rectangle (overlay,
1692 playsink->overlay_x, playsink->overlay_y,
1693 playsink->overlay_width, playsink->overlay_height);
1694 gst_object_unref (overlay);
1698 /* decouple decoder from sink, this improves playback quite a lot since the
1699 * decoder can continue while the sink blocks for synchronisation. We don't
1700 * need a lot of buffers as this consumes a lot of memory and we don't want
1701 * too little because else we would be context switching too quickly. */
1702 chain->queue = gst_element_factory_make ("queue", "vqueue");
1703 if (chain->queue == NULL) {
1704 post_missing_element_message (playsink, "queue");
1705 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1706 (_("Missing element '%s' - check your GStreamer installation."),
1707 "queue"), ("video rendering might be suboptimal"));
1711 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1712 "max-size-bytes", 0, "max-size-time", (gint64) 0, "silent", TRUE, NULL);
1713 gst_bin_add (bin, chain->queue);
1714 head = prev = chain->queue;
1717 GST_OBJECT_LOCK (playsink);
1718 if (playsink->colorbalance_element) {
1719 g_signal_handler_disconnect (playsink->colorbalance_element,
1720 playsink->colorbalance_value_changed_id);
1721 gst_object_unref (playsink->colorbalance_element);
1722 playsink->colorbalance_value_changed_id = 0;
1724 playsink->colorbalance_element = find_color_balance_element (chain->sink);
1725 if (playsink->colorbalance_element) {
1726 playsink->colorbalance_value_changed_id =
1727 g_signal_connect (playsink->colorbalance_element, "value-changed",
1728 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1730 GST_OBJECT_UNLOCK (playsink);
1732 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)
1733 || (!playsink->colorbalance_element
1734 && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE))) {
1735 gboolean use_converters = !(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO);
1736 gboolean use_balance = !playsink->colorbalance_element
1737 && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE);
1739 GST_DEBUG_OBJECT (playsink, "creating videoconverter");
1741 g_object_new (GST_TYPE_PLAY_SINK_VIDEO_CONVERT, "name", "vconv",
1742 "use-converters", use_converters, "use-balance", use_balance, NULL);
1744 GST_OBJECT_LOCK (playsink);
1745 if (use_balance && GST_PLAY_SINK_VIDEO_CONVERT (chain->conv)->balance) {
1746 playsink->colorbalance_element =
1747 GST_COLOR_BALANCE (gst_object_ref (GST_PLAY_SINK_VIDEO_CONVERT
1748 (chain->conv)->balance));
1749 playsink->colorbalance_value_changed_id =
1750 g_signal_connect (playsink->colorbalance_element, "value-changed",
1751 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1753 GST_OBJECT_UNLOCK (playsink);
1755 gst_bin_add (bin, chain->conv);
1757 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
1758 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1766 update_colorbalance (playsink);
1769 GST_DEBUG_OBJECT (playsink, "linking to sink");
1770 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
1771 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1775 pad = gst_element_get_static_pad (head, "sink");
1776 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1778 /* sending audio/video flushes break stream changes when the pipeline
1779 * is paused and played again in 0.10 */
1781 gst_pad_set_event_function (chain->sinkpad,
1782 GST_DEBUG_FUNCPTR (gst_play_sink_video_sink_event));
1783 gst_pad_set_chain_function (chain->sinkpad,
1784 GST_DEBUG_FUNCPTR (gst_play_sink_video_sink_chain));
1787 gst_object_unref (pad);
1788 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1795 if (!elem && !playsink->video_sink) {
1796 post_missing_element_message (playsink, "autovideosink");
1797 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1798 post_missing_element_message (playsink, DEFAULT_VIDEOSINK);
1799 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1800 (_("Both autovideosink and %s elements are missing."),
1801 DEFAULT_VIDEOSINK), (NULL));
1803 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1804 (_("The autovideosink element is missing.")), (NULL));
1807 if (playsink->video_sink) {
1808 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1809 (_("Configured videosink %s is not working."),
1810 GST_ELEMENT_NAME (playsink->video_sink)), (NULL));
1811 } else if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1812 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1813 (_("Both autovideosink and %s elements are not working."),
1814 DEFAULT_VIDEOSINK), (NULL));
1816 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1817 (_("The autovideosink element is not working.")), (NULL));
1820 free_chain ((GstPlayChain *) chain);
1826 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1827 (NULL), ("Failed to configure the video sink."));
1828 /* checking sink made it READY */
1829 gst_element_set_state (chain->sink, GST_STATE_NULL);
1830 /* Remove chain from the bin to allow reuse later */
1831 gst_bin_remove (bin, chain->sink);
1832 free_chain ((GstPlayChain *) chain);
1838 setup_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1841 GstPlayVideoChain *chain;
1842 GstStateChangeReturn ret;
1844 chain = playsink->videochain;
1846 chain->chain.raw = raw;
1848 /* if the chain was active we don't do anything */
1849 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
1852 /* try to set the sink element to READY again */
1853 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
1854 if (ret == GST_STATE_CHANGE_FAILURE)
1857 /* Get the VideoOverlay element */
1859 GstVideoOverlay *overlay = NULL;
1861 GST_OBJECT_LOCK (playsink);
1862 if (playsink->overlay_element)
1863 gst_object_unref (playsink->overlay_element);
1864 playsink->overlay_element =
1865 GST_VIDEO_OVERLAY (gst_bin_get_by_interface (GST_BIN (chain->chain.bin),
1866 GST_TYPE_VIDEO_OVERLAY));
1867 if (playsink->overlay_element)
1868 overlay = GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
1869 GST_OBJECT_UNLOCK (playsink);
1872 if (playsink->overlay_handle_set)
1873 gst_video_overlay_set_window_handle (overlay, playsink->overlay_handle);
1874 if (playsink->overlay_handle_events_set)
1875 gst_video_overlay_handle_events (overlay,
1876 playsink->overlay_handle_events);
1877 if (playsink->overlay_render_rectangle_set)
1878 gst_video_overlay_set_render_rectangle (overlay,
1879 playsink->overlay_x, playsink->overlay_y,
1880 playsink->overlay_width, playsink->overlay_height);
1881 gst_object_unref (overlay);
1885 /* find ts-offset element */
1886 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1887 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1890 /* if we can disable async behaviour of the sink, we can avoid adding a
1891 * queue for the audio chain. */
1893 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1896 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1897 async, GST_ELEMENT_NAME (elem));
1898 g_object_set (elem, "async", async, NULL);
1899 chain->async = async;
1901 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1902 chain->async = TRUE;
1905 /* Make sure the aspect ratio is kept */
1907 gst_play_sink_find_property_sinks (playsink, chain->sink,
1908 "force-aspect-ratio", G_TYPE_BOOLEAN);
1910 g_object_set (elem, "force-aspect-ratio", playsink->force_aspect_ratio,
1913 GST_OBJECT_LOCK (playsink);
1914 if (playsink->colorbalance_element) {
1915 g_signal_handler_disconnect (playsink->colorbalance_element,
1916 playsink->colorbalance_value_changed_id);
1917 playsink->colorbalance_value_changed_id = 0;
1918 gst_object_unref (playsink->colorbalance_element);
1920 playsink->colorbalance_element = find_color_balance_element (chain->sink);
1921 if (playsink->colorbalance_element) {
1922 playsink->colorbalance_value_changed_id =
1923 g_signal_connect (playsink->colorbalance_element, "value-changed",
1924 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1926 GST_OBJECT_UNLOCK (playsink);
1929 gboolean use_balance = !playsink->colorbalance_element
1930 && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE);
1932 g_object_set (chain->conv, "use-balance", use_balance, NULL);
1934 GST_OBJECT_LOCK (playsink);
1935 if (use_balance && GST_PLAY_SINK_VIDEO_CONVERT (chain->conv)->balance) {
1936 playsink->colorbalance_element =
1937 GST_COLOR_BALANCE (gst_object_ref (GST_PLAY_SINK_VIDEO_CONVERT
1938 (chain->conv)->balance));
1939 playsink->colorbalance_value_changed_id =
1940 g_signal_connect (playsink->colorbalance_element, "value-changed",
1941 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1943 GST_OBJECT_UNLOCK (playsink);
1946 update_colorbalance (playsink);
1952 gst_play_sink_sink_event (GstPad * pad, GstObject * parent, GstEvent * event,
1953 const gchar * sink_type,
1954 gboolean * sink_ignore_wrong_state,
1955 gboolean * sink_custom_flush_finished, gboolean * sink_pending_flush)
1957 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_object_get_parent (parent));
1959 const GstStructure *structure = gst_event_get_structure (event);
1961 if (GST_EVENT_TYPE (event) == GST_EVENT_CUSTOM_DOWNSTREAM_OOB && structure) {
1962 gchar *custom_flush;
1963 gchar *custom_flush_finish;
1965 custom_flush = g_strdup_printf ("playsink-custom-%s-flush", sink_type);
1966 custom_flush_finish =
1967 g_strdup_printf ("playsink-custom-%s-flush-finish", sink_type);
1968 if (strcmp (gst_structure_get_name (structure), custom_flush) == 0) {
1969 GST_DEBUG_OBJECT (pad,
1970 "Custom %s flush event received, marking to flush %s", sink_type,
1972 GST_PLAY_SINK_LOCK (playsink);
1973 *sink_ignore_wrong_state = TRUE;
1974 *sink_custom_flush_finished = FALSE;
1975 GST_PLAY_SINK_UNLOCK (playsink);
1976 } else if (strcmp (gst_structure_get_name (structure),
1977 custom_flush_finish) == 0) {
1978 GST_DEBUG_OBJECT (pad, "Custom %s flush finish event received",
1980 GST_PLAY_SINK_LOCK (playsink);
1981 *sink_pending_flush = TRUE;
1982 *sink_custom_flush_finished = TRUE;
1983 GST_PLAY_SINK_UNLOCK (playsink);
1986 g_free (custom_flush);
1987 g_free (custom_flush_finish);
1990 GST_DEBUG_OBJECT (pad, "Forwarding event %" GST_PTR_FORMAT, event);
1991 ret = gst_pad_event_default (pad, parent, gst_event_ref (event));
1993 gst_event_unref (event);
1994 gst_object_unref (playsink);
1998 static GstFlowReturn
1999 gst_play_sink_sink_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer,
2000 const gchar * sink_type,
2001 gboolean * sink_ignore_wrong_state,
2002 gboolean * sink_custom_flush_finished, gboolean * sink_pending_flush)
2004 GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2005 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2008 GST_PLAY_SINK_LOCK (playsink);
2010 if (*sink_pending_flush) {
2011 GstEvent *segment_event;
2013 GstStructure *structure;
2015 *sink_pending_flush = FALSE;
2017 GST_PLAY_SINK_UNLOCK (playsink);
2019 segment_event = gst_pad_get_sticky_event (pad, GST_EVENT_SEGMENT, 0);
2021 /* make the bin drop all cached data.
2022 * This event will be dropped on the src pad, if any. */
2023 event = gst_event_new_flush_start ();
2024 structure = gst_event_writable_structure (event);
2025 gst_structure_id_set (structure,
2026 _playsink_reset_segment_event_marker_id, G_TYPE_BOOLEAN, TRUE, NULL);
2028 GST_DEBUG_OBJECT (pad,
2029 "Pushing %s flush-start event with reset segment marker set: %"
2030 GST_PTR_FORMAT, sink_type, event);
2031 gst_pad_send_event (pad, event);
2033 /* make queue drop all cached data.
2034 * This event will be dropped on the src pad. */
2035 event = gst_event_new_flush_stop (TRUE);
2036 structure = gst_event_writable_structure (event);
2037 gst_structure_id_set (structure,
2038 _playsink_reset_segment_event_marker_id, G_TYPE_BOOLEAN, TRUE, NULL);
2040 GST_DEBUG_OBJECT (pad,
2041 "Pushing %s flush-stop event with reset segment marker set: %"
2042 GST_PTR_FORMAT, sink_type, event);
2043 gst_pad_send_event (pad, event);
2045 /* Re-sync queue segment info after flush-stop.
2046 * This event will be dropped on the src pad. */
2047 if (segment_event) {
2048 event = gst_event_copy (segment_event);
2049 structure = gst_event_writable_structure (event);
2050 gst_structure_id_set (structure,
2051 _playsink_reset_segment_event_marker_id, G_TYPE_BOOLEAN, TRUE, NULL);
2053 GST_DEBUG_OBJECT (playsink,
2054 "Pushing segment event with reset "
2055 "segment marker set: %" GST_PTR_FORMAT, event);
2056 gst_pad_send_event (pad, event);
2057 gst_event_unref (segment_event);
2060 GST_PLAY_SINK_UNLOCK (playsink);
2063 ret = gst_proxy_pad_chain_default (pad, parent, buffer);
2065 GST_PLAY_SINK_LOCK (playsink);
2066 if (ret == GST_FLOW_FLUSHING && *sink_ignore_wrong_state) {
2067 GST_DEBUG_OBJECT (pad, "Ignoring wrong state for %s during flush",
2069 if (*sink_custom_flush_finished) {
2070 GST_DEBUG_OBJECT (pad, "Custom flush finished, stop ignoring "
2071 "wrong state for %s", sink_type);
2072 *sink_ignore_wrong_state = FALSE;
2077 GST_PLAY_SINK_UNLOCK (playsink);
2079 gst_object_unref (playsink);
2080 gst_object_unref (tbin);
2084 /* sending audio/video flushes break stream changes when the pipeline
2085 * is paused and played again in 0.10 */
2088 gst_play_sink_video_sink_event (GstPad * pad, GstEvent * event)
2090 GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2091 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2094 ret = gst_play_sink_sink_event (pad, event, "video",
2095 &playsink->video_ignore_wrong_state,
2096 &playsink->video_custom_flush_finished,
2097 &playsink->video_pending_flush, &playsink->video_segment);
2099 gst_object_unref (playsink);
2100 gst_object_unref (tbin);
2104 static GstFlowReturn
2105 gst_play_sink_video_sink_chain (GstPad * pad, GstBuffer * buffer)
2107 GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2108 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2111 ret = gst_play_sink_sink_chain (pad, buffer, "video",
2112 &playsink->video_ignore_wrong_state,
2113 &playsink->video_custom_flush_finished,
2114 &playsink->video_pending_flush, &playsink->video_segment);
2116 gst_object_unref (playsink);
2117 gst_object_unref (tbin);
2122 gst_play_sink_audio_sink_event (GstPad * pad, GstEvent * event)
2124 GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2125 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2128 ret = gst_play_sink_sink_event (pad, event, "audio",
2129 &playsink->audio_ignore_wrong_state,
2130 &playsink->audio_custom_flush_finished,
2131 &playsink->audio_pending_flush, &playsink->audio_segment);
2133 gst_object_unref (playsink);
2134 gst_object_unref (tbin);
2138 static GstFlowReturn
2139 gst_play_sink_audio_sink_chain (GstPad * pad, GstBuffer * buffer)
2141 GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2142 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2145 ret = gst_play_sink_sink_chain (pad, buffer, "audio",
2146 &playsink->audio_ignore_wrong_state,
2147 &playsink->audio_custom_flush_finished,
2148 &playsink->audio_pending_flush, &playsink->audio_segment);
2150 gst_object_unref (playsink);
2151 gst_object_unref (tbin);
2157 gst_play_sink_text_sink_event (GstPad * pad, GstObject * parent,
2160 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_object_get_parent (parent));
2163 ret = gst_play_sink_sink_event (pad, parent, event, "subtitle",
2164 &playsink->text_ignore_wrong_state,
2165 &playsink->text_custom_flush_finished, &playsink->text_pending_flush);
2167 gst_object_unref (playsink);
2172 static GstFlowReturn
2173 gst_play_sink_text_sink_chain (GstPad * pad, GstObject * parent,
2177 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_object_get_parent (parent));
2179 ret = gst_play_sink_sink_chain (pad, parent, buffer, "subtitle",
2180 &playsink->text_ignore_wrong_state,
2181 &playsink->text_custom_flush_finished, &playsink->text_pending_flush);
2183 gst_object_unref (playsink);
2188 gst_play_sink_text_src_event (GstPad * pad, GstObject * parent,
2192 const GstStructure *structure;
2194 GST_DEBUG_OBJECT (pad, "Got event %" GST_PTR_FORMAT, event);
2196 structure = gst_event_get_structure (event);
2199 gst_structure_id_has_field (structure,
2200 _playsink_reset_segment_event_marker_id)) {
2201 /* the events marked with a reset segment marker
2202 * are sent internally to reset the queue and
2203 * must be dropped here */
2204 GST_DEBUG_OBJECT (pad, "Dropping event with reset "
2205 "segment marker set: %" GST_PTR_FORMAT, event);
2210 ret = gst_pad_event_default (pad, parent, gst_event_ref (event));
2213 gst_event_unref (event);
2217 /* make an element for playback of video with subtitles embedded.
2218 * Only used for *raw* video streams.
2220 * +--------------------------------------------+
2222 * | +--------+ +-----------------+ |
2223 * | | queue | | subtitleoverlay | |
2224 * video--src sink---video_sink | |
2225 * | +--------+ | src--src
2226 * text------------------text_sink | |
2227 * | +-----------------+ |
2228 * +--------------------------------------------+
2231 static GstPlayTextChain *
2232 gen_text_chain (GstPlaySink * playsink)
2234 GstPlayTextChain *chain;
2237 GstPad *videosinkpad, *textsinkpad, *srcpad;
2239 chain = g_new0 (GstPlayTextChain, 1);
2240 chain->chain.playsink = playsink;
2242 GST_DEBUG_OBJECT (playsink, "making text chain %p", chain);
2244 chain->chain.bin = gst_bin_new ("tbin");
2245 bin = GST_BIN_CAST (chain->chain.bin);
2246 gst_object_ref_sink (bin);
2248 videosinkpad = textsinkpad = srcpad = NULL;
2250 /* first try to hook the text pad to the custom sink */
2251 if (playsink->text_sink) {
2252 GST_DEBUG_OBJECT (playsink, "trying configured textsink");
2253 chain->sink = try_element (playsink, playsink->text_sink, FALSE);
2256 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
2259 /* make sure the sparse subtitles don't participate in the preroll */
2260 g_object_set (elem, "async", FALSE, NULL);
2261 GST_DEBUG_OBJECT (playsink, "adding custom text sink");
2262 gst_bin_add (bin, chain->sink);
2263 /* NOTE streamsynchronizer needs streams decoupled */
2264 /* make a little queue */
2265 chain->queue = gst_element_factory_make ("queue", "subqueue");
2266 if (chain->queue == NULL) {
2267 post_missing_element_message (playsink, "queue");
2268 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2269 (_("Missing element '%s' - check your GStreamer installation."),
2270 "queue"), ("rendering might be suboptimal"));
2272 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
2273 "max-size-bytes", 0, "max-size-time", (gint64) GST_SECOND,
2274 "silent", TRUE, NULL);
2275 gst_bin_add (bin, chain->queue);
2277 /* we have a custom sink, this will be our textsinkpad */
2278 if (gst_element_link_pads_full (chain->queue, "src", chain->sink,
2279 "sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
2280 /* we're all fine now and we can add the sink to the chain */
2281 GST_DEBUG_OBJECT (playsink, "using custom text sink");
2282 textsinkpad = gst_element_get_static_pad (chain->queue, "sink");
2284 GST_WARNING_OBJECT (playsink,
2285 "can't find a sink pad on custom text sink");
2286 gst_bin_remove (bin, chain->sink);
2287 gst_bin_remove (bin, chain->queue);
2289 chain->queue = NULL;
2291 /* try to set sync to true but it's no biggie when we can't */
2292 if (chain->sink && (elem =
2293 gst_play_sink_find_property_sinks (playsink, chain->sink,
2294 "sync", G_TYPE_BOOLEAN)))
2295 g_object_set (elem, "sync", TRUE, NULL);
2298 gst_bin_remove (bin, chain->sink);
2300 GST_WARNING_OBJECT (playsink,
2301 "can't find async property in custom text sink");
2304 if (textsinkpad == NULL) {
2305 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2306 (_("Custom text sink element is not usable.")),
2307 ("fallback to default subtitleoverlay"));
2311 if (textsinkpad == NULL) {
2312 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
2313 /* make a little queue */
2314 chain->queue = gst_element_factory_make ("queue", "vqueue");
2315 if (chain->queue == NULL) {
2316 post_missing_element_message (playsink, "queue");
2317 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2318 (_("Missing element '%s' - check your GStreamer installation."),
2319 "queue"), ("video rendering might be suboptimal"));
2321 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
2322 "max-size-bytes", 0, "max-size-time", (gint64) 0,
2323 "silent", TRUE, NULL);
2324 gst_bin_add (bin, chain->queue);
2325 videosinkpad = gst_element_get_static_pad (chain->queue, "sink");
2329 gst_element_factory_make ("subtitleoverlay", "suboverlay");
2330 if (chain->overlay == NULL) {
2331 post_missing_element_message (playsink, "subtitleoverlay");
2332 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2333 (_("Missing element '%s' - check your GStreamer installation."),
2334 "subtitleoverlay"), ("subtitle rendering disabled"));
2336 GstElement *element;
2338 gst_bin_add (bin, chain->overlay);
2340 g_object_set (G_OBJECT (chain->overlay), "silent", FALSE, NULL);
2341 if (playsink->font_desc) {
2342 g_object_set (G_OBJECT (chain->overlay), "font-desc",
2343 playsink->font_desc, NULL);
2345 if (playsink->subtitle_encoding) {
2346 g_object_set (G_OBJECT (chain->overlay), "subtitle-encoding",
2347 playsink->subtitle_encoding, NULL);
2350 gst_element_link_pads_full (chain->queue, "src", chain->overlay,
2351 "video_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS);
2353 /* make another little queue to decouple streams */
2354 element = gst_element_factory_make ("queue", "subqueue");
2355 if (element == NULL) {
2356 post_missing_element_message (playsink, "queue");
2357 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2358 (_("Missing element '%s' - check your GStreamer installation."),
2359 "queue"), ("rendering might be suboptimal"));
2361 g_object_set (G_OBJECT (element), "max-size-buffers", 3,
2362 "max-size-bytes", 0, "max-size-time", (gint64) GST_SECOND,
2363 "silent", TRUE, NULL);
2364 gst_bin_add (bin, element);
2365 if (gst_element_link_pads_full (element, "src", chain->overlay,
2366 "subtitle_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
2367 textsinkpad = gst_element_get_static_pad (element, "sink");
2368 srcpad = gst_element_get_static_pad (chain->overlay, "src");
2370 gst_bin_remove (bin, chain->sink);
2371 gst_bin_remove (bin, chain->overlay);
2373 chain->overlay = NULL;
2374 gst_object_unref (videosinkpad);
2375 videosinkpad = NULL;
2382 if (videosinkpad == NULL) {
2383 /* if we still don't have a videosink, we don't have an overlay. the only
2384 * thing we can do is insert an identity and ghost the src
2386 chain->identity = gst_element_factory_make ("identity", "tidentity");
2387 if (chain->identity == NULL) {
2388 post_missing_element_message (playsink, "identity");
2389 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2390 (_("Missing element '%s' - check your GStreamer installation."),
2391 "identity"), (NULL));
2393 g_object_set (chain->identity, "signal-handoffs", FALSE, NULL);
2394 g_object_set (chain->identity, "silent", TRUE, NULL);
2395 gst_bin_add (bin, chain->identity);
2396 srcpad = gst_element_get_static_pad (chain->identity, "src");
2397 videosinkpad = gst_element_get_static_pad (chain->identity, "sink");
2401 /* expose the ghostpads */
2403 chain->videosinkpad = gst_ghost_pad_new ("sink", videosinkpad);
2404 gst_object_unref (videosinkpad);
2405 gst_element_add_pad (chain->chain.bin, chain->videosinkpad);
2408 chain->textsinkpad = gst_ghost_pad_new ("text_sink", textsinkpad);
2409 gst_object_unref (textsinkpad);
2411 gst_pad_set_event_function (chain->textsinkpad,
2412 GST_DEBUG_FUNCPTR (gst_play_sink_text_sink_event));
2413 gst_pad_set_chain_function (chain->textsinkpad,
2414 GST_DEBUG_FUNCPTR (gst_play_sink_text_sink_chain));
2416 gst_element_add_pad (chain->chain.bin, chain->textsinkpad);
2419 chain->srcpad = gst_ghost_pad_new ("src", srcpad);
2420 gst_object_unref (srcpad);
2422 gst_pad_set_event_function (chain->srcpad,
2423 GST_DEBUG_FUNCPTR (gst_play_sink_text_src_event));
2425 gst_element_add_pad (chain->chain.bin, chain->srcpad);
2432 notify_volume_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
2436 g_object_get (object, "volume", &vol, NULL);
2437 playsink->volume = vol;
2439 g_object_notify (G_OBJECT (playsink), "volume");
2443 notify_mute_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
2447 g_object_get (object, "mute", &mute, NULL);
2448 playsink->mute = mute;
2450 g_object_notify (G_OBJECT (playsink), "mute");
2453 /* make the chain that contains the elements needed to perform
2456 * We add a tee as the first element so that we can link the visualisation chain
2457 * to it when requested.
2459 * +-------------------------------------------------------------+
2461 * | +---------+ +----------+ +---------+ +---------+ |
2462 * | |audioconv| |audioscale| | volume | |audiosink| |
2463 * | +-srck src-sink src-sink src-sink | |
2464 * | | +---------+ +----------+ +---------+ +---------+ |
2466 * +-------------------------------------------------------------+
2468 static GstPlayAudioChain *
2469 gen_audio_chain (GstPlaySink * playsink, gboolean raw)
2471 GstPlayAudioChain *chain;
2473 gboolean have_volume;
2475 GstElement *head, *prev, *elem = NULL;
2477 chain = g_new0 (GstPlayAudioChain, 1);
2478 chain->chain.playsink = playsink;
2479 chain->chain.raw = raw;
2481 GST_DEBUG_OBJECT (playsink, "making audio chain %p", chain);
2483 if (playsink->audio_sink) {
2484 GST_DEBUG_OBJECT (playsink, "trying configured audiosink %" GST_PTR_FORMAT,
2485 playsink->audio_sink);
2486 chain->sink = try_element (playsink, playsink->audio_sink, FALSE);
2488 /* only try fallback if no specific sink was chosen */
2489 if (chain->sink == NULL) {
2490 GST_DEBUG_OBJECT (playsink, "trying autoaudiosink");
2491 elem = gst_element_factory_make ("autoaudiosink", "audiosink");
2492 chain->sink = try_element (playsink, elem, TRUE);
2494 if (chain->sink == NULL) {
2495 /* if default sink from config.h is different then try it too */
2496 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
2497 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_AUDIOSINK);
2498 elem = gst_element_factory_make (DEFAULT_AUDIOSINK, "audiosink");
2499 chain->sink = try_element (playsink, elem, TRUE);
2503 playsink->audio_sink = gst_object_ref (chain->sink);
2505 if (chain->sink == NULL)
2508 chain->chain.bin = gst_bin_new ("abin");
2509 bin = GST_BIN_CAST (chain->chain.bin);
2510 gst_object_ref_sink (bin);
2511 gst_bin_add (bin, chain->sink);
2513 /* we have to add a queue when we need to decouple for the video sink in
2514 * visualisations and for streamsynchronizer */
2515 GST_DEBUG_OBJECT (playsink, "adding audio queue");
2516 chain->queue = gst_element_factory_make ("queue", "aqueue");
2517 if (chain->queue == NULL) {
2518 post_missing_element_message (playsink, "queue");
2519 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2520 (_("Missing element '%s' - check your GStreamer installation."),
2521 "queue"), ("audio playback and visualizations might not work"));
2525 g_object_set (chain->queue, "silent", TRUE, NULL);
2526 gst_bin_add (bin, chain->queue);
2527 prev = head = chain->queue;
2530 /* find ts-offset element */
2531 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
2532 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
2535 /* check if the sink, or something within the sink, has the volume property.
2536 * If it does we don't need to add a volume element. */
2538 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
2540 chain->notify_volume_id = chain->notify_mute_id = 0;
2542 chain->volume = elem;
2544 chain->notify_volume_id = g_signal_connect (chain->volume, "notify::volume",
2545 G_CALLBACK (notify_volume_cb), playsink);
2547 GST_DEBUG_OBJECT (playsink, "the sink has a volume property");
2549 chain->sink_volume = TRUE;
2550 /* if the sink also has a mute property we can use this as well. We'll only
2551 * use the mute property if there is a volume property. We can simulate the
2552 * mute with the volume otherwise. */
2554 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
2557 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
2558 chain->notify_mute_id = g_signal_connect (chain->mute, "notify::mute",
2559 G_CALLBACK (notify_mute_cb), playsink);
2561 /* use the sink to control the volume and mute */
2562 if (playsink->volume_changed) {
2563 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2564 playsink->volume_changed = FALSE;
2566 if (playsink->mute_changed) {
2568 g_object_set (chain->mute, "mute", playsink->mute, NULL);
2571 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
2573 playsink->mute_changed = FALSE;
2576 /* no volume, we need to add a volume element when we can */
2577 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
2578 have_volume = FALSE;
2579 chain->sink_volume = FALSE;
2582 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO) || (!have_volume
2583 && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME))) {
2584 gboolean use_converters = !(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO);
2585 gboolean use_volume =
2586 !have_volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME);
2587 GST_DEBUG_OBJECT (playsink,
2588 "creating audioconvert with use-converters %d, use-volume %d",
2589 use_converters, use_volume);
2591 g_object_new (GST_TYPE_PLAY_SINK_AUDIO_CONVERT, "name", "aconv",
2592 "use-converters", use_converters, "use-volume", use_volume, NULL);
2593 gst_bin_add (bin, chain->conv);
2595 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
2596 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
2603 if (!have_volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
2604 GstPlaySinkAudioConvert *conv =
2605 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
2608 chain->volume = conv->volume;
2611 chain->notify_volume_id =
2612 g_signal_connect (chain->volume, "notify::volume",
2613 G_CALLBACK (notify_volume_cb), playsink);
2615 /* volume also has the mute property */
2616 chain->mute = chain->volume;
2617 chain->notify_mute_id = g_signal_connect (chain->mute, "notify::mute",
2618 G_CALLBACK (notify_mute_cb), playsink);
2620 /* configure with the latest volume and mute */
2621 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume,
2623 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
2629 /* we only have to link to the previous element if we have something in
2630 * front of the sink */
2631 GST_DEBUG_OBJECT (playsink, "linking to sink");
2632 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
2633 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
2637 /* post a warning if we have no way to configure the volume */
2639 GST_ELEMENT_WARNING (playsink, STREAM, NOT_IMPLEMENTED,
2640 (_("No volume control found")), ("Volume/mute is not available"));
2643 /* and ghost the sinkpad of the headmost element */
2644 GST_DEBUG_OBJECT (playsink, "ghosting sink pad");
2645 pad = gst_element_get_static_pad (head, "sink");
2646 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
2648 /* sending audio/video flushes break stream changes when the pipeline
2649 * is paused and played again in 0.10 */
2651 gst_pad_set_event_function (chain->sinkpad,
2652 GST_DEBUG_FUNCPTR (gst_play_sink_audio_sink_event));
2653 gst_pad_set_chain_function (chain->sinkpad,
2654 GST_DEBUG_FUNCPTR (gst_play_sink_audio_sink_chain));
2657 gst_object_unref (pad);
2658 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
2665 if (!elem && !playsink->audio_sink) {
2666 post_missing_element_message (playsink, "autoaudiosink");
2667 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
2668 post_missing_element_message (playsink, DEFAULT_AUDIOSINK);
2669 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2670 (_("Both autoaudiosink and %s elements are missing."),
2671 DEFAULT_AUDIOSINK), (NULL));
2673 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2674 (_("The autoaudiosink element is missing.")), (NULL));
2677 if (playsink->audio_sink) {
2678 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2679 (_("Configured audiosink %s is not working."),
2680 GST_ELEMENT_NAME (playsink->audio_sink)), (NULL));
2681 } else if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
2682 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2683 (_("Both autoaudiosink and %s elements are not working."),
2684 DEFAULT_AUDIOSINK), (NULL));
2686 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2687 (_("The autoaudiosink element is not working.")), (NULL));
2690 free_chain ((GstPlayChain *) chain);
2695 GST_ELEMENT_ERROR (playsink, CORE, PAD,
2696 (NULL), ("Failed to configure the audio sink."));
2697 /* checking sink made it READY */
2698 gst_element_set_state (chain->sink, GST_STATE_NULL);
2699 /* Remove chain from the bin to allow reuse later */
2700 gst_bin_remove (bin, chain->sink);
2701 free_chain ((GstPlayChain *) chain);
2707 setup_audio_chain (GstPlaySink * playsink, gboolean raw)
2710 GstPlayAudioChain *chain;
2711 GstStateChangeReturn ret;
2712 GstPlaySinkAudioConvert *conv;
2714 chain = playsink->audiochain;
2715 conv = GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
2717 chain->chain.raw = raw;
2719 /* if the chain was active we don't do anything */
2720 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
2723 /* try to set the sink element to READY again */
2724 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
2725 if (ret == GST_STATE_CHANGE_FAILURE)
2728 /* find ts-offset element */
2729 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
2730 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
2733 /* Disconnect signals */
2734 disconnect_audio_chain (chain, playsink);
2736 /* check if the sink, or something within the sink, has the volume property.
2737 * If it does we don't need to add a volume element. */
2739 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
2742 chain->volume = elem;
2744 if (playsink->volume_changed) {
2745 GST_DEBUG_OBJECT (playsink, "the sink has a volume property, setting %f",
2747 /* use the sink to control the volume */
2748 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2749 playsink->volume_changed = FALSE;
2752 chain->notify_volume_id = g_signal_connect (chain->volume, "notify::volume",
2753 G_CALLBACK (notify_volume_cb), playsink);
2754 /* if the sink also has a mute property we can use this as well. We'll only
2755 * use the mute property if there is a volume property. We can simulate the
2756 * mute with the volume otherwise. */
2758 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
2761 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
2762 chain->notify_mute_id = g_signal_connect (chain->mute, "notify::mute",
2763 G_CALLBACK (notify_mute_cb), playsink);
2766 g_object_set (chain->conv, "use-volume", FALSE, NULL);
2768 /* no volume, we need to add a volume element when we can */
2769 g_object_set (chain->conv, "use-volume",
2770 ! !(playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME), NULL);
2771 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
2773 if (conv->volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
2774 chain->volume = conv->volume;
2775 chain->mute = chain->volume;
2777 chain->notify_volume_id =
2778 g_signal_connect (chain->volume, "notify::volume",
2779 G_CALLBACK (notify_volume_cb), playsink);
2781 chain->notify_mute_id = g_signal_connect (chain->mute, "notify::mute",
2782 G_CALLBACK (notify_mute_cb), playsink);
2784 /* configure with the latest volume and mute */
2785 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2786 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
2789 GST_DEBUG_OBJECT (playsink, "reusing existing volume element");
2795 * +-------------------------------------------------------------------+
2797 * | +----------+ +------------+ +----------+ +-------+ |
2798 * | | visqueue | | audioconv | | audiores | | vis | |
2799 * | +-sink src-sink + samp src-sink src-sink src-+ |
2800 * | | +----------+ +------------+ +----------+ +-------+ | |
2802 * +-------------------------------------------------------------------+
2805 static GstPlayVisChain *
2806 gen_vis_chain (GstPlaySink * playsink)
2808 GstPlayVisChain *chain;
2814 chain = g_new0 (GstPlayVisChain, 1);
2815 chain->chain.playsink = playsink;
2817 GST_DEBUG_OBJECT (playsink, "making vis chain %p", chain);
2819 chain->chain.bin = gst_bin_new ("visbin");
2820 bin = GST_BIN_CAST (chain->chain.bin);
2821 gst_object_ref_sink (bin);
2823 /* we're queuing raw audio here, we can remove this queue when we can disable
2824 * async behaviour in the video sink. */
2825 chain->queue = gst_element_factory_make ("queue", "visqueue");
2826 if (chain->queue == NULL)
2828 g_object_set (chain->queue, "silent", TRUE, NULL);
2829 gst_bin_add (bin, chain->queue);
2831 chain->conv = gst_element_factory_make ("audioconvert", "aconv");
2832 if (chain->conv == NULL)
2833 goto no_audioconvert;
2834 gst_bin_add (bin, chain->conv);
2836 chain->resample = gst_element_factory_make ("audioresample", "aresample");
2837 if (chain->resample == NULL)
2838 goto no_audioresample;
2839 gst_bin_add (bin, chain->resample);
2841 /* this pad will be used for blocking the dataflow and switching the vis
2842 * plugin, we block right after the queue, this makes it possible for the
2843 * resample and convert to convert to a format supported by the new vis
2845 chain->blockpad = gst_element_get_static_pad (chain->queue, "src");
2846 /* this is the pad where the vis is linked to */
2847 chain->vispeerpad = gst_element_get_static_pad (chain->resample, "src");
2849 if (playsink->visualisation) {
2850 GST_DEBUG_OBJECT (playsink, "trying configure vis");
2851 chain->vis = try_element (playsink, playsink->visualisation, FALSE);
2853 if (chain->vis == NULL) {
2854 GST_DEBUG_OBJECT (playsink, "trying goom");
2855 elem = gst_element_factory_make ("goom", "vis");
2856 chain->vis = try_element (playsink, elem, TRUE);
2858 if (chain->vis == NULL)
2861 gst_bin_add (bin, chain->vis);
2863 res = gst_element_link_pads_full (chain->queue, "src", chain->conv, "sink",
2864 GST_PAD_LINK_CHECK_NOTHING);
2866 gst_element_link_pads_full (chain->conv, "src", chain->resample, "sink",
2867 GST_PAD_LINK_CHECK_NOTHING);
2869 gst_element_link_pads_full (chain->resample, "src", chain->vis, "sink",
2870 GST_PAD_LINK_CHECK_NOTHING);
2874 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
2875 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
2877 pad = gst_element_get_static_pad (chain->queue, "sink");
2878 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
2879 gst_object_unref (pad);
2880 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
2882 chain->srcpad = gst_ghost_pad_new ("src", chain->vissrcpad);
2883 gst_element_add_pad (chain->chain.bin, chain->srcpad);
2890 post_missing_element_message (playsink, "queue");
2891 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2892 (_("Missing element '%s' - check your GStreamer installation."),
2894 free_chain ((GstPlayChain *) chain);
2899 post_missing_element_message (playsink, "audioconvert");
2900 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2901 (_("Missing element '%s' - check your GStreamer installation."),
2902 "audioconvert"), ("make sure audioconvert isn't blacklisted"));
2903 free_chain ((GstPlayChain *) chain);
2908 post_missing_element_message (playsink, "audioresample");
2909 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2910 (_("Missing element '%s' - check your GStreamer installation."),
2911 "audioresample"), (NULL));
2912 free_chain ((GstPlayChain *) chain);
2917 post_missing_element_message (playsink, "goom");
2918 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2919 (_("Missing element '%s' - check your GStreamer installation."),
2921 free_chain ((GstPlayChain *) chain);
2926 GST_ELEMENT_ERROR (playsink, CORE, PAD,
2927 (NULL), ("Failed to configure the visualisation element."));
2928 /* element made it to READY */
2929 gst_element_set_state (chain->vis, GST_STATE_NULL);
2930 free_chain ((GstPlayChain *) chain);
2935 /* this function is called when all the request pads are requested and when we
2936 * have to construct the final pipeline. Based on the flags we construct the
2937 * final output pipelines.
2940 gst_play_sink_do_reconfigure (GstPlaySink * playsink)
2943 gboolean need_audio, need_video, need_deinterlace, need_vis, need_text;
2945 GST_DEBUG_OBJECT (playsink, "reconfiguring");
2947 /* assume we need nothing */
2948 need_audio = need_video = need_deinterlace = need_vis = need_text = FALSE;
2950 GST_PLAY_SINK_LOCK (playsink);
2951 GST_OBJECT_LOCK (playsink);
2952 /* get flags, there are protected with the object lock */
2953 flags = playsink->flags;
2954 GST_OBJECT_UNLOCK (playsink);
2956 /* figure out which components we need */
2957 if (flags & GST_PLAY_FLAG_TEXT && playsink->text_pad) {
2958 /* we have subtitles and we are requested to show it */
2962 if (((flags & GST_PLAY_FLAG_VIDEO)
2963 || (flags & GST_PLAY_FLAG_NATIVE_VIDEO)) && playsink->video_pad) {
2964 /* we have video and we are requested to show it */
2967 /* we only deinterlace if native video is not requested and
2968 * we have raw video */
2969 if ((flags & GST_PLAY_FLAG_DEINTERLACE)
2970 && !(flags & GST_PLAY_FLAG_NATIVE_VIDEO) && playsink->video_pad_raw)
2971 need_deinterlace = TRUE;
2974 if (playsink->audio_pad) {
2975 if ((flags & GST_PLAY_FLAG_AUDIO) || (flags & GST_PLAY_FLAG_NATIVE_AUDIO)) {
2978 if (playsink->audio_pad_raw) {
2979 /* only can do vis with raw uncompressed audio */
2980 if (flags & GST_PLAY_FLAG_VIS && !need_video) {
2981 /* also add video when we add visualisation */
2988 /* we have a text_pad and we need text rendering, in this case we need a
2989 * video_pad to combine the video with the text or visualizations */
2990 if (need_text && !need_video && !playsink->text_sink) {
2991 if (playsink->video_pad) {
2993 } else if (need_audio) {
2994 GST_ELEMENT_WARNING (playsink, STREAM, FORMAT,
2995 (_("Can't play a text file without video or visualizations.")),
2996 ("Have text pad but no video pad or visualizations"));
2999 GST_ELEMENT_ERROR (playsink, STREAM, FORMAT,
3000 (_("Can't play a text file without video or visualizations.")),
3001 ("Have text pad but no video pad or visualizations"));
3002 GST_PLAY_SINK_UNLOCK (playsink);
3007 GST_DEBUG_OBJECT (playsink, "audio:%d, video:%d, vis:%d, text:%d", need_audio,
3008 need_video, need_vis, need_text);
3010 /* set up video pipeline */
3012 gboolean raw, async;
3014 /* we need a raw sink when we do vis or when we have a raw pad */
3015 raw = need_vis ? TRUE : playsink->video_pad_raw;
3016 /* we try to set the sink async=FALSE when we need vis, this way we can
3017 * avoid a queue in the audio chain. */
3020 GST_DEBUG_OBJECT (playsink, "adding video, raw %d",
3021 playsink->video_pad_raw);
3023 if (playsink->videochain) {
3024 /* try to reactivate the chain */
3025 if ((playsink->video_sink
3026 && playsink->video_sink != playsink->videochain->sink)
3027 || !setup_video_chain (playsink, raw, async)) {
3028 if (playsink->video_sinkpad_stream_synchronizer) {
3029 gst_element_release_request_pad (GST_ELEMENT_CAST
3030 (playsink->stream_synchronizer),
3031 playsink->video_sinkpad_stream_synchronizer);
3032 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3033 playsink->video_sinkpad_stream_synchronizer = NULL;
3034 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3035 playsink->video_srcpad_stream_synchronizer = NULL;
3038 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3040 /* Remove the sink from the bin to keep its state
3041 * and unparent it to allow reuse */
3042 if (playsink->videochain->sink) {
3043 if (playsink->videochain->sink != playsink->video_sink)
3044 gst_element_set_state (playsink->videochain->sink, GST_STATE_NULL);
3045 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
3046 playsink->videochain->sink);
3049 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3050 free_chain ((GstPlayChain *) playsink->videochain);
3051 playsink->videochain = NULL;
3053 GST_OBJECT_LOCK (playsink);
3054 if (playsink->overlay_element)
3055 gst_object_unref (playsink->overlay_element);
3056 playsink->overlay_element = NULL;
3058 if (playsink->colorbalance_element) {
3059 g_signal_handler_disconnect (playsink->colorbalance_element,
3060 playsink->colorbalance_value_changed_id);
3061 playsink->colorbalance_value_changed_id = 0;
3062 gst_object_unref (playsink->colorbalance_element);
3064 playsink->colorbalance_element = NULL;
3065 GST_OBJECT_UNLOCK (playsink);
3069 if (!playsink->videochain)
3070 playsink->videochain = gen_video_chain (playsink, raw, async);
3071 if (!playsink->videochain)
3074 if (!playsink->video_sinkpad_stream_synchronizer) {
3075 GValue item = { 0, };
3078 playsink->video_sinkpad_stream_synchronizer =
3079 gst_element_get_request_pad (GST_ELEMENT_CAST
3080 (playsink->stream_synchronizer), "sink_%u");
3081 it = gst_pad_iterate_internal_links
3082 (playsink->video_sinkpad_stream_synchronizer);
3084 gst_iterator_next (it, &item);
3085 playsink->video_srcpad_stream_synchronizer = g_value_dup_object (&item);
3086 g_value_unset (&item);
3087 g_assert (playsink->video_srcpad_stream_synchronizer);
3088 gst_iterator_free (it);
3091 if (playsink->video_pad)
3092 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad),
3093 playsink->video_sinkpad_stream_synchronizer);
3095 if (need_deinterlace) {
3096 if (!playsink->videodeinterlacechain)
3097 playsink->videodeinterlacechain =
3098 gen_video_deinterlace_chain (playsink);
3099 if (!playsink->videodeinterlacechain)
3102 GST_DEBUG_OBJECT (playsink, "adding video deinterlace chain");
3104 GST_DEBUG_OBJECT (playsink, "setting up deinterlacing chain");
3106 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
3107 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
3109 gst_pad_unlink (playsink->video_srcpad_stream_synchronizer,
3110 playsink->videochain->sinkpad);
3111 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3112 playsink->videodeinterlacechain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3114 if (playsink->videodeinterlacechain) {
3115 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3116 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
3121 GST_DEBUG_OBJECT (playsink, "adding video chain");
3122 add_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
3123 activate_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
3124 /* if we are not part of vis or subtitles, set the ghostpad target */
3125 if (!need_vis && !need_text && (!playsink->textchain
3126 || !playsink->text_pad)) {
3127 GST_DEBUG_OBJECT (playsink, "ghosting video sinkpad");
3128 gst_pad_unlink (playsink->video_srcpad_stream_synchronizer,
3129 playsink->videochain->sinkpad);
3130 if (playsink->videodeinterlacechain
3131 && playsink->videodeinterlacechain->srcpad)
3132 gst_pad_unlink (playsink->videodeinterlacechain->srcpad,
3133 playsink->videochain->sinkpad);
3134 if (need_deinterlace)
3135 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
3136 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3138 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3139 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3142 GST_DEBUG_OBJECT (playsink, "no video needed");
3143 if (playsink->videochain) {
3144 GST_DEBUG_OBJECT (playsink, "removing video chain");
3145 if (playsink->vischain) {
3148 GST_DEBUG_OBJECT (playsink, "unlinking vis chain");
3150 /* also had visualisation, release the tee srcpad before we then
3151 * unlink the video from it */
3152 if (playsink->audio_tee_vissrc) {
3153 gst_element_release_request_pad (playsink->audio_tee,
3154 playsink->audio_tee_vissrc);
3155 gst_object_unref (playsink->audio_tee_vissrc);
3156 playsink->audio_tee_vissrc = NULL;
3159 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
3160 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
3163 if (playsink->video_sinkpad_stream_synchronizer) {
3164 gst_element_release_request_pad (GST_ELEMENT_CAST
3165 (playsink->stream_synchronizer),
3166 playsink->video_sinkpad_stream_synchronizer);
3167 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3168 playsink->video_sinkpad_stream_synchronizer = NULL;
3169 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3170 playsink->video_srcpad_stream_synchronizer = NULL;
3173 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3174 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3175 if (playsink->videochain->ts_offset)
3176 gst_object_unref (playsink->videochain->ts_offset);
3177 playsink->videochain->ts_offset = NULL;
3180 if (playsink->videodeinterlacechain) {
3181 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3182 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3185 if (playsink->video_pad)
3186 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
3188 GST_OBJECT_LOCK (playsink);
3189 if (playsink->overlay_element)
3190 gst_object_unref (playsink->overlay_element);
3191 playsink->overlay_element = NULL;
3193 if (playsink->colorbalance_element) {
3194 g_signal_handler_disconnect (playsink->colorbalance_element,
3195 playsink->colorbalance_value_changed_id);
3196 playsink->colorbalance_value_changed_id = 0;
3197 gst_object_unref (playsink->colorbalance_element);
3199 playsink->colorbalance_element = NULL;
3200 GST_OBJECT_UNLOCK (playsink);
3202 if (playsink->video_sink)
3203 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
3209 GST_DEBUG_OBJECT (playsink, "adding audio");
3211 /* get a raw sink if we are asked for a raw pad */
3212 raw = playsink->audio_pad_raw;
3214 if (playsink->audiochain) {
3215 /* try to reactivate the chain */
3216 if ((playsink->audio_sink
3217 && playsink->audio_sink != playsink->audiochain->sink)
3218 || !setup_audio_chain (playsink, raw)) {
3219 GST_DEBUG_OBJECT (playsink, "removing current audio chain");
3220 if (playsink->audio_tee_asrc) {
3221 gst_element_release_request_pad (playsink->audio_tee,
3222 playsink->audio_tee_asrc);
3223 gst_object_unref (playsink->audio_tee_asrc);
3224 playsink->audio_tee_asrc = NULL;
3227 if (playsink->audio_sinkpad_stream_synchronizer) {
3228 gst_element_release_request_pad (GST_ELEMENT_CAST
3229 (playsink->stream_synchronizer),
3230 playsink->audio_sinkpad_stream_synchronizer);
3231 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
3232 playsink->audio_sinkpad_stream_synchronizer = NULL;
3233 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
3234 playsink->audio_srcpad_stream_synchronizer = NULL;
3237 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3239 /* Remove the sink from the bin to keep its state
3240 * and unparent it to allow reuse */
3241 if (playsink->audiochain->sink) {
3242 if (playsink->audiochain->sink != playsink->audio_sink)
3243 gst_element_set_state (playsink->audiochain->sink, GST_STATE_NULL);
3244 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
3245 playsink->audiochain->sink);
3248 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3249 disconnect_audio_chain (playsink->audiochain, playsink);
3250 playsink->audiochain->volume = NULL;
3251 playsink->audiochain->mute = NULL;
3252 if (playsink->audiochain->ts_offset)
3253 gst_object_unref (playsink->audiochain->ts_offset);
3254 playsink->audiochain->ts_offset = NULL;
3255 free_chain ((GstPlayChain *) playsink->audiochain);
3256 playsink->audiochain = NULL;
3257 playsink->volume_changed = playsink->mute_changed = FALSE;
3261 if (!playsink->audiochain) {
3262 GST_DEBUG_OBJECT (playsink, "creating new audio chain");
3263 playsink->audiochain = gen_audio_chain (playsink, raw);
3266 if (!playsink->audiochain)
3269 if (!playsink->audio_sinkpad_stream_synchronizer) {
3270 GValue item = { 0, };
3273 playsink->audio_sinkpad_stream_synchronizer =
3274 gst_element_get_request_pad (GST_ELEMENT_CAST
3275 (playsink->stream_synchronizer), "sink_%u");
3276 it = gst_pad_iterate_internal_links
3277 (playsink->audio_sinkpad_stream_synchronizer);
3279 gst_iterator_next (it, &item);
3280 playsink->audio_srcpad_stream_synchronizer = g_value_dup_object (&item);
3281 g_value_unset (&item);
3282 g_assert (playsink->audio_srcpad_stream_synchronizer);
3283 gst_iterator_free (it);
3286 if (playsink->audiochain) {
3287 GST_DEBUG_OBJECT (playsink, "adding audio chain");
3288 if (playsink->audio_tee_asrc == NULL) {
3289 playsink->audio_tee_asrc =
3290 gst_element_get_request_pad (playsink->audio_tee, "src_%u");
3292 add_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
3293 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
3294 gst_pad_link_full (playsink->audio_tee_asrc,
3295 playsink->audio_sinkpad_stream_synchronizer,
3296 GST_PAD_LINK_CHECK_NOTHING);
3297 gst_pad_link_full (playsink->audio_srcpad_stream_synchronizer,
3298 playsink->audiochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3301 GST_DEBUG_OBJECT (playsink, "no audio needed");
3302 /* we have no audio or we are requested to not play audio */
3303 if (playsink->audiochain) {
3304 GST_DEBUG_OBJECT (playsink, "removing audio chain");
3305 /* release the audio pad */
3306 if (playsink->audio_tee_asrc) {
3307 gst_element_release_request_pad (playsink->audio_tee,
3308 playsink->audio_tee_asrc);
3309 gst_object_unref (playsink->audio_tee_asrc);
3310 playsink->audio_tee_asrc = NULL;
3313 if (playsink->audio_sinkpad_stream_synchronizer) {
3314 gst_element_release_request_pad (GST_ELEMENT_CAST
3315 (playsink->stream_synchronizer),
3316 playsink->audio_sinkpad_stream_synchronizer);
3317 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
3318 playsink->audio_sinkpad_stream_synchronizer = NULL;
3319 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
3320 playsink->audio_srcpad_stream_synchronizer = NULL;
3323 if (playsink->audiochain->sink_volume) {
3324 disconnect_audio_chain (playsink->audiochain, playsink);
3325 playsink->audiochain->volume = NULL;
3326 playsink->audiochain->mute = NULL;
3327 if (playsink->audiochain->ts_offset)
3328 gst_object_unref (playsink->audiochain->ts_offset);
3329 playsink->audiochain->ts_offset = NULL;
3331 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3332 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3335 if (playsink->audio_sink)
3336 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
3342 if (!playsink->vischain)
3343 playsink->vischain = gen_vis_chain (playsink);
3345 GST_DEBUG_OBJECT (playsink, "adding visualisation");
3347 if (playsink->vischain) {
3348 GST_DEBUG_OBJECT (playsink, "setting up vis chain");
3350 /* Just change vis plugin or set up chain? */
3351 if (playsink->vischain->vis != playsink->visualisation) {
3352 /* unlink the old plugin and unghost the pad */
3353 gst_pad_unlink (playsink->vischain->vispeerpad,
3354 playsink->vischain->vissinkpad);
3355 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->
3356 vischain->srcpad), NULL);
3358 /* set the old plugin to NULL and remove */
3359 gst_element_set_state (playsink->vischain->vis, GST_STATE_NULL);
3360 gst_bin_remove (GST_BIN_CAST (playsink->vischain->chain.bin),
3361 playsink->vischain->vis);
3363 /* add new plugin and set state to playing */
3364 playsink->vischain->vis = playsink->visualisation;
3365 gst_bin_add (GST_BIN_CAST (playsink->vischain->chain.bin),
3366 playsink->vischain->vis);
3367 gst_element_set_state (playsink->vischain->vis, GST_STATE_PLAYING);
3370 playsink->vischain->vissinkpad =
3371 gst_element_get_static_pad (playsink->vischain->vis, "sink");
3372 playsink->vischain->vissrcpad =
3373 gst_element_get_static_pad (playsink->vischain->vis, "src");
3376 gst_pad_link_full (playsink->vischain->vispeerpad,
3377 playsink->vischain->vissinkpad, GST_PAD_LINK_CHECK_NOTHING);
3378 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->
3379 vischain->srcpad), playsink->vischain->vissrcpad);
3382 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
3383 add_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
3384 activate_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
3385 if (playsink->audio_tee_vissrc == NULL) {
3386 playsink->audio_tee_vissrc =
3387 gst_element_get_request_pad (playsink->audio_tee, "src_%u");
3389 gst_pad_link_full (playsink->audio_tee_vissrc,
3390 playsink->vischain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3391 gst_pad_link_full (srcpad, playsink->video_sinkpad_stream_synchronizer,
3392 GST_PAD_LINK_CHECK_NOTHING);
3393 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3394 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3395 gst_object_unref (srcpad);
3399 GST_DEBUG_OBJECT (playsink, "no vis needed");
3400 if (playsink->vischain) {
3401 if (playsink->audio_tee_vissrc) {
3402 gst_element_release_request_pad (playsink->audio_tee,
3403 playsink->audio_tee_vissrc);
3404 gst_object_unref (playsink->audio_tee_vissrc);
3405 playsink->audio_tee_vissrc = NULL;
3407 GST_DEBUG_OBJECT (playsink, "removing vis chain");
3408 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3409 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3414 GST_DEBUG_OBJECT (playsink, "adding text");
3415 if (!playsink->textchain) {
3416 GST_DEBUG_OBJECT (playsink, "creating text chain");
3417 playsink->textchain = gen_text_chain (playsink);
3419 if (playsink->textchain) {
3422 GST_DEBUG_OBJECT (playsink, "adding text chain");
3423 if (playsink->textchain->overlay)
3424 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", FALSE,
3426 add_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
3428 if (!playsink->text_sinkpad_stream_synchronizer) {
3429 GValue item = { 0, };
3431 playsink->text_sinkpad_stream_synchronizer =
3432 gst_element_get_request_pad (GST_ELEMENT_CAST
3433 (playsink->stream_synchronizer), "sink_%u");
3434 it = gst_pad_iterate_internal_links
3435 (playsink->text_sinkpad_stream_synchronizer);
3437 gst_iterator_next (it, &item);
3438 playsink->text_srcpad_stream_synchronizer = g_value_dup_object (&item);
3439 g_value_unset (&item);
3440 g_assert (playsink->text_srcpad_stream_synchronizer);
3441 gst_iterator_free (it);
3443 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad),
3444 playsink->text_sinkpad_stream_synchronizer);
3445 gst_pad_link_full (playsink->text_srcpad_stream_synchronizer,
3446 playsink->textchain->textsinkpad, GST_PAD_LINK_CHECK_NOTHING);
3449 if (need_vis || need_video) {
3454 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
3455 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
3456 gst_pad_link_full (srcpad, playsink->textchain->videosinkpad,
3457 GST_PAD_LINK_CHECK_NOTHING);
3458 gst_object_unref (srcpad);
3460 if (need_deinterlace)
3461 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
3462 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
3464 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3465 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
3467 gst_pad_link_full (playsink->textchain->srcpad,
3468 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3471 activate_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
3474 GST_DEBUG_OBJECT (playsink, "no text needed");
3475 /* we have no subtitles/text or we are requested to not show them */
3477 if (playsink->text_sinkpad_stream_synchronizer) {
3478 gst_element_release_request_pad (GST_ELEMENT_CAST
3479 (playsink->stream_synchronizer),
3480 playsink->text_sinkpad_stream_synchronizer);
3481 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
3482 playsink->text_sinkpad_stream_synchronizer = NULL;
3483 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
3484 playsink->text_srcpad_stream_synchronizer = NULL;
3487 if (playsink->textchain) {
3488 if (playsink->text_pad == NULL) {
3489 /* no text pad, remove the chain entirely */
3490 GST_DEBUG_OBJECT (playsink, "removing text chain");
3491 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3492 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3494 /* we have a chain and a textpad, turn the subtitles off */
3495 GST_DEBUG_OBJECT (playsink, "turning off the text");
3496 if (playsink->textchain->overlay)
3497 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", TRUE,
3501 if (!need_video && playsink->video_pad) {
3502 if (playsink->video_sinkpad_stream_synchronizer) {
3503 gst_element_release_request_pad (GST_ELEMENT_CAST
3504 (playsink->stream_synchronizer),
3505 playsink->video_sinkpad_stream_synchronizer);
3506 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3507 playsink->video_sinkpad_stream_synchronizer = NULL;
3508 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3509 playsink->video_srcpad_stream_synchronizer = NULL;
3512 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
3515 if (playsink->text_pad && !playsink->textchain)
3516 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad), NULL);
3518 if (playsink->text_sink)
3519 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
3521 update_av_offset (playsink);
3522 do_async_done (playsink);
3523 GST_PLAY_SINK_UNLOCK (playsink);
3530 /* gen_ chain already posted error */
3531 GST_DEBUG_OBJECT (playsink, "failed to setup chain");
3532 GST_PLAY_SINK_UNLOCK (playsink);
3538 * gst_play_sink_set_flags:
3539 * @playsink: a #GstPlaySink
3540 * @flags: #GstPlayFlags
3542 * Configure @flags on @playsink. The flags control the behaviour of @playsink
3543 * when constructing the sink pipelins.
3545 * Returns: TRUE if the flags could be configured.
3548 gst_play_sink_set_flags (GstPlaySink * playsink, GstPlayFlags flags)
3550 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), FALSE);
3552 GST_OBJECT_LOCK (playsink);
3553 playsink->flags = flags;
3554 GST_OBJECT_UNLOCK (playsink);
3560 * gst_play_sink_get_flags:
3561 * @playsink: a #GstPlaySink
3563 * Get the flags of @playsink. That flags control the behaviour of the sink when
3564 * it constructs the sink pipelines.
3566 * Returns: the currently configured #GstPlayFlags.
3569 gst_play_sink_get_flags (GstPlaySink * playsink)
3573 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), 0);
3575 GST_OBJECT_LOCK (playsink);
3576 res = playsink->flags;
3577 GST_OBJECT_UNLOCK (playsink);
3583 gst_play_sink_set_font_desc (GstPlaySink * playsink, const gchar * desc)
3585 GstPlayTextChain *chain;
3587 GST_PLAY_SINK_LOCK (playsink);
3588 chain = (GstPlayTextChain *) playsink->textchain;
3589 g_free (playsink->font_desc);
3590 playsink->font_desc = g_strdup (desc);
3591 if (chain && chain->overlay) {
3592 g_object_set (chain->overlay, "font-desc", desc, NULL);
3594 GST_PLAY_SINK_UNLOCK (playsink);
3598 gst_play_sink_get_font_desc (GstPlaySink * playsink)
3600 gchar *result = NULL;
3601 GstPlayTextChain *chain;
3603 GST_PLAY_SINK_LOCK (playsink);
3604 chain = (GstPlayTextChain *) playsink->textchain;
3605 if (chain && chain->overlay) {
3606 g_object_get (chain->overlay, "font-desc", &result, NULL);
3607 playsink->font_desc = g_strdup (result);
3609 result = g_strdup (playsink->font_desc);
3611 GST_PLAY_SINK_UNLOCK (playsink);
3617 gst_play_sink_set_subtitle_encoding (GstPlaySink * playsink,
3618 const gchar * encoding)
3620 GstPlayTextChain *chain;
3622 GST_PLAY_SINK_LOCK (playsink);
3623 chain = (GstPlayTextChain *) playsink->textchain;
3624 g_free (playsink->subtitle_encoding);
3625 playsink->subtitle_encoding = g_strdup (encoding);
3626 if (chain && chain->overlay) {
3627 g_object_set (chain->overlay, "subtitle-encoding", encoding, NULL);
3629 GST_PLAY_SINK_UNLOCK (playsink);
3633 gst_play_sink_get_subtitle_encoding (GstPlaySink * playsink)
3635 gchar *result = NULL;
3636 GstPlayTextChain *chain;
3638 GST_PLAY_SINK_LOCK (playsink);
3639 chain = (GstPlayTextChain *) playsink->textchain;
3640 if (chain && chain->overlay) {
3641 g_object_get (chain->overlay, "subtitle-encoding", &result, NULL);
3642 playsink->subtitle_encoding = g_strdup (result);
3644 result = g_strdup (playsink->subtitle_encoding);
3646 GST_PLAY_SINK_UNLOCK (playsink);
3652 update_av_offset (GstPlaySink * playsink)
3655 GstPlayAudioChain *achain;
3656 GstPlayVideoChain *vchain;
3658 av_offset = playsink->av_offset;
3659 achain = (GstPlayAudioChain *) playsink->audiochain;
3660 vchain = (GstPlayVideoChain *) playsink->videochain;
3662 if (achain && vchain && achain->ts_offset && vchain->ts_offset) {
3663 g_object_set (achain->ts_offset,
3664 "ts-offset", MAX (G_GINT64_CONSTANT (0), -av_offset), NULL);
3665 g_object_set (vchain->ts_offset,
3666 "ts-offset", MAX (G_GINT64_CONSTANT (0), av_offset), NULL);
3668 GST_LOG_OBJECT (playsink, "no ts_offset elements");
3673 gst_play_sink_set_av_offset (GstPlaySink * playsink, gint64 av_offset)
3675 GST_PLAY_SINK_LOCK (playsink);
3676 playsink->av_offset = av_offset;
3677 update_av_offset (playsink);
3678 GST_PLAY_SINK_UNLOCK (playsink);
3682 gst_play_sink_get_av_offset (GstPlaySink * playsink)
3686 GST_PLAY_SINK_LOCK (playsink);
3687 result = playsink->av_offset;
3688 GST_PLAY_SINK_UNLOCK (playsink);
3694 * gst_play_sink_get_last_sample:
3695 * @playsink: a #GstPlaySink
3697 * Get the last displayed sample from @playsink. This sample is in the native
3698 * format of the sink element, the caps in the result sample contain the format
3699 * of the frame data.
3701 * Returns: a #GstSample with the frame data or %NULL when no video frame is
3705 gst_play_sink_get_last_sample (GstPlaySink * playsink)
3707 GstSample *result = NULL;
3708 GstPlayVideoChain *chain;
3710 GST_PLAY_SINK_LOCK (playsink);
3711 GST_DEBUG_OBJECT (playsink, "taking last sample");
3712 /* get the video chain if we can */
3713 if ((chain = (GstPlayVideoChain *) playsink->videochain)) {
3714 GST_DEBUG_OBJECT (playsink, "found video chain");
3715 /* see if the chain is active */
3716 if (chain->chain.activated && chain->sink) {
3719 GST_DEBUG_OBJECT (playsink, "video chain active and has a sink");
3721 /* find and get the last-buffer property now */
3723 gst_play_sink_find_property (playsink, chain->sink,
3724 "last-sample", GST_TYPE_SAMPLE))) {
3725 GST_DEBUG_OBJECT (playsink, "getting last-sample property");
3726 g_object_get (elem, "last-sample", &result, NULL);
3727 gst_object_unref (elem);
3731 GST_PLAY_SINK_UNLOCK (playsink);
3737 * gst_play_sink_convert_sample:
3738 * @playsink: a #GstPlaySink
3741 * Get the last displayed frame from @playsink. If caps is %NULL, the video will
3742 * be in the native format of the sink element and the caps on the buffer
3743 * describe the format of the frame. If @caps is not %NULL, the video
3744 * frame will be converted to the format of the caps.
3746 * Returns: a #GstSample of the current video sample converted to #caps.
3747 * The caps in the sample will describe the final layout of the buffer data.
3748 * %NULL is returned when no current sample can be retrieved or when the
3749 * conversion failed.
3752 gst_play_sink_convert_sample (GstPlaySink * playsink, GstCaps * caps)
3757 result = gst_play_sink_get_last_sample (playsink);
3758 if (result != NULL && caps != NULL) {
3761 temp = gst_video_convert_sample (result, caps, 25 * GST_SECOND, &err);
3762 if (temp == NULL && err)
3765 gst_sample_unref (result);
3773 /* I'm really uncertain whether we should make playsink post an error
3774 * on the bus or not. It's not like it's a critical issue regarding
3775 * playsink behaviour. */
3776 GST_ERROR ("Error converting frame: %s", err->message);
3777 gst_sample_unref (result);
3784 is_raw_structure (GstStructure * s)
3788 name = gst_structure_get_name (s);
3790 if (g_str_equal (name, "video/x-raw") || g_str_equal (name, "audio/x-raw"))
3796 is_raw_pad (GstPad * pad)
3798 GstPad *peer = gst_pad_get_peer (pad);
3800 gboolean raw = TRUE;
3805 caps = gst_pad_get_current_caps (peer);
3809 caps = gst_pad_query_caps (peer, NULL);
3811 n = gst_caps_get_size (caps);
3812 for (i = 0; i < n; i++) {
3813 gboolean r = is_raw_structure (gst_caps_get_structure (caps, i));
3817 } else if (raw != r) {
3818 GST_ERROR_OBJECT (pad,
3819 "Caps contains raw and non-raw structures: %" GST_PTR_FORMAT, caps);
3825 raw = is_raw_structure (gst_caps_get_structure (caps, 0));
3827 gst_caps_unref (caps);
3828 gst_object_unref (peer);
3833 static GstPadProbeReturn
3834 sinkpad_blocked_cb (GstPad * blockedpad, GstPadProbeInfo * info,
3835 gpointer user_data);
3838 video_set_blocked (GstPlaySink * playsink, gboolean blocked)
3840 if (playsink->video_pad) {
3842 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3843 (playsink->video_pad)));
3844 if (blocked && playsink->video_block_id == 0) {
3845 if (playsink->vis_pad_block_id)
3846 gst_pad_remove_probe (((GstPlayVisChain *) playsink->
3847 vischain)->blockpad, playsink->vis_pad_block_id);
3848 playsink->vis_pad_block_id = 0;
3850 playsink->video_block_id =
3851 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3852 sinkpad_blocked_cb, playsink, NULL);
3853 } else if (!blocked && playsink->video_block_id) {
3854 gst_pad_remove_probe (opad, playsink->video_block_id);
3855 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO_RAW);
3856 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO);
3857 playsink->video_block_id = 0;
3858 playsink->video_pad_blocked = FALSE;
3860 gst_object_unref (opad);
3865 audio_set_blocked (GstPlaySink * playsink, gboolean blocked)
3867 if (playsink->audio_pad) {
3869 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3870 (playsink->audio_pad)));
3871 if (blocked && playsink->audio_block_id == 0) {
3872 if (playsink->vis_pad_block_id)
3873 gst_pad_remove_probe (((GstPlayVisChain *) playsink->
3874 vischain)->blockpad, playsink->vis_pad_block_id);
3875 playsink->vis_pad_block_id = 0;
3877 playsink->audio_block_id =
3878 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3879 sinkpad_blocked_cb, playsink, NULL);
3880 } else if (!blocked && playsink->audio_block_id) {
3881 if (playsink->vis_pad_block_id)
3882 gst_pad_remove_probe (((GstPlayVisChain *) playsink->
3883 vischain)->blockpad, playsink->vis_pad_block_id);
3884 playsink->vis_pad_block_id = 0;
3886 gst_pad_remove_probe (opad, playsink->audio_block_id);
3887 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO_RAW);
3888 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO);
3889 playsink->audio_block_id = 0;
3890 playsink->audio_pad_blocked = FALSE;
3892 gst_object_unref (opad);
3897 text_set_blocked (GstPlaySink * playsink, gboolean blocked)
3899 if (playsink->text_pad) {
3901 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3902 (playsink->text_pad)));
3903 if (blocked && playsink->text_block_id == 0) {
3904 if (playsink->vis_pad_block_id)
3905 gst_pad_remove_probe (((GstPlayVisChain *) playsink->
3906 vischain)->blockpad, playsink->vis_pad_block_id);
3907 playsink->vis_pad_block_id = 0;
3909 playsink->text_block_id =
3910 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3911 sinkpad_blocked_cb, playsink, NULL);
3912 } else if (!blocked && playsink->text_block_id) {
3913 gst_pad_remove_probe (opad, playsink->text_block_id);
3914 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_TEXT);
3915 playsink->text_block_id = 0;
3916 playsink->text_pad_blocked = FALSE;
3918 gst_object_unref (opad);
3923 gst_play_sink_reconfigure (GstPlaySink * playsink)
3925 GST_LOG_OBJECT (playsink, "Triggering reconfiguration");
3927 GST_PLAY_SINK_LOCK (playsink);
3928 video_set_blocked (playsink, TRUE);
3929 audio_set_blocked (playsink, TRUE);
3930 text_set_blocked (playsink, TRUE);
3931 GST_PLAY_SINK_UNLOCK (playsink);
3936 static GstPadProbeReturn
3937 sinkpad_blocked_cb (GstPad * blockedpad, GstPadProbeInfo * info,
3940 GstPlaySink *playsink = (GstPlaySink *) user_data;
3943 GST_PLAY_SINK_LOCK (playsink);
3945 pad = GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (blockedpad)));
3946 if (pad == playsink->video_pad) {
3947 playsink->video_pad_blocked = TRUE;
3948 GST_DEBUG_OBJECT (pad, "Video pad blocked");
3949 } else if (pad == playsink->audio_pad) {
3950 playsink->audio_pad_blocked = TRUE;
3951 GST_DEBUG_OBJECT (pad, "Audio pad blocked");
3952 } else if (pad == playsink->text_pad) {
3953 playsink->text_pad_blocked = TRUE;
3954 GST_DEBUG_OBJECT (pad, "Text pad blocked");
3957 /* We reconfigure when for ALL streams:
3958 * * there isn't a pad
3959 * * OR the pad is blocked
3960 * * OR there are no pending blocks on that pad
3963 if ((!playsink->video_pad || playsink->video_pad_blocked
3964 || !PENDING_VIDEO_BLOCK (playsink)) && (!playsink->audio_pad
3965 || playsink->audio_pad_blocked || !PENDING_AUDIO_BLOCK (playsink))
3966 && (!playsink->text_pad || playsink->text_pad_blocked
3967 || !PENDING_TEXT_BLOCK (playsink))) {
3968 GST_DEBUG_OBJECT (playsink, "All pads blocked -- reconfiguring");
3970 if (playsink->video_pad) {
3971 playsink->video_pad_raw = is_raw_pad (playsink->video_pad);
3972 GST_DEBUG_OBJECT (playsink, "Video pad is raw: %d",
3973 playsink->video_pad_raw);
3976 if (playsink->audio_pad) {
3977 playsink->audio_pad_raw = is_raw_pad (playsink->audio_pad);
3978 GST_DEBUG_OBJECT (playsink, "Audio pad is raw: %d",
3979 playsink->audio_pad_raw);
3982 gst_play_sink_do_reconfigure (playsink);
3984 video_set_blocked (playsink, FALSE);
3985 audio_set_blocked (playsink, FALSE);
3986 text_set_blocked (playsink, FALSE);
3989 gst_object_unref (pad);
3991 GST_PLAY_SINK_UNLOCK (playsink);
3993 return GST_PAD_PROBE_OK;
3997 caps_notify_cb (GstPad * pad, GParamSpec * unused, GstPlaySink * playsink)
3999 gboolean reconfigure = FALSE;
4003 g_object_get (pad, "caps", &caps, NULL);
4007 if (pad == playsink->audio_pad) {
4008 raw = is_raw_pad (pad);
4009 reconfigure = (! !playsink->audio_pad_raw != ! !raw)
4010 && playsink->audiochain;
4011 GST_DEBUG_OBJECT (pad,
4012 "Audio caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
4014 } else if (pad == playsink->video_pad) {
4015 raw = is_raw_pad (pad);
4016 reconfigure = (! !playsink->video_pad_raw != ! !raw)
4017 && playsink->videochain;
4018 GST_DEBUG_OBJECT (pad,
4019 "Video caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
4023 gst_caps_unref (caps);
4026 gst_play_sink_reconfigure (playsink);
4030 gst_play_sink_refresh_pad (GstPlaySink * playsink, GstPad * pad,
4031 GstPlaySinkType type)
4033 gulong *block_id = NULL;
4035 GST_DEBUG_OBJECT (playsink, "refresh pad %" GST_PTR_FORMAT, pad);
4037 GST_PLAY_SINK_LOCK (playsink);
4038 if (pad == playsink->video_pad) {
4039 if (type != GST_PLAY_SINK_TYPE_VIDEO_RAW &&
4040 type != GST_PLAY_SINK_TYPE_VIDEO)
4042 block_id = &playsink->video_block_id;
4043 } else if (pad == playsink->audio_pad) {
4044 if (type != GST_PLAY_SINK_TYPE_AUDIO_RAW &&
4045 type != GST_PLAY_SINK_TYPE_AUDIO)
4047 block_id = &playsink->audio_block_id;
4048 } else if (pad == playsink->text_pad) {
4049 if (type != GST_PLAY_SINK_TYPE_TEXT)
4051 block_id = &playsink->text_block_id;
4054 if (type != GST_PLAY_SINK_TYPE_FLUSHING && (block_id && *block_id == 0)) {
4056 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (pad)));
4058 if (playsink->vis_pad_block_id)
4059 gst_pad_remove_probe (((GstPlayVisChain *) playsink->vischain)->blockpad,
4060 playsink->vis_pad_block_id);
4061 playsink->vis_pad_block_id = 0;
4064 gst_pad_add_probe (blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
4065 sinkpad_blocked_cb, playsink, NULL);
4066 PENDING_FLAG_SET (playsink, type);
4067 gst_object_unref (blockpad);
4069 GST_PLAY_SINK_UNLOCK (playsink);
4076 GST_WARNING_OBJECT (playsink, "wrong type %u for pad %" GST_PTR_FORMAT,
4078 GST_PLAY_SINK_UNLOCK (playsink);
4084 * gst_play_sink_request_pad
4085 * @playsink: a #GstPlaySink
4086 * @type: a #GstPlaySinkType
4088 * Create or return a pad of @type.
4090 * Returns: a #GstPad of @type or %NULL when the pad could not be created.
4093 gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type)
4096 gboolean created = FALSE;
4097 gboolean activate = TRUE;
4098 const gchar *pad_name = NULL;
4099 gulong *block_id = NULL;
4101 GST_DEBUG_OBJECT (playsink, "request pad type %d", type);
4103 GST_PLAY_SINK_LOCK (playsink);
4105 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
4106 case GST_PLAY_SINK_TYPE_AUDIO:
4107 pad_name = "audio_sink";
4108 if (!playsink->audio_tee) {
4109 GST_LOG_OBJECT (playsink, "creating tee");
4110 /* create tee when needed. This element will feed the audio sink chain
4111 * and the vis chain. */
4112 playsink->audio_tee = gst_element_factory_make ("tee", "audiotee");
4113 if (playsink->audio_tee == NULL) {
4114 post_missing_element_message (playsink, "tee");
4115 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
4116 (_("Missing element '%s' - check your GStreamer installation."),
4121 playsink->audio_tee_sink =
4122 gst_element_get_static_pad (playsink->audio_tee, "sink");
4123 gst_bin_add (GST_BIN_CAST (playsink), playsink->audio_tee);
4124 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
4127 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
4129 if (!playsink->audio_pad) {
4130 GST_LOG_OBJECT (playsink, "ghosting tee sinkpad");
4131 playsink->audio_pad =
4132 gst_ghost_pad_new (pad_name, playsink->audio_tee_sink);
4133 playsink->audio_notify_caps_id =
4134 g_signal_connect (G_OBJECT (playsink->audio_pad), "notify::caps",
4135 G_CALLBACK (caps_notify_cb), playsink);
4138 playsink->audio_pad_raw = FALSE;
4139 res = playsink->audio_pad;
4140 block_id = &playsink->audio_block_id;
4142 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
4143 case GST_PLAY_SINK_TYPE_VIDEO:
4144 pad_name = "video_sink";
4145 if (!playsink->video_pad) {
4146 GST_LOG_OBJECT (playsink, "ghosting videosink");
4147 playsink->video_pad =
4148 gst_ghost_pad_new_no_target (pad_name, GST_PAD_SINK);
4149 playsink->video_notify_caps_id =
4150 g_signal_connect (G_OBJECT (playsink->video_pad), "notify::caps",
4151 G_CALLBACK (caps_notify_cb), playsink);
4154 playsink->video_pad_raw = FALSE;
4155 res = playsink->video_pad;
4156 block_id = &playsink->video_block_id;
4158 case GST_PLAY_SINK_TYPE_TEXT:
4159 GST_LOG_OBJECT (playsink, "ghosting text");
4160 if (!playsink->text_pad) {
4161 playsink->text_pad =
4162 gst_ghost_pad_new_no_target ("text_sink", GST_PAD_SINK);
4165 res = playsink->text_pad;
4166 block_id = &playsink->text_block_id;
4168 case GST_PLAY_SINK_TYPE_FLUSHING:
4172 /* we need a unique padname for the flushing pad. */
4173 padname = g_strdup_printf ("flushing_%u", playsink->count);
4174 res = gst_ghost_pad_new_no_target (padname, GST_PAD_SINK);
4185 GST_PLAY_SINK_UNLOCK (playsink);
4187 if (created && res) {
4188 /* we have to add the pad when it's active or we get an error when the
4189 * element is 'running' */
4190 gst_pad_set_active (res, TRUE);
4191 gst_element_add_pad (GST_ELEMENT_CAST (playsink), res);
4192 if (block_id && *block_id == 0) {
4194 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (res)));
4196 if (playsink->vis_pad_block_id)
4197 gst_pad_remove_probe (((GstPlayVisChain *) playsink->
4198 vischain)->blockpad, playsink->vis_pad_block_id);
4199 playsink->vis_pad_block_id = 0;
4202 gst_pad_add_probe (blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
4203 sinkpad_blocked_cb, playsink, NULL);
4204 PENDING_FLAG_SET (playsink, type);
4205 gst_object_unref (blockpad);
4208 gst_pad_set_active (res, activate);
4216 gst_play_sink_request_new_pad (GstElement * element, GstPadTemplate * templ,
4217 const gchar * name, const GstCaps * caps)
4221 GstPlaySinkType type;
4222 const gchar *tplname;
4224 g_return_val_if_fail (templ != NULL, NULL);
4226 GST_DEBUG_OBJECT (element, "name:%s", name);
4228 psink = GST_PLAY_SINK (element);
4229 tplname = GST_PAD_TEMPLATE_NAME_TEMPLATE (templ);
4231 /* Figure out the GstPlaySinkType based on the template */
4232 if (!strcmp (tplname, "audio_sink"))
4233 type = GST_PLAY_SINK_TYPE_AUDIO;
4234 else if (!strcmp (tplname, "audio_raw_sink"))
4235 type = GST_PLAY_SINK_TYPE_AUDIO_RAW;
4236 else if (!strcmp (tplname, "video_sink"))
4237 type = GST_PLAY_SINK_TYPE_VIDEO;
4238 else if (!strcmp (tplname, "video_raw_sink"))
4239 type = GST_PLAY_SINK_TYPE_VIDEO_RAW;
4240 else if (!strcmp (tplname, "text_sink"))
4241 type = GST_PLAY_SINK_TYPE_TEXT;
4243 goto unknown_template;
4245 pad = gst_play_sink_request_pad (psink, type);
4249 GST_WARNING_OBJECT (element, "Unknown pad template");
4254 gst_play_sink_release_pad (GstPlaySink * playsink, GstPad * pad)
4256 GstPad **res = NULL;
4257 gboolean untarget = TRUE;
4259 GST_DEBUG_OBJECT (playsink, "release pad %" GST_PTR_FORMAT, pad);
4261 GST_PLAY_SINK_LOCK (playsink);
4262 if (pad == playsink->video_pad) {
4263 res = &playsink->video_pad;
4264 g_signal_handler_disconnect (playsink->video_pad,
4265 playsink->video_notify_caps_id);
4266 video_set_blocked (playsink, FALSE);
4267 } else if (pad == playsink->audio_pad) {
4268 res = &playsink->audio_pad;
4269 g_signal_handler_disconnect (playsink->audio_pad,
4270 playsink->audio_notify_caps_id);
4271 audio_set_blocked (playsink, FALSE);
4272 } else if (pad == playsink->text_pad) {
4273 res = &playsink->text_pad;
4274 text_set_blocked (playsink, FALSE);
4276 /* try to release the given pad anyway, these could be the FLUSHING pads. */
4280 GST_PLAY_SINK_UNLOCK (playsink);
4283 GST_DEBUG_OBJECT (playsink, "deactivate pad %" GST_PTR_FORMAT, *res);
4284 gst_pad_set_active (*res, FALSE);
4286 GST_DEBUG_OBJECT (playsink, "untargeting pad %" GST_PTR_FORMAT, *res);
4287 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (*res), NULL);
4289 GST_DEBUG_OBJECT (playsink, "remove pad %" GST_PTR_FORMAT, *res);
4290 gst_element_remove_pad (GST_ELEMENT_CAST (playsink), *res);
4296 gst_play_sink_release_request_pad (GstElement * element, GstPad * pad)
4298 GstPlaySink *psink = GST_PLAY_SINK (element);
4300 gst_play_sink_release_pad (psink, pad);
4304 gst_play_sink_handle_message (GstBin * bin, GstMessage * message)
4306 GstPlaySink *playsink;
4308 playsink = GST_PLAY_SINK_CAST (bin);
4310 switch (GST_MESSAGE_TYPE (message)) {
4311 case GST_MESSAGE_STEP_DONE:
4316 gboolean flush, intermediate, eos;
4319 GST_INFO_OBJECT (playsink, "Handling step-done message");
4320 gst_message_parse_step_done (message, &format, &amount, &rate, &flush,
4321 &intermediate, &duration, &eos);
4323 if (format == GST_FORMAT_BUFFERS) {
4324 /* for the buffer format, we align the other streams */
4325 if (playsink->audiochain) {
4329 gst_event_new_step (GST_FORMAT_TIME, duration, rate, flush,
4332 if (!gst_element_send_event (playsink->audiochain->chain.bin, event)) {
4333 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
4337 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
4340 case GST_MESSAGE_ELEMENT:{
4341 if (gst_is_video_overlay_prepare_window_handle_message (message)) {
4342 GstVideoOverlay *overlay;
4344 GST_OBJECT_LOCK (playsink);
4345 if (playsink->overlay_element
4346 && GST_OBJECT_CAST (playsink->overlay_element) !=
4347 GST_MESSAGE_SRC (message)) {
4348 gst_object_unref (playsink->overlay_element);
4349 playsink->overlay_element = NULL;
4352 if (!playsink->overlay_element)
4353 playsink->overlay_element =
4354 GST_VIDEO_OVERLAY (gst_object_ref (GST_MESSAGE_SRC (message)));
4356 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4357 GST_OBJECT_UNLOCK (playsink);
4359 GST_DEBUG_OBJECT (playsink, "Got prepare-xwindow-id message");
4361 if (playsink->overlay_handle_set)
4362 gst_video_overlay_set_window_handle (playsink->overlay_element,
4363 playsink->overlay_handle);
4364 if (playsink->overlay_handle_events_set)
4365 gst_video_overlay_handle_events (playsink->overlay_element,
4366 playsink->overlay_handle_events);
4367 if (playsink->overlay_render_rectangle_set)
4368 gst_video_overlay_set_render_rectangle (playsink->overlay_element,
4369 playsink->overlay_x, playsink->overlay_y,
4370 playsink->overlay_width, playsink->overlay_height);
4372 gst_object_unref (overlay);
4373 gst_message_unref (message);
4374 gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (playsink));
4376 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin,
4382 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
4387 /* Send an event to our sinks until one of them works; don't then send to the
4388 * remaining sinks (unlike GstBin)
4389 * Special case: If a text sink is set we need to send the event
4390 * to them in case it's source is different from the a/v stream's source.
4393 gst_play_sink_send_event_to_sink (GstPlaySink * playsink, GstEvent * event)
4395 gboolean res = TRUE;
4396 if (playsink->send_event_mode == MODE_FIRST) {
4397 if (playsink->textchain && playsink->textchain->sink) {
4398 gst_event_ref (event);
4400 gst_element_send_event (playsink->textchain->chain.bin, event))) {
4401 GST_DEBUG_OBJECT (playsink, "Sent event successfully to text sink");
4403 GST_DEBUG_OBJECT (playsink, "Event failed when sent to text sink");
4407 if (playsink->videochain) {
4408 gst_event_ref (event);
4410 gst_element_send_event (playsink->videochain->chain.bin,
4412 GST_DEBUG_OBJECT (playsink, "Sent event successfully to video sink");
4415 GST_DEBUG_OBJECT (playsink, "Event failed when sent to video sink");
4417 if (playsink->audiochain) {
4418 gst_event_ref (event);
4420 gst_element_send_event (playsink->audiochain->chain.bin,
4422 GST_DEBUG_OBJECT (playsink, "Sent event successfully to audio sink");
4425 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
4429 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event
4430 (GST_ELEMENT_CAST (playsink), event);
4434 gst_event_unref (event);
4438 /* We only want to send the event to a single sink (overriding GstBin's
4439 * behaviour), but we want to keep GstPipeline's behaviour - wrapping seek
4440 * events appropriately. So, this is a messy duplication of code. */
4442 gst_play_sink_send_event (GstElement * element, GstEvent * event)
4444 gboolean res = FALSE;
4445 GstEventType event_type = GST_EVENT_TYPE (event);
4446 GstPlaySink *playsink;
4447 playsink = GST_PLAY_SINK_CAST (element);
4448 switch (event_type) {
4449 case GST_EVENT_SEEK:
4450 GST_DEBUG_OBJECT (element, "Sending event to a sink");
4451 res = gst_play_sink_send_event_to_sink (playsink, event);
4453 case GST_EVENT_STEP:
4458 gboolean flush, intermediate;
4459 gst_event_parse_step (event, &format, &amount, &rate, &flush,
4461 if (format == GST_FORMAT_BUFFERS) {
4462 /* for buffers, we will try to step video frames, for other formats we
4463 * send the step to all sinks */
4464 res = gst_play_sink_send_event_to_sink (playsink, event);
4467 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
4474 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
4481 static GstStateChangeReturn
4482 gst_play_sink_change_state (GstElement * element, GstStateChange transition)
4484 GstStateChangeReturn ret;
4485 GstStateChangeReturn bret;
4486 GstPlaySink *playsink;
4487 playsink = GST_PLAY_SINK (element);
4488 switch (transition) {
4489 case GST_STATE_CHANGE_READY_TO_PAUSED:
4490 playsink->need_async_start = TRUE;
4491 /* we want to go async to PAUSED until we managed to configure and add the
4493 do_async_start (playsink);
4494 ret = GST_STATE_CHANGE_ASYNC;
4496 /* block all pads here */
4497 if (!gst_play_sink_reconfigure (playsink))
4498 ret = GST_STATE_CHANGE_FAILURE;
4500 case GST_STATE_CHANGE_PAUSED_TO_READY:
4501 /* unblock all pads here */
4502 GST_PLAY_SINK_LOCK (playsink);
4503 video_set_blocked (playsink, FALSE);
4504 audio_set_blocked (playsink, FALSE);
4505 text_set_blocked (playsink, FALSE);
4506 if (playsink->vis_pad_block_id)
4507 gst_pad_remove_probe (((GstPlayVisChain *) playsink->
4508 vischain)->blockpad, playsink->vis_pad_block_id);
4509 playsink->vis_pad_block_id = 0;
4511 GST_PLAY_SINK_UNLOCK (playsink);
4513 case GST_STATE_CHANGE_READY_TO_NULL:
4514 if (playsink->audiochain && playsink->audiochain->sink_volume) {
4515 /* remove our links to the mute and volume elements when they were
4516 * provided by a sink */
4517 disconnect_audio_chain (playsink->audiochain, playsink);
4518 playsink->audiochain->volume = NULL;
4519 playsink->audiochain->mute = NULL;
4522 if (playsink->audiochain && playsink->audiochain->ts_offset) {
4523 gst_object_unref (playsink->audiochain->ts_offset);
4524 playsink->audiochain->ts_offset = NULL;
4527 if (playsink->videochain && playsink->videochain->ts_offset) {
4528 gst_object_unref (playsink->videochain->ts_offset);
4529 playsink->videochain->ts_offset = NULL;
4532 GST_OBJECT_LOCK (playsink);
4533 if (playsink->overlay_element)
4534 gst_object_unref (playsink->overlay_element);
4535 playsink->overlay_element = NULL;
4537 if (playsink->colorbalance_element) {
4538 g_signal_handler_disconnect (playsink->colorbalance_element,
4539 playsink->colorbalance_value_changed_id);
4540 playsink->colorbalance_value_changed_id = 0;
4541 gst_object_unref (playsink->colorbalance_element);
4543 playsink->colorbalance_element = NULL;
4544 GST_OBJECT_UNLOCK (playsink);
4546 ret = GST_STATE_CHANGE_SUCCESS;
4549 /* all other state changes return SUCCESS by default, this value can be
4550 * overridden by the result of the children */
4551 ret = GST_STATE_CHANGE_SUCCESS;
4555 /* do the state change of the children */
4557 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->change_state (element,
4559 /* now look at the result of our children and adjust the return value */
4561 case GST_STATE_CHANGE_FAILURE:
4562 /* failure, we stop */
4563 goto activate_failed;
4564 case GST_STATE_CHANGE_NO_PREROLL:
4565 /* some child returned NO_PREROLL. This is strange but we never know. We
4566 * commit our async state change (if any) and return the NO_PREROLL */
4567 do_async_done (playsink);
4570 case GST_STATE_CHANGE_ASYNC:
4571 /* some child was async, return this */
4575 /* return our previously configured return value */
4579 switch (transition) {
4580 case GST_STATE_CHANGE_READY_TO_PAUSED:
4582 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
4583 /* FIXME Release audio device when we implement that */
4584 playsink->need_async_start = TRUE;
4586 case GST_STATE_CHANGE_PAUSED_TO_READY:{
4587 if (playsink->video_sinkpad_stream_synchronizer) {
4588 gst_element_release_request_pad (GST_ELEMENT_CAST
4589 (playsink->stream_synchronizer),
4590 playsink->video_sinkpad_stream_synchronizer);
4591 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
4592 playsink->video_sinkpad_stream_synchronizer = NULL;
4593 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
4594 playsink->video_srcpad_stream_synchronizer = NULL;
4596 if (playsink->audio_sinkpad_stream_synchronizer) {
4597 gst_element_release_request_pad (GST_ELEMENT_CAST
4598 (playsink->stream_synchronizer),
4599 playsink->audio_sinkpad_stream_synchronizer);
4600 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
4601 playsink->audio_sinkpad_stream_synchronizer = NULL;
4602 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
4603 playsink->audio_srcpad_stream_synchronizer = NULL;
4605 if (playsink->text_sinkpad_stream_synchronizer) {
4606 gst_element_release_request_pad (GST_ELEMENT_CAST
4607 (playsink->stream_synchronizer),
4608 playsink->text_sinkpad_stream_synchronizer);
4609 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
4610 playsink->text_sinkpad_stream_synchronizer = NULL;
4611 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
4612 playsink->text_srcpad_stream_synchronizer = NULL;
4616 case GST_STATE_CHANGE_READY_TO_NULL:
4617 /* remove sinks we added */
4618 if (playsink->videodeinterlacechain) {
4619 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
4621 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
4623 if (playsink->videochain) {
4624 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
4625 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
4627 if (playsink->audiochain) {
4628 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
4629 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
4631 if (playsink->vischain) {
4632 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
4633 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
4635 if (playsink->textchain) {
4636 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
4637 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
4639 do_async_done (playsink);
4640 /* when going to READY, keep elements around as long as possible,
4641 * so they may be re-used faster next time/url around.
4642 * when really going to NULL, clean up everything completely. */
4643 if (transition == GST_STATE_CHANGE_READY_TO_NULL) {
4645 /* Unparent the sinks to allow reuse */
4646 if (playsink->videochain && playsink->videochain->sink)
4647 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
4648 playsink->videochain->sink);
4649 if (playsink->audiochain && playsink->audiochain->sink)
4650 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
4651 playsink->audiochain->sink);
4652 if (playsink->textchain && playsink->textchain->sink)
4653 gst_bin_remove (GST_BIN_CAST (playsink->textchain->chain.bin),
4654 playsink->textchain->sink);
4655 if (playsink->audio_sink != NULL)
4656 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
4657 if (playsink->video_sink != NULL)
4658 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
4659 if (playsink->visualisation != NULL)
4660 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
4661 if (playsink->text_sink != NULL)
4662 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
4663 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
4664 playsink->videodeinterlacechain = NULL;
4665 free_chain ((GstPlayChain *) playsink->videochain);
4666 playsink->videochain = NULL;
4667 free_chain ((GstPlayChain *) playsink->audiochain);
4668 playsink->audiochain = NULL;
4669 free_chain ((GstPlayChain *) playsink->vischain);
4670 playsink->vischain = NULL;
4671 free_chain ((GstPlayChain *) playsink->textchain);
4672 playsink->textchain = NULL;
4682 GST_DEBUG_OBJECT (element,
4683 "element failed to change states -- activation problem?");
4684 return GST_STATE_CHANGE_FAILURE;
4689 gst_play_sink_set_property (GObject * object, guint prop_id,
4690 const GValue * value, GParamSpec * spec)
4692 GstPlaySink *playsink = GST_PLAY_SINK (object);
4695 gst_play_sink_set_flags (playsink, g_value_get_flags (value));
4698 gst_play_sink_set_volume (playsink, g_value_get_double (value));
4701 gst_play_sink_set_mute (playsink, g_value_get_boolean (value));
4703 case PROP_FONT_DESC:
4704 gst_play_sink_set_font_desc (playsink, g_value_get_string (value));
4706 case PROP_SUBTITLE_ENCODING:
4707 gst_play_sink_set_subtitle_encoding (playsink,
4708 g_value_get_string (value));
4710 case PROP_VIS_PLUGIN:
4711 gst_play_sink_set_vis_plugin (playsink, g_value_get_object (value));
4713 case PROP_AV_OFFSET:
4714 gst_play_sink_set_av_offset (playsink, g_value_get_int64 (value));
4716 case PROP_VIDEO_SINK:
4717 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_VIDEO,
4718 g_value_get_object (value));
4720 case PROP_AUDIO_SINK:
4721 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_AUDIO,
4722 g_value_get_object (value));
4724 case PROP_TEXT_SINK:
4725 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_TEXT,
4726 g_value_get_object (value));
4728 case PROP_SEND_EVENT_MODE:
4729 playsink->send_event_mode = g_value_get_enum (value);
4731 case PROP_FORCE_ASPECT_RATIO:{
4732 GstPlayVideoChain *chain;
4735 playsink->force_aspect_ratio = g_value_get_boolean (value);
4737 GST_PLAY_SINK_LOCK (playsink);
4738 if (playsink->videochain) {
4739 chain = (GstPlayVideoChain *) playsink->videochain;
4743 gst_play_sink_find_property_sinks (playsink, chain->sink,
4744 "force-aspect-ratio", G_TYPE_BOOLEAN);
4747 g_object_set (elem, "force-aspect-ratio",
4748 playsink->force_aspect_ratio, NULL);
4751 GST_PLAY_SINK_UNLOCK (playsink);
4755 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
4761 gst_play_sink_get_property (GObject * object, guint prop_id,
4762 GValue * value, GParamSpec * spec)
4764 GstPlaySink *playsink = GST_PLAY_SINK (object);
4767 g_value_set_flags (value, gst_play_sink_get_flags (playsink));
4770 g_value_set_double (value, gst_play_sink_get_volume (playsink));
4773 g_value_set_boolean (value, gst_play_sink_get_mute (playsink));
4775 case PROP_FONT_DESC:
4776 g_value_take_string (value, gst_play_sink_get_font_desc (playsink));
4778 case PROP_SUBTITLE_ENCODING:
4779 g_value_take_string (value,
4780 gst_play_sink_get_subtitle_encoding (playsink));
4782 case PROP_VIS_PLUGIN:
4783 g_value_take_object (value, gst_play_sink_get_vis_plugin (playsink));
4786 gst_value_take_sample (value, gst_play_sink_get_last_sample (playsink));
4788 case PROP_AV_OFFSET:
4789 g_value_set_int64 (value, gst_play_sink_get_av_offset (playsink));
4791 case PROP_VIDEO_SINK:
4792 g_value_take_object (value, gst_play_sink_get_sink (playsink,
4793 GST_PLAY_SINK_TYPE_VIDEO));
4795 case PROP_AUDIO_SINK:
4796 g_value_take_object (value, gst_play_sink_get_sink (playsink,
4797 GST_PLAY_SINK_TYPE_AUDIO));
4799 case PROP_TEXT_SINK:
4800 g_value_take_object (value, gst_play_sink_get_sink (playsink,
4801 GST_PLAY_SINK_TYPE_TEXT));
4803 case PROP_SEND_EVENT_MODE:
4804 g_value_set_enum (value, playsink->send_event_mode);
4806 case PROP_FORCE_ASPECT_RATIO:
4807 g_value_set_boolean (value, playsink->force_aspect_ratio);
4810 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
4816 gst_play_sink_overlay_expose (GstVideoOverlay * overlay)
4818 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4819 GstVideoOverlay *overlay_element;
4821 GST_OBJECT_LOCK (playsink);
4822 if (playsink->overlay_element)
4824 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4826 overlay_element = NULL;
4827 GST_OBJECT_UNLOCK (playsink);
4829 if (overlay_element) {
4830 gst_video_overlay_expose (overlay_element);
4831 gst_object_unref (overlay_element);
4836 gst_play_sink_overlay_handle_events (GstVideoOverlay * overlay,
4837 gboolean handle_events)
4839 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4840 GstVideoOverlay *overlay_element;
4842 GST_OBJECT_LOCK (playsink);
4843 if (playsink->overlay_element)
4845 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4847 overlay_element = NULL;
4848 GST_OBJECT_UNLOCK (playsink);
4850 playsink->overlay_handle_events_set = TRUE;
4851 playsink->overlay_handle_events = handle_events;
4853 if (overlay_element) {
4854 gst_video_overlay_handle_events (overlay_element, handle_events);
4855 gst_object_unref (overlay_element);
4860 gst_play_sink_overlay_set_render_rectangle (GstVideoOverlay * overlay, gint x,
4861 gint y, gint width, gint height)
4863 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4864 GstVideoOverlay *overlay_element;
4866 GST_OBJECT_LOCK (playsink);
4867 if (playsink->overlay_element)
4869 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4871 overlay_element = NULL;
4872 GST_OBJECT_UNLOCK (playsink);
4874 playsink->overlay_render_rectangle_set = TRUE;
4875 playsink->overlay_x = x;
4876 playsink->overlay_y = y;
4877 playsink->overlay_width = width;
4878 playsink->overlay_height = height;
4880 if (overlay_element) {
4881 gst_video_overlay_set_render_rectangle (overlay_element, x, y, width,
4883 gst_object_unref (overlay_element);
4888 gst_play_sink_overlay_set_window_handle (GstVideoOverlay * overlay,
4891 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4892 GstVideoOverlay *overlay_element;
4894 GST_OBJECT_LOCK (playsink);
4895 if (playsink->overlay_element)
4897 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4899 overlay_element = NULL;
4900 GST_OBJECT_UNLOCK (playsink);
4902 playsink->overlay_handle_set = TRUE;
4903 playsink->overlay_handle = handle;
4905 if (overlay_element) {
4906 gst_video_overlay_set_window_handle (overlay_element, handle);
4907 gst_object_unref (overlay_element);
4912 gst_play_sink_overlay_init (gpointer g_iface, gpointer g_iface_data)
4914 GstVideoOverlayInterface *iface = (GstVideoOverlayInterface *) g_iface;
4915 iface->expose = gst_play_sink_overlay_expose;
4916 iface->handle_events = gst_play_sink_overlay_handle_events;
4917 iface->set_render_rectangle = gst_play_sink_overlay_set_render_rectangle;
4918 iface->set_window_handle = gst_play_sink_overlay_set_window_handle;
4922 gst_play_sink_navigation_send_event (GstNavigation * navigation,
4923 GstStructure * structure)
4925 GstPlaySink *playsink = GST_PLAY_SINK (navigation);
4928 GST_PLAY_SINK_LOCK (playsink);
4929 if (playsink->videochain && playsink->videochain->chain.bin)
4930 bin = GST_BIN (gst_object_ref (playsink->videochain->chain.bin));
4931 GST_PLAY_SINK_UNLOCK (playsink);
4934 GstElement *nav = gst_bin_get_by_interface (bin, GST_TYPE_NAVIGATION);
4937 gst_navigation_send_event (GST_NAVIGATION (nav), structure);
4939 gst_object_unref (nav);
4941 GstEvent *event = gst_event_new_navigation (structure);
4943 gst_element_send_event (GST_ELEMENT (bin), event);
4946 gst_object_unref (bin);
4950 gst_structure_free (structure);
4954 gst_play_sink_navigation_init (gpointer g_iface, gpointer g_iface_data)
4956 GstNavigationInterface *iface = (GstNavigationInterface *) g_iface;
4958 iface->send_event = gst_play_sink_navigation_send_event;
4961 static const GList *
4962 gst_play_sink_colorbalance_list_channels (GstColorBalance * balance)
4964 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4966 return playsink->colorbalance_channels;
4970 gst_play_sink_colorbalance_set_value (GstColorBalance * balance,
4971 GstColorBalanceChannel * proxy, gint value)
4973 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4976 GstColorBalance *balance_element = NULL;
4978 GST_OBJECT_LOCK (playsink);
4979 if (playsink->colorbalance_element)
4981 GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
4982 GST_OBJECT_UNLOCK (playsink);
4984 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
4985 GstColorBalanceChannel *proxy_tmp = l->data;
4988 if (proxy_tmp != proxy)
4991 playsink->colorbalance_values[i] = value;
4993 if (balance_element) {
4994 GstColorBalanceChannel *channel = NULL;
4995 const GList *channels, *k;
4997 channels = gst_color_balance_list_channels (balance_element);
4998 for (k = channels; k; k = k->next) {
4999 GstColorBalanceChannel *tmp = l->data;
5001 if (g_strrstr (tmp->label, proxy->label)) {
5009 /* Convert to [0, 1] range */
5012 (gdouble) proxy->min_value) / ((gdouble) proxy->max_value -
5013 (gdouble) proxy->min_value);
5014 /* Convert to channel range */
5016 channel->min_value + new_val * ((gdouble) channel->max_value -
5017 (gdouble) channel->min_value);
5019 gst_color_balance_set_value (balance_element, channel,
5020 (gint) (new_val + 0.5));
5022 gst_object_unref (balance_element);
5025 gst_color_balance_value_changed (balance, proxy, value);
5031 gst_play_sink_colorbalance_get_value (GstColorBalance * balance,
5032 GstColorBalanceChannel * proxy)
5034 GstPlaySink *playsink = GST_PLAY_SINK (balance);
5038 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
5039 GstColorBalanceChannel *proxy_tmp = l->data;
5041 if (proxy_tmp != proxy)
5044 return playsink->colorbalance_values[i];
5047 g_return_val_if_reached (0);
5050 static GstColorBalanceType
5051 gst_play_sink_colorbalance_get_balance_type (GstColorBalance * balance)
5053 GstPlaySink *playsink = GST_PLAY_SINK (balance);
5054 GstColorBalance *balance_element = NULL;
5055 GstColorBalanceType t = GST_COLOR_BALANCE_SOFTWARE;
5057 GST_OBJECT_LOCK (playsink);
5058 if (playsink->colorbalance_element)
5060 GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
5061 GST_OBJECT_UNLOCK (playsink);
5063 if (balance_element) {
5064 t = gst_color_balance_get_balance_type (balance_element);
5065 gst_object_unref (balance_element);
5072 gst_play_sink_colorbalance_init (gpointer g_iface, gpointer g_iface_data)
5074 GstColorBalanceInterface *iface = (GstColorBalanceInterface *) g_iface;
5076 iface->list_channels = gst_play_sink_colorbalance_list_channels;
5077 iface->set_value = gst_play_sink_colorbalance_set_value;
5078 iface->get_value = gst_play_sink_colorbalance_get_value;
5079 iface->get_balance_type = gst_play_sink_colorbalance_get_balance_type;
5083 gst_play_sink_plugin_init (GstPlugin * plugin)
5085 GST_DEBUG_CATEGORY_INIT (gst_play_sink_debug, "playsink", 0, "play bin");
5086 return gst_element_register (plugin, "playsink", GST_RANK_NONE,
5087 GST_TYPE_PLAY_SINK);