playbin2: Send flush events when changing subtitle tracks and use new input-selector...
[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 = gst_message_new_async_done (GST_OBJECT_CAST (playsink), FALSE);
1270     GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1271         (playsink), message);
1272
1273     playsink->async_pending = FALSE;
1274   }
1275
1276   playsink->need_async_start = FALSE;
1277 }
1278
1279 /* try to change the state of an element. This function returns the element when
1280  * the state change could be performed. When this function returns NULL an error
1281  * occured and the element is unreffed if @unref is TRUE. */
1282 static GstElement *
1283 try_element (GstPlaySink * playsink, GstElement * element, gboolean unref)
1284 {
1285   GstStateChangeReturn ret;
1286
1287   if (element) {
1288     ret = gst_element_set_state (element, GST_STATE_READY);
1289     if (ret == GST_STATE_CHANGE_FAILURE) {
1290       GST_DEBUG_OBJECT (playsink, "failed state change..");
1291       gst_element_set_state (element, GST_STATE_NULL);
1292       if (unref)
1293         gst_object_unref (element);
1294       element = NULL;
1295     }
1296   }
1297   return element;
1298 }
1299
1300 /* make the element (bin) that contains the elements needed to perform
1301  * video display. Only used for *raw* video streams.
1302  *
1303  *  +------------------------------------------------------------+
1304  *  | vbin                                                       |
1305  *  |      +-------+   +----------+   +----------+   +---------+ |
1306  *  |      | queue |   |colorspace|   |videoscale|   |videosink| |
1307  *  |   +-sink    src-sink       src-sink       src-sink       | |
1308  *  |   |  +-------+   +----------+   +----------+   +---------+ |
1309  * sink-+                                                        |
1310  *  +------------------------------------------------------------+
1311  *
1312  */
1313 static GstPlayVideoDeinterlaceChain *
1314 gen_video_deinterlace_chain (GstPlaySink * playsink)
1315 {
1316   GstPlayVideoDeinterlaceChain *chain;
1317   GstBin *bin;
1318   GstPad *pad;
1319   GstElement *head = NULL, *prev = NULL;
1320
1321   chain = g_new0 (GstPlayVideoDeinterlaceChain, 1);
1322   chain->chain.playsink = playsink;
1323
1324   GST_DEBUG_OBJECT (playsink, "making video deinterlace chain %p", chain);
1325
1326   /* create a bin to hold objects, as we create them we add them to this bin so
1327    * that when something goes wrong we only need to unref the bin */
1328   chain->chain.bin = gst_bin_new ("vdbin");
1329   bin = GST_BIN_CAST (chain->chain.bin);
1330   gst_object_ref_sink (bin);
1331
1332   GST_DEBUG_OBJECT (playsink, "creating " COLORSPACE);
1333   chain->conv = gst_element_factory_make (COLORSPACE, "vdconv");
1334   if (chain->conv == NULL) {
1335     post_missing_element_message (playsink, COLORSPACE);
1336     GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1337         (_("Missing element '%s' - check your GStreamer installation."),
1338             COLORSPACE), ("video rendering might fail"));
1339   } else {
1340     gst_bin_add (bin, chain->conv);
1341     head = chain->conv;
1342     prev = chain->conv;
1343   }
1344
1345   GST_DEBUG_OBJECT (playsink, "creating deinterlace");
1346   chain->deinterlace = gst_element_factory_make ("deinterlace", "deinterlace");
1347   if (chain->deinterlace == NULL) {
1348     post_missing_element_message (playsink, "deinterlace");
1349     GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1350         (_("Missing element '%s' - check your GStreamer installation."),
1351             "deinterlace"), ("deinterlacing won't work"));
1352   } else {
1353     gst_bin_add (bin, chain->deinterlace);
1354     if (prev) {
1355       if (!gst_element_link_pads_full (prev, "src", chain->deinterlace, "sink",
1356               GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1357         goto link_failed;
1358     } else {
1359       head = chain->deinterlace;
1360     }
1361     prev = chain->deinterlace;
1362   }
1363
1364   if (head) {
1365     pad = gst_element_get_static_pad (head, "sink");
1366     chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1367     gst_object_unref (pad);
1368   } else {
1369     chain->sinkpad = gst_ghost_pad_new_no_target ("sink", GST_PAD_SINK);
1370   }
1371
1372   if (prev) {
1373     pad = gst_element_get_static_pad (prev, "src");
1374     chain->srcpad = gst_ghost_pad_new ("src", pad);
1375     gst_object_unref (pad);
1376   } else {
1377     chain->srcpad = gst_ghost_pad_new ("src", chain->sinkpad);
1378   }
1379
1380   gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1381   gst_element_add_pad (chain->chain.bin, chain->srcpad);
1382
1383   return chain;
1384
1385 link_failed:
1386   {
1387     GST_ELEMENT_ERROR (playsink, CORE, PAD,
1388         (NULL), ("Failed to configure the video deinterlace chain."));
1389     free_chain ((GstPlayChain *) chain);
1390     return NULL;
1391   }
1392 }
1393
1394 static gboolean
1395 is_valid_color_balance_element (GstColorBalance * bal)
1396 {
1397   gboolean have_brightness = FALSE;
1398   gboolean have_contrast = FALSE;
1399   gboolean have_hue = FALSE;
1400   gboolean have_saturation = FALSE;
1401   const GList *channels, *l;
1402
1403   channels = gst_color_balance_list_channels (bal);
1404   for (l = channels; l; l = l->next) {
1405     GstColorBalanceChannel *ch = l->data;
1406
1407     if (g_strrstr (ch->label, "BRIGHTNESS"))
1408       have_brightness = TRUE;
1409     else if (g_strrstr (ch->label, "CONTRAST"))
1410       have_contrast = TRUE;
1411     else if (g_strrstr (ch->label, "HUE"))
1412       have_hue = TRUE;
1413     else if (g_strrstr (ch->label, "SATURATION"))
1414       have_saturation = TRUE;
1415   }
1416
1417   return have_brightness && have_contrast && have_hue && have_saturation;
1418 }
1419
1420 static void
1421 iterate_color_balance_elements (const GValue * item, gpointer user_data)
1422 {
1423   gboolean valid;
1424   GstColorBalance *cb, **cb_out = user_data;
1425
1426   cb = GST_COLOR_BALANCE (g_value_get_object (item));
1427   valid = is_valid_color_balance_element (cb);
1428   if (valid) {
1429     if (*cb_out
1430         && gst_color_balance_get_balance_type (*cb_out) ==
1431         GST_COLOR_BALANCE_SOFTWARE) {
1432       gst_object_unref (*cb_out);
1433       *cb_out = GST_COLOR_BALANCE (gst_object_ref (cb));
1434     } else if (!*cb_out) {
1435       *cb_out = GST_COLOR_BALANCE (gst_object_ref (cb));
1436     }
1437   }
1438 }
1439
1440 static GstColorBalance *
1441 find_color_balance_element (GstElement * element)
1442 {
1443   GstIterator *it;
1444   GstColorBalance *cb = NULL;
1445
1446   if (GST_IS_COLOR_BALANCE (element)
1447       && is_valid_color_balance_element (GST_COLOR_BALANCE (element)))
1448     return GST_COLOR_BALANCE (gst_object_ref (element));
1449   else if (!GST_IS_BIN (element))
1450     return FALSE;
1451
1452   it = gst_bin_iterate_all_by_interface (GST_BIN (element),
1453       GST_TYPE_COLOR_BALANCE);
1454   while (gst_iterator_foreach (it, iterate_color_balance_elements,
1455           &cb) == GST_ITERATOR_RESYNC)
1456     gst_iterator_resync (it);
1457   gst_iterator_free (it);
1458
1459   return cb;
1460 }
1461
1462 static void
1463 colorbalance_value_changed_cb (GstColorBalance * balance,
1464     GstColorBalanceChannel * channel, gint value, GstPlaySink * playsink)
1465 {
1466   GList *l;
1467   gint i;
1468
1469   for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
1470     GstColorBalanceChannel *proxy = l->data;
1471
1472     if (g_strrstr (channel->label, proxy->label)) {
1473       gdouble new_val;
1474
1475       /* Convert to [0, 1] range */
1476       new_val =
1477           ((gdouble) value -
1478           (gdouble) channel->min_value) / ((gdouble) channel->max_value -
1479           (gdouble) channel->min_value);
1480       /* Convert to proxy range */
1481       new_val =
1482           proxy->min_value + new_val * ((gdouble) proxy->max_value -
1483           (gdouble) proxy->min_value);
1484       playsink->colorbalance_values[i] = (gint) (0.5 + new_val);
1485
1486       gst_color_balance_value_changed (GST_COLOR_BALANCE (playsink), proxy,
1487           playsink->colorbalance_values[i]);
1488       break;
1489     }
1490   }
1491 }
1492
1493 static void
1494 update_colorbalance (GstPlaySink * playsink)
1495 {
1496   GstColorBalance *balance = NULL;
1497   GList *l;
1498   gint i;
1499
1500   GST_OBJECT_LOCK (playsink);
1501   if (playsink->colorbalance_element) {
1502     balance =
1503         GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
1504   }
1505   GST_OBJECT_UNLOCK (playsink);
1506   if (!balance)
1507     return;
1508
1509   g_signal_handlers_block_by_func (balance,
1510       G_CALLBACK (colorbalance_value_changed_cb), playsink);
1511
1512   for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
1513     GstColorBalanceChannel *proxy = l->data;
1514     GstColorBalanceChannel *channel = NULL;
1515     const GList *channels, *k;
1516
1517     channels = gst_color_balance_list_channels (balance);
1518     for (k = channels; k; k = k->next) {
1519       GstColorBalanceChannel *tmp = k->data;
1520
1521       if (g_strrstr (tmp->label, proxy->label)) {
1522         channel = tmp;
1523         break;
1524       }
1525     }
1526
1527     g_assert (channel);
1528
1529     gst_color_balance_set_value (balance, channel,
1530         playsink->colorbalance_values[i]);
1531   }
1532
1533   g_signal_handlers_unblock_by_func (balance,
1534       G_CALLBACK (colorbalance_value_changed_cb), playsink);
1535
1536   gst_object_unref (balance);
1537 }
1538
1539 /* make the element (bin) that contains the elements needed to perform
1540  * video display.
1541  *
1542  *  +------------------------------------------------------------+
1543  *  | vbin                                                       |
1544  *  |      +-------+   +----------+   +----------+   +---------+ |
1545  *  |      | queue |   |colorspace|   |videoscale|   |videosink| |
1546  *  |   +-sink    src-sink       src-sink       src-sink       | |
1547  *  |   |  +-------+   +----------+   +----------+   +---------+ |
1548  * sink-+                                                        |
1549  *  +------------------------------------------------------------+
1550  *
1551  */
1552 static GstPlayVideoChain *
1553 gen_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1554 {
1555   GstPlayVideoChain *chain;
1556   GstBin *bin;
1557   GstPad *pad;
1558   GstElement *head = NULL, *prev = NULL, *elem = NULL;
1559
1560   chain = g_new0 (GstPlayVideoChain, 1);
1561   chain->chain.playsink = playsink;
1562   chain->chain.raw = raw;
1563
1564   GST_DEBUG_OBJECT (playsink, "making video chain %p", chain);
1565
1566   if (playsink->video_sink) {
1567     GST_DEBUG_OBJECT (playsink, "trying configured videosink");
1568     chain->sink = try_element (playsink, playsink->video_sink, FALSE);
1569   } else {
1570     /* only try fallback if no specific sink was chosen */
1571     if (chain->sink == NULL) {
1572       GST_DEBUG_OBJECT (playsink, "trying autovideosink");
1573       elem = gst_element_factory_make ("autovideosink", "videosink");
1574       chain->sink = try_element (playsink, elem, TRUE);
1575     }
1576     if (chain->sink == NULL) {
1577       /* if default sink from config.h is different then try it too */
1578       if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1579         GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_VIDEOSINK);
1580         elem = gst_element_factory_make (DEFAULT_VIDEOSINK, "videosink");
1581         chain->sink = try_element (playsink, elem, TRUE);
1582       }
1583     }
1584     if (chain->sink)
1585       playsink->video_sink = gst_object_ref (chain->sink);
1586   }
1587   if (chain->sink == NULL)
1588     goto no_sinks;
1589   head = chain->sink;
1590
1591   /* if we can disable async behaviour of the sink, we can avoid adding a
1592    * queue for the audio chain. */
1593   elem =
1594       gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1595       G_TYPE_BOOLEAN);
1596   if (elem) {
1597     GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1598         async, GST_ELEMENT_NAME (elem));
1599     g_object_set (elem, "async", async, NULL);
1600     chain->async = async;
1601   } else {
1602     GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1603     chain->async = TRUE;
1604   }
1605
1606   /* Make sure the aspect ratio is kept */
1607   elem =
1608       gst_play_sink_find_property_sinks (playsink, chain->sink,
1609       "force-aspect-ratio", G_TYPE_BOOLEAN);
1610   if (elem)
1611     g_object_set (elem, "force-aspect-ratio", TRUE, NULL);
1612
1613   /* find ts-offset element */
1614   gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1615       gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1616           G_TYPE_INT64));
1617
1618   /* create a bin to hold objects, as we create them we add them to this bin so
1619    * that when something goes wrong we only need to unref the bin */
1620   chain->chain.bin = gst_bin_new ("vbin");
1621   bin = GST_BIN_CAST (chain->chain.bin);
1622   gst_object_ref_sink (bin);
1623   gst_bin_add (bin, chain->sink);
1624
1625   /* Get the VideoOverlay element */
1626   {
1627     GstVideoOverlay *overlay = NULL;
1628
1629     GST_OBJECT_LOCK (playsink);
1630     if (playsink->overlay_element)
1631       gst_object_unref (playsink->overlay_element);
1632     playsink->overlay_element =
1633         GST_VIDEO_OVERLAY (gst_bin_get_by_interface (GST_BIN (chain->chain.bin),
1634             GST_TYPE_VIDEO_OVERLAY));
1635     if (playsink->overlay_element)
1636       overlay = GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
1637     GST_OBJECT_UNLOCK (playsink);
1638
1639     if (overlay) {
1640       if (playsink->overlay_handle_set)
1641         gst_video_overlay_set_window_handle (overlay, playsink->overlay_handle);
1642       if (playsink->overlay_handle_events_set)
1643         gst_video_overlay_handle_events (overlay,
1644             playsink->overlay_handle_events);
1645       if (playsink->overlay_render_rectangle_set)
1646         gst_video_overlay_set_render_rectangle (overlay,
1647             playsink->overlay_x, playsink->overlay_y,
1648             playsink->overlay_width, playsink->overlay_height);
1649       gst_object_unref (overlay);
1650     }
1651   }
1652
1653   /* decouple decoder from sink, this improves playback quite a lot since the
1654    * decoder can continue while the sink blocks for synchronisation. We don't
1655    * need a lot of buffers as this consumes a lot of memory and we don't want
1656    * too little because else we would be context switching too quickly. */
1657   chain->queue = gst_element_factory_make ("queue", "vqueue");
1658   if (chain->queue == NULL) {
1659     post_missing_element_message (playsink, "queue");
1660     GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1661         (_("Missing element '%s' - check your GStreamer installation."),
1662             "queue"), ("video rendering might be suboptimal"));
1663     head = chain->sink;
1664     prev = NULL;
1665   } else {
1666     g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1667         "max-size-bytes", 0, "max-size-time", (gint64) 0, "silent", TRUE, NULL);
1668     gst_bin_add (bin, chain->queue);
1669     head = prev = chain->queue;
1670   }
1671
1672   GST_OBJECT_LOCK (playsink);
1673   if (playsink->colorbalance_element) {
1674     g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
1675         G_CALLBACK (colorbalance_value_changed_cb), playsink);
1676     gst_object_unref (playsink->colorbalance_element);
1677   }
1678   playsink->colorbalance_element = find_color_balance_element (chain->sink);
1679   GST_OBJECT_UNLOCK (playsink);
1680
1681   if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)
1682       || (!playsink->colorbalance_element
1683           && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE))) {
1684     gboolean use_converters = !(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO);
1685     gboolean use_balance = !playsink->colorbalance_element
1686         && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE);
1687
1688     GST_DEBUG_OBJECT (playsink, "creating videoconverter");
1689     chain->conv =
1690         g_object_new (GST_TYPE_PLAY_SINK_VIDEO_CONVERT, "name", "vconv",
1691         "use-converters", use_converters, "use-balance", use_balance, NULL);
1692
1693     GST_OBJECT_LOCK (playsink);
1694     if (use_balance && GST_PLAY_SINK_VIDEO_CONVERT (chain->conv)->balance)
1695       playsink->colorbalance_element =
1696           GST_COLOR_BALANCE (gst_object_ref (GST_PLAY_SINK_VIDEO_CONVERT
1697               (chain->conv)->balance));
1698     GST_OBJECT_UNLOCK (playsink);
1699
1700     gst_bin_add (bin, chain->conv);
1701     if (prev) {
1702       if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
1703               GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1704         goto link_failed;
1705     } else {
1706       head = chain->conv;
1707     }
1708     prev = chain->conv;
1709   }
1710
1711   update_colorbalance (playsink);
1712
1713   if (prev) {
1714     GST_DEBUG_OBJECT (playsink, "linking to sink");
1715     if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
1716             GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1717       goto link_failed;
1718   }
1719
1720   pad = gst_element_get_static_pad (head, "sink");
1721   chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1722
1723   /* sending audio/video flushes break stream changes when the pipeline
1724    * is paused and played again in 0.10 */
1725 #if 0
1726   gst_pad_set_event_function (chain->sinkpad,
1727       GST_DEBUG_FUNCPTR (gst_play_sink_video_sink_event));
1728   gst_pad_set_chain_function (chain->sinkpad,
1729       GST_DEBUG_FUNCPTR (gst_play_sink_video_sink_chain));
1730 #endif
1731
1732   gst_object_unref (pad);
1733   gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1734
1735   return chain;
1736
1737   /* ERRORS */
1738 no_sinks:
1739   {
1740     if (!elem && !playsink->video_sink) {
1741       post_missing_element_message (playsink, "autovideosink");
1742       if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1743         post_missing_element_message (playsink, DEFAULT_VIDEOSINK);
1744         GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1745             (_("Both autovideosink and %s elements are missing."),
1746                 DEFAULT_VIDEOSINK), (NULL));
1747       } else {
1748         GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1749             (_("The autovideosink element is missing.")), (NULL));
1750       }
1751     } else {
1752       if (playsink->video_sink) {
1753         GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1754             (_("Configured videosink %s is not working."),
1755                 GST_ELEMENT_NAME (playsink->video_sink)), (NULL));
1756       } else if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1757         GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1758             (_("Both autovideosink and %s elements are not working."),
1759                 DEFAULT_VIDEOSINK), (NULL));
1760       } else {
1761         GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1762             (_("The autovideosink element is not working.")), (NULL));
1763       }
1764     }
1765     free_chain ((GstPlayChain *) chain);
1766     return NULL;
1767   }
1768
1769 link_failed:
1770   {
1771     GST_ELEMENT_ERROR (playsink, CORE, PAD,
1772         (NULL), ("Failed to configure the video sink."));
1773     /* checking sink made it READY */
1774     gst_element_set_state (chain->sink, GST_STATE_NULL);
1775     /* Remove chain from the bin to allow reuse later */
1776     gst_bin_remove (bin, chain->sink);
1777     free_chain ((GstPlayChain *) chain);
1778     return NULL;
1779   }
1780 }
1781
1782 static gboolean
1783 setup_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1784 {
1785   GstElement *elem;
1786   GstPlayVideoChain *chain;
1787   GstStateChangeReturn ret;
1788
1789   chain = playsink->videochain;
1790
1791   chain->chain.raw = raw;
1792
1793   /* if the chain was active we don't do anything */
1794   if (GST_PLAY_CHAIN (chain)->activated == TRUE)
1795     return TRUE;
1796
1797   /* try to set the sink element to READY again */
1798   ret = gst_element_set_state (chain->sink, GST_STATE_READY);
1799   if (ret == GST_STATE_CHANGE_FAILURE)
1800     return FALSE;
1801
1802   /* Get the VideoOverlay element */
1803   {
1804     GstVideoOverlay *overlay = NULL;
1805
1806     GST_OBJECT_LOCK (playsink);
1807     if (playsink->overlay_element)
1808       gst_object_unref (playsink->overlay_element);
1809     playsink->overlay_element =
1810         GST_VIDEO_OVERLAY (gst_bin_get_by_interface (GST_BIN (chain->chain.bin),
1811             GST_TYPE_VIDEO_OVERLAY));
1812     if (playsink->overlay_element)
1813       overlay = GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
1814     GST_OBJECT_UNLOCK (playsink);
1815
1816     if (overlay) {
1817       if (playsink->overlay_handle_set)
1818         gst_video_overlay_set_window_handle (overlay, playsink->overlay_handle);
1819       if (playsink->overlay_handle_events_set)
1820         gst_video_overlay_handle_events (overlay,
1821             playsink->overlay_handle_events);
1822       if (playsink->overlay_render_rectangle_set)
1823         gst_video_overlay_set_render_rectangle (overlay,
1824             playsink->overlay_x, playsink->overlay_y,
1825             playsink->overlay_width, playsink->overlay_height);
1826       gst_object_unref (overlay);
1827     }
1828   }
1829
1830   /* find ts-offset element */
1831   gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1832       gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1833           G_TYPE_INT64));
1834
1835   /* if we can disable async behaviour of the sink, we can avoid adding a
1836    * queue for the audio chain. */
1837   elem =
1838       gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1839       G_TYPE_BOOLEAN);
1840   if (elem) {
1841     GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1842         async, GST_ELEMENT_NAME (elem));
1843     g_object_set (elem, "async", async, NULL);
1844     chain->async = async;
1845   } else {
1846     GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1847     chain->async = TRUE;
1848   }
1849
1850   /* Make sure the aspect ratio is kept */
1851   elem =
1852       gst_play_sink_find_property_sinks (playsink, chain->sink,
1853       "force-aspect-ratio", G_TYPE_BOOLEAN);
1854   if (elem)
1855     g_object_set (elem, "force-aspect-ratio", TRUE, NULL);
1856
1857   GST_OBJECT_LOCK (playsink);
1858   if (playsink->colorbalance_element) {
1859     g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
1860         G_CALLBACK (colorbalance_value_changed_cb), playsink);
1861     gst_object_unref (playsink->colorbalance_element);
1862   }
1863   playsink->colorbalance_element = find_color_balance_element (chain->sink);
1864   GST_OBJECT_UNLOCK (playsink);
1865
1866   if (chain->conv) {
1867     gboolean use_balance = !playsink->colorbalance_element
1868         && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE);
1869
1870     g_object_set (chain->conv, "use-balance", use_balance, NULL);
1871
1872     GST_OBJECT_LOCK (playsink);
1873     if (use_balance && GST_PLAY_SINK_VIDEO_CONVERT (chain->conv)->balance)
1874       playsink->colorbalance_element =
1875           GST_COLOR_BALANCE (gst_object_ref (GST_PLAY_SINK_VIDEO_CONVERT
1876               (chain->conv)->balance));
1877     GST_OBJECT_UNLOCK (playsink);
1878   }
1879
1880   update_colorbalance (playsink);
1881
1882   return TRUE;
1883 }
1884
1885 static void
1886 _generate_update_newsegment_event (GstPad * pad, GstSegment * segment,
1887     GstEvent ** event1)
1888 {
1889   GstEvent *event;
1890   GstStructure *structure;
1891   event = gst_event_new_segment (segment);
1892   structure = gst_event_writable_structure (event);
1893   gst_structure_id_set (structure,
1894       _playsink_reset_segment_event_marker_id, G_TYPE_BOOLEAN, TRUE, NULL);
1895   *event1 = event;
1896 }
1897
1898 static gboolean
1899 gst_play_sink_sink_event (GstPad * pad, GstObject * parent, GstEvent * event,
1900     const gchar * sink_type,
1901     gboolean * sink_ignore_wrong_state,
1902     gboolean * sink_custom_flush_finished,
1903     gboolean * sink_pending_flush, GstSegment * sink_segment)
1904 {
1905   GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_object_get_parent (parent));
1906   gboolean ret;
1907   const GstStructure *structure = gst_event_get_structure (event);
1908
1909   if (GST_EVENT_TYPE (event) == GST_EVENT_CUSTOM_DOWNSTREAM_OOB && structure) {
1910     gchar *custom_flush;
1911     gchar *custom_flush_finish;
1912
1913     custom_flush = g_strdup_printf ("playsink-custom-%s-flush", sink_type);
1914     custom_flush_finish =
1915         g_strdup_printf ("playsink-custom-%s-flush-finish", sink_type);
1916     if (strcmp (gst_structure_get_name (structure), custom_flush) == 0) {
1917       GST_DEBUG_OBJECT (pad,
1918           "Custom %s flush event received, marking to flush %s", sink_type,
1919           sink_type);
1920       GST_PLAY_SINK_LOCK (playsink);
1921       *sink_ignore_wrong_state = TRUE;
1922       *sink_custom_flush_finished = FALSE;
1923       GST_PLAY_SINK_UNLOCK (playsink);
1924     } else if (strcmp (gst_structure_get_name (structure),
1925             custom_flush_finish) == 0) {
1926       GST_DEBUG_OBJECT (pad, "Custom %s flush finish event received",
1927           sink_type);
1928       GST_PLAY_SINK_LOCK (playsink);
1929       *sink_pending_flush = TRUE;
1930       *sink_custom_flush_finished = TRUE;
1931       GST_PLAY_SINK_UNLOCK (playsink);
1932     }
1933
1934     g_free (custom_flush);
1935     g_free (custom_flush_finish);
1936   } else if (GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_STOP) {
1937     GST_PLAY_SINK_LOCK (playsink);
1938     GST_DEBUG_OBJECT (pad, "Resetting %s segment because of flush-stop event",
1939         sink_type);
1940     gst_segment_init (sink_segment, GST_FORMAT_UNDEFINED);
1941     GST_PLAY_SINK_UNLOCK (playsink);
1942   }
1943
1944   GST_DEBUG_OBJECT (pad, "Forwarding event %" GST_PTR_FORMAT, event);
1945   ret = gst_proxy_pad_event_default (pad, parent, gst_event_ref (event));
1946
1947   if (GST_EVENT_TYPE (event) == GST_EVENT_SEGMENT) {
1948     const GstSegment *segment;
1949
1950     gst_event_parse_segment (event, &segment);
1951     GST_DEBUG_OBJECT (pad, "Segment event: %" GST_SEGMENT_FORMAT, segment);
1952
1953     GST_PLAY_SINK_LOCK (playsink);
1954     if (sink_segment->format != segment->format) {
1955       GST_DEBUG_OBJECT (pad, "%s segment format changed: %s -> %s",
1956           sink_type,
1957           gst_format_get_name (sink_segment->format),
1958           gst_format_get_name (segment->format));
1959       gst_segment_init (sink_segment, segment->format);
1960     }
1961
1962     GST_DEBUG_OBJECT (pad, "Old %s segment: %" GST_SEGMENT_FORMAT,
1963         sink_type, sink_segment);
1964     gst_segment_copy_into (&playsink->text_segment, sink_segment);
1965     GST_DEBUG_OBJECT (pad, "New %s segment: %" GST_SEGMENT_FORMAT,
1966         sink_type, sink_segment);
1967     GST_PLAY_SINK_UNLOCK (playsink);
1968   }
1969
1970   gst_event_unref (event);
1971   gst_object_unref (playsink);
1972   return ret;
1973 }
1974
1975 static GstFlowReturn
1976 gst_play_sink_sink_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer,
1977     const gchar * sink_type,
1978     gboolean * sink_ignore_wrong_state,
1979     gboolean * sink_custom_flush_finished,
1980     gboolean * sink_pending_flush, GstSegment * sink_segment)
1981 {
1982   GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
1983   GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
1984   GstFlowReturn ret;
1985
1986   GST_PLAY_SINK_LOCK (playsink);
1987
1988   if (*sink_pending_flush) {
1989     GstSegment segment_copy;
1990     GstEvent *event;
1991     GstStructure *structure;
1992
1993     *sink_pending_flush = FALSE;
1994
1995     /* sink_segment will be updated in flush */
1996     segment_copy = *sink_segment;
1997
1998     GST_PLAY_SINK_UNLOCK (playsink);
1999
2000     /* make the bin drop all cached data.
2001      * This event will be dropped on the src pad, if any. */
2002     event = gst_event_new_flush_start ();
2003     structure = gst_event_writable_structure (event);
2004     gst_structure_id_set (structure,
2005         _playsink_reset_segment_event_marker_id, G_TYPE_BOOLEAN, TRUE, NULL);
2006
2007     GST_DEBUG_OBJECT (pad,
2008         "Pushing %s flush-start event with reset segment marker set: %"
2009         GST_PTR_FORMAT, sink_type, event);
2010     gst_pad_send_event (pad, event);
2011
2012     /* make queue drop all cached data.
2013      * This event will be dropped on the src pad. */
2014     event = gst_event_new_flush_stop (TRUE);
2015     structure = gst_event_writable_structure (event);
2016     gst_structure_id_set (structure,
2017         _playsink_reset_segment_event_marker_id, G_TYPE_BOOLEAN, TRUE, NULL);
2018
2019     GST_DEBUG_OBJECT (pad,
2020         "Pushing %s flush-stop event with reset segment marker set: %"
2021         GST_PTR_FORMAT, sink_type, event);
2022     gst_pad_send_event (pad, event);
2023
2024     /* Re-sync queue segment info after flush-stop.
2025      * This event will be dropped on the src pad. */
2026     if (sink_segment->format != GST_FORMAT_UNDEFINED) {
2027       GstEvent *event1;
2028
2029       _generate_update_newsegment_event (pad, sink_segment, &event1);
2030       GST_DEBUG_OBJECT (playsink,
2031           "Pushing segment event with reset "
2032           "segment marker set: %" GST_PTR_FORMAT, event1);
2033       gst_pad_send_event (pad, event1);
2034     }
2035   } else {
2036     GST_PLAY_SINK_UNLOCK (playsink);
2037   }
2038
2039   ret = gst_proxy_pad_chain_default (pad, parent, buffer);
2040
2041   GST_PLAY_SINK_LOCK (playsink);
2042   if (ret == GST_FLOW_FLUSHING && *sink_ignore_wrong_state) {
2043     GST_DEBUG_OBJECT (pad, "Ignoring wrong state for %s during flush",
2044         sink_type);
2045     if (*sink_custom_flush_finished) {
2046       GST_DEBUG_OBJECT (pad, "Custom flush finished, stop ignoring "
2047           "wrong state for %s", sink_type);
2048       *sink_ignore_wrong_state = FALSE;
2049     }
2050
2051     ret = GST_FLOW_OK;
2052   }
2053   GST_PLAY_SINK_UNLOCK (playsink);
2054
2055   gst_object_unref (playsink);
2056   gst_object_unref (tbin);
2057   return ret;
2058 }
2059
2060 /* sending audio/video flushes break stream changes when the pipeline
2061  * is paused and played again in 0.10 */
2062 #if 0
2063 static gboolean
2064 gst_play_sink_video_sink_event (GstPad * pad, GstEvent * event)
2065 {
2066   GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2067   GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2068   gboolean ret;
2069
2070   ret = gst_play_sink_sink_event (pad, event, "video",
2071       &playsink->video_ignore_wrong_state,
2072       &playsink->video_custom_flush_finished,
2073       &playsink->video_pending_flush, &playsink->video_segment);
2074
2075   gst_object_unref (playsink);
2076   gst_object_unref (tbin);
2077   return ret;
2078 }
2079
2080 static GstFlowReturn
2081 gst_play_sink_video_sink_chain (GstPad * pad, GstBuffer * buffer)
2082 {
2083   GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2084   GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2085   gboolean ret;
2086
2087   ret = gst_play_sink_sink_chain (pad, buffer, "video",
2088       &playsink->video_ignore_wrong_state,
2089       &playsink->video_custom_flush_finished,
2090       &playsink->video_pending_flush, &playsink->video_segment);
2091
2092   gst_object_unref (playsink);
2093   gst_object_unref (tbin);
2094   return ret;
2095 }
2096
2097 static gboolean
2098 gst_play_sink_audio_sink_event (GstPad * pad, GstEvent * event)
2099 {
2100   GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2101   GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2102   gboolean ret;
2103
2104   ret = gst_play_sink_sink_event (pad, event, "audio",
2105       &playsink->audio_ignore_wrong_state,
2106       &playsink->audio_custom_flush_finished,
2107       &playsink->audio_pending_flush, &playsink->audio_segment);
2108
2109   gst_object_unref (playsink);
2110   gst_object_unref (tbin);
2111   return ret;
2112 }
2113
2114 static GstFlowReturn
2115 gst_play_sink_audio_sink_chain (GstPad * pad, GstBuffer * buffer)
2116 {
2117   GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2118   GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2119   gboolean ret;
2120
2121   ret = gst_play_sink_sink_chain (pad, buffer, "audio",
2122       &playsink->audio_ignore_wrong_state,
2123       &playsink->audio_custom_flush_finished,
2124       &playsink->audio_pending_flush, &playsink->audio_segment);
2125
2126   gst_object_unref (playsink);
2127   gst_object_unref (tbin);
2128   return ret;
2129 }
2130 #endif
2131
2132 static gboolean
2133 gst_play_sink_text_sink_event (GstPad * pad, GstObject * parent,
2134     GstEvent * event)
2135 {
2136   GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_object_get_parent (parent));
2137   gboolean ret;
2138
2139   ret = gst_play_sink_sink_event (pad, parent, event, "subtitle",
2140       &playsink->text_ignore_wrong_state,
2141       &playsink->text_custom_flush_finished,
2142       &playsink->text_pending_flush, &playsink->text_segment);
2143
2144   gst_object_unref (playsink);
2145
2146   return ret;
2147 }
2148
2149 static GstFlowReturn
2150 gst_play_sink_text_sink_chain (GstPad * pad, GstObject * parent,
2151     GstBuffer * buffer)
2152 {
2153   gboolean ret;
2154   GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_object_get_parent (parent));
2155
2156   ret = gst_play_sink_sink_chain (pad, parent, buffer, "subtitle",
2157       &playsink->text_ignore_wrong_state,
2158       &playsink->text_custom_flush_finished,
2159       &playsink->text_pending_flush, &playsink->text_segment);
2160
2161   gst_object_unref (playsink);
2162   return ret;
2163 }
2164
2165 static gboolean
2166 gst_play_sink_text_src_event (GstPad * pad, GstObject * parent,
2167     GstEvent * event)
2168 {
2169   gboolean ret;
2170   const GstStructure *structure;
2171
2172   GST_DEBUG_OBJECT (pad, "Got event %" GST_PTR_FORMAT, event);
2173
2174   structure = gst_event_get_structure (event);
2175
2176   if (structure &&
2177       gst_structure_id_has_field (structure,
2178           _playsink_reset_segment_event_marker_id)) {
2179     /* the events marked with a reset segment marker
2180      * are sent internally to reset the queue and
2181      * must be dropped here */
2182     GST_DEBUG_OBJECT (pad, "Dropping event with reset "
2183         "segment marker set: %" GST_PTR_FORMAT, event);
2184     ret = TRUE;
2185     goto out;
2186   }
2187
2188   ret = gst_proxy_pad_event_default (pad, parent, gst_event_ref (event));
2189
2190 out:
2191   gst_event_unref (event);
2192   return ret;
2193 }
2194
2195 /* make an element for playback of video with subtitles embedded.
2196  * Only used for *raw* video streams.
2197  *
2198  *  +--------------------------------------------+
2199  *  | tbin                                       |
2200  *  |     +--------+      +-----------------+    |
2201  *  |     | queue  |      | subtitleoverlay |    |
2202  * video--src     sink---video_sink         |    |
2203  *  |     +--------+     |                src--src
2204  * text------------------text_sink          |    |
2205  *  |                     +-----------------+    |
2206  *  +--------------------------------------------+
2207  *
2208  */
2209 static GstPlayTextChain *
2210 gen_text_chain (GstPlaySink * playsink)
2211 {
2212   GstPlayTextChain *chain;
2213   GstBin *bin;
2214   GstElement *elem;
2215   GstPad *videosinkpad, *textsinkpad, *srcpad;
2216
2217   chain = g_new0 (GstPlayTextChain, 1);
2218   chain->chain.playsink = playsink;
2219
2220   GST_DEBUG_OBJECT (playsink, "making text chain %p", chain);
2221
2222   chain->chain.bin = gst_bin_new ("tbin");
2223   bin = GST_BIN_CAST (chain->chain.bin);
2224   gst_object_ref_sink (bin);
2225
2226   videosinkpad = textsinkpad = srcpad = NULL;
2227
2228   /* first try to hook the text pad to the custom sink */
2229   if (playsink->text_sink) {
2230     GST_DEBUG_OBJECT (playsink, "trying configured textsink");
2231     chain->sink = try_element (playsink, playsink->text_sink, FALSE);
2232     if (chain->sink) {
2233       elem =
2234           gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
2235           G_TYPE_BOOLEAN);
2236       if (elem) {
2237         /* make sure the sparse subtitles don't participate in the preroll */
2238         g_object_set (elem, "async", FALSE, NULL);
2239         GST_DEBUG_OBJECT (playsink, "adding custom text sink");
2240         gst_bin_add (bin, chain->sink);
2241         /* NOTE streamsynchronizer needs streams decoupled */
2242         /* make a little queue */
2243         chain->queue = gst_element_factory_make ("queue", "subqueue");
2244         if (chain->queue == NULL) {
2245           post_missing_element_message (playsink, "queue");
2246           GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2247               (_("Missing element '%s' - check your GStreamer installation."),
2248                   "queue"), ("rendering might be suboptimal"));
2249         } else {
2250           g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
2251               "max-size-bytes", 0, "max-size-time", (gint64) GST_SECOND,
2252               "silent", TRUE, NULL);
2253           gst_bin_add (bin, chain->queue);
2254         }
2255         /* we have a custom sink, this will be our textsinkpad */
2256         if (gst_element_link_pads_full (chain->queue, "src", chain->sink,
2257                 "sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
2258           /* we're all fine now and we can add the sink to the chain */
2259           GST_DEBUG_OBJECT (playsink, "using custom text sink");
2260           textsinkpad = gst_element_get_static_pad (chain->queue, "sink");
2261         } else {
2262           GST_WARNING_OBJECT (playsink,
2263               "can't find a sink pad on custom text sink");
2264           gst_bin_remove (bin, chain->sink);
2265           gst_bin_remove (bin, chain->queue);
2266           chain->sink = NULL;
2267           chain->queue = NULL;
2268         }
2269         /* try to set sync to true but it's no biggie when we can't */
2270         if (chain->sink && (elem =
2271                 gst_play_sink_find_property_sinks (playsink, chain->sink,
2272                     "sync", G_TYPE_BOOLEAN)))
2273           g_object_set (elem, "sync", TRUE, NULL);
2274
2275         if (!textsinkpad)
2276           gst_bin_remove (bin, chain->sink);
2277       } else {
2278         GST_WARNING_OBJECT (playsink,
2279             "can't find async property in custom text sink");
2280       }
2281     }
2282     if (textsinkpad == NULL) {
2283       GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2284           (_("Custom text sink element is not usable.")),
2285           ("fallback to default subtitleoverlay"));
2286     }
2287   }
2288
2289   if (textsinkpad == NULL) {
2290     if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
2291       /* make a little queue */
2292       chain->queue = gst_element_factory_make ("queue", "vqueue");
2293       if (chain->queue == NULL) {
2294         post_missing_element_message (playsink, "queue");
2295         GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2296             (_("Missing element '%s' - check your GStreamer installation."),
2297                 "queue"), ("video rendering might be suboptimal"));
2298       } else {
2299         g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
2300             "max-size-bytes", 0, "max-size-time", (gint64) 0,
2301             "silent", TRUE, NULL);
2302         gst_bin_add (bin, chain->queue);
2303         videosinkpad = gst_element_get_static_pad (chain->queue, "sink");
2304       }
2305
2306       chain->overlay =
2307           gst_element_factory_make ("subtitleoverlay", "suboverlay");
2308       if (chain->overlay == NULL) {
2309         post_missing_element_message (playsink, "subtitleoverlay");
2310         GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2311             (_("Missing element '%s' - check your GStreamer installation."),
2312                 "subtitleoverlay"), ("subtitle rendering disabled"));
2313       } else {
2314         GstElement *element;
2315
2316         gst_bin_add (bin, chain->overlay);
2317
2318         g_object_set (G_OBJECT (chain->overlay), "silent", FALSE, NULL);
2319         if (playsink->font_desc) {
2320           g_object_set (G_OBJECT (chain->overlay), "font-desc",
2321               playsink->font_desc, NULL);
2322         }
2323         if (playsink->subtitle_encoding) {
2324           g_object_set (G_OBJECT (chain->overlay), "subtitle-encoding",
2325               playsink->subtitle_encoding, NULL);
2326         }
2327
2328         gst_element_link_pads_full (chain->queue, "src", chain->overlay,
2329             "video_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS);
2330
2331         /* make another little queue to decouple streams */
2332         element = gst_element_factory_make ("queue", "subqueue");
2333         if (element == NULL) {
2334           post_missing_element_message (playsink, "queue");
2335           GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2336               (_("Missing element '%s' - check your GStreamer installation."),
2337                   "queue"), ("rendering might be suboptimal"));
2338         } else {
2339           g_object_set (G_OBJECT (element), "max-size-buffers", 3,
2340               "max-size-bytes", 0, "max-size-time", (gint64) GST_SECOND,
2341               "silent", TRUE, NULL);
2342           gst_bin_add (bin, element);
2343           if (gst_element_link_pads_full (element, "src", chain->overlay,
2344                   "subtitle_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
2345             textsinkpad = gst_element_get_static_pad (element, "sink");
2346             srcpad = gst_element_get_static_pad (chain->overlay, "src");
2347           } else {
2348             gst_bin_remove (bin, chain->sink);
2349             gst_bin_remove (bin, chain->overlay);
2350             chain->sink = NULL;
2351             chain->overlay = NULL;
2352             gst_object_unref (videosinkpad);
2353             videosinkpad = NULL;
2354           }
2355         }
2356       }
2357     }
2358   }
2359
2360   if (videosinkpad == NULL) {
2361     /* if we still don't have a videosink, we don't have an overlay. the only
2362      * thing we can do is insert an identity and ghost the src
2363      * and sink pads. */
2364     chain->identity = gst_element_factory_make ("identity", "tidentity");
2365     if (chain->identity == NULL) {
2366       post_missing_element_message (playsink, "identity");
2367       GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2368           (_("Missing element '%s' - check your GStreamer installation."),
2369               "identity"), (NULL));
2370     } else {
2371       g_object_set (chain->identity, "signal-handoffs", FALSE, NULL);
2372       g_object_set (chain->identity, "silent", TRUE, NULL);
2373       gst_bin_add (bin, chain->identity);
2374       srcpad = gst_element_get_static_pad (chain->identity, "src");
2375       videosinkpad = gst_element_get_static_pad (chain->identity, "sink");
2376     }
2377   }
2378
2379   /* expose the ghostpads */
2380   if (videosinkpad) {
2381     chain->videosinkpad = gst_ghost_pad_new ("sink", videosinkpad);
2382     gst_object_unref (videosinkpad);
2383     gst_element_add_pad (chain->chain.bin, chain->videosinkpad);
2384   }
2385   if (textsinkpad) {
2386     chain->textsinkpad = gst_ghost_pad_new ("text_sink", textsinkpad);
2387     gst_object_unref (textsinkpad);
2388
2389     gst_pad_set_event_function (chain->textsinkpad,
2390         GST_DEBUG_FUNCPTR (gst_play_sink_text_sink_event));
2391     gst_pad_set_chain_function (chain->textsinkpad,
2392         GST_DEBUG_FUNCPTR (gst_play_sink_text_sink_chain));
2393
2394     gst_element_add_pad (chain->chain.bin, chain->textsinkpad);
2395   }
2396   if (srcpad) {
2397     chain->srcpad = gst_ghost_pad_new ("src", srcpad);
2398     gst_object_unref (srcpad);
2399
2400     gst_pad_set_event_function (chain->srcpad,
2401         GST_DEBUG_FUNCPTR (gst_play_sink_text_src_event));
2402
2403     gst_element_add_pad (chain->chain.bin, chain->srcpad);
2404   }
2405
2406   return chain;
2407 }
2408
2409 static void
2410 notify_volume_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
2411 {
2412   gdouble vol;
2413
2414   g_object_get (object, "volume", &vol, NULL);
2415   playsink->volume = vol;
2416
2417   g_object_notify (G_OBJECT (playsink), "volume");
2418 }
2419
2420 static void
2421 notify_mute_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
2422 {
2423   gboolean mute;
2424
2425   g_object_get (object, "mute", &mute, NULL);
2426   playsink->mute = mute;
2427
2428   g_object_notify (G_OBJECT (playsink), "mute");
2429 }
2430
2431 /* make the chain that contains the elements needed to perform
2432  * audio playback.
2433  *
2434  * We add a tee as the first element so that we can link the visualisation chain
2435  * to it when requested.
2436  *
2437  *  +-------------------------------------------------------------+
2438  *  | abin                                                        |
2439  *  |      +---------+   +----------+   +---------+   +---------+ |
2440  *  |      |audioconv|   |audioscale|   | volume  |   |audiosink| |
2441  *  |   +-srck      src-sink       src-sink      src-sink       | |
2442  *  |   |  +---------+   +----------+   +---------+   +---------+ |
2443  * sink-+                                                         |
2444  *  +-------------------------------------------------------------+
2445  */
2446 static GstPlayAudioChain *
2447 gen_audio_chain (GstPlaySink * playsink, gboolean raw)
2448 {
2449   GstPlayAudioChain *chain;
2450   GstBin *bin;
2451   gboolean have_volume;
2452   GstPad *pad;
2453   GstElement *head, *prev, *elem = NULL;
2454
2455   chain = g_new0 (GstPlayAudioChain, 1);
2456   chain->chain.playsink = playsink;
2457   chain->chain.raw = raw;
2458
2459   GST_DEBUG_OBJECT (playsink, "making audio chain %p", chain);
2460
2461   if (playsink->audio_sink) {
2462     GST_DEBUG_OBJECT (playsink, "trying configured audiosink %" GST_PTR_FORMAT,
2463         playsink->audio_sink);
2464     chain->sink = try_element (playsink, playsink->audio_sink, FALSE);
2465   } else {
2466     /* only try fallback if no specific sink was chosen */
2467     if (chain->sink == NULL) {
2468       GST_DEBUG_OBJECT (playsink, "trying autoaudiosink");
2469       elem = gst_element_factory_make ("autoaudiosink", "audiosink");
2470       chain->sink = try_element (playsink, elem, TRUE);
2471     }
2472     if (chain->sink == NULL) {
2473       /* if default sink from config.h is different then try it too */
2474       if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
2475         GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_AUDIOSINK);
2476         elem = gst_element_factory_make (DEFAULT_AUDIOSINK, "audiosink");
2477         chain->sink = try_element (playsink, elem, TRUE);
2478       }
2479     }
2480     if (chain->sink)
2481       playsink->audio_sink = gst_object_ref (chain->sink);
2482   }
2483   if (chain->sink == NULL)
2484     goto no_sinks;
2485
2486   chain->chain.bin = gst_bin_new ("abin");
2487   bin = GST_BIN_CAST (chain->chain.bin);
2488   gst_object_ref_sink (bin);
2489   gst_bin_add (bin, chain->sink);
2490
2491   /* we have to add a queue when we need to decouple for the video sink in
2492    * visualisations and for streamsynchronizer */
2493   GST_DEBUG_OBJECT (playsink, "adding audio queue");
2494   chain->queue = gst_element_factory_make ("queue", "aqueue");
2495   if (chain->queue == NULL) {
2496     post_missing_element_message (playsink, "queue");
2497     GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2498         (_("Missing element '%s' - check your GStreamer installation."),
2499             "queue"), ("audio playback and visualizations might not work"));
2500     head = chain->sink;
2501     prev = NULL;
2502   } else {
2503     g_object_set (chain->queue, "silent", TRUE, NULL);
2504     gst_bin_add (bin, chain->queue);
2505     prev = head = chain->queue;
2506   }
2507
2508   /* find ts-offset element */
2509   gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
2510       gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
2511           G_TYPE_INT64));
2512
2513   /* check if the sink, or something within the sink, has the volume property.
2514    * If it does we don't need to add a volume element.  */
2515   elem =
2516       gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
2517       G_TYPE_DOUBLE);
2518   if (elem) {
2519     chain->volume = elem;
2520
2521     g_signal_connect (chain->volume, "notify::volume",
2522         G_CALLBACK (notify_volume_cb), playsink);
2523
2524     GST_DEBUG_OBJECT (playsink, "the sink has a volume property");
2525     have_volume = TRUE;
2526     chain->sink_volume = TRUE;
2527     /* if the sink also has a mute property we can use this as well. We'll only
2528      * use the mute property if there is a volume property. We can simulate the
2529      * mute with the volume otherwise. */
2530     chain->mute =
2531         gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
2532         G_TYPE_BOOLEAN);
2533     if (chain->mute) {
2534       GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
2535       g_signal_connect (chain->mute, "notify::mute",
2536           G_CALLBACK (notify_mute_cb), playsink);
2537     }
2538     /* use the sink to control the volume and mute */
2539     if (playsink->volume_changed) {
2540       g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2541       playsink->volume_changed = FALSE;
2542     }
2543     if (playsink->mute_changed) {
2544       if (chain->mute) {
2545         g_object_set (chain->mute, "mute", playsink->mute, NULL);
2546       } else {
2547         if (playsink->mute)
2548           g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
2549       }
2550       playsink->mute_changed = FALSE;
2551     }
2552   } else {
2553     /* no volume, we need to add a volume element when we can */
2554     GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
2555     have_volume = FALSE;
2556     chain->sink_volume = FALSE;
2557   }
2558
2559   if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO) || (!have_volume
2560           && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME))) {
2561     gboolean use_converters = !(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO);
2562     gboolean use_volume =
2563         !have_volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME);
2564     GST_DEBUG_OBJECT (playsink,
2565         "creating audioconvert with use-converters %d, use-volume %d",
2566         use_converters, use_volume);
2567     chain->conv =
2568         g_object_new (GST_TYPE_PLAY_SINK_AUDIO_CONVERT, "name", "aconv",
2569         "use-converters", use_converters, "use-volume", use_volume, NULL);
2570     gst_bin_add (bin, chain->conv);
2571     if (prev) {
2572       if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
2573               GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
2574         goto link_failed;
2575     } else {
2576       head = chain->conv;
2577     }
2578     prev = chain->conv;
2579
2580     if (!have_volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
2581       GstPlaySinkAudioConvert *conv =
2582           GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
2583
2584       if (conv->volume) {
2585         chain->volume = conv->volume;
2586         have_volume = TRUE;
2587
2588         g_signal_connect (chain->volume, "notify::volume",
2589             G_CALLBACK (notify_volume_cb), playsink);
2590
2591         /* volume also has the mute property */
2592         chain->mute = chain->volume;
2593         g_signal_connect (chain->mute, "notify::mute",
2594             G_CALLBACK (notify_mute_cb), playsink);
2595
2596         /* configure with the latest volume and mute */
2597         g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume,
2598             NULL);
2599         g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
2600       }
2601     }
2602   }
2603
2604   if (prev) {
2605     /* we only have to link to the previous element if we have something in
2606      * front of the sink */
2607     GST_DEBUG_OBJECT (playsink, "linking to sink");
2608     if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
2609             GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
2610       goto link_failed;
2611   }
2612
2613   /* post a warning if we have no way to configure the volume */
2614   if (!have_volume) {
2615     GST_ELEMENT_WARNING (playsink, STREAM, NOT_IMPLEMENTED,
2616         (_("No volume control found")), ("Volume/mute is not available"));
2617   }
2618
2619   /* and ghost the sinkpad of the headmost element */
2620   GST_DEBUG_OBJECT (playsink, "ghosting sink pad");
2621   pad = gst_element_get_static_pad (head, "sink");
2622   chain->sinkpad = gst_ghost_pad_new ("sink", pad);
2623
2624   /* sending audio/video flushes break stream changes when the pipeline
2625    * is paused and played again in 0.10 */
2626 #if 0
2627   gst_pad_set_event_function (chain->sinkpad,
2628       GST_DEBUG_FUNCPTR (gst_play_sink_audio_sink_event));
2629   gst_pad_set_chain_function (chain->sinkpad,
2630       GST_DEBUG_FUNCPTR (gst_play_sink_audio_sink_chain));
2631 #endif
2632
2633   gst_object_unref (pad);
2634   gst_element_add_pad (chain->chain.bin, chain->sinkpad);
2635
2636   return chain;
2637
2638   /* ERRORS */
2639 no_sinks:
2640   {
2641     if (!elem && !playsink->audio_sink) {
2642       post_missing_element_message (playsink, "autoaudiosink");
2643       if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
2644         post_missing_element_message (playsink, DEFAULT_AUDIOSINK);
2645         GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2646             (_("Both autoaudiosink and %s elements are missing."),
2647                 DEFAULT_AUDIOSINK), (NULL));
2648       } else {
2649         GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2650             (_("The autoaudiosink element is missing.")), (NULL));
2651       }
2652     } else {
2653       if (playsink->audio_sink) {
2654         GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2655             (_("Configured audiosink %s is not working."),
2656                 GST_ELEMENT_NAME (playsink->audio_sink)), (NULL));
2657       } else if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
2658         GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2659             (_("Both autoaudiosink and %s elements are not working."),
2660                 DEFAULT_AUDIOSINK), (NULL));
2661       } else {
2662         GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2663             (_("The autoaudiosink element is not working.")), (NULL));
2664       }
2665     }
2666     free_chain ((GstPlayChain *) chain);
2667     return NULL;
2668   }
2669 link_failed:
2670   {
2671     GST_ELEMENT_ERROR (playsink, CORE, PAD,
2672         (NULL), ("Failed to configure the audio sink."));
2673     /* checking sink made it READY */
2674     gst_element_set_state (chain->sink, GST_STATE_NULL);
2675     /* Remove chain from the bin to allow reuse later */
2676     gst_bin_remove (bin, chain->sink);
2677     free_chain ((GstPlayChain *) chain);
2678     return NULL;
2679   }
2680 }
2681
2682 static gboolean
2683 setup_audio_chain (GstPlaySink * playsink, gboolean raw)
2684 {
2685   GstElement *elem;
2686   GstPlayAudioChain *chain;
2687   GstStateChangeReturn ret;
2688   GstPlaySinkAudioConvert *conv;
2689
2690   chain = playsink->audiochain;
2691   conv = GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
2692
2693   chain->chain.raw = raw;
2694
2695   /* if the chain was active we don't do anything */
2696   if (GST_PLAY_CHAIN (chain)->activated == TRUE)
2697     return TRUE;
2698
2699   /* try to set the sink element to READY again */
2700   ret = gst_element_set_state (chain->sink, GST_STATE_READY);
2701   if (ret == GST_STATE_CHANGE_FAILURE)
2702     return FALSE;
2703
2704   /* find ts-offset element */
2705   gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
2706       gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
2707           G_TYPE_INT64));
2708
2709   /* check if the sink, or something within the sink, has the volume property.
2710    * If it does we don't need to add a volume element.  */
2711   elem =
2712       gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
2713       G_TYPE_DOUBLE);
2714   if (elem) {
2715     chain->volume = elem;
2716
2717     if (playsink->volume_changed) {
2718       GST_DEBUG_OBJECT (playsink, "the sink has a volume property, setting %f",
2719           playsink->volume);
2720       /* use the sink to control the volume */
2721       g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2722       playsink->volume_changed = FALSE;
2723     }
2724
2725     g_signal_connect (chain->volume, "notify::volume",
2726         G_CALLBACK (notify_volume_cb), playsink);
2727     /* if the sink also has a mute property we can use this as well. We'll only
2728      * use the mute property if there is a volume property. We can simulate the
2729      * mute with the volume otherwise. */
2730     chain->mute =
2731         gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
2732         G_TYPE_BOOLEAN);
2733     if (chain->mute) {
2734       GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
2735       g_signal_connect (chain->mute, "notify::mute",
2736           G_CALLBACK (notify_mute_cb), playsink);
2737     }
2738
2739     g_object_set (chain->conv, "use-volume", FALSE, NULL);
2740   } else if (conv) {
2741     /* no volume, we need to add a volume element when we can */
2742     g_object_set (chain->conv, "use-volume",
2743         !!(playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME), NULL);
2744     GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
2745
2746     /* Disconnect signals */
2747     disconnect_chain (chain, playsink);
2748
2749     if (conv->volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
2750       chain->volume = conv->volume;
2751       chain->mute = chain->volume;
2752
2753       g_signal_connect (chain->volume, "notify::volume",
2754           G_CALLBACK (notify_volume_cb), playsink);
2755
2756       g_signal_connect (chain->mute, "notify::mute",
2757           G_CALLBACK (notify_mute_cb), playsink);
2758
2759       /* configure with the latest volume and mute */
2760       g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2761       g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
2762     }
2763
2764     GST_DEBUG_OBJECT (playsink, "reusing existing volume element");
2765   }
2766   return TRUE;
2767 }
2768
2769 /*
2770  *  +-------------------------------------------------------------------+
2771  *  | visbin                                                            |
2772  *  |      +----------+   +------------+   +----------+   +-------+     |
2773  *  |      | visqueue |   | audioconv  |   | audiores |   |  vis  |     |
2774  *  |   +-sink       src-sink + samp  src-sink       src-sink    src-+  |
2775  *  |   |  +----------+   +------------+   +----------+   +-------+  |  |
2776  * sink-+                                                            +-src
2777  *  +-------------------------------------------------------------------+
2778  *
2779  */
2780 static GstPlayVisChain *
2781 gen_vis_chain (GstPlaySink * playsink)
2782 {
2783   GstPlayVisChain *chain;
2784   GstBin *bin;
2785   gboolean res;
2786   GstPad *pad;
2787   GstElement *elem;
2788
2789   chain = g_new0 (GstPlayVisChain, 1);
2790   chain->chain.playsink = playsink;
2791
2792   GST_DEBUG_OBJECT (playsink, "making vis chain %p", chain);
2793
2794   chain->chain.bin = gst_bin_new ("visbin");
2795   bin = GST_BIN_CAST (chain->chain.bin);
2796   gst_object_ref_sink (bin);
2797
2798   /* we're queuing raw audio here, we can remove this queue when we can disable
2799    * async behaviour in the video sink. */
2800   chain->queue = gst_element_factory_make ("queue", "visqueue");
2801   if (chain->queue == NULL)
2802     goto no_queue;
2803   g_object_set (chain->queue, "silent", TRUE, NULL);
2804   gst_bin_add (bin, chain->queue);
2805
2806   chain->conv = gst_element_factory_make ("audioconvert", "aconv");
2807   if (chain->conv == NULL)
2808     goto no_audioconvert;
2809   gst_bin_add (bin, chain->conv);
2810
2811   chain->resample = gst_element_factory_make ("audioresample", "aresample");
2812   if (chain->resample == NULL)
2813     goto no_audioresample;
2814   gst_bin_add (bin, chain->resample);
2815
2816   /* this pad will be used for blocking the dataflow and switching the vis
2817    * plugin */
2818   chain->blockpad = gst_element_get_static_pad (chain->resample, "src");
2819
2820   if (playsink->visualisation) {
2821     GST_DEBUG_OBJECT (playsink, "trying configure vis");
2822     chain->vis = try_element (playsink, playsink->visualisation, FALSE);
2823   }
2824   if (chain->vis == NULL) {
2825     GST_DEBUG_OBJECT (playsink, "trying goom");
2826     elem = gst_element_factory_make ("goom", "vis");
2827     chain->vis = try_element (playsink, elem, TRUE);
2828   }
2829   if (chain->vis == NULL)
2830     goto no_goom;
2831
2832   gst_bin_add (bin, chain->vis);
2833
2834   res = gst_element_link_pads_full (chain->queue, "src", chain->conv, "sink",
2835       GST_PAD_LINK_CHECK_NOTHING);
2836   res &=
2837       gst_element_link_pads_full (chain->conv, "src", chain->resample, "sink",
2838       GST_PAD_LINK_CHECK_NOTHING);
2839   res &=
2840       gst_element_link_pads_full (chain->resample, "src", chain->vis, "sink",
2841       GST_PAD_LINK_CHECK_NOTHING);
2842   if (!res)
2843     goto link_failed;
2844
2845   chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
2846   chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
2847
2848   pad = gst_element_get_static_pad (chain->queue, "sink");
2849   chain->sinkpad = gst_ghost_pad_new ("sink", pad);
2850   gst_object_unref (pad);
2851   gst_element_add_pad (chain->chain.bin, chain->sinkpad);
2852
2853   chain->srcpad = gst_ghost_pad_new ("src", chain->vissrcpad);
2854   gst_element_add_pad (chain->chain.bin, chain->srcpad);
2855
2856   return chain;
2857
2858   /* ERRORS */
2859 no_queue:
2860   {
2861     post_missing_element_message (playsink, "queue");
2862     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2863         (_("Missing element '%s' - check your GStreamer installation."),
2864             "queue"), (NULL));
2865     free_chain ((GstPlayChain *) chain);
2866     return NULL;
2867   }
2868 no_audioconvert:
2869   {
2870     post_missing_element_message (playsink, "audioconvert");
2871     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2872         (_("Missing element '%s' - check your GStreamer installation."),
2873             "audioconvert"), ("possibly a liboil version mismatch?"));
2874     free_chain ((GstPlayChain *) chain);
2875     return NULL;
2876   }
2877 no_audioresample:
2878   {
2879     post_missing_element_message (playsink, "audioresample");
2880     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2881         (_("Missing element '%s' - check your GStreamer installation."),
2882             "audioresample"), (NULL));
2883     free_chain ((GstPlayChain *) chain);
2884     return NULL;
2885   }
2886 no_goom:
2887   {
2888     post_missing_element_message (playsink, "goom");
2889     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2890         (_("Missing element '%s' - check your GStreamer installation."),
2891             "goom"), (NULL));
2892     free_chain ((GstPlayChain *) chain);
2893     return NULL;
2894   }
2895 link_failed:
2896   {
2897     GST_ELEMENT_ERROR (playsink, CORE, PAD,
2898         (NULL), ("Failed to configure the visualisation element."));
2899     /* element made it to READY */
2900     gst_element_set_state (chain->vis, GST_STATE_NULL);
2901     free_chain ((GstPlayChain *) chain);
2902     return NULL;
2903   }
2904 }
2905
2906 /* this function is called when all the request pads are requested and when we
2907  * have to construct the final pipeline. Based on the flags we construct the
2908  * final output pipelines.
2909  */
2910 gboolean
2911 gst_play_sink_reconfigure (GstPlaySink * playsink)
2912 {
2913   GstPlayFlags flags;
2914   gboolean need_audio, need_video, need_deinterlace, need_vis, need_text;
2915
2916   GST_DEBUG_OBJECT (playsink, "reconfiguring");
2917
2918   /* assume we need nothing */
2919   need_audio = need_video = need_deinterlace = need_vis = need_text = FALSE;
2920
2921   GST_PLAY_SINK_LOCK (playsink);
2922   GST_OBJECT_LOCK (playsink);
2923   /* get flags, there are protected with the object lock */
2924   flags = playsink->flags;
2925   GST_OBJECT_UNLOCK (playsink);
2926
2927   /* figure out which components we need */
2928   if (flags & GST_PLAY_FLAG_TEXT && playsink->text_pad) {
2929     /* we have subtitles and we are requested to show it */
2930     need_text = TRUE;
2931   }
2932
2933   GST_OBJECT_LOCK (playsink);
2934   if (playsink->overlay_element)
2935     gst_object_unref (playsink->overlay_element);
2936   playsink->overlay_element = NULL;
2937
2938   if (playsink->colorbalance_element) {
2939     g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
2940         G_CALLBACK (colorbalance_value_changed_cb), playsink);
2941     gst_object_unref (playsink->colorbalance_element);
2942   }
2943   playsink->colorbalance_element = NULL;
2944   GST_OBJECT_UNLOCK (playsink);
2945
2946   if (((flags & GST_PLAY_FLAG_VIDEO)
2947           || (flags & GST_PLAY_FLAG_NATIVE_VIDEO)) && playsink->video_pad) {
2948     /* we have video and we are requested to show it */
2949     need_video = TRUE;
2950
2951     /* we only deinterlace if native video is not requested and
2952      * we have raw video */
2953     if ((flags & GST_PLAY_FLAG_DEINTERLACE)
2954         && !(flags & GST_PLAY_FLAG_NATIVE_VIDEO) && playsink->video_pad_raw)
2955       need_deinterlace = TRUE;
2956   }
2957
2958   if (playsink->audio_pad) {
2959     if ((flags & GST_PLAY_FLAG_AUDIO) || (flags & GST_PLAY_FLAG_NATIVE_AUDIO)) {
2960       need_audio = TRUE;
2961     }
2962     if (playsink->audio_pad_raw) {
2963       /* only can do vis with raw uncompressed audio */
2964       if (flags & GST_PLAY_FLAG_VIS && !need_video) {
2965         /* also add video when we add visualisation */
2966         need_video = TRUE;
2967         need_vis = TRUE;
2968       }
2969     }
2970   }
2971
2972   /* we have a text_pad and we need text rendering, in this case we need a
2973    * video_pad to combine the video with the text or visualizations */
2974   if (need_text && !need_video && !playsink->text_sink) {
2975     if (playsink->video_pad) {
2976       need_video = TRUE;
2977     } else if (need_audio) {
2978       GST_ELEMENT_WARNING (playsink, STREAM, FORMAT,
2979           (_("Can't play a text file without video or visualizations.")),
2980           ("Have text pad but no video pad or visualizations"));
2981       need_text = FALSE;
2982     } else {
2983       GST_ELEMENT_ERROR (playsink, STREAM, FORMAT,
2984           (_("Can't play a text file without video or visualizations.")),
2985           ("Have text pad but no video pad or visualizations"));
2986       GST_PLAY_SINK_UNLOCK (playsink);
2987       return FALSE;
2988     }
2989   }
2990
2991   GST_DEBUG_OBJECT (playsink, "audio:%d, video:%d, vis:%d, text:%d", need_audio,
2992       need_video, need_vis, need_text);
2993
2994   /* set up video pipeline */
2995   if (need_video) {
2996     gboolean raw, async;
2997
2998     /* we need a raw sink when we do vis or when we have a raw pad */
2999     raw = need_vis ? TRUE : playsink->video_pad_raw;
3000     /* we try to set the sink async=FALSE when we need vis, this way we can
3001      * avoid a queue in the audio chain. */
3002     async = !need_vis;
3003
3004     GST_DEBUG_OBJECT (playsink, "adding video, raw %d",
3005         playsink->video_pad_raw);
3006
3007     if (playsink->videochain) {
3008       /* try to reactivate the chain */
3009       if (!setup_video_chain (playsink, raw, async)) {
3010         if (playsink->video_sinkpad_stream_synchronizer) {
3011           gst_element_release_request_pad (GST_ELEMENT_CAST
3012               (playsink->stream_synchronizer),
3013               playsink->video_sinkpad_stream_synchronizer);
3014           gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3015           playsink->video_sinkpad_stream_synchronizer = NULL;
3016           gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3017           playsink->video_srcpad_stream_synchronizer = NULL;
3018         }
3019
3020         add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3021
3022         /* Remove the sink from the bin to keep its state
3023          * and unparent it to allow reuse */
3024         if (playsink->videochain->sink)
3025           gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
3026               playsink->videochain->sink);
3027
3028         activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3029         free_chain ((GstPlayChain *) playsink->videochain);
3030         playsink->videochain = NULL;
3031       }
3032     }
3033
3034     if (!playsink->videochain)
3035       playsink->videochain = gen_video_chain (playsink, raw, async);
3036     if (!playsink->videochain)
3037       goto no_chain;
3038
3039     if (!playsink->video_sinkpad_stream_synchronizer) {
3040       GValue item = { 0, };
3041       GstIterator *it;
3042
3043       playsink->video_sinkpad_stream_synchronizer =
3044           gst_element_get_request_pad (GST_ELEMENT_CAST
3045           (playsink->stream_synchronizer), "sink_%u");
3046       it = gst_pad_iterate_internal_links
3047           (playsink->video_sinkpad_stream_synchronizer);
3048       g_assert (it);
3049       gst_iterator_next (it, &item);
3050       playsink->video_srcpad_stream_synchronizer = g_value_dup_object (&item);
3051       g_value_unset (&item);
3052       g_assert (playsink->video_srcpad_stream_synchronizer);
3053       gst_iterator_free (it);
3054     }
3055
3056     if (playsink->video_pad)
3057       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad),
3058           playsink->video_sinkpad_stream_synchronizer);
3059
3060     if (need_deinterlace) {
3061       if (!playsink->videodeinterlacechain)
3062         playsink->videodeinterlacechain =
3063             gen_video_deinterlace_chain (playsink);
3064       if (!playsink->videodeinterlacechain)
3065         goto no_chain;
3066
3067       GST_DEBUG_OBJECT (playsink, "adding video deinterlace chain");
3068
3069       GST_DEBUG_OBJECT (playsink, "setting up deinterlacing chain");
3070
3071       add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
3072       activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
3073
3074       gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3075           playsink->videodeinterlacechain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3076     } else {
3077       if (playsink->videodeinterlacechain) {
3078         add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3079         activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
3080             FALSE);
3081       }
3082     }
3083
3084     GST_DEBUG_OBJECT (playsink, "adding video chain");
3085     add_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
3086     activate_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
3087     /* if we are not part of vis or subtitles, set the ghostpad target */
3088     if (!need_vis && !need_text && (!playsink->textchain
3089             || !playsink->text_pad)) {
3090       GST_DEBUG_OBJECT (playsink, "ghosting video sinkpad");
3091       if (need_deinterlace)
3092         gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
3093             playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3094       else
3095         gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3096             playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3097     }
3098   } else {
3099     GST_DEBUG_OBJECT (playsink, "no video needed");
3100     if (playsink->videochain) {
3101       GST_DEBUG_OBJECT (playsink, "removing video chain");
3102       if (playsink->vischain) {
3103         GstPad *srcpad;
3104
3105         GST_DEBUG_OBJECT (playsink, "unlinking vis chain");
3106
3107         /* also had visualisation, release the tee srcpad before we then
3108          * unlink the video from it */
3109         if (playsink->audio_tee_vissrc) {
3110           gst_element_release_request_pad (playsink->audio_tee,
3111               playsink->audio_tee_vissrc);
3112           gst_object_unref (playsink->audio_tee_vissrc);
3113           playsink->audio_tee_vissrc = NULL;
3114         }
3115         srcpad =
3116             gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
3117         gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
3118       }
3119
3120       if (playsink->video_sinkpad_stream_synchronizer) {
3121         gst_element_release_request_pad (GST_ELEMENT_CAST
3122             (playsink->stream_synchronizer),
3123             playsink->video_sinkpad_stream_synchronizer);
3124         gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3125         playsink->video_sinkpad_stream_synchronizer = NULL;
3126         gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3127         playsink->video_srcpad_stream_synchronizer = NULL;
3128       }
3129
3130       add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3131       activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3132       if (playsink->videochain->ts_offset)
3133         gst_object_unref (playsink->videochain->ts_offset);
3134       playsink->videochain->ts_offset = NULL;
3135     }
3136
3137     if (playsink->videodeinterlacechain) {
3138       add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3139       activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3140     }
3141
3142     if (playsink->video_pad)
3143       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
3144   }
3145
3146   if (need_audio) {
3147     gboolean raw;
3148
3149     GST_DEBUG_OBJECT (playsink, "adding audio");
3150
3151     /* get a raw sink if we are asked for a raw pad */
3152     raw = playsink->audio_pad_raw;
3153
3154     if (playsink->audiochain) {
3155       /* try to reactivate the chain */
3156       if (!setup_audio_chain (playsink, raw)) {
3157         GST_DEBUG_OBJECT (playsink, "removing current audio chain");
3158         if (playsink->audio_tee_asrc) {
3159           gst_element_release_request_pad (playsink->audio_tee,
3160               playsink->audio_tee_asrc);
3161           gst_object_unref (playsink->audio_tee_asrc);
3162           playsink->audio_tee_asrc = NULL;
3163         }
3164
3165         if (playsink->audio_sinkpad_stream_synchronizer) {
3166           gst_element_release_request_pad (GST_ELEMENT_CAST
3167               (playsink->stream_synchronizer),
3168               playsink->audio_sinkpad_stream_synchronizer);
3169           gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
3170           playsink->audio_sinkpad_stream_synchronizer = NULL;
3171           gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
3172           playsink->audio_srcpad_stream_synchronizer = NULL;
3173         }
3174
3175         add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3176
3177         /* Remove the sink from the bin to keep its state
3178          * and unparent it to allow reuse */
3179         if (playsink->audiochain->sink)
3180           gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
3181               playsink->audiochain->sink);
3182
3183         activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3184         disconnect_chain (playsink->audiochain, playsink);
3185         playsink->audiochain->volume = NULL;
3186         playsink->audiochain->mute = NULL;
3187         if (playsink->audiochain->ts_offset)
3188           gst_object_unref (playsink->audiochain->ts_offset);
3189         playsink->audiochain->ts_offset = NULL;
3190         free_chain ((GstPlayChain *) playsink->audiochain);
3191         playsink->audiochain = NULL;
3192         playsink->volume_changed = playsink->mute_changed = FALSE;
3193       }
3194     }
3195
3196     if (!playsink->audiochain) {
3197       GST_DEBUG_OBJECT (playsink, "creating new audio chain");
3198       playsink->audiochain = gen_audio_chain (playsink, raw);
3199     }
3200
3201     if (!playsink->audio_sinkpad_stream_synchronizer) {
3202       GValue item = { 0, };
3203       GstIterator *it;
3204
3205       playsink->audio_sinkpad_stream_synchronizer =
3206           gst_element_get_request_pad (GST_ELEMENT_CAST
3207           (playsink->stream_synchronizer), "sink_%u");
3208       it = gst_pad_iterate_internal_links
3209           (playsink->audio_sinkpad_stream_synchronizer);
3210       g_assert (it);
3211       gst_iterator_next (it, &item);
3212       playsink->audio_srcpad_stream_synchronizer = g_value_dup_object (&item);
3213       g_value_unset (&item);
3214       g_assert (playsink->audio_srcpad_stream_synchronizer);
3215       gst_iterator_free (it);
3216     }
3217
3218     if (playsink->audiochain) {
3219       GST_DEBUG_OBJECT (playsink, "adding audio chain");
3220       if (playsink->audio_tee_asrc == NULL) {
3221         playsink->audio_tee_asrc =
3222             gst_element_get_request_pad (playsink->audio_tee, "src_%u");
3223       }
3224       add_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
3225       activate_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
3226       gst_pad_link_full (playsink->audio_tee_asrc,
3227           playsink->audio_sinkpad_stream_synchronizer,
3228           GST_PAD_LINK_CHECK_NOTHING);
3229       gst_pad_link_full (playsink->audio_srcpad_stream_synchronizer,
3230           playsink->audiochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3231     }
3232   } else {
3233     GST_DEBUG_OBJECT (playsink, "no audio needed");
3234     /* we have no audio or we are requested to not play audio */
3235     if (playsink->audiochain) {
3236       GST_DEBUG_OBJECT (playsink, "removing audio chain");
3237       /* release the audio pad */
3238       if (playsink->audio_tee_asrc) {
3239         gst_element_release_request_pad (playsink->audio_tee,
3240             playsink->audio_tee_asrc);
3241         gst_object_unref (playsink->audio_tee_asrc);
3242         playsink->audio_tee_asrc = NULL;
3243       }
3244
3245       if (playsink->audio_sinkpad_stream_synchronizer) {
3246         gst_element_release_request_pad (GST_ELEMENT_CAST
3247             (playsink->stream_synchronizer),
3248             playsink->audio_sinkpad_stream_synchronizer);
3249         gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
3250         playsink->audio_sinkpad_stream_synchronizer = NULL;
3251         gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
3252         playsink->audio_srcpad_stream_synchronizer = NULL;
3253       }
3254
3255       if (playsink->audiochain->sink_volume) {
3256         disconnect_chain (playsink->audiochain, playsink);
3257         playsink->audiochain->volume = NULL;
3258         playsink->audiochain->mute = NULL;
3259         if (playsink->audiochain->ts_offset)
3260           gst_object_unref (playsink->audiochain->ts_offset);
3261         playsink->audiochain->ts_offset = NULL;
3262       }
3263       add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3264       activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3265     }
3266   }
3267
3268   if (need_vis) {
3269     GstPad *srcpad;
3270
3271     if (!playsink->vischain)
3272       playsink->vischain = gen_vis_chain (playsink);
3273
3274     GST_DEBUG_OBJECT (playsink, "adding visualisation");
3275
3276     if (playsink->vischain) {
3277       GST_DEBUG_OBJECT (playsink, "setting up vis chain");
3278       srcpad =
3279           gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
3280       add_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
3281       activate_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
3282       if (playsink->audio_tee_vissrc == NULL) {
3283         playsink->audio_tee_vissrc =
3284             gst_element_get_request_pad (playsink->audio_tee, "src_%u");
3285       }
3286       gst_pad_link_full (playsink->audio_tee_vissrc,
3287           playsink->vischain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3288       gst_pad_link_full (srcpad, playsink->video_sinkpad_stream_synchronizer,
3289           GST_PAD_LINK_CHECK_NOTHING);
3290       gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3291           playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3292       gst_object_unref (srcpad);
3293     }
3294   } else {
3295     GST_DEBUG_OBJECT (playsink, "no vis needed");
3296     if (playsink->vischain) {
3297       if (playsink->audio_tee_vissrc) {
3298         gst_element_release_request_pad (playsink->audio_tee,
3299             playsink->audio_tee_vissrc);
3300         gst_object_unref (playsink->audio_tee_vissrc);
3301         playsink->audio_tee_vissrc = NULL;
3302       }
3303       GST_DEBUG_OBJECT (playsink, "removing vis chain");
3304       add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3305       activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3306     }
3307   }
3308
3309   if (need_text) {
3310     GST_DEBUG_OBJECT (playsink, "adding text");
3311     if (!playsink->textchain) {
3312       GST_DEBUG_OBJECT (playsink, "creating text chain");
3313       playsink->textchain = gen_text_chain (playsink);
3314     }
3315     if (playsink->textchain) {
3316       GstIterator *it;
3317
3318       GST_DEBUG_OBJECT (playsink, "adding text chain");
3319       if (playsink->textchain->overlay)
3320         g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", FALSE,
3321             NULL);
3322       add_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
3323
3324       if (!playsink->text_sinkpad_stream_synchronizer) {
3325         GValue item = { 0, };
3326
3327         playsink->text_sinkpad_stream_synchronizer =
3328             gst_element_get_request_pad (GST_ELEMENT_CAST
3329             (playsink->stream_synchronizer), "sink_%u");
3330         it = gst_pad_iterate_internal_links
3331             (playsink->text_sinkpad_stream_synchronizer);
3332         g_assert (it);
3333         gst_iterator_next (it, &item);
3334         playsink->text_srcpad_stream_synchronizer = g_value_dup_object (&item);
3335         g_value_unset (&item);
3336         g_assert (playsink->text_srcpad_stream_synchronizer);
3337         gst_iterator_free (it);
3338
3339         gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad),
3340             playsink->text_sinkpad_stream_synchronizer);
3341         gst_pad_link_full (playsink->text_srcpad_stream_synchronizer,
3342             playsink->textchain->textsinkpad, GST_PAD_LINK_CHECK_NOTHING);
3343       }
3344
3345       if (need_vis || need_video) {
3346         if (need_vis) {
3347           GstPad *srcpad;
3348
3349           srcpad =
3350               gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
3351           gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
3352           gst_pad_link_full (srcpad, playsink->textchain->videosinkpad,
3353               GST_PAD_LINK_CHECK_NOTHING);
3354           gst_object_unref (srcpad);
3355         } else {
3356           if (need_deinterlace)
3357             gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
3358                 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
3359           else
3360             gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3361                 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
3362         }
3363         gst_pad_link_full (playsink->textchain->srcpad,
3364             playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3365       }
3366
3367       activate_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
3368     }
3369   } else {
3370     GST_DEBUG_OBJECT (playsink, "no text needed");
3371     /* we have no subtitles/text or we are requested to not show them */
3372
3373     if (playsink->text_sinkpad_stream_synchronizer) {
3374       gst_element_release_request_pad (GST_ELEMENT_CAST
3375           (playsink->stream_synchronizer),
3376           playsink->text_sinkpad_stream_synchronizer);
3377       gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
3378       playsink->text_sinkpad_stream_synchronizer = NULL;
3379       gst_object_unref (playsink->text_srcpad_stream_synchronizer);
3380       playsink->text_srcpad_stream_synchronizer = NULL;
3381     }
3382
3383     if (playsink->textchain) {
3384       if (playsink->text_pad == NULL) {
3385         /* no text pad, remove the chain entirely */
3386         GST_DEBUG_OBJECT (playsink, "removing text chain");
3387         add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3388         activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3389       } else {
3390         /* we have a chain and a textpad, turn the subtitles off */
3391         GST_DEBUG_OBJECT (playsink, "turning off the text");
3392         if (playsink->textchain->overlay)
3393           g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", TRUE,
3394               NULL);
3395       }
3396     }
3397     if (!need_video && playsink->video_pad) {
3398       if (playsink->video_sinkpad_stream_synchronizer) {
3399         gst_element_release_request_pad (GST_ELEMENT_CAST
3400             (playsink->stream_synchronizer),
3401             playsink->video_sinkpad_stream_synchronizer);
3402         gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3403         playsink->video_sinkpad_stream_synchronizer = NULL;
3404         gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3405         playsink->video_srcpad_stream_synchronizer = NULL;
3406       }
3407
3408       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
3409     }
3410
3411     if (playsink->text_pad && !playsink->textchain)
3412       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad), NULL);
3413   }
3414   update_av_offset (playsink);
3415   do_async_done (playsink);
3416   GST_PLAY_SINK_UNLOCK (playsink);
3417
3418   return TRUE;
3419
3420   /* ERRORS */
3421 no_chain:
3422   {
3423     /* gen_ chain already posted error */
3424     GST_DEBUG_OBJECT (playsink, "failed to setup chain");
3425     GST_PLAY_SINK_UNLOCK (playsink);
3426     return FALSE;
3427   }
3428 }
3429
3430 /**
3431  * gst_play_sink_set_flags:
3432  * @playsink: a #GstPlaySink
3433  * @flags: #GstPlayFlags
3434  *
3435  * Configure @flags on @playsink. The flags control the behaviour of @playsink
3436  * when constructing the sink pipelins.
3437  *
3438  * Returns: TRUE if the flags could be configured.
3439  */
3440 gboolean
3441 gst_play_sink_set_flags (GstPlaySink * playsink, GstPlayFlags flags)
3442 {
3443   g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), FALSE);
3444
3445   GST_OBJECT_LOCK (playsink);
3446   playsink->flags = flags;
3447   GST_OBJECT_UNLOCK (playsink);
3448
3449   return TRUE;
3450 }
3451
3452 /**
3453  * gst_play_sink_get_flags:
3454  * @playsink: a #GstPlaySink
3455  *
3456  * Get the flags of @playsink. That flags control the behaviour of the sink when
3457  * it constructs the sink pipelines.
3458  *
3459  * Returns: the currently configured #GstPlayFlags.
3460  */
3461 GstPlayFlags
3462 gst_play_sink_get_flags (GstPlaySink * playsink)
3463 {
3464   GstPlayFlags res;
3465
3466   g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), 0);
3467
3468   GST_OBJECT_LOCK (playsink);
3469   res = playsink->flags;
3470   GST_OBJECT_UNLOCK (playsink);
3471
3472   return res;
3473 }
3474
3475 void
3476 gst_play_sink_set_font_desc (GstPlaySink * playsink, const gchar * desc)
3477 {
3478   GstPlayTextChain *chain;
3479
3480   GST_PLAY_SINK_LOCK (playsink);
3481   chain = (GstPlayTextChain *) playsink->textchain;
3482   g_free (playsink->font_desc);
3483   playsink->font_desc = g_strdup (desc);
3484   if (chain && chain->overlay) {
3485     g_object_set (chain->overlay, "font-desc", desc, NULL);
3486   }
3487   GST_PLAY_SINK_UNLOCK (playsink);
3488 }
3489
3490 gchar *
3491 gst_play_sink_get_font_desc (GstPlaySink * playsink)
3492 {
3493   gchar *result = NULL;
3494   GstPlayTextChain *chain;
3495
3496   GST_PLAY_SINK_LOCK (playsink);
3497   chain = (GstPlayTextChain *) playsink->textchain;
3498   if (chain && chain->overlay) {
3499     g_object_get (chain->overlay, "font-desc", &result, NULL);
3500     playsink->font_desc = g_strdup (result);
3501   } else {
3502     result = g_strdup (playsink->font_desc);
3503   }
3504   GST_PLAY_SINK_UNLOCK (playsink);
3505
3506   return result;
3507 }
3508
3509 void
3510 gst_play_sink_set_subtitle_encoding (GstPlaySink * playsink,
3511     const gchar * encoding)
3512 {
3513   GstPlayTextChain *chain;
3514
3515   GST_PLAY_SINK_LOCK (playsink);
3516   chain = (GstPlayTextChain *) playsink->textchain;
3517   g_free (playsink->subtitle_encoding);
3518   playsink->subtitle_encoding = g_strdup (encoding);
3519   if (chain && chain->overlay) {
3520     g_object_set (chain->overlay, "subtitle-encoding", encoding, NULL);
3521   }
3522   GST_PLAY_SINK_UNLOCK (playsink);
3523 }
3524
3525 gchar *
3526 gst_play_sink_get_subtitle_encoding (GstPlaySink * playsink)
3527 {
3528   gchar *result = NULL;
3529   GstPlayTextChain *chain;
3530
3531   GST_PLAY_SINK_LOCK (playsink);
3532   chain = (GstPlayTextChain *) playsink->textchain;
3533   if (chain && chain->overlay) {
3534     g_object_get (chain->overlay, "subtitle-encoding", &result, NULL);
3535     playsink->subtitle_encoding = g_strdup (result);
3536   } else {
3537     result = g_strdup (playsink->subtitle_encoding);
3538   }
3539   GST_PLAY_SINK_UNLOCK (playsink);
3540
3541   return result;
3542 }
3543
3544 static void
3545 update_av_offset (GstPlaySink * playsink)
3546 {
3547   gint64 av_offset;
3548   GstPlayAudioChain *achain;
3549   GstPlayVideoChain *vchain;
3550
3551   av_offset = playsink->av_offset;
3552   achain = (GstPlayAudioChain *) playsink->audiochain;
3553   vchain = (GstPlayVideoChain *) playsink->videochain;
3554
3555   if (achain && vchain && achain->ts_offset && vchain->ts_offset) {
3556     g_object_set (achain->ts_offset, "ts-offset", MAX (0, -av_offset), NULL);
3557     g_object_set (vchain->ts_offset, "ts-offset", MAX (0, av_offset), NULL);
3558   } else {
3559     GST_LOG_OBJECT (playsink, "no ts_offset elements");
3560   }
3561 }
3562
3563 void
3564 gst_play_sink_set_av_offset (GstPlaySink * playsink, gint64 av_offset)
3565 {
3566   GST_PLAY_SINK_LOCK (playsink);
3567   playsink->av_offset = av_offset;
3568   update_av_offset (playsink);
3569   GST_PLAY_SINK_UNLOCK (playsink);
3570 }
3571
3572 gint64
3573 gst_play_sink_get_av_offset (GstPlaySink * playsink)
3574 {
3575   gint64 result;
3576
3577   GST_PLAY_SINK_LOCK (playsink);
3578   result = playsink->av_offset;
3579   GST_PLAY_SINK_UNLOCK (playsink);
3580
3581   return result;
3582 }
3583
3584 /**
3585  * gst_play_sink_get_last_sample:
3586  * @playsink: a #GstPlaySink
3587  *
3588  * Get the last displayed sample from @playsink. This sample is in the native
3589  * format of the sink element, the caps in the result sample contain the format
3590  * of the frame data.
3591  *
3592  * Returns: a #GstSample with the frame data or %NULL when no video frame is
3593  * available.
3594  */
3595 GstSample *
3596 gst_play_sink_get_last_sample (GstPlaySink * playsink)
3597 {
3598   GstSample *result = NULL;
3599   GstPlayVideoChain *chain;
3600
3601   GST_PLAY_SINK_LOCK (playsink);
3602   GST_DEBUG_OBJECT (playsink, "taking last sample");
3603   /* get the video chain if we can */
3604   if ((chain = (GstPlayVideoChain *) playsink->videochain)) {
3605     GST_DEBUG_OBJECT (playsink, "found video chain");
3606     /* see if the chain is active */
3607     if (chain->chain.activated && chain->sink) {
3608       GstElement *elem;
3609
3610       GST_DEBUG_OBJECT (playsink, "video chain active and has a sink");
3611
3612       /* find and get the last-buffer property now */
3613       if ((elem =
3614               gst_play_sink_find_property (playsink, chain->sink,
3615                   "last-sample", GST_TYPE_SAMPLE))) {
3616         GST_DEBUG_OBJECT (playsink, "getting last-sample property");
3617         g_object_get (elem, "last-sample", &result, NULL);
3618         gst_object_unref (elem);
3619       }
3620     }
3621   }
3622   GST_PLAY_SINK_UNLOCK (playsink);
3623
3624   return result;
3625 }
3626
3627 /**
3628  * gst_play_sink_convert_sample:
3629  * @playsink: a #GstPlaySink
3630  * @caps: a #GstCaps
3631  *
3632  * Get the last displayed frame from @playsink. If caps is %NULL, the video will
3633  * be in the native format of the sink element and the caps on the buffer
3634  * describe the format of the frame. If @caps is not %NULL, the video
3635  * frame will be converted to the format of the caps.
3636  *
3637  * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
3638  * available or when the conversion failed.
3639  */
3640 GstSample *
3641 gst_play_sink_convert_sample (GstPlaySink * playsink, GstCaps * caps)
3642 {
3643   GstSample *result;
3644   GError *err = NULL;
3645
3646   result = gst_play_sink_get_last_sample (playsink);
3647   if (result != NULL && caps != NULL) {
3648     GstSample *temp;
3649
3650     temp = gst_video_convert_sample (result, caps, 25 * GST_SECOND, &err);
3651     if (temp == NULL && err)
3652       goto error;
3653
3654     gst_sample_unref (result);
3655     result = temp;
3656   }
3657   return result;
3658
3659   /* ERRORS */
3660 error:
3661   {
3662     /* I'm really uncertain whether we should make playsink post an error
3663      * on the bus or not. It's not like it's a critical issue regarding
3664      * playsink behaviour. */
3665     GST_ERROR ("Error converting frame: %s", err->message);
3666     gst_sample_unref (result);
3667     g_error_free (err);
3668     return NULL;
3669   }
3670 }
3671
3672 static gboolean
3673 is_raw_structure (GstStructure * s)
3674 {
3675   const gchar *name;
3676
3677   name = gst_structure_get_name (s);
3678
3679   if (g_str_equal (name, "video/x-raw") || g_str_equal (name, "audio/x-raw"))
3680     return TRUE;
3681   return FALSE;
3682 }
3683
3684 static gboolean
3685 is_raw_pad (GstPad * pad)
3686 {
3687   GstPad *peer = gst_pad_get_peer (pad);
3688   GstCaps *caps;
3689   gboolean raw = TRUE;
3690
3691   if (!peer)
3692     return raw;
3693
3694   caps = gst_pad_get_current_caps (peer);
3695   if (!caps) {
3696     guint i, n;
3697
3698     caps = gst_pad_query_caps (peer, NULL);
3699
3700     n = gst_caps_get_size (caps);
3701     for (i = 0; i < n; i++) {
3702       gboolean r = is_raw_structure (gst_caps_get_structure (caps, i));
3703
3704       if (i == 0) {
3705         raw = r;
3706       } else if (raw != r) {
3707         GST_ERROR_OBJECT (pad,
3708             "Caps contains raw and non-raw structures: %" GST_PTR_FORMAT, caps);
3709         raw = FALSE;
3710         break;
3711       }
3712     }
3713   } else {
3714     raw = is_raw_structure (gst_caps_get_structure (caps, 0));
3715   }
3716   gst_caps_unref (caps);
3717   gst_object_unref (peer);
3718
3719   return raw;
3720 }
3721
3722 static GstPadProbeReturn
3723 sinkpad_blocked_cb (GstPad * blockedpad, GstPadProbeInfo * info,
3724     gpointer user_data);
3725
3726 static void
3727 video_set_blocked (GstPlaySink * playsink, gboolean blocked)
3728 {
3729   if (playsink->video_pad) {
3730     GstPad *opad =
3731         GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3732             (playsink->video_pad)));
3733     if (blocked && playsink->video_block_id == 0) {
3734       playsink->video_block_id =
3735           gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3736           sinkpad_blocked_cb, playsink, NULL);
3737     } else if (!blocked && playsink->video_block_id) {
3738       gst_pad_remove_probe (opad, playsink->video_block_id);
3739       PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO_RAW);
3740       PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO);
3741       playsink->video_block_id = 0;
3742       playsink->video_pad_blocked = FALSE;
3743     }
3744     gst_object_unref (opad);
3745   }
3746 }
3747
3748 static void
3749 audio_set_blocked (GstPlaySink * playsink, gboolean blocked)
3750 {
3751   if (playsink->audio_pad) {
3752     GstPad *opad =
3753         GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3754             (playsink->audio_pad)));
3755     if (blocked && playsink->audio_block_id == 0) {
3756       playsink->audio_block_id =
3757           gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3758           sinkpad_blocked_cb, playsink, NULL);
3759     } else if (!blocked && playsink->audio_block_id) {
3760       gst_pad_remove_probe (opad, playsink->audio_block_id);
3761       PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO_RAW);
3762       PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO);
3763       playsink->audio_block_id = 0;
3764       playsink->audio_pad_blocked = FALSE;
3765     }
3766     gst_object_unref (opad);
3767   }
3768 }
3769
3770 static void
3771 text_set_blocked (GstPlaySink * playsink, gboolean blocked)
3772 {
3773   if (playsink->text_pad) {
3774     GstPad *opad =
3775         GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3776             (playsink->text_pad)));
3777     if (blocked && playsink->text_block_id == 0) {
3778       playsink->text_block_id =
3779           gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3780           sinkpad_blocked_cb, playsink, NULL);
3781     } else if (!blocked && playsink->text_block_id) {
3782       gst_pad_remove_probe (opad, playsink->text_block_id);
3783       PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_TEXT);
3784       playsink->text_block_id = 0;
3785       playsink->text_pad_blocked = FALSE;
3786     }
3787     gst_object_unref (opad);
3788   }
3789 }
3790
3791 static GstPadProbeReturn
3792 sinkpad_blocked_cb (GstPad * blockedpad, GstPadProbeInfo * info,
3793     gpointer user_data)
3794 {
3795   GstPlaySink *playsink = (GstPlaySink *) user_data;
3796   GstPad *pad;
3797
3798   GST_PLAY_SINK_LOCK (playsink);
3799
3800   pad = GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (blockedpad)));
3801   if (pad == playsink->video_pad) {
3802     playsink->video_pad_blocked = TRUE;
3803     GST_DEBUG_OBJECT (pad, "Video pad blocked");
3804   } else if (pad == playsink->audio_pad) {
3805     playsink->audio_pad_blocked = TRUE;
3806     GST_DEBUG_OBJECT (pad, "Audio pad blocked");
3807   } else if (pad == playsink->text_pad) {
3808     playsink->text_pad_blocked = TRUE;
3809     GST_DEBUG_OBJECT (pad, "Text pad blocked");
3810   }
3811
3812   /* We reconfigure when for ALL streams:
3813    * * there isn't a pad
3814    * * OR the pad is blocked
3815    * * OR there are no pending blocks on that pad
3816    */
3817
3818   if ((!playsink->video_pad || playsink->video_pad_blocked
3819           || !PENDING_VIDEO_BLOCK (playsink)) && (!playsink->audio_pad
3820           || playsink->audio_pad_blocked || !PENDING_AUDIO_BLOCK (playsink))
3821       && (!playsink->text_pad || playsink->text_pad_blocked
3822           || !PENDING_TEXT_BLOCK (playsink))) {
3823     GST_DEBUG_OBJECT (playsink, "All pads blocked -- reconfiguring");
3824
3825     if (playsink->video_pad) {
3826       playsink->video_pad_raw = is_raw_pad (playsink->video_pad);
3827       GST_DEBUG_OBJECT (playsink, "Video pad is raw: %d",
3828           playsink->video_pad_raw);
3829     }
3830
3831     if (playsink->audio_pad) {
3832       playsink->audio_pad_raw = is_raw_pad (playsink->audio_pad);
3833       GST_DEBUG_OBJECT (playsink, "Audio pad is raw: %d",
3834           playsink->audio_pad_raw);
3835     }
3836
3837     gst_play_sink_reconfigure (playsink);
3838
3839     video_set_blocked (playsink, FALSE);
3840     audio_set_blocked (playsink, FALSE);
3841     text_set_blocked (playsink, FALSE);
3842   }
3843
3844   gst_object_unref (pad);
3845
3846   GST_PLAY_SINK_UNLOCK (playsink);
3847
3848   return GST_PAD_PROBE_OK;
3849 }
3850
3851 static void
3852 caps_notify_cb (GstPad * pad, GParamSpec * unused, GstPlaySink * playsink)
3853 {
3854   gboolean reconfigure = FALSE;
3855   GstCaps *caps;
3856   gboolean raw;
3857
3858   g_object_get (pad, "caps", &caps, NULL);
3859   if (!caps)
3860     return;
3861
3862   if (pad == playsink->audio_pad) {
3863     raw = is_raw_pad (pad);
3864     reconfigure = (!!playsink->audio_pad_raw != !!raw)
3865         && playsink->audiochain;
3866     GST_DEBUG_OBJECT (pad,
3867         "Audio caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3868         reconfigure, caps);
3869   } else if (pad == playsink->video_pad) {
3870     raw = is_raw_pad (pad);
3871     reconfigure = (!!playsink->video_pad_raw != !!raw)
3872         && playsink->videochain;
3873     GST_DEBUG_OBJECT (pad,
3874         "Video caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3875         reconfigure, caps);
3876   }
3877
3878   gst_caps_unref (caps);
3879
3880   if (reconfigure) {
3881     GST_PLAY_SINK_LOCK (playsink);
3882     video_set_blocked (playsink, TRUE);
3883     audio_set_blocked (playsink, TRUE);
3884     text_set_blocked (playsink, TRUE);
3885     GST_PLAY_SINK_UNLOCK (playsink);
3886   }
3887 }
3888
3889 void
3890 gst_play_sink_refresh_pad (GstPlaySink * playsink, GstPad * pad,
3891     GstPlaySinkType type)
3892 {
3893   gulong *block_id = NULL;
3894
3895   GST_DEBUG_OBJECT (playsink, "refresh pad %" GST_PTR_FORMAT, pad);
3896
3897   GST_PLAY_SINK_LOCK (playsink);
3898   if (pad == playsink->video_pad) {
3899     if (type != GST_PLAY_SINK_TYPE_VIDEO_RAW &&
3900         type != GST_PLAY_SINK_TYPE_VIDEO)
3901       goto wrong_type;
3902     block_id = &playsink->video_block_id;
3903   } else if (pad == playsink->audio_pad) {
3904     if (type != GST_PLAY_SINK_TYPE_AUDIO_RAW &&
3905         type != GST_PLAY_SINK_TYPE_AUDIO)
3906       goto wrong_type;
3907     block_id = &playsink->audio_block_id;
3908   } else if (pad == playsink->text_pad) {
3909     if (type != GST_PLAY_SINK_TYPE_TEXT)
3910       goto wrong_type;
3911     block_id = &playsink->text_block_id;
3912   }
3913
3914   if (type != GST_PLAY_SINK_TYPE_FLUSHING && (block_id && *block_id == 0)) {
3915     GstPad *blockpad =
3916         GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (pad)));
3917
3918     *block_id =
3919         gst_pad_add_probe (blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3920         sinkpad_blocked_cb, playsink, NULL);
3921     PENDING_FLAG_SET (playsink, type);
3922     gst_object_unref (blockpad);
3923   }
3924   GST_PLAY_SINK_UNLOCK (playsink);
3925
3926   return;
3927
3928   /* ERRORS */
3929 wrong_type:
3930   {
3931     GST_WARNING_OBJECT (playsink, "wrong type %u for pad %" GST_PTR_FORMAT,
3932         type, pad);
3933     GST_PLAY_SINK_UNLOCK (playsink);
3934     return;
3935   }
3936 }
3937
3938 /**
3939  * gst_play_sink_request_pad
3940  * @playsink: a #GstPlaySink
3941  * @type: a #GstPlaySinkType
3942  *
3943  * Create or return a pad of @type.
3944  *
3945  * Returns: a #GstPad of @type or %NULL when the pad could not be created.
3946  */
3947 GstPad *
3948 gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type)
3949 {
3950   GstPad *res = NULL;
3951   gboolean created = FALSE;
3952   gboolean activate = TRUE;
3953   const gchar *pad_name = NULL;
3954   gulong *block_id = NULL;
3955
3956   GST_DEBUG_OBJECT (playsink, "request pad type %d", type);
3957
3958   GST_PLAY_SINK_LOCK (playsink);
3959   switch (type) {
3960     case GST_PLAY_SINK_TYPE_AUDIO_RAW:
3961     case GST_PLAY_SINK_TYPE_AUDIO:
3962       pad_name = "audio_sink";
3963       if (!playsink->audio_tee) {
3964         GST_LOG_OBJECT (playsink, "creating tee");
3965         /* create tee when needed. This element will feed the audio sink chain
3966          * and the vis chain. */
3967         playsink->audio_tee = gst_element_factory_make ("tee", "audiotee");
3968         if (playsink->audio_tee == NULL) {
3969           post_missing_element_message (playsink, "tee");
3970           GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
3971               (_("Missing element '%s' - check your GStreamer installation."),
3972                   "tee"), (NULL));
3973           res = NULL;
3974           break;
3975         } else {
3976           playsink->audio_tee_sink =
3977               gst_element_get_static_pad (playsink->audio_tee, "sink");
3978           gst_bin_add (GST_BIN_CAST (playsink), playsink->audio_tee);
3979           gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
3980         }
3981       } else {
3982         gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
3983       }
3984       if (!playsink->audio_pad) {
3985         GST_LOG_OBJECT (playsink, "ghosting tee sinkpad");
3986         playsink->audio_pad =
3987             gst_ghost_pad_new (pad_name, playsink->audio_tee_sink);
3988         g_signal_connect (G_OBJECT (playsink->audio_pad), "notify::caps",
3989             G_CALLBACK (caps_notify_cb), playsink);
3990         created = TRUE;
3991       }
3992       playsink->audio_pad_raw = FALSE;
3993       res = playsink->audio_pad;
3994       block_id = &playsink->audio_block_id;
3995       break;
3996     case GST_PLAY_SINK_TYPE_VIDEO_RAW:
3997     case GST_PLAY_SINK_TYPE_VIDEO:
3998       pad_name = "video_sink";
3999       if (!playsink->video_pad) {
4000         GST_LOG_OBJECT (playsink, "ghosting videosink");
4001         playsink->video_pad =
4002             gst_ghost_pad_new_no_target (pad_name, GST_PAD_SINK);
4003         g_signal_connect (G_OBJECT (playsink->video_pad), "notify::caps",
4004             G_CALLBACK (caps_notify_cb), playsink);
4005         created = TRUE;
4006       }
4007       playsink->video_pad_raw = FALSE;
4008       res = playsink->video_pad;
4009       block_id = &playsink->video_block_id;
4010       break;
4011     case GST_PLAY_SINK_TYPE_TEXT:
4012       GST_LOG_OBJECT (playsink, "ghosting text");
4013       if (!playsink->text_pad) {
4014         playsink->text_pad =
4015             gst_ghost_pad_new_no_target ("text_sink", GST_PAD_SINK);
4016         created = TRUE;
4017       }
4018       res = playsink->text_pad;
4019       block_id = &playsink->text_block_id;
4020       break;
4021     case GST_PLAY_SINK_TYPE_FLUSHING:
4022     {
4023       gchar *padname;
4024
4025       /* we need a unique padname for the flushing pad. */
4026       padname = g_strdup_printf ("flushing_%u", playsink->count);
4027       res = gst_ghost_pad_new_no_target (padname, GST_PAD_SINK);
4028       g_free (padname);
4029       playsink->count++;
4030       activate = FALSE;
4031       created = TRUE;
4032       break;
4033     }
4034     default:
4035       res = NULL;
4036       break;
4037   }
4038   GST_PLAY_SINK_UNLOCK (playsink);
4039
4040   if (created && res) {
4041     /* we have to add the pad when it's active or we get an error when the
4042      * element is 'running' */
4043     gst_pad_set_active (res, TRUE);
4044     gst_element_add_pad (GST_ELEMENT_CAST (playsink), res);
4045     if (block_id && *block_id == 0) {
4046       GstPad *blockpad =
4047           GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (res)));
4048
4049       *block_id =
4050           gst_pad_add_probe (blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
4051           sinkpad_blocked_cb, playsink, NULL);
4052       PENDING_FLAG_SET (playsink, type);
4053       gst_object_unref (blockpad);
4054     }
4055     if (!activate)
4056       gst_pad_set_active (res, activate);
4057   }
4058
4059   return res;
4060 }
4061
4062
4063 static GstPad *
4064 gst_play_sink_request_new_pad (GstElement * element, GstPadTemplate * templ,
4065     const gchar * name, const GstCaps * caps)
4066 {
4067   GstPlaySink *psink;
4068   GstPad *pad;
4069   GstPlaySinkType type;
4070   const gchar *tplname;
4071
4072   g_return_val_if_fail (templ != NULL, NULL);
4073
4074   GST_DEBUG_OBJECT (element, "name:%s", name);
4075
4076   psink = GST_PLAY_SINK (element);
4077   tplname = GST_PAD_TEMPLATE_NAME_TEMPLATE (templ);
4078
4079   /* Figure out the GstPlaySinkType based on the template */
4080   if (!strcmp (tplname, "audio_sink"))
4081     type = GST_PLAY_SINK_TYPE_AUDIO;
4082   else if (!strcmp (tplname, "audio_raw_sink"))
4083     type = GST_PLAY_SINK_TYPE_AUDIO_RAW;
4084   else if (!strcmp (tplname, "video_sink"))
4085     type = GST_PLAY_SINK_TYPE_VIDEO;
4086   else if (!strcmp (tplname, "video_raw_sink"))
4087     type = GST_PLAY_SINK_TYPE_VIDEO_RAW;
4088   else if (!strcmp (tplname, "text_sink"))
4089     type = GST_PLAY_SINK_TYPE_TEXT;
4090   else
4091     goto unknown_template;
4092
4093   pad = gst_play_sink_request_pad (psink, type);
4094   return pad;
4095
4096 unknown_template:
4097   GST_WARNING_OBJECT (element, "Unknown pad template");
4098   return NULL;
4099 }
4100
4101 void
4102 gst_play_sink_release_pad (GstPlaySink * playsink, GstPad * pad)
4103 {
4104   GstPad **res = NULL;
4105   gboolean untarget = TRUE;
4106
4107   GST_DEBUG_OBJECT (playsink, "release pad %" GST_PTR_FORMAT, pad);
4108
4109   GST_PLAY_SINK_LOCK (playsink);
4110   if (pad == playsink->video_pad) {
4111     res = &playsink->video_pad;
4112     g_signal_handlers_disconnect_by_func (playsink->video_pad, caps_notify_cb,
4113         playsink);
4114   } else if (pad == playsink->audio_pad) {
4115     res = &playsink->audio_pad;
4116     g_signal_handlers_disconnect_by_func (playsink->audio_pad, caps_notify_cb,
4117         playsink);
4118   } else if (pad == playsink->text_pad) {
4119     res = &playsink->text_pad;
4120   } else {
4121     /* try to release the given pad anyway, these could be the FLUSHING pads. */
4122     res = &pad;
4123     untarget = FALSE;
4124   }
4125   GST_PLAY_SINK_UNLOCK (playsink);
4126
4127   if (*res) {
4128     GST_DEBUG_OBJECT (playsink, "deactivate pad %" GST_PTR_FORMAT, *res);
4129     gst_pad_set_active (*res, FALSE);
4130     if (untarget) {
4131       GST_DEBUG_OBJECT (playsink, "untargeting pad %" GST_PTR_FORMAT, *res);
4132       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (*res), NULL);
4133     }
4134     GST_DEBUG_OBJECT (playsink, "remove pad %" GST_PTR_FORMAT, *res);
4135     gst_element_remove_pad (GST_ELEMENT_CAST (playsink), *res);
4136     *res = NULL;
4137   }
4138 }
4139
4140 static void
4141 gst_play_sink_release_request_pad (GstElement * element, GstPad * pad)
4142 {
4143   GstPlaySink *psink = GST_PLAY_SINK (element);
4144
4145   gst_play_sink_release_pad (psink, pad);
4146 }
4147
4148 static void
4149 gst_play_sink_handle_message (GstBin * bin, GstMessage * message)
4150 {
4151   GstPlaySink *playsink;
4152
4153   playsink = GST_PLAY_SINK_CAST (bin);
4154
4155   switch (GST_MESSAGE_TYPE (message)) {
4156     case GST_MESSAGE_STEP_DONE:
4157     {
4158       GstFormat format;
4159       guint64 amount;
4160       gdouble rate;
4161       gboolean flush, intermediate, eos;
4162       guint64 duration;
4163
4164       GST_INFO_OBJECT (playsink, "Handling step-done message");
4165       gst_message_parse_step_done (message, &format, &amount, &rate, &flush,
4166           &intermediate, &duration, &eos);
4167
4168       if (format == GST_FORMAT_BUFFERS) {
4169         /* for the buffer format, we align the other streams */
4170         if (playsink->audiochain) {
4171           GstEvent *event;
4172
4173           event =
4174               gst_event_new_step (GST_FORMAT_TIME, duration, rate, flush,
4175               intermediate);
4176
4177           if (!gst_element_send_event (playsink->audiochain->chain.bin, event)) {
4178             GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
4179           }
4180         }
4181       }
4182       GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
4183       break;
4184     }
4185     case GST_MESSAGE_ELEMENT:{
4186       if (gst_is_video_overlay_prepare_window_handle_message (message)) {
4187         GstVideoOverlay *overlay;
4188
4189         GST_OBJECT_LOCK (playsink);
4190         if (playsink->overlay_element
4191             && GST_OBJECT_CAST (playsink->overlay_element) !=
4192             GST_MESSAGE_SRC (message)) {
4193           gst_object_unref (playsink->overlay_element);
4194           playsink->overlay_element = NULL;
4195         }
4196
4197         if (!playsink->overlay_element)
4198           playsink->overlay_element =
4199               GST_VIDEO_OVERLAY (gst_object_ref (GST_MESSAGE_SRC (message)));
4200         overlay =
4201             GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4202         GST_OBJECT_UNLOCK (playsink);
4203
4204         GST_DEBUG_OBJECT (playsink, "Got prepare-xwindow-id message");
4205
4206         if (playsink->overlay_handle_set)
4207           gst_video_overlay_set_window_handle (playsink->overlay_element,
4208               playsink->overlay_handle);
4209         if (playsink->overlay_handle_events_set)
4210           gst_video_overlay_handle_events (playsink->overlay_element,
4211               playsink->overlay_handle_events);
4212         if (playsink->overlay_render_rectangle_set)
4213           gst_video_overlay_set_render_rectangle (playsink->overlay_element,
4214               playsink->overlay_x, playsink->overlay_y,
4215               playsink->overlay_width, playsink->overlay_height);
4216
4217         gst_object_unref (overlay);
4218         gst_message_unref (message);
4219         gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (playsink));
4220       } else {
4221         GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin,
4222             message);
4223       }
4224       break;
4225     }
4226     default:
4227       GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
4228       break;
4229   }
4230 }
4231
4232 /* Send an event to our sinks until one of them works; don't then send to the
4233  * remaining sinks (unlike GstBin)
4234  * Special case: If a text sink is set we need to send the event
4235  * to them in case it's source is different from the a/v stream's source.
4236  */
4237 static gboolean
4238 gst_play_sink_send_event_to_sink (GstPlaySink * playsink, GstEvent * event)
4239 {
4240   gboolean res = TRUE;
4241   if (playsink->send_event_mode == MODE_FIRST) {
4242     if (playsink->textchain && playsink->textchain->sink) {
4243       gst_event_ref (event);
4244       if ((res =
4245               gst_element_send_event (playsink->textchain->chain.bin, event))) {
4246         GST_DEBUG_OBJECT (playsink, "Sent event successfully to text sink");
4247       } else {
4248         GST_DEBUG_OBJECT (playsink, "Event failed when sent to text sink");
4249       }
4250     }
4251
4252     if (playsink->videochain) {
4253       gst_event_ref (event);
4254       if ((res =
4255               gst_element_send_event (playsink->videochain->chain.bin,
4256                   event))) {
4257         GST_DEBUG_OBJECT (playsink, "Sent event successfully to video sink");
4258         goto done;
4259       }
4260       GST_DEBUG_OBJECT (playsink, "Event failed when sent to video sink");
4261     }
4262     if (playsink->audiochain) {
4263       gst_event_ref (event);
4264       if ((res =
4265               gst_element_send_event (playsink->audiochain->chain.bin,
4266                   event))) {
4267         GST_DEBUG_OBJECT (playsink, "Sent event successfully to audio sink");
4268         goto done;
4269       }
4270       GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
4271     }
4272   } else {
4273     return
4274         GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event
4275         (GST_ELEMENT_CAST (playsink), event);
4276   }
4277
4278 done:
4279   gst_event_unref (event);
4280   return res;
4281 }
4282
4283 /* We only want to send the event to a single sink (overriding GstBin's
4284  * behaviour), but we want to keep GstPipeline's behaviour - wrapping seek
4285  * events appropriately. So, this is a messy duplication of code. */
4286 static gboolean
4287 gst_play_sink_send_event (GstElement * element, GstEvent * event)
4288 {
4289   gboolean res = FALSE;
4290   GstEventType event_type = GST_EVENT_TYPE (event);
4291   GstPlaySink *playsink;
4292   playsink = GST_PLAY_SINK_CAST (element);
4293   switch (event_type) {
4294     case GST_EVENT_SEEK:
4295       GST_DEBUG_OBJECT (element, "Sending event to a sink");
4296       res = gst_play_sink_send_event_to_sink (playsink, event);
4297       break;
4298     case GST_EVENT_STEP:
4299     {
4300       GstFormat format;
4301       guint64 amount;
4302       gdouble rate;
4303       gboolean flush, intermediate;
4304       gst_event_parse_step (event, &format, &amount, &rate, &flush,
4305           &intermediate);
4306       if (format == GST_FORMAT_BUFFERS) {
4307         /* for buffers, we will try to step video frames, for other formats we
4308          * send the step to all sinks */
4309         res = gst_play_sink_send_event_to_sink (playsink, event);
4310       } else {
4311         res =
4312             GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
4313             event);
4314       }
4315       break;
4316     }
4317     default:
4318       res =
4319           GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
4320           event);
4321       break;
4322   }
4323   return res;
4324 }
4325
4326 static GstStateChangeReturn
4327 gst_play_sink_change_state (GstElement * element, GstStateChange transition)
4328 {
4329   GstStateChangeReturn ret;
4330   GstStateChangeReturn bret;
4331   GstPlaySink *playsink;
4332   playsink = GST_PLAY_SINK (element);
4333   switch (transition) {
4334     case GST_STATE_CHANGE_READY_TO_PAUSED:
4335       gst_segment_init (&playsink->text_segment, GST_FORMAT_UNDEFINED);
4336
4337       playsink->need_async_start = TRUE;
4338       /* we want to go async to PAUSED until we managed to configure and add the
4339        * sinks */
4340       do_async_start (playsink);
4341       ret = GST_STATE_CHANGE_ASYNC;
4342
4343       /* block all pads here */
4344       GST_PLAY_SINK_LOCK (playsink);
4345       if (playsink->video_pad && playsink->video_block_id == 0) {
4346         GstPad *opad =
4347             GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
4348                 (playsink->video_pad)));
4349         playsink->video_block_id =
4350             gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
4351             sinkpad_blocked_cb, playsink, NULL);
4352         PENDING_FLAG_SET (playsink, GST_PLAY_SINK_TYPE_VIDEO);
4353         gst_object_unref (opad);
4354       }
4355
4356       if (playsink->audio_pad && playsink->audio_block_id == 0) {
4357         GstPad *opad =
4358             GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
4359                 (playsink->audio_pad)));
4360         playsink->audio_block_id =
4361             gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
4362             sinkpad_blocked_cb, playsink, NULL);
4363         PENDING_FLAG_SET (playsink, GST_PLAY_SINK_TYPE_AUDIO);
4364         gst_object_unref (opad);
4365       }
4366
4367       if (playsink->text_pad && playsink->text_block_id == 0) {
4368         GstPad *opad =
4369             GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
4370                 (playsink->text_pad)));
4371         playsink->text_block_id =
4372             gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
4373             sinkpad_blocked_cb, playsink, NULL);
4374         PENDING_FLAG_SET (playsink, GST_PLAY_SINK_TYPE_TEXT);
4375         gst_object_unref (opad);
4376       }
4377       GST_PLAY_SINK_UNLOCK (playsink);
4378       break;
4379     case GST_STATE_CHANGE_PAUSED_TO_READY:
4380       /* unblock all pads here */
4381       GST_PLAY_SINK_LOCK (playsink);
4382       video_set_blocked (playsink, FALSE);
4383       audio_set_blocked (playsink, FALSE);
4384       text_set_blocked (playsink, FALSE);
4385       GST_PLAY_SINK_UNLOCK (playsink);
4386       /* fall through */
4387     case GST_STATE_CHANGE_READY_TO_NULL:
4388       if (playsink->audiochain && playsink->audiochain->sink_volume) {
4389         /* remove our links to the mute and volume elements when they were
4390          * provided by a sink */
4391         disconnect_chain (playsink->audiochain, playsink);
4392         playsink->audiochain->volume = NULL;
4393         playsink->audiochain->mute = NULL;
4394       }
4395
4396       if (playsink->audiochain && playsink->audiochain->ts_offset) {
4397         gst_object_unref (playsink->audiochain->ts_offset);
4398         playsink->audiochain->ts_offset = NULL;
4399       }
4400
4401       if (playsink->videochain && playsink->videochain->ts_offset) {
4402         gst_object_unref (playsink->videochain->ts_offset);
4403         playsink->videochain->ts_offset = NULL;
4404       }
4405
4406       GST_OBJECT_LOCK (playsink);
4407       if (playsink->overlay_element)
4408         gst_object_unref (playsink->overlay_element);
4409       playsink->overlay_element = NULL;
4410
4411       if (playsink->colorbalance_element) {
4412         g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
4413             G_CALLBACK (colorbalance_value_changed_cb), playsink);
4414         gst_object_unref (playsink->colorbalance_element);
4415       }
4416       playsink->colorbalance_element = NULL;
4417       GST_OBJECT_UNLOCK (playsink);
4418
4419       ret = GST_STATE_CHANGE_SUCCESS;
4420       break;
4421     default:
4422       /* all other state changes return SUCCESS by default, this value can be
4423        * overridden by the result of the children */
4424       ret = GST_STATE_CHANGE_SUCCESS;
4425       break;
4426   }
4427
4428   /* do the state change of the children */
4429   bret =
4430       GST_ELEMENT_CLASS (gst_play_sink_parent_class)->change_state (element,
4431       transition);
4432   /* now look at the result of our children and adjust the return value */
4433   switch (bret) {
4434     case GST_STATE_CHANGE_FAILURE:
4435       /* failure, we stop */
4436       goto activate_failed;
4437     case GST_STATE_CHANGE_NO_PREROLL:
4438       /* some child returned NO_PREROLL. This is strange but we never know. We
4439        * commit our async state change (if any) and return the NO_PREROLL */
4440       do_async_done (playsink);
4441       ret = bret;
4442       break;
4443     case GST_STATE_CHANGE_ASYNC:
4444       /* some child was async, return this */
4445       ret = bret;
4446       break;
4447     default:
4448       /* return our previously configured return value */
4449       break;
4450   }
4451
4452   switch (transition) {
4453     case GST_STATE_CHANGE_READY_TO_PAUSED:
4454       break;
4455     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
4456       /* FIXME Release audio device when we implement that */
4457       playsink->need_async_start = TRUE;
4458       break;
4459     case GST_STATE_CHANGE_PAUSED_TO_READY:{
4460       if (playsink->video_sinkpad_stream_synchronizer) {
4461         gst_element_release_request_pad (GST_ELEMENT_CAST
4462             (playsink->stream_synchronizer),
4463             playsink->video_sinkpad_stream_synchronizer);
4464         gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
4465         playsink->video_sinkpad_stream_synchronizer = NULL;
4466         gst_object_unref (playsink->video_srcpad_stream_synchronizer);
4467         playsink->video_srcpad_stream_synchronizer = NULL;
4468       }
4469       if (playsink->audio_sinkpad_stream_synchronizer) {
4470         gst_element_release_request_pad (GST_ELEMENT_CAST
4471             (playsink->stream_synchronizer),
4472             playsink->audio_sinkpad_stream_synchronizer);
4473         gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
4474         playsink->audio_sinkpad_stream_synchronizer = NULL;
4475         gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
4476         playsink->audio_srcpad_stream_synchronizer = NULL;
4477       }
4478       if (playsink->text_sinkpad_stream_synchronizer) {
4479         gst_element_release_request_pad (GST_ELEMENT_CAST
4480             (playsink->stream_synchronizer),
4481             playsink->text_sinkpad_stream_synchronizer);
4482         gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
4483         playsink->text_sinkpad_stream_synchronizer = NULL;
4484         gst_object_unref (playsink->text_srcpad_stream_synchronizer);
4485         playsink->text_srcpad_stream_synchronizer = NULL;
4486       }
4487     }
4488       /* fall through */
4489     case GST_STATE_CHANGE_READY_TO_NULL:
4490       /* remove sinks we added */
4491       if (playsink->videodeinterlacechain) {
4492         activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
4493             FALSE);
4494         add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
4495       }
4496       if (playsink->videochain) {
4497         activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
4498         add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
4499       }
4500       if (playsink->audiochain) {
4501         activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
4502         add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
4503       }
4504       if (playsink->vischain) {
4505         activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
4506         add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
4507       }
4508       if (playsink->textchain) {
4509         activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
4510         add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
4511       }
4512       do_async_done (playsink);
4513       /* when going to READY, keep elements around as long as possible,
4514        * so they may be re-used faster next time/url around.
4515        * when really going to NULL, clean up everything completely. */
4516       if (transition == GST_STATE_CHANGE_READY_TO_NULL) {
4517
4518         /* Unparent the sinks to allow reuse */
4519         if (playsink->videochain && playsink->videochain->sink)
4520           gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
4521               playsink->videochain->sink);
4522         if (playsink->audiochain && playsink->audiochain->sink)
4523           gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
4524               playsink->audiochain->sink);
4525         if (playsink->textchain && playsink->textchain->sink)
4526           gst_bin_remove (GST_BIN_CAST (playsink->textchain->chain.bin),
4527               playsink->textchain->sink);
4528         if (playsink->audio_sink != NULL)
4529           gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
4530         if (playsink->video_sink != NULL)
4531           gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
4532         if (playsink->visualisation != NULL)
4533           gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
4534         if (playsink->text_sink != NULL)
4535           gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
4536         free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
4537         playsink->videodeinterlacechain = NULL;
4538         free_chain ((GstPlayChain *) playsink->videochain);
4539         playsink->videochain = NULL;
4540         free_chain ((GstPlayChain *) playsink->audiochain);
4541         playsink->audiochain = NULL;
4542         free_chain ((GstPlayChain *) playsink->vischain);
4543         playsink->vischain = NULL;
4544         free_chain ((GstPlayChain *) playsink->textchain);
4545         playsink->textchain = NULL;
4546       }
4547       break;
4548     default:
4549       break;
4550   }
4551   return ret;
4552   /* ERRORS */
4553 activate_failed:
4554   {
4555     GST_DEBUG_OBJECT (element,
4556         "element failed to change states -- activation problem?");
4557     return GST_STATE_CHANGE_FAILURE;
4558   }
4559 }
4560
4561 static void
4562 gst_play_sink_set_property (GObject * object, guint prop_id,
4563     const GValue * value, GParamSpec * spec)
4564 {
4565   GstPlaySink *playsink = GST_PLAY_SINK (object);
4566   switch (prop_id) {
4567     case PROP_FLAGS:
4568       gst_play_sink_set_flags (playsink, g_value_get_flags (value));
4569       break;
4570     case PROP_VOLUME:
4571       gst_play_sink_set_volume (playsink, g_value_get_double (value));
4572       break;
4573     case PROP_MUTE:
4574       gst_play_sink_set_mute (playsink, g_value_get_boolean (value));
4575       break;
4576     case PROP_FONT_DESC:
4577       gst_play_sink_set_font_desc (playsink, g_value_get_string (value));
4578       break;
4579     case PROP_SUBTITLE_ENCODING:
4580       gst_play_sink_set_subtitle_encoding (playsink,
4581           g_value_get_string (value));
4582       break;
4583     case PROP_VIS_PLUGIN:
4584       gst_play_sink_set_vis_plugin (playsink, g_value_get_object (value));
4585       break;
4586     case PROP_AV_OFFSET:
4587       gst_play_sink_set_av_offset (playsink, g_value_get_int64 (value));
4588       break;
4589     case PROP_VIDEO_SINK:
4590       gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_VIDEO,
4591           g_value_get_object (value));
4592       break;
4593     case PROP_AUDIO_SINK:
4594       gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_AUDIO,
4595           g_value_get_object (value));
4596       break;
4597     case PROP_TEXT_SINK:
4598       gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_TEXT,
4599           g_value_get_object (value));
4600       break;
4601     case PROP_SEND_EVENT_MODE:
4602       playsink->send_event_mode = g_value_get_enum (value);
4603       break;
4604     default:
4605       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
4606       break;
4607   }
4608 }
4609
4610 static void
4611 gst_play_sink_get_property (GObject * object, guint prop_id,
4612     GValue * value, GParamSpec * spec)
4613 {
4614   GstPlaySink *playsink = GST_PLAY_SINK (object);
4615   switch (prop_id) {
4616     case PROP_FLAGS:
4617       g_value_set_flags (value, gst_play_sink_get_flags (playsink));
4618       break;
4619     case PROP_VOLUME:
4620       g_value_set_double (value, gst_play_sink_get_volume (playsink));
4621       break;
4622     case PROP_MUTE:
4623       g_value_set_boolean (value, gst_play_sink_get_mute (playsink));
4624       break;
4625     case PROP_FONT_DESC:
4626       g_value_take_string (value, gst_play_sink_get_font_desc (playsink));
4627       break;
4628     case PROP_SUBTITLE_ENCODING:
4629       g_value_take_string (value,
4630           gst_play_sink_get_subtitle_encoding (playsink));
4631       break;
4632     case PROP_VIS_PLUGIN:
4633       g_value_take_object (value, gst_play_sink_get_vis_plugin (playsink));
4634       break;
4635     case PROP_SAMPLE:
4636       gst_value_take_sample (value, gst_play_sink_get_last_sample (playsink));
4637       break;
4638     case PROP_AV_OFFSET:
4639       g_value_set_int64 (value, gst_play_sink_get_av_offset (playsink));
4640       break;
4641     case PROP_VIDEO_SINK:
4642       g_value_take_object (value, gst_play_sink_get_sink (playsink,
4643               GST_PLAY_SINK_TYPE_VIDEO));
4644       break;
4645     case PROP_AUDIO_SINK:
4646       g_value_take_object (value, gst_play_sink_get_sink (playsink,
4647               GST_PLAY_SINK_TYPE_AUDIO));
4648       break;
4649     case PROP_TEXT_SINK:
4650       g_value_take_object (value, gst_play_sink_get_sink (playsink,
4651               GST_PLAY_SINK_TYPE_TEXT));
4652       break;
4653     case PROP_SEND_EVENT_MODE:
4654       g_value_set_enum (value, playsink->send_event_mode);
4655       break;
4656     default:
4657       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
4658       break;
4659   }
4660 }
4661
4662 static void
4663 gst_play_sink_overlay_expose (GstVideoOverlay * overlay)
4664 {
4665   GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4666   GstVideoOverlay *overlay_element;
4667
4668   GST_OBJECT_LOCK (playsink);
4669   if (playsink->overlay_element)
4670     overlay_element =
4671         GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4672   else
4673     overlay_element = NULL;
4674   GST_OBJECT_UNLOCK (playsink);
4675
4676   if (overlay_element) {
4677     gst_video_overlay_expose (overlay_element);
4678     gst_object_unref (overlay_element);
4679   }
4680 }
4681
4682 static void
4683 gst_play_sink_overlay_handle_events (GstVideoOverlay * overlay,
4684     gboolean handle_events)
4685 {
4686   GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4687   GstVideoOverlay *overlay_element;
4688
4689   GST_OBJECT_LOCK (playsink);
4690   if (playsink->overlay_element)
4691     overlay_element =
4692         GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4693   else
4694     overlay_element = NULL;
4695   GST_OBJECT_UNLOCK (playsink);
4696
4697   playsink->overlay_handle_events_set = TRUE;
4698   playsink->overlay_handle_events = handle_events;
4699
4700   if (overlay_element) {
4701     gst_video_overlay_handle_events (overlay_element, handle_events);
4702     gst_object_unref (overlay_element);
4703   }
4704 }
4705
4706 static void
4707 gst_play_sink_overlay_set_render_rectangle (GstVideoOverlay * overlay, gint x,
4708     gint y, gint width, gint height)
4709 {
4710   GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4711   GstVideoOverlay *overlay_element;
4712
4713   GST_OBJECT_LOCK (playsink);
4714   if (playsink->overlay_element)
4715     overlay_element =
4716         GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4717   else
4718     overlay_element = NULL;
4719   GST_OBJECT_UNLOCK (playsink);
4720
4721   playsink->overlay_render_rectangle_set = TRUE;
4722   playsink->overlay_x = x;
4723   playsink->overlay_y = y;
4724   playsink->overlay_width = width;
4725   playsink->overlay_height = height;
4726
4727   if (overlay_element) {
4728     gst_video_overlay_set_render_rectangle (overlay_element, x, y, width,
4729         height);
4730     gst_object_unref (overlay_element);
4731   }
4732 }
4733
4734 static void
4735 gst_play_sink_overlay_set_window_handle (GstVideoOverlay * overlay,
4736     guintptr handle)
4737 {
4738   GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4739   GstVideoOverlay *overlay_element;
4740
4741   GST_OBJECT_LOCK (playsink);
4742   if (playsink->overlay_element)
4743     overlay_element =
4744         GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4745   else
4746     overlay_element = NULL;
4747   GST_OBJECT_UNLOCK (playsink);
4748
4749   playsink->overlay_handle_set = TRUE;
4750   playsink->overlay_handle = handle;
4751
4752   if (overlay_element) {
4753     gst_video_overlay_set_window_handle (overlay_element, handle);
4754     gst_object_unref (overlay_element);
4755   }
4756 }
4757
4758 static void
4759 gst_play_sink_overlay_init (gpointer g_iface, gpointer g_iface_data)
4760 {
4761   GstVideoOverlayInterface *iface = (GstVideoOverlayInterface *) g_iface;
4762   iface->expose = gst_play_sink_overlay_expose;
4763   iface->handle_events = gst_play_sink_overlay_handle_events;
4764   iface->set_render_rectangle = gst_play_sink_overlay_set_render_rectangle;
4765   iface->set_window_handle = gst_play_sink_overlay_set_window_handle;
4766 }
4767
4768 static void
4769 gst_play_sink_navigation_send_event (GstNavigation * navigation,
4770     GstStructure * structure)
4771 {
4772   GstPlaySink *playsink = GST_PLAY_SINK (navigation);
4773   GstBin *bin = NULL;
4774
4775   GST_PLAY_SINK_LOCK (playsink);
4776   if (playsink->videochain && playsink->videochain->chain.bin)
4777     bin = GST_BIN (gst_object_ref (playsink->videochain->chain.bin));
4778   GST_PLAY_SINK_UNLOCK (playsink);
4779
4780   if (bin) {
4781     GstElement *nav = gst_bin_get_by_interface (bin, GST_TYPE_NAVIGATION);
4782
4783     if (nav) {
4784       gst_navigation_send_event (GST_NAVIGATION (nav), structure);
4785       structure = NULL;
4786       gst_object_unref (nav);
4787     } else {
4788       GstEvent *event = gst_event_new_navigation (structure);
4789       structure = NULL;
4790       gst_element_send_event (GST_ELEMENT (bin), event);
4791     }
4792
4793     gst_object_unref (bin);
4794   }
4795
4796   if (structure)
4797     gst_structure_free (structure);
4798 }
4799
4800 static void
4801 gst_play_sink_navigation_init (gpointer g_iface, gpointer g_iface_data)
4802 {
4803   GstNavigationInterface *iface = (GstNavigationInterface *) g_iface;
4804
4805   iface->send_event = gst_play_sink_navigation_send_event;
4806 }
4807
4808 static const GList *
4809 gst_play_sink_colorbalance_list_channels (GstColorBalance * balance)
4810 {
4811   GstPlaySink *playsink = GST_PLAY_SINK (balance);
4812
4813   return playsink->colorbalance_channels;
4814 }
4815
4816 static void
4817 gst_play_sink_colorbalance_set_value (GstColorBalance * balance,
4818     GstColorBalanceChannel * proxy, gint value)
4819 {
4820   GstPlaySink *playsink = GST_PLAY_SINK (balance);
4821   GList *l;
4822   gint i;
4823   GstColorBalance *balance_element = NULL;
4824
4825   GST_OBJECT_LOCK (playsink);
4826   if (playsink->colorbalance_element)
4827     balance_element =
4828         GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
4829   GST_OBJECT_UNLOCK (playsink);
4830
4831   for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
4832     GstColorBalanceChannel *proxy_tmp = l->data;
4833     gdouble new_val;
4834
4835     if (proxy_tmp != proxy)
4836       continue;
4837
4838     playsink->colorbalance_values[i] = value;
4839
4840     if (balance_element) {
4841       GstColorBalanceChannel *channel = NULL;
4842       const GList *channels, *k;
4843
4844       channels = gst_color_balance_list_channels (balance_element);
4845       for (k = channels; k; k = k->next) {
4846         GstColorBalanceChannel *tmp = l->data;
4847
4848         if (g_strrstr (tmp->label, proxy->label)) {
4849           channel = tmp;
4850           break;
4851         }
4852       }
4853
4854       g_assert (channel);
4855
4856       /* Convert to [0, 1] range */
4857       new_val =
4858           ((gdouble) value -
4859           (gdouble) proxy->min_value) / ((gdouble) proxy->max_value -
4860           (gdouble) proxy->min_value);
4861       /* Convert to channel range */
4862       new_val =
4863           channel->min_value + new_val * ((gdouble) channel->max_value -
4864           (gdouble) channel->min_value);
4865
4866       gst_color_balance_set_value (balance_element, channel,
4867           (gint) (new_val + 0.5));
4868
4869       gst_object_unref (balance_element);
4870     }
4871
4872     gst_color_balance_value_changed (balance, proxy, value);
4873     break;
4874   }
4875 }
4876
4877 static gint
4878 gst_play_sink_colorbalance_get_value (GstColorBalance * balance,
4879     GstColorBalanceChannel * proxy)
4880 {
4881   GstPlaySink *playsink = GST_PLAY_SINK (balance);
4882   GList *l;
4883   gint i;
4884
4885   for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
4886     GstColorBalanceChannel *proxy_tmp = l->data;
4887
4888     if (proxy_tmp != proxy)
4889       continue;
4890
4891     return playsink->colorbalance_values[i];
4892   }
4893
4894   g_return_val_if_reached (0);
4895 }
4896
4897 static GstColorBalanceType
4898 gst_play_sink_colorbalance_get_balance_type (GstColorBalance * balance)
4899 {
4900   GstPlaySink *playsink = GST_PLAY_SINK (balance);
4901   GstColorBalance *balance_element = NULL;
4902   GstColorBalanceType t = GST_COLOR_BALANCE_SOFTWARE;
4903
4904   GST_OBJECT_LOCK (playsink);
4905   if (playsink->colorbalance_element)
4906     balance_element =
4907         GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
4908   GST_OBJECT_UNLOCK (playsink);
4909
4910   if (balance_element) {
4911     t = gst_color_balance_get_balance_type (balance_element);
4912     gst_object_unref (balance_element);
4913   }
4914
4915   return t;
4916 }
4917
4918 static void
4919 gst_play_sink_colorbalance_init (gpointer g_iface, gpointer g_iface_data)
4920 {
4921   GstColorBalanceInterface *iface = (GstColorBalanceInterface *) g_iface;
4922
4923   iface->list_channels = gst_play_sink_colorbalance_list_channels;
4924   iface->set_value = gst_play_sink_colorbalance_set_value;
4925   iface->get_value = gst_play_sink_colorbalance_get_value;
4926   iface->get_balance_type = gst_play_sink_colorbalance_get_balance_type;
4927 }
4928
4929 gboolean
4930 gst_play_sink_plugin_init (GstPlugin * plugin)
4931 {
4932   GST_DEBUG_CATEGORY_INIT (gst_play_sink_debug, "playsink", 0, "play bin");
4933   return gst_element_register (plugin, "playsink", GST_RANK_NONE,
4934       GST_TYPE_PLAY_SINK);
4935 }