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