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