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