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