Merge branch '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
2256   chain = playsink->audiochain;
2257
2258   chain->chain.raw = raw;
2259
2260   /* if the chain was active we don't do anything */
2261   if (GST_PLAY_CHAIN (chain)->activated == TRUE)
2262     return TRUE;
2263
2264   /* try to set the sink element to READY again */
2265   ret = gst_element_set_state (chain->sink, GST_STATE_READY);
2266   if (ret == GST_STATE_CHANGE_FAILURE)
2267     return FALSE;
2268
2269   /* find ts-offset element */
2270   gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
2271       gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
2272           G_TYPE_INT64));
2273
2274   /* check if the sink, or something within the sink, has the volume property.
2275    * If it does we don't need to add a volume element.  */
2276   elem =
2277       gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
2278       G_TYPE_DOUBLE);
2279   if (elem) {
2280     chain->volume = elem;
2281
2282     if (playsink->volume_changed) {
2283       GST_DEBUG_OBJECT (playsink, "the sink has a volume property, setting %f",
2284           playsink->volume);
2285       /* use the sink to control the volume */
2286       g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2287       playsink->volume_changed = FALSE;
2288     }
2289
2290     g_signal_connect (chain->volume, "notify::volume",
2291         G_CALLBACK (notify_volume_cb), playsink);
2292     /* if the sink also has a mute property we can use this as well. We'll only
2293      * use the mute property if there is a volume property. We can simulate the
2294      * mute with the volume otherwise. */
2295     chain->mute =
2296         gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
2297         G_TYPE_BOOLEAN);
2298     if (chain->mute) {
2299       GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
2300       g_signal_connect (chain->mute, "notify::mute",
2301           G_CALLBACK (notify_mute_cb), playsink);
2302     }
2303
2304     g_object_set (chain->conv, "use-volume", FALSE, NULL);
2305   } else {
2306     GstPlaySinkAudioConvert *conv =
2307         GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
2308
2309     /* no volume, we need to add a volume element when we can */
2310     g_object_set (chain->conv, "use-volume",
2311         ! !(playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME), NULL);
2312     GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
2313
2314     /* Disconnect signals */
2315     disconnect_chain (chain, playsink);
2316
2317     if (conv->volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
2318       chain->volume = conv->volume;
2319       chain->mute = chain->volume;
2320
2321       g_signal_connect (chain->volume, "notify::volume",
2322           G_CALLBACK (notify_volume_cb), playsink);
2323
2324       g_signal_connect (chain->mute, "notify::mute",
2325           G_CALLBACK (notify_mute_cb), playsink);
2326
2327       /* configure with the latest volume and mute */
2328       g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2329       g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
2330     }
2331
2332     GST_DEBUG_OBJECT (playsink, "reusing existing volume element");
2333   }
2334   return TRUE;
2335 }
2336
2337 /*
2338  *  +-------------------------------------------------------------------+
2339  *  | visbin                                                            |
2340  *  |      +----------+   +------------+   +----------+   +-------+     |
2341  *  |      | visqueue |   | audioconv  |   | audiores |   |  vis  |     |
2342  *  |   +-sink       src-sink + samp  src-sink       src-sink    src-+  |
2343  *  |   |  +----------+   +------------+   +----------+   +-------+  |  |
2344  * sink-+                                                            +-src
2345  *  +-------------------------------------------------------------------+
2346  *
2347  */
2348 static GstPlayVisChain *
2349 gen_vis_chain (GstPlaySink * playsink)
2350 {
2351   GstPlayVisChain *chain;
2352   GstBin *bin;
2353   gboolean res;
2354   GstPad *pad;
2355   GstElement *elem;
2356
2357   chain = g_new0 (GstPlayVisChain, 1);
2358   chain->chain.playsink = playsink;
2359
2360   GST_DEBUG_OBJECT (playsink, "making vis chain %p", chain);
2361
2362   chain->chain.bin = gst_bin_new ("visbin");
2363   bin = GST_BIN_CAST (chain->chain.bin);
2364   gst_object_ref_sink (bin);
2365
2366   /* we're queuing raw audio here, we can remove this queue when we can disable
2367    * async behaviour in the video sink. */
2368   chain->queue = gst_element_factory_make ("queue", "visqueue");
2369   if (chain->queue == NULL)
2370     goto no_queue;
2371   g_object_set (chain->queue, "silent", TRUE, NULL);
2372   gst_bin_add (bin, chain->queue);
2373
2374   chain->conv = gst_element_factory_make ("audioconvert", "aconv");
2375   if (chain->conv == NULL)
2376     goto no_audioconvert;
2377   gst_bin_add (bin, chain->conv);
2378
2379   chain->resample = gst_element_factory_make ("audioresample", "aresample");
2380   if (chain->resample == NULL)
2381     goto no_audioresample;
2382   gst_bin_add (bin, chain->resample);
2383
2384   /* this pad will be used for blocking the dataflow and switching the vis
2385    * plugin */
2386   chain->blockpad = gst_element_get_static_pad (chain->resample, "src");
2387
2388   if (playsink->visualisation) {
2389     GST_DEBUG_OBJECT (playsink, "trying configure vis");
2390     chain->vis = try_element (playsink, playsink->visualisation, FALSE);
2391   }
2392   if (chain->vis == NULL) {
2393     GST_DEBUG_OBJECT (playsink, "trying goom");
2394     elem = gst_element_factory_make ("goom", "vis");
2395     chain->vis = try_element (playsink, elem, TRUE);
2396   }
2397   if (chain->vis == NULL)
2398     goto no_goom;
2399
2400   gst_bin_add (bin, chain->vis);
2401
2402   res = gst_element_link_pads_full (chain->queue, "src", chain->conv, "sink",
2403       GST_PAD_LINK_CHECK_NOTHING);
2404   res &=
2405       gst_element_link_pads_full (chain->conv, "src", chain->resample, "sink",
2406       GST_PAD_LINK_CHECK_NOTHING);
2407   res &=
2408       gst_element_link_pads_full (chain->resample, "src", chain->vis, "sink",
2409       GST_PAD_LINK_CHECK_NOTHING);
2410   if (!res)
2411     goto link_failed;
2412
2413   chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
2414   chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
2415
2416   pad = gst_element_get_static_pad (chain->queue, "sink");
2417   chain->sinkpad = gst_ghost_pad_new ("sink", pad);
2418   gst_object_unref (pad);
2419   gst_element_add_pad (chain->chain.bin, chain->sinkpad);
2420
2421   chain->srcpad = gst_ghost_pad_new ("src", chain->vissrcpad);
2422   gst_element_add_pad (chain->chain.bin, chain->srcpad);
2423
2424   return chain;
2425
2426   /* ERRORS */
2427 no_queue:
2428   {
2429     post_missing_element_message (playsink, "queue");
2430     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2431         (_("Missing element '%s' - check your GStreamer installation."),
2432             "queue"), (NULL));
2433     free_chain ((GstPlayChain *) chain);
2434     return NULL;
2435   }
2436 no_audioconvert:
2437   {
2438     post_missing_element_message (playsink, "audioconvert");
2439     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2440         (_("Missing element '%s' - check your GStreamer installation."),
2441             "audioconvert"), ("possibly a liboil version mismatch?"));
2442     free_chain ((GstPlayChain *) chain);
2443     return NULL;
2444   }
2445 no_audioresample:
2446   {
2447     post_missing_element_message (playsink, "audioresample");
2448     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2449         (_("Missing element '%s' - check your GStreamer installation."),
2450             "audioresample"), (NULL));
2451     free_chain ((GstPlayChain *) chain);
2452     return NULL;
2453   }
2454 no_goom:
2455   {
2456     post_missing_element_message (playsink, "goom");
2457     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2458         (_("Missing element '%s' - check your GStreamer installation."),
2459             "goom"), (NULL));
2460     free_chain ((GstPlayChain *) chain);
2461     return NULL;
2462   }
2463 link_failed:
2464   {
2465     GST_ELEMENT_ERROR (playsink, CORE, PAD,
2466         (NULL), ("Failed to configure the visualisation element."));
2467     /* element made it to READY */
2468     gst_element_set_state (chain->vis, GST_STATE_NULL);
2469     free_chain ((GstPlayChain *) chain);
2470     return NULL;
2471   }
2472 }
2473
2474 /* this function is called when all the request pads are requested and when we
2475  * have to construct the final pipeline. Based on the flags we construct the
2476  * final output pipelines.
2477  */
2478 gboolean
2479 gst_play_sink_reconfigure (GstPlaySink * playsink)
2480 {
2481   GstPlayFlags flags;
2482   gboolean need_audio, need_video, need_deinterlace, need_vis, need_text;
2483
2484   GST_DEBUG_OBJECT (playsink, "reconfiguring");
2485
2486   /* assume we need nothing */
2487   need_audio = need_video = need_deinterlace = need_vis = need_text = FALSE;
2488
2489   GST_PLAY_SINK_LOCK (playsink);
2490   GST_OBJECT_LOCK (playsink);
2491   /* get flags, there are protected with the object lock */
2492   flags = playsink->flags;
2493   GST_OBJECT_UNLOCK (playsink);
2494
2495   /* figure out which components we need */
2496   if (flags & GST_PLAY_FLAG_TEXT && playsink->text_pad) {
2497     /* we have subtitles and we are requested to show it */
2498     need_text = TRUE;
2499   }
2500
2501   GST_OBJECT_LOCK (playsink);
2502   if (playsink->overlay_element)
2503     gst_object_unref (playsink->overlay_element);
2504   playsink->overlay_element = NULL;
2505
2506   if (playsink->colorbalance_element) {
2507     g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
2508         G_CALLBACK (colorbalance_value_changed_cb), playsink);
2509     gst_object_unref (playsink->colorbalance_element);
2510   }
2511   playsink->colorbalance_element = NULL;
2512   GST_OBJECT_UNLOCK (playsink);
2513
2514   if (((flags & GST_PLAY_FLAG_VIDEO)
2515           || (flags & GST_PLAY_FLAG_NATIVE_VIDEO)) && playsink->video_pad) {
2516     /* we have video and we are requested to show it */
2517     need_video = TRUE;
2518
2519     /* we only deinterlace if native video is not requested and
2520      * we have raw video */
2521     if ((flags & GST_PLAY_FLAG_DEINTERLACE)
2522         && !(flags & GST_PLAY_FLAG_NATIVE_VIDEO) && playsink->video_pad_raw)
2523       need_deinterlace = TRUE;
2524   }
2525
2526   if (playsink->audio_pad) {
2527     if ((flags & GST_PLAY_FLAG_AUDIO) || (flags & GST_PLAY_FLAG_NATIVE_AUDIO)) {
2528       need_audio = TRUE;
2529     }
2530     if (playsink->audio_pad_raw) {
2531       /* only can do vis with raw uncompressed audio */
2532       if (flags & GST_PLAY_FLAG_VIS && !need_video) {
2533         /* also add video when we add visualisation */
2534         need_video = TRUE;
2535         need_vis = TRUE;
2536       }
2537     }
2538   }
2539
2540   /* we have a text_pad and we need text rendering, in this case we need a
2541    * video_pad to combine the video with the text or visualizations */
2542   if (need_text && !need_video) {
2543     if (playsink->video_pad) {
2544       need_video = TRUE;
2545     } else if (need_audio) {
2546       GST_ELEMENT_WARNING (playsink, STREAM, FORMAT,
2547           (_("Can't play a text file without video or visualizations.")),
2548           ("Have text pad but no video pad or visualizations"));
2549       need_text = FALSE;
2550     } else {
2551       GST_ELEMENT_ERROR (playsink, STREAM, FORMAT,
2552           (_("Can't play a text file without video or visualizations.")),
2553           ("Have text pad but no video pad or visualizations"));
2554       GST_PLAY_SINK_UNLOCK (playsink);
2555       return FALSE;
2556     }
2557   }
2558
2559   GST_DEBUG_OBJECT (playsink, "audio:%d, video:%d, vis:%d, text:%d", need_audio,
2560       need_video, need_vis, need_text);
2561
2562   /* set up video pipeline */
2563   if (need_video) {
2564     gboolean raw, async;
2565
2566     /* we need a raw sink when we do vis or when we have a raw pad */
2567     raw = need_vis ? TRUE : playsink->video_pad_raw;
2568     /* we try to set the sink async=FALSE when we need vis, this way we can
2569      * avoid a queue in the audio chain. */
2570     async = !need_vis;
2571
2572     GST_DEBUG_OBJECT (playsink, "adding video, raw %d",
2573         playsink->video_pad_raw);
2574
2575     if (playsink->videochain) {
2576       /* try to reactivate the chain */
2577       if (!setup_video_chain (playsink, raw, async)) {
2578         if (playsink->video_sinkpad_stream_synchronizer) {
2579           gst_element_release_request_pad (GST_ELEMENT_CAST
2580               (playsink->stream_synchronizer),
2581               playsink->video_sinkpad_stream_synchronizer);
2582           gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2583           playsink->video_sinkpad_stream_synchronizer = NULL;
2584           gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2585           playsink->video_srcpad_stream_synchronizer = NULL;
2586         }
2587
2588         add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2589
2590         /* Remove the sink from the bin to keep its state
2591          * and unparent it to allow reuse */
2592         if (playsink->videochain->sink)
2593           gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
2594               playsink->videochain->sink);
2595
2596         activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2597         free_chain ((GstPlayChain *) playsink->videochain);
2598         playsink->videochain = NULL;
2599       }
2600     }
2601
2602     if (!playsink->videochain)
2603       playsink->videochain = gen_video_chain (playsink, raw, async);
2604     if (!playsink->videochain)
2605       goto no_chain;
2606
2607     if (!playsink->video_sinkpad_stream_synchronizer) {
2608       GValue item = { 0, };
2609       GstIterator *it;
2610
2611       playsink->video_sinkpad_stream_synchronizer =
2612           gst_element_get_request_pad (GST_ELEMENT_CAST
2613           (playsink->stream_synchronizer), "sink_%u");
2614       it = gst_pad_iterate_internal_links
2615           (playsink->video_sinkpad_stream_synchronizer);
2616       g_assert (it);
2617       gst_iterator_next (it, &item);
2618       playsink->video_srcpad_stream_synchronizer = g_value_dup_object (&item);
2619       g_value_unset (&item);
2620       g_assert (playsink->video_srcpad_stream_synchronizer);
2621       gst_iterator_free (it);
2622     }
2623
2624     if (playsink->video_pad)
2625       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad),
2626           playsink->video_sinkpad_stream_synchronizer);
2627
2628     if (need_deinterlace) {
2629       if (!playsink->videodeinterlacechain)
2630         playsink->videodeinterlacechain =
2631             gen_video_deinterlace_chain (playsink);
2632       if (!playsink->videodeinterlacechain)
2633         goto no_chain;
2634
2635       GST_DEBUG_OBJECT (playsink, "adding video deinterlace chain");
2636
2637       GST_DEBUG_OBJECT (playsink, "setting up deinterlacing chain");
2638
2639       add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
2640       activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
2641
2642       gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2643           playsink->videodeinterlacechain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2644     } else {
2645       if (playsink->videodeinterlacechain) {
2646         add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2647         activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
2648             FALSE);
2649       }
2650     }
2651
2652     GST_DEBUG_OBJECT (playsink, "adding video chain");
2653     add_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
2654     activate_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
2655     /* if we are not part of vis or subtitles, set the ghostpad target */
2656     if (!need_vis && !need_text && (!playsink->textchain
2657             || !playsink->text_pad)) {
2658       GST_DEBUG_OBJECT (playsink, "ghosting video sinkpad");
2659       if (need_deinterlace)
2660         gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
2661             playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2662       else
2663         gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2664             playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2665     }
2666   } else {
2667     GST_DEBUG_OBJECT (playsink, "no video needed");
2668     if (playsink->videochain) {
2669       GST_DEBUG_OBJECT (playsink, "removing video chain");
2670       if (playsink->vischain) {
2671         GstPad *srcpad;
2672
2673         GST_DEBUG_OBJECT (playsink, "unlinking vis chain");
2674
2675         /* also had visualisation, release the tee srcpad before we then
2676          * unlink the video from it */
2677         if (playsink->audio_tee_vissrc) {
2678           gst_element_release_request_pad (playsink->audio_tee,
2679               playsink->audio_tee_vissrc);
2680           gst_object_unref (playsink->audio_tee_vissrc);
2681           playsink->audio_tee_vissrc = NULL;
2682         }
2683         srcpad =
2684             gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2685         gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
2686       }
2687
2688       if (playsink->video_sinkpad_stream_synchronizer) {
2689         gst_element_release_request_pad (GST_ELEMENT_CAST
2690             (playsink->stream_synchronizer),
2691             playsink->video_sinkpad_stream_synchronizer);
2692         gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2693         playsink->video_sinkpad_stream_synchronizer = NULL;
2694         gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2695         playsink->video_srcpad_stream_synchronizer = NULL;
2696       }
2697
2698       add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2699       activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2700       if (playsink->videochain->ts_offset)
2701         gst_object_unref (playsink->videochain->ts_offset);
2702       playsink->videochain->ts_offset = NULL;
2703     }
2704
2705     if (playsink->videodeinterlacechain) {
2706       add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2707       activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2708     }
2709
2710     if (playsink->video_pad)
2711       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
2712   }
2713
2714   if (need_audio) {
2715     gboolean raw;
2716
2717     GST_DEBUG_OBJECT (playsink, "adding audio");
2718
2719     /* get a raw sink if we are asked for a raw pad */
2720     raw = playsink->audio_pad_raw;
2721
2722     if (playsink->audiochain) {
2723       /* try to reactivate the chain */
2724       if (!setup_audio_chain (playsink, raw)) {
2725         GST_DEBUG_OBJECT (playsink, "removing current audio chain");
2726         if (playsink->audio_tee_asrc) {
2727           gst_element_release_request_pad (playsink->audio_tee,
2728               playsink->audio_tee_asrc);
2729           gst_object_unref (playsink->audio_tee_asrc);
2730           playsink->audio_tee_asrc = NULL;
2731         }
2732
2733         if (playsink->audio_sinkpad_stream_synchronizer) {
2734           gst_element_release_request_pad (GST_ELEMENT_CAST
2735               (playsink->stream_synchronizer),
2736               playsink->audio_sinkpad_stream_synchronizer);
2737           gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
2738           playsink->audio_sinkpad_stream_synchronizer = NULL;
2739           gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
2740           playsink->audio_srcpad_stream_synchronizer = NULL;
2741         }
2742
2743         add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2744
2745         /* Remove the sink from the bin to keep its state
2746          * and unparent it to allow reuse */
2747         if (playsink->audiochain->sink)
2748           gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
2749               playsink->audiochain->sink);
2750
2751         activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2752         disconnect_chain (playsink->audiochain, playsink);
2753         playsink->audiochain->volume = NULL;
2754         playsink->audiochain->mute = NULL;
2755         if (playsink->audiochain->ts_offset)
2756           gst_object_unref (playsink->audiochain->ts_offset);
2757         playsink->audiochain->ts_offset = NULL;
2758         free_chain ((GstPlayChain *) playsink->audiochain);
2759         playsink->audiochain = NULL;
2760         playsink->volume_changed = playsink->mute_changed = FALSE;
2761       }
2762     }
2763
2764     if (!playsink->audiochain) {
2765       GST_DEBUG_OBJECT (playsink, "creating new audio chain");
2766       playsink->audiochain = gen_audio_chain (playsink, raw);
2767     }
2768
2769     if (!playsink->audio_sinkpad_stream_synchronizer) {
2770       GValue item = { 0, };
2771       GstIterator *it;
2772
2773       playsink->audio_sinkpad_stream_synchronizer =
2774           gst_element_get_request_pad (GST_ELEMENT_CAST
2775           (playsink->stream_synchronizer), "sink_%u");
2776       it = gst_pad_iterate_internal_links
2777           (playsink->audio_sinkpad_stream_synchronizer);
2778       g_assert (it);
2779       gst_iterator_next (it, &item);
2780       playsink->audio_srcpad_stream_synchronizer = g_value_dup_object (&item);
2781       g_value_unset (&item);
2782       g_assert (playsink->audio_srcpad_stream_synchronizer);
2783       gst_iterator_free (it);
2784     }
2785
2786     if (playsink->audiochain) {
2787       GST_DEBUG_OBJECT (playsink, "adding audio chain");
2788       if (playsink->audio_tee_asrc == NULL) {
2789         playsink->audio_tee_asrc =
2790             gst_element_get_request_pad (playsink->audio_tee, "src_%u");
2791       }
2792       add_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
2793       activate_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
2794       gst_pad_link_full (playsink->audio_tee_asrc,
2795           playsink->audio_sinkpad_stream_synchronizer,
2796           GST_PAD_LINK_CHECK_NOTHING);
2797       gst_pad_link_full (playsink->audio_srcpad_stream_synchronizer,
2798           playsink->audiochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2799     }
2800   } else {
2801     GST_DEBUG_OBJECT (playsink, "no audio needed");
2802     /* we have no audio or we are requested to not play audio */
2803     if (playsink->audiochain) {
2804       GST_DEBUG_OBJECT (playsink, "removing audio chain");
2805       /* release the audio pad */
2806       if (playsink->audio_tee_asrc) {
2807         gst_element_release_request_pad (playsink->audio_tee,
2808             playsink->audio_tee_asrc);
2809         gst_object_unref (playsink->audio_tee_asrc);
2810         playsink->audio_tee_asrc = NULL;
2811       }
2812
2813       if (playsink->audio_sinkpad_stream_synchronizer) {
2814         gst_element_release_request_pad (GST_ELEMENT_CAST
2815             (playsink->stream_synchronizer),
2816             playsink->audio_sinkpad_stream_synchronizer);
2817         gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
2818         playsink->audio_sinkpad_stream_synchronizer = NULL;
2819         gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
2820         playsink->audio_srcpad_stream_synchronizer = NULL;
2821       }
2822
2823       if (playsink->audiochain->sink_volume) {
2824         disconnect_chain (playsink->audiochain, playsink);
2825         playsink->audiochain->volume = NULL;
2826         playsink->audiochain->mute = NULL;
2827         if (playsink->audiochain->ts_offset)
2828           gst_object_unref (playsink->audiochain->ts_offset);
2829         playsink->audiochain->ts_offset = NULL;
2830       }
2831       add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2832       activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2833     }
2834   }
2835
2836   if (need_vis) {
2837     GstPad *srcpad;
2838
2839     if (!playsink->vischain)
2840       playsink->vischain = gen_vis_chain (playsink);
2841
2842     GST_DEBUG_OBJECT (playsink, "adding visualisation");
2843
2844     if (playsink->vischain) {
2845       GST_DEBUG_OBJECT (playsink, "setting up vis chain");
2846       srcpad =
2847           gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2848       add_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
2849       activate_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
2850       if (playsink->audio_tee_vissrc == NULL) {
2851         playsink->audio_tee_vissrc =
2852             gst_element_get_request_pad (playsink->audio_tee, "src_%u");
2853       }
2854       gst_pad_link_full (playsink->audio_tee_vissrc,
2855           playsink->vischain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2856       gst_pad_link_full (srcpad, playsink->video_sinkpad_stream_synchronizer,
2857           GST_PAD_LINK_CHECK_NOTHING);
2858       gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2859           playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2860       gst_object_unref (srcpad);
2861     }
2862   } else {
2863     GST_DEBUG_OBJECT (playsink, "no vis needed");
2864     if (playsink->vischain) {
2865       if (playsink->audio_tee_vissrc) {
2866         gst_element_release_request_pad (playsink->audio_tee,
2867             playsink->audio_tee_vissrc);
2868         gst_object_unref (playsink->audio_tee_vissrc);
2869         playsink->audio_tee_vissrc = NULL;
2870       }
2871       GST_DEBUG_OBJECT (playsink, "removing vis chain");
2872       add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
2873       activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
2874     }
2875   }
2876
2877   if (need_text) {
2878     GST_DEBUG_OBJECT (playsink, "adding text");
2879     if (!playsink->textchain) {
2880       GST_DEBUG_OBJECT (playsink, "creating text chain");
2881       playsink->textchain = gen_text_chain (playsink);
2882     }
2883     if (playsink->textchain) {
2884       GstIterator *it;
2885
2886       GST_DEBUG_OBJECT (playsink, "adding text chain");
2887       if (playsink->textchain->overlay)
2888         g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", FALSE,
2889             NULL);
2890       add_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
2891
2892       if (!playsink->text_sinkpad_stream_synchronizer) {
2893         GValue item = { 0, };
2894
2895         playsink->text_sinkpad_stream_synchronizer =
2896             gst_element_get_request_pad (GST_ELEMENT_CAST
2897             (playsink->stream_synchronizer), "sink_%u");
2898         it = gst_pad_iterate_internal_links
2899             (playsink->text_sinkpad_stream_synchronizer);
2900         g_assert (it);
2901         gst_iterator_next (it, &item);
2902         playsink->text_srcpad_stream_synchronizer = g_value_dup_object (&item);
2903         g_value_unset (&item);
2904         g_assert (playsink->text_srcpad_stream_synchronizer);
2905         gst_iterator_free (it);
2906
2907         gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad),
2908             playsink->text_sinkpad_stream_synchronizer);
2909         gst_pad_link_full (playsink->text_srcpad_stream_synchronizer,
2910             playsink->textchain->textsinkpad, GST_PAD_LINK_CHECK_NOTHING);
2911       }
2912
2913       if (need_vis) {
2914         GstPad *srcpad;
2915
2916         srcpad =
2917             gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2918         gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
2919         gst_pad_link_full (srcpad, playsink->textchain->videosinkpad,
2920             GST_PAD_LINK_CHECK_NOTHING);
2921         gst_object_unref (srcpad);
2922       } else {
2923         if (need_deinterlace)
2924           gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
2925               playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
2926         else
2927           gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2928               playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
2929       }
2930       gst_pad_link_full (playsink->textchain->srcpad,
2931           playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2932
2933       activate_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
2934     }
2935   } else {
2936     GST_DEBUG_OBJECT (playsink, "no text needed");
2937     /* we have no subtitles/text or we are requested to not show them */
2938
2939     if (playsink->text_sinkpad_stream_synchronizer) {
2940       gst_element_release_request_pad (GST_ELEMENT_CAST
2941           (playsink->stream_synchronizer),
2942           playsink->text_sinkpad_stream_synchronizer);
2943       gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
2944       playsink->text_sinkpad_stream_synchronizer = NULL;
2945       gst_object_unref (playsink->text_srcpad_stream_synchronizer);
2946       playsink->text_srcpad_stream_synchronizer = NULL;
2947     }
2948
2949     if (playsink->textchain) {
2950       if (playsink->text_pad == NULL) {
2951         /* no text pad, remove the chain entirely */
2952         GST_DEBUG_OBJECT (playsink, "removing text chain");
2953         add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
2954         activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
2955       } else {
2956         /* we have a chain and a textpad, turn the subtitles off */
2957         GST_DEBUG_OBJECT (playsink, "turning off the text");
2958         if (playsink->textchain->overlay)
2959           g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", TRUE,
2960               NULL);
2961       }
2962     }
2963     if (!need_video && playsink->video_pad) {
2964       if (playsink->video_sinkpad_stream_synchronizer) {
2965         gst_element_release_request_pad (GST_ELEMENT_CAST
2966             (playsink->stream_synchronizer),
2967             playsink->video_sinkpad_stream_synchronizer);
2968         gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2969         playsink->video_sinkpad_stream_synchronizer = NULL;
2970         gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2971         playsink->video_srcpad_stream_synchronizer = NULL;
2972       }
2973
2974       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
2975     }
2976
2977     if (playsink->text_pad && !playsink->textchain)
2978       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad), NULL);
2979   }
2980   update_av_offset (playsink);
2981   do_async_done (playsink);
2982   GST_PLAY_SINK_UNLOCK (playsink);
2983
2984   return TRUE;
2985
2986   /* ERRORS */
2987 no_chain:
2988   {
2989     /* gen_ chain already posted error */
2990     GST_DEBUG_OBJECT (playsink, "failed to setup chain");
2991     GST_PLAY_SINK_UNLOCK (playsink);
2992     return FALSE;
2993   }
2994 }
2995
2996 /**
2997  * gst_play_sink_set_flags:
2998  * @playsink: a #GstPlaySink
2999  * @flags: #GstPlayFlags
3000  *
3001  * Configure @flags on @playsink. The flags control the behaviour of @playsink
3002  * when constructing the sink pipelins.
3003  *
3004  * Returns: TRUE if the flags could be configured.
3005  */
3006 gboolean
3007 gst_play_sink_set_flags (GstPlaySink * playsink, GstPlayFlags flags)
3008 {
3009   g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), FALSE);
3010
3011   GST_OBJECT_LOCK (playsink);
3012   playsink->flags = flags;
3013   GST_OBJECT_UNLOCK (playsink);
3014
3015   return TRUE;
3016 }
3017
3018 /**
3019  * gst_play_sink_get_flags:
3020  * @playsink: a #GstPlaySink
3021  *
3022  * Get the flags of @playsink. That flags control the behaviour of the sink when
3023  * it constructs the sink pipelines.
3024  *
3025  * Returns: the currently configured #GstPlayFlags.
3026  */
3027 GstPlayFlags
3028 gst_play_sink_get_flags (GstPlaySink * playsink)
3029 {
3030   GstPlayFlags res;
3031
3032   g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), 0);
3033
3034   GST_OBJECT_LOCK (playsink);
3035   res = playsink->flags;
3036   GST_OBJECT_UNLOCK (playsink);
3037
3038   return res;
3039 }
3040
3041 void
3042 gst_play_sink_set_font_desc (GstPlaySink * playsink, const gchar * desc)
3043 {
3044   GstPlayTextChain *chain;
3045
3046   GST_PLAY_SINK_LOCK (playsink);
3047   chain = (GstPlayTextChain *) playsink->textchain;
3048   g_free (playsink->font_desc);
3049   playsink->font_desc = g_strdup (desc);
3050   if (chain && chain->overlay) {
3051     g_object_set (chain->overlay, "font-desc", desc, NULL);
3052   }
3053   GST_PLAY_SINK_UNLOCK (playsink);
3054 }
3055
3056 gchar *
3057 gst_play_sink_get_font_desc (GstPlaySink * playsink)
3058 {
3059   gchar *result = NULL;
3060   GstPlayTextChain *chain;
3061
3062   GST_PLAY_SINK_LOCK (playsink);
3063   chain = (GstPlayTextChain *) playsink->textchain;
3064   if (chain && chain->overlay) {
3065     g_object_get (chain->overlay, "font-desc", &result, NULL);
3066     playsink->font_desc = g_strdup (result);
3067   } else {
3068     result = g_strdup (playsink->font_desc);
3069   }
3070   GST_PLAY_SINK_UNLOCK (playsink);
3071
3072   return result;
3073 }
3074
3075 void
3076 gst_play_sink_set_subtitle_encoding (GstPlaySink * playsink,
3077     const gchar * encoding)
3078 {
3079   GstPlayTextChain *chain;
3080
3081   GST_PLAY_SINK_LOCK (playsink);
3082   chain = (GstPlayTextChain *) playsink->textchain;
3083   g_free (playsink->subtitle_encoding);
3084   playsink->subtitle_encoding = g_strdup (encoding);
3085   if (chain && chain->overlay) {
3086     g_object_set (chain->overlay, "subtitle-encoding", encoding, NULL);
3087   }
3088   GST_PLAY_SINK_UNLOCK (playsink);
3089 }
3090
3091 gchar *
3092 gst_play_sink_get_subtitle_encoding (GstPlaySink * playsink)
3093 {
3094   gchar *result = NULL;
3095   GstPlayTextChain *chain;
3096
3097   GST_PLAY_SINK_LOCK (playsink);
3098   chain = (GstPlayTextChain *) playsink->textchain;
3099   if (chain && chain->overlay) {
3100     g_object_get (chain->overlay, "subtitle-encoding", &result, NULL);
3101     playsink->subtitle_encoding = g_strdup (result);
3102   } else {
3103     result = g_strdup (playsink->subtitle_encoding);
3104   }
3105   GST_PLAY_SINK_UNLOCK (playsink);
3106
3107   return result;
3108 }
3109
3110 static void
3111 update_av_offset (GstPlaySink * playsink)
3112 {
3113   gint64 av_offset;
3114   GstPlayAudioChain *achain;
3115   GstPlayVideoChain *vchain;
3116
3117   av_offset = playsink->av_offset;
3118   achain = (GstPlayAudioChain *) playsink->audiochain;
3119   vchain = (GstPlayVideoChain *) playsink->videochain;
3120
3121   if (achain && vchain && achain->ts_offset && vchain->ts_offset) {
3122     g_object_set (achain->ts_offset, "ts-offset", MAX (0, -av_offset), NULL);
3123     g_object_set (vchain->ts_offset, "ts-offset", MAX (0, av_offset), NULL);
3124   } else {
3125     GST_LOG_OBJECT (playsink, "no ts_offset elements");
3126   }
3127 }
3128
3129 void
3130 gst_play_sink_set_av_offset (GstPlaySink * playsink, gint64 av_offset)
3131 {
3132   GST_PLAY_SINK_LOCK (playsink);
3133   playsink->av_offset = av_offset;
3134   update_av_offset (playsink);
3135   GST_PLAY_SINK_UNLOCK (playsink);
3136 }
3137
3138 gint64
3139 gst_play_sink_get_av_offset (GstPlaySink * playsink)
3140 {
3141   gint64 result;
3142
3143   GST_PLAY_SINK_LOCK (playsink);
3144   result = playsink->av_offset;
3145   GST_PLAY_SINK_UNLOCK (playsink);
3146
3147   return result;
3148 }
3149
3150 /**
3151  * gst_play_sink_get_last_sample:
3152  * @playsink: a #GstPlaySink
3153  *
3154  * Get the last displayed sample from @playsink. This sample is in the native
3155  * format of the sink element, the caps in the result sample contain the format
3156  * of the frame data.
3157  *
3158  * Returns: a #GstSample with the frame data or %NULL when no video frame is
3159  * available.
3160  */
3161 GstSample *
3162 gst_play_sink_get_last_sample (GstPlaySink * playsink)
3163 {
3164   GstSample *result = NULL;
3165   GstPlayVideoChain *chain;
3166
3167   GST_PLAY_SINK_LOCK (playsink);
3168   GST_DEBUG_OBJECT (playsink, "taking last sample");
3169   /* get the video chain if we can */
3170   if ((chain = (GstPlayVideoChain *) playsink->videochain)) {
3171     GST_DEBUG_OBJECT (playsink, "found video chain");
3172     /* see if the chain is active */
3173     if (chain->chain.activated && chain->sink) {
3174       GstElement *elem;
3175
3176       GST_DEBUG_OBJECT (playsink, "video chain active and has a sink");
3177
3178       /* find and get the last-buffer property now */
3179       if ((elem =
3180               gst_play_sink_find_property (playsink, chain->sink,
3181                   "last-sample", GST_TYPE_SAMPLE))) {
3182         GST_DEBUG_OBJECT (playsink, "getting last-sample property");
3183         g_object_get (elem, "last-sample", &result, NULL);
3184         gst_object_unref (elem);
3185       }
3186     }
3187   }
3188   GST_PLAY_SINK_UNLOCK (playsink);
3189
3190   return result;
3191 }
3192
3193 /**
3194  * gst_play_sink_convert_sample:
3195  * @playsink: a #GstPlaySink
3196  * @caps: a #GstCaps
3197  *
3198  * Get the last displayed frame from @playsink. If caps is %NULL, the video will
3199  * be in the native format of the sink element and the caps on the buffer
3200  * describe the format of the frame. If @caps is not %NULL, the video
3201  * frame will be converted to the format of the caps.
3202  *
3203  * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
3204  * available or when the conversion failed.
3205  */
3206 GstSample *
3207 gst_play_sink_convert_sample (GstPlaySink * playsink, GstCaps * caps)
3208 {
3209   GstSample *result;
3210   GError *err = NULL;
3211
3212   result = gst_play_sink_get_last_sample (playsink);
3213   if (result != NULL && caps != NULL) {
3214     GstSample *temp;
3215
3216     temp = gst_video_convert_sample (result, caps, 25 * GST_SECOND, &err);
3217     if (temp == NULL && err)
3218       goto error;
3219
3220     gst_sample_unref (result);
3221     result = temp;
3222   }
3223   return result;
3224
3225   /* ERRORS */
3226 error:
3227   {
3228     /* I'm really uncertain whether we should make playsink post an error
3229      * on the bus or not. It's not like it's a critical issue regarding
3230      * playsink behaviour. */
3231     GST_ERROR ("Error converting frame: %s", err->message);
3232     gst_sample_unref (result);
3233     g_error_free (err);
3234     return NULL;
3235   }
3236 }
3237
3238 static gboolean
3239 is_raw_structure (GstStructure * s)
3240 {
3241   const gchar *name;
3242
3243   name = gst_structure_get_name (s);
3244
3245   if (g_str_equal (name, "video/x-raw") || g_str_equal (name, "audio/x-raw"))
3246     return TRUE;
3247   return FALSE;
3248 }
3249
3250 static gboolean
3251 is_raw_pad (GstPad * pad)
3252 {
3253   GstPad *peer = gst_pad_get_peer (pad);
3254   GstCaps *caps;
3255   gboolean raw = TRUE;
3256
3257   if (!peer)
3258     return raw;
3259
3260   caps = gst_pad_get_current_caps (peer);
3261   if (!caps) {
3262     guint i, n;
3263
3264     caps = gst_pad_query_caps (peer, NULL);
3265
3266     n = gst_caps_get_size (caps);
3267     for (i = 0; i < n; i++) {
3268       gboolean r = is_raw_structure (gst_caps_get_structure (caps, i));
3269
3270       if (i == 0) {
3271         raw = r;
3272       } else if (raw != r) {
3273         GST_ERROR_OBJECT (pad,
3274             "Caps contains raw and non-raw structures: %" GST_PTR_FORMAT, caps);
3275         raw = FALSE;
3276         break;
3277       }
3278     }
3279   } else {
3280     raw = is_raw_structure (gst_caps_get_structure (caps, 0));
3281   }
3282   gst_caps_unref (caps);
3283   gst_object_unref (peer);
3284
3285   return raw;
3286 }
3287
3288 static GstPadProbeReturn
3289 sinkpad_blocked_cb (GstPad * blockedpad, GstPadProbeInfo * info,
3290     gpointer user_data);
3291
3292 static void
3293 video_set_blocked (GstPlaySink * playsink, gboolean blocked)
3294 {
3295   if (playsink->video_pad) {
3296     GstPad *opad =
3297         GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3298             (playsink->video_pad)));
3299     if (blocked && playsink->video_block_id == 0) {
3300       playsink->video_block_id =
3301           gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3302           sinkpad_blocked_cb, gst_object_ref (playsink),
3303           (GDestroyNotify) gst_object_unref);
3304     } else if (!blocked && playsink->video_block_id) {
3305       gst_pad_remove_probe (opad, playsink->video_block_id);
3306       PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO_RAW);
3307       PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO);
3308       playsink->video_block_id = 0;
3309       playsink->video_pad_blocked = FALSE;
3310     }
3311     gst_object_unref (opad);
3312   }
3313 }
3314
3315 static void
3316 audio_set_blocked (GstPlaySink * playsink, gboolean blocked)
3317 {
3318   if (playsink->audio_pad) {
3319     GstPad *opad =
3320         GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3321             (playsink->audio_pad)));
3322     if (blocked && playsink->audio_block_id == 0) {
3323       playsink->audio_block_id =
3324           gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3325           sinkpad_blocked_cb, gst_object_ref (playsink),
3326           (GDestroyNotify) gst_object_unref);
3327     } else if (!blocked && playsink->audio_block_id) {
3328       gst_pad_remove_probe (opad, playsink->audio_block_id);
3329       PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO_RAW);
3330       PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO);
3331       playsink->audio_block_id = 0;
3332       playsink->audio_pad_blocked = FALSE;
3333     }
3334     gst_object_unref (opad);
3335   }
3336 }
3337
3338 static void
3339 text_set_blocked (GstPlaySink * playsink, gboolean blocked)
3340 {
3341   if (playsink->text_pad) {
3342     GstPad *opad =
3343         GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3344             (playsink->text_pad)));
3345     if (blocked && playsink->text_block_id == 0) {
3346       playsink->text_block_id =
3347           gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3348           sinkpad_blocked_cb, gst_object_ref (playsink),
3349           (GDestroyNotify) gst_object_unref);
3350     } else if (!blocked && playsink->text_block_id) {
3351       gst_pad_remove_probe (opad, playsink->text_block_id);
3352       PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_TEXT);
3353       playsink->text_block_id = 0;
3354       playsink->text_pad_blocked = FALSE;
3355     }
3356     gst_object_unref (opad);
3357   }
3358 }
3359
3360 static GstPadProbeReturn
3361 sinkpad_blocked_cb (GstPad * blockedpad, GstPadProbeInfo * info,
3362     gpointer user_data)
3363 {
3364   GstPlaySink *playsink = (GstPlaySink *) user_data;
3365   GstPad *pad;
3366
3367   GST_PLAY_SINK_LOCK (playsink);
3368
3369   pad = GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (blockedpad)));
3370   if (pad == playsink->video_pad) {
3371     playsink->video_pad_blocked = TRUE;
3372     GST_DEBUG_OBJECT (pad, "Video pad blocked");
3373   } else if (pad == playsink->audio_pad) {
3374     playsink->audio_pad_blocked = TRUE;
3375     GST_DEBUG_OBJECT (pad, "Audio pad blocked");
3376   } else if (pad == playsink->text_pad) {
3377     playsink->text_pad_blocked = TRUE;
3378     GST_DEBUG_OBJECT (pad, "Text pad blocked");
3379   }
3380
3381   /* We reconfigure when for ALL streams:
3382    * * there isn't a pad
3383    * * OR the pad is blocked
3384    * * OR there are no pending blocks on that pad
3385    */
3386
3387   if ((!playsink->video_pad || playsink->video_pad_blocked
3388           || !PENDING_VIDEO_BLOCK (playsink)) && (!playsink->audio_pad
3389           || playsink->audio_pad_blocked || !PENDING_AUDIO_BLOCK (playsink))
3390       && (!playsink->text_pad || playsink->text_pad_blocked
3391           || !PENDING_TEXT_BLOCK (playsink))) {
3392     GST_DEBUG_OBJECT (playsink, "All pads blocked -- reconfiguring");
3393
3394     if (playsink->video_pad) {
3395       playsink->video_pad_raw = is_raw_pad (playsink->video_pad);
3396       GST_DEBUG_OBJECT (playsink, "Video pad is raw: %d",
3397           playsink->video_pad_raw);
3398     }
3399
3400     if (playsink->audio_pad) {
3401       playsink->audio_pad_raw = is_raw_pad (playsink->audio_pad);
3402       GST_DEBUG_OBJECT (playsink, "Audio pad is raw: %d",
3403           playsink->audio_pad_raw);
3404     }
3405
3406     gst_play_sink_reconfigure (playsink);
3407
3408     video_set_blocked (playsink, FALSE);
3409     audio_set_blocked (playsink, FALSE);
3410     text_set_blocked (playsink, FALSE);
3411   }
3412
3413   gst_object_unref (pad);
3414
3415   GST_PLAY_SINK_UNLOCK (playsink);
3416
3417   return GST_PAD_PROBE_OK;
3418 }
3419
3420 static void
3421 caps_notify_cb (GstPad * pad, GParamSpec * unused, GstPlaySink * playsink)
3422 {
3423   gboolean reconfigure = FALSE;
3424   GstCaps *caps;
3425   gboolean raw;
3426
3427   g_object_get (pad, "caps", &caps, NULL);
3428   if (!caps)
3429     return;
3430
3431   if (pad == playsink->audio_pad) {
3432     raw = is_raw_pad (pad);
3433     reconfigure = (! !playsink->audio_pad_raw != ! !raw)
3434         && playsink->audiochain;
3435     GST_DEBUG_OBJECT (pad,
3436         "Audio caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3437         reconfigure, caps);
3438   } else if (pad == playsink->video_pad) {
3439     raw = is_raw_pad (pad);
3440     reconfigure = (! !playsink->video_pad_raw != ! !raw)
3441         && playsink->videochain;
3442     GST_DEBUG_OBJECT (pad,
3443         "Video caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3444         reconfigure, caps);
3445   }
3446
3447   gst_caps_unref (caps);
3448
3449   if (reconfigure) {
3450     GST_PLAY_SINK_LOCK (playsink);
3451     video_set_blocked (playsink, TRUE);
3452     audio_set_blocked (playsink, TRUE);
3453     text_set_blocked (playsink, TRUE);
3454     GST_PLAY_SINK_UNLOCK (playsink);
3455   }
3456 }
3457
3458 /**
3459  * gst_play_sink_request_pad
3460  * @playsink: a #GstPlaySink
3461  * @type: a #GstPlaySinkType
3462  *
3463  * Create or return a pad of @type.
3464  *
3465  * Returns: a #GstPad of @type or %NULL when the pad could not be created.
3466  */
3467 GstPad *
3468 gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type)
3469 {
3470   GstPad *res = NULL;
3471   gboolean created = FALSE;
3472   gboolean activate = TRUE;
3473   const gchar *pad_name = NULL;
3474   gulong *block_id = NULL;
3475
3476   GST_DEBUG_OBJECT (playsink, "request pad type %d", type);
3477
3478   GST_PLAY_SINK_LOCK (playsink);
3479   switch (type) {
3480     case GST_PLAY_SINK_TYPE_AUDIO_RAW:
3481     case GST_PLAY_SINK_TYPE_AUDIO:
3482       pad_name = "audio_sink";
3483       if (!playsink->audio_tee) {
3484         GST_LOG_OBJECT (playsink, "creating tee");
3485         /* create tee when needed. This element will feed the audio sink chain
3486          * and the vis chain. */
3487         playsink->audio_tee = gst_element_factory_make ("tee", "audiotee");
3488         if (playsink->audio_tee == NULL) {
3489           post_missing_element_message (playsink, "tee");
3490           GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
3491               (_("Missing element '%s' - check your GStreamer installation."),
3492                   "tee"), (NULL));
3493           res = NULL;
3494           break;
3495         } else {
3496           playsink->audio_tee_sink =
3497               gst_element_get_static_pad (playsink->audio_tee, "sink");
3498           gst_bin_add (GST_BIN_CAST (playsink), playsink->audio_tee);
3499           gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
3500         }
3501       } else {
3502         gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
3503       }
3504       if (!playsink->audio_pad) {
3505         GST_LOG_OBJECT (playsink, "ghosting tee sinkpad");
3506         playsink->audio_pad =
3507             gst_ghost_pad_new (pad_name, playsink->audio_tee_sink);
3508         g_signal_connect (G_OBJECT (playsink->audio_pad), "notify::caps",
3509             G_CALLBACK (caps_notify_cb), playsink);
3510         created = TRUE;
3511       }
3512       playsink->audio_pad_raw = FALSE;
3513       res = playsink->audio_pad;
3514       block_id = &playsink->audio_block_id;
3515       break;
3516     case GST_PLAY_SINK_TYPE_VIDEO_RAW:
3517     case GST_PLAY_SINK_TYPE_VIDEO:
3518       pad_name = "video_sink";
3519       if (!playsink->video_pad) {
3520         GST_LOG_OBJECT (playsink, "ghosting videosink");
3521         playsink->video_pad =
3522             gst_ghost_pad_new_no_target (pad_name, GST_PAD_SINK);
3523         g_signal_connect (G_OBJECT (playsink->video_pad), "notify::caps",
3524             G_CALLBACK (caps_notify_cb), playsink);
3525         created = TRUE;
3526       }
3527       playsink->video_pad_raw = FALSE;
3528       res = playsink->video_pad;
3529       block_id = &playsink->video_block_id;
3530       break;
3531     case GST_PLAY_SINK_TYPE_TEXT:
3532       GST_LOG_OBJECT (playsink, "ghosting text");
3533       if (!playsink->text_pad) {
3534         playsink->text_pad =
3535             gst_ghost_pad_new_no_target ("text_sink", GST_PAD_SINK);
3536         created = TRUE;
3537       }
3538       res = playsink->text_pad;
3539       block_id = &playsink->text_block_id;
3540       break;
3541     case GST_PLAY_SINK_TYPE_FLUSHING:
3542     {
3543       gchar *padname;
3544
3545       /* we need a unique padname for the flushing pad. */
3546       padname = g_strdup_printf ("flushing_%u", playsink->count);
3547       res = gst_ghost_pad_new_no_target (padname, GST_PAD_SINK);
3548       g_free (padname);
3549       playsink->count++;
3550       activate = FALSE;
3551       created = TRUE;
3552       break;
3553     }
3554     default:
3555       res = NULL;
3556       break;
3557   }
3558   GST_PLAY_SINK_UNLOCK (playsink);
3559
3560   if (created && res) {
3561     /* we have to add the pad when it's active or we get an error when the
3562      * element is 'running' */
3563     gst_pad_set_active (res, TRUE);
3564     gst_element_add_pad (GST_ELEMENT_CAST (playsink), res);
3565     if (block_id && *block_id == 0) {
3566       GstPad *blockpad =
3567           GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (res)));
3568
3569       *block_id =
3570           gst_pad_add_probe (blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3571           sinkpad_blocked_cb, gst_object_ref (playsink),
3572           (GDestroyNotify) gst_object_unref);
3573       PENDING_FLAG_SET (playsink, type);
3574       gst_object_unref (blockpad);
3575     }
3576     if (!activate)
3577       gst_pad_set_active (res, activate);
3578   }
3579
3580   return res;
3581 }
3582
3583 static GstPad *
3584 gst_play_sink_request_new_pad (GstElement * element, GstPadTemplate * templ,
3585     const gchar * name, const GstCaps * caps)
3586 {
3587   GstPlaySink *psink;
3588   GstPad *pad;
3589   GstPlaySinkType type;
3590   const gchar *tplname;
3591
3592   g_return_val_if_fail (templ != NULL, NULL);
3593
3594   GST_DEBUG_OBJECT (element, "name:%s", name);
3595
3596   psink = GST_PLAY_SINK (element);
3597   tplname = GST_PAD_TEMPLATE_NAME_TEMPLATE (templ);
3598
3599   /* Figure out the GstPlaySinkType based on the template */
3600   if (!strcmp (tplname, "audio_sink"))
3601     type = GST_PLAY_SINK_TYPE_AUDIO;
3602   else if (!strcmp (tplname, "audio_raw_sink"))
3603     type = GST_PLAY_SINK_TYPE_AUDIO_RAW;
3604   else if (!strcmp (tplname, "video_sink"))
3605     type = GST_PLAY_SINK_TYPE_VIDEO;
3606   else if (!strcmp (tplname, "video_raw_sink"))
3607     type = GST_PLAY_SINK_TYPE_VIDEO_RAW;
3608   else if (!strcmp (tplname, "text_sink"))
3609     type = GST_PLAY_SINK_TYPE_TEXT;
3610   else
3611     goto unknown_template;
3612
3613   pad = gst_play_sink_request_pad (psink, type);
3614   return pad;
3615
3616 unknown_template:
3617   GST_WARNING_OBJECT (element, "Unknown pad template");
3618   return NULL;
3619 }
3620
3621 void
3622 gst_play_sink_release_pad (GstPlaySink * playsink, GstPad * pad)
3623 {
3624   GstPad **res = NULL;
3625   gboolean untarget = TRUE;
3626
3627   GST_DEBUG_OBJECT (playsink, "release pad %" GST_PTR_FORMAT, pad);
3628
3629   GST_PLAY_SINK_LOCK (playsink);
3630   if (pad == playsink->video_pad) {
3631     res = &playsink->video_pad;
3632     g_signal_handlers_disconnect_by_func (playsink->video_pad, caps_notify_cb,
3633         playsink);
3634   } else if (pad == playsink->audio_pad) {
3635     res = &playsink->audio_pad;
3636     g_signal_handlers_disconnect_by_func (playsink->audio_pad, caps_notify_cb,
3637         playsink);
3638   } else if (pad == playsink->text_pad) {
3639     res = &playsink->text_pad;
3640   } else {
3641     /* try to release the given pad anyway, these could be the FLUSHING pads. */
3642     res = &pad;
3643     untarget = FALSE;
3644   }
3645   GST_PLAY_SINK_UNLOCK (playsink);
3646
3647   if (*res) {
3648     GST_DEBUG_OBJECT (playsink, "deactivate pad %" GST_PTR_FORMAT, *res);
3649     gst_pad_set_active (*res, FALSE);
3650     if (untarget) {
3651       GST_DEBUG_OBJECT (playsink, "untargeting pad %" GST_PTR_FORMAT, *res);
3652       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (*res), NULL);
3653     }
3654     GST_DEBUG_OBJECT (playsink, "remove pad %" GST_PTR_FORMAT, *res);
3655     gst_element_remove_pad (GST_ELEMENT_CAST (playsink), *res);
3656     *res = NULL;
3657   }
3658 }
3659
3660 static void
3661 gst_play_sink_release_request_pad (GstElement * element, GstPad * pad)
3662 {
3663   GstPlaySink *psink = GST_PLAY_SINK (element);
3664
3665   gst_play_sink_release_pad (psink, pad);
3666 }
3667
3668 static void
3669 gst_play_sink_handle_message (GstBin * bin, GstMessage * message)
3670 {
3671   GstPlaySink *playsink;
3672
3673   playsink = GST_PLAY_SINK_CAST (bin);
3674
3675   switch (GST_MESSAGE_TYPE (message)) {
3676     case GST_MESSAGE_STEP_DONE:
3677     {
3678       GstFormat format;
3679       guint64 amount;
3680       gdouble rate;
3681       gboolean flush, intermediate, eos;
3682       guint64 duration;
3683
3684       GST_INFO_OBJECT (playsink, "Handling step-done message");
3685       gst_message_parse_step_done (message, &format, &amount, &rate, &flush,
3686           &intermediate, &duration, &eos);
3687
3688       if (format == GST_FORMAT_BUFFERS) {
3689         /* for the buffer format, we align the other streams */
3690         if (playsink->audiochain) {
3691           GstEvent *event;
3692
3693           event =
3694               gst_event_new_step (GST_FORMAT_TIME, duration, rate, flush,
3695               intermediate);
3696
3697           if (!gst_element_send_event (playsink->audiochain->chain.bin, event)) {
3698             GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
3699           }
3700         }
3701       }
3702       GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
3703       break;
3704     }
3705     case GST_MESSAGE_ELEMENT:{
3706       if (gst_is_video_overlay_prepare_window_handle_message (message)) {
3707         GstVideoOverlay *overlay;
3708
3709         GST_OBJECT_LOCK (playsink);
3710         if (playsink->overlay_element
3711             && GST_OBJECT_CAST (playsink->overlay_element) !=
3712             GST_MESSAGE_SRC (message)) {
3713           gst_object_unref (playsink->overlay_element);
3714           playsink->overlay_element = NULL;
3715         }
3716
3717         if (!playsink->overlay_element)
3718           playsink->overlay_element =
3719               GST_VIDEO_OVERLAY (gst_object_ref (GST_MESSAGE_SRC (message)));
3720         overlay =
3721             GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
3722         GST_OBJECT_UNLOCK (playsink);
3723
3724         GST_DEBUG_OBJECT (playsink, "Got prepare-xwindow-id message");
3725
3726         if (playsink->overlay_handle_set)
3727           gst_video_overlay_set_window_handle (playsink->overlay_element,
3728               playsink->overlay_handle);
3729         if (playsink->overlay_handle_events_set)
3730           gst_video_overlay_handle_events (playsink->overlay_element,
3731               playsink->overlay_handle_events);
3732         if (playsink->overlay_render_rectangle_set)
3733           gst_video_overlay_set_render_rectangle (playsink->overlay_element,
3734               playsink->overlay_x, playsink->overlay_y,
3735               playsink->overlay_width, playsink->overlay_height);
3736
3737         gst_object_unref (overlay);
3738         gst_message_unref (message);
3739         gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (playsink));
3740       }
3741       break;
3742     }
3743     default:
3744       GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
3745       break;
3746   }
3747 }
3748
3749 /* Send an event to our sinks until one of them works; don't then send to the
3750  * remaining sinks (unlike GstBin)
3751  * Special case: If a text sink is set we need to send the event
3752  * to them in case it's source is different from the a/v stream's source.
3753  */
3754 static gboolean
3755 gst_play_sink_send_event_to_sink (GstPlaySink * playsink, GstEvent * event)
3756 {
3757   gboolean res = TRUE;
3758   if (playsink->textchain && playsink->textchain->sink) {
3759     gst_event_ref (event);
3760     if ((res = gst_element_send_event (playsink->textchain->chain.bin, event))) {
3761       GST_DEBUG_OBJECT (playsink, "Sent event successfully to text sink");
3762     } else {
3763       GST_DEBUG_OBJECT (playsink, "Event failed when sent to text sink");
3764     }
3765   }
3766
3767   if (playsink->videochain) {
3768     gst_event_ref (event);
3769     if ((res = gst_element_send_event (playsink->videochain->chain.bin, event))) {
3770       GST_DEBUG_OBJECT (playsink, "Sent event successfully to video sink");
3771       goto done;
3772     }
3773     GST_DEBUG_OBJECT (playsink, "Event failed when sent to video sink");
3774   }
3775   if (playsink->audiochain) {
3776     gst_event_ref (event);
3777     if ((res = gst_element_send_event (playsink->audiochain->chain.bin, event))) {
3778       GST_DEBUG_OBJECT (playsink, "Sent event successfully to audio sink");
3779       goto done;
3780     }
3781     GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
3782   }
3783
3784 done:
3785   gst_event_unref (event);
3786   return res;
3787 }
3788
3789 /* We only want to send the event to a single sink (overriding GstBin's
3790  * behaviour), but we want to keep GstPipeline's behaviour - wrapping seek
3791  * events appropriately. So, this is a messy duplication of code. */
3792 static gboolean
3793 gst_play_sink_send_event (GstElement * element, GstEvent * event)
3794 {
3795   gboolean res = FALSE;
3796   GstEventType event_type = GST_EVENT_TYPE (event);
3797   GstPlaySink *playsink;
3798   playsink = GST_PLAY_SINK_CAST (element);
3799   switch (event_type) {
3800     case GST_EVENT_SEEK:
3801       GST_DEBUG_OBJECT (element, "Sending event to a sink");
3802       res = gst_play_sink_send_event_to_sink (playsink, event);
3803       break;
3804     case GST_EVENT_STEP:
3805     {
3806       GstFormat format;
3807       guint64 amount;
3808       gdouble rate;
3809       gboolean flush, intermediate;
3810       gst_event_parse_step (event, &format, &amount, &rate, &flush,
3811           &intermediate);
3812       if (format == GST_FORMAT_BUFFERS) {
3813         /* for buffers, we will try to step video frames, for other formats we
3814          * send the step to all sinks */
3815         res = gst_play_sink_send_event_to_sink (playsink, event);
3816       } else {
3817         res =
3818             GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
3819             event);
3820       }
3821       break;
3822     }
3823     default:
3824       res =
3825           GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
3826           event);
3827       break;
3828   }
3829   return res;
3830 }
3831
3832 static GstStateChangeReturn
3833 gst_play_sink_change_state (GstElement * element, GstStateChange transition)
3834 {
3835   GstStateChangeReturn ret;
3836   GstStateChangeReturn bret;
3837   GstPlaySink *playsink;
3838   playsink = GST_PLAY_SINK (element);
3839   switch (transition) {
3840     case GST_STATE_CHANGE_READY_TO_PAUSED:
3841       playsink->need_async_start = TRUE;
3842       /* we want to go async to PAUSED until we managed to configure and add the
3843        * sinks */
3844       do_async_start (playsink);
3845       ret = GST_STATE_CHANGE_ASYNC;
3846       break;
3847     case GST_STATE_CHANGE_PAUSED_TO_READY:
3848       /* unblock all pads here */
3849       GST_PLAY_SINK_LOCK (playsink);
3850       video_set_blocked (playsink, FALSE);
3851       audio_set_blocked (playsink, FALSE);
3852       text_set_blocked (playsink, FALSE);
3853       GST_PLAY_SINK_UNLOCK (playsink);
3854       /* fall through */
3855     case GST_STATE_CHANGE_READY_TO_NULL:
3856       if (playsink->audiochain && playsink->audiochain->sink_volume) {
3857         /* remove our links to the mute and volume elements when they were
3858          * provided by a sink */
3859         disconnect_chain (playsink->audiochain, playsink);
3860         playsink->audiochain->volume = NULL;
3861         playsink->audiochain->mute = NULL;
3862       }
3863
3864       if (playsink->audiochain && playsink->audiochain->ts_offset) {
3865         gst_object_unref (playsink->audiochain->ts_offset);
3866         playsink->audiochain->ts_offset = NULL;
3867       }
3868
3869       if (playsink->videochain && playsink->videochain->ts_offset) {
3870         gst_object_unref (playsink->videochain->ts_offset);
3871         playsink->videochain->ts_offset = NULL;
3872       }
3873
3874       GST_OBJECT_LOCK (playsink);
3875       if (playsink->overlay_element)
3876         gst_object_unref (playsink->overlay_element);
3877       playsink->overlay_element = NULL;
3878
3879       if (playsink->colorbalance_element) {
3880         g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
3881             G_CALLBACK (colorbalance_value_changed_cb), playsink);
3882         gst_object_unref (playsink->colorbalance_element);
3883       }
3884       playsink->colorbalance_element = NULL;
3885       GST_OBJECT_UNLOCK (playsink);
3886
3887       ret = GST_STATE_CHANGE_SUCCESS;
3888       break;
3889     default:
3890       /* all other state changes return SUCCESS by default, this value can be
3891        * overridden by the result of the children */
3892       ret = GST_STATE_CHANGE_SUCCESS;
3893       break;
3894   }
3895
3896   /* do the state change of the children */
3897   bret =
3898       GST_ELEMENT_CLASS (gst_play_sink_parent_class)->change_state (element,
3899       transition);
3900   /* now look at the result of our children and adjust the return value */
3901   switch (bret) {
3902     case GST_STATE_CHANGE_FAILURE:
3903       /* failure, we stop */
3904       goto activate_failed;
3905     case GST_STATE_CHANGE_NO_PREROLL:
3906       /* some child returned NO_PREROLL. This is strange but we never know. We
3907        * commit our async state change (if any) and return the NO_PREROLL */
3908       do_async_done (playsink);
3909       ret = bret;
3910       break;
3911     case GST_STATE_CHANGE_ASYNC:
3912       /* some child was async, return this */
3913       ret = bret;
3914       break;
3915     default:
3916       /* return our previously configured return value */
3917       break;
3918   }
3919
3920   switch (transition) {
3921     case GST_STATE_CHANGE_READY_TO_PAUSED:
3922       break;
3923     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
3924       /* FIXME Release audio device when we implement that */
3925       playsink->need_async_start = TRUE;
3926       break;
3927     case GST_STATE_CHANGE_PAUSED_TO_READY:{
3928       if (playsink->video_sinkpad_stream_synchronizer) {
3929         gst_element_release_request_pad (GST_ELEMENT_CAST
3930             (playsink->stream_synchronizer),
3931             playsink->video_sinkpad_stream_synchronizer);
3932         gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3933         playsink->video_sinkpad_stream_synchronizer = NULL;
3934         gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3935         playsink->video_srcpad_stream_synchronizer = NULL;
3936       }
3937       if (playsink->audio_sinkpad_stream_synchronizer) {
3938         gst_element_release_request_pad (GST_ELEMENT_CAST
3939             (playsink->stream_synchronizer),
3940             playsink->audio_sinkpad_stream_synchronizer);
3941         gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
3942         playsink->audio_sinkpad_stream_synchronizer = NULL;
3943         gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
3944         playsink->audio_srcpad_stream_synchronizer = NULL;
3945       }
3946       if (playsink->text_sinkpad_stream_synchronizer) {
3947         gst_element_release_request_pad (GST_ELEMENT_CAST
3948             (playsink->stream_synchronizer),
3949             playsink->text_sinkpad_stream_synchronizer);
3950         gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
3951         playsink->text_sinkpad_stream_synchronizer = NULL;
3952         gst_object_unref (playsink->text_srcpad_stream_synchronizer);
3953         playsink->text_srcpad_stream_synchronizer = NULL;
3954       }
3955     }
3956       /* fall through */
3957     case GST_STATE_CHANGE_READY_TO_NULL:
3958       /* remove sinks we added */
3959       if (playsink->videodeinterlacechain) {
3960         activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
3961             FALSE);
3962         add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3963       }
3964       if (playsink->videochain) {
3965         activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3966         add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3967       }
3968       if (playsink->audiochain) {
3969         activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3970         add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3971       }
3972       if (playsink->vischain) {
3973         activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3974         add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3975       }
3976       if (playsink->textchain) {
3977         activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3978         add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3979       }
3980       do_async_done (playsink);
3981       /* when going to READY, keep elements around as long as possible,
3982        * so they may be re-used faster next time/url around.
3983        * when really going to NULL, clean up everything completely. */
3984       if (transition == GST_STATE_CHANGE_READY_TO_NULL) {
3985
3986         /* Unparent the sinks to allow reuse */
3987         if (playsink->videochain && playsink->videochain->sink)
3988           gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
3989               playsink->videochain->sink);
3990         if (playsink->audiochain && playsink->audiochain->sink)
3991           gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
3992               playsink->audiochain->sink);
3993         if (playsink->textchain && playsink->textchain->sink)
3994           gst_bin_remove (GST_BIN_CAST (playsink->textchain->chain.bin),
3995               playsink->textchain->sink);
3996         if (playsink->audio_sink != NULL)
3997           gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
3998         if (playsink->video_sink != NULL)
3999           gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
4000         if (playsink->visualisation != NULL)
4001           gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
4002         if (playsink->text_sink != NULL)
4003           gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
4004         free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
4005         playsink->videodeinterlacechain = NULL;
4006         free_chain ((GstPlayChain *) playsink->videochain);
4007         playsink->videochain = NULL;
4008         free_chain ((GstPlayChain *) playsink->audiochain);
4009         playsink->audiochain = NULL;
4010         free_chain ((GstPlayChain *) playsink->vischain);
4011         playsink->vischain = NULL;
4012         free_chain ((GstPlayChain *) playsink->textchain);
4013         playsink->textchain = NULL;
4014       }
4015       break;
4016     default:
4017       break;
4018   }
4019   return ret;
4020   /* ERRORS */
4021 activate_failed:
4022   {
4023     GST_DEBUG_OBJECT (element,
4024         "element failed to change states -- activation problem?");
4025     return GST_STATE_CHANGE_FAILURE;
4026   }
4027 }
4028
4029 static void
4030 gst_play_sink_set_property (GObject * object, guint prop_id,
4031     const GValue * value, GParamSpec * spec)
4032 {
4033   GstPlaySink *playsink = GST_PLAY_SINK (object);
4034   switch (prop_id) {
4035     case PROP_FLAGS:
4036       gst_play_sink_set_flags (playsink, g_value_get_flags (value));
4037       break;
4038     case PROP_VOLUME:
4039       gst_play_sink_set_volume (playsink, g_value_get_double (value));
4040       break;
4041     case PROP_MUTE:
4042       gst_play_sink_set_mute (playsink, g_value_get_boolean (value));
4043       break;
4044     case PROP_FONT_DESC:
4045       gst_play_sink_set_font_desc (playsink, g_value_get_string (value));
4046       break;
4047     case PROP_SUBTITLE_ENCODING:
4048       gst_play_sink_set_subtitle_encoding (playsink,
4049           g_value_get_string (value));
4050       break;
4051     case PROP_VIS_PLUGIN:
4052       gst_play_sink_set_vis_plugin (playsink, g_value_get_object (value));
4053       break;
4054     case PROP_AV_OFFSET:
4055       gst_play_sink_set_av_offset (playsink, g_value_get_int64 (value));
4056       break;
4057     case PROP_VIDEO_SINK:
4058       gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_VIDEO,
4059           g_value_get_object (value));
4060       break;
4061     case PROP_AUDIO_SINK:
4062       gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_AUDIO,
4063           g_value_get_object (value));
4064       break;
4065     case PROP_TEXT_SINK:
4066       gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_TEXT,
4067           g_value_get_object (value));
4068       break;
4069     default:
4070       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
4071       break;
4072   }
4073 }
4074
4075 static void
4076 gst_play_sink_get_property (GObject * object, guint prop_id,
4077     GValue * value, GParamSpec * spec)
4078 {
4079   GstPlaySink *playsink = GST_PLAY_SINK (object);
4080   switch (prop_id) {
4081     case PROP_FLAGS:
4082       g_value_set_flags (value, gst_play_sink_get_flags (playsink));
4083       break;
4084     case PROP_VOLUME:
4085       g_value_set_double (value, gst_play_sink_get_volume (playsink));
4086       break;
4087     case PROP_MUTE:
4088       g_value_set_boolean (value, gst_play_sink_get_mute (playsink));
4089       break;
4090     case PROP_FONT_DESC:
4091       g_value_take_string (value, gst_play_sink_get_font_desc (playsink));
4092       break;
4093     case PROP_SUBTITLE_ENCODING:
4094       g_value_take_string (value,
4095           gst_play_sink_get_subtitle_encoding (playsink));
4096       break;
4097     case PROP_VIS_PLUGIN:
4098       g_value_take_object (value, gst_play_sink_get_vis_plugin (playsink));
4099       break;
4100     case PROP_SAMPLE:
4101       gst_value_take_sample (value, gst_play_sink_get_last_sample (playsink));
4102       break;
4103     case PROP_AV_OFFSET:
4104       g_value_set_int64 (value, gst_play_sink_get_av_offset (playsink));
4105       break;
4106     case PROP_VIDEO_SINK:
4107       g_value_take_object (value, gst_play_sink_get_sink (playsink,
4108               GST_PLAY_SINK_TYPE_VIDEO));
4109       break;
4110     case PROP_AUDIO_SINK:
4111       g_value_take_object (value, gst_play_sink_get_sink (playsink,
4112               GST_PLAY_SINK_TYPE_AUDIO));
4113       break;
4114     case PROP_TEXT_SINK:
4115       g_value_take_object (value, gst_play_sink_get_sink (playsink,
4116               GST_PLAY_SINK_TYPE_TEXT));
4117       break;
4118     default:
4119       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
4120       break;
4121   }
4122 }
4123
4124 static void
4125 gst_play_sink_overlay_expose (GstVideoOverlay * overlay)
4126 {
4127   GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4128   GstVideoOverlay *overlay_element;
4129
4130   GST_OBJECT_LOCK (playsink);
4131   if (playsink->overlay_element)
4132     overlay_element =
4133         GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4134   else
4135     overlay_element = NULL;
4136   GST_OBJECT_UNLOCK (playsink);
4137
4138   if (overlay_element) {
4139     gst_video_overlay_expose (overlay_element);
4140     gst_object_unref (overlay_element);
4141   }
4142 }
4143
4144 static void
4145 gst_play_sink_overlay_handle_events (GstVideoOverlay * overlay,
4146     gboolean handle_events)
4147 {
4148   GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4149   GstVideoOverlay *overlay_element;
4150
4151   GST_OBJECT_LOCK (playsink);
4152   if (playsink->overlay_element)
4153     overlay_element =
4154         GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4155   else
4156     overlay_element = NULL;
4157   GST_OBJECT_UNLOCK (playsink);
4158
4159   playsink->overlay_handle_events_set = TRUE;
4160   playsink->overlay_handle_events = handle_events;
4161
4162   if (overlay_element) {
4163     gst_video_overlay_handle_events (overlay_element, handle_events);
4164     gst_object_unref (overlay_element);
4165   }
4166 }
4167
4168 static void
4169 gst_play_sink_overlay_set_render_rectangle (GstVideoOverlay * overlay, gint x,
4170     gint y, gint width, gint height)
4171 {
4172   GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4173   GstVideoOverlay *overlay_element;
4174
4175   GST_OBJECT_LOCK (playsink);
4176   if (playsink->overlay_element)
4177     overlay_element =
4178         GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4179   else
4180     overlay_element = NULL;
4181   GST_OBJECT_UNLOCK (playsink);
4182
4183   playsink->overlay_render_rectangle_set = TRUE;
4184   playsink->overlay_x = x;
4185   playsink->overlay_y = y;
4186   playsink->overlay_width = width;
4187   playsink->overlay_height = height;
4188
4189   if (overlay_element) {
4190     gst_video_overlay_set_render_rectangle (overlay_element, x, y, width,
4191         height);
4192     gst_object_unref (overlay_element);
4193   }
4194 }
4195
4196 static void
4197 gst_play_sink_overlay_set_window_handle (GstVideoOverlay * overlay,
4198     guintptr handle)
4199 {
4200   GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4201   GstVideoOverlay *overlay_element;
4202
4203   GST_OBJECT_LOCK (playsink);
4204   if (playsink->overlay_element)
4205     overlay_element =
4206         GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4207   else
4208     overlay_element = NULL;
4209   GST_OBJECT_UNLOCK (playsink);
4210
4211   playsink->overlay_handle_set = TRUE;
4212   playsink->overlay_handle = handle;
4213
4214   if (overlay_element) {
4215     gst_video_overlay_set_window_handle (overlay_element, handle);
4216     gst_object_unref (overlay_element);
4217   }
4218 }
4219
4220 static void
4221 gst_play_sink_overlay_init (gpointer g_iface, gpointer g_iface_data)
4222 {
4223   GstVideoOverlayInterface *iface = (GstVideoOverlayInterface *) g_iface;
4224   iface->expose = gst_play_sink_overlay_expose;
4225   iface->handle_events = gst_play_sink_overlay_handle_events;
4226   iface->set_render_rectangle = gst_play_sink_overlay_set_render_rectangle;
4227   iface->set_window_handle = gst_play_sink_overlay_set_window_handle;
4228 }
4229
4230 static void
4231 gst_play_sink_navigation_send_event (GstNavigation * navigation,
4232     GstStructure * structure)
4233 {
4234   GstPlaySink *playsink = GST_PLAY_SINK (navigation);
4235   GstBin *bin = NULL;
4236
4237   GST_PLAY_SINK_LOCK (playsink);
4238   if (playsink->videochain && playsink->videochain->chain.bin)
4239     bin = GST_BIN (gst_object_ref (playsink->videochain->chain.bin));
4240   GST_PLAY_SINK_UNLOCK (playsink);
4241
4242   if (bin) {
4243     GstElement *nav = gst_bin_get_by_interface (bin, GST_TYPE_NAVIGATION);
4244
4245     if (nav) {
4246       gst_navigation_send_event (GST_NAVIGATION (nav), structure);
4247       structure = NULL;
4248       gst_object_unref (nav);
4249     }
4250
4251     gst_object_unref (bin);
4252   }
4253
4254   if (structure)
4255     gst_structure_free (structure);
4256 }
4257
4258 static void
4259 gst_play_sink_navigation_init (gpointer g_iface, gpointer g_iface_data)
4260 {
4261   GstNavigationInterface *iface = (GstNavigationInterface *) g_iface;
4262
4263   iface->send_event = gst_play_sink_navigation_send_event;
4264 }
4265
4266 static const GList *
4267 gst_play_sink_colorbalance_list_channels (GstColorBalance * balance)
4268 {
4269   GstPlaySink *playsink = GST_PLAY_SINK (balance);
4270
4271   return playsink->colorbalance_channels;
4272 }
4273
4274 static void
4275 gst_play_sink_colorbalance_set_value (GstColorBalance * balance,
4276     GstColorBalanceChannel * proxy, gint value)
4277 {
4278   GstPlaySink *playsink = GST_PLAY_SINK (balance);
4279   GList *l;
4280   gint i;
4281   GstColorBalance *balance_element = NULL;
4282
4283   GST_OBJECT_LOCK (playsink);
4284   if (playsink->colorbalance_element)
4285     balance_element =
4286         GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
4287   GST_OBJECT_UNLOCK (playsink);
4288
4289   for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
4290     GstColorBalanceChannel *proxy_tmp = l->data;
4291     gdouble new_val;
4292
4293     if (proxy_tmp != proxy)
4294       continue;
4295
4296     playsink->colorbalance_values[i] = value;
4297
4298     if (balance_element) {
4299       GstColorBalanceChannel *channel = NULL;
4300       const GList *channels, *k;
4301
4302       channels = gst_color_balance_list_channels (balance_element);
4303       for (k = channels; k; k = k->next) {
4304         GstColorBalanceChannel *tmp = l->data;
4305
4306         if (g_strrstr (tmp->label, proxy->label)) {
4307           channel = tmp;
4308           break;
4309         }
4310       }
4311
4312       g_assert (channel);
4313
4314       /* Convert to [0, 1] range */
4315       new_val =
4316           ((gdouble) value -
4317           (gdouble) proxy->min_value) / ((gdouble) proxy->max_value -
4318           (gdouble) proxy->min_value);
4319       /* Convert to channel range */
4320       new_val =
4321           channel->min_value + new_val * ((gdouble) channel->max_value -
4322           (gdouble) channel->min_value);
4323
4324       gst_color_balance_set_value (balance_element, channel,
4325           (gint) (new_val + 0.5));
4326
4327       gst_object_unref (balance_element);
4328     }
4329
4330     gst_color_balance_value_changed (balance, proxy, value);
4331     break;
4332   }
4333 }
4334
4335 static gint
4336 gst_play_sink_colorbalance_get_value (GstColorBalance * balance,
4337     GstColorBalanceChannel * proxy)
4338 {
4339   GstPlaySink *playsink = GST_PLAY_SINK (balance);
4340   GList *l;
4341   gint i;
4342
4343   for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
4344     GstColorBalanceChannel *proxy_tmp = l->data;
4345
4346     if (proxy_tmp != proxy)
4347       continue;
4348
4349     return playsink->colorbalance_values[i];
4350   }
4351
4352   g_return_val_if_reached (0);
4353 }
4354
4355 static GstColorBalanceType
4356 gst_play_sink_colorbalance_get_balance_type (GstColorBalance * balance)
4357 {
4358   GstPlaySink *playsink = GST_PLAY_SINK (balance);
4359   GstColorBalance *balance_element = NULL;
4360   GstColorBalanceType t = GST_COLOR_BALANCE_SOFTWARE;
4361
4362   GST_OBJECT_LOCK (playsink);
4363   if (playsink->colorbalance_element)
4364     balance_element =
4365         GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
4366   GST_OBJECT_UNLOCK (playsink);
4367
4368   if (balance_element) {
4369     t = gst_color_balance_get_balance_type (balance_element);
4370     gst_object_unref (balance_element);
4371   }
4372
4373   return t;
4374 }
4375
4376 static void
4377 gst_play_sink_colorbalance_init (gpointer g_iface, gpointer g_iface_data)
4378 {
4379   GstColorBalanceInterface *iface = (GstColorBalanceInterface *) g_iface;
4380
4381   iface->list_channels = gst_play_sink_colorbalance_list_channels;
4382   iface->set_value = gst_play_sink_colorbalance_set_value;
4383   iface->get_value = gst_play_sink_colorbalance_get_value;
4384   iface->get_balance_type = gst_play_sink_colorbalance_get_balance_type;
4385 }
4386
4387 gboolean
4388 gst_play_sink_plugin_init (GstPlugin * plugin)
4389 {
4390   GST_DEBUG_CATEGORY_INIT (gst_play_sink_debug, "playsink", 0, "play bin");
4391   return gst_element_register (plugin, "playsink", GST_RANK_NONE,
4392       GST_TYPE_PLAY_SINK);
4393 }