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