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