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 GstElement *mute; /* element with the mute property */
107 GstElement *ts_offset;
113 GstPad *sinkpad, *srcpad;
115 GstElement *deinterlace;
116 } GstPlayVideoDeinterlaceChain;
126 GstElement *ts_offset;
135 GstElement *resample;
136 GstPad *blockpad; /* srcpad of queue, used for blocking the vis */
137 GstPad *vispeerpad; /* srcpad of resample, used for unlinking the vis */
138 GstPad *vissinkpad; /* visualisation sinkpad, */
140 GstPad *vissrcpad; /* visualisation srcpad, */
141 GstPad *srcpad; /* outgoing srcpad, used to connect to the next
150 GstElement *identity;
152 GstPad *videosinkpad;
154 GstPad *srcpad; /* outgoing srcpad, used to connect to the next
156 GstElement *sink; /* custom sink to receive subtitle buffers */
159 #define GST_PLAY_SINK_GET_LOCK(playsink) (&((GstPlaySink *)playsink)->lock)
160 #define GST_PLAY_SINK_LOCK(playsink) G_STMT_START { \
161 GST_LOG_OBJECT (playsink, "locking from thread %p", g_thread_self ()); \
162 g_rec_mutex_lock (GST_PLAY_SINK_GET_LOCK (playsink)); \
163 GST_LOG_OBJECT (playsink, "locked from thread %p", g_thread_self ()); \
165 #define GST_PLAY_SINK_UNLOCK(playsink) G_STMT_START { \
166 GST_LOG_OBJECT (playsink, "unlocking from thread %p", g_thread_self ()); \
167 g_rec_mutex_unlock (GST_PLAY_SINK_GET_LOCK (playsink)); \
170 #define PENDING_FLAG_SET(playsink, flagtype) \
171 ((playsink->pending_blocked_pads) |= (1 << flagtype))
172 #define PENDING_FLAG_UNSET(playsink, flagtype) \
173 ((playsink->pending_blocked_pads) &= ~(1 << flagtype))
174 #define PENDING_FLAG_IS_SET(playsink, flagtype) \
175 ((playsink->pending_blocked_pads) & (1 << flagtype))
176 #define PENDING_VIDEO_BLOCK(playsink) \
177 ((playsink->pending_blocked_pads) & (1 << GST_PLAY_SINK_TYPE_VIDEO_RAW | 1 << GST_PLAY_SINK_TYPE_VIDEO))
178 #define PENDING_AUDIO_BLOCK(playsink) \
179 ((playsink->pending_blocked_pads) & (1 << GST_PLAY_SINK_TYPE_AUDIO_RAW | 1 << GST_PLAY_SINK_TYPE_AUDIO))
180 #define PENDING_TEXT_BLOCK(playsink) \
181 PENDING_FLAG_IS_SET(playsink, GST_PLAY_SINK_TYPE_TEXT)
189 gboolean async_pending;
190 gboolean need_async_start;
194 GstStreamSynchronizer *stream_synchronizer;
197 GstPlayAudioChain *audiochain;
198 GstPlayVideoDeinterlaceChain *videodeinterlacechain;
199 GstPlayVideoChain *videochain;
200 GstPlayVisChain *vischain;
201 GstPlayTextChain *textchain;
205 gboolean audio_pad_raw;
206 gboolean audio_pad_blocked;
207 GstPad *audio_srcpad_stream_synchronizer;
208 GstPad *audio_sinkpad_stream_synchronizer;
209 gulong audio_block_id;
211 GstElement *audio_tee;
212 GstPad *audio_tee_sink;
213 GstPad *audio_tee_asrc;
214 GstPad *audio_tee_vissrc;
217 gboolean video_pad_raw;
218 gboolean video_pad_blocked;
219 GstPad *video_srcpad_stream_synchronizer;
220 GstPad *video_sinkpad_stream_synchronizer;
221 gulong video_block_id;
224 gboolean text_pad_blocked;
225 GstPad *text_srcpad_stream_synchronizer;
226 GstPad *text_sinkpad_stream_synchronizer;
227 gulong text_block_id;
229 guint32 pending_blocked_pads;
232 GstElement *audio_sink;
233 GstElement *video_sink;
234 GstElement *visualisation;
235 GstElement *text_sink;
238 gchar *font_desc; /* font description */
239 gchar *subtitle_encoding; /* subtitle encoding */
240 guint connection_speed; /* connection speed in bits/sec (0 = unknown) */
242 gboolean volume_changed; /* volume/mute changed while no audiochain */
243 gboolean mute_changed; /* ... has been created yet */
245 GstPlaySinkSendEventMode send_event_mode;
246 gboolean force_aspect_ratio;
248 /* videooverlay proxy interface */
249 GstVideoOverlay *overlay_element; /* protected with LOCK */
250 gboolean overlay_handle_set;
251 guintptr overlay_handle;
252 gboolean overlay_render_rectangle_set;
253 gint overlay_x, overlay_y, overlay_width, overlay_height;
254 gboolean overlay_handle_events_set;
255 gboolean overlay_handle_events;
257 /* colorbalance proxy interface */
258 GstColorBalance *colorbalance_element;
259 GList *colorbalance_channels; /* CONTRAST, BRIGHTNESS, HUE, SATURATION */
260 gint colorbalance_values[4];
262 /* sending audio/video flushes break stream changes when the pipeline
263 * is paused and played again in 0.10 */
265 GstSegment video_segment;
266 gboolean video_custom_flush_finished;
267 gboolean video_ignore_wrong_state;
268 gboolean video_pending_flush;
270 GstSegment audio_segment;
271 gboolean audio_custom_flush_finished;
272 gboolean audio_ignore_wrong_state;
273 gboolean audio_pending_flush;
276 GstSegment text_segment;
277 gboolean text_custom_flush_finished;
278 gboolean text_ignore_wrong_state;
279 gboolean text_pending_flush;
282 struct _GstPlaySinkClass
284 GstBinClass parent_class;
286 gboolean (*reconfigure) (GstPlaySink * playsink);
288 GstSample *(*convert_sample) (GstPlaySink * playsink, GstCaps * caps);
292 static GstStaticPadTemplate audiotemplate =
293 GST_STATIC_PAD_TEMPLATE ("audio_sink",
296 GST_STATIC_CAPS_ANY);
297 static GstStaticPadTemplate videotemplate =
298 GST_STATIC_PAD_TEMPLATE ("video_sink",
301 GST_STATIC_CAPS_ANY);
302 static GstStaticPadTemplate texttemplate = GST_STATIC_PAD_TEMPLATE ("text_sink",
305 GST_STATIC_CAPS_ANY);
307 /* FIXME 0.11: Remove */
308 static GstStaticPadTemplate audiorawtemplate =
309 GST_STATIC_PAD_TEMPLATE ("audio_raw_sink",
312 GST_STATIC_CAPS_ANY);
313 static GstStaticPadTemplate videorawtemplate =
314 GST_STATIC_PAD_TEMPLATE ("video_raw_sink",
317 GST_STATIC_CAPS_ANY);
328 PROP_SUBTITLE_ENCODING,
335 PROP_SEND_EVENT_MODE,
336 PROP_FORCE_ASPECT_RATIO,
346 static void gst_play_sink_dispose (GObject * object);
347 static void gst_play_sink_finalize (GObject * object);
348 static void gst_play_sink_set_property (GObject * object, guint prop_id,
349 const GValue * value, GParamSpec * spec);
350 static void gst_play_sink_get_property (GObject * object, guint prop_id,
351 GValue * value, GParamSpec * spec);
353 static GstPad *gst_play_sink_request_new_pad (GstElement * element,
354 GstPadTemplate * templ, const gchar * name, const GstCaps * caps);
355 static void gst_play_sink_release_request_pad (GstElement * element,
357 static gboolean gst_play_sink_send_event (GstElement * element,
359 static GstStateChangeReturn gst_play_sink_change_state (GstElement * element,
360 GstStateChange transition);
362 static void gst_play_sink_handle_message (GstBin * bin, GstMessage * message);
364 /* sending audio/video flushes break stream changes when the pipeline
365 * is paused and played again in 0.10 */
367 static gboolean gst_play_sink_video_sink_event (GstPad * pad, GstEvent * event);
368 static GstFlowReturn gst_play_sink_video_sink_chain (GstPad * pad,
370 static gboolean gst_play_sink_audio_sink_event (GstPad * pad, GstEvent * event);
371 static GstFlowReturn gst_play_sink_audio_sink_chain (GstPad * pad,
374 static gboolean gst_play_sink_text_sink_event (GstPad * pad, GstObject * parent,
376 static GstFlowReturn gst_play_sink_text_sink_chain (GstPad * pad,
377 GstObject * parent, GstBuffer * buffer);
379 static void notify_volume_cb (GObject * object, GParamSpec * pspec,
380 GstPlaySink * playsink);
381 static void notify_mute_cb (GObject * object, GParamSpec * pspec,
382 GstPlaySink * playsink);
384 static void update_av_offset (GstPlaySink * playsink);
386 static gboolean gst_play_sink_do_reconfigure (GstPlaySink * playsink);
388 static GQuark _playsink_reset_segment_event_marker_id = 0;
390 /* static guint gst_play_sink_signals[LAST_SIGNAL] = { 0 }; */
392 static void gst_play_sink_overlay_init (gpointer g_iface,
393 gpointer g_iface_data);
394 static void gst_play_sink_navigation_init (gpointer g_iface,
395 gpointer g_iface_data);
396 static void gst_play_sink_colorbalance_init (gpointer g_iface,
397 gpointer g_iface_data);
400 _do_init (GType type)
402 static const GInterfaceInfo svol_info = {
405 static const GInterfaceInfo ov_info = {
406 gst_play_sink_overlay_init,
409 static const GInterfaceInfo nav_info = {
410 gst_play_sink_navigation_init,
413 static const GInterfaceInfo col_info = {
414 gst_play_sink_colorbalance_init,
418 g_type_add_interface_static (type, GST_TYPE_STREAM_VOLUME, &svol_info);
419 g_type_add_interface_static (type, GST_TYPE_VIDEO_OVERLAY, &ov_info);
420 g_type_add_interface_static (type, GST_TYPE_NAVIGATION, &nav_info);
421 g_type_add_interface_static (type, GST_TYPE_COLOR_BALANCE, &col_info);
424 G_DEFINE_TYPE_WITH_CODE (GstPlaySink, gst_play_sink, GST_TYPE_BIN,
425 _do_init (g_define_type_id));
428 gst_play_sink_class_init (GstPlaySinkClass * klass)
430 GObjectClass *gobject_klass;
431 GstElementClass *gstelement_klass;
432 GstBinClass *gstbin_klass;
434 gobject_klass = (GObjectClass *) klass;
435 gstelement_klass = (GstElementClass *) klass;
436 gstbin_klass = (GstBinClass *) klass;
438 gobject_klass->dispose = gst_play_sink_dispose;
439 gobject_klass->finalize = gst_play_sink_finalize;
440 gobject_klass->set_property = gst_play_sink_set_property;
441 gobject_klass->get_property = gst_play_sink_get_property;
447 * Control the behaviour of playsink.
449 g_object_class_install_property (gobject_klass, PROP_FLAGS,
450 g_param_spec_flags ("flags", "Flags", "Flags to control behaviour",
451 GST_TYPE_PLAY_FLAGS, DEFAULT_FLAGS,
452 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
455 * GstPlaySink:volume:
457 * Get or set the current audio stream volume. 1.0 means 100%,
458 * 0.0 means mute. This uses a linear volume scale.
461 g_object_class_install_property (gobject_klass, PROP_VOLUME,
462 g_param_spec_double ("volume", "Volume", "The audio volume, 1.0=100%",
463 0.0, VOLUME_MAX_DOUBLE, 1.0,
464 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
465 g_object_class_install_property (gobject_klass, PROP_MUTE,
466 g_param_spec_boolean ("mute", "Mute",
467 "Mute the audio channel without changing the volume", FALSE,
468 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
469 g_object_class_install_property (gobject_klass, PROP_FONT_DESC,
470 g_param_spec_string ("subtitle-font-desc",
471 "Subtitle font description",
472 "Pango font description of font "
473 "to be used for subtitle rendering", NULL,
474 G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
475 g_object_class_install_property (gobject_klass, PROP_SUBTITLE_ENCODING,
476 g_param_spec_string ("subtitle-encoding", "subtitle encoding",
477 "Encoding to assume if input subtitles are not in UTF-8 encoding. "
478 "If not set, the GST_SUBTITLE_ENCODING environment variable will "
479 "be checked for an encoding to use. If that is not set either, "
480 "ISO-8859-15 will be assumed.", NULL,
481 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
482 g_object_class_install_property (gobject_klass, PROP_VIS_PLUGIN,
483 g_param_spec_object ("vis-plugin", "Vis plugin",
484 "the visualization element to use (NULL = default)",
485 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
487 * GstPlaySink:sample:
489 * Get the currently rendered or prerolled sample in the video sink.
490 * The #GstCaps in the sample will describe the format of the buffer.
492 g_object_class_install_property (gobject_klass, PROP_SAMPLE,
493 g_param_spec_boxed ("sample", "Sample",
494 "The last sample (NULL = no video available)",
495 GST_TYPE_SAMPLE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
497 * GstPlaySink:av-offset:
499 * Control the synchronisation offset between the audio and video streams.
500 * Positive values make the audio ahead of the video and negative values make
501 * the audio go behind the video.
505 g_object_class_install_property (gobject_klass, PROP_AV_OFFSET,
506 g_param_spec_int64 ("av-offset", "AV Offset",
507 "The synchronisation offset between audio and video in nanoseconds",
508 G_MININT64, G_MAXINT64, 0,
509 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
512 * GstPlaySink:video-sink:
514 * Set the used video sink element. NULL will use the default sink. playsink
515 * must be in %GST_STATE_NULL
519 g_object_class_install_property (gobject_klass, PROP_VIDEO_SINK,
520 g_param_spec_object ("video-sink", "Video Sink",
521 "the video output element to use (NULL = default sink)",
522 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
524 * GstPlaySink:audio-sink:
526 * Set the used audio sink element. NULL will use the default sink. playsink
527 * must be in %GST_STATE_NULL
531 g_object_class_install_property (gobject_klass, PROP_AUDIO_SINK,
532 g_param_spec_object ("audio-sink", "Audio Sink",
533 "the audio output element to use (NULL = default sink)",
534 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
537 * GstPlaySink:text-sink:
539 * Set the used text sink element. NULL will use the default sink. playsink
540 * must be in %GST_STATE_NULL
544 g_object_class_install_property (gobject_klass, PROP_TEXT_SINK,
545 g_param_spec_object ("text-sink", "Text sink",
546 "the text output element to use (NULL = default subtitleoverlay)",
547 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
550 * GstPlaySink::send-event-mode:
552 * Sets the handling method used for events received from send_event
553 * function. The default is %MODE_DEFAULT, that uses %GstBin's default
554 * handling (push the event to all internal sinks).
558 g_object_class_install_property (gobject_klass, PROP_SEND_EVENT_MODE,
559 g_param_spec_enum ("send-event-mode", "Send event mode",
560 "How to send events received in send_event function",
561 GST_TYPE_PLAY_SINK_SEND_EVENT_MODE, MODE_DEFAULT,
562 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
565 * GstPlaySink::force-aspect-ratio:
567 * Requests the video sink to enforce the video display aspect ratio.
571 g_object_class_install_property (gobject_klass, PROP_FORCE_ASPECT_RATIO,
572 g_param_spec_boolean ("force-aspect-ratio", "Force Aspect Ratio",
573 "When enabled, scaling will respect original aspect ratio", TRUE,
574 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
576 g_signal_new ("reconfigure", G_TYPE_FROM_CLASS (klass),
577 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstPlaySinkClass,
578 reconfigure), NULL, NULL, g_cclosure_marshal_generic, G_TYPE_BOOLEAN,
581 * GstPlaySink::convert-sample
582 * @playsink: a #GstPlaySink
583 * @caps: the target format of the sample
585 * Action signal to retrieve the currently playing video sample in the format
586 * specified by @caps.
587 * If @caps is %NULL, no conversion will be performed and this function is
588 * equivalent to the #GstPlaySink::sample property.
590 * Returns: a #GstSample of the current video sample converted to #caps.
591 * The caps in the sample will describe the final layout of the buffer data.
592 * %NULL is returned when no current sample can be retrieved or when the
595 g_signal_new ("convert-sample", G_TYPE_FROM_CLASS (klass),
596 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
597 G_STRUCT_OFFSET (GstPlaySinkClass, convert_sample), NULL, NULL,
598 g_cclosure_marshal_generic, GST_TYPE_SAMPLE, 1, GST_TYPE_CAPS);
600 gst_element_class_add_pad_template (gstelement_klass,
601 gst_static_pad_template_get (&audiorawtemplate));
602 gst_element_class_add_pad_template (gstelement_klass,
603 gst_static_pad_template_get (&audiotemplate));
604 gst_element_class_add_pad_template (gstelement_klass,
605 gst_static_pad_template_get (&videorawtemplate));
606 gst_element_class_add_pad_template (gstelement_klass,
607 gst_static_pad_template_get (&videotemplate));
608 gst_element_class_add_pad_template (gstelement_klass,
609 gst_static_pad_template_get (&texttemplate));
610 gst_element_class_set_static_metadata (gstelement_klass, "Player Sink",
612 "Convenience sink for multiple streams",
613 "Wim Taymans <wim.taymans@gmail.com>");
615 gstelement_klass->change_state =
616 GST_DEBUG_FUNCPTR (gst_play_sink_change_state);
617 gstelement_klass->send_event = GST_DEBUG_FUNCPTR (gst_play_sink_send_event);
618 gstelement_klass->request_new_pad =
619 GST_DEBUG_FUNCPTR (gst_play_sink_request_new_pad);
620 gstelement_klass->release_pad =
621 GST_DEBUG_FUNCPTR (gst_play_sink_release_request_pad);
623 gstbin_klass->handle_message =
624 GST_DEBUG_FUNCPTR (gst_play_sink_handle_message);
626 klass->reconfigure = GST_DEBUG_FUNCPTR (gst_play_sink_reconfigure);
627 klass->convert_sample = GST_DEBUG_FUNCPTR (gst_play_sink_convert_sample);
629 _playsink_reset_segment_event_marker_id =
630 g_quark_from_static_string ("gst-playsink-reset-segment-event-marker");
632 g_type_class_ref (GST_TYPE_STREAM_SYNCHRONIZER);
633 g_type_class_ref (GST_TYPE_COLOR_BALANCE_CHANNEL);
637 gst_play_sink_init (GstPlaySink * playsink)
639 GstColorBalanceChannel *channel;
642 playsink->video_sink = NULL;
643 playsink->audio_sink = NULL;
644 playsink->visualisation = NULL;
645 playsink->text_sink = NULL;
646 playsink->volume = 1.0;
647 playsink->font_desc = NULL;
648 playsink->subtitle_encoding = NULL;
649 playsink->flags = DEFAULT_FLAGS;
650 playsink->send_event_mode = MODE_DEFAULT;
651 playsink->force_aspect_ratio = TRUE;
653 playsink->stream_synchronizer =
654 g_object_new (GST_TYPE_STREAM_SYNCHRONIZER, NULL);
655 gst_bin_add (GST_BIN_CAST (playsink),
656 GST_ELEMENT_CAST (playsink->stream_synchronizer));
658 g_rec_mutex_init (&playsink->lock);
659 GST_OBJECT_FLAG_SET (playsink, GST_ELEMENT_FLAG_SINK);
662 GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
664 channel->label = g_strdup ("CONTRAST");
665 channel->min_value = -1000;
666 channel->max_value = 1000;
667 playsink->colorbalance_channels =
668 g_list_append (playsink->colorbalance_channels, channel);
669 playsink->colorbalance_values[0] = 0;
672 GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
674 channel->label = g_strdup ("BRIGHTNESS");
675 channel->min_value = -1000;
676 channel->max_value = 1000;
677 playsink->colorbalance_channels =
678 g_list_append (playsink->colorbalance_channels, channel);
679 playsink->colorbalance_values[1] = 0;
682 GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
684 channel->label = g_strdup ("HUE");
685 channel->min_value = -1000;
686 channel->max_value = 1000;
687 playsink->colorbalance_channels =
688 g_list_append (playsink->colorbalance_channels, channel);
689 playsink->colorbalance_values[2] = 0;
692 GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
694 channel->label = g_strdup ("SATURATION");
695 channel->min_value = -1000;
696 channel->max_value = 1000;
697 playsink->colorbalance_channels =
698 g_list_append (playsink->colorbalance_channels, channel);
699 playsink->colorbalance_values[3] = 0;
703 disconnect_chain (GstPlayAudioChain * chain, GstPlaySink * playsink)
707 g_signal_handlers_disconnect_by_func (chain->volume, notify_volume_cb,
710 g_signal_handlers_disconnect_by_func (chain->mute, notify_mute_cb,
716 free_chain (GstPlayChain * chain)
720 gst_object_unref (chain->bin);
726 gst_play_sink_dispose (GObject * object)
728 GstPlaySink *playsink;
730 playsink = GST_PLAY_SINK (object);
732 if (playsink->audio_sink != NULL) {
733 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
734 gst_object_unref (playsink->audio_sink);
735 playsink->audio_sink = NULL;
737 if (playsink->video_sink != NULL) {
738 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
739 gst_object_unref (playsink->video_sink);
740 playsink->video_sink = NULL;
742 if (playsink->visualisation != NULL) {
743 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
744 gst_object_unref (playsink->visualisation);
745 playsink->visualisation = NULL;
747 if (playsink->text_sink != NULL) {
748 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
749 gst_object_unref (playsink->text_sink);
750 playsink->text_sink = NULL;
753 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
754 playsink->videodeinterlacechain = NULL;
755 free_chain ((GstPlayChain *) playsink->videochain);
756 playsink->videochain = NULL;
757 free_chain ((GstPlayChain *) playsink->audiochain);
758 playsink->audiochain = NULL;
759 free_chain ((GstPlayChain *) playsink->vischain);
760 playsink->vischain = NULL;
761 free_chain ((GstPlayChain *) playsink->textchain);
762 playsink->textchain = NULL;
764 if (playsink->audio_tee_sink) {
765 gst_object_unref (playsink->audio_tee_sink);
766 playsink->audio_tee_sink = NULL;
769 if (playsink->audio_tee_vissrc) {
770 gst_element_release_request_pad (playsink->audio_tee,
771 playsink->audio_tee_vissrc);
772 gst_object_unref (playsink->audio_tee_vissrc);
773 playsink->audio_tee_vissrc = NULL;
776 if (playsink->audio_tee_asrc) {
777 gst_element_release_request_pad (playsink->audio_tee,
778 playsink->audio_tee_asrc);
779 gst_object_unref (playsink->audio_tee_asrc);
780 playsink->audio_tee_asrc = NULL;
783 g_free (playsink->font_desc);
784 playsink->font_desc = NULL;
786 g_free (playsink->subtitle_encoding);
787 playsink->subtitle_encoding = NULL;
789 playsink->stream_synchronizer = NULL;
791 g_list_foreach (playsink->colorbalance_channels, (GFunc) gst_object_unref,
793 g_list_free (playsink->colorbalance_channels);
794 playsink->colorbalance_channels = NULL;
796 G_OBJECT_CLASS (gst_play_sink_parent_class)->dispose (object);
800 gst_play_sink_finalize (GObject * object)
802 GstPlaySink *playsink;
804 playsink = GST_PLAY_SINK (object);
806 g_rec_mutex_clear (&playsink->lock);
808 G_OBJECT_CLASS (gst_play_sink_parent_class)->finalize (object);
812 gst_play_sink_set_sink (GstPlaySink * playsink, GstPlaySinkType type,
815 GstElement **elem = NULL, *old = NULL;
817 GST_LOG ("Setting sink %" GST_PTR_FORMAT " as sink type %d", sink, type);
819 GST_PLAY_SINK_LOCK (playsink);
821 case GST_PLAY_SINK_TYPE_AUDIO:
822 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
823 elem = &playsink->audio_sink;
825 case GST_PLAY_SINK_TYPE_VIDEO:
826 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
827 elem = &playsink->video_sink;
829 case GST_PLAY_SINK_TYPE_TEXT:
830 elem = &playsink->text_sink;
838 gst_object_ref (sink);
841 GST_PLAY_SINK_UNLOCK (playsink);
845 gst_element_set_state (old, GST_STATE_NULL);
846 gst_object_unref (old);
851 gst_play_sink_get_sink (GstPlaySink * playsink, GstPlaySinkType type)
853 GstElement *result = NULL;
854 GstElement *elem = NULL, *chainp = NULL;
856 GST_PLAY_SINK_LOCK (playsink);
858 case GST_PLAY_SINK_TYPE_AUDIO:
859 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
861 GstPlayAudioChain *chain;
862 if ((chain = (GstPlayAudioChain *) playsink->audiochain))
863 chainp = chain->sink;
864 elem = playsink->audio_sink;
867 case GST_PLAY_SINK_TYPE_VIDEO:
868 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
870 GstPlayVideoChain *chain;
871 if ((chain = (GstPlayVideoChain *) playsink->videochain))
872 chainp = chain->sink;
873 elem = playsink->video_sink;
876 case GST_PLAY_SINK_TYPE_TEXT:
878 GstPlayTextChain *chain;
879 if ((chain = (GstPlayTextChain *) playsink->textchain))
880 chainp = chain->sink;
881 elem = playsink->text_sink;
888 /* we have an active chain with a sink, get the sink */
889 result = gst_object_ref (chainp);
891 /* nothing found, return last configured sink */
892 if (result == NULL && elem)
893 result = gst_object_ref (elem);
894 GST_PLAY_SINK_UNLOCK (playsink);
899 static GstPadProbeReturn
900 gst_play_sink_vis_blocked (GstPad * tee_pad, GstPadProbeInfo * info,
903 GstPlaySink *playsink;
904 GstPlayVisChain *chain;
906 playsink = GST_PLAY_SINK (user_data);
908 GST_PLAY_SINK_LOCK (playsink);
909 GST_DEBUG_OBJECT (playsink, "vis pad blocked");
910 /* now try to change the plugin in the running vis chain */
911 if (!(chain = (GstPlayVisChain *) playsink->vischain))
914 /* unlink the old plugin and unghost the pad */
915 gst_pad_unlink (chain->vispeerpad, chain->vissinkpad);
916 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad), NULL);
918 /* set the old plugin to NULL and remove */
919 gst_element_set_state (chain->vis, GST_STATE_NULL);
920 gst_bin_remove (GST_BIN_CAST (chain->chain.bin), chain->vis);
922 /* add new plugin and set state to playing */
923 chain->vis = playsink->visualisation;
924 gst_bin_add (GST_BIN_CAST (chain->chain.bin), chain->vis);
925 gst_element_set_state (chain->vis, GST_STATE_PLAYING);
928 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
929 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
932 gst_pad_link_full (chain->vispeerpad, chain->vissinkpad,
933 GST_PAD_LINK_CHECK_NOTHING);
934 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad),
938 GST_PLAY_SINK_UNLOCK (playsink);
940 /* remove the probe and unblock the pad */
941 return GST_PAD_PROBE_REMOVE;
945 gst_play_sink_set_vis_plugin (GstPlaySink * playsink, GstElement * vis)
947 GstPlayVisChain *chain;
949 /* setting NULL means creating the default vis plugin */
951 vis = gst_element_factory_make ("goom", "vis");
953 /* simply return if we don't have a vis plugin here */
957 GST_PLAY_SINK_LOCK (playsink);
958 /* first store the new vis */
959 if (playsink->visualisation)
960 gst_object_unref (playsink->visualisation);
962 gst_object_ref_sink (vis);
963 playsink->visualisation = vis;
965 /* now try to change the plugin in the running vis chain, if we have no chain,
966 * we don't bother, any future vis chain will be created with the new vis
968 if (!(chain = (GstPlayVisChain *) playsink->vischain))
971 /* block the pad, the next time the callback is called we can change the
972 * visualisation. It's possible that this never happens or that the pad was
973 * already blocked. If the callback never happens, we don't have new data so
974 * we don't need the new vis plugin. If the pad was already blocked, the
975 * function returns FALSE but the previous pad block will do the right thing
977 GST_DEBUG_OBJECT (playsink, "blocking vis pad");
978 gst_pad_add_probe (chain->blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
979 gst_play_sink_vis_blocked, playsink, NULL);
981 GST_PLAY_SINK_UNLOCK (playsink);
987 gst_play_sink_get_vis_plugin (GstPlaySink * playsink)
989 GstElement *result = NULL;
990 GstPlayVisChain *chain;
992 GST_PLAY_SINK_LOCK (playsink);
993 if ((chain = (GstPlayVisChain *) playsink->vischain)) {
994 /* we have an active chain, get the sink */
996 result = gst_object_ref (chain->vis);
998 /* nothing found, return last configured sink */
999 if (result == NULL && playsink->visualisation)
1000 result = gst_object_ref (playsink->visualisation);
1001 GST_PLAY_SINK_UNLOCK (playsink);
1007 gst_play_sink_set_volume (GstPlaySink * playsink, gdouble volume)
1009 GstPlayAudioChain *chain;
1011 GST_PLAY_SINK_LOCK (playsink);
1012 playsink->volume = volume;
1013 chain = (GstPlayAudioChain *) playsink->audiochain;
1014 if (chain && chain->volume) {
1015 GST_LOG_OBJECT (playsink, "elements: volume=%" GST_PTR_FORMAT ", mute=%"
1016 GST_PTR_FORMAT "; new volume=%.03f, mute=%d", chain->volume,
1017 chain->mute, volume, playsink->mute);
1018 /* if there is a mute element or we are not muted, set the volume */
1019 if (chain->mute || !playsink->mute)
1020 g_object_set (chain->volume, "volume", volume, NULL);
1022 GST_LOG_OBJECT (playsink, "no volume element");
1023 playsink->volume_changed = TRUE;
1025 GST_PLAY_SINK_UNLOCK (playsink);
1029 gst_play_sink_get_volume (GstPlaySink * playsink)
1032 GstPlayAudioChain *chain;
1034 GST_PLAY_SINK_LOCK (playsink);
1035 chain = (GstPlayAudioChain *) playsink->audiochain;
1036 result = playsink->volume;
1037 if (chain && chain->volume) {
1038 if (chain->mute || !playsink->mute) {
1039 g_object_get (chain->volume, "volume", &result, NULL);
1040 playsink->volume = result;
1043 GST_PLAY_SINK_UNLOCK (playsink);
1049 gst_play_sink_set_mute (GstPlaySink * playsink, gboolean mute)
1051 GstPlayAudioChain *chain;
1053 GST_PLAY_SINK_LOCK (playsink);
1054 playsink->mute = mute;
1055 chain = (GstPlayAudioChain *) playsink->audiochain;
1058 g_object_set (chain->mute, "mute", mute, NULL);
1059 } else if (chain->volume) {
1061 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
1063 g_object_set (chain->volume, "volume", (gdouble) playsink->volume,
1067 playsink->mute_changed = TRUE;
1069 GST_PLAY_SINK_UNLOCK (playsink);
1073 gst_play_sink_get_mute (GstPlaySink * playsink)
1076 GstPlayAudioChain *chain;
1078 GST_PLAY_SINK_LOCK (playsink);
1079 chain = (GstPlayAudioChain *) playsink->audiochain;
1080 if (chain && chain->mute) {
1081 g_object_get (chain->mute, "mute", &result, NULL);
1082 playsink->mute = result;
1084 result = playsink->mute;
1086 GST_PLAY_SINK_UNLOCK (playsink);
1092 post_missing_element_message (GstPlaySink * playsink, const gchar * name)
1096 msg = gst_missing_element_message_new (GST_ELEMENT_CAST (playsink), name);
1097 gst_element_post_message (GST_ELEMENT_CAST (playsink), msg);
1101 add_chain (GstPlayChain * chain, gboolean add)
1103 if (chain->added == add)
1107 gst_bin_add (GST_BIN_CAST (chain->playsink), chain->bin);
1109 gst_bin_remove (GST_BIN_CAST (chain->playsink), chain->bin);
1110 /* we don't want to lose our sink status */
1111 GST_OBJECT_FLAG_SET (chain->playsink, GST_ELEMENT_FLAG_SINK);
1120 activate_chain (GstPlayChain * chain, gboolean activate)
1124 if (chain->activated == activate)
1127 GST_OBJECT_LOCK (chain->playsink);
1128 state = GST_STATE_TARGET (chain->playsink);
1129 GST_OBJECT_UNLOCK (chain->playsink);
1132 gst_element_set_state (chain->bin, state);
1134 gst_element_set_state (chain->bin, GST_STATE_NULL);
1136 chain->activated = activate;
1142 element_is_sink (GstElement * element)
1146 GST_OBJECT_LOCK (element);
1147 is_sink = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_FLAG_SINK);
1148 GST_OBJECT_UNLOCK (element);
1150 GST_DEBUG_OBJECT (element, "is a sink: %s", (is_sink) ? "yes" : "no");
1155 element_has_property (GstElement * element, const gchar * pname, GType type)
1159 pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (element), pname);
1161 if (pspec == NULL) {
1162 GST_DEBUG_OBJECT (element, "no %s property", pname);
1166 if (type == G_TYPE_INVALID || type == pspec->value_type ||
1167 g_type_is_a (pspec->value_type, type)) {
1168 GST_DEBUG_OBJECT (element, "has %s property of type %s", pname,
1169 (type == G_TYPE_INVALID) ? "any type" : g_type_name (type));
1173 GST_WARNING_OBJECT (element, "has %s property, but property is of type %s "
1174 "and we expected it to be of type %s", pname,
1175 g_type_name (pspec->value_type), g_type_name (type));
1182 const gchar *prop_name;
1185 } FindPropertyHelper;
1188 find_property (const GValue * item, FindPropertyHelper * helper)
1190 GstElement *element = g_value_get_object (item);
1191 if (helper->need_sink && !element_is_sink (element)) {
1195 if (!element_has_property (element, helper->prop_name, helper->prop_type)) {
1199 GST_INFO_OBJECT (element, "found %s with %s property", helper->prop_name,
1200 (helper->need_sink) ? "sink" : "element");
1201 return 0; /* keep it */
1204 /* FIXME: why not move these functions into core? */
1205 /* find a sink in the hierarchy with a property named @name. This function does
1206 * not increase the refcount of the returned object and thus remains valid as
1207 * long as the bin is valid. */
1209 gst_play_sink_find_property_sinks (GstPlaySink * playsink, GstElement * obj,
1210 const gchar * name, GType expected_type)
1212 GstElement *result = NULL;
1215 if (element_has_property (obj, name, expected_type)) {
1217 } else if (GST_IS_BIN (obj)) {
1219 GValue item = { 0, };
1220 FindPropertyHelper helper = { name, expected_type, TRUE };
1222 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1223 found = gst_iterator_find_custom (it,
1224 (GCompareFunc) find_property, &item, &helper);
1225 gst_iterator_free (it);
1227 result = g_value_get_object (&item);
1228 /* we don't need the extra ref */
1229 g_value_unset (&item);
1235 /* find an object in the hierarchy with a property named @name */
1237 gst_play_sink_find_property (GstPlaySink * playsink, GstElement * obj,
1238 const gchar * name, GType expected_type)
1240 GstElement *result = NULL;
1243 if (GST_IS_BIN (obj)) {
1245 GValue item = { 0, };
1246 FindPropertyHelper helper = { name, expected_type, FALSE };
1248 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1249 found = gst_iterator_find_custom (it,
1250 (GCompareFunc) find_property, &item, &helper);
1251 gst_iterator_free (it);
1253 result = g_value_dup_object (&item);
1254 g_value_unset (&item);
1257 if (element_has_property (obj, name, expected_type)) {
1259 gst_object_ref (obj);
1266 do_async_start (GstPlaySink * playsink)
1268 GstMessage *message;
1270 if (!playsink->need_async_start) {
1271 GST_INFO_OBJECT (playsink, "no async_start needed");
1275 playsink->async_pending = TRUE;
1277 GST_INFO_OBJECT (playsink, "Sending async_start message");
1278 message = gst_message_new_async_start (GST_OBJECT_CAST (playsink));
1279 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1280 (playsink), message);
1284 do_async_done (GstPlaySink * playsink)
1286 GstMessage *message;
1288 if (playsink->async_pending) {
1289 GST_INFO_OBJECT (playsink, "Sending async_done message");
1291 gst_message_new_async_done (GST_OBJECT_CAST (playsink),
1292 GST_CLOCK_TIME_NONE);
1293 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1294 (playsink), message);
1296 playsink->async_pending = FALSE;
1299 playsink->need_async_start = FALSE;
1302 /* try to change the state of an element. This function returns the element when
1303 * the state change could be performed. When this function returns NULL an error
1304 * occured and the element is unreffed if @unref is TRUE. */
1306 try_element (GstPlaySink * playsink, GstElement * element, gboolean unref)
1308 GstStateChangeReturn ret;
1311 ret = gst_element_set_state (element, GST_STATE_READY);
1312 if (ret == GST_STATE_CHANGE_FAILURE) {
1313 GST_DEBUG_OBJECT (playsink, "failed state change..");
1314 gst_element_set_state (element, GST_STATE_NULL);
1316 gst_object_unref (element);
1323 /* make the element (bin) that contains the elements needed to perform
1324 * video display. Only used for *raw* video streams.
1326 * +------------------------------------------------------------+
1328 * | +-------+ +----------+ +----------+ +---------+ |
1329 * | | queue | |colorspace| |videoscale| |videosink| |
1330 * | +-sink src-sink src-sink src-sink | |
1331 * | | +-------+ +----------+ +----------+ +---------+ |
1333 * +------------------------------------------------------------+
1336 static GstPlayVideoDeinterlaceChain *
1337 gen_video_deinterlace_chain (GstPlaySink * playsink)
1339 GstPlayVideoDeinterlaceChain *chain;
1342 GstElement *head = NULL, *prev = NULL;
1344 chain = g_new0 (GstPlayVideoDeinterlaceChain, 1);
1345 chain->chain.playsink = playsink;
1347 GST_DEBUG_OBJECT (playsink, "making video deinterlace chain %p", chain);
1349 /* create a bin to hold objects, as we create them we add them to this bin so
1350 * that when something goes wrong we only need to unref the bin */
1351 chain->chain.bin = gst_bin_new ("vdbin");
1352 bin = GST_BIN_CAST (chain->chain.bin);
1353 gst_object_ref_sink (bin);
1355 GST_DEBUG_OBJECT (playsink, "creating " COLORSPACE);
1356 chain->conv = gst_element_factory_make (COLORSPACE, "vdconv");
1357 if (chain->conv == NULL) {
1358 post_missing_element_message (playsink, COLORSPACE);
1359 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1360 (_("Missing element '%s' - check your GStreamer installation."),
1361 COLORSPACE), ("video rendering might fail"));
1363 gst_bin_add (bin, chain->conv);
1368 GST_DEBUG_OBJECT (playsink, "creating deinterlace");
1369 chain->deinterlace = gst_element_factory_make ("deinterlace", "deinterlace");
1370 if (chain->deinterlace == NULL) {
1371 chain->deinterlace =
1372 gst_element_factory_make ("avdeinterlace", "deinterlace");
1374 if (chain->deinterlace == NULL) {
1375 post_missing_element_message (playsink, "deinterlace");
1376 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1377 (_("Missing element '%s' - check your GStreamer installation."),
1378 "deinterlace"), ("deinterlacing won't work"));
1380 gst_bin_add (bin, chain->deinterlace);
1382 if (!gst_element_link_pads_full (prev, "src", chain->deinterlace, "sink",
1383 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1386 head = chain->deinterlace;
1388 prev = chain->deinterlace;
1392 pad = gst_element_get_static_pad (head, "sink");
1393 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1394 gst_object_unref (pad);
1396 chain->sinkpad = gst_ghost_pad_new_no_target ("sink", GST_PAD_SINK);
1400 pad = gst_element_get_static_pad (prev, "src");
1401 chain->srcpad = gst_ghost_pad_new ("src", pad);
1402 gst_object_unref (pad);
1404 chain->srcpad = gst_ghost_pad_new ("src", chain->sinkpad);
1407 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1408 gst_element_add_pad (chain->chain.bin, chain->srcpad);
1414 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1415 (NULL), ("Failed to configure the video deinterlace chain."));
1416 free_chain ((GstPlayChain *) chain);
1422 is_valid_color_balance_element (GstColorBalance * bal)
1424 gboolean have_brightness = FALSE;
1425 gboolean have_contrast = FALSE;
1426 gboolean have_hue = FALSE;
1427 gboolean have_saturation = FALSE;
1428 const GList *channels, *l;
1430 channels = gst_color_balance_list_channels (bal);
1431 for (l = channels; l; l = l->next) {
1432 GstColorBalanceChannel *ch = l->data;
1434 if (g_strrstr (ch->label, "BRIGHTNESS"))
1435 have_brightness = TRUE;
1436 else if (g_strrstr (ch->label, "CONTRAST"))
1437 have_contrast = TRUE;
1438 else if (g_strrstr (ch->label, "HUE"))
1440 else if (g_strrstr (ch->label, "SATURATION"))
1441 have_saturation = TRUE;
1444 return have_brightness && have_contrast && have_hue && have_saturation;
1448 iterate_color_balance_elements (const GValue * item, gpointer user_data)
1451 GstColorBalance *cb, **cb_out = user_data;
1453 cb = GST_COLOR_BALANCE (g_value_get_object (item));
1454 valid = is_valid_color_balance_element (cb);
1457 && gst_color_balance_get_balance_type (*cb_out) ==
1458 GST_COLOR_BALANCE_SOFTWARE) {
1459 gst_object_unref (*cb_out);
1460 *cb_out = GST_COLOR_BALANCE (gst_object_ref (cb));
1461 } else if (!*cb_out) {
1462 *cb_out = GST_COLOR_BALANCE (gst_object_ref (cb));
1467 static GstColorBalance *
1468 find_color_balance_element (GstElement * element)
1471 GstColorBalance *cb = NULL;
1473 if (GST_IS_COLOR_BALANCE (element)
1474 && is_valid_color_balance_element (GST_COLOR_BALANCE (element)))
1475 return GST_COLOR_BALANCE (gst_object_ref (element));
1476 else if (!GST_IS_BIN (element))
1479 it = gst_bin_iterate_all_by_interface (GST_BIN (element),
1480 GST_TYPE_COLOR_BALANCE);
1481 while (gst_iterator_foreach (it, iterate_color_balance_elements,
1482 &cb) == GST_ITERATOR_RESYNC)
1483 gst_iterator_resync (it);
1484 gst_iterator_free (it);
1490 colorbalance_value_changed_cb (GstColorBalance * balance,
1491 GstColorBalanceChannel * channel, gint value, GstPlaySink * playsink)
1496 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
1497 GstColorBalanceChannel *proxy = l->data;
1499 if (g_strrstr (channel->label, proxy->label)) {
1502 /* Convert to [0, 1] range */
1505 (gdouble) channel->min_value) / ((gdouble) channel->max_value -
1506 (gdouble) channel->min_value);
1507 /* Convert to proxy range */
1509 proxy->min_value + new_val * ((gdouble) proxy->max_value -
1510 (gdouble) proxy->min_value);
1511 playsink->colorbalance_values[i] = (gint) (0.5 + new_val);
1513 gst_color_balance_value_changed (GST_COLOR_BALANCE (playsink), proxy,
1514 playsink->colorbalance_values[i]);
1521 update_colorbalance (GstPlaySink * playsink)
1523 GstColorBalance *balance = NULL;
1527 GST_OBJECT_LOCK (playsink);
1528 if (playsink->colorbalance_element) {
1530 GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
1532 GST_OBJECT_UNLOCK (playsink);
1536 g_signal_handlers_block_by_func (balance,
1537 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1539 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
1540 GstColorBalanceChannel *proxy = l->data;
1541 GstColorBalanceChannel *channel = NULL;
1542 const GList *channels, *k;
1544 channels = gst_color_balance_list_channels (balance);
1545 for (k = channels; k; k = k->next) {
1546 GstColorBalanceChannel *tmp = k->data;
1548 if (g_strrstr (tmp->label, proxy->label)) {
1556 gst_color_balance_set_value (balance, channel,
1557 playsink->colorbalance_values[i]);
1560 g_signal_handlers_unblock_by_func (balance,
1561 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1563 gst_object_unref (balance);
1566 /* make the element (bin) that contains the elements needed to perform
1569 * +------------------------------------------------------------+
1571 * | +-------+ +----------+ +----------+ +---------+ |
1572 * | | queue | |colorspace| |videoscale| |videosink| |
1573 * | +-sink src-sink src-sink src-sink | |
1574 * | | +-------+ +----------+ +----------+ +---------+ |
1576 * +------------------------------------------------------------+
1579 static GstPlayVideoChain *
1580 gen_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1582 GstPlayVideoChain *chain;
1585 GstElement *head = NULL, *prev = NULL, *elem = NULL;
1587 chain = g_new0 (GstPlayVideoChain, 1);
1588 chain->chain.playsink = playsink;
1589 chain->chain.raw = raw;
1591 GST_DEBUG_OBJECT (playsink, "making video chain %p", chain);
1593 if (playsink->video_sink) {
1594 GST_DEBUG_OBJECT (playsink, "trying configured videosink");
1595 chain->sink = try_element (playsink, playsink->video_sink, FALSE);
1597 /* only try fallback if no specific sink was chosen */
1598 if (chain->sink == NULL) {
1599 GST_DEBUG_OBJECT (playsink, "trying autovideosink");
1600 elem = gst_element_factory_make ("autovideosink", "videosink");
1601 chain->sink = try_element (playsink, elem, TRUE);
1603 if (chain->sink == NULL) {
1604 /* if default sink from config.h is different then try it too */
1605 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1606 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_VIDEOSINK);
1607 elem = gst_element_factory_make (DEFAULT_VIDEOSINK, "videosink");
1608 chain->sink = try_element (playsink, elem, TRUE);
1612 playsink->video_sink = gst_object_ref (chain->sink);
1614 if (chain->sink == NULL)
1618 /* if we can disable async behaviour of the sink, we can avoid adding a
1619 * queue for the audio chain. */
1621 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1624 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1625 async, GST_ELEMENT_NAME (elem));
1626 g_object_set (elem, "async", async, NULL);
1627 chain->async = async;
1629 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1630 chain->async = TRUE;
1633 /* Make sure the aspect ratio is kept */
1635 gst_play_sink_find_property_sinks (playsink, chain->sink,
1636 "force-aspect-ratio", G_TYPE_BOOLEAN);
1638 g_object_set (elem, "force-aspect-ratio", playsink->force_aspect_ratio,
1641 /* find ts-offset element */
1642 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1643 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1646 /* create a bin to hold objects, as we create them we add them to this bin so
1647 * that when something goes wrong we only need to unref the bin */
1648 chain->chain.bin = gst_bin_new ("vbin");
1649 bin = GST_BIN_CAST (chain->chain.bin);
1650 gst_object_ref_sink (bin);
1651 gst_bin_add (bin, chain->sink);
1653 /* Get the VideoOverlay element */
1655 GstVideoOverlay *overlay = NULL;
1657 GST_OBJECT_LOCK (playsink);
1658 if (playsink->overlay_element)
1659 gst_object_unref (playsink->overlay_element);
1660 playsink->overlay_element =
1661 GST_VIDEO_OVERLAY (gst_bin_get_by_interface (GST_BIN (chain->chain.bin),
1662 GST_TYPE_VIDEO_OVERLAY));
1663 if (playsink->overlay_element)
1664 overlay = GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
1665 GST_OBJECT_UNLOCK (playsink);
1668 if (playsink->overlay_handle_set)
1669 gst_video_overlay_set_window_handle (overlay, playsink->overlay_handle);
1670 if (playsink->overlay_handle_events_set)
1671 gst_video_overlay_handle_events (overlay,
1672 playsink->overlay_handle_events);
1673 if (playsink->overlay_render_rectangle_set)
1674 gst_video_overlay_set_render_rectangle (overlay,
1675 playsink->overlay_x, playsink->overlay_y,
1676 playsink->overlay_width, playsink->overlay_height);
1677 gst_object_unref (overlay);
1681 /* decouple decoder from sink, this improves playback quite a lot since the
1682 * decoder can continue while the sink blocks for synchronisation. We don't
1683 * need a lot of buffers as this consumes a lot of memory and we don't want
1684 * too little because else we would be context switching too quickly. */
1685 chain->queue = gst_element_factory_make ("queue", "vqueue");
1686 if (chain->queue == NULL) {
1687 post_missing_element_message (playsink, "queue");
1688 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1689 (_("Missing element '%s' - check your GStreamer installation."),
1690 "queue"), ("video rendering might be suboptimal"));
1694 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1695 "max-size-bytes", 0, "max-size-time", (gint64) 0, "silent", TRUE, NULL);
1696 gst_bin_add (bin, chain->queue);
1697 head = prev = chain->queue;
1700 GST_OBJECT_LOCK (playsink);
1701 if (playsink->colorbalance_element) {
1702 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
1703 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1704 gst_object_unref (playsink->colorbalance_element);
1706 playsink->colorbalance_element = find_color_balance_element (chain->sink);
1707 if (playsink->colorbalance_element) {
1708 g_signal_connect (playsink->colorbalance_element, "value-changed",
1709 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1711 GST_OBJECT_UNLOCK (playsink);
1713 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)
1714 || (!playsink->colorbalance_element
1715 && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE))) {
1716 gboolean use_converters = !(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO);
1717 gboolean use_balance = !playsink->colorbalance_element
1718 && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE);
1720 GST_DEBUG_OBJECT (playsink, "creating videoconverter");
1722 g_object_new (GST_TYPE_PLAY_SINK_VIDEO_CONVERT, "name", "vconv",
1723 "use-converters", use_converters, "use-balance", use_balance, NULL);
1725 GST_OBJECT_LOCK (playsink);
1726 if (use_balance && GST_PLAY_SINK_VIDEO_CONVERT (chain->conv)->balance)
1727 playsink->colorbalance_element =
1728 GST_COLOR_BALANCE (gst_object_ref (GST_PLAY_SINK_VIDEO_CONVERT
1729 (chain->conv)->balance));
1730 GST_OBJECT_UNLOCK (playsink);
1732 gst_bin_add (bin, chain->conv);
1734 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
1735 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1743 update_colorbalance (playsink);
1746 GST_DEBUG_OBJECT (playsink, "linking to sink");
1747 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
1748 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1752 pad = gst_element_get_static_pad (head, "sink");
1753 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1755 /* sending audio/video flushes break stream changes when the pipeline
1756 * is paused and played again in 0.10 */
1758 gst_pad_set_event_function (chain->sinkpad,
1759 GST_DEBUG_FUNCPTR (gst_play_sink_video_sink_event));
1760 gst_pad_set_chain_function (chain->sinkpad,
1761 GST_DEBUG_FUNCPTR (gst_play_sink_video_sink_chain));
1764 gst_object_unref (pad);
1765 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1772 if (!elem && !playsink->video_sink) {
1773 post_missing_element_message (playsink, "autovideosink");
1774 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1775 post_missing_element_message (playsink, DEFAULT_VIDEOSINK);
1776 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1777 (_("Both autovideosink and %s elements are missing."),
1778 DEFAULT_VIDEOSINK), (NULL));
1780 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1781 (_("The autovideosink element is missing.")), (NULL));
1784 if (playsink->video_sink) {
1785 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1786 (_("Configured videosink %s is not working."),
1787 GST_ELEMENT_NAME (playsink->video_sink)), (NULL));
1788 } else if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1789 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1790 (_("Both autovideosink and %s elements are not working."),
1791 DEFAULT_VIDEOSINK), (NULL));
1793 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1794 (_("The autovideosink element is not working.")), (NULL));
1797 free_chain ((GstPlayChain *) chain);
1803 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1804 (NULL), ("Failed to configure the video sink."));
1805 /* checking sink made it READY */
1806 gst_element_set_state (chain->sink, GST_STATE_NULL);
1807 /* Remove chain from the bin to allow reuse later */
1808 gst_bin_remove (bin, chain->sink);
1809 free_chain ((GstPlayChain *) chain);
1815 setup_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1818 GstPlayVideoChain *chain;
1819 GstStateChangeReturn ret;
1821 chain = playsink->videochain;
1823 chain->chain.raw = raw;
1825 /* if the chain was active we don't do anything */
1826 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
1829 /* try to set the sink element to READY again */
1830 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
1831 if (ret == GST_STATE_CHANGE_FAILURE)
1834 /* Get the VideoOverlay element */
1836 GstVideoOverlay *overlay = NULL;
1838 GST_OBJECT_LOCK (playsink);
1839 if (playsink->overlay_element)
1840 gst_object_unref (playsink->overlay_element);
1841 playsink->overlay_element =
1842 GST_VIDEO_OVERLAY (gst_bin_get_by_interface (GST_BIN (chain->chain.bin),
1843 GST_TYPE_VIDEO_OVERLAY));
1844 if (playsink->overlay_element)
1845 overlay = GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
1846 GST_OBJECT_UNLOCK (playsink);
1849 if (playsink->overlay_handle_set)
1850 gst_video_overlay_set_window_handle (overlay, playsink->overlay_handle);
1851 if (playsink->overlay_handle_events_set)
1852 gst_video_overlay_handle_events (overlay,
1853 playsink->overlay_handle_events);
1854 if (playsink->overlay_render_rectangle_set)
1855 gst_video_overlay_set_render_rectangle (overlay,
1856 playsink->overlay_x, playsink->overlay_y,
1857 playsink->overlay_width, playsink->overlay_height);
1858 gst_object_unref (overlay);
1862 /* find ts-offset element */
1863 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1864 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1867 /* if we can disable async behaviour of the sink, we can avoid adding a
1868 * queue for the audio chain. */
1870 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1873 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1874 async, GST_ELEMENT_NAME (elem));
1875 g_object_set (elem, "async", async, NULL);
1876 chain->async = async;
1878 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1879 chain->async = TRUE;
1882 /* Make sure the aspect ratio is kept */
1884 gst_play_sink_find_property_sinks (playsink, chain->sink,
1885 "force-aspect-ratio", G_TYPE_BOOLEAN);
1887 g_object_set (elem, "force-aspect-ratio", playsink->force_aspect_ratio,
1890 GST_OBJECT_LOCK (playsink);
1891 if (playsink->colorbalance_element) {
1892 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
1893 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1894 gst_object_unref (playsink->colorbalance_element);
1896 playsink->colorbalance_element = find_color_balance_element (chain->sink);
1897 if (playsink->colorbalance_element) {
1898 g_signal_connect (playsink->colorbalance_element, "value-changed",
1899 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1901 GST_OBJECT_UNLOCK (playsink);
1904 gboolean use_balance = !playsink->colorbalance_element
1905 && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE);
1907 g_object_set (chain->conv, "use-balance", use_balance, NULL);
1909 GST_OBJECT_LOCK (playsink);
1910 if (use_balance && GST_PLAY_SINK_VIDEO_CONVERT (chain->conv)->balance)
1911 playsink->colorbalance_element =
1912 GST_COLOR_BALANCE (gst_object_ref (GST_PLAY_SINK_VIDEO_CONVERT
1913 (chain->conv)->balance));
1914 GST_OBJECT_UNLOCK (playsink);
1917 update_colorbalance (playsink);
1923 _generate_update_newsegment_event (GstPad * pad, GstSegment * segment,
1927 GstStructure *structure;
1928 event = gst_event_new_segment (segment);
1929 structure = gst_event_writable_structure (event);
1930 gst_structure_id_set (structure,
1931 _playsink_reset_segment_event_marker_id, G_TYPE_BOOLEAN, TRUE, NULL);
1936 gst_play_sink_sink_event (GstPad * pad, GstObject * parent, GstEvent * event,
1937 const gchar * sink_type,
1938 gboolean * sink_ignore_wrong_state,
1939 gboolean * sink_custom_flush_finished,
1940 gboolean * sink_pending_flush, GstSegment * sink_segment)
1942 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_object_get_parent (parent));
1944 const GstStructure *structure = gst_event_get_structure (event);
1946 if (GST_EVENT_TYPE (event) == GST_EVENT_CUSTOM_DOWNSTREAM_OOB && structure) {
1947 gchar *custom_flush;
1948 gchar *custom_flush_finish;
1950 custom_flush = g_strdup_printf ("playsink-custom-%s-flush", sink_type);
1951 custom_flush_finish =
1952 g_strdup_printf ("playsink-custom-%s-flush-finish", sink_type);
1953 if (strcmp (gst_structure_get_name (structure), custom_flush) == 0) {
1954 GST_DEBUG_OBJECT (pad,
1955 "Custom %s flush event received, marking to flush %s", sink_type,
1957 GST_PLAY_SINK_LOCK (playsink);
1958 *sink_ignore_wrong_state = TRUE;
1959 *sink_custom_flush_finished = FALSE;
1960 GST_PLAY_SINK_UNLOCK (playsink);
1961 } else if (strcmp (gst_structure_get_name (structure),
1962 custom_flush_finish) == 0) {
1963 GST_DEBUG_OBJECT (pad, "Custom %s flush finish event received",
1965 GST_PLAY_SINK_LOCK (playsink);
1966 *sink_pending_flush = TRUE;
1967 *sink_custom_flush_finished = TRUE;
1968 GST_PLAY_SINK_UNLOCK (playsink);
1971 g_free (custom_flush);
1972 g_free (custom_flush_finish);
1973 } else if (GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_STOP) {
1974 GST_PLAY_SINK_LOCK (playsink);
1975 GST_DEBUG_OBJECT (pad, "Resetting %s segment because of flush-stop event",
1977 gst_segment_init (sink_segment, GST_FORMAT_UNDEFINED);
1978 GST_PLAY_SINK_UNLOCK (playsink);
1981 GST_DEBUG_OBJECT (pad, "Forwarding event %" GST_PTR_FORMAT, event);
1982 ret = gst_pad_event_default (pad, parent, gst_event_ref (event));
1984 if (GST_EVENT_TYPE (event) == GST_EVENT_SEGMENT) {
1985 const GstSegment *segment;
1987 gst_event_parse_segment (event, &segment);
1988 GST_DEBUG_OBJECT (pad, "Segment event: %" GST_SEGMENT_FORMAT, segment);
1990 GST_PLAY_SINK_LOCK (playsink);
1991 if (sink_segment->format != segment->format) {
1992 GST_DEBUG_OBJECT (pad, "%s segment format changed: %s -> %s",
1994 gst_format_get_name (sink_segment->format),
1995 gst_format_get_name (segment->format));
1996 gst_segment_init (sink_segment, segment->format);
1999 GST_DEBUG_OBJECT (pad, "Old %s segment: %" GST_SEGMENT_FORMAT,
2000 sink_type, sink_segment);
2001 gst_segment_copy_into (&playsink->text_segment, sink_segment);
2002 GST_DEBUG_OBJECT (pad, "New %s segment: %" GST_SEGMENT_FORMAT,
2003 sink_type, sink_segment);
2004 GST_PLAY_SINK_UNLOCK (playsink);
2007 gst_event_unref (event);
2008 gst_object_unref (playsink);
2012 static GstFlowReturn
2013 gst_play_sink_sink_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer,
2014 const gchar * sink_type,
2015 gboolean * sink_ignore_wrong_state,
2016 gboolean * sink_custom_flush_finished,
2017 gboolean * sink_pending_flush, GstSegment * sink_segment)
2019 GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2020 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2023 GST_PLAY_SINK_LOCK (playsink);
2025 if (*sink_pending_flush) {
2027 GstStructure *structure;
2029 *sink_pending_flush = FALSE;
2031 GST_PLAY_SINK_UNLOCK (playsink);
2033 /* make the bin drop all cached data.
2034 * This event will be dropped on the src pad, if any. */
2035 event = gst_event_new_flush_start ();
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-start event with reset segment marker set: %"
2042 GST_PTR_FORMAT, sink_type, event);
2043 gst_pad_send_event (pad, event);
2045 /* make queue drop all cached data.
2046 * This event will be dropped on the src pad. */
2047 event = gst_event_new_flush_stop (TRUE);
2048 structure = gst_event_writable_structure (event);
2049 gst_structure_id_set (structure,
2050 _playsink_reset_segment_event_marker_id, G_TYPE_BOOLEAN, TRUE, NULL);
2052 GST_DEBUG_OBJECT (pad,
2053 "Pushing %s flush-stop event with reset segment marker set: %"
2054 GST_PTR_FORMAT, sink_type, event);
2055 gst_pad_send_event (pad, event);
2057 /* Re-sync queue segment info after flush-stop.
2058 * This event will be dropped on the src pad. */
2059 if (sink_segment->format != GST_FORMAT_UNDEFINED) {
2062 _generate_update_newsegment_event (pad, sink_segment, &event1);
2063 GST_DEBUG_OBJECT (playsink,
2064 "Pushing segment event with reset "
2065 "segment marker set: %" GST_PTR_FORMAT, event1);
2066 gst_pad_send_event (pad, event1);
2069 GST_PLAY_SINK_UNLOCK (playsink);
2072 ret = gst_proxy_pad_chain_default (pad, parent, buffer);
2074 GST_PLAY_SINK_LOCK (playsink);
2075 if (ret == GST_FLOW_FLUSHING && *sink_ignore_wrong_state) {
2076 GST_DEBUG_OBJECT (pad, "Ignoring wrong state for %s during flush",
2078 if (*sink_custom_flush_finished) {
2079 GST_DEBUG_OBJECT (pad, "Custom flush finished, stop ignoring "
2080 "wrong state for %s", sink_type);
2081 *sink_ignore_wrong_state = FALSE;
2086 GST_PLAY_SINK_UNLOCK (playsink);
2088 gst_object_unref (playsink);
2089 gst_object_unref (tbin);
2093 /* sending audio/video flushes break stream changes when the pipeline
2094 * is paused and played again in 0.10 */
2097 gst_play_sink_video_sink_event (GstPad * pad, GstEvent * event)
2099 GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2100 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2103 ret = gst_play_sink_sink_event (pad, event, "video",
2104 &playsink->video_ignore_wrong_state,
2105 &playsink->video_custom_flush_finished,
2106 &playsink->video_pending_flush, &playsink->video_segment);
2108 gst_object_unref (playsink);
2109 gst_object_unref (tbin);
2113 static GstFlowReturn
2114 gst_play_sink_video_sink_chain (GstPad * pad, GstBuffer * buffer)
2116 GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2117 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2120 ret = gst_play_sink_sink_chain (pad, buffer, "video",
2121 &playsink->video_ignore_wrong_state,
2122 &playsink->video_custom_flush_finished,
2123 &playsink->video_pending_flush, &playsink->video_segment);
2125 gst_object_unref (playsink);
2126 gst_object_unref (tbin);
2131 gst_play_sink_audio_sink_event (GstPad * pad, GstEvent * event)
2133 GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2134 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2137 ret = gst_play_sink_sink_event (pad, event, "audio",
2138 &playsink->audio_ignore_wrong_state,
2139 &playsink->audio_custom_flush_finished,
2140 &playsink->audio_pending_flush, &playsink->audio_segment);
2142 gst_object_unref (playsink);
2143 gst_object_unref (tbin);
2147 static GstFlowReturn
2148 gst_play_sink_audio_sink_chain (GstPad * pad, GstBuffer * buffer)
2150 GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2151 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2154 ret = gst_play_sink_sink_chain (pad, buffer, "audio",
2155 &playsink->audio_ignore_wrong_state,
2156 &playsink->audio_custom_flush_finished,
2157 &playsink->audio_pending_flush, &playsink->audio_segment);
2159 gst_object_unref (playsink);
2160 gst_object_unref (tbin);
2166 gst_play_sink_text_sink_event (GstPad * pad, GstObject * parent,
2169 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_object_get_parent (parent));
2172 ret = gst_play_sink_sink_event (pad, parent, event, "subtitle",
2173 &playsink->text_ignore_wrong_state,
2174 &playsink->text_custom_flush_finished,
2175 &playsink->text_pending_flush, &playsink->text_segment);
2177 gst_object_unref (playsink);
2182 static GstFlowReturn
2183 gst_play_sink_text_sink_chain (GstPad * pad, GstObject * parent,
2187 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_object_get_parent (parent));
2189 ret = gst_play_sink_sink_chain (pad, parent, buffer, "subtitle",
2190 &playsink->text_ignore_wrong_state,
2191 &playsink->text_custom_flush_finished,
2192 &playsink->text_pending_flush, &playsink->text_segment);
2194 gst_object_unref (playsink);
2199 gst_play_sink_text_src_event (GstPad * pad, GstObject * parent,
2203 const GstStructure *structure;
2205 GST_DEBUG_OBJECT (pad, "Got event %" GST_PTR_FORMAT, event);
2207 structure = gst_event_get_structure (event);
2210 gst_structure_id_has_field (structure,
2211 _playsink_reset_segment_event_marker_id)) {
2212 /* the events marked with a reset segment marker
2213 * are sent internally to reset the queue and
2214 * must be dropped here */
2215 GST_DEBUG_OBJECT (pad, "Dropping event with reset "
2216 "segment marker set: %" GST_PTR_FORMAT, event);
2221 ret = gst_pad_event_default (pad, parent, gst_event_ref (event));
2224 gst_event_unref (event);
2228 /* make an element for playback of video with subtitles embedded.
2229 * Only used for *raw* video streams.
2231 * +--------------------------------------------+
2233 * | +--------+ +-----------------+ |
2234 * | | queue | | subtitleoverlay | |
2235 * video--src sink---video_sink | |
2236 * | +--------+ | src--src
2237 * text------------------text_sink | |
2238 * | +-----------------+ |
2239 * +--------------------------------------------+
2242 static GstPlayTextChain *
2243 gen_text_chain (GstPlaySink * playsink)
2245 GstPlayTextChain *chain;
2248 GstPad *videosinkpad, *textsinkpad, *srcpad;
2250 chain = g_new0 (GstPlayTextChain, 1);
2251 chain->chain.playsink = playsink;
2253 GST_DEBUG_OBJECT (playsink, "making text chain %p", chain);
2255 chain->chain.bin = gst_bin_new ("tbin");
2256 bin = GST_BIN_CAST (chain->chain.bin);
2257 gst_object_ref_sink (bin);
2259 videosinkpad = textsinkpad = srcpad = NULL;
2261 /* first try to hook the text pad to the custom sink */
2262 if (playsink->text_sink) {
2263 GST_DEBUG_OBJECT (playsink, "trying configured textsink");
2264 chain->sink = try_element (playsink, playsink->text_sink, FALSE);
2267 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
2270 /* make sure the sparse subtitles don't participate in the preroll */
2271 g_object_set (elem, "async", FALSE, NULL);
2272 GST_DEBUG_OBJECT (playsink, "adding custom text sink");
2273 gst_bin_add (bin, chain->sink);
2274 /* NOTE streamsynchronizer needs streams decoupled */
2275 /* make a little queue */
2276 chain->queue = gst_element_factory_make ("queue", "subqueue");
2277 if (chain->queue == NULL) {
2278 post_missing_element_message (playsink, "queue");
2279 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2280 (_("Missing element '%s' - check your GStreamer installation."),
2281 "queue"), ("rendering might be suboptimal"));
2283 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
2284 "max-size-bytes", 0, "max-size-time", (gint64) GST_SECOND,
2285 "silent", TRUE, NULL);
2286 gst_bin_add (bin, chain->queue);
2288 /* we have a custom sink, this will be our textsinkpad */
2289 if (gst_element_link_pads_full (chain->queue, "src", chain->sink,
2290 "sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
2291 /* we're all fine now and we can add the sink to the chain */
2292 GST_DEBUG_OBJECT (playsink, "using custom text sink");
2293 textsinkpad = gst_element_get_static_pad (chain->queue, "sink");
2295 GST_WARNING_OBJECT (playsink,
2296 "can't find a sink pad on custom text sink");
2297 gst_bin_remove (bin, chain->sink);
2298 gst_bin_remove (bin, chain->queue);
2300 chain->queue = NULL;
2302 /* try to set sync to true but it's no biggie when we can't */
2303 if (chain->sink && (elem =
2304 gst_play_sink_find_property_sinks (playsink, chain->sink,
2305 "sync", G_TYPE_BOOLEAN)))
2306 g_object_set (elem, "sync", TRUE, NULL);
2309 gst_bin_remove (bin, chain->sink);
2311 GST_WARNING_OBJECT (playsink,
2312 "can't find async property in custom text sink");
2315 if (textsinkpad == NULL) {
2316 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2317 (_("Custom text sink element is not usable.")),
2318 ("fallback to default subtitleoverlay"));
2322 if (textsinkpad == NULL) {
2323 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
2324 /* make a little queue */
2325 chain->queue = gst_element_factory_make ("queue", "vqueue");
2326 if (chain->queue == NULL) {
2327 post_missing_element_message (playsink, "queue");
2328 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2329 (_("Missing element '%s' - check your GStreamer installation."),
2330 "queue"), ("video rendering might be suboptimal"));
2332 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
2333 "max-size-bytes", 0, "max-size-time", (gint64) 0,
2334 "silent", TRUE, NULL);
2335 gst_bin_add (bin, chain->queue);
2336 videosinkpad = gst_element_get_static_pad (chain->queue, "sink");
2340 gst_element_factory_make ("subtitleoverlay", "suboverlay");
2341 if (chain->overlay == NULL) {
2342 post_missing_element_message (playsink, "subtitleoverlay");
2343 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2344 (_("Missing element '%s' - check your GStreamer installation."),
2345 "subtitleoverlay"), ("subtitle rendering disabled"));
2347 GstElement *element;
2349 gst_bin_add (bin, chain->overlay);
2351 g_object_set (G_OBJECT (chain->overlay), "silent", FALSE, NULL);
2352 if (playsink->font_desc) {
2353 g_object_set (G_OBJECT (chain->overlay), "font-desc",
2354 playsink->font_desc, NULL);
2356 if (playsink->subtitle_encoding) {
2357 g_object_set (G_OBJECT (chain->overlay), "subtitle-encoding",
2358 playsink->subtitle_encoding, NULL);
2361 gst_element_link_pads_full (chain->queue, "src", chain->overlay,
2362 "video_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS);
2364 /* make another little queue to decouple streams */
2365 element = gst_element_factory_make ("queue", "subqueue");
2366 if (element == NULL) {
2367 post_missing_element_message (playsink, "queue");
2368 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2369 (_("Missing element '%s' - check your GStreamer installation."),
2370 "queue"), ("rendering might be suboptimal"));
2372 g_object_set (G_OBJECT (element), "max-size-buffers", 3,
2373 "max-size-bytes", 0, "max-size-time", (gint64) GST_SECOND,
2374 "silent", TRUE, NULL);
2375 gst_bin_add (bin, element);
2376 if (gst_element_link_pads_full (element, "src", chain->overlay,
2377 "subtitle_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
2378 textsinkpad = gst_element_get_static_pad (element, "sink");
2379 srcpad = gst_element_get_static_pad (chain->overlay, "src");
2381 gst_bin_remove (bin, chain->sink);
2382 gst_bin_remove (bin, chain->overlay);
2384 chain->overlay = NULL;
2385 gst_object_unref (videosinkpad);
2386 videosinkpad = NULL;
2393 if (videosinkpad == NULL) {
2394 /* if we still don't have a videosink, we don't have an overlay. the only
2395 * thing we can do is insert an identity and ghost the src
2397 chain->identity = gst_element_factory_make ("identity", "tidentity");
2398 if (chain->identity == NULL) {
2399 post_missing_element_message (playsink, "identity");
2400 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2401 (_("Missing element '%s' - check your GStreamer installation."),
2402 "identity"), (NULL));
2404 g_object_set (chain->identity, "signal-handoffs", FALSE, NULL);
2405 g_object_set (chain->identity, "silent", TRUE, NULL);
2406 gst_bin_add (bin, chain->identity);
2407 srcpad = gst_element_get_static_pad (chain->identity, "src");
2408 videosinkpad = gst_element_get_static_pad (chain->identity, "sink");
2412 /* expose the ghostpads */
2414 chain->videosinkpad = gst_ghost_pad_new ("sink", videosinkpad);
2415 gst_object_unref (videosinkpad);
2416 gst_element_add_pad (chain->chain.bin, chain->videosinkpad);
2419 chain->textsinkpad = gst_ghost_pad_new ("text_sink", textsinkpad);
2420 gst_object_unref (textsinkpad);
2422 gst_pad_set_event_function (chain->textsinkpad,
2423 GST_DEBUG_FUNCPTR (gst_play_sink_text_sink_event));
2424 gst_pad_set_chain_function (chain->textsinkpad,
2425 GST_DEBUG_FUNCPTR (gst_play_sink_text_sink_chain));
2427 gst_element_add_pad (chain->chain.bin, chain->textsinkpad);
2430 chain->srcpad = gst_ghost_pad_new ("src", srcpad);
2431 gst_object_unref (srcpad);
2433 gst_pad_set_event_function (chain->srcpad,
2434 GST_DEBUG_FUNCPTR (gst_play_sink_text_src_event));
2436 gst_element_add_pad (chain->chain.bin, chain->srcpad);
2443 notify_volume_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
2447 g_object_get (object, "volume", &vol, NULL);
2448 playsink->volume = vol;
2450 g_object_notify (G_OBJECT (playsink), "volume");
2454 notify_mute_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
2458 g_object_get (object, "mute", &mute, NULL);
2459 playsink->mute = mute;
2461 g_object_notify (G_OBJECT (playsink), "mute");
2464 /* make the chain that contains the elements needed to perform
2467 * We add a tee as the first element so that we can link the visualisation chain
2468 * to it when requested.
2470 * +-------------------------------------------------------------+
2472 * | +---------+ +----------+ +---------+ +---------+ |
2473 * | |audioconv| |audioscale| | volume | |audiosink| |
2474 * | +-srck src-sink src-sink src-sink | |
2475 * | | +---------+ +----------+ +---------+ +---------+ |
2477 * +-------------------------------------------------------------+
2479 static GstPlayAudioChain *
2480 gen_audio_chain (GstPlaySink * playsink, gboolean raw)
2482 GstPlayAudioChain *chain;
2484 gboolean have_volume;
2486 GstElement *head, *prev, *elem = NULL;
2488 chain = g_new0 (GstPlayAudioChain, 1);
2489 chain->chain.playsink = playsink;
2490 chain->chain.raw = raw;
2492 GST_DEBUG_OBJECT (playsink, "making audio chain %p", chain);
2494 if (playsink->audio_sink) {
2495 GST_DEBUG_OBJECT (playsink, "trying configured audiosink %" GST_PTR_FORMAT,
2496 playsink->audio_sink);
2497 chain->sink = try_element (playsink, playsink->audio_sink, FALSE);
2499 /* only try fallback if no specific sink was chosen */
2500 if (chain->sink == NULL) {
2501 GST_DEBUG_OBJECT (playsink, "trying autoaudiosink");
2502 elem = gst_element_factory_make ("autoaudiosink", "audiosink");
2503 chain->sink = try_element (playsink, elem, TRUE);
2505 if (chain->sink == NULL) {
2506 /* if default sink from config.h is different then try it too */
2507 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
2508 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_AUDIOSINK);
2509 elem = gst_element_factory_make (DEFAULT_AUDIOSINK, "audiosink");
2510 chain->sink = try_element (playsink, elem, TRUE);
2514 playsink->audio_sink = gst_object_ref (chain->sink);
2516 if (chain->sink == NULL)
2519 chain->chain.bin = gst_bin_new ("abin");
2520 bin = GST_BIN_CAST (chain->chain.bin);
2521 gst_object_ref_sink (bin);
2522 gst_bin_add (bin, chain->sink);
2524 /* we have to add a queue when we need to decouple for the video sink in
2525 * visualisations and for streamsynchronizer */
2526 GST_DEBUG_OBJECT (playsink, "adding audio queue");
2527 chain->queue = gst_element_factory_make ("queue", "aqueue");
2528 if (chain->queue == NULL) {
2529 post_missing_element_message (playsink, "queue");
2530 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2531 (_("Missing element '%s' - check your GStreamer installation."),
2532 "queue"), ("audio playback and visualizations might not work"));
2536 g_object_set (chain->queue, "silent", TRUE, NULL);
2537 gst_bin_add (bin, chain->queue);
2538 prev = head = chain->queue;
2541 /* find ts-offset element */
2542 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
2543 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
2546 /* check if the sink, or something within the sink, has the volume property.
2547 * If it does we don't need to add a volume element. */
2549 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
2552 chain->volume = elem;
2554 g_signal_connect (chain->volume, "notify::volume",
2555 G_CALLBACK (notify_volume_cb), playsink);
2557 GST_DEBUG_OBJECT (playsink, "the sink has a volume property");
2559 chain->sink_volume = TRUE;
2560 /* if the sink also has a mute property we can use this as well. We'll only
2561 * use the mute property if there is a volume property. We can simulate the
2562 * mute with the volume otherwise. */
2564 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
2567 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
2568 g_signal_connect (chain->mute, "notify::mute",
2569 G_CALLBACK (notify_mute_cb), playsink);
2571 /* use the sink to control the volume and mute */
2572 if (playsink->volume_changed) {
2573 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2574 playsink->volume_changed = FALSE;
2576 if (playsink->mute_changed) {
2578 g_object_set (chain->mute, "mute", playsink->mute, NULL);
2581 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
2583 playsink->mute_changed = FALSE;
2586 /* no volume, we need to add a volume element when we can */
2587 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
2588 have_volume = FALSE;
2589 chain->sink_volume = FALSE;
2592 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO) || (!have_volume
2593 && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME))) {
2594 gboolean use_converters = !(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO);
2595 gboolean use_volume =
2596 !have_volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME);
2597 GST_DEBUG_OBJECT (playsink,
2598 "creating audioconvert with use-converters %d, use-volume %d",
2599 use_converters, use_volume);
2601 g_object_new (GST_TYPE_PLAY_SINK_AUDIO_CONVERT, "name", "aconv",
2602 "use-converters", use_converters, "use-volume", use_volume, NULL);
2603 gst_bin_add (bin, chain->conv);
2605 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
2606 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
2613 if (!have_volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
2614 GstPlaySinkAudioConvert *conv =
2615 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
2618 chain->volume = conv->volume;
2621 g_signal_connect (chain->volume, "notify::volume",
2622 G_CALLBACK (notify_volume_cb), playsink);
2624 /* volume also has the mute property */
2625 chain->mute = chain->volume;
2626 g_signal_connect (chain->mute, "notify::mute",
2627 G_CALLBACK (notify_mute_cb), playsink);
2629 /* configure with the latest volume and mute */
2630 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume,
2632 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
2638 /* we only have to link to the previous element if we have something in
2639 * front of the sink */
2640 GST_DEBUG_OBJECT (playsink, "linking to sink");
2641 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
2642 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
2646 /* post a warning if we have no way to configure the volume */
2648 GST_ELEMENT_WARNING (playsink, STREAM, NOT_IMPLEMENTED,
2649 (_("No volume control found")), ("Volume/mute is not available"));
2652 /* and ghost the sinkpad of the headmost element */
2653 GST_DEBUG_OBJECT (playsink, "ghosting sink pad");
2654 pad = gst_element_get_static_pad (head, "sink");
2655 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
2657 /* sending audio/video flushes break stream changes when the pipeline
2658 * is paused and played again in 0.10 */
2660 gst_pad_set_event_function (chain->sinkpad,
2661 GST_DEBUG_FUNCPTR (gst_play_sink_audio_sink_event));
2662 gst_pad_set_chain_function (chain->sinkpad,
2663 GST_DEBUG_FUNCPTR (gst_play_sink_audio_sink_chain));
2666 gst_object_unref (pad);
2667 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
2674 if (!elem && !playsink->audio_sink) {
2675 post_missing_element_message (playsink, "autoaudiosink");
2676 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
2677 post_missing_element_message (playsink, DEFAULT_AUDIOSINK);
2678 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2679 (_("Both autoaudiosink and %s elements are missing."),
2680 DEFAULT_AUDIOSINK), (NULL));
2682 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2683 (_("The autoaudiosink element is missing.")), (NULL));
2686 if (playsink->audio_sink) {
2687 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2688 (_("Configured audiosink %s is not working."),
2689 GST_ELEMENT_NAME (playsink->audio_sink)), (NULL));
2690 } else if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
2691 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2692 (_("Both autoaudiosink and %s elements are not working."),
2693 DEFAULT_AUDIOSINK), (NULL));
2695 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2696 (_("The autoaudiosink element is not working.")), (NULL));
2699 free_chain ((GstPlayChain *) chain);
2704 GST_ELEMENT_ERROR (playsink, CORE, PAD,
2705 (NULL), ("Failed to configure the audio sink."));
2706 /* checking sink made it READY */
2707 gst_element_set_state (chain->sink, GST_STATE_NULL);
2708 /* Remove chain from the bin to allow reuse later */
2709 gst_bin_remove (bin, chain->sink);
2710 free_chain ((GstPlayChain *) chain);
2716 setup_audio_chain (GstPlaySink * playsink, gboolean raw)
2719 GstPlayAudioChain *chain;
2720 GstStateChangeReturn ret;
2721 GstPlaySinkAudioConvert *conv;
2723 chain = playsink->audiochain;
2724 conv = GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
2726 chain->chain.raw = raw;
2728 /* if the chain was active we don't do anything */
2729 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
2732 /* try to set the sink element to READY again */
2733 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
2734 if (ret == GST_STATE_CHANGE_FAILURE)
2737 /* find ts-offset element */
2738 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
2739 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
2742 /* check if the sink, or something within the sink, has the volume property.
2743 * If it does we don't need to add a volume element. */
2745 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
2748 chain->volume = elem;
2750 if (playsink->volume_changed) {
2751 GST_DEBUG_OBJECT (playsink, "the sink has a volume property, setting %f",
2753 /* use the sink to control the volume */
2754 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2755 playsink->volume_changed = FALSE;
2758 g_signal_connect (chain->volume, "notify::volume",
2759 G_CALLBACK (notify_volume_cb), playsink);
2760 /* if the sink also has a mute property we can use this as well. We'll only
2761 * use the mute property if there is a volume property. We can simulate the
2762 * mute with the volume otherwise. */
2764 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
2767 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
2768 g_signal_connect (chain->mute, "notify::mute",
2769 G_CALLBACK (notify_mute_cb), playsink);
2772 g_object_set (chain->conv, "use-volume", FALSE, NULL);
2774 /* no volume, we need to add a volume element when we can */
2775 g_object_set (chain->conv, "use-volume",
2776 ! !(playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME), NULL);
2777 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
2779 /* Disconnect signals */
2780 disconnect_chain (chain, playsink);
2782 if (conv->volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
2783 chain->volume = conv->volume;
2784 chain->mute = chain->volume;
2786 g_signal_connect (chain->volume, "notify::volume",
2787 G_CALLBACK (notify_volume_cb), playsink);
2789 g_signal_connect (chain->mute, "notify::mute",
2790 G_CALLBACK (notify_mute_cb), playsink);
2792 /* configure with the latest volume and mute */
2793 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2794 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
2797 GST_DEBUG_OBJECT (playsink, "reusing existing volume element");
2803 * +-------------------------------------------------------------------+
2805 * | +----------+ +------------+ +----------+ +-------+ |
2806 * | | visqueue | | audioconv | | audiores | | vis | |
2807 * | +-sink src-sink + samp src-sink src-sink src-+ |
2808 * | | +----------+ +------------+ +----------+ +-------+ | |
2810 * +-------------------------------------------------------------------+
2813 static GstPlayVisChain *
2814 gen_vis_chain (GstPlaySink * playsink)
2816 GstPlayVisChain *chain;
2822 chain = g_new0 (GstPlayVisChain, 1);
2823 chain->chain.playsink = playsink;
2825 GST_DEBUG_OBJECT (playsink, "making vis chain %p", chain);
2827 chain->chain.bin = gst_bin_new ("visbin");
2828 bin = GST_BIN_CAST (chain->chain.bin);
2829 gst_object_ref_sink (bin);
2831 /* we're queuing raw audio here, we can remove this queue when we can disable
2832 * async behaviour in the video sink. */
2833 chain->queue = gst_element_factory_make ("queue", "visqueue");
2834 if (chain->queue == NULL)
2836 g_object_set (chain->queue, "silent", TRUE, NULL);
2837 gst_bin_add (bin, chain->queue);
2839 chain->conv = gst_element_factory_make ("audioconvert", "aconv");
2840 if (chain->conv == NULL)
2841 goto no_audioconvert;
2842 gst_bin_add (bin, chain->conv);
2844 chain->resample = gst_element_factory_make ("audioresample", "aresample");
2845 if (chain->resample == NULL)
2846 goto no_audioresample;
2847 gst_bin_add (bin, chain->resample);
2849 /* this pad will be used for blocking the dataflow and switching the vis
2850 * plugin, we block right after the queue, this makes it possible for the
2851 * resample and convert to convert to a format supported by the new vis
2853 chain->blockpad = gst_element_get_static_pad (chain->queue, "src");
2854 /* this is the pad where the vis is linked to */
2855 chain->vispeerpad = gst_element_get_static_pad (chain->resample, "src");
2857 if (playsink->visualisation) {
2858 GST_DEBUG_OBJECT (playsink, "trying configure vis");
2859 chain->vis = try_element (playsink, playsink->visualisation, FALSE);
2861 if (chain->vis == NULL) {
2862 GST_DEBUG_OBJECT (playsink, "trying goom");
2863 elem = gst_element_factory_make ("goom", "vis");
2864 chain->vis = try_element (playsink, elem, TRUE);
2866 if (chain->vis == NULL)
2869 gst_bin_add (bin, chain->vis);
2871 res = gst_element_link_pads_full (chain->queue, "src", chain->conv, "sink",
2872 GST_PAD_LINK_CHECK_NOTHING);
2874 gst_element_link_pads_full (chain->conv, "src", chain->resample, "sink",
2875 GST_PAD_LINK_CHECK_NOTHING);
2877 gst_element_link_pads_full (chain->resample, "src", chain->vis, "sink",
2878 GST_PAD_LINK_CHECK_NOTHING);
2882 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
2883 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
2885 pad = gst_element_get_static_pad (chain->queue, "sink");
2886 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
2887 gst_object_unref (pad);
2888 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
2890 chain->srcpad = gst_ghost_pad_new ("src", chain->vissrcpad);
2891 gst_element_add_pad (chain->chain.bin, chain->srcpad);
2898 post_missing_element_message (playsink, "queue");
2899 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2900 (_("Missing element '%s' - check your GStreamer installation."),
2902 free_chain ((GstPlayChain *) chain);
2907 post_missing_element_message (playsink, "audioconvert");
2908 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2909 (_("Missing element '%s' - check your GStreamer installation."),
2910 "audioconvert"), ("make sure audioconvert isn't blacklisted"));
2911 free_chain ((GstPlayChain *) chain);
2916 post_missing_element_message (playsink, "audioresample");
2917 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2918 (_("Missing element '%s' - check your GStreamer installation."),
2919 "audioresample"), (NULL));
2920 free_chain ((GstPlayChain *) chain);
2925 post_missing_element_message (playsink, "goom");
2926 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2927 (_("Missing element '%s' - check your GStreamer installation."),
2929 free_chain ((GstPlayChain *) chain);
2934 GST_ELEMENT_ERROR (playsink, CORE, PAD,
2935 (NULL), ("Failed to configure the visualisation element."));
2936 /* element made it to READY */
2937 gst_element_set_state (chain->vis, GST_STATE_NULL);
2938 free_chain ((GstPlayChain *) chain);
2943 /* this function is called when all the request pads are requested and when we
2944 * have to construct the final pipeline. Based on the flags we construct the
2945 * final output pipelines.
2948 gst_play_sink_do_reconfigure (GstPlaySink * playsink)
2951 gboolean need_audio, need_video, need_deinterlace, need_vis, need_text;
2953 GST_DEBUG_OBJECT (playsink, "reconfiguring");
2955 /* assume we need nothing */
2956 need_audio = need_video = need_deinterlace = need_vis = need_text = FALSE;
2958 GST_PLAY_SINK_LOCK (playsink);
2959 GST_OBJECT_LOCK (playsink);
2960 /* get flags, there are protected with the object lock */
2961 flags = playsink->flags;
2962 GST_OBJECT_UNLOCK (playsink);
2964 /* figure out which components we need */
2965 if (flags & GST_PLAY_FLAG_TEXT && playsink->text_pad) {
2966 /* we have subtitles and we are requested to show it */
2970 if (((flags & GST_PLAY_FLAG_VIDEO)
2971 || (flags & GST_PLAY_FLAG_NATIVE_VIDEO)) && playsink->video_pad) {
2972 /* we have video and we are requested to show it */
2975 /* we only deinterlace if native video is not requested and
2976 * we have raw video */
2977 if ((flags & GST_PLAY_FLAG_DEINTERLACE)
2978 && !(flags & GST_PLAY_FLAG_NATIVE_VIDEO) && playsink->video_pad_raw)
2979 need_deinterlace = TRUE;
2982 if (playsink->audio_pad) {
2983 if ((flags & GST_PLAY_FLAG_AUDIO) || (flags & GST_PLAY_FLAG_NATIVE_AUDIO)) {
2986 if (playsink->audio_pad_raw) {
2987 /* only can do vis with raw uncompressed audio */
2988 if (flags & GST_PLAY_FLAG_VIS && !need_video) {
2989 /* also add video when we add visualisation */
2996 /* we have a text_pad and we need text rendering, in this case we need a
2997 * video_pad to combine the video with the text or visualizations */
2998 if (need_text && !need_video && !playsink->text_sink) {
2999 if (playsink->video_pad) {
3001 } else if (need_audio) {
3002 GST_ELEMENT_WARNING (playsink, STREAM, FORMAT,
3003 (_("Can't play a text file without video or visualizations.")),
3004 ("Have text pad but no video pad or visualizations"));
3007 GST_ELEMENT_ERROR (playsink, STREAM, FORMAT,
3008 (_("Can't play a text file without video or visualizations.")),
3009 ("Have text pad but no video pad or visualizations"));
3010 GST_PLAY_SINK_UNLOCK (playsink);
3015 GST_DEBUG_OBJECT (playsink, "audio:%d, video:%d, vis:%d, text:%d", need_audio,
3016 need_video, need_vis, need_text);
3018 /* set up video pipeline */
3020 gboolean raw, async;
3022 /* we need a raw sink when we do vis or when we have a raw pad */
3023 raw = need_vis ? TRUE : playsink->video_pad_raw;
3024 /* we try to set the sink async=FALSE when we need vis, this way we can
3025 * avoid a queue in the audio chain. */
3028 GST_DEBUG_OBJECT (playsink, "adding video, raw %d",
3029 playsink->video_pad_raw);
3031 if (playsink->videochain) {
3032 /* try to reactivate the chain */
3033 if (!setup_video_chain (playsink, raw, async)) {
3034 if (playsink->video_sinkpad_stream_synchronizer) {
3035 gst_element_release_request_pad (GST_ELEMENT_CAST
3036 (playsink->stream_synchronizer),
3037 playsink->video_sinkpad_stream_synchronizer);
3038 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3039 playsink->video_sinkpad_stream_synchronizer = NULL;
3040 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3041 playsink->video_srcpad_stream_synchronizer = NULL;
3044 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3046 /* Remove the sink from the bin to keep its state
3047 * and unparent it to allow reuse */
3048 if (playsink->videochain->sink)
3049 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
3050 playsink->videochain->sink);
3052 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3053 free_chain ((GstPlayChain *) playsink->videochain);
3054 playsink->videochain = NULL;
3056 GST_OBJECT_LOCK (playsink);
3057 if (playsink->overlay_element)
3058 gst_object_unref (playsink->overlay_element);
3059 playsink->overlay_element = NULL;
3061 if (playsink->colorbalance_element) {
3062 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
3063 G_CALLBACK (colorbalance_value_changed_cb), playsink);
3064 gst_object_unref (playsink->colorbalance_element);
3066 playsink->colorbalance_element = NULL;
3067 GST_OBJECT_UNLOCK (playsink);
3071 if (!playsink->videochain)
3072 playsink->videochain = gen_video_chain (playsink, raw, async);
3073 if (!playsink->videochain)
3076 if (!playsink->video_sinkpad_stream_synchronizer) {
3077 GValue item = { 0, };
3080 playsink->video_sinkpad_stream_synchronizer =
3081 gst_element_get_request_pad (GST_ELEMENT_CAST
3082 (playsink->stream_synchronizer), "sink_%u");
3083 it = gst_pad_iterate_internal_links
3084 (playsink->video_sinkpad_stream_synchronizer);
3086 gst_iterator_next (it, &item);
3087 playsink->video_srcpad_stream_synchronizer = g_value_dup_object (&item);
3088 g_value_unset (&item);
3089 g_assert (playsink->video_srcpad_stream_synchronizer);
3090 gst_iterator_free (it);
3093 if (playsink->video_pad)
3094 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad),
3095 playsink->video_sinkpad_stream_synchronizer);
3097 if (need_deinterlace) {
3098 if (!playsink->videodeinterlacechain)
3099 playsink->videodeinterlacechain =
3100 gen_video_deinterlace_chain (playsink);
3101 if (!playsink->videodeinterlacechain)
3104 GST_DEBUG_OBJECT (playsink, "adding video deinterlace chain");
3106 GST_DEBUG_OBJECT (playsink, "setting up deinterlacing chain");
3108 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
3109 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
3111 gst_pad_unlink (playsink->video_srcpad_stream_synchronizer,
3112 playsink->videochain->sinkpad);
3113 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3114 playsink->videodeinterlacechain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3116 if (playsink->videodeinterlacechain) {
3117 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3118 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
3123 GST_DEBUG_OBJECT (playsink, "adding video chain");
3124 add_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
3125 activate_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
3126 /* if we are not part of vis or subtitles, set the ghostpad target */
3127 if (!need_vis && !need_text && (!playsink->textchain
3128 || !playsink->text_pad)) {
3129 GST_DEBUG_OBJECT (playsink, "ghosting video sinkpad");
3130 gst_pad_unlink (playsink->video_srcpad_stream_synchronizer,
3131 playsink->videochain->sinkpad);
3132 if (playsink->videodeinterlacechain
3133 && playsink->videodeinterlacechain->srcpad)
3134 gst_pad_unlink (playsink->videodeinterlacechain->srcpad,
3135 playsink->videochain->sinkpad);
3136 if (need_deinterlace)
3137 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
3138 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3140 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3141 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3144 GST_DEBUG_OBJECT (playsink, "no video needed");
3145 if (playsink->videochain) {
3146 GST_DEBUG_OBJECT (playsink, "removing video chain");
3147 if (playsink->vischain) {
3150 GST_DEBUG_OBJECT (playsink, "unlinking vis chain");
3152 /* also had visualisation, release the tee srcpad before we then
3153 * unlink the video from it */
3154 if (playsink->audio_tee_vissrc) {
3155 gst_element_release_request_pad (playsink->audio_tee,
3156 playsink->audio_tee_vissrc);
3157 gst_object_unref (playsink->audio_tee_vissrc);
3158 playsink->audio_tee_vissrc = NULL;
3161 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
3162 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
3165 if (playsink->video_sinkpad_stream_synchronizer) {
3166 gst_element_release_request_pad (GST_ELEMENT_CAST
3167 (playsink->stream_synchronizer),
3168 playsink->video_sinkpad_stream_synchronizer);
3169 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3170 playsink->video_sinkpad_stream_synchronizer = NULL;
3171 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3172 playsink->video_srcpad_stream_synchronizer = NULL;
3175 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3176 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3177 if (playsink->videochain->ts_offset)
3178 gst_object_unref (playsink->videochain->ts_offset);
3179 playsink->videochain->ts_offset = NULL;
3182 if (playsink->videodeinterlacechain) {
3183 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3184 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3187 if (playsink->video_pad)
3188 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
3190 GST_OBJECT_LOCK (playsink);
3191 if (playsink->overlay_element)
3192 gst_object_unref (playsink->overlay_element);
3193 playsink->overlay_element = NULL;
3195 if (playsink->colorbalance_element) {
3196 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
3197 G_CALLBACK (colorbalance_value_changed_cb), playsink);
3198 gst_object_unref (playsink->colorbalance_element);
3200 playsink->colorbalance_element = NULL;
3201 GST_OBJECT_UNLOCK (playsink);
3208 GST_DEBUG_OBJECT (playsink, "adding audio");
3210 /* get a raw sink if we are asked for a raw pad */
3211 raw = playsink->audio_pad_raw;
3213 if (playsink->audiochain) {
3214 /* try to reactivate the chain */
3215 if (!setup_audio_chain (playsink, raw)) {
3216 GST_DEBUG_OBJECT (playsink, "removing current audio chain");
3217 if (playsink->audio_tee_asrc) {
3218 gst_element_release_request_pad (playsink->audio_tee,
3219 playsink->audio_tee_asrc);
3220 gst_object_unref (playsink->audio_tee_asrc);
3221 playsink->audio_tee_asrc = NULL;
3224 if (playsink->audio_sinkpad_stream_synchronizer) {
3225 gst_element_release_request_pad (GST_ELEMENT_CAST
3226 (playsink->stream_synchronizer),
3227 playsink->audio_sinkpad_stream_synchronizer);
3228 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
3229 playsink->audio_sinkpad_stream_synchronizer = NULL;
3230 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
3231 playsink->audio_srcpad_stream_synchronizer = NULL;
3234 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3236 /* Remove the sink from the bin to keep its state
3237 * and unparent it to allow reuse */
3238 if (playsink->audiochain->sink)
3239 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
3240 playsink->audiochain->sink);
3242 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3243 disconnect_chain (playsink->audiochain, playsink);
3244 playsink->audiochain->volume = NULL;
3245 playsink->audiochain->mute = NULL;
3246 if (playsink->audiochain->ts_offset)
3247 gst_object_unref (playsink->audiochain->ts_offset);
3248 playsink->audiochain->ts_offset = NULL;
3249 free_chain ((GstPlayChain *) playsink->audiochain);
3250 playsink->audiochain = NULL;
3251 playsink->volume_changed = playsink->mute_changed = FALSE;
3255 if (!playsink->audiochain) {
3256 GST_DEBUG_OBJECT (playsink, "creating new audio chain");
3257 playsink->audiochain = gen_audio_chain (playsink, raw);
3260 if (!playsink->audio_sinkpad_stream_synchronizer) {
3261 GValue item = { 0, };
3264 playsink->audio_sinkpad_stream_synchronizer =
3265 gst_element_get_request_pad (GST_ELEMENT_CAST
3266 (playsink->stream_synchronizer), "sink_%u");
3267 it = gst_pad_iterate_internal_links
3268 (playsink->audio_sinkpad_stream_synchronizer);
3270 gst_iterator_next (it, &item);
3271 playsink->audio_srcpad_stream_synchronizer = g_value_dup_object (&item);
3272 g_value_unset (&item);
3273 g_assert (playsink->audio_srcpad_stream_synchronizer);
3274 gst_iterator_free (it);
3277 if (playsink->audiochain) {
3278 GST_DEBUG_OBJECT (playsink, "adding audio chain");
3279 if (playsink->audio_tee_asrc == NULL) {
3280 playsink->audio_tee_asrc =
3281 gst_element_get_request_pad (playsink->audio_tee, "src_%u");
3283 add_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
3284 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
3285 gst_pad_link_full (playsink->audio_tee_asrc,
3286 playsink->audio_sinkpad_stream_synchronizer,
3287 GST_PAD_LINK_CHECK_NOTHING);
3288 gst_pad_link_full (playsink->audio_srcpad_stream_synchronizer,
3289 playsink->audiochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3292 GST_DEBUG_OBJECT (playsink, "no audio needed");
3293 /* we have no audio or we are requested to not play audio */
3294 if (playsink->audiochain) {
3295 GST_DEBUG_OBJECT (playsink, "removing audio chain");
3296 /* release the audio pad */
3297 if (playsink->audio_tee_asrc) {
3298 gst_element_release_request_pad (playsink->audio_tee,
3299 playsink->audio_tee_asrc);
3300 gst_object_unref (playsink->audio_tee_asrc);
3301 playsink->audio_tee_asrc = NULL;
3304 if (playsink->audio_sinkpad_stream_synchronizer) {
3305 gst_element_release_request_pad (GST_ELEMENT_CAST
3306 (playsink->stream_synchronizer),
3307 playsink->audio_sinkpad_stream_synchronizer);
3308 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
3309 playsink->audio_sinkpad_stream_synchronizer = NULL;
3310 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
3311 playsink->audio_srcpad_stream_synchronizer = NULL;
3314 if (playsink->audiochain->sink_volume) {
3315 disconnect_chain (playsink->audiochain, playsink);
3316 playsink->audiochain->volume = NULL;
3317 playsink->audiochain->mute = NULL;
3318 if (playsink->audiochain->ts_offset)
3319 gst_object_unref (playsink->audiochain->ts_offset);
3320 playsink->audiochain->ts_offset = NULL;
3322 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3323 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3330 if (!playsink->vischain)
3331 playsink->vischain = gen_vis_chain (playsink);
3333 GST_DEBUG_OBJECT (playsink, "adding visualisation");
3335 if (playsink->vischain) {
3336 GST_DEBUG_OBJECT (playsink, "setting up vis chain");
3338 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
3339 add_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
3340 activate_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
3341 if (playsink->audio_tee_vissrc == NULL) {
3342 playsink->audio_tee_vissrc =
3343 gst_element_get_request_pad (playsink->audio_tee, "src_%u");
3345 gst_pad_link_full (playsink->audio_tee_vissrc,
3346 playsink->vischain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3347 gst_pad_link_full (srcpad, playsink->video_sinkpad_stream_synchronizer,
3348 GST_PAD_LINK_CHECK_NOTHING);
3349 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3350 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3351 gst_object_unref (srcpad);
3354 GST_DEBUG_OBJECT (playsink, "no vis needed");
3355 if (playsink->vischain) {
3356 if (playsink->audio_tee_vissrc) {
3357 gst_element_release_request_pad (playsink->audio_tee,
3358 playsink->audio_tee_vissrc);
3359 gst_object_unref (playsink->audio_tee_vissrc);
3360 playsink->audio_tee_vissrc = NULL;
3362 GST_DEBUG_OBJECT (playsink, "removing vis chain");
3363 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3364 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3369 GST_DEBUG_OBJECT (playsink, "adding text");
3370 if (!playsink->textchain) {
3371 GST_DEBUG_OBJECT (playsink, "creating text chain");
3372 playsink->textchain = gen_text_chain (playsink);
3374 if (playsink->textchain) {
3377 GST_DEBUG_OBJECT (playsink, "adding text chain");
3378 if (playsink->textchain->overlay)
3379 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", FALSE,
3381 add_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
3383 if (!playsink->text_sinkpad_stream_synchronizer) {
3384 GValue item = { 0, };
3386 playsink->text_sinkpad_stream_synchronizer =
3387 gst_element_get_request_pad (GST_ELEMENT_CAST
3388 (playsink->stream_synchronizer), "sink_%u");
3389 it = gst_pad_iterate_internal_links
3390 (playsink->text_sinkpad_stream_synchronizer);
3392 gst_iterator_next (it, &item);
3393 playsink->text_srcpad_stream_synchronizer = g_value_dup_object (&item);
3394 g_value_unset (&item);
3395 g_assert (playsink->text_srcpad_stream_synchronizer);
3396 gst_iterator_free (it);
3398 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad),
3399 playsink->text_sinkpad_stream_synchronizer);
3400 gst_pad_link_full (playsink->text_srcpad_stream_synchronizer,
3401 playsink->textchain->textsinkpad, GST_PAD_LINK_CHECK_NOTHING);
3404 if (need_vis || need_video) {
3409 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
3410 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
3411 gst_pad_link_full (srcpad, playsink->textchain->videosinkpad,
3412 GST_PAD_LINK_CHECK_NOTHING);
3413 gst_object_unref (srcpad);
3415 if (need_deinterlace)
3416 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
3417 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
3419 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3420 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
3422 gst_pad_link_full (playsink->textchain->srcpad,
3423 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3426 activate_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
3429 GST_DEBUG_OBJECT (playsink, "no text needed");
3430 /* we have no subtitles/text or we are requested to not show them */
3432 if (playsink->text_sinkpad_stream_synchronizer) {
3433 gst_element_release_request_pad (GST_ELEMENT_CAST
3434 (playsink->stream_synchronizer),
3435 playsink->text_sinkpad_stream_synchronizer);
3436 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
3437 playsink->text_sinkpad_stream_synchronizer = NULL;
3438 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
3439 playsink->text_srcpad_stream_synchronizer = NULL;
3442 if (playsink->textchain) {
3443 if (playsink->text_pad == NULL) {
3444 /* no text pad, remove the chain entirely */
3445 GST_DEBUG_OBJECT (playsink, "removing text chain");
3446 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3447 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3449 /* we have a chain and a textpad, turn the subtitles off */
3450 GST_DEBUG_OBJECT (playsink, "turning off the text");
3451 if (playsink->textchain->overlay)
3452 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", TRUE,
3456 if (!need_video && playsink->video_pad) {
3457 if (playsink->video_sinkpad_stream_synchronizer) {
3458 gst_element_release_request_pad (GST_ELEMENT_CAST
3459 (playsink->stream_synchronizer),
3460 playsink->video_sinkpad_stream_synchronizer);
3461 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3462 playsink->video_sinkpad_stream_synchronizer = NULL;
3463 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3464 playsink->video_srcpad_stream_synchronizer = NULL;
3467 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
3470 if (playsink->text_pad && !playsink->textchain)
3471 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad), NULL);
3473 update_av_offset (playsink);
3474 do_async_done (playsink);
3475 GST_PLAY_SINK_UNLOCK (playsink);
3482 /* gen_ chain already posted error */
3483 GST_DEBUG_OBJECT (playsink, "failed to setup chain");
3484 GST_PLAY_SINK_UNLOCK (playsink);
3490 * gst_play_sink_set_flags:
3491 * @playsink: a #GstPlaySink
3492 * @flags: #GstPlayFlags
3494 * Configure @flags on @playsink. The flags control the behaviour of @playsink
3495 * when constructing the sink pipelins.
3497 * Returns: TRUE if the flags could be configured.
3500 gst_play_sink_set_flags (GstPlaySink * playsink, GstPlayFlags flags)
3502 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), FALSE);
3504 GST_OBJECT_LOCK (playsink);
3505 playsink->flags = flags;
3506 GST_OBJECT_UNLOCK (playsink);
3512 * gst_play_sink_get_flags:
3513 * @playsink: a #GstPlaySink
3515 * Get the flags of @playsink. That flags control the behaviour of the sink when
3516 * it constructs the sink pipelines.
3518 * Returns: the currently configured #GstPlayFlags.
3521 gst_play_sink_get_flags (GstPlaySink * playsink)
3525 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), 0);
3527 GST_OBJECT_LOCK (playsink);
3528 res = playsink->flags;
3529 GST_OBJECT_UNLOCK (playsink);
3535 gst_play_sink_set_font_desc (GstPlaySink * playsink, const gchar * desc)
3537 GstPlayTextChain *chain;
3539 GST_PLAY_SINK_LOCK (playsink);
3540 chain = (GstPlayTextChain *) playsink->textchain;
3541 g_free (playsink->font_desc);
3542 playsink->font_desc = g_strdup (desc);
3543 if (chain && chain->overlay) {
3544 g_object_set (chain->overlay, "font-desc", desc, NULL);
3546 GST_PLAY_SINK_UNLOCK (playsink);
3550 gst_play_sink_get_font_desc (GstPlaySink * playsink)
3552 gchar *result = NULL;
3553 GstPlayTextChain *chain;
3555 GST_PLAY_SINK_LOCK (playsink);
3556 chain = (GstPlayTextChain *) playsink->textchain;
3557 if (chain && chain->overlay) {
3558 g_object_get (chain->overlay, "font-desc", &result, NULL);
3559 playsink->font_desc = g_strdup (result);
3561 result = g_strdup (playsink->font_desc);
3563 GST_PLAY_SINK_UNLOCK (playsink);
3569 gst_play_sink_set_subtitle_encoding (GstPlaySink * playsink,
3570 const gchar * encoding)
3572 GstPlayTextChain *chain;
3574 GST_PLAY_SINK_LOCK (playsink);
3575 chain = (GstPlayTextChain *) playsink->textchain;
3576 g_free (playsink->subtitle_encoding);
3577 playsink->subtitle_encoding = g_strdup (encoding);
3578 if (chain && chain->overlay) {
3579 g_object_set (chain->overlay, "subtitle-encoding", encoding, NULL);
3581 GST_PLAY_SINK_UNLOCK (playsink);
3585 gst_play_sink_get_subtitle_encoding (GstPlaySink * playsink)
3587 gchar *result = NULL;
3588 GstPlayTextChain *chain;
3590 GST_PLAY_SINK_LOCK (playsink);
3591 chain = (GstPlayTextChain *) playsink->textchain;
3592 if (chain && chain->overlay) {
3593 g_object_get (chain->overlay, "subtitle-encoding", &result, NULL);
3594 playsink->subtitle_encoding = g_strdup (result);
3596 result = g_strdup (playsink->subtitle_encoding);
3598 GST_PLAY_SINK_UNLOCK (playsink);
3604 update_av_offset (GstPlaySink * playsink)
3607 GstPlayAudioChain *achain;
3608 GstPlayVideoChain *vchain;
3610 av_offset = playsink->av_offset;
3611 achain = (GstPlayAudioChain *) playsink->audiochain;
3612 vchain = (GstPlayVideoChain *) playsink->videochain;
3614 if (achain && vchain && achain->ts_offset && vchain->ts_offset) {
3615 g_object_set (achain->ts_offset,
3616 "ts-offset", MAX (G_GINT64_CONSTANT (0), -av_offset), NULL);
3617 g_object_set (vchain->ts_offset,
3618 "ts-offset", MAX (G_GINT64_CONSTANT (0), av_offset), NULL);
3620 GST_LOG_OBJECT (playsink, "no ts_offset elements");
3625 gst_play_sink_set_av_offset (GstPlaySink * playsink, gint64 av_offset)
3627 GST_PLAY_SINK_LOCK (playsink);
3628 playsink->av_offset = av_offset;
3629 update_av_offset (playsink);
3630 GST_PLAY_SINK_UNLOCK (playsink);
3634 gst_play_sink_get_av_offset (GstPlaySink * playsink)
3638 GST_PLAY_SINK_LOCK (playsink);
3639 result = playsink->av_offset;
3640 GST_PLAY_SINK_UNLOCK (playsink);
3646 * gst_play_sink_get_last_sample:
3647 * @playsink: a #GstPlaySink
3649 * Get the last displayed sample from @playsink. This sample is in the native
3650 * format of the sink element, the caps in the result sample contain the format
3651 * of the frame data.
3653 * Returns: a #GstSample with the frame data or %NULL when no video frame is
3657 gst_play_sink_get_last_sample (GstPlaySink * playsink)
3659 GstSample *result = NULL;
3660 GstPlayVideoChain *chain;
3662 GST_PLAY_SINK_LOCK (playsink);
3663 GST_DEBUG_OBJECT (playsink, "taking last sample");
3664 /* get the video chain if we can */
3665 if ((chain = (GstPlayVideoChain *) playsink->videochain)) {
3666 GST_DEBUG_OBJECT (playsink, "found video chain");
3667 /* see if the chain is active */
3668 if (chain->chain.activated && chain->sink) {
3671 GST_DEBUG_OBJECT (playsink, "video chain active and has a sink");
3673 /* find and get the last-buffer property now */
3675 gst_play_sink_find_property (playsink, chain->sink,
3676 "last-sample", GST_TYPE_SAMPLE))) {
3677 GST_DEBUG_OBJECT (playsink, "getting last-sample property");
3678 g_object_get (elem, "last-sample", &result, NULL);
3679 gst_object_unref (elem);
3683 GST_PLAY_SINK_UNLOCK (playsink);
3689 * gst_play_sink_convert_sample:
3690 * @playsink: a #GstPlaySink
3693 * Get the last displayed frame from @playsink. If caps is %NULL, the video will
3694 * be in the native format of the sink element and the caps on the buffer
3695 * describe the format of the frame. If @caps is not %NULL, the video
3696 * frame will be converted to the format of the caps.
3698 * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
3699 * available or when the conversion failed.
3702 gst_play_sink_convert_sample (GstPlaySink * playsink, GstCaps * caps)
3707 result = gst_play_sink_get_last_sample (playsink);
3708 if (result != NULL && caps != NULL) {
3711 temp = gst_video_convert_sample (result, caps, 25 * GST_SECOND, &err);
3712 if (temp == NULL && err)
3715 gst_sample_unref (result);
3723 /* I'm really uncertain whether we should make playsink post an error
3724 * on the bus or not. It's not like it's a critical issue regarding
3725 * playsink behaviour. */
3726 GST_ERROR ("Error converting frame: %s", err->message);
3727 gst_sample_unref (result);
3734 is_raw_structure (GstStructure * s)
3738 name = gst_structure_get_name (s);
3740 if (g_str_equal (name, "video/x-raw") || g_str_equal (name, "audio/x-raw"))
3746 is_raw_pad (GstPad * pad)
3748 GstPad *peer = gst_pad_get_peer (pad);
3750 gboolean raw = TRUE;
3755 caps = gst_pad_get_current_caps (peer);
3759 caps = gst_pad_query_caps (peer, NULL);
3761 n = gst_caps_get_size (caps);
3762 for (i = 0; i < n; i++) {
3763 gboolean r = is_raw_structure (gst_caps_get_structure (caps, i));
3767 } else if (raw != r) {
3768 GST_ERROR_OBJECT (pad,
3769 "Caps contains raw and non-raw structures: %" GST_PTR_FORMAT, caps);
3775 raw = is_raw_structure (gst_caps_get_structure (caps, 0));
3777 gst_caps_unref (caps);
3778 gst_object_unref (peer);
3783 static GstPadProbeReturn
3784 sinkpad_blocked_cb (GstPad * blockedpad, GstPadProbeInfo * info,
3785 gpointer user_data);
3788 video_set_blocked (GstPlaySink * playsink, gboolean blocked)
3790 if (playsink->video_pad) {
3792 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3793 (playsink->video_pad)));
3794 if (blocked && playsink->video_block_id == 0) {
3795 playsink->video_block_id =
3796 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3797 sinkpad_blocked_cb, playsink, NULL);
3798 } else if (!blocked && playsink->video_block_id) {
3799 gst_pad_remove_probe (opad, playsink->video_block_id);
3800 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO_RAW);
3801 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO);
3802 playsink->video_block_id = 0;
3803 playsink->video_pad_blocked = FALSE;
3805 gst_object_unref (opad);
3810 audio_set_blocked (GstPlaySink * playsink, gboolean blocked)
3812 if (playsink->audio_pad) {
3814 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3815 (playsink->audio_pad)));
3816 if (blocked && playsink->audio_block_id == 0) {
3817 playsink->audio_block_id =
3818 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3819 sinkpad_blocked_cb, playsink, NULL);
3820 } else if (!blocked && playsink->audio_block_id) {
3821 gst_pad_remove_probe (opad, playsink->audio_block_id);
3822 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO_RAW);
3823 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO);
3824 playsink->audio_block_id = 0;
3825 playsink->audio_pad_blocked = FALSE;
3827 gst_object_unref (opad);
3832 text_set_blocked (GstPlaySink * playsink, gboolean blocked)
3834 if (playsink->text_pad) {
3836 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3837 (playsink->text_pad)));
3838 if (blocked && playsink->text_block_id == 0) {
3839 playsink->text_block_id =
3840 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3841 sinkpad_blocked_cb, playsink, NULL);
3842 } else if (!blocked && playsink->text_block_id) {
3843 gst_pad_remove_probe (opad, playsink->text_block_id);
3844 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_TEXT);
3845 playsink->text_block_id = 0;
3846 playsink->text_pad_blocked = FALSE;
3848 gst_object_unref (opad);
3853 gst_play_sink_reconfigure (GstPlaySink * playsink)
3855 GST_LOG_OBJECT (playsink, "Triggering reconfiguration");
3857 GST_PLAY_SINK_LOCK (playsink);
3858 video_set_blocked (playsink, TRUE);
3859 audio_set_blocked (playsink, TRUE);
3860 text_set_blocked (playsink, TRUE);
3861 GST_PLAY_SINK_UNLOCK (playsink);
3866 static GstPadProbeReturn
3867 sinkpad_blocked_cb (GstPad * blockedpad, GstPadProbeInfo * info,
3870 GstPlaySink *playsink = (GstPlaySink *) user_data;
3873 GST_PLAY_SINK_LOCK (playsink);
3875 pad = GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (blockedpad)));
3876 if (pad == playsink->video_pad) {
3877 playsink->video_pad_blocked = TRUE;
3878 GST_DEBUG_OBJECT (pad, "Video pad blocked");
3879 } else if (pad == playsink->audio_pad) {
3880 playsink->audio_pad_blocked = TRUE;
3881 GST_DEBUG_OBJECT (pad, "Audio pad blocked");
3882 } else if (pad == playsink->text_pad) {
3883 playsink->text_pad_blocked = TRUE;
3884 GST_DEBUG_OBJECT (pad, "Text pad blocked");
3887 /* We reconfigure when for ALL streams:
3888 * * there isn't a pad
3889 * * OR the pad is blocked
3890 * * OR there are no pending blocks on that pad
3893 if ((!playsink->video_pad || playsink->video_pad_blocked
3894 || !PENDING_VIDEO_BLOCK (playsink)) && (!playsink->audio_pad
3895 || playsink->audio_pad_blocked || !PENDING_AUDIO_BLOCK (playsink))
3896 && (!playsink->text_pad || playsink->text_pad_blocked
3897 || !PENDING_TEXT_BLOCK (playsink))) {
3898 GST_DEBUG_OBJECT (playsink, "All pads blocked -- reconfiguring");
3900 if (playsink->video_pad) {
3901 playsink->video_pad_raw = is_raw_pad (playsink->video_pad);
3902 GST_DEBUG_OBJECT (playsink, "Video pad is raw: %d",
3903 playsink->video_pad_raw);
3906 if (playsink->audio_pad) {
3907 playsink->audio_pad_raw = is_raw_pad (playsink->audio_pad);
3908 GST_DEBUG_OBJECT (playsink, "Audio pad is raw: %d",
3909 playsink->audio_pad_raw);
3912 gst_play_sink_do_reconfigure (playsink);
3914 video_set_blocked (playsink, FALSE);
3915 audio_set_blocked (playsink, FALSE);
3916 text_set_blocked (playsink, FALSE);
3919 gst_object_unref (pad);
3921 GST_PLAY_SINK_UNLOCK (playsink);
3923 return GST_PAD_PROBE_OK;
3927 caps_notify_cb (GstPad * pad, GParamSpec * unused, GstPlaySink * playsink)
3929 gboolean reconfigure = FALSE;
3933 g_object_get (pad, "caps", &caps, NULL);
3937 if (pad == playsink->audio_pad) {
3938 raw = is_raw_pad (pad);
3939 reconfigure = (! !playsink->audio_pad_raw != ! !raw)
3940 && playsink->audiochain;
3941 GST_DEBUG_OBJECT (pad,
3942 "Audio caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3944 } else if (pad == playsink->video_pad) {
3945 raw = is_raw_pad (pad);
3946 reconfigure = (! !playsink->video_pad_raw != ! !raw)
3947 && playsink->videochain;
3948 GST_DEBUG_OBJECT (pad,
3949 "Video caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3953 gst_caps_unref (caps);
3956 gst_play_sink_reconfigure (playsink);
3960 gst_play_sink_refresh_pad (GstPlaySink * playsink, GstPad * pad,
3961 GstPlaySinkType type)
3963 gulong *block_id = NULL;
3965 GST_DEBUG_OBJECT (playsink, "refresh pad %" GST_PTR_FORMAT, pad);
3967 GST_PLAY_SINK_LOCK (playsink);
3968 if (pad == playsink->video_pad) {
3969 if (type != GST_PLAY_SINK_TYPE_VIDEO_RAW &&
3970 type != GST_PLAY_SINK_TYPE_VIDEO)
3972 block_id = &playsink->video_block_id;
3973 } else if (pad == playsink->audio_pad) {
3974 if (type != GST_PLAY_SINK_TYPE_AUDIO_RAW &&
3975 type != GST_PLAY_SINK_TYPE_AUDIO)
3977 block_id = &playsink->audio_block_id;
3978 } else if (pad == playsink->text_pad) {
3979 if (type != GST_PLAY_SINK_TYPE_TEXT)
3981 block_id = &playsink->text_block_id;
3984 if (type != GST_PLAY_SINK_TYPE_FLUSHING && (block_id && *block_id == 0)) {
3986 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (pad)));
3989 gst_pad_add_probe (blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3990 sinkpad_blocked_cb, playsink, NULL);
3991 PENDING_FLAG_SET (playsink, type);
3992 gst_object_unref (blockpad);
3994 GST_PLAY_SINK_UNLOCK (playsink);
4001 GST_WARNING_OBJECT (playsink, "wrong type %u for pad %" GST_PTR_FORMAT,
4003 GST_PLAY_SINK_UNLOCK (playsink);
4009 * gst_play_sink_request_pad
4010 * @playsink: a #GstPlaySink
4011 * @type: a #GstPlaySinkType
4013 * Create or return a pad of @type.
4015 * Returns: a #GstPad of @type or %NULL when the pad could not be created.
4018 gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type)
4021 gboolean created = FALSE;
4022 gboolean activate = TRUE;
4023 const gchar *pad_name = NULL;
4024 gulong *block_id = NULL;
4026 GST_DEBUG_OBJECT (playsink, "request pad type %d", type);
4028 GST_PLAY_SINK_LOCK (playsink);
4030 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
4031 case GST_PLAY_SINK_TYPE_AUDIO:
4032 pad_name = "audio_sink";
4033 if (!playsink->audio_tee) {
4034 GST_LOG_OBJECT (playsink, "creating tee");
4035 /* create tee when needed. This element will feed the audio sink chain
4036 * and the vis chain. */
4037 playsink->audio_tee = gst_element_factory_make ("tee", "audiotee");
4038 if (playsink->audio_tee == NULL) {
4039 post_missing_element_message (playsink, "tee");
4040 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
4041 (_("Missing element '%s' - check your GStreamer installation."),
4046 playsink->audio_tee_sink =
4047 gst_element_get_static_pad (playsink->audio_tee, "sink");
4048 gst_bin_add (GST_BIN_CAST (playsink), playsink->audio_tee);
4049 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
4052 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
4054 if (!playsink->audio_pad) {
4055 GST_LOG_OBJECT (playsink, "ghosting tee sinkpad");
4056 playsink->audio_pad =
4057 gst_ghost_pad_new (pad_name, playsink->audio_tee_sink);
4058 g_signal_connect (G_OBJECT (playsink->audio_pad), "notify::caps",
4059 G_CALLBACK (caps_notify_cb), playsink);
4062 playsink->audio_pad_raw = FALSE;
4063 res = playsink->audio_pad;
4064 block_id = &playsink->audio_block_id;
4066 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
4067 case GST_PLAY_SINK_TYPE_VIDEO:
4068 pad_name = "video_sink";
4069 if (!playsink->video_pad) {
4070 GST_LOG_OBJECT (playsink, "ghosting videosink");
4071 playsink->video_pad =
4072 gst_ghost_pad_new_no_target (pad_name, GST_PAD_SINK);
4073 g_signal_connect (G_OBJECT (playsink->video_pad), "notify::caps",
4074 G_CALLBACK (caps_notify_cb), playsink);
4077 playsink->video_pad_raw = FALSE;
4078 res = playsink->video_pad;
4079 block_id = &playsink->video_block_id;
4081 case GST_PLAY_SINK_TYPE_TEXT:
4082 GST_LOG_OBJECT (playsink, "ghosting text");
4083 if (!playsink->text_pad) {
4084 playsink->text_pad =
4085 gst_ghost_pad_new_no_target ("text_sink", GST_PAD_SINK);
4088 res = playsink->text_pad;
4089 block_id = &playsink->text_block_id;
4091 case GST_PLAY_SINK_TYPE_FLUSHING:
4095 /* we need a unique padname for the flushing pad. */
4096 padname = g_strdup_printf ("flushing_%u", playsink->count);
4097 res = gst_ghost_pad_new_no_target (padname, GST_PAD_SINK);
4108 GST_PLAY_SINK_UNLOCK (playsink);
4110 if (created && res) {
4111 /* we have to add the pad when it's active or we get an error when the
4112 * element is 'running' */
4113 gst_pad_set_active (res, TRUE);
4114 gst_element_add_pad (GST_ELEMENT_CAST (playsink), res);
4115 if (block_id && *block_id == 0) {
4117 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (res)));
4120 gst_pad_add_probe (blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
4121 sinkpad_blocked_cb, playsink, NULL);
4122 PENDING_FLAG_SET (playsink, type);
4123 gst_object_unref (blockpad);
4126 gst_pad_set_active (res, activate);
4134 gst_play_sink_request_new_pad (GstElement * element, GstPadTemplate * templ,
4135 const gchar * name, const GstCaps * caps)
4139 GstPlaySinkType type;
4140 const gchar *tplname;
4142 g_return_val_if_fail (templ != NULL, NULL);
4144 GST_DEBUG_OBJECT (element, "name:%s", name);
4146 psink = GST_PLAY_SINK (element);
4147 tplname = GST_PAD_TEMPLATE_NAME_TEMPLATE (templ);
4149 /* Figure out the GstPlaySinkType based on the template */
4150 if (!strcmp (tplname, "audio_sink"))
4151 type = GST_PLAY_SINK_TYPE_AUDIO;
4152 else if (!strcmp (tplname, "audio_raw_sink"))
4153 type = GST_PLAY_SINK_TYPE_AUDIO_RAW;
4154 else if (!strcmp (tplname, "video_sink"))
4155 type = GST_PLAY_SINK_TYPE_VIDEO;
4156 else if (!strcmp (tplname, "video_raw_sink"))
4157 type = GST_PLAY_SINK_TYPE_VIDEO_RAW;
4158 else if (!strcmp (tplname, "text_sink"))
4159 type = GST_PLAY_SINK_TYPE_TEXT;
4161 goto unknown_template;
4163 pad = gst_play_sink_request_pad (psink, type);
4167 GST_WARNING_OBJECT (element, "Unknown pad template");
4172 gst_play_sink_release_pad (GstPlaySink * playsink, GstPad * pad)
4174 GstPad **res = NULL;
4175 gboolean untarget = TRUE;
4177 GST_DEBUG_OBJECT (playsink, "release pad %" GST_PTR_FORMAT, pad);
4179 GST_PLAY_SINK_LOCK (playsink);
4180 if (pad == playsink->video_pad) {
4181 res = &playsink->video_pad;
4182 g_signal_handlers_disconnect_by_func (playsink->video_pad, caps_notify_cb,
4184 video_set_blocked (playsink, FALSE);
4185 } else if (pad == playsink->audio_pad) {
4186 res = &playsink->audio_pad;
4187 g_signal_handlers_disconnect_by_func (playsink->audio_pad, caps_notify_cb,
4189 audio_set_blocked (playsink, FALSE);
4190 } else if (pad == playsink->text_pad) {
4191 res = &playsink->text_pad;
4192 text_set_blocked (playsink, FALSE);
4194 /* try to release the given pad anyway, these could be the FLUSHING pads. */
4198 GST_PLAY_SINK_UNLOCK (playsink);
4201 GST_DEBUG_OBJECT (playsink, "deactivate pad %" GST_PTR_FORMAT, *res);
4202 gst_pad_set_active (*res, FALSE);
4204 GST_DEBUG_OBJECT (playsink, "untargeting pad %" GST_PTR_FORMAT, *res);
4205 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (*res), NULL);
4207 GST_DEBUG_OBJECT (playsink, "remove pad %" GST_PTR_FORMAT, *res);
4208 gst_element_remove_pad (GST_ELEMENT_CAST (playsink), *res);
4214 gst_play_sink_release_request_pad (GstElement * element, GstPad * pad)
4216 GstPlaySink *psink = GST_PLAY_SINK (element);
4218 gst_play_sink_release_pad (psink, pad);
4222 gst_play_sink_handle_message (GstBin * bin, GstMessage * message)
4224 GstPlaySink *playsink;
4226 playsink = GST_PLAY_SINK_CAST (bin);
4228 switch (GST_MESSAGE_TYPE (message)) {
4229 case GST_MESSAGE_STEP_DONE:
4234 gboolean flush, intermediate, eos;
4237 GST_INFO_OBJECT (playsink, "Handling step-done message");
4238 gst_message_parse_step_done (message, &format, &amount, &rate, &flush,
4239 &intermediate, &duration, &eos);
4241 if (format == GST_FORMAT_BUFFERS) {
4242 /* for the buffer format, we align the other streams */
4243 if (playsink->audiochain) {
4247 gst_event_new_step (GST_FORMAT_TIME, duration, rate, flush,
4250 if (!gst_element_send_event (playsink->audiochain->chain.bin, event)) {
4251 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
4255 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
4258 case GST_MESSAGE_ELEMENT:{
4259 if (gst_is_video_overlay_prepare_window_handle_message (message)) {
4260 GstVideoOverlay *overlay;
4262 GST_OBJECT_LOCK (playsink);
4263 if (playsink->overlay_element
4264 && GST_OBJECT_CAST (playsink->overlay_element) !=
4265 GST_MESSAGE_SRC (message)) {
4266 gst_object_unref (playsink->overlay_element);
4267 playsink->overlay_element = NULL;
4270 if (!playsink->overlay_element)
4271 playsink->overlay_element =
4272 GST_VIDEO_OVERLAY (gst_object_ref (GST_MESSAGE_SRC (message)));
4274 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4275 GST_OBJECT_UNLOCK (playsink);
4277 GST_DEBUG_OBJECT (playsink, "Got prepare-xwindow-id message");
4279 if (playsink->overlay_handle_set)
4280 gst_video_overlay_set_window_handle (playsink->overlay_element,
4281 playsink->overlay_handle);
4282 if (playsink->overlay_handle_events_set)
4283 gst_video_overlay_handle_events (playsink->overlay_element,
4284 playsink->overlay_handle_events);
4285 if (playsink->overlay_render_rectangle_set)
4286 gst_video_overlay_set_render_rectangle (playsink->overlay_element,
4287 playsink->overlay_x, playsink->overlay_y,
4288 playsink->overlay_width, playsink->overlay_height);
4290 gst_object_unref (overlay);
4291 gst_message_unref (message);
4292 gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (playsink));
4294 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin,
4300 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
4305 /* Send an event to our sinks until one of them works; don't then send to the
4306 * remaining sinks (unlike GstBin)
4307 * Special case: If a text sink is set we need to send the event
4308 * to them in case it's source is different from the a/v stream's source.
4311 gst_play_sink_send_event_to_sink (GstPlaySink * playsink, GstEvent * event)
4313 gboolean res = TRUE;
4314 if (playsink->send_event_mode == MODE_FIRST) {
4315 if (playsink->textchain && playsink->textchain->sink) {
4316 gst_event_ref (event);
4318 gst_element_send_event (playsink->textchain->chain.bin, event))) {
4319 GST_DEBUG_OBJECT (playsink, "Sent event successfully to text sink");
4321 GST_DEBUG_OBJECT (playsink, "Event failed when sent to text sink");
4325 if (playsink->videochain) {
4326 gst_event_ref (event);
4328 gst_element_send_event (playsink->videochain->chain.bin,
4330 GST_DEBUG_OBJECT (playsink, "Sent event successfully to video sink");
4333 GST_DEBUG_OBJECT (playsink, "Event failed when sent to video sink");
4335 if (playsink->audiochain) {
4336 gst_event_ref (event);
4338 gst_element_send_event (playsink->audiochain->chain.bin,
4340 GST_DEBUG_OBJECT (playsink, "Sent event successfully to audio sink");
4343 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
4347 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event
4348 (GST_ELEMENT_CAST (playsink), event);
4352 gst_event_unref (event);
4356 /* We only want to send the event to a single sink (overriding GstBin's
4357 * behaviour), but we want to keep GstPipeline's behaviour - wrapping seek
4358 * events appropriately. So, this is a messy duplication of code. */
4360 gst_play_sink_send_event (GstElement * element, GstEvent * event)
4362 gboolean res = FALSE;
4363 GstEventType event_type = GST_EVENT_TYPE (event);
4364 GstPlaySink *playsink;
4365 playsink = GST_PLAY_SINK_CAST (element);
4366 switch (event_type) {
4367 case GST_EVENT_SEEK:
4368 GST_DEBUG_OBJECT (element, "Sending event to a sink");
4369 res = gst_play_sink_send_event_to_sink (playsink, event);
4371 case GST_EVENT_STEP:
4376 gboolean flush, intermediate;
4377 gst_event_parse_step (event, &format, &amount, &rate, &flush,
4379 if (format == GST_FORMAT_BUFFERS) {
4380 /* for buffers, we will try to step video frames, for other formats we
4381 * send the step to all sinks */
4382 res = gst_play_sink_send_event_to_sink (playsink, event);
4385 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
4392 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
4399 static GstStateChangeReturn
4400 gst_play_sink_change_state (GstElement * element, GstStateChange transition)
4402 GstStateChangeReturn ret;
4403 GstStateChangeReturn bret;
4404 GstPlaySink *playsink;
4405 playsink = GST_PLAY_SINK (element);
4406 switch (transition) {
4407 case GST_STATE_CHANGE_READY_TO_PAUSED:
4408 gst_segment_init (&playsink->text_segment, GST_FORMAT_UNDEFINED);
4410 playsink->need_async_start = TRUE;
4411 /* we want to go async to PAUSED until we managed to configure and add the
4413 do_async_start (playsink);
4414 ret = GST_STATE_CHANGE_ASYNC;
4416 /* block all pads here */
4417 if (!gst_play_sink_reconfigure (playsink))
4418 ret = GST_STATE_CHANGE_FAILURE;
4420 case GST_STATE_CHANGE_PAUSED_TO_READY:
4421 /* unblock all pads here */
4422 GST_PLAY_SINK_LOCK (playsink);
4423 video_set_blocked (playsink, FALSE);
4424 audio_set_blocked (playsink, FALSE);
4425 text_set_blocked (playsink, FALSE);
4426 GST_PLAY_SINK_UNLOCK (playsink);
4428 case GST_STATE_CHANGE_READY_TO_NULL:
4429 if (playsink->audiochain && playsink->audiochain->sink_volume) {
4430 /* remove our links to the mute and volume elements when they were
4431 * provided by a sink */
4432 disconnect_chain (playsink->audiochain, playsink);
4433 playsink->audiochain->volume = NULL;
4434 playsink->audiochain->mute = NULL;
4437 if (playsink->audiochain && playsink->audiochain->ts_offset) {
4438 gst_object_unref (playsink->audiochain->ts_offset);
4439 playsink->audiochain->ts_offset = NULL;
4442 if (playsink->videochain && playsink->videochain->ts_offset) {
4443 gst_object_unref (playsink->videochain->ts_offset);
4444 playsink->videochain->ts_offset = NULL;
4447 GST_OBJECT_LOCK (playsink);
4448 if (playsink->overlay_element)
4449 gst_object_unref (playsink->overlay_element);
4450 playsink->overlay_element = NULL;
4452 if (playsink->colorbalance_element) {
4453 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
4454 G_CALLBACK (colorbalance_value_changed_cb), playsink);
4455 gst_object_unref (playsink->colorbalance_element);
4457 playsink->colorbalance_element = NULL;
4458 GST_OBJECT_UNLOCK (playsink);
4460 ret = GST_STATE_CHANGE_SUCCESS;
4463 /* all other state changes return SUCCESS by default, this value can be
4464 * overridden by the result of the children */
4465 ret = GST_STATE_CHANGE_SUCCESS;
4469 /* do the state change of the children */
4471 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->change_state (element,
4473 /* now look at the result of our children and adjust the return value */
4475 case GST_STATE_CHANGE_FAILURE:
4476 /* failure, we stop */
4477 goto activate_failed;
4478 case GST_STATE_CHANGE_NO_PREROLL:
4479 /* some child returned NO_PREROLL. This is strange but we never know. We
4480 * commit our async state change (if any) and return the NO_PREROLL */
4481 do_async_done (playsink);
4484 case GST_STATE_CHANGE_ASYNC:
4485 /* some child was async, return this */
4489 /* return our previously configured return value */
4493 switch (transition) {
4494 case GST_STATE_CHANGE_READY_TO_PAUSED:
4496 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
4497 /* FIXME Release audio device when we implement that */
4498 playsink->need_async_start = TRUE;
4500 case GST_STATE_CHANGE_PAUSED_TO_READY:{
4501 if (playsink->video_sinkpad_stream_synchronizer) {
4502 gst_element_release_request_pad (GST_ELEMENT_CAST
4503 (playsink->stream_synchronizer),
4504 playsink->video_sinkpad_stream_synchronizer);
4505 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
4506 playsink->video_sinkpad_stream_synchronizer = NULL;
4507 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
4508 playsink->video_srcpad_stream_synchronizer = NULL;
4510 if (playsink->audio_sinkpad_stream_synchronizer) {
4511 gst_element_release_request_pad (GST_ELEMENT_CAST
4512 (playsink->stream_synchronizer),
4513 playsink->audio_sinkpad_stream_synchronizer);
4514 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
4515 playsink->audio_sinkpad_stream_synchronizer = NULL;
4516 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
4517 playsink->audio_srcpad_stream_synchronizer = NULL;
4519 if (playsink->text_sinkpad_stream_synchronizer) {
4520 gst_element_release_request_pad (GST_ELEMENT_CAST
4521 (playsink->stream_synchronizer),
4522 playsink->text_sinkpad_stream_synchronizer);
4523 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
4524 playsink->text_sinkpad_stream_synchronizer = NULL;
4525 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
4526 playsink->text_srcpad_stream_synchronizer = NULL;
4530 case GST_STATE_CHANGE_READY_TO_NULL:
4531 /* remove sinks we added */
4532 if (playsink->videodeinterlacechain) {
4533 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
4535 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
4537 if (playsink->videochain) {
4538 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
4539 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
4541 if (playsink->audiochain) {
4542 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
4543 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
4545 if (playsink->vischain) {
4546 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
4547 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
4549 if (playsink->textchain) {
4550 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
4551 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
4553 do_async_done (playsink);
4554 /* when going to READY, keep elements around as long as possible,
4555 * so they may be re-used faster next time/url around.
4556 * when really going to NULL, clean up everything completely. */
4557 if (transition == GST_STATE_CHANGE_READY_TO_NULL) {
4559 /* Unparent the sinks to allow reuse */
4560 if (playsink->videochain && playsink->videochain->sink)
4561 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
4562 playsink->videochain->sink);
4563 if (playsink->audiochain && playsink->audiochain->sink)
4564 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
4565 playsink->audiochain->sink);
4566 if (playsink->textchain && playsink->textchain->sink)
4567 gst_bin_remove (GST_BIN_CAST (playsink->textchain->chain.bin),
4568 playsink->textchain->sink);
4569 if (playsink->audio_sink != NULL)
4570 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
4571 if (playsink->video_sink != NULL)
4572 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
4573 if (playsink->visualisation != NULL)
4574 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
4575 if (playsink->text_sink != NULL)
4576 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
4577 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
4578 playsink->videodeinterlacechain = NULL;
4579 free_chain ((GstPlayChain *) playsink->videochain);
4580 playsink->videochain = NULL;
4581 free_chain ((GstPlayChain *) playsink->audiochain);
4582 playsink->audiochain = NULL;
4583 free_chain ((GstPlayChain *) playsink->vischain);
4584 playsink->vischain = NULL;
4585 free_chain ((GstPlayChain *) playsink->textchain);
4586 playsink->textchain = NULL;
4596 GST_DEBUG_OBJECT (element,
4597 "element failed to change states -- activation problem?");
4598 return GST_STATE_CHANGE_FAILURE;
4603 gst_play_sink_set_property (GObject * object, guint prop_id,
4604 const GValue * value, GParamSpec * spec)
4606 GstPlaySink *playsink = GST_PLAY_SINK (object);
4609 gst_play_sink_set_flags (playsink, g_value_get_flags (value));
4612 gst_play_sink_set_volume (playsink, g_value_get_double (value));
4615 gst_play_sink_set_mute (playsink, g_value_get_boolean (value));
4617 case PROP_FONT_DESC:
4618 gst_play_sink_set_font_desc (playsink, g_value_get_string (value));
4620 case PROP_SUBTITLE_ENCODING:
4621 gst_play_sink_set_subtitle_encoding (playsink,
4622 g_value_get_string (value));
4624 case PROP_VIS_PLUGIN:
4625 gst_play_sink_set_vis_plugin (playsink, g_value_get_object (value));
4627 case PROP_AV_OFFSET:
4628 gst_play_sink_set_av_offset (playsink, g_value_get_int64 (value));
4630 case PROP_VIDEO_SINK:
4631 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_VIDEO,
4632 g_value_get_object (value));
4634 case PROP_AUDIO_SINK:
4635 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_AUDIO,
4636 g_value_get_object (value));
4638 case PROP_TEXT_SINK:
4639 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_TEXT,
4640 g_value_get_object (value));
4642 case PROP_SEND_EVENT_MODE:
4643 playsink->send_event_mode = g_value_get_enum (value);
4645 case PROP_FORCE_ASPECT_RATIO:{
4646 GstPlayVideoChain *chain;
4649 playsink->force_aspect_ratio = g_value_get_boolean (value);
4651 GST_PLAY_SINK_LOCK (playsink);
4652 if (playsink->videochain) {
4653 chain = (GstPlayVideoChain *) playsink->videochain;
4657 gst_play_sink_find_property_sinks (playsink, chain->sink,
4658 "force-aspect-ratio", G_TYPE_BOOLEAN);
4661 g_object_set (elem, "force-aspect-ratio",
4662 playsink->force_aspect_ratio, NULL);
4665 GST_PLAY_SINK_UNLOCK (playsink);
4669 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
4675 gst_play_sink_get_property (GObject * object, guint prop_id,
4676 GValue * value, GParamSpec * spec)
4678 GstPlaySink *playsink = GST_PLAY_SINK (object);
4681 g_value_set_flags (value, gst_play_sink_get_flags (playsink));
4684 g_value_set_double (value, gst_play_sink_get_volume (playsink));
4687 g_value_set_boolean (value, gst_play_sink_get_mute (playsink));
4689 case PROP_FONT_DESC:
4690 g_value_take_string (value, gst_play_sink_get_font_desc (playsink));
4692 case PROP_SUBTITLE_ENCODING:
4693 g_value_take_string (value,
4694 gst_play_sink_get_subtitle_encoding (playsink));
4696 case PROP_VIS_PLUGIN:
4697 g_value_take_object (value, gst_play_sink_get_vis_plugin (playsink));
4700 gst_value_take_sample (value, gst_play_sink_get_last_sample (playsink));
4702 case PROP_AV_OFFSET:
4703 g_value_set_int64 (value, gst_play_sink_get_av_offset (playsink));
4705 case PROP_VIDEO_SINK:
4706 g_value_take_object (value, gst_play_sink_get_sink (playsink,
4707 GST_PLAY_SINK_TYPE_VIDEO));
4709 case PROP_AUDIO_SINK:
4710 g_value_take_object (value, gst_play_sink_get_sink (playsink,
4711 GST_PLAY_SINK_TYPE_AUDIO));
4713 case PROP_TEXT_SINK:
4714 g_value_take_object (value, gst_play_sink_get_sink (playsink,
4715 GST_PLAY_SINK_TYPE_TEXT));
4717 case PROP_SEND_EVENT_MODE:
4718 g_value_set_enum (value, playsink->send_event_mode);
4720 case PROP_FORCE_ASPECT_RATIO:
4721 g_value_set_boolean (value, playsink->force_aspect_ratio);
4724 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
4730 gst_play_sink_overlay_expose (GstVideoOverlay * overlay)
4732 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4733 GstVideoOverlay *overlay_element;
4735 GST_OBJECT_LOCK (playsink);
4736 if (playsink->overlay_element)
4738 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4740 overlay_element = NULL;
4741 GST_OBJECT_UNLOCK (playsink);
4743 if (overlay_element) {
4744 gst_video_overlay_expose (overlay_element);
4745 gst_object_unref (overlay_element);
4750 gst_play_sink_overlay_handle_events (GstVideoOverlay * overlay,
4751 gboolean handle_events)
4753 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4754 GstVideoOverlay *overlay_element;
4756 GST_OBJECT_LOCK (playsink);
4757 if (playsink->overlay_element)
4759 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4761 overlay_element = NULL;
4762 GST_OBJECT_UNLOCK (playsink);
4764 playsink->overlay_handle_events_set = TRUE;
4765 playsink->overlay_handle_events = handle_events;
4767 if (overlay_element) {
4768 gst_video_overlay_handle_events (overlay_element, handle_events);
4769 gst_object_unref (overlay_element);
4774 gst_play_sink_overlay_set_render_rectangle (GstVideoOverlay * overlay, gint x,
4775 gint y, gint width, gint height)
4777 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4778 GstVideoOverlay *overlay_element;
4780 GST_OBJECT_LOCK (playsink);
4781 if (playsink->overlay_element)
4783 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4785 overlay_element = NULL;
4786 GST_OBJECT_UNLOCK (playsink);
4788 playsink->overlay_render_rectangle_set = TRUE;
4789 playsink->overlay_x = x;
4790 playsink->overlay_y = y;
4791 playsink->overlay_width = width;
4792 playsink->overlay_height = height;
4794 if (overlay_element) {
4795 gst_video_overlay_set_render_rectangle (overlay_element, x, y, width,
4797 gst_object_unref (overlay_element);
4802 gst_play_sink_overlay_set_window_handle (GstVideoOverlay * overlay,
4805 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4806 GstVideoOverlay *overlay_element;
4808 GST_OBJECT_LOCK (playsink);
4809 if (playsink->overlay_element)
4811 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4813 overlay_element = NULL;
4814 GST_OBJECT_UNLOCK (playsink);
4816 playsink->overlay_handle_set = TRUE;
4817 playsink->overlay_handle = handle;
4819 if (overlay_element) {
4820 gst_video_overlay_set_window_handle (overlay_element, handle);
4821 gst_object_unref (overlay_element);
4826 gst_play_sink_overlay_init (gpointer g_iface, gpointer g_iface_data)
4828 GstVideoOverlayInterface *iface = (GstVideoOverlayInterface *) g_iface;
4829 iface->expose = gst_play_sink_overlay_expose;
4830 iface->handle_events = gst_play_sink_overlay_handle_events;
4831 iface->set_render_rectangle = gst_play_sink_overlay_set_render_rectangle;
4832 iface->set_window_handle = gst_play_sink_overlay_set_window_handle;
4836 gst_play_sink_navigation_send_event (GstNavigation * navigation,
4837 GstStructure * structure)
4839 GstPlaySink *playsink = GST_PLAY_SINK (navigation);
4842 GST_PLAY_SINK_LOCK (playsink);
4843 if (playsink->videochain && playsink->videochain->chain.bin)
4844 bin = GST_BIN (gst_object_ref (playsink->videochain->chain.bin));
4845 GST_PLAY_SINK_UNLOCK (playsink);
4848 GstElement *nav = gst_bin_get_by_interface (bin, GST_TYPE_NAVIGATION);
4851 gst_navigation_send_event (GST_NAVIGATION (nav), structure);
4853 gst_object_unref (nav);
4855 GstEvent *event = gst_event_new_navigation (structure);
4857 gst_element_send_event (GST_ELEMENT (bin), event);
4860 gst_object_unref (bin);
4864 gst_structure_free (structure);
4868 gst_play_sink_navigation_init (gpointer g_iface, gpointer g_iface_data)
4870 GstNavigationInterface *iface = (GstNavigationInterface *) g_iface;
4872 iface->send_event = gst_play_sink_navigation_send_event;
4875 static const GList *
4876 gst_play_sink_colorbalance_list_channels (GstColorBalance * balance)
4878 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4880 return playsink->colorbalance_channels;
4884 gst_play_sink_colorbalance_set_value (GstColorBalance * balance,
4885 GstColorBalanceChannel * proxy, gint value)
4887 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4890 GstColorBalance *balance_element = NULL;
4892 GST_OBJECT_LOCK (playsink);
4893 if (playsink->colorbalance_element)
4895 GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
4896 GST_OBJECT_UNLOCK (playsink);
4898 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
4899 GstColorBalanceChannel *proxy_tmp = l->data;
4902 if (proxy_tmp != proxy)
4905 playsink->colorbalance_values[i] = value;
4907 if (balance_element) {
4908 GstColorBalanceChannel *channel = NULL;
4909 const GList *channels, *k;
4911 channels = gst_color_balance_list_channels (balance_element);
4912 for (k = channels; k; k = k->next) {
4913 GstColorBalanceChannel *tmp = l->data;
4915 if (g_strrstr (tmp->label, proxy->label)) {
4923 /* Convert to [0, 1] range */
4926 (gdouble) proxy->min_value) / ((gdouble) proxy->max_value -
4927 (gdouble) proxy->min_value);
4928 /* Convert to channel range */
4930 channel->min_value + new_val * ((gdouble) channel->max_value -
4931 (gdouble) channel->min_value);
4933 gst_color_balance_set_value (balance_element, channel,
4934 (gint) (new_val + 0.5));
4936 gst_object_unref (balance_element);
4939 gst_color_balance_value_changed (balance, proxy, value);
4945 gst_play_sink_colorbalance_get_value (GstColorBalance * balance,
4946 GstColorBalanceChannel * proxy)
4948 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4952 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
4953 GstColorBalanceChannel *proxy_tmp = l->data;
4955 if (proxy_tmp != proxy)
4958 return playsink->colorbalance_values[i];
4961 g_return_val_if_reached (0);
4964 static GstColorBalanceType
4965 gst_play_sink_colorbalance_get_balance_type (GstColorBalance * balance)
4967 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4968 GstColorBalance *balance_element = NULL;
4969 GstColorBalanceType t = GST_COLOR_BALANCE_SOFTWARE;
4971 GST_OBJECT_LOCK (playsink);
4972 if (playsink->colorbalance_element)
4974 GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
4975 GST_OBJECT_UNLOCK (playsink);
4977 if (balance_element) {
4978 t = gst_color_balance_get_balance_type (balance_element);
4979 gst_object_unref (balance_element);
4986 gst_play_sink_colorbalance_init (gpointer g_iface, gpointer g_iface_data)
4988 GstColorBalanceInterface *iface = (GstColorBalanceInterface *) g_iface;
4990 iface->list_channels = gst_play_sink_colorbalance_list_channels;
4991 iface->set_value = gst_play_sink_colorbalance_set_value;
4992 iface->get_value = gst_play_sink_colorbalance_get_value;
4993 iface->get_balance_type = gst_play_sink_colorbalance_get_balance_type;
4997 gst_play_sink_plugin_init (GstPlugin * plugin)
4999 GST_DEBUG_CATEGORY_INIT (gst_play_sink_debug, "playsink", 0, "play bin");
5000 return gst_element_register (plugin, "playsink", GST_RANK_NONE,
5001 GST_TYPE_PLAY_SINK);