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