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