playsink: Only really use software volume if requested
[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",
2026         ! !(playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME), NULL);
2027     GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
2028
2029     /* Disconnect signals */
2030     disconnect_chain (chain, playsink);
2031
2032     if (conv->volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
2033       chain->volume = conv->volume;
2034       chain->mute = chain->volume;
2035
2036       g_signal_connect (chain->volume, "notify::volume",
2037           G_CALLBACK (notify_volume_cb), playsink);
2038
2039       g_signal_connect (chain->mute, "notify::mute",
2040           G_CALLBACK (notify_mute_cb), playsink);
2041
2042       /* configure with the latest volume and mute */
2043       g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2044       g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
2045     }
2046
2047     GST_DEBUG_OBJECT (playsink, "reusing existing volume element");
2048   }
2049   return TRUE;
2050 }
2051
2052 /*
2053  *  +-------------------------------------------------------------------+
2054  *  | visbin                                                            |
2055  *  |      +----------+   +------------+   +----------+   +-------+     |
2056  *  |      | visqueue |   | audioconv  |   | audiores |   |  vis  |     |
2057  *  |   +-sink       src-sink + samp  src-sink       src-sink    src-+  |
2058  *  |   |  +----------+   +------------+   +----------+   +-------+  |  |
2059  * sink-+                                                            +-src
2060  *  +-------------------------------------------------------------------+
2061  *
2062  */
2063 static GstPlayVisChain *
2064 gen_vis_chain (GstPlaySink * playsink)
2065 {
2066   GstPlayVisChain *chain;
2067   GstBin *bin;
2068   gboolean res;
2069   GstPad *pad;
2070   GstElement *elem;
2071
2072   chain = g_new0 (GstPlayVisChain, 1);
2073   chain->chain.playsink = playsink;
2074
2075   GST_DEBUG_OBJECT (playsink, "making vis chain %p", chain);
2076
2077   chain->chain.bin = gst_bin_new ("visbin");
2078   bin = GST_BIN_CAST (chain->chain.bin);
2079   gst_object_ref_sink (bin);
2080
2081   /* we're queuing raw audio here, we can remove this queue when we can disable
2082    * async behaviour in the video sink. */
2083   chain->queue = gst_element_factory_make ("queue", "visqueue");
2084   if (chain->queue == NULL)
2085     goto no_queue;
2086   g_object_set (chain->queue, "silent", TRUE, NULL);
2087   gst_bin_add (bin, chain->queue);
2088
2089   chain->conv = gst_element_factory_make ("audioconvert", "aconv");
2090   if (chain->conv == NULL)
2091     goto no_audioconvert;
2092   gst_bin_add (bin, chain->conv);
2093
2094   chain->resample = gst_element_factory_make ("audioresample", "aresample");
2095   if (chain->resample == NULL)
2096     goto no_audioresample;
2097   gst_bin_add (bin, chain->resample);
2098
2099   /* this pad will be used for blocking the dataflow and switching the vis
2100    * plugin */
2101   chain->blockpad = gst_element_get_static_pad (chain->resample, "src");
2102
2103   if (playsink->visualisation) {
2104     GST_DEBUG_OBJECT (playsink, "trying configure vis");
2105     chain->vis = try_element (playsink, playsink->visualisation, FALSE);
2106   }
2107   if (chain->vis == NULL) {
2108     GST_DEBUG_OBJECT (playsink, "trying goom");
2109     elem = gst_element_factory_make ("goom", "vis");
2110     chain->vis = try_element (playsink, elem, TRUE);
2111   }
2112   if (chain->vis == NULL)
2113     goto no_goom;
2114
2115   gst_bin_add (bin, chain->vis);
2116
2117   res = gst_element_link_pads_full (chain->queue, "src", chain->conv, "sink",
2118       GST_PAD_LINK_CHECK_NOTHING);
2119   res &=
2120       gst_element_link_pads_full (chain->conv, "src", chain->resample, "sink",
2121       GST_PAD_LINK_CHECK_NOTHING);
2122   res &=
2123       gst_element_link_pads_full (chain->resample, "src", chain->vis, "sink",
2124       GST_PAD_LINK_CHECK_NOTHING);
2125   if (!res)
2126     goto link_failed;
2127
2128   chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
2129   chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
2130
2131   pad = gst_element_get_static_pad (chain->queue, "sink");
2132   chain->sinkpad = gst_ghost_pad_new ("sink", pad);
2133   gst_object_unref (pad);
2134   gst_element_add_pad (chain->chain.bin, chain->sinkpad);
2135
2136   chain->srcpad = gst_ghost_pad_new ("src", chain->vissrcpad);
2137   gst_element_add_pad (chain->chain.bin, chain->srcpad);
2138
2139   return chain;
2140
2141   /* ERRORS */
2142 no_queue:
2143   {
2144     post_missing_element_message (playsink, "queue");
2145     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2146         (_("Missing element '%s' - check your GStreamer installation."),
2147             "queue"), (NULL));
2148     free_chain ((GstPlayChain *) chain);
2149     return NULL;
2150   }
2151 no_audioconvert:
2152   {
2153     post_missing_element_message (playsink, "audioconvert");
2154     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2155         (_("Missing element '%s' - check your GStreamer installation."),
2156             "audioconvert"), ("possibly a liboil version mismatch?"));
2157     free_chain ((GstPlayChain *) chain);
2158     return NULL;
2159   }
2160 no_audioresample:
2161   {
2162     post_missing_element_message (playsink, "audioresample");
2163     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2164         (_("Missing element '%s' - check your GStreamer installation."),
2165             "audioresample"), (NULL));
2166     free_chain ((GstPlayChain *) chain);
2167     return NULL;
2168   }
2169 no_goom:
2170   {
2171     post_missing_element_message (playsink, "goom");
2172     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2173         (_("Missing element '%s' - check your GStreamer installation."),
2174             "goom"), (NULL));
2175     free_chain ((GstPlayChain *) chain);
2176     return NULL;
2177   }
2178 link_failed:
2179   {
2180     GST_ELEMENT_ERROR (playsink, CORE, PAD,
2181         (NULL), ("Failed to configure the visualisation element."));
2182     /* element made it to READY */
2183     gst_element_set_state (chain->vis, GST_STATE_NULL);
2184     free_chain ((GstPlayChain *) chain);
2185     return NULL;
2186   }
2187 }
2188
2189 /* this function is called when all the request pads are requested and when we
2190  * have to construct the final pipeline. Based on the flags we construct the
2191  * final output pipelines.
2192  */
2193 gboolean
2194 gst_play_sink_reconfigure (GstPlaySink * playsink)
2195 {
2196   GstPlayFlags flags;
2197   gboolean need_audio, need_video, need_deinterlace, need_vis, need_text;
2198
2199   GST_DEBUG_OBJECT (playsink, "reconfiguring");
2200
2201   /* assume we need nothing */
2202   need_audio = need_video = need_deinterlace = need_vis = need_text = FALSE;
2203
2204   GST_PLAY_SINK_LOCK (playsink);
2205   GST_OBJECT_LOCK (playsink);
2206   /* get flags, there are protected with the object lock */
2207   flags = playsink->flags;
2208   GST_OBJECT_UNLOCK (playsink);
2209
2210   /* figure out which components we need */
2211   if (flags & GST_PLAY_FLAG_TEXT && playsink->text_pad) {
2212     /* we have subtitles and we are requested to show it */
2213     need_text = TRUE;
2214   }
2215
2216   if (((flags & GST_PLAY_FLAG_VIDEO)
2217           || (flags & GST_PLAY_FLAG_NATIVE_VIDEO)) && playsink->video_pad) {
2218     /* we have video and we are requested to show it */
2219     need_video = TRUE;
2220
2221     /* we only deinterlace if native video is not requested and
2222      * we have raw video */
2223     if ((flags & GST_PLAY_FLAG_DEINTERLACE)
2224         && !(flags & GST_PLAY_FLAG_NATIVE_VIDEO) && playsink->video_pad_raw)
2225       need_deinterlace = TRUE;
2226   }
2227
2228   if (playsink->audio_pad) {
2229     if ((flags & GST_PLAY_FLAG_AUDIO) || (flags & GST_PLAY_FLAG_NATIVE_AUDIO)) {
2230       need_audio = TRUE;
2231     }
2232     if (playsink->audio_pad_raw) {
2233       /* only can do vis with raw uncompressed audio */
2234       if (flags & GST_PLAY_FLAG_VIS && !need_video) {
2235         /* also add video when we add visualisation */
2236         need_video = TRUE;
2237         need_vis = TRUE;
2238       }
2239     }
2240   }
2241
2242   /* we have a text_pad and we need text rendering, in this case we need a
2243    * video_pad to combine the video with the text or visualizations */
2244   if (need_text && !need_video) {
2245     if (playsink->video_pad) {
2246       need_video = TRUE;
2247     } else if (need_audio) {
2248       GST_ELEMENT_WARNING (playsink, STREAM, FORMAT,
2249           (_("Can't play a text file without video or visualizations.")),
2250           ("Have text pad but no video pad or visualizations"));
2251       need_text = FALSE;
2252     } else {
2253       GST_ELEMENT_ERROR (playsink, STREAM, FORMAT,
2254           (_("Can't play a text file without video or visualizations.")),
2255           ("Have text pad but no video pad or visualizations"));
2256       GST_PLAY_SINK_UNLOCK (playsink);
2257       return FALSE;
2258     }
2259   }
2260
2261   GST_DEBUG_OBJECT (playsink, "audio:%d, video:%d, vis:%d, text:%d", need_audio,
2262       need_video, need_vis, need_text);
2263
2264   /* set up video pipeline */
2265   if (need_video) {
2266     gboolean raw, async;
2267
2268     /* we need a raw sink when we do vis or when we have a raw pad */
2269     raw = need_vis ? TRUE : playsink->video_pad_raw;
2270     /* we try to set the sink async=FALSE when we need vis, this way we can
2271      * avoid a queue in the audio chain. */
2272     async = !need_vis;
2273
2274     GST_DEBUG_OBJECT (playsink, "adding video, raw %d",
2275         playsink->video_pad_raw);
2276
2277     if (playsink->videochain) {
2278       /* try to reactivate the chain */
2279       if (!setup_video_chain (playsink, raw, async)) {
2280         if (playsink->video_sinkpad_stream_synchronizer) {
2281           gst_element_release_request_pad (GST_ELEMENT_CAST
2282               (playsink->stream_synchronizer),
2283               playsink->video_sinkpad_stream_synchronizer);
2284           gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2285           playsink->video_sinkpad_stream_synchronizer = NULL;
2286           gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2287           playsink->video_srcpad_stream_synchronizer = NULL;
2288         }
2289
2290         add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2291
2292         /* Remove the sink from the bin to keep its state
2293          * and unparent it to allow reuse */
2294         if (playsink->videochain->sink)
2295           gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
2296               playsink->videochain->sink);
2297
2298         activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2299         free_chain ((GstPlayChain *) playsink->videochain);
2300         playsink->videochain = NULL;
2301       }
2302     }
2303
2304     if (!playsink->videochain)
2305       playsink->videochain = gen_video_chain (playsink, raw, async);
2306     if (!playsink->videochain)
2307       goto no_chain;
2308
2309     if (!playsink->video_sinkpad_stream_synchronizer) {
2310       GstIterator *it;
2311
2312       playsink->video_sinkpad_stream_synchronizer =
2313           gst_element_get_request_pad (GST_ELEMENT_CAST
2314           (playsink->stream_synchronizer), "sink_%d");
2315       it = gst_pad_iterate_internal_links
2316           (playsink->video_sinkpad_stream_synchronizer);
2317       g_assert (it);
2318       gst_iterator_next (it,
2319           (gpointer *) & playsink->video_srcpad_stream_synchronizer);
2320       g_assert (playsink->video_srcpad_stream_synchronizer);
2321       gst_iterator_free (it);
2322     }
2323
2324     if (playsink->video_pad)
2325       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad),
2326           playsink->video_sinkpad_stream_synchronizer);
2327
2328     if (need_deinterlace) {
2329       if (!playsink->videodeinterlacechain)
2330         playsink->videodeinterlacechain =
2331             gen_video_deinterlace_chain (playsink);
2332       if (!playsink->videodeinterlacechain)
2333         goto no_chain;
2334
2335       GST_DEBUG_OBJECT (playsink, "adding video deinterlace chain");
2336
2337       GST_DEBUG_OBJECT (playsink, "setting up deinterlacing chain");
2338
2339       add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
2340       activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
2341
2342       gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2343           playsink->videodeinterlacechain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2344     } else {
2345       if (playsink->videodeinterlacechain) {
2346         add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2347         activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
2348             FALSE);
2349       }
2350     }
2351
2352     GST_DEBUG_OBJECT (playsink, "adding video chain");
2353     add_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
2354     activate_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
2355     /* if we are not part of vis or subtitles, set the ghostpad target */
2356     if (!need_vis && !need_text && (!playsink->textchain
2357             || !playsink->text_pad)) {
2358       GST_DEBUG_OBJECT (playsink, "ghosting video sinkpad");
2359       if (need_deinterlace)
2360         gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
2361             playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2362       else
2363         gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2364             playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2365     }
2366   } else {
2367     GST_DEBUG_OBJECT (playsink, "no video needed");
2368     if (playsink->videochain) {
2369       GST_DEBUG_OBJECT (playsink, "removing video chain");
2370       if (playsink->vischain) {
2371         GstPad *srcpad;
2372
2373         GST_DEBUG_OBJECT (playsink, "unlinking vis chain");
2374
2375         /* also had visualisation, release the tee srcpad before we then
2376          * unlink the video from it */
2377         if (playsink->audio_tee_vissrc) {
2378           gst_element_release_request_pad (playsink->audio_tee,
2379               playsink->audio_tee_vissrc);
2380           gst_object_unref (playsink->audio_tee_vissrc);
2381           playsink->audio_tee_vissrc = NULL;
2382         }
2383         srcpad =
2384             gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2385         gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
2386       }
2387
2388       if (playsink->video_sinkpad_stream_synchronizer) {
2389         gst_element_release_request_pad (GST_ELEMENT_CAST
2390             (playsink->stream_synchronizer),
2391             playsink->video_sinkpad_stream_synchronizer);
2392         gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2393         playsink->video_sinkpad_stream_synchronizer = NULL;
2394         gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2395         playsink->video_srcpad_stream_synchronizer = NULL;
2396       }
2397
2398       add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2399       activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2400       if (playsink->videochain->ts_offset)
2401         gst_object_unref (playsink->videochain->ts_offset);
2402       playsink->videochain->ts_offset = NULL;
2403     }
2404
2405     if (playsink->videodeinterlacechain) {
2406       add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2407       activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2408     }
2409
2410     if (playsink->video_pad)
2411       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
2412   }
2413
2414   if (need_audio) {
2415     gboolean raw;
2416
2417     GST_DEBUG_OBJECT (playsink, "adding audio");
2418
2419     /* get a raw sink if we are asked for a raw pad */
2420     raw = playsink->audio_pad_raw;
2421
2422     if (playsink->audiochain) {
2423       /* try to reactivate the chain */
2424       if (!setup_audio_chain (playsink, raw)) {
2425         GST_DEBUG_OBJECT (playsink, "removing current audio chain");
2426         if (playsink->audio_tee_asrc) {
2427           gst_element_release_request_pad (playsink->audio_tee,
2428               playsink->audio_tee_asrc);
2429           gst_object_unref (playsink->audio_tee_asrc);
2430           playsink->audio_tee_asrc = NULL;
2431         }
2432
2433         if (playsink->audio_sinkpad_stream_synchronizer) {
2434           gst_element_release_request_pad (GST_ELEMENT_CAST
2435               (playsink->stream_synchronizer),
2436               playsink->audio_sinkpad_stream_synchronizer);
2437           gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
2438           playsink->audio_sinkpad_stream_synchronizer = NULL;
2439           gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
2440           playsink->audio_srcpad_stream_synchronizer = NULL;
2441         }
2442
2443         add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2444
2445         /* Remove the sink from the bin to keep its state
2446          * and unparent it to allow reuse */
2447         if (playsink->audiochain->sink)
2448           gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
2449               playsink->audiochain->sink);
2450
2451         activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2452         disconnect_chain (playsink->audiochain, playsink);
2453         playsink->audiochain->volume = NULL;
2454         playsink->audiochain->mute = NULL;
2455         if (playsink->audiochain->ts_offset)
2456           gst_object_unref (playsink->audiochain->ts_offset);
2457         playsink->audiochain->ts_offset = NULL;
2458         free_chain ((GstPlayChain *) playsink->audiochain);
2459         playsink->audiochain = NULL;
2460         playsink->volume_changed = playsink->mute_changed = FALSE;
2461       }
2462     }
2463
2464     if (!playsink->audiochain) {
2465       GST_DEBUG_OBJECT (playsink, "creating new audio chain");
2466       playsink->audiochain = gen_audio_chain (playsink, raw);
2467     }
2468
2469     if (!playsink->audio_sinkpad_stream_synchronizer) {
2470       GstIterator *it;
2471
2472       playsink->audio_sinkpad_stream_synchronizer =
2473           gst_element_get_request_pad (GST_ELEMENT_CAST
2474           (playsink->stream_synchronizer), "sink_%d");
2475       it = gst_pad_iterate_internal_links
2476           (playsink->audio_sinkpad_stream_synchronizer);
2477       g_assert (it);
2478       gst_iterator_next (it,
2479           (gpointer *) & playsink->audio_srcpad_stream_synchronizer);
2480       g_assert (playsink->audio_srcpad_stream_synchronizer);
2481       gst_iterator_free (it);
2482     }
2483
2484     if (playsink->audiochain) {
2485       GST_DEBUG_OBJECT (playsink, "adding audio chain");
2486       if (playsink->audio_tee_asrc == NULL) {
2487         playsink->audio_tee_asrc =
2488             gst_element_get_request_pad (playsink->audio_tee, "src%d");
2489       }
2490       add_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
2491       activate_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
2492       gst_pad_link_full (playsink->audio_tee_asrc,
2493           playsink->audio_sinkpad_stream_synchronizer,
2494           GST_PAD_LINK_CHECK_NOTHING);
2495       gst_pad_link_full (playsink->audio_srcpad_stream_synchronizer,
2496           playsink->audiochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2497     }
2498   } else {
2499     GST_DEBUG_OBJECT (playsink, "no audio needed");
2500     /* we have no audio or we are requested to not play audio */
2501     if (playsink->audiochain) {
2502       GST_DEBUG_OBJECT (playsink, "removing audio chain");
2503       /* release the audio pad */
2504       if (playsink->audio_tee_asrc) {
2505         gst_element_release_request_pad (playsink->audio_tee,
2506             playsink->audio_tee_asrc);
2507         gst_object_unref (playsink->audio_tee_asrc);
2508         playsink->audio_tee_asrc = NULL;
2509       }
2510
2511       if (playsink->audio_sinkpad_stream_synchronizer) {
2512         gst_element_release_request_pad (GST_ELEMENT_CAST
2513             (playsink->stream_synchronizer),
2514             playsink->audio_sinkpad_stream_synchronizer);
2515         gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
2516         playsink->audio_sinkpad_stream_synchronizer = NULL;
2517         gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
2518         playsink->audio_srcpad_stream_synchronizer = NULL;
2519       }
2520
2521       if (playsink->audiochain->sink_volume) {
2522         disconnect_chain (playsink->audiochain, playsink);
2523         playsink->audiochain->volume = NULL;
2524         playsink->audiochain->mute = NULL;
2525         if (playsink->audiochain->ts_offset)
2526           gst_object_unref (playsink->audiochain->ts_offset);
2527         playsink->audiochain->ts_offset = NULL;
2528       }
2529       add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2530       activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2531     }
2532   }
2533
2534   if (need_vis) {
2535     GstPad *srcpad;
2536
2537     if (!playsink->vischain)
2538       playsink->vischain = gen_vis_chain (playsink);
2539
2540     GST_DEBUG_OBJECT (playsink, "adding visualisation");
2541
2542     if (playsink->vischain) {
2543       GST_DEBUG_OBJECT (playsink, "setting up vis chain");
2544       srcpad =
2545           gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2546       add_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
2547       activate_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
2548       if (playsink->audio_tee_vissrc == NULL) {
2549         playsink->audio_tee_vissrc =
2550             gst_element_get_request_pad (playsink->audio_tee, "src%d");
2551       }
2552       gst_pad_link_full (playsink->audio_tee_vissrc,
2553           playsink->vischain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2554       gst_pad_link_full (srcpad, playsink->video_sinkpad_stream_synchronizer,
2555           GST_PAD_LINK_CHECK_NOTHING);
2556       gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2557           playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2558       gst_object_unref (srcpad);
2559     }
2560   } else {
2561     GST_DEBUG_OBJECT (playsink, "no vis needed");
2562     if (playsink->vischain) {
2563       if (playsink->audio_tee_vissrc) {
2564         gst_element_release_request_pad (playsink->audio_tee,
2565             playsink->audio_tee_vissrc);
2566         gst_object_unref (playsink->audio_tee_vissrc);
2567         playsink->audio_tee_vissrc = NULL;
2568       }
2569       GST_DEBUG_OBJECT (playsink, "removing vis chain");
2570       add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
2571       activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
2572     }
2573   }
2574
2575   if (need_text) {
2576     GST_DEBUG_OBJECT (playsink, "adding text");
2577     if (!playsink->textchain) {
2578       GST_DEBUG_OBJECT (playsink, "creating text chain");
2579       playsink->textchain = gen_text_chain (playsink);
2580     }
2581     if (playsink->textchain) {
2582       GstIterator *it;
2583
2584       GST_DEBUG_OBJECT (playsink, "adding text chain");
2585       if (playsink->textchain->overlay)
2586         g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", FALSE,
2587             NULL);
2588       add_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
2589
2590       if (!playsink->text_sinkpad_stream_synchronizer) {
2591         playsink->text_sinkpad_stream_synchronizer =
2592             gst_element_get_request_pad (GST_ELEMENT_CAST
2593             (playsink->stream_synchronizer), "sink_%d");
2594         it = gst_pad_iterate_internal_links
2595             (playsink->text_sinkpad_stream_synchronizer);
2596         g_assert (it);
2597         gst_iterator_next (it,
2598             (gpointer *) & playsink->text_srcpad_stream_synchronizer);
2599         g_assert (playsink->text_srcpad_stream_synchronizer);
2600         gst_iterator_free (it);
2601
2602         gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad),
2603             playsink->text_sinkpad_stream_synchronizer);
2604         gst_pad_link_full (playsink->text_srcpad_stream_synchronizer,
2605             playsink->textchain->textsinkpad, GST_PAD_LINK_CHECK_NOTHING);
2606       }
2607
2608       if (need_vis) {
2609         GstPad *srcpad;
2610
2611         srcpad =
2612             gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2613         gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
2614         gst_pad_link_full (srcpad, playsink->textchain->videosinkpad,
2615             GST_PAD_LINK_CHECK_NOTHING);
2616         gst_object_unref (srcpad);
2617       } else {
2618         if (need_deinterlace)
2619           gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
2620               playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
2621         else
2622           gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2623               playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
2624       }
2625       gst_pad_link_full (playsink->textchain->srcpad,
2626           playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2627
2628       activate_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
2629     }
2630   } else {
2631     GST_DEBUG_OBJECT (playsink, "no text needed");
2632     /* we have no subtitles/text or we are requested to not show them */
2633
2634     if (playsink->text_sinkpad_stream_synchronizer) {
2635       gst_element_release_request_pad (GST_ELEMENT_CAST
2636           (playsink->stream_synchronizer),
2637           playsink->text_sinkpad_stream_synchronizer);
2638       gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
2639       playsink->text_sinkpad_stream_synchronizer = NULL;
2640       gst_object_unref (playsink->text_srcpad_stream_synchronizer);
2641       playsink->text_srcpad_stream_synchronizer = NULL;
2642     }
2643
2644     if (playsink->textchain) {
2645       if (playsink->text_pad == NULL) {
2646         /* no text pad, remove the chain entirely */
2647         GST_DEBUG_OBJECT (playsink, "removing text chain");
2648         add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
2649         activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
2650       } else {
2651         /* we have a chain and a textpad, turn the subtitles off */
2652         GST_DEBUG_OBJECT (playsink, "turning off the text");
2653         if (playsink->textchain->overlay)
2654           g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", TRUE,
2655               NULL);
2656       }
2657     }
2658     if (!need_video && playsink->video_pad) {
2659       if (playsink->video_sinkpad_stream_synchronizer) {
2660         gst_element_release_request_pad (GST_ELEMENT_CAST
2661             (playsink->stream_synchronizer),
2662             playsink->video_sinkpad_stream_synchronizer);
2663         gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2664         playsink->video_sinkpad_stream_synchronizer = NULL;
2665         gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2666         playsink->video_srcpad_stream_synchronizer = NULL;
2667       }
2668
2669       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
2670     }
2671
2672     if (playsink->text_pad && !playsink->textchain)
2673       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad), NULL);
2674   }
2675   update_av_offset (playsink);
2676   do_async_done (playsink);
2677   GST_PLAY_SINK_UNLOCK (playsink);
2678
2679   return TRUE;
2680
2681   /* ERRORS */
2682 no_chain:
2683   {
2684     /* gen_ chain already posted error */
2685     GST_DEBUG_OBJECT (playsink, "failed to setup chain");
2686     GST_PLAY_SINK_UNLOCK (playsink);
2687     return FALSE;
2688   }
2689 }
2690
2691 /**
2692  * gst_play_sink_set_flags:
2693  * @playsink: a #GstPlaySink
2694  * @flags: #GstPlayFlags
2695  *
2696  * Configure @flags on @playsink. The flags control the behaviour of @playsink
2697  * when constructing the sink pipelins.
2698  *
2699  * Returns: TRUE if the flags could be configured.
2700  */
2701 gboolean
2702 gst_play_sink_set_flags (GstPlaySink * playsink, GstPlayFlags flags)
2703 {
2704   g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), FALSE);
2705
2706   GST_OBJECT_LOCK (playsink);
2707   playsink->flags = flags;
2708   GST_OBJECT_UNLOCK (playsink);
2709
2710   return TRUE;
2711 }
2712
2713 /**
2714  * gst_play_sink_get_flags:
2715  * @playsink: a #GstPlaySink
2716  *
2717  * Get the flags of @playsink. That flags control the behaviour of the sink when
2718  * it constructs the sink pipelines.
2719  *
2720  * Returns: the currently configured #GstPlayFlags.
2721  */
2722 GstPlayFlags
2723 gst_play_sink_get_flags (GstPlaySink * playsink)
2724 {
2725   GstPlayFlags res;
2726
2727   g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), 0);
2728
2729   GST_OBJECT_LOCK (playsink);
2730   res = playsink->flags;
2731   GST_OBJECT_UNLOCK (playsink);
2732
2733   return res;
2734 }
2735
2736 void
2737 gst_play_sink_set_font_desc (GstPlaySink * playsink, const gchar * desc)
2738 {
2739   GstPlayTextChain *chain;
2740
2741   GST_PLAY_SINK_LOCK (playsink);
2742   chain = (GstPlayTextChain *) playsink->textchain;
2743   g_free (playsink->font_desc);
2744   playsink->font_desc = g_strdup (desc);
2745   if (chain && chain->overlay) {
2746     g_object_set (chain->overlay, "font-desc", desc, NULL);
2747   }
2748   GST_PLAY_SINK_UNLOCK (playsink);
2749 }
2750
2751 gchar *
2752 gst_play_sink_get_font_desc (GstPlaySink * playsink)
2753 {
2754   gchar *result = NULL;
2755   GstPlayTextChain *chain;
2756
2757   GST_PLAY_SINK_LOCK (playsink);
2758   chain = (GstPlayTextChain *) playsink->textchain;
2759   if (chain && chain->overlay) {
2760     g_object_get (chain->overlay, "font-desc", &result, NULL);
2761     playsink->font_desc = g_strdup (result);
2762   } else {
2763     result = g_strdup (playsink->font_desc);
2764   }
2765   GST_PLAY_SINK_UNLOCK (playsink);
2766
2767   return result;
2768 }
2769
2770 void
2771 gst_play_sink_set_subtitle_encoding (GstPlaySink * playsink,
2772     const gchar * encoding)
2773 {
2774   GstPlayTextChain *chain;
2775
2776   GST_PLAY_SINK_LOCK (playsink);
2777   chain = (GstPlayTextChain *) playsink->textchain;
2778   g_free (playsink->subtitle_encoding);
2779   playsink->subtitle_encoding = g_strdup (encoding);
2780   if (chain && chain->overlay) {
2781     g_object_set (chain->overlay, "subtitle-encoding", encoding, NULL);
2782   }
2783   GST_PLAY_SINK_UNLOCK (playsink);
2784 }
2785
2786 gchar *
2787 gst_play_sink_get_subtitle_encoding (GstPlaySink * playsink)
2788 {
2789   gchar *result = NULL;
2790   GstPlayTextChain *chain;
2791
2792   GST_PLAY_SINK_LOCK (playsink);
2793   chain = (GstPlayTextChain *) playsink->textchain;
2794   if (chain && chain->overlay) {
2795     g_object_get (chain->overlay, "subtitle-encoding", &result, NULL);
2796     playsink->subtitle_encoding = g_strdup (result);
2797   } else {
2798     result = g_strdup (playsink->subtitle_encoding);
2799   }
2800   GST_PLAY_SINK_UNLOCK (playsink);
2801
2802   return result;
2803 }
2804
2805 static void
2806 update_av_offset (GstPlaySink * playsink)
2807 {
2808   gint64 av_offset;
2809   GstPlayAudioChain *achain;
2810   GstPlayVideoChain *vchain;
2811
2812   av_offset = playsink->av_offset;
2813   achain = (GstPlayAudioChain *) playsink->audiochain;
2814   vchain = (GstPlayVideoChain *) playsink->videochain;
2815
2816   if (achain && vchain && achain->ts_offset && vchain->ts_offset) {
2817     g_object_set (achain->ts_offset, "ts-offset", MAX (0, -av_offset), NULL);
2818     g_object_set (vchain->ts_offset, "ts-offset", MAX (0, av_offset), NULL);
2819   } else {
2820     GST_LOG_OBJECT (playsink, "no ts_offset elements");
2821   }
2822 }
2823
2824 void
2825 gst_play_sink_set_av_offset (GstPlaySink * playsink, gint64 av_offset)
2826 {
2827   GST_PLAY_SINK_LOCK (playsink);
2828   playsink->av_offset = av_offset;
2829   update_av_offset (playsink);
2830   GST_PLAY_SINK_UNLOCK (playsink);
2831 }
2832
2833 gint64
2834 gst_play_sink_get_av_offset (GstPlaySink * playsink)
2835 {
2836   gint64 result;
2837
2838   GST_PLAY_SINK_LOCK (playsink);
2839   result = playsink->av_offset;
2840   GST_PLAY_SINK_UNLOCK (playsink);
2841
2842   return result;
2843 }
2844
2845 /**
2846  * gst_play_sink_get_last_frame:
2847  * @playsink: a #GstPlaySink
2848  *
2849  * Get the last displayed frame from @playsink. This frame is in the native
2850  * format of the sink element, the caps on the result buffer contain the format
2851  * of the frame data.
2852  *
2853  * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
2854  * available.
2855  */
2856 GstBuffer *
2857 gst_play_sink_get_last_frame (GstPlaySink * playsink)
2858 {
2859   GstBuffer *result = NULL;
2860   GstPlayVideoChain *chain;
2861
2862   GST_PLAY_SINK_LOCK (playsink);
2863   GST_DEBUG_OBJECT (playsink, "taking last frame");
2864   /* get the video chain if we can */
2865   if ((chain = (GstPlayVideoChain *) playsink->videochain)) {
2866     GST_DEBUG_OBJECT (playsink, "found video chain");
2867     /* see if the chain is active */
2868     if (chain->chain.activated && chain->sink) {
2869       GstElement *elem;
2870
2871       GST_DEBUG_OBJECT (playsink, "video chain active and has a sink");
2872
2873       /* find and get the last-buffer property now */
2874       if ((elem =
2875               gst_play_sink_find_property (playsink, chain->sink,
2876                   "last-buffer", GST_TYPE_BUFFER))) {
2877         GST_DEBUG_OBJECT (playsink, "getting last-buffer property");
2878         g_object_get (elem, "last-buffer", &result, NULL);
2879         gst_object_unref (elem);
2880       }
2881     }
2882   }
2883   GST_PLAY_SINK_UNLOCK (playsink);
2884
2885   return result;
2886 }
2887
2888 /**
2889  * gst_play_sink_convert_frame:
2890  * @playsink: a #GstPlaySink
2891  * @caps: a #GstCaps
2892  *
2893  * Get the last displayed frame from @playsink. If caps is %NULL, the video will
2894  * be in the native format of the sink element and the caps on the buffer
2895  * describe the format of the frame. If @caps is not %NULL, the video
2896  * frame will be converted to the format of the caps.
2897  *
2898  * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
2899  * available or when the conversion failed.
2900  */
2901 GstBuffer *
2902 gst_play_sink_convert_frame (GstPlaySink * playsink, GstCaps * caps)
2903 {
2904   GstBuffer *result;
2905
2906   result = gst_play_sink_get_last_frame (playsink);
2907   if (result != NULL && caps != NULL) {
2908     GstBuffer *temp;
2909     GError *err = NULL;
2910
2911     temp = gst_video_convert_frame (result, caps, 25 * GST_SECOND, &err);
2912     gst_buffer_unref (result);
2913     if (temp == NULL && err) {
2914       /* I'm really uncertain whether we should make playsink post an error
2915        * on the bus or not. It's not like it's a critical issue regarding
2916        * playsink behaviour. */
2917       GST_ERROR ("Error converting frame: %s", err->message);
2918       g_error_free (err);
2919     }
2920     result = temp;
2921   }
2922   return result;
2923 }
2924
2925 static gboolean
2926 is_raw_structure (GstStructure * s)
2927 {
2928   const gchar *name;
2929
2930   name = gst_structure_get_name (s);
2931
2932   if (g_str_has_prefix (name, "video/x-raw-") ||
2933       g_str_has_prefix (name, "audio/x-raw-"))
2934     return TRUE;
2935   return FALSE;
2936 }
2937
2938 static gboolean
2939 is_raw_pad (GstPad * pad)
2940 {
2941   GstPad *peer = gst_pad_get_peer (pad);
2942   GstCaps *caps;
2943   gboolean raw = TRUE;
2944
2945   if (!peer)
2946     return raw;
2947
2948   caps = gst_pad_get_negotiated_caps (peer);
2949   if (!caps) {
2950     guint i, n;
2951
2952     caps = gst_pad_get_caps_reffed (peer);
2953
2954     n = gst_caps_get_size (caps);
2955     for (i = 0; i < n; i++) {
2956       gboolean r = is_raw_structure (gst_caps_get_structure (caps, i));
2957
2958       if (i == 0) {
2959         raw = r;
2960       } else if (raw != r) {
2961         GST_ERROR_OBJECT (pad,
2962             "Caps contains raw and non-raw structures: %" GST_PTR_FORMAT, caps);
2963         raw = FALSE;
2964         break;
2965       }
2966     }
2967   } else {
2968     raw = is_raw_structure (gst_caps_get_structure (caps, 0));
2969   }
2970   gst_caps_unref (caps);
2971   gst_object_unref (peer);
2972
2973   return raw;
2974 }
2975
2976 static void
2977 sinkpad_blocked_cb (GstPad * blockedpad, gboolean blocked, gpointer user_data)
2978 {
2979   GstPlaySink *playsink = (GstPlaySink *) user_data;
2980   GstPad *pad;
2981
2982   GST_PLAY_SINK_LOCK (playsink);
2983
2984   pad = GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (blockedpad)));
2985   if (pad == playsink->video_pad) {
2986     playsink->video_pad_blocked = blocked;
2987     GST_DEBUG_OBJECT (pad, "Video pad blocked: %d", blocked);
2988     if (!blocked) {
2989       PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO_RAW);
2990       PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO);
2991     }
2992   } else if (pad == playsink->audio_pad) {
2993     playsink->audio_pad_blocked = blocked;
2994     GST_DEBUG_OBJECT (pad, "Audio pad blocked: %d", blocked);
2995     if (!blocked) {
2996       PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO_RAW);
2997       PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO);
2998     }
2999   } else if (pad == playsink->text_pad) {
3000     playsink->text_pad_blocked = blocked;
3001     GST_DEBUG_OBJECT (pad, "Text pad blocked: %d", blocked);
3002     if (!blocked)
3003       PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_TEXT);
3004   }
3005
3006   if (!blocked) {
3007     gst_object_unref (pad);
3008     GST_PLAY_SINK_UNLOCK (playsink);
3009     return;
3010   }
3011
3012   /* We reconfigure when for ALL streams:
3013    * * there isn't a pad
3014    * * OR the pad is blocked
3015    * * OR there are no pending blocks on that pad
3016    */
3017
3018   if ((!playsink->video_pad || playsink->video_pad_blocked
3019           || !PENDING_VIDEO_BLOCK (playsink)) && (!playsink->audio_pad
3020           || playsink->audio_pad_blocked || !PENDING_AUDIO_BLOCK (playsink))
3021       && (!playsink->text_pad || playsink->text_pad_blocked
3022           || !PENDING_TEXT_BLOCK (playsink))) {
3023     GST_DEBUG_OBJECT (playsink, "All pads blocked -- reconfiguring");
3024
3025     if (playsink->video_pad) {
3026       playsink->video_pad_raw = is_raw_pad (playsink->video_pad);
3027       GST_DEBUG_OBJECT (playsink, "Video pad is raw: %d",
3028           playsink->video_pad_raw);
3029     }
3030
3031     if (playsink->audio_pad) {
3032       playsink->audio_pad_raw = is_raw_pad (playsink->audio_pad);
3033       GST_DEBUG_OBJECT (playsink, "Audio pad is raw: %d",
3034           playsink->audio_pad_raw);
3035     }
3036
3037     gst_play_sink_reconfigure (playsink);
3038
3039     if (playsink->video_pad) {
3040       GstPad *opad =
3041           GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3042               (playsink->video_pad)));
3043       gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3044           gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3045       gst_object_unref (opad);
3046     }
3047
3048     if (playsink->audio_pad) {
3049       GstPad *opad =
3050           GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3051               (playsink->audio_pad)));
3052       gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3053           gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3054       gst_object_unref (opad);
3055     }
3056
3057     if (playsink->text_pad) {
3058       GstPad *opad =
3059           GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3060               (playsink->text_pad)));
3061       gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3062           gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3063       gst_object_unref (opad);
3064     }
3065   }
3066
3067   gst_object_unref (pad);
3068
3069   GST_PLAY_SINK_UNLOCK (playsink);
3070 }
3071
3072 static void
3073 caps_notify_cb (GstPad * pad, GParamSpec * unused, GstPlaySink * playsink)
3074 {
3075   gboolean reconfigure = FALSE;
3076   GstCaps *caps;
3077   gboolean raw;
3078
3079   g_object_get (pad, "caps", &caps, NULL);
3080   if (!caps)
3081     return;
3082
3083   if (pad == playsink->audio_pad) {
3084     raw = is_raw_pad (pad);
3085     reconfigure = (! !playsink->audio_pad_raw != ! !raw)
3086         && playsink->audiochain;
3087     GST_DEBUG_OBJECT (pad,
3088         "Audio caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3089         reconfigure, caps);
3090   } else if (pad == playsink->video_pad) {
3091     raw = is_raw_pad (pad);
3092     reconfigure = (! !playsink->video_pad_raw != ! !raw)
3093         && playsink->videochain;
3094     GST_DEBUG_OBJECT (pad,
3095         "Video caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3096         reconfigure, caps);
3097   }
3098
3099   gst_caps_unref (caps);
3100
3101   if (reconfigure) {
3102     GST_PLAY_SINK_LOCK (playsink);
3103     if (playsink->video_pad) {
3104       GstPad *opad =
3105           GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3106               (playsink->video_pad)));
3107       gst_pad_set_blocked_async_full (opad, TRUE, sinkpad_blocked_cb,
3108           gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3109       gst_object_unref (opad);
3110     }
3111
3112     if (playsink->audio_pad) {
3113       GstPad *opad =
3114           GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3115               (playsink->audio_pad)));
3116       gst_pad_set_blocked_async_full (opad, TRUE, sinkpad_blocked_cb,
3117           gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3118       gst_object_unref (opad);
3119     }
3120
3121     if (playsink->text_pad) {
3122       GstPad *opad =
3123           GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3124               (playsink->text_pad)));
3125       gst_pad_set_blocked_async_full (opad, TRUE, sinkpad_blocked_cb,
3126           gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3127       gst_object_unref (opad);
3128     }
3129     GST_PLAY_SINK_UNLOCK (playsink);
3130   }
3131 }
3132
3133 /**
3134  * gst_play_sink_request_pad
3135  * @playsink: a #GstPlaySink
3136  * @type: a #GstPlaySinkType
3137  *
3138  * Create or return a pad of @type.
3139  *
3140  * Returns: a #GstPad of @type or %NULL when the pad could not be created.
3141  */
3142 GstPad *
3143 gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type)
3144 {
3145   GstPad *res = NULL;
3146   gboolean created = FALSE;
3147   gboolean activate = TRUE;
3148   const gchar *pad_name = NULL;
3149
3150   GST_DEBUG_OBJECT (playsink, "request pad type %d", type);
3151
3152   GST_PLAY_SINK_LOCK (playsink);
3153   switch (type) {
3154     case GST_PLAY_SINK_TYPE_AUDIO_RAW:
3155     case GST_PLAY_SINK_TYPE_AUDIO:
3156       pad_name = "audio_sink";
3157       if (!playsink->audio_tee) {
3158         GST_LOG_OBJECT (playsink, "creating tee");
3159         /* create tee when needed. This element will feed the audio sink chain
3160          * and the vis chain. */
3161         playsink->audio_tee = gst_element_factory_make ("tee", "audiotee");
3162         if (playsink->audio_tee == NULL) {
3163           post_missing_element_message (playsink, "tee");
3164           GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
3165               (_("Missing element '%s' - check your GStreamer installation."),
3166                   "tee"), (NULL));
3167           res = NULL;
3168           break;
3169         } else {
3170           playsink->audio_tee_sink =
3171               gst_element_get_static_pad (playsink->audio_tee, "sink");
3172           gst_bin_add (GST_BIN_CAST (playsink), playsink->audio_tee);
3173           gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
3174         }
3175       } else {
3176         gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
3177       }
3178       if (!playsink->audio_pad) {
3179         GST_LOG_OBJECT (playsink, "ghosting tee sinkpad");
3180         playsink->audio_pad =
3181             gst_ghost_pad_new (pad_name, playsink->audio_tee_sink);
3182         g_signal_connect (G_OBJECT (playsink->audio_pad), "notify::caps",
3183             G_CALLBACK (caps_notify_cb), playsink);
3184         created = TRUE;
3185       }
3186       playsink->audio_pad_raw = FALSE;
3187       res = playsink->audio_pad;
3188       break;
3189     case GST_PLAY_SINK_TYPE_VIDEO_RAW:
3190     case GST_PLAY_SINK_TYPE_VIDEO:
3191       pad_name = "video_sink";
3192       if (!playsink->video_pad) {
3193         GST_LOG_OBJECT (playsink, "ghosting videosink");
3194         playsink->video_pad =
3195             gst_ghost_pad_new_no_target (pad_name, GST_PAD_SINK);
3196         g_signal_connect (G_OBJECT (playsink->video_pad), "notify::caps",
3197             G_CALLBACK (caps_notify_cb), playsink);
3198         created = TRUE;
3199       }
3200       playsink->video_pad_raw = FALSE;
3201       res = playsink->video_pad;
3202       break;
3203     case GST_PLAY_SINK_TYPE_TEXT:
3204       GST_LOG_OBJECT (playsink, "ghosting text");
3205       if (!playsink->text_pad) {
3206         playsink->text_pad =
3207             gst_ghost_pad_new_no_target ("text_sink", GST_PAD_SINK);
3208         created = TRUE;
3209       }
3210       res = playsink->text_pad;
3211       break;
3212     case GST_PLAY_SINK_TYPE_FLUSHING:
3213     {
3214       gchar *padname;
3215
3216       /* we need a unique padname for the flushing pad. */
3217       padname = g_strdup_printf ("flushing_%d", playsink->count);
3218       res = gst_ghost_pad_new_no_target (padname, GST_PAD_SINK);
3219       g_free (padname);
3220       playsink->count++;
3221       activate = FALSE;
3222       created = TRUE;
3223       break;
3224     }
3225     default:
3226       res = NULL;
3227       break;
3228   }
3229   GST_PLAY_SINK_UNLOCK (playsink);
3230
3231   if (created && res) {
3232     /* we have to add the pad when it's active or we get an error when the
3233      * element is 'running' */
3234     gst_pad_set_active (res, TRUE);
3235     gst_element_add_pad (GST_ELEMENT_CAST (playsink), res);
3236     if (type != GST_PLAY_SINK_TYPE_FLUSHING) {
3237       GstPad *blockpad =
3238           GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (res)));
3239
3240       gst_pad_set_blocked_async_full (blockpad, TRUE, sinkpad_blocked_cb,
3241           gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3242       PENDING_FLAG_SET (playsink, type);
3243       gst_object_unref (blockpad);
3244     }
3245     if (!activate)
3246       gst_pad_set_active (res, activate);
3247   }
3248
3249   return res;
3250 }
3251
3252 static GstPad *
3253 gst_play_sink_request_new_pad (GstElement * element, GstPadTemplate * templ,
3254     const gchar * name)
3255 {
3256   GstPlaySink *psink;
3257   GstPad *pad;
3258   GstPlaySinkType type;
3259   const gchar *tplname;
3260
3261   g_return_val_if_fail (templ != NULL, NULL);
3262
3263   GST_DEBUG_OBJECT (element, "name:%s", name);
3264
3265   psink = GST_PLAY_SINK (element);
3266   tplname = GST_PAD_TEMPLATE_NAME_TEMPLATE (templ);
3267
3268   /* Figure out the GstPlaySinkType based on the template */
3269   if (!strcmp (tplname, "audio_sink"))
3270     type = GST_PLAY_SINK_TYPE_AUDIO;
3271   else if (!strcmp (tplname, "audio_raw_sink"))
3272     type = GST_PLAY_SINK_TYPE_AUDIO_RAW;
3273   else if (!strcmp (tplname, "video_sink"))
3274     type = GST_PLAY_SINK_TYPE_VIDEO;
3275   else if (!strcmp (tplname, "video_raw_sink"))
3276     type = GST_PLAY_SINK_TYPE_VIDEO_RAW;
3277   else if (!strcmp (tplname, "text_sink"))
3278     type = GST_PLAY_SINK_TYPE_TEXT;
3279   else
3280     goto unknown_template;
3281
3282   pad = gst_play_sink_request_pad (psink, type);
3283   return pad;
3284
3285 unknown_template:
3286   GST_WARNING_OBJECT (element, "Unknown pad template");
3287   return NULL;
3288 }
3289
3290 void
3291 gst_play_sink_release_pad (GstPlaySink * playsink, GstPad * pad)
3292 {
3293   GstPad **res = NULL;
3294   gboolean untarget = TRUE;
3295
3296   GST_DEBUG_OBJECT (playsink, "release pad %" GST_PTR_FORMAT, pad);
3297
3298   GST_PLAY_SINK_LOCK (playsink);
3299   if (pad == playsink->video_pad) {
3300     res = &playsink->video_pad;
3301     g_signal_handlers_disconnect_by_func (playsink->video_pad, caps_notify_cb,
3302         playsink);
3303   } else if (pad == playsink->audio_pad) {
3304     res = &playsink->audio_pad;
3305     g_signal_handlers_disconnect_by_func (playsink->audio_pad, caps_notify_cb,
3306         playsink);
3307   } else if (pad == playsink->text_pad) {
3308     res = &playsink->text_pad;
3309   } else {
3310     /* try to release the given pad anyway, these could be the FLUSHING pads. */
3311     res = &pad;
3312     untarget = FALSE;
3313   }
3314   GST_PLAY_SINK_UNLOCK (playsink);
3315
3316   if (*res) {
3317     GST_DEBUG_OBJECT (playsink, "deactivate pad %" GST_PTR_FORMAT, *res);
3318     gst_pad_set_active (*res, FALSE);
3319     if (untarget) {
3320       GST_DEBUG_OBJECT (playsink, "untargeting pad %" GST_PTR_FORMAT, *res);
3321       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (*res), NULL);
3322     }
3323     GST_DEBUG_OBJECT (playsink, "remove pad %" GST_PTR_FORMAT, *res);
3324     gst_element_remove_pad (GST_ELEMENT_CAST (playsink), *res);
3325     *res = NULL;
3326   }
3327 }
3328
3329 static void
3330 gst_play_sink_release_request_pad (GstElement * element, GstPad * pad)
3331 {
3332   GstPlaySink *psink = GST_PLAY_SINK (element);
3333
3334   gst_play_sink_release_pad (psink, pad);
3335 }
3336
3337 static void
3338 gst_play_sink_handle_message (GstBin * bin, GstMessage * message)
3339 {
3340   GstPlaySink *playsink;
3341
3342   playsink = GST_PLAY_SINK_CAST (bin);
3343
3344   switch (GST_MESSAGE_TYPE (message)) {
3345     case GST_MESSAGE_STEP_DONE:
3346     {
3347       GstFormat format;
3348       guint64 amount;
3349       gdouble rate;
3350       gboolean flush, intermediate, eos;
3351       guint64 duration;
3352
3353       GST_INFO_OBJECT (playsink, "Handling step-done message");
3354       gst_message_parse_step_done (message, &format, &amount, &rate, &flush,
3355           &intermediate, &duration, &eos);
3356
3357       if (format == GST_FORMAT_BUFFERS) {
3358         /* for the buffer format, we align the other streams */
3359         if (playsink->audiochain) {
3360           GstEvent *event;
3361
3362           event =
3363               gst_event_new_step (GST_FORMAT_TIME, duration, rate, flush,
3364               intermediate);
3365
3366           if (!gst_element_send_event (playsink->audiochain->chain.bin, event)) {
3367             GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
3368           }
3369         }
3370       }
3371       GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
3372       break;
3373     }
3374     default:
3375       GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
3376       break;
3377   }
3378 }
3379
3380 /* Send an event to our sinks until one of them works; don't then send to the
3381  * remaining sinks (unlike GstBin)
3382  * Special case: If a text sink is set we need to send the event
3383  * to them in case it's source is different from the a/v stream's source.
3384  */
3385 static gboolean
3386 gst_play_sink_send_event_to_sink (GstPlaySink * playsink, GstEvent * event)
3387 {
3388   gboolean res = TRUE;
3389
3390   if (playsink->textchain && playsink->textchain->sink) {
3391     gst_event_ref (event);
3392     if ((res = gst_element_send_event (playsink->textchain->chain.bin, event))) {
3393       GST_DEBUG_OBJECT (playsink, "Sent event successfully to text sink");
3394     } else {
3395       GST_DEBUG_OBJECT (playsink, "Event failed when sent to text sink");
3396     }
3397   }
3398
3399   if (playsink->videochain) {
3400     gst_event_ref (event);
3401     if ((res = gst_element_send_event (playsink->videochain->chain.bin, event))) {
3402       GST_DEBUG_OBJECT (playsink, "Sent event successfully to video sink");
3403       goto done;
3404     }
3405     GST_DEBUG_OBJECT (playsink, "Event failed when sent to video sink");
3406   }
3407   if (playsink->audiochain) {
3408     gst_event_ref (event);
3409     if ((res = gst_element_send_event (playsink->audiochain->chain.bin, event))) {
3410       GST_DEBUG_OBJECT (playsink, "Sent event successfully to audio sink");
3411       goto done;
3412     }
3413     GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
3414   }
3415
3416 done:
3417   gst_event_unref (event);
3418   return res;
3419 }
3420
3421 /* We only want to send the event to a single sink (overriding GstBin's
3422  * behaviour), but we want to keep GstPipeline's behaviour - wrapping seek
3423  * events appropriately. So, this is a messy duplication of code. */
3424 static gboolean
3425 gst_play_sink_send_event (GstElement * element, GstEvent * event)
3426 {
3427   gboolean res = FALSE;
3428   GstEventType event_type = GST_EVENT_TYPE (event);
3429   GstPlaySink *playsink;
3430
3431   playsink = GST_PLAY_SINK_CAST (element);
3432
3433   switch (event_type) {
3434     case GST_EVENT_SEEK:
3435       GST_DEBUG_OBJECT (element, "Sending event to a sink");
3436       res = gst_play_sink_send_event_to_sink (playsink, event);
3437       break;
3438     case GST_EVENT_STEP:
3439     {
3440       GstFormat format;
3441       guint64 amount;
3442       gdouble rate;
3443       gboolean flush, intermediate;
3444
3445       gst_event_parse_step (event, &format, &amount, &rate, &flush,
3446           &intermediate);
3447
3448       if (format == GST_FORMAT_BUFFERS) {
3449         /* for buffers, we will try to step video frames, for other formats we
3450          * send the step to all sinks */
3451         res = gst_play_sink_send_event_to_sink (playsink, event);
3452       } else {
3453         res =
3454             GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
3455             event);
3456       }
3457       break;
3458     }
3459     default:
3460       res =
3461           GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
3462           event);
3463       break;
3464   }
3465   return res;
3466 }
3467
3468 static GstStateChangeReturn
3469 gst_play_sink_change_state (GstElement * element, GstStateChange transition)
3470 {
3471   GstStateChangeReturn ret;
3472   GstStateChangeReturn bret;
3473
3474   GstPlaySink *playsink;
3475
3476   playsink = GST_PLAY_SINK (element);
3477
3478   switch (transition) {
3479     case GST_STATE_CHANGE_READY_TO_PAUSED:
3480       playsink->need_async_start = TRUE;
3481       /* we want to go async to PAUSED until we managed to configure and add the
3482        * sinks */
3483       do_async_start (playsink);
3484       ret = GST_STATE_CHANGE_ASYNC;
3485       break;
3486     case GST_STATE_CHANGE_PAUSED_TO_READY:
3487       /* unblock all pads here */
3488       GST_PLAY_SINK_LOCK (playsink);
3489       if (playsink->video_pad) {
3490         GstPad *opad =
3491             GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3492                 (playsink->video_pad)));
3493         if (gst_pad_is_blocked (opad)) {
3494           gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3495               gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3496         }
3497         gst_object_unref (opad);
3498         playsink->video_pad_blocked = FALSE;
3499       }
3500
3501       if (playsink->audio_pad) {
3502         GstPad *opad =
3503             GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3504                 (playsink->audio_pad)));
3505
3506         if (gst_pad_is_blocked (opad)) {
3507           gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3508               gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3509         }
3510         gst_object_unref (opad);
3511         playsink->audio_pad_blocked = FALSE;
3512       }
3513
3514       if (playsink->text_pad) {
3515         GstPad *opad =
3516             GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3517                 (playsink->text_pad)));
3518         if (gst_pad_is_blocked (opad)) {
3519           gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3520               gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3521         }
3522         gst_object_unref (opad);
3523         playsink->text_pad_blocked = FALSE;
3524       }
3525       GST_PLAY_SINK_UNLOCK (playsink);
3526       /* fall through */
3527     case GST_STATE_CHANGE_READY_TO_NULL:
3528       if (playsink->audiochain && playsink->audiochain->sink_volume) {
3529         /* remove our links to the mute and volume elements when they were
3530          * provided by a sink */
3531         disconnect_chain (playsink->audiochain, playsink);
3532         playsink->audiochain->volume = NULL;
3533         playsink->audiochain->mute = NULL;
3534       }
3535
3536       if (playsink->audiochain && playsink->audiochain->ts_offset) {
3537         gst_object_unref (playsink->audiochain->ts_offset);
3538         playsink->audiochain->ts_offset = NULL;
3539       }
3540
3541       if (playsink->videochain && playsink->videochain->ts_offset) {
3542         gst_object_unref (playsink->videochain->ts_offset);
3543         playsink->videochain->ts_offset = NULL;
3544       }
3545       ret = GST_STATE_CHANGE_SUCCESS;
3546       break;
3547     default:
3548       /* all other state changes return SUCCESS by default, this value can be
3549        * overridden by the result of the children */
3550       ret = GST_STATE_CHANGE_SUCCESS;
3551       break;
3552   }
3553
3554   /* do the state change of the children */
3555   bret =
3556       GST_ELEMENT_CLASS (gst_play_sink_parent_class)->change_state (element,
3557       transition);
3558   /* now look at the result of our children and adjust the return value */
3559   switch (bret) {
3560     case GST_STATE_CHANGE_FAILURE:
3561       /* failure, we stop */
3562       goto activate_failed;
3563     case GST_STATE_CHANGE_NO_PREROLL:
3564       /* some child returned NO_PREROLL. This is strange but we never know. We
3565        * commit our async state change (if any) and return the NO_PREROLL */
3566       do_async_done (playsink);
3567       ret = bret;
3568       break;
3569     case GST_STATE_CHANGE_ASYNC:
3570       /* some child was async, return this */
3571       ret = bret;
3572       break;
3573     default:
3574       /* return our previously configured return value */
3575       break;
3576   }
3577
3578   switch (transition) {
3579     case GST_STATE_CHANGE_READY_TO_PAUSED:
3580       break;
3581     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
3582       /* FIXME Release audio device when we implement that */
3583       playsink->need_async_start = TRUE;
3584       break;
3585     case GST_STATE_CHANGE_PAUSED_TO_READY:{
3586       if (playsink->video_sinkpad_stream_synchronizer) {
3587         gst_element_release_request_pad (GST_ELEMENT_CAST
3588             (playsink->stream_synchronizer),
3589             playsink->video_sinkpad_stream_synchronizer);
3590         gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3591         playsink->video_sinkpad_stream_synchronizer = NULL;
3592         gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3593         playsink->video_srcpad_stream_synchronizer = NULL;
3594       }
3595       if (playsink->audio_sinkpad_stream_synchronizer) {
3596         gst_element_release_request_pad (GST_ELEMENT_CAST
3597             (playsink->stream_synchronizer),
3598             playsink->audio_sinkpad_stream_synchronizer);
3599         gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
3600         playsink->audio_sinkpad_stream_synchronizer = NULL;
3601         gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
3602         playsink->audio_srcpad_stream_synchronizer = NULL;
3603       }
3604       if (playsink->text_sinkpad_stream_synchronizer) {
3605         gst_element_release_request_pad (GST_ELEMENT_CAST
3606             (playsink->stream_synchronizer),
3607             playsink->text_sinkpad_stream_synchronizer);
3608         gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
3609         playsink->text_sinkpad_stream_synchronizer = NULL;
3610         gst_object_unref (playsink->text_srcpad_stream_synchronizer);
3611         playsink->text_srcpad_stream_synchronizer = NULL;
3612       }
3613     }
3614       /* fall through */
3615     case GST_STATE_CHANGE_READY_TO_NULL:
3616       /* remove sinks we added */
3617       if (playsink->videodeinterlacechain) {
3618         activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
3619             FALSE);
3620         add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3621       }
3622       if (playsink->videochain) {
3623         activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3624         add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3625       }
3626       if (playsink->audiochain) {
3627         activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3628         add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3629       }
3630       if (playsink->vischain) {
3631         activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3632         add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3633       }
3634       if (playsink->textchain) {
3635         activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3636         add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3637       }
3638       do_async_done (playsink);
3639       /* when going to READY, keep elements around as long as possible,
3640        * so they may be re-used faster next time/url around.
3641        * when really going to NULL, clean up everything completely. */
3642       if (transition == GST_STATE_CHANGE_READY_TO_NULL) {
3643
3644         /* Unparent the sinks to allow reuse */
3645         if (playsink->videochain && playsink->videochain->sink)
3646           gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
3647               playsink->videochain->sink);
3648         if (playsink->audiochain && playsink->audiochain->sink)
3649           gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
3650               playsink->audiochain->sink);
3651         if (playsink->textchain && playsink->textchain->sink)
3652           gst_bin_remove (GST_BIN_CAST (playsink->textchain->chain.bin),
3653               playsink->textchain->sink);
3654
3655         if (playsink->audio_sink != NULL)
3656           gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
3657         if (playsink->video_sink != NULL)
3658           gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
3659         if (playsink->visualisation != NULL)
3660           gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
3661         if (playsink->text_sink != NULL)
3662           gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
3663
3664         free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
3665         playsink->videodeinterlacechain = NULL;
3666         free_chain ((GstPlayChain *) playsink->videochain);
3667         playsink->videochain = NULL;
3668         free_chain ((GstPlayChain *) playsink->audiochain);
3669         playsink->audiochain = NULL;
3670         free_chain ((GstPlayChain *) playsink->vischain);
3671         playsink->vischain = NULL;
3672         free_chain ((GstPlayChain *) playsink->textchain);
3673         playsink->textchain = NULL;
3674       }
3675       break;
3676     default:
3677       break;
3678   }
3679   return ret;
3680
3681   /* ERRORS */
3682 activate_failed:
3683   {
3684     GST_DEBUG_OBJECT (element,
3685         "element failed to change states -- activation problem?");
3686     return GST_STATE_CHANGE_FAILURE;
3687   }
3688 }
3689
3690 static void
3691 gst_play_sink_set_property (GObject * object, guint prop_id,
3692     const GValue * value, GParamSpec * spec)
3693 {
3694   GstPlaySink *playsink = GST_PLAY_SINK (object);
3695
3696   switch (prop_id) {
3697     case PROP_FLAGS:
3698       gst_play_sink_set_flags (playsink, g_value_get_flags (value));
3699       break;
3700     case PROP_VOLUME:
3701       gst_play_sink_set_volume (playsink, g_value_get_double (value));
3702       break;
3703     case PROP_MUTE:
3704       gst_play_sink_set_mute (playsink, g_value_get_boolean (value));
3705       break;
3706     case PROP_FONT_DESC:
3707       gst_play_sink_set_font_desc (playsink, g_value_get_string (value));
3708       break;
3709     case PROP_SUBTITLE_ENCODING:
3710       gst_play_sink_set_subtitle_encoding (playsink,
3711           g_value_get_string (value));
3712       break;
3713     case PROP_VIS_PLUGIN:
3714       gst_play_sink_set_vis_plugin (playsink, g_value_get_object (value));
3715       break;
3716     case PROP_AV_OFFSET:
3717       gst_play_sink_set_av_offset (playsink, g_value_get_int64 (value));
3718       break;
3719     case PROP_VIDEO_SINK:
3720       gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_VIDEO,
3721           g_value_get_object (value));
3722       break;
3723     case PROP_AUDIO_SINK:
3724       gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_AUDIO,
3725           g_value_get_object (value));
3726       break;
3727     case PROP_TEXT_SINK:
3728       gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_TEXT,
3729           g_value_get_object (value));
3730       break;
3731     default:
3732       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
3733       break;
3734   }
3735 }
3736
3737 static void
3738 gst_play_sink_get_property (GObject * object, guint prop_id,
3739     GValue * value, GParamSpec * spec)
3740 {
3741   GstPlaySink *playsink = GST_PLAY_SINK (object);
3742
3743   switch (prop_id) {
3744     case PROP_FLAGS:
3745       g_value_set_flags (value, gst_play_sink_get_flags (playsink));
3746       break;
3747     case PROP_VOLUME:
3748       g_value_set_double (value, gst_play_sink_get_volume (playsink));
3749       break;
3750     case PROP_MUTE:
3751       g_value_set_boolean (value, gst_play_sink_get_mute (playsink));
3752       break;
3753     case PROP_FONT_DESC:
3754       g_value_take_string (value, gst_play_sink_get_font_desc (playsink));
3755       break;
3756     case PROP_SUBTITLE_ENCODING:
3757       g_value_take_string (value,
3758           gst_play_sink_get_subtitle_encoding (playsink));
3759       break;
3760     case PROP_VIS_PLUGIN:
3761       g_value_take_object (value, gst_play_sink_get_vis_plugin (playsink));
3762       break;
3763     case PROP_FRAME:
3764       gst_value_take_buffer (value, gst_play_sink_get_last_frame (playsink));
3765       break;
3766     case PROP_AV_OFFSET:
3767       g_value_set_int64 (value, gst_play_sink_get_av_offset (playsink));
3768       break;
3769     case PROP_VIDEO_SINK:
3770       g_value_take_object (value, gst_play_sink_get_sink (playsink,
3771               GST_PLAY_SINK_TYPE_VIDEO));
3772       break;
3773     case PROP_AUDIO_SINK:
3774       g_value_take_object (value, gst_play_sink_get_sink (playsink,
3775               GST_PLAY_SINK_TYPE_AUDIO));
3776       break;
3777     case PROP_TEXT_SINK:
3778       g_value_take_object (value, gst_play_sink_get_sink (playsink,
3779               GST_PLAY_SINK_TYPE_TEXT));
3780       break;
3781     default:
3782       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
3783       break;
3784   }
3785 }
3786
3787 gboolean
3788 gst_play_sink_plugin_init (GstPlugin * plugin)
3789 {
3790   GST_DEBUG_CATEGORY_INIT (gst_play_sink_debug, "playsink", 0, "play bin");
3791
3792   return gst_element_register (plugin, "playsink", GST_RANK_NONE,
3793       GST_TYPE_PLAY_SINK);
3794 }