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