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