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