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