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