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