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