playsink: Only use the sink's colorbalance interface if it supports at least brightne...
[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 /* FIXME 0.11: suppress warnings for deprecated API such as GStaticRecMutex
26  * with newer GLib versions (>= 2.31.0) */
27 #define GLIB_DISABLE_DEPRECATION_WARNINGS
28
29 #include <string.h>
30 #include <gst/gst.h>
31
32 #include <gst/gst-i18n-plugin.h>
33 #include <gst/pbutils/pbutils.h>
34 #include <gst/video/video.h>
35 #include <gst/interfaces/streamvolume.h>
36 #include <gst/interfaces/colorbalance.h>
37
38 #include "gstplaysink.h"
39 #include "gststreamsynchronizer.h"
40 #include "gstplaysinkvideoconvert.h"
41 #include "gstplaysinkaudioconvert.h"
42
43 GST_DEBUG_CATEGORY_STATIC (gst_play_sink_debug);
44 #define GST_CAT_DEFAULT gst_play_sink_debug
45
46 #define VOLUME_MAX_DOUBLE 10.0
47
48 #define DEFAULT_FLAGS             GST_PLAY_FLAG_AUDIO | GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_TEXT | \
49                                   GST_PLAY_FLAG_SOFT_VOLUME | GST_PLAY_FLAG_SOFT_COLORBALANCE
50
51 #define GST_PLAY_CHAIN(c) ((GstPlayChain *)(c))
52
53 /* holds the common data fields for the audio and video pipelines. We keep them
54  * in a structure to more easily have all the info available. */
55 typedef struct
56 {
57   GstPlaySink *playsink;
58   GstElement *bin;
59   gboolean added;
60   gboolean activated;
61   gboolean raw;
62 } GstPlayChain;
63
64 typedef struct
65 {
66   GstPlayChain chain;
67   GstPad *sinkpad;
68   GstElement *queue;
69   GstElement *conv;
70   GstElement *volume;           /* element with the volume property */
71   gboolean sink_volume;         /* if the volume was provided by the sink */
72   GstElement *mute;             /* element with the mute property */
73   GstElement *sink;
74   GstElement *ts_offset;
75 } GstPlayAudioChain;
76
77 typedef struct
78 {
79   GstPlayChain chain;
80   GstPad *sinkpad, *srcpad;
81   GstElement *conv;
82   GstElement *deinterlace;
83 } GstPlayVideoDeinterlaceChain;
84
85 typedef struct
86 {
87   GstPlayChain chain;
88   GstPad *sinkpad;
89   GstElement *queue;
90   GstElement *conv;
91   GstElement *sink;
92   gboolean async;
93   GstElement *ts_offset;
94 } GstPlayVideoChain;
95
96 typedef struct
97 {
98   GstPlayChain chain;
99   GstPad *sinkpad;
100   GstElement *queue;
101   GstElement *conv;
102   GstElement *resample;
103   GstPad *blockpad;             /* srcpad of resample, used for switching the vis */
104   GstPad *vissinkpad;           /* visualisation sinkpad, */
105   GstElement *vis;
106   GstPad *vissrcpad;            /* visualisation srcpad, */
107   GstPad *srcpad;               /* outgoing srcpad, used to connect to the next
108                                  * chain */
109 } GstPlayVisChain;
110
111 typedef struct
112 {
113   GstPlayChain chain;
114   GstPad *sinkpad;
115   GstElement *queue;
116   GstElement *identity;
117   GstElement *overlay;
118   GstPad *videosinkpad;
119   GstPad *textsinkpad;
120   GstPad *srcpad;               /* outgoing srcpad, used to connect to the next
121                                  * chain */
122   GstElement *sink;             /* custom sink to receive subtitle buffers */
123 } GstPlayTextChain;
124
125 #define GST_PLAY_SINK_GET_LOCK(playsink) (&((GstPlaySink *)playsink)->lock)
126 #define GST_PLAY_SINK_LOCK(playsink)     G_STMT_START { \
127   GST_LOG_OBJECT (playsink, "locking from thread %p", g_thread_self ()); \
128   g_static_rec_mutex_lock (GST_PLAY_SINK_GET_LOCK (playsink)); \
129   GST_LOG_OBJECT (playsink, "locked from thread %p", g_thread_self ()); \
130 } G_STMT_END
131 #define GST_PLAY_SINK_UNLOCK(playsink)   G_STMT_START { \
132   GST_LOG_OBJECT (playsink, "unlocking from thread %p", g_thread_self ()); \
133   g_static_rec_mutex_unlock (GST_PLAY_SINK_GET_LOCK (playsink)); \
134 } G_STMT_END
135
136 #define PENDING_FLAG_SET(playsink, flagtype) \
137   ((playsink->pending_blocked_pads) |= (1 << flagtype))
138 #define PENDING_FLAG_UNSET(playsink, flagtype) \
139   ((playsink->pending_blocked_pads) &= ~(1 << flagtype))
140 #define PENDING_FLAG_IS_SET(playsink, flagtype) \
141   ((playsink->pending_blocked_pads) & (1 << flagtype))
142 #define PENDING_VIDEO_BLOCK(playsink) \
143   ((playsink->pending_blocked_pads) & (1 << GST_PLAY_SINK_TYPE_VIDEO_RAW | 1 << GST_PLAY_SINK_TYPE_VIDEO))
144 #define PENDING_AUDIO_BLOCK(playsink) \
145   ((playsink->pending_blocked_pads) & (1 << GST_PLAY_SINK_TYPE_AUDIO_RAW | 1 << GST_PLAY_SINK_TYPE_AUDIO))
146 #define PENDING_TEXT_BLOCK(playsink) \
147   PENDING_FLAG_IS_SET(playsink, GST_PLAY_SINK_TYPE_TEXT)
148
149 struct _GstPlaySink
150 {
151   GstBin bin;
152
153   GStaticRecMutex lock;
154
155   gboolean async_pending;
156   gboolean need_async_start;
157
158   GstPlayFlags flags;
159
160   GstStreamSynchronizer *stream_synchronizer;
161
162   /* chains */
163   GstPlayAudioChain *audiochain;
164   GstPlayVideoDeinterlaceChain *videodeinterlacechain;
165   GstPlayVideoChain *videochain;
166   GstPlayVisChain *vischain;
167   GstPlayTextChain *textchain;
168
169   /* audio */
170   GstPad *audio_pad;
171   gboolean audio_pad_raw;
172   gboolean audio_pad_blocked;
173   GstPad *audio_srcpad_stream_synchronizer;
174   GstPad *audio_sinkpad_stream_synchronizer;
175   /* audio tee */
176   GstElement *audio_tee;
177   GstPad *audio_tee_sink;
178   GstPad *audio_tee_asrc;
179   GstPad *audio_tee_vissrc;
180   /* video */
181   GstPad *video_pad;
182   gboolean video_pad_raw;
183   gboolean video_pad_blocked;
184   GstPad *video_srcpad_stream_synchronizer;
185   GstPad *video_sinkpad_stream_synchronizer;
186   /* text */
187   GstPad *text_pad;
188   gboolean text_pad_blocked;
189   GstPad *text_srcpad_stream_synchronizer;
190   GstPad *text_sinkpad_stream_synchronizer;
191
192   guint32 pending_blocked_pads;
193
194   /* properties */
195   GstElement *audio_sink;
196   GstElement *video_sink;
197   GstElement *visualisation;
198   GstElement *text_sink;
199   gdouble volume;
200   gboolean mute;
201   gchar *font_desc;             /* font description */
202   gchar *subtitle_encoding;     /* subtitle encoding */
203   guint connection_speed;       /* connection speed in bits/sec (0 = unknown) */
204   gint count;
205   gboolean volume_changed;      /* volume/mute changed while no audiochain */
206   gboolean mute_changed;        /* ... has been created yet */
207   gint64 av_offset;
208 };
209
210 struct _GstPlaySinkClass
211 {
212   GstBinClass parent_class;
213
214     gboolean (*reconfigure) (GstPlaySink * playsink);
215
216   GstBuffer *(*convert_frame) (GstPlaySink * playsink, GstCaps * caps);
217 };
218
219
220 static GstStaticPadTemplate audiotemplate =
221 GST_STATIC_PAD_TEMPLATE ("audio_sink",
222     GST_PAD_SINK,
223     GST_PAD_REQUEST,
224     GST_STATIC_CAPS_ANY);
225 static GstStaticPadTemplate videotemplate =
226 GST_STATIC_PAD_TEMPLATE ("video_sink",
227     GST_PAD_SINK,
228     GST_PAD_REQUEST,
229     GST_STATIC_CAPS_ANY);
230 static GstStaticPadTemplate texttemplate = GST_STATIC_PAD_TEMPLATE ("text_sink",
231     GST_PAD_SINK,
232     GST_PAD_REQUEST,
233     GST_STATIC_CAPS_ANY);
234
235 /* FIXME 0.11: Remove */
236 static GstStaticPadTemplate audiorawtemplate =
237 GST_STATIC_PAD_TEMPLATE ("audio_raw_sink",
238     GST_PAD_SINK,
239     GST_PAD_REQUEST,
240     GST_STATIC_CAPS_ANY);
241 static GstStaticPadTemplate videorawtemplate =
242 GST_STATIC_PAD_TEMPLATE ("video_raw_sink",
243     GST_PAD_SINK,
244     GST_PAD_REQUEST,
245     GST_STATIC_CAPS_ANY);
246
247
248 /* props */
249 enum
250 {
251   PROP_0,
252   PROP_FLAGS,
253   PROP_MUTE,
254   PROP_VOLUME,
255   PROP_FONT_DESC,
256   PROP_SUBTITLE_ENCODING,
257   PROP_VIS_PLUGIN,
258   PROP_FRAME,
259   PROP_AV_OFFSET,
260   PROP_VIDEO_SINK,
261   PROP_AUDIO_SINK,
262   PROP_TEXT_SINK,
263   PROP_LAST
264 };
265
266 /* signals */
267 enum
268 {
269   LAST_SIGNAL
270 };
271
272 static void gst_play_sink_dispose (GObject * object);
273 static void gst_play_sink_finalize (GObject * object);
274 static void gst_play_sink_set_property (GObject * object, guint prop_id,
275     const GValue * value, GParamSpec * spec);
276 static void gst_play_sink_get_property (GObject * object, guint prop_id,
277     GValue * value, GParamSpec * spec);
278
279 static GstPad *gst_play_sink_request_new_pad (GstElement * element,
280     GstPadTemplate * templ, const gchar * name);
281 static void gst_play_sink_release_request_pad (GstElement * element,
282     GstPad * pad);
283 static gboolean gst_play_sink_send_event (GstElement * element,
284     GstEvent * event);
285 static GstStateChangeReturn gst_play_sink_change_state (GstElement * element,
286     GstStateChange transition);
287
288 static void gst_play_sink_handle_message (GstBin * bin, GstMessage * message);
289
290 static void notify_volume_cb (GObject * object, GParamSpec * pspec,
291     GstPlaySink * playsink);
292 static void notify_mute_cb (GObject * object, GParamSpec * pspec,
293     GstPlaySink * playsink);
294
295 static void update_av_offset (GstPlaySink * playsink);
296
297 void
298 gst_play_marshal_BUFFER__BOXED (GClosure * closure,
299     GValue * return_value G_GNUC_UNUSED,
300     guint n_param_values,
301     const GValue * param_values,
302     gpointer invocation_hint G_GNUC_UNUSED, gpointer marshal_data)
303 {
304   typedef GstBuffer *(*GMarshalFunc_OBJECT__BOXED) (gpointer data1,
305       gpointer arg_1, gpointer data2);
306   register GMarshalFunc_OBJECT__BOXED callback;
307   register GCClosure *cc = (GCClosure *) closure;
308   register gpointer data1, data2;
309   GstBuffer *v_return;
310   g_return_if_fail (return_value != NULL);
311   g_return_if_fail (n_param_values == 2);
312
313   if (G_CCLOSURE_SWAP_DATA (closure)) {
314     data1 = closure->data;
315     data2 = g_value_peek_pointer (param_values + 0);
316   } else {
317     data1 = g_value_peek_pointer (param_values + 0);
318     data2 = closure->data;
319   }
320   callback =
321       (GMarshalFunc_OBJECT__BOXED) (marshal_data ? marshal_data : cc->callback);
322
323   v_return = callback (data1, g_value_get_boxed (param_values + 1), data2);
324
325   gst_value_take_buffer (return_value, v_return);
326 }
327
328 /* static guint gst_play_sink_signals[LAST_SIGNAL] = { 0 }; */
329
330 static void
331 _do_init (GType type)
332 {
333   static const GInterfaceInfo svol_info = {
334     NULL, NULL, NULL
335   };
336
337   g_type_add_interface_static (type, GST_TYPE_STREAM_VOLUME, &svol_info);
338 }
339
340 G_DEFINE_TYPE_WITH_CODE (GstPlaySink, gst_play_sink, GST_TYPE_BIN,
341     _do_init (g_define_type_id));
342
343 static void
344 gst_play_sink_class_init (GstPlaySinkClass * klass)
345 {
346   GObjectClass *gobject_klass;
347   GstElementClass *gstelement_klass;
348   GstBinClass *gstbin_klass;
349
350   gobject_klass = (GObjectClass *) klass;
351   gstelement_klass = (GstElementClass *) klass;
352   gstbin_klass = (GstBinClass *) klass;
353
354   gobject_klass->dispose = gst_play_sink_dispose;
355   gobject_klass->finalize = gst_play_sink_finalize;
356   gobject_klass->set_property = gst_play_sink_set_property;
357   gobject_klass->get_property = gst_play_sink_get_property;
358
359
360   /**
361    * GstPlaySink:flags
362    *
363    * Control the behaviour of playsink.
364    */
365   g_object_class_install_property (gobject_klass, PROP_FLAGS,
366       g_param_spec_flags ("flags", "Flags", "Flags to control behaviour",
367           GST_TYPE_PLAY_FLAGS, DEFAULT_FLAGS,
368           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
369
370   /**
371    * GstPlaySink:volume:
372    *
373    * Get or set the current audio stream volume. 1.0 means 100%,
374    * 0.0 means mute. This uses a linear volume scale.
375    *
376    */
377   g_object_class_install_property (gobject_klass, PROP_VOLUME,
378       g_param_spec_double ("volume", "Volume", "The audio volume, 1.0=100%",
379           0.0, VOLUME_MAX_DOUBLE, 1.0,
380           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
381   g_object_class_install_property (gobject_klass, PROP_MUTE,
382       g_param_spec_boolean ("mute", "Mute",
383           "Mute the audio channel without changing the volume", FALSE,
384           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
385   g_object_class_install_property (gobject_klass, PROP_FONT_DESC,
386       g_param_spec_string ("subtitle-font-desc",
387           "Subtitle font description",
388           "Pango font description of font "
389           "to be used for subtitle rendering", NULL,
390           G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
391   g_object_class_install_property (gobject_klass, PROP_SUBTITLE_ENCODING,
392       g_param_spec_string ("subtitle-encoding", "subtitle encoding",
393           "Encoding to assume if input subtitles are not in UTF-8 encoding. "
394           "If not set, the GST_SUBTITLE_ENCODING environment variable will "
395           "be checked for an encoding to use. If that is not set either, "
396           "ISO-8859-15 will be assumed.", NULL,
397           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
398   g_object_class_install_property (gobject_klass, PROP_VIS_PLUGIN,
399       g_param_spec_object ("vis-plugin", "Vis plugin",
400           "the visualization element to use (NULL = default)",
401           GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
402   /**
403    * GstPlaySink:frame:
404    *
405    * Get the currently rendered or prerolled frame in the video sink.
406    * The #GstCaps on the buffer will describe the format of the buffer.
407    *
408    * Since: 0.10.30
409    */
410   g_object_class_install_property (gobject_klass, PROP_FRAME,
411       gst_param_spec_mini_object ("frame", "Frame",
412           "The last frame (NULL = no video available)",
413           GST_TYPE_BUFFER, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
414   /**
415    * GstPlaySink:av-offset:
416    *
417    * Control the synchronisation offset between the audio and video streams.
418    * Positive values make the audio ahead of the video and negative values make
419    * the audio go behind the video.
420    *
421    * Since: 0.10.30
422    */
423   g_object_class_install_property (gobject_klass, PROP_AV_OFFSET,
424       g_param_spec_int64 ("av-offset", "AV Offset",
425           "The synchronisation offset between audio and video in nanoseconds",
426           G_MININT64, G_MAXINT64, 0,
427           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
428
429   /**
430    * GstPlaySink:video-sink:
431    *
432    * Set the used video sink element. NULL will use the default sink. playsink
433    * must be in %GST_STATE_NULL
434    *
435    * Since: 0.10.36
436    */
437   g_object_class_install_property (gobject_klass, PROP_VIDEO_SINK,
438       g_param_spec_object ("video-sink", "Video Sink",
439           "the video output element to use (NULL = default sink)",
440           GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
441   /**
442    * GstPlaySink:audio-sink:
443    *
444    * Set the used audio sink element. NULL will use the default sink. playsink
445    * must be in %GST_STATE_NULL
446    *
447    * Since: 0.10.36
448    */
449   g_object_class_install_property (gobject_klass, PROP_AUDIO_SINK,
450       g_param_spec_object ("audio-sink", "Audio Sink",
451           "the audio output element to use (NULL = default sink)",
452           GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
453   /**
454    * GstPlaySink:text-sink:
455    *
456    * Set the used text sink element. NULL will use the default sink. playsink
457    * must be in %GST_STATE_NULL
458    *
459    * Since: 0.10.36
460    */
461   g_object_class_install_property (gobject_klass, PROP_TEXT_SINK,
462       g_param_spec_object ("text-sink", "Text sink",
463           "the text output element to use (NULL = default textoverlay)",
464           GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
465
466
467   g_signal_new ("reconfigure", G_TYPE_FROM_CLASS (klass),
468       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstPlaySinkClass,
469           reconfigure), NULL, NULL, gst_marshal_BOOLEAN__VOID, G_TYPE_BOOLEAN,
470       0, G_TYPE_NONE);
471   /**
472    * GstPlaySink::convert-frame
473    * @playsink: a #GstPlaySink
474    * @caps: the target format of the frame
475    *
476    * Action signal to retrieve the currently playing video frame in the format
477    * specified by @caps.
478    * If @caps is %NULL, no conversion will be performed and this function is
479    * equivalent to the #GstPlaySink::frame property.
480    *
481    * Returns: a #GstBuffer of the current video frame converted to #caps.
482    * The caps on the buffer will describe the final layout of the buffer data.
483    * %NULL is returned when no current buffer can be retrieved or when the
484    * conversion failed.
485    *
486    * Since: 0.10.30
487    */
488   g_signal_new ("convert-frame", G_TYPE_FROM_CLASS (klass),
489       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
490       G_STRUCT_OFFSET (GstPlaySinkClass, convert_frame), NULL, NULL,
491       gst_play_marshal_BUFFER__BOXED, GST_TYPE_BUFFER, 1, GST_TYPE_CAPS);
492
493   gst_element_class_add_static_pad_template (gstelement_klass,
494       &audiorawtemplate);
495   gst_element_class_add_static_pad_template (gstelement_klass, &audiotemplate);
496   gst_element_class_add_static_pad_template (gstelement_klass,
497       &videorawtemplate);
498   gst_element_class_add_static_pad_template (gstelement_klass, &videotemplate);
499   gst_element_class_add_static_pad_template (gstelement_klass, &texttemplate);
500   gst_element_class_set_details_simple (gstelement_klass, "Player Sink",
501       "Generic/Bin/Sink",
502       "Convenience sink for multiple streams",
503       "Wim Taymans <wim.taymans@gmail.com>");
504
505   gstelement_klass->change_state =
506       GST_DEBUG_FUNCPTR (gst_play_sink_change_state);
507   gstelement_klass->send_event = GST_DEBUG_FUNCPTR (gst_play_sink_send_event);
508   gstelement_klass->request_new_pad =
509       GST_DEBUG_FUNCPTR (gst_play_sink_request_new_pad);
510   gstelement_klass->release_pad =
511       GST_DEBUG_FUNCPTR (gst_play_sink_release_request_pad);
512
513   gstbin_klass->handle_message =
514       GST_DEBUG_FUNCPTR (gst_play_sink_handle_message);
515
516   klass->reconfigure = GST_DEBUG_FUNCPTR (gst_play_sink_reconfigure);
517   klass->convert_frame = GST_DEBUG_FUNCPTR (gst_play_sink_convert_frame);
518 }
519
520 static void
521 gst_play_sink_init (GstPlaySink * playsink)
522 {
523   /* init groups */
524   playsink->video_sink = NULL;
525   playsink->audio_sink = NULL;
526   playsink->visualisation = NULL;
527   playsink->text_sink = NULL;
528   playsink->volume = 1.0;
529   playsink->font_desc = NULL;
530   playsink->subtitle_encoding = NULL;
531   playsink->flags = DEFAULT_FLAGS;
532
533   playsink->stream_synchronizer =
534       g_object_new (GST_TYPE_STREAM_SYNCHRONIZER, NULL);
535   gst_bin_add (GST_BIN_CAST (playsink),
536       GST_ELEMENT_CAST (playsink->stream_synchronizer));
537
538   g_static_rec_mutex_init (&playsink->lock);
539   GST_OBJECT_FLAG_SET (playsink, GST_ELEMENT_IS_SINK);
540 }
541
542 static void
543 disconnect_chain (GstPlayAudioChain * chain, GstPlaySink * playsink)
544 {
545   if (chain) {
546     if (chain->volume)
547       g_signal_handlers_disconnect_by_func (chain->volume, notify_volume_cb,
548           playsink);
549     if (chain->mute)
550       g_signal_handlers_disconnect_by_func (chain->mute, notify_mute_cb,
551           playsink);
552   }
553 }
554
555 static void
556 free_chain (GstPlayChain * chain)
557 {
558   if (chain) {
559     if (chain->bin)
560       gst_object_unref (chain->bin);
561     g_free (chain);
562   }
563 }
564
565 static void
566 gst_play_sink_dispose (GObject * object)
567 {
568   GstPlaySink *playsink;
569
570   playsink = GST_PLAY_SINK (object);
571
572   if (playsink->audio_sink != NULL) {
573     gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
574     gst_object_unref (playsink->audio_sink);
575     playsink->audio_sink = NULL;
576   }
577   if (playsink->video_sink != NULL) {
578     gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
579     gst_object_unref (playsink->video_sink);
580     playsink->video_sink = NULL;
581   }
582   if (playsink->visualisation != NULL) {
583     gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
584     gst_object_unref (playsink->visualisation);
585     playsink->visualisation = NULL;
586   }
587   if (playsink->text_sink != NULL) {
588     gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
589     gst_object_unref (playsink->text_sink);
590     playsink->text_sink = NULL;
591   }
592
593   free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
594   playsink->videodeinterlacechain = NULL;
595   free_chain ((GstPlayChain *) playsink->videochain);
596   playsink->videochain = NULL;
597   free_chain ((GstPlayChain *) playsink->audiochain);
598   playsink->audiochain = NULL;
599   free_chain ((GstPlayChain *) playsink->vischain);
600   playsink->vischain = NULL;
601   free_chain ((GstPlayChain *) playsink->textchain);
602   playsink->textchain = NULL;
603
604   if (playsink->audio_tee_sink) {
605     gst_object_unref (playsink->audio_tee_sink);
606     playsink->audio_tee_sink = NULL;
607   }
608
609   if (playsink->audio_tee_vissrc) {
610     gst_element_release_request_pad (playsink->audio_tee,
611         playsink->audio_tee_vissrc);
612     gst_object_unref (playsink->audio_tee_vissrc);
613     playsink->audio_tee_vissrc = NULL;
614   }
615
616   if (playsink->audio_tee_asrc) {
617     gst_element_release_request_pad (playsink->audio_tee,
618         playsink->audio_tee_asrc);
619     gst_object_unref (playsink->audio_tee_asrc);
620     playsink->audio_tee_asrc = NULL;
621   }
622
623   g_free (playsink->font_desc);
624   playsink->font_desc = NULL;
625
626   g_free (playsink->subtitle_encoding);
627   playsink->subtitle_encoding = NULL;
628
629   playsink->stream_synchronizer = NULL;
630
631   G_OBJECT_CLASS (gst_play_sink_parent_class)->dispose (object);
632 }
633
634 static void
635 gst_play_sink_finalize (GObject * object)
636 {
637   GstPlaySink *playsink;
638
639   playsink = GST_PLAY_SINK (object);
640
641   g_static_rec_mutex_free (&playsink->lock);
642
643   G_OBJECT_CLASS (gst_play_sink_parent_class)->finalize (object);
644 }
645
646 void
647 gst_play_sink_set_sink (GstPlaySink * playsink, GstPlaySinkType type,
648     GstElement * sink)
649 {
650   GstElement **elem = NULL, *old = NULL;
651
652   GST_LOG ("Setting sink %" GST_PTR_FORMAT " as sink type %d", sink, type);
653
654   GST_PLAY_SINK_LOCK (playsink);
655   switch (type) {
656     case GST_PLAY_SINK_TYPE_AUDIO:
657     case GST_PLAY_SINK_TYPE_AUDIO_RAW:
658       elem = &playsink->audio_sink;
659       break;
660     case GST_PLAY_SINK_TYPE_VIDEO:
661     case GST_PLAY_SINK_TYPE_VIDEO_RAW:
662       elem = &playsink->video_sink;
663       break;
664     case GST_PLAY_SINK_TYPE_TEXT:
665       elem = &playsink->text_sink;
666       break;
667     default:
668       break;
669   }
670   if (elem) {
671     old = *elem;
672     if (sink)
673       gst_object_ref (sink);
674     *elem = sink;
675   }
676   GST_PLAY_SINK_UNLOCK (playsink);
677
678   if (old) {
679     if (old != sink)
680       gst_element_set_state (old, GST_STATE_NULL);
681     gst_object_unref (old);
682   }
683 }
684
685 GstElement *
686 gst_play_sink_get_sink (GstPlaySink * playsink, GstPlaySinkType type)
687 {
688   GstElement *result = NULL;
689   GstElement *elem = NULL, *chainp = NULL;
690
691   GST_PLAY_SINK_LOCK (playsink);
692   switch (type) {
693     case GST_PLAY_SINK_TYPE_AUDIO:
694     case GST_PLAY_SINK_TYPE_AUDIO_RAW:
695     {
696       GstPlayAudioChain *chain;
697       if ((chain = (GstPlayAudioChain *) playsink->audiochain))
698         chainp = chain->sink;
699       elem = playsink->audio_sink;
700       break;
701     }
702     case GST_PLAY_SINK_TYPE_VIDEO:
703     case GST_PLAY_SINK_TYPE_VIDEO_RAW:
704     {
705       GstPlayVideoChain *chain;
706       if ((chain = (GstPlayVideoChain *) playsink->videochain))
707         chainp = chain->sink;
708       elem = playsink->video_sink;
709       break;
710     }
711     case GST_PLAY_SINK_TYPE_TEXT:
712     {
713       GstPlayTextChain *chain;
714       if ((chain = (GstPlayTextChain *) playsink->textchain))
715         chainp = chain->sink;
716       elem = playsink->text_sink;
717       break;
718     }
719     default:
720       break;
721   }
722   if (chainp) {
723     /* we have an active chain with a sink, get the sink */
724     result = gst_object_ref (chainp);
725   }
726   /* nothing found, return last configured sink */
727   if (result == NULL && elem)
728     result = gst_object_ref (elem);
729   GST_PLAY_SINK_UNLOCK (playsink);
730
731   return result;
732 }
733
734 static void
735 gst_play_sink_vis_unblocked (GstPad * tee_pad, gboolean blocked,
736     gpointer user_data)
737 {
738   GstPlaySink *playsink;
739
740   playsink = GST_PLAY_SINK (user_data);
741   /* nothing to do here, we need a dummy callback here to make the async call
742    * non-blocking. */
743   GST_DEBUG_OBJECT (playsink, "vis pad unblocked");
744 }
745
746 static void
747 gst_play_sink_vis_blocked (GstPad * tee_pad, gboolean blocked,
748     gpointer user_data)
749 {
750   GstPlaySink *playsink;
751   GstPlayVisChain *chain;
752
753   playsink = GST_PLAY_SINK (user_data);
754
755   GST_PLAY_SINK_LOCK (playsink);
756   GST_DEBUG_OBJECT (playsink, "vis pad blocked");
757   /* now try to change the plugin in the running vis chain */
758   if (!(chain = (GstPlayVisChain *) playsink->vischain))
759     goto done;
760
761   /* unlink the old plugin and unghost the pad */
762   gst_pad_unlink (chain->blockpad, chain->vissinkpad);
763   gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad), NULL);
764
765   /* set the old plugin to NULL and remove */
766   gst_element_set_state (chain->vis, GST_STATE_NULL);
767   gst_bin_remove (GST_BIN_CAST (chain->chain.bin), chain->vis);
768
769   /* add new plugin and set state to playing */
770   chain->vis = playsink->visualisation;
771   gst_bin_add (GST_BIN_CAST (chain->chain.bin), chain->vis);
772   gst_element_set_state (chain->vis, GST_STATE_PLAYING);
773
774   /* get pads */
775   chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
776   chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
777
778   /* link pads */
779   gst_pad_link_full (chain->blockpad, chain->vissinkpad,
780       GST_PAD_LINK_CHECK_NOTHING);
781   gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad),
782       chain->vissrcpad);
783
784 done:
785   /* Unblock the pad */
786   gst_pad_set_blocked_async (tee_pad, FALSE, gst_play_sink_vis_unblocked,
787       playsink);
788   GST_PLAY_SINK_UNLOCK (playsink);
789 }
790
791 void
792 gst_play_sink_set_vis_plugin (GstPlaySink * playsink, GstElement * vis)
793 {
794   GstPlayVisChain *chain;
795
796   /* setting NULL means creating the default vis plugin */
797   if (vis == NULL)
798     vis = gst_element_factory_make ("goom", "vis");
799
800   /* simply return if we don't have a vis plugin here */
801   if (vis == NULL)
802     return;
803
804   GST_PLAY_SINK_LOCK (playsink);
805   /* first store the new vis */
806   if (playsink->visualisation)
807     gst_object_unref (playsink->visualisation);
808   /* take ownership */
809   gst_object_ref_sink (vis);
810   playsink->visualisation = vis;
811
812   /* now try to change the plugin in the running vis chain, if we have no chain,
813    * we don't bother, any future vis chain will be created with the new vis
814    * plugin. */
815   if (!(chain = (GstPlayVisChain *) playsink->vischain))
816     goto done;
817
818   /* block the pad, the next time the callback is called we can change the
819    * visualisation. It's possible that this never happens or that the pad was
820    * already blocked. If the callback never happens, we don't have new data so
821    * we don't need the new vis plugin. If the pad was already blocked, the
822    * function returns FALSE but the previous pad block will do the right thing
823    * anyway. */
824   GST_DEBUG_OBJECT (playsink, "blocking vis pad");
825   gst_pad_set_blocked_async (chain->blockpad, TRUE, gst_play_sink_vis_blocked,
826       playsink);
827 done:
828   GST_PLAY_SINK_UNLOCK (playsink);
829
830   return;
831 }
832
833 GstElement *
834 gst_play_sink_get_vis_plugin (GstPlaySink * playsink)
835 {
836   GstElement *result = NULL;
837   GstPlayVisChain *chain;
838
839   GST_PLAY_SINK_LOCK (playsink);
840   if ((chain = (GstPlayVisChain *) playsink->vischain)) {
841     /* we have an active chain, get the sink */
842     if (chain->vis)
843       result = gst_object_ref (chain->vis);
844   }
845   /* nothing found, return last configured sink */
846   if (result == NULL && playsink->visualisation)
847     result = gst_object_ref (playsink->visualisation);
848   GST_PLAY_SINK_UNLOCK (playsink);
849
850   return result;
851 }
852
853 void
854 gst_play_sink_set_volume (GstPlaySink * playsink, gdouble volume)
855 {
856   GstPlayAudioChain *chain;
857
858   GST_PLAY_SINK_LOCK (playsink);
859   playsink->volume = volume;
860   chain = (GstPlayAudioChain *) playsink->audiochain;
861   if (chain && chain->volume) {
862     GST_LOG_OBJECT (playsink, "elements: volume=%" GST_PTR_FORMAT ", mute=%"
863         GST_PTR_FORMAT "; new volume=%.03f, mute=%d", chain->volume,
864         chain->mute, volume, playsink->mute);
865     /* if there is a mute element or we are not muted, set the volume */
866     if (chain->mute || !playsink->mute)
867       g_object_set (chain->volume, "volume", volume, NULL);
868   } else {
869     GST_LOG_OBJECT (playsink, "no volume element");
870     playsink->volume_changed = TRUE;
871   }
872   GST_PLAY_SINK_UNLOCK (playsink);
873 }
874
875 gdouble
876 gst_play_sink_get_volume (GstPlaySink * playsink)
877 {
878   gdouble result;
879   GstPlayAudioChain *chain;
880
881   GST_PLAY_SINK_LOCK (playsink);
882   chain = (GstPlayAudioChain *) playsink->audiochain;
883   result = playsink->volume;
884   if (chain && chain->volume) {
885     if (chain->mute || !playsink->mute) {
886       g_object_get (chain->volume, "volume", &result, NULL);
887       playsink->volume = result;
888     }
889   }
890   GST_PLAY_SINK_UNLOCK (playsink);
891
892   return result;
893 }
894
895 void
896 gst_play_sink_set_mute (GstPlaySink * playsink, gboolean mute)
897 {
898   GstPlayAudioChain *chain;
899
900   GST_PLAY_SINK_LOCK (playsink);
901   playsink->mute = mute;
902   chain = (GstPlayAudioChain *) playsink->audiochain;
903   if (chain) {
904     if (chain->mute) {
905       g_object_set (chain->mute, "mute", mute, NULL);
906     } else if (chain->volume) {
907       if (mute)
908         g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
909       else
910         g_object_set (chain->volume, "volume", (gdouble) playsink->volume,
911             NULL);
912     }
913   } else {
914     playsink->mute_changed = TRUE;
915   }
916   GST_PLAY_SINK_UNLOCK (playsink);
917 }
918
919 gboolean
920 gst_play_sink_get_mute (GstPlaySink * playsink)
921 {
922   gboolean result;
923   GstPlayAudioChain *chain;
924
925   GST_PLAY_SINK_LOCK (playsink);
926   chain = (GstPlayAudioChain *) playsink->audiochain;
927   if (chain && chain->mute) {
928     g_object_get (chain->mute, "mute", &result, NULL);
929     playsink->mute = result;
930   } else {
931     result = playsink->mute;
932   }
933   GST_PLAY_SINK_UNLOCK (playsink);
934
935   return result;
936 }
937
938 static void
939 post_missing_element_message (GstPlaySink * playsink, const gchar * name)
940 {
941   GstMessage *msg;
942
943   msg = gst_missing_element_message_new (GST_ELEMENT_CAST (playsink), name);
944   gst_element_post_message (GST_ELEMENT_CAST (playsink), msg);
945 }
946
947 static gboolean
948 add_chain (GstPlayChain * chain, gboolean add)
949 {
950   if (chain->added == add)
951     return TRUE;
952
953   if (add)
954     gst_bin_add (GST_BIN_CAST (chain->playsink), chain->bin);
955   else {
956     gst_bin_remove (GST_BIN_CAST (chain->playsink), chain->bin);
957     /* we don't want to lose our sink status */
958     GST_OBJECT_FLAG_SET (chain->playsink, GST_ELEMENT_IS_SINK);
959   }
960
961   chain->added = add;
962
963   return TRUE;
964 }
965
966 static gboolean
967 activate_chain (GstPlayChain * chain, gboolean activate)
968 {
969   GstState state;
970
971   if (chain->activated == activate)
972     return TRUE;
973
974   GST_OBJECT_LOCK (chain->playsink);
975   state = GST_STATE_TARGET (chain->playsink);
976   GST_OBJECT_UNLOCK (chain->playsink);
977
978   if (activate)
979     gst_element_set_state (chain->bin, state);
980   else
981     gst_element_set_state (chain->bin, GST_STATE_NULL);
982
983   chain->activated = activate;
984
985   return TRUE;
986 }
987
988 static gboolean
989 element_is_sink (GstElement * element)
990 {
991   gboolean is_sink;
992
993   GST_OBJECT_LOCK (element);
994   is_sink = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_IS_SINK);
995   GST_OBJECT_UNLOCK (element);
996
997   GST_DEBUG_OBJECT (element, "is a sink: %s", (is_sink) ? "yes" : "no");
998   return is_sink;
999 }
1000
1001 static gboolean
1002 element_has_property (GstElement * element, const gchar * pname, GType type)
1003 {
1004   GParamSpec *pspec;
1005
1006   pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (element), pname);
1007
1008   if (pspec == NULL) {
1009     GST_DEBUG_OBJECT (element, "no %s property", pname);
1010     return FALSE;
1011   }
1012
1013   if (type == G_TYPE_INVALID || type == pspec->value_type ||
1014       g_type_is_a (pspec->value_type, type)) {
1015     GST_DEBUG_OBJECT (element, "has %s property of type %s", pname,
1016         (type == G_TYPE_INVALID) ? "any type" : g_type_name (type));
1017     return TRUE;
1018   }
1019
1020   GST_WARNING_OBJECT (element, "has %s property, but property is of type %s "
1021       "and we expected it to be of type %s", pname,
1022       g_type_name (pspec->value_type), g_type_name (type));
1023
1024   return FALSE;
1025 }
1026
1027 typedef struct
1028 {
1029   const gchar *prop_name;
1030   GType prop_type;
1031   gboolean need_sink;
1032 } FindPropertyHelper;
1033
1034 static gint
1035 find_property (GstElement * element, FindPropertyHelper * helper)
1036 {
1037   if (helper->need_sink && !element_is_sink (element)) {
1038     gst_object_unref (element);
1039     return 1;
1040   }
1041
1042   if (!element_has_property (element, helper->prop_name, helper->prop_type)) {
1043     gst_object_unref (element);
1044     return 1;
1045   }
1046
1047   GST_INFO_OBJECT (element, "found %s with %s property", helper->prop_name,
1048       (helper->need_sink) ? "sink" : "element");
1049   return 0;                     /* keep it */
1050 }
1051
1052 /* FIXME: why not move these functions into core? */
1053 /* find a sink in the hierarchy with a property named @name. This function does
1054  * not increase the refcount of the returned object and thus remains valid as
1055  * long as the bin is valid. */
1056 static GstElement *
1057 gst_play_sink_find_property_sinks (GstPlaySink * playsink, GstElement * obj,
1058     const gchar * name, GType expected_type)
1059 {
1060   GstElement *result = NULL;
1061   GstIterator *it;
1062
1063   if (element_has_property (obj, name, expected_type)) {
1064     result = obj;
1065   } else if (GST_IS_BIN (obj)) {
1066     FindPropertyHelper helper = { name, expected_type, TRUE };
1067
1068     it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1069     result = gst_iterator_find_custom (it,
1070         (GCompareFunc) find_property, &helper);
1071     gst_iterator_free (it);
1072     /* we don't need the extra ref */
1073     if (result)
1074       gst_object_unref (result);
1075   }
1076   return result;
1077 }
1078
1079 /* find an object in the hierarchy with a property named @name */
1080 static GstElement *
1081 gst_play_sink_find_property (GstPlaySink * playsink, GstElement * obj,
1082     const gchar * name, GType expected_type)
1083 {
1084   GstElement *result = NULL;
1085   GstIterator *it;
1086
1087   if (GST_IS_BIN (obj)) {
1088     FindPropertyHelper helper = { name, expected_type, FALSE };
1089
1090     it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1091     result = gst_iterator_find_custom (it,
1092         (GCompareFunc) find_property, &helper);
1093     gst_iterator_free (it);
1094   } else {
1095     if (element_has_property (obj, name, expected_type)) {
1096       result = obj;
1097       gst_object_ref (obj);
1098     }
1099   }
1100   return result;
1101 }
1102
1103 static void
1104 do_async_start (GstPlaySink * playsink)
1105 {
1106   GstMessage *message;
1107
1108   if (!playsink->need_async_start) {
1109     GST_INFO_OBJECT (playsink, "no async_start needed");
1110     return;
1111   }
1112
1113   playsink->async_pending = TRUE;
1114
1115   GST_INFO_OBJECT (playsink, "Sending async_start message");
1116   message = gst_message_new_async_start (GST_OBJECT_CAST (playsink), FALSE);
1117   GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1118       (playsink), message);
1119 }
1120
1121 static void
1122 do_async_done (GstPlaySink * playsink)
1123 {
1124   GstMessage *message;
1125
1126   if (playsink->async_pending) {
1127     GST_INFO_OBJECT (playsink, "Sending async_done message");
1128     message = gst_message_new_async_done (GST_OBJECT_CAST (playsink));
1129     GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1130         (playsink), message);
1131
1132     playsink->async_pending = FALSE;
1133   }
1134
1135   playsink->need_async_start = FALSE;
1136 }
1137
1138 /* try to change the state of an element. This function returns the element when
1139  * the state change could be performed. When this function returns NULL an error
1140  * occured and the element is unreffed if @unref is TRUE. */
1141 static GstElement *
1142 try_element (GstPlaySink * playsink, GstElement * element, gboolean unref)
1143 {
1144   GstStateChangeReturn ret;
1145
1146   if (element) {
1147     ret = gst_element_set_state (element, GST_STATE_READY);
1148     if (ret == GST_STATE_CHANGE_FAILURE) {
1149       GST_DEBUG_OBJECT (playsink, "failed state change..");
1150       gst_element_set_state (element, GST_STATE_NULL);
1151       if (unref)
1152         gst_object_unref (element);
1153       element = NULL;
1154     }
1155   }
1156   return element;
1157 }
1158
1159 /* make the element (bin) that contains the elements needed to perform
1160  * video display. Only used for *raw* video streams.
1161  *
1162  *  +------------------------------------------------------------+
1163  *  | vbin                                                       |
1164  *  |      +-------+   +----------+   +----------+   +---------+ |
1165  *  |      | queue |   |colorspace|   |videoscale|   |videosink| |
1166  *  |   +-sink    src-sink       src-sink       src-sink       | |
1167  *  |   |  +-------+   +----------+   +----------+   +---------+ |
1168  * sink-+                                                        |
1169  *  +------------------------------------------------------------+
1170  *
1171  */
1172 static GstPlayVideoDeinterlaceChain *
1173 gen_video_deinterlace_chain (GstPlaySink * playsink)
1174 {
1175   GstPlayVideoDeinterlaceChain *chain;
1176   GstBin *bin;
1177   GstPad *pad;
1178   GstElement *head = NULL, *prev = NULL;
1179
1180   chain = g_new0 (GstPlayVideoDeinterlaceChain, 1);
1181   chain->chain.playsink = playsink;
1182
1183   GST_DEBUG_OBJECT (playsink, "making video deinterlace chain %p", chain);
1184
1185   /* create a bin to hold objects, as we create them we add them to this bin so
1186    * that when something goes wrong we only need to unref the bin */
1187   chain->chain.bin = gst_bin_new ("vdbin");
1188   bin = GST_BIN_CAST (chain->chain.bin);
1189   gst_object_ref_sink (bin);
1190
1191   GST_DEBUG_OBJECT (playsink, "creating " COLORSPACE);
1192   chain->conv = gst_element_factory_make (COLORSPACE, "vdconv");
1193   if (chain->conv == NULL) {
1194     post_missing_element_message (playsink, COLORSPACE);
1195     GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1196         (_("Missing element '%s' - check your GStreamer installation."),
1197             COLORSPACE), ("video rendering might fail"));
1198   } else {
1199     gst_bin_add (bin, chain->conv);
1200     head = chain->conv;
1201     prev = chain->conv;
1202   }
1203
1204   GST_DEBUG_OBJECT (playsink, "creating deinterlace");
1205   chain->deinterlace = gst_element_factory_make ("deinterlace", "deinterlace");
1206   if (chain->deinterlace == NULL) {
1207     post_missing_element_message (playsink, "deinterlace");
1208     GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1209         (_("Missing element '%s' - check your GStreamer installation."),
1210             "deinterlace"), ("deinterlacing won't work"));
1211   } else {
1212     gst_bin_add (bin, chain->deinterlace);
1213     if (prev) {
1214       if (!gst_element_link_pads_full (prev, "src", chain->deinterlace, "sink",
1215               GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1216         goto link_failed;
1217     } else {
1218       head = chain->deinterlace;
1219     }
1220     prev = chain->deinterlace;
1221   }
1222
1223   if (head) {
1224     pad = gst_element_get_static_pad (head, "sink");
1225     chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1226     gst_object_unref (pad);
1227   } else {
1228     chain->sinkpad = gst_ghost_pad_new_no_target ("sink", GST_PAD_SINK);
1229   }
1230
1231   if (prev) {
1232     pad = gst_element_get_static_pad (prev, "src");
1233     chain->srcpad = gst_ghost_pad_new ("src", pad);
1234     gst_object_unref (pad);
1235   } else {
1236     chain->srcpad = gst_ghost_pad_new ("src", chain->sinkpad);
1237   }
1238
1239   gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1240   gst_element_add_pad (chain->chain.bin, chain->srcpad);
1241
1242   return chain;
1243
1244 link_failed:
1245   {
1246     GST_ELEMENT_ERROR (playsink, CORE, PAD,
1247         (NULL), ("Failed to configure the video deinterlace chain."));
1248     free_chain ((GstPlayChain *) chain);
1249     return NULL;
1250   }
1251 }
1252
1253 static gboolean
1254 is_valid_color_balance_element (GstElement * element)
1255 {
1256   GstColorBalance *bal = GST_COLOR_BALANCE (element);
1257   gboolean have_brightness = FALSE;
1258   gboolean have_contrast = FALSE;
1259   gboolean have_hue = FALSE;
1260   gboolean have_saturation = FALSE;
1261   const GList *channels, *l;
1262
1263   channels = gst_color_balance_list_channels (bal);
1264   for (l = channels; l; l = l->next) {
1265     GstColorBalanceChannel *ch = l->data;
1266
1267     if (g_strrstr (ch->label, "BRIGHTNESS"))
1268       have_brightness = TRUE;
1269     else if (g_strrstr (ch->label, "CONTRAST"))
1270       have_contrast = TRUE;
1271     else if (g_strrstr (ch->label, "HUE"))
1272       have_hue = TRUE;
1273     else if (g_strrstr (ch->label, "SATURATION"))
1274       have_saturation = TRUE;
1275   }
1276
1277   return have_brightness && have_contrast && have_hue && have_saturation;
1278 }
1279
1280 static void
1281 iterate_color_balance_elements (gpointer data, gpointer user_data)
1282 {
1283   gboolean valid = is_valid_color_balance_element (data);
1284   gboolean *valid_out = user_data;
1285
1286   *valid_out = *valid_out && valid;
1287
1288   gst_object_unref (data);
1289 }
1290
1291 static gboolean
1292 has_color_balance_element (GstElement * element)
1293 {
1294   GstIterator *it;
1295   gboolean valid = FALSE;
1296
1297   if (GST_IS_COLOR_BALANCE (element))
1298     return is_valid_color_balance_element (element);
1299   else if (!GST_IS_BIN (element))
1300     return FALSE;
1301
1302   it = gst_bin_iterate_all_by_interface (GST_BIN (element),
1303       GST_TYPE_COLOR_BALANCE);
1304   while (gst_iterator_foreach (it, iterate_color_balance_elements,
1305           &valid) == GST_ITERATOR_RESYNC)
1306     gst_iterator_resync (it);
1307   gst_iterator_free (it);
1308
1309   return valid;
1310 }
1311
1312 /* make the element (bin) that contains the elements needed to perform
1313  * video display.
1314  *
1315  *  +------------------------------------------------------------+
1316  *  | vbin                                                       |
1317  *  |      +-------+   +----------+   +----------+   +---------+ |
1318  *  |      | queue |   |colorspace|   |videoscale|   |videosink| |
1319  *  |   +-sink    src-sink       src-sink       src-sink       | |
1320  *  |   |  +-------+   +----------+   +----------+   +---------+ |
1321  * sink-+                                                        |
1322  *  +------------------------------------------------------------+
1323  *
1324  */
1325 static GstPlayVideoChain *
1326 gen_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1327 {
1328   GstPlayVideoChain *chain;
1329   GstBin *bin;
1330   GstPad *pad;
1331   GstElement *head = NULL, *prev = NULL, *elem = NULL;
1332
1333   chain = g_new0 (GstPlayVideoChain, 1);
1334   chain->chain.playsink = playsink;
1335   chain->chain.raw = raw;
1336
1337   GST_DEBUG_OBJECT (playsink, "making video chain %p", chain);
1338
1339   if (playsink->video_sink) {
1340     GST_DEBUG_OBJECT (playsink, "trying configured videosink");
1341     chain->sink = try_element (playsink, playsink->video_sink, FALSE);
1342   } else {
1343     /* only try fallback if no specific sink was chosen */
1344     if (chain->sink == NULL) {
1345       GST_DEBUG_OBJECT (playsink, "trying autovideosink");
1346       elem = gst_element_factory_make ("autovideosink", "videosink");
1347       chain->sink = try_element (playsink, elem, TRUE);
1348     }
1349     if (chain->sink == NULL) {
1350       /* if default sink from config.h is different then try it too */
1351       if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1352         GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_VIDEOSINK);
1353         elem = gst_element_factory_make (DEFAULT_VIDEOSINK, "videosink");
1354         chain->sink = try_element (playsink, elem, TRUE);
1355       }
1356     }
1357     if (chain->sink)
1358       playsink->video_sink = gst_object_ref (chain->sink);
1359   }
1360   if (chain->sink == NULL)
1361     goto no_sinks;
1362   head = chain->sink;
1363
1364   /* if we can disable async behaviour of the sink, we can avoid adding a
1365    * queue for the audio chain. */
1366   elem =
1367       gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1368       G_TYPE_BOOLEAN);
1369   if (elem) {
1370     GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1371         async, GST_ELEMENT_NAME (elem));
1372     g_object_set (elem, "async", async, NULL);
1373     chain->async = async;
1374   } else {
1375     GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1376     chain->async = TRUE;
1377   }
1378
1379   /* find ts-offset element */
1380   gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1381       gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1382           G_TYPE_INT64));
1383
1384   /* create a bin to hold objects, as we create them we add them to this bin so
1385    * that when something goes wrong we only need to unref the bin */
1386   chain->chain.bin = gst_bin_new ("vbin");
1387   bin = GST_BIN_CAST (chain->chain.bin);
1388   gst_object_ref_sink (bin);
1389   gst_bin_add (bin, chain->sink);
1390
1391   /* decouple decoder from sink, this improves playback quite a lot since the
1392    * decoder can continue while the sink blocks for synchronisation. We don't
1393    * need a lot of buffers as this consumes a lot of memory and we don't want
1394    * too little because else we would be context switching too quickly. */
1395   chain->queue = gst_element_factory_make ("queue", "vqueue");
1396   if (chain->queue == NULL) {
1397     post_missing_element_message (playsink, "queue");
1398     GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1399         (_("Missing element '%s' - check your GStreamer installation."),
1400             "queue"), ("video rendering might be suboptimal"));
1401     head = chain->sink;
1402     prev = NULL;
1403   } else {
1404     g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1405         "max-size-bytes", 0, "max-size-time", (gint64) 0, "silent", TRUE, NULL);
1406     gst_bin_add (bin, chain->queue);
1407     head = prev = chain->queue;
1408   }
1409
1410   if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)
1411       || (!has_color_balance_element (chain->sink)
1412           && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE))) {
1413     gboolean use_converters = !(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO);
1414     gboolean use_balance = !has_color_balance_element (chain->sink)
1415         && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE);
1416
1417     GST_DEBUG_OBJECT (playsink, "creating videoconverter");
1418     chain->conv =
1419         g_object_new (GST_TYPE_PLAY_SINK_VIDEO_CONVERT, "name", "vconv",
1420         "use-converters", use_converters, "use-balance", use_balance, NULL);
1421     gst_bin_add (bin, chain->conv);
1422     if (prev) {
1423       if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
1424               GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1425         goto link_failed;
1426     } else {
1427       head = chain->conv;
1428     }
1429     prev = chain->conv;
1430   }
1431
1432   if (prev) {
1433     GST_DEBUG_OBJECT (playsink, "linking to sink");
1434     if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
1435             GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1436       goto link_failed;
1437   }
1438
1439   pad = gst_element_get_static_pad (head, "sink");
1440   chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1441   gst_object_unref (pad);
1442
1443   gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1444
1445   return chain;
1446
1447   /* ERRORS */
1448 no_sinks:
1449   {
1450     if (!elem && !playsink->video_sink) {
1451       post_missing_element_message (playsink, "autovideosink");
1452       if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1453         post_missing_element_message (playsink, DEFAULT_VIDEOSINK);
1454         GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1455             (_("Both autovideosink and %s elements are missing."),
1456                 DEFAULT_VIDEOSINK), (NULL));
1457       } else {
1458         GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1459             (_("The autovideosink element is missing.")), (NULL));
1460       }
1461     } else {
1462       if (playsink->video_sink) {
1463         GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1464             (_("Configured videosink %s is not working."),
1465                 GST_ELEMENT_NAME (playsink->video_sink)), (NULL));
1466       } else if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1467         GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1468             (_("Both autovideosink and %s elements are not working."),
1469                 DEFAULT_VIDEOSINK), (NULL));
1470       } else {
1471         GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1472             (_("The autovideosink element is not working.")), (NULL));
1473       }
1474     }
1475     free_chain ((GstPlayChain *) chain);
1476     return NULL;
1477   }
1478 link_failed:
1479   {
1480     GST_ELEMENT_ERROR (playsink, CORE, PAD,
1481         (NULL), ("Failed to configure the video sink."));
1482     /* checking sink made it READY */
1483     gst_element_set_state (chain->sink, GST_STATE_NULL);
1484     /* Remove chain from the bin to allow reuse later */
1485     gst_bin_remove (bin, chain->sink);
1486     free_chain ((GstPlayChain *) chain);
1487     return NULL;
1488   }
1489 }
1490
1491 static gboolean
1492 setup_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1493 {
1494   GstElement *elem;
1495   GstPlayVideoChain *chain;
1496   GstStateChangeReturn ret;
1497
1498   chain = playsink->videochain;
1499
1500   chain->chain.raw = raw;
1501
1502   /* if the chain was active we don't do anything */
1503   if (GST_PLAY_CHAIN (chain)->activated == TRUE)
1504     return TRUE;
1505
1506   /* try to set the sink element to READY again */
1507   ret = gst_element_set_state (chain->sink, GST_STATE_READY);
1508   if (ret == GST_STATE_CHANGE_FAILURE)
1509     return FALSE;
1510
1511   /* find ts-offset element */
1512
1513   gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1514       gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1515           G_TYPE_INT64));
1516
1517   /* if we can disable async behaviour of the sink, we can avoid adding a
1518    * queue for the audio chain. */
1519   elem =
1520       gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1521       G_TYPE_BOOLEAN);
1522   if (elem) {
1523     GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1524         async, GST_ELEMENT_NAME (elem));
1525     g_object_set (elem, "async", async, NULL);
1526     chain->async = async;
1527   } else {
1528     GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1529     chain->async = TRUE;
1530   }
1531
1532   if (chain->conv)
1533     g_object_set (chain->conv, "use-balance",
1534         !has_color_balance_element (chain->sink)
1535         && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE), NULL);
1536
1537   return TRUE;
1538 }
1539
1540 /* make an element for playback of video with subtitles embedded.
1541  * Only used for *raw* video streams.
1542  *
1543  *  +--------------------------------------------+
1544  *  | tbin                                       |
1545  *  |     +--------+      +-----------------+    |
1546  *  |     | queue  |      | subtitleoverlay |    |
1547  * video--src     sink---video_sink         |    |
1548  *  |     +--------+     |                src--src
1549  * text------------------text_sink          |    |
1550  *  |                     +-----------------+    |
1551  *  +--------------------------------------------+
1552  *
1553  */
1554 static GstPlayTextChain *
1555 gen_text_chain (GstPlaySink * playsink)
1556 {
1557   GstPlayTextChain *chain;
1558   GstBin *bin;
1559   GstElement *elem;
1560   GstPad *videosinkpad, *textsinkpad, *srcpad;
1561
1562   chain = g_new0 (GstPlayTextChain, 1);
1563   chain->chain.playsink = playsink;
1564
1565   GST_DEBUG_OBJECT (playsink, "making text chain %p", chain);
1566
1567   chain->chain.bin = gst_bin_new ("tbin");
1568   bin = GST_BIN_CAST (chain->chain.bin);
1569   gst_object_ref_sink (bin);
1570
1571   videosinkpad = textsinkpad = srcpad = NULL;
1572
1573   /* first try to hook the text pad to the custom sink */
1574   if (playsink->text_sink) {
1575     GST_DEBUG_OBJECT (playsink, "trying configured textsink");
1576     chain->sink = try_element (playsink, playsink->text_sink, FALSE);
1577     if (chain->sink) {
1578       elem =
1579           gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1580           G_TYPE_BOOLEAN);
1581       if (elem) {
1582         /* make sure the sparse subtitles don't participate in the preroll */
1583         g_object_set (elem, "async", FALSE, NULL);
1584         GST_DEBUG_OBJECT (playsink, "adding custom text sink");
1585         gst_bin_add (bin, chain->sink);
1586         /* NOTE streamsynchronizer needs streams decoupled */
1587         /* make a little queue */
1588         chain->queue = gst_element_factory_make ("queue", "subqueue");
1589         if (chain->queue == NULL) {
1590           post_missing_element_message (playsink, "queue");
1591           GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1592               (_("Missing element '%s' - check your GStreamer installation."),
1593                   "queue"), ("rendering might be suboptimal"));
1594         } else {
1595           g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1596               "max-size-bytes", 0, "max-size-time", (gint64) 0,
1597               "silent", TRUE, NULL);
1598           gst_bin_add (bin, chain->queue);
1599         }
1600         /* we have a custom sink, this will be our textsinkpad */
1601         if (gst_element_link_pads_full (chain->queue, "src", chain->sink,
1602                 "sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
1603           /* we're all fine now and we can add the sink to the chain */
1604           GST_DEBUG_OBJECT (playsink, "using custom text sink");
1605           textsinkpad = gst_element_get_static_pad (chain->queue, "sink");
1606         } else {
1607           GST_WARNING_OBJECT (playsink,
1608               "can't find a sink pad on custom text sink");
1609           gst_bin_remove (bin, chain->sink);
1610           gst_bin_remove (bin, chain->queue);
1611           chain->sink = NULL;
1612           chain->queue = NULL;
1613         }
1614         /* try to set sync to true but it's no biggie when we can't */
1615         if (chain->sink && (elem =
1616                 gst_play_sink_find_property_sinks (playsink, chain->sink,
1617                     "sync", G_TYPE_BOOLEAN)))
1618           g_object_set (elem, "sync", TRUE, NULL);
1619
1620         if (!textsinkpad)
1621           gst_bin_remove (bin, chain->sink);
1622       } else {
1623         GST_WARNING_OBJECT (playsink,
1624             "can't find async property in custom text sink");
1625       }
1626     }
1627     if (textsinkpad == NULL) {
1628       GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1629           (_("Custom text sink element is not usable.")),
1630           ("fallback to default textoverlay"));
1631     }
1632   }
1633
1634   if (textsinkpad == NULL) {
1635     if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
1636       /* make a little queue */
1637       chain->queue = gst_element_factory_make ("queue", "vqueue");
1638       if (chain->queue == NULL) {
1639         post_missing_element_message (playsink, "queue");
1640         GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1641             (_("Missing element '%s' - check your GStreamer installation."),
1642                 "queue"), ("video rendering might be suboptimal"));
1643       } else {
1644         g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1645             "max-size-bytes", 0, "max-size-time", (gint64) 0,
1646             "silent", TRUE, NULL);
1647         gst_bin_add (bin, chain->queue);
1648         videosinkpad = gst_element_get_static_pad (chain->queue, "sink");
1649       }
1650
1651       chain->overlay =
1652           gst_element_factory_make ("subtitleoverlay", "suboverlay");
1653       if (chain->overlay == NULL) {
1654         post_missing_element_message (playsink, "subtitleoverlay");
1655         GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1656             (_("Missing element '%s' - check your GStreamer installation."),
1657                 "subtitleoverlay"), ("subtitle rendering disabled"));
1658       } else {
1659         GstElement *element;
1660
1661         gst_bin_add (bin, chain->overlay);
1662
1663         g_object_set (G_OBJECT (chain->overlay), "silent", FALSE, NULL);
1664         if (playsink->font_desc) {
1665           g_object_set (G_OBJECT (chain->overlay), "font-desc",
1666               playsink->font_desc, NULL);
1667         }
1668         if (playsink->subtitle_encoding) {
1669           g_object_set (G_OBJECT (chain->overlay), "subtitle-encoding",
1670               playsink->subtitle_encoding, NULL);
1671         }
1672
1673         gst_element_link_pads_full (chain->queue, "src", chain->overlay,
1674             "video_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS);
1675
1676         /* make another little queue to decouple streams */
1677         element = gst_element_factory_make ("queue", "subqueue");
1678         if (element == NULL) {
1679           post_missing_element_message (playsink, "queue");
1680           GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1681               (_("Missing element '%s' - check your GStreamer installation."),
1682                   "queue"), ("rendering might be suboptimal"));
1683         } else {
1684           g_object_set (G_OBJECT (element), "max-size-buffers", 3,
1685               "max-size-bytes", 0, "max-size-time", (gint64) 0,
1686               "silent", TRUE, NULL);
1687           gst_bin_add (bin, element);
1688           if (gst_element_link_pads_full (element, "src", chain->overlay,
1689                   "subtitle_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
1690             textsinkpad = gst_element_get_static_pad (element, "sink");
1691             srcpad = gst_element_get_static_pad (chain->overlay, "src");
1692           } else {
1693             gst_bin_remove (bin, chain->sink);
1694             gst_bin_remove (bin, chain->overlay);
1695             chain->sink = NULL;
1696             chain->overlay = NULL;
1697             gst_object_unref (videosinkpad);
1698             videosinkpad = NULL;
1699           }
1700         }
1701       }
1702     }
1703   }
1704
1705   if (videosinkpad == NULL) {
1706     /* if we still don't have a videosink, we don't have an overlay. the only
1707      * thing we can do is insert an identity and ghost the src
1708      * and sink pads. */
1709     chain->identity = gst_element_factory_make ("identity", "tidentity");
1710     if (chain->identity == NULL) {
1711       post_missing_element_message (playsink, "identity");
1712       GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1713           (_("Missing element '%s' - check your GStreamer installation."),
1714               "identity"), (NULL));
1715     } else {
1716       g_object_set (chain->identity, "signal-handoffs", FALSE, NULL);
1717       g_object_set (chain->identity, "silent", TRUE, NULL);
1718       gst_bin_add (bin, chain->identity);
1719       srcpad = gst_element_get_static_pad (chain->identity, "src");
1720       videosinkpad = gst_element_get_static_pad (chain->identity, "sink");
1721     }
1722   }
1723
1724   /* expose the ghostpads */
1725   if (videosinkpad) {
1726     chain->videosinkpad = gst_ghost_pad_new ("sink", videosinkpad);
1727     gst_object_unref (videosinkpad);
1728     gst_element_add_pad (chain->chain.bin, chain->videosinkpad);
1729   }
1730   if (textsinkpad) {
1731     chain->textsinkpad = gst_ghost_pad_new ("text_sink", textsinkpad);
1732     gst_object_unref (textsinkpad);
1733     gst_element_add_pad (chain->chain.bin, chain->textsinkpad);
1734   }
1735   if (srcpad) {
1736     chain->srcpad = gst_ghost_pad_new ("src", srcpad);
1737     gst_object_unref (srcpad);
1738     gst_element_add_pad (chain->chain.bin, chain->srcpad);
1739   }
1740
1741   return chain;
1742 }
1743
1744 static void
1745 notify_volume_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
1746 {
1747   gdouble vol;
1748
1749   g_object_get (object, "volume", &vol, NULL);
1750   playsink->volume = vol;
1751
1752   g_object_notify (G_OBJECT (playsink), "volume");
1753 }
1754
1755 static void
1756 notify_mute_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
1757 {
1758   gboolean mute;
1759
1760   g_object_get (object, "mute", &mute, NULL);
1761   playsink->mute = mute;
1762
1763   g_object_notify (G_OBJECT (playsink), "mute");
1764 }
1765
1766 /* make the chain that contains the elements needed to perform
1767  * audio playback.
1768  *
1769  * We add a tee as the first element so that we can link the visualisation chain
1770  * to it when requested.
1771  *
1772  *  +-------------------------------------------------------------+
1773  *  | abin                                                        |
1774  *  |      +---------+   +----------+   +---------+   +---------+ |
1775  *  |      |audioconv|   |audioscale|   | volume  |   |audiosink| |
1776  *  |   +-srck      src-sink       src-sink      src-sink       | |
1777  *  |   |  +---------+   +----------+   +---------+   +---------+ |
1778  * sink-+                                                         |
1779  *  +-------------------------------------------------------------+
1780  */
1781 static GstPlayAudioChain *
1782 gen_audio_chain (GstPlaySink * playsink, gboolean raw)
1783 {
1784   GstPlayAudioChain *chain;
1785   GstBin *bin;
1786   gboolean have_volume;
1787   GstPad *pad;
1788   GstElement *head, *prev, *elem = NULL;
1789
1790   chain = g_new0 (GstPlayAudioChain, 1);
1791   chain->chain.playsink = playsink;
1792   chain->chain.raw = raw;
1793
1794   GST_DEBUG_OBJECT (playsink, "making audio chain %p", chain);
1795
1796   if (playsink->audio_sink) {
1797     GST_DEBUG_OBJECT (playsink, "trying configured audiosink %" GST_PTR_FORMAT,
1798         playsink->audio_sink);
1799     chain->sink = try_element (playsink, playsink->audio_sink, FALSE);
1800   } else {
1801     /* only try fallback if no specific sink was chosen */
1802     if (chain->sink == NULL) {
1803       GST_DEBUG_OBJECT (playsink, "trying autoaudiosink");
1804       elem = gst_element_factory_make ("autoaudiosink", "audiosink");
1805       chain->sink = try_element (playsink, elem, TRUE);
1806     }
1807     if (chain->sink == NULL) {
1808       /* if default sink from config.h is different then try it too */
1809       if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1810         GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_AUDIOSINK);
1811         elem = gst_element_factory_make (DEFAULT_AUDIOSINK, "audiosink");
1812         chain->sink = try_element (playsink, elem, TRUE);
1813       }
1814     }
1815     if (chain->sink)
1816       playsink->audio_sink = gst_object_ref (chain->sink);
1817   }
1818   if (chain->sink == NULL)
1819     goto no_sinks;
1820
1821   chain->chain.bin = gst_bin_new ("abin");
1822   bin = GST_BIN_CAST (chain->chain.bin);
1823   gst_object_ref_sink (bin);
1824   gst_bin_add (bin, chain->sink);
1825
1826   /* we have to add a queue when we need to decouple for the video sink in
1827    * visualisations and for streamsynchronizer */
1828   GST_DEBUG_OBJECT (playsink, "adding audio queue");
1829   chain->queue = gst_element_factory_make ("queue", "aqueue");
1830   if (chain->queue == NULL) {
1831     post_missing_element_message (playsink, "queue");
1832     GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1833         (_("Missing element '%s' - check your GStreamer installation."),
1834             "queue"), ("audio playback and visualizations might not work"));
1835     head = chain->sink;
1836     prev = NULL;
1837   } else {
1838     g_object_set (chain->queue, "silent", TRUE, NULL);
1839     gst_bin_add (bin, chain->queue);
1840     prev = head = chain->queue;
1841   }
1842
1843   /* find ts-offset element */
1844   gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1845       gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1846           G_TYPE_INT64));
1847
1848   /* check if the sink, or something within the sink, has the volume property.
1849    * If it does we don't need to add a volume element.  */
1850   elem =
1851       gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
1852       G_TYPE_DOUBLE);
1853   if (elem) {
1854     chain->volume = elem;
1855
1856     g_signal_connect (chain->volume, "notify::volume",
1857         G_CALLBACK (notify_volume_cb), playsink);
1858
1859     GST_DEBUG_OBJECT (playsink, "the sink has a volume property");
1860     have_volume = TRUE;
1861     chain->sink_volume = TRUE;
1862     /* if the sink also has a mute property we can use this as well. We'll only
1863      * use the mute property if there is a volume property. We can simulate the
1864      * mute with the volume otherwise. */
1865     chain->mute =
1866         gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
1867         G_TYPE_BOOLEAN);
1868     if (chain->mute) {
1869       GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
1870       g_signal_connect (chain->mute, "notify::mute",
1871           G_CALLBACK (notify_mute_cb), playsink);
1872     }
1873     /* use the sink to control the volume and mute */
1874     if (playsink->volume_changed) {
1875       g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
1876       playsink->volume_changed = FALSE;
1877     }
1878     if (playsink->mute_changed) {
1879       if (chain->mute) {
1880         g_object_set (chain->mute, "mute", playsink->mute, NULL);
1881       } else {
1882         if (playsink->mute)
1883           g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
1884       }
1885       playsink->mute_changed = FALSE;
1886     }
1887   } else {
1888     /* no volume, we need to add a volume element when we can */
1889     GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
1890     have_volume = FALSE;
1891     chain->sink_volume = FALSE;
1892   }
1893
1894   if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO) || (!have_volume
1895           && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME))) {
1896     gboolean use_converters = !(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO);
1897     gboolean use_volume =
1898         !have_volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME);
1899     GST_DEBUG_OBJECT (playsink,
1900         "creating audioconvert with use-converters %d, use-volume %d",
1901         use_converters, use_volume);
1902     chain->conv =
1903         g_object_new (GST_TYPE_PLAY_SINK_AUDIO_CONVERT, "name", "aconv",
1904         "use-converters", use_converters, "use-volume", use_volume, NULL);
1905     gst_bin_add (bin, chain->conv);
1906     if (prev) {
1907       if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
1908               GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1909         goto link_failed;
1910     } else {
1911       head = chain->conv;
1912     }
1913     prev = chain->conv;
1914
1915     if (!have_volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
1916       GstPlaySinkAudioConvert *conv =
1917           GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
1918
1919       if (conv->volume) {
1920         chain->volume = conv->volume;
1921         have_volume = TRUE;
1922
1923         g_signal_connect (chain->volume, "notify::volume",
1924             G_CALLBACK (notify_volume_cb), playsink);
1925
1926         /* volume also has the mute property */
1927         chain->mute = chain->volume;
1928         g_signal_connect (chain->mute, "notify::mute",
1929             G_CALLBACK (notify_mute_cb), playsink);
1930
1931         /* configure with the latest volume and mute */
1932         g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume,
1933             NULL);
1934         g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
1935       }
1936     }
1937   }
1938
1939   if (prev) {
1940     /* we only have to link to the previous element if we have something in
1941      * front of the sink */
1942     GST_DEBUG_OBJECT (playsink, "linking to sink");
1943     if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
1944             GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1945       goto link_failed;
1946   }
1947
1948   /* post a warning if we have no way to configure the volume */
1949   if (!have_volume) {
1950     GST_ELEMENT_WARNING (playsink, STREAM, NOT_IMPLEMENTED,
1951         (_("No volume control found")), ("Volume/mute is not available"));
1952   }
1953
1954   /* and ghost the sinkpad of the headmost element */
1955   GST_DEBUG_OBJECT (playsink, "ghosting sink pad");
1956   pad = gst_element_get_static_pad (head, "sink");
1957   chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1958   gst_object_unref (pad);
1959   gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1960
1961   return chain;
1962
1963   /* ERRORS */
1964 no_sinks:
1965   {
1966     if (!elem && !playsink->audio_sink) {
1967       post_missing_element_message (playsink, "autoaudiosink");
1968       if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1969         post_missing_element_message (playsink, DEFAULT_AUDIOSINK);
1970         GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1971             (_("Both autoaudiosink and %s elements are missing."),
1972                 DEFAULT_AUDIOSINK), (NULL));
1973       } else {
1974         GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1975             (_("The autoaudiosink element is missing.")), (NULL));
1976       }
1977     } else {
1978       if (playsink->audio_sink) {
1979         GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1980             (_("Configured audiosink %s is not working."),
1981                 GST_ELEMENT_NAME (playsink->audio_sink)), (NULL));
1982       } else if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1983         GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1984             (_("Both autoaudiosink and %s elements are not working."),
1985                 DEFAULT_AUDIOSINK), (NULL));
1986       } else {
1987         GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1988             (_("The autoaudiosink element is not working.")), (NULL));
1989       }
1990     }
1991     free_chain ((GstPlayChain *) chain);
1992     return NULL;
1993   }
1994 link_failed:
1995   {
1996     GST_ELEMENT_ERROR (playsink, CORE, PAD,
1997         (NULL), ("Failed to configure the audio sink."));
1998     /* checking sink made it READY */
1999     gst_element_set_state (chain->sink, GST_STATE_NULL);
2000     /* Remove chain from the bin to allow reuse later */
2001     gst_bin_remove (bin, chain->sink);
2002     free_chain ((GstPlayChain *) chain);
2003     return NULL;
2004   }
2005 }
2006
2007 static gboolean
2008 setup_audio_chain (GstPlaySink * playsink, gboolean raw)
2009 {
2010   GstElement *elem;
2011   GstPlayAudioChain *chain;
2012   GstStateChangeReturn ret;
2013
2014   chain = playsink->audiochain;
2015
2016   chain->chain.raw = raw;
2017
2018   /* if the chain was active we don't do anything */
2019   if (GST_PLAY_CHAIN (chain)->activated == TRUE)
2020     return TRUE;
2021
2022   /* try to set the sink element to READY again */
2023   ret = gst_element_set_state (chain->sink, GST_STATE_READY);
2024   if (ret == GST_STATE_CHANGE_FAILURE)
2025     return FALSE;
2026
2027   /* find ts-offset element */
2028   gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
2029       gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
2030           G_TYPE_INT64));
2031
2032   /* check if the sink, or something within the sink, has the volume property.
2033    * If it does we don't need to add a volume element.  */
2034   elem =
2035       gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
2036       G_TYPE_DOUBLE);
2037   if (elem) {
2038     chain->volume = elem;
2039
2040     if (playsink->volume_changed) {
2041       GST_DEBUG_OBJECT (playsink, "the sink has a volume property, setting %f",
2042           playsink->volume);
2043       /* use the sink to control the volume */
2044       g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2045       playsink->volume_changed = FALSE;
2046     }
2047
2048     g_signal_connect (chain->volume, "notify::volume",
2049         G_CALLBACK (notify_volume_cb), playsink);
2050     /* if the sink also has a mute property we can use this as well. We'll only
2051      * use the mute property if there is a volume property. We can simulate the
2052      * mute with the volume otherwise. */
2053     chain->mute =
2054         gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
2055         G_TYPE_BOOLEAN);
2056     if (chain->mute) {
2057       GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
2058       g_signal_connect (chain->mute, "notify::mute",
2059           G_CALLBACK (notify_mute_cb), playsink);
2060     }
2061
2062     g_object_set (chain->conv, "use-volume", FALSE, NULL);
2063   } else {
2064     GstPlaySinkAudioConvert *conv =
2065         GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
2066
2067     /* no volume, we need to add a volume element when we can */
2068     g_object_set (chain->conv, "use-volume",
2069         ! !(playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME), NULL);
2070     GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
2071
2072     /* Disconnect signals */
2073     disconnect_chain (chain, playsink);
2074
2075     if (conv->volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
2076       chain->volume = conv->volume;
2077       chain->mute = chain->volume;
2078
2079       g_signal_connect (chain->volume, "notify::volume",
2080           G_CALLBACK (notify_volume_cb), playsink);
2081
2082       g_signal_connect (chain->mute, "notify::mute",
2083           G_CALLBACK (notify_mute_cb), playsink);
2084
2085       /* configure with the latest volume and mute */
2086       g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2087       g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
2088     }
2089
2090     GST_DEBUG_OBJECT (playsink, "reusing existing volume element");
2091   }
2092   return TRUE;
2093 }
2094
2095 /*
2096  *  +-------------------------------------------------------------------+
2097  *  | visbin                                                            |
2098  *  |      +----------+   +------------+   +----------+   +-------+     |
2099  *  |      | visqueue |   | audioconv  |   | audiores |   |  vis  |     |
2100  *  |   +-sink       src-sink + samp  src-sink       src-sink    src-+  |
2101  *  |   |  +----------+   +------------+   +----------+   +-------+  |  |
2102  * sink-+                                                            +-src
2103  *  +-------------------------------------------------------------------+
2104  *
2105  */
2106 static GstPlayVisChain *
2107 gen_vis_chain (GstPlaySink * playsink)
2108 {
2109   GstPlayVisChain *chain;
2110   GstBin *bin;
2111   gboolean res;
2112   GstPad *pad;
2113   GstElement *elem;
2114
2115   chain = g_new0 (GstPlayVisChain, 1);
2116   chain->chain.playsink = playsink;
2117
2118   GST_DEBUG_OBJECT (playsink, "making vis chain %p", chain);
2119
2120   chain->chain.bin = gst_bin_new ("visbin");
2121   bin = GST_BIN_CAST (chain->chain.bin);
2122   gst_object_ref_sink (bin);
2123
2124   /* we're queuing raw audio here, we can remove this queue when we can disable
2125    * async behaviour in the video sink. */
2126   chain->queue = gst_element_factory_make ("queue", "visqueue");
2127   if (chain->queue == NULL)
2128     goto no_queue;
2129   g_object_set (chain->queue, "silent", TRUE, NULL);
2130   gst_bin_add (bin, chain->queue);
2131
2132   chain->conv = gst_element_factory_make ("audioconvert", "aconv");
2133   if (chain->conv == NULL)
2134     goto no_audioconvert;
2135   gst_bin_add (bin, chain->conv);
2136
2137   chain->resample = gst_element_factory_make ("audioresample", "aresample");
2138   if (chain->resample == NULL)
2139     goto no_audioresample;
2140   gst_bin_add (bin, chain->resample);
2141
2142   /* this pad will be used for blocking the dataflow and switching the vis
2143    * plugin */
2144   chain->blockpad = gst_element_get_static_pad (chain->resample, "src");
2145
2146   if (playsink->visualisation) {
2147     GST_DEBUG_OBJECT (playsink, "trying configure vis");
2148     chain->vis = try_element (playsink, playsink->visualisation, FALSE);
2149   }
2150   if (chain->vis == NULL) {
2151     GST_DEBUG_OBJECT (playsink, "trying goom");
2152     elem = gst_element_factory_make ("goom", "vis");
2153     chain->vis = try_element (playsink, elem, TRUE);
2154   }
2155   if (chain->vis == NULL)
2156     goto no_goom;
2157
2158   gst_bin_add (bin, chain->vis);
2159
2160   res = gst_element_link_pads_full (chain->queue, "src", chain->conv, "sink",
2161       GST_PAD_LINK_CHECK_NOTHING);
2162   res &=
2163       gst_element_link_pads_full (chain->conv, "src", chain->resample, "sink",
2164       GST_PAD_LINK_CHECK_NOTHING);
2165   res &=
2166       gst_element_link_pads_full (chain->resample, "src", chain->vis, "sink",
2167       GST_PAD_LINK_CHECK_NOTHING);
2168   if (!res)
2169     goto link_failed;
2170
2171   chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
2172   chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
2173
2174   pad = gst_element_get_static_pad (chain->queue, "sink");
2175   chain->sinkpad = gst_ghost_pad_new ("sink", pad);
2176   gst_object_unref (pad);
2177   gst_element_add_pad (chain->chain.bin, chain->sinkpad);
2178
2179   chain->srcpad = gst_ghost_pad_new ("src", chain->vissrcpad);
2180   gst_element_add_pad (chain->chain.bin, chain->srcpad);
2181
2182   return chain;
2183
2184   /* ERRORS */
2185 no_queue:
2186   {
2187     post_missing_element_message (playsink, "queue");
2188     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2189         (_("Missing element '%s' - check your GStreamer installation."),
2190             "queue"), (NULL));
2191     free_chain ((GstPlayChain *) chain);
2192     return NULL;
2193   }
2194 no_audioconvert:
2195   {
2196     post_missing_element_message (playsink, "audioconvert");
2197     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2198         (_("Missing element '%s' - check your GStreamer installation."),
2199             "audioconvert"), ("possibly a liboil version mismatch?"));
2200     free_chain ((GstPlayChain *) chain);
2201     return NULL;
2202   }
2203 no_audioresample:
2204   {
2205     post_missing_element_message (playsink, "audioresample");
2206     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2207         (_("Missing element '%s' - check your GStreamer installation."),
2208             "audioresample"), (NULL));
2209     free_chain ((GstPlayChain *) chain);
2210     return NULL;
2211   }
2212 no_goom:
2213   {
2214     post_missing_element_message (playsink, "goom");
2215     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2216         (_("Missing element '%s' - check your GStreamer installation."),
2217             "goom"), (NULL));
2218     free_chain ((GstPlayChain *) chain);
2219     return NULL;
2220   }
2221 link_failed:
2222   {
2223     GST_ELEMENT_ERROR (playsink, CORE, PAD,
2224         (NULL), ("Failed to configure the visualisation element."));
2225     /* element made it to READY */
2226     gst_element_set_state (chain->vis, GST_STATE_NULL);
2227     free_chain ((GstPlayChain *) chain);
2228     return NULL;
2229   }
2230 }
2231
2232 /* this function is called when all the request pads are requested and when we
2233  * have to construct the final pipeline. Based on the flags we construct the
2234  * final output pipelines.
2235  */
2236 gboolean
2237 gst_play_sink_reconfigure (GstPlaySink * playsink)
2238 {
2239   GstPlayFlags flags;
2240   gboolean need_audio, need_video, need_deinterlace, need_vis, need_text;
2241
2242   GST_DEBUG_OBJECT (playsink, "reconfiguring");
2243
2244   /* assume we need nothing */
2245   need_audio = need_video = need_deinterlace = need_vis = need_text = FALSE;
2246
2247   GST_PLAY_SINK_LOCK (playsink);
2248   GST_OBJECT_LOCK (playsink);
2249   /* get flags, there are protected with the object lock */
2250   flags = playsink->flags;
2251   GST_OBJECT_UNLOCK (playsink);
2252
2253   /* figure out which components we need */
2254   if (flags & GST_PLAY_FLAG_TEXT && playsink->text_pad) {
2255     /* we have subtitles and we are requested to show it */
2256     need_text = TRUE;
2257   }
2258
2259   if (((flags & GST_PLAY_FLAG_VIDEO)
2260           || (flags & GST_PLAY_FLAG_NATIVE_VIDEO)) && playsink->video_pad) {
2261     /* we have video and we are requested to show it */
2262     need_video = TRUE;
2263
2264     /* we only deinterlace if native video is not requested and
2265      * we have raw video */
2266     if ((flags & GST_PLAY_FLAG_DEINTERLACE)
2267         && !(flags & GST_PLAY_FLAG_NATIVE_VIDEO) && playsink->video_pad_raw)
2268       need_deinterlace = TRUE;
2269   }
2270
2271   if (playsink->audio_pad) {
2272     if ((flags & GST_PLAY_FLAG_AUDIO) || (flags & GST_PLAY_FLAG_NATIVE_AUDIO)) {
2273       need_audio = TRUE;
2274     }
2275     if (playsink->audio_pad_raw) {
2276       /* only can do vis with raw uncompressed audio */
2277       if (flags & GST_PLAY_FLAG_VIS && !need_video) {
2278         /* also add video when we add visualisation */
2279         need_video = TRUE;
2280         need_vis = TRUE;
2281       }
2282     }
2283   }
2284
2285   /* we have a text_pad and we need text rendering, in this case we need a
2286    * video_pad to combine the video with the text or visualizations */
2287   if (need_text && !need_video) {
2288     if (playsink->video_pad) {
2289       need_video = TRUE;
2290     } else if (need_audio) {
2291       GST_ELEMENT_WARNING (playsink, STREAM, FORMAT,
2292           (_("Can't play a text file without video or visualizations.")),
2293           ("Have text pad but no video pad or visualizations"));
2294       need_text = FALSE;
2295     } else {
2296       GST_ELEMENT_ERROR (playsink, STREAM, FORMAT,
2297           (_("Can't play a text file without video or visualizations.")),
2298           ("Have text pad but no video pad or visualizations"));
2299       GST_PLAY_SINK_UNLOCK (playsink);
2300       return FALSE;
2301     }
2302   }
2303
2304   GST_DEBUG_OBJECT (playsink, "audio:%d, video:%d, vis:%d, text:%d", need_audio,
2305       need_video, need_vis, need_text);
2306
2307   /* set up video pipeline */
2308   if (need_video) {
2309     gboolean raw, async;
2310
2311     /* we need a raw sink when we do vis or when we have a raw pad */
2312     raw = need_vis ? TRUE : playsink->video_pad_raw;
2313     /* we try to set the sink async=FALSE when we need vis, this way we can
2314      * avoid a queue in the audio chain. */
2315     async = !need_vis;
2316
2317     GST_DEBUG_OBJECT (playsink, "adding video, raw %d",
2318         playsink->video_pad_raw);
2319
2320     if (playsink->videochain) {
2321       /* try to reactivate the chain */
2322       if (!setup_video_chain (playsink, raw, async)) {
2323         if (playsink->video_sinkpad_stream_synchronizer) {
2324           gst_element_release_request_pad (GST_ELEMENT_CAST
2325               (playsink->stream_synchronizer),
2326               playsink->video_sinkpad_stream_synchronizer);
2327           gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2328           playsink->video_sinkpad_stream_synchronizer = NULL;
2329           gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2330           playsink->video_srcpad_stream_synchronizer = NULL;
2331         }
2332
2333         add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2334
2335         /* Remove the sink from the bin to keep its state
2336          * and unparent it to allow reuse */
2337         if (playsink->videochain->sink)
2338           gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
2339               playsink->videochain->sink);
2340
2341         activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2342         free_chain ((GstPlayChain *) playsink->videochain);
2343         playsink->videochain = NULL;
2344       }
2345     }
2346
2347     if (!playsink->videochain)
2348       playsink->videochain = gen_video_chain (playsink, raw, async);
2349     if (!playsink->videochain)
2350       goto no_chain;
2351
2352     if (!playsink->video_sinkpad_stream_synchronizer) {
2353       GstIterator *it;
2354
2355       playsink->video_sinkpad_stream_synchronizer =
2356           gst_element_get_request_pad (GST_ELEMENT_CAST
2357           (playsink->stream_synchronizer), "sink_%d");
2358       it = gst_pad_iterate_internal_links
2359           (playsink->video_sinkpad_stream_synchronizer);
2360       g_assert (it);
2361       gst_iterator_next (it,
2362           (gpointer *) & playsink->video_srcpad_stream_synchronizer);
2363       g_assert (playsink->video_srcpad_stream_synchronizer);
2364       gst_iterator_free (it);
2365     }
2366
2367     if (playsink->video_pad)
2368       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad),
2369           playsink->video_sinkpad_stream_synchronizer);
2370
2371     if (need_deinterlace) {
2372       if (!playsink->videodeinterlacechain)
2373         playsink->videodeinterlacechain =
2374             gen_video_deinterlace_chain (playsink);
2375       if (!playsink->videodeinterlacechain)
2376         goto no_chain;
2377
2378       GST_DEBUG_OBJECT (playsink, "adding video deinterlace chain");
2379
2380       GST_DEBUG_OBJECT (playsink, "setting up deinterlacing chain");
2381
2382       add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
2383       activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
2384
2385       gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2386           playsink->videodeinterlacechain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2387     } else {
2388       if (playsink->videodeinterlacechain) {
2389         add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2390         activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
2391             FALSE);
2392       }
2393     }
2394
2395     GST_DEBUG_OBJECT (playsink, "adding video chain");
2396     add_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
2397     activate_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
2398     /* if we are not part of vis or subtitles, set the ghostpad target */
2399     if (!need_vis && !need_text && (!playsink->textchain
2400             || !playsink->text_pad)) {
2401       GST_DEBUG_OBJECT (playsink, "ghosting video sinkpad");
2402       if (need_deinterlace)
2403         gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
2404             playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2405       else
2406         gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2407             playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2408     }
2409   } else {
2410     GST_DEBUG_OBJECT (playsink, "no video needed");
2411     if (playsink->videochain) {
2412       GST_DEBUG_OBJECT (playsink, "removing video chain");
2413       if (playsink->vischain) {
2414         GstPad *srcpad;
2415
2416         GST_DEBUG_OBJECT (playsink, "unlinking vis chain");
2417
2418         /* also had visualisation, release the tee srcpad before we then
2419          * unlink the video from it */
2420         if (playsink->audio_tee_vissrc) {
2421           gst_element_release_request_pad (playsink->audio_tee,
2422               playsink->audio_tee_vissrc);
2423           gst_object_unref (playsink->audio_tee_vissrc);
2424           playsink->audio_tee_vissrc = NULL;
2425         }
2426         srcpad =
2427             gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2428         gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
2429       }
2430
2431       if (playsink->video_sinkpad_stream_synchronizer) {
2432         gst_element_release_request_pad (GST_ELEMENT_CAST
2433             (playsink->stream_synchronizer),
2434             playsink->video_sinkpad_stream_synchronizer);
2435         gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2436         playsink->video_sinkpad_stream_synchronizer = NULL;
2437         gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2438         playsink->video_srcpad_stream_synchronizer = NULL;
2439       }
2440
2441       add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2442       activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2443       if (playsink->videochain->ts_offset)
2444         gst_object_unref (playsink->videochain->ts_offset);
2445       playsink->videochain->ts_offset = NULL;
2446     }
2447
2448     if (playsink->videodeinterlacechain) {
2449       add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2450       activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2451     }
2452
2453     if (playsink->video_pad)
2454       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
2455   }
2456
2457   if (need_audio) {
2458     gboolean raw;
2459
2460     GST_DEBUG_OBJECT (playsink, "adding audio");
2461
2462     /* get a raw sink if we are asked for a raw pad */
2463     raw = playsink->audio_pad_raw;
2464
2465     if (playsink->audiochain) {
2466       /* try to reactivate the chain */
2467       if (!setup_audio_chain (playsink, raw)) {
2468         GST_DEBUG_OBJECT (playsink, "removing current audio chain");
2469         if (playsink->audio_tee_asrc) {
2470           gst_element_release_request_pad (playsink->audio_tee,
2471               playsink->audio_tee_asrc);
2472           gst_object_unref (playsink->audio_tee_asrc);
2473           playsink->audio_tee_asrc = NULL;
2474         }
2475
2476         if (playsink->audio_sinkpad_stream_synchronizer) {
2477           gst_element_release_request_pad (GST_ELEMENT_CAST
2478               (playsink->stream_synchronizer),
2479               playsink->audio_sinkpad_stream_synchronizer);
2480           gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
2481           playsink->audio_sinkpad_stream_synchronizer = NULL;
2482           gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
2483           playsink->audio_srcpad_stream_synchronizer = NULL;
2484         }
2485
2486         add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2487
2488         /* Remove the sink from the bin to keep its state
2489          * and unparent it to allow reuse */
2490         if (playsink->audiochain->sink)
2491           gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
2492               playsink->audiochain->sink);
2493
2494         activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2495         disconnect_chain (playsink->audiochain, playsink);
2496         playsink->audiochain->volume = NULL;
2497         playsink->audiochain->mute = NULL;
2498         if (playsink->audiochain->ts_offset)
2499           gst_object_unref (playsink->audiochain->ts_offset);
2500         playsink->audiochain->ts_offset = NULL;
2501         free_chain ((GstPlayChain *) playsink->audiochain);
2502         playsink->audiochain = NULL;
2503         playsink->volume_changed = playsink->mute_changed = FALSE;
2504       }
2505     }
2506
2507     if (!playsink->audiochain) {
2508       GST_DEBUG_OBJECT (playsink, "creating new audio chain");
2509       playsink->audiochain = gen_audio_chain (playsink, raw);
2510     }
2511
2512     if (!playsink->audio_sinkpad_stream_synchronizer) {
2513       GstIterator *it;
2514
2515       playsink->audio_sinkpad_stream_synchronizer =
2516           gst_element_get_request_pad (GST_ELEMENT_CAST
2517           (playsink->stream_synchronizer), "sink_%d");
2518       it = gst_pad_iterate_internal_links
2519           (playsink->audio_sinkpad_stream_synchronizer);
2520       g_assert (it);
2521       gst_iterator_next (it,
2522           (gpointer *) & playsink->audio_srcpad_stream_synchronizer);
2523       g_assert (playsink->audio_srcpad_stream_synchronizer);
2524       gst_iterator_free (it);
2525     }
2526
2527     if (playsink->audiochain) {
2528       GST_DEBUG_OBJECT (playsink, "adding audio chain");
2529       if (playsink->audio_tee_asrc == NULL) {
2530         playsink->audio_tee_asrc =
2531             gst_element_get_request_pad (playsink->audio_tee, "src%d");
2532       }
2533       add_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
2534       activate_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
2535       gst_pad_link_full (playsink->audio_tee_asrc,
2536           playsink->audio_sinkpad_stream_synchronizer,
2537           GST_PAD_LINK_CHECK_NOTHING);
2538       gst_pad_link_full (playsink->audio_srcpad_stream_synchronizer,
2539           playsink->audiochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2540     }
2541   } else {
2542     GST_DEBUG_OBJECT (playsink, "no audio needed");
2543     /* we have no audio or we are requested to not play audio */
2544     if (playsink->audiochain) {
2545       GST_DEBUG_OBJECT (playsink, "removing audio chain");
2546       /* release the audio pad */
2547       if (playsink->audio_tee_asrc) {
2548         gst_element_release_request_pad (playsink->audio_tee,
2549             playsink->audio_tee_asrc);
2550         gst_object_unref (playsink->audio_tee_asrc);
2551         playsink->audio_tee_asrc = NULL;
2552       }
2553
2554       if (playsink->audio_sinkpad_stream_synchronizer) {
2555         gst_element_release_request_pad (GST_ELEMENT_CAST
2556             (playsink->stream_synchronizer),
2557             playsink->audio_sinkpad_stream_synchronizer);
2558         gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
2559         playsink->audio_sinkpad_stream_synchronizer = NULL;
2560         gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
2561         playsink->audio_srcpad_stream_synchronizer = NULL;
2562       }
2563
2564       if (playsink->audiochain->sink_volume) {
2565         disconnect_chain (playsink->audiochain, playsink);
2566         playsink->audiochain->volume = NULL;
2567         playsink->audiochain->mute = NULL;
2568         if (playsink->audiochain->ts_offset)
2569           gst_object_unref (playsink->audiochain->ts_offset);
2570         playsink->audiochain->ts_offset = NULL;
2571       }
2572       add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2573       activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2574     }
2575   }
2576
2577   if (need_vis) {
2578     GstPad *srcpad;
2579
2580     if (!playsink->vischain)
2581       playsink->vischain = gen_vis_chain (playsink);
2582
2583     GST_DEBUG_OBJECT (playsink, "adding visualisation");
2584
2585     if (playsink->vischain) {
2586       GST_DEBUG_OBJECT (playsink, "setting up vis chain");
2587       srcpad =
2588           gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2589       add_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
2590       activate_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
2591       if (playsink->audio_tee_vissrc == NULL) {
2592         playsink->audio_tee_vissrc =
2593             gst_element_get_request_pad (playsink->audio_tee, "src%d");
2594       }
2595       gst_pad_link_full (playsink->audio_tee_vissrc,
2596           playsink->vischain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2597       gst_pad_link_full (srcpad, playsink->video_sinkpad_stream_synchronizer,
2598           GST_PAD_LINK_CHECK_NOTHING);
2599       gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2600           playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2601       gst_object_unref (srcpad);
2602     }
2603   } else {
2604     GST_DEBUG_OBJECT (playsink, "no vis needed");
2605     if (playsink->vischain) {
2606       if (playsink->audio_tee_vissrc) {
2607         gst_element_release_request_pad (playsink->audio_tee,
2608             playsink->audio_tee_vissrc);
2609         gst_object_unref (playsink->audio_tee_vissrc);
2610         playsink->audio_tee_vissrc = NULL;
2611       }
2612       GST_DEBUG_OBJECT (playsink, "removing vis chain");
2613       add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
2614       activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
2615     }
2616   }
2617
2618   if (need_text) {
2619     GST_DEBUG_OBJECT (playsink, "adding text");
2620     if (!playsink->textchain) {
2621       GST_DEBUG_OBJECT (playsink, "creating text chain");
2622       playsink->textchain = gen_text_chain (playsink);
2623     }
2624     if (playsink->textchain) {
2625       GstIterator *it;
2626
2627       GST_DEBUG_OBJECT (playsink, "adding text chain");
2628       if (playsink->textchain->overlay)
2629         g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", FALSE,
2630             NULL);
2631       add_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
2632
2633       if (!playsink->text_sinkpad_stream_synchronizer) {
2634         playsink->text_sinkpad_stream_synchronizer =
2635             gst_element_get_request_pad (GST_ELEMENT_CAST
2636             (playsink->stream_synchronizer), "sink_%d");
2637         it = gst_pad_iterate_internal_links
2638             (playsink->text_sinkpad_stream_synchronizer);
2639         g_assert (it);
2640         gst_iterator_next (it,
2641             (gpointer *) & playsink->text_srcpad_stream_synchronizer);
2642         g_assert (playsink->text_srcpad_stream_synchronizer);
2643         gst_iterator_free (it);
2644
2645         gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad),
2646             playsink->text_sinkpad_stream_synchronizer);
2647         gst_pad_link_full (playsink->text_srcpad_stream_synchronizer,
2648             playsink->textchain->textsinkpad, GST_PAD_LINK_CHECK_NOTHING);
2649       }
2650
2651       if (need_vis) {
2652         GstPad *srcpad;
2653
2654         srcpad =
2655             gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2656         gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
2657         gst_pad_link_full (srcpad, playsink->textchain->videosinkpad,
2658             GST_PAD_LINK_CHECK_NOTHING);
2659         gst_object_unref (srcpad);
2660       } else {
2661         if (need_deinterlace)
2662           gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
2663               playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
2664         else
2665           gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2666               playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
2667       }
2668       gst_pad_link_full (playsink->textchain->srcpad,
2669           playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2670
2671       activate_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
2672     }
2673   } else {
2674     GST_DEBUG_OBJECT (playsink, "no text needed");
2675     /* we have no subtitles/text or we are requested to not show them */
2676
2677     if (playsink->text_sinkpad_stream_synchronizer) {
2678       gst_element_release_request_pad (GST_ELEMENT_CAST
2679           (playsink->stream_synchronizer),
2680           playsink->text_sinkpad_stream_synchronizer);
2681       gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
2682       playsink->text_sinkpad_stream_synchronizer = NULL;
2683       gst_object_unref (playsink->text_srcpad_stream_synchronizer);
2684       playsink->text_srcpad_stream_synchronizer = NULL;
2685     }
2686
2687     if (playsink->textchain) {
2688       if (playsink->text_pad == NULL) {
2689         /* no text pad, remove the chain entirely */
2690         GST_DEBUG_OBJECT (playsink, "removing text chain");
2691         add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
2692         activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
2693       } else {
2694         /* we have a chain and a textpad, turn the subtitles off */
2695         GST_DEBUG_OBJECT (playsink, "turning off the text");
2696         if (playsink->textchain->overlay)
2697           g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", TRUE,
2698               NULL);
2699       }
2700     }
2701     if (!need_video && playsink->video_pad) {
2702       if (playsink->video_sinkpad_stream_synchronizer) {
2703         gst_element_release_request_pad (GST_ELEMENT_CAST
2704             (playsink->stream_synchronizer),
2705             playsink->video_sinkpad_stream_synchronizer);
2706         gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2707         playsink->video_sinkpad_stream_synchronizer = NULL;
2708         gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2709         playsink->video_srcpad_stream_synchronizer = NULL;
2710       }
2711
2712       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
2713     }
2714
2715     if (playsink->text_pad && !playsink->textchain)
2716       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad), NULL);
2717   }
2718   update_av_offset (playsink);
2719   do_async_done (playsink);
2720   GST_PLAY_SINK_UNLOCK (playsink);
2721
2722   return TRUE;
2723
2724   /* ERRORS */
2725 no_chain:
2726   {
2727     /* gen_ chain already posted error */
2728     GST_DEBUG_OBJECT (playsink, "failed to setup chain");
2729     GST_PLAY_SINK_UNLOCK (playsink);
2730     return FALSE;
2731   }
2732 }
2733
2734 /**
2735  * gst_play_sink_set_flags:
2736  * @playsink: a #GstPlaySink
2737  * @flags: #GstPlayFlags
2738  *
2739  * Configure @flags on @playsink. The flags control the behaviour of @playsink
2740  * when constructing the sink pipelins.
2741  *
2742  * Returns: TRUE if the flags could be configured.
2743  */
2744 gboolean
2745 gst_play_sink_set_flags (GstPlaySink * playsink, GstPlayFlags flags)
2746 {
2747   g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), FALSE);
2748
2749   GST_OBJECT_LOCK (playsink);
2750   playsink->flags = flags;
2751   GST_OBJECT_UNLOCK (playsink);
2752
2753   return TRUE;
2754 }
2755
2756 /**
2757  * gst_play_sink_get_flags:
2758  * @playsink: a #GstPlaySink
2759  *
2760  * Get the flags of @playsink. That flags control the behaviour of the sink when
2761  * it constructs the sink pipelines.
2762  *
2763  * Returns: the currently configured #GstPlayFlags.
2764  */
2765 GstPlayFlags
2766 gst_play_sink_get_flags (GstPlaySink * playsink)
2767 {
2768   GstPlayFlags res;
2769
2770   g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), 0);
2771
2772   GST_OBJECT_LOCK (playsink);
2773   res = playsink->flags;
2774   GST_OBJECT_UNLOCK (playsink);
2775
2776   return res;
2777 }
2778
2779 void
2780 gst_play_sink_set_font_desc (GstPlaySink * playsink, const gchar * desc)
2781 {
2782   GstPlayTextChain *chain;
2783
2784   GST_PLAY_SINK_LOCK (playsink);
2785   chain = (GstPlayTextChain *) playsink->textchain;
2786   g_free (playsink->font_desc);
2787   playsink->font_desc = g_strdup (desc);
2788   if (chain && chain->overlay) {
2789     g_object_set (chain->overlay, "font-desc", desc, NULL);
2790   }
2791   GST_PLAY_SINK_UNLOCK (playsink);
2792 }
2793
2794 gchar *
2795 gst_play_sink_get_font_desc (GstPlaySink * playsink)
2796 {
2797   gchar *result = NULL;
2798   GstPlayTextChain *chain;
2799
2800   GST_PLAY_SINK_LOCK (playsink);
2801   chain = (GstPlayTextChain *) playsink->textchain;
2802   if (chain && chain->overlay) {
2803     g_object_get (chain->overlay, "font-desc", &result, NULL);
2804     playsink->font_desc = g_strdup (result);
2805   } else {
2806     result = g_strdup (playsink->font_desc);
2807   }
2808   GST_PLAY_SINK_UNLOCK (playsink);
2809
2810   return result;
2811 }
2812
2813 void
2814 gst_play_sink_set_subtitle_encoding (GstPlaySink * playsink,
2815     const gchar * encoding)
2816 {
2817   GstPlayTextChain *chain;
2818
2819   GST_PLAY_SINK_LOCK (playsink);
2820   chain = (GstPlayTextChain *) playsink->textchain;
2821   g_free (playsink->subtitle_encoding);
2822   playsink->subtitle_encoding = g_strdup (encoding);
2823   if (chain && chain->overlay) {
2824     g_object_set (chain->overlay, "subtitle-encoding", encoding, NULL);
2825   }
2826   GST_PLAY_SINK_UNLOCK (playsink);
2827 }
2828
2829 gchar *
2830 gst_play_sink_get_subtitle_encoding (GstPlaySink * playsink)
2831 {
2832   gchar *result = NULL;
2833   GstPlayTextChain *chain;
2834
2835   GST_PLAY_SINK_LOCK (playsink);
2836   chain = (GstPlayTextChain *) playsink->textchain;
2837   if (chain && chain->overlay) {
2838     g_object_get (chain->overlay, "subtitle-encoding", &result, NULL);
2839     playsink->subtitle_encoding = g_strdup (result);
2840   } else {
2841     result = g_strdup (playsink->subtitle_encoding);
2842   }
2843   GST_PLAY_SINK_UNLOCK (playsink);
2844
2845   return result;
2846 }
2847
2848 static void
2849 update_av_offset (GstPlaySink * playsink)
2850 {
2851   gint64 av_offset;
2852   GstPlayAudioChain *achain;
2853   GstPlayVideoChain *vchain;
2854
2855   av_offset = playsink->av_offset;
2856   achain = (GstPlayAudioChain *) playsink->audiochain;
2857   vchain = (GstPlayVideoChain *) playsink->videochain;
2858
2859   if (achain && vchain && achain->ts_offset && vchain->ts_offset) {
2860     g_object_set (achain->ts_offset, "ts-offset", MAX (0, -av_offset), NULL);
2861     g_object_set (vchain->ts_offset, "ts-offset", MAX (0, av_offset), NULL);
2862   } else {
2863     GST_LOG_OBJECT (playsink, "no ts_offset elements");
2864   }
2865 }
2866
2867 void
2868 gst_play_sink_set_av_offset (GstPlaySink * playsink, gint64 av_offset)
2869 {
2870   GST_PLAY_SINK_LOCK (playsink);
2871   playsink->av_offset = av_offset;
2872   update_av_offset (playsink);
2873   GST_PLAY_SINK_UNLOCK (playsink);
2874 }
2875
2876 gint64
2877 gst_play_sink_get_av_offset (GstPlaySink * playsink)
2878 {
2879   gint64 result;
2880
2881   GST_PLAY_SINK_LOCK (playsink);
2882   result = playsink->av_offset;
2883   GST_PLAY_SINK_UNLOCK (playsink);
2884
2885   return result;
2886 }
2887
2888 /**
2889  * gst_play_sink_get_last_frame:
2890  * @playsink: a #GstPlaySink
2891  *
2892  * Get the last displayed frame from @playsink. This frame is in the native
2893  * format of the sink element, the caps on the result buffer contain the format
2894  * of the frame data.
2895  *
2896  * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
2897  * available.
2898  */
2899 GstBuffer *
2900 gst_play_sink_get_last_frame (GstPlaySink * playsink)
2901 {
2902   GstBuffer *result = NULL;
2903   GstPlayVideoChain *chain;
2904
2905   GST_PLAY_SINK_LOCK (playsink);
2906   GST_DEBUG_OBJECT (playsink, "taking last frame");
2907   /* get the video chain if we can */
2908   if ((chain = (GstPlayVideoChain *) playsink->videochain)) {
2909     GST_DEBUG_OBJECT (playsink, "found video chain");
2910     /* see if the chain is active */
2911     if (chain->chain.activated && chain->sink) {
2912       GstElement *elem;
2913
2914       GST_DEBUG_OBJECT (playsink, "video chain active and has a sink");
2915
2916       /* find and get the last-buffer property now */
2917       if ((elem =
2918               gst_play_sink_find_property (playsink, chain->sink,
2919                   "last-buffer", GST_TYPE_BUFFER))) {
2920         GST_DEBUG_OBJECT (playsink, "getting last-buffer property");
2921         g_object_get (elem, "last-buffer", &result, NULL);
2922         gst_object_unref (elem);
2923       }
2924     }
2925   }
2926   GST_PLAY_SINK_UNLOCK (playsink);
2927
2928   return result;
2929 }
2930
2931 /**
2932  * gst_play_sink_convert_frame:
2933  * @playsink: a #GstPlaySink
2934  * @caps: a #GstCaps
2935  *
2936  * Get the last displayed frame from @playsink. If caps is %NULL, the video will
2937  * be in the native format of the sink element and the caps on the buffer
2938  * describe the format of the frame. If @caps is not %NULL, the video
2939  * frame will be converted to the format of the caps.
2940  *
2941  * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
2942  * available or when the conversion failed.
2943  */
2944 GstBuffer *
2945 gst_play_sink_convert_frame (GstPlaySink * playsink, GstCaps * caps)
2946 {
2947   GstBuffer *result;
2948
2949   result = gst_play_sink_get_last_frame (playsink);
2950   if (result != NULL && caps != NULL) {
2951     GstBuffer *temp;
2952     GError *err = NULL;
2953
2954     temp = gst_video_convert_frame (result, caps, 25 * GST_SECOND, &err);
2955     gst_buffer_unref (result);
2956     if (temp == NULL && err) {
2957       /* I'm really uncertain whether we should make playsink post an error
2958        * on the bus or not. It's not like it's a critical issue regarding
2959        * playsink behaviour. */
2960       GST_ERROR ("Error converting frame: %s", err->message);
2961       g_error_free (err);
2962     }
2963     result = temp;
2964   }
2965   return result;
2966 }
2967
2968 static gboolean
2969 is_raw_structure (GstStructure * s)
2970 {
2971   const gchar *name;
2972
2973   name = gst_structure_get_name (s);
2974
2975   if (g_str_has_prefix (name, "video/x-raw-") ||
2976       g_str_has_prefix (name, "audio/x-raw-"))
2977     return TRUE;
2978   return FALSE;
2979 }
2980
2981 static gboolean
2982 is_raw_pad (GstPad * pad)
2983 {
2984   GstPad *peer = gst_pad_get_peer (pad);
2985   GstCaps *caps;
2986   gboolean raw = TRUE;
2987
2988   if (!peer)
2989     return raw;
2990
2991   caps = gst_pad_get_negotiated_caps (peer);
2992   if (!caps) {
2993     guint i, n;
2994
2995     caps = gst_pad_get_caps_reffed (peer);
2996
2997     n = gst_caps_get_size (caps);
2998     for (i = 0; i < n; i++) {
2999       gboolean r = is_raw_structure (gst_caps_get_structure (caps, i));
3000
3001       if (i == 0) {
3002         raw = r;
3003       } else if (raw != r) {
3004         GST_ERROR_OBJECT (pad,
3005             "Caps contains raw and non-raw structures: %" GST_PTR_FORMAT, caps);
3006         raw = FALSE;
3007         break;
3008       }
3009     }
3010   } else {
3011     raw = is_raw_structure (gst_caps_get_structure (caps, 0));
3012   }
3013   gst_caps_unref (caps);
3014   gst_object_unref (peer);
3015
3016   return raw;
3017 }
3018
3019 static void
3020 sinkpad_blocked_cb (GstPad * blockedpad, gboolean blocked, gpointer user_data)
3021 {
3022   GstPlaySink *playsink = (GstPlaySink *) user_data;
3023   GstPad *pad;
3024
3025   GST_PLAY_SINK_LOCK (playsink);
3026
3027   pad = GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (blockedpad)));
3028   if (pad == playsink->video_pad) {
3029     playsink->video_pad_blocked = blocked;
3030     GST_DEBUG_OBJECT (pad, "Video pad blocked: %d", blocked);
3031     if (!blocked) {
3032       PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO_RAW);
3033       PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO);
3034     }
3035   } else if (pad == playsink->audio_pad) {
3036     playsink->audio_pad_blocked = blocked;
3037     GST_DEBUG_OBJECT (pad, "Audio pad blocked: %d", blocked);
3038     if (!blocked) {
3039       PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO_RAW);
3040       PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO);
3041     }
3042   } else if (pad == playsink->text_pad) {
3043     playsink->text_pad_blocked = blocked;
3044     GST_DEBUG_OBJECT (pad, "Text pad blocked: %d", blocked);
3045     if (!blocked)
3046       PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_TEXT);
3047   }
3048
3049   if (!blocked) {
3050     gst_object_unref (pad);
3051     GST_PLAY_SINK_UNLOCK (playsink);
3052     return;
3053   }
3054
3055   /* We reconfigure when for ALL streams:
3056    * * there isn't a pad
3057    * * OR the pad is blocked
3058    * * OR there are no pending blocks on that pad
3059    */
3060
3061   if ((!playsink->video_pad || playsink->video_pad_blocked
3062           || !PENDING_VIDEO_BLOCK (playsink)) && (!playsink->audio_pad
3063           || playsink->audio_pad_blocked || !PENDING_AUDIO_BLOCK (playsink))
3064       && (!playsink->text_pad || playsink->text_pad_blocked
3065           || !PENDING_TEXT_BLOCK (playsink))) {
3066     GST_DEBUG_OBJECT (playsink, "All pads blocked -- reconfiguring");
3067
3068     if (playsink->video_pad) {
3069       playsink->video_pad_raw = is_raw_pad (playsink->video_pad);
3070       GST_DEBUG_OBJECT (playsink, "Video pad is raw: %d",
3071           playsink->video_pad_raw);
3072     }
3073
3074     if (playsink->audio_pad) {
3075       playsink->audio_pad_raw = is_raw_pad (playsink->audio_pad);
3076       GST_DEBUG_OBJECT (playsink, "Audio pad is raw: %d",
3077           playsink->audio_pad_raw);
3078     }
3079
3080     gst_play_sink_reconfigure (playsink);
3081
3082     if (playsink->video_pad) {
3083       GstPad *opad =
3084           GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3085               (playsink->video_pad)));
3086       gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3087           gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3088       gst_object_unref (opad);
3089     }
3090
3091     if (playsink->audio_pad) {
3092       GstPad *opad =
3093           GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3094               (playsink->audio_pad)));
3095       gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3096           gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3097       gst_object_unref (opad);
3098     }
3099
3100     if (playsink->text_pad) {
3101       GstPad *opad =
3102           GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3103               (playsink->text_pad)));
3104       gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3105           gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3106       gst_object_unref (opad);
3107     }
3108   }
3109
3110   gst_object_unref (pad);
3111
3112   GST_PLAY_SINK_UNLOCK (playsink);
3113 }
3114
3115 static void
3116 caps_notify_cb (GstPad * pad, GParamSpec * unused, GstPlaySink * playsink)
3117 {
3118   gboolean reconfigure = FALSE;
3119   GstCaps *caps;
3120   gboolean raw;
3121
3122   g_object_get (pad, "caps", &caps, NULL);
3123   if (!caps)
3124     return;
3125
3126   if (pad == playsink->audio_pad) {
3127     raw = is_raw_pad (pad);
3128     reconfigure = (! !playsink->audio_pad_raw != ! !raw)
3129         && playsink->audiochain;
3130     GST_DEBUG_OBJECT (pad,
3131         "Audio caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3132         reconfigure, caps);
3133   } else if (pad == playsink->video_pad) {
3134     raw = is_raw_pad (pad);
3135     reconfigure = (! !playsink->video_pad_raw != ! !raw)
3136         && playsink->videochain;
3137     GST_DEBUG_OBJECT (pad,
3138         "Video caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3139         reconfigure, caps);
3140   }
3141
3142   gst_caps_unref (caps);
3143
3144   if (reconfigure) {
3145     GST_PLAY_SINK_LOCK (playsink);
3146     if (playsink->video_pad) {
3147       GstPad *opad =
3148           GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3149               (playsink->video_pad)));
3150       gst_pad_set_blocked_async_full (opad, TRUE, sinkpad_blocked_cb,
3151           gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3152       gst_object_unref (opad);
3153     }
3154
3155     if (playsink->audio_pad) {
3156       GstPad *opad =
3157           GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3158               (playsink->audio_pad)));
3159       gst_pad_set_blocked_async_full (opad, TRUE, sinkpad_blocked_cb,
3160           gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3161       gst_object_unref (opad);
3162     }
3163
3164     if (playsink->text_pad) {
3165       GstPad *opad =
3166           GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3167               (playsink->text_pad)));
3168       gst_pad_set_blocked_async_full (opad, TRUE, sinkpad_blocked_cb,
3169           gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3170       gst_object_unref (opad);
3171     }
3172     GST_PLAY_SINK_UNLOCK (playsink);
3173   }
3174 }
3175
3176 /**
3177  * gst_play_sink_request_pad
3178  * @playsink: a #GstPlaySink
3179  * @type: a #GstPlaySinkType
3180  *
3181  * Create or return a pad of @type.
3182  *
3183  * Returns: a #GstPad of @type or %NULL when the pad could not be created.
3184  */
3185 GstPad *
3186 gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type)
3187 {
3188   GstPad *res = NULL;
3189   gboolean created = FALSE;
3190   gboolean activate = TRUE;
3191   const gchar *pad_name = NULL;
3192
3193   GST_DEBUG_OBJECT (playsink, "request pad type %d", type);
3194
3195   GST_PLAY_SINK_LOCK (playsink);
3196   switch (type) {
3197     case GST_PLAY_SINK_TYPE_AUDIO_RAW:
3198     case GST_PLAY_SINK_TYPE_AUDIO:
3199       pad_name = "audio_sink";
3200       if (!playsink->audio_tee) {
3201         GST_LOG_OBJECT (playsink, "creating tee");
3202         /* create tee when needed. This element will feed the audio sink chain
3203          * and the vis chain. */
3204         playsink->audio_tee = gst_element_factory_make ("tee", "audiotee");
3205         if (playsink->audio_tee == NULL) {
3206           post_missing_element_message (playsink, "tee");
3207           GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
3208               (_("Missing element '%s' - check your GStreamer installation."),
3209                   "tee"), (NULL));
3210           res = NULL;
3211           break;
3212         } else {
3213           playsink->audio_tee_sink =
3214               gst_element_get_static_pad (playsink->audio_tee, "sink");
3215           gst_bin_add (GST_BIN_CAST (playsink), playsink->audio_tee);
3216           gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
3217         }
3218       } else {
3219         gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
3220       }
3221       if (!playsink->audio_pad) {
3222         GST_LOG_OBJECT (playsink, "ghosting tee sinkpad");
3223         playsink->audio_pad =
3224             gst_ghost_pad_new (pad_name, playsink->audio_tee_sink);
3225         g_signal_connect (G_OBJECT (playsink->audio_pad), "notify::caps",
3226             G_CALLBACK (caps_notify_cb), playsink);
3227         created = TRUE;
3228       }
3229       playsink->audio_pad_raw = FALSE;
3230       res = playsink->audio_pad;
3231       break;
3232     case GST_PLAY_SINK_TYPE_VIDEO_RAW:
3233     case GST_PLAY_SINK_TYPE_VIDEO:
3234       pad_name = "video_sink";
3235       if (!playsink->video_pad) {
3236         GST_LOG_OBJECT (playsink, "ghosting videosink");
3237         playsink->video_pad =
3238             gst_ghost_pad_new_no_target (pad_name, GST_PAD_SINK);
3239         g_signal_connect (G_OBJECT (playsink->video_pad), "notify::caps",
3240             G_CALLBACK (caps_notify_cb), playsink);
3241         created = TRUE;
3242       }
3243       playsink->video_pad_raw = FALSE;
3244       res = playsink->video_pad;
3245       break;
3246     case GST_PLAY_SINK_TYPE_TEXT:
3247       GST_LOG_OBJECT (playsink, "ghosting text");
3248       if (!playsink->text_pad) {
3249         playsink->text_pad =
3250             gst_ghost_pad_new_no_target ("text_sink", GST_PAD_SINK);
3251         created = TRUE;
3252       }
3253       res = playsink->text_pad;
3254       break;
3255     case GST_PLAY_SINK_TYPE_FLUSHING:
3256     {
3257       gchar *padname;
3258
3259       /* we need a unique padname for the flushing pad. */
3260       padname = g_strdup_printf ("flushing_%d", playsink->count);
3261       res = gst_ghost_pad_new_no_target (padname, GST_PAD_SINK);
3262       g_free (padname);
3263       playsink->count++;
3264       activate = FALSE;
3265       created = TRUE;
3266       break;
3267     }
3268     default:
3269       res = NULL;
3270       break;
3271   }
3272   GST_PLAY_SINK_UNLOCK (playsink);
3273
3274   if (created && res) {
3275     /* we have to add the pad when it's active or we get an error when the
3276      * element is 'running' */
3277     gst_pad_set_active (res, TRUE);
3278     gst_element_add_pad (GST_ELEMENT_CAST (playsink), res);
3279     if (type != GST_PLAY_SINK_TYPE_FLUSHING) {
3280       GstPad *blockpad =
3281           GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (res)));
3282
3283       gst_pad_set_blocked_async_full (blockpad, TRUE, sinkpad_blocked_cb,
3284           gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3285       PENDING_FLAG_SET (playsink, type);
3286       gst_object_unref (blockpad);
3287     }
3288     if (!activate)
3289       gst_pad_set_active (res, activate);
3290   }
3291
3292   return res;
3293 }
3294
3295 static GstPad *
3296 gst_play_sink_request_new_pad (GstElement * element, GstPadTemplate * templ,
3297     const gchar * name)
3298 {
3299   GstPlaySink *psink;
3300   GstPad *pad;
3301   GstPlaySinkType type;
3302   const gchar *tplname;
3303
3304   g_return_val_if_fail (templ != NULL, NULL);
3305
3306   GST_DEBUG_OBJECT (element, "name:%s", name);
3307
3308   psink = GST_PLAY_SINK (element);
3309   tplname = GST_PAD_TEMPLATE_NAME_TEMPLATE (templ);
3310
3311   /* Figure out the GstPlaySinkType based on the template */
3312   if (!strcmp (tplname, "audio_sink"))
3313     type = GST_PLAY_SINK_TYPE_AUDIO;
3314   else if (!strcmp (tplname, "audio_raw_sink"))
3315     type = GST_PLAY_SINK_TYPE_AUDIO_RAW;
3316   else if (!strcmp (tplname, "video_sink"))
3317     type = GST_PLAY_SINK_TYPE_VIDEO;
3318   else if (!strcmp (tplname, "video_raw_sink"))
3319     type = GST_PLAY_SINK_TYPE_VIDEO_RAW;
3320   else if (!strcmp (tplname, "text_sink"))
3321     type = GST_PLAY_SINK_TYPE_TEXT;
3322   else
3323     goto unknown_template;
3324
3325   pad = gst_play_sink_request_pad (psink, type);
3326   return pad;
3327
3328 unknown_template:
3329   GST_WARNING_OBJECT (element, "Unknown pad template");
3330   return NULL;
3331 }
3332
3333 void
3334 gst_play_sink_release_pad (GstPlaySink * playsink, GstPad * pad)
3335 {
3336   GstPad **res = NULL;
3337   gboolean untarget = TRUE;
3338
3339   GST_DEBUG_OBJECT (playsink, "release pad %" GST_PTR_FORMAT, pad);
3340
3341   GST_PLAY_SINK_LOCK (playsink);
3342   if (pad == playsink->video_pad) {
3343     res = &playsink->video_pad;
3344     g_signal_handlers_disconnect_by_func (playsink->video_pad, caps_notify_cb,
3345         playsink);
3346   } else if (pad == playsink->audio_pad) {
3347     res = &playsink->audio_pad;
3348     g_signal_handlers_disconnect_by_func (playsink->audio_pad, caps_notify_cb,
3349         playsink);
3350   } else if (pad == playsink->text_pad) {
3351     res = &playsink->text_pad;
3352   } else {
3353     /* try to release the given pad anyway, these could be the FLUSHING pads. */
3354     res = &pad;
3355     untarget = FALSE;
3356   }
3357   GST_PLAY_SINK_UNLOCK (playsink);
3358
3359   if (*res) {
3360     GST_DEBUG_OBJECT (playsink, "deactivate pad %" GST_PTR_FORMAT, *res);
3361     gst_pad_set_active (*res, FALSE);
3362     if (untarget) {
3363       GST_DEBUG_OBJECT (playsink, "untargeting pad %" GST_PTR_FORMAT, *res);
3364       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (*res), NULL);
3365     }
3366     GST_DEBUG_OBJECT (playsink, "remove pad %" GST_PTR_FORMAT, *res);
3367     gst_element_remove_pad (GST_ELEMENT_CAST (playsink), *res);
3368     *res = NULL;
3369   }
3370 }
3371
3372 static void
3373 gst_play_sink_release_request_pad (GstElement * element, GstPad * pad)
3374 {
3375   GstPlaySink *psink = GST_PLAY_SINK (element);
3376
3377   gst_play_sink_release_pad (psink, pad);
3378 }
3379
3380 static void
3381 gst_play_sink_handle_message (GstBin * bin, GstMessage * message)
3382 {
3383   GstPlaySink *playsink;
3384
3385   playsink = GST_PLAY_SINK_CAST (bin);
3386
3387   switch (GST_MESSAGE_TYPE (message)) {
3388     case GST_MESSAGE_STEP_DONE:
3389     {
3390       GstFormat format;
3391       guint64 amount;
3392       gdouble rate;
3393       gboolean flush, intermediate, eos;
3394       guint64 duration;
3395
3396       GST_INFO_OBJECT (playsink, "Handling step-done message");
3397       gst_message_parse_step_done (message, &format, &amount, &rate, &flush,
3398           &intermediate, &duration, &eos);
3399
3400       if (format == GST_FORMAT_BUFFERS) {
3401         /* for the buffer format, we align the other streams */
3402         if (playsink->audiochain) {
3403           GstEvent *event;
3404
3405           event =
3406               gst_event_new_step (GST_FORMAT_TIME, duration, rate, flush,
3407               intermediate);
3408
3409           if (!gst_element_send_event (playsink->audiochain->chain.bin, event)) {
3410             GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
3411           }
3412         }
3413       }
3414       GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
3415       break;
3416     }
3417     default:
3418       GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
3419       break;
3420   }
3421 }
3422
3423 /* Send an event to our sinks until one of them works; don't then send to the
3424  * remaining sinks (unlike GstBin)
3425  * Special case: If a text sink is set we need to send the event
3426  * to them in case it's source is different from the a/v stream's source.
3427  */
3428 static gboolean
3429 gst_play_sink_send_event_to_sink (GstPlaySink * playsink, GstEvent * event)
3430 {
3431   gboolean res = TRUE;
3432
3433   if (playsink->textchain && playsink->textchain->sink) {
3434     gst_event_ref (event);
3435     if ((res = gst_element_send_event (playsink->textchain->chain.bin, event))) {
3436       GST_DEBUG_OBJECT (playsink, "Sent event successfully to text sink");
3437     } else {
3438       GST_DEBUG_OBJECT (playsink, "Event failed when sent to text sink");
3439     }
3440   }
3441
3442   if (playsink->videochain) {
3443     gst_event_ref (event);
3444     if ((res = gst_element_send_event (playsink->videochain->chain.bin, event))) {
3445       GST_DEBUG_OBJECT (playsink, "Sent event successfully to video sink");
3446       goto done;
3447     }
3448     GST_DEBUG_OBJECT (playsink, "Event failed when sent to video sink");
3449   }
3450   if (playsink->audiochain) {
3451     gst_event_ref (event);
3452     if ((res = gst_element_send_event (playsink->audiochain->chain.bin, event))) {
3453       GST_DEBUG_OBJECT (playsink, "Sent event successfully to audio sink");
3454       goto done;
3455     }
3456     GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
3457   }
3458
3459 done:
3460   gst_event_unref (event);
3461   return res;
3462 }
3463
3464 /* We only want to send the event to a single sink (overriding GstBin's
3465  * behaviour), but we want to keep GstPipeline's behaviour - wrapping seek
3466  * events appropriately. So, this is a messy duplication of code. */
3467 static gboolean
3468 gst_play_sink_send_event (GstElement * element, GstEvent * event)
3469 {
3470   gboolean res = FALSE;
3471   GstEventType event_type = GST_EVENT_TYPE (event);
3472   GstPlaySink *playsink;
3473
3474   playsink = GST_PLAY_SINK_CAST (element);
3475
3476   switch (event_type) {
3477     case GST_EVENT_SEEK:
3478       GST_DEBUG_OBJECT (element, "Sending event to a sink");
3479       res = gst_play_sink_send_event_to_sink (playsink, event);
3480       break;
3481     case GST_EVENT_STEP:
3482     {
3483       GstFormat format;
3484       guint64 amount;
3485       gdouble rate;
3486       gboolean flush, intermediate;
3487
3488       gst_event_parse_step (event, &format, &amount, &rate, &flush,
3489           &intermediate);
3490
3491       if (format == GST_FORMAT_BUFFERS) {
3492         /* for buffers, we will try to step video frames, for other formats we
3493          * send the step to all sinks */
3494         res = gst_play_sink_send_event_to_sink (playsink, event);
3495       } else {
3496         res =
3497             GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
3498             event);
3499       }
3500       break;
3501     }
3502     default:
3503       res =
3504           GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
3505           event);
3506       break;
3507   }
3508   return res;
3509 }
3510
3511 static GstStateChangeReturn
3512 gst_play_sink_change_state (GstElement * element, GstStateChange transition)
3513 {
3514   GstStateChangeReturn ret;
3515   GstStateChangeReturn bret;
3516
3517   GstPlaySink *playsink;
3518
3519   playsink = GST_PLAY_SINK (element);
3520
3521   switch (transition) {
3522     case GST_STATE_CHANGE_READY_TO_PAUSED:
3523       playsink->need_async_start = TRUE;
3524       /* we want to go async to PAUSED until we managed to configure and add the
3525        * sinks */
3526       do_async_start (playsink);
3527       ret = GST_STATE_CHANGE_ASYNC;
3528       break;
3529     case GST_STATE_CHANGE_PAUSED_TO_READY:
3530       /* unblock all pads here */
3531       GST_PLAY_SINK_LOCK (playsink);
3532       if (playsink->video_pad) {
3533         GstPad *opad =
3534             GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3535                 (playsink->video_pad)));
3536         if (gst_pad_is_blocked (opad)) {
3537           gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3538               gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3539         }
3540         gst_object_unref (opad);
3541         playsink->video_pad_blocked = FALSE;
3542       }
3543
3544       if (playsink->audio_pad) {
3545         GstPad *opad =
3546             GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3547                 (playsink->audio_pad)));
3548
3549         if (gst_pad_is_blocked (opad)) {
3550           gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3551               gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3552         }
3553         gst_object_unref (opad);
3554         playsink->audio_pad_blocked = FALSE;
3555       }
3556
3557       if (playsink->text_pad) {
3558         GstPad *opad =
3559             GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3560                 (playsink->text_pad)));
3561         if (gst_pad_is_blocked (opad)) {
3562           gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3563               gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3564         }
3565         gst_object_unref (opad);
3566         playsink->text_pad_blocked = FALSE;
3567       }
3568       GST_PLAY_SINK_UNLOCK (playsink);
3569       /* fall through */
3570     case GST_STATE_CHANGE_READY_TO_NULL:
3571       if (playsink->audiochain && playsink->audiochain->sink_volume) {
3572         /* remove our links to the mute and volume elements when they were
3573          * provided by a sink */
3574         disconnect_chain (playsink->audiochain, playsink);
3575         playsink->audiochain->volume = NULL;
3576         playsink->audiochain->mute = NULL;
3577       }
3578
3579       if (playsink->audiochain && playsink->audiochain->ts_offset) {
3580         gst_object_unref (playsink->audiochain->ts_offset);
3581         playsink->audiochain->ts_offset = NULL;
3582       }
3583
3584       if (playsink->videochain && playsink->videochain->ts_offset) {
3585         gst_object_unref (playsink->videochain->ts_offset);
3586         playsink->videochain->ts_offset = NULL;
3587       }
3588       ret = GST_STATE_CHANGE_SUCCESS;
3589       break;
3590     default:
3591       /* all other state changes return SUCCESS by default, this value can be
3592        * overridden by the result of the children */
3593       ret = GST_STATE_CHANGE_SUCCESS;
3594       break;
3595   }
3596
3597   /* do the state change of the children */
3598   bret =
3599       GST_ELEMENT_CLASS (gst_play_sink_parent_class)->change_state (element,
3600       transition);
3601   /* now look at the result of our children and adjust the return value */
3602   switch (bret) {
3603     case GST_STATE_CHANGE_FAILURE:
3604       /* failure, we stop */
3605       goto activate_failed;
3606     case GST_STATE_CHANGE_NO_PREROLL:
3607       /* some child returned NO_PREROLL. This is strange but we never know. We
3608        * commit our async state change (if any) and return the NO_PREROLL */
3609       do_async_done (playsink);
3610       ret = bret;
3611       break;
3612     case GST_STATE_CHANGE_ASYNC:
3613       /* some child was async, return this */
3614       ret = bret;
3615       break;
3616     default:
3617       /* return our previously configured return value */
3618       break;
3619   }
3620
3621   switch (transition) {
3622     case GST_STATE_CHANGE_READY_TO_PAUSED:
3623       break;
3624     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
3625       /* FIXME Release audio device when we implement that */
3626       playsink->need_async_start = TRUE;
3627       break;
3628     case GST_STATE_CHANGE_PAUSED_TO_READY:{
3629       if (playsink->video_sinkpad_stream_synchronizer) {
3630         gst_element_release_request_pad (GST_ELEMENT_CAST
3631             (playsink->stream_synchronizer),
3632             playsink->video_sinkpad_stream_synchronizer);
3633         gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3634         playsink->video_sinkpad_stream_synchronizer = NULL;
3635         gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3636         playsink->video_srcpad_stream_synchronizer = NULL;
3637       }
3638       if (playsink->audio_sinkpad_stream_synchronizer) {
3639         gst_element_release_request_pad (GST_ELEMENT_CAST
3640             (playsink->stream_synchronizer),
3641             playsink->audio_sinkpad_stream_synchronizer);
3642         gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
3643         playsink->audio_sinkpad_stream_synchronizer = NULL;
3644         gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
3645         playsink->audio_srcpad_stream_synchronizer = NULL;
3646       }
3647       if (playsink->text_sinkpad_stream_synchronizer) {
3648         gst_element_release_request_pad (GST_ELEMENT_CAST
3649             (playsink->stream_synchronizer),
3650             playsink->text_sinkpad_stream_synchronizer);
3651         gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
3652         playsink->text_sinkpad_stream_synchronizer = NULL;
3653         gst_object_unref (playsink->text_srcpad_stream_synchronizer);
3654         playsink->text_srcpad_stream_synchronizer = NULL;
3655       }
3656     }
3657       /* fall through */
3658     case GST_STATE_CHANGE_READY_TO_NULL:
3659       /* remove sinks we added */
3660       if (playsink->videodeinterlacechain) {
3661         activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
3662             FALSE);
3663         add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3664       }
3665       if (playsink->videochain) {
3666         activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3667         add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3668       }
3669       if (playsink->audiochain) {
3670         activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3671         add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3672       }
3673       if (playsink->vischain) {
3674         activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3675         add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3676       }
3677       if (playsink->textchain) {
3678         activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3679         add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3680       }
3681       do_async_done (playsink);
3682       /* when going to READY, keep elements around as long as possible,
3683        * so they may be re-used faster next time/url around.
3684        * when really going to NULL, clean up everything completely. */
3685       if (transition == GST_STATE_CHANGE_READY_TO_NULL) {
3686
3687         /* Unparent the sinks to allow reuse */
3688         if (playsink->videochain && playsink->videochain->sink)
3689           gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
3690               playsink->videochain->sink);
3691         if (playsink->audiochain && playsink->audiochain->sink)
3692           gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
3693               playsink->audiochain->sink);
3694         if (playsink->textchain && playsink->textchain->sink)
3695           gst_bin_remove (GST_BIN_CAST (playsink->textchain->chain.bin),
3696               playsink->textchain->sink);
3697
3698         if (playsink->audio_sink != NULL)
3699           gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
3700         if (playsink->video_sink != NULL)
3701           gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
3702         if (playsink->visualisation != NULL)
3703           gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
3704         if (playsink->text_sink != NULL)
3705           gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
3706
3707         free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
3708         playsink->videodeinterlacechain = NULL;
3709         free_chain ((GstPlayChain *) playsink->videochain);
3710         playsink->videochain = NULL;
3711         free_chain ((GstPlayChain *) playsink->audiochain);
3712         playsink->audiochain = NULL;
3713         free_chain ((GstPlayChain *) playsink->vischain);
3714         playsink->vischain = NULL;
3715         free_chain ((GstPlayChain *) playsink->textchain);
3716         playsink->textchain = NULL;
3717       }
3718       break;
3719     default:
3720       break;
3721   }
3722   return ret;
3723
3724   /* ERRORS */
3725 activate_failed:
3726   {
3727     GST_DEBUG_OBJECT (element,
3728         "element failed to change states -- activation problem?");
3729     return GST_STATE_CHANGE_FAILURE;
3730   }
3731 }
3732
3733 static void
3734 gst_play_sink_set_property (GObject * object, guint prop_id,
3735     const GValue * value, GParamSpec * spec)
3736 {
3737   GstPlaySink *playsink = GST_PLAY_SINK (object);
3738
3739   switch (prop_id) {
3740     case PROP_FLAGS:
3741       gst_play_sink_set_flags (playsink, g_value_get_flags (value));
3742       break;
3743     case PROP_VOLUME:
3744       gst_play_sink_set_volume (playsink, g_value_get_double (value));
3745       break;
3746     case PROP_MUTE:
3747       gst_play_sink_set_mute (playsink, g_value_get_boolean (value));
3748       break;
3749     case PROP_FONT_DESC:
3750       gst_play_sink_set_font_desc (playsink, g_value_get_string (value));
3751       break;
3752     case PROP_SUBTITLE_ENCODING:
3753       gst_play_sink_set_subtitle_encoding (playsink,
3754           g_value_get_string (value));
3755       break;
3756     case PROP_VIS_PLUGIN:
3757       gst_play_sink_set_vis_plugin (playsink, g_value_get_object (value));
3758       break;
3759     case PROP_AV_OFFSET:
3760       gst_play_sink_set_av_offset (playsink, g_value_get_int64 (value));
3761       break;
3762     case PROP_VIDEO_SINK:
3763       gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_VIDEO,
3764           g_value_get_object (value));
3765       break;
3766     case PROP_AUDIO_SINK:
3767       gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_AUDIO,
3768           g_value_get_object (value));
3769       break;
3770     case PROP_TEXT_SINK:
3771       gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_TEXT,
3772           g_value_get_object (value));
3773       break;
3774     default:
3775       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
3776       break;
3777   }
3778 }
3779
3780 static void
3781 gst_play_sink_get_property (GObject * object, guint prop_id,
3782     GValue * value, GParamSpec * spec)
3783 {
3784   GstPlaySink *playsink = GST_PLAY_SINK (object);
3785
3786   switch (prop_id) {
3787     case PROP_FLAGS:
3788       g_value_set_flags (value, gst_play_sink_get_flags (playsink));
3789       break;
3790     case PROP_VOLUME:
3791       g_value_set_double (value, gst_play_sink_get_volume (playsink));
3792       break;
3793     case PROP_MUTE:
3794       g_value_set_boolean (value, gst_play_sink_get_mute (playsink));
3795       break;
3796     case PROP_FONT_DESC:
3797       g_value_take_string (value, gst_play_sink_get_font_desc (playsink));
3798       break;
3799     case PROP_SUBTITLE_ENCODING:
3800       g_value_take_string (value,
3801           gst_play_sink_get_subtitle_encoding (playsink));
3802       break;
3803     case PROP_VIS_PLUGIN:
3804       g_value_take_object (value, gst_play_sink_get_vis_plugin (playsink));
3805       break;
3806     case PROP_FRAME:
3807       gst_value_take_buffer (value, gst_play_sink_get_last_frame (playsink));
3808       break;
3809     case PROP_AV_OFFSET:
3810       g_value_set_int64 (value, gst_play_sink_get_av_offset (playsink));
3811       break;
3812     case PROP_VIDEO_SINK:
3813       g_value_take_object (value, gst_play_sink_get_sink (playsink,
3814               GST_PLAY_SINK_TYPE_VIDEO));
3815       break;
3816     case PROP_AUDIO_SINK:
3817       g_value_take_object (value, gst_play_sink_get_sink (playsink,
3818               GST_PLAY_SINK_TYPE_AUDIO));
3819       break;
3820     case PROP_TEXT_SINK:
3821       g_value_take_object (value, gst_play_sink_get_sink (playsink,
3822               GST_PLAY_SINK_TYPE_TEXT));
3823       break;
3824     default:
3825       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
3826       break;
3827   }
3828 }
3829
3830 gboolean
3831 gst_play_sink_plugin_init (GstPlugin * plugin)
3832 {
3833   GST_DEBUG_CATEGORY_INIT (gst_play_sink_debug, "playsink", 0, "play bin");
3834
3835   return gst_element_register (plugin, "playsink", GST_RANK_NONE,
3836       GST_TYPE_PLAY_SINK);
3837 }