playsink: Add a software color-balance element before the sink if the sink doesn...
[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 has_color_balance_element (GstElement * element)
1255 {
1256   GstElement *cb = NULL;
1257
1258   if (GST_IS_COLOR_BALANCE (element))
1259     return TRUE;
1260   else if (!GST_IS_BIN (element))
1261     return FALSE;
1262
1263   cb = gst_bin_get_by_interface (GST_BIN (element), GST_TYPE_COLOR_BALANCE);
1264   gst_object_unref (cb);
1265
1266   return (cb != NULL);
1267 }
1268
1269 /* make the element (bin) that contains the elements needed to perform
1270  * video display.
1271  *
1272  *  +------------------------------------------------------------+
1273  *  | vbin                                                       |
1274  *  |      +-------+   +----------+   +----------+   +---------+ |
1275  *  |      | queue |   |colorspace|   |videoscale|   |videosink| |
1276  *  |   +-sink    src-sink       src-sink       src-sink       | |
1277  *  |   |  +-------+   +----------+   +----------+   +---------+ |
1278  * sink-+                                                        |
1279  *  +------------------------------------------------------------+
1280  *
1281  */
1282 static GstPlayVideoChain *
1283 gen_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1284 {
1285   GstPlayVideoChain *chain;
1286   GstBin *bin;
1287   GstPad *pad;
1288   GstElement *head = NULL, *prev = NULL, *elem = NULL;
1289
1290   chain = g_new0 (GstPlayVideoChain, 1);
1291   chain->chain.playsink = playsink;
1292   chain->chain.raw = raw;
1293
1294   GST_DEBUG_OBJECT (playsink, "making video chain %p", chain);
1295
1296   if (playsink->video_sink) {
1297     GST_DEBUG_OBJECT (playsink, "trying configured videosink");
1298     chain->sink = try_element (playsink, playsink->video_sink, FALSE);
1299   } else {
1300     /* only try fallback if no specific sink was chosen */
1301     if (chain->sink == NULL) {
1302       GST_DEBUG_OBJECT (playsink, "trying autovideosink");
1303       elem = gst_element_factory_make ("autovideosink", "videosink");
1304       chain->sink = try_element (playsink, elem, TRUE);
1305     }
1306     if (chain->sink == NULL) {
1307       /* if default sink from config.h is different then try it too */
1308       if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1309         GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_VIDEOSINK);
1310         elem = gst_element_factory_make (DEFAULT_VIDEOSINK, "videosink");
1311         chain->sink = try_element (playsink, elem, TRUE);
1312       }
1313     }
1314     if (chain->sink)
1315       playsink->video_sink = gst_object_ref (chain->sink);
1316   }
1317   if (chain->sink == NULL)
1318     goto no_sinks;
1319   head = chain->sink;
1320
1321   /* if we can disable async behaviour of the sink, we can avoid adding a
1322    * queue for the audio chain. */
1323   elem =
1324       gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1325       G_TYPE_BOOLEAN);
1326   if (elem) {
1327     GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1328         async, GST_ELEMENT_NAME (elem));
1329     g_object_set (elem, "async", async, NULL);
1330     chain->async = async;
1331   } else {
1332     GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1333     chain->async = TRUE;
1334   }
1335
1336   /* find ts-offset element */
1337   gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1338       gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1339           G_TYPE_INT64));
1340
1341   /* create a bin to hold objects, as we create them we add them to this bin so
1342    * that when something goes wrong we only need to unref the bin */
1343   chain->chain.bin = gst_bin_new ("vbin");
1344   bin = GST_BIN_CAST (chain->chain.bin);
1345   gst_object_ref_sink (bin);
1346   gst_bin_add (bin, chain->sink);
1347
1348   /* decouple decoder from sink, this improves playback quite a lot since the
1349    * decoder can continue while the sink blocks for synchronisation. We don't
1350    * need a lot of buffers as this consumes a lot of memory and we don't want
1351    * too little because else we would be context switching too quickly. */
1352   chain->queue = gst_element_factory_make ("queue", "vqueue");
1353   if (chain->queue == NULL) {
1354     post_missing_element_message (playsink, "queue");
1355     GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1356         (_("Missing element '%s' - check your GStreamer installation."),
1357             "queue"), ("video rendering might be suboptimal"));
1358     head = chain->sink;
1359     prev = NULL;
1360   } else {
1361     g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1362         "max-size-bytes", 0, "max-size-time", (gint64) 0, "silent", TRUE, NULL);
1363     gst_bin_add (bin, chain->queue);
1364     head = prev = chain->queue;
1365   }
1366
1367   if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)
1368       || (!has_color_balance_element (chain->sink)
1369           && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE))) {
1370     gboolean use_converters = !(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO);
1371     gboolean use_balance = !has_color_balance_element (chain->sink)
1372         && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE);
1373
1374     GST_DEBUG_OBJECT (playsink, "creating videoconverter");
1375     chain->conv =
1376         g_object_new (GST_TYPE_PLAY_SINK_VIDEO_CONVERT, "name", "vconv",
1377         "use-converters", use_converters, "use-balance", use_balance, NULL);
1378     gst_bin_add (bin, chain->conv);
1379     if (prev) {
1380       if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
1381               GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1382         goto link_failed;
1383     } else {
1384       head = chain->conv;
1385     }
1386     prev = chain->conv;
1387   }
1388
1389   if (prev) {
1390     GST_DEBUG_OBJECT (playsink, "linking to sink");
1391     if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
1392             GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1393       goto link_failed;
1394   }
1395
1396   pad = gst_element_get_static_pad (head, "sink");
1397   chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1398   gst_object_unref (pad);
1399
1400   gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1401
1402   return chain;
1403
1404   /* ERRORS */
1405 no_sinks:
1406   {
1407     if (!elem && !playsink->video_sink) {
1408       post_missing_element_message (playsink, "autovideosink");
1409       if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1410         post_missing_element_message (playsink, DEFAULT_VIDEOSINK);
1411         GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1412             (_("Both autovideosink and %s elements are missing."),
1413                 DEFAULT_VIDEOSINK), (NULL));
1414       } else {
1415         GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1416             (_("The autovideosink element is missing.")), (NULL));
1417       }
1418     } else {
1419       if (playsink->video_sink) {
1420         GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1421             (_("Configured videosink %s is not working."),
1422                 GST_ELEMENT_NAME (playsink->video_sink)), (NULL));
1423       } else if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1424         GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1425             (_("Both autovideosink and %s elements are not working."),
1426                 DEFAULT_VIDEOSINK), (NULL));
1427       } else {
1428         GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1429             (_("The autovideosink element is not working.")), (NULL));
1430       }
1431     }
1432     free_chain ((GstPlayChain *) chain);
1433     return NULL;
1434   }
1435 link_failed:
1436   {
1437     GST_ELEMENT_ERROR (playsink, CORE, PAD,
1438         (NULL), ("Failed to configure the video sink."));
1439     /* checking sink made it READY */
1440     gst_element_set_state (chain->sink, GST_STATE_NULL);
1441     /* Remove chain from the bin to allow reuse later */
1442     gst_bin_remove (bin, chain->sink);
1443     free_chain ((GstPlayChain *) chain);
1444     return NULL;
1445   }
1446 }
1447
1448 static gboolean
1449 setup_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1450 {
1451   GstElement *elem;
1452   GstPlayVideoChain *chain;
1453   GstStateChangeReturn ret;
1454
1455   chain = playsink->videochain;
1456
1457   chain->chain.raw = raw;
1458
1459   /* if the chain was active we don't do anything */
1460   if (GST_PLAY_CHAIN (chain)->activated == TRUE)
1461     return TRUE;
1462
1463   /* try to set the sink element to READY again */
1464   ret = gst_element_set_state (chain->sink, GST_STATE_READY);
1465   if (ret == GST_STATE_CHANGE_FAILURE)
1466     return FALSE;
1467
1468   /* find ts-offset element */
1469
1470   gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1471       gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1472           G_TYPE_INT64));
1473
1474   /* if we can disable async behaviour of the sink, we can avoid adding a
1475    * queue for the audio chain. */
1476   elem =
1477       gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1478       G_TYPE_BOOLEAN);
1479   if (elem) {
1480     GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1481         async, GST_ELEMENT_NAME (elem));
1482     g_object_set (elem, "async", async, NULL);
1483     chain->async = async;
1484   } else {
1485     GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1486     chain->async = TRUE;
1487   }
1488
1489   if (chain->conv)
1490     g_object_set (chain->conv, "use-balance",
1491         !has_color_balance_element (chain->sink)
1492         && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE), NULL);
1493
1494   return TRUE;
1495 }
1496
1497 /* make an element for playback of video with subtitles embedded.
1498  * Only used for *raw* video streams.
1499  *
1500  *  +--------------------------------------------+
1501  *  | tbin                                       |
1502  *  |     +--------+      +-----------------+    |
1503  *  |     | queue  |      | subtitleoverlay |    |
1504  * video--src     sink---video_sink         |    |
1505  *  |     +--------+     |                src--src
1506  * text------------------text_sink          |    |
1507  *  |                     +-----------------+    |
1508  *  +--------------------------------------------+
1509  *
1510  */
1511 static GstPlayTextChain *
1512 gen_text_chain (GstPlaySink * playsink)
1513 {
1514   GstPlayTextChain *chain;
1515   GstBin *bin;
1516   GstElement *elem;
1517   GstPad *videosinkpad, *textsinkpad, *srcpad;
1518
1519   chain = g_new0 (GstPlayTextChain, 1);
1520   chain->chain.playsink = playsink;
1521
1522   GST_DEBUG_OBJECT (playsink, "making text chain %p", chain);
1523
1524   chain->chain.bin = gst_bin_new ("tbin");
1525   bin = GST_BIN_CAST (chain->chain.bin);
1526   gst_object_ref_sink (bin);
1527
1528   videosinkpad = textsinkpad = srcpad = NULL;
1529
1530   /* first try to hook the text pad to the custom sink */
1531   if (playsink->text_sink) {
1532     GST_DEBUG_OBJECT (playsink, "trying configured textsink");
1533     chain->sink = try_element (playsink, playsink->text_sink, FALSE);
1534     if (chain->sink) {
1535       elem =
1536           gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1537           G_TYPE_BOOLEAN);
1538       if (elem) {
1539         /* make sure the sparse subtitles don't participate in the preroll */
1540         g_object_set (elem, "async", FALSE, NULL);
1541         GST_DEBUG_OBJECT (playsink, "adding custom text sink");
1542         gst_bin_add (bin, chain->sink);
1543         /* NOTE streamsynchronizer needs streams decoupled */
1544         /* make a little queue */
1545         chain->queue = gst_element_factory_make ("queue", "subqueue");
1546         if (chain->queue == NULL) {
1547           post_missing_element_message (playsink, "queue");
1548           GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1549               (_("Missing element '%s' - check your GStreamer installation."),
1550                   "queue"), ("rendering might be suboptimal"));
1551         } else {
1552           g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1553               "max-size-bytes", 0, "max-size-time", (gint64) 0,
1554               "silent", TRUE, NULL);
1555           gst_bin_add (bin, chain->queue);
1556         }
1557         /* we have a custom sink, this will be our textsinkpad */
1558         if (gst_element_link_pads_full (chain->queue, "src", chain->sink,
1559                 "sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
1560           /* we're all fine now and we can add the sink to the chain */
1561           GST_DEBUG_OBJECT (playsink, "using custom text sink");
1562           textsinkpad = gst_element_get_static_pad (chain->queue, "sink");
1563         } else {
1564           GST_WARNING_OBJECT (playsink,
1565               "can't find a sink pad on custom text sink");
1566           gst_bin_remove (bin, chain->sink);
1567           gst_bin_remove (bin, chain->queue);
1568           chain->sink = NULL;
1569           chain->queue = NULL;
1570         }
1571         /* try to set sync to true but it's no biggie when we can't */
1572         if (chain->sink && (elem =
1573                 gst_play_sink_find_property_sinks (playsink, chain->sink,
1574                     "sync", G_TYPE_BOOLEAN)))
1575           g_object_set (elem, "sync", TRUE, NULL);
1576
1577         if (!textsinkpad)
1578           gst_bin_remove (bin, chain->sink);
1579       } else {
1580         GST_WARNING_OBJECT (playsink,
1581             "can't find async property in custom text sink");
1582       }
1583     }
1584     if (textsinkpad == NULL) {
1585       GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1586           (_("Custom text sink element is not usable.")),
1587           ("fallback to default textoverlay"));
1588     }
1589   }
1590
1591   if (textsinkpad == NULL) {
1592     if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
1593       /* make a little queue */
1594       chain->queue = gst_element_factory_make ("queue", "vqueue");
1595       if (chain->queue == NULL) {
1596         post_missing_element_message (playsink, "queue");
1597         GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1598             (_("Missing element '%s' - check your GStreamer installation."),
1599                 "queue"), ("video rendering might be suboptimal"));
1600       } else {
1601         g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1602             "max-size-bytes", 0, "max-size-time", (gint64) 0,
1603             "silent", TRUE, NULL);
1604         gst_bin_add (bin, chain->queue);
1605         videosinkpad = gst_element_get_static_pad (chain->queue, "sink");
1606       }
1607
1608       chain->overlay =
1609           gst_element_factory_make ("subtitleoverlay", "suboverlay");
1610       if (chain->overlay == NULL) {
1611         post_missing_element_message (playsink, "subtitleoverlay");
1612         GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1613             (_("Missing element '%s' - check your GStreamer installation."),
1614                 "subtitleoverlay"), ("subtitle rendering disabled"));
1615       } else {
1616         GstElement *element;
1617
1618         gst_bin_add (bin, chain->overlay);
1619
1620         g_object_set (G_OBJECT (chain->overlay), "silent", FALSE, NULL);
1621         if (playsink->font_desc) {
1622           g_object_set (G_OBJECT (chain->overlay), "font-desc",
1623               playsink->font_desc, NULL);
1624         }
1625         if (playsink->subtitle_encoding) {
1626           g_object_set (G_OBJECT (chain->overlay), "subtitle-encoding",
1627               playsink->subtitle_encoding, NULL);
1628         }
1629
1630         gst_element_link_pads_full (chain->queue, "src", chain->overlay,
1631             "video_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS);
1632
1633         /* make another little queue to decouple streams */
1634         element = gst_element_factory_make ("queue", "subqueue");
1635         if (element == NULL) {
1636           post_missing_element_message (playsink, "queue");
1637           GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1638               (_("Missing element '%s' - check your GStreamer installation."),
1639                   "queue"), ("rendering might be suboptimal"));
1640         } else {
1641           g_object_set (G_OBJECT (element), "max-size-buffers", 3,
1642               "max-size-bytes", 0, "max-size-time", (gint64) 0,
1643               "silent", TRUE, NULL);
1644           gst_bin_add (bin, element);
1645           if (gst_element_link_pads_full (element, "src", chain->overlay,
1646                   "subtitle_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
1647             textsinkpad = gst_element_get_static_pad (element, "sink");
1648             srcpad = gst_element_get_static_pad (chain->overlay, "src");
1649           } else {
1650             gst_bin_remove (bin, chain->sink);
1651             gst_bin_remove (bin, chain->overlay);
1652             chain->sink = NULL;
1653             chain->overlay = NULL;
1654             gst_object_unref (videosinkpad);
1655             videosinkpad = NULL;
1656           }
1657         }
1658       }
1659     }
1660   }
1661
1662   if (videosinkpad == NULL) {
1663     /* if we still don't have a videosink, we don't have an overlay. the only
1664      * thing we can do is insert an identity and ghost the src
1665      * and sink pads. */
1666     chain->identity = gst_element_factory_make ("identity", "tidentity");
1667     if (chain->identity == NULL) {
1668       post_missing_element_message (playsink, "identity");
1669       GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1670           (_("Missing element '%s' - check your GStreamer installation."),
1671               "identity"), (NULL));
1672     } else {
1673       g_object_set (chain->identity, "signal-handoffs", FALSE, NULL);
1674       g_object_set (chain->identity, "silent", TRUE, NULL);
1675       gst_bin_add (bin, chain->identity);
1676       srcpad = gst_element_get_static_pad (chain->identity, "src");
1677       videosinkpad = gst_element_get_static_pad (chain->identity, "sink");
1678     }
1679   }
1680
1681   /* expose the ghostpads */
1682   if (videosinkpad) {
1683     chain->videosinkpad = gst_ghost_pad_new ("sink", videosinkpad);
1684     gst_object_unref (videosinkpad);
1685     gst_element_add_pad (chain->chain.bin, chain->videosinkpad);
1686   }
1687   if (textsinkpad) {
1688     chain->textsinkpad = gst_ghost_pad_new ("text_sink", textsinkpad);
1689     gst_object_unref (textsinkpad);
1690     gst_element_add_pad (chain->chain.bin, chain->textsinkpad);
1691   }
1692   if (srcpad) {
1693     chain->srcpad = gst_ghost_pad_new ("src", srcpad);
1694     gst_object_unref (srcpad);
1695     gst_element_add_pad (chain->chain.bin, chain->srcpad);
1696   }
1697
1698   return chain;
1699 }
1700
1701 static void
1702 notify_volume_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
1703 {
1704   gdouble vol;
1705
1706   g_object_get (object, "volume", &vol, NULL);
1707   playsink->volume = vol;
1708
1709   g_object_notify (G_OBJECT (playsink), "volume");
1710 }
1711
1712 static void
1713 notify_mute_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
1714 {
1715   gboolean mute;
1716
1717   g_object_get (object, "mute", &mute, NULL);
1718   playsink->mute = mute;
1719
1720   g_object_notify (G_OBJECT (playsink), "mute");
1721 }
1722
1723 /* make the chain that contains the elements needed to perform
1724  * audio playback.
1725  *
1726  * We add a tee as the first element so that we can link the visualisation chain
1727  * to it when requested.
1728  *
1729  *  +-------------------------------------------------------------+
1730  *  | abin                                                        |
1731  *  |      +---------+   +----------+   +---------+   +---------+ |
1732  *  |      |audioconv|   |audioscale|   | volume  |   |audiosink| |
1733  *  |   +-srck      src-sink       src-sink      src-sink       | |
1734  *  |   |  +---------+   +----------+   +---------+   +---------+ |
1735  * sink-+                                                         |
1736  *  +-------------------------------------------------------------+
1737  */
1738 static GstPlayAudioChain *
1739 gen_audio_chain (GstPlaySink * playsink, gboolean raw)
1740 {
1741   GstPlayAudioChain *chain;
1742   GstBin *bin;
1743   gboolean have_volume;
1744   GstPad *pad;
1745   GstElement *head, *prev, *elem = NULL;
1746
1747   chain = g_new0 (GstPlayAudioChain, 1);
1748   chain->chain.playsink = playsink;
1749   chain->chain.raw = raw;
1750
1751   GST_DEBUG_OBJECT (playsink, "making audio chain %p", chain);
1752
1753   if (playsink->audio_sink) {
1754     GST_DEBUG_OBJECT (playsink, "trying configured audiosink %" GST_PTR_FORMAT,
1755         playsink->audio_sink);
1756     chain->sink = try_element (playsink, playsink->audio_sink, FALSE);
1757   } else {
1758     /* only try fallback if no specific sink was chosen */
1759     if (chain->sink == NULL) {
1760       GST_DEBUG_OBJECT (playsink, "trying autoaudiosink");
1761       elem = gst_element_factory_make ("autoaudiosink", "audiosink");
1762       chain->sink = try_element (playsink, elem, TRUE);
1763     }
1764     if (chain->sink == NULL) {
1765       /* if default sink from config.h is different then try it too */
1766       if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1767         GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_AUDIOSINK);
1768         elem = gst_element_factory_make (DEFAULT_AUDIOSINK, "audiosink");
1769         chain->sink = try_element (playsink, elem, TRUE);
1770       }
1771     }
1772     if (chain->sink)
1773       playsink->audio_sink = gst_object_ref (chain->sink);
1774   }
1775   if (chain->sink == NULL)
1776     goto no_sinks;
1777
1778   chain->chain.bin = gst_bin_new ("abin");
1779   bin = GST_BIN_CAST (chain->chain.bin);
1780   gst_object_ref_sink (bin);
1781   gst_bin_add (bin, chain->sink);
1782
1783   /* we have to add a queue when we need to decouple for the video sink in
1784    * visualisations and for streamsynchronizer */
1785   GST_DEBUG_OBJECT (playsink, "adding audio queue");
1786   chain->queue = gst_element_factory_make ("queue", "aqueue");
1787   if (chain->queue == NULL) {
1788     post_missing_element_message (playsink, "queue");
1789     GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1790         (_("Missing element '%s' - check your GStreamer installation."),
1791             "queue"), ("audio playback and visualizations might not work"));
1792     head = chain->sink;
1793     prev = NULL;
1794   } else {
1795     g_object_set (chain->queue, "silent", TRUE, NULL);
1796     gst_bin_add (bin, chain->queue);
1797     prev = head = chain->queue;
1798   }
1799
1800   /* find ts-offset element */
1801   gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1802       gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1803           G_TYPE_INT64));
1804
1805   /* check if the sink, or something within the sink, has the volume property.
1806    * If it does we don't need to add a volume element.  */
1807   elem =
1808       gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
1809       G_TYPE_DOUBLE);
1810   if (elem) {
1811     chain->volume = elem;
1812
1813     g_signal_connect (chain->volume, "notify::volume",
1814         G_CALLBACK (notify_volume_cb), playsink);
1815
1816     GST_DEBUG_OBJECT (playsink, "the sink has a volume property");
1817     have_volume = TRUE;
1818     chain->sink_volume = TRUE;
1819     /* if the sink also has a mute property we can use this as well. We'll only
1820      * use the mute property if there is a volume property. We can simulate the
1821      * mute with the volume otherwise. */
1822     chain->mute =
1823         gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
1824         G_TYPE_BOOLEAN);
1825     if (chain->mute) {
1826       GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
1827       g_signal_connect (chain->mute, "notify::mute",
1828           G_CALLBACK (notify_mute_cb), playsink);
1829     }
1830     /* use the sink to control the volume and mute */
1831     if (playsink->volume_changed) {
1832       g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
1833       playsink->volume_changed = FALSE;
1834     }
1835     if (playsink->mute_changed) {
1836       if (chain->mute) {
1837         g_object_set (chain->mute, "mute", playsink->mute, NULL);
1838       } else {
1839         if (playsink->mute)
1840           g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
1841       }
1842       playsink->mute_changed = FALSE;
1843     }
1844   } else {
1845     /* no volume, we need to add a volume element when we can */
1846     GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
1847     have_volume = FALSE;
1848     chain->sink_volume = FALSE;
1849   }
1850
1851   if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO) || (!have_volume
1852           && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
1853     gboolean use_converters = !(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO);
1854     gboolean use_volume =
1855         !have_volume && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME;
1856     GST_DEBUG_OBJECT (playsink,
1857         "creating audioconvert with use-converters %d, use-volume %d",
1858         use_converters, use_volume);
1859     chain->conv =
1860         g_object_new (GST_TYPE_PLAY_SINK_AUDIO_CONVERT, "name", "aconv",
1861         "use-converters", use_converters, "use-volume", use_volume, NULL);
1862     gst_bin_add (bin, chain->conv);
1863     if (prev) {
1864       if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
1865               GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1866         goto link_failed;
1867     } else {
1868       head = chain->conv;
1869     }
1870     prev = chain->conv;
1871
1872     if (!have_volume && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME) {
1873       GstPlaySinkAudioConvert *conv =
1874           GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
1875
1876       if (conv->volume) {
1877         chain->volume = conv->volume;
1878         have_volume = TRUE;
1879
1880         g_signal_connect (chain->volume, "notify::volume",
1881             G_CALLBACK (notify_volume_cb), playsink);
1882
1883         /* volume also has the mute property */
1884         chain->mute = chain->volume;
1885         g_signal_connect (chain->mute, "notify::mute",
1886             G_CALLBACK (notify_mute_cb), playsink);
1887
1888         /* configure with the latest volume and mute */
1889         g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume,
1890             NULL);
1891         g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
1892       }
1893     }
1894   }
1895
1896   if (prev) {
1897     /* we only have to link to the previous element if we have something in
1898      * front of the sink */
1899     GST_DEBUG_OBJECT (playsink, "linking to sink");
1900     if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
1901             GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1902       goto link_failed;
1903   }
1904
1905   /* post a warning if we have no way to configure the volume */
1906   if (!have_volume) {
1907     GST_ELEMENT_WARNING (playsink, STREAM, NOT_IMPLEMENTED,
1908         (_("No volume control found")), ("Volume/mute is not available"));
1909   }
1910
1911   /* and ghost the sinkpad of the headmost element */
1912   GST_DEBUG_OBJECT (playsink, "ghosting sink pad");
1913   pad = gst_element_get_static_pad (head, "sink");
1914   chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1915   gst_object_unref (pad);
1916   gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1917
1918   return chain;
1919
1920   /* ERRORS */
1921 no_sinks:
1922   {
1923     if (!elem && !playsink->audio_sink) {
1924       post_missing_element_message (playsink, "autoaudiosink");
1925       if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1926         post_missing_element_message (playsink, DEFAULT_AUDIOSINK);
1927         GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1928             (_("Both autoaudiosink and %s elements are missing."),
1929                 DEFAULT_AUDIOSINK), (NULL));
1930       } else {
1931         GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1932             (_("The autoaudiosink element is missing.")), (NULL));
1933       }
1934     } else {
1935       if (playsink->audio_sink) {
1936         GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1937             (_("Configured audiosink %s is not working."),
1938                 GST_ELEMENT_NAME (playsink->audio_sink)), (NULL));
1939       } else if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1940         GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1941             (_("Both autoaudiosink and %s elements are not working."),
1942                 DEFAULT_AUDIOSINK), (NULL));
1943       } else {
1944         GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1945             (_("The autoaudiosink element is not working.")), (NULL));
1946       }
1947     }
1948     free_chain ((GstPlayChain *) chain);
1949     return NULL;
1950   }
1951 link_failed:
1952   {
1953     GST_ELEMENT_ERROR (playsink, CORE, PAD,
1954         (NULL), ("Failed to configure the audio sink."));
1955     /* checking sink made it READY */
1956     gst_element_set_state (chain->sink, GST_STATE_NULL);
1957     /* Remove chain from the bin to allow reuse later */
1958     gst_bin_remove (bin, chain->sink);
1959     free_chain ((GstPlayChain *) chain);
1960     return NULL;
1961   }
1962 }
1963
1964 static gboolean
1965 setup_audio_chain (GstPlaySink * playsink, gboolean raw)
1966 {
1967   GstElement *elem;
1968   GstPlayAudioChain *chain;
1969   GstStateChangeReturn ret;
1970
1971   chain = playsink->audiochain;
1972
1973   chain->chain.raw = raw;
1974
1975   /* if the chain was active we don't do anything */
1976   if (GST_PLAY_CHAIN (chain)->activated == TRUE)
1977     return TRUE;
1978
1979   /* try to set the sink element to READY again */
1980   ret = gst_element_set_state (chain->sink, GST_STATE_READY);
1981   if (ret == GST_STATE_CHANGE_FAILURE)
1982     return FALSE;
1983
1984   /* find ts-offset element */
1985   gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1986       gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1987           G_TYPE_INT64));
1988
1989   /* check if the sink, or something within the sink, has the volume property.
1990    * If it does we don't need to add a volume element.  */
1991   elem =
1992       gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
1993       G_TYPE_DOUBLE);
1994   if (elem) {
1995     chain->volume = elem;
1996
1997     if (playsink->volume_changed) {
1998       GST_DEBUG_OBJECT (playsink, "the sink has a volume property, setting %f",
1999           playsink->volume);
2000       /* use the sink to control the volume */
2001       g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2002       playsink->volume_changed = FALSE;
2003     }
2004
2005     g_signal_connect (chain->volume, "notify::volume",
2006         G_CALLBACK (notify_volume_cb), playsink);
2007     /* if the sink also has a mute property we can use this as well. We'll only
2008      * use the mute property if there is a volume property. We can simulate the
2009      * mute with the volume otherwise. */
2010     chain->mute =
2011         gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
2012         G_TYPE_BOOLEAN);
2013     if (chain->mute) {
2014       GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
2015       g_signal_connect (chain->mute, "notify::mute",
2016           G_CALLBACK (notify_mute_cb), playsink);
2017     }
2018
2019     g_object_set (chain->conv, "use-volume", FALSE, NULL);
2020   } else {
2021     GstPlaySinkAudioConvert *conv =
2022         GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
2023
2024     /* no volume, we need to add a volume element when we can */
2025     g_object_set (chain->conv, "use-volume", TRUE, NULL);
2026     GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
2027
2028     /* Disconnect signals */
2029     disconnect_chain (chain, playsink);
2030
2031     if (conv->volume) {
2032       chain->volume = conv->volume;
2033       chain->mute = chain->volume;
2034
2035       g_signal_connect (chain->volume, "notify::volume",
2036           G_CALLBACK (notify_volume_cb), playsink);
2037
2038       g_signal_connect (chain->mute, "notify::mute",
2039           G_CALLBACK (notify_mute_cb), playsink);
2040
2041       /* configure with the latest volume and mute */
2042       g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2043       g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
2044     }
2045
2046     GST_DEBUG_OBJECT (playsink, "reusing existing volume element");
2047   }
2048   return TRUE;
2049 }
2050
2051 /*
2052  *  +-------------------------------------------------------------------+
2053  *  | visbin                                                            |
2054  *  |      +----------+   +------------+   +----------+   +-------+     |
2055  *  |      | visqueue |   | audioconv  |   | audiores |   |  vis  |     |
2056  *  |   +-sink       src-sink + samp  src-sink       src-sink    src-+  |
2057  *  |   |  +----------+   +------------+   +----------+   +-------+  |  |
2058  * sink-+                                                            +-src
2059  *  +-------------------------------------------------------------------+
2060  *
2061  */
2062 static GstPlayVisChain *
2063 gen_vis_chain (GstPlaySink * playsink)
2064 {
2065   GstPlayVisChain *chain;
2066   GstBin *bin;
2067   gboolean res;
2068   GstPad *pad;
2069   GstElement *elem;
2070
2071   chain = g_new0 (GstPlayVisChain, 1);
2072   chain->chain.playsink = playsink;
2073
2074   GST_DEBUG_OBJECT (playsink, "making vis chain %p", chain);
2075
2076   chain->chain.bin = gst_bin_new ("visbin");
2077   bin = GST_BIN_CAST (chain->chain.bin);
2078   gst_object_ref_sink (bin);
2079
2080   /* we're queuing raw audio here, we can remove this queue when we can disable
2081    * async behaviour in the video sink. */
2082   chain->queue = gst_element_factory_make ("queue", "visqueue");
2083   if (chain->queue == NULL)
2084     goto no_queue;
2085   g_object_set (chain->queue, "silent", TRUE, NULL);
2086   gst_bin_add (bin, chain->queue);
2087
2088   chain->conv = gst_element_factory_make ("audioconvert", "aconv");
2089   if (chain->conv == NULL)
2090     goto no_audioconvert;
2091   gst_bin_add (bin, chain->conv);
2092
2093   chain->resample = gst_element_factory_make ("audioresample", "aresample");
2094   if (chain->resample == NULL)
2095     goto no_audioresample;
2096   gst_bin_add (bin, chain->resample);
2097
2098   /* this pad will be used for blocking the dataflow and switching the vis
2099    * plugin */
2100   chain->blockpad = gst_element_get_static_pad (chain->resample, "src");
2101
2102   if (playsink->visualisation) {
2103     GST_DEBUG_OBJECT (playsink, "trying configure vis");
2104     chain->vis = try_element (playsink, playsink->visualisation, FALSE);
2105   }
2106   if (chain->vis == NULL) {
2107     GST_DEBUG_OBJECT (playsink, "trying goom");
2108     elem = gst_element_factory_make ("goom", "vis");
2109     chain->vis = try_element (playsink, elem, TRUE);
2110   }
2111   if (chain->vis == NULL)
2112     goto no_goom;
2113
2114   gst_bin_add (bin, chain->vis);
2115
2116   res = gst_element_link_pads_full (chain->queue, "src", chain->conv, "sink",
2117       GST_PAD_LINK_CHECK_NOTHING);
2118   res &=
2119       gst_element_link_pads_full (chain->conv, "src", chain->resample, "sink",
2120       GST_PAD_LINK_CHECK_NOTHING);
2121   res &=
2122       gst_element_link_pads_full (chain->resample, "src", chain->vis, "sink",
2123       GST_PAD_LINK_CHECK_NOTHING);
2124   if (!res)
2125     goto link_failed;
2126
2127   chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
2128   chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
2129
2130   pad = gst_element_get_static_pad (chain->queue, "sink");
2131   chain->sinkpad = gst_ghost_pad_new ("sink", pad);
2132   gst_object_unref (pad);
2133   gst_element_add_pad (chain->chain.bin, chain->sinkpad);
2134
2135   chain->srcpad = gst_ghost_pad_new ("src", chain->vissrcpad);
2136   gst_element_add_pad (chain->chain.bin, chain->srcpad);
2137
2138   return chain;
2139
2140   /* ERRORS */
2141 no_queue:
2142   {
2143     post_missing_element_message (playsink, "queue");
2144     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2145         (_("Missing element '%s' - check your GStreamer installation."),
2146             "queue"), (NULL));
2147     free_chain ((GstPlayChain *) chain);
2148     return NULL;
2149   }
2150 no_audioconvert:
2151   {
2152     post_missing_element_message (playsink, "audioconvert");
2153     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2154         (_("Missing element '%s' - check your GStreamer installation."),
2155             "audioconvert"), ("possibly a liboil version mismatch?"));
2156     free_chain ((GstPlayChain *) chain);
2157     return NULL;
2158   }
2159 no_audioresample:
2160   {
2161     post_missing_element_message (playsink, "audioresample");
2162     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2163         (_("Missing element '%s' - check your GStreamer installation."),
2164             "audioresample"), (NULL));
2165     free_chain ((GstPlayChain *) chain);
2166     return NULL;
2167   }
2168 no_goom:
2169   {
2170     post_missing_element_message (playsink, "goom");
2171     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2172         (_("Missing element '%s' - check your GStreamer installation."),
2173             "goom"), (NULL));
2174     free_chain ((GstPlayChain *) chain);
2175     return NULL;
2176   }
2177 link_failed:
2178   {
2179     GST_ELEMENT_ERROR (playsink, CORE, PAD,
2180         (NULL), ("Failed to configure the visualisation element."));
2181     /* element made it to READY */
2182     gst_element_set_state (chain->vis, GST_STATE_NULL);
2183     free_chain ((GstPlayChain *) chain);
2184     return NULL;
2185   }
2186 }
2187
2188 /* this function is called when all the request pads are requested and when we
2189  * have to construct the final pipeline. Based on the flags we construct the
2190  * final output pipelines.
2191  */
2192 gboolean
2193 gst_play_sink_reconfigure (GstPlaySink * playsink)
2194 {
2195   GstPlayFlags flags;
2196   gboolean need_audio, need_video, need_deinterlace, need_vis, need_text;
2197
2198   GST_DEBUG_OBJECT (playsink, "reconfiguring");
2199
2200   /* assume we need nothing */
2201   need_audio = need_video = need_deinterlace = need_vis = need_text = FALSE;
2202
2203   GST_PLAY_SINK_LOCK (playsink);
2204   GST_OBJECT_LOCK (playsink);
2205   /* get flags, there are protected with the object lock */
2206   flags = playsink->flags;
2207   GST_OBJECT_UNLOCK (playsink);
2208
2209   /* figure out which components we need */
2210   if (flags & GST_PLAY_FLAG_TEXT && playsink->text_pad) {
2211     /* we have subtitles and we are requested to show it */
2212     need_text = TRUE;
2213   }
2214
2215   if (((flags & GST_PLAY_FLAG_VIDEO)
2216           || (flags & GST_PLAY_FLAG_NATIVE_VIDEO)) && playsink->video_pad) {
2217     /* we have video and we are requested to show it */
2218     need_video = TRUE;
2219
2220     /* we only deinterlace if native video is not requested and
2221      * we have raw video */
2222     if ((flags & GST_PLAY_FLAG_DEINTERLACE)
2223         && !(flags & GST_PLAY_FLAG_NATIVE_VIDEO) && playsink->video_pad_raw)
2224       need_deinterlace = TRUE;
2225   }
2226
2227   if (playsink->audio_pad) {
2228     if ((flags & GST_PLAY_FLAG_AUDIO) || (flags & GST_PLAY_FLAG_NATIVE_AUDIO)) {
2229       need_audio = TRUE;
2230     }
2231     if (playsink->audio_pad_raw) {
2232       /* only can do vis with raw uncompressed audio */
2233       if (flags & GST_PLAY_FLAG_VIS && !need_video) {
2234         /* also add video when we add visualisation */
2235         need_video = TRUE;
2236         need_vis = TRUE;
2237       }
2238     }
2239   }
2240
2241   /* we have a text_pad and we need text rendering, in this case we need a
2242    * video_pad to combine the video with the text or visualizations */
2243   if (need_text && !need_video) {
2244     if (playsink->video_pad) {
2245       need_video = TRUE;
2246     } else if (need_audio) {
2247       GST_ELEMENT_WARNING (playsink, STREAM, FORMAT,
2248           (_("Can't play a text file without video or visualizations.")),
2249           ("Have text pad but no video pad or visualizations"));
2250       need_text = FALSE;
2251     } else {
2252       GST_ELEMENT_ERROR (playsink, STREAM, FORMAT,
2253           (_("Can't play a text file without video or visualizations.")),
2254           ("Have text pad but no video pad or visualizations"));
2255       GST_PLAY_SINK_UNLOCK (playsink);
2256       return FALSE;
2257     }
2258   }
2259
2260   GST_DEBUG_OBJECT (playsink, "audio:%d, video:%d, vis:%d, text:%d", need_audio,
2261       need_video, need_vis, need_text);
2262
2263   /* set up video pipeline */
2264   if (need_video) {
2265     gboolean raw, async;
2266
2267     /* we need a raw sink when we do vis or when we have a raw pad */
2268     raw = need_vis ? TRUE : playsink->video_pad_raw;
2269     /* we try to set the sink async=FALSE when we need vis, this way we can
2270      * avoid a queue in the audio chain. */
2271     async = !need_vis;
2272
2273     GST_DEBUG_OBJECT (playsink, "adding video, raw %d",
2274         playsink->video_pad_raw);
2275
2276     if (playsink->videochain) {
2277       /* try to reactivate the chain */
2278       if (!setup_video_chain (playsink, raw, async)) {
2279         if (playsink->video_sinkpad_stream_synchronizer) {
2280           gst_element_release_request_pad (GST_ELEMENT_CAST
2281               (playsink->stream_synchronizer),
2282               playsink->video_sinkpad_stream_synchronizer);
2283           gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2284           playsink->video_sinkpad_stream_synchronizer = NULL;
2285           gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2286           playsink->video_srcpad_stream_synchronizer = NULL;
2287         }
2288
2289         add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2290
2291         /* Remove the sink from the bin to keep its state
2292          * and unparent it to allow reuse */
2293         if (playsink->videochain->sink)
2294           gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
2295               playsink->videochain->sink);
2296
2297         activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2298         free_chain ((GstPlayChain *) playsink->videochain);
2299         playsink->videochain = NULL;
2300       }
2301     }
2302
2303     if (!playsink->videochain)
2304       playsink->videochain = gen_video_chain (playsink, raw, async);
2305     if (!playsink->videochain)
2306       goto no_chain;
2307
2308     if (!playsink->video_sinkpad_stream_synchronizer) {
2309       GstIterator *it;
2310
2311       playsink->video_sinkpad_stream_synchronizer =
2312           gst_element_get_request_pad (GST_ELEMENT_CAST
2313           (playsink->stream_synchronizer), "sink_%d");
2314       it = gst_pad_iterate_internal_links
2315           (playsink->video_sinkpad_stream_synchronizer);
2316       g_assert (it);
2317       gst_iterator_next (it,
2318           (gpointer *) & playsink->video_srcpad_stream_synchronizer);
2319       g_assert (playsink->video_srcpad_stream_synchronizer);
2320       gst_iterator_free (it);
2321     }
2322
2323     if (playsink->video_pad)
2324       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad),
2325           playsink->video_sinkpad_stream_synchronizer);
2326
2327     if (need_deinterlace) {
2328       if (!playsink->videodeinterlacechain)
2329         playsink->videodeinterlacechain =
2330             gen_video_deinterlace_chain (playsink);
2331       if (!playsink->videodeinterlacechain)
2332         goto no_chain;
2333
2334       GST_DEBUG_OBJECT (playsink, "adding video deinterlace chain");
2335
2336       GST_DEBUG_OBJECT (playsink, "setting up deinterlacing chain");
2337
2338       add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
2339       activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
2340
2341       gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2342           playsink->videodeinterlacechain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2343     } else {
2344       if (playsink->videodeinterlacechain) {
2345         add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2346         activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
2347             FALSE);
2348       }
2349     }
2350
2351     GST_DEBUG_OBJECT (playsink, "adding video chain");
2352     add_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
2353     activate_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
2354     /* if we are not part of vis or subtitles, set the ghostpad target */
2355     if (!need_vis && !need_text && (!playsink->textchain
2356             || !playsink->text_pad)) {
2357       GST_DEBUG_OBJECT (playsink, "ghosting video sinkpad");
2358       if (need_deinterlace)
2359         gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
2360             playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2361       else
2362         gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2363             playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2364     }
2365   } else {
2366     GST_DEBUG_OBJECT (playsink, "no video needed");
2367     if (playsink->videochain) {
2368       GST_DEBUG_OBJECT (playsink, "removing video chain");
2369       if (playsink->vischain) {
2370         GstPad *srcpad;
2371
2372         GST_DEBUG_OBJECT (playsink, "unlinking vis chain");
2373
2374         /* also had visualisation, release the tee srcpad before we then
2375          * unlink the video from it */
2376         if (playsink->audio_tee_vissrc) {
2377           gst_element_release_request_pad (playsink->audio_tee,
2378               playsink->audio_tee_vissrc);
2379           gst_object_unref (playsink->audio_tee_vissrc);
2380           playsink->audio_tee_vissrc = NULL;
2381         }
2382         srcpad =
2383             gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2384         gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
2385       }
2386
2387       if (playsink->video_sinkpad_stream_synchronizer) {
2388         gst_element_release_request_pad (GST_ELEMENT_CAST
2389             (playsink->stream_synchronizer),
2390             playsink->video_sinkpad_stream_synchronizer);
2391         gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2392         playsink->video_sinkpad_stream_synchronizer = NULL;
2393         gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2394         playsink->video_srcpad_stream_synchronizer = NULL;
2395       }
2396
2397       add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2398       activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2399       if (playsink->videochain->ts_offset)
2400         gst_object_unref (playsink->videochain->ts_offset);
2401       playsink->videochain->ts_offset = NULL;
2402     }
2403
2404     if (playsink->videodeinterlacechain) {
2405       add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2406       activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2407     }
2408
2409     if (playsink->video_pad)
2410       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
2411   }
2412
2413   if (need_audio) {
2414     gboolean raw;
2415
2416     GST_DEBUG_OBJECT (playsink, "adding audio");
2417
2418     /* get a raw sink if we are asked for a raw pad */
2419     raw = playsink->audio_pad_raw;
2420
2421     if (playsink->audiochain) {
2422       /* try to reactivate the chain */
2423       if (!setup_audio_chain (playsink, raw)) {
2424         GST_DEBUG_OBJECT (playsink, "removing current audio chain");
2425         if (playsink->audio_tee_asrc) {
2426           gst_element_release_request_pad (playsink->audio_tee,
2427               playsink->audio_tee_asrc);
2428           gst_object_unref (playsink->audio_tee_asrc);
2429           playsink->audio_tee_asrc = NULL;
2430         }
2431
2432         if (playsink->audio_sinkpad_stream_synchronizer) {
2433           gst_element_release_request_pad (GST_ELEMENT_CAST
2434               (playsink->stream_synchronizer),
2435               playsink->audio_sinkpad_stream_synchronizer);
2436           gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
2437           playsink->audio_sinkpad_stream_synchronizer = NULL;
2438           gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
2439           playsink->audio_srcpad_stream_synchronizer = NULL;
2440         }
2441
2442         add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2443
2444         /* Remove the sink from the bin to keep its state
2445          * and unparent it to allow reuse */
2446         if (playsink->audiochain->sink)
2447           gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
2448               playsink->audiochain->sink);
2449
2450         activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2451         disconnect_chain (playsink->audiochain, playsink);
2452         playsink->audiochain->volume = NULL;
2453         playsink->audiochain->mute = NULL;
2454         if (playsink->audiochain->ts_offset)
2455           gst_object_unref (playsink->audiochain->ts_offset);
2456         playsink->audiochain->ts_offset = NULL;
2457         free_chain ((GstPlayChain *) playsink->audiochain);
2458         playsink->audiochain = NULL;
2459         playsink->volume_changed = playsink->mute_changed = FALSE;
2460       }
2461     }
2462
2463     if (!playsink->audiochain) {
2464       GST_DEBUG_OBJECT (playsink, "creating new audio chain");
2465       playsink->audiochain = gen_audio_chain (playsink, raw);
2466     }
2467
2468     if (!playsink->audio_sinkpad_stream_synchronizer) {
2469       GstIterator *it;
2470
2471       playsink->audio_sinkpad_stream_synchronizer =
2472           gst_element_get_request_pad (GST_ELEMENT_CAST
2473           (playsink->stream_synchronizer), "sink_%d");
2474       it = gst_pad_iterate_internal_links
2475           (playsink->audio_sinkpad_stream_synchronizer);
2476       g_assert (it);
2477       gst_iterator_next (it,
2478           (gpointer *) & playsink->audio_srcpad_stream_synchronizer);
2479       g_assert (playsink->audio_srcpad_stream_synchronizer);
2480       gst_iterator_free (it);
2481     }
2482
2483     if (playsink->audiochain) {
2484       GST_DEBUG_OBJECT (playsink, "adding audio chain");
2485       if (playsink->audio_tee_asrc == NULL) {
2486         playsink->audio_tee_asrc =
2487             gst_element_get_request_pad (playsink->audio_tee, "src%d");
2488       }
2489       add_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
2490       activate_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
2491       gst_pad_link_full (playsink->audio_tee_asrc,
2492           playsink->audio_sinkpad_stream_synchronizer,
2493           GST_PAD_LINK_CHECK_NOTHING);
2494       gst_pad_link_full (playsink->audio_srcpad_stream_synchronizer,
2495           playsink->audiochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2496     }
2497   } else {
2498     GST_DEBUG_OBJECT (playsink, "no audio needed");
2499     /* we have no audio or we are requested to not play audio */
2500     if (playsink->audiochain) {
2501       GST_DEBUG_OBJECT (playsink, "removing audio chain");
2502       /* release the audio pad */
2503       if (playsink->audio_tee_asrc) {
2504         gst_element_release_request_pad (playsink->audio_tee,
2505             playsink->audio_tee_asrc);
2506         gst_object_unref (playsink->audio_tee_asrc);
2507         playsink->audio_tee_asrc = NULL;
2508       }
2509
2510       if (playsink->audio_sinkpad_stream_synchronizer) {
2511         gst_element_release_request_pad (GST_ELEMENT_CAST
2512             (playsink->stream_synchronizer),
2513             playsink->audio_sinkpad_stream_synchronizer);
2514         gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
2515         playsink->audio_sinkpad_stream_synchronizer = NULL;
2516         gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
2517         playsink->audio_srcpad_stream_synchronizer = NULL;
2518       }
2519
2520       if (playsink->audiochain->sink_volume) {
2521         disconnect_chain (playsink->audiochain, playsink);
2522         playsink->audiochain->volume = NULL;
2523         playsink->audiochain->mute = NULL;
2524         if (playsink->audiochain->ts_offset)
2525           gst_object_unref (playsink->audiochain->ts_offset);
2526         playsink->audiochain->ts_offset = NULL;
2527       }
2528       add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2529       activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2530     }
2531   }
2532
2533   if (need_vis) {
2534     GstPad *srcpad;
2535
2536     if (!playsink->vischain)
2537       playsink->vischain = gen_vis_chain (playsink);
2538
2539     GST_DEBUG_OBJECT (playsink, "adding visualisation");
2540
2541     if (playsink->vischain) {
2542       GST_DEBUG_OBJECT (playsink, "setting up vis chain");
2543       srcpad =
2544           gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2545       add_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
2546       activate_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
2547       if (playsink->audio_tee_vissrc == NULL) {
2548         playsink->audio_tee_vissrc =
2549             gst_element_get_request_pad (playsink->audio_tee, "src%d");
2550       }
2551       gst_pad_link_full (playsink->audio_tee_vissrc,
2552           playsink->vischain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2553       gst_pad_link_full (srcpad, playsink->video_sinkpad_stream_synchronizer,
2554           GST_PAD_LINK_CHECK_NOTHING);
2555       gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2556           playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2557       gst_object_unref (srcpad);
2558     }
2559   } else {
2560     GST_DEBUG_OBJECT (playsink, "no vis needed");
2561     if (playsink->vischain) {
2562       if (playsink->audio_tee_vissrc) {
2563         gst_element_release_request_pad (playsink->audio_tee,
2564             playsink->audio_tee_vissrc);
2565         gst_object_unref (playsink->audio_tee_vissrc);
2566         playsink->audio_tee_vissrc = NULL;
2567       }
2568       GST_DEBUG_OBJECT (playsink, "removing vis chain");
2569       add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
2570       activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
2571     }
2572   }
2573
2574   if (need_text) {
2575     GST_DEBUG_OBJECT (playsink, "adding text");
2576     if (!playsink->textchain) {
2577       GST_DEBUG_OBJECT (playsink, "creating text chain");
2578       playsink->textchain = gen_text_chain (playsink);
2579     }
2580     if (playsink->textchain) {
2581       GstIterator *it;
2582
2583       GST_DEBUG_OBJECT (playsink, "adding text chain");
2584       if (playsink->textchain->overlay)
2585         g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", FALSE,
2586             NULL);
2587       add_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
2588
2589       if (!playsink->text_sinkpad_stream_synchronizer) {
2590         playsink->text_sinkpad_stream_synchronizer =
2591             gst_element_get_request_pad (GST_ELEMENT_CAST
2592             (playsink->stream_synchronizer), "sink_%d");
2593         it = gst_pad_iterate_internal_links
2594             (playsink->text_sinkpad_stream_synchronizer);
2595         g_assert (it);
2596         gst_iterator_next (it,
2597             (gpointer *) & playsink->text_srcpad_stream_synchronizer);
2598         g_assert (playsink->text_srcpad_stream_synchronizer);
2599         gst_iterator_free (it);
2600
2601         gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad),
2602             playsink->text_sinkpad_stream_synchronizer);
2603         gst_pad_link_full (playsink->text_srcpad_stream_synchronizer,
2604             playsink->textchain->textsinkpad, GST_PAD_LINK_CHECK_NOTHING);
2605       }
2606
2607       if (need_vis) {
2608         GstPad *srcpad;
2609
2610         srcpad =
2611             gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2612         gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
2613         gst_pad_link_full (srcpad, playsink->textchain->videosinkpad,
2614             GST_PAD_LINK_CHECK_NOTHING);
2615         gst_object_unref (srcpad);
2616       } else {
2617         if (need_deinterlace)
2618           gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
2619               playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
2620         else
2621           gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2622               playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
2623       }
2624       gst_pad_link_full (playsink->textchain->srcpad,
2625           playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2626
2627       activate_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
2628     }
2629   } else {
2630     GST_DEBUG_OBJECT (playsink, "no text needed");
2631     /* we have no subtitles/text or we are requested to not show them */
2632
2633     if (playsink->text_sinkpad_stream_synchronizer) {
2634       gst_element_release_request_pad (GST_ELEMENT_CAST
2635           (playsink->stream_synchronizer),
2636           playsink->text_sinkpad_stream_synchronizer);
2637       gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
2638       playsink->text_sinkpad_stream_synchronizer = NULL;
2639       gst_object_unref (playsink->text_srcpad_stream_synchronizer);
2640       playsink->text_srcpad_stream_synchronizer = NULL;
2641     }
2642
2643     if (playsink->textchain) {
2644       if (playsink->text_pad == NULL) {
2645         /* no text pad, remove the chain entirely */
2646         GST_DEBUG_OBJECT (playsink, "removing text chain");
2647         add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
2648         activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
2649       } else {
2650         /* we have a chain and a textpad, turn the subtitles off */
2651         GST_DEBUG_OBJECT (playsink, "turning off the text");
2652         if (playsink->textchain->overlay)
2653           g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", TRUE,
2654               NULL);
2655       }
2656     }
2657     if (!need_video && playsink->video_pad) {
2658       if (playsink->video_sinkpad_stream_synchronizer) {
2659         gst_element_release_request_pad (GST_ELEMENT_CAST
2660             (playsink->stream_synchronizer),
2661             playsink->video_sinkpad_stream_synchronizer);
2662         gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2663         playsink->video_sinkpad_stream_synchronizer = NULL;
2664         gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2665         playsink->video_srcpad_stream_synchronizer = NULL;
2666       }
2667
2668       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
2669     }
2670
2671     if (playsink->text_pad && !playsink->textchain)
2672       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad), NULL);
2673   }
2674   update_av_offset (playsink);
2675   do_async_done (playsink);
2676   GST_PLAY_SINK_UNLOCK (playsink);
2677
2678   return TRUE;
2679
2680   /* ERRORS */
2681 no_chain:
2682   {
2683     /* gen_ chain already posted error */
2684     GST_DEBUG_OBJECT (playsink, "failed to setup chain");
2685     GST_PLAY_SINK_UNLOCK (playsink);
2686     return FALSE;
2687   }
2688 }
2689
2690 /**
2691  * gst_play_sink_set_flags:
2692  * @playsink: a #GstPlaySink
2693  * @flags: #GstPlayFlags
2694  *
2695  * Configure @flags on @playsink. The flags control the behaviour of @playsink
2696  * when constructing the sink pipelins.
2697  *
2698  * Returns: TRUE if the flags could be configured.
2699  */
2700 gboolean
2701 gst_play_sink_set_flags (GstPlaySink * playsink, GstPlayFlags flags)
2702 {
2703   g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), FALSE);
2704
2705   GST_OBJECT_LOCK (playsink);
2706   playsink->flags = flags;
2707   GST_OBJECT_UNLOCK (playsink);
2708
2709   return TRUE;
2710 }
2711
2712 /**
2713  * gst_play_sink_get_flags:
2714  * @playsink: a #GstPlaySink
2715  *
2716  * Get the flags of @playsink. That flags control the behaviour of the sink when
2717  * it constructs the sink pipelines.
2718  *
2719  * Returns: the currently configured #GstPlayFlags.
2720  */
2721 GstPlayFlags
2722 gst_play_sink_get_flags (GstPlaySink * playsink)
2723 {
2724   GstPlayFlags res;
2725
2726   g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), 0);
2727
2728   GST_OBJECT_LOCK (playsink);
2729   res = playsink->flags;
2730   GST_OBJECT_UNLOCK (playsink);
2731
2732   return res;
2733 }
2734
2735 void
2736 gst_play_sink_set_font_desc (GstPlaySink * playsink, const gchar * desc)
2737 {
2738   GstPlayTextChain *chain;
2739
2740   GST_PLAY_SINK_LOCK (playsink);
2741   chain = (GstPlayTextChain *) playsink->textchain;
2742   g_free (playsink->font_desc);
2743   playsink->font_desc = g_strdup (desc);
2744   if (chain && chain->overlay) {
2745     g_object_set (chain->overlay, "font-desc", desc, NULL);
2746   }
2747   GST_PLAY_SINK_UNLOCK (playsink);
2748 }
2749
2750 gchar *
2751 gst_play_sink_get_font_desc (GstPlaySink * playsink)
2752 {
2753   gchar *result = NULL;
2754   GstPlayTextChain *chain;
2755
2756   GST_PLAY_SINK_LOCK (playsink);
2757   chain = (GstPlayTextChain *) playsink->textchain;
2758   if (chain && chain->overlay) {
2759     g_object_get (chain->overlay, "font-desc", &result, NULL);
2760     playsink->font_desc = g_strdup (result);
2761   } else {
2762     result = g_strdup (playsink->font_desc);
2763   }
2764   GST_PLAY_SINK_UNLOCK (playsink);
2765
2766   return result;
2767 }
2768
2769 void
2770 gst_play_sink_set_subtitle_encoding (GstPlaySink * playsink,
2771     const gchar * encoding)
2772 {
2773   GstPlayTextChain *chain;
2774
2775   GST_PLAY_SINK_LOCK (playsink);
2776   chain = (GstPlayTextChain *) playsink->textchain;
2777   g_free (playsink->subtitle_encoding);
2778   playsink->subtitle_encoding = g_strdup (encoding);
2779   if (chain && chain->overlay) {
2780     g_object_set (chain->overlay, "subtitle-encoding", encoding, NULL);
2781   }
2782   GST_PLAY_SINK_UNLOCK (playsink);
2783 }
2784
2785 gchar *
2786 gst_play_sink_get_subtitle_encoding (GstPlaySink * playsink)
2787 {
2788   gchar *result = NULL;
2789   GstPlayTextChain *chain;
2790
2791   GST_PLAY_SINK_LOCK (playsink);
2792   chain = (GstPlayTextChain *) playsink->textchain;
2793   if (chain && chain->overlay) {
2794     g_object_get (chain->overlay, "subtitle-encoding", &result, NULL);
2795     playsink->subtitle_encoding = g_strdup (result);
2796   } else {
2797     result = g_strdup (playsink->subtitle_encoding);
2798   }
2799   GST_PLAY_SINK_UNLOCK (playsink);
2800
2801   return result;
2802 }
2803
2804 static void
2805 update_av_offset (GstPlaySink * playsink)
2806 {
2807   gint64 av_offset;
2808   GstPlayAudioChain *achain;
2809   GstPlayVideoChain *vchain;
2810
2811   av_offset = playsink->av_offset;
2812   achain = (GstPlayAudioChain *) playsink->audiochain;
2813   vchain = (GstPlayVideoChain *) playsink->videochain;
2814
2815   if (achain && vchain && achain->ts_offset && vchain->ts_offset) {
2816     g_object_set (achain->ts_offset, "ts-offset", MAX (0, -av_offset), NULL);
2817     g_object_set (vchain->ts_offset, "ts-offset", MAX (0, av_offset), NULL);
2818   } else {
2819     GST_LOG_OBJECT (playsink, "no ts_offset elements");
2820   }
2821 }
2822
2823 void
2824 gst_play_sink_set_av_offset (GstPlaySink * playsink, gint64 av_offset)
2825 {
2826   GST_PLAY_SINK_LOCK (playsink);
2827   playsink->av_offset = av_offset;
2828   update_av_offset (playsink);
2829   GST_PLAY_SINK_UNLOCK (playsink);
2830 }
2831
2832 gint64
2833 gst_play_sink_get_av_offset (GstPlaySink * playsink)
2834 {
2835   gint64 result;
2836
2837   GST_PLAY_SINK_LOCK (playsink);
2838   result = playsink->av_offset;
2839   GST_PLAY_SINK_UNLOCK (playsink);
2840
2841   return result;
2842 }
2843
2844 /**
2845  * gst_play_sink_get_last_frame:
2846  * @playsink: a #GstPlaySink
2847  *
2848  * Get the last displayed frame from @playsink. This frame is in the native
2849  * format of the sink element, the caps on the result buffer contain the format
2850  * of the frame data.
2851  *
2852  * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
2853  * available.
2854  */
2855 GstBuffer *
2856 gst_play_sink_get_last_frame (GstPlaySink * playsink)
2857 {
2858   GstBuffer *result = NULL;
2859   GstPlayVideoChain *chain;
2860
2861   GST_PLAY_SINK_LOCK (playsink);
2862   GST_DEBUG_OBJECT (playsink, "taking last frame");
2863   /* get the video chain if we can */
2864   if ((chain = (GstPlayVideoChain *) playsink->videochain)) {
2865     GST_DEBUG_OBJECT (playsink, "found video chain");
2866     /* see if the chain is active */
2867     if (chain->chain.activated && chain->sink) {
2868       GstElement *elem;
2869
2870       GST_DEBUG_OBJECT (playsink, "video chain active and has a sink");
2871
2872       /* find and get the last-buffer property now */
2873       if ((elem =
2874               gst_play_sink_find_property (playsink, chain->sink,
2875                   "last-buffer", GST_TYPE_BUFFER))) {
2876         GST_DEBUG_OBJECT (playsink, "getting last-buffer property");
2877         g_object_get (elem, "last-buffer", &result, NULL);
2878         gst_object_unref (elem);
2879       }
2880     }
2881   }
2882   GST_PLAY_SINK_UNLOCK (playsink);
2883
2884   return result;
2885 }
2886
2887 /**
2888  * gst_play_sink_convert_frame:
2889  * @playsink: a #GstPlaySink
2890  * @caps: a #GstCaps
2891  *
2892  * Get the last displayed frame from @playsink. If caps is %NULL, the video will
2893  * be in the native format of the sink element and the caps on the buffer
2894  * describe the format of the frame. If @caps is not %NULL, the video
2895  * frame will be converted to the format of the caps.
2896  *
2897  * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
2898  * available or when the conversion failed.
2899  */
2900 GstBuffer *
2901 gst_play_sink_convert_frame (GstPlaySink * playsink, GstCaps * caps)
2902 {
2903   GstBuffer *result;
2904
2905   result = gst_play_sink_get_last_frame (playsink);
2906   if (result != NULL && caps != NULL) {
2907     GstBuffer *temp;
2908     GError *err = NULL;
2909
2910     temp = gst_video_convert_frame (result, caps, 25 * GST_SECOND, &err);
2911     gst_buffer_unref (result);
2912     if (temp == NULL && err) {
2913       /* I'm really uncertain whether we should make playsink post an error
2914        * on the bus or not. It's not like it's a critical issue regarding
2915        * playsink behaviour. */
2916       GST_ERROR ("Error converting frame: %s", err->message);
2917       g_error_free (err);
2918     }
2919     result = temp;
2920   }
2921   return result;
2922 }
2923
2924 static gboolean
2925 is_raw_structure (GstStructure * s)
2926 {
2927   const gchar *name;
2928
2929   name = gst_structure_get_name (s);
2930
2931   if (g_str_has_prefix (name, "video/x-raw-") ||
2932       g_str_has_prefix (name, "audio/x-raw-"))
2933     return TRUE;
2934   return FALSE;
2935 }
2936
2937 static gboolean
2938 is_raw_pad (GstPad * pad)
2939 {
2940   GstPad *peer = gst_pad_get_peer (pad);
2941   GstCaps *caps;
2942   gboolean raw = TRUE;
2943
2944   if (!peer)
2945     return raw;
2946
2947   caps = gst_pad_get_negotiated_caps (peer);
2948   if (!caps) {
2949     guint i, n;
2950
2951     caps = gst_pad_get_caps_reffed (peer);
2952
2953     n = gst_caps_get_size (caps);
2954     for (i = 0; i < n; i++) {
2955       gboolean r = is_raw_structure (gst_caps_get_structure (caps, i));
2956
2957       if (i == 0) {
2958         raw = r;
2959       } else if (raw != r) {
2960         GST_ERROR_OBJECT (pad,
2961             "Caps contains raw and non-raw structures: %" GST_PTR_FORMAT, caps);
2962         raw = FALSE;
2963         break;
2964       }
2965     }
2966   } else {
2967     raw = is_raw_structure (gst_caps_get_structure (caps, 0));
2968   }
2969   gst_caps_unref (caps);
2970   gst_object_unref (peer);
2971
2972   return raw;
2973 }
2974
2975 static void
2976 sinkpad_blocked_cb (GstPad * blockedpad, gboolean blocked, gpointer user_data)
2977 {
2978   GstPlaySink *playsink = (GstPlaySink *) user_data;
2979   GstPad *pad;
2980
2981   GST_PLAY_SINK_LOCK (playsink);
2982
2983   pad = GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (blockedpad)));
2984   if (pad == playsink->video_pad) {
2985     playsink->video_pad_blocked = blocked;
2986     GST_DEBUG_OBJECT (pad, "Video pad blocked: %d", blocked);
2987     if (!blocked) {
2988       PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO_RAW);
2989       PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO);
2990     }
2991   } else if (pad == playsink->audio_pad) {
2992     playsink->audio_pad_blocked = blocked;
2993     GST_DEBUG_OBJECT (pad, "Audio pad blocked: %d", blocked);
2994     if (!blocked) {
2995       PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO_RAW);
2996       PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO);
2997     }
2998   } else if (pad == playsink->text_pad) {
2999     playsink->text_pad_blocked = blocked;
3000     GST_DEBUG_OBJECT (pad, "Text pad blocked: %d", blocked);
3001     if (!blocked)
3002       PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_TEXT);
3003   }
3004
3005   if (!blocked) {
3006     gst_object_unref (pad);
3007     GST_PLAY_SINK_UNLOCK (playsink);
3008     return;
3009   }
3010
3011   /* We reconfigure when for ALL streams:
3012    * * there isn't a pad
3013    * * OR the pad is blocked
3014    * * OR there are no pending blocks on that pad
3015    */
3016
3017   if ((!playsink->video_pad || playsink->video_pad_blocked
3018           || !PENDING_VIDEO_BLOCK (playsink)) && (!playsink->audio_pad
3019           || playsink->audio_pad_blocked || !PENDING_AUDIO_BLOCK (playsink))
3020       && (!playsink->text_pad || playsink->text_pad_blocked
3021           || !PENDING_TEXT_BLOCK (playsink))) {
3022     GST_DEBUG_OBJECT (playsink, "All pads blocked -- reconfiguring");
3023
3024     if (playsink->video_pad) {
3025       playsink->video_pad_raw = is_raw_pad (playsink->video_pad);
3026       GST_DEBUG_OBJECT (playsink, "Video pad is raw: %d",
3027           playsink->video_pad_raw);
3028     }
3029
3030     if (playsink->audio_pad) {
3031       playsink->audio_pad_raw = is_raw_pad (playsink->audio_pad);
3032       GST_DEBUG_OBJECT (playsink, "Audio pad is raw: %d",
3033           playsink->audio_pad_raw);
3034     }
3035
3036     gst_play_sink_reconfigure (playsink);
3037
3038     if (playsink->video_pad) {
3039       GstPad *opad =
3040           GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3041               (playsink->video_pad)));
3042       gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3043           gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3044       gst_object_unref (opad);
3045     }
3046
3047     if (playsink->audio_pad) {
3048       GstPad *opad =
3049           GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3050               (playsink->audio_pad)));
3051       gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3052           gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3053       gst_object_unref (opad);
3054     }
3055
3056     if (playsink->text_pad) {
3057       GstPad *opad =
3058           GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3059               (playsink->text_pad)));
3060       gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3061           gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3062       gst_object_unref (opad);
3063     }
3064   }
3065
3066   gst_object_unref (pad);
3067
3068   GST_PLAY_SINK_UNLOCK (playsink);
3069 }
3070
3071 static void
3072 caps_notify_cb (GstPad * pad, GParamSpec * unused, GstPlaySink * playsink)
3073 {
3074   gboolean reconfigure = FALSE;
3075   GstCaps *caps;
3076   gboolean raw;
3077
3078   g_object_get (pad, "caps", &caps, NULL);
3079   if (!caps)
3080     return;
3081
3082   if (pad == playsink->audio_pad) {
3083     raw = is_raw_pad (pad);
3084     reconfigure = (! !playsink->audio_pad_raw != ! !raw)
3085         && playsink->audiochain;
3086     GST_DEBUG_OBJECT (pad,
3087         "Audio caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3088         reconfigure, caps);
3089   } else if (pad == playsink->video_pad) {
3090     raw = is_raw_pad (pad);
3091     reconfigure = (! !playsink->video_pad_raw != ! !raw)
3092         && playsink->videochain;
3093     GST_DEBUG_OBJECT (pad,
3094         "Video caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3095         reconfigure, caps);
3096   }
3097
3098   gst_caps_unref (caps);
3099
3100   if (reconfigure) {
3101     GST_PLAY_SINK_LOCK (playsink);
3102     if (playsink->video_pad) {
3103       GstPad *opad =
3104           GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3105               (playsink->video_pad)));
3106       gst_pad_set_blocked_async_full (opad, TRUE, sinkpad_blocked_cb,
3107           gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3108       gst_object_unref (opad);
3109     }
3110
3111     if (playsink->audio_pad) {
3112       GstPad *opad =
3113           GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3114               (playsink->audio_pad)));
3115       gst_pad_set_blocked_async_full (opad, TRUE, sinkpad_blocked_cb,
3116           gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3117       gst_object_unref (opad);
3118     }
3119
3120     if (playsink->text_pad) {
3121       GstPad *opad =
3122           GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3123               (playsink->text_pad)));
3124       gst_pad_set_blocked_async_full (opad, TRUE, sinkpad_blocked_cb,
3125           gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3126       gst_object_unref (opad);
3127     }
3128     GST_PLAY_SINK_UNLOCK (playsink);
3129   }
3130 }
3131
3132 /**
3133  * gst_play_sink_request_pad
3134  * @playsink: a #GstPlaySink
3135  * @type: a #GstPlaySinkType
3136  *
3137  * Create or return a pad of @type.
3138  *
3139  * Returns: a #GstPad of @type or %NULL when the pad could not be created.
3140  */
3141 GstPad *
3142 gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type)
3143 {
3144   GstPad *res = NULL;
3145   gboolean created = FALSE;
3146   gboolean activate = TRUE;
3147   const gchar *pad_name = NULL;
3148
3149   GST_DEBUG_OBJECT (playsink, "request pad type %d", type);
3150
3151   GST_PLAY_SINK_LOCK (playsink);
3152   switch (type) {
3153     case GST_PLAY_SINK_TYPE_AUDIO_RAW:
3154     case GST_PLAY_SINK_TYPE_AUDIO:
3155       pad_name = "audio_sink";
3156       if (!playsink->audio_tee) {
3157         GST_LOG_OBJECT (playsink, "creating tee");
3158         /* create tee when needed. This element will feed the audio sink chain
3159          * and the vis chain. */
3160         playsink->audio_tee = gst_element_factory_make ("tee", "audiotee");
3161         if (playsink->audio_tee == NULL) {
3162           post_missing_element_message (playsink, "tee");
3163           GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
3164               (_("Missing element '%s' - check your GStreamer installation."),
3165                   "tee"), (NULL));
3166           res = NULL;
3167           break;
3168         } else {
3169           playsink->audio_tee_sink =
3170               gst_element_get_static_pad (playsink->audio_tee, "sink");
3171           gst_bin_add (GST_BIN_CAST (playsink), playsink->audio_tee);
3172           gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
3173         }
3174       } else {
3175         gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
3176       }
3177       if (!playsink->audio_pad) {
3178         GST_LOG_OBJECT (playsink, "ghosting tee sinkpad");
3179         playsink->audio_pad =
3180             gst_ghost_pad_new (pad_name, playsink->audio_tee_sink);
3181         g_signal_connect (G_OBJECT (playsink->audio_pad), "notify::caps",
3182             G_CALLBACK (caps_notify_cb), playsink);
3183         created = TRUE;
3184       }
3185       playsink->audio_pad_raw = FALSE;
3186       res = playsink->audio_pad;
3187       break;
3188     case GST_PLAY_SINK_TYPE_VIDEO_RAW:
3189     case GST_PLAY_SINK_TYPE_VIDEO:
3190       pad_name = "video_sink";
3191       if (!playsink->video_pad) {
3192         GST_LOG_OBJECT (playsink, "ghosting videosink");
3193         playsink->video_pad =
3194             gst_ghost_pad_new_no_target (pad_name, GST_PAD_SINK);
3195         g_signal_connect (G_OBJECT (playsink->video_pad), "notify::caps",
3196             G_CALLBACK (caps_notify_cb), playsink);
3197         created = TRUE;
3198       }
3199       playsink->video_pad_raw = FALSE;
3200       res = playsink->video_pad;
3201       break;
3202     case GST_PLAY_SINK_TYPE_TEXT:
3203       GST_LOG_OBJECT (playsink, "ghosting text");
3204       if (!playsink->text_pad) {
3205         playsink->text_pad =
3206             gst_ghost_pad_new_no_target ("text_sink", GST_PAD_SINK);
3207         created = TRUE;
3208       }
3209       res = playsink->text_pad;
3210       break;
3211     case GST_PLAY_SINK_TYPE_FLUSHING:
3212     {
3213       gchar *padname;
3214
3215       /* we need a unique padname for the flushing pad. */
3216       padname = g_strdup_printf ("flushing_%d", playsink->count);
3217       res = gst_ghost_pad_new_no_target (padname, GST_PAD_SINK);
3218       g_free (padname);
3219       playsink->count++;
3220       activate = FALSE;
3221       created = TRUE;
3222       break;
3223     }
3224     default:
3225       res = NULL;
3226       break;
3227   }
3228   GST_PLAY_SINK_UNLOCK (playsink);
3229
3230   if (created && res) {
3231     /* we have to add the pad when it's active or we get an error when the
3232      * element is 'running' */
3233     gst_pad_set_active (res, TRUE);
3234     gst_element_add_pad (GST_ELEMENT_CAST (playsink), res);
3235     if (type != GST_PLAY_SINK_TYPE_FLUSHING) {
3236       GstPad *blockpad =
3237           GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (res)));
3238
3239       gst_pad_set_blocked_async_full (blockpad, TRUE, sinkpad_blocked_cb,
3240           gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3241       PENDING_FLAG_SET (playsink, type);
3242       gst_object_unref (blockpad);
3243     }
3244     if (!activate)
3245       gst_pad_set_active (res, activate);
3246   }
3247
3248   return res;
3249 }
3250
3251 static GstPad *
3252 gst_play_sink_request_new_pad (GstElement * element, GstPadTemplate * templ,
3253     const gchar * name)
3254 {
3255   GstPlaySink *psink;
3256   GstPad *pad;
3257   GstPlaySinkType type;
3258   const gchar *tplname;
3259
3260   g_return_val_if_fail (templ != NULL, NULL);
3261
3262   GST_DEBUG_OBJECT (element, "name:%s", name);
3263
3264   psink = GST_PLAY_SINK (element);
3265   tplname = GST_PAD_TEMPLATE_NAME_TEMPLATE (templ);
3266
3267   /* Figure out the GstPlaySinkType based on the template */
3268   if (!strcmp (tplname, "audio_sink"))
3269     type = GST_PLAY_SINK_TYPE_AUDIO;
3270   else if (!strcmp (tplname, "audio_raw_sink"))
3271     type = GST_PLAY_SINK_TYPE_AUDIO_RAW;
3272   else if (!strcmp (tplname, "video_sink"))
3273     type = GST_PLAY_SINK_TYPE_VIDEO;
3274   else if (!strcmp (tplname, "video_raw_sink"))
3275     type = GST_PLAY_SINK_TYPE_VIDEO_RAW;
3276   else if (!strcmp (tplname, "text_sink"))
3277     type = GST_PLAY_SINK_TYPE_TEXT;
3278   else
3279     goto unknown_template;
3280
3281   pad = gst_play_sink_request_pad (psink, type);
3282   return pad;
3283
3284 unknown_template:
3285   GST_WARNING_OBJECT (element, "Unknown pad template");
3286   return NULL;
3287 }
3288
3289 void
3290 gst_play_sink_release_pad (GstPlaySink * playsink, GstPad * pad)
3291 {
3292   GstPad **res = NULL;
3293   gboolean untarget = TRUE;
3294
3295   GST_DEBUG_OBJECT (playsink, "release pad %" GST_PTR_FORMAT, pad);
3296
3297   GST_PLAY_SINK_LOCK (playsink);
3298   if (pad == playsink->video_pad) {
3299     res = &playsink->video_pad;
3300     g_signal_handlers_disconnect_by_func (playsink->video_pad, caps_notify_cb,
3301         playsink);
3302   } else if (pad == playsink->audio_pad) {
3303     res = &playsink->audio_pad;
3304     g_signal_handlers_disconnect_by_func (playsink->audio_pad, caps_notify_cb,
3305         playsink);
3306   } else if (pad == playsink->text_pad) {
3307     res = &playsink->text_pad;
3308   } else {
3309     /* try to release the given pad anyway, these could be the FLUSHING pads. */
3310     res = &pad;
3311     untarget = FALSE;
3312   }
3313   GST_PLAY_SINK_UNLOCK (playsink);
3314
3315   if (*res) {
3316     GST_DEBUG_OBJECT (playsink, "deactivate pad %" GST_PTR_FORMAT, *res);
3317     gst_pad_set_active (*res, FALSE);
3318     if (untarget) {
3319       GST_DEBUG_OBJECT (playsink, "untargeting pad %" GST_PTR_FORMAT, *res);
3320       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (*res), NULL);
3321     }
3322     GST_DEBUG_OBJECT (playsink, "remove pad %" GST_PTR_FORMAT, *res);
3323     gst_element_remove_pad (GST_ELEMENT_CAST (playsink), *res);
3324     *res = NULL;
3325   }
3326 }
3327
3328 static void
3329 gst_play_sink_release_request_pad (GstElement * element, GstPad * pad)
3330 {
3331   GstPlaySink *psink = GST_PLAY_SINK (element);
3332
3333   gst_play_sink_release_pad (psink, pad);
3334 }
3335
3336 static void
3337 gst_play_sink_handle_message (GstBin * bin, GstMessage * message)
3338 {
3339   GstPlaySink *playsink;
3340
3341   playsink = GST_PLAY_SINK_CAST (bin);
3342
3343   switch (GST_MESSAGE_TYPE (message)) {
3344     case GST_MESSAGE_STEP_DONE:
3345     {
3346       GstFormat format;
3347       guint64 amount;
3348       gdouble rate;
3349       gboolean flush, intermediate, eos;
3350       guint64 duration;
3351
3352       GST_INFO_OBJECT (playsink, "Handling step-done message");
3353       gst_message_parse_step_done (message, &format, &amount, &rate, &flush,
3354           &intermediate, &duration, &eos);
3355
3356       if (format == GST_FORMAT_BUFFERS) {
3357         /* for the buffer format, we align the other streams */
3358         if (playsink->audiochain) {
3359           GstEvent *event;
3360
3361           event =
3362               gst_event_new_step (GST_FORMAT_TIME, duration, rate, flush,
3363               intermediate);
3364
3365           if (!gst_element_send_event (playsink->audiochain->chain.bin, event)) {
3366             GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
3367           }
3368         }
3369       }
3370       GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
3371       break;
3372     }
3373     default:
3374       GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
3375       break;
3376   }
3377 }
3378
3379 /* Send an event to our sinks until one of them works; don't then send to the
3380  * remaining sinks (unlike GstBin)
3381  * Special case: If a text sink is set we need to send the event
3382  * to them in case it's source is different from the a/v stream's source.
3383  */
3384 static gboolean
3385 gst_play_sink_send_event_to_sink (GstPlaySink * playsink, GstEvent * event)
3386 {
3387   gboolean res = TRUE;
3388
3389   if (playsink->textchain && playsink->textchain->sink) {
3390     gst_event_ref (event);
3391     if ((res = gst_element_send_event (playsink->textchain->chain.bin, event))) {
3392       GST_DEBUG_OBJECT (playsink, "Sent event successfully to text sink");
3393     } else {
3394       GST_DEBUG_OBJECT (playsink, "Event failed when sent to text sink");
3395     }
3396   }
3397
3398   if (playsink->videochain) {
3399     gst_event_ref (event);
3400     if ((res = gst_element_send_event (playsink->videochain->chain.bin, event))) {
3401       GST_DEBUG_OBJECT (playsink, "Sent event successfully to video sink");
3402       goto done;
3403     }
3404     GST_DEBUG_OBJECT (playsink, "Event failed when sent to video sink");
3405   }
3406   if (playsink->audiochain) {
3407     gst_event_ref (event);
3408     if ((res = gst_element_send_event (playsink->audiochain->chain.bin, event))) {
3409       GST_DEBUG_OBJECT (playsink, "Sent event successfully to audio sink");
3410       goto done;
3411     }
3412     GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
3413   }
3414
3415 done:
3416   gst_event_unref (event);
3417   return res;
3418 }
3419
3420 /* We only want to send the event to a single sink (overriding GstBin's
3421  * behaviour), but we want to keep GstPipeline's behaviour - wrapping seek
3422  * events appropriately. So, this is a messy duplication of code. */
3423 static gboolean
3424 gst_play_sink_send_event (GstElement * element, GstEvent * event)
3425 {
3426   gboolean res = FALSE;
3427   GstEventType event_type = GST_EVENT_TYPE (event);
3428   GstPlaySink *playsink;
3429
3430   playsink = GST_PLAY_SINK_CAST (element);
3431
3432   switch (event_type) {
3433     case GST_EVENT_SEEK:
3434       GST_DEBUG_OBJECT (element, "Sending event to a sink");
3435       res = gst_play_sink_send_event_to_sink (playsink, event);
3436       break;
3437     case GST_EVENT_STEP:
3438     {
3439       GstFormat format;
3440       guint64 amount;
3441       gdouble rate;
3442       gboolean flush, intermediate;
3443
3444       gst_event_parse_step (event, &format, &amount, &rate, &flush,
3445           &intermediate);
3446
3447       if (format == GST_FORMAT_BUFFERS) {
3448         /* for buffers, we will try to step video frames, for other formats we
3449          * send the step to all sinks */
3450         res = gst_play_sink_send_event_to_sink (playsink, event);
3451       } else {
3452         res =
3453             GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
3454             event);
3455       }
3456       break;
3457     }
3458     default:
3459       res =
3460           GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
3461           event);
3462       break;
3463   }
3464   return res;
3465 }
3466
3467 static GstStateChangeReturn
3468 gst_play_sink_change_state (GstElement * element, GstStateChange transition)
3469 {
3470   GstStateChangeReturn ret;
3471   GstStateChangeReturn bret;
3472
3473   GstPlaySink *playsink;
3474
3475   playsink = GST_PLAY_SINK (element);
3476
3477   switch (transition) {
3478     case GST_STATE_CHANGE_READY_TO_PAUSED:
3479       playsink->need_async_start = TRUE;
3480       /* we want to go async to PAUSED until we managed to configure and add the
3481        * sinks */
3482       do_async_start (playsink);
3483       ret = GST_STATE_CHANGE_ASYNC;
3484       break;
3485     case GST_STATE_CHANGE_PAUSED_TO_READY:
3486       /* unblock all pads here */
3487       GST_PLAY_SINK_LOCK (playsink);
3488       if (playsink->video_pad) {
3489         GstPad *opad =
3490             GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3491                 (playsink->video_pad)));
3492         if (gst_pad_is_blocked (opad)) {
3493           gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3494               gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3495         }
3496         gst_object_unref (opad);
3497         playsink->video_pad_blocked = FALSE;
3498       }
3499
3500       if (playsink->audio_pad) {
3501         GstPad *opad =
3502             GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3503                 (playsink->audio_pad)));
3504
3505         if (gst_pad_is_blocked (opad)) {
3506           gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3507               gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3508         }
3509         gst_object_unref (opad);
3510         playsink->audio_pad_blocked = FALSE;
3511       }
3512
3513       if (playsink->text_pad) {
3514         GstPad *opad =
3515             GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3516                 (playsink->text_pad)));
3517         if (gst_pad_is_blocked (opad)) {
3518           gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3519               gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3520         }
3521         gst_object_unref (opad);
3522         playsink->text_pad_blocked = FALSE;
3523       }
3524       GST_PLAY_SINK_UNLOCK (playsink);
3525       /* fall through */
3526     case GST_STATE_CHANGE_READY_TO_NULL:
3527       if (playsink->audiochain && playsink->audiochain->sink_volume) {
3528         /* remove our links to the mute and volume elements when they were
3529          * provided by a sink */
3530         disconnect_chain (playsink->audiochain, playsink);
3531         playsink->audiochain->volume = NULL;
3532         playsink->audiochain->mute = NULL;
3533       }
3534
3535       if (playsink->audiochain && playsink->audiochain->ts_offset) {
3536         gst_object_unref (playsink->audiochain->ts_offset);
3537         playsink->audiochain->ts_offset = NULL;
3538       }
3539
3540       if (playsink->videochain && playsink->videochain->ts_offset) {
3541         gst_object_unref (playsink->videochain->ts_offset);
3542         playsink->videochain->ts_offset = NULL;
3543       }
3544       ret = GST_STATE_CHANGE_SUCCESS;
3545       break;
3546     default:
3547       /* all other state changes return SUCCESS by default, this value can be
3548        * overridden by the result of the children */
3549       ret = GST_STATE_CHANGE_SUCCESS;
3550       break;
3551   }
3552
3553   /* do the state change of the children */
3554   bret =
3555       GST_ELEMENT_CLASS (gst_play_sink_parent_class)->change_state (element,
3556       transition);
3557   /* now look at the result of our children and adjust the return value */
3558   switch (bret) {
3559     case GST_STATE_CHANGE_FAILURE:
3560       /* failure, we stop */
3561       goto activate_failed;
3562     case GST_STATE_CHANGE_NO_PREROLL:
3563       /* some child returned NO_PREROLL. This is strange but we never know. We
3564        * commit our async state change (if any) and return the NO_PREROLL */
3565       do_async_done (playsink);
3566       ret = bret;
3567       break;
3568     case GST_STATE_CHANGE_ASYNC:
3569       /* some child was async, return this */
3570       ret = bret;
3571       break;
3572     default:
3573       /* return our previously configured return value */
3574       break;
3575   }
3576
3577   switch (transition) {
3578     case GST_STATE_CHANGE_READY_TO_PAUSED:
3579       break;
3580     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
3581       /* FIXME Release audio device when we implement that */
3582       playsink->need_async_start = TRUE;
3583       break;
3584     case GST_STATE_CHANGE_PAUSED_TO_READY:{
3585       if (playsink->video_sinkpad_stream_synchronizer) {
3586         gst_element_release_request_pad (GST_ELEMENT_CAST
3587             (playsink->stream_synchronizer),
3588             playsink->video_sinkpad_stream_synchronizer);
3589         gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3590         playsink->video_sinkpad_stream_synchronizer = NULL;
3591         gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3592         playsink->video_srcpad_stream_synchronizer = NULL;
3593       }
3594       if (playsink->audio_sinkpad_stream_synchronizer) {
3595         gst_element_release_request_pad (GST_ELEMENT_CAST
3596             (playsink->stream_synchronizer),
3597             playsink->audio_sinkpad_stream_synchronizer);
3598         gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
3599         playsink->audio_sinkpad_stream_synchronizer = NULL;
3600         gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
3601         playsink->audio_srcpad_stream_synchronizer = NULL;
3602       }
3603       if (playsink->text_sinkpad_stream_synchronizer) {
3604         gst_element_release_request_pad (GST_ELEMENT_CAST
3605             (playsink->stream_synchronizer),
3606             playsink->text_sinkpad_stream_synchronizer);
3607         gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
3608         playsink->text_sinkpad_stream_synchronizer = NULL;
3609         gst_object_unref (playsink->text_srcpad_stream_synchronizer);
3610         playsink->text_srcpad_stream_synchronizer = NULL;
3611       }
3612     }
3613       /* fall through */
3614     case GST_STATE_CHANGE_READY_TO_NULL:
3615       /* remove sinks we added */
3616       if (playsink->videodeinterlacechain) {
3617         activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
3618             FALSE);
3619         add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3620       }
3621       if (playsink->videochain) {
3622         activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3623         add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3624       }
3625       if (playsink->audiochain) {
3626         activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3627         add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3628       }
3629       if (playsink->vischain) {
3630         activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3631         add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3632       }
3633       if (playsink->textchain) {
3634         activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3635         add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3636       }
3637       do_async_done (playsink);
3638       /* when going to READY, keep elements around as long as possible,
3639        * so they may be re-used faster next time/url around.
3640        * when really going to NULL, clean up everything completely. */
3641       if (transition == GST_STATE_CHANGE_READY_TO_NULL) {
3642
3643         /* Unparent the sinks to allow reuse */
3644         if (playsink->videochain && playsink->videochain->sink)
3645           gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
3646               playsink->videochain->sink);
3647         if (playsink->audiochain && playsink->audiochain->sink)
3648           gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
3649               playsink->audiochain->sink);
3650         if (playsink->textchain && playsink->textchain->sink)
3651           gst_bin_remove (GST_BIN_CAST (playsink->textchain->chain.bin),
3652               playsink->textchain->sink);
3653
3654         if (playsink->audio_sink != NULL)
3655           gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
3656         if (playsink->video_sink != NULL)
3657           gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
3658         if (playsink->visualisation != NULL)
3659           gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
3660         if (playsink->text_sink != NULL)
3661           gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
3662
3663         free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
3664         playsink->videodeinterlacechain = NULL;
3665         free_chain ((GstPlayChain *) playsink->videochain);
3666         playsink->videochain = NULL;
3667         free_chain ((GstPlayChain *) playsink->audiochain);
3668         playsink->audiochain = NULL;
3669         free_chain ((GstPlayChain *) playsink->vischain);
3670         playsink->vischain = NULL;
3671         free_chain ((GstPlayChain *) playsink->textchain);
3672         playsink->textchain = NULL;
3673       }
3674       break;
3675     default:
3676       break;
3677   }
3678   return ret;
3679
3680   /* ERRORS */
3681 activate_failed:
3682   {
3683     GST_DEBUG_OBJECT (element,
3684         "element failed to change states -- activation problem?");
3685     return GST_STATE_CHANGE_FAILURE;
3686   }
3687 }
3688
3689 static void
3690 gst_play_sink_set_property (GObject * object, guint prop_id,
3691     const GValue * value, GParamSpec * spec)
3692 {
3693   GstPlaySink *playsink = GST_PLAY_SINK (object);
3694
3695   switch (prop_id) {
3696     case PROP_FLAGS:
3697       gst_play_sink_set_flags (playsink, g_value_get_flags (value));
3698       break;
3699     case PROP_VOLUME:
3700       gst_play_sink_set_volume (playsink, g_value_get_double (value));
3701       break;
3702     case PROP_MUTE:
3703       gst_play_sink_set_mute (playsink, g_value_get_boolean (value));
3704       break;
3705     case PROP_FONT_DESC:
3706       gst_play_sink_set_font_desc (playsink, g_value_get_string (value));
3707       break;
3708     case PROP_SUBTITLE_ENCODING:
3709       gst_play_sink_set_subtitle_encoding (playsink,
3710           g_value_get_string (value));
3711       break;
3712     case PROP_VIS_PLUGIN:
3713       gst_play_sink_set_vis_plugin (playsink, g_value_get_object (value));
3714       break;
3715     case PROP_AV_OFFSET:
3716       gst_play_sink_set_av_offset (playsink, g_value_get_int64 (value));
3717       break;
3718     case PROP_VIDEO_SINK:
3719       gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_VIDEO,
3720           g_value_get_object (value));
3721       break;
3722     case PROP_AUDIO_SINK:
3723       gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_AUDIO,
3724           g_value_get_object (value));
3725       break;
3726     case PROP_TEXT_SINK:
3727       gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_TEXT,
3728           g_value_get_object (value));
3729       break;
3730     default:
3731       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
3732       break;
3733   }
3734 }
3735
3736 static void
3737 gst_play_sink_get_property (GObject * object, guint prop_id,
3738     GValue * value, GParamSpec * spec)
3739 {
3740   GstPlaySink *playsink = GST_PLAY_SINK (object);
3741
3742   switch (prop_id) {
3743     case PROP_FLAGS:
3744       g_value_set_flags (value, gst_play_sink_get_flags (playsink));
3745       break;
3746     case PROP_VOLUME:
3747       g_value_set_double (value, gst_play_sink_get_volume (playsink));
3748       break;
3749     case PROP_MUTE:
3750       g_value_set_boolean (value, gst_play_sink_get_mute (playsink));
3751       break;
3752     case PROP_FONT_DESC:
3753       g_value_take_string (value, gst_play_sink_get_font_desc (playsink));
3754       break;
3755     case PROP_SUBTITLE_ENCODING:
3756       g_value_take_string (value,
3757           gst_play_sink_get_subtitle_encoding (playsink));
3758       break;
3759     case PROP_VIS_PLUGIN:
3760       g_value_take_object (value, gst_play_sink_get_vis_plugin (playsink));
3761       break;
3762     case PROP_FRAME:
3763       gst_value_take_buffer (value, gst_play_sink_get_last_frame (playsink));
3764       break;
3765     case PROP_AV_OFFSET:
3766       g_value_set_int64 (value, gst_play_sink_get_av_offset (playsink));
3767       break;
3768     case PROP_VIDEO_SINK:
3769       g_value_take_object (value, gst_play_sink_get_sink (playsink,
3770               GST_PLAY_SINK_TYPE_VIDEO));
3771       break;
3772     case PROP_AUDIO_SINK:
3773       g_value_take_object (value, gst_play_sink_get_sink (playsink,
3774               GST_PLAY_SINK_TYPE_AUDIO));
3775       break;
3776     case PROP_TEXT_SINK:
3777       g_value_take_object (value, gst_play_sink_get_sink (playsink,
3778               GST_PLAY_SINK_TYPE_TEXT));
3779       break;
3780     default:
3781       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
3782       break;
3783   }
3784 }
3785
3786 gboolean
3787 gst_play_sink_plugin_init (GstPlugin * plugin)
3788 {
3789   GST_DEBUG_CATEGORY_INIT (gst_play_sink_debug, "playsink", 0, "play bin");
3790
3791   return gst_element_register (plugin, "playsink", GST_RANK_NONE,
3792       GST_TYPE_PLAY_SINK);
3793 }