uridecodebin: remove commented code
[platform/upstream/gstreamer.git] / gst / playback / gstplaysink.c
1 /* GStreamer
2  * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
3  * Copyright (C) <2011> Sebastian Dröge <sebastian.droege@collabora.co.uk>
4  *
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.
9  *
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.
14  *
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.
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include <string.h>
26 #include <gst/gst.h>
27
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>
35
36 #include "gstplaysink.h"
37 #include "gststreamsynchronizer.h"
38 #include "gstplaysinkvideoconvert.h"
39 #include "gstplaysinkaudioconvert.h"
40
41 GST_DEBUG_CATEGORY_STATIC (gst_play_sink_debug);
42 #define GST_CAT_DEFAULT gst_play_sink_debug
43
44 #define VOLUME_MAX_DOUBLE 10.0
45
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
48
49 #define GST_PLAY_CHAIN(c) ((GstPlayChain *)(c))
50
51 /* enum types */
52 /**
53  * GstPlaySinkSendEventMode:
54  * @MODE_DEFAULT: default GstBin's send_event handling
55  * @MODE_FIRST: send event only to the first sink that return true
56  *
57  * Send event handling to use
58  */
59 typedef enum
60 {
61   MODE_DEFAULT = 0,
62   MODE_FIRST = 1
63 } GstPlaySinkSendEventMode;
64
65
66 #define GST_TYPE_PLAY_SINK_SEND_EVENT_MODE (gst_play_sink_send_event_mode_get_type ())
67 static GType
68 gst_play_sink_send_event_mode_get_type (void)
69 {
70   static GType gtype = 0;
71
72   if (gtype == 0) {
73     static const GEnumValue values[] = {
74       {MODE_DEFAULT, "Default GstBin's send_event handling (default)",
75           "default"},
76       {MODE_FIRST, "Sends the event to sinks until the first one handles it",
77           "first"},
78       {0, NULL, NULL}
79     };
80
81     gtype = g_enum_register_static ("GstPlaySinkSendEventMode", values);
82   }
83   return gtype;
84 }
85
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. */
88 typedef struct
89 {
90   GstPlaySink *playsink;
91   GstElement *bin;
92   gboolean added;
93   gboolean activated;
94   gboolean raw;
95 } GstPlayChain;
96
97 typedef struct
98 {
99   GstPlayChain chain;
100   GstPad *sinkpad;
101   GstElement *queue;
102   GstElement *conv;
103   GstElement *volume;           /* element with the volume property */
104   gboolean sink_volume;         /* if the volume was provided by the sink */
105   GstElement *mute;             /* element with the mute property */
106   GstElement *sink;
107   GstElement *ts_offset;
108 } GstPlayAudioChain;
109
110 typedef struct
111 {
112   GstPlayChain chain;
113   GstPad *sinkpad, *srcpad;
114   GstElement *conv;
115   GstElement *deinterlace;
116 } GstPlayVideoDeinterlaceChain;
117
118 typedef struct
119 {
120   GstPlayChain chain;
121   GstPad *sinkpad;
122   GstElement *queue;
123   GstElement *conv;
124   GstElement *sink;
125   gboolean async;
126   GstElement *ts_offset;
127 } GstPlayVideoChain;
128
129 typedef struct
130 {
131   GstPlayChain chain;
132   GstPad *sinkpad;
133   GstElement *queue;
134   GstElement *conv;
135   GstElement *resample;
136   GstPad *blockpad;             /* srcpad of queue, used for blocking the vis */
137   GstPad *vispeerpad;           /* srcpad of resample, used for unlinking the vis */
138   GstPad *vissinkpad;           /* visualisation sinkpad, */
139   GstElement *vis;
140   GstPad *vissrcpad;            /* visualisation srcpad, */
141   GstPad *srcpad;               /* outgoing srcpad, used to connect to the next
142                                  * chain */
143 } GstPlayVisChain;
144
145 typedef struct
146 {
147   GstPlayChain chain;
148   GstPad *sinkpad;
149   GstElement *queue;
150   GstElement *identity;
151   GstElement *overlay;
152   GstPad *videosinkpad;
153   GstPad *textsinkpad;
154   GstPad *srcpad;               /* outgoing srcpad, used to connect to the next
155                                  * chain */
156   GstElement *sink;             /* custom sink to receive subtitle buffers */
157 } GstPlayTextChain;
158
159 #define GST_PLAY_SINK_GET_LOCK(playsink) (&((GstPlaySink *)playsink)->lock)
160 #define GST_PLAY_SINK_LOCK(playsink)     G_STMT_START { \
161   GST_LOG_OBJECT (playsink, "locking from thread %p", g_thread_self ()); \
162   g_rec_mutex_lock (GST_PLAY_SINK_GET_LOCK (playsink)); \
163   GST_LOG_OBJECT (playsink, "locked from thread %p", g_thread_self ()); \
164 } G_STMT_END
165 #define GST_PLAY_SINK_UNLOCK(playsink)   G_STMT_START { \
166   GST_LOG_OBJECT (playsink, "unlocking from thread %p", g_thread_self ()); \
167   g_rec_mutex_unlock (GST_PLAY_SINK_GET_LOCK (playsink)); \
168 } G_STMT_END
169
170 #define PENDING_FLAG_SET(playsink, flagtype) \
171   ((playsink->pending_blocked_pads) |= (1 << flagtype))
172 #define PENDING_FLAG_UNSET(playsink, flagtype) \
173   ((playsink->pending_blocked_pads) &= ~(1 << flagtype))
174 #define PENDING_FLAG_IS_SET(playsink, flagtype) \
175   ((playsink->pending_blocked_pads) & (1 << flagtype))
176 #define PENDING_VIDEO_BLOCK(playsink) \
177   ((playsink->pending_blocked_pads) & (1 << GST_PLAY_SINK_TYPE_VIDEO_RAW | 1 << GST_PLAY_SINK_TYPE_VIDEO))
178 #define PENDING_AUDIO_BLOCK(playsink) \
179   ((playsink->pending_blocked_pads) & (1 << GST_PLAY_SINK_TYPE_AUDIO_RAW | 1 << GST_PLAY_SINK_TYPE_AUDIO))
180 #define PENDING_TEXT_BLOCK(playsink) \
181   PENDING_FLAG_IS_SET(playsink, GST_PLAY_SINK_TYPE_TEXT)
182
183 struct _GstPlaySink
184 {
185   GstBin bin;
186
187   GRecMutex lock;
188
189   gboolean async_pending;
190   gboolean need_async_start;
191
192   GstPlayFlags flags;
193
194   GstStreamSynchronizer *stream_synchronizer;
195
196   /* chains */
197   GstPlayAudioChain *audiochain;
198   GstPlayVideoDeinterlaceChain *videodeinterlacechain;
199   GstPlayVideoChain *videochain;
200   GstPlayVisChain *vischain;
201   GstPlayTextChain *textchain;
202
203   /* audio */
204   GstPad *audio_pad;
205   gboolean audio_pad_raw;
206   gboolean audio_pad_blocked;
207   GstPad *audio_srcpad_stream_synchronizer;
208   GstPad *audio_sinkpad_stream_synchronizer;
209   gulong audio_block_id;
210   /* audio tee */
211   GstElement *audio_tee;
212   GstPad *audio_tee_sink;
213   GstPad *audio_tee_asrc;
214   GstPad *audio_tee_vissrc;
215   /* video */
216   GstPad *video_pad;
217   gboolean video_pad_raw;
218   gboolean video_pad_blocked;
219   GstPad *video_srcpad_stream_synchronizer;
220   GstPad *video_sinkpad_stream_synchronizer;
221   gulong video_block_id;
222   /* text */
223   GstPad *text_pad;
224   gboolean text_pad_blocked;
225   GstPad *text_srcpad_stream_synchronizer;
226   GstPad *text_sinkpad_stream_synchronizer;
227   gulong text_block_id;
228
229   guint32 pending_blocked_pads;
230
231   /* properties */
232   GstElement *audio_sink;
233   GstElement *video_sink;
234   GstElement *visualisation;
235   GstElement *text_sink;
236   gdouble volume;
237   gboolean mute;
238   gchar *font_desc;             /* font description */
239   gchar *subtitle_encoding;     /* subtitle encoding */
240   guint connection_speed;       /* connection speed in bits/sec (0 = unknown) */
241   guint count;
242   gboolean volume_changed;      /* volume/mute changed while no audiochain */
243   gboolean mute_changed;        /* ... has been created yet */
244   gint64 av_offset;
245   GstPlaySinkSendEventMode send_event_mode;
246   gboolean force_aspect_ratio;
247
248   /* videooverlay proxy interface */
249   GstVideoOverlay *overlay_element;     /* protected with LOCK */
250   gboolean overlay_handle_set;
251   guintptr overlay_handle;
252   gboolean overlay_render_rectangle_set;
253   gint overlay_x, overlay_y, overlay_width, overlay_height;
254   gboolean overlay_handle_events_set;
255   gboolean overlay_handle_events;
256
257   /* colorbalance proxy interface */
258   GstColorBalance *colorbalance_element;
259   GList *colorbalance_channels; /* CONTRAST, BRIGHTNESS, HUE, SATURATION */
260   gint colorbalance_values[4];
261
262   /* sending audio/video flushes break stream changes when the pipeline
263    * is paused and played again in 0.10 */
264 #if 0
265   GstSegment video_segment;
266   gboolean video_custom_flush_finished;
267   gboolean video_ignore_wrong_state;
268   gboolean video_pending_flush;
269
270   GstSegment audio_segment;
271   gboolean audio_custom_flush_finished;
272   gboolean audio_ignore_wrong_state;
273   gboolean audio_pending_flush;
274 #endif
275
276   GstSegment text_segment;
277   gboolean text_custom_flush_finished;
278   gboolean text_ignore_wrong_state;
279   gboolean text_pending_flush;
280 };
281
282 struct _GstPlaySinkClass
283 {
284   GstBinClass parent_class;
285
286     gboolean (*reconfigure) (GstPlaySink * playsink);
287
288   GstSample *(*convert_sample) (GstPlaySink * playsink, GstCaps * caps);
289 };
290
291
292 static GstStaticPadTemplate audiotemplate =
293 GST_STATIC_PAD_TEMPLATE ("audio_sink",
294     GST_PAD_SINK,
295     GST_PAD_REQUEST,
296     GST_STATIC_CAPS_ANY);
297 static GstStaticPadTemplate videotemplate =
298 GST_STATIC_PAD_TEMPLATE ("video_sink",
299     GST_PAD_SINK,
300     GST_PAD_REQUEST,
301     GST_STATIC_CAPS_ANY);
302 static GstStaticPadTemplate texttemplate = GST_STATIC_PAD_TEMPLATE ("text_sink",
303     GST_PAD_SINK,
304     GST_PAD_REQUEST,
305     GST_STATIC_CAPS_ANY);
306
307 /* FIXME 0.11: Remove */
308 static GstStaticPadTemplate audiorawtemplate =
309 GST_STATIC_PAD_TEMPLATE ("audio_raw_sink",
310     GST_PAD_SINK,
311     GST_PAD_REQUEST,
312     GST_STATIC_CAPS_ANY);
313 static GstStaticPadTemplate videorawtemplate =
314 GST_STATIC_PAD_TEMPLATE ("video_raw_sink",
315     GST_PAD_SINK,
316     GST_PAD_REQUEST,
317     GST_STATIC_CAPS_ANY);
318
319
320 /* props */
321 enum
322 {
323   PROP_0,
324   PROP_FLAGS,
325   PROP_MUTE,
326   PROP_VOLUME,
327   PROP_FONT_DESC,
328   PROP_SUBTITLE_ENCODING,
329   PROP_VIS_PLUGIN,
330   PROP_SAMPLE,
331   PROP_AV_OFFSET,
332   PROP_VIDEO_SINK,
333   PROP_AUDIO_SINK,
334   PROP_TEXT_SINK,
335   PROP_SEND_EVENT_MODE,
336   PROP_FORCE_ASPECT_RATIO,
337   PROP_LAST
338 };
339
340 /* signals */
341 enum
342 {
343   LAST_SIGNAL
344 };
345
346 static void gst_play_sink_dispose (GObject * object);
347 static void gst_play_sink_finalize (GObject * object);
348 static void gst_play_sink_set_property (GObject * object, guint prop_id,
349     const GValue * value, GParamSpec * spec);
350 static void gst_play_sink_get_property (GObject * object, guint prop_id,
351     GValue * value, GParamSpec * spec);
352
353 static GstPad *gst_play_sink_request_new_pad (GstElement * element,
354     GstPadTemplate * templ, const gchar * name, const GstCaps * caps);
355 static void gst_play_sink_release_request_pad (GstElement * element,
356     GstPad * pad);
357 static gboolean gst_play_sink_send_event (GstElement * element,
358     GstEvent * event);
359 static GstStateChangeReturn gst_play_sink_change_state (GstElement * element,
360     GstStateChange transition);
361
362 static void gst_play_sink_handle_message (GstBin * bin, GstMessage * message);
363
364 /* sending audio/video flushes break stream changes when the pipeline
365  * is paused and played again in 0.10 */
366 #if 0
367 static gboolean gst_play_sink_video_sink_event (GstPad * pad, GstEvent * event);
368 static GstFlowReturn gst_play_sink_video_sink_chain (GstPad * pad,
369     GstBuffer * buffer);
370 static gboolean gst_play_sink_audio_sink_event (GstPad * pad, GstEvent * event);
371 static GstFlowReturn gst_play_sink_audio_sink_chain (GstPad * pad,
372     GstBuffer * buffer);
373 #endif
374 static gboolean gst_play_sink_text_sink_event (GstPad * pad, GstObject * parent,
375     GstEvent * event);
376 static GstFlowReturn gst_play_sink_text_sink_chain (GstPad * pad,
377     GstObject * parent, GstBuffer * buffer);
378
379 static void notify_volume_cb (GObject * object, GParamSpec * pspec,
380     GstPlaySink * playsink);
381 static void notify_mute_cb (GObject * object, GParamSpec * pspec,
382     GstPlaySink * playsink);
383
384 static void update_av_offset (GstPlaySink * playsink);
385
386 static gboolean gst_play_sink_do_reconfigure (GstPlaySink * playsink);
387
388 static GQuark _playsink_reset_segment_event_marker_id = 0;
389
390 /* static guint gst_play_sink_signals[LAST_SIGNAL] = { 0 }; */
391
392 static void gst_play_sink_overlay_init (gpointer g_iface,
393     gpointer g_iface_data);
394 static void gst_play_sink_navigation_init (gpointer g_iface,
395     gpointer g_iface_data);
396 static void gst_play_sink_colorbalance_init (gpointer g_iface,
397     gpointer g_iface_data);
398
399 static void
400 _do_init (GType type)
401 {
402   static const GInterfaceInfo svol_info = {
403     NULL, NULL, NULL
404   };
405   static const GInterfaceInfo ov_info = {
406     gst_play_sink_overlay_init,
407     NULL, NULL
408   };
409   static const GInterfaceInfo nav_info = {
410     gst_play_sink_navigation_init,
411     NULL, NULL
412   };
413   static const GInterfaceInfo col_info = {
414     gst_play_sink_colorbalance_init,
415     NULL, NULL
416   };
417
418   g_type_add_interface_static (type, GST_TYPE_STREAM_VOLUME, &svol_info);
419   g_type_add_interface_static (type, GST_TYPE_VIDEO_OVERLAY, &ov_info);
420   g_type_add_interface_static (type, GST_TYPE_NAVIGATION, &nav_info);
421   g_type_add_interface_static (type, GST_TYPE_COLOR_BALANCE, &col_info);
422 }
423
424 G_DEFINE_TYPE_WITH_CODE (GstPlaySink, gst_play_sink, GST_TYPE_BIN,
425     _do_init (g_define_type_id));
426
427 static void
428 gst_play_sink_class_init (GstPlaySinkClass * klass)
429 {
430   GObjectClass *gobject_klass;
431   GstElementClass *gstelement_klass;
432   GstBinClass *gstbin_klass;
433
434   gobject_klass = (GObjectClass *) klass;
435   gstelement_klass = (GstElementClass *) klass;
436   gstbin_klass = (GstBinClass *) klass;
437
438   gobject_klass->dispose = gst_play_sink_dispose;
439   gobject_klass->finalize = gst_play_sink_finalize;
440   gobject_klass->set_property = gst_play_sink_set_property;
441   gobject_klass->get_property = gst_play_sink_get_property;
442
443
444   /**
445    * GstPlaySink:flags
446    *
447    * Control the behaviour of playsink.
448    */
449   g_object_class_install_property (gobject_klass, PROP_FLAGS,
450       g_param_spec_flags ("flags", "Flags", "Flags to control behaviour",
451           GST_TYPE_PLAY_FLAGS, DEFAULT_FLAGS,
452           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
453
454   /**
455    * GstPlaySink:volume:
456    *
457    * Get or set the current audio stream volume. 1.0 means 100%,
458    * 0.0 means mute. This uses a linear volume scale.
459    *
460    */
461   g_object_class_install_property (gobject_klass, PROP_VOLUME,
462       g_param_spec_double ("volume", "Volume", "The audio volume, 1.0=100%",
463           0.0, VOLUME_MAX_DOUBLE, 1.0,
464           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
465   g_object_class_install_property (gobject_klass, PROP_MUTE,
466       g_param_spec_boolean ("mute", "Mute",
467           "Mute the audio channel without changing the volume", FALSE,
468           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
469   g_object_class_install_property (gobject_klass, PROP_FONT_DESC,
470       g_param_spec_string ("subtitle-font-desc",
471           "Subtitle font description",
472           "Pango font description of font "
473           "to be used for subtitle rendering", NULL,
474           G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
475   g_object_class_install_property (gobject_klass, PROP_SUBTITLE_ENCODING,
476       g_param_spec_string ("subtitle-encoding", "subtitle encoding",
477           "Encoding to assume if input subtitles are not in UTF-8 encoding. "
478           "If not set, the GST_SUBTITLE_ENCODING environment variable will "
479           "be checked for an encoding to use. If that is not set either, "
480           "ISO-8859-15 will be assumed.", NULL,
481           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
482   g_object_class_install_property (gobject_klass, PROP_VIS_PLUGIN,
483       g_param_spec_object ("vis-plugin", "Vis plugin",
484           "the visualization element to use (NULL = default)",
485           GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
486   /**
487    * GstPlaySink:sample:
488    *
489    * Get the currently rendered or prerolled sample in the video sink.
490    * The #GstCaps in the sample will describe the format of the buffer.
491    */
492   g_object_class_install_property (gobject_klass, PROP_SAMPLE,
493       g_param_spec_boxed ("sample", "Sample",
494           "The last sample (NULL = no video available)",
495           GST_TYPE_SAMPLE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
496   /**
497    * GstPlaySink:av-offset:
498    *
499    * Control the synchronisation offset between the audio and video streams.
500    * Positive values make the audio ahead of the video and negative values make
501    * the audio go behind the video.
502    *
503    * Since: 0.10.30
504    */
505   g_object_class_install_property (gobject_klass, PROP_AV_OFFSET,
506       g_param_spec_int64 ("av-offset", "AV Offset",
507           "The synchronisation offset between audio and video in nanoseconds",
508           G_MININT64, G_MAXINT64, 0,
509           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
510
511   /**
512    * GstPlaySink:video-sink:
513    *
514    * Set the used video sink element. NULL will use the default sink. playsink
515    * must be in %GST_STATE_NULL
516    *
517    * Since: 0.10.36
518    */
519   g_object_class_install_property (gobject_klass, PROP_VIDEO_SINK,
520       g_param_spec_object ("video-sink", "Video Sink",
521           "the video output element to use (NULL = default sink)",
522           GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
523   /**
524    * GstPlaySink:audio-sink:
525    *
526    * Set the used audio sink element. NULL will use the default sink. playsink
527    * must be in %GST_STATE_NULL
528    *
529    * Since: 0.10.36
530    */
531   g_object_class_install_property (gobject_klass, PROP_AUDIO_SINK,
532       g_param_spec_object ("audio-sink", "Audio Sink",
533           "the audio output element to use (NULL = default sink)",
534           GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
535
536   /**
537    * GstPlaySink:text-sink:
538    *
539    * Set the used text sink element. NULL will use the default sink. playsink
540    * must be in %GST_STATE_NULL
541    *
542    * Since: 0.10.36
543    */
544   g_object_class_install_property (gobject_klass, PROP_TEXT_SINK,
545       g_param_spec_object ("text-sink", "Text sink",
546           "the text output element to use (NULL = default subtitleoverlay)",
547           GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
548
549   /**
550    * GstPlaySink::send-event-mode:
551    *
552    * Sets the handling method used for events received from send_event
553    * function. The default is %MODE_DEFAULT, that uses %GstBin's default
554    * handling (push the event to all internal sinks).
555    *
556    * Since: 0.10.37
557    */
558   g_object_class_install_property (gobject_klass, PROP_SEND_EVENT_MODE,
559       g_param_spec_enum ("send-event-mode", "Send event mode",
560           "How to send events received in send_event function",
561           GST_TYPE_PLAY_SINK_SEND_EVENT_MODE, MODE_DEFAULT,
562           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
563
564   /**
565    * GstPlaySink::force-aspect-ratio:
566    *
567    * Requests the video sink to enforce the video display aspect ratio.
568    *
569    * Since: 0.10.37
570    */
571   g_object_class_install_property (gobject_klass, PROP_FORCE_ASPECT_RATIO,
572       g_param_spec_boolean ("force-aspect-ratio", "Force Aspect Ratio",
573           "When enabled, scaling will respect original aspect ratio", TRUE,
574           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
575
576   g_signal_new ("reconfigure", G_TYPE_FROM_CLASS (klass),
577       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstPlaySinkClass,
578           reconfigure), NULL, NULL, g_cclosure_marshal_generic, G_TYPE_BOOLEAN,
579       0, G_TYPE_NONE);
580   /**
581    * GstPlaySink::convert-sample
582    * @playsink: a #GstPlaySink
583    * @caps: the target format of the sample
584    *
585    * Action signal to retrieve the currently playing video sample in the format
586    * specified by @caps.
587    * If @caps is %NULL, no conversion will be performed and this function is
588    * equivalent to the #GstPlaySink::sample property.
589    *
590    * Returns: a #GstSample of the current video sample converted to #caps.
591    * The caps in the sample will describe the final layout of the buffer data.
592    * %NULL is returned when no current sample can be retrieved or when the
593    * conversion failed.
594    */
595   g_signal_new ("convert-sample", G_TYPE_FROM_CLASS (klass),
596       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
597       G_STRUCT_OFFSET (GstPlaySinkClass, convert_sample), NULL, NULL,
598       g_cclosure_marshal_generic, GST_TYPE_SAMPLE, 1, GST_TYPE_CAPS);
599
600   gst_element_class_add_pad_template (gstelement_klass,
601       gst_static_pad_template_get (&audiorawtemplate));
602   gst_element_class_add_pad_template (gstelement_klass,
603       gst_static_pad_template_get (&audiotemplate));
604   gst_element_class_add_pad_template (gstelement_klass,
605       gst_static_pad_template_get (&videorawtemplate));
606   gst_element_class_add_pad_template (gstelement_klass,
607       gst_static_pad_template_get (&videotemplate));
608   gst_element_class_add_pad_template (gstelement_klass,
609       gst_static_pad_template_get (&texttemplate));
610   gst_element_class_set_static_metadata (gstelement_klass, "Player Sink",
611       "Generic/Bin/Sink",
612       "Convenience sink for multiple streams",
613       "Wim Taymans <wim.taymans@gmail.com>");
614
615   gstelement_klass->change_state =
616       GST_DEBUG_FUNCPTR (gst_play_sink_change_state);
617   gstelement_klass->send_event = GST_DEBUG_FUNCPTR (gst_play_sink_send_event);
618   gstelement_klass->request_new_pad =
619       GST_DEBUG_FUNCPTR (gst_play_sink_request_new_pad);
620   gstelement_klass->release_pad =
621       GST_DEBUG_FUNCPTR (gst_play_sink_release_request_pad);
622
623   gstbin_klass->handle_message =
624       GST_DEBUG_FUNCPTR (gst_play_sink_handle_message);
625
626   klass->reconfigure = GST_DEBUG_FUNCPTR (gst_play_sink_reconfigure);
627   klass->convert_sample = GST_DEBUG_FUNCPTR (gst_play_sink_convert_sample);
628
629   _playsink_reset_segment_event_marker_id =
630       g_quark_from_static_string ("gst-playsink-reset-segment-event-marker");
631
632   g_type_class_ref (GST_TYPE_STREAM_SYNCHRONIZER);
633   g_type_class_ref (GST_TYPE_COLOR_BALANCE_CHANNEL);
634 }
635
636 static void
637 gst_play_sink_init (GstPlaySink * playsink)
638 {
639   GstColorBalanceChannel *channel;
640
641   /* init groups */
642   playsink->video_sink = NULL;
643   playsink->audio_sink = NULL;
644   playsink->visualisation = NULL;
645   playsink->text_sink = NULL;
646   playsink->volume = 1.0;
647   playsink->font_desc = NULL;
648   playsink->subtitle_encoding = NULL;
649   playsink->flags = DEFAULT_FLAGS;
650   playsink->send_event_mode = MODE_DEFAULT;
651   playsink->force_aspect_ratio = TRUE;
652
653   playsink->stream_synchronizer =
654       g_object_new (GST_TYPE_STREAM_SYNCHRONIZER, NULL);
655   gst_bin_add (GST_BIN_CAST (playsink),
656       GST_ELEMENT_CAST (playsink->stream_synchronizer));
657
658   g_rec_mutex_init (&playsink->lock);
659   GST_OBJECT_FLAG_SET (playsink, GST_ELEMENT_FLAG_SINK);
660
661   channel =
662       GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
663           NULL));
664   channel->label = g_strdup ("CONTRAST");
665   channel->min_value = -1000;
666   channel->max_value = 1000;
667   playsink->colorbalance_channels =
668       g_list_append (playsink->colorbalance_channels, channel);
669   playsink->colorbalance_values[0] = 0;
670
671   channel =
672       GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
673           NULL));
674   channel->label = g_strdup ("BRIGHTNESS");
675   channel->min_value = -1000;
676   channel->max_value = 1000;
677   playsink->colorbalance_channels =
678       g_list_append (playsink->colorbalance_channels, channel);
679   playsink->colorbalance_values[1] = 0;
680
681   channel =
682       GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
683           NULL));
684   channel->label = g_strdup ("HUE");
685   channel->min_value = -1000;
686   channel->max_value = 1000;
687   playsink->colorbalance_channels =
688       g_list_append (playsink->colorbalance_channels, channel);
689   playsink->colorbalance_values[2] = 0;
690
691   channel =
692       GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
693           NULL));
694   channel->label = g_strdup ("SATURATION");
695   channel->min_value = -1000;
696   channel->max_value = 1000;
697   playsink->colorbalance_channels =
698       g_list_append (playsink->colorbalance_channels, channel);
699   playsink->colorbalance_values[3] = 0;
700 }
701
702 static void
703 disconnect_chain (GstPlayAudioChain * chain, GstPlaySink * playsink)
704 {
705   if (chain) {
706     if (chain->volume)
707       g_signal_handlers_disconnect_by_func (chain->volume, notify_volume_cb,
708           playsink);
709     if (chain->mute)
710       g_signal_handlers_disconnect_by_func (chain->mute, notify_mute_cb,
711           playsink);
712   }
713 }
714
715 static void
716 free_chain (GstPlayChain * chain)
717 {
718   if (chain) {
719     if (chain->bin)
720       gst_object_unref (chain->bin);
721     g_free (chain);
722   }
723 }
724
725 static void
726 gst_play_sink_dispose (GObject * object)
727 {
728   GstPlaySink *playsink;
729
730   playsink = GST_PLAY_SINK (object);
731
732   if (playsink->audio_sink != NULL) {
733     gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
734     gst_object_unref (playsink->audio_sink);
735     playsink->audio_sink = NULL;
736   }
737   if (playsink->video_sink != NULL) {
738     gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
739     gst_object_unref (playsink->video_sink);
740     playsink->video_sink = NULL;
741   }
742   if (playsink->visualisation != NULL) {
743     gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
744     gst_object_unref (playsink->visualisation);
745     playsink->visualisation = NULL;
746   }
747   if (playsink->text_sink != NULL) {
748     gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
749     gst_object_unref (playsink->text_sink);
750     playsink->text_sink = NULL;
751   }
752
753   free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
754   playsink->videodeinterlacechain = NULL;
755   free_chain ((GstPlayChain *) playsink->videochain);
756   playsink->videochain = NULL;
757   free_chain ((GstPlayChain *) playsink->audiochain);
758   playsink->audiochain = NULL;
759   free_chain ((GstPlayChain *) playsink->vischain);
760   playsink->vischain = NULL;
761   free_chain ((GstPlayChain *) playsink->textchain);
762   playsink->textchain = NULL;
763
764   if (playsink->audio_tee_sink) {
765     gst_object_unref (playsink->audio_tee_sink);
766     playsink->audio_tee_sink = NULL;
767   }
768
769   if (playsink->audio_tee_vissrc) {
770     gst_element_release_request_pad (playsink->audio_tee,
771         playsink->audio_tee_vissrc);
772     gst_object_unref (playsink->audio_tee_vissrc);
773     playsink->audio_tee_vissrc = NULL;
774   }
775
776   if (playsink->audio_tee_asrc) {
777     gst_element_release_request_pad (playsink->audio_tee,
778         playsink->audio_tee_asrc);
779     gst_object_unref (playsink->audio_tee_asrc);
780     playsink->audio_tee_asrc = NULL;
781   }
782
783   g_free (playsink->font_desc);
784   playsink->font_desc = NULL;
785
786   g_free (playsink->subtitle_encoding);
787   playsink->subtitle_encoding = NULL;
788
789   playsink->stream_synchronizer = NULL;
790
791   g_list_foreach (playsink->colorbalance_channels, (GFunc) gst_object_unref,
792       NULL);
793   g_list_free (playsink->colorbalance_channels);
794   playsink->colorbalance_channels = NULL;
795
796   G_OBJECT_CLASS (gst_play_sink_parent_class)->dispose (object);
797 }
798
799 static void
800 gst_play_sink_finalize (GObject * object)
801 {
802   GstPlaySink *playsink;
803
804   playsink = GST_PLAY_SINK (object);
805
806   g_rec_mutex_clear (&playsink->lock);
807
808   G_OBJECT_CLASS (gst_play_sink_parent_class)->finalize (object);
809 }
810
811 void
812 gst_play_sink_set_sink (GstPlaySink * playsink, GstPlaySinkType type,
813     GstElement * sink)
814 {
815   GstElement **elem = NULL, *old = NULL;
816
817   GST_LOG ("Setting sink %" GST_PTR_FORMAT " as sink type %d", sink, type);
818
819   GST_PLAY_SINK_LOCK (playsink);
820   switch (type) {
821     case GST_PLAY_SINK_TYPE_AUDIO:
822     case GST_PLAY_SINK_TYPE_AUDIO_RAW:
823       elem = &playsink->audio_sink;
824       break;
825     case GST_PLAY_SINK_TYPE_VIDEO:
826     case GST_PLAY_SINK_TYPE_VIDEO_RAW:
827       elem = &playsink->video_sink;
828       break;
829     case GST_PLAY_SINK_TYPE_TEXT:
830       elem = &playsink->text_sink;
831       break;
832     default:
833       break;
834   }
835   if (elem) {
836     old = *elem;
837     if (sink)
838       gst_object_ref (sink);
839     *elem = sink;
840   }
841   GST_PLAY_SINK_UNLOCK (playsink);
842
843   if (old) {
844     if (old != sink)
845       gst_element_set_state (old, GST_STATE_NULL);
846     gst_object_unref (old);
847   }
848 }
849
850 GstElement *
851 gst_play_sink_get_sink (GstPlaySink * playsink, GstPlaySinkType type)
852 {
853   GstElement *result = NULL;
854   GstElement *elem = NULL, *chainp = NULL;
855
856   GST_PLAY_SINK_LOCK (playsink);
857   switch (type) {
858     case GST_PLAY_SINK_TYPE_AUDIO:
859     case GST_PLAY_SINK_TYPE_AUDIO_RAW:
860     {
861       GstPlayAudioChain *chain;
862       if ((chain = (GstPlayAudioChain *) playsink->audiochain))
863         chainp = chain->sink;
864       elem = playsink->audio_sink;
865       break;
866     }
867     case GST_PLAY_SINK_TYPE_VIDEO:
868     case GST_PLAY_SINK_TYPE_VIDEO_RAW:
869     {
870       GstPlayVideoChain *chain;
871       if ((chain = (GstPlayVideoChain *) playsink->videochain))
872         chainp = chain->sink;
873       elem = playsink->video_sink;
874       break;
875     }
876     case GST_PLAY_SINK_TYPE_TEXT:
877     {
878       GstPlayTextChain *chain;
879       if ((chain = (GstPlayTextChain *) playsink->textchain))
880         chainp = chain->sink;
881       elem = playsink->text_sink;
882       break;
883     }
884     default:
885       break;
886   }
887   if (chainp) {
888     /* we have an active chain with a sink, get the sink */
889     result = gst_object_ref (chainp);
890   }
891   /* nothing found, return last configured sink */
892   if (result == NULL && elem)
893     result = gst_object_ref (elem);
894   GST_PLAY_SINK_UNLOCK (playsink);
895
896   return result;
897 }
898
899 static GstPadProbeReturn
900 gst_play_sink_vis_blocked (GstPad * tee_pad, GstPadProbeInfo * info,
901     gpointer user_data)
902 {
903   GstPlaySink *playsink;
904   GstPlayVisChain *chain;
905
906   playsink = GST_PLAY_SINK (user_data);
907
908   GST_PLAY_SINK_LOCK (playsink);
909   GST_DEBUG_OBJECT (playsink, "vis pad blocked");
910   /* now try to change the plugin in the running vis chain */
911   if (!(chain = (GstPlayVisChain *) playsink->vischain))
912     goto done;
913
914   /* unlink the old plugin and unghost the pad */
915   gst_pad_unlink (chain->vispeerpad, chain->vissinkpad);
916   gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad), NULL);
917
918   /* set the old plugin to NULL and remove */
919   gst_element_set_state (chain->vis, GST_STATE_NULL);
920   gst_bin_remove (GST_BIN_CAST (chain->chain.bin), chain->vis);
921
922   /* add new plugin and set state to playing */
923   chain->vis = playsink->visualisation;
924   gst_bin_add (GST_BIN_CAST (chain->chain.bin), chain->vis);
925   gst_element_set_state (chain->vis, GST_STATE_PLAYING);
926
927   /* get pads */
928   chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
929   chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
930
931   /* link pads */
932   gst_pad_link_full (chain->vispeerpad, chain->vissinkpad,
933       GST_PAD_LINK_CHECK_NOTHING);
934   gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad),
935       chain->vissrcpad);
936
937 done:
938   GST_PLAY_SINK_UNLOCK (playsink);
939
940   /* remove the probe and unblock the pad */
941   return GST_PAD_PROBE_REMOVE;
942 }
943
944 void
945 gst_play_sink_set_vis_plugin (GstPlaySink * playsink, GstElement * vis)
946 {
947   GstPlayVisChain *chain;
948
949   /* setting NULL means creating the default vis plugin */
950   if (vis == NULL)
951     vis = gst_element_factory_make ("goom", "vis");
952
953   /* simply return if we don't have a vis plugin here */
954   if (vis == NULL)
955     return;
956
957   GST_PLAY_SINK_LOCK (playsink);
958   /* first store the new vis */
959   if (playsink->visualisation)
960     gst_object_unref (playsink->visualisation);
961   /* take ownership */
962   gst_object_ref_sink (vis);
963   playsink->visualisation = vis;
964
965   /* now try to change the plugin in the running vis chain, if we have no chain,
966    * we don't bother, any future vis chain will be created with the new vis
967    * plugin. */
968   if (!(chain = (GstPlayVisChain *) playsink->vischain))
969     goto done;
970
971   /* block the pad, the next time the callback is called we can change the
972    * visualisation. It's possible that this never happens or that the pad was
973    * already blocked. If the callback never happens, we don't have new data so
974    * we don't need the new vis plugin. If the pad was already blocked, the
975    * function returns FALSE but the previous pad block will do the right thing
976    * anyway. */
977   GST_DEBUG_OBJECT (playsink, "blocking vis pad");
978   gst_pad_add_probe (chain->blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
979       gst_play_sink_vis_blocked, playsink, NULL);
980 done:
981   GST_PLAY_SINK_UNLOCK (playsink);
982
983   return;
984 }
985
986 GstElement *
987 gst_play_sink_get_vis_plugin (GstPlaySink * playsink)
988 {
989   GstElement *result = NULL;
990   GstPlayVisChain *chain;
991
992   GST_PLAY_SINK_LOCK (playsink);
993   if ((chain = (GstPlayVisChain *) playsink->vischain)) {
994     /* we have an active chain, get the sink */
995     if (chain->vis)
996       result = gst_object_ref (chain->vis);
997   }
998   /* nothing found, return last configured sink */
999   if (result == NULL && playsink->visualisation)
1000     result = gst_object_ref (playsink->visualisation);
1001   GST_PLAY_SINK_UNLOCK (playsink);
1002
1003   return result;
1004 }
1005
1006 void
1007 gst_play_sink_set_volume (GstPlaySink * playsink, gdouble volume)
1008 {
1009   GstPlayAudioChain *chain;
1010
1011   GST_PLAY_SINK_LOCK (playsink);
1012   playsink->volume = volume;
1013   chain = (GstPlayAudioChain *) playsink->audiochain;
1014   if (chain && chain->volume) {
1015     GST_LOG_OBJECT (playsink, "elements: volume=%" GST_PTR_FORMAT ", mute=%"
1016         GST_PTR_FORMAT "; new volume=%.03f, mute=%d", chain->volume,
1017         chain->mute, volume, playsink->mute);
1018     /* if there is a mute element or we are not muted, set the volume */
1019     if (chain->mute || !playsink->mute)
1020       g_object_set (chain->volume, "volume", volume, NULL);
1021   } else {
1022     GST_LOG_OBJECT (playsink, "no volume element");
1023     playsink->volume_changed = TRUE;
1024   }
1025   GST_PLAY_SINK_UNLOCK (playsink);
1026 }
1027
1028 gdouble
1029 gst_play_sink_get_volume (GstPlaySink * playsink)
1030 {
1031   gdouble result;
1032   GstPlayAudioChain *chain;
1033
1034   GST_PLAY_SINK_LOCK (playsink);
1035   chain = (GstPlayAudioChain *) playsink->audiochain;
1036   result = playsink->volume;
1037   if (chain && chain->volume) {
1038     if (chain->mute || !playsink->mute) {
1039       g_object_get (chain->volume, "volume", &result, NULL);
1040       playsink->volume = result;
1041     }
1042   }
1043   GST_PLAY_SINK_UNLOCK (playsink);
1044
1045   return result;
1046 }
1047
1048 void
1049 gst_play_sink_set_mute (GstPlaySink * playsink, gboolean mute)
1050 {
1051   GstPlayAudioChain *chain;
1052
1053   GST_PLAY_SINK_LOCK (playsink);
1054   playsink->mute = mute;
1055   chain = (GstPlayAudioChain *) playsink->audiochain;
1056   if (chain) {
1057     if (chain->mute) {
1058       g_object_set (chain->mute, "mute", mute, NULL);
1059     } else if (chain->volume) {
1060       if (mute)
1061         g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
1062       else
1063         g_object_set (chain->volume, "volume", (gdouble) playsink->volume,
1064             NULL);
1065     }
1066   } else {
1067     playsink->mute_changed = TRUE;
1068   }
1069   GST_PLAY_SINK_UNLOCK (playsink);
1070 }
1071
1072 gboolean
1073 gst_play_sink_get_mute (GstPlaySink * playsink)
1074 {
1075   gboolean result;
1076   GstPlayAudioChain *chain;
1077
1078   GST_PLAY_SINK_LOCK (playsink);
1079   chain = (GstPlayAudioChain *) playsink->audiochain;
1080   if (chain && chain->mute) {
1081     g_object_get (chain->mute, "mute", &result, NULL);
1082     playsink->mute = result;
1083   } else {
1084     result = playsink->mute;
1085   }
1086   GST_PLAY_SINK_UNLOCK (playsink);
1087
1088   return result;
1089 }
1090
1091 static void
1092 post_missing_element_message (GstPlaySink * playsink, const gchar * name)
1093 {
1094   GstMessage *msg;
1095
1096   msg = gst_missing_element_message_new (GST_ELEMENT_CAST (playsink), name);
1097   gst_element_post_message (GST_ELEMENT_CAST (playsink), msg);
1098 }
1099
1100 static gboolean
1101 add_chain (GstPlayChain * chain, gboolean add)
1102 {
1103   if (chain->added == add)
1104     return TRUE;
1105
1106   if (add)
1107     gst_bin_add (GST_BIN_CAST (chain->playsink), chain->bin);
1108   else {
1109     gst_bin_remove (GST_BIN_CAST (chain->playsink), chain->bin);
1110     /* we don't want to lose our sink status */
1111     GST_OBJECT_FLAG_SET (chain->playsink, GST_ELEMENT_FLAG_SINK);
1112   }
1113
1114   chain->added = add;
1115
1116   return TRUE;
1117 }
1118
1119 static gboolean
1120 activate_chain (GstPlayChain * chain, gboolean activate)
1121 {
1122   GstState state;
1123
1124   if (chain->activated == activate)
1125     return TRUE;
1126
1127   GST_OBJECT_LOCK (chain->playsink);
1128   state = GST_STATE_TARGET (chain->playsink);
1129   GST_OBJECT_UNLOCK (chain->playsink);
1130
1131   if (activate)
1132     gst_element_set_state (chain->bin, state);
1133   else
1134     gst_element_set_state (chain->bin, GST_STATE_NULL);
1135
1136   chain->activated = activate;
1137
1138   return TRUE;
1139 }
1140
1141 static gboolean
1142 element_is_sink (GstElement * element)
1143 {
1144   gboolean is_sink;
1145
1146   GST_OBJECT_LOCK (element);
1147   is_sink = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_FLAG_SINK);
1148   GST_OBJECT_UNLOCK (element);
1149
1150   GST_DEBUG_OBJECT (element, "is a sink: %s", (is_sink) ? "yes" : "no");
1151   return is_sink;
1152 }
1153
1154 static gboolean
1155 element_has_property (GstElement * element, const gchar * pname, GType type)
1156 {
1157   GParamSpec *pspec;
1158
1159   pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (element), pname);
1160
1161   if (pspec == NULL) {
1162     GST_DEBUG_OBJECT (element, "no %s property", pname);
1163     return FALSE;
1164   }
1165
1166   if (type == G_TYPE_INVALID || type == pspec->value_type ||
1167       g_type_is_a (pspec->value_type, type)) {
1168     GST_DEBUG_OBJECT (element, "has %s property of type %s", pname,
1169         (type == G_TYPE_INVALID) ? "any type" : g_type_name (type));
1170     return TRUE;
1171   }
1172
1173   GST_WARNING_OBJECT (element, "has %s property, but property is of type %s "
1174       "and we expected it to be of type %s", pname,
1175       g_type_name (pspec->value_type), g_type_name (type));
1176
1177   return FALSE;
1178 }
1179
1180 typedef struct
1181 {
1182   const gchar *prop_name;
1183   GType prop_type;
1184   gboolean need_sink;
1185 } FindPropertyHelper;
1186
1187 static gint
1188 find_property (const GValue * item, FindPropertyHelper * helper)
1189 {
1190   GstElement *element = g_value_get_object (item);
1191   if (helper->need_sink && !element_is_sink (element)) {
1192     return 1;
1193   }
1194
1195   if (!element_has_property (element, helper->prop_name, helper->prop_type)) {
1196     return 1;
1197   }
1198
1199   GST_INFO_OBJECT (element, "found %s with %s property", helper->prop_name,
1200       (helper->need_sink) ? "sink" : "element");
1201   return 0;                     /* keep it */
1202 }
1203
1204 /* FIXME: why not move these functions into core? */
1205 /* find a sink in the hierarchy with a property named @name. This function does
1206  * not increase the refcount of the returned object and thus remains valid as
1207  * long as the bin is valid. */
1208 static GstElement *
1209 gst_play_sink_find_property_sinks (GstPlaySink * playsink, GstElement * obj,
1210     const gchar * name, GType expected_type)
1211 {
1212   GstElement *result = NULL;
1213   GstIterator *it;
1214
1215   if (element_has_property (obj, name, expected_type)) {
1216     result = obj;
1217   } else if (GST_IS_BIN (obj)) {
1218     gboolean found;
1219     GValue item = { 0, };
1220     FindPropertyHelper helper = { name, expected_type, TRUE };
1221
1222     it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1223     found = gst_iterator_find_custom (it,
1224         (GCompareFunc) find_property, &item, &helper);
1225     gst_iterator_free (it);
1226     if (found) {
1227       result = g_value_get_object (&item);
1228       /* we don't need the extra ref */
1229       g_value_unset (&item);
1230     }
1231   }
1232   return result;
1233 }
1234
1235 /* find an object in the hierarchy with a property named @name */
1236 static GstElement *
1237 gst_play_sink_find_property (GstPlaySink * playsink, GstElement * obj,
1238     const gchar * name, GType expected_type)
1239 {
1240   GstElement *result = NULL;
1241   GstIterator *it;
1242
1243   if (GST_IS_BIN (obj)) {
1244     gboolean found;
1245     GValue item = { 0, };
1246     FindPropertyHelper helper = { name, expected_type, FALSE };
1247
1248     it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1249     found = gst_iterator_find_custom (it,
1250         (GCompareFunc) find_property, &item, &helper);
1251     gst_iterator_free (it);
1252     if (found) {
1253       result = g_value_dup_object (&item);
1254       g_value_unset (&item);
1255     }
1256   } else {
1257     if (element_has_property (obj, name, expected_type)) {
1258       result = obj;
1259       gst_object_ref (obj);
1260     }
1261   }
1262   return result;
1263 }
1264
1265 static void
1266 do_async_start (GstPlaySink * playsink)
1267 {
1268   GstMessage *message;
1269
1270   if (!playsink->need_async_start) {
1271     GST_INFO_OBJECT (playsink, "no async_start needed");
1272     return;
1273   }
1274
1275   playsink->async_pending = TRUE;
1276
1277   GST_INFO_OBJECT (playsink, "Sending async_start message");
1278   message = gst_message_new_async_start (GST_OBJECT_CAST (playsink));
1279   GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1280       (playsink), message);
1281 }
1282
1283 static void
1284 do_async_done (GstPlaySink * playsink)
1285 {
1286   GstMessage *message;
1287
1288   if (playsink->async_pending) {
1289     GST_INFO_OBJECT (playsink, "Sending async_done message");
1290     message =
1291         gst_message_new_async_done (GST_OBJECT_CAST (playsink),
1292         GST_CLOCK_TIME_NONE);
1293     GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1294         (playsink), message);
1295
1296     playsink->async_pending = FALSE;
1297   }
1298
1299   playsink->need_async_start = FALSE;
1300 }
1301
1302 /* try to change the state of an element. This function returns the element when
1303  * the state change could be performed. When this function returns NULL an error
1304  * occured and the element is unreffed if @unref is TRUE. */
1305 static GstElement *
1306 try_element (GstPlaySink * playsink, GstElement * element, gboolean unref)
1307 {
1308   GstStateChangeReturn ret;
1309
1310   if (element) {
1311     ret = gst_element_set_state (element, GST_STATE_READY);
1312     if (ret == GST_STATE_CHANGE_FAILURE) {
1313       GST_DEBUG_OBJECT (playsink, "failed state change..");
1314       gst_element_set_state (element, GST_STATE_NULL);
1315       if (unref)
1316         gst_object_unref (element);
1317       element = NULL;
1318     }
1319   }
1320   return element;
1321 }
1322
1323 /* make the element (bin) that contains the elements needed to perform
1324  * video display. Only used for *raw* video streams.
1325  *
1326  *  +------------------------------------------------------------+
1327  *  | vbin                                                       |
1328  *  |      +-------+   +----------+   +----------+   +---------+ |
1329  *  |      | queue |   |colorspace|   |videoscale|   |videosink| |
1330  *  |   +-sink    src-sink       src-sink       src-sink       | |
1331  *  |   |  +-------+   +----------+   +----------+   +---------+ |
1332  * sink-+                                                        |
1333  *  +------------------------------------------------------------+
1334  *
1335  */
1336 static GstPlayVideoDeinterlaceChain *
1337 gen_video_deinterlace_chain (GstPlaySink * playsink)
1338 {
1339   GstPlayVideoDeinterlaceChain *chain;
1340   GstBin *bin;
1341   GstPad *pad;
1342   GstElement *head = NULL, *prev = NULL;
1343
1344   chain = g_new0 (GstPlayVideoDeinterlaceChain, 1);
1345   chain->chain.playsink = playsink;
1346
1347   GST_DEBUG_OBJECT (playsink, "making video deinterlace chain %p", chain);
1348
1349   /* create a bin to hold objects, as we create them we add them to this bin so
1350    * that when something goes wrong we only need to unref the bin */
1351   chain->chain.bin = gst_bin_new ("vdbin");
1352   bin = GST_BIN_CAST (chain->chain.bin);
1353   gst_object_ref_sink (bin);
1354
1355   GST_DEBUG_OBJECT (playsink, "creating " COLORSPACE);
1356   chain->conv = gst_element_factory_make (COLORSPACE, "vdconv");
1357   if (chain->conv == NULL) {
1358     post_missing_element_message (playsink, COLORSPACE);
1359     GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1360         (_("Missing element '%s' - check your GStreamer installation."),
1361             COLORSPACE), ("video rendering might fail"));
1362   } else {
1363     gst_bin_add (bin, chain->conv);
1364     head = chain->conv;
1365     prev = chain->conv;
1366   }
1367
1368   GST_DEBUG_OBJECT (playsink, "creating deinterlace");
1369   chain->deinterlace = gst_element_factory_make ("deinterlace", "deinterlace");
1370   if (chain->deinterlace == NULL) {
1371     chain->deinterlace =
1372         gst_element_factory_make ("avdeinterlace", "deinterlace");
1373   }
1374   if (chain->deinterlace == NULL) {
1375     post_missing_element_message (playsink, "deinterlace");
1376     GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1377         (_("Missing element '%s' - check your GStreamer installation."),
1378             "deinterlace"), ("deinterlacing won't work"));
1379   } else {
1380     gst_bin_add (bin, chain->deinterlace);
1381     if (prev) {
1382       if (!gst_element_link_pads_full (prev, "src", chain->deinterlace, "sink",
1383               GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1384         goto link_failed;
1385     } else {
1386       head = chain->deinterlace;
1387     }
1388     prev = chain->deinterlace;
1389   }
1390
1391   if (head) {
1392     pad = gst_element_get_static_pad (head, "sink");
1393     chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1394     gst_object_unref (pad);
1395   } else {
1396     chain->sinkpad = gst_ghost_pad_new_no_target ("sink", GST_PAD_SINK);
1397   }
1398
1399   if (prev) {
1400     pad = gst_element_get_static_pad (prev, "src");
1401     chain->srcpad = gst_ghost_pad_new ("src", pad);
1402     gst_object_unref (pad);
1403   } else {
1404     chain->srcpad = gst_ghost_pad_new ("src", chain->sinkpad);
1405   }
1406
1407   gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1408   gst_element_add_pad (chain->chain.bin, chain->srcpad);
1409
1410   return chain;
1411
1412 link_failed:
1413   {
1414     GST_ELEMENT_ERROR (playsink, CORE, PAD,
1415         (NULL), ("Failed to configure the video deinterlace chain."));
1416     free_chain ((GstPlayChain *) chain);
1417     return NULL;
1418   }
1419 }
1420
1421 static gboolean
1422 is_valid_color_balance_element (GstColorBalance * bal)
1423 {
1424   gboolean have_brightness = FALSE;
1425   gboolean have_contrast = FALSE;
1426   gboolean have_hue = FALSE;
1427   gboolean have_saturation = FALSE;
1428   const GList *channels, *l;
1429
1430   channels = gst_color_balance_list_channels (bal);
1431   for (l = channels; l; l = l->next) {
1432     GstColorBalanceChannel *ch = l->data;
1433
1434     if (g_strrstr (ch->label, "BRIGHTNESS"))
1435       have_brightness = TRUE;
1436     else if (g_strrstr (ch->label, "CONTRAST"))
1437       have_contrast = TRUE;
1438     else if (g_strrstr (ch->label, "HUE"))
1439       have_hue = TRUE;
1440     else if (g_strrstr (ch->label, "SATURATION"))
1441       have_saturation = TRUE;
1442   }
1443
1444   return have_brightness && have_contrast && have_hue && have_saturation;
1445 }
1446
1447 static void
1448 iterate_color_balance_elements (const GValue * item, gpointer user_data)
1449 {
1450   gboolean valid;
1451   GstColorBalance *cb, **cb_out = user_data;
1452
1453   cb = GST_COLOR_BALANCE (g_value_get_object (item));
1454   valid = is_valid_color_balance_element (cb);
1455   if (valid) {
1456     if (*cb_out
1457         && gst_color_balance_get_balance_type (*cb_out) ==
1458         GST_COLOR_BALANCE_SOFTWARE) {
1459       gst_object_unref (*cb_out);
1460       *cb_out = GST_COLOR_BALANCE (gst_object_ref (cb));
1461     } else if (!*cb_out) {
1462       *cb_out = GST_COLOR_BALANCE (gst_object_ref (cb));
1463     }
1464   }
1465 }
1466
1467 static GstColorBalance *
1468 find_color_balance_element (GstElement * element)
1469 {
1470   GstIterator *it;
1471   GstColorBalance *cb = NULL;
1472
1473   if (GST_IS_COLOR_BALANCE (element)
1474       && is_valid_color_balance_element (GST_COLOR_BALANCE (element)))
1475     return GST_COLOR_BALANCE (gst_object_ref (element));
1476   else if (!GST_IS_BIN (element))
1477     return FALSE;
1478
1479   it = gst_bin_iterate_all_by_interface (GST_BIN (element),
1480       GST_TYPE_COLOR_BALANCE);
1481   while (gst_iterator_foreach (it, iterate_color_balance_elements,
1482           &cb) == GST_ITERATOR_RESYNC)
1483     gst_iterator_resync (it);
1484   gst_iterator_free (it);
1485
1486   return cb;
1487 }
1488
1489 static void
1490 colorbalance_value_changed_cb (GstColorBalance * balance,
1491     GstColorBalanceChannel * channel, gint value, GstPlaySink * playsink)
1492 {
1493   GList *l;
1494   gint i;
1495
1496   for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
1497     GstColorBalanceChannel *proxy = l->data;
1498
1499     if (g_strrstr (channel->label, proxy->label)) {
1500       gdouble new_val;
1501
1502       /* Convert to [0, 1] range */
1503       new_val =
1504           ((gdouble) value -
1505           (gdouble) channel->min_value) / ((gdouble) channel->max_value -
1506           (gdouble) channel->min_value);
1507       /* Convert to proxy range */
1508       new_val =
1509           proxy->min_value + new_val * ((gdouble) proxy->max_value -
1510           (gdouble) proxy->min_value);
1511       playsink->colorbalance_values[i] = (gint) (0.5 + new_val);
1512
1513       gst_color_balance_value_changed (GST_COLOR_BALANCE (playsink), proxy,
1514           playsink->colorbalance_values[i]);
1515       break;
1516     }
1517   }
1518 }
1519
1520 static void
1521 update_colorbalance (GstPlaySink * playsink)
1522 {
1523   GstColorBalance *balance = NULL;
1524   GList *l;
1525   gint i;
1526
1527   GST_OBJECT_LOCK (playsink);
1528   if (playsink->colorbalance_element) {
1529     balance =
1530         GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
1531   }
1532   GST_OBJECT_UNLOCK (playsink);
1533   if (!balance)
1534     return;
1535
1536   g_signal_handlers_block_by_func (balance,
1537       G_CALLBACK (colorbalance_value_changed_cb), playsink);
1538
1539   for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
1540     GstColorBalanceChannel *proxy = l->data;
1541     GstColorBalanceChannel *channel = NULL;
1542     const GList *channels, *k;
1543
1544     channels = gst_color_balance_list_channels (balance);
1545     for (k = channels; k; k = k->next) {
1546       GstColorBalanceChannel *tmp = k->data;
1547
1548       if (g_strrstr (tmp->label, proxy->label)) {
1549         channel = tmp;
1550         break;
1551       }
1552     }
1553
1554     g_assert (channel);
1555
1556     gst_color_balance_set_value (balance, channel,
1557         playsink->colorbalance_values[i]);
1558   }
1559
1560   g_signal_handlers_unblock_by_func (balance,
1561       G_CALLBACK (colorbalance_value_changed_cb), playsink);
1562
1563   gst_object_unref (balance);
1564 }
1565
1566 /* make the element (bin) that contains the elements needed to perform
1567  * video display.
1568  *
1569  *  +------------------------------------------------------------+
1570  *  | vbin                                                       |
1571  *  |      +-------+   +----------+   +----------+   +---------+ |
1572  *  |      | queue |   |colorspace|   |videoscale|   |videosink| |
1573  *  |   +-sink    src-sink       src-sink       src-sink       | |
1574  *  |   |  +-------+   +----------+   +----------+   +---------+ |
1575  * sink-+                                                        |
1576  *  +------------------------------------------------------------+
1577  *
1578  */
1579 static GstPlayVideoChain *
1580 gen_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1581 {
1582   GstPlayVideoChain *chain;
1583   GstBin *bin;
1584   GstPad *pad;
1585   GstElement *head = NULL, *prev = NULL, *elem = NULL;
1586
1587   chain = g_new0 (GstPlayVideoChain, 1);
1588   chain->chain.playsink = playsink;
1589   chain->chain.raw = raw;
1590
1591   GST_DEBUG_OBJECT (playsink, "making video chain %p", chain);
1592
1593   if (playsink->video_sink) {
1594     GST_DEBUG_OBJECT (playsink, "trying configured videosink");
1595     chain->sink = try_element (playsink, playsink->video_sink, FALSE);
1596   } else {
1597     /* only try fallback if no specific sink was chosen */
1598     if (chain->sink == NULL) {
1599       GST_DEBUG_OBJECT (playsink, "trying autovideosink");
1600       elem = gst_element_factory_make ("autovideosink", "videosink");
1601       chain->sink = try_element (playsink, elem, TRUE);
1602     }
1603     if (chain->sink == NULL) {
1604       /* if default sink from config.h is different then try it too */
1605       if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1606         GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_VIDEOSINK);
1607         elem = gst_element_factory_make (DEFAULT_VIDEOSINK, "videosink");
1608         chain->sink = try_element (playsink, elem, TRUE);
1609       }
1610     }
1611     if (chain->sink)
1612       playsink->video_sink = gst_object_ref (chain->sink);
1613   }
1614   if (chain->sink == NULL)
1615     goto no_sinks;
1616   head = chain->sink;
1617
1618   /* if we can disable async behaviour of the sink, we can avoid adding a
1619    * queue for the audio chain. */
1620   elem =
1621       gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1622       G_TYPE_BOOLEAN);
1623   if (elem) {
1624     GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1625         async, GST_ELEMENT_NAME (elem));
1626     g_object_set (elem, "async", async, NULL);
1627     chain->async = async;
1628   } else {
1629     GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1630     chain->async = TRUE;
1631   }
1632
1633   /* Make sure the aspect ratio is kept */
1634   elem =
1635       gst_play_sink_find_property_sinks (playsink, chain->sink,
1636       "force-aspect-ratio", G_TYPE_BOOLEAN);
1637   if (elem)
1638     g_object_set (elem, "force-aspect-ratio", playsink->force_aspect_ratio,
1639         NULL);
1640
1641   /* find ts-offset element */
1642   gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1643       gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1644           G_TYPE_INT64));
1645
1646   /* create a bin to hold objects, as we create them we add them to this bin so
1647    * that when something goes wrong we only need to unref the bin */
1648   chain->chain.bin = gst_bin_new ("vbin");
1649   bin = GST_BIN_CAST (chain->chain.bin);
1650   gst_object_ref_sink (bin);
1651   gst_bin_add (bin, chain->sink);
1652
1653   /* Get the VideoOverlay element */
1654   {
1655     GstVideoOverlay *overlay = NULL;
1656
1657     GST_OBJECT_LOCK (playsink);
1658     if (playsink->overlay_element)
1659       gst_object_unref (playsink->overlay_element);
1660     playsink->overlay_element =
1661         GST_VIDEO_OVERLAY (gst_bin_get_by_interface (GST_BIN (chain->chain.bin),
1662             GST_TYPE_VIDEO_OVERLAY));
1663     if (playsink->overlay_element)
1664       overlay = GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
1665     GST_OBJECT_UNLOCK (playsink);
1666
1667     if (overlay) {
1668       if (playsink->overlay_handle_set)
1669         gst_video_overlay_set_window_handle (overlay, playsink->overlay_handle);
1670       if (playsink->overlay_handle_events_set)
1671         gst_video_overlay_handle_events (overlay,
1672             playsink->overlay_handle_events);
1673       if (playsink->overlay_render_rectangle_set)
1674         gst_video_overlay_set_render_rectangle (overlay,
1675             playsink->overlay_x, playsink->overlay_y,
1676             playsink->overlay_width, playsink->overlay_height);
1677       gst_object_unref (overlay);
1678     }
1679   }
1680
1681   /* decouple decoder from sink, this improves playback quite a lot since the
1682    * decoder can continue while the sink blocks for synchronisation. We don't
1683    * need a lot of buffers as this consumes a lot of memory and we don't want
1684    * too little because else we would be context switching too quickly. */
1685   chain->queue = gst_element_factory_make ("queue", "vqueue");
1686   if (chain->queue == NULL) {
1687     post_missing_element_message (playsink, "queue");
1688     GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1689         (_("Missing element '%s' - check your GStreamer installation."),
1690             "queue"), ("video rendering might be suboptimal"));
1691     head = chain->sink;
1692     prev = NULL;
1693   } else {
1694     g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1695         "max-size-bytes", 0, "max-size-time", (gint64) 0, "silent", TRUE, NULL);
1696     gst_bin_add (bin, chain->queue);
1697     head = prev = chain->queue;
1698   }
1699
1700   GST_OBJECT_LOCK (playsink);
1701   if (playsink->colorbalance_element) {
1702     g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
1703         G_CALLBACK (colorbalance_value_changed_cb), playsink);
1704     gst_object_unref (playsink->colorbalance_element);
1705   }
1706   playsink->colorbalance_element = find_color_balance_element (chain->sink);
1707   if (playsink->colorbalance_element) {
1708     g_signal_connect (playsink->colorbalance_element, "value-changed",
1709         G_CALLBACK (colorbalance_value_changed_cb), playsink);
1710   }
1711   GST_OBJECT_UNLOCK (playsink);
1712
1713   if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)
1714       || (!playsink->colorbalance_element
1715           && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE))) {
1716     gboolean use_converters = !(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO);
1717     gboolean use_balance = !playsink->colorbalance_element
1718         && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE);
1719
1720     GST_DEBUG_OBJECT (playsink, "creating videoconverter");
1721     chain->conv =
1722         g_object_new (GST_TYPE_PLAY_SINK_VIDEO_CONVERT, "name", "vconv",
1723         "use-converters", use_converters, "use-balance", use_balance, NULL);
1724
1725     GST_OBJECT_LOCK (playsink);
1726     if (use_balance && GST_PLAY_SINK_VIDEO_CONVERT (chain->conv)->balance)
1727       playsink->colorbalance_element =
1728           GST_COLOR_BALANCE (gst_object_ref (GST_PLAY_SINK_VIDEO_CONVERT
1729               (chain->conv)->balance));
1730     GST_OBJECT_UNLOCK (playsink);
1731
1732     gst_bin_add (bin, chain->conv);
1733     if (prev) {
1734       if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
1735               GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1736         goto link_failed;
1737     } else {
1738       head = chain->conv;
1739     }
1740     prev = chain->conv;
1741   }
1742
1743   update_colorbalance (playsink);
1744
1745   if (prev) {
1746     GST_DEBUG_OBJECT (playsink, "linking to sink");
1747     if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
1748             GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1749       goto link_failed;
1750   }
1751
1752   pad = gst_element_get_static_pad (head, "sink");
1753   chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1754
1755   /* sending audio/video flushes break stream changes when the pipeline
1756    * is paused and played again in 0.10 */
1757 #if 0
1758   gst_pad_set_event_function (chain->sinkpad,
1759       GST_DEBUG_FUNCPTR (gst_play_sink_video_sink_event));
1760   gst_pad_set_chain_function (chain->sinkpad,
1761       GST_DEBUG_FUNCPTR (gst_play_sink_video_sink_chain));
1762 #endif
1763
1764   gst_object_unref (pad);
1765   gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1766
1767   return chain;
1768
1769   /* ERRORS */
1770 no_sinks:
1771   {
1772     if (!elem && !playsink->video_sink) {
1773       post_missing_element_message (playsink, "autovideosink");
1774       if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1775         post_missing_element_message (playsink, DEFAULT_VIDEOSINK);
1776         GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1777             (_("Both autovideosink and %s elements are missing."),
1778                 DEFAULT_VIDEOSINK), (NULL));
1779       } else {
1780         GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1781             (_("The autovideosink element is missing.")), (NULL));
1782       }
1783     } else {
1784       if (playsink->video_sink) {
1785         GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1786             (_("Configured videosink %s is not working."),
1787                 GST_ELEMENT_NAME (playsink->video_sink)), (NULL));
1788       } else if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1789         GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1790             (_("Both autovideosink and %s elements are not working."),
1791                 DEFAULT_VIDEOSINK), (NULL));
1792       } else {
1793         GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1794             (_("The autovideosink element is not working.")), (NULL));
1795       }
1796     }
1797     free_chain ((GstPlayChain *) chain);
1798     return NULL;
1799   }
1800
1801 link_failed:
1802   {
1803     GST_ELEMENT_ERROR (playsink, CORE, PAD,
1804         (NULL), ("Failed to configure the video sink."));
1805     /* checking sink made it READY */
1806     gst_element_set_state (chain->sink, GST_STATE_NULL);
1807     /* Remove chain from the bin to allow reuse later */
1808     gst_bin_remove (bin, chain->sink);
1809     free_chain ((GstPlayChain *) chain);
1810     return NULL;
1811   }
1812 }
1813
1814 static gboolean
1815 setup_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1816 {
1817   GstElement *elem;
1818   GstPlayVideoChain *chain;
1819   GstStateChangeReturn ret;
1820
1821   chain = playsink->videochain;
1822
1823   chain->chain.raw = raw;
1824
1825   /* if the chain was active we don't do anything */
1826   if (GST_PLAY_CHAIN (chain)->activated == TRUE)
1827     return TRUE;
1828
1829   /* try to set the sink element to READY again */
1830   ret = gst_element_set_state (chain->sink, GST_STATE_READY);
1831   if (ret == GST_STATE_CHANGE_FAILURE)
1832     return FALSE;
1833
1834   /* Get the VideoOverlay element */
1835   {
1836     GstVideoOverlay *overlay = NULL;
1837
1838     GST_OBJECT_LOCK (playsink);
1839     if (playsink->overlay_element)
1840       gst_object_unref (playsink->overlay_element);
1841     playsink->overlay_element =
1842         GST_VIDEO_OVERLAY (gst_bin_get_by_interface (GST_BIN (chain->chain.bin),
1843             GST_TYPE_VIDEO_OVERLAY));
1844     if (playsink->overlay_element)
1845       overlay = GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
1846     GST_OBJECT_UNLOCK (playsink);
1847
1848     if (overlay) {
1849       if (playsink->overlay_handle_set)
1850         gst_video_overlay_set_window_handle (overlay, playsink->overlay_handle);
1851       if (playsink->overlay_handle_events_set)
1852         gst_video_overlay_handle_events (overlay,
1853             playsink->overlay_handle_events);
1854       if (playsink->overlay_render_rectangle_set)
1855         gst_video_overlay_set_render_rectangle (overlay,
1856             playsink->overlay_x, playsink->overlay_y,
1857             playsink->overlay_width, playsink->overlay_height);
1858       gst_object_unref (overlay);
1859     }
1860   }
1861
1862   /* find ts-offset element */
1863   gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1864       gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1865           G_TYPE_INT64));
1866
1867   /* if we can disable async behaviour of the sink, we can avoid adding a
1868    * queue for the audio chain. */
1869   elem =
1870       gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1871       G_TYPE_BOOLEAN);
1872   if (elem) {
1873     GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1874         async, GST_ELEMENT_NAME (elem));
1875     g_object_set (elem, "async", async, NULL);
1876     chain->async = async;
1877   } else {
1878     GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1879     chain->async = TRUE;
1880   }
1881
1882   /* Make sure the aspect ratio is kept */
1883   elem =
1884       gst_play_sink_find_property_sinks (playsink, chain->sink,
1885       "force-aspect-ratio", G_TYPE_BOOLEAN);
1886   if (elem)
1887     g_object_set (elem, "force-aspect-ratio", playsink->force_aspect_ratio,
1888         NULL);
1889
1890   GST_OBJECT_LOCK (playsink);
1891   if (playsink->colorbalance_element) {
1892     g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
1893         G_CALLBACK (colorbalance_value_changed_cb), playsink);
1894     gst_object_unref (playsink->colorbalance_element);
1895   }
1896   playsink->colorbalance_element = find_color_balance_element (chain->sink);
1897   if (playsink->colorbalance_element) {
1898     g_signal_connect (playsink->colorbalance_element, "value-changed",
1899         G_CALLBACK (colorbalance_value_changed_cb), playsink);
1900   }
1901   GST_OBJECT_UNLOCK (playsink);
1902
1903   if (chain->conv) {
1904     gboolean use_balance = !playsink->colorbalance_element
1905         && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE);
1906
1907     g_object_set (chain->conv, "use-balance", use_balance, NULL);
1908
1909     GST_OBJECT_LOCK (playsink);
1910     if (use_balance && GST_PLAY_SINK_VIDEO_CONVERT (chain->conv)->balance)
1911       playsink->colorbalance_element =
1912           GST_COLOR_BALANCE (gst_object_ref (GST_PLAY_SINK_VIDEO_CONVERT
1913               (chain->conv)->balance));
1914     GST_OBJECT_UNLOCK (playsink);
1915   }
1916
1917   update_colorbalance (playsink);
1918
1919   return TRUE;
1920 }
1921
1922 static void
1923 _generate_update_newsegment_event (GstPad * pad, GstSegment * segment,
1924     GstEvent ** event1)
1925 {
1926   GstEvent *event;
1927   GstStructure *structure;
1928   event = gst_event_new_segment (segment);
1929   structure = gst_event_writable_structure (event);
1930   gst_structure_id_set (structure,
1931       _playsink_reset_segment_event_marker_id, G_TYPE_BOOLEAN, TRUE, NULL);
1932   *event1 = event;
1933 }
1934
1935 static gboolean
1936 gst_play_sink_sink_event (GstPad * pad, GstObject * parent, GstEvent * event,
1937     const gchar * sink_type,
1938     gboolean * sink_ignore_wrong_state,
1939     gboolean * sink_custom_flush_finished,
1940     gboolean * sink_pending_flush, GstSegment * sink_segment)
1941 {
1942   GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_object_get_parent (parent));
1943   gboolean ret;
1944   const GstStructure *structure = gst_event_get_structure (event);
1945
1946   if (GST_EVENT_TYPE (event) == GST_EVENT_CUSTOM_DOWNSTREAM_OOB && structure) {
1947     gchar *custom_flush;
1948     gchar *custom_flush_finish;
1949
1950     custom_flush = g_strdup_printf ("playsink-custom-%s-flush", sink_type);
1951     custom_flush_finish =
1952         g_strdup_printf ("playsink-custom-%s-flush-finish", sink_type);
1953     if (strcmp (gst_structure_get_name (structure), custom_flush) == 0) {
1954       GST_DEBUG_OBJECT (pad,
1955           "Custom %s flush event received, marking to flush %s", sink_type,
1956           sink_type);
1957       GST_PLAY_SINK_LOCK (playsink);
1958       *sink_ignore_wrong_state = TRUE;
1959       *sink_custom_flush_finished = FALSE;
1960       GST_PLAY_SINK_UNLOCK (playsink);
1961     } else if (strcmp (gst_structure_get_name (structure),
1962             custom_flush_finish) == 0) {
1963       GST_DEBUG_OBJECT (pad, "Custom %s flush finish event received",
1964           sink_type);
1965       GST_PLAY_SINK_LOCK (playsink);
1966       *sink_pending_flush = TRUE;
1967       *sink_custom_flush_finished = TRUE;
1968       GST_PLAY_SINK_UNLOCK (playsink);
1969     }
1970
1971     g_free (custom_flush);
1972     g_free (custom_flush_finish);
1973   } else if (GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_STOP) {
1974     GST_PLAY_SINK_LOCK (playsink);
1975     GST_DEBUG_OBJECT (pad, "Resetting %s segment because of flush-stop event",
1976         sink_type);
1977     gst_segment_init (sink_segment, GST_FORMAT_UNDEFINED);
1978     GST_PLAY_SINK_UNLOCK (playsink);
1979   }
1980
1981   GST_DEBUG_OBJECT (pad, "Forwarding event %" GST_PTR_FORMAT, event);
1982   ret = gst_pad_event_default (pad, parent, gst_event_ref (event));
1983
1984   if (GST_EVENT_TYPE (event) == GST_EVENT_SEGMENT) {
1985     const GstSegment *segment;
1986
1987     gst_event_parse_segment (event, &segment);
1988     GST_DEBUG_OBJECT (pad, "Segment event: %" GST_SEGMENT_FORMAT, segment);
1989
1990     GST_PLAY_SINK_LOCK (playsink);
1991     if (sink_segment->format != segment->format) {
1992       GST_DEBUG_OBJECT (pad, "%s segment format changed: %s -> %s",
1993           sink_type,
1994           gst_format_get_name (sink_segment->format),
1995           gst_format_get_name (segment->format));
1996       gst_segment_init (sink_segment, segment->format);
1997     }
1998
1999     GST_DEBUG_OBJECT (pad, "Old %s segment: %" GST_SEGMENT_FORMAT,
2000         sink_type, sink_segment);
2001     gst_segment_copy_into (&playsink->text_segment, sink_segment);
2002     GST_DEBUG_OBJECT (pad, "New %s segment: %" GST_SEGMENT_FORMAT,
2003         sink_type, sink_segment);
2004     GST_PLAY_SINK_UNLOCK (playsink);
2005   }
2006
2007   gst_event_unref (event);
2008   gst_object_unref (playsink);
2009   return ret;
2010 }
2011
2012 static GstFlowReturn
2013 gst_play_sink_sink_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer,
2014     const gchar * sink_type,
2015     gboolean * sink_ignore_wrong_state,
2016     gboolean * sink_custom_flush_finished,
2017     gboolean * sink_pending_flush, GstSegment * sink_segment)
2018 {
2019   GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2020   GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2021   GstFlowReturn ret;
2022
2023   GST_PLAY_SINK_LOCK (playsink);
2024
2025   if (*sink_pending_flush) {
2026     GstEvent *event;
2027     GstStructure *structure;
2028
2029     *sink_pending_flush = FALSE;
2030
2031     GST_PLAY_SINK_UNLOCK (playsink);
2032
2033     /* make the bin drop all cached data.
2034      * This event will be dropped on the src pad, if any. */
2035     event = gst_event_new_flush_start ();
2036     structure = gst_event_writable_structure (event);
2037     gst_structure_id_set (structure,
2038         _playsink_reset_segment_event_marker_id, G_TYPE_BOOLEAN, TRUE, NULL);
2039
2040     GST_DEBUG_OBJECT (pad,
2041         "Pushing %s flush-start event with reset segment marker set: %"
2042         GST_PTR_FORMAT, sink_type, event);
2043     gst_pad_send_event (pad, event);
2044
2045     /* make queue drop all cached data.
2046      * This event will be dropped on the src pad. */
2047     event = gst_event_new_flush_stop (TRUE);
2048     structure = gst_event_writable_structure (event);
2049     gst_structure_id_set (structure,
2050         _playsink_reset_segment_event_marker_id, G_TYPE_BOOLEAN, TRUE, NULL);
2051
2052     GST_DEBUG_OBJECT (pad,
2053         "Pushing %s flush-stop event with reset segment marker set: %"
2054         GST_PTR_FORMAT, sink_type, event);
2055     gst_pad_send_event (pad, event);
2056
2057     /* Re-sync queue segment info after flush-stop.
2058      * This event will be dropped on the src pad. */
2059     if (sink_segment->format != GST_FORMAT_UNDEFINED) {
2060       GstEvent *event1;
2061
2062       _generate_update_newsegment_event (pad, sink_segment, &event1);
2063       GST_DEBUG_OBJECT (playsink,
2064           "Pushing segment event with reset "
2065           "segment marker set: %" GST_PTR_FORMAT, event1);
2066       gst_pad_send_event (pad, event1);
2067     }
2068   } else {
2069     GST_PLAY_SINK_UNLOCK (playsink);
2070   }
2071
2072   ret = gst_proxy_pad_chain_default (pad, parent, buffer);
2073
2074   GST_PLAY_SINK_LOCK (playsink);
2075   if (ret == GST_FLOW_FLUSHING && *sink_ignore_wrong_state) {
2076     GST_DEBUG_OBJECT (pad, "Ignoring wrong state for %s during flush",
2077         sink_type);
2078     if (*sink_custom_flush_finished) {
2079       GST_DEBUG_OBJECT (pad, "Custom flush finished, stop ignoring "
2080           "wrong state for %s", sink_type);
2081       *sink_ignore_wrong_state = FALSE;
2082     }
2083
2084     ret = GST_FLOW_OK;
2085   }
2086   GST_PLAY_SINK_UNLOCK (playsink);
2087
2088   gst_object_unref (playsink);
2089   gst_object_unref (tbin);
2090   return ret;
2091 }
2092
2093 /* sending audio/video flushes break stream changes when the pipeline
2094  * is paused and played again in 0.10 */
2095 #if 0
2096 static gboolean
2097 gst_play_sink_video_sink_event (GstPad * pad, GstEvent * event)
2098 {
2099   GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2100   GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2101   gboolean ret;
2102
2103   ret = gst_play_sink_sink_event (pad, event, "video",
2104       &playsink->video_ignore_wrong_state,
2105       &playsink->video_custom_flush_finished,
2106       &playsink->video_pending_flush, &playsink->video_segment);
2107
2108   gst_object_unref (playsink);
2109   gst_object_unref (tbin);
2110   return ret;
2111 }
2112
2113 static GstFlowReturn
2114 gst_play_sink_video_sink_chain (GstPad * pad, GstBuffer * buffer)
2115 {
2116   GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2117   GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2118   gboolean ret;
2119
2120   ret = gst_play_sink_sink_chain (pad, buffer, "video",
2121       &playsink->video_ignore_wrong_state,
2122       &playsink->video_custom_flush_finished,
2123       &playsink->video_pending_flush, &playsink->video_segment);
2124
2125   gst_object_unref (playsink);
2126   gst_object_unref (tbin);
2127   return ret;
2128 }
2129
2130 static gboolean
2131 gst_play_sink_audio_sink_event (GstPad * pad, GstEvent * event)
2132 {
2133   GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2134   GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2135   gboolean ret;
2136
2137   ret = gst_play_sink_sink_event (pad, event, "audio",
2138       &playsink->audio_ignore_wrong_state,
2139       &playsink->audio_custom_flush_finished,
2140       &playsink->audio_pending_flush, &playsink->audio_segment);
2141
2142   gst_object_unref (playsink);
2143   gst_object_unref (tbin);
2144   return ret;
2145 }
2146
2147 static GstFlowReturn
2148 gst_play_sink_audio_sink_chain (GstPad * pad, GstBuffer * buffer)
2149 {
2150   GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2151   GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2152   gboolean ret;
2153
2154   ret = gst_play_sink_sink_chain (pad, buffer, "audio",
2155       &playsink->audio_ignore_wrong_state,
2156       &playsink->audio_custom_flush_finished,
2157       &playsink->audio_pending_flush, &playsink->audio_segment);
2158
2159   gst_object_unref (playsink);
2160   gst_object_unref (tbin);
2161   return ret;
2162 }
2163 #endif
2164
2165 static gboolean
2166 gst_play_sink_text_sink_event (GstPad * pad, GstObject * parent,
2167     GstEvent * event)
2168 {
2169   GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_object_get_parent (parent));
2170   gboolean ret;
2171
2172   ret = gst_play_sink_sink_event (pad, parent, event, "subtitle",
2173       &playsink->text_ignore_wrong_state,
2174       &playsink->text_custom_flush_finished,
2175       &playsink->text_pending_flush, &playsink->text_segment);
2176
2177   gst_object_unref (playsink);
2178
2179   return ret;
2180 }
2181
2182 static GstFlowReturn
2183 gst_play_sink_text_sink_chain (GstPad * pad, GstObject * parent,
2184     GstBuffer * buffer)
2185 {
2186   gboolean ret;
2187   GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_object_get_parent (parent));
2188
2189   ret = gst_play_sink_sink_chain (pad, parent, buffer, "subtitle",
2190       &playsink->text_ignore_wrong_state,
2191       &playsink->text_custom_flush_finished,
2192       &playsink->text_pending_flush, &playsink->text_segment);
2193
2194   gst_object_unref (playsink);
2195   return ret;
2196 }
2197
2198 static gboolean
2199 gst_play_sink_text_src_event (GstPad * pad, GstObject * parent,
2200     GstEvent * event)
2201 {
2202   gboolean ret;
2203   const GstStructure *structure;
2204
2205   GST_DEBUG_OBJECT (pad, "Got event %" GST_PTR_FORMAT, event);
2206
2207   structure = gst_event_get_structure (event);
2208
2209   if (structure &&
2210       gst_structure_id_has_field (structure,
2211           _playsink_reset_segment_event_marker_id)) {
2212     /* the events marked with a reset segment marker
2213      * are sent internally to reset the queue and
2214      * must be dropped here */
2215     GST_DEBUG_OBJECT (pad, "Dropping event with reset "
2216         "segment marker set: %" GST_PTR_FORMAT, event);
2217     ret = TRUE;
2218     goto out;
2219   }
2220
2221   ret = gst_pad_event_default (pad, parent, gst_event_ref (event));
2222
2223 out:
2224   gst_event_unref (event);
2225   return ret;
2226 }
2227
2228 /* make an element for playback of video with subtitles embedded.
2229  * Only used for *raw* video streams.
2230  *
2231  *  +--------------------------------------------+
2232  *  | tbin                                       |
2233  *  |     +--------+      +-----------------+    |
2234  *  |     | queue  |      | subtitleoverlay |    |
2235  * video--src     sink---video_sink         |    |
2236  *  |     +--------+     |                src--src
2237  * text------------------text_sink          |    |
2238  *  |                     +-----------------+    |
2239  *  +--------------------------------------------+
2240  *
2241  */
2242 static GstPlayTextChain *
2243 gen_text_chain (GstPlaySink * playsink)
2244 {
2245   GstPlayTextChain *chain;
2246   GstBin *bin;
2247   GstElement *elem;
2248   GstPad *videosinkpad, *textsinkpad, *srcpad;
2249
2250   chain = g_new0 (GstPlayTextChain, 1);
2251   chain->chain.playsink = playsink;
2252
2253   GST_DEBUG_OBJECT (playsink, "making text chain %p", chain);
2254
2255   chain->chain.bin = gst_bin_new ("tbin");
2256   bin = GST_BIN_CAST (chain->chain.bin);
2257   gst_object_ref_sink (bin);
2258
2259   videosinkpad = textsinkpad = srcpad = NULL;
2260
2261   /* first try to hook the text pad to the custom sink */
2262   if (playsink->text_sink) {
2263     GST_DEBUG_OBJECT (playsink, "trying configured textsink");
2264     chain->sink = try_element (playsink, playsink->text_sink, FALSE);
2265     if (chain->sink) {
2266       elem =
2267           gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
2268           G_TYPE_BOOLEAN);
2269       if (elem) {
2270         /* make sure the sparse subtitles don't participate in the preroll */
2271         g_object_set (elem, "async", FALSE, NULL);
2272         GST_DEBUG_OBJECT (playsink, "adding custom text sink");
2273         gst_bin_add (bin, chain->sink);
2274         /* NOTE streamsynchronizer needs streams decoupled */
2275         /* make a little queue */
2276         chain->queue = gst_element_factory_make ("queue", "subqueue");
2277         if (chain->queue == NULL) {
2278           post_missing_element_message (playsink, "queue");
2279           GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2280               (_("Missing element '%s' - check your GStreamer installation."),
2281                   "queue"), ("rendering might be suboptimal"));
2282         } else {
2283           g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
2284               "max-size-bytes", 0, "max-size-time", (gint64) GST_SECOND,
2285               "silent", TRUE, NULL);
2286           gst_bin_add (bin, chain->queue);
2287         }
2288         /* we have a custom sink, this will be our textsinkpad */
2289         if (gst_element_link_pads_full (chain->queue, "src", chain->sink,
2290                 "sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
2291           /* we're all fine now and we can add the sink to the chain */
2292           GST_DEBUG_OBJECT (playsink, "using custom text sink");
2293           textsinkpad = gst_element_get_static_pad (chain->queue, "sink");
2294         } else {
2295           GST_WARNING_OBJECT (playsink,
2296               "can't find a sink pad on custom text sink");
2297           gst_bin_remove (bin, chain->sink);
2298           gst_bin_remove (bin, chain->queue);
2299           chain->sink = NULL;
2300           chain->queue = NULL;
2301         }
2302         /* try to set sync to true but it's no biggie when we can't */
2303         if (chain->sink && (elem =
2304                 gst_play_sink_find_property_sinks (playsink, chain->sink,
2305                     "sync", G_TYPE_BOOLEAN)))
2306           g_object_set (elem, "sync", TRUE, NULL);
2307
2308         if (!textsinkpad)
2309           gst_bin_remove (bin, chain->sink);
2310       } else {
2311         GST_WARNING_OBJECT (playsink,
2312             "can't find async property in custom text sink");
2313       }
2314     }
2315     if (textsinkpad == NULL) {
2316       GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2317           (_("Custom text sink element is not usable.")),
2318           ("fallback to default subtitleoverlay"));
2319     }
2320   }
2321
2322   if (textsinkpad == NULL) {
2323     if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
2324       /* make a little queue */
2325       chain->queue = gst_element_factory_make ("queue", "vqueue");
2326       if (chain->queue == NULL) {
2327         post_missing_element_message (playsink, "queue");
2328         GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2329             (_("Missing element '%s' - check your GStreamer installation."),
2330                 "queue"), ("video rendering might be suboptimal"));
2331       } else {
2332         g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
2333             "max-size-bytes", 0, "max-size-time", (gint64) 0,
2334             "silent", TRUE, NULL);
2335         gst_bin_add (bin, chain->queue);
2336         videosinkpad = gst_element_get_static_pad (chain->queue, "sink");
2337       }
2338
2339       chain->overlay =
2340           gst_element_factory_make ("subtitleoverlay", "suboverlay");
2341       if (chain->overlay == NULL) {
2342         post_missing_element_message (playsink, "subtitleoverlay");
2343         GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2344             (_("Missing element '%s' - check your GStreamer installation."),
2345                 "subtitleoverlay"), ("subtitle rendering disabled"));
2346       } else {
2347         GstElement *element;
2348
2349         gst_bin_add (bin, chain->overlay);
2350
2351         g_object_set (G_OBJECT (chain->overlay), "silent", FALSE, NULL);
2352         if (playsink->font_desc) {
2353           g_object_set (G_OBJECT (chain->overlay), "font-desc",
2354               playsink->font_desc, NULL);
2355         }
2356         if (playsink->subtitle_encoding) {
2357           g_object_set (G_OBJECT (chain->overlay), "subtitle-encoding",
2358               playsink->subtitle_encoding, NULL);
2359         }
2360
2361         gst_element_link_pads_full (chain->queue, "src", chain->overlay,
2362             "video_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS);
2363
2364         /* make another little queue to decouple streams */
2365         element = gst_element_factory_make ("queue", "subqueue");
2366         if (element == NULL) {
2367           post_missing_element_message (playsink, "queue");
2368           GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2369               (_("Missing element '%s' - check your GStreamer installation."),
2370                   "queue"), ("rendering might be suboptimal"));
2371         } else {
2372           g_object_set (G_OBJECT (element), "max-size-buffers", 3,
2373               "max-size-bytes", 0, "max-size-time", (gint64) GST_SECOND,
2374               "silent", TRUE, NULL);
2375           gst_bin_add (bin, element);
2376           if (gst_element_link_pads_full (element, "src", chain->overlay,
2377                   "subtitle_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
2378             textsinkpad = gst_element_get_static_pad (element, "sink");
2379             srcpad = gst_element_get_static_pad (chain->overlay, "src");
2380           } else {
2381             gst_bin_remove (bin, chain->sink);
2382             gst_bin_remove (bin, chain->overlay);
2383             chain->sink = NULL;
2384             chain->overlay = NULL;
2385             gst_object_unref (videosinkpad);
2386             videosinkpad = NULL;
2387           }
2388         }
2389       }
2390     }
2391   }
2392
2393   if (videosinkpad == NULL) {
2394     /* if we still don't have a videosink, we don't have an overlay. the only
2395      * thing we can do is insert an identity and ghost the src
2396      * and sink pads. */
2397     chain->identity = gst_element_factory_make ("identity", "tidentity");
2398     if (chain->identity == NULL) {
2399       post_missing_element_message (playsink, "identity");
2400       GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2401           (_("Missing element '%s' - check your GStreamer installation."),
2402               "identity"), (NULL));
2403     } else {
2404       g_object_set (chain->identity, "signal-handoffs", FALSE, NULL);
2405       g_object_set (chain->identity, "silent", TRUE, NULL);
2406       gst_bin_add (bin, chain->identity);
2407       srcpad = gst_element_get_static_pad (chain->identity, "src");
2408       videosinkpad = gst_element_get_static_pad (chain->identity, "sink");
2409     }
2410   }
2411
2412   /* expose the ghostpads */
2413   if (videosinkpad) {
2414     chain->videosinkpad = gst_ghost_pad_new ("sink", videosinkpad);
2415     gst_object_unref (videosinkpad);
2416     gst_element_add_pad (chain->chain.bin, chain->videosinkpad);
2417   }
2418   if (textsinkpad) {
2419     chain->textsinkpad = gst_ghost_pad_new ("text_sink", textsinkpad);
2420     gst_object_unref (textsinkpad);
2421
2422     gst_pad_set_event_function (chain->textsinkpad,
2423         GST_DEBUG_FUNCPTR (gst_play_sink_text_sink_event));
2424     gst_pad_set_chain_function (chain->textsinkpad,
2425         GST_DEBUG_FUNCPTR (gst_play_sink_text_sink_chain));
2426
2427     gst_element_add_pad (chain->chain.bin, chain->textsinkpad);
2428   }
2429   if (srcpad) {
2430     chain->srcpad = gst_ghost_pad_new ("src", srcpad);
2431     gst_object_unref (srcpad);
2432
2433     gst_pad_set_event_function (chain->srcpad,
2434         GST_DEBUG_FUNCPTR (gst_play_sink_text_src_event));
2435
2436     gst_element_add_pad (chain->chain.bin, chain->srcpad);
2437   }
2438
2439   return chain;
2440 }
2441
2442 static void
2443 notify_volume_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
2444 {
2445   gdouble vol;
2446
2447   g_object_get (object, "volume", &vol, NULL);
2448   playsink->volume = vol;
2449
2450   g_object_notify (G_OBJECT (playsink), "volume");
2451 }
2452
2453 static void
2454 notify_mute_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
2455 {
2456   gboolean mute;
2457
2458   g_object_get (object, "mute", &mute, NULL);
2459   playsink->mute = mute;
2460
2461   g_object_notify (G_OBJECT (playsink), "mute");
2462 }
2463
2464 /* make the chain that contains the elements needed to perform
2465  * audio playback.
2466  *
2467  * We add a tee as the first element so that we can link the visualisation chain
2468  * to it when requested.
2469  *
2470  *  +-------------------------------------------------------------+
2471  *  | abin                                                        |
2472  *  |      +---------+   +----------+   +---------+   +---------+ |
2473  *  |      |audioconv|   |audioscale|   | volume  |   |audiosink| |
2474  *  |   +-srck      src-sink       src-sink      src-sink       | |
2475  *  |   |  +---------+   +----------+   +---------+   +---------+ |
2476  * sink-+                                                         |
2477  *  +-------------------------------------------------------------+
2478  */
2479 static GstPlayAudioChain *
2480 gen_audio_chain (GstPlaySink * playsink, gboolean raw)
2481 {
2482   GstPlayAudioChain *chain;
2483   GstBin *bin;
2484   gboolean have_volume;
2485   GstPad *pad;
2486   GstElement *head, *prev, *elem = NULL;
2487
2488   chain = g_new0 (GstPlayAudioChain, 1);
2489   chain->chain.playsink = playsink;
2490   chain->chain.raw = raw;
2491
2492   GST_DEBUG_OBJECT (playsink, "making audio chain %p", chain);
2493
2494   if (playsink->audio_sink) {
2495     GST_DEBUG_OBJECT (playsink, "trying configured audiosink %" GST_PTR_FORMAT,
2496         playsink->audio_sink);
2497     chain->sink = try_element (playsink, playsink->audio_sink, FALSE);
2498   } else {
2499     /* only try fallback if no specific sink was chosen */
2500     if (chain->sink == NULL) {
2501       GST_DEBUG_OBJECT (playsink, "trying autoaudiosink");
2502       elem = gst_element_factory_make ("autoaudiosink", "audiosink");
2503       chain->sink = try_element (playsink, elem, TRUE);
2504     }
2505     if (chain->sink == NULL) {
2506       /* if default sink from config.h is different then try it too */
2507       if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
2508         GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_AUDIOSINK);
2509         elem = gst_element_factory_make (DEFAULT_AUDIOSINK, "audiosink");
2510         chain->sink = try_element (playsink, elem, TRUE);
2511       }
2512     }
2513     if (chain->sink)
2514       playsink->audio_sink = gst_object_ref (chain->sink);
2515   }
2516   if (chain->sink == NULL)
2517     goto no_sinks;
2518
2519   chain->chain.bin = gst_bin_new ("abin");
2520   bin = GST_BIN_CAST (chain->chain.bin);
2521   gst_object_ref_sink (bin);
2522   gst_bin_add (bin, chain->sink);
2523
2524   /* we have to add a queue when we need to decouple for the video sink in
2525    * visualisations and for streamsynchronizer */
2526   GST_DEBUG_OBJECT (playsink, "adding audio queue");
2527   chain->queue = gst_element_factory_make ("queue", "aqueue");
2528   if (chain->queue == NULL) {
2529     post_missing_element_message (playsink, "queue");
2530     GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2531         (_("Missing element '%s' - check your GStreamer installation."),
2532             "queue"), ("audio playback and visualizations might not work"));
2533     head = chain->sink;
2534     prev = NULL;
2535   } else {
2536     g_object_set (chain->queue, "silent", TRUE, NULL);
2537     gst_bin_add (bin, chain->queue);
2538     prev = head = chain->queue;
2539   }
2540
2541   /* find ts-offset element */
2542   gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
2543       gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
2544           G_TYPE_INT64));
2545
2546   /* check if the sink, or something within the sink, has the volume property.
2547    * If it does we don't need to add a volume element.  */
2548   elem =
2549       gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
2550       G_TYPE_DOUBLE);
2551   if (elem) {
2552     chain->volume = elem;
2553
2554     g_signal_connect (chain->volume, "notify::volume",
2555         G_CALLBACK (notify_volume_cb), playsink);
2556
2557     GST_DEBUG_OBJECT (playsink, "the sink has a volume property");
2558     have_volume = TRUE;
2559     chain->sink_volume = TRUE;
2560     /* if the sink also has a mute property we can use this as well. We'll only
2561      * use the mute property if there is a volume property. We can simulate the
2562      * mute with the volume otherwise. */
2563     chain->mute =
2564         gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
2565         G_TYPE_BOOLEAN);
2566     if (chain->mute) {
2567       GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
2568       g_signal_connect (chain->mute, "notify::mute",
2569           G_CALLBACK (notify_mute_cb), playsink);
2570     }
2571     /* use the sink to control the volume and mute */
2572     if (playsink->volume_changed) {
2573       g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2574       playsink->volume_changed = FALSE;
2575     }
2576     if (playsink->mute_changed) {
2577       if (chain->mute) {
2578         g_object_set (chain->mute, "mute", playsink->mute, NULL);
2579       } else {
2580         if (playsink->mute)
2581           g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
2582       }
2583       playsink->mute_changed = FALSE;
2584     }
2585   } else {
2586     /* no volume, we need to add a volume element when we can */
2587     GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
2588     have_volume = FALSE;
2589     chain->sink_volume = FALSE;
2590   }
2591
2592   if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO) || (!have_volume
2593           && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME))) {
2594     gboolean use_converters = !(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO);
2595     gboolean use_volume =
2596         !have_volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME);
2597     GST_DEBUG_OBJECT (playsink,
2598         "creating audioconvert with use-converters %d, use-volume %d",
2599         use_converters, use_volume);
2600     chain->conv =
2601         g_object_new (GST_TYPE_PLAY_SINK_AUDIO_CONVERT, "name", "aconv",
2602         "use-converters", use_converters, "use-volume", use_volume, NULL);
2603     gst_bin_add (bin, chain->conv);
2604     if (prev) {
2605       if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
2606               GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
2607         goto link_failed;
2608     } else {
2609       head = chain->conv;
2610     }
2611     prev = chain->conv;
2612
2613     if (!have_volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
2614       GstPlaySinkAudioConvert *conv =
2615           GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
2616
2617       if (conv->volume) {
2618         chain->volume = conv->volume;
2619         have_volume = TRUE;
2620
2621         g_signal_connect (chain->volume, "notify::volume",
2622             G_CALLBACK (notify_volume_cb), playsink);
2623
2624         /* volume also has the mute property */
2625         chain->mute = chain->volume;
2626         g_signal_connect (chain->mute, "notify::mute",
2627             G_CALLBACK (notify_mute_cb), playsink);
2628
2629         /* configure with the latest volume and mute */
2630         g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume,
2631             NULL);
2632         g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
2633       }
2634     }
2635   }
2636
2637   if (prev) {
2638     /* we only have to link to the previous element if we have something in
2639      * front of the sink */
2640     GST_DEBUG_OBJECT (playsink, "linking to sink");
2641     if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
2642             GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
2643       goto link_failed;
2644   }
2645
2646   /* post a warning if we have no way to configure the volume */
2647   if (!have_volume) {
2648     GST_ELEMENT_WARNING (playsink, STREAM, NOT_IMPLEMENTED,
2649         (_("No volume control found")), ("Volume/mute is not available"));
2650   }
2651
2652   /* and ghost the sinkpad of the headmost element */
2653   GST_DEBUG_OBJECT (playsink, "ghosting sink pad");
2654   pad = gst_element_get_static_pad (head, "sink");
2655   chain->sinkpad = gst_ghost_pad_new ("sink", pad);
2656
2657   /* sending audio/video flushes break stream changes when the pipeline
2658    * is paused and played again in 0.10 */
2659 #if 0
2660   gst_pad_set_event_function (chain->sinkpad,
2661       GST_DEBUG_FUNCPTR (gst_play_sink_audio_sink_event));
2662   gst_pad_set_chain_function (chain->sinkpad,
2663       GST_DEBUG_FUNCPTR (gst_play_sink_audio_sink_chain));
2664 #endif
2665
2666   gst_object_unref (pad);
2667   gst_element_add_pad (chain->chain.bin, chain->sinkpad);
2668
2669   return chain;
2670
2671   /* ERRORS */
2672 no_sinks:
2673   {
2674     if (!elem && !playsink->audio_sink) {
2675       post_missing_element_message (playsink, "autoaudiosink");
2676       if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
2677         post_missing_element_message (playsink, DEFAULT_AUDIOSINK);
2678         GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2679             (_("Both autoaudiosink and %s elements are missing."),
2680                 DEFAULT_AUDIOSINK), (NULL));
2681       } else {
2682         GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2683             (_("The autoaudiosink element is missing.")), (NULL));
2684       }
2685     } else {
2686       if (playsink->audio_sink) {
2687         GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2688             (_("Configured audiosink %s is not working."),
2689                 GST_ELEMENT_NAME (playsink->audio_sink)), (NULL));
2690       } else if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
2691         GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2692             (_("Both autoaudiosink and %s elements are not working."),
2693                 DEFAULT_AUDIOSINK), (NULL));
2694       } else {
2695         GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2696             (_("The autoaudiosink element is not working.")), (NULL));
2697       }
2698     }
2699     free_chain ((GstPlayChain *) chain);
2700     return NULL;
2701   }
2702 link_failed:
2703   {
2704     GST_ELEMENT_ERROR (playsink, CORE, PAD,
2705         (NULL), ("Failed to configure the audio sink."));
2706     /* checking sink made it READY */
2707     gst_element_set_state (chain->sink, GST_STATE_NULL);
2708     /* Remove chain from the bin to allow reuse later */
2709     gst_bin_remove (bin, chain->sink);
2710     free_chain ((GstPlayChain *) chain);
2711     return NULL;
2712   }
2713 }
2714
2715 static gboolean
2716 setup_audio_chain (GstPlaySink * playsink, gboolean raw)
2717 {
2718   GstElement *elem;
2719   GstPlayAudioChain *chain;
2720   GstStateChangeReturn ret;
2721   GstPlaySinkAudioConvert *conv;
2722
2723   chain = playsink->audiochain;
2724   conv = GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
2725
2726   chain->chain.raw = raw;
2727
2728   /* if the chain was active we don't do anything */
2729   if (GST_PLAY_CHAIN (chain)->activated == TRUE)
2730     return TRUE;
2731
2732   /* try to set the sink element to READY again */
2733   ret = gst_element_set_state (chain->sink, GST_STATE_READY);
2734   if (ret == GST_STATE_CHANGE_FAILURE)
2735     return FALSE;
2736
2737   /* find ts-offset element */
2738   gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
2739       gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
2740           G_TYPE_INT64));
2741
2742   /* check if the sink, or something within the sink, has the volume property.
2743    * If it does we don't need to add a volume element.  */
2744   elem =
2745       gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
2746       G_TYPE_DOUBLE);
2747   if (elem) {
2748     chain->volume = elem;
2749
2750     if (playsink->volume_changed) {
2751       GST_DEBUG_OBJECT (playsink, "the sink has a volume property, setting %f",
2752           playsink->volume);
2753       /* use the sink to control the volume */
2754       g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2755       playsink->volume_changed = FALSE;
2756     }
2757
2758     g_signal_connect (chain->volume, "notify::volume",
2759         G_CALLBACK (notify_volume_cb), playsink);
2760     /* if the sink also has a mute property we can use this as well. We'll only
2761      * use the mute property if there is a volume property. We can simulate the
2762      * mute with the volume otherwise. */
2763     chain->mute =
2764         gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
2765         G_TYPE_BOOLEAN);
2766     if (chain->mute) {
2767       GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
2768       g_signal_connect (chain->mute, "notify::mute",
2769           G_CALLBACK (notify_mute_cb), playsink);
2770     }
2771
2772     g_object_set (chain->conv, "use-volume", FALSE, NULL);
2773   } else if (conv) {
2774     /* no volume, we need to add a volume element when we can */
2775     g_object_set (chain->conv, "use-volume",
2776         ! !(playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME), NULL);
2777     GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
2778
2779     /* Disconnect signals */
2780     disconnect_chain (chain, playsink);
2781
2782     if (conv->volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
2783       chain->volume = conv->volume;
2784       chain->mute = chain->volume;
2785
2786       g_signal_connect (chain->volume, "notify::volume",
2787           G_CALLBACK (notify_volume_cb), playsink);
2788
2789       g_signal_connect (chain->mute, "notify::mute",
2790           G_CALLBACK (notify_mute_cb), playsink);
2791
2792       /* configure with the latest volume and mute */
2793       g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2794       g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
2795     }
2796
2797     GST_DEBUG_OBJECT (playsink, "reusing existing volume element");
2798   }
2799   return TRUE;
2800 }
2801
2802 /*
2803  *  +-------------------------------------------------------------------+
2804  *  | visbin                                                            |
2805  *  |      +----------+   +------------+   +----------+   +-------+     |
2806  *  |      | visqueue |   | audioconv  |   | audiores |   |  vis  |     |
2807  *  |   +-sink       src-sink + samp  src-sink       src-sink    src-+  |
2808  *  |   |  +----------+   +------------+   +----------+   +-------+  |  |
2809  * sink-+                                                            +-src
2810  *  +-------------------------------------------------------------------+
2811  *
2812  */
2813 static GstPlayVisChain *
2814 gen_vis_chain (GstPlaySink * playsink)
2815 {
2816   GstPlayVisChain *chain;
2817   GstBin *bin;
2818   gboolean res;
2819   GstPad *pad;
2820   GstElement *elem;
2821
2822   chain = g_new0 (GstPlayVisChain, 1);
2823   chain->chain.playsink = playsink;
2824
2825   GST_DEBUG_OBJECT (playsink, "making vis chain %p", chain);
2826
2827   chain->chain.bin = gst_bin_new ("visbin");
2828   bin = GST_BIN_CAST (chain->chain.bin);
2829   gst_object_ref_sink (bin);
2830
2831   /* we're queuing raw audio here, we can remove this queue when we can disable
2832    * async behaviour in the video sink. */
2833   chain->queue = gst_element_factory_make ("queue", "visqueue");
2834   if (chain->queue == NULL)
2835     goto no_queue;
2836   g_object_set (chain->queue, "silent", TRUE, NULL);
2837   gst_bin_add (bin, chain->queue);
2838
2839   chain->conv = gst_element_factory_make ("audioconvert", "aconv");
2840   if (chain->conv == NULL)
2841     goto no_audioconvert;
2842   gst_bin_add (bin, chain->conv);
2843
2844   chain->resample = gst_element_factory_make ("audioresample", "aresample");
2845   if (chain->resample == NULL)
2846     goto no_audioresample;
2847   gst_bin_add (bin, chain->resample);
2848
2849   /* this pad will be used for blocking the dataflow and switching the vis
2850    * plugin, we block right after the queue, this makes it possible for the
2851    * resample and convert to convert to a format supported by the new vis
2852    * plugin */
2853   chain->blockpad = gst_element_get_static_pad (chain->queue, "src");
2854   /* this is the pad where the vis is linked to */
2855   chain->vispeerpad = gst_element_get_static_pad (chain->resample, "src");
2856
2857   if (playsink->visualisation) {
2858     GST_DEBUG_OBJECT (playsink, "trying configure vis");
2859     chain->vis = try_element (playsink, playsink->visualisation, FALSE);
2860   }
2861   if (chain->vis == NULL) {
2862     GST_DEBUG_OBJECT (playsink, "trying goom");
2863     elem = gst_element_factory_make ("goom", "vis");
2864     chain->vis = try_element (playsink, elem, TRUE);
2865   }
2866   if (chain->vis == NULL)
2867     goto no_goom;
2868
2869   gst_bin_add (bin, chain->vis);
2870
2871   res = gst_element_link_pads_full (chain->queue, "src", chain->conv, "sink",
2872       GST_PAD_LINK_CHECK_NOTHING);
2873   res &=
2874       gst_element_link_pads_full (chain->conv, "src", chain->resample, "sink",
2875       GST_PAD_LINK_CHECK_NOTHING);
2876   res &=
2877       gst_element_link_pads_full (chain->resample, "src", chain->vis, "sink",
2878       GST_PAD_LINK_CHECK_NOTHING);
2879   if (!res)
2880     goto link_failed;
2881
2882   chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
2883   chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
2884
2885   pad = gst_element_get_static_pad (chain->queue, "sink");
2886   chain->sinkpad = gst_ghost_pad_new ("sink", pad);
2887   gst_object_unref (pad);
2888   gst_element_add_pad (chain->chain.bin, chain->sinkpad);
2889
2890   chain->srcpad = gst_ghost_pad_new ("src", chain->vissrcpad);
2891   gst_element_add_pad (chain->chain.bin, chain->srcpad);
2892
2893   return chain;
2894
2895   /* ERRORS */
2896 no_queue:
2897   {
2898     post_missing_element_message (playsink, "queue");
2899     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2900         (_("Missing element '%s' - check your GStreamer installation."),
2901             "queue"), (NULL));
2902     free_chain ((GstPlayChain *) chain);
2903     return NULL;
2904   }
2905 no_audioconvert:
2906   {
2907     post_missing_element_message (playsink, "audioconvert");
2908     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2909         (_("Missing element '%s' - check your GStreamer installation."),
2910             "audioconvert"), ("make sure audioconvert isn't blacklisted"));
2911     free_chain ((GstPlayChain *) chain);
2912     return NULL;
2913   }
2914 no_audioresample:
2915   {
2916     post_missing_element_message (playsink, "audioresample");
2917     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2918         (_("Missing element '%s' - check your GStreamer installation."),
2919             "audioresample"), (NULL));
2920     free_chain ((GstPlayChain *) chain);
2921     return NULL;
2922   }
2923 no_goom:
2924   {
2925     post_missing_element_message (playsink, "goom");
2926     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2927         (_("Missing element '%s' - check your GStreamer installation."),
2928             "goom"), (NULL));
2929     free_chain ((GstPlayChain *) chain);
2930     return NULL;
2931   }
2932 link_failed:
2933   {
2934     GST_ELEMENT_ERROR (playsink, CORE, PAD,
2935         (NULL), ("Failed to configure the visualisation element."));
2936     /* element made it to READY */
2937     gst_element_set_state (chain->vis, GST_STATE_NULL);
2938     free_chain ((GstPlayChain *) chain);
2939     return NULL;
2940   }
2941 }
2942
2943 /* this function is called when all the request pads are requested and when we
2944  * have to construct the final pipeline. Based on the flags we construct the
2945  * final output pipelines.
2946  */
2947 static gboolean
2948 gst_play_sink_do_reconfigure (GstPlaySink * playsink)
2949 {
2950   GstPlayFlags flags;
2951   gboolean need_audio, need_video, need_deinterlace, need_vis, need_text;
2952
2953   GST_DEBUG_OBJECT (playsink, "reconfiguring");
2954
2955   /* assume we need nothing */
2956   need_audio = need_video = need_deinterlace = need_vis = need_text = FALSE;
2957
2958   GST_PLAY_SINK_LOCK (playsink);
2959   GST_OBJECT_LOCK (playsink);
2960   /* get flags, there are protected with the object lock */
2961   flags = playsink->flags;
2962   GST_OBJECT_UNLOCK (playsink);
2963
2964   /* figure out which components we need */
2965   if (flags & GST_PLAY_FLAG_TEXT && playsink->text_pad) {
2966     /* we have subtitles and we are requested to show it */
2967     need_text = TRUE;
2968   }
2969
2970   if (((flags & GST_PLAY_FLAG_VIDEO)
2971           || (flags & GST_PLAY_FLAG_NATIVE_VIDEO)) && playsink->video_pad) {
2972     /* we have video and we are requested to show it */
2973     need_video = TRUE;
2974
2975     /* we only deinterlace if native video is not requested and
2976      * we have raw video */
2977     if ((flags & GST_PLAY_FLAG_DEINTERLACE)
2978         && !(flags & GST_PLAY_FLAG_NATIVE_VIDEO) && playsink->video_pad_raw)
2979       need_deinterlace = TRUE;
2980   }
2981
2982   if (playsink->audio_pad) {
2983     if ((flags & GST_PLAY_FLAG_AUDIO) || (flags & GST_PLAY_FLAG_NATIVE_AUDIO)) {
2984       need_audio = TRUE;
2985     }
2986     if (playsink->audio_pad_raw) {
2987       /* only can do vis with raw uncompressed audio */
2988       if (flags & GST_PLAY_FLAG_VIS && !need_video) {
2989         /* also add video when we add visualisation */
2990         need_video = TRUE;
2991         need_vis = TRUE;
2992       }
2993     }
2994   }
2995
2996   /* we have a text_pad and we need text rendering, in this case we need a
2997    * video_pad to combine the video with the text or visualizations */
2998   if (need_text && !need_video && !playsink->text_sink) {
2999     if (playsink->video_pad) {
3000       need_video = TRUE;
3001     } else if (need_audio) {
3002       GST_ELEMENT_WARNING (playsink, STREAM, FORMAT,
3003           (_("Can't play a text file without video or visualizations.")),
3004           ("Have text pad but no video pad or visualizations"));
3005       need_text = FALSE;
3006     } else {
3007       GST_ELEMENT_ERROR (playsink, STREAM, FORMAT,
3008           (_("Can't play a text file without video or visualizations.")),
3009           ("Have text pad but no video pad or visualizations"));
3010       GST_PLAY_SINK_UNLOCK (playsink);
3011       return FALSE;
3012     }
3013   }
3014
3015   GST_DEBUG_OBJECT (playsink, "audio:%d, video:%d, vis:%d, text:%d", need_audio,
3016       need_video, need_vis, need_text);
3017
3018   /* set up video pipeline */
3019   if (need_video) {
3020     gboolean raw, async;
3021
3022     /* we need a raw sink when we do vis or when we have a raw pad */
3023     raw = need_vis ? TRUE : playsink->video_pad_raw;
3024     /* we try to set the sink async=FALSE when we need vis, this way we can
3025      * avoid a queue in the audio chain. */
3026     async = !need_vis;
3027
3028     GST_DEBUG_OBJECT (playsink, "adding video, raw %d",
3029         playsink->video_pad_raw);
3030
3031     if (playsink->videochain) {
3032       /* try to reactivate the chain */
3033       if (!setup_video_chain (playsink, raw, async)) {
3034         if (playsink->video_sinkpad_stream_synchronizer) {
3035           gst_element_release_request_pad (GST_ELEMENT_CAST
3036               (playsink->stream_synchronizer),
3037               playsink->video_sinkpad_stream_synchronizer);
3038           gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3039           playsink->video_sinkpad_stream_synchronizer = NULL;
3040           gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3041           playsink->video_srcpad_stream_synchronizer = NULL;
3042         }
3043
3044         add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3045
3046         /* Remove the sink from the bin to keep its state
3047          * and unparent it to allow reuse */
3048         if (playsink->videochain->sink)
3049           gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
3050               playsink->videochain->sink);
3051
3052         activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3053         free_chain ((GstPlayChain *) playsink->videochain);
3054         playsink->videochain = NULL;
3055
3056         GST_OBJECT_LOCK (playsink);
3057         if (playsink->overlay_element)
3058           gst_object_unref (playsink->overlay_element);
3059         playsink->overlay_element = NULL;
3060
3061         if (playsink->colorbalance_element) {
3062           g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
3063               G_CALLBACK (colorbalance_value_changed_cb), playsink);
3064           gst_object_unref (playsink->colorbalance_element);
3065         }
3066         playsink->colorbalance_element = NULL;
3067         GST_OBJECT_UNLOCK (playsink);
3068       }
3069     }
3070
3071     if (!playsink->videochain)
3072       playsink->videochain = gen_video_chain (playsink, raw, async);
3073     if (!playsink->videochain)
3074       goto no_chain;
3075
3076     if (!playsink->video_sinkpad_stream_synchronizer) {
3077       GValue item = { 0, };
3078       GstIterator *it;
3079
3080       playsink->video_sinkpad_stream_synchronizer =
3081           gst_element_get_request_pad (GST_ELEMENT_CAST
3082           (playsink->stream_synchronizer), "sink_%u");
3083       it = gst_pad_iterate_internal_links
3084           (playsink->video_sinkpad_stream_synchronizer);
3085       g_assert (it);
3086       gst_iterator_next (it, &item);
3087       playsink->video_srcpad_stream_synchronizer = g_value_dup_object (&item);
3088       g_value_unset (&item);
3089       g_assert (playsink->video_srcpad_stream_synchronizer);
3090       gst_iterator_free (it);
3091     }
3092
3093     if (playsink->video_pad)
3094       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad),
3095           playsink->video_sinkpad_stream_synchronizer);
3096
3097     if (need_deinterlace) {
3098       if (!playsink->videodeinterlacechain)
3099         playsink->videodeinterlacechain =
3100             gen_video_deinterlace_chain (playsink);
3101       if (!playsink->videodeinterlacechain)
3102         goto no_chain;
3103
3104       GST_DEBUG_OBJECT (playsink, "adding video deinterlace chain");
3105
3106       GST_DEBUG_OBJECT (playsink, "setting up deinterlacing chain");
3107
3108       add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
3109       activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
3110
3111       gst_pad_unlink (playsink->video_srcpad_stream_synchronizer,
3112           playsink->videochain->sinkpad);
3113       gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3114           playsink->videodeinterlacechain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3115     } else {
3116       if (playsink->videodeinterlacechain) {
3117         add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3118         activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
3119             FALSE);
3120       }
3121     }
3122
3123     GST_DEBUG_OBJECT (playsink, "adding video chain");
3124     add_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
3125     activate_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
3126     /* if we are not part of vis or subtitles, set the ghostpad target */
3127     if (!need_vis && !need_text && (!playsink->textchain
3128             || !playsink->text_pad)) {
3129       GST_DEBUG_OBJECT (playsink, "ghosting video sinkpad");
3130       gst_pad_unlink (playsink->video_srcpad_stream_synchronizer,
3131           playsink->videochain->sinkpad);
3132       if (playsink->videodeinterlacechain
3133           && playsink->videodeinterlacechain->srcpad)
3134         gst_pad_unlink (playsink->videodeinterlacechain->srcpad,
3135             playsink->videochain->sinkpad);
3136       if (need_deinterlace)
3137         gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
3138             playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3139       else
3140         gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3141             playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3142     }
3143   } else {
3144     GST_DEBUG_OBJECT (playsink, "no video needed");
3145     if (playsink->videochain) {
3146       GST_DEBUG_OBJECT (playsink, "removing video chain");
3147       if (playsink->vischain) {
3148         GstPad *srcpad;
3149
3150         GST_DEBUG_OBJECT (playsink, "unlinking vis chain");
3151
3152         /* also had visualisation, release the tee srcpad before we then
3153          * unlink the video from it */
3154         if (playsink->audio_tee_vissrc) {
3155           gst_element_release_request_pad (playsink->audio_tee,
3156               playsink->audio_tee_vissrc);
3157           gst_object_unref (playsink->audio_tee_vissrc);
3158           playsink->audio_tee_vissrc = NULL;
3159         }
3160         srcpad =
3161             gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
3162         gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
3163       }
3164
3165       if (playsink->video_sinkpad_stream_synchronizer) {
3166         gst_element_release_request_pad (GST_ELEMENT_CAST
3167             (playsink->stream_synchronizer),
3168             playsink->video_sinkpad_stream_synchronizer);
3169         gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3170         playsink->video_sinkpad_stream_synchronizer = NULL;
3171         gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3172         playsink->video_srcpad_stream_synchronizer = NULL;
3173       }
3174
3175       add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3176       activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3177       if (playsink->videochain->ts_offset)
3178         gst_object_unref (playsink->videochain->ts_offset);
3179       playsink->videochain->ts_offset = NULL;
3180     }
3181
3182     if (playsink->videodeinterlacechain) {
3183       add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3184       activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3185     }
3186
3187     if (playsink->video_pad)
3188       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
3189
3190     GST_OBJECT_LOCK (playsink);
3191     if (playsink->overlay_element)
3192       gst_object_unref (playsink->overlay_element);
3193     playsink->overlay_element = NULL;
3194
3195     if (playsink->colorbalance_element) {
3196       g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
3197           G_CALLBACK (colorbalance_value_changed_cb), playsink);
3198       gst_object_unref (playsink->colorbalance_element);
3199     }
3200     playsink->colorbalance_element = NULL;
3201     GST_OBJECT_UNLOCK (playsink);
3202
3203   }
3204
3205   if (need_audio) {
3206     gboolean raw;
3207
3208     GST_DEBUG_OBJECT (playsink, "adding audio");
3209
3210     /* get a raw sink if we are asked for a raw pad */
3211     raw = playsink->audio_pad_raw;
3212
3213     if (playsink->audiochain) {
3214       /* try to reactivate the chain */
3215       if (!setup_audio_chain (playsink, raw)) {
3216         GST_DEBUG_OBJECT (playsink, "removing current audio chain");
3217         if (playsink->audio_tee_asrc) {
3218           gst_element_release_request_pad (playsink->audio_tee,
3219               playsink->audio_tee_asrc);
3220           gst_object_unref (playsink->audio_tee_asrc);
3221           playsink->audio_tee_asrc = NULL;
3222         }
3223
3224         if (playsink->audio_sinkpad_stream_synchronizer) {
3225           gst_element_release_request_pad (GST_ELEMENT_CAST
3226               (playsink->stream_synchronizer),
3227               playsink->audio_sinkpad_stream_synchronizer);
3228           gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
3229           playsink->audio_sinkpad_stream_synchronizer = NULL;
3230           gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
3231           playsink->audio_srcpad_stream_synchronizer = NULL;
3232         }
3233
3234         add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3235
3236         /* Remove the sink from the bin to keep its state
3237          * and unparent it to allow reuse */
3238         if (playsink->audiochain->sink)
3239           gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
3240               playsink->audiochain->sink);
3241
3242         activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3243         disconnect_chain (playsink->audiochain, playsink);
3244         playsink->audiochain->volume = NULL;
3245         playsink->audiochain->mute = NULL;
3246         if (playsink->audiochain->ts_offset)
3247           gst_object_unref (playsink->audiochain->ts_offset);
3248         playsink->audiochain->ts_offset = NULL;
3249         free_chain ((GstPlayChain *) playsink->audiochain);
3250         playsink->audiochain = NULL;
3251         playsink->volume_changed = playsink->mute_changed = FALSE;
3252       }
3253     }
3254
3255     if (!playsink->audiochain) {
3256       GST_DEBUG_OBJECT (playsink, "creating new audio chain");
3257       playsink->audiochain = gen_audio_chain (playsink, raw);
3258     }
3259
3260     if (!playsink->audio_sinkpad_stream_synchronizer) {
3261       GValue item = { 0, };
3262       GstIterator *it;
3263
3264       playsink->audio_sinkpad_stream_synchronizer =
3265           gst_element_get_request_pad (GST_ELEMENT_CAST
3266           (playsink->stream_synchronizer), "sink_%u");
3267       it = gst_pad_iterate_internal_links
3268           (playsink->audio_sinkpad_stream_synchronizer);
3269       g_assert (it);
3270       gst_iterator_next (it, &item);
3271       playsink->audio_srcpad_stream_synchronizer = g_value_dup_object (&item);
3272       g_value_unset (&item);
3273       g_assert (playsink->audio_srcpad_stream_synchronizer);
3274       gst_iterator_free (it);
3275     }
3276
3277     if (playsink->audiochain) {
3278       GST_DEBUG_OBJECT (playsink, "adding audio chain");
3279       if (playsink->audio_tee_asrc == NULL) {
3280         playsink->audio_tee_asrc =
3281             gst_element_get_request_pad (playsink->audio_tee, "src_%u");
3282       }
3283       add_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
3284       activate_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
3285       gst_pad_link_full (playsink->audio_tee_asrc,
3286           playsink->audio_sinkpad_stream_synchronizer,
3287           GST_PAD_LINK_CHECK_NOTHING);
3288       gst_pad_link_full (playsink->audio_srcpad_stream_synchronizer,
3289           playsink->audiochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3290     }
3291   } else {
3292     GST_DEBUG_OBJECT (playsink, "no audio needed");
3293     /* we have no audio or we are requested to not play audio */
3294     if (playsink->audiochain) {
3295       GST_DEBUG_OBJECT (playsink, "removing audio chain");
3296       /* release the audio pad */
3297       if (playsink->audio_tee_asrc) {
3298         gst_element_release_request_pad (playsink->audio_tee,
3299             playsink->audio_tee_asrc);
3300         gst_object_unref (playsink->audio_tee_asrc);
3301         playsink->audio_tee_asrc = NULL;
3302       }
3303
3304       if (playsink->audio_sinkpad_stream_synchronizer) {
3305         gst_element_release_request_pad (GST_ELEMENT_CAST
3306             (playsink->stream_synchronizer),
3307             playsink->audio_sinkpad_stream_synchronizer);
3308         gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
3309         playsink->audio_sinkpad_stream_synchronizer = NULL;
3310         gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
3311         playsink->audio_srcpad_stream_synchronizer = NULL;
3312       }
3313
3314       if (playsink->audiochain->sink_volume) {
3315         disconnect_chain (playsink->audiochain, playsink);
3316         playsink->audiochain->volume = NULL;
3317         playsink->audiochain->mute = NULL;
3318         if (playsink->audiochain->ts_offset)
3319           gst_object_unref (playsink->audiochain->ts_offset);
3320         playsink->audiochain->ts_offset = NULL;
3321       }
3322       add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3323       activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3324     }
3325   }
3326
3327   if (need_vis) {
3328     GstPad *srcpad;
3329
3330     if (!playsink->vischain)
3331       playsink->vischain = gen_vis_chain (playsink);
3332
3333     GST_DEBUG_OBJECT (playsink, "adding visualisation");
3334
3335     if (playsink->vischain) {
3336       GST_DEBUG_OBJECT (playsink, "setting up vis chain");
3337       srcpad =
3338           gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
3339       add_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
3340       activate_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
3341       if (playsink->audio_tee_vissrc == NULL) {
3342         playsink->audio_tee_vissrc =
3343             gst_element_get_request_pad (playsink->audio_tee, "src_%u");
3344       }
3345       gst_pad_link_full (playsink->audio_tee_vissrc,
3346           playsink->vischain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3347       gst_pad_link_full (srcpad, playsink->video_sinkpad_stream_synchronizer,
3348           GST_PAD_LINK_CHECK_NOTHING);
3349       gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3350           playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3351       gst_object_unref (srcpad);
3352     }
3353   } else {
3354     GST_DEBUG_OBJECT (playsink, "no vis needed");
3355     if (playsink->vischain) {
3356       if (playsink->audio_tee_vissrc) {
3357         gst_element_release_request_pad (playsink->audio_tee,
3358             playsink->audio_tee_vissrc);
3359         gst_object_unref (playsink->audio_tee_vissrc);
3360         playsink->audio_tee_vissrc = NULL;
3361       }
3362       GST_DEBUG_OBJECT (playsink, "removing vis chain");
3363       add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3364       activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3365     }
3366   }
3367
3368   if (need_text) {
3369     GST_DEBUG_OBJECT (playsink, "adding text");
3370     if (!playsink->textchain) {
3371       GST_DEBUG_OBJECT (playsink, "creating text chain");
3372       playsink->textchain = gen_text_chain (playsink);
3373     }
3374     if (playsink->textchain) {
3375       GstIterator *it;
3376
3377       GST_DEBUG_OBJECT (playsink, "adding text chain");
3378       if (playsink->textchain->overlay)
3379         g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", FALSE,
3380             NULL);
3381       add_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
3382
3383       if (!playsink->text_sinkpad_stream_synchronizer) {
3384         GValue item = { 0, };
3385
3386         playsink->text_sinkpad_stream_synchronizer =
3387             gst_element_get_request_pad (GST_ELEMENT_CAST
3388             (playsink->stream_synchronizer), "sink_%u");
3389         it = gst_pad_iterate_internal_links
3390             (playsink->text_sinkpad_stream_synchronizer);
3391         g_assert (it);
3392         gst_iterator_next (it, &item);
3393         playsink->text_srcpad_stream_synchronizer = g_value_dup_object (&item);
3394         g_value_unset (&item);
3395         g_assert (playsink->text_srcpad_stream_synchronizer);
3396         gst_iterator_free (it);
3397
3398         gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad),
3399             playsink->text_sinkpad_stream_synchronizer);
3400         gst_pad_link_full (playsink->text_srcpad_stream_synchronizer,
3401             playsink->textchain->textsinkpad, GST_PAD_LINK_CHECK_NOTHING);
3402       }
3403
3404       if (need_vis || need_video) {
3405         if (need_vis) {
3406           GstPad *srcpad;
3407
3408           srcpad =
3409               gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
3410           gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
3411           gst_pad_link_full (srcpad, playsink->textchain->videosinkpad,
3412               GST_PAD_LINK_CHECK_NOTHING);
3413           gst_object_unref (srcpad);
3414         } else {
3415           if (need_deinterlace)
3416             gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
3417                 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
3418           else
3419             gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3420                 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
3421         }
3422         gst_pad_link_full (playsink->textchain->srcpad,
3423             playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3424       }
3425
3426       activate_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
3427     }
3428   } else {
3429     GST_DEBUG_OBJECT (playsink, "no text needed");
3430     /* we have no subtitles/text or we are requested to not show them */
3431
3432     if (playsink->text_sinkpad_stream_synchronizer) {
3433       gst_element_release_request_pad (GST_ELEMENT_CAST
3434           (playsink->stream_synchronizer),
3435           playsink->text_sinkpad_stream_synchronizer);
3436       gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
3437       playsink->text_sinkpad_stream_synchronizer = NULL;
3438       gst_object_unref (playsink->text_srcpad_stream_synchronizer);
3439       playsink->text_srcpad_stream_synchronizer = NULL;
3440     }
3441
3442     if (playsink->textchain) {
3443       if (playsink->text_pad == NULL) {
3444         /* no text pad, remove the chain entirely */
3445         GST_DEBUG_OBJECT (playsink, "removing text chain");
3446         add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3447         activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3448       } else {
3449         /* we have a chain and a textpad, turn the subtitles off */
3450         GST_DEBUG_OBJECT (playsink, "turning off the text");
3451         if (playsink->textchain->overlay)
3452           g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", TRUE,
3453               NULL);
3454       }
3455     }
3456     if (!need_video && playsink->video_pad) {
3457       if (playsink->video_sinkpad_stream_synchronizer) {
3458         gst_element_release_request_pad (GST_ELEMENT_CAST
3459             (playsink->stream_synchronizer),
3460             playsink->video_sinkpad_stream_synchronizer);
3461         gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3462         playsink->video_sinkpad_stream_synchronizer = NULL;
3463         gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3464         playsink->video_srcpad_stream_synchronizer = NULL;
3465       }
3466
3467       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
3468     }
3469
3470     if (playsink->text_pad && !playsink->textchain)
3471       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad), NULL);
3472   }
3473   update_av_offset (playsink);
3474   do_async_done (playsink);
3475   GST_PLAY_SINK_UNLOCK (playsink);
3476
3477   return TRUE;
3478
3479   /* ERRORS */
3480 no_chain:
3481   {
3482     /* gen_ chain already posted error */
3483     GST_DEBUG_OBJECT (playsink, "failed to setup chain");
3484     GST_PLAY_SINK_UNLOCK (playsink);
3485     return FALSE;
3486   }
3487 }
3488
3489 /**
3490  * gst_play_sink_set_flags:
3491  * @playsink: a #GstPlaySink
3492  * @flags: #GstPlayFlags
3493  *
3494  * Configure @flags on @playsink. The flags control the behaviour of @playsink
3495  * when constructing the sink pipelins.
3496  *
3497  * Returns: TRUE if the flags could be configured.
3498  */
3499 gboolean
3500 gst_play_sink_set_flags (GstPlaySink * playsink, GstPlayFlags flags)
3501 {
3502   g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), FALSE);
3503
3504   GST_OBJECT_LOCK (playsink);
3505   playsink->flags = flags;
3506   GST_OBJECT_UNLOCK (playsink);
3507
3508   return TRUE;
3509 }
3510
3511 /**
3512  * gst_play_sink_get_flags:
3513  * @playsink: a #GstPlaySink
3514  *
3515  * Get the flags of @playsink. That flags control the behaviour of the sink when
3516  * it constructs the sink pipelines.
3517  *
3518  * Returns: the currently configured #GstPlayFlags.
3519  */
3520 GstPlayFlags
3521 gst_play_sink_get_flags (GstPlaySink * playsink)
3522 {
3523   GstPlayFlags res;
3524
3525   g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), 0);
3526
3527   GST_OBJECT_LOCK (playsink);
3528   res = playsink->flags;
3529   GST_OBJECT_UNLOCK (playsink);
3530
3531   return res;
3532 }
3533
3534 void
3535 gst_play_sink_set_font_desc (GstPlaySink * playsink, const gchar * desc)
3536 {
3537   GstPlayTextChain *chain;
3538
3539   GST_PLAY_SINK_LOCK (playsink);
3540   chain = (GstPlayTextChain *) playsink->textchain;
3541   g_free (playsink->font_desc);
3542   playsink->font_desc = g_strdup (desc);
3543   if (chain && chain->overlay) {
3544     g_object_set (chain->overlay, "font-desc", desc, NULL);
3545   }
3546   GST_PLAY_SINK_UNLOCK (playsink);
3547 }
3548
3549 gchar *
3550 gst_play_sink_get_font_desc (GstPlaySink * playsink)
3551 {
3552   gchar *result = NULL;
3553   GstPlayTextChain *chain;
3554
3555   GST_PLAY_SINK_LOCK (playsink);
3556   chain = (GstPlayTextChain *) playsink->textchain;
3557   if (chain && chain->overlay) {
3558     g_object_get (chain->overlay, "font-desc", &result, NULL);
3559     playsink->font_desc = g_strdup (result);
3560   } else {
3561     result = g_strdup (playsink->font_desc);
3562   }
3563   GST_PLAY_SINK_UNLOCK (playsink);
3564
3565   return result;
3566 }
3567
3568 void
3569 gst_play_sink_set_subtitle_encoding (GstPlaySink * playsink,
3570     const gchar * encoding)
3571 {
3572   GstPlayTextChain *chain;
3573
3574   GST_PLAY_SINK_LOCK (playsink);
3575   chain = (GstPlayTextChain *) playsink->textchain;
3576   g_free (playsink->subtitle_encoding);
3577   playsink->subtitle_encoding = g_strdup (encoding);
3578   if (chain && chain->overlay) {
3579     g_object_set (chain->overlay, "subtitle-encoding", encoding, NULL);
3580   }
3581   GST_PLAY_SINK_UNLOCK (playsink);
3582 }
3583
3584 gchar *
3585 gst_play_sink_get_subtitle_encoding (GstPlaySink * playsink)
3586 {
3587   gchar *result = NULL;
3588   GstPlayTextChain *chain;
3589
3590   GST_PLAY_SINK_LOCK (playsink);
3591   chain = (GstPlayTextChain *) playsink->textchain;
3592   if (chain && chain->overlay) {
3593     g_object_get (chain->overlay, "subtitle-encoding", &result, NULL);
3594     playsink->subtitle_encoding = g_strdup (result);
3595   } else {
3596     result = g_strdup (playsink->subtitle_encoding);
3597   }
3598   GST_PLAY_SINK_UNLOCK (playsink);
3599
3600   return result;
3601 }
3602
3603 static void
3604 update_av_offset (GstPlaySink * playsink)
3605 {
3606   gint64 av_offset;
3607   GstPlayAudioChain *achain;
3608   GstPlayVideoChain *vchain;
3609
3610   av_offset = playsink->av_offset;
3611   achain = (GstPlayAudioChain *) playsink->audiochain;
3612   vchain = (GstPlayVideoChain *) playsink->videochain;
3613
3614   if (achain && vchain && achain->ts_offset && vchain->ts_offset) {
3615     g_object_set (achain->ts_offset,
3616         "ts-offset", MAX (G_GINT64_CONSTANT (0), -av_offset), NULL);
3617     g_object_set (vchain->ts_offset,
3618         "ts-offset", MAX (G_GINT64_CONSTANT (0), av_offset), NULL);
3619   } else {
3620     GST_LOG_OBJECT (playsink, "no ts_offset elements");
3621   }
3622 }
3623
3624 void
3625 gst_play_sink_set_av_offset (GstPlaySink * playsink, gint64 av_offset)
3626 {
3627   GST_PLAY_SINK_LOCK (playsink);
3628   playsink->av_offset = av_offset;
3629   update_av_offset (playsink);
3630   GST_PLAY_SINK_UNLOCK (playsink);
3631 }
3632
3633 gint64
3634 gst_play_sink_get_av_offset (GstPlaySink * playsink)
3635 {
3636   gint64 result;
3637
3638   GST_PLAY_SINK_LOCK (playsink);
3639   result = playsink->av_offset;
3640   GST_PLAY_SINK_UNLOCK (playsink);
3641
3642   return result;
3643 }
3644
3645 /**
3646  * gst_play_sink_get_last_sample:
3647  * @playsink: a #GstPlaySink
3648  *
3649  * Get the last displayed sample from @playsink. This sample is in the native
3650  * format of the sink element, the caps in the result sample contain the format
3651  * of the frame data.
3652  *
3653  * Returns: a #GstSample with the frame data or %NULL when no video frame is
3654  * available.
3655  */
3656 GstSample *
3657 gst_play_sink_get_last_sample (GstPlaySink * playsink)
3658 {
3659   GstSample *result = NULL;
3660   GstPlayVideoChain *chain;
3661
3662   GST_PLAY_SINK_LOCK (playsink);
3663   GST_DEBUG_OBJECT (playsink, "taking last sample");
3664   /* get the video chain if we can */
3665   if ((chain = (GstPlayVideoChain *) playsink->videochain)) {
3666     GST_DEBUG_OBJECT (playsink, "found video chain");
3667     /* see if the chain is active */
3668     if (chain->chain.activated && chain->sink) {
3669       GstElement *elem;
3670
3671       GST_DEBUG_OBJECT (playsink, "video chain active and has a sink");
3672
3673       /* find and get the last-buffer property now */
3674       if ((elem =
3675               gst_play_sink_find_property (playsink, chain->sink,
3676                   "last-sample", GST_TYPE_SAMPLE))) {
3677         GST_DEBUG_OBJECT (playsink, "getting last-sample property");
3678         g_object_get (elem, "last-sample", &result, NULL);
3679         gst_object_unref (elem);
3680       }
3681     }
3682   }
3683   GST_PLAY_SINK_UNLOCK (playsink);
3684
3685   return result;
3686 }
3687
3688 /**
3689  * gst_play_sink_convert_sample:
3690  * @playsink: a #GstPlaySink
3691  * @caps: a #GstCaps
3692  *
3693  * Get the last displayed frame from @playsink. If caps is %NULL, the video will
3694  * be in the native format of the sink element and the caps on the buffer
3695  * describe the format of the frame. If @caps is not %NULL, the video
3696  * frame will be converted to the format of the caps.
3697  *
3698  * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
3699  * available or when the conversion failed.
3700  */
3701 GstSample *
3702 gst_play_sink_convert_sample (GstPlaySink * playsink, GstCaps * caps)
3703 {
3704   GstSample *result;
3705   GError *err = NULL;
3706
3707   result = gst_play_sink_get_last_sample (playsink);
3708   if (result != NULL && caps != NULL) {
3709     GstSample *temp;
3710
3711     temp = gst_video_convert_sample (result, caps, 25 * GST_SECOND, &err);
3712     if (temp == NULL && err)
3713       goto error;
3714
3715     gst_sample_unref (result);
3716     result = temp;
3717   }
3718   return result;
3719
3720   /* ERRORS */
3721 error:
3722   {
3723     /* I'm really uncertain whether we should make playsink post an error
3724      * on the bus or not. It's not like it's a critical issue regarding
3725      * playsink behaviour. */
3726     GST_ERROR ("Error converting frame: %s", err->message);
3727     gst_sample_unref (result);
3728     g_error_free (err);
3729     return NULL;
3730   }
3731 }
3732
3733 static gboolean
3734 is_raw_structure (GstStructure * s)
3735 {
3736   const gchar *name;
3737
3738   name = gst_structure_get_name (s);
3739
3740   if (g_str_equal (name, "video/x-raw") || g_str_equal (name, "audio/x-raw"))
3741     return TRUE;
3742   return FALSE;
3743 }
3744
3745 static gboolean
3746 is_raw_pad (GstPad * pad)
3747 {
3748   GstPad *peer = gst_pad_get_peer (pad);
3749   GstCaps *caps;
3750   gboolean raw = TRUE;
3751
3752   if (!peer)
3753     return raw;
3754
3755   caps = gst_pad_get_current_caps (peer);
3756   if (!caps) {
3757     guint i, n;
3758
3759     caps = gst_pad_query_caps (peer, NULL);
3760
3761     n = gst_caps_get_size (caps);
3762     for (i = 0; i < n; i++) {
3763       gboolean r = is_raw_structure (gst_caps_get_structure (caps, i));
3764
3765       if (i == 0) {
3766         raw = r;
3767       } else if (raw != r) {
3768         GST_ERROR_OBJECT (pad,
3769             "Caps contains raw and non-raw structures: %" GST_PTR_FORMAT, caps);
3770         raw = FALSE;
3771         break;
3772       }
3773     }
3774   } else {
3775     raw = is_raw_structure (gst_caps_get_structure (caps, 0));
3776   }
3777   gst_caps_unref (caps);
3778   gst_object_unref (peer);
3779
3780   return raw;
3781 }
3782
3783 static GstPadProbeReturn
3784 sinkpad_blocked_cb (GstPad * blockedpad, GstPadProbeInfo * info,
3785     gpointer user_data);
3786
3787 static void
3788 video_set_blocked (GstPlaySink * playsink, gboolean blocked)
3789 {
3790   if (playsink->video_pad) {
3791     GstPad *opad =
3792         GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3793             (playsink->video_pad)));
3794     if (blocked && playsink->video_block_id == 0) {
3795       playsink->video_block_id =
3796           gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3797           sinkpad_blocked_cb, playsink, NULL);
3798     } else if (!blocked && playsink->video_block_id) {
3799       gst_pad_remove_probe (opad, playsink->video_block_id);
3800       PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO_RAW);
3801       PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO);
3802       playsink->video_block_id = 0;
3803       playsink->video_pad_blocked = FALSE;
3804     }
3805     gst_object_unref (opad);
3806   }
3807 }
3808
3809 static void
3810 audio_set_blocked (GstPlaySink * playsink, gboolean blocked)
3811 {
3812   if (playsink->audio_pad) {
3813     GstPad *opad =
3814         GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3815             (playsink->audio_pad)));
3816     if (blocked && playsink->audio_block_id == 0) {
3817       playsink->audio_block_id =
3818           gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3819           sinkpad_blocked_cb, playsink, NULL);
3820     } else if (!blocked && playsink->audio_block_id) {
3821       gst_pad_remove_probe (opad, playsink->audio_block_id);
3822       PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO_RAW);
3823       PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO);
3824       playsink->audio_block_id = 0;
3825       playsink->audio_pad_blocked = FALSE;
3826     }
3827     gst_object_unref (opad);
3828   }
3829 }
3830
3831 static void
3832 text_set_blocked (GstPlaySink * playsink, gboolean blocked)
3833 {
3834   if (playsink->text_pad) {
3835     GstPad *opad =
3836         GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3837             (playsink->text_pad)));
3838     if (blocked && playsink->text_block_id == 0) {
3839       playsink->text_block_id =
3840           gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3841           sinkpad_blocked_cb, playsink, NULL);
3842     } else if (!blocked && playsink->text_block_id) {
3843       gst_pad_remove_probe (opad, playsink->text_block_id);
3844       PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_TEXT);
3845       playsink->text_block_id = 0;
3846       playsink->text_pad_blocked = FALSE;
3847     }
3848     gst_object_unref (opad);
3849   }
3850 }
3851
3852 gboolean
3853 gst_play_sink_reconfigure (GstPlaySink * playsink)
3854 {
3855   GST_LOG_OBJECT (playsink, "Triggering reconfiguration");
3856
3857   GST_PLAY_SINK_LOCK (playsink);
3858   video_set_blocked (playsink, TRUE);
3859   audio_set_blocked (playsink, TRUE);
3860   text_set_blocked (playsink, TRUE);
3861   GST_PLAY_SINK_UNLOCK (playsink);
3862
3863   return TRUE;
3864 }
3865
3866 static GstPadProbeReturn
3867 sinkpad_blocked_cb (GstPad * blockedpad, GstPadProbeInfo * info,
3868     gpointer user_data)
3869 {
3870   GstPlaySink *playsink = (GstPlaySink *) user_data;
3871   GstPad *pad;
3872
3873   GST_PLAY_SINK_LOCK (playsink);
3874
3875   pad = GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (blockedpad)));
3876   if (pad == playsink->video_pad) {
3877     playsink->video_pad_blocked = TRUE;
3878     GST_DEBUG_OBJECT (pad, "Video pad blocked");
3879   } else if (pad == playsink->audio_pad) {
3880     playsink->audio_pad_blocked = TRUE;
3881     GST_DEBUG_OBJECT (pad, "Audio pad blocked");
3882   } else if (pad == playsink->text_pad) {
3883     playsink->text_pad_blocked = TRUE;
3884     GST_DEBUG_OBJECT (pad, "Text pad blocked");
3885   }
3886
3887   /* We reconfigure when for ALL streams:
3888    * * there isn't a pad
3889    * * OR the pad is blocked
3890    * * OR there are no pending blocks on that pad
3891    */
3892
3893   if ((!playsink->video_pad || playsink->video_pad_blocked
3894           || !PENDING_VIDEO_BLOCK (playsink)) && (!playsink->audio_pad
3895           || playsink->audio_pad_blocked || !PENDING_AUDIO_BLOCK (playsink))
3896       && (!playsink->text_pad || playsink->text_pad_blocked
3897           || !PENDING_TEXT_BLOCK (playsink))) {
3898     GST_DEBUG_OBJECT (playsink, "All pads blocked -- reconfiguring");
3899
3900     if (playsink->video_pad) {
3901       playsink->video_pad_raw = is_raw_pad (playsink->video_pad);
3902       GST_DEBUG_OBJECT (playsink, "Video pad is raw: %d",
3903           playsink->video_pad_raw);
3904     }
3905
3906     if (playsink->audio_pad) {
3907       playsink->audio_pad_raw = is_raw_pad (playsink->audio_pad);
3908       GST_DEBUG_OBJECT (playsink, "Audio pad is raw: %d",
3909           playsink->audio_pad_raw);
3910     }
3911
3912     gst_play_sink_do_reconfigure (playsink);
3913
3914     video_set_blocked (playsink, FALSE);
3915     audio_set_blocked (playsink, FALSE);
3916     text_set_blocked (playsink, FALSE);
3917   }
3918
3919   gst_object_unref (pad);
3920
3921   GST_PLAY_SINK_UNLOCK (playsink);
3922
3923   return GST_PAD_PROBE_OK;
3924 }
3925
3926 static void
3927 caps_notify_cb (GstPad * pad, GParamSpec * unused, GstPlaySink * playsink)
3928 {
3929   gboolean reconfigure = FALSE;
3930   GstCaps *caps;
3931   gboolean raw;
3932
3933   g_object_get (pad, "caps", &caps, NULL);
3934   if (!caps)
3935     return;
3936
3937   if (pad == playsink->audio_pad) {
3938     raw = is_raw_pad (pad);
3939     reconfigure = (! !playsink->audio_pad_raw != ! !raw)
3940         && playsink->audiochain;
3941     GST_DEBUG_OBJECT (pad,
3942         "Audio caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3943         reconfigure, caps);
3944   } else if (pad == playsink->video_pad) {
3945     raw = is_raw_pad (pad);
3946     reconfigure = (! !playsink->video_pad_raw != ! !raw)
3947         && playsink->videochain;
3948     GST_DEBUG_OBJECT (pad,
3949         "Video caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3950         reconfigure, caps);
3951   }
3952
3953   gst_caps_unref (caps);
3954
3955   if (reconfigure)
3956     gst_play_sink_reconfigure (playsink);
3957 }
3958
3959 void
3960 gst_play_sink_refresh_pad (GstPlaySink * playsink, GstPad * pad,
3961     GstPlaySinkType type)
3962 {
3963   gulong *block_id = NULL;
3964
3965   GST_DEBUG_OBJECT (playsink, "refresh pad %" GST_PTR_FORMAT, pad);
3966
3967   GST_PLAY_SINK_LOCK (playsink);
3968   if (pad == playsink->video_pad) {
3969     if (type != GST_PLAY_SINK_TYPE_VIDEO_RAW &&
3970         type != GST_PLAY_SINK_TYPE_VIDEO)
3971       goto wrong_type;
3972     block_id = &playsink->video_block_id;
3973   } else if (pad == playsink->audio_pad) {
3974     if (type != GST_PLAY_SINK_TYPE_AUDIO_RAW &&
3975         type != GST_PLAY_SINK_TYPE_AUDIO)
3976       goto wrong_type;
3977     block_id = &playsink->audio_block_id;
3978   } else if (pad == playsink->text_pad) {
3979     if (type != GST_PLAY_SINK_TYPE_TEXT)
3980       goto wrong_type;
3981     block_id = &playsink->text_block_id;
3982   }
3983
3984   if (type != GST_PLAY_SINK_TYPE_FLUSHING && (block_id && *block_id == 0)) {
3985     GstPad *blockpad =
3986         GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (pad)));
3987
3988     *block_id =
3989         gst_pad_add_probe (blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3990         sinkpad_blocked_cb, playsink, NULL);
3991     PENDING_FLAG_SET (playsink, type);
3992     gst_object_unref (blockpad);
3993   }
3994   GST_PLAY_SINK_UNLOCK (playsink);
3995
3996   return;
3997
3998   /* ERRORS */
3999 wrong_type:
4000   {
4001     GST_WARNING_OBJECT (playsink, "wrong type %u for pad %" GST_PTR_FORMAT,
4002         type, pad);
4003     GST_PLAY_SINK_UNLOCK (playsink);
4004     return;
4005   }
4006 }
4007
4008 /**
4009  * gst_play_sink_request_pad
4010  * @playsink: a #GstPlaySink
4011  * @type: a #GstPlaySinkType
4012  *
4013  * Create or return a pad of @type.
4014  *
4015  * Returns: a #GstPad of @type or %NULL when the pad could not be created.
4016  */
4017 GstPad *
4018 gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type)
4019 {
4020   GstPad *res = NULL;
4021   gboolean created = FALSE;
4022   gboolean activate = TRUE;
4023   const gchar *pad_name = NULL;
4024   gulong *block_id = NULL;
4025
4026   GST_DEBUG_OBJECT (playsink, "request pad type %d", type);
4027
4028   GST_PLAY_SINK_LOCK (playsink);
4029   switch (type) {
4030     case GST_PLAY_SINK_TYPE_AUDIO_RAW:
4031     case GST_PLAY_SINK_TYPE_AUDIO:
4032       pad_name = "audio_sink";
4033       if (!playsink->audio_tee) {
4034         GST_LOG_OBJECT (playsink, "creating tee");
4035         /* create tee when needed. This element will feed the audio sink chain
4036          * and the vis chain. */
4037         playsink->audio_tee = gst_element_factory_make ("tee", "audiotee");
4038         if (playsink->audio_tee == NULL) {
4039           post_missing_element_message (playsink, "tee");
4040           GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
4041               (_("Missing element '%s' - check your GStreamer installation."),
4042                   "tee"), (NULL));
4043           res = NULL;
4044           break;
4045         } else {
4046           playsink->audio_tee_sink =
4047               gst_element_get_static_pad (playsink->audio_tee, "sink");
4048           gst_bin_add (GST_BIN_CAST (playsink), playsink->audio_tee);
4049           gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
4050         }
4051       } else {
4052         gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
4053       }
4054       if (!playsink->audio_pad) {
4055         GST_LOG_OBJECT (playsink, "ghosting tee sinkpad");
4056         playsink->audio_pad =
4057             gst_ghost_pad_new (pad_name, playsink->audio_tee_sink);
4058         g_signal_connect (G_OBJECT (playsink->audio_pad), "notify::caps",
4059             G_CALLBACK (caps_notify_cb), playsink);
4060         created = TRUE;
4061       }
4062       playsink->audio_pad_raw = FALSE;
4063       res = playsink->audio_pad;
4064       block_id = &playsink->audio_block_id;
4065       break;
4066     case GST_PLAY_SINK_TYPE_VIDEO_RAW:
4067     case GST_PLAY_SINK_TYPE_VIDEO:
4068       pad_name = "video_sink";
4069       if (!playsink->video_pad) {
4070         GST_LOG_OBJECT (playsink, "ghosting videosink");
4071         playsink->video_pad =
4072             gst_ghost_pad_new_no_target (pad_name, GST_PAD_SINK);
4073         g_signal_connect (G_OBJECT (playsink->video_pad), "notify::caps",
4074             G_CALLBACK (caps_notify_cb), playsink);
4075         created = TRUE;
4076       }
4077       playsink->video_pad_raw = FALSE;
4078       res = playsink->video_pad;
4079       block_id = &playsink->video_block_id;
4080       break;
4081     case GST_PLAY_SINK_TYPE_TEXT:
4082       GST_LOG_OBJECT (playsink, "ghosting text");
4083       if (!playsink->text_pad) {
4084         playsink->text_pad =
4085             gst_ghost_pad_new_no_target ("text_sink", GST_PAD_SINK);
4086         created = TRUE;
4087       }
4088       res = playsink->text_pad;
4089       block_id = &playsink->text_block_id;
4090       break;
4091     case GST_PLAY_SINK_TYPE_FLUSHING:
4092     {
4093       gchar *padname;
4094
4095       /* we need a unique padname for the flushing pad. */
4096       padname = g_strdup_printf ("flushing_%u", playsink->count);
4097       res = gst_ghost_pad_new_no_target (padname, GST_PAD_SINK);
4098       g_free (padname);
4099       playsink->count++;
4100       activate = FALSE;
4101       created = TRUE;
4102       break;
4103     }
4104     default:
4105       res = NULL;
4106       break;
4107   }
4108   GST_PLAY_SINK_UNLOCK (playsink);
4109
4110   if (created && res) {
4111     /* we have to add the pad when it's active or we get an error when the
4112      * element is 'running' */
4113     gst_pad_set_active (res, TRUE);
4114     gst_element_add_pad (GST_ELEMENT_CAST (playsink), res);
4115     if (block_id && *block_id == 0) {
4116       GstPad *blockpad =
4117           GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (res)));
4118
4119       *block_id =
4120           gst_pad_add_probe (blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
4121           sinkpad_blocked_cb, playsink, NULL);
4122       PENDING_FLAG_SET (playsink, type);
4123       gst_object_unref (blockpad);
4124     }
4125     if (!activate)
4126       gst_pad_set_active (res, activate);
4127   }
4128
4129   return res;
4130 }
4131
4132
4133 static GstPad *
4134 gst_play_sink_request_new_pad (GstElement * element, GstPadTemplate * templ,
4135     const gchar * name, const GstCaps * caps)
4136 {
4137   GstPlaySink *psink;
4138   GstPad *pad;
4139   GstPlaySinkType type;
4140   const gchar *tplname;
4141
4142   g_return_val_if_fail (templ != NULL, NULL);
4143
4144   GST_DEBUG_OBJECT (element, "name:%s", name);
4145
4146   psink = GST_PLAY_SINK (element);
4147   tplname = GST_PAD_TEMPLATE_NAME_TEMPLATE (templ);
4148
4149   /* Figure out the GstPlaySinkType based on the template */
4150   if (!strcmp (tplname, "audio_sink"))
4151     type = GST_PLAY_SINK_TYPE_AUDIO;
4152   else if (!strcmp (tplname, "audio_raw_sink"))
4153     type = GST_PLAY_SINK_TYPE_AUDIO_RAW;
4154   else if (!strcmp (tplname, "video_sink"))
4155     type = GST_PLAY_SINK_TYPE_VIDEO;
4156   else if (!strcmp (tplname, "video_raw_sink"))
4157     type = GST_PLAY_SINK_TYPE_VIDEO_RAW;
4158   else if (!strcmp (tplname, "text_sink"))
4159     type = GST_PLAY_SINK_TYPE_TEXT;
4160   else
4161     goto unknown_template;
4162
4163   pad = gst_play_sink_request_pad (psink, type);
4164   return pad;
4165
4166 unknown_template:
4167   GST_WARNING_OBJECT (element, "Unknown pad template");
4168   return NULL;
4169 }
4170
4171 void
4172 gst_play_sink_release_pad (GstPlaySink * playsink, GstPad * pad)
4173 {
4174   GstPad **res = NULL;
4175   gboolean untarget = TRUE;
4176
4177   GST_DEBUG_OBJECT (playsink, "release pad %" GST_PTR_FORMAT, pad);
4178
4179   GST_PLAY_SINK_LOCK (playsink);
4180   if (pad == playsink->video_pad) {
4181     res = &playsink->video_pad;
4182     g_signal_handlers_disconnect_by_func (playsink->video_pad, caps_notify_cb,
4183         playsink);
4184     video_set_blocked (playsink, FALSE);
4185   } else if (pad == playsink->audio_pad) {
4186     res = &playsink->audio_pad;
4187     g_signal_handlers_disconnect_by_func (playsink->audio_pad, caps_notify_cb,
4188         playsink);
4189     audio_set_blocked (playsink, FALSE);
4190   } else if (pad == playsink->text_pad) {
4191     res = &playsink->text_pad;
4192     text_set_blocked (playsink, FALSE);
4193   } else {
4194     /* try to release the given pad anyway, these could be the FLUSHING pads. */
4195     res = &pad;
4196     untarget = FALSE;
4197   }
4198   GST_PLAY_SINK_UNLOCK (playsink);
4199
4200   if (*res) {
4201     GST_DEBUG_OBJECT (playsink, "deactivate pad %" GST_PTR_FORMAT, *res);
4202     gst_pad_set_active (*res, FALSE);
4203     if (untarget) {
4204       GST_DEBUG_OBJECT (playsink, "untargeting pad %" GST_PTR_FORMAT, *res);
4205       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (*res), NULL);
4206     }
4207     GST_DEBUG_OBJECT (playsink, "remove pad %" GST_PTR_FORMAT, *res);
4208     gst_element_remove_pad (GST_ELEMENT_CAST (playsink), *res);
4209     *res = NULL;
4210   }
4211 }
4212
4213 static void
4214 gst_play_sink_release_request_pad (GstElement * element, GstPad * pad)
4215 {
4216   GstPlaySink *psink = GST_PLAY_SINK (element);
4217
4218   gst_play_sink_release_pad (psink, pad);
4219 }
4220
4221 static void
4222 gst_play_sink_handle_message (GstBin * bin, GstMessage * message)
4223 {
4224   GstPlaySink *playsink;
4225
4226   playsink = GST_PLAY_SINK_CAST (bin);
4227
4228   switch (GST_MESSAGE_TYPE (message)) {
4229     case GST_MESSAGE_STEP_DONE:
4230     {
4231       GstFormat format;
4232       guint64 amount;
4233       gdouble rate;
4234       gboolean flush, intermediate, eos;
4235       guint64 duration;
4236
4237       GST_INFO_OBJECT (playsink, "Handling step-done message");
4238       gst_message_parse_step_done (message, &format, &amount, &rate, &flush,
4239           &intermediate, &duration, &eos);
4240
4241       if (format == GST_FORMAT_BUFFERS) {
4242         /* for the buffer format, we align the other streams */
4243         if (playsink->audiochain) {
4244           GstEvent *event;
4245
4246           event =
4247               gst_event_new_step (GST_FORMAT_TIME, duration, rate, flush,
4248               intermediate);
4249
4250           if (!gst_element_send_event (playsink->audiochain->chain.bin, event)) {
4251             GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
4252           }
4253         }
4254       }
4255       GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
4256       break;
4257     }
4258     case GST_MESSAGE_ELEMENT:{
4259       if (gst_is_video_overlay_prepare_window_handle_message (message)) {
4260         GstVideoOverlay *overlay;
4261
4262         GST_OBJECT_LOCK (playsink);
4263         if (playsink->overlay_element
4264             && GST_OBJECT_CAST (playsink->overlay_element) !=
4265             GST_MESSAGE_SRC (message)) {
4266           gst_object_unref (playsink->overlay_element);
4267           playsink->overlay_element = NULL;
4268         }
4269
4270         if (!playsink->overlay_element)
4271           playsink->overlay_element =
4272               GST_VIDEO_OVERLAY (gst_object_ref (GST_MESSAGE_SRC (message)));
4273         overlay =
4274             GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4275         GST_OBJECT_UNLOCK (playsink);
4276
4277         GST_DEBUG_OBJECT (playsink, "Got prepare-xwindow-id message");
4278
4279         if (playsink->overlay_handle_set)
4280           gst_video_overlay_set_window_handle (playsink->overlay_element,
4281               playsink->overlay_handle);
4282         if (playsink->overlay_handle_events_set)
4283           gst_video_overlay_handle_events (playsink->overlay_element,
4284               playsink->overlay_handle_events);
4285         if (playsink->overlay_render_rectangle_set)
4286           gst_video_overlay_set_render_rectangle (playsink->overlay_element,
4287               playsink->overlay_x, playsink->overlay_y,
4288               playsink->overlay_width, playsink->overlay_height);
4289
4290         gst_object_unref (overlay);
4291         gst_message_unref (message);
4292         gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (playsink));
4293       } else {
4294         GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin,
4295             message);
4296       }
4297       break;
4298     }
4299     default:
4300       GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
4301       break;
4302   }
4303 }
4304
4305 /* Send an event to our sinks until one of them works; don't then send to the
4306  * remaining sinks (unlike GstBin)
4307  * Special case: If a text sink is set we need to send the event
4308  * to them in case it's source is different from the a/v stream's source.
4309  */
4310 static gboolean
4311 gst_play_sink_send_event_to_sink (GstPlaySink * playsink, GstEvent * event)
4312 {
4313   gboolean res = TRUE;
4314   if (playsink->send_event_mode == MODE_FIRST) {
4315     if (playsink->textchain && playsink->textchain->sink) {
4316       gst_event_ref (event);
4317       if ((res =
4318               gst_element_send_event (playsink->textchain->chain.bin, event))) {
4319         GST_DEBUG_OBJECT (playsink, "Sent event successfully to text sink");
4320       } else {
4321         GST_DEBUG_OBJECT (playsink, "Event failed when sent to text sink");
4322       }
4323     }
4324
4325     if (playsink->videochain) {
4326       gst_event_ref (event);
4327       if ((res =
4328               gst_element_send_event (playsink->videochain->chain.bin,
4329                   event))) {
4330         GST_DEBUG_OBJECT (playsink, "Sent event successfully to video sink");
4331         goto done;
4332       }
4333       GST_DEBUG_OBJECT (playsink, "Event failed when sent to video sink");
4334     }
4335     if (playsink->audiochain) {
4336       gst_event_ref (event);
4337       if ((res =
4338               gst_element_send_event (playsink->audiochain->chain.bin,
4339                   event))) {
4340         GST_DEBUG_OBJECT (playsink, "Sent event successfully to audio sink");
4341         goto done;
4342       }
4343       GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
4344     }
4345   } else {
4346     return
4347         GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event
4348         (GST_ELEMENT_CAST (playsink), event);
4349   }
4350
4351 done:
4352   gst_event_unref (event);
4353   return res;
4354 }
4355
4356 /* We only want to send the event to a single sink (overriding GstBin's
4357  * behaviour), but we want to keep GstPipeline's behaviour - wrapping seek
4358  * events appropriately. So, this is a messy duplication of code. */
4359 static gboolean
4360 gst_play_sink_send_event (GstElement * element, GstEvent * event)
4361 {
4362   gboolean res = FALSE;
4363   GstEventType event_type = GST_EVENT_TYPE (event);
4364   GstPlaySink *playsink;
4365   playsink = GST_PLAY_SINK_CAST (element);
4366   switch (event_type) {
4367     case GST_EVENT_SEEK:
4368       GST_DEBUG_OBJECT (element, "Sending event to a sink");
4369       res = gst_play_sink_send_event_to_sink (playsink, event);
4370       break;
4371     case GST_EVENT_STEP:
4372     {
4373       GstFormat format;
4374       guint64 amount;
4375       gdouble rate;
4376       gboolean flush, intermediate;
4377       gst_event_parse_step (event, &format, &amount, &rate, &flush,
4378           &intermediate);
4379       if (format == GST_FORMAT_BUFFERS) {
4380         /* for buffers, we will try to step video frames, for other formats we
4381          * send the step to all sinks */
4382         res = gst_play_sink_send_event_to_sink (playsink, event);
4383       } else {
4384         res =
4385             GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
4386             event);
4387       }
4388       break;
4389     }
4390     default:
4391       res =
4392           GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
4393           event);
4394       break;
4395   }
4396   return res;
4397 }
4398
4399 static GstStateChangeReturn
4400 gst_play_sink_change_state (GstElement * element, GstStateChange transition)
4401 {
4402   GstStateChangeReturn ret;
4403   GstStateChangeReturn bret;
4404   GstPlaySink *playsink;
4405   playsink = GST_PLAY_SINK (element);
4406   switch (transition) {
4407     case GST_STATE_CHANGE_READY_TO_PAUSED:
4408       gst_segment_init (&playsink->text_segment, GST_FORMAT_UNDEFINED);
4409
4410       playsink->need_async_start = TRUE;
4411       /* we want to go async to PAUSED until we managed to configure and add the
4412        * sinks */
4413       do_async_start (playsink);
4414       ret = GST_STATE_CHANGE_ASYNC;
4415
4416       /* block all pads here */
4417       if (!gst_play_sink_reconfigure (playsink))
4418         ret = GST_STATE_CHANGE_FAILURE;
4419       break;
4420     case GST_STATE_CHANGE_PAUSED_TO_READY:
4421       /* unblock all pads here */
4422       GST_PLAY_SINK_LOCK (playsink);
4423       video_set_blocked (playsink, FALSE);
4424       audio_set_blocked (playsink, FALSE);
4425       text_set_blocked (playsink, FALSE);
4426       GST_PLAY_SINK_UNLOCK (playsink);
4427       /* fall through */
4428     case GST_STATE_CHANGE_READY_TO_NULL:
4429       if (playsink->audiochain && playsink->audiochain->sink_volume) {
4430         /* remove our links to the mute and volume elements when they were
4431          * provided by a sink */
4432         disconnect_chain (playsink->audiochain, playsink);
4433         playsink->audiochain->volume = NULL;
4434         playsink->audiochain->mute = NULL;
4435       }
4436
4437       if (playsink->audiochain && playsink->audiochain->ts_offset) {
4438         gst_object_unref (playsink->audiochain->ts_offset);
4439         playsink->audiochain->ts_offset = NULL;
4440       }
4441
4442       if (playsink->videochain && playsink->videochain->ts_offset) {
4443         gst_object_unref (playsink->videochain->ts_offset);
4444         playsink->videochain->ts_offset = NULL;
4445       }
4446
4447       GST_OBJECT_LOCK (playsink);
4448       if (playsink->overlay_element)
4449         gst_object_unref (playsink->overlay_element);
4450       playsink->overlay_element = NULL;
4451
4452       if (playsink->colorbalance_element) {
4453         g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
4454             G_CALLBACK (colorbalance_value_changed_cb), playsink);
4455         gst_object_unref (playsink->colorbalance_element);
4456       }
4457       playsink->colorbalance_element = NULL;
4458       GST_OBJECT_UNLOCK (playsink);
4459
4460       ret = GST_STATE_CHANGE_SUCCESS;
4461       break;
4462     default:
4463       /* all other state changes return SUCCESS by default, this value can be
4464        * overridden by the result of the children */
4465       ret = GST_STATE_CHANGE_SUCCESS;
4466       break;
4467   }
4468
4469   /* do the state change of the children */
4470   bret =
4471       GST_ELEMENT_CLASS (gst_play_sink_parent_class)->change_state (element,
4472       transition);
4473   /* now look at the result of our children and adjust the return value */
4474   switch (bret) {
4475     case GST_STATE_CHANGE_FAILURE:
4476       /* failure, we stop */
4477       goto activate_failed;
4478     case GST_STATE_CHANGE_NO_PREROLL:
4479       /* some child returned NO_PREROLL. This is strange but we never know. We
4480        * commit our async state change (if any) and return the NO_PREROLL */
4481       do_async_done (playsink);
4482       ret = bret;
4483       break;
4484     case GST_STATE_CHANGE_ASYNC:
4485       /* some child was async, return this */
4486       ret = bret;
4487       break;
4488     default:
4489       /* return our previously configured return value */
4490       break;
4491   }
4492
4493   switch (transition) {
4494     case GST_STATE_CHANGE_READY_TO_PAUSED:
4495       break;
4496     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
4497       /* FIXME Release audio device when we implement that */
4498       playsink->need_async_start = TRUE;
4499       break;
4500     case GST_STATE_CHANGE_PAUSED_TO_READY:{
4501       if (playsink->video_sinkpad_stream_synchronizer) {
4502         gst_element_release_request_pad (GST_ELEMENT_CAST
4503             (playsink->stream_synchronizer),
4504             playsink->video_sinkpad_stream_synchronizer);
4505         gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
4506         playsink->video_sinkpad_stream_synchronizer = NULL;
4507         gst_object_unref (playsink->video_srcpad_stream_synchronizer);
4508         playsink->video_srcpad_stream_synchronizer = NULL;
4509       }
4510       if (playsink->audio_sinkpad_stream_synchronizer) {
4511         gst_element_release_request_pad (GST_ELEMENT_CAST
4512             (playsink->stream_synchronizer),
4513             playsink->audio_sinkpad_stream_synchronizer);
4514         gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
4515         playsink->audio_sinkpad_stream_synchronizer = NULL;
4516         gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
4517         playsink->audio_srcpad_stream_synchronizer = NULL;
4518       }
4519       if (playsink->text_sinkpad_stream_synchronizer) {
4520         gst_element_release_request_pad (GST_ELEMENT_CAST
4521             (playsink->stream_synchronizer),
4522             playsink->text_sinkpad_stream_synchronizer);
4523         gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
4524         playsink->text_sinkpad_stream_synchronizer = NULL;
4525         gst_object_unref (playsink->text_srcpad_stream_synchronizer);
4526         playsink->text_srcpad_stream_synchronizer = NULL;
4527       }
4528     }
4529       /* fall through */
4530     case GST_STATE_CHANGE_READY_TO_NULL:
4531       /* remove sinks we added */
4532       if (playsink->videodeinterlacechain) {
4533         activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
4534             FALSE);
4535         add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
4536       }
4537       if (playsink->videochain) {
4538         activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
4539         add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
4540       }
4541       if (playsink->audiochain) {
4542         activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
4543         add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
4544       }
4545       if (playsink->vischain) {
4546         activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
4547         add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
4548       }
4549       if (playsink->textchain) {
4550         activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
4551         add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
4552       }
4553       do_async_done (playsink);
4554       /* when going to READY, keep elements around as long as possible,
4555        * so they may be re-used faster next time/url around.
4556        * when really going to NULL, clean up everything completely. */
4557       if (transition == GST_STATE_CHANGE_READY_TO_NULL) {
4558
4559         /* Unparent the sinks to allow reuse */
4560         if (playsink->videochain && playsink->videochain->sink)
4561           gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
4562               playsink->videochain->sink);
4563         if (playsink->audiochain && playsink->audiochain->sink)
4564           gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
4565               playsink->audiochain->sink);
4566         if (playsink->textchain && playsink->textchain->sink)
4567           gst_bin_remove (GST_BIN_CAST (playsink->textchain->chain.bin),
4568               playsink->textchain->sink);
4569         if (playsink->audio_sink != NULL)
4570           gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
4571         if (playsink->video_sink != NULL)
4572           gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
4573         if (playsink->visualisation != NULL)
4574           gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
4575         if (playsink->text_sink != NULL)
4576           gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
4577         free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
4578         playsink->videodeinterlacechain = NULL;
4579         free_chain ((GstPlayChain *) playsink->videochain);
4580         playsink->videochain = NULL;
4581         free_chain ((GstPlayChain *) playsink->audiochain);
4582         playsink->audiochain = NULL;
4583         free_chain ((GstPlayChain *) playsink->vischain);
4584         playsink->vischain = NULL;
4585         free_chain ((GstPlayChain *) playsink->textchain);
4586         playsink->textchain = NULL;
4587       }
4588       break;
4589     default:
4590       break;
4591   }
4592   return ret;
4593   /* ERRORS */
4594 activate_failed:
4595   {
4596     GST_DEBUG_OBJECT (element,
4597         "element failed to change states -- activation problem?");
4598     return GST_STATE_CHANGE_FAILURE;
4599   }
4600 }
4601
4602 static void
4603 gst_play_sink_set_property (GObject * object, guint prop_id,
4604     const GValue * value, GParamSpec * spec)
4605 {
4606   GstPlaySink *playsink = GST_PLAY_SINK (object);
4607   switch (prop_id) {
4608     case PROP_FLAGS:
4609       gst_play_sink_set_flags (playsink, g_value_get_flags (value));
4610       break;
4611     case PROP_VOLUME:
4612       gst_play_sink_set_volume (playsink, g_value_get_double (value));
4613       break;
4614     case PROP_MUTE:
4615       gst_play_sink_set_mute (playsink, g_value_get_boolean (value));
4616       break;
4617     case PROP_FONT_DESC:
4618       gst_play_sink_set_font_desc (playsink, g_value_get_string (value));
4619       break;
4620     case PROP_SUBTITLE_ENCODING:
4621       gst_play_sink_set_subtitle_encoding (playsink,
4622           g_value_get_string (value));
4623       break;
4624     case PROP_VIS_PLUGIN:
4625       gst_play_sink_set_vis_plugin (playsink, g_value_get_object (value));
4626       break;
4627     case PROP_AV_OFFSET:
4628       gst_play_sink_set_av_offset (playsink, g_value_get_int64 (value));
4629       break;
4630     case PROP_VIDEO_SINK:
4631       gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_VIDEO,
4632           g_value_get_object (value));
4633       break;
4634     case PROP_AUDIO_SINK:
4635       gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_AUDIO,
4636           g_value_get_object (value));
4637       break;
4638     case PROP_TEXT_SINK:
4639       gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_TEXT,
4640           g_value_get_object (value));
4641       break;
4642     case PROP_SEND_EVENT_MODE:
4643       playsink->send_event_mode = g_value_get_enum (value);
4644       break;
4645     case PROP_FORCE_ASPECT_RATIO:{
4646       GstPlayVideoChain *chain;
4647       GstElement *elem;
4648
4649       playsink->force_aspect_ratio = g_value_get_boolean (value);
4650
4651       GST_PLAY_SINK_LOCK (playsink);
4652       if (playsink->videochain) {
4653         chain = (GstPlayVideoChain *) playsink->videochain;
4654
4655         if (chain->sink) {
4656           elem =
4657               gst_play_sink_find_property_sinks (playsink, chain->sink,
4658               "force-aspect-ratio", G_TYPE_BOOLEAN);
4659
4660           if (elem)
4661             g_object_set (elem, "force-aspect-ratio",
4662                 playsink->force_aspect_ratio, NULL);
4663         }
4664       }
4665       GST_PLAY_SINK_UNLOCK (playsink);
4666       break;
4667     }
4668     default:
4669       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
4670       break;
4671   }
4672 }
4673
4674 static void
4675 gst_play_sink_get_property (GObject * object, guint prop_id,
4676     GValue * value, GParamSpec * spec)
4677 {
4678   GstPlaySink *playsink = GST_PLAY_SINK (object);
4679   switch (prop_id) {
4680     case PROP_FLAGS:
4681       g_value_set_flags (value, gst_play_sink_get_flags (playsink));
4682       break;
4683     case PROP_VOLUME:
4684       g_value_set_double (value, gst_play_sink_get_volume (playsink));
4685       break;
4686     case PROP_MUTE:
4687       g_value_set_boolean (value, gst_play_sink_get_mute (playsink));
4688       break;
4689     case PROP_FONT_DESC:
4690       g_value_take_string (value, gst_play_sink_get_font_desc (playsink));
4691       break;
4692     case PROP_SUBTITLE_ENCODING:
4693       g_value_take_string (value,
4694           gst_play_sink_get_subtitle_encoding (playsink));
4695       break;
4696     case PROP_VIS_PLUGIN:
4697       g_value_take_object (value, gst_play_sink_get_vis_plugin (playsink));
4698       break;
4699     case PROP_SAMPLE:
4700       gst_value_take_sample (value, gst_play_sink_get_last_sample (playsink));
4701       break;
4702     case PROP_AV_OFFSET:
4703       g_value_set_int64 (value, gst_play_sink_get_av_offset (playsink));
4704       break;
4705     case PROP_VIDEO_SINK:
4706       g_value_take_object (value, gst_play_sink_get_sink (playsink,
4707               GST_PLAY_SINK_TYPE_VIDEO));
4708       break;
4709     case PROP_AUDIO_SINK:
4710       g_value_take_object (value, gst_play_sink_get_sink (playsink,
4711               GST_PLAY_SINK_TYPE_AUDIO));
4712       break;
4713     case PROP_TEXT_SINK:
4714       g_value_take_object (value, gst_play_sink_get_sink (playsink,
4715               GST_PLAY_SINK_TYPE_TEXT));
4716       break;
4717     case PROP_SEND_EVENT_MODE:
4718       g_value_set_enum (value, playsink->send_event_mode);
4719       break;
4720     case PROP_FORCE_ASPECT_RATIO:
4721       g_value_set_boolean (value, playsink->force_aspect_ratio);
4722       break;
4723     default:
4724       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
4725       break;
4726   }
4727 }
4728
4729 static void
4730 gst_play_sink_overlay_expose (GstVideoOverlay * overlay)
4731 {
4732   GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4733   GstVideoOverlay *overlay_element;
4734
4735   GST_OBJECT_LOCK (playsink);
4736   if (playsink->overlay_element)
4737     overlay_element =
4738         GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4739   else
4740     overlay_element = NULL;
4741   GST_OBJECT_UNLOCK (playsink);
4742
4743   if (overlay_element) {
4744     gst_video_overlay_expose (overlay_element);
4745     gst_object_unref (overlay_element);
4746   }
4747 }
4748
4749 static void
4750 gst_play_sink_overlay_handle_events (GstVideoOverlay * overlay,
4751     gboolean handle_events)
4752 {
4753   GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4754   GstVideoOverlay *overlay_element;
4755
4756   GST_OBJECT_LOCK (playsink);
4757   if (playsink->overlay_element)
4758     overlay_element =
4759         GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4760   else
4761     overlay_element = NULL;
4762   GST_OBJECT_UNLOCK (playsink);
4763
4764   playsink->overlay_handle_events_set = TRUE;
4765   playsink->overlay_handle_events = handle_events;
4766
4767   if (overlay_element) {
4768     gst_video_overlay_handle_events (overlay_element, handle_events);
4769     gst_object_unref (overlay_element);
4770   }
4771 }
4772
4773 static void
4774 gst_play_sink_overlay_set_render_rectangle (GstVideoOverlay * overlay, gint x,
4775     gint y, gint width, gint height)
4776 {
4777   GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4778   GstVideoOverlay *overlay_element;
4779
4780   GST_OBJECT_LOCK (playsink);
4781   if (playsink->overlay_element)
4782     overlay_element =
4783         GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4784   else
4785     overlay_element = NULL;
4786   GST_OBJECT_UNLOCK (playsink);
4787
4788   playsink->overlay_render_rectangle_set = TRUE;
4789   playsink->overlay_x = x;
4790   playsink->overlay_y = y;
4791   playsink->overlay_width = width;
4792   playsink->overlay_height = height;
4793
4794   if (overlay_element) {
4795     gst_video_overlay_set_render_rectangle (overlay_element, x, y, width,
4796         height);
4797     gst_object_unref (overlay_element);
4798   }
4799 }
4800
4801 static void
4802 gst_play_sink_overlay_set_window_handle (GstVideoOverlay * overlay,
4803     guintptr handle)
4804 {
4805   GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4806   GstVideoOverlay *overlay_element;
4807
4808   GST_OBJECT_LOCK (playsink);
4809   if (playsink->overlay_element)
4810     overlay_element =
4811         GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4812   else
4813     overlay_element = NULL;
4814   GST_OBJECT_UNLOCK (playsink);
4815
4816   playsink->overlay_handle_set = TRUE;
4817   playsink->overlay_handle = handle;
4818
4819   if (overlay_element) {
4820     gst_video_overlay_set_window_handle (overlay_element, handle);
4821     gst_object_unref (overlay_element);
4822   }
4823 }
4824
4825 static void
4826 gst_play_sink_overlay_init (gpointer g_iface, gpointer g_iface_data)
4827 {
4828   GstVideoOverlayInterface *iface = (GstVideoOverlayInterface *) g_iface;
4829   iface->expose = gst_play_sink_overlay_expose;
4830   iface->handle_events = gst_play_sink_overlay_handle_events;
4831   iface->set_render_rectangle = gst_play_sink_overlay_set_render_rectangle;
4832   iface->set_window_handle = gst_play_sink_overlay_set_window_handle;
4833 }
4834
4835 static void
4836 gst_play_sink_navigation_send_event (GstNavigation * navigation,
4837     GstStructure * structure)
4838 {
4839   GstPlaySink *playsink = GST_PLAY_SINK (navigation);
4840   GstBin *bin = NULL;
4841
4842   GST_PLAY_SINK_LOCK (playsink);
4843   if (playsink->videochain && playsink->videochain->chain.bin)
4844     bin = GST_BIN (gst_object_ref (playsink->videochain->chain.bin));
4845   GST_PLAY_SINK_UNLOCK (playsink);
4846
4847   if (bin) {
4848     GstElement *nav = gst_bin_get_by_interface (bin, GST_TYPE_NAVIGATION);
4849
4850     if (nav) {
4851       gst_navigation_send_event (GST_NAVIGATION (nav), structure);
4852       structure = NULL;
4853       gst_object_unref (nav);
4854     } else {
4855       GstEvent *event = gst_event_new_navigation (structure);
4856       structure = NULL;
4857       gst_element_send_event (GST_ELEMENT (bin), event);
4858     }
4859
4860     gst_object_unref (bin);
4861   }
4862
4863   if (structure)
4864     gst_structure_free (structure);
4865 }
4866
4867 static void
4868 gst_play_sink_navigation_init (gpointer g_iface, gpointer g_iface_data)
4869 {
4870   GstNavigationInterface *iface = (GstNavigationInterface *) g_iface;
4871
4872   iface->send_event = gst_play_sink_navigation_send_event;
4873 }
4874
4875 static const GList *
4876 gst_play_sink_colorbalance_list_channels (GstColorBalance * balance)
4877 {
4878   GstPlaySink *playsink = GST_PLAY_SINK (balance);
4879
4880   return playsink->colorbalance_channels;
4881 }
4882
4883 static void
4884 gst_play_sink_colorbalance_set_value (GstColorBalance * balance,
4885     GstColorBalanceChannel * proxy, gint value)
4886 {
4887   GstPlaySink *playsink = GST_PLAY_SINK (balance);
4888   GList *l;
4889   gint i;
4890   GstColorBalance *balance_element = NULL;
4891
4892   GST_OBJECT_LOCK (playsink);
4893   if (playsink->colorbalance_element)
4894     balance_element =
4895         GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
4896   GST_OBJECT_UNLOCK (playsink);
4897
4898   for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
4899     GstColorBalanceChannel *proxy_tmp = l->data;
4900     gdouble new_val;
4901
4902     if (proxy_tmp != proxy)
4903       continue;
4904
4905     playsink->colorbalance_values[i] = value;
4906
4907     if (balance_element) {
4908       GstColorBalanceChannel *channel = NULL;
4909       const GList *channels, *k;
4910
4911       channels = gst_color_balance_list_channels (balance_element);
4912       for (k = channels; k; k = k->next) {
4913         GstColorBalanceChannel *tmp = l->data;
4914
4915         if (g_strrstr (tmp->label, proxy->label)) {
4916           channel = tmp;
4917           break;
4918         }
4919       }
4920
4921       g_assert (channel);
4922
4923       /* Convert to [0, 1] range */
4924       new_val =
4925           ((gdouble) value -
4926           (gdouble) proxy->min_value) / ((gdouble) proxy->max_value -
4927           (gdouble) proxy->min_value);
4928       /* Convert to channel range */
4929       new_val =
4930           channel->min_value + new_val * ((gdouble) channel->max_value -
4931           (gdouble) channel->min_value);
4932
4933       gst_color_balance_set_value (balance_element, channel,
4934           (gint) (new_val + 0.5));
4935
4936       gst_object_unref (balance_element);
4937     }
4938
4939     gst_color_balance_value_changed (balance, proxy, value);
4940     break;
4941   }
4942 }
4943
4944 static gint
4945 gst_play_sink_colorbalance_get_value (GstColorBalance * balance,
4946     GstColorBalanceChannel * proxy)
4947 {
4948   GstPlaySink *playsink = GST_PLAY_SINK (balance);
4949   GList *l;
4950   gint i;
4951
4952   for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
4953     GstColorBalanceChannel *proxy_tmp = l->data;
4954
4955     if (proxy_tmp != proxy)
4956       continue;
4957
4958     return playsink->colorbalance_values[i];
4959   }
4960
4961   g_return_val_if_reached (0);
4962 }
4963
4964 static GstColorBalanceType
4965 gst_play_sink_colorbalance_get_balance_type (GstColorBalance * balance)
4966 {
4967   GstPlaySink *playsink = GST_PLAY_SINK (balance);
4968   GstColorBalance *balance_element = NULL;
4969   GstColorBalanceType t = GST_COLOR_BALANCE_SOFTWARE;
4970
4971   GST_OBJECT_LOCK (playsink);
4972   if (playsink->colorbalance_element)
4973     balance_element =
4974         GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
4975   GST_OBJECT_UNLOCK (playsink);
4976
4977   if (balance_element) {
4978     t = gst_color_balance_get_balance_type (balance_element);
4979     gst_object_unref (balance_element);
4980   }
4981
4982   return t;
4983 }
4984
4985 static void
4986 gst_play_sink_colorbalance_init (gpointer g_iface, gpointer g_iface_data)
4987 {
4988   GstColorBalanceInterface *iface = (GstColorBalanceInterface *) g_iface;
4989
4990   iface->list_channels = gst_play_sink_colorbalance_list_channels;
4991   iface->set_value = gst_play_sink_colorbalance_set_value;
4992   iface->get_value = gst_play_sink_colorbalance_get_value;
4993   iface->get_balance_type = gst_play_sink_colorbalance_get_balance_type;
4994 }
4995
4996 gboolean
4997 gst_play_sink_plugin_init (GstPlugin * plugin)
4998 {
4999   GST_DEBUG_CATEGORY_INIT (gst_play_sink_debug, "playsink", 0, "play bin");
5000   return gst_element_register (plugin, "playsink", GST_RANK_NONE,
5001       GST_TYPE_PLAY_SINK);
5002 }