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