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