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