d02ec60235e1801892c8a087c4d812c35e90773e
[profile/ivi/gst-plugins-base.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 (chain->sink && (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           if (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           } else {
1611             gst_bin_remove (bin, chain->sink);
1612             gst_bin_remove (bin, chain->overlay);
1613             chain->sink = NULL;
1614             chain->overlay = NULL;
1615             gst_object_unref (videosinkpad);
1616             videosinkpad = NULL;
1617           }
1618         }
1619       }
1620     }
1621   }
1622
1623   if (videosinkpad == NULL) {
1624     /* if we still don't have a videosink, we don't have an overlay. the only
1625      * thing we can do is insert an identity and ghost the src
1626      * and sink pads. */
1627     chain->identity = gst_element_factory_make ("identity", "tidentity");
1628     if (chain->identity == NULL) {
1629       post_missing_element_message (playsink, "identity");
1630       GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1631           (_("Missing element '%s' - check your GStreamer installation."),
1632               "identity"), (NULL));
1633     } else {
1634       g_object_set (chain->identity, "signal-handoffs", FALSE, NULL);
1635       g_object_set (chain->identity, "silent", TRUE, NULL);
1636       gst_bin_add (bin, chain->identity);
1637       srcpad = gst_element_get_static_pad (chain->identity, "src");
1638       videosinkpad = gst_element_get_static_pad (chain->identity, "sink");
1639     }
1640   }
1641
1642   /* expose the ghostpads */
1643   if (videosinkpad) {
1644     chain->videosinkpad = gst_ghost_pad_new ("sink", videosinkpad);
1645     gst_object_unref (videosinkpad);
1646     gst_element_add_pad (chain->chain.bin, chain->videosinkpad);
1647   }
1648   if (textsinkpad) {
1649     chain->textsinkpad = gst_ghost_pad_new ("text_sink", textsinkpad);
1650     gst_object_unref (textsinkpad);
1651     gst_element_add_pad (chain->chain.bin, chain->textsinkpad);
1652   }
1653   if (srcpad) {
1654     chain->srcpad = gst_ghost_pad_new ("src", srcpad);
1655     gst_object_unref (srcpad);
1656     gst_element_add_pad (chain->chain.bin, chain->srcpad);
1657   }
1658
1659   return chain;
1660 }
1661
1662 static void
1663 notify_volume_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
1664 {
1665   gdouble vol;
1666
1667   g_object_get (object, "volume", &vol, NULL);
1668   playsink->volume = vol;
1669
1670   g_object_notify (G_OBJECT (playsink), "volume");
1671 }
1672
1673 static void
1674 notify_mute_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
1675 {
1676   gboolean mute;
1677
1678   g_object_get (object, "mute", &mute, NULL);
1679   playsink->mute = mute;
1680
1681   g_object_notify (G_OBJECT (playsink), "mute");
1682 }
1683
1684 /* make the chain that contains the elements needed to perform
1685  * audio playback.
1686  *
1687  * We add a tee as the first element so that we can link the visualisation chain
1688  * to it when requested.
1689  *
1690  *  +-------------------------------------------------------------+
1691  *  | abin                                                        |
1692  *  |      +---------+   +----------+   +---------+   +---------+ |
1693  *  |      |audioconv|   |audioscale|   | volume  |   |audiosink| |
1694  *  |   +-srck      src-sink       src-sink      src-sink       | |
1695  *  |   |  +---------+   +----------+   +---------+   +---------+ |
1696  * sink-+                                                         |
1697  *  +-------------------------------------------------------------+
1698  */
1699 static GstPlayAudioChain *
1700 gen_audio_chain (GstPlaySink * playsink, gboolean raw)
1701 {
1702   GstPlayAudioChain *chain;
1703   GstBin *bin;
1704   gboolean have_volume;
1705   GstPad *pad;
1706   GstElement *head, *prev, *elem = NULL;
1707
1708   chain = g_new0 (GstPlayAudioChain, 1);
1709   chain->chain.playsink = playsink;
1710   chain->chain.raw = raw;
1711
1712   GST_DEBUG_OBJECT (playsink, "making audio chain %p", chain);
1713
1714   if (playsink->audio_sink) {
1715     GST_DEBUG_OBJECT (playsink, "trying configured audiosink %" GST_PTR_FORMAT,
1716         playsink->audio_sink);
1717     chain->sink = try_element (playsink, playsink->audio_sink, FALSE);
1718   } else {
1719     /* only try fallback if no specific sink was chosen */
1720     if (chain->sink == NULL) {
1721       GST_DEBUG_OBJECT (playsink, "trying autoaudiosink");
1722       elem = gst_element_factory_make ("autoaudiosink", "audiosink");
1723       chain->sink = try_element (playsink, elem, TRUE);
1724     }
1725     if (chain->sink == NULL) {
1726       /* if default sink from config.h is different then try it too */
1727       if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1728         GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_AUDIOSINK);
1729         elem = gst_element_factory_make (DEFAULT_AUDIOSINK, "audiosink");
1730         chain->sink = try_element (playsink, elem, TRUE);
1731       }
1732     }
1733     if (chain->sink)
1734       playsink->audio_sink = gst_object_ref (chain->sink);
1735   }
1736   if (chain->sink == NULL)
1737     goto no_sinks;
1738
1739   chain->chain.bin = gst_bin_new ("abin");
1740   bin = GST_BIN_CAST (chain->chain.bin);
1741   gst_object_ref_sink (bin);
1742   gst_bin_add (bin, chain->sink);
1743
1744   /* we have to add a queue when we need to decouple for the video sink in
1745    * visualisations and for streamsynchronizer */
1746   GST_DEBUG_OBJECT (playsink, "adding audio queue");
1747   chain->queue = gst_element_factory_make ("queue", "aqueue");
1748   if (chain->queue == NULL) {
1749     post_missing_element_message (playsink, "queue");
1750     GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1751         (_("Missing element '%s' - check your GStreamer installation."),
1752             "queue"), ("audio playback and visualizations might not work"));
1753     head = chain->sink;
1754     prev = NULL;
1755   } else {
1756     g_object_set (chain->queue, "silent", TRUE, NULL);
1757     gst_bin_add (bin, chain->queue);
1758     prev = head = chain->queue;
1759   }
1760
1761   /* find ts-offset element */
1762   gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1763       gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1764           G_TYPE_INT64));
1765
1766   /* check if the sink, or something within the sink, has the volume property.
1767    * If it does we don't need to add a volume element.  */
1768   elem =
1769       gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
1770       G_TYPE_DOUBLE);
1771   if (elem) {
1772     chain->volume = elem;
1773
1774     g_signal_connect (chain->volume, "notify::volume",
1775         G_CALLBACK (notify_volume_cb), playsink);
1776
1777     GST_DEBUG_OBJECT (playsink, "the sink has a volume property");
1778     have_volume = TRUE;
1779     chain->sink_volume = TRUE;
1780     /* if the sink also has a mute property we can use this as well. We'll only
1781      * use the mute property if there is a volume property. We can simulate the
1782      * mute with the volume otherwise. */
1783     chain->mute =
1784         gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
1785         G_TYPE_BOOLEAN);
1786     if (chain->mute) {
1787       GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
1788       g_signal_connect (chain->mute, "notify::mute",
1789           G_CALLBACK (notify_mute_cb), playsink);
1790     }
1791     /* use the sink to control the volume and mute */
1792     if (playsink->volume_changed) {
1793       g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
1794       playsink->volume_changed = FALSE;
1795     }
1796     if (playsink->mute_changed) {
1797       if (chain->mute) {
1798         g_object_set (chain->mute, "mute", playsink->mute, NULL);
1799       } else {
1800         if (playsink->mute)
1801           g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
1802       }
1803       playsink->mute_changed = FALSE;
1804     }
1805   } else {
1806     /* no volume, we need to add a volume element when we can */
1807     GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
1808     have_volume = FALSE;
1809     chain->sink_volume = FALSE;
1810   }
1811
1812   if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO) || (!have_volume
1813           && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
1814     gboolean use_converters = !(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO);
1815     gboolean use_volume =
1816         !have_volume && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME;
1817     GST_DEBUG_OBJECT (playsink,
1818         "creating audioconvert with use-converters %d, use-volume %d",
1819         use_converters, use_volume);
1820     chain->conv =
1821         g_object_new (GST_TYPE_PLAY_SINK_AUDIO_CONVERT, "name", "aconv",
1822         "use-converters", use_converters, "use-volume", use_volume, NULL);
1823     gst_bin_add (bin, chain->conv);
1824     if (prev) {
1825       if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
1826               GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1827         goto link_failed;
1828     } else {
1829       head = chain->conv;
1830     }
1831     prev = chain->conv;
1832
1833     if (!have_volume && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME) {
1834       GstPlaySinkAudioConvert *conv =
1835           GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
1836
1837       if (conv->volume) {
1838         chain->volume = conv->volume;
1839         have_volume = TRUE;
1840
1841         g_signal_connect (chain->volume, "notify::volume",
1842             G_CALLBACK (notify_volume_cb), playsink);
1843
1844         /* volume also has the mute property */
1845         chain->mute = chain->volume;
1846         g_signal_connect (chain->mute, "notify::mute",
1847             G_CALLBACK (notify_mute_cb), playsink);
1848
1849         /* configure with the latest volume and mute */
1850         g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume,
1851             NULL);
1852         g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
1853       }
1854     }
1855   }
1856
1857   if (prev) {
1858     /* we only have to link to the previous element if we have something in
1859      * front of the sink */
1860     GST_DEBUG_OBJECT (playsink, "linking to sink");
1861     if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
1862             GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1863       goto link_failed;
1864   }
1865
1866   /* post a warning if we have no way to configure the volume */
1867   if (!have_volume) {
1868     GST_ELEMENT_WARNING (playsink, STREAM, NOT_IMPLEMENTED,
1869         (_("No volume control found")), ("Volume/mute is not available"));
1870   }
1871
1872   /* and ghost the sinkpad of the headmost element */
1873   GST_DEBUG_OBJECT (playsink, "ghosting sink pad");
1874   pad = gst_element_get_static_pad (head, "sink");
1875   chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1876   gst_object_unref (pad);
1877   gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1878
1879   return chain;
1880
1881   /* ERRORS */
1882 no_sinks:
1883   {
1884     if (!elem && !playsink->audio_sink) {
1885       post_missing_element_message (playsink, "autoaudiosink");
1886       if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1887         post_missing_element_message (playsink, DEFAULT_AUDIOSINK);
1888         GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1889             (_("Both autoaudiosink and %s elements are missing."),
1890                 DEFAULT_AUDIOSINK), (NULL));
1891       } else {
1892         GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1893             (_("The autoaudiosink element is missing.")), (NULL));
1894       }
1895     } else {
1896       if (playsink->audio_sink) {
1897         GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1898             (_("Configured audiosink %s is not working."),
1899                 GST_ELEMENT_NAME (playsink->audio_sink)), (NULL));
1900       } else if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1901         GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1902             (_("Both autoaudiosink and %s elements are not working."),
1903                 DEFAULT_AUDIOSINK), (NULL));
1904       } else {
1905         GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1906             (_("The autoaudiosink element is not working.")), (NULL));
1907       }
1908     }
1909     free_chain ((GstPlayChain *) chain);
1910     return NULL;
1911   }
1912 link_failed:
1913   {
1914     GST_ELEMENT_ERROR (playsink, CORE, PAD,
1915         (NULL), ("Failed to configure the audio sink."));
1916     /* checking sink made it READY */
1917     gst_element_set_state (chain->sink, GST_STATE_NULL);
1918     /* Remove chain from the bin to allow reuse later */
1919     gst_bin_remove (bin, chain->sink);
1920     free_chain ((GstPlayChain *) chain);
1921     return NULL;
1922   }
1923 }
1924
1925 static gboolean
1926 setup_audio_chain (GstPlaySink * playsink, gboolean raw)
1927 {
1928   GstElement *elem;
1929   GstPlayAudioChain *chain;
1930   GstStateChangeReturn ret;
1931
1932   chain = playsink->audiochain;
1933
1934   chain->chain.raw = raw;
1935
1936   /* if the chain was active we don't do anything */
1937   if (GST_PLAY_CHAIN (chain)->activated == TRUE)
1938     return TRUE;
1939
1940   /* try to set the sink element to READY again */
1941   ret = gst_element_set_state (chain->sink, GST_STATE_READY);
1942   if (ret == GST_STATE_CHANGE_FAILURE)
1943     return FALSE;
1944
1945   /* find ts-offset element */
1946   gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1947       gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1948           G_TYPE_INT64));
1949
1950   /* check if the sink, or something within the sink, has the volume property.
1951    * If it does we don't need to add a volume element.  */
1952   elem =
1953       gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
1954       G_TYPE_DOUBLE);
1955   if (elem) {
1956     chain->volume = elem;
1957
1958     if (playsink->volume_changed) {
1959       GST_DEBUG_OBJECT (playsink, "the sink has a volume property, setting %f",
1960           playsink->volume);
1961       /* use the sink to control the volume */
1962       g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
1963       playsink->volume_changed = FALSE;
1964     }
1965
1966     g_signal_connect (chain->volume, "notify::volume",
1967         G_CALLBACK (notify_volume_cb), playsink);
1968     /* if the sink also has a mute property we can use this as well. We'll only
1969      * use the mute property if there is a volume property. We can simulate the
1970      * mute with the volume otherwise. */
1971     chain->mute =
1972         gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
1973         G_TYPE_BOOLEAN);
1974     if (chain->mute) {
1975       GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
1976       g_signal_connect (chain->mute, "notify::mute",
1977           G_CALLBACK (notify_mute_cb), playsink);
1978     }
1979
1980     g_object_set (chain->conv, "use-volume", FALSE, NULL);
1981   } else {
1982     GstPlaySinkAudioConvert *conv =
1983         GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
1984
1985     /* no volume, we need to add a volume element when we can */
1986     g_object_set (chain->conv, "use-volume", TRUE, NULL);
1987     GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
1988
1989     /* Disconnect signals */
1990     disconnect_chain (chain, playsink);
1991
1992     if (conv->volume) {
1993       chain->volume = conv->volume;
1994       chain->mute = chain->volume;
1995
1996       g_signal_connect (chain->volume, "notify::volume",
1997           G_CALLBACK (notify_volume_cb), playsink);
1998
1999       g_signal_connect (chain->mute, "notify::mute",
2000           G_CALLBACK (notify_mute_cb), playsink);
2001
2002       /* configure with the latest volume and mute */
2003       g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2004       g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
2005     }
2006
2007     GST_DEBUG_OBJECT (playsink, "reusing existing volume element");
2008   }
2009   return TRUE;
2010 }
2011
2012 /*
2013  *  +-------------------------------------------------------------------+
2014  *  | visbin                                                            |
2015  *  |      +----------+   +------------+   +----------+   +-------+     |
2016  *  |      | visqueue |   | audioconv  |   | audiores |   |  vis  |     |
2017  *  |   +-sink       src-sink + samp  src-sink       src-sink    src-+  |
2018  *  |   |  +----------+   +------------+   +----------+   +-------+  |  |
2019  * sink-+                                                            +-src
2020  *  +-------------------------------------------------------------------+
2021  *
2022  */
2023 static GstPlayVisChain *
2024 gen_vis_chain (GstPlaySink * playsink)
2025 {
2026   GstPlayVisChain *chain;
2027   GstBin *bin;
2028   gboolean res;
2029   GstPad *pad;
2030   GstElement *elem;
2031
2032   chain = g_new0 (GstPlayVisChain, 1);
2033   chain->chain.playsink = playsink;
2034
2035   GST_DEBUG_OBJECT (playsink, "making vis chain %p", chain);
2036
2037   chain->chain.bin = gst_bin_new ("visbin");
2038   bin = GST_BIN_CAST (chain->chain.bin);
2039   gst_object_ref_sink (bin);
2040
2041   /* we're queuing raw audio here, we can remove this queue when we can disable
2042    * async behaviour in the video sink. */
2043   chain->queue = gst_element_factory_make ("queue", "visqueue");
2044   if (chain->queue == NULL)
2045     goto no_queue;
2046   g_object_set (chain->queue, "silent", TRUE, NULL);
2047   gst_bin_add (bin, chain->queue);
2048
2049   chain->conv = gst_element_factory_make ("audioconvert", "aconv");
2050   if (chain->conv == NULL)
2051     goto no_audioconvert;
2052   gst_bin_add (bin, chain->conv);
2053
2054   chain->resample = gst_element_factory_make ("audioresample", "aresample");
2055   if (chain->resample == NULL)
2056     goto no_audioresample;
2057   gst_bin_add (bin, chain->resample);
2058
2059   /* this pad will be used for blocking the dataflow and switching the vis
2060    * plugin */
2061   chain->blockpad = gst_element_get_static_pad (chain->resample, "src");
2062
2063   if (playsink->visualisation) {
2064     GST_DEBUG_OBJECT (playsink, "trying configure vis");
2065     chain->vis = try_element (playsink, playsink->visualisation, FALSE);
2066   }
2067   if (chain->vis == NULL) {
2068     GST_DEBUG_OBJECT (playsink, "trying goom");
2069     elem = gst_element_factory_make ("goom", "vis");
2070     chain->vis = try_element (playsink, elem, TRUE);
2071   }
2072   if (chain->vis == NULL)
2073     goto no_goom;
2074
2075   gst_bin_add (bin, chain->vis);
2076
2077   res = gst_element_link_pads_full (chain->queue, "src", chain->conv, "sink",
2078       GST_PAD_LINK_CHECK_NOTHING);
2079   res &=
2080       gst_element_link_pads_full (chain->conv, "src", chain->resample, "sink",
2081       GST_PAD_LINK_CHECK_NOTHING);
2082   res &=
2083       gst_element_link_pads_full (chain->resample, "src", chain->vis, "sink",
2084       GST_PAD_LINK_CHECK_NOTHING);
2085   if (!res)
2086     goto link_failed;
2087
2088   chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
2089   chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
2090
2091   pad = gst_element_get_static_pad (chain->queue, "sink");
2092   chain->sinkpad = gst_ghost_pad_new ("sink", pad);
2093   gst_object_unref (pad);
2094   gst_element_add_pad (chain->chain.bin, chain->sinkpad);
2095
2096   chain->srcpad = gst_ghost_pad_new ("src", chain->vissrcpad);
2097   gst_element_add_pad (chain->chain.bin, chain->srcpad);
2098
2099   return chain;
2100
2101   /* ERRORS */
2102 no_queue:
2103   {
2104     post_missing_element_message (playsink, "queue");
2105     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2106         (_("Missing element '%s' - check your GStreamer installation."),
2107             "queue"), (NULL));
2108     free_chain ((GstPlayChain *) chain);
2109     return NULL;
2110   }
2111 no_audioconvert:
2112   {
2113     post_missing_element_message (playsink, "audioconvert");
2114     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2115         (_("Missing element '%s' - check your GStreamer installation."),
2116             "audioconvert"), ("possibly a liboil version mismatch?"));
2117     free_chain ((GstPlayChain *) chain);
2118     return NULL;
2119   }
2120 no_audioresample:
2121   {
2122     post_missing_element_message (playsink, "audioresample");
2123     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2124         (_("Missing element '%s' - check your GStreamer installation."),
2125             "audioresample"), (NULL));
2126     free_chain ((GstPlayChain *) chain);
2127     return NULL;
2128   }
2129 no_goom:
2130   {
2131     post_missing_element_message (playsink, "goom");
2132     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2133         (_("Missing element '%s' - check your GStreamer installation."),
2134             "goom"), (NULL));
2135     free_chain ((GstPlayChain *) chain);
2136     return NULL;
2137   }
2138 link_failed:
2139   {
2140     GST_ELEMENT_ERROR (playsink, CORE, PAD,
2141         (NULL), ("Failed to configure the visualisation element."));
2142     /* element made it to READY */
2143     gst_element_set_state (chain->vis, GST_STATE_NULL);
2144     free_chain ((GstPlayChain *) chain);
2145     return NULL;
2146   }
2147 }
2148
2149 /* this function is called when all the request pads are requested and when we
2150  * have to construct the final pipeline. Based on the flags we construct the
2151  * final output pipelines.
2152  */
2153 gboolean
2154 gst_play_sink_reconfigure (GstPlaySink * playsink)
2155 {
2156   GstPlayFlags flags;
2157   gboolean need_audio, need_video, need_deinterlace, need_vis, need_text;
2158
2159   GST_DEBUG_OBJECT (playsink, "reconfiguring");
2160
2161   /* assume we need nothing */
2162   need_audio = need_video = need_deinterlace = need_vis = need_text = FALSE;
2163
2164   GST_PLAY_SINK_LOCK (playsink);
2165   GST_OBJECT_LOCK (playsink);
2166   /* get flags, there are protected with the object lock */
2167   flags = playsink->flags;
2168   GST_OBJECT_UNLOCK (playsink);
2169
2170   /* figure out which components we need */
2171   if (flags & GST_PLAY_FLAG_TEXT && playsink->text_pad) {
2172     /* we have subtitles and we are requested to show it */
2173     need_text = TRUE;
2174   }
2175
2176   if (((flags & GST_PLAY_FLAG_VIDEO)
2177           || (flags & GST_PLAY_FLAG_NATIVE_VIDEO)) && playsink->video_pad) {
2178     /* we have video and we are requested to show it */
2179     need_video = TRUE;
2180
2181     /* we only deinterlace if native video is not requested and
2182      * we have raw video */
2183     if ((flags & GST_PLAY_FLAG_DEINTERLACE)
2184         && !(flags & GST_PLAY_FLAG_NATIVE_VIDEO) && playsink->video_pad_raw)
2185       need_deinterlace = TRUE;
2186   }
2187
2188   if (playsink->audio_pad) {
2189     if ((flags & GST_PLAY_FLAG_AUDIO) || (flags & GST_PLAY_FLAG_NATIVE_AUDIO)) {
2190       need_audio = TRUE;
2191     }
2192     if (playsink->audio_pad_raw) {
2193       /* only can do vis with raw uncompressed audio */
2194       if (flags & GST_PLAY_FLAG_VIS && !need_video) {
2195         /* also add video when we add visualisation */
2196         need_video = TRUE;
2197         need_vis = TRUE;
2198       }
2199     }
2200   }
2201
2202   /* we have a text_pad and we need text rendering, in this case we need a
2203    * video_pad to combine the video with the text or visualizations */
2204   if (need_text && !need_video) {
2205     if (playsink->video_pad) {
2206       need_video = TRUE;
2207     } else if (need_audio) {
2208       GST_ELEMENT_WARNING (playsink, STREAM, FORMAT,
2209           (_("Can't play a text file without video or visualizations.")),
2210           ("Have text pad but no video pad or visualizations"));
2211       need_text = FALSE;
2212     } else {
2213       GST_ELEMENT_ERROR (playsink, STREAM, FORMAT,
2214           (_("Can't play a text file without video or visualizations.")),
2215           ("Have text pad but no video pad or visualizations"));
2216       GST_PLAY_SINK_UNLOCK (playsink);
2217       return FALSE;
2218     }
2219   }
2220
2221   GST_DEBUG_OBJECT (playsink, "audio:%d, video:%d, vis:%d, text:%d", need_audio,
2222       need_video, need_vis, need_text);
2223
2224   /* set up video pipeline */
2225   if (need_video) {
2226     gboolean raw, async;
2227
2228     /* we need a raw sink when we do vis or when we have a raw pad */
2229     raw = need_vis ? TRUE : playsink->video_pad_raw;
2230     /* we try to set the sink async=FALSE when we need vis, this way we can
2231      * avoid a queue in the audio chain. */
2232     async = !need_vis;
2233
2234     GST_DEBUG_OBJECT (playsink, "adding video, raw %d",
2235         playsink->video_pad_raw);
2236
2237     if (playsink->videochain) {
2238       /* try to reactivate the chain */
2239       if (!setup_video_chain (playsink, raw, async)) {
2240         if (playsink->video_sinkpad_stream_synchronizer) {
2241           gst_element_release_request_pad (GST_ELEMENT_CAST
2242               (playsink->stream_synchronizer),
2243               playsink->video_sinkpad_stream_synchronizer);
2244           gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2245           playsink->video_sinkpad_stream_synchronizer = NULL;
2246           gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2247           playsink->video_srcpad_stream_synchronizer = NULL;
2248         }
2249
2250         add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2251
2252         /* Remove the sink from the bin to keep its state
2253          * and unparent it to allow reuse */
2254         if (playsink->videochain->sink)
2255           gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
2256               playsink->videochain->sink);
2257
2258         activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2259         free_chain ((GstPlayChain *) playsink->videochain);
2260         playsink->videochain = NULL;
2261       }
2262     }
2263
2264     if (!playsink->videochain)
2265       playsink->videochain = gen_video_chain (playsink, raw, async);
2266     if (!playsink->videochain)
2267       goto no_chain;
2268
2269     if (!playsink->video_sinkpad_stream_synchronizer) {
2270       GstIterator *it;
2271
2272       playsink->video_sinkpad_stream_synchronizer =
2273           gst_element_get_request_pad (GST_ELEMENT_CAST
2274           (playsink->stream_synchronizer), "sink_%d");
2275       it = gst_pad_iterate_internal_links
2276           (playsink->video_sinkpad_stream_synchronizer);
2277       g_assert (it);
2278       gst_iterator_next (it,
2279           (gpointer *) & playsink->video_srcpad_stream_synchronizer);
2280       g_assert (playsink->video_srcpad_stream_synchronizer);
2281       gst_iterator_free (it);
2282     }
2283
2284     if (playsink->video_pad)
2285       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad),
2286           playsink->video_sinkpad_stream_synchronizer);
2287
2288     if (need_deinterlace) {
2289       if (!playsink->videodeinterlacechain)
2290         playsink->videodeinterlacechain =
2291             gen_video_deinterlace_chain (playsink);
2292       if (!playsink->videodeinterlacechain)
2293         goto no_chain;
2294
2295       GST_DEBUG_OBJECT (playsink, "adding video deinterlace chain");
2296
2297       GST_DEBUG_OBJECT (playsink, "setting up deinterlacing chain");
2298
2299       add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
2300       activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
2301
2302       gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2303           playsink->videodeinterlacechain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2304     } else {
2305       if (playsink->videodeinterlacechain) {
2306         add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2307         activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
2308             FALSE);
2309       }
2310     }
2311
2312     GST_DEBUG_OBJECT (playsink, "adding video chain");
2313     add_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
2314     activate_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
2315     /* if we are not part of vis or subtitles, set the ghostpad target */
2316     if (!need_vis && !need_text && (!playsink->textchain
2317             || !playsink->text_pad)) {
2318       GST_DEBUG_OBJECT (playsink, "ghosting video sinkpad");
2319       if (need_deinterlace)
2320         gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
2321             playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2322       else
2323         gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2324             playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2325     }
2326   } else {
2327     GST_DEBUG_OBJECT (playsink, "no video needed");
2328     if (playsink->videochain) {
2329       GST_DEBUG_OBJECT (playsink, "removing video chain");
2330       if (playsink->vischain) {
2331         GstPad *srcpad;
2332
2333         GST_DEBUG_OBJECT (playsink, "unlinking vis chain");
2334
2335         /* also had visualisation, release the tee srcpad before we then
2336          * unlink the video from it */
2337         if (playsink->audio_tee_vissrc) {
2338           gst_element_release_request_pad (playsink->audio_tee,
2339               playsink->audio_tee_vissrc);
2340           gst_object_unref (playsink->audio_tee_vissrc);
2341           playsink->audio_tee_vissrc = NULL;
2342         }
2343         srcpad =
2344             gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2345         gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
2346       }
2347
2348       if (playsink->video_sinkpad_stream_synchronizer) {
2349         gst_element_release_request_pad (GST_ELEMENT_CAST
2350             (playsink->stream_synchronizer),
2351             playsink->video_sinkpad_stream_synchronizer);
2352         gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2353         playsink->video_sinkpad_stream_synchronizer = NULL;
2354         gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2355         playsink->video_srcpad_stream_synchronizer = NULL;
2356       }
2357
2358       add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2359       activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2360       if (playsink->videochain->ts_offset)
2361         gst_object_unref (playsink->videochain->ts_offset);
2362       playsink->videochain->ts_offset = NULL;
2363     }
2364
2365     if (playsink->videodeinterlacechain) {
2366       add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2367       activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2368     }
2369
2370     if (playsink->video_pad)
2371       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
2372   }
2373
2374   if (need_audio) {
2375     gboolean raw;
2376
2377     GST_DEBUG_OBJECT (playsink, "adding audio");
2378
2379     /* get a raw sink if we are asked for a raw pad */
2380     raw = playsink->audio_pad_raw;
2381
2382     if (playsink->audiochain) {
2383       /* try to reactivate the chain */
2384       if (!setup_audio_chain (playsink, raw)) {
2385         GST_DEBUG_OBJECT (playsink, "removing current audio chain");
2386         if (playsink->audio_tee_asrc) {
2387           gst_element_release_request_pad (playsink->audio_tee,
2388               playsink->audio_tee_asrc);
2389           gst_object_unref (playsink->audio_tee_asrc);
2390           playsink->audio_tee_asrc = NULL;
2391         }
2392
2393         if (playsink->audio_sinkpad_stream_synchronizer) {
2394           gst_element_release_request_pad (GST_ELEMENT_CAST
2395               (playsink->stream_synchronizer),
2396               playsink->audio_sinkpad_stream_synchronizer);
2397           gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
2398           playsink->audio_sinkpad_stream_synchronizer = NULL;
2399           gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
2400           playsink->audio_srcpad_stream_synchronizer = NULL;
2401         }
2402
2403         add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2404
2405         /* Remove the sink from the bin to keep its state
2406          * and unparent it to allow reuse */
2407         if (playsink->audiochain->sink)
2408           gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
2409               playsink->audiochain->sink);
2410
2411         activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2412         disconnect_chain (playsink->audiochain, playsink);
2413         playsink->audiochain->volume = NULL;
2414         playsink->audiochain->mute = NULL;
2415         if (playsink->audiochain->ts_offset)
2416           gst_object_unref (playsink->audiochain->ts_offset);
2417         playsink->audiochain->ts_offset = NULL;
2418         free_chain ((GstPlayChain *) playsink->audiochain);
2419         playsink->audiochain = NULL;
2420         playsink->volume_changed = playsink->mute_changed = FALSE;
2421       }
2422     }
2423
2424     if (!playsink->audiochain) {
2425       GST_DEBUG_OBJECT (playsink, "creating new audio chain");
2426       playsink->audiochain = gen_audio_chain (playsink, raw);
2427     }
2428
2429     if (!playsink->audio_sinkpad_stream_synchronizer) {
2430       GstIterator *it;
2431
2432       playsink->audio_sinkpad_stream_synchronizer =
2433           gst_element_get_request_pad (GST_ELEMENT_CAST
2434           (playsink->stream_synchronizer), "sink_%d");
2435       it = gst_pad_iterate_internal_links
2436           (playsink->audio_sinkpad_stream_synchronizer);
2437       g_assert (it);
2438       gst_iterator_next (it,
2439           (gpointer *) & playsink->audio_srcpad_stream_synchronizer);
2440       g_assert (playsink->audio_srcpad_stream_synchronizer);
2441       gst_iterator_free (it);
2442     }
2443
2444     if (playsink->audiochain) {
2445       GST_DEBUG_OBJECT (playsink, "adding audio chain");
2446       if (playsink->audio_tee_asrc == NULL) {
2447         playsink->audio_tee_asrc =
2448             gst_element_get_request_pad (playsink->audio_tee, "src%d");
2449       }
2450       add_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
2451       activate_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
2452       gst_pad_link_full (playsink->audio_tee_asrc,
2453           playsink->audio_sinkpad_stream_synchronizer,
2454           GST_PAD_LINK_CHECK_NOTHING);
2455       gst_pad_link_full (playsink->audio_srcpad_stream_synchronizer,
2456           playsink->audiochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2457     }
2458   } else {
2459     GST_DEBUG_OBJECT (playsink, "no audio needed");
2460     /* we have no audio or we are requested to not play audio */
2461     if (playsink->audiochain) {
2462       GST_DEBUG_OBJECT (playsink, "removing audio chain");
2463       /* release the audio pad */
2464       if (playsink->audio_tee_asrc) {
2465         gst_element_release_request_pad (playsink->audio_tee,
2466             playsink->audio_tee_asrc);
2467         gst_object_unref (playsink->audio_tee_asrc);
2468         playsink->audio_tee_asrc = NULL;
2469       }
2470
2471       if (playsink->audio_sinkpad_stream_synchronizer) {
2472         gst_element_release_request_pad (GST_ELEMENT_CAST
2473             (playsink->stream_synchronizer),
2474             playsink->audio_sinkpad_stream_synchronizer);
2475         gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
2476         playsink->audio_sinkpad_stream_synchronizer = NULL;
2477         gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
2478         playsink->audio_srcpad_stream_synchronizer = NULL;
2479       }
2480
2481       if (playsink->audiochain->sink_volume) {
2482         disconnect_chain (playsink->audiochain, playsink);
2483         playsink->audiochain->volume = NULL;
2484         playsink->audiochain->mute = NULL;
2485         if (playsink->audiochain->ts_offset)
2486           gst_object_unref (playsink->audiochain->ts_offset);
2487         playsink->audiochain->ts_offset = NULL;
2488       }
2489       add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2490       activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2491     }
2492   }
2493
2494   if (need_vis) {
2495     GstPad *srcpad;
2496
2497     if (!playsink->vischain)
2498       playsink->vischain = gen_vis_chain (playsink);
2499
2500     GST_DEBUG_OBJECT (playsink, "adding visualisation");
2501
2502     if (playsink->vischain) {
2503       GST_DEBUG_OBJECT (playsink, "setting up vis chain");
2504       srcpad =
2505           gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2506       add_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
2507       activate_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
2508       if (playsink->audio_tee_vissrc == NULL) {
2509         playsink->audio_tee_vissrc =
2510             gst_element_get_request_pad (playsink->audio_tee, "src%d");
2511       }
2512       gst_pad_link_full (playsink->audio_tee_vissrc,
2513           playsink->vischain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2514       gst_pad_link_full (srcpad, playsink->video_sinkpad_stream_synchronizer,
2515           GST_PAD_LINK_CHECK_NOTHING);
2516       gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2517           playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2518       gst_object_unref (srcpad);
2519     }
2520   } else {
2521     GST_DEBUG_OBJECT (playsink, "no vis needed");
2522     if (playsink->vischain) {
2523       if (playsink->audio_tee_vissrc) {
2524         gst_element_release_request_pad (playsink->audio_tee,
2525             playsink->audio_tee_vissrc);
2526         gst_object_unref (playsink->audio_tee_vissrc);
2527         playsink->audio_tee_vissrc = NULL;
2528       }
2529       GST_DEBUG_OBJECT (playsink, "removing vis chain");
2530       add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
2531       activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
2532     }
2533   }
2534
2535   if (need_text) {
2536     GST_DEBUG_OBJECT (playsink, "adding text");
2537     if (!playsink->textchain) {
2538       GST_DEBUG_OBJECT (playsink, "creating text chain");
2539       playsink->textchain = gen_text_chain (playsink);
2540     }
2541     if (playsink->textchain) {
2542       GstIterator *it;
2543
2544       GST_DEBUG_OBJECT (playsink, "adding text chain");
2545       if (playsink->textchain->overlay)
2546         g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", FALSE,
2547             NULL);
2548       add_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
2549
2550       if (!playsink->text_sinkpad_stream_synchronizer) {
2551         playsink->text_sinkpad_stream_synchronizer =
2552             gst_element_get_request_pad (GST_ELEMENT_CAST
2553             (playsink->stream_synchronizer), "sink_%d");
2554         it = gst_pad_iterate_internal_links
2555             (playsink->text_sinkpad_stream_synchronizer);
2556         g_assert (it);
2557         gst_iterator_next (it,
2558             (gpointer *) & playsink->text_srcpad_stream_synchronizer);
2559         g_assert (playsink->text_srcpad_stream_synchronizer);
2560         gst_iterator_free (it);
2561
2562         gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad),
2563             playsink->text_sinkpad_stream_synchronizer);
2564         gst_pad_link_full (playsink->text_srcpad_stream_synchronizer,
2565             playsink->textchain->textsinkpad, GST_PAD_LINK_CHECK_NOTHING);
2566       }
2567
2568       if (need_vis) {
2569         GstPad *srcpad;
2570
2571         srcpad =
2572             gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2573         gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
2574         gst_pad_link_full (srcpad, playsink->textchain->videosinkpad,
2575             GST_PAD_LINK_CHECK_NOTHING);
2576         gst_object_unref (srcpad);
2577       } else {
2578         if (need_deinterlace)
2579           gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
2580               playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
2581         else
2582           gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2583               playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
2584       }
2585       gst_pad_link_full (playsink->textchain->srcpad,
2586           playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2587
2588       activate_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
2589     }
2590   } else {
2591     GST_DEBUG_OBJECT (playsink, "no text needed");
2592     /* we have no subtitles/text or we are requested to not show them */
2593
2594     if (playsink->text_sinkpad_stream_synchronizer) {
2595       gst_element_release_request_pad (GST_ELEMENT_CAST
2596           (playsink->stream_synchronizer),
2597           playsink->text_sinkpad_stream_synchronizer);
2598       gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
2599       playsink->text_sinkpad_stream_synchronizer = NULL;
2600       gst_object_unref (playsink->text_srcpad_stream_synchronizer);
2601       playsink->text_srcpad_stream_synchronizer = NULL;
2602     }
2603
2604     if (playsink->textchain) {
2605       if (playsink->text_pad == NULL) {
2606         /* no text pad, remove the chain entirely */
2607         GST_DEBUG_OBJECT (playsink, "removing text chain");
2608         add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
2609         activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
2610       } else {
2611         /* we have a chain and a textpad, turn the subtitles off */
2612         GST_DEBUG_OBJECT (playsink, "turning off the text");
2613         if (playsink->textchain->overlay)
2614           g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", TRUE,
2615               NULL);
2616       }
2617     }
2618     if (!need_video && playsink->video_pad) {
2619       if (playsink->video_sinkpad_stream_synchronizer) {
2620         gst_element_release_request_pad (GST_ELEMENT_CAST
2621             (playsink->stream_synchronizer),
2622             playsink->video_sinkpad_stream_synchronizer);
2623         gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2624         playsink->video_sinkpad_stream_synchronizer = NULL;
2625         gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2626         playsink->video_srcpad_stream_synchronizer = NULL;
2627       }
2628
2629       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
2630     }
2631
2632     if (playsink->text_pad && !playsink->textchain)
2633       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad), NULL);
2634   }
2635   update_av_offset (playsink);
2636   do_async_done (playsink);
2637   GST_PLAY_SINK_UNLOCK (playsink);
2638
2639   return TRUE;
2640
2641   /* ERRORS */
2642 no_chain:
2643   {
2644     /* gen_ chain already posted error */
2645     GST_DEBUG_OBJECT (playsink, "failed to setup chain");
2646     GST_PLAY_SINK_UNLOCK (playsink);
2647     return FALSE;
2648   }
2649 }
2650
2651 /**
2652  * gst_play_sink_set_flags:
2653  * @playsink: a #GstPlaySink
2654  * @flags: #GstPlayFlags
2655  *
2656  * Configure @flags on @playsink. The flags control the behaviour of @playsink
2657  * when constructing the sink pipelins.
2658  *
2659  * Returns: TRUE if the flags could be configured.
2660  */
2661 gboolean
2662 gst_play_sink_set_flags (GstPlaySink * playsink, GstPlayFlags flags)
2663 {
2664   g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), FALSE);
2665
2666   GST_OBJECT_LOCK (playsink);
2667   playsink->flags = flags;
2668   GST_OBJECT_UNLOCK (playsink);
2669
2670   return TRUE;
2671 }
2672
2673 /**
2674  * gst_play_sink_get_flags:
2675  * @playsink: a #GstPlaySink
2676  *
2677  * Get the flags of @playsink. That flags control the behaviour of the sink when
2678  * it constructs the sink pipelines.
2679  *
2680  * Returns: the currently configured #GstPlayFlags.
2681  */
2682 GstPlayFlags
2683 gst_play_sink_get_flags (GstPlaySink * playsink)
2684 {
2685   GstPlayFlags res;
2686
2687   g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), 0);
2688
2689   GST_OBJECT_LOCK (playsink);
2690   res = playsink->flags;
2691   GST_OBJECT_UNLOCK (playsink);
2692
2693   return res;
2694 }
2695
2696 void
2697 gst_play_sink_set_font_desc (GstPlaySink * playsink, const gchar * desc)
2698 {
2699   GstPlayTextChain *chain;
2700
2701   GST_PLAY_SINK_LOCK (playsink);
2702   chain = (GstPlayTextChain *) playsink->textchain;
2703   g_free (playsink->font_desc);
2704   playsink->font_desc = g_strdup (desc);
2705   if (chain && chain->overlay) {
2706     g_object_set (chain->overlay, "font-desc", desc, NULL);
2707   }
2708   GST_PLAY_SINK_UNLOCK (playsink);
2709 }
2710
2711 gchar *
2712 gst_play_sink_get_font_desc (GstPlaySink * playsink)
2713 {
2714   gchar *result = NULL;
2715   GstPlayTextChain *chain;
2716
2717   GST_PLAY_SINK_LOCK (playsink);
2718   chain = (GstPlayTextChain *) playsink->textchain;
2719   if (chain && chain->overlay) {
2720     g_object_get (chain->overlay, "font-desc", &result, NULL);
2721     playsink->font_desc = g_strdup (result);
2722   } else {
2723     result = g_strdup (playsink->font_desc);
2724   }
2725   GST_PLAY_SINK_UNLOCK (playsink);
2726
2727   return result;
2728 }
2729
2730 void
2731 gst_play_sink_set_subtitle_encoding (GstPlaySink * playsink,
2732     const gchar * encoding)
2733 {
2734   GstPlayTextChain *chain;
2735
2736   GST_PLAY_SINK_LOCK (playsink);
2737   chain = (GstPlayTextChain *) playsink->textchain;
2738   g_free (playsink->subtitle_encoding);
2739   playsink->subtitle_encoding = g_strdup (encoding);
2740   if (chain && chain->overlay) {
2741     g_object_set (chain->overlay, "subtitle-encoding", encoding, NULL);
2742   }
2743   GST_PLAY_SINK_UNLOCK (playsink);
2744 }
2745
2746 gchar *
2747 gst_play_sink_get_subtitle_encoding (GstPlaySink * playsink)
2748 {
2749   gchar *result = NULL;
2750   GstPlayTextChain *chain;
2751
2752   GST_PLAY_SINK_LOCK (playsink);
2753   chain = (GstPlayTextChain *) playsink->textchain;
2754   if (chain && chain->overlay) {
2755     g_object_get (chain->overlay, "subtitle-encoding", &result, NULL);
2756     playsink->subtitle_encoding = g_strdup (result);
2757   } else {
2758     result = g_strdup (playsink->subtitle_encoding);
2759   }
2760   GST_PLAY_SINK_UNLOCK (playsink);
2761
2762   return result;
2763 }
2764
2765 static void
2766 update_av_offset (GstPlaySink * playsink)
2767 {
2768   gint64 av_offset;
2769   GstPlayAudioChain *achain;
2770   GstPlayVideoChain *vchain;
2771
2772   av_offset = playsink->av_offset;
2773   achain = (GstPlayAudioChain *) playsink->audiochain;
2774   vchain = (GstPlayVideoChain *) playsink->videochain;
2775
2776   if (achain && vchain && achain->ts_offset && vchain->ts_offset) {
2777     g_object_set (achain->ts_offset, "ts-offset", MAX (0, -av_offset), NULL);
2778     g_object_set (vchain->ts_offset, "ts-offset", MAX (0, av_offset), NULL);
2779   } else {
2780     GST_LOG_OBJECT (playsink, "no ts_offset elements");
2781   }
2782 }
2783
2784 void
2785 gst_play_sink_set_av_offset (GstPlaySink * playsink, gint64 av_offset)
2786 {
2787   GST_PLAY_SINK_LOCK (playsink);
2788   playsink->av_offset = av_offset;
2789   update_av_offset (playsink);
2790   GST_PLAY_SINK_UNLOCK (playsink);
2791 }
2792
2793 gint64
2794 gst_play_sink_get_av_offset (GstPlaySink * playsink)
2795 {
2796   gint64 result;
2797
2798   GST_PLAY_SINK_LOCK (playsink);
2799   result = playsink->av_offset;
2800   GST_PLAY_SINK_UNLOCK (playsink);
2801
2802   return result;
2803 }
2804
2805 /**
2806  * gst_play_sink_get_last_frame:
2807  * @playsink: a #GstPlaySink
2808  *
2809  * Get the last displayed frame from @playsink. This frame is in the native
2810  * format of the sink element, the caps on the result buffer contain the format
2811  * of the frame data.
2812  *
2813  * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
2814  * available.
2815  */
2816 GstBuffer *
2817 gst_play_sink_get_last_frame (GstPlaySink * playsink)
2818 {
2819   GstBuffer *result = NULL;
2820   GstPlayVideoChain *chain;
2821
2822   GST_PLAY_SINK_LOCK (playsink);
2823   GST_DEBUG_OBJECT (playsink, "taking last frame");
2824   /* get the video chain if we can */
2825   if ((chain = (GstPlayVideoChain *) playsink->videochain)) {
2826     GST_DEBUG_OBJECT (playsink, "found video chain");
2827     /* see if the chain is active */
2828     if (chain->chain.activated && chain->sink) {
2829       GstElement *elem;
2830
2831       GST_DEBUG_OBJECT (playsink, "video chain active and has a sink");
2832
2833       /* find and get the last-buffer property now */
2834       if ((elem =
2835               gst_play_sink_find_property (playsink, chain->sink,
2836                   "last-buffer", GST_TYPE_BUFFER))) {
2837         GST_DEBUG_OBJECT (playsink, "getting last-buffer property");
2838         g_object_get (elem, "last-buffer", &result, NULL);
2839         gst_object_unref (elem);
2840       }
2841     }
2842   }
2843   GST_PLAY_SINK_UNLOCK (playsink);
2844
2845   return result;
2846 }
2847
2848 /**
2849  * gst_play_sink_convert_frame:
2850  * @playsink: a #GstPlaySink
2851  * @caps: a #GstCaps
2852  *
2853  * Get the last displayed frame from @playsink. If caps is %NULL, the video will
2854  * be in the native format of the sink element and the caps on the buffer
2855  * describe the format of the frame. If @caps is not %NULL, the video
2856  * frame will be converted to the format of the caps.
2857  *
2858  * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
2859  * available or when the conversion failed.
2860  */
2861 GstBuffer *
2862 gst_play_sink_convert_frame (GstPlaySink * playsink, GstCaps * caps)
2863 {
2864   GstBuffer *result;
2865
2866   result = gst_play_sink_get_last_frame (playsink);
2867   if (result != NULL && caps != NULL) {
2868     GstBuffer *temp;
2869     GError *err = NULL;
2870
2871     temp = gst_video_convert_frame (result, caps, 25 * GST_SECOND, &err);
2872     gst_buffer_unref (result);
2873     if (temp == NULL && err) {
2874       /* I'm really uncertain whether we should make playsink post an error
2875        * on the bus or not. It's not like it's a critical issue regarding
2876        * playsink behaviour. */
2877       GST_ERROR ("Error converting frame: %s", err->message);
2878       g_error_free (err);
2879     }
2880     result = temp;
2881   }
2882   return result;
2883 }
2884
2885 static gboolean
2886 is_raw_structure (GstStructure * s)
2887 {
2888   const gchar *name;
2889
2890   name = gst_structure_get_name (s);
2891
2892   if (g_str_has_prefix (name, "video/x-raw-") ||
2893       g_str_has_prefix (name, "audio/x-raw-"))
2894     return TRUE;
2895   return FALSE;
2896 }
2897
2898 static gboolean
2899 is_raw_pad (GstPad * pad)
2900 {
2901   GstPad *peer = gst_pad_get_peer (pad);
2902   GstCaps *caps;
2903   gboolean raw = TRUE;
2904
2905   if (!peer)
2906     return raw;
2907
2908   caps = gst_pad_get_negotiated_caps (peer);
2909   if (!caps) {
2910     guint i, n;
2911
2912     caps = gst_pad_get_caps_reffed (peer);
2913
2914     n = gst_caps_get_size (caps);
2915     for (i = 0; i < n; i++) {
2916       gboolean r = is_raw_structure (gst_caps_get_structure (caps, i));
2917
2918       if (i == 0) {
2919         raw = r;
2920       } else if (raw != r) {
2921         GST_ERROR_OBJECT (pad,
2922             "Caps contains raw and non-raw structures: %" GST_PTR_FORMAT, caps);
2923         raw = FALSE;
2924         break;
2925       }
2926     }
2927   } else {
2928     raw = is_raw_structure (gst_caps_get_structure (caps, 0));
2929   }
2930   gst_caps_unref (caps);
2931   gst_object_unref (peer);
2932
2933   return raw;
2934 }
2935
2936 static void
2937 sinkpad_blocked_cb (GstPad * blockedpad, gboolean blocked, gpointer user_data)
2938 {
2939   GstPlaySink *playsink = (GstPlaySink *) user_data;
2940   GstPad *pad;
2941
2942   GST_PLAY_SINK_LOCK (playsink);
2943
2944   pad = GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (blockedpad)));
2945   if (pad == playsink->video_pad) {
2946     playsink->video_pad_blocked = blocked;
2947     GST_DEBUG_OBJECT (pad, "Video pad blocked: %d", blocked);
2948     if (!blocked) {
2949       PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO_RAW);
2950       PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO);
2951     }
2952   } else if (pad == playsink->audio_pad) {
2953     playsink->audio_pad_blocked = blocked;
2954     GST_DEBUG_OBJECT (pad, "Audio pad blocked: %d", blocked);
2955     if (!blocked) {
2956       PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO_RAW);
2957       PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO);
2958     }
2959   } else if (pad == playsink->text_pad) {
2960     playsink->text_pad_blocked = blocked;
2961     GST_DEBUG_OBJECT (pad, "Text pad blocked: %d", blocked);
2962     if (!blocked)
2963       PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_TEXT);
2964   }
2965
2966   if (!blocked) {
2967     gst_object_unref (pad);
2968     GST_PLAY_SINK_UNLOCK (playsink);
2969     return;
2970   }
2971
2972   /* We reconfigure when for ALL streams:
2973    * * there isn't a pad
2974    * * OR the pad is blocked
2975    * * OR there are no pending blocks on that pad
2976    */
2977
2978   if ((!playsink->video_pad || playsink->video_pad_blocked
2979           || !PENDING_VIDEO_BLOCK (playsink)) && (!playsink->audio_pad
2980           || playsink->audio_pad_blocked || !PENDING_AUDIO_BLOCK (playsink))
2981       && (!playsink->text_pad || playsink->text_pad_blocked
2982           || !PENDING_TEXT_BLOCK (playsink))) {
2983     GST_DEBUG_OBJECT (playsink, "All pads blocked -- reconfiguring");
2984
2985     if (playsink->video_pad) {
2986       playsink->video_pad_raw = is_raw_pad (playsink->video_pad);
2987       GST_DEBUG_OBJECT (playsink, "Video pad is raw: %d",
2988           playsink->video_pad_raw);
2989     }
2990
2991     if (playsink->audio_pad) {
2992       playsink->audio_pad_raw = is_raw_pad (playsink->audio_pad);
2993       GST_DEBUG_OBJECT (playsink, "Audio pad is raw: %d",
2994           playsink->audio_pad_raw);
2995     }
2996
2997     gst_play_sink_reconfigure (playsink);
2998
2999     if (playsink->video_pad) {
3000       GstPad *opad =
3001           GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3002               (playsink->video_pad)));
3003       gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3004           gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3005       gst_object_unref (opad);
3006     }
3007
3008     if (playsink->audio_pad) {
3009       GstPad *opad =
3010           GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3011               (playsink->audio_pad)));
3012       gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3013           gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3014       gst_object_unref (opad);
3015     }
3016
3017     if (playsink->text_pad) {
3018       GstPad *opad =
3019           GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3020               (playsink->text_pad)));
3021       gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3022           gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3023       gst_object_unref (opad);
3024     }
3025   }
3026
3027   gst_object_unref (pad);
3028
3029   GST_PLAY_SINK_UNLOCK (playsink);
3030 }
3031
3032 static void
3033 caps_notify_cb (GstPad * pad, GParamSpec * unused, GstPlaySink * playsink)
3034 {
3035   gboolean reconfigure = FALSE;
3036   GstCaps *caps;
3037   gboolean raw;
3038
3039   g_object_get (pad, "caps", &caps, NULL);
3040   if (!caps)
3041     return;
3042
3043   if (pad == playsink->audio_pad) {
3044     raw = is_raw_pad (pad);
3045     reconfigure = (!!playsink->audio_pad_raw != !!raw)
3046         && playsink->audiochain;
3047     GST_DEBUG_OBJECT (pad,
3048         "Audio caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3049         reconfigure, caps);
3050   } else if (pad == playsink->video_pad) {
3051     raw = is_raw_pad (pad);
3052     reconfigure = (!!playsink->video_pad_raw != !!raw)
3053         && playsink->videochain;
3054     GST_DEBUG_OBJECT (pad,
3055         "Video caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3056         reconfigure, caps);
3057   }
3058
3059   gst_caps_unref (caps);
3060
3061   if (reconfigure) {
3062     GST_PLAY_SINK_LOCK (playsink);
3063     if (playsink->video_pad) {
3064       GstPad *opad =
3065           GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3066               (playsink->video_pad)));
3067       gst_pad_set_blocked_async_full (opad, TRUE, sinkpad_blocked_cb,
3068           gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3069       gst_object_unref (opad);
3070     }
3071
3072     if (playsink->audio_pad) {
3073       GstPad *opad =
3074           GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3075               (playsink->audio_pad)));
3076       gst_pad_set_blocked_async_full (opad, TRUE, sinkpad_blocked_cb,
3077           gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3078       gst_object_unref (opad);
3079     }
3080
3081     if (playsink->text_pad) {
3082       GstPad *opad =
3083           GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3084               (playsink->text_pad)));
3085       gst_pad_set_blocked_async_full (opad, TRUE, sinkpad_blocked_cb,
3086           gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3087       gst_object_unref (opad);
3088     }
3089     GST_PLAY_SINK_UNLOCK (playsink);
3090   }
3091 }
3092
3093 /**
3094  * gst_play_sink_request_pad
3095  * @playsink: a #GstPlaySink
3096  * @type: a #GstPlaySinkType
3097  *
3098  * Create or return a pad of @type.
3099  *
3100  * Returns: a #GstPad of @type or %NULL when the pad could not be created.
3101  */
3102 GstPad *
3103 gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type)
3104 {
3105   GstPad *res = NULL;
3106   gboolean created = FALSE;
3107   gboolean activate = TRUE;
3108   const gchar *pad_name = NULL;
3109
3110   GST_DEBUG_OBJECT (playsink, "request pad type %d", type);
3111
3112   GST_PLAY_SINK_LOCK (playsink);
3113   switch (type) {
3114     case GST_PLAY_SINK_TYPE_AUDIO_RAW:
3115     case GST_PLAY_SINK_TYPE_AUDIO:
3116       pad_name = "audio_sink";
3117       if (!playsink->audio_tee) {
3118         GST_LOG_OBJECT (playsink, "creating tee");
3119         /* create tee when needed. This element will feed the audio sink chain
3120          * and the vis chain. */
3121         playsink->audio_tee = gst_element_factory_make ("tee", "audiotee");
3122         if (playsink->audio_tee == NULL) {
3123           post_missing_element_message (playsink, "tee");
3124           GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
3125               (_("Missing element '%s' - check your GStreamer installation."),
3126                   "tee"), (NULL));
3127           res = NULL;
3128           break;
3129         } else {
3130           playsink->audio_tee_sink =
3131               gst_element_get_static_pad (playsink->audio_tee, "sink");
3132           gst_bin_add (GST_BIN_CAST (playsink), playsink->audio_tee);
3133           gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
3134         }
3135       } else {
3136         gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
3137       }
3138       if (!playsink->audio_pad) {
3139         GST_LOG_OBJECT (playsink, "ghosting tee sinkpad");
3140         playsink->audio_pad =
3141             gst_ghost_pad_new (pad_name, playsink->audio_tee_sink);
3142         g_signal_connect (G_OBJECT (playsink->audio_pad), "notify::caps",
3143             G_CALLBACK (caps_notify_cb), playsink);
3144         created = TRUE;
3145       }
3146       playsink->audio_pad_raw = FALSE;
3147       res = playsink->audio_pad;
3148       break;
3149     case GST_PLAY_SINK_TYPE_VIDEO_RAW:
3150     case GST_PLAY_SINK_TYPE_VIDEO:
3151       pad_name = "video_sink";
3152       if (!playsink->video_pad) {
3153         GST_LOG_OBJECT (playsink, "ghosting videosink");
3154         playsink->video_pad =
3155             gst_ghost_pad_new_no_target (pad_name, GST_PAD_SINK);
3156         g_signal_connect (G_OBJECT (playsink->video_pad), "notify::caps",
3157             G_CALLBACK (caps_notify_cb), playsink);
3158         created = TRUE;
3159       }
3160       playsink->video_pad_raw = FALSE;
3161       res = playsink->video_pad;
3162       break;
3163     case GST_PLAY_SINK_TYPE_TEXT:
3164       GST_LOG_OBJECT (playsink, "ghosting text");
3165       if (!playsink->text_pad) {
3166         playsink->text_pad =
3167             gst_ghost_pad_new_no_target ("text_sink", GST_PAD_SINK);
3168         created = TRUE;
3169       }
3170       res = playsink->text_pad;
3171       break;
3172     case GST_PLAY_SINK_TYPE_FLUSHING:
3173     {
3174       gchar *padname;
3175
3176       /* we need a unique padname for the flushing pad. */
3177       padname = g_strdup_printf ("flushing_%d", playsink->count);
3178       res = gst_ghost_pad_new_no_target (padname, GST_PAD_SINK);
3179       g_free (padname);
3180       playsink->count++;
3181       activate = FALSE;
3182       created = TRUE;
3183       break;
3184     }
3185     default:
3186       res = NULL;
3187       break;
3188   }
3189   GST_PLAY_SINK_UNLOCK (playsink);
3190
3191   if (created && res) {
3192     /* we have to add the pad when it's active or we get an error when the
3193      * element is 'running' */
3194     gst_pad_set_active (res, TRUE);
3195     gst_element_add_pad (GST_ELEMENT_CAST (playsink), res);
3196     if (type != GST_PLAY_SINK_TYPE_FLUSHING) {
3197       GstPad *blockpad =
3198           GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (res)));
3199
3200       gst_pad_set_blocked_async_full (blockpad, TRUE, sinkpad_blocked_cb,
3201           gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3202       PENDING_FLAG_SET (playsink, type);
3203       gst_object_unref (blockpad);
3204     }
3205     if (!activate)
3206       gst_pad_set_active (res, activate);
3207   }
3208
3209   return res;
3210 }
3211
3212 static GstPad *
3213 gst_play_sink_request_new_pad (GstElement * element, GstPadTemplate * templ,
3214     const gchar * name)
3215 {
3216   GstPlaySink *psink;
3217   GstPad *pad;
3218   GstPlaySinkType type;
3219   const gchar *tplname;
3220
3221   g_return_val_if_fail (templ != NULL, NULL);
3222
3223   GST_DEBUG_OBJECT (element, "name:%s", name);
3224
3225   psink = GST_PLAY_SINK (element);
3226   tplname = GST_PAD_TEMPLATE_NAME_TEMPLATE (templ);
3227
3228   /* Figure out the GstPlaySinkType based on the template */
3229   if (!strcmp (tplname, "audio_sink"))
3230     type = GST_PLAY_SINK_TYPE_AUDIO;
3231   else if (!strcmp (tplname, "audio_raw_sink"))
3232     type = GST_PLAY_SINK_TYPE_AUDIO_RAW;
3233   else if (!strcmp (tplname, "video_sink"))
3234     type = GST_PLAY_SINK_TYPE_VIDEO;
3235   else if (!strcmp (tplname, "video_raw_sink"))
3236     type = GST_PLAY_SINK_TYPE_VIDEO_RAW;
3237   else if (!strcmp (tplname, "text_sink"))
3238     type = GST_PLAY_SINK_TYPE_TEXT;
3239   else
3240     goto unknown_template;
3241
3242   pad = gst_play_sink_request_pad (psink, type);
3243   return pad;
3244
3245 unknown_template:
3246   GST_WARNING_OBJECT (element, "Unknown pad template");
3247   return NULL;
3248 }
3249
3250 void
3251 gst_play_sink_release_pad (GstPlaySink * playsink, GstPad * pad)
3252 {
3253   GstPad **res = NULL;
3254   gboolean untarget = TRUE;
3255
3256   GST_DEBUG_OBJECT (playsink, "release pad %" GST_PTR_FORMAT, pad);
3257
3258   GST_PLAY_SINK_LOCK (playsink);
3259   if (pad == playsink->video_pad) {
3260     res = &playsink->video_pad;
3261     g_signal_handlers_disconnect_by_func (playsink->video_pad, caps_notify_cb,
3262         playsink);
3263   } else if (pad == playsink->audio_pad) {
3264     res = &playsink->audio_pad;
3265     g_signal_handlers_disconnect_by_func (playsink->audio_pad, caps_notify_cb,
3266         playsink);
3267   } else if (pad == playsink->text_pad) {
3268     res = &playsink->text_pad;
3269   } else {
3270     /* try to release the given pad anyway, these could be the FLUSHING pads. */
3271     res = &pad;
3272     untarget = FALSE;
3273   }
3274   GST_PLAY_SINK_UNLOCK (playsink);
3275
3276   if (*res) {
3277     GST_DEBUG_OBJECT (playsink, "deactivate pad %" GST_PTR_FORMAT, *res);
3278     gst_pad_set_active (*res, FALSE);
3279     if (untarget) {
3280       GST_DEBUG_OBJECT (playsink, "untargeting pad %" GST_PTR_FORMAT, *res);
3281       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (*res), NULL);
3282     }
3283     GST_DEBUG_OBJECT (playsink, "remove pad %" GST_PTR_FORMAT, *res);
3284     gst_element_remove_pad (GST_ELEMENT_CAST (playsink), *res);
3285     *res = NULL;
3286   }
3287 }
3288
3289 static void
3290 gst_play_sink_release_request_pad (GstElement * element, GstPad * pad)
3291 {
3292   GstPlaySink *psink = GST_PLAY_SINK (element);
3293
3294   gst_play_sink_release_pad (psink, pad);
3295 }
3296
3297 static void
3298 gst_play_sink_handle_message (GstBin * bin, GstMessage * message)
3299 {
3300   GstPlaySink *playsink;
3301
3302   playsink = GST_PLAY_SINK_CAST (bin);
3303
3304   switch (GST_MESSAGE_TYPE (message)) {
3305     case GST_MESSAGE_STEP_DONE:
3306     {
3307       GstFormat format;
3308       guint64 amount;
3309       gdouble rate;
3310       gboolean flush, intermediate, eos;
3311       guint64 duration;
3312
3313       GST_INFO_OBJECT (playsink, "Handling step-done message");
3314       gst_message_parse_step_done (message, &format, &amount, &rate, &flush,
3315           &intermediate, &duration, &eos);
3316
3317       if (format == GST_FORMAT_BUFFERS) {
3318         /* for the buffer format, we align the other streams */
3319         if (playsink->audiochain) {
3320           GstEvent *event;
3321
3322           event =
3323               gst_event_new_step (GST_FORMAT_TIME, duration, rate, flush,
3324               intermediate);
3325
3326           if (!gst_element_send_event (playsink->audiochain->chain.bin, event)) {
3327             GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
3328           }
3329         }
3330       }
3331       GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
3332       break;
3333     }
3334     default:
3335       GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
3336       break;
3337   }
3338 }
3339
3340 /* Send an event to our sinks until one of them works; don't then send to the
3341  * remaining sinks (unlike GstBin)
3342  * Special case: If a text sink is set we need to send the event
3343  * to them in case it's source is different from the a/v stream's source.
3344  */
3345 static gboolean
3346 gst_play_sink_send_event_to_sink (GstPlaySink * playsink, GstEvent * event)
3347 {
3348   gboolean res = TRUE;
3349
3350   if (playsink->textchain && playsink->textchain->sink) {
3351     gst_event_ref (event);
3352     if ((res = gst_element_send_event (playsink->textchain->chain.bin, event))) {
3353       GST_DEBUG_OBJECT (playsink, "Sent event successfully to text sink");
3354     } else {
3355       GST_DEBUG_OBJECT (playsink, "Event failed when sent to text sink");
3356     }
3357   }
3358
3359   if (playsink->videochain) {
3360     gst_event_ref (event);
3361     if ((res = gst_element_send_event (playsink->videochain->chain.bin, event))) {
3362       GST_DEBUG_OBJECT (playsink, "Sent event successfully to video sink");
3363       goto done;
3364     }
3365     GST_DEBUG_OBJECT (playsink, "Event failed when sent to video sink");
3366   }
3367   if (playsink->audiochain) {
3368     gst_event_ref (event);
3369     if ((res = gst_element_send_event (playsink->audiochain->chain.bin, event))) {
3370       GST_DEBUG_OBJECT (playsink, "Sent event successfully to audio sink");
3371       goto done;
3372     }
3373     GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
3374   }
3375
3376 done:
3377   gst_event_unref (event);
3378   return res;
3379 }
3380
3381 /* We only want to send the event to a single sink (overriding GstBin's
3382  * behaviour), but we want to keep GstPipeline's behaviour - wrapping seek
3383  * events appropriately. So, this is a messy duplication of code. */
3384 static gboolean
3385 gst_play_sink_send_event (GstElement * element, GstEvent * event)
3386 {
3387   gboolean res = FALSE;
3388   GstEventType event_type = GST_EVENT_TYPE (event);
3389   GstPlaySink *playsink;
3390
3391   playsink = GST_PLAY_SINK_CAST (element);
3392
3393   switch (event_type) {
3394     case GST_EVENT_SEEK:
3395       GST_DEBUG_OBJECT (element, "Sending event to a sink");
3396       res = gst_play_sink_send_event_to_sink (playsink, event);
3397       break;
3398     case GST_EVENT_STEP:
3399     {
3400       GstFormat format;
3401       guint64 amount;
3402       gdouble rate;
3403       gboolean flush, intermediate;
3404
3405       gst_event_parse_step (event, &format, &amount, &rate, &flush,
3406           &intermediate);
3407
3408       if (format == GST_FORMAT_BUFFERS) {
3409         /* for buffers, we will try to step video frames, for other formats we
3410          * send the step to all sinks */
3411         res = gst_play_sink_send_event_to_sink (playsink, event);
3412       } else {
3413         res =
3414             GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
3415             event);
3416       }
3417       break;
3418     }
3419     default:
3420       res =
3421           GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
3422           event);
3423       break;
3424   }
3425   return res;
3426 }
3427
3428 static GstStateChangeReturn
3429 gst_play_sink_change_state (GstElement * element, GstStateChange transition)
3430 {
3431   GstStateChangeReturn ret;
3432   GstStateChangeReturn bret;
3433
3434   GstPlaySink *playsink;
3435
3436   playsink = GST_PLAY_SINK (element);
3437
3438   switch (transition) {
3439     case GST_STATE_CHANGE_READY_TO_PAUSED:
3440       playsink->need_async_start = TRUE;
3441       /* we want to go async to PAUSED until we managed to configure and add the
3442        * sinks */
3443       do_async_start (playsink);
3444       ret = GST_STATE_CHANGE_ASYNC;
3445       break;
3446     case GST_STATE_CHANGE_PAUSED_TO_READY:
3447       /* unblock all pads here */
3448       GST_PLAY_SINK_LOCK (playsink);
3449       if (playsink->video_pad) {
3450         GstPad *opad =
3451             GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3452                 (playsink->video_pad)));
3453         if (gst_pad_is_blocked (opad)) {
3454           gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3455               gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3456         }
3457         gst_object_unref (opad);
3458         playsink->video_pad_blocked = FALSE;
3459       }
3460
3461       if (playsink->audio_pad) {
3462         GstPad *opad =
3463             GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3464                 (playsink->audio_pad)));
3465
3466         if (gst_pad_is_blocked (opad)) {
3467           gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3468               gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3469         }
3470         gst_object_unref (opad);
3471         playsink->audio_pad_blocked = FALSE;
3472       }
3473
3474       if (playsink->text_pad) {
3475         GstPad *opad =
3476             GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3477                 (playsink->text_pad)));
3478         if (gst_pad_is_blocked (opad)) {
3479           gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
3480               gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
3481         }
3482         gst_object_unref (opad);
3483         playsink->text_pad_blocked = FALSE;
3484       }
3485       GST_PLAY_SINK_UNLOCK (playsink);
3486       /* fall through */
3487     case GST_STATE_CHANGE_READY_TO_NULL:
3488       if (playsink->audiochain && playsink->audiochain->sink_volume) {
3489         /* remove our links to the mute and volume elements when they were
3490          * provided by a sink */
3491         disconnect_chain (playsink->audiochain, playsink);
3492         playsink->audiochain->volume = NULL;
3493         playsink->audiochain->mute = NULL;
3494       }
3495
3496       if (playsink->audiochain && playsink->audiochain->ts_offset) {
3497         gst_object_unref (playsink->audiochain->ts_offset);
3498         playsink->audiochain->ts_offset = NULL;
3499       }
3500
3501       if (playsink->videochain && playsink->videochain->ts_offset) {
3502         gst_object_unref (playsink->videochain->ts_offset);
3503         playsink->videochain->ts_offset = NULL;
3504       }
3505       ret = GST_STATE_CHANGE_SUCCESS;
3506       break;
3507     default:
3508       /* all other state changes return SUCCESS by default, this value can be
3509        * overridden by the result of the children */
3510       ret = GST_STATE_CHANGE_SUCCESS;
3511       break;
3512   }
3513
3514   /* do the state change of the children */
3515   bret =
3516       GST_ELEMENT_CLASS (gst_play_sink_parent_class)->change_state (element,
3517       transition);
3518   /* now look at the result of our children and adjust the return value */
3519   switch (bret) {
3520     case GST_STATE_CHANGE_FAILURE:
3521       /* failure, we stop */
3522       goto activate_failed;
3523     case GST_STATE_CHANGE_NO_PREROLL:
3524       /* some child returned NO_PREROLL. This is strange but we never know. We
3525        * commit our async state change (if any) and return the NO_PREROLL */
3526       do_async_done (playsink);
3527       ret = bret;
3528       break;
3529     case GST_STATE_CHANGE_ASYNC:
3530       /* some child was async, return this */
3531       ret = bret;
3532       break;
3533     default:
3534       /* return our previously configured return value */
3535       break;
3536   }
3537
3538   switch (transition) {
3539     case GST_STATE_CHANGE_READY_TO_PAUSED:
3540       break;
3541     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
3542       /* FIXME Release audio device when we implement that */
3543       playsink->need_async_start = TRUE;
3544       break;
3545     case GST_STATE_CHANGE_PAUSED_TO_READY:{
3546       if (playsink->video_sinkpad_stream_synchronizer) {
3547         gst_element_release_request_pad (GST_ELEMENT_CAST
3548             (playsink->stream_synchronizer),
3549             playsink->video_sinkpad_stream_synchronizer);
3550         gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3551         playsink->video_sinkpad_stream_synchronizer = NULL;
3552         gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3553         playsink->video_srcpad_stream_synchronizer = NULL;
3554       }
3555       if (playsink->audio_sinkpad_stream_synchronizer) {
3556         gst_element_release_request_pad (GST_ELEMENT_CAST
3557             (playsink->stream_synchronizer),
3558             playsink->audio_sinkpad_stream_synchronizer);
3559         gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
3560         playsink->audio_sinkpad_stream_synchronizer = NULL;
3561         gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
3562         playsink->audio_srcpad_stream_synchronizer = NULL;
3563       }
3564       if (playsink->text_sinkpad_stream_synchronizer) {
3565         gst_element_release_request_pad (GST_ELEMENT_CAST
3566             (playsink->stream_synchronizer),
3567             playsink->text_sinkpad_stream_synchronizer);
3568         gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
3569         playsink->text_sinkpad_stream_synchronizer = NULL;
3570         gst_object_unref (playsink->text_srcpad_stream_synchronizer);
3571         playsink->text_srcpad_stream_synchronizer = NULL;
3572       }
3573     }
3574       /* fall through */
3575     case GST_STATE_CHANGE_READY_TO_NULL:
3576       /* remove sinks we added */
3577       if (playsink->videodeinterlacechain) {
3578         activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
3579             FALSE);
3580         add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3581       }
3582       if (playsink->videochain) {
3583         activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3584         add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3585       }
3586       if (playsink->audiochain) {
3587         activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3588         add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3589       }
3590       if (playsink->vischain) {
3591         activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3592         add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3593       }
3594       if (playsink->textchain) {
3595         activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3596         add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3597       }
3598       do_async_done (playsink);
3599       /* when going to READY, keep elements around as long as possible,
3600        * so they may be re-used faster next time/url around.
3601        * when really going to NULL, clean up everything completely. */
3602       if (transition == GST_STATE_CHANGE_READY_TO_NULL) {
3603
3604         /* Unparent the sinks to allow reuse */
3605         if (playsink->videochain && playsink->videochain->sink)
3606           gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
3607               playsink->videochain->sink);
3608         if (playsink->audiochain && playsink->audiochain->sink)
3609           gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
3610               playsink->audiochain->sink);
3611         if (playsink->textchain && playsink->textchain->sink)
3612           gst_bin_remove (GST_BIN_CAST (playsink->textchain->chain.bin),
3613               playsink->textchain->sink);
3614
3615         if (playsink->audio_sink != NULL)
3616           gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
3617         if (playsink->video_sink != NULL)
3618           gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
3619         if (playsink->visualisation != NULL)
3620           gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
3621         if (playsink->text_sink != NULL)
3622           gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
3623
3624         free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
3625         playsink->videodeinterlacechain = NULL;
3626         free_chain ((GstPlayChain *) playsink->videochain);
3627         playsink->videochain = NULL;
3628         free_chain ((GstPlayChain *) playsink->audiochain);
3629         playsink->audiochain = NULL;
3630         free_chain ((GstPlayChain *) playsink->vischain);
3631         playsink->vischain = NULL;
3632         free_chain ((GstPlayChain *) playsink->textchain);
3633         playsink->textchain = NULL;
3634       }
3635       break;
3636     default:
3637       break;
3638   }
3639   return ret;
3640
3641   /* ERRORS */
3642 activate_failed:
3643   {
3644     GST_DEBUG_OBJECT (element,
3645         "element failed to change states -- activation problem?");
3646     return GST_STATE_CHANGE_FAILURE;
3647   }
3648 }
3649
3650 static void
3651 gst_play_sink_set_property (GObject * object, guint prop_id,
3652     const GValue * value, GParamSpec * spec)
3653 {
3654   GstPlaySink *playsink = GST_PLAY_SINK (object);
3655
3656   switch (prop_id) {
3657     case PROP_FLAGS:
3658       gst_play_sink_set_flags (playsink, g_value_get_flags (value));
3659       break;
3660     case PROP_VOLUME:
3661       gst_play_sink_set_volume (playsink, g_value_get_double (value));
3662       break;
3663     case PROP_MUTE:
3664       gst_play_sink_set_mute (playsink, g_value_get_boolean (value));
3665       break;
3666     case PROP_FONT_DESC:
3667       gst_play_sink_set_font_desc (playsink, g_value_get_string (value));
3668       break;
3669     case PROP_SUBTITLE_ENCODING:
3670       gst_play_sink_set_subtitle_encoding (playsink,
3671           g_value_get_string (value));
3672       break;
3673     case PROP_VIS_PLUGIN:
3674       gst_play_sink_set_vis_plugin (playsink, g_value_get_object (value));
3675       break;
3676     case PROP_AV_OFFSET:
3677       gst_play_sink_set_av_offset (playsink, g_value_get_int64 (value));
3678       break;
3679     case PROP_VIDEO_SINK:
3680       gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_VIDEO,
3681           g_value_get_object (value));
3682       break;
3683     case PROP_AUDIO_SINK:
3684       gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_AUDIO,
3685           g_value_get_object (value));
3686       break;
3687     case PROP_TEXT_SINK:
3688       gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_TEXT,
3689           g_value_get_object (value));
3690       break;
3691     default:
3692       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
3693       break;
3694   }
3695 }
3696
3697 static void
3698 gst_play_sink_get_property (GObject * object, guint prop_id,
3699     GValue * value, GParamSpec * spec)
3700 {
3701   GstPlaySink *playsink = GST_PLAY_SINK (object);
3702
3703   switch (prop_id) {
3704     case PROP_FLAGS:
3705       g_value_set_flags (value, gst_play_sink_get_flags (playsink));
3706       break;
3707     case PROP_VOLUME:
3708       g_value_set_double (value, gst_play_sink_get_volume (playsink));
3709       break;
3710     case PROP_MUTE:
3711       g_value_set_boolean (value, gst_play_sink_get_mute (playsink));
3712       break;
3713     case PROP_FONT_DESC:
3714       g_value_take_string (value, gst_play_sink_get_font_desc (playsink));
3715       break;
3716     case PROP_SUBTITLE_ENCODING:
3717       g_value_take_string (value,
3718           gst_play_sink_get_subtitle_encoding (playsink));
3719       break;
3720     case PROP_VIS_PLUGIN:
3721       g_value_take_object (value, gst_play_sink_get_vis_plugin (playsink));
3722       break;
3723     case PROP_FRAME:
3724       gst_value_take_buffer (value, gst_play_sink_get_last_frame (playsink));
3725       break;
3726     case PROP_AV_OFFSET:
3727       g_value_set_int64 (value, gst_play_sink_get_av_offset (playsink));
3728       break;
3729     case PROP_VIDEO_SINK:
3730       g_value_take_object (value, gst_play_sink_get_sink (playsink,
3731               GST_PLAY_SINK_TYPE_VIDEO));
3732       break;
3733     case PROP_AUDIO_SINK:
3734       g_value_take_object (value, gst_play_sink_get_sink (playsink,
3735               GST_PLAY_SINK_TYPE_AUDIO));
3736       break;
3737     case PROP_TEXT_SINK:
3738       g_value_take_object (value, gst_play_sink_get_sink (playsink,
3739               GST_PLAY_SINK_TYPE_TEXT));
3740       break;
3741     default:
3742       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
3743       break;
3744   }
3745 }
3746
3747
3748 gboolean
3749 gst_play_sink_plugin_init (GstPlugin * plugin)
3750 {
3751   GST_DEBUG_CATEGORY_INIT (gst_play_sink_debug, "playsink", 0, "play bin");
3752
3753   return gst_element_register (plugin, "playsink", GST_RANK_NONE,
3754       GST_TYPE_PLAY_SINK);
3755 }