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