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