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