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