2 * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
3 * Copyright (C) <2011> Sebastian Dröge <sebastian.droege@collabora.co.uk>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
28 #include <gst/gst-i18n-plugin.h>
29 #include <gst/pbutils/pbutils.h>
30 #include <gst/video/video.h>
31 #include <gst/audio/streamvolume.h>
32 #include <gst/video/colorbalance.h>
33 #include <gst/video/videooverlay.h>
34 #include <gst/video/navigation.h>
36 #include "gstplaysink.h"
37 #include "gststreamsynchronizer.h"
38 #include "gstplaysinkvideoconvert.h"
39 #include "gstplaysinkaudioconvert.h"
41 GST_DEBUG_CATEGORY_STATIC (gst_play_sink_debug);
42 #define GST_CAT_DEFAULT gst_play_sink_debug
44 #define VOLUME_MAX_DOUBLE 10.0
46 #define DEFAULT_FLAGS GST_PLAY_FLAG_AUDIO | GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_TEXT | \
47 GST_PLAY_FLAG_SOFT_VOLUME | GST_PLAY_FLAG_SOFT_COLORBALANCE
49 #define GST_PLAY_CHAIN(c) ((GstPlayChain *)(c))
53 * GstPlaySinkSendEventMode:
54 * @MODE_DEFAULT: default GstBin's send_event handling
55 * @MODE_FIRST: send event only to the first sink that return true
57 * Send event handling to use
63 } GstPlaySinkSendEventMode;
66 #define GST_TYPE_PLAY_SINK_SEND_EVENT_MODE (gst_play_sink_send_event_mode_get_type ())
68 gst_play_sink_send_event_mode_get_type (void)
70 static GType gtype = 0;
73 static const GEnumValue values[] = {
74 {MODE_DEFAULT, "Default GstBin's send_event handling (default)",
76 {MODE_FIRST, "Sends the event to sinks until the first one handles it",
81 gtype = g_enum_register_static ("GstPlaySinkSendEventMode", values);
86 /* holds the common data fields for the audio and video pipelines. We keep them
87 * in a structure to more easily have all the info available. */
90 GstPlaySink *playsink;
103 GstElement *volume; /* element with the volume property */
104 gboolean sink_volume; /* if the volume was provided by the sink */
105 gulong notify_volume_id;
106 GstElement *mute; /* element with the mute property */
107 gulong notify_mute_id;
109 GstElement *ts_offset;
115 GstPad *sinkpad, *srcpad;
117 GstElement *deinterlace;
118 } GstPlayVideoDeinterlaceChain;
128 GstElement *ts_offset;
137 GstElement *resample;
138 GstPad *blockpad; /* srcpad of queue, used for blocking the vis */
139 GstPad *vispeerpad; /* srcpad of resample, used for unlinking the vis */
140 GstPad *vissinkpad; /* visualisation sinkpad, */
142 GstPad *vissrcpad; /* visualisation srcpad, */
143 GstPad *srcpad; /* outgoing srcpad, used to connect to the next
152 GstElement *identity;
154 GstPad *videosinkpad;
156 GstPad *srcpad; /* outgoing srcpad, used to connect to the next
158 GstElement *sink; /* custom sink to receive subtitle buffers */
161 #define GST_PLAY_SINK_GET_LOCK(playsink) (&((GstPlaySink *)playsink)->lock)
162 #define GST_PLAY_SINK_LOCK(playsink) G_STMT_START { \
163 GST_LOG_OBJECT (playsink, "locking from thread %p", g_thread_self ()); \
164 g_rec_mutex_lock (GST_PLAY_SINK_GET_LOCK (playsink)); \
165 GST_LOG_OBJECT (playsink, "locked from thread %p", g_thread_self ()); \
167 #define GST_PLAY_SINK_UNLOCK(playsink) G_STMT_START { \
168 GST_LOG_OBJECT (playsink, "unlocking from thread %p", g_thread_self ()); \
169 g_rec_mutex_unlock (GST_PLAY_SINK_GET_LOCK (playsink)); \
172 #define PENDING_FLAG_SET(playsink, flagtype) \
173 ((playsink->pending_blocked_pads) |= (1 << flagtype))
174 #define PENDING_FLAG_UNSET(playsink, flagtype) \
175 ((playsink->pending_blocked_pads) &= ~(1 << flagtype))
176 #define PENDING_FLAG_IS_SET(playsink, flagtype) \
177 ((playsink->pending_blocked_pads) & (1 << flagtype))
178 #define PENDING_VIDEO_BLOCK(playsink) \
179 ((playsink->pending_blocked_pads) & (1 << GST_PLAY_SINK_TYPE_VIDEO_RAW | 1 << GST_PLAY_SINK_TYPE_VIDEO))
180 #define PENDING_AUDIO_BLOCK(playsink) \
181 ((playsink->pending_blocked_pads) & (1 << GST_PLAY_SINK_TYPE_AUDIO_RAW | 1 << GST_PLAY_SINK_TYPE_AUDIO))
182 #define PENDING_TEXT_BLOCK(playsink) \
183 PENDING_FLAG_IS_SET(playsink, GST_PLAY_SINK_TYPE_TEXT)
191 gboolean async_pending;
192 gboolean need_async_start;
196 GstStreamSynchronizer *stream_synchronizer;
199 GstPlayAudioChain *audiochain;
200 GstPlayVideoDeinterlaceChain *videodeinterlacechain;
201 GstPlayVideoChain *videochain;
202 GstPlayVisChain *vischain;
203 GstPlayTextChain *textchain;
207 gboolean audio_pad_raw;
208 gboolean audio_pad_blocked;
209 GstPad *audio_srcpad_stream_synchronizer;
210 GstPad *audio_sinkpad_stream_synchronizer;
211 gulong audio_block_id;
212 gulong audio_notify_caps_id;
214 GstElement *audio_tee;
215 GstPad *audio_tee_sink;
216 GstPad *audio_tee_asrc;
217 GstPad *audio_tee_vissrc;
220 gboolean video_pad_raw;
221 gboolean video_pad_blocked;
222 GstPad *video_srcpad_stream_synchronizer;
223 GstPad *video_sinkpad_stream_synchronizer;
224 gulong video_block_id;
225 gulong video_notify_caps_id;
228 gboolean text_pad_blocked;
229 GstPad *text_srcpad_stream_synchronizer;
230 GstPad *text_sinkpad_stream_synchronizer;
231 gulong text_block_id;
233 guint32 pending_blocked_pads;
236 GstElement *audio_sink;
237 GstElement *video_sink;
238 GstElement *visualisation;
239 GstElement *text_sink;
242 gchar *font_desc; /* font description */
243 gchar *subtitle_encoding; /* subtitle encoding */
244 guint connection_speed; /* connection speed in bits/sec (0 = unknown) */
246 gboolean volume_changed; /* volume/mute changed while no audiochain */
247 gboolean mute_changed; /* ... has been created yet */
249 GstPlaySinkSendEventMode send_event_mode;
250 gboolean force_aspect_ratio;
252 /* videooverlay proxy interface */
253 GstVideoOverlay *overlay_element; /* protected with LOCK */
254 gboolean overlay_handle_set;
255 guintptr overlay_handle;
256 gboolean overlay_render_rectangle_set;
257 gint overlay_x, overlay_y, overlay_width, overlay_height;
258 gboolean overlay_handle_events_set;
259 gboolean overlay_handle_events;
261 /* colorbalance proxy interface */
262 GstColorBalance *colorbalance_element;
263 GList *colorbalance_channels; /* CONTRAST, BRIGHTNESS, HUE, SATURATION */
264 gint colorbalance_values[4];
265 gulong colorbalance_value_changed_id;
267 /* sending audio/video flushes break stream changes when the pipeline
268 * is paused and played again in 0.10 */
270 gboolean video_custom_flush_finished;
271 gboolean video_ignore_wrong_state;
272 gboolean video_pending_flush;
274 gboolean audio_custom_flush_finished;
275 gboolean audio_ignore_wrong_state;
276 gboolean audio_pending_flush;
279 gboolean text_custom_flush_finished;
280 gboolean text_ignore_wrong_state;
281 gboolean text_pending_flush;
284 struct _GstPlaySinkClass
286 GstBinClass parent_class;
288 gboolean (*reconfigure) (GstPlaySink * playsink);
290 GstSample *(*convert_sample) (GstPlaySink * playsink, GstCaps * caps);
294 static GstStaticPadTemplate audiotemplate =
295 GST_STATIC_PAD_TEMPLATE ("audio_sink",
298 GST_STATIC_CAPS_ANY);
299 static GstStaticPadTemplate videotemplate =
300 GST_STATIC_PAD_TEMPLATE ("video_sink",
303 GST_STATIC_CAPS_ANY);
304 static GstStaticPadTemplate texttemplate = GST_STATIC_PAD_TEMPLATE ("text_sink",
307 GST_STATIC_CAPS_ANY);
309 /* FIXME 0.11: Remove */
310 static GstStaticPadTemplate audiorawtemplate =
311 GST_STATIC_PAD_TEMPLATE ("audio_raw_sink",
314 GST_STATIC_CAPS_ANY);
315 static GstStaticPadTemplate videorawtemplate =
316 GST_STATIC_PAD_TEMPLATE ("video_raw_sink",
319 GST_STATIC_CAPS_ANY);
330 PROP_SUBTITLE_ENCODING,
337 PROP_SEND_EVENT_MODE,
338 PROP_FORCE_ASPECT_RATIO,
348 static void gst_play_sink_dispose (GObject * object);
349 static void gst_play_sink_finalize (GObject * object);
350 static void gst_play_sink_set_property (GObject * object, guint prop_id,
351 const GValue * value, GParamSpec * spec);
352 static void gst_play_sink_get_property (GObject * object, guint prop_id,
353 GValue * value, GParamSpec * spec);
355 static GstPad *gst_play_sink_request_new_pad (GstElement * element,
356 GstPadTemplate * templ, const gchar * name, const GstCaps * caps);
357 static void gst_play_sink_release_request_pad (GstElement * element,
359 static gboolean gst_play_sink_send_event (GstElement * element,
361 static GstStateChangeReturn gst_play_sink_change_state (GstElement * element,
362 GstStateChange transition);
364 static void gst_play_sink_handle_message (GstBin * bin, GstMessage * message);
366 /* sending audio/video flushes break stream changes when the pipeline
367 * is paused and played again in 0.10 */
369 static gboolean gst_play_sink_video_sink_event (GstPad * pad, GstEvent * event);
370 static GstFlowReturn gst_play_sink_video_sink_chain (GstPad * pad,
372 static gboolean gst_play_sink_audio_sink_event (GstPad * pad, GstEvent * event);
373 static GstFlowReturn gst_play_sink_audio_sink_chain (GstPad * pad,
376 static gboolean gst_play_sink_text_sink_event (GstPad * pad, GstObject * parent,
378 static GstFlowReturn gst_play_sink_text_sink_chain (GstPad * pad,
379 GstObject * parent, GstBuffer * buffer);
381 static void notify_volume_cb (GObject * object, GParamSpec * pspec,
382 GstPlaySink * playsink);
383 static void notify_mute_cb (GObject * object, GParamSpec * pspec,
384 GstPlaySink * playsink);
386 static void update_av_offset (GstPlaySink * playsink);
388 static gboolean gst_play_sink_do_reconfigure (GstPlaySink * playsink);
390 static GQuark _playsink_reset_segment_event_marker_id = 0;
392 /* static guint gst_play_sink_signals[LAST_SIGNAL] = { 0 }; */
394 static void gst_play_sink_overlay_init (gpointer g_iface,
395 gpointer g_iface_data);
396 static void gst_play_sink_navigation_init (gpointer g_iface,
397 gpointer g_iface_data);
398 static void gst_play_sink_colorbalance_init (gpointer g_iface,
399 gpointer g_iface_data);
402 _do_init (GType type)
404 static const GInterfaceInfo svol_info = {
407 static const GInterfaceInfo ov_info = {
408 gst_play_sink_overlay_init,
411 static const GInterfaceInfo nav_info = {
412 gst_play_sink_navigation_init,
415 static const GInterfaceInfo col_info = {
416 gst_play_sink_colorbalance_init,
420 g_type_add_interface_static (type, GST_TYPE_STREAM_VOLUME, &svol_info);
421 g_type_add_interface_static (type, GST_TYPE_VIDEO_OVERLAY, &ov_info);
422 g_type_add_interface_static (type, GST_TYPE_NAVIGATION, &nav_info);
423 g_type_add_interface_static (type, GST_TYPE_COLOR_BALANCE, &col_info);
426 G_DEFINE_TYPE_WITH_CODE (GstPlaySink, gst_play_sink, GST_TYPE_BIN,
427 _do_init (g_define_type_id));
430 gst_play_sink_class_init (GstPlaySinkClass * klass)
432 GObjectClass *gobject_klass;
433 GstElementClass *gstelement_klass;
434 GstBinClass *gstbin_klass;
436 gobject_klass = (GObjectClass *) klass;
437 gstelement_klass = (GstElementClass *) klass;
438 gstbin_klass = (GstBinClass *) klass;
440 gobject_klass->dispose = gst_play_sink_dispose;
441 gobject_klass->finalize = gst_play_sink_finalize;
442 gobject_klass->set_property = gst_play_sink_set_property;
443 gobject_klass->get_property = gst_play_sink_get_property;
449 * Control the behaviour of playsink.
451 g_object_class_install_property (gobject_klass, PROP_FLAGS,
452 g_param_spec_flags ("flags", "Flags", "Flags to control behaviour",
453 GST_TYPE_PLAY_FLAGS, DEFAULT_FLAGS,
454 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
457 * GstPlaySink:volume:
459 * Get or set the current audio stream volume. 1.0 means 100%,
460 * 0.0 means mute. This uses a linear volume scale.
463 g_object_class_install_property (gobject_klass, PROP_VOLUME,
464 g_param_spec_double ("volume", "Volume", "The audio volume, 1.0=100%",
465 0.0, VOLUME_MAX_DOUBLE, 1.0,
466 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
467 g_object_class_install_property (gobject_klass, PROP_MUTE,
468 g_param_spec_boolean ("mute", "Mute",
469 "Mute the audio channel without changing the volume", FALSE,
470 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
471 g_object_class_install_property (gobject_klass, PROP_FONT_DESC,
472 g_param_spec_string ("subtitle-font-desc",
473 "Subtitle font description",
474 "Pango font description of font "
475 "to be used for subtitle rendering", NULL,
476 G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
477 g_object_class_install_property (gobject_klass, PROP_SUBTITLE_ENCODING,
478 g_param_spec_string ("subtitle-encoding", "subtitle encoding",
479 "Encoding to assume if input subtitles are not in UTF-8 encoding. "
480 "If not set, the GST_SUBTITLE_ENCODING environment variable will "
481 "be checked for an encoding to use. If that is not set either, "
482 "ISO-8859-15 will be assumed.", NULL,
483 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
484 g_object_class_install_property (gobject_klass, PROP_VIS_PLUGIN,
485 g_param_spec_object ("vis-plugin", "Vis plugin",
486 "the visualization element to use (NULL = default)",
487 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
489 * GstPlaySink:sample:
491 * Get the currently rendered or prerolled sample in the video sink.
492 * The #GstCaps in the sample will describe the format of the buffer.
494 g_object_class_install_property (gobject_klass, PROP_SAMPLE,
495 g_param_spec_boxed ("sample", "Sample",
496 "The last sample (NULL = no video available)",
497 GST_TYPE_SAMPLE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
499 * GstPlaySink:av-offset:
501 * Control the synchronisation offset between the audio and video streams.
502 * Positive values make the audio ahead of the video and negative values make
503 * the audio go behind the video.
507 g_object_class_install_property (gobject_klass, PROP_AV_OFFSET,
508 g_param_spec_int64 ("av-offset", "AV Offset",
509 "The synchronisation offset between audio and video in nanoseconds",
510 G_MININT64, G_MAXINT64, 0,
511 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
514 * GstPlaySink:video-sink:
516 * Set the used video sink element. NULL will use the default sink. playsink
517 * must be in %GST_STATE_NULL
521 g_object_class_install_property (gobject_klass, PROP_VIDEO_SINK,
522 g_param_spec_object ("video-sink", "Video Sink",
523 "the video output element to use (NULL = default sink)",
524 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
526 * GstPlaySink:audio-sink:
528 * Set the used audio sink element. NULL will use the default sink. playsink
529 * must be in %GST_STATE_NULL
533 g_object_class_install_property (gobject_klass, PROP_AUDIO_SINK,
534 g_param_spec_object ("audio-sink", "Audio Sink",
535 "the audio output element to use (NULL = default sink)",
536 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
539 * GstPlaySink:text-sink:
541 * Set the used text sink element. NULL will use the default sink. playsink
542 * must be in %GST_STATE_NULL
546 g_object_class_install_property (gobject_klass, PROP_TEXT_SINK,
547 g_param_spec_object ("text-sink", "Text sink",
548 "the text output element to use (NULL = default subtitleoverlay)",
549 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
552 * GstPlaySink::send-event-mode:
554 * Sets the handling method used for events received from send_event
555 * function. The default is %MODE_DEFAULT, that uses %GstBin's default
556 * handling (push the event to all internal sinks).
560 g_object_class_install_property (gobject_klass, PROP_SEND_EVENT_MODE,
561 g_param_spec_enum ("send-event-mode", "Send event mode",
562 "How to send events received in send_event function",
563 GST_TYPE_PLAY_SINK_SEND_EVENT_MODE, MODE_DEFAULT,
564 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
567 * GstPlaySink::force-aspect-ratio:
569 * Requests the video sink to enforce the video display aspect ratio.
573 g_object_class_install_property (gobject_klass, PROP_FORCE_ASPECT_RATIO,
574 g_param_spec_boolean ("force-aspect-ratio", "Force Aspect Ratio",
575 "When enabled, scaling will respect original aspect ratio", TRUE,
576 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
578 g_signal_new ("reconfigure", G_TYPE_FROM_CLASS (klass),
579 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstPlaySinkClass,
580 reconfigure), NULL, NULL, g_cclosure_marshal_generic, G_TYPE_BOOLEAN,
583 * GstPlaySink::convert-sample
584 * @playsink: a #GstPlaySink
585 * @caps: the target format of the sample
587 * Action signal to retrieve the currently playing video sample in the format
588 * specified by @caps.
589 * If @caps is %NULL, no conversion will be performed and this function is
590 * equivalent to the #GstPlaySink::sample property.
592 * Returns: a #GstSample of the current video sample converted to #caps.
593 * The caps in the sample will describe the final layout of the buffer data.
594 * %NULL is returned when no current sample can be retrieved or when the
597 g_signal_new ("convert-sample", G_TYPE_FROM_CLASS (klass),
598 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
599 G_STRUCT_OFFSET (GstPlaySinkClass, convert_sample), NULL, NULL,
600 g_cclosure_marshal_generic, GST_TYPE_SAMPLE, 1, GST_TYPE_CAPS);
602 gst_element_class_add_pad_template (gstelement_klass,
603 gst_static_pad_template_get (&audiorawtemplate));
604 gst_element_class_add_pad_template (gstelement_klass,
605 gst_static_pad_template_get (&audiotemplate));
606 gst_element_class_add_pad_template (gstelement_klass,
607 gst_static_pad_template_get (&videorawtemplate));
608 gst_element_class_add_pad_template (gstelement_klass,
609 gst_static_pad_template_get (&videotemplate));
610 gst_element_class_add_pad_template (gstelement_klass,
611 gst_static_pad_template_get (&texttemplate));
612 gst_element_class_set_static_metadata (gstelement_klass, "Player Sink",
614 "Convenience sink for multiple streams",
615 "Wim Taymans <wim.taymans@gmail.com>");
617 gstelement_klass->change_state =
618 GST_DEBUG_FUNCPTR (gst_play_sink_change_state);
619 gstelement_klass->send_event = GST_DEBUG_FUNCPTR (gst_play_sink_send_event);
620 gstelement_klass->request_new_pad =
621 GST_DEBUG_FUNCPTR (gst_play_sink_request_new_pad);
622 gstelement_klass->release_pad =
623 GST_DEBUG_FUNCPTR (gst_play_sink_release_request_pad);
625 gstbin_klass->handle_message =
626 GST_DEBUG_FUNCPTR (gst_play_sink_handle_message);
628 klass->reconfigure = GST_DEBUG_FUNCPTR (gst_play_sink_reconfigure);
629 klass->convert_sample = GST_DEBUG_FUNCPTR (gst_play_sink_convert_sample);
631 _playsink_reset_segment_event_marker_id =
632 g_quark_from_static_string ("gst-playsink-reset-segment-event-marker");
634 g_type_class_ref (GST_TYPE_STREAM_SYNCHRONIZER);
635 g_type_class_ref (GST_TYPE_COLOR_BALANCE_CHANNEL);
639 gst_play_sink_init (GstPlaySink * playsink)
641 GstColorBalanceChannel *channel;
644 playsink->video_sink = NULL;
645 playsink->audio_sink = NULL;
646 playsink->visualisation = NULL;
647 playsink->text_sink = NULL;
648 playsink->volume = 1.0;
649 playsink->font_desc = NULL;
650 playsink->subtitle_encoding = NULL;
651 playsink->flags = DEFAULT_FLAGS;
652 playsink->send_event_mode = MODE_DEFAULT;
653 playsink->force_aspect_ratio = TRUE;
655 playsink->stream_synchronizer =
656 g_object_new (GST_TYPE_STREAM_SYNCHRONIZER, NULL);
657 gst_bin_add (GST_BIN_CAST (playsink),
658 GST_ELEMENT_CAST (playsink->stream_synchronizer));
660 g_rec_mutex_init (&playsink->lock);
661 GST_OBJECT_FLAG_SET (playsink, GST_ELEMENT_FLAG_SINK);
664 GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
666 channel->label = g_strdup ("CONTRAST");
667 channel->min_value = -1000;
668 channel->max_value = 1000;
669 playsink->colorbalance_channels =
670 g_list_append (playsink->colorbalance_channels, channel);
671 playsink->colorbalance_values[0] = 0;
674 GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
676 channel->label = g_strdup ("BRIGHTNESS");
677 channel->min_value = -1000;
678 channel->max_value = 1000;
679 playsink->colorbalance_channels =
680 g_list_append (playsink->colorbalance_channels, channel);
681 playsink->colorbalance_values[1] = 0;
684 GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
686 channel->label = g_strdup ("HUE");
687 channel->min_value = -1000;
688 channel->max_value = 1000;
689 playsink->colorbalance_channels =
690 g_list_append (playsink->colorbalance_channels, channel);
691 playsink->colorbalance_values[2] = 0;
694 GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
696 channel->label = g_strdup ("SATURATION");
697 channel->min_value = -1000;
698 channel->max_value = 1000;
699 playsink->colorbalance_channels =
700 g_list_append (playsink->colorbalance_channels, channel);
701 playsink->colorbalance_values[3] = 0;
705 disconnect_audio_chain (GstPlayAudioChain * chain, GstPlaySink * playsink)
708 if (chain->notify_volume_id)
709 g_signal_handler_disconnect (chain->volume, chain->notify_volume_id);
710 if (chain->notify_mute_id)
711 g_signal_handler_disconnect (chain->mute, chain->notify_mute_id);
712 chain->notify_volume_id = chain->notify_mute_id = 0;
717 free_chain (GstPlayChain * chain)
721 gst_object_unref (chain->bin);
727 gst_play_sink_dispose (GObject * object)
729 GstPlaySink *playsink;
731 playsink = GST_PLAY_SINK (object);
733 if (playsink->audio_sink != NULL) {
734 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
735 gst_object_unref (playsink->audio_sink);
736 playsink->audio_sink = NULL;
738 if (playsink->video_sink != NULL) {
739 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
740 gst_object_unref (playsink->video_sink);
741 playsink->video_sink = NULL;
743 if (playsink->visualisation != NULL) {
744 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
745 gst_object_unref (playsink->visualisation);
746 playsink->visualisation = NULL;
748 if (playsink->text_sink != NULL) {
749 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
750 gst_object_unref (playsink->text_sink);
751 playsink->text_sink = NULL;
754 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
755 playsink->videodeinterlacechain = NULL;
756 free_chain ((GstPlayChain *) playsink->videochain);
757 playsink->videochain = NULL;
758 free_chain ((GstPlayChain *) playsink->audiochain);
759 playsink->audiochain = NULL;
760 free_chain ((GstPlayChain *) playsink->vischain);
761 playsink->vischain = NULL;
762 free_chain ((GstPlayChain *) playsink->textchain);
763 playsink->textchain = NULL;
765 if (playsink->audio_tee_sink) {
766 gst_object_unref (playsink->audio_tee_sink);
767 playsink->audio_tee_sink = NULL;
770 if (playsink->audio_tee_vissrc) {
771 gst_element_release_request_pad (playsink->audio_tee,
772 playsink->audio_tee_vissrc);
773 gst_object_unref (playsink->audio_tee_vissrc);
774 playsink->audio_tee_vissrc = NULL;
777 if (playsink->audio_tee_asrc) {
778 gst_element_release_request_pad (playsink->audio_tee,
779 playsink->audio_tee_asrc);
780 gst_object_unref (playsink->audio_tee_asrc);
781 playsink->audio_tee_asrc = NULL;
784 g_free (playsink->font_desc);
785 playsink->font_desc = NULL;
787 g_free (playsink->subtitle_encoding);
788 playsink->subtitle_encoding = NULL;
790 playsink->stream_synchronizer = NULL;
792 g_list_foreach (playsink->colorbalance_channels, (GFunc) gst_object_unref,
794 g_list_free (playsink->colorbalance_channels);
795 playsink->colorbalance_channels = NULL;
797 G_OBJECT_CLASS (gst_play_sink_parent_class)->dispose (object);
801 gst_play_sink_finalize (GObject * object)
803 GstPlaySink *playsink;
805 playsink = GST_PLAY_SINK (object);
807 g_rec_mutex_clear (&playsink->lock);
809 G_OBJECT_CLASS (gst_play_sink_parent_class)->finalize (object);
813 gst_play_sink_set_sink (GstPlaySink * playsink, GstPlaySinkType type,
816 GstElement **elem = NULL, *old = NULL;
818 GST_LOG ("Setting sink %" GST_PTR_FORMAT " as sink type %d", sink, type);
820 GST_PLAY_SINK_LOCK (playsink);
822 case GST_PLAY_SINK_TYPE_AUDIO:
823 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
824 elem = &playsink->audio_sink;
826 case GST_PLAY_SINK_TYPE_VIDEO:
827 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
828 elem = &playsink->video_sink;
830 case GST_PLAY_SINK_TYPE_TEXT:
831 elem = &playsink->text_sink;
839 gst_object_ref_sink (sink);
842 GST_PLAY_SINK_UNLOCK (playsink);
845 /* Set the old sink to NULL if it is not used any longer */
846 if (old != sink && !GST_OBJECT_PARENT (old))
847 gst_element_set_state (old, GST_STATE_NULL);
848 gst_object_unref (old);
853 gst_play_sink_get_sink (GstPlaySink * playsink, GstPlaySinkType type)
855 GstElement *result = NULL;
856 GstElement *elem = NULL, *chainp = NULL;
858 GST_PLAY_SINK_LOCK (playsink);
860 case GST_PLAY_SINK_TYPE_AUDIO:
861 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
863 GstPlayAudioChain *chain;
864 if ((chain = (GstPlayAudioChain *) playsink->audiochain))
865 chainp = chain->sink;
866 elem = playsink->audio_sink;
869 case GST_PLAY_SINK_TYPE_VIDEO:
870 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
872 GstPlayVideoChain *chain;
873 if ((chain = (GstPlayVideoChain *) playsink->videochain))
874 chainp = chain->sink;
875 elem = playsink->video_sink;
878 case GST_PLAY_SINK_TYPE_TEXT:
880 GstPlayTextChain *chain;
881 if ((chain = (GstPlayTextChain *) playsink->textchain))
882 chainp = chain->sink;
883 elem = playsink->text_sink;
890 /* we have an active chain with a sink, get the sink */
891 result = gst_object_ref (chainp);
893 /* nothing found, return last configured sink */
894 if (result == NULL && elem)
895 result = gst_object_ref (elem);
896 GST_PLAY_SINK_UNLOCK (playsink);
901 static GstPadProbeReturn
902 gst_play_sink_vis_blocked (GstPad * tee_pad, GstPadProbeInfo * info,
905 GstPlaySink *playsink;
906 GstPlayVisChain *chain;
908 playsink = GST_PLAY_SINK (user_data);
910 GST_PLAY_SINK_LOCK (playsink);
911 GST_DEBUG_OBJECT (playsink, "vis pad blocked");
912 /* now try to change the plugin in the running vis chain */
913 if (!(chain = (GstPlayVisChain *) playsink->vischain))
916 /* unlink the old plugin and unghost the pad */
917 gst_pad_unlink (chain->vispeerpad, chain->vissinkpad);
918 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad), NULL);
920 /* set the old plugin to NULL and remove */
921 gst_element_set_state (chain->vis, GST_STATE_NULL);
922 gst_bin_remove (GST_BIN_CAST (chain->chain.bin), chain->vis);
924 /* add new plugin and set state to playing */
925 chain->vis = playsink->visualisation;
926 gst_bin_add (GST_BIN_CAST (chain->chain.bin), chain->vis);
927 gst_element_set_state (chain->vis, GST_STATE_PLAYING);
930 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
931 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
934 gst_pad_link_full (chain->vispeerpad, chain->vissinkpad,
935 GST_PAD_LINK_CHECK_NOTHING);
936 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad),
940 GST_PLAY_SINK_UNLOCK (playsink);
942 /* remove the probe and unblock the pad */
943 return GST_PAD_PROBE_REMOVE;
947 gst_play_sink_set_vis_plugin (GstPlaySink * playsink, GstElement * vis)
949 GstPlayVisChain *chain;
951 /* setting NULL means creating the default vis plugin */
953 vis = gst_element_factory_make ("goom", "vis");
955 /* simply return if we don't have a vis plugin here */
959 GST_PLAY_SINK_LOCK (playsink);
960 /* first store the new vis */
961 if (playsink->visualisation)
962 gst_object_unref (playsink->visualisation);
964 gst_object_ref_sink (vis);
965 playsink->visualisation = vis;
967 /* now try to change the plugin in the running vis chain, if we have no chain,
968 * we don't bother, any future vis chain will be created with the new vis
970 if (!(chain = (GstPlayVisChain *) playsink->vischain))
973 /* block the pad, the next time the callback is called we can change the
974 * visualisation. It's possible that this never happens or that the pad was
975 * already blocked. If the callback never happens, we don't have new data so
976 * we don't need the new vis plugin. If the pad was already blocked, the
977 * function returns FALSE but the previous pad block will do the right thing
979 GST_DEBUG_OBJECT (playsink, "blocking vis pad");
980 gst_pad_add_probe (chain->blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
981 gst_play_sink_vis_blocked, playsink, NULL);
983 GST_PLAY_SINK_UNLOCK (playsink);
989 gst_play_sink_get_vis_plugin (GstPlaySink * playsink)
991 GstElement *result = NULL;
992 GstPlayVisChain *chain;
994 GST_PLAY_SINK_LOCK (playsink);
995 if ((chain = (GstPlayVisChain *) playsink->vischain)) {
996 /* we have an active chain, get the sink */
998 result = gst_object_ref (chain->vis);
1000 /* nothing found, return last configured sink */
1001 if (result == NULL && playsink->visualisation)
1002 result = gst_object_ref (playsink->visualisation);
1003 GST_PLAY_SINK_UNLOCK (playsink);
1009 gst_play_sink_set_volume (GstPlaySink * playsink, gdouble volume)
1011 GstPlayAudioChain *chain;
1013 GST_PLAY_SINK_LOCK (playsink);
1014 playsink->volume = volume;
1015 chain = (GstPlayAudioChain *) playsink->audiochain;
1016 if (chain && chain->volume) {
1017 GST_LOG_OBJECT (playsink, "elements: volume=%" GST_PTR_FORMAT ", mute=%"
1018 GST_PTR_FORMAT "; new volume=%.03f, mute=%d", chain->volume,
1019 chain->mute, volume, playsink->mute);
1020 /* if there is a mute element or we are not muted, set the volume */
1021 if (chain->mute || !playsink->mute)
1022 g_object_set (chain->volume, "volume", volume, NULL);
1024 GST_LOG_OBJECT (playsink, "no volume element");
1025 playsink->volume_changed = TRUE;
1027 GST_PLAY_SINK_UNLOCK (playsink);
1031 gst_play_sink_get_volume (GstPlaySink * playsink)
1034 GstPlayAudioChain *chain;
1036 GST_PLAY_SINK_LOCK (playsink);
1037 chain = (GstPlayAudioChain *) playsink->audiochain;
1038 result = playsink->volume;
1039 if (chain && chain->volume) {
1040 if (chain->mute || !playsink->mute) {
1041 g_object_get (chain->volume, "volume", &result, NULL);
1042 playsink->volume = result;
1045 GST_PLAY_SINK_UNLOCK (playsink);
1051 gst_play_sink_set_mute (GstPlaySink * playsink, gboolean mute)
1053 GstPlayAudioChain *chain;
1055 GST_PLAY_SINK_LOCK (playsink);
1056 playsink->mute = mute;
1057 chain = (GstPlayAudioChain *) playsink->audiochain;
1060 g_object_set (chain->mute, "mute", mute, NULL);
1061 } else if (chain->volume) {
1063 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
1065 g_object_set (chain->volume, "volume", (gdouble) playsink->volume,
1069 playsink->mute_changed = TRUE;
1071 GST_PLAY_SINK_UNLOCK (playsink);
1075 gst_play_sink_get_mute (GstPlaySink * playsink)
1078 GstPlayAudioChain *chain;
1080 GST_PLAY_SINK_LOCK (playsink);
1081 chain = (GstPlayAudioChain *) playsink->audiochain;
1082 if (chain && chain->mute) {
1083 g_object_get (chain->mute, "mute", &result, NULL);
1084 playsink->mute = result;
1086 result = playsink->mute;
1088 GST_PLAY_SINK_UNLOCK (playsink);
1094 post_missing_element_message (GstPlaySink * playsink, const gchar * name)
1098 msg = gst_missing_element_message_new (GST_ELEMENT_CAST (playsink), name);
1099 gst_element_post_message (GST_ELEMENT_CAST (playsink), msg);
1103 add_chain (GstPlayChain * chain, gboolean add)
1105 if (chain->added == add)
1109 gst_bin_add (GST_BIN_CAST (chain->playsink), chain->bin);
1111 gst_bin_remove (GST_BIN_CAST (chain->playsink), chain->bin);
1112 /* we don't want to lose our sink status */
1113 GST_OBJECT_FLAG_SET (chain->playsink, GST_ELEMENT_FLAG_SINK);
1122 activate_chain (GstPlayChain * chain, gboolean activate)
1126 if (chain->activated == activate)
1129 GST_OBJECT_LOCK (chain->playsink);
1130 state = GST_STATE_TARGET (chain->playsink);
1131 GST_OBJECT_UNLOCK (chain->playsink);
1134 gst_element_set_state (chain->bin, state);
1136 gst_element_set_state (chain->bin, GST_STATE_NULL);
1138 chain->activated = activate;
1144 element_is_sink (GstElement * element)
1148 GST_OBJECT_LOCK (element);
1149 is_sink = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_FLAG_SINK);
1150 GST_OBJECT_UNLOCK (element);
1152 GST_DEBUG_OBJECT (element, "is a sink: %s", (is_sink) ? "yes" : "no");
1157 element_has_property (GstElement * element, const gchar * pname, GType type)
1161 pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (element), pname);
1163 if (pspec == NULL) {
1164 GST_DEBUG_OBJECT (element, "no %s property", pname);
1168 if (type == G_TYPE_INVALID || type == pspec->value_type ||
1169 g_type_is_a (pspec->value_type, type)) {
1170 GST_DEBUG_OBJECT (element, "has %s property of type %s", pname,
1171 (type == G_TYPE_INVALID) ? "any type" : g_type_name (type));
1175 GST_WARNING_OBJECT (element, "has %s property, but property is of type %s "
1176 "and we expected it to be of type %s", pname,
1177 g_type_name (pspec->value_type), g_type_name (type));
1184 const gchar *prop_name;
1187 } FindPropertyHelper;
1190 find_property (const GValue * item, FindPropertyHelper * helper)
1192 GstElement *element = g_value_get_object (item);
1193 if (helper->need_sink && !element_is_sink (element)) {
1197 if (!element_has_property (element, helper->prop_name, helper->prop_type)) {
1201 GST_INFO_OBJECT (element, "found %s with %s property", helper->prop_name,
1202 (helper->need_sink) ? "sink" : "element");
1203 return 0; /* keep it */
1206 /* FIXME: why not move these functions into core? */
1207 /* find a sink in the hierarchy with a property named @name. This function does
1208 * not increase the refcount of the returned object and thus remains valid as
1209 * long as the bin is valid. */
1211 gst_play_sink_find_property_sinks (GstPlaySink * playsink, GstElement * obj,
1212 const gchar * name, GType expected_type)
1214 GstElement *result = NULL;
1217 if (element_has_property (obj, name, expected_type)) {
1219 } else if (GST_IS_BIN (obj)) {
1221 GValue item = { 0, };
1222 FindPropertyHelper helper = { name, expected_type, TRUE };
1224 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1225 found = gst_iterator_find_custom (it,
1226 (GCompareFunc) find_property, &item, &helper);
1227 gst_iterator_free (it);
1229 result = g_value_get_object (&item);
1230 /* we don't need the extra ref */
1231 g_value_unset (&item);
1237 /* find an object in the hierarchy with a property named @name */
1239 gst_play_sink_find_property (GstPlaySink * playsink, GstElement * obj,
1240 const gchar * name, GType expected_type)
1242 GstElement *result = NULL;
1245 if (GST_IS_BIN (obj)) {
1247 GValue item = { 0, };
1248 FindPropertyHelper helper = { name, expected_type, FALSE };
1250 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1251 found = gst_iterator_find_custom (it,
1252 (GCompareFunc) find_property, &item, &helper);
1253 gst_iterator_free (it);
1255 result = g_value_dup_object (&item);
1256 g_value_unset (&item);
1259 if (element_has_property (obj, name, expected_type)) {
1261 gst_object_ref (obj);
1268 do_async_start (GstPlaySink * playsink)
1270 GstMessage *message;
1272 if (!playsink->need_async_start) {
1273 GST_INFO_OBJECT (playsink, "no async_start needed");
1277 playsink->async_pending = TRUE;
1279 GST_INFO_OBJECT (playsink, "Sending async_start message");
1280 message = gst_message_new_async_start (GST_OBJECT_CAST (playsink));
1281 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1282 (playsink), message);
1286 do_async_done (GstPlaySink * playsink)
1288 GstMessage *message;
1290 if (playsink->async_pending) {
1291 GST_INFO_OBJECT (playsink, "Sending async_done message");
1293 gst_message_new_async_done (GST_OBJECT_CAST (playsink),
1294 GST_CLOCK_TIME_NONE);
1295 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1296 (playsink), message);
1298 playsink->async_pending = FALSE;
1301 playsink->need_async_start = FALSE;
1304 /* try to change the state of an element. This function returns the element when
1305 * the state change could be performed. When this function returns NULL an error
1306 * occured and the element is unreffed if @unref is TRUE. */
1308 try_element (GstPlaySink * playsink, GstElement * element, gboolean unref)
1310 GstStateChangeReturn ret;
1313 ret = gst_element_set_state (element, GST_STATE_READY);
1314 if (ret == GST_STATE_CHANGE_FAILURE) {
1315 GST_DEBUG_OBJECT (playsink, "failed state change..");
1316 gst_element_set_state (element, GST_STATE_NULL);
1318 gst_object_unref (element);
1325 /* make the element (bin) that contains the elements needed to perform
1326 * video deinterlacing. Only used for *raw* video streams.
1328 * +---------------------------------------+
1330 * | +----------+ +-----------+ |
1331 * | |colorspace| |deinterlace| |
1332 * | +-sink src-sink src-+ |
1333 * | | +----------+ +-----------+ | |
1335 * +---------------------------------------+
1338 static GstPlayVideoDeinterlaceChain *
1339 gen_video_deinterlace_chain (GstPlaySink * playsink)
1341 GstPlayVideoDeinterlaceChain *chain;
1344 GstElement *head = NULL, *prev = NULL;
1346 chain = g_new0 (GstPlayVideoDeinterlaceChain, 1);
1347 chain->chain.playsink = playsink;
1349 GST_DEBUG_OBJECT (playsink, "making video deinterlace chain %p", chain);
1351 /* create a bin to hold objects, as we create them we add them to this bin so
1352 * that when something goes wrong we only need to unref the bin */
1353 chain->chain.bin = gst_bin_new ("vdbin");
1354 bin = GST_BIN_CAST (chain->chain.bin);
1355 gst_object_ref_sink (bin);
1357 GST_DEBUG_OBJECT (playsink, "creating " COLORSPACE);
1358 chain->conv = gst_element_factory_make (COLORSPACE, "vdconv");
1359 if (chain->conv == NULL) {
1360 post_missing_element_message (playsink, COLORSPACE);
1361 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1362 (_("Missing element '%s' - check your GStreamer installation."),
1363 COLORSPACE), ("video rendering might fail"));
1365 gst_bin_add (bin, chain->conv);
1370 GST_DEBUG_OBJECT (playsink, "creating deinterlace");
1371 chain->deinterlace = gst_element_factory_make ("deinterlace", "deinterlace");
1372 if (chain->deinterlace == NULL) {
1373 chain->deinterlace =
1374 gst_element_factory_make ("avdeinterlace", "deinterlace");
1376 if (chain->deinterlace == NULL) {
1377 post_missing_element_message (playsink, "deinterlace");
1378 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1379 (_("Missing element '%s' - check your GStreamer installation."),
1380 "deinterlace"), ("deinterlacing won't work"));
1382 gst_bin_add (bin, chain->deinterlace);
1384 if (!gst_element_link_pads_full (prev, "src", chain->deinterlace, "sink",
1385 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1388 head = chain->deinterlace;
1390 prev = chain->deinterlace;
1394 pad = gst_element_get_static_pad (head, "sink");
1395 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1396 gst_object_unref (pad);
1398 chain->sinkpad = gst_ghost_pad_new_no_target ("sink", GST_PAD_SINK);
1402 pad = gst_element_get_static_pad (prev, "src");
1403 chain->srcpad = gst_ghost_pad_new ("src", pad);
1404 gst_object_unref (pad);
1406 chain->srcpad = gst_ghost_pad_new ("src", chain->sinkpad);
1409 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1410 gst_element_add_pad (chain->chain.bin, chain->srcpad);
1416 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1417 (NULL), ("Failed to configure the video deinterlace chain."));
1418 free_chain ((GstPlayChain *) chain);
1424 is_valid_color_balance_element (GstColorBalance * bal)
1426 gboolean have_brightness = FALSE;
1427 gboolean have_contrast = FALSE;
1428 gboolean have_hue = FALSE;
1429 gboolean have_saturation = FALSE;
1430 const GList *channels, *l;
1432 channels = gst_color_balance_list_channels (bal);
1433 for (l = channels; l; l = l->next) {
1434 GstColorBalanceChannel *ch = l->data;
1436 if (g_strrstr (ch->label, "BRIGHTNESS"))
1437 have_brightness = TRUE;
1438 else if (g_strrstr (ch->label, "CONTRAST"))
1439 have_contrast = TRUE;
1440 else if (g_strrstr (ch->label, "HUE"))
1442 else if (g_strrstr (ch->label, "SATURATION"))
1443 have_saturation = TRUE;
1446 return have_brightness && have_contrast && have_hue && have_saturation;
1450 iterate_color_balance_elements (const GValue * item, gpointer user_data)
1453 GstColorBalance *cb, **cb_out = user_data;
1455 cb = GST_COLOR_BALANCE (g_value_get_object (item));
1456 valid = is_valid_color_balance_element (cb);
1459 && gst_color_balance_get_balance_type (*cb_out) ==
1460 GST_COLOR_BALANCE_SOFTWARE) {
1461 gst_object_unref (*cb_out);
1462 *cb_out = GST_COLOR_BALANCE (gst_object_ref (cb));
1463 } else if (!*cb_out) {
1464 *cb_out = GST_COLOR_BALANCE (gst_object_ref (cb));
1469 static GstColorBalance *
1470 find_color_balance_element (GstElement * element)
1473 GstColorBalance *cb = NULL;
1475 if (GST_IS_COLOR_BALANCE (element)
1476 && is_valid_color_balance_element (GST_COLOR_BALANCE (element)))
1477 return GST_COLOR_BALANCE (gst_object_ref (element));
1478 else if (!GST_IS_BIN (element))
1481 it = gst_bin_iterate_all_by_interface (GST_BIN (element),
1482 GST_TYPE_COLOR_BALANCE);
1483 while (gst_iterator_foreach (it, iterate_color_balance_elements,
1484 &cb) == GST_ITERATOR_RESYNC)
1485 gst_iterator_resync (it);
1486 gst_iterator_free (it);
1492 colorbalance_value_changed_cb (GstColorBalance * balance,
1493 GstColorBalanceChannel * channel, gint value, GstPlaySink * playsink)
1498 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
1499 GstColorBalanceChannel *proxy = l->data;
1501 if (g_strrstr (channel->label, proxy->label)) {
1504 /* Convert to [0, 1] range */
1507 (gdouble) channel->min_value) / ((gdouble) channel->max_value -
1508 (gdouble) channel->min_value);
1509 /* Convert to proxy range */
1511 proxy->min_value + new_val * ((gdouble) proxy->max_value -
1512 (gdouble) proxy->min_value);
1513 playsink->colorbalance_values[i] = (gint) (0.5 + new_val);
1515 gst_color_balance_value_changed (GST_COLOR_BALANCE (playsink), proxy,
1516 playsink->colorbalance_values[i]);
1523 update_colorbalance (GstPlaySink * playsink)
1525 GstColorBalance *balance = NULL;
1529 GST_OBJECT_LOCK (playsink);
1530 if (playsink->colorbalance_element) {
1532 GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
1534 GST_OBJECT_UNLOCK (playsink);
1538 g_signal_handler_block (balance, playsink->colorbalance_value_changed_id);
1540 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
1541 GstColorBalanceChannel *proxy = l->data;
1542 GstColorBalanceChannel *channel = NULL;
1543 const GList *channels, *k;
1546 channels = gst_color_balance_list_channels (balance);
1547 for (k = channels; k; k = k->next) {
1548 GstColorBalanceChannel *tmp = k->data;
1550 if (g_strrstr (tmp->label, proxy->label)) {
1558 /* Convert to [0, 1] range */
1560 ((gdouble) playsink->colorbalance_values[i] -
1561 (gdouble) proxy->min_value) / ((gdouble) proxy->max_value -
1562 (gdouble) proxy->min_value);
1563 /* Convert to channel range */
1565 channel->min_value + new_val * ((gdouble) channel->max_value -
1566 (gdouble) channel->min_value);
1568 gst_color_balance_set_value (balance, channel, (gint) (new_val + 0.5));
1571 g_signal_handler_unblock (balance, playsink->colorbalance_value_changed_id);
1573 gst_object_unref (balance);
1576 /* make the element (bin) that contains the elements needed to perform
1579 * +------------------------------------------------------------+
1581 * | +-------+ +----------+ +----------+ +---------+ |
1582 * | | queue | |colorspace| |videoscale| |videosink| |
1583 * | +-sink src-sink src-sink src-sink | |
1584 * | | +-------+ +----------+ +----------+ +---------+ |
1586 * +------------------------------------------------------------+
1589 static GstPlayVideoChain *
1590 gen_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1592 GstPlayVideoChain *chain;
1595 GstElement *head = NULL, *prev = NULL, *elem = NULL;
1597 chain = g_new0 (GstPlayVideoChain, 1);
1598 chain->chain.playsink = playsink;
1599 chain->chain.raw = raw;
1601 GST_DEBUG_OBJECT (playsink, "making video chain %p", chain);
1603 if (playsink->video_sink) {
1604 GST_DEBUG_OBJECT (playsink, "trying configured videosink");
1605 chain->sink = try_element (playsink, playsink->video_sink, FALSE);
1607 /* only try fallback if no specific sink was chosen */
1608 if (chain->sink == NULL) {
1609 GST_DEBUG_OBJECT (playsink, "trying autovideosink");
1610 elem = gst_element_factory_make ("autovideosink", "videosink");
1611 chain->sink = try_element (playsink, elem, TRUE);
1613 if (chain->sink == NULL) {
1614 /* if default sink from config.h is different then try it too */
1615 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1616 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_VIDEOSINK);
1617 elem = gst_element_factory_make (DEFAULT_VIDEOSINK, "videosink");
1618 chain->sink = try_element (playsink, elem, TRUE);
1622 playsink->video_sink = gst_object_ref (chain->sink);
1624 if (chain->sink == NULL)
1628 /* if we can disable async behaviour of the sink, we can avoid adding a
1629 * queue for the audio chain. */
1631 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1634 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1635 async, GST_ELEMENT_NAME (elem));
1636 g_object_set (elem, "async", async, NULL);
1637 chain->async = async;
1639 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1640 chain->async = TRUE;
1643 /* Make sure the aspect ratio is kept */
1645 gst_play_sink_find_property_sinks (playsink, chain->sink,
1646 "force-aspect-ratio", G_TYPE_BOOLEAN);
1648 g_object_set (elem, "force-aspect-ratio", playsink->force_aspect_ratio,
1651 /* find ts-offset element */
1652 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1653 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1656 /* create a bin to hold objects, as we create them we add them to this bin so
1657 * that when something goes wrong we only need to unref the bin */
1658 chain->chain.bin = gst_bin_new ("vbin");
1659 bin = GST_BIN_CAST (chain->chain.bin);
1660 gst_object_ref_sink (bin);
1661 gst_bin_add (bin, chain->sink);
1663 /* Get the VideoOverlay element */
1665 GstVideoOverlay *overlay = NULL;
1667 GST_OBJECT_LOCK (playsink);
1668 if (playsink->overlay_element)
1669 gst_object_unref (playsink->overlay_element);
1670 playsink->overlay_element =
1671 GST_VIDEO_OVERLAY (gst_bin_get_by_interface (GST_BIN (chain->chain.bin),
1672 GST_TYPE_VIDEO_OVERLAY));
1673 if (playsink->overlay_element)
1674 overlay = GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
1675 GST_OBJECT_UNLOCK (playsink);
1678 if (playsink->overlay_handle_set)
1679 gst_video_overlay_set_window_handle (overlay, playsink->overlay_handle);
1680 if (playsink->overlay_handle_events_set)
1681 gst_video_overlay_handle_events (overlay,
1682 playsink->overlay_handle_events);
1683 if (playsink->overlay_render_rectangle_set)
1684 gst_video_overlay_set_render_rectangle (overlay,
1685 playsink->overlay_x, playsink->overlay_y,
1686 playsink->overlay_width, playsink->overlay_height);
1687 gst_object_unref (overlay);
1691 /* decouple decoder from sink, this improves playback quite a lot since the
1692 * decoder can continue while the sink blocks for synchronisation. We don't
1693 * need a lot of buffers as this consumes a lot of memory and we don't want
1694 * too little because else we would be context switching too quickly. */
1695 chain->queue = gst_element_factory_make ("queue", "vqueue");
1696 if (chain->queue == NULL) {
1697 post_missing_element_message (playsink, "queue");
1698 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1699 (_("Missing element '%s' - check your GStreamer installation."),
1700 "queue"), ("video rendering might be suboptimal"));
1704 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1705 "max-size-bytes", 0, "max-size-time", (gint64) 0, "silent", TRUE, NULL);
1706 gst_bin_add (bin, chain->queue);
1707 head = prev = chain->queue;
1710 GST_OBJECT_LOCK (playsink);
1711 if (playsink->colorbalance_element) {
1712 g_signal_handler_disconnect (playsink->colorbalance_element,
1713 playsink->colorbalance_value_changed_id);
1714 gst_object_unref (playsink->colorbalance_element);
1715 playsink->colorbalance_value_changed_id = 0;
1717 playsink->colorbalance_element = find_color_balance_element (chain->sink);
1718 if (playsink->colorbalance_element) {
1719 playsink->colorbalance_value_changed_id =
1720 g_signal_connect (playsink->colorbalance_element, "value-changed",
1721 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1723 GST_OBJECT_UNLOCK (playsink);
1725 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)
1726 || (!playsink->colorbalance_element
1727 && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE))) {
1728 gboolean use_converters = !(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO);
1729 gboolean use_balance = !playsink->colorbalance_element
1730 && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE);
1732 GST_DEBUG_OBJECT (playsink, "creating videoconverter");
1734 g_object_new (GST_TYPE_PLAY_SINK_VIDEO_CONVERT, "name", "vconv",
1735 "use-converters", use_converters, "use-balance", use_balance, NULL);
1737 GST_OBJECT_LOCK (playsink);
1738 if (use_balance && GST_PLAY_SINK_VIDEO_CONVERT (chain->conv)->balance) {
1739 playsink->colorbalance_element =
1740 GST_COLOR_BALANCE (gst_object_ref (GST_PLAY_SINK_VIDEO_CONVERT
1741 (chain->conv)->balance));
1742 playsink->colorbalance_value_changed_id =
1743 g_signal_connect (playsink->colorbalance_element, "value-changed",
1744 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1746 GST_OBJECT_UNLOCK (playsink);
1748 gst_bin_add (bin, chain->conv);
1750 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
1751 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1759 update_colorbalance (playsink);
1762 GST_DEBUG_OBJECT (playsink, "linking to sink");
1763 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
1764 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1768 pad = gst_element_get_static_pad (head, "sink");
1769 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1771 /* sending audio/video flushes break stream changes when the pipeline
1772 * is paused and played again in 0.10 */
1774 gst_pad_set_event_function (chain->sinkpad,
1775 GST_DEBUG_FUNCPTR (gst_play_sink_video_sink_event));
1776 gst_pad_set_chain_function (chain->sinkpad,
1777 GST_DEBUG_FUNCPTR (gst_play_sink_video_sink_chain));
1780 gst_object_unref (pad);
1781 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1788 if (!elem && !playsink->video_sink) {
1789 post_missing_element_message (playsink, "autovideosink");
1790 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1791 post_missing_element_message (playsink, DEFAULT_VIDEOSINK);
1792 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1793 (_("Both autovideosink and %s elements are missing."),
1794 DEFAULT_VIDEOSINK), (NULL));
1796 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1797 (_("The autovideosink element is missing.")), (NULL));
1800 if (playsink->video_sink) {
1801 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1802 (_("Configured videosink %s is not working."),
1803 GST_ELEMENT_NAME (playsink->video_sink)), (NULL));
1804 } else if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1805 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1806 (_("Both autovideosink and %s elements are not working."),
1807 DEFAULT_VIDEOSINK), (NULL));
1809 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1810 (_("The autovideosink element is not working.")), (NULL));
1813 free_chain ((GstPlayChain *) chain);
1819 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1820 (NULL), ("Failed to configure the video sink."));
1821 /* checking sink made it READY */
1822 gst_element_set_state (chain->sink, GST_STATE_NULL);
1823 /* Remove chain from the bin to allow reuse later */
1824 gst_bin_remove (bin, chain->sink);
1825 free_chain ((GstPlayChain *) chain);
1831 setup_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1834 GstPlayVideoChain *chain;
1835 GstStateChangeReturn ret;
1837 chain = playsink->videochain;
1839 chain->chain.raw = raw;
1841 /* if the chain was active we don't do anything */
1842 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
1845 /* try to set the sink element to READY again */
1846 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
1847 if (ret == GST_STATE_CHANGE_FAILURE)
1850 /* Get the VideoOverlay element */
1852 GstVideoOverlay *overlay = NULL;
1854 GST_OBJECT_LOCK (playsink);
1855 if (playsink->overlay_element)
1856 gst_object_unref (playsink->overlay_element);
1857 playsink->overlay_element =
1858 GST_VIDEO_OVERLAY (gst_bin_get_by_interface (GST_BIN (chain->chain.bin),
1859 GST_TYPE_VIDEO_OVERLAY));
1860 if (playsink->overlay_element)
1861 overlay = GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
1862 GST_OBJECT_UNLOCK (playsink);
1865 if (playsink->overlay_handle_set)
1866 gst_video_overlay_set_window_handle (overlay, playsink->overlay_handle);
1867 if (playsink->overlay_handle_events_set)
1868 gst_video_overlay_handle_events (overlay,
1869 playsink->overlay_handle_events);
1870 if (playsink->overlay_render_rectangle_set)
1871 gst_video_overlay_set_render_rectangle (overlay,
1872 playsink->overlay_x, playsink->overlay_y,
1873 playsink->overlay_width, playsink->overlay_height);
1874 gst_object_unref (overlay);
1878 /* find ts-offset element */
1879 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1880 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1883 /* if we can disable async behaviour of the sink, we can avoid adding a
1884 * queue for the audio chain. */
1886 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1889 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1890 async, GST_ELEMENT_NAME (elem));
1891 g_object_set (elem, "async", async, NULL);
1892 chain->async = async;
1894 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1895 chain->async = TRUE;
1898 /* Make sure the aspect ratio is kept */
1900 gst_play_sink_find_property_sinks (playsink, chain->sink,
1901 "force-aspect-ratio", G_TYPE_BOOLEAN);
1903 g_object_set (elem, "force-aspect-ratio", playsink->force_aspect_ratio,
1906 GST_OBJECT_LOCK (playsink);
1907 if (playsink->colorbalance_element) {
1908 g_signal_handler_disconnect (playsink->colorbalance_element,
1909 playsink->colorbalance_value_changed_id);
1910 playsink->colorbalance_value_changed_id = 0;
1911 gst_object_unref (playsink->colorbalance_element);
1913 playsink->colorbalance_element = find_color_balance_element (chain->sink);
1914 if (playsink->colorbalance_element) {
1915 playsink->colorbalance_value_changed_id =
1916 g_signal_connect (playsink->colorbalance_element, "value-changed",
1917 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1919 GST_OBJECT_UNLOCK (playsink);
1922 gboolean use_balance = !playsink->colorbalance_element
1923 && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE);
1925 g_object_set (chain->conv, "use-balance", use_balance, NULL);
1927 GST_OBJECT_LOCK (playsink);
1928 if (use_balance && GST_PLAY_SINK_VIDEO_CONVERT (chain->conv)->balance) {
1929 playsink->colorbalance_element =
1930 GST_COLOR_BALANCE (gst_object_ref (GST_PLAY_SINK_VIDEO_CONVERT
1931 (chain->conv)->balance));
1932 playsink->colorbalance_value_changed_id =
1933 g_signal_connect (playsink->colorbalance_element, "value-changed",
1934 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1936 GST_OBJECT_UNLOCK (playsink);
1939 update_colorbalance (playsink);
1945 gst_play_sink_sink_event (GstPad * pad, GstObject * parent, GstEvent * event,
1946 const gchar * sink_type,
1947 gboolean * sink_ignore_wrong_state,
1948 gboolean * sink_custom_flush_finished, gboolean * sink_pending_flush)
1950 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_object_get_parent (parent));
1952 const GstStructure *structure = gst_event_get_structure (event);
1954 if (GST_EVENT_TYPE (event) == GST_EVENT_CUSTOM_DOWNSTREAM_OOB && structure) {
1955 gchar *custom_flush;
1956 gchar *custom_flush_finish;
1958 custom_flush = g_strdup_printf ("playsink-custom-%s-flush", sink_type);
1959 custom_flush_finish =
1960 g_strdup_printf ("playsink-custom-%s-flush-finish", sink_type);
1961 if (strcmp (gst_structure_get_name (structure), custom_flush) == 0) {
1962 GST_DEBUG_OBJECT (pad,
1963 "Custom %s flush event received, marking to flush %s", sink_type,
1965 GST_PLAY_SINK_LOCK (playsink);
1966 *sink_ignore_wrong_state = TRUE;
1967 *sink_custom_flush_finished = FALSE;
1968 GST_PLAY_SINK_UNLOCK (playsink);
1969 } else if (strcmp (gst_structure_get_name (structure),
1970 custom_flush_finish) == 0) {
1971 GST_DEBUG_OBJECT (pad, "Custom %s flush finish event received",
1973 GST_PLAY_SINK_LOCK (playsink);
1974 *sink_pending_flush = TRUE;
1975 *sink_custom_flush_finished = TRUE;
1976 GST_PLAY_SINK_UNLOCK (playsink);
1979 g_free (custom_flush);
1980 g_free (custom_flush_finish);
1983 GST_DEBUG_OBJECT (pad, "Forwarding event %" GST_PTR_FORMAT, event);
1984 ret = gst_pad_event_default (pad, parent, gst_event_ref (event));
1986 gst_event_unref (event);
1987 gst_object_unref (playsink);
1991 static GstFlowReturn
1992 gst_play_sink_sink_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer,
1993 const gchar * sink_type,
1994 gboolean * sink_ignore_wrong_state,
1995 gboolean * sink_custom_flush_finished, gboolean * sink_pending_flush)
1997 GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
1998 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2001 GST_PLAY_SINK_LOCK (playsink);
2003 if (*sink_pending_flush) {
2004 GstEvent *segment_event;
2006 GstStructure *structure;
2008 *sink_pending_flush = FALSE;
2010 GST_PLAY_SINK_UNLOCK (playsink);
2012 segment_event = gst_pad_get_sticky_event (pad, GST_EVENT_SEGMENT, 0);
2014 /* make the bin drop all cached data.
2015 * This event will be dropped on the src pad, if any. */
2016 event = gst_event_new_flush_start ();
2017 structure = gst_event_writable_structure (event);
2018 gst_structure_id_set (structure,
2019 _playsink_reset_segment_event_marker_id, G_TYPE_BOOLEAN, TRUE, NULL);
2021 GST_DEBUG_OBJECT (pad,
2022 "Pushing %s flush-start event with reset segment marker set: %"
2023 GST_PTR_FORMAT, sink_type, event);
2024 gst_pad_send_event (pad, event);
2026 /* make queue drop all cached data.
2027 * This event will be dropped on the src pad. */
2028 event = gst_event_new_flush_stop (TRUE);
2029 structure = gst_event_writable_structure (event);
2030 gst_structure_id_set (structure,
2031 _playsink_reset_segment_event_marker_id, G_TYPE_BOOLEAN, TRUE, NULL);
2033 GST_DEBUG_OBJECT (pad,
2034 "Pushing %s flush-stop event with reset segment marker set: %"
2035 GST_PTR_FORMAT, sink_type, event);
2036 gst_pad_send_event (pad, event);
2038 /* Re-sync queue segment info after flush-stop.
2039 * This event will be dropped on the src pad. */
2040 if (segment_event) {
2041 event = gst_event_copy (segment_event);
2042 structure = gst_event_writable_structure (event);
2043 gst_structure_id_set (structure,
2044 _playsink_reset_segment_event_marker_id, G_TYPE_BOOLEAN, TRUE, NULL);
2046 GST_DEBUG_OBJECT (playsink,
2047 "Pushing segment event with reset "
2048 "segment marker set: %" GST_PTR_FORMAT, event);
2049 gst_pad_send_event (pad, event);
2050 gst_event_unref (segment_event);
2053 GST_PLAY_SINK_UNLOCK (playsink);
2056 ret = gst_proxy_pad_chain_default (pad, parent, buffer);
2058 GST_PLAY_SINK_LOCK (playsink);
2059 if (ret == GST_FLOW_FLUSHING && *sink_ignore_wrong_state) {
2060 GST_DEBUG_OBJECT (pad, "Ignoring wrong state for %s during flush",
2062 if (*sink_custom_flush_finished) {
2063 GST_DEBUG_OBJECT (pad, "Custom flush finished, stop ignoring "
2064 "wrong state for %s", sink_type);
2065 *sink_ignore_wrong_state = FALSE;
2070 GST_PLAY_SINK_UNLOCK (playsink);
2072 gst_object_unref (playsink);
2073 gst_object_unref (tbin);
2077 /* sending audio/video flushes break stream changes when the pipeline
2078 * is paused and played again in 0.10 */
2081 gst_play_sink_video_sink_event (GstPad * pad, GstEvent * event)
2083 GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2084 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2087 ret = gst_play_sink_sink_event (pad, event, "video",
2088 &playsink->video_ignore_wrong_state,
2089 &playsink->video_custom_flush_finished,
2090 &playsink->video_pending_flush, &playsink->video_segment);
2092 gst_object_unref (playsink);
2093 gst_object_unref (tbin);
2097 static GstFlowReturn
2098 gst_play_sink_video_sink_chain (GstPad * pad, GstBuffer * buffer)
2100 GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2101 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2104 ret = gst_play_sink_sink_chain (pad, buffer, "video",
2105 &playsink->video_ignore_wrong_state,
2106 &playsink->video_custom_flush_finished,
2107 &playsink->video_pending_flush, &playsink->video_segment);
2109 gst_object_unref (playsink);
2110 gst_object_unref (tbin);
2115 gst_play_sink_audio_sink_event (GstPad * pad, GstEvent * event)
2117 GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2118 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2121 ret = gst_play_sink_sink_event (pad, event, "audio",
2122 &playsink->audio_ignore_wrong_state,
2123 &playsink->audio_custom_flush_finished,
2124 &playsink->audio_pending_flush, &playsink->audio_segment);
2126 gst_object_unref (playsink);
2127 gst_object_unref (tbin);
2131 static GstFlowReturn
2132 gst_play_sink_audio_sink_chain (GstPad * pad, GstBuffer * buffer)
2134 GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2135 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2138 ret = gst_play_sink_sink_chain (pad, buffer, "audio",
2139 &playsink->audio_ignore_wrong_state,
2140 &playsink->audio_custom_flush_finished,
2141 &playsink->audio_pending_flush, &playsink->audio_segment);
2143 gst_object_unref (playsink);
2144 gst_object_unref (tbin);
2150 gst_play_sink_text_sink_event (GstPad * pad, GstObject * parent,
2153 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_object_get_parent (parent));
2156 ret = gst_play_sink_sink_event (pad, parent, event, "subtitle",
2157 &playsink->text_ignore_wrong_state,
2158 &playsink->text_custom_flush_finished, &playsink->text_pending_flush);
2160 gst_object_unref (playsink);
2165 static GstFlowReturn
2166 gst_play_sink_text_sink_chain (GstPad * pad, GstObject * parent,
2170 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_object_get_parent (parent));
2172 ret = gst_play_sink_sink_chain (pad, parent, buffer, "subtitle",
2173 &playsink->text_ignore_wrong_state,
2174 &playsink->text_custom_flush_finished, &playsink->text_pending_flush);
2176 gst_object_unref (playsink);
2181 gst_play_sink_text_src_event (GstPad * pad, GstObject * parent,
2185 const GstStructure *structure;
2187 GST_DEBUG_OBJECT (pad, "Got event %" GST_PTR_FORMAT, event);
2189 structure = gst_event_get_structure (event);
2192 gst_structure_id_has_field (structure,
2193 _playsink_reset_segment_event_marker_id)) {
2194 /* the events marked with a reset segment marker
2195 * are sent internally to reset the queue and
2196 * must be dropped here */
2197 GST_DEBUG_OBJECT (pad, "Dropping event with reset "
2198 "segment marker set: %" GST_PTR_FORMAT, event);
2203 ret = gst_pad_event_default (pad, parent, gst_event_ref (event));
2206 gst_event_unref (event);
2210 /* make an element for playback of video with subtitles embedded.
2211 * Only used for *raw* video streams.
2213 * +--------------------------------------------+
2215 * | +--------+ +-----------------+ |
2216 * | | queue | | subtitleoverlay | |
2217 * video--src sink---video_sink | |
2218 * | +--------+ | src--src
2219 * text------------------text_sink | |
2220 * | +-----------------+ |
2221 * +--------------------------------------------+
2224 static GstPlayTextChain *
2225 gen_text_chain (GstPlaySink * playsink)
2227 GstPlayTextChain *chain;
2230 GstPad *videosinkpad, *textsinkpad, *srcpad;
2232 chain = g_new0 (GstPlayTextChain, 1);
2233 chain->chain.playsink = playsink;
2235 GST_DEBUG_OBJECT (playsink, "making text chain %p", chain);
2237 chain->chain.bin = gst_bin_new ("tbin");
2238 bin = GST_BIN_CAST (chain->chain.bin);
2239 gst_object_ref_sink (bin);
2241 videosinkpad = textsinkpad = srcpad = NULL;
2243 /* first try to hook the text pad to the custom sink */
2244 if (playsink->text_sink) {
2245 GST_DEBUG_OBJECT (playsink, "trying configured textsink");
2246 chain->sink = try_element (playsink, playsink->text_sink, FALSE);
2249 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
2252 /* make sure the sparse subtitles don't participate in the preroll */
2253 g_object_set (elem, "async", FALSE, NULL);
2254 GST_DEBUG_OBJECT (playsink, "adding custom text sink");
2255 gst_bin_add (bin, chain->sink);
2256 /* NOTE streamsynchronizer needs streams decoupled */
2257 /* make a little queue */
2258 chain->queue = gst_element_factory_make ("queue", "subqueue");
2259 if (chain->queue == NULL) {
2260 post_missing_element_message (playsink, "queue");
2261 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2262 (_("Missing element '%s' - check your GStreamer installation."),
2263 "queue"), ("rendering might be suboptimal"));
2265 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
2266 "max-size-bytes", 0, "max-size-time", (gint64) GST_SECOND,
2267 "silent", TRUE, NULL);
2268 gst_bin_add (bin, chain->queue);
2270 /* we have a custom sink, this will be our textsinkpad */
2271 if (gst_element_link_pads_full (chain->queue, "src", chain->sink,
2272 "sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
2273 /* we're all fine now and we can add the sink to the chain */
2274 GST_DEBUG_OBJECT (playsink, "using custom text sink");
2275 textsinkpad = gst_element_get_static_pad (chain->queue, "sink");
2277 GST_WARNING_OBJECT (playsink,
2278 "can't find a sink pad on custom text sink");
2279 gst_bin_remove (bin, chain->sink);
2280 gst_bin_remove (bin, chain->queue);
2282 chain->queue = NULL;
2284 /* try to set sync to true but it's no biggie when we can't */
2285 if (chain->sink && (elem =
2286 gst_play_sink_find_property_sinks (playsink, chain->sink,
2287 "sync", G_TYPE_BOOLEAN)))
2288 g_object_set (elem, "sync", TRUE, NULL);
2291 gst_bin_remove (bin, chain->sink);
2293 GST_WARNING_OBJECT (playsink,
2294 "can't find async property in custom text sink");
2297 if (textsinkpad == NULL) {
2298 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2299 (_("Custom text sink element is not usable.")),
2300 ("fallback to default subtitleoverlay"));
2304 if (textsinkpad == NULL) {
2305 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
2306 /* make a little queue */
2307 chain->queue = gst_element_factory_make ("queue", "vqueue");
2308 if (chain->queue == NULL) {
2309 post_missing_element_message (playsink, "queue");
2310 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2311 (_("Missing element '%s' - check your GStreamer installation."),
2312 "queue"), ("video rendering might be suboptimal"));
2314 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
2315 "max-size-bytes", 0, "max-size-time", (gint64) 0,
2316 "silent", TRUE, NULL);
2317 gst_bin_add (bin, chain->queue);
2318 videosinkpad = gst_element_get_static_pad (chain->queue, "sink");
2322 gst_element_factory_make ("subtitleoverlay", "suboverlay");
2323 if (chain->overlay == NULL) {
2324 post_missing_element_message (playsink, "subtitleoverlay");
2325 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2326 (_("Missing element '%s' - check your GStreamer installation."),
2327 "subtitleoverlay"), ("subtitle rendering disabled"));
2329 GstElement *element;
2331 gst_bin_add (bin, chain->overlay);
2333 g_object_set (G_OBJECT (chain->overlay), "silent", FALSE, NULL);
2334 if (playsink->font_desc) {
2335 g_object_set (G_OBJECT (chain->overlay), "font-desc",
2336 playsink->font_desc, NULL);
2338 if (playsink->subtitle_encoding) {
2339 g_object_set (G_OBJECT (chain->overlay), "subtitle-encoding",
2340 playsink->subtitle_encoding, NULL);
2343 gst_element_link_pads_full (chain->queue, "src", chain->overlay,
2344 "video_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS);
2346 /* make another little queue to decouple streams */
2347 element = gst_element_factory_make ("queue", "subqueue");
2348 if (element == NULL) {
2349 post_missing_element_message (playsink, "queue");
2350 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2351 (_("Missing element '%s' - check your GStreamer installation."),
2352 "queue"), ("rendering might be suboptimal"));
2354 g_object_set (G_OBJECT (element), "max-size-buffers", 3,
2355 "max-size-bytes", 0, "max-size-time", (gint64) GST_SECOND,
2356 "silent", TRUE, NULL);
2357 gst_bin_add (bin, element);
2358 if (gst_element_link_pads_full (element, "src", chain->overlay,
2359 "subtitle_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
2360 textsinkpad = gst_element_get_static_pad (element, "sink");
2361 srcpad = gst_element_get_static_pad (chain->overlay, "src");
2363 gst_bin_remove (bin, chain->sink);
2364 gst_bin_remove (bin, chain->overlay);
2366 chain->overlay = NULL;
2367 gst_object_unref (videosinkpad);
2368 videosinkpad = NULL;
2375 if (videosinkpad == NULL) {
2376 /* if we still don't have a videosink, we don't have an overlay. the only
2377 * thing we can do is insert an identity and ghost the src
2379 chain->identity = gst_element_factory_make ("identity", "tidentity");
2380 if (chain->identity == NULL) {
2381 post_missing_element_message (playsink, "identity");
2382 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2383 (_("Missing element '%s' - check your GStreamer installation."),
2384 "identity"), (NULL));
2386 g_object_set (chain->identity, "signal-handoffs", FALSE, NULL);
2387 g_object_set (chain->identity, "silent", TRUE, NULL);
2388 gst_bin_add (bin, chain->identity);
2389 srcpad = gst_element_get_static_pad (chain->identity, "src");
2390 videosinkpad = gst_element_get_static_pad (chain->identity, "sink");
2394 /* expose the ghostpads */
2396 chain->videosinkpad = gst_ghost_pad_new ("sink", videosinkpad);
2397 gst_object_unref (videosinkpad);
2398 gst_element_add_pad (chain->chain.bin, chain->videosinkpad);
2401 chain->textsinkpad = gst_ghost_pad_new ("text_sink", textsinkpad);
2402 gst_object_unref (textsinkpad);
2404 gst_pad_set_event_function (chain->textsinkpad,
2405 GST_DEBUG_FUNCPTR (gst_play_sink_text_sink_event));
2406 gst_pad_set_chain_function (chain->textsinkpad,
2407 GST_DEBUG_FUNCPTR (gst_play_sink_text_sink_chain));
2409 gst_element_add_pad (chain->chain.bin, chain->textsinkpad);
2412 chain->srcpad = gst_ghost_pad_new ("src", srcpad);
2413 gst_object_unref (srcpad);
2415 gst_pad_set_event_function (chain->srcpad,
2416 GST_DEBUG_FUNCPTR (gst_play_sink_text_src_event));
2418 gst_element_add_pad (chain->chain.bin, chain->srcpad);
2425 notify_volume_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
2429 g_object_get (object, "volume", &vol, NULL);
2430 playsink->volume = vol;
2432 g_object_notify (G_OBJECT (playsink), "volume");
2436 notify_mute_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
2440 g_object_get (object, "mute", &mute, NULL);
2441 playsink->mute = mute;
2443 g_object_notify (G_OBJECT (playsink), "mute");
2446 /* make the chain that contains the elements needed to perform
2449 * We add a tee as the first element so that we can link the visualisation chain
2450 * to it when requested.
2452 * +-------------------------------------------------------------+
2454 * | +---------+ +----------+ +---------+ +---------+ |
2455 * | |audioconv| |audioscale| | volume | |audiosink| |
2456 * | +-srck src-sink src-sink src-sink | |
2457 * | | +---------+ +----------+ +---------+ +---------+ |
2459 * +-------------------------------------------------------------+
2461 static GstPlayAudioChain *
2462 gen_audio_chain (GstPlaySink * playsink, gboolean raw)
2464 GstPlayAudioChain *chain;
2466 gboolean have_volume;
2468 GstElement *head, *prev, *elem = NULL;
2470 chain = g_new0 (GstPlayAudioChain, 1);
2471 chain->chain.playsink = playsink;
2472 chain->chain.raw = raw;
2474 GST_DEBUG_OBJECT (playsink, "making audio chain %p", chain);
2476 if (playsink->audio_sink) {
2477 GST_DEBUG_OBJECT (playsink, "trying configured audiosink %" GST_PTR_FORMAT,
2478 playsink->audio_sink);
2479 chain->sink = try_element (playsink, playsink->audio_sink, FALSE);
2481 /* only try fallback if no specific sink was chosen */
2482 if (chain->sink == NULL) {
2483 GST_DEBUG_OBJECT (playsink, "trying autoaudiosink");
2484 elem = gst_element_factory_make ("autoaudiosink", "audiosink");
2485 chain->sink = try_element (playsink, elem, TRUE);
2487 if (chain->sink == NULL) {
2488 /* if default sink from config.h is different then try it too */
2489 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
2490 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_AUDIOSINK);
2491 elem = gst_element_factory_make (DEFAULT_AUDIOSINK, "audiosink");
2492 chain->sink = try_element (playsink, elem, TRUE);
2496 playsink->audio_sink = gst_object_ref (chain->sink);
2498 if (chain->sink == NULL)
2501 chain->chain.bin = gst_bin_new ("abin");
2502 bin = GST_BIN_CAST (chain->chain.bin);
2503 gst_object_ref_sink (bin);
2504 gst_bin_add (bin, chain->sink);
2506 /* we have to add a queue when we need to decouple for the video sink in
2507 * visualisations and for streamsynchronizer */
2508 GST_DEBUG_OBJECT (playsink, "adding audio queue");
2509 chain->queue = gst_element_factory_make ("queue", "aqueue");
2510 if (chain->queue == NULL) {
2511 post_missing_element_message (playsink, "queue");
2512 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2513 (_("Missing element '%s' - check your GStreamer installation."),
2514 "queue"), ("audio playback and visualizations might not work"));
2518 g_object_set (chain->queue, "silent", TRUE, NULL);
2519 gst_bin_add (bin, chain->queue);
2520 prev = head = chain->queue;
2523 /* find ts-offset element */
2524 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
2525 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
2528 /* check if the sink, or something within the sink, has the volume property.
2529 * If it does we don't need to add a volume element. */
2531 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
2533 chain->notify_volume_id = chain->notify_mute_id = 0;
2535 chain->volume = elem;
2537 chain->notify_volume_id = g_signal_connect (chain->volume, "notify::volume",
2538 G_CALLBACK (notify_volume_cb), playsink);
2540 GST_DEBUG_OBJECT (playsink, "the sink has a volume property");
2542 chain->sink_volume = TRUE;
2543 /* if the sink also has a mute property we can use this as well. We'll only
2544 * use the mute property if there is a volume property. We can simulate the
2545 * mute with the volume otherwise. */
2547 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
2550 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
2551 chain->notify_mute_id = g_signal_connect (chain->mute, "notify::mute",
2552 G_CALLBACK (notify_mute_cb), playsink);
2554 /* use the sink to control the volume and mute */
2555 if (playsink->volume_changed) {
2556 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2557 playsink->volume_changed = FALSE;
2559 if (playsink->mute_changed) {
2561 g_object_set (chain->mute, "mute", playsink->mute, NULL);
2564 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
2566 playsink->mute_changed = FALSE;
2569 /* no volume, we need to add a volume element when we can */
2570 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
2571 have_volume = FALSE;
2572 chain->sink_volume = FALSE;
2575 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO) || (!have_volume
2576 && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME))) {
2577 gboolean use_converters = !(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO);
2578 gboolean use_volume =
2579 !have_volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME);
2580 GST_DEBUG_OBJECT (playsink,
2581 "creating audioconvert with use-converters %d, use-volume %d",
2582 use_converters, use_volume);
2584 g_object_new (GST_TYPE_PLAY_SINK_AUDIO_CONVERT, "name", "aconv",
2585 "use-converters", use_converters, "use-volume", use_volume, NULL);
2586 gst_bin_add (bin, chain->conv);
2588 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
2589 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
2596 if (!have_volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
2597 GstPlaySinkAudioConvert *conv =
2598 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
2601 chain->volume = conv->volume;
2604 chain->notify_volume_id =
2605 g_signal_connect (chain->volume, "notify::volume",
2606 G_CALLBACK (notify_volume_cb), playsink);
2608 /* volume also has the mute property */
2609 chain->mute = chain->volume;
2610 chain->notify_mute_id = g_signal_connect (chain->mute, "notify::mute",
2611 G_CALLBACK (notify_mute_cb), playsink);
2613 /* configure with the latest volume and mute */
2614 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume,
2616 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
2622 /* we only have to link to the previous element if we have something in
2623 * front of the sink */
2624 GST_DEBUG_OBJECT (playsink, "linking to sink");
2625 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
2626 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
2630 /* post a warning if we have no way to configure the volume */
2632 GST_ELEMENT_WARNING (playsink, STREAM, NOT_IMPLEMENTED,
2633 (_("No volume control found")), ("Volume/mute is not available"));
2636 /* and ghost the sinkpad of the headmost element */
2637 GST_DEBUG_OBJECT (playsink, "ghosting sink pad");
2638 pad = gst_element_get_static_pad (head, "sink");
2639 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
2641 /* sending audio/video flushes break stream changes when the pipeline
2642 * is paused and played again in 0.10 */
2644 gst_pad_set_event_function (chain->sinkpad,
2645 GST_DEBUG_FUNCPTR (gst_play_sink_audio_sink_event));
2646 gst_pad_set_chain_function (chain->sinkpad,
2647 GST_DEBUG_FUNCPTR (gst_play_sink_audio_sink_chain));
2650 gst_object_unref (pad);
2651 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
2658 if (!elem && !playsink->audio_sink) {
2659 post_missing_element_message (playsink, "autoaudiosink");
2660 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
2661 post_missing_element_message (playsink, DEFAULT_AUDIOSINK);
2662 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2663 (_("Both autoaudiosink and %s elements are missing."),
2664 DEFAULT_AUDIOSINK), (NULL));
2666 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2667 (_("The autoaudiosink element is missing.")), (NULL));
2670 if (playsink->audio_sink) {
2671 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2672 (_("Configured audiosink %s is not working."),
2673 GST_ELEMENT_NAME (playsink->audio_sink)), (NULL));
2674 } else if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
2675 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2676 (_("Both autoaudiosink and %s elements are not working."),
2677 DEFAULT_AUDIOSINK), (NULL));
2679 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2680 (_("The autoaudiosink element is not working.")), (NULL));
2683 free_chain ((GstPlayChain *) chain);
2688 GST_ELEMENT_ERROR (playsink, CORE, PAD,
2689 (NULL), ("Failed to configure the audio sink."));
2690 /* checking sink made it READY */
2691 gst_element_set_state (chain->sink, GST_STATE_NULL);
2692 /* Remove chain from the bin to allow reuse later */
2693 gst_bin_remove (bin, chain->sink);
2694 free_chain ((GstPlayChain *) chain);
2700 setup_audio_chain (GstPlaySink * playsink, gboolean raw)
2703 GstPlayAudioChain *chain;
2704 GstStateChangeReturn ret;
2705 GstPlaySinkAudioConvert *conv;
2707 chain = playsink->audiochain;
2708 conv = GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
2710 chain->chain.raw = raw;
2712 /* if the chain was active we don't do anything */
2713 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
2716 /* try to set the sink element to READY again */
2717 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
2718 if (ret == GST_STATE_CHANGE_FAILURE)
2721 /* find ts-offset element */
2722 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
2723 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
2726 /* Disconnect signals */
2727 disconnect_audio_chain (chain, playsink);
2729 /* check if the sink, or something within the sink, has the volume property.
2730 * If it does we don't need to add a volume element. */
2732 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
2735 chain->volume = elem;
2737 if (playsink->volume_changed) {
2738 GST_DEBUG_OBJECT (playsink, "the sink has a volume property, setting %f",
2740 /* use the sink to control the volume */
2741 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2742 playsink->volume_changed = FALSE;
2745 chain->notify_volume_id = g_signal_connect (chain->volume, "notify::volume",
2746 G_CALLBACK (notify_volume_cb), playsink);
2747 /* if the sink also has a mute property we can use this as well. We'll only
2748 * use the mute property if there is a volume property. We can simulate the
2749 * mute with the volume otherwise. */
2751 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
2754 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
2755 chain->notify_mute_id = g_signal_connect (chain->mute, "notify::mute",
2756 G_CALLBACK (notify_mute_cb), playsink);
2759 g_object_set (chain->conv, "use-volume", FALSE, NULL);
2761 /* no volume, we need to add a volume element when we can */
2762 g_object_set (chain->conv, "use-volume",
2763 ! !(playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME), NULL);
2764 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
2766 if (conv->volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
2767 chain->volume = conv->volume;
2768 chain->mute = chain->volume;
2770 chain->notify_volume_id =
2771 g_signal_connect (chain->volume, "notify::volume",
2772 G_CALLBACK (notify_volume_cb), playsink);
2774 chain->notify_mute_id = g_signal_connect (chain->mute, "notify::mute",
2775 G_CALLBACK (notify_mute_cb), playsink);
2777 /* configure with the latest volume and mute */
2778 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2779 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
2782 GST_DEBUG_OBJECT (playsink, "reusing existing volume element");
2788 * +-------------------------------------------------------------------+
2790 * | +----------+ +------------+ +----------+ +-------+ |
2791 * | | visqueue | | audioconv | | audiores | | vis | |
2792 * | +-sink src-sink + samp src-sink src-sink src-+ |
2793 * | | +----------+ +------------+ +----------+ +-------+ | |
2795 * +-------------------------------------------------------------------+
2798 static GstPlayVisChain *
2799 gen_vis_chain (GstPlaySink * playsink)
2801 GstPlayVisChain *chain;
2807 chain = g_new0 (GstPlayVisChain, 1);
2808 chain->chain.playsink = playsink;
2810 GST_DEBUG_OBJECT (playsink, "making vis chain %p", chain);
2812 chain->chain.bin = gst_bin_new ("visbin");
2813 bin = GST_BIN_CAST (chain->chain.bin);
2814 gst_object_ref_sink (bin);
2816 /* we're queuing raw audio here, we can remove this queue when we can disable
2817 * async behaviour in the video sink. */
2818 chain->queue = gst_element_factory_make ("queue", "visqueue");
2819 if (chain->queue == NULL)
2821 g_object_set (chain->queue, "silent", TRUE, NULL);
2822 gst_bin_add (bin, chain->queue);
2824 chain->conv = gst_element_factory_make ("audioconvert", "aconv");
2825 if (chain->conv == NULL)
2826 goto no_audioconvert;
2827 gst_bin_add (bin, chain->conv);
2829 chain->resample = gst_element_factory_make ("audioresample", "aresample");
2830 if (chain->resample == NULL)
2831 goto no_audioresample;
2832 gst_bin_add (bin, chain->resample);
2834 /* this pad will be used for blocking the dataflow and switching the vis
2835 * plugin, we block right after the queue, this makes it possible for the
2836 * resample and convert to convert to a format supported by the new vis
2838 chain->blockpad = gst_element_get_static_pad (chain->queue, "src");
2839 /* this is the pad where the vis is linked to */
2840 chain->vispeerpad = gst_element_get_static_pad (chain->resample, "src");
2842 if (playsink->visualisation) {
2843 GST_DEBUG_OBJECT (playsink, "trying configure vis");
2844 chain->vis = try_element (playsink, playsink->visualisation, FALSE);
2846 if (chain->vis == NULL) {
2847 GST_DEBUG_OBJECT (playsink, "trying goom");
2848 elem = gst_element_factory_make ("goom", "vis");
2849 chain->vis = try_element (playsink, elem, TRUE);
2851 if (chain->vis == NULL)
2854 gst_bin_add (bin, chain->vis);
2856 res = gst_element_link_pads_full (chain->queue, "src", chain->conv, "sink",
2857 GST_PAD_LINK_CHECK_NOTHING);
2859 gst_element_link_pads_full (chain->conv, "src", chain->resample, "sink",
2860 GST_PAD_LINK_CHECK_NOTHING);
2862 gst_element_link_pads_full (chain->resample, "src", chain->vis, "sink",
2863 GST_PAD_LINK_CHECK_NOTHING);
2867 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
2868 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
2870 pad = gst_element_get_static_pad (chain->queue, "sink");
2871 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
2872 gst_object_unref (pad);
2873 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
2875 chain->srcpad = gst_ghost_pad_new ("src", chain->vissrcpad);
2876 gst_element_add_pad (chain->chain.bin, chain->srcpad);
2883 post_missing_element_message (playsink, "queue");
2884 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2885 (_("Missing element '%s' - check your GStreamer installation."),
2887 free_chain ((GstPlayChain *) chain);
2892 post_missing_element_message (playsink, "audioconvert");
2893 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2894 (_("Missing element '%s' - check your GStreamer installation."),
2895 "audioconvert"), ("make sure audioconvert isn't blacklisted"));
2896 free_chain ((GstPlayChain *) chain);
2901 post_missing_element_message (playsink, "audioresample");
2902 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2903 (_("Missing element '%s' - check your GStreamer installation."),
2904 "audioresample"), (NULL));
2905 free_chain ((GstPlayChain *) chain);
2910 post_missing_element_message (playsink, "goom");
2911 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2912 (_("Missing element '%s' - check your GStreamer installation."),
2914 free_chain ((GstPlayChain *) chain);
2919 GST_ELEMENT_ERROR (playsink, CORE, PAD,
2920 (NULL), ("Failed to configure the visualisation element."));
2921 /* element made it to READY */
2922 gst_element_set_state (chain->vis, GST_STATE_NULL);
2923 free_chain ((GstPlayChain *) chain);
2928 /* this function is called when all the request pads are requested and when we
2929 * have to construct the final pipeline. Based on the flags we construct the
2930 * final output pipelines.
2933 gst_play_sink_do_reconfigure (GstPlaySink * playsink)
2936 gboolean need_audio, need_video, need_deinterlace, need_vis, need_text;
2938 GST_DEBUG_OBJECT (playsink, "reconfiguring");
2940 /* assume we need nothing */
2941 need_audio = need_video = need_deinterlace = need_vis = need_text = FALSE;
2943 GST_PLAY_SINK_LOCK (playsink);
2944 GST_OBJECT_LOCK (playsink);
2945 /* get flags, there are protected with the object lock */
2946 flags = playsink->flags;
2947 GST_OBJECT_UNLOCK (playsink);
2949 /* figure out which components we need */
2950 if (flags & GST_PLAY_FLAG_TEXT && playsink->text_pad) {
2951 /* we have subtitles and we are requested to show it */
2955 if (((flags & GST_PLAY_FLAG_VIDEO)
2956 || (flags & GST_PLAY_FLAG_NATIVE_VIDEO)) && playsink->video_pad) {
2957 /* we have video and we are requested to show it */
2960 /* we only deinterlace if native video is not requested and
2961 * we have raw video */
2962 if ((flags & GST_PLAY_FLAG_DEINTERLACE)
2963 && !(flags & GST_PLAY_FLAG_NATIVE_VIDEO) && playsink->video_pad_raw)
2964 need_deinterlace = TRUE;
2967 if (playsink->audio_pad) {
2968 if ((flags & GST_PLAY_FLAG_AUDIO) || (flags & GST_PLAY_FLAG_NATIVE_AUDIO)) {
2971 if (playsink->audio_pad_raw) {
2972 /* only can do vis with raw uncompressed audio */
2973 if (flags & GST_PLAY_FLAG_VIS && !need_video) {
2974 /* also add video when we add visualisation */
2981 /* we have a text_pad and we need text rendering, in this case we need a
2982 * video_pad to combine the video with the text or visualizations */
2983 if (need_text && !need_video && !playsink->text_sink) {
2984 if (playsink->video_pad) {
2986 } else if (need_audio) {
2987 GST_ELEMENT_WARNING (playsink, STREAM, FORMAT,
2988 (_("Can't play a text file without video or visualizations.")),
2989 ("Have text pad but no video pad or visualizations"));
2992 GST_ELEMENT_ERROR (playsink, STREAM, FORMAT,
2993 (_("Can't play a text file without video or visualizations.")),
2994 ("Have text pad but no video pad or visualizations"));
2995 GST_PLAY_SINK_UNLOCK (playsink);
3000 GST_DEBUG_OBJECT (playsink, "audio:%d, video:%d, vis:%d, text:%d", need_audio,
3001 need_video, need_vis, need_text);
3003 /* set up video pipeline */
3005 gboolean raw, async;
3007 /* we need a raw sink when we do vis or when we have a raw pad */
3008 raw = need_vis ? TRUE : playsink->video_pad_raw;
3009 /* we try to set the sink async=FALSE when we need vis, this way we can
3010 * avoid a queue in the audio chain. */
3013 GST_DEBUG_OBJECT (playsink, "adding video, raw %d",
3014 playsink->video_pad_raw);
3016 if (playsink->videochain) {
3017 /* try to reactivate the chain */
3018 if ((playsink->video_sink
3019 && playsink->video_sink != playsink->videochain->sink)
3020 || !setup_video_chain (playsink, raw, async)) {
3021 if (playsink->video_sinkpad_stream_synchronizer) {
3022 gst_element_release_request_pad (GST_ELEMENT_CAST
3023 (playsink->stream_synchronizer),
3024 playsink->video_sinkpad_stream_synchronizer);
3025 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3026 playsink->video_sinkpad_stream_synchronizer = NULL;
3027 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3028 playsink->video_srcpad_stream_synchronizer = NULL;
3031 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3033 /* Remove the sink from the bin to keep its state
3034 * and unparent it to allow reuse */
3035 if (playsink->videochain->sink) {
3036 if (playsink->videochain->sink != playsink->video_sink)
3037 gst_element_set_state (playsink->videochain->sink, GST_STATE_NULL);
3038 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
3039 playsink->videochain->sink);
3042 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3043 free_chain ((GstPlayChain *) playsink->videochain);
3044 playsink->videochain = NULL;
3046 GST_OBJECT_LOCK (playsink);
3047 if (playsink->overlay_element)
3048 gst_object_unref (playsink->overlay_element);
3049 playsink->overlay_element = NULL;
3051 if (playsink->colorbalance_element) {
3052 g_signal_handler_disconnect (playsink->colorbalance_element,
3053 playsink->colorbalance_value_changed_id);
3054 playsink->colorbalance_value_changed_id = 0;
3055 gst_object_unref (playsink->colorbalance_element);
3057 playsink->colorbalance_element = NULL;
3058 GST_OBJECT_UNLOCK (playsink);
3062 if (!playsink->videochain)
3063 playsink->videochain = gen_video_chain (playsink, raw, async);
3064 if (!playsink->videochain)
3067 if (!playsink->video_sinkpad_stream_synchronizer) {
3068 GValue item = { 0, };
3071 playsink->video_sinkpad_stream_synchronizer =
3072 gst_element_get_request_pad (GST_ELEMENT_CAST
3073 (playsink->stream_synchronizer), "sink_%u");
3074 it = gst_pad_iterate_internal_links
3075 (playsink->video_sinkpad_stream_synchronizer);
3077 gst_iterator_next (it, &item);
3078 playsink->video_srcpad_stream_synchronizer = g_value_dup_object (&item);
3079 g_value_unset (&item);
3080 g_assert (playsink->video_srcpad_stream_synchronizer);
3081 gst_iterator_free (it);
3084 if (playsink->video_pad)
3085 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad),
3086 playsink->video_sinkpad_stream_synchronizer);
3088 if (need_deinterlace) {
3089 if (!playsink->videodeinterlacechain)
3090 playsink->videodeinterlacechain =
3091 gen_video_deinterlace_chain (playsink);
3092 if (!playsink->videodeinterlacechain)
3095 GST_DEBUG_OBJECT (playsink, "adding video deinterlace chain");
3097 GST_DEBUG_OBJECT (playsink, "setting up deinterlacing chain");
3099 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
3100 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
3102 gst_pad_unlink (playsink->video_srcpad_stream_synchronizer,
3103 playsink->videochain->sinkpad);
3104 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3105 playsink->videodeinterlacechain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3107 if (playsink->videodeinterlacechain) {
3108 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3109 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
3114 GST_DEBUG_OBJECT (playsink, "adding video chain");
3115 add_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
3116 activate_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
3117 /* if we are not part of vis or subtitles, set the ghostpad target */
3118 if (!need_vis && !need_text && (!playsink->textchain
3119 || !playsink->text_pad)) {
3120 GST_DEBUG_OBJECT (playsink, "ghosting video sinkpad");
3121 gst_pad_unlink (playsink->video_srcpad_stream_synchronizer,
3122 playsink->videochain->sinkpad);
3123 if (playsink->videodeinterlacechain
3124 && playsink->videodeinterlacechain->srcpad)
3125 gst_pad_unlink (playsink->videodeinterlacechain->srcpad,
3126 playsink->videochain->sinkpad);
3127 if (need_deinterlace)
3128 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
3129 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3131 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3132 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3135 GST_DEBUG_OBJECT (playsink, "no video needed");
3136 if (playsink->videochain) {
3137 GST_DEBUG_OBJECT (playsink, "removing video chain");
3138 if (playsink->vischain) {
3141 GST_DEBUG_OBJECT (playsink, "unlinking vis chain");
3143 /* also had visualisation, release the tee srcpad before we then
3144 * unlink the video from it */
3145 if (playsink->audio_tee_vissrc) {
3146 gst_element_release_request_pad (playsink->audio_tee,
3147 playsink->audio_tee_vissrc);
3148 gst_object_unref (playsink->audio_tee_vissrc);
3149 playsink->audio_tee_vissrc = NULL;
3152 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
3153 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
3156 if (playsink->video_sinkpad_stream_synchronizer) {
3157 gst_element_release_request_pad (GST_ELEMENT_CAST
3158 (playsink->stream_synchronizer),
3159 playsink->video_sinkpad_stream_synchronizer);
3160 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3161 playsink->video_sinkpad_stream_synchronizer = NULL;
3162 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3163 playsink->video_srcpad_stream_synchronizer = NULL;
3166 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3167 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3168 if (playsink->videochain->ts_offset)
3169 gst_object_unref (playsink->videochain->ts_offset);
3170 playsink->videochain->ts_offset = NULL;
3173 if (playsink->videodeinterlacechain) {
3174 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3175 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3178 if (playsink->video_pad)
3179 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
3181 GST_OBJECT_LOCK (playsink);
3182 if (playsink->overlay_element)
3183 gst_object_unref (playsink->overlay_element);
3184 playsink->overlay_element = NULL;
3186 if (playsink->colorbalance_element) {
3187 g_signal_handler_disconnect (playsink->colorbalance_element,
3188 playsink->colorbalance_value_changed_id);
3189 playsink->colorbalance_value_changed_id = 0;
3190 gst_object_unref (playsink->colorbalance_element);
3192 playsink->colorbalance_element = NULL;
3193 GST_OBJECT_UNLOCK (playsink);
3195 if (playsink->video_sink)
3196 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
3202 GST_DEBUG_OBJECT (playsink, "adding audio");
3204 /* get a raw sink if we are asked for a raw pad */
3205 raw = playsink->audio_pad_raw;
3207 if (playsink->audiochain) {
3208 /* try to reactivate the chain */
3209 if ((playsink->audio_sink
3210 && playsink->audio_sink != playsink->audiochain->sink)
3211 || !setup_audio_chain (playsink, raw)) {
3212 GST_DEBUG_OBJECT (playsink, "removing current audio chain");
3213 if (playsink->audio_tee_asrc) {
3214 gst_element_release_request_pad (playsink->audio_tee,
3215 playsink->audio_tee_asrc);
3216 gst_object_unref (playsink->audio_tee_asrc);
3217 playsink->audio_tee_asrc = NULL;
3220 if (playsink->audio_sinkpad_stream_synchronizer) {
3221 gst_element_release_request_pad (GST_ELEMENT_CAST
3222 (playsink->stream_synchronizer),
3223 playsink->audio_sinkpad_stream_synchronizer);
3224 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
3225 playsink->audio_sinkpad_stream_synchronizer = NULL;
3226 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
3227 playsink->audio_srcpad_stream_synchronizer = NULL;
3230 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3232 /* Remove the sink from the bin to keep its state
3233 * and unparent it to allow reuse */
3234 if (playsink->audiochain->sink) {
3235 if (playsink->audiochain->sink != playsink->audio_sink)
3236 gst_element_set_state (playsink->audiochain->sink, GST_STATE_NULL);
3237 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
3238 playsink->audiochain->sink);
3241 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3242 disconnect_audio_chain (playsink->audiochain, playsink);
3243 playsink->audiochain->volume = NULL;
3244 playsink->audiochain->mute = NULL;
3245 if (playsink->audiochain->ts_offset)
3246 gst_object_unref (playsink->audiochain->ts_offset);
3247 playsink->audiochain->ts_offset = NULL;
3248 free_chain ((GstPlayChain *) playsink->audiochain);
3249 playsink->audiochain = NULL;
3250 playsink->volume_changed = playsink->mute_changed = FALSE;
3254 if (!playsink->audiochain) {
3255 GST_DEBUG_OBJECT (playsink, "creating new audio chain");
3256 playsink->audiochain = gen_audio_chain (playsink, raw);
3259 if (!playsink->audiochain)
3262 if (!playsink->audio_sinkpad_stream_synchronizer) {
3263 GValue item = { 0, };
3266 playsink->audio_sinkpad_stream_synchronizer =
3267 gst_element_get_request_pad (GST_ELEMENT_CAST
3268 (playsink->stream_synchronizer), "sink_%u");
3269 it = gst_pad_iterate_internal_links
3270 (playsink->audio_sinkpad_stream_synchronizer);
3272 gst_iterator_next (it, &item);
3273 playsink->audio_srcpad_stream_synchronizer = g_value_dup_object (&item);
3274 g_value_unset (&item);
3275 g_assert (playsink->audio_srcpad_stream_synchronizer);
3276 gst_iterator_free (it);
3279 if (playsink->audiochain) {
3280 GST_DEBUG_OBJECT (playsink, "adding audio chain");
3281 if (playsink->audio_tee_asrc == NULL) {
3282 playsink->audio_tee_asrc =
3283 gst_element_get_request_pad (playsink->audio_tee, "src_%u");
3285 add_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
3286 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
3287 gst_pad_link_full (playsink->audio_tee_asrc,
3288 playsink->audio_sinkpad_stream_synchronizer,
3289 GST_PAD_LINK_CHECK_NOTHING);
3290 gst_pad_link_full (playsink->audio_srcpad_stream_synchronizer,
3291 playsink->audiochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3294 GST_DEBUG_OBJECT (playsink, "no audio needed");
3295 /* we have no audio or we are requested to not play audio */
3296 if (playsink->audiochain) {
3297 GST_DEBUG_OBJECT (playsink, "removing audio chain");
3298 /* release the audio pad */
3299 if (playsink->audio_tee_asrc) {
3300 gst_element_release_request_pad (playsink->audio_tee,
3301 playsink->audio_tee_asrc);
3302 gst_object_unref (playsink->audio_tee_asrc);
3303 playsink->audio_tee_asrc = NULL;
3306 if (playsink->audio_sinkpad_stream_synchronizer) {
3307 gst_element_release_request_pad (GST_ELEMENT_CAST
3308 (playsink->stream_synchronizer),
3309 playsink->audio_sinkpad_stream_synchronizer);
3310 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
3311 playsink->audio_sinkpad_stream_synchronizer = NULL;
3312 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
3313 playsink->audio_srcpad_stream_synchronizer = NULL;
3316 if (playsink->audiochain->sink_volume) {
3317 disconnect_audio_chain (playsink->audiochain, playsink);
3318 playsink->audiochain->volume = NULL;
3319 playsink->audiochain->mute = NULL;
3320 if (playsink->audiochain->ts_offset)
3321 gst_object_unref (playsink->audiochain->ts_offset);
3322 playsink->audiochain->ts_offset = NULL;
3324 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3325 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3328 if (playsink->audio_sink)
3329 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
3335 if (!playsink->vischain)
3336 playsink->vischain = gen_vis_chain (playsink);
3338 GST_DEBUG_OBJECT (playsink, "adding visualisation");
3340 if (playsink->vischain) {
3341 GST_DEBUG_OBJECT (playsink, "setting up vis chain");
3343 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
3344 add_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
3345 activate_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
3346 if (playsink->audio_tee_vissrc == NULL) {
3347 playsink->audio_tee_vissrc =
3348 gst_element_get_request_pad (playsink->audio_tee, "src_%u");
3350 gst_pad_link_full (playsink->audio_tee_vissrc,
3351 playsink->vischain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3352 gst_pad_link_full (srcpad, playsink->video_sinkpad_stream_synchronizer,
3353 GST_PAD_LINK_CHECK_NOTHING);
3354 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3355 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3356 gst_object_unref (srcpad);
3359 GST_DEBUG_OBJECT (playsink, "no vis needed");
3360 if (playsink->vischain) {
3361 if (playsink->audio_tee_vissrc) {
3362 gst_element_release_request_pad (playsink->audio_tee,
3363 playsink->audio_tee_vissrc);
3364 gst_object_unref (playsink->audio_tee_vissrc);
3365 playsink->audio_tee_vissrc = NULL;
3367 GST_DEBUG_OBJECT (playsink, "removing vis chain");
3368 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3369 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3374 GST_DEBUG_OBJECT (playsink, "adding text");
3375 if (!playsink->textchain) {
3376 GST_DEBUG_OBJECT (playsink, "creating text chain");
3377 playsink->textchain = gen_text_chain (playsink);
3379 if (playsink->textchain) {
3382 GST_DEBUG_OBJECT (playsink, "adding text chain");
3383 if (playsink->textchain->overlay)
3384 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", FALSE,
3386 add_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
3388 if (!playsink->text_sinkpad_stream_synchronizer) {
3389 GValue item = { 0, };
3391 playsink->text_sinkpad_stream_synchronizer =
3392 gst_element_get_request_pad (GST_ELEMENT_CAST
3393 (playsink->stream_synchronizer), "sink_%u");
3394 it = gst_pad_iterate_internal_links
3395 (playsink->text_sinkpad_stream_synchronizer);
3397 gst_iterator_next (it, &item);
3398 playsink->text_srcpad_stream_synchronizer = g_value_dup_object (&item);
3399 g_value_unset (&item);
3400 g_assert (playsink->text_srcpad_stream_synchronizer);
3401 gst_iterator_free (it);
3403 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad),
3404 playsink->text_sinkpad_stream_synchronizer);
3405 gst_pad_link_full (playsink->text_srcpad_stream_synchronizer,
3406 playsink->textchain->textsinkpad, GST_PAD_LINK_CHECK_NOTHING);
3409 if (need_vis || need_video) {
3414 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
3415 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
3416 gst_pad_link_full (srcpad, playsink->textchain->videosinkpad,
3417 GST_PAD_LINK_CHECK_NOTHING);
3418 gst_object_unref (srcpad);
3420 if (need_deinterlace)
3421 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
3422 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
3424 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3425 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
3427 gst_pad_link_full (playsink->textchain->srcpad,
3428 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3431 activate_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
3434 GST_DEBUG_OBJECT (playsink, "no text needed");
3435 /* we have no subtitles/text or we are requested to not show them */
3437 if (playsink->text_sinkpad_stream_synchronizer) {
3438 gst_element_release_request_pad (GST_ELEMENT_CAST
3439 (playsink->stream_synchronizer),
3440 playsink->text_sinkpad_stream_synchronizer);
3441 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
3442 playsink->text_sinkpad_stream_synchronizer = NULL;
3443 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
3444 playsink->text_srcpad_stream_synchronizer = NULL;
3447 if (playsink->textchain) {
3448 if (playsink->text_pad == NULL) {
3449 /* no text pad, remove the chain entirely */
3450 GST_DEBUG_OBJECT (playsink, "removing text chain");
3451 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3452 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3454 /* we have a chain and a textpad, turn the subtitles off */
3455 GST_DEBUG_OBJECT (playsink, "turning off the text");
3456 if (playsink->textchain->overlay)
3457 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", TRUE,
3461 if (!need_video && playsink->video_pad) {
3462 if (playsink->video_sinkpad_stream_synchronizer) {
3463 gst_element_release_request_pad (GST_ELEMENT_CAST
3464 (playsink->stream_synchronizer),
3465 playsink->video_sinkpad_stream_synchronizer);
3466 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3467 playsink->video_sinkpad_stream_synchronizer = NULL;
3468 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3469 playsink->video_srcpad_stream_synchronizer = NULL;
3472 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
3475 if (playsink->text_pad && !playsink->textchain)
3476 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad), NULL);
3478 if (playsink->text_sink)
3479 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
3481 update_av_offset (playsink);
3482 do_async_done (playsink);
3483 GST_PLAY_SINK_UNLOCK (playsink);
3490 /* gen_ chain already posted error */
3491 GST_DEBUG_OBJECT (playsink, "failed to setup chain");
3492 GST_PLAY_SINK_UNLOCK (playsink);
3498 * gst_play_sink_set_flags:
3499 * @playsink: a #GstPlaySink
3500 * @flags: #GstPlayFlags
3502 * Configure @flags on @playsink. The flags control the behaviour of @playsink
3503 * when constructing the sink pipelins.
3505 * Returns: TRUE if the flags could be configured.
3508 gst_play_sink_set_flags (GstPlaySink * playsink, GstPlayFlags flags)
3510 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), FALSE);
3512 GST_OBJECT_LOCK (playsink);
3513 playsink->flags = flags;
3514 GST_OBJECT_UNLOCK (playsink);
3520 * gst_play_sink_get_flags:
3521 * @playsink: a #GstPlaySink
3523 * Get the flags of @playsink. That flags control the behaviour of the sink when
3524 * it constructs the sink pipelines.
3526 * Returns: the currently configured #GstPlayFlags.
3529 gst_play_sink_get_flags (GstPlaySink * playsink)
3533 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), 0);
3535 GST_OBJECT_LOCK (playsink);
3536 res = playsink->flags;
3537 GST_OBJECT_UNLOCK (playsink);
3543 gst_play_sink_set_font_desc (GstPlaySink * playsink, const gchar * desc)
3545 GstPlayTextChain *chain;
3547 GST_PLAY_SINK_LOCK (playsink);
3548 chain = (GstPlayTextChain *) playsink->textchain;
3549 g_free (playsink->font_desc);
3550 playsink->font_desc = g_strdup (desc);
3551 if (chain && chain->overlay) {
3552 g_object_set (chain->overlay, "font-desc", desc, NULL);
3554 GST_PLAY_SINK_UNLOCK (playsink);
3558 gst_play_sink_get_font_desc (GstPlaySink * playsink)
3560 gchar *result = NULL;
3561 GstPlayTextChain *chain;
3563 GST_PLAY_SINK_LOCK (playsink);
3564 chain = (GstPlayTextChain *) playsink->textchain;
3565 if (chain && chain->overlay) {
3566 g_object_get (chain->overlay, "font-desc", &result, NULL);
3567 playsink->font_desc = g_strdup (result);
3569 result = g_strdup (playsink->font_desc);
3571 GST_PLAY_SINK_UNLOCK (playsink);
3577 gst_play_sink_set_subtitle_encoding (GstPlaySink * playsink,
3578 const gchar * encoding)
3580 GstPlayTextChain *chain;
3582 GST_PLAY_SINK_LOCK (playsink);
3583 chain = (GstPlayTextChain *) playsink->textchain;
3584 g_free (playsink->subtitle_encoding);
3585 playsink->subtitle_encoding = g_strdup (encoding);
3586 if (chain && chain->overlay) {
3587 g_object_set (chain->overlay, "subtitle-encoding", encoding, NULL);
3589 GST_PLAY_SINK_UNLOCK (playsink);
3593 gst_play_sink_get_subtitle_encoding (GstPlaySink * playsink)
3595 gchar *result = NULL;
3596 GstPlayTextChain *chain;
3598 GST_PLAY_SINK_LOCK (playsink);
3599 chain = (GstPlayTextChain *) playsink->textchain;
3600 if (chain && chain->overlay) {
3601 g_object_get (chain->overlay, "subtitle-encoding", &result, NULL);
3602 playsink->subtitle_encoding = g_strdup (result);
3604 result = g_strdup (playsink->subtitle_encoding);
3606 GST_PLAY_SINK_UNLOCK (playsink);
3612 update_av_offset (GstPlaySink * playsink)
3615 GstPlayAudioChain *achain;
3616 GstPlayVideoChain *vchain;
3618 av_offset = playsink->av_offset;
3619 achain = (GstPlayAudioChain *) playsink->audiochain;
3620 vchain = (GstPlayVideoChain *) playsink->videochain;
3622 if (achain && vchain && achain->ts_offset && vchain->ts_offset) {
3623 g_object_set (achain->ts_offset,
3624 "ts-offset", MAX (G_GINT64_CONSTANT (0), -av_offset), NULL);
3625 g_object_set (vchain->ts_offset,
3626 "ts-offset", MAX (G_GINT64_CONSTANT (0), av_offset), NULL);
3628 GST_LOG_OBJECT (playsink, "no ts_offset elements");
3633 gst_play_sink_set_av_offset (GstPlaySink * playsink, gint64 av_offset)
3635 GST_PLAY_SINK_LOCK (playsink);
3636 playsink->av_offset = av_offset;
3637 update_av_offset (playsink);
3638 GST_PLAY_SINK_UNLOCK (playsink);
3642 gst_play_sink_get_av_offset (GstPlaySink * playsink)
3646 GST_PLAY_SINK_LOCK (playsink);
3647 result = playsink->av_offset;
3648 GST_PLAY_SINK_UNLOCK (playsink);
3654 * gst_play_sink_get_last_sample:
3655 * @playsink: a #GstPlaySink
3657 * Get the last displayed sample from @playsink. This sample is in the native
3658 * format of the sink element, the caps in the result sample contain the format
3659 * of the frame data.
3661 * Returns: a #GstSample with the frame data or %NULL when no video frame is
3665 gst_play_sink_get_last_sample (GstPlaySink * playsink)
3667 GstSample *result = NULL;
3668 GstPlayVideoChain *chain;
3670 GST_PLAY_SINK_LOCK (playsink);
3671 GST_DEBUG_OBJECT (playsink, "taking last sample");
3672 /* get the video chain if we can */
3673 if ((chain = (GstPlayVideoChain *) playsink->videochain)) {
3674 GST_DEBUG_OBJECT (playsink, "found video chain");
3675 /* see if the chain is active */
3676 if (chain->chain.activated && chain->sink) {
3679 GST_DEBUG_OBJECT (playsink, "video chain active and has a sink");
3681 /* find and get the last-buffer property now */
3683 gst_play_sink_find_property (playsink, chain->sink,
3684 "last-sample", GST_TYPE_SAMPLE))) {
3685 GST_DEBUG_OBJECT (playsink, "getting last-sample property");
3686 g_object_get (elem, "last-sample", &result, NULL);
3687 gst_object_unref (elem);
3691 GST_PLAY_SINK_UNLOCK (playsink);
3697 * gst_play_sink_convert_sample:
3698 * @playsink: a #GstPlaySink
3701 * Get the last displayed frame from @playsink. If caps is %NULL, the video will
3702 * be in the native format of the sink element and the caps on the buffer
3703 * describe the format of the frame. If @caps is not %NULL, the video
3704 * frame will be converted to the format of the caps.
3706 * Returns: a #GstSample of the current video sample converted to #caps.
3707 * The caps in the sample will describe the final layout of the buffer data.
3708 * %NULL is returned when no current sample can be retrieved or when the
3709 * conversion failed.
3712 gst_play_sink_convert_sample (GstPlaySink * playsink, GstCaps * caps)
3717 result = gst_play_sink_get_last_sample (playsink);
3718 if (result != NULL && caps != NULL) {
3721 temp = gst_video_convert_sample (result, caps, 25 * GST_SECOND, &err);
3722 if (temp == NULL && err)
3725 gst_sample_unref (result);
3733 /* I'm really uncertain whether we should make playsink post an error
3734 * on the bus or not. It's not like it's a critical issue regarding
3735 * playsink behaviour. */
3736 GST_ERROR ("Error converting frame: %s", err->message);
3737 gst_sample_unref (result);
3744 is_raw_structure (GstStructure * s)
3748 name = gst_structure_get_name (s);
3750 if (g_str_equal (name, "video/x-raw") || g_str_equal (name, "audio/x-raw"))
3756 is_raw_pad (GstPad * pad)
3758 GstPad *peer = gst_pad_get_peer (pad);
3760 gboolean raw = TRUE;
3765 caps = gst_pad_get_current_caps (peer);
3769 caps = gst_pad_query_caps (peer, NULL);
3771 n = gst_caps_get_size (caps);
3772 for (i = 0; i < n; i++) {
3773 gboolean r = is_raw_structure (gst_caps_get_structure (caps, i));
3777 } else if (raw != r) {
3778 GST_ERROR_OBJECT (pad,
3779 "Caps contains raw and non-raw structures: %" GST_PTR_FORMAT, caps);
3785 raw = is_raw_structure (gst_caps_get_structure (caps, 0));
3787 gst_caps_unref (caps);
3788 gst_object_unref (peer);
3793 static GstPadProbeReturn
3794 sinkpad_blocked_cb (GstPad * blockedpad, GstPadProbeInfo * info,
3795 gpointer user_data);
3798 video_set_blocked (GstPlaySink * playsink, gboolean blocked)
3800 if (playsink->video_pad) {
3802 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3803 (playsink->video_pad)));
3804 if (blocked && playsink->video_block_id == 0) {
3805 playsink->video_block_id =
3806 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3807 sinkpad_blocked_cb, playsink, NULL);
3808 } else if (!blocked && playsink->video_block_id) {
3809 gst_pad_remove_probe (opad, playsink->video_block_id);
3810 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO_RAW);
3811 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO);
3812 playsink->video_block_id = 0;
3813 playsink->video_pad_blocked = FALSE;
3815 gst_object_unref (opad);
3820 audio_set_blocked (GstPlaySink * playsink, gboolean blocked)
3822 if (playsink->audio_pad) {
3824 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3825 (playsink->audio_pad)));
3826 if (blocked && playsink->audio_block_id == 0) {
3827 playsink->audio_block_id =
3828 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3829 sinkpad_blocked_cb, playsink, NULL);
3830 } else if (!blocked && playsink->audio_block_id) {
3831 gst_pad_remove_probe (opad, playsink->audio_block_id);
3832 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO_RAW);
3833 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO);
3834 playsink->audio_block_id = 0;
3835 playsink->audio_pad_blocked = FALSE;
3837 gst_object_unref (opad);
3842 text_set_blocked (GstPlaySink * playsink, gboolean blocked)
3844 if (playsink->text_pad) {
3846 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3847 (playsink->text_pad)));
3848 if (blocked && playsink->text_block_id == 0) {
3849 playsink->text_block_id =
3850 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3851 sinkpad_blocked_cb, playsink, NULL);
3852 } else if (!blocked && playsink->text_block_id) {
3853 gst_pad_remove_probe (opad, playsink->text_block_id);
3854 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_TEXT);
3855 playsink->text_block_id = 0;
3856 playsink->text_pad_blocked = FALSE;
3858 gst_object_unref (opad);
3863 gst_play_sink_reconfigure (GstPlaySink * playsink)
3865 GST_LOG_OBJECT (playsink, "Triggering reconfiguration");
3867 GST_PLAY_SINK_LOCK (playsink);
3868 video_set_blocked (playsink, TRUE);
3869 audio_set_blocked (playsink, TRUE);
3870 text_set_blocked (playsink, TRUE);
3871 GST_PLAY_SINK_UNLOCK (playsink);
3876 static GstPadProbeReturn
3877 sinkpad_blocked_cb (GstPad * blockedpad, GstPadProbeInfo * info,
3880 GstPlaySink *playsink = (GstPlaySink *) user_data;
3883 GST_PLAY_SINK_LOCK (playsink);
3885 pad = GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (blockedpad)));
3886 if (pad == playsink->video_pad) {
3887 playsink->video_pad_blocked = TRUE;
3888 GST_DEBUG_OBJECT (pad, "Video pad blocked");
3889 } else if (pad == playsink->audio_pad) {
3890 playsink->audio_pad_blocked = TRUE;
3891 GST_DEBUG_OBJECT (pad, "Audio pad blocked");
3892 } else if (pad == playsink->text_pad) {
3893 playsink->text_pad_blocked = TRUE;
3894 GST_DEBUG_OBJECT (pad, "Text pad blocked");
3897 /* We reconfigure when for ALL streams:
3898 * * there isn't a pad
3899 * * OR the pad is blocked
3900 * * OR there are no pending blocks on that pad
3903 if ((!playsink->video_pad || playsink->video_pad_blocked
3904 || !PENDING_VIDEO_BLOCK (playsink)) && (!playsink->audio_pad
3905 || playsink->audio_pad_blocked || !PENDING_AUDIO_BLOCK (playsink))
3906 && (!playsink->text_pad || playsink->text_pad_blocked
3907 || !PENDING_TEXT_BLOCK (playsink))) {
3908 GST_DEBUG_OBJECT (playsink, "All pads blocked -- reconfiguring");
3910 if (playsink->video_pad) {
3911 playsink->video_pad_raw = is_raw_pad (playsink->video_pad);
3912 GST_DEBUG_OBJECT (playsink, "Video pad is raw: %d",
3913 playsink->video_pad_raw);
3916 if (playsink->audio_pad) {
3917 playsink->audio_pad_raw = is_raw_pad (playsink->audio_pad);
3918 GST_DEBUG_OBJECT (playsink, "Audio pad is raw: %d",
3919 playsink->audio_pad_raw);
3922 gst_play_sink_do_reconfigure (playsink);
3924 video_set_blocked (playsink, FALSE);
3925 audio_set_blocked (playsink, FALSE);
3926 text_set_blocked (playsink, FALSE);
3929 gst_object_unref (pad);
3931 GST_PLAY_SINK_UNLOCK (playsink);
3933 return GST_PAD_PROBE_OK;
3937 caps_notify_cb (GstPad * pad, GParamSpec * unused, GstPlaySink * playsink)
3939 gboolean reconfigure = FALSE;
3943 g_object_get (pad, "caps", &caps, NULL);
3947 if (pad == playsink->audio_pad) {
3948 raw = is_raw_pad (pad);
3949 reconfigure = (! !playsink->audio_pad_raw != ! !raw)
3950 && playsink->audiochain;
3951 GST_DEBUG_OBJECT (pad,
3952 "Audio caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3954 } else if (pad == playsink->video_pad) {
3955 raw = is_raw_pad (pad);
3956 reconfigure = (! !playsink->video_pad_raw != ! !raw)
3957 && playsink->videochain;
3958 GST_DEBUG_OBJECT (pad,
3959 "Video caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3963 gst_caps_unref (caps);
3966 gst_play_sink_reconfigure (playsink);
3970 gst_play_sink_refresh_pad (GstPlaySink * playsink, GstPad * pad,
3971 GstPlaySinkType type)
3973 gulong *block_id = NULL;
3975 GST_DEBUG_OBJECT (playsink, "refresh pad %" GST_PTR_FORMAT, pad);
3977 GST_PLAY_SINK_LOCK (playsink);
3978 if (pad == playsink->video_pad) {
3979 if (type != GST_PLAY_SINK_TYPE_VIDEO_RAW &&
3980 type != GST_PLAY_SINK_TYPE_VIDEO)
3982 block_id = &playsink->video_block_id;
3983 } else if (pad == playsink->audio_pad) {
3984 if (type != GST_PLAY_SINK_TYPE_AUDIO_RAW &&
3985 type != GST_PLAY_SINK_TYPE_AUDIO)
3987 block_id = &playsink->audio_block_id;
3988 } else if (pad == playsink->text_pad) {
3989 if (type != GST_PLAY_SINK_TYPE_TEXT)
3991 block_id = &playsink->text_block_id;
3994 if (type != GST_PLAY_SINK_TYPE_FLUSHING && (block_id && *block_id == 0)) {
3996 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (pad)));
3999 gst_pad_add_probe (blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
4000 sinkpad_blocked_cb, playsink, NULL);
4001 PENDING_FLAG_SET (playsink, type);
4002 gst_object_unref (blockpad);
4004 GST_PLAY_SINK_UNLOCK (playsink);
4011 GST_WARNING_OBJECT (playsink, "wrong type %u for pad %" GST_PTR_FORMAT,
4013 GST_PLAY_SINK_UNLOCK (playsink);
4019 * gst_play_sink_request_pad
4020 * @playsink: a #GstPlaySink
4021 * @type: a #GstPlaySinkType
4023 * Create or return a pad of @type.
4025 * Returns: a #GstPad of @type or %NULL when the pad could not be created.
4028 gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type)
4031 gboolean created = FALSE;
4032 gboolean activate = TRUE;
4033 const gchar *pad_name = NULL;
4034 gulong *block_id = NULL;
4036 GST_DEBUG_OBJECT (playsink, "request pad type %d", type);
4038 GST_PLAY_SINK_LOCK (playsink);
4040 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
4041 case GST_PLAY_SINK_TYPE_AUDIO:
4042 pad_name = "audio_sink";
4043 if (!playsink->audio_tee) {
4044 GST_LOG_OBJECT (playsink, "creating tee");
4045 /* create tee when needed. This element will feed the audio sink chain
4046 * and the vis chain. */
4047 playsink->audio_tee = gst_element_factory_make ("tee", "audiotee");
4048 if (playsink->audio_tee == NULL) {
4049 post_missing_element_message (playsink, "tee");
4050 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
4051 (_("Missing element '%s' - check your GStreamer installation."),
4056 playsink->audio_tee_sink =
4057 gst_element_get_static_pad (playsink->audio_tee, "sink");
4058 gst_bin_add (GST_BIN_CAST (playsink), playsink->audio_tee);
4059 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
4062 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
4064 if (!playsink->audio_pad) {
4065 GST_LOG_OBJECT (playsink, "ghosting tee sinkpad");
4066 playsink->audio_pad =
4067 gst_ghost_pad_new (pad_name, playsink->audio_tee_sink);
4068 playsink->audio_notify_caps_id =
4069 g_signal_connect (G_OBJECT (playsink->audio_pad), "notify::caps",
4070 G_CALLBACK (caps_notify_cb), playsink);
4073 playsink->audio_pad_raw = FALSE;
4074 res = playsink->audio_pad;
4075 block_id = &playsink->audio_block_id;
4077 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
4078 case GST_PLAY_SINK_TYPE_VIDEO:
4079 pad_name = "video_sink";
4080 if (!playsink->video_pad) {
4081 GST_LOG_OBJECT (playsink, "ghosting videosink");
4082 playsink->video_pad =
4083 gst_ghost_pad_new_no_target (pad_name, GST_PAD_SINK);
4084 playsink->video_notify_caps_id =
4085 g_signal_connect (G_OBJECT (playsink->video_pad), "notify::caps",
4086 G_CALLBACK (caps_notify_cb), playsink);
4089 playsink->video_pad_raw = FALSE;
4090 res = playsink->video_pad;
4091 block_id = &playsink->video_block_id;
4093 case GST_PLAY_SINK_TYPE_TEXT:
4094 GST_LOG_OBJECT (playsink, "ghosting text");
4095 if (!playsink->text_pad) {
4096 playsink->text_pad =
4097 gst_ghost_pad_new_no_target ("text_sink", GST_PAD_SINK);
4100 res = playsink->text_pad;
4101 block_id = &playsink->text_block_id;
4103 case GST_PLAY_SINK_TYPE_FLUSHING:
4107 /* we need a unique padname for the flushing pad. */
4108 padname = g_strdup_printf ("flushing_%u", playsink->count);
4109 res = gst_ghost_pad_new_no_target (padname, GST_PAD_SINK);
4120 GST_PLAY_SINK_UNLOCK (playsink);
4122 if (created && res) {
4123 /* we have to add the pad when it's active or we get an error when the
4124 * element is 'running' */
4125 gst_pad_set_active (res, TRUE);
4126 gst_element_add_pad (GST_ELEMENT_CAST (playsink), res);
4127 if (block_id && *block_id == 0) {
4129 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (res)));
4132 gst_pad_add_probe (blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
4133 sinkpad_blocked_cb, playsink, NULL);
4134 PENDING_FLAG_SET (playsink, type);
4135 gst_object_unref (blockpad);
4138 gst_pad_set_active (res, activate);
4146 gst_play_sink_request_new_pad (GstElement * element, GstPadTemplate * templ,
4147 const gchar * name, const GstCaps * caps)
4151 GstPlaySinkType type;
4152 const gchar *tplname;
4154 g_return_val_if_fail (templ != NULL, NULL);
4156 GST_DEBUG_OBJECT (element, "name:%s", name);
4158 psink = GST_PLAY_SINK (element);
4159 tplname = GST_PAD_TEMPLATE_NAME_TEMPLATE (templ);
4161 /* Figure out the GstPlaySinkType based on the template */
4162 if (!strcmp (tplname, "audio_sink"))
4163 type = GST_PLAY_SINK_TYPE_AUDIO;
4164 else if (!strcmp (tplname, "audio_raw_sink"))
4165 type = GST_PLAY_SINK_TYPE_AUDIO_RAW;
4166 else if (!strcmp (tplname, "video_sink"))
4167 type = GST_PLAY_SINK_TYPE_VIDEO;
4168 else if (!strcmp (tplname, "video_raw_sink"))
4169 type = GST_PLAY_SINK_TYPE_VIDEO_RAW;
4170 else if (!strcmp (tplname, "text_sink"))
4171 type = GST_PLAY_SINK_TYPE_TEXT;
4173 goto unknown_template;
4175 pad = gst_play_sink_request_pad (psink, type);
4179 GST_WARNING_OBJECT (element, "Unknown pad template");
4184 gst_play_sink_release_pad (GstPlaySink * playsink, GstPad * pad)
4186 GstPad **res = NULL;
4187 gboolean untarget = TRUE;
4189 GST_DEBUG_OBJECT (playsink, "release pad %" GST_PTR_FORMAT, pad);
4191 GST_PLAY_SINK_LOCK (playsink);
4192 if (pad == playsink->video_pad) {
4193 res = &playsink->video_pad;
4194 g_signal_handler_disconnect (playsink->video_pad,
4195 playsink->video_notify_caps_id);
4196 video_set_blocked (playsink, FALSE);
4197 } else if (pad == playsink->audio_pad) {
4198 res = &playsink->audio_pad;
4199 g_signal_handler_disconnect (playsink->audio_pad,
4200 playsink->audio_notify_caps_id);
4201 audio_set_blocked (playsink, FALSE);
4202 } else if (pad == playsink->text_pad) {
4203 res = &playsink->text_pad;
4204 text_set_blocked (playsink, FALSE);
4206 /* try to release the given pad anyway, these could be the FLUSHING pads. */
4210 GST_PLAY_SINK_UNLOCK (playsink);
4213 GST_DEBUG_OBJECT (playsink, "deactivate pad %" GST_PTR_FORMAT, *res);
4214 gst_pad_set_active (*res, FALSE);
4216 GST_DEBUG_OBJECT (playsink, "untargeting pad %" GST_PTR_FORMAT, *res);
4217 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (*res), NULL);
4219 GST_DEBUG_OBJECT (playsink, "remove pad %" GST_PTR_FORMAT, *res);
4220 gst_element_remove_pad (GST_ELEMENT_CAST (playsink), *res);
4226 gst_play_sink_release_request_pad (GstElement * element, GstPad * pad)
4228 GstPlaySink *psink = GST_PLAY_SINK (element);
4230 gst_play_sink_release_pad (psink, pad);
4234 gst_play_sink_handle_message (GstBin * bin, GstMessage * message)
4236 GstPlaySink *playsink;
4238 playsink = GST_PLAY_SINK_CAST (bin);
4240 switch (GST_MESSAGE_TYPE (message)) {
4241 case GST_MESSAGE_STEP_DONE:
4246 gboolean flush, intermediate, eos;
4249 GST_INFO_OBJECT (playsink, "Handling step-done message");
4250 gst_message_parse_step_done (message, &format, &amount, &rate, &flush,
4251 &intermediate, &duration, &eos);
4253 if (format == GST_FORMAT_BUFFERS) {
4254 /* for the buffer format, we align the other streams */
4255 if (playsink->audiochain) {
4259 gst_event_new_step (GST_FORMAT_TIME, duration, rate, flush,
4262 if (!gst_element_send_event (playsink->audiochain->chain.bin, event)) {
4263 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
4267 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
4270 case GST_MESSAGE_ELEMENT:{
4271 if (gst_is_video_overlay_prepare_window_handle_message (message)) {
4272 GstVideoOverlay *overlay;
4274 GST_OBJECT_LOCK (playsink);
4275 if (playsink->overlay_element
4276 && GST_OBJECT_CAST (playsink->overlay_element) !=
4277 GST_MESSAGE_SRC (message)) {
4278 gst_object_unref (playsink->overlay_element);
4279 playsink->overlay_element = NULL;
4282 if (!playsink->overlay_element)
4283 playsink->overlay_element =
4284 GST_VIDEO_OVERLAY (gst_object_ref (GST_MESSAGE_SRC (message)));
4286 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4287 GST_OBJECT_UNLOCK (playsink);
4289 GST_DEBUG_OBJECT (playsink, "Got prepare-xwindow-id message");
4291 if (playsink->overlay_handle_set)
4292 gst_video_overlay_set_window_handle (playsink->overlay_element,
4293 playsink->overlay_handle);
4294 if (playsink->overlay_handle_events_set)
4295 gst_video_overlay_handle_events (playsink->overlay_element,
4296 playsink->overlay_handle_events);
4297 if (playsink->overlay_render_rectangle_set)
4298 gst_video_overlay_set_render_rectangle (playsink->overlay_element,
4299 playsink->overlay_x, playsink->overlay_y,
4300 playsink->overlay_width, playsink->overlay_height);
4302 gst_object_unref (overlay);
4303 gst_message_unref (message);
4304 gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (playsink));
4306 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin,
4312 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
4317 /* Send an event to our sinks until one of them works; don't then send to the
4318 * remaining sinks (unlike GstBin)
4319 * Special case: If a text sink is set we need to send the event
4320 * to them in case it's source is different from the a/v stream's source.
4323 gst_play_sink_send_event_to_sink (GstPlaySink * playsink, GstEvent * event)
4325 gboolean res = TRUE;
4326 if (playsink->send_event_mode == MODE_FIRST) {
4327 if (playsink->textchain && playsink->textchain->sink) {
4328 gst_event_ref (event);
4330 gst_element_send_event (playsink->textchain->chain.bin, event))) {
4331 GST_DEBUG_OBJECT (playsink, "Sent event successfully to text sink");
4333 GST_DEBUG_OBJECT (playsink, "Event failed when sent to text sink");
4337 if (playsink->videochain) {
4338 gst_event_ref (event);
4340 gst_element_send_event (playsink->videochain->chain.bin,
4342 GST_DEBUG_OBJECT (playsink, "Sent event successfully to video sink");
4345 GST_DEBUG_OBJECT (playsink, "Event failed when sent to video sink");
4347 if (playsink->audiochain) {
4348 gst_event_ref (event);
4350 gst_element_send_event (playsink->audiochain->chain.bin,
4352 GST_DEBUG_OBJECT (playsink, "Sent event successfully to audio sink");
4355 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
4359 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event
4360 (GST_ELEMENT_CAST (playsink), event);
4364 gst_event_unref (event);
4368 /* We only want to send the event to a single sink (overriding GstBin's
4369 * behaviour), but we want to keep GstPipeline's behaviour - wrapping seek
4370 * events appropriately. So, this is a messy duplication of code. */
4372 gst_play_sink_send_event (GstElement * element, GstEvent * event)
4374 gboolean res = FALSE;
4375 GstEventType event_type = GST_EVENT_TYPE (event);
4376 GstPlaySink *playsink;
4377 playsink = GST_PLAY_SINK_CAST (element);
4378 switch (event_type) {
4379 case GST_EVENT_SEEK:
4380 GST_DEBUG_OBJECT (element, "Sending event to a sink");
4381 res = gst_play_sink_send_event_to_sink (playsink, event);
4383 case GST_EVENT_STEP:
4388 gboolean flush, intermediate;
4389 gst_event_parse_step (event, &format, &amount, &rate, &flush,
4391 if (format == GST_FORMAT_BUFFERS) {
4392 /* for buffers, we will try to step video frames, for other formats we
4393 * send the step to all sinks */
4394 res = gst_play_sink_send_event_to_sink (playsink, event);
4397 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
4404 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
4411 static GstStateChangeReturn
4412 gst_play_sink_change_state (GstElement * element, GstStateChange transition)
4414 GstStateChangeReturn ret;
4415 GstStateChangeReturn bret;
4416 GstPlaySink *playsink;
4417 playsink = GST_PLAY_SINK (element);
4418 switch (transition) {
4419 case GST_STATE_CHANGE_READY_TO_PAUSED:
4420 playsink->need_async_start = TRUE;
4421 /* we want to go async to PAUSED until we managed to configure and add the
4423 do_async_start (playsink);
4424 ret = GST_STATE_CHANGE_ASYNC;
4426 /* block all pads here */
4427 if (!gst_play_sink_reconfigure (playsink))
4428 ret = GST_STATE_CHANGE_FAILURE;
4430 case GST_STATE_CHANGE_PAUSED_TO_READY:
4431 /* unblock all pads here */
4432 GST_PLAY_SINK_LOCK (playsink);
4433 video_set_blocked (playsink, FALSE);
4434 audio_set_blocked (playsink, FALSE);
4435 text_set_blocked (playsink, FALSE);
4436 GST_PLAY_SINK_UNLOCK (playsink);
4438 case GST_STATE_CHANGE_READY_TO_NULL:
4439 if (playsink->audiochain && playsink->audiochain->sink_volume) {
4440 /* remove our links to the mute and volume elements when they were
4441 * provided by a sink */
4442 disconnect_audio_chain (playsink->audiochain, playsink);
4443 playsink->audiochain->volume = NULL;
4444 playsink->audiochain->mute = NULL;
4447 if (playsink->audiochain && playsink->audiochain->ts_offset) {
4448 gst_object_unref (playsink->audiochain->ts_offset);
4449 playsink->audiochain->ts_offset = NULL;
4452 if (playsink->videochain && playsink->videochain->ts_offset) {
4453 gst_object_unref (playsink->videochain->ts_offset);
4454 playsink->videochain->ts_offset = NULL;
4457 GST_OBJECT_LOCK (playsink);
4458 if (playsink->overlay_element)
4459 gst_object_unref (playsink->overlay_element);
4460 playsink->overlay_element = NULL;
4462 if (playsink->colorbalance_element) {
4463 g_signal_handler_disconnect (playsink->colorbalance_element,
4464 playsink->colorbalance_value_changed_id);
4465 playsink->colorbalance_value_changed_id = 0;
4466 gst_object_unref (playsink->colorbalance_element);
4468 playsink->colorbalance_element = NULL;
4469 GST_OBJECT_UNLOCK (playsink);
4471 ret = GST_STATE_CHANGE_SUCCESS;
4474 /* all other state changes return SUCCESS by default, this value can be
4475 * overridden by the result of the children */
4476 ret = GST_STATE_CHANGE_SUCCESS;
4480 /* do the state change of the children */
4482 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->change_state (element,
4484 /* now look at the result of our children and adjust the return value */
4486 case GST_STATE_CHANGE_FAILURE:
4487 /* failure, we stop */
4488 goto activate_failed;
4489 case GST_STATE_CHANGE_NO_PREROLL:
4490 /* some child returned NO_PREROLL. This is strange but we never know. We
4491 * commit our async state change (if any) and return the NO_PREROLL */
4492 do_async_done (playsink);
4495 case GST_STATE_CHANGE_ASYNC:
4496 /* some child was async, return this */
4500 /* return our previously configured return value */
4504 switch (transition) {
4505 case GST_STATE_CHANGE_READY_TO_PAUSED:
4507 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
4508 /* FIXME Release audio device when we implement that */
4509 playsink->need_async_start = TRUE;
4511 case GST_STATE_CHANGE_PAUSED_TO_READY:{
4512 if (playsink->video_sinkpad_stream_synchronizer) {
4513 gst_element_release_request_pad (GST_ELEMENT_CAST
4514 (playsink->stream_synchronizer),
4515 playsink->video_sinkpad_stream_synchronizer);
4516 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
4517 playsink->video_sinkpad_stream_synchronizer = NULL;
4518 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
4519 playsink->video_srcpad_stream_synchronizer = NULL;
4521 if (playsink->audio_sinkpad_stream_synchronizer) {
4522 gst_element_release_request_pad (GST_ELEMENT_CAST
4523 (playsink->stream_synchronizer),
4524 playsink->audio_sinkpad_stream_synchronizer);
4525 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
4526 playsink->audio_sinkpad_stream_synchronizer = NULL;
4527 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
4528 playsink->audio_srcpad_stream_synchronizer = NULL;
4530 if (playsink->text_sinkpad_stream_synchronizer) {
4531 gst_element_release_request_pad (GST_ELEMENT_CAST
4532 (playsink->stream_synchronizer),
4533 playsink->text_sinkpad_stream_synchronizer);
4534 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
4535 playsink->text_sinkpad_stream_synchronizer = NULL;
4536 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
4537 playsink->text_srcpad_stream_synchronizer = NULL;
4541 case GST_STATE_CHANGE_READY_TO_NULL:
4542 /* remove sinks we added */
4543 if (playsink->videodeinterlacechain) {
4544 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
4546 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
4548 if (playsink->videochain) {
4549 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
4550 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
4552 if (playsink->audiochain) {
4553 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
4554 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
4556 if (playsink->vischain) {
4557 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
4558 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
4560 if (playsink->textchain) {
4561 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
4562 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
4564 do_async_done (playsink);
4565 /* when going to READY, keep elements around as long as possible,
4566 * so they may be re-used faster next time/url around.
4567 * when really going to NULL, clean up everything completely. */
4568 if (transition == GST_STATE_CHANGE_READY_TO_NULL) {
4570 /* Unparent the sinks to allow reuse */
4571 if (playsink->videochain && playsink->videochain->sink)
4572 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
4573 playsink->videochain->sink);
4574 if (playsink->audiochain && playsink->audiochain->sink)
4575 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
4576 playsink->audiochain->sink);
4577 if (playsink->textchain && playsink->textchain->sink)
4578 gst_bin_remove (GST_BIN_CAST (playsink->textchain->chain.bin),
4579 playsink->textchain->sink);
4580 if (playsink->audio_sink != NULL)
4581 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
4582 if (playsink->video_sink != NULL)
4583 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
4584 if (playsink->visualisation != NULL)
4585 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
4586 if (playsink->text_sink != NULL)
4587 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
4588 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
4589 playsink->videodeinterlacechain = NULL;
4590 free_chain ((GstPlayChain *) playsink->videochain);
4591 playsink->videochain = NULL;
4592 free_chain ((GstPlayChain *) playsink->audiochain);
4593 playsink->audiochain = NULL;
4594 free_chain ((GstPlayChain *) playsink->vischain);
4595 playsink->vischain = NULL;
4596 free_chain ((GstPlayChain *) playsink->textchain);
4597 playsink->textchain = NULL;
4607 GST_DEBUG_OBJECT (element,
4608 "element failed to change states -- activation problem?");
4609 return GST_STATE_CHANGE_FAILURE;
4614 gst_play_sink_set_property (GObject * object, guint prop_id,
4615 const GValue * value, GParamSpec * spec)
4617 GstPlaySink *playsink = GST_PLAY_SINK (object);
4620 gst_play_sink_set_flags (playsink, g_value_get_flags (value));
4623 gst_play_sink_set_volume (playsink, g_value_get_double (value));
4626 gst_play_sink_set_mute (playsink, g_value_get_boolean (value));
4628 case PROP_FONT_DESC:
4629 gst_play_sink_set_font_desc (playsink, g_value_get_string (value));
4631 case PROP_SUBTITLE_ENCODING:
4632 gst_play_sink_set_subtitle_encoding (playsink,
4633 g_value_get_string (value));
4635 case PROP_VIS_PLUGIN:
4636 gst_play_sink_set_vis_plugin (playsink, g_value_get_object (value));
4638 case PROP_AV_OFFSET:
4639 gst_play_sink_set_av_offset (playsink, g_value_get_int64 (value));
4641 case PROP_VIDEO_SINK:
4642 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_VIDEO,
4643 g_value_get_object (value));
4645 case PROP_AUDIO_SINK:
4646 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_AUDIO,
4647 g_value_get_object (value));
4649 case PROP_TEXT_SINK:
4650 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_TEXT,
4651 g_value_get_object (value));
4653 case PROP_SEND_EVENT_MODE:
4654 playsink->send_event_mode = g_value_get_enum (value);
4656 case PROP_FORCE_ASPECT_RATIO:{
4657 GstPlayVideoChain *chain;
4660 playsink->force_aspect_ratio = g_value_get_boolean (value);
4662 GST_PLAY_SINK_LOCK (playsink);
4663 if (playsink->videochain) {
4664 chain = (GstPlayVideoChain *) playsink->videochain;
4668 gst_play_sink_find_property_sinks (playsink, chain->sink,
4669 "force-aspect-ratio", G_TYPE_BOOLEAN);
4672 g_object_set (elem, "force-aspect-ratio",
4673 playsink->force_aspect_ratio, NULL);
4676 GST_PLAY_SINK_UNLOCK (playsink);
4680 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
4686 gst_play_sink_get_property (GObject * object, guint prop_id,
4687 GValue * value, GParamSpec * spec)
4689 GstPlaySink *playsink = GST_PLAY_SINK (object);
4692 g_value_set_flags (value, gst_play_sink_get_flags (playsink));
4695 g_value_set_double (value, gst_play_sink_get_volume (playsink));
4698 g_value_set_boolean (value, gst_play_sink_get_mute (playsink));
4700 case PROP_FONT_DESC:
4701 g_value_take_string (value, gst_play_sink_get_font_desc (playsink));
4703 case PROP_SUBTITLE_ENCODING:
4704 g_value_take_string (value,
4705 gst_play_sink_get_subtitle_encoding (playsink));
4707 case PROP_VIS_PLUGIN:
4708 g_value_take_object (value, gst_play_sink_get_vis_plugin (playsink));
4711 gst_value_take_sample (value, gst_play_sink_get_last_sample (playsink));
4713 case PROP_AV_OFFSET:
4714 g_value_set_int64 (value, gst_play_sink_get_av_offset (playsink));
4716 case PROP_VIDEO_SINK:
4717 g_value_take_object (value, gst_play_sink_get_sink (playsink,
4718 GST_PLAY_SINK_TYPE_VIDEO));
4720 case PROP_AUDIO_SINK:
4721 g_value_take_object (value, gst_play_sink_get_sink (playsink,
4722 GST_PLAY_SINK_TYPE_AUDIO));
4724 case PROP_TEXT_SINK:
4725 g_value_take_object (value, gst_play_sink_get_sink (playsink,
4726 GST_PLAY_SINK_TYPE_TEXT));
4728 case PROP_SEND_EVENT_MODE:
4729 g_value_set_enum (value, playsink->send_event_mode);
4731 case PROP_FORCE_ASPECT_RATIO:
4732 g_value_set_boolean (value, playsink->force_aspect_ratio);
4735 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
4741 gst_play_sink_overlay_expose (GstVideoOverlay * overlay)
4743 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4744 GstVideoOverlay *overlay_element;
4746 GST_OBJECT_LOCK (playsink);
4747 if (playsink->overlay_element)
4749 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4751 overlay_element = NULL;
4752 GST_OBJECT_UNLOCK (playsink);
4754 if (overlay_element) {
4755 gst_video_overlay_expose (overlay_element);
4756 gst_object_unref (overlay_element);
4761 gst_play_sink_overlay_handle_events (GstVideoOverlay * overlay,
4762 gboolean handle_events)
4764 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4765 GstVideoOverlay *overlay_element;
4767 GST_OBJECT_LOCK (playsink);
4768 if (playsink->overlay_element)
4770 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4772 overlay_element = NULL;
4773 GST_OBJECT_UNLOCK (playsink);
4775 playsink->overlay_handle_events_set = TRUE;
4776 playsink->overlay_handle_events = handle_events;
4778 if (overlay_element) {
4779 gst_video_overlay_handle_events (overlay_element, handle_events);
4780 gst_object_unref (overlay_element);
4785 gst_play_sink_overlay_set_render_rectangle (GstVideoOverlay * overlay, gint x,
4786 gint y, gint width, gint height)
4788 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4789 GstVideoOverlay *overlay_element;
4791 GST_OBJECT_LOCK (playsink);
4792 if (playsink->overlay_element)
4794 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4796 overlay_element = NULL;
4797 GST_OBJECT_UNLOCK (playsink);
4799 playsink->overlay_render_rectangle_set = TRUE;
4800 playsink->overlay_x = x;
4801 playsink->overlay_y = y;
4802 playsink->overlay_width = width;
4803 playsink->overlay_height = height;
4805 if (overlay_element) {
4806 gst_video_overlay_set_render_rectangle (overlay_element, x, y, width,
4808 gst_object_unref (overlay_element);
4813 gst_play_sink_overlay_set_window_handle (GstVideoOverlay * overlay,
4816 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4817 GstVideoOverlay *overlay_element;
4819 GST_OBJECT_LOCK (playsink);
4820 if (playsink->overlay_element)
4822 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4824 overlay_element = NULL;
4825 GST_OBJECT_UNLOCK (playsink);
4827 playsink->overlay_handle_set = TRUE;
4828 playsink->overlay_handle = handle;
4830 if (overlay_element) {
4831 gst_video_overlay_set_window_handle (overlay_element, handle);
4832 gst_object_unref (overlay_element);
4837 gst_play_sink_overlay_init (gpointer g_iface, gpointer g_iface_data)
4839 GstVideoOverlayInterface *iface = (GstVideoOverlayInterface *) g_iface;
4840 iface->expose = gst_play_sink_overlay_expose;
4841 iface->handle_events = gst_play_sink_overlay_handle_events;
4842 iface->set_render_rectangle = gst_play_sink_overlay_set_render_rectangle;
4843 iface->set_window_handle = gst_play_sink_overlay_set_window_handle;
4847 gst_play_sink_navigation_send_event (GstNavigation * navigation,
4848 GstStructure * structure)
4850 GstPlaySink *playsink = GST_PLAY_SINK (navigation);
4853 GST_PLAY_SINK_LOCK (playsink);
4854 if (playsink->videochain && playsink->videochain->chain.bin)
4855 bin = GST_BIN (gst_object_ref (playsink->videochain->chain.bin));
4856 GST_PLAY_SINK_UNLOCK (playsink);
4859 GstElement *nav = gst_bin_get_by_interface (bin, GST_TYPE_NAVIGATION);
4862 gst_navigation_send_event (GST_NAVIGATION (nav), structure);
4864 gst_object_unref (nav);
4866 GstEvent *event = gst_event_new_navigation (structure);
4868 gst_element_send_event (GST_ELEMENT (bin), event);
4871 gst_object_unref (bin);
4875 gst_structure_free (structure);
4879 gst_play_sink_navigation_init (gpointer g_iface, gpointer g_iface_data)
4881 GstNavigationInterface *iface = (GstNavigationInterface *) g_iface;
4883 iface->send_event = gst_play_sink_navigation_send_event;
4886 static const GList *
4887 gst_play_sink_colorbalance_list_channels (GstColorBalance * balance)
4889 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4891 return playsink->colorbalance_channels;
4895 gst_play_sink_colorbalance_set_value (GstColorBalance * balance,
4896 GstColorBalanceChannel * proxy, gint value)
4898 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4901 GstColorBalance *balance_element = NULL;
4903 GST_OBJECT_LOCK (playsink);
4904 if (playsink->colorbalance_element)
4906 GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
4907 GST_OBJECT_UNLOCK (playsink);
4909 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
4910 GstColorBalanceChannel *proxy_tmp = l->data;
4913 if (proxy_tmp != proxy)
4916 playsink->colorbalance_values[i] = value;
4918 if (balance_element) {
4919 GstColorBalanceChannel *channel = NULL;
4920 const GList *channels, *k;
4922 channels = gst_color_balance_list_channels (balance_element);
4923 for (k = channels; k; k = k->next) {
4924 GstColorBalanceChannel *tmp = l->data;
4926 if (g_strrstr (tmp->label, proxy->label)) {
4934 /* Convert to [0, 1] range */
4937 (gdouble) proxy->min_value) / ((gdouble) proxy->max_value -
4938 (gdouble) proxy->min_value);
4939 /* Convert to channel range */
4941 channel->min_value + new_val * ((gdouble) channel->max_value -
4942 (gdouble) channel->min_value);
4944 gst_color_balance_set_value (balance_element, channel,
4945 (gint) (new_val + 0.5));
4947 gst_object_unref (balance_element);
4950 gst_color_balance_value_changed (balance, proxy, value);
4956 gst_play_sink_colorbalance_get_value (GstColorBalance * balance,
4957 GstColorBalanceChannel * proxy)
4959 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4963 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
4964 GstColorBalanceChannel *proxy_tmp = l->data;
4966 if (proxy_tmp != proxy)
4969 return playsink->colorbalance_values[i];
4972 g_return_val_if_reached (0);
4975 static GstColorBalanceType
4976 gst_play_sink_colorbalance_get_balance_type (GstColorBalance * balance)
4978 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4979 GstColorBalance *balance_element = NULL;
4980 GstColorBalanceType t = GST_COLOR_BALANCE_SOFTWARE;
4982 GST_OBJECT_LOCK (playsink);
4983 if (playsink->colorbalance_element)
4985 GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
4986 GST_OBJECT_UNLOCK (playsink);
4988 if (balance_element) {
4989 t = gst_color_balance_get_balance_type (balance_element);
4990 gst_object_unref (balance_element);
4997 gst_play_sink_colorbalance_init (gpointer g_iface, gpointer g_iface_data)
4999 GstColorBalanceInterface *iface = (GstColorBalanceInterface *) g_iface;
5001 iface->list_channels = gst_play_sink_colorbalance_list_channels;
5002 iface->set_value = gst_play_sink_colorbalance_set_value;
5003 iface->get_value = gst_play_sink_colorbalance_get_value;
5004 iface->get_balance_type = gst_play_sink_colorbalance_get_balance_type;
5008 gst_play_sink_plugin_init (GstPlugin * plugin)
5010 GST_DEBUG_CATEGORY_INIT (gst_play_sink_debug, "playsink", 0, "play bin");
5011 return gst_element_register (plugin, "playsink", GST_RANK_NONE,
5012 GST_TYPE_PLAY_SINK);