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