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