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