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