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