playsink: Connect to the value-changed signal of the child colorbalance element and...
[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_link_full (playsink->video_srcpad_stream_synchronizer,
3098           playsink->videodeinterlacechain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3099     } else {
3100       if (playsink->videodeinterlacechain) {
3101         add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3102         activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
3103             FALSE);
3104       }
3105     }
3106
3107     GST_DEBUG_OBJECT (playsink, "adding video chain");
3108     add_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
3109     activate_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
3110     /* if we are not part of vis or subtitles, set the ghostpad target */
3111     if (!need_vis && !need_text && (!playsink->textchain
3112             || !playsink->text_pad)) {
3113       GST_DEBUG_OBJECT (playsink, "ghosting video sinkpad");
3114       if (need_deinterlace)
3115         gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
3116             playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3117       else
3118         gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3119             playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3120     }
3121   } else {
3122     GST_DEBUG_OBJECT (playsink, "no video needed");
3123     if (playsink->videochain) {
3124       GST_DEBUG_OBJECT (playsink, "removing video chain");
3125       if (playsink->vischain) {
3126         GstPad *srcpad;
3127
3128         GST_DEBUG_OBJECT (playsink, "unlinking vis chain");
3129
3130         /* also had visualisation, release the tee srcpad before we then
3131          * unlink the video from it */
3132         if (playsink->audio_tee_vissrc) {
3133           gst_element_release_request_pad (playsink->audio_tee,
3134               playsink->audio_tee_vissrc);
3135           gst_object_unref (playsink->audio_tee_vissrc);
3136           playsink->audio_tee_vissrc = NULL;
3137         }
3138         srcpad =
3139             gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
3140         gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
3141       }
3142
3143       if (playsink->video_sinkpad_stream_synchronizer) {
3144         gst_element_release_request_pad (GST_ELEMENT_CAST
3145             (playsink->stream_synchronizer),
3146             playsink->video_sinkpad_stream_synchronizer);
3147         gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3148         playsink->video_sinkpad_stream_synchronizer = NULL;
3149         gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3150         playsink->video_srcpad_stream_synchronizer = NULL;
3151       }
3152
3153       add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3154       activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3155       if (playsink->videochain->ts_offset)
3156         gst_object_unref (playsink->videochain->ts_offset);
3157       playsink->videochain->ts_offset = NULL;
3158     }
3159
3160     if (playsink->videodeinterlacechain) {
3161       add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3162       activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3163     }
3164
3165     if (playsink->video_pad)
3166       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
3167
3168     GST_OBJECT_LOCK (playsink);
3169     if (playsink->overlay_element)
3170       gst_object_unref (playsink->overlay_element);
3171     playsink->overlay_element = NULL;
3172
3173     if (playsink->colorbalance_element) {
3174       g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
3175           G_CALLBACK (colorbalance_value_changed_cb), playsink);
3176       gst_object_unref (playsink->colorbalance_element);
3177     }
3178     playsink->colorbalance_element = NULL;
3179     GST_OBJECT_UNLOCK (playsink);
3180
3181   }
3182
3183   if (need_audio) {
3184     gboolean raw;
3185
3186     GST_DEBUG_OBJECT (playsink, "adding audio");
3187
3188     /* get a raw sink if we are asked for a raw pad */
3189     raw = playsink->audio_pad_raw;
3190
3191     if (playsink->audiochain) {
3192       /* try to reactivate the chain */
3193       if (!setup_audio_chain (playsink, raw)) {
3194         GST_DEBUG_OBJECT (playsink, "removing current audio chain");
3195         if (playsink->audio_tee_asrc) {
3196           gst_element_release_request_pad (playsink->audio_tee,
3197               playsink->audio_tee_asrc);
3198           gst_object_unref (playsink->audio_tee_asrc);
3199           playsink->audio_tee_asrc = NULL;
3200         }
3201
3202         if (playsink->audio_sinkpad_stream_synchronizer) {
3203           gst_element_release_request_pad (GST_ELEMENT_CAST
3204               (playsink->stream_synchronizer),
3205               playsink->audio_sinkpad_stream_synchronizer);
3206           gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
3207           playsink->audio_sinkpad_stream_synchronizer = NULL;
3208           gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
3209           playsink->audio_srcpad_stream_synchronizer = NULL;
3210         }
3211
3212         add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3213
3214         /* Remove the sink from the bin to keep its state
3215          * and unparent it to allow reuse */
3216         if (playsink->audiochain->sink)
3217           gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
3218               playsink->audiochain->sink);
3219
3220         activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3221         disconnect_chain (playsink->audiochain, playsink);
3222         playsink->audiochain->volume = NULL;
3223         playsink->audiochain->mute = NULL;
3224         if (playsink->audiochain->ts_offset)
3225           gst_object_unref (playsink->audiochain->ts_offset);
3226         playsink->audiochain->ts_offset = NULL;
3227         free_chain ((GstPlayChain *) playsink->audiochain);
3228         playsink->audiochain = NULL;
3229         playsink->volume_changed = playsink->mute_changed = FALSE;
3230       }
3231     }
3232
3233     if (!playsink->audiochain) {
3234       GST_DEBUG_OBJECT (playsink, "creating new audio chain");
3235       playsink->audiochain = gen_audio_chain (playsink, raw);
3236     }
3237
3238     if (!playsink->audio_sinkpad_stream_synchronizer) {
3239       GValue item = { 0, };
3240       GstIterator *it;
3241
3242       playsink->audio_sinkpad_stream_synchronizer =
3243           gst_element_get_request_pad (GST_ELEMENT_CAST
3244           (playsink->stream_synchronizer), "sink_%u");
3245       it = gst_pad_iterate_internal_links
3246           (playsink->audio_sinkpad_stream_synchronizer);
3247       g_assert (it);
3248       gst_iterator_next (it, &item);
3249       playsink->audio_srcpad_stream_synchronizer = g_value_dup_object (&item);
3250       g_value_unset (&item);
3251       g_assert (playsink->audio_srcpad_stream_synchronizer);
3252       gst_iterator_free (it);
3253     }
3254
3255     if (playsink->audiochain) {
3256       GST_DEBUG_OBJECT (playsink, "adding audio chain");
3257       if (playsink->audio_tee_asrc == NULL) {
3258         playsink->audio_tee_asrc =
3259             gst_element_get_request_pad (playsink->audio_tee, "src_%u");
3260       }
3261       add_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
3262       activate_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
3263       gst_pad_link_full (playsink->audio_tee_asrc,
3264           playsink->audio_sinkpad_stream_synchronizer,
3265           GST_PAD_LINK_CHECK_NOTHING);
3266       gst_pad_link_full (playsink->audio_srcpad_stream_synchronizer,
3267           playsink->audiochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3268     }
3269   } else {
3270     GST_DEBUG_OBJECT (playsink, "no audio needed");
3271     /* we have no audio or we are requested to not play audio */
3272     if (playsink->audiochain) {
3273       GST_DEBUG_OBJECT (playsink, "removing audio chain");
3274       /* release the audio pad */
3275       if (playsink->audio_tee_asrc) {
3276         gst_element_release_request_pad (playsink->audio_tee,
3277             playsink->audio_tee_asrc);
3278         gst_object_unref (playsink->audio_tee_asrc);
3279         playsink->audio_tee_asrc = NULL;
3280       }
3281
3282       if (playsink->audio_sinkpad_stream_synchronizer) {
3283         gst_element_release_request_pad (GST_ELEMENT_CAST
3284             (playsink->stream_synchronizer),
3285             playsink->audio_sinkpad_stream_synchronizer);
3286         gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
3287         playsink->audio_sinkpad_stream_synchronizer = NULL;
3288         gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
3289         playsink->audio_srcpad_stream_synchronizer = NULL;
3290       }
3291
3292       if (playsink->audiochain->sink_volume) {
3293         disconnect_chain (playsink->audiochain, playsink);
3294         playsink->audiochain->volume = NULL;
3295         playsink->audiochain->mute = NULL;
3296         if (playsink->audiochain->ts_offset)
3297           gst_object_unref (playsink->audiochain->ts_offset);
3298         playsink->audiochain->ts_offset = NULL;
3299       }
3300       add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3301       activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3302     }
3303   }
3304
3305   if (need_vis) {
3306     GstPad *srcpad;
3307
3308     if (!playsink->vischain)
3309       playsink->vischain = gen_vis_chain (playsink);
3310
3311     GST_DEBUG_OBJECT (playsink, "adding visualisation");
3312
3313     if (playsink->vischain) {
3314       GST_DEBUG_OBJECT (playsink, "setting up vis chain");
3315       srcpad =
3316           gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
3317       add_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
3318       activate_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
3319       if (playsink->audio_tee_vissrc == NULL) {
3320         playsink->audio_tee_vissrc =
3321             gst_element_get_request_pad (playsink->audio_tee, "src_%u");
3322       }
3323       gst_pad_link_full (playsink->audio_tee_vissrc,
3324           playsink->vischain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3325       gst_pad_link_full (srcpad, playsink->video_sinkpad_stream_synchronizer,
3326           GST_PAD_LINK_CHECK_NOTHING);
3327       gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3328           playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3329       gst_object_unref (srcpad);
3330     }
3331   } else {
3332     GST_DEBUG_OBJECT (playsink, "no vis needed");
3333     if (playsink->vischain) {
3334       if (playsink->audio_tee_vissrc) {
3335         gst_element_release_request_pad (playsink->audio_tee,
3336             playsink->audio_tee_vissrc);
3337         gst_object_unref (playsink->audio_tee_vissrc);
3338         playsink->audio_tee_vissrc = NULL;
3339       }
3340       GST_DEBUG_OBJECT (playsink, "removing vis chain");
3341       add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3342       activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3343     }
3344   }
3345
3346   if (need_text) {
3347     GST_DEBUG_OBJECT (playsink, "adding text");
3348     if (!playsink->textchain) {
3349       GST_DEBUG_OBJECT (playsink, "creating text chain");
3350       playsink->textchain = gen_text_chain (playsink);
3351     }
3352     if (playsink->textchain) {
3353       GstIterator *it;
3354
3355       GST_DEBUG_OBJECT (playsink, "adding text chain");
3356       if (playsink->textchain->overlay)
3357         g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", FALSE,
3358             NULL);
3359       add_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
3360
3361       if (!playsink->text_sinkpad_stream_synchronizer) {
3362         GValue item = { 0, };
3363
3364         playsink->text_sinkpad_stream_synchronizer =
3365             gst_element_get_request_pad (GST_ELEMENT_CAST
3366             (playsink->stream_synchronizer), "sink_%u");
3367         it = gst_pad_iterate_internal_links
3368             (playsink->text_sinkpad_stream_synchronizer);
3369         g_assert (it);
3370         gst_iterator_next (it, &item);
3371         playsink->text_srcpad_stream_synchronizer = g_value_dup_object (&item);
3372         g_value_unset (&item);
3373         g_assert (playsink->text_srcpad_stream_synchronizer);
3374         gst_iterator_free (it);
3375
3376         gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad),
3377             playsink->text_sinkpad_stream_synchronizer);
3378         gst_pad_link_full (playsink->text_srcpad_stream_synchronizer,
3379             playsink->textchain->textsinkpad, GST_PAD_LINK_CHECK_NOTHING);
3380       }
3381
3382       if (need_vis || need_video) {
3383         if (need_vis) {
3384           GstPad *srcpad;
3385
3386           srcpad =
3387               gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
3388           gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
3389           gst_pad_link_full (srcpad, playsink->textchain->videosinkpad,
3390               GST_PAD_LINK_CHECK_NOTHING);
3391           gst_object_unref (srcpad);
3392         } else {
3393           if (need_deinterlace)
3394             gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
3395                 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
3396           else
3397             gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3398                 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
3399         }
3400         gst_pad_link_full (playsink->textchain->srcpad,
3401             playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3402       }
3403
3404       activate_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
3405     }
3406   } else {
3407     GST_DEBUG_OBJECT (playsink, "no text needed");
3408     /* we have no subtitles/text or we are requested to not show them */
3409
3410     if (playsink->text_sinkpad_stream_synchronizer) {
3411       gst_element_release_request_pad (GST_ELEMENT_CAST
3412           (playsink->stream_synchronizer),
3413           playsink->text_sinkpad_stream_synchronizer);
3414       gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
3415       playsink->text_sinkpad_stream_synchronizer = NULL;
3416       gst_object_unref (playsink->text_srcpad_stream_synchronizer);
3417       playsink->text_srcpad_stream_synchronizer = NULL;
3418     }
3419
3420     if (playsink->textchain) {
3421       if (playsink->text_pad == NULL) {
3422         /* no text pad, remove the chain entirely */
3423         GST_DEBUG_OBJECT (playsink, "removing text chain");
3424         add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3425         activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3426       } else {
3427         /* we have a chain and a textpad, turn the subtitles off */
3428         GST_DEBUG_OBJECT (playsink, "turning off the text");
3429         if (playsink->textchain->overlay)
3430           g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", TRUE,
3431               NULL);
3432       }
3433     }
3434     if (!need_video && playsink->video_pad) {
3435       if (playsink->video_sinkpad_stream_synchronizer) {
3436         gst_element_release_request_pad (GST_ELEMENT_CAST
3437             (playsink->stream_synchronizer),
3438             playsink->video_sinkpad_stream_synchronizer);
3439         gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3440         playsink->video_sinkpad_stream_synchronizer = NULL;
3441         gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3442         playsink->video_srcpad_stream_synchronizer = NULL;
3443       }
3444
3445       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
3446     }
3447
3448     if (playsink->text_pad && !playsink->textchain)
3449       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad), NULL);
3450   }
3451   update_av_offset (playsink);
3452   do_async_done (playsink);
3453   GST_PLAY_SINK_UNLOCK (playsink);
3454
3455   return TRUE;
3456
3457   /* ERRORS */
3458 no_chain:
3459   {
3460     /* gen_ chain already posted error */
3461     GST_DEBUG_OBJECT (playsink, "failed to setup chain");
3462     GST_PLAY_SINK_UNLOCK (playsink);
3463     return FALSE;
3464   }
3465 }
3466
3467 /**
3468  * gst_play_sink_set_flags:
3469  * @playsink: a #GstPlaySink
3470  * @flags: #GstPlayFlags
3471  *
3472  * Configure @flags on @playsink. The flags control the behaviour of @playsink
3473  * when constructing the sink pipelins.
3474  *
3475  * Returns: TRUE if the flags could be configured.
3476  */
3477 gboolean
3478 gst_play_sink_set_flags (GstPlaySink * playsink, GstPlayFlags flags)
3479 {
3480   g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), FALSE);
3481
3482   GST_OBJECT_LOCK (playsink);
3483   playsink->flags = flags;
3484   GST_OBJECT_UNLOCK (playsink);
3485
3486   return TRUE;
3487 }
3488
3489 /**
3490  * gst_play_sink_get_flags:
3491  * @playsink: a #GstPlaySink
3492  *
3493  * Get the flags of @playsink. That flags control the behaviour of the sink when
3494  * it constructs the sink pipelines.
3495  *
3496  * Returns: the currently configured #GstPlayFlags.
3497  */
3498 GstPlayFlags
3499 gst_play_sink_get_flags (GstPlaySink * playsink)
3500 {
3501   GstPlayFlags res;
3502
3503   g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), 0);
3504
3505   GST_OBJECT_LOCK (playsink);
3506   res = playsink->flags;
3507   GST_OBJECT_UNLOCK (playsink);
3508
3509   return res;
3510 }
3511
3512 void
3513 gst_play_sink_set_font_desc (GstPlaySink * playsink, const gchar * desc)
3514 {
3515   GstPlayTextChain *chain;
3516
3517   GST_PLAY_SINK_LOCK (playsink);
3518   chain = (GstPlayTextChain *) playsink->textchain;
3519   g_free (playsink->font_desc);
3520   playsink->font_desc = g_strdup (desc);
3521   if (chain && chain->overlay) {
3522     g_object_set (chain->overlay, "font-desc", desc, NULL);
3523   }
3524   GST_PLAY_SINK_UNLOCK (playsink);
3525 }
3526
3527 gchar *
3528 gst_play_sink_get_font_desc (GstPlaySink * playsink)
3529 {
3530   gchar *result = NULL;
3531   GstPlayTextChain *chain;
3532
3533   GST_PLAY_SINK_LOCK (playsink);
3534   chain = (GstPlayTextChain *) playsink->textchain;
3535   if (chain && chain->overlay) {
3536     g_object_get (chain->overlay, "font-desc", &result, NULL);
3537     playsink->font_desc = g_strdup (result);
3538   } else {
3539     result = g_strdup (playsink->font_desc);
3540   }
3541   GST_PLAY_SINK_UNLOCK (playsink);
3542
3543   return result;
3544 }
3545
3546 void
3547 gst_play_sink_set_subtitle_encoding (GstPlaySink * playsink,
3548     const gchar * encoding)
3549 {
3550   GstPlayTextChain *chain;
3551
3552   GST_PLAY_SINK_LOCK (playsink);
3553   chain = (GstPlayTextChain *) playsink->textchain;
3554   g_free (playsink->subtitle_encoding);
3555   playsink->subtitle_encoding = g_strdup (encoding);
3556   if (chain && chain->overlay) {
3557     g_object_set (chain->overlay, "subtitle-encoding", encoding, NULL);
3558   }
3559   GST_PLAY_SINK_UNLOCK (playsink);
3560 }
3561
3562 gchar *
3563 gst_play_sink_get_subtitle_encoding (GstPlaySink * playsink)
3564 {
3565   gchar *result = NULL;
3566   GstPlayTextChain *chain;
3567
3568   GST_PLAY_SINK_LOCK (playsink);
3569   chain = (GstPlayTextChain *) playsink->textchain;
3570   if (chain && chain->overlay) {
3571     g_object_get (chain->overlay, "subtitle-encoding", &result, NULL);
3572     playsink->subtitle_encoding = g_strdup (result);
3573   } else {
3574     result = g_strdup (playsink->subtitle_encoding);
3575   }
3576   GST_PLAY_SINK_UNLOCK (playsink);
3577
3578   return result;
3579 }
3580
3581 static void
3582 update_av_offset (GstPlaySink * playsink)
3583 {
3584   gint64 av_offset;
3585   GstPlayAudioChain *achain;
3586   GstPlayVideoChain *vchain;
3587
3588   av_offset = playsink->av_offset;
3589   achain = (GstPlayAudioChain *) playsink->audiochain;
3590   vchain = (GstPlayVideoChain *) playsink->videochain;
3591
3592   if (achain && vchain && achain->ts_offset && vchain->ts_offset) {
3593     g_object_set (achain->ts_offset, "ts-offset", MAX (0, -av_offset), NULL);
3594     g_object_set (vchain->ts_offset, "ts-offset", MAX (0, av_offset), NULL);
3595   } else {
3596     GST_LOG_OBJECT (playsink, "no ts_offset elements");
3597   }
3598 }
3599
3600 void
3601 gst_play_sink_set_av_offset (GstPlaySink * playsink, gint64 av_offset)
3602 {
3603   GST_PLAY_SINK_LOCK (playsink);
3604   playsink->av_offset = av_offset;
3605   update_av_offset (playsink);
3606   GST_PLAY_SINK_UNLOCK (playsink);
3607 }
3608
3609 gint64
3610 gst_play_sink_get_av_offset (GstPlaySink * playsink)
3611 {
3612   gint64 result;
3613
3614   GST_PLAY_SINK_LOCK (playsink);
3615   result = playsink->av_offset;
3616   GST_PLAY_SINK_UNLOCK (playsink);
3617
3618   return result;
3619 }
3620
3621 /**
3622  * gst_play_sink_get_last_sample:
3623  * @playsink: a #GstPlaySink
3624  *
3625  * Get the last displayed sample from @playsink. This sample is in the native
3626  * format of the sink element, the caps in the result sample contain the format
3627  * of the frame data.
3628  *
3629  * Returns: a #GstSample with the frame data or %NULL when no video frame is
3630  * available.
3631  */
3632 GstSample *
3633 gst_play_sink_get_last_sample (GstPlaySink * playsink)
3634 {
3635   GstSample *result = NULL;
3636   GstPlayVideoChain *chain;
3637
3638   GST_PLAY_SINK_LOCK (playsink);
3639   GST_DEBUG_OBJECT (playsink, "taking last sample");
3640   /* get the video chain if we can */
3641   if ((chain = (GstPlayVideoChain *) playsink->videochain)) {
3642     GST_DEBUG_OBJECT (playsink, "found video chain");
3643     /* see if the chain is active */
3644     if (chain->chain.activated && chain->sink) {
3645       GstElement *elem;
3646
3647       GST_DEBUG_OBJECT (playsink, "video chain active and has a sink");
3648
3649       /* find and get the last-buffer property now */
3650       if ((elem =
3651               gst_play_sink_find_property (playsink, chain->sink,
3652                   "last-sample", GST_TYPE_SAMPLE))) {
3653         GST_DEBUG_OBJECT (playsink, "getting last-sample property");
3654         g_object_get (elem, "last-sample", &result, NULL);
3655         gst_object_unref (elem);
3656       }
3657     }
3658   }
3659   GST_PLAY_SINK_UNLOCK (playsink);
3660
3661   return result;
3662 }
3663
3664 /**
3665  * gst_play_sink_convert_sample:
3666  * @playsink: a #GstPlaySink
3667  * @caps: a #GstCaps
3668  *
3669  * Get the last displayed frame from @playsink. If caps is %NULL, the video will
3670  * be in the native format of the sink element and the caps on the buffer
3671  * describe the format of the frame. If @caps is not %NULL, the video
3672  * frame will be converted to the format of the caps.
3673  *
3674  * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
3675  * available or when the conversion failed.
3676  */
3677 GstSample *
3678 gst_play_sink_convert_sample (GstPlaySink * playsink, GstCaps * caps)
3679 {
3680   GstSample *result;
3681   GError *err = NULL;
3682
3683   result = gst_play_sink_get_last_sample (playsink);
3684   if (result != NULL && caps != NULL) {
3685     GstSample *temp;
3686
3687     temp = gst_video_convert_sample (result, caps, 25 * GST_SECOND, &err);
3688     if (temp == NULL && err)
3689       goto error;
3690
3691     gst_sample_unref (result);
3692     result = temp;
3693   }
3694   return result;
3695
3696   /* ERRORS */
3697 error:
3698   {
3699     /* I'm really uncertain whether we should make playsink post an error
3700      * on the bus or not. It's not like it's a critical issue regarding
3701      * playsink behaviour. */
3702     GST_ERROR ("Error converting frame: %s", err->message);
3703     gst_sample_unref (result);
3704     g_error_free (err);
3705     return NULL;
3706   }
3707 }
3708
3709 static gboolean
3710 is_raw_structure (GstStructure * s)
3711 {
3712   const gchar *name;
3713
3714   name = gst_structure_get_name (s);
3715
3716   if (g_str_equal (name, "video/x-raw") || g_str_equal (name, "audio/x-raw"))
3717     return TRUE;
3718   return FALSE;
3719 }
3720
3721 static gboolean
3722 is_raw_pad (GstPad * pad)
3723 {
3724   GstPad *peer = gst_pad_get_peer (pad);
3725   GstCaps *caps;
3726   gboolean raw = TRUE;
3727
3728   if (!peer)
3729     return raw;
3730
3731   caps = gst_pad_get_current_caps (peer);
3732   if (!caps) {
3733     guint i, n;
3734
3735     caps = gst_pad_query_caps (peer, NULL);
3736
3737     n = gst_caps_get_size (caps);
3738     for (i = 0; i < n; i++) {
3739       gboolean r = is_raw_structure (gst_caps_get_structure (caps, i));
3740
3741       if (i == 0) {
3742         raw = r;
3743       } else if (raw != r) {
3744         GST_ERROR_OBJECT (pad,
3745             "Caps contains raw and non-raw structures: %" GST_PTR_FORMAT, caps);
3746         raw = FALSE;
3747         break;
3748       }
3749     }
3750   } else {
3751     raw = is_raw_structure (gst_caps_get_structure (caps, 0));
3752   }
3753   gst_caps_unref (caps);
3754   gst_object_unref (peer);
3755
3756   return raw;
3757 }
3758
3759 static GstPadProbeReturn
3760 sinkpad_blocked_cb (GstPad * blockedpad, GstPadProbeInfo * info,
3761     gpointer user_data);
3762
3763 static void
3764 video_set_blocked (GstPlaySink * playsink, gboolean blocked)
3765 {
3766   if (playsink->video_pad) {
3767     GstPad *opad =
3768         GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3769             (playsink->video_pad)));
3770     if (blocked && playsink->video_block_id == 0) {
3771       playsink->video_block_id =
3772           gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3773           sinkpad_blocked_cb, playsink, NULL);
3774     } else if (!blocked && playsink->video_block_id) {
3775       gst_pad_remove_probe (opad, playsink->video_block_id);
3776       PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO_RAW);
3777       PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO);
3778       playsink->video_block_id = 0;
3779       playsink->video_pad_blocked = FALSE;
3780     }
3781     gst_object_unref (opad);
3782   }
3783 }
3784
3785 static void
3786 audio_set_blocked (GstPlaySink * playsink, gboolean blocked)
3787 {
3788   if (playsink->audio_pad) {
3789     GstPad *opad =
3790         GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3791             (playsink->audio_pad)));
3792     if (blocked && playsink->audio_block_id == 0) {
3793       playsink->audio_block_id =
3794           gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3795           sinkpad_blocked_cb, playsink, NULL);
3796     } else if (!blocked && playsink->audio_block_id) {
3797       gst_pad_remove_probe (opad, playsink->audio_block_id);
3798       PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO_RAW);
3799       PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO);
3800       playsink->audio_block_id = 0;
3801       playsink->audio_pad_blocked = FALSE;
3802     }
3803     gst_object_unref (opad);
3804   }
3805 }
3806
3807 static void
3808 text_set_blocked (GstPlaySink * playsink, gboolean blocked)
3809 {
3810   if (playsink->text_pad) {
3811     GstPad *opad =
3812         GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3813             (playsink->text_pad)));
3814     if (blocked && playsink->text_block_id == 0) {
3815       playsink->text_block_id =
3816           gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3817           sinkpad_blocked_cb, playsink, NULL);
3818     } else if (!blocked && playsink->text_block_id) {
3819       gst_pad_remove_probe (opad, playsink->text_block_id);
3820       PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_TEXT);
3821       playsink->text_block_id = 0;
3822       playsink->text_pad_blocked = FALSE;
3823     }
3824     gst_object_unref (opad);
3825   }
3826 }
3827
3828 static GstPadProbeReturn
3829 sinkpad_blocked_cb (GstPad * blockedpad, GstPadProbeInfo * info,
3830     gpointer user_data)
3831 {
3832   GstPlaySink *playsink = (GstPlaySink *) user_data;
3833   GstPad *pad;
3834
3835   GST_PLAY_SINK_LOCK (playsink);
3836
3837   pad = GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (blockedpad)));
3838   if (pad == playsink->video_pad) {
3839     playsink->video_pad_blocked = TRUE;
3840     GST_DEBUG_OBJECT (pad, "Video pad blocked");
3841   } else if (pad == playsink->audio_pad) {
3842     playsink->audio_pad_blocked = TRUE;
3843     GST_DEBUG_OBJECT (pad, "Audio pad blocked");
3844   } else if (pad == playsink->text_pad) {
3845     playsink->text_pad_blocked = TRUE;
3846     GST_DEBUG_OBJECT (pad, "Text pad blocked");
3847   }
3848
3849   /* We reconfigure when for ALL streams:
3850    * * there isn't a pad
3851    * * OR the pad is blocked
3852    * * OR there are no pending blocks on that pad
3853    */
3854
3855   if ((!playsink->video_pad || playsink->video_pad_blocked
3856           || !PENDING_VIDEO_BLOCK (playsink)) && (!playsink->audio_pad
3857           || playsink->audio_pad_blocked || !PENDING_AUDIO_BLOCK (playsink))
3858       && (!playsink->text_pad || playsink->text_pad_blocked
3859           || !PENDING_TEXT_BLOCK (playsink))) {
3860     GST_DEBUG_OBJECT (playsink, "All pads blocked -- reconfiguring");
3861
3862     if (playsink->video_pad) {
3863       playsink->video_pad_raw = is_raw_pad (playsink->video_pad);
3864       GST_DEBUG_OBJECT (playsink, "Video pad is raw: %d",
3865           playsink->video_pad_raw);
3866     }
3867
3868     if (playsink->audio_pad) {
3869       playsink->audio_pad_raw = is_raw_pad (playsink->audio_pad);
3870       GST_DEBUG_OBJECT (playsink, "Audio pad is raw: %d",
3871           playsink->audio_pad_raw);
3872     }
3873
3874     gst_play_sink_reconfigure (playsink);
3875
3876     video_set_blocked (playsink, FALSE);
3877     audio_set_blocked (playsink, FALSE);
3878     text_set_blocked (playsink, FALSE);
3879   }
3880
3881   gst_object_unref (pad);
3882
3883   GST_PLAY_SINK_UNLOCK (playsink);
3884
3885   return GST_PAD_PROBE_OK;
3886 }
3887
3888 static void
3889 caps_notify_cb (GstPad * pad, GParamSpec * unused, GstPlaySink * playsink)
3890 {
3891   gboolean reconfigure = FALSE;
3892   GstCaps *caps;
3893   gboolean raw;
3894
3895   g_object_get (pad, "caps", &caps, NULL);
3896   if (!caps)
3897     return;
3898
3899   if (pad == playsink->audio_pad) {
3900     raw = is_raw_pad (pad);
3901     reconfigure = (! !playsink->audio_pad_raw != ! !raw)
3902         && playsink->audiochain;
3903     GST_DEBUG_OBJECT (pad,
3904         "Audio caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3905         reconfigure, caps);
3906   } else if (pad == playsink->video_pad) {
3907     raw = is_raw_pad (pad);
3908     reconfigure = (! !playsink->video_pad_raw != ! !raw)
3909         && playsink->videochain;
3910     GST_DEBUG_OBJECT (pad,
3911         "Video caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3912         reconfigure, caps);
3913   }
3914
3915   gst_caps_unref (caps);
3916
3917   if (reconfigure) {
3918     GST_PLAY_SINK_LOCK (playsink);
3919     video_set_blocked (playsink, TRUE);
3920     audio_set_blocked (playsink, TRUE);
3921     text_set_blocked (playsink, TRUE);
3922     GST_PLAY_SINK_UNLOCK (playsink);
3923   }
3924 }
3925
3926 void
3927 gst_play_sink_refresh_pad (GstPlaySink * playsink, GstPad * pad,
3928     GstPlaySinkType type)
3929 {
3930   gulong *block_id = NULL;
3931
3932   GST_DEBUG_OBJECT (playsink, "refresh pad %" GST_PTR_FORMAT, pad);
3933
3934   GST_PLAY_SINK_LOCK (playsink);
3935   if (pad == playsink->video_pad) {
3936     if (type != GST_PLAY_SINK_TYPE_VIDEO_RAW &&
3937         type != GST_PLAY_SINK_TYPE_VIDEO)
3938       goto wrong_type;
3939     block_id = &playsink->video_block_id;
3940   } else if (pad == playsink->audio_pad) {
3941     if (type != GST_PLAY_SINK_TYPE_AUDIO_RAW &&
3942         type != GST_PLAY_SINK_TYPE_AUDIO)
3943       goto wrong_type;
3944     block_id = &playsink->audio_block_id;
3945   } else if (pad == playsink->text_pad) {
3946     if (type != GST_PLAY_SINK_TYPE_TEXT)
3947       goto wrong_type;
3948     block_id = &playsink->text_block_id;
3949   }
3950
3951   if (type != GST_PLAY_SINK_TYPE_FLUSHING && (block_id && *block_id == 0)) {
3952     GstPad *blockpad =
3953         GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (pad)));
3954
3955     *block_id =
3956         gst_pad_add_probe (blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3957         sinkpad_blocked_cb, playsink, NULL);
3958     PENDING_FLAG_SET (playsink, type);
3959     gst_object_unref (blockpad);
3960   }
3961   GST_PLAY_SINK_UNLOCK (playsink);
3962
3963   return;
3964
3965   /* ERRORS */
3966 wrong_type:
3967   {
3968     GST_WARNING_OBJECT (playsink, "wrong type %u for pad %" GST_PTR_FORMAT,
3969         type, pad);
3970     GST_PLAY_SINK_UNLOCK (playsink);
3971     return;
3972   }
3973 }
3974
3975 /**
3976  * gst_play_sink_request_pad
3977  * @playsink: a #GstPlaySink
3978  * @type: a #GstPlaySinkType
3979  *
3980  * Create or return a pad of @type.
3981  *
3982  * Returns: a #GstPad of @type or %NULL when the pad could not be created.
3983  */
3984 GstPad *
3985 gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type)
3986 {
3987   GstPad *res = NULL;
3988   gboolean created = FALSE;
3989   gboolean activate = TRUE;
3990   const gchar *pad_name = NULL;
3991   gulong *block_id = NULL;
3992
3993   GST_DEBUG_OBJECT (playsink, "request pad type %d", type);
3994
3995   GST_PLAY_SINK_LOCK (playsink);
3996   switch (type) {
3997     case GST_PLAY_SINK_TYPE_AUDIO_RAW:
3998     case GST_PLAY_SINK_TYPE_AUDIO:
3999       pad_name = "audio_sink";
4000       if (!playsink->audio_tee) {
4001         GST_LOG_OBJECT (playsink, "creating tee");
4002         /* create tee when needed. This element will feed the audio sink chain
4003          * and the vis chain. */
4004         playsink->audio_tee = gst_element_factory_make ("tee", "audiotee");
4005         if (playsink->audio_tee == NULL) {
4006           post_missing_element_message (playsink, "tee");
4007           GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
4008               (_("Missing element '%s' - check your GStreamer installation."),
4009                   "tee"), (NULL));
4010           res = NULL;
4011           break;
4012         } else {
4013           playsink->audio_tee_sink =
4014               gst_element_get_static_pad (playsink->audio_tee, "sink");
4015           gst_bin_add (GST_BIN_CAST (playsink), playsink->audio_tee);
4016           gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
4017         }
4018       } else {
4019         gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
4020       }
4021       if (!playsink->audio_pad) {
4022         GST_LOG_OBJECT (playsink, "ghosting tee sinkpad");
4023         playsink->audio_pad =
4024             gst_ghost_pad_new (pad_name, playsink->audio_tee_sink);
4025         g_signal_connect (G_OBJECT (playsink->audio_pad), "notify::caps",
4026             G_CALLBACK (caps_notify_cb), playsink);
4027         created = TRUE;
4028       }
4029       playsink->audio_pad_raw = FALSE;
4030       res = playsink->audio_pad;
4031       block_id = &playsink->audio_block_id;
4032       break;
4033     case GST_PLAY_SINK_TYPE_VIDEO_RAW:
4034     case GST_PLAY_SINK_TYPE_VIDEO:
4035       pad_name = "video_sink";
4036       if (!playsink->video_pad) {
4037         GST_LOG_OBJECT (playsink, "ghosting videosink");
4038         playsink->video_pad =
4039             gst_ghost_pad_new_no_target (pad_name, GST_PAD_SINK);
4040         g_signal_connect (G_OBJECT (playsink->video_pad), "notify::caps",
4041             G_CALLBACK (caps_notify_cb), playsink);
4042         created = TRUE;
4043       }
4044       playsink->video_pad_raw = FALSE;
4045       res = playsink->video_pad;
4046       block_id = &playsink->video_block_id;
4047       break;
4048     case GST_PLAY_SINK_TYPE_TEXT:
4049       GST_LOG_OBJECT (playsink, "ghosting text");
4050       if (!playsink->text_pad) {
4051         playsink->text_pad =
4052             gst_ghost_pad_new_no_target ("text_sink", GST_PAD_SINK);
4053         created = TRUE;
4054       }
4055       res = playsink->text_pad;
4056       block_id = &playsink->text_block_id;
4057       break;
4058     case GST_PLAY_SINK_TYPE_FLUSHING:
4059     {
4060       gchar *padname;
4061
4062       /* we need a unique padname for the flushing pad. */
4063       padname = g_strdup_printf ("flushing_%u", playsink->count);
4064       res = gst_ghost_pad_new_no_target (padname, GST_PAD_SINK);
4065       g_free (padname);
4066       playsink->count++;
4067       activate = FALSE;
4068       created = TRUE;
4069       break;
4070     }
4071     default:
4072       res = NULL;
4073       break;
4074   }
4075   GST_PLAY_SINK_UNLOCK (playsink);
4076
4077   if (created && res) {
4078     /* we have to add the pad when it's active or we get an error when the
4079      * element is 'running' */
4080     gst_pad_set_active (res, TRUE);
4081     gst_element_add_pad (GST_ELEMENT_CAST (playsink), res);
4082     if (block_id && *block_id == 0) {
4083       GstPad *blockpad =
4084           GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (res)));
4085
4086       *block_id =
4087           gst_pad_add_probe (blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
4088           sinkpad_blocked_cb, playsink, NULL);
4089       PENDING_FLAG_SET (playsink, type);
4090       gst_object_unref (blockpad);
4091     }
4092     if (!activate)
4093       gst_pad_set_active (res, activate);
4094   }
4095
4096   return res;
4097 }
4098
4099
4100 static GstPad *
4101 gst_play_sink_request_new_pad (GstElement * element, GstPadTemplate * templ,
4102     const gchar * name, const GstCaps * caps)
4103 {
4104   GstPlaySink *psink;
4105   GstPad *pad;
4106   GstPlaySinkType type;
4107   const gchar *tplname;
4108
4109   g_return_val_if_fail (templ != NULL, NULL);
4110
4111   GST_DEBUG_OBJECT (element, "name:%s", name);
4112
4113   psink = GST_PLAY_SINK (element);
4114   tplname = GST_PAD_TEMPLATE_NAME_TEMPLATE (templ);
4115
4116   /* Figure out the GstPlaySinkType based on the template */
4117   if (!strcmp (tplname, "audio_sink"))
4118     type = GST_PLAY_SINK_TYPE_AUDIO;
4119   else if (!strcmp (tplname, "audio_raw_sink"))
4120     type = GST_PLAY_SINK_TYPE_AUDIO_RAW;
4121   else if (!strcmp (tplname, "video_sink"))
4122     type = GST_PLAY_SINK_TYPE_VIDEO;
4123   else if (!strcmp (tplname, "video_raw_sink"))
4124     type = GST_PLAY_SINK_TYPE_VIDEO_RAW;
4125   else if (!strcmp (tplname, "text_sink"))
4126     type = GST_PLAY_SINK_TYPE_TEXT;
4127   else
4128     goto unknown_template;
4129
4130   pad = gst_play_sink_request_pad (psink, type);
4131   return pad;
4132
4133 unknown_template:
4134   GST_WARNING_OBJECT (element, "Unknown pad template");
4135   return NULL;
4136 }
4137
4138 void
4139 gst_play_sink_release_pad (GstPlaySink * playsink, GstPad * pad)
4140 {
4141   GstPad **res = NULL;
4142   gboolean untarget = TRUE;
4143
4144   GST_DEBUG_OBJECT (playsink, "release pad %" GST_PTR_FORMAT, pad);
4145
4146   GST_PLAY_SINK_LOCK (playsink);
4147   if (pad == playsink->video_pad) {
4148     res = &playsink->video_pad;
4149     g_signal_handlers_disconnect_by_func (playsink->video_pad, caps_notify_cb,
4150         playsink);
4151   } else if (pad == playsink->audio_pad) {
4152     res = &playsink->audio_pad;
4153     g_signal_handlers_disconnect_by_func (playsink->audio_pad, caps_notify_cb,
4154         playsink);
4155   } else if (pad == playsink->text_pad) {
4156     res = &playsink->text_pad;
4157   } else {
4158     /* try to release the given pad anyway, these could be the FLUSHING pads. */
4159     res = &pad;
4160     untarget = FALSE;
4161   }
4162   GST_PLAY_SINK_UNLOCK (playsink);
4163
4164   if (*res) {
4165     GST_DEBUG_OBJECT (playsink, "deactivate pad %" GST_PTR_FORMAT, *res);
4166     gst_pad_set_active (*res, FALSE);
4167     if (untarget) {
4168       GST_DEBUG_OBJECT (playsink, "untargeting pad %" GST_PTR_FORMAT, *res);
4169       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (*res), NULL);
4170     }
4171     GST_DEBUG_OBJECT (playsink, "remove pad %" GST_PTR_FORMAT, *res);
4172     gst_element_remove_pad (GST_ELEMENT_CAST (playsink), *res);
4173     *res = NULL;
4174   }
4175 }
4176
4177 static void
4178 gst_play_sink_release_request_pad (GstElement * element, GstPad * pad)
4179 {
4180   GstPlaySink *psink = GST_PLAY_SINK (element);
4181
4182   gst_play_sink_release_pad (psink, pad);
4183 }
4184
4185 static void
4186 gst_play_sink_handle_message (GstBin * bin, GstMessage * message)
4187 {
4188   GstPlaySink *playsink;
4189
4190   playsink = GST_PLAY_SINK_CAST (bin);
4191
4192   switch (GST_MESSAGE_TYPE (message)) {
4193     case GST_MESSAGE_STEP_DONE:
4194     {
4195       GstFormat format;
4196       guint64 amount;
4197       gdouble rate;
4198       gboolean flush, intermediate, eos;
4199       guint64 duration;
4200
4201       GST_INFO_OBJECT (playsink, "Handling step-done message");
4202       gst_message_parse_step_done (message, &format, &amount, &rate, &flush,
4203           &intermediate, &duration, &eos);
4204
4205       if (format == GST_FORMAT_BUFFERS) {
4206         /* for the buffer format, we align the other streams */
4207         if (playsink->audiochain) {
4208           GstEvent *event;
4209
4210           event =
4211               gst_event_new_step (GST_FORMAT_TIME, duration, rate, flush,
4212               intermediate);
4213
4214           if (!gst_element_send_event (playsink->audiochain->chain.bin, event)) {
4215             GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
4216           }
4217         }
4218       }
4219       GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
4220       break;
4221     }
4222     case GST_MESSAGE_ELEMENT:{
4223       if (gst_is_video_overlay_prepare_window_handle_message (message)) {
4224         GstVideoOverlay *overlay;
4225
4226         GST_OBJECT_LOCK (playsink);
4227         if (playsink->overlay_element
4228             && GST_OBJECT_CAST (playsink->overlay_element) !=
4229             GST_MESSAGE_SRC (message)) {
4230           gst_object_unref (playsink->overlay_element);
4231           playsink->overlay_element = NULL;
4232         }
4233
4234         if (!playsink->overlay_element)
4235           playsink->overlay_element =
4236               GST_VIDEO_OVERLAY (gst_object_ref (GST_MESSAGE_SRC (message)));
4237         overlay =
4238             GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4239         GST_OBJECT_UNLOCK (playsink);
4240
4241         GST_DEBUG_OBJECT (playsink, "Got prepare-xwindow-id message");
4242
4243         if (playsink->overlay_handle_set)
4244           gst_video_overlay_set_window_handle (playsink->overlay_element,
4245               playsink->overlay_handle);
4246         if (playsink->overlay_handle_events_set)
4247           gst_video_overlay_handle_events (playsink->overlay_element,
4248               playsink->overlay_handle_events);
4249         if (playsink->overlay_render_rectangle_set)
4250           gst_video_overlay_set_render_rectangle (playsink->overlay_element,
4251               playsink->overlay_x, playsink->overlay_y,
4252               playsink->overlay_width, playsink->overlay_height);
4253
4254         gst_object_unref (overlay);
4255         gst_message_unref (message);
4256         gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (playsink));
4257       } else {
4258         GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin,
4259             message);
4260       }
4261       break;
4262     }
4263     default:
4264       GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
4265       break;
4266   }
4267 }
4268
4269 /* Send an event to our sinks until one of them works; don't then send to the
4270  * remaining sinks (unlike GstBin)
4271  * Special case: If a text sink is set we need to send the event
4272  * to them in case it's source is different from the a/v stream's source.
4273  */
4274 static gboolean
4275 gst_play_sink_send_event_to_sink (GstPlaySink * playsink, GstEvent * event)
4276 {
4277   gboolean res = TRUE;
4278   if (playsink->send_event_mode == MODE_FIRST) {
4279     if (playsink->textchain && playsink->textchain->sink) {
4280       gst_event_ref (event);
4281       if ((res =
4282               gst_element_send_event (playsink->textchain->chain.bin, event))) {
4283         GST_DEBUG_OBJECT (playsink, "Sent event successfully to text sink");
4284       } else {
4285         GST_DEBUG_OBJECT (playsink, "Event failed when sent to text sink");
4286       }
4287     }
4288
4289     if (playsink->videochain) {
4290       gst_event_ref (event);
4291       if ((res =
4292               gst_element_send_event (playsink->videochain->chain.bin,
4293                   event))) {
4294         GST_DEBUG_OBJECT (playsink, "Sent event successfully to video sink");
4295         goto done;
4296       }
4297       GST_DEBUG_OBJECT (playsink, "Event failed when sent to video sink");
4298     }
4299     if (playsink->audiochain) {
4300       gst_event_ref (event);
4301       if ((res =
4302               gst_element_send_event (playsink->audiochain->chain.bin,
4303                   event))) {
4304         GST_DEBUG_OBJECT (playsink, "Sent event successfully to audio sink");
4305         goto done;
4306       }
4307       GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
4308     }
4309   } else {
4310     return
4311         GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event
4312         (GST_ELEMENT_CAST (playsink), event);
4313   }
4314
4315 done:
4316   gst_event_unref (event);
4317   return res;
4318 }
4319
4320 /* We only want to send the event to a single sink (overriding GstBin's
4321  * behaviour), but we want to keep GstPipeline's behaviour - wrapping seek
4322  * events appropriately. So, this is a messy duplication of code. */
4323 static gboolean
4324 gst_play_sink_send_event (GstElement * element, GstEvent * event)
4325 {
4326   gboolean res = FALSE;
4327   GstEventType event_type = GST_EVENT_TYPE (event);
4328   GstPlaySink *playsink;
4329   playsink = GST_PLAY_SINK_CAST (element);
4330   switch (event_type) {
4331     case GST_EVENT_SEEK:
4332       GST_DEBUG_OBJECT (element, "Sending event to a sink");
4333       res = gst_play_sink_send_event_to_sink (playsink, event);
4334       break;
4335     case GST_EVENT_STEP:
4336     {
4337       GstFormat format;
4338       guint64 amount;
4339       gdouble rate;
4340       gboolean flush, intermediate;
4341       gst_event_parse_step (event, &format, &amount, &rate, &flush,
4342           &intermediate);
4343       if (format == GST_FORMAT_BUFFERS) {
4344         /* for buffers, we will try to step video frames, for other formats we
4345          * send the step to all sinks */
4346         res = gst_play_sink_send_event_to_sink (playsink, event);
4347       } else {
4348         res =
4349             GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
4350             event);
4351       }
4352       break;
4353     }
4354     default:
4355       res =
4356           GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
4357           event);
4358       break;
4359   }
4360   return res;
4361 }
4362
4363 static GstStateChangeReturn
4364 gst_play_sink_change_state (GstElement * element, GstStateChange transition)
4365 {
4366   GstStateChangeReturn ret;
4367   GstStateChangeReturn bret;
4368   GstPlaySink *playsink;
4369   playsink = GST_PLAY_SINK (element);
4370   switch (transition) {
4371     case GST_STATE_CHANGE_READY_TO_PAUSED:
4372       gst_segment_init (&playsink->text_segment, GST_FORMAT_UNDEFINED);
4373
4374       playsink->need_async_start = TRUE;
4375       /* we want to go async to PAUSED until we managed to configure and add the
4376        * sinks */
4377       do_async_start (playsink);
4378       ret = GST_STATE_CHANGE_ASYNC;
4379
4380       /* block all pads here */
4381       GST_PLAY_SINK_LOCK (playsink);
4382       if (playsink->video_pad && playsink->video_block_id == 0) {
4383         GstPad *opad =
4384             GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
4385                 (playsink->video_pad)));
4386         playsink->video_block_id =
4387             gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
4388             sinkpad_blocked_cb, playsink, NULL);
4389         PENDING_FLAG_SET (playsink, GST_PLAY_SINK_TYPE_VIDEO);
4390         gst_object_unref (opad);
4391       }
4392
4393       if (playsink->audio_pad && playsink->audio_block_id == 0) {
4394         GstPad *opad =
4395             GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
4396                 (playsink->audio_pad)));
4397         playsink->audio_block_id =
4398             gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
4399             sinkpad_blocked_cb, playsink, NULL);
4400         PENDING_FLAG_SET (playsink, GST_PLAY_SINK_TYPE_AUDIO);
4401         gst_object_unref (opad);
4402       }
4403
4404       if (playsink->text_pad && playsink->text_block_id == 0) {
4405         GstPad *opad =
4406             GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
4407                 (playsink->text_pad)));
4408         playsink->text_block_id =
4409             gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
4410             sinkpad_blocked_cb, playsink, NULL);
4411         PENDING_FLAG_SET (playsink, GST_PLAY_SINK_TYPE_TEXT);
4412         gst_object_unref (opad);
4413       }
4414       GST_PLAY_SINK_UNLOCK (playsink);
4415       break;
4416     case GST_STATE_CHANGE_PAUSED_TO_READY:
4417       /* unblock all pads here */
4418       GST_PLAY_SINK_LOCK (playsink);
4419       video_set_blocked (playsink, FALSE);
4420       audio_set_blocked (playsink, FALSE);
4421       text_set_blocked (playsink, FALSE);
4422       GST_PLAY_SINK_UNLOCK (playsink);
4423       /* fall through */
4424     case GST_STATE_CHANGE_READY_TO_NULL:
4425       if (playsink->audiochain && playsink->audiochain->sink_volume) {
4426         /* remove our links to the mute and volume elements when they were
4427          * provided by a sink */
4428         disconnect_chain (playsink->audiochain, playsink);
4429         playsink->audiochain->volume = NULL;
4430         playsink->audiochain->mute = NULL;
4431       }
4432
4433       if (playsink->audiochain && playsink->audiochain->ts_offset) {
4434         gst_object_unref (playsink->audiochain->ts_offset);
4435         playsink->audiochain->ts_offset = NULL;
4436       }
4437
4438       if (playsink->videochain && playsink->videochain->ts_offset) {
4439         gst_object_unref (playsink->videochain->ts_offset);
4440         playsink->videochain->ts_offset = NULL;
4441       }
4442
4443       GST_OBJECT_LOCK (playsink);
4444       if (playsink->overlay_element)
4445         gst_object_unref (playsink->overlay_element);
4446       playsink->overlay_element = NULL;
4447
4448       if (playsink->colorbalance_element) {
4449         g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
4450             G_CALLBACK (colorbalance_value_changed_cb), playsink);
4451         gst_object_unref (playsink->colorbalance_element);
4452       }
4453       playsink->colorbalance_element = NULL;
4454       GST_OBJECT_UNLOCK (playsink);
4455
4456       ret = GST_STATE_CHANGE_SUCCESS;
4457       break;
4458     default:
4459       /* all other state changes return SUCCESS by default, this value can be
4460        * overridden by the result of the children */
4461       ret = GST_STATE_CHANGE_SUCCESS;
4462       break;
4463   }
4464
4465   /* do the state change of the children */
4466   bret =
4467       GST_ELEMENT_CLASS (gst_play_sink_parent_class)->change_state (element,
4468       transition);
4469   /* now look at the result of our children and adjust the return value */
4470   switch (bret) {
4471     case GST_STATE_CHANGE_FAILURE:
4472       /* failure, we stop */
4473       goto activate_failed;
4474     case GST_STATE_CHANGE_NO_PREROLL:
4475       /* some child returned NO_PREROLL. This is strange but we never know. We
4476        * commit our async state change (if any) and return the NO_PREROLL */
4477       do_async_done (playsink);
4478       ret = bret;
4479       break;
4480     case GST_STATE_CHANGE_ASYNC:
4481       /* some child was async, return this */
4482       ret = bret;
4483       break;
4484     default:
4485       /* return our previously configured return value */
4486       break;
4487   }
4488
4489   switch (transition) {
4490     case GST_STATE_CHANGE_READY_TO_PAUSED:
4491       break;
4492     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
4493       /* FIXME Release audio device when we implement that */
4494       playsink->need_async_start = TRUE;
4495       break;
4496     case GST_STATE_CHANGE_PAUSED_TO_READY:{
4497       if (playsink->video_sinkpad_stream_synchronizer) {
4498         gst_element_release_request_pad (GST_ELEMENT_CAST
4499             (playsink->stream_synchronizer),
4500             playsink->video_sinkpad_stream_synchronizer);
4501         gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
4502         playsink->video_sinkpad_stream_synchronizer = NULL;
4503         gst_object_unref (playsink->video_srcpad_stream_synchronizer);
4504         playsink->video_srcpad_stream_synchronizer = NULL;
4505       }
4506       if (playsink->audio_sinkpad_stream_synchronizer) {
4507         gst_element_release_request_pad (GST_ELEMENT_CAST
4508             (playsink->stream_synchronizer),
4509             playsink->audio_sinkpad_stream_synchronizer);
4510         gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
4511         playsink->audio_sinkpad_stream_synchronizer = NULL;
4512         gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
4513         playsink->audio_srcpad_stream_synchronizer = NULL;
4514       }
4515       if (playsink->text_sinkpad_stream_synchronizer) {
4516         gst_element_release_request_pad (GST_ELEMENT_CAST
4517             (playsink->stream_synchronizer),
4518             playsink->text_sinkpad_stream_synchronizer);
4519         gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
4520         playsink->text_sinkpad_stream_synchronizer = NULL;
4521         gst_object_unref (playsink->text_srcpad_stream_synchronizer);
4522         playsink->text_srcpad_stream_synchronizer = NULL;
4523       }
4524     }
4525       /* fall through */
4526     case GST_STATE_CHANGE_READY_TO_NULL:
4527       /* remove sinks we added */
4528       if (playsink->videodeinterlacechain) {
4529         activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
4530             FALSE);
4531         add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
4532       }
4533       if (playsink->videochain) {
4534         activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
4535         add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
4536       }
4537       if (playsink->audiochain) {
4538         activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
4539         add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
4540       }
4541       if (playsink->vischain) {
4542         activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
4543         add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
4544       }
4545       if (playsink->textchain) {
4546         activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
4547         add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
4548       }
4549       do_async_done (playsink);
4550       /* when going to READY, keep elements around as long as possible,
4551        * so they may be re-used faster next time/url around.
4552        * when really going to NULL, clean up everything completely. */
4553       if (transition == GST_STATE_CHANGE_READY_TO_NULL) {
4554
4555         /* Unparent the sinks to allow reuse */
4556         if (playsink->videochain && playsink->videochain->sink)
4557           gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
4558               playsink->videochain->sink);
4559         if (playsink->audiochain && playsink->audiochain->sink)
4560           gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
4561               playsink->audiochain->sink);
4562         if (playsink->textchain && playsink->textchain->sink)
4563           gst_bin_remove (GST_BIN_CAST (playsink->textchain->chain.bin),
4564               playsink->textchain->sink);
4565         if (playsink->audio_sink != NULL)
4566           gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
4567         if (playsink->video_sink != NULL)
4568           gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
4569         if (playsink->visualisation != NULL)
4570           gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
4571         if (playsink->text_sink != NULL)
4572           gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
4573         free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
4574         playsink->videodeinterlacechain = NULL;
4575         free_chain ((GstPlayChain *) playsink->videochain);
4576         playsink->videochain = NULL;
4577         free_chain ((GstPlayChain *) playsink->audiochain);
4578         playsink->audiochain = NULL;
4579         free_chain ((GstPlayChain *) playsink->vischain);
4580         playsink->vischain = NULL;
4581         free_chain ((GstPlayChain *) playsink->textchain);
4582         playsink->textchain = NULL;
4583       }
4584       break;
4585     default:
4586       break;
4587   }
4588   return ret;
4589   /* ERRORS */
4590 activate_failed:
4591   {
4592     GST_DEBUG_OBJECT (element,
4593         "element failed to change states -- activation problem?");
4594     return GST_STATE_CHANGE_FAILURE;
4595   }
4596 }
4597
4598 static void
4599 gst_play_sink_set_property (GObject * object, guint prop_id,
4600     const GValue * value, GParamSpec * spec)
4601 {
4602   GstPlaySink *playsink = GST_PLAY_SINK (object);
4603   switch (prop_id) {
4604     case PROP_FLAGS:
4605       gst_play_sink_set_flags (playsink, g_value_get_flags (value));
4606       break;
4607     case PROP_VOLUME:
4608       gst_play_sink_set_volume (playsink, g_value_get_double (value));
4609       break;
4610     case PROP_MUTE:
4611       gst_play_sink_set_mute (playsink, g_value_get_boolean (value));
4612       break;
4613     case PROP_FONT_DESC:
4614       gst_play_sink_set_font_desc (playsink, g_value_get_string (value));
4615       break;
4616     case PROP_SUBTITLE_ENCODING:
4617       gst_play_sink_set_subtitle_encoding (playsink,
4618           g_value_get_string (value));
4619       break;
4620     case PROP_VIS_PLUGIN:
4621       gst_play_sink_set_vis_plugin (playsink, g_value_get_object (value));
4622       break;
4623     case PROP_AV_OFFSET:
4624       gst_play_sink_set_av_offset (playsink, g_value_get_int64 (value));
4625       break;
4626     case PROP_VIDEO_SINK:
4627       gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_VIDEO,
4628           g_value_get_object (value));
4629       break;
4630     case PROP_AUDIO_SINK:
4631       gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_AUDIO,
4632           g_value_get_object (value));
4633       break;
4634     case PROP_TEXT_SINK:
4635       gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_TEXT,
4636           g_value_get_object (value));
4637       break;
4638     case PROP_SEND_EVENT_MODE:
4639       playsink->send_event_mode = g_value_get_enum (value);
4640       break;
4641     case PROP_FORCE_ASPECT_RATIO:{
4642       GstPlayVideoChain *chain;
4643       GstElement *elem;
4644
4645       playsink->force_aspect_ratio = g_value_get_boolean (value);
4646
4647       GST_PLAY_SINK_LOCK (playsink);
4648       if (playsink->videochain) {
4649         chain = (GstPlayVideoChain *) playsink->videochain;
4650
4651         if (chain->sink) {
4652           elem =
4653               gst_play_sink_find_property_sinks (playsink, chain->sink,
4654               "force-aspect-ratio", G_TYPE_BOOLEAN);
4655
4656           if (elem)
4657             g_object_set (elem, "force-aspect-ratio",
4658                 playsink->force_aspect_ratio, NULL);
4659         }
4660       }
4661       GST_PLAY_SINK_UNLOCK (playsink);
4662       break;
4663     }
4664     default:
4665       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
4666       break;
4667   }
4668 }
4669
4670 static void
4671 gst_play_sink_get_property (GObject * object, guint prop_id,
4672     GValue * value, GParamSpec * spec)
4673 {
4674   GstPlaySink *playsink = GST_PLAY_SINK (object);
4675   switch (prop_id) {
4676     case PROP_FLAGS:
4677       g_value_set_flags (value, gst_play_sink_get_flags (playsink));
4678       break;
4679     case PROP_VOLUME:
4680       g_value_set_double (value, gst_play_sink_get_volume (playsink));
4681       break;
4682     case PROP_MUTE:
4683       g_value_set_boolean (value, gst_play_sink_get_mute (playsink));
4684       break;
4685     case PROP_FONT_DESC:
4686       g_value_take_string (value, gst_play_sink_get_font_desc (playsink));
4687       break;
4688     case PROP_SUBTITLE_ENCODING:
4689       g_value_take_string (value,
4690           gst_play_sink_get_subtitle_encoding (playsink));
4691       break;
4692     case PROP_VIS_PLUGIN:
4693       g_value_take_object (value, gst_play_sink_get_vis_plugin (playsink));
4694       break;
4695     case PROP_SAMPLE:
4696       gst_value_take_sample (value, gst_play_sink_get_last_sample (playsink));
4697       break;
4698     case PROP_AV_OFFSET:
4699       g_value_set_int64 (value, gst_play_sink_get_av_offset (playsink));
4700       break;
4701     case PROP_VIDEO_SINK:
4702       g_value_take_object (value, gst_play_sink_get_sink (playsink,
4703               GST_PLAY_SINK_TYPE_VIDEO));
4704       break;
4705     case PROP_AUDIO_SINK:
4706       g_value_take_object (value, gst_play_sink_get_sink (playsink,
4707               GST_PLAY_SINK_TYPE_AUDIO));
4708       break;
4709     case PROP_TEXT_SINK:
4710       g_value_take_object (value, gst_play_sink_get_sink (playsink,
4711               GST_PLAY_SINK_TYPE_TEXT));
4712       break;
4713     case PROP_SEND_EVENT_MODE:
4714       g_value_set_enum (value, playsink->send_event_mode);
4715       break;
4716     case PROP_FORCE_ASPECT_RATIO:
4717       g_value_set_boolean (value, playsink->force_aspect_ratio);
4718       break;
4719     default:
4720       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
4721       break;
4722   }
4723 }
4724
4725 static void
4726 gst_play_sink_overlay_expose (GstVideoOverlay * overlay)
4727 {
4728   GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4729   GstVideoOverlay *overlay_element;
4730
4731   GST_OBJECT_LOCK (playsink);
4732   if (playsink->overlay_element)
4733     overlay_element =
4734         GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4735   else
4736     overlay_element = NULL;
4737   GST_OBJECT_UNLOCK (playsink);
4738
4739   if (overlay_element) {
4740     gst_video_overlay_expose (overlay_element);
4741     gst_object_unref (overlay_element);
4742   }
4743 }
4744
4745 static void
4746 gst_play_sink_overlay_handle_events (GstVideoOverlay * overlay,
4747     gboolean handle_events)
4748 {
4749   GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4750   GstVideoOverlay *overlay_element;
4751
4752   GST_OBJECT_LOCK (playsink);
4753   if (playsink->overlay_element)
4754     overlay_element =
4755         GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4756   else
4757     overlay_element = NULL;
4758   GST_OBJECT_UNLOCK (playsink);
4759
4760   playsink->overlay_handle_events_set = TRUE;
4761   playsink->overlay_handle_events = handle_events;
4762
4763   if (overlay_element) {
4764     gst_video_overlay_handle_events (overlay_element, handle_events);
4765     gst_object_unref (overlay_element);
4766   }
4767 }
4768
4769 static void
4770 gst_play_sink_overlay_set_render_rectangle (GstVideoOverlay * overlay, gint x,
4771     gint y, gint width, gint height)
4772 {
4773   GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4774   GstVideoOverlay *overlay_element;
4775
4776   GST_OBJECT_LOCK (playsink);
4777   if (playsink->overlay_element)
4778     overlay_element =
4779         GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4780   else
4781     overlay_element = NULL;
4782   GST_OBJECT_UNLOCK (playsink);
4783
4784   playsink->overlay_render_rectangle_set = TRUE;
4785   playsink->overlay_x = x;
4786   playsink->overlay_y = y;
4787   playsink->overlay_width = width;
4788   playsink->overlay_height = height;
4789
4790   if (overlay_element) {
4791     gst_video_overlay_set_render_rectangle (overlay_element, x, y, width,
4792         height);
4793     gst_object_unref (overlay_element);
4794   }
4795 }
4796
4797 static void
4798 gst_play_sink_overlay_set_window_handle (GstVideoOverlay * overlay,
4799     guintptr handle)
4800 {
4801   GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4802   GstVideoOverlay *overlay_element;
4803
4804   GST_OBJECT_LOCK (playsink);
4805   if (playsink->overlay_element)
4806     overlay_element =
4807         GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4808   else
4809     overlay_element = NULL;
4810   GST_OBJECT_UNLOCK (playsink);
4811
4812   playsink->overlay_handle_set = TRUE;
4813   playsink->overlay_handle = handle;
4814
4815   if (overlay_element) {
4816     gst_video_overlay_set_window_handle (overlay_element, handle);
4817     gst_object_unref (overlay_element);
4818   }
4819 }
4820
4821 static void
4822 gst_play_sink_overlay_init (gpointer g_iface, gpointer g_iface_data)
4823 {
4824   GstVideoOverlayInterface *iface = (GstVideoOverlayInterface *) g_iface;
4825   iface->expose = gst_play_sink_overlay_expose;
4826   iface->handle_events = gst_play_sink_overlay_handle_events;
4827   iface->set_render_rectangle = gst_play_sink_overlay_set_render_rectangle;
4828   iface->set_window_handle = gst_play_sink_overlay_set_window_handle;
4829 }
4830
4831 static void
4832 gst_play_sink_navigation_send_event (GstNavigation * navigation,
4833     GstStructure * structure)
4834 {
4835   GstPlaySink *playsink = GST_PLAY_SINK (navigation);
4836   GstBin *bin = NULL;
4837
4838   GST_PLAY_SINK_LOCK (playsink);
4839   if (playsink->videochain && playsink->videochain->chain.bin)
4840     bin = GST_BIN (gst_object_ref (playsink->videochain->chain.bin));
4841   GST_PLAY_SINK_UNLOCK (playsink);
4842
4843   if (bin) {
4844     GstElement *nav = gst_bin_get_by_interface (bin, GST_TYPE_NAVIGATION);
4845
4846     if (nav) {
4847       gst_navigation_send_event (GST_NAVIGATION (nav), structure);
4848       structure = NULL;
4849       gst_object_unref (nav);
4850     } else {
4851       GstEvent *event = gst_event_new_navigation (structure);
4852       structure = NULL;
4853       gst_element_send_event (GST_ELEMENT (bin), event);
4854     }
4855
4856     gst_object_unref (bin);
4857   }
4858
4859   if (structure)
4860     gst_structure_free (structure);
4861 }
4862
4863 static void
4864 gst_play_sink_navigation_init (gpointer g_iface, gpointer g_iface_data)
4865 {
4866   GstNavigationInterface *iface = (GstNavigationInterface *) g_iface;
4867
4868   iface->send_event = gst_play_sink_navigation_send_event;
4869 }
4870
4871 static const GList *
4872 gst_play_sink_colorbalance_list_channels (GstColorBalance * balance)
4873 {
4874   GstPlaySink *playsink = GST_PLAY_SINK (balance);
4875
4876   return playsink->colorbalance_channels;
4877 }
4878
4879 static void
4880 gst_play_sink_colorbalance_set_value (GstColorBalance * balance,
4881     GstColorBalanceChannel * proxy, gint value)
4882 {
4883   GstPlaySink *playsink = GST_PLAY_SINK (balance);
4884   GList *l;
4885   gint i;
4886   GstColorBalance *balance_element = NULL;
4887
4888   GST_OBJECT_LOCK (playsink);
4889   if (playsink->colorbalance_element)
4890     balance_element =
4891         GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
4892   GST_OBJECT_UNLOCK (playsink);
4893
4894   for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
4895     GstColorBalanceChannel *proxy_tmp = l->data;
4896     gdouble new_val;
4897
4898     if (proxy_tmp != proxy)
4899       continue;
4900
4901     playsink->colorbalance_values[i] = value;
4902
4903     if (balance_element) {
4904       GstColorBalanceChannel *channel = NULL;
4905       const GList *channels, *k;
4906
4907       channels = gst_color_balance_list_channels (balance_element);
4908       for (k = channels; k; k = k->next) {
4909         GstColorBalanceChannel *tmp = l->data;
4910
4911         if (g_strrstr (tmp->label, proxy->label)) {
4912           channel = tmp;
4913           break;
4914         }
4915       }
4916
4917       g_assert (channel);
4918
4919       /* Convert to [0, 1] range */
4920       new_val =
4921           ((gdouble) value -
4922           (gdouble) proxy->min_value) / ((gdouble) proxy->max_value -
4923           (gdouble) proxy->min_value);
4924       /* Convert to channel range */
4925       new_val =
4926           channel->min_value + new_val * ((gdouble) channel->max_value -
4927           (gdouble) channel->min_value);
4928
4929       gst_color_balance_set_value (balance_element, channel,
4930           (gint) (new_val + 0.5));
4931
4932       gst_object_unref (balance_element);
4933     }
4934
4935     gst_color_balance_value_changed (balance, proxy, value);
4936     break;
4937   }
4938 }
4939
4940 static gint
4941 gst_play_sink_colorbalance_get_value (GstColorBalance * balance,
4942     GstColorBalanceChannel * proxy)
4943 {
4944   GstPlaySink *playsink = GST_PLAY_SINK (balance);
4945   GList *l;
4946   gint i;
4947
4948   for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
4949     GstColorBalanceChannel *proxy_tmp = l->data;
4950
4951     if (proxy_tmp != proxy)
4952       continue;
4953
4954     return playsink->colorbalance_values[i];
4955   }
4956
4957   g_return_val_if_reached (0);
4958 }
4959
4960 static GstColorBalanceType
4961 gst_play_sink_colorbalance_get_balance_type (GstColorBalance * balance)
4962 {
4963   GstPlaySink *playsink = GST_PLAY_SINK (balance);
4964   GstColorBalance *balance_element = NULL;
4965   GstColorBalanceType t = GST_COLOR_BALANCE_SOFTWARE;
4966
4967   GST_OBJECT_LOCK (playsink);
4968   if (playsink->colorbalance_element)
4969     balance_element =
4970         GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
4971   GST_OBJECT_UNLOCK (playsink);
4972
4973   if (balance_element) {
4974     t = gst_color_balance_get_balance_type (balance_element);
4975     gst_object_unref (balance_element);
4976   }
4977
4978   return t;
4979 }
4980
4981 static void
4982 gst_play_sink_colorbalance_init (gpointer g_iface, gpointer g_iface_data)
4983 {
4984   GstColorBalanceInterface *iface = (GstColorBalanceInterface *) g_iface;
4985
4986   iface->list_channels = gst_play_sink_colorbalance_list_channels;
4987   iface->set_value = gst_play_sink_colorbalance_set_value;
4988   iface->get_value = gst_play_sink_colorbalance_get_value;
4989   iface->get_balance_type = gst_play_sink_colorbalance_get_balance_type;
4990 }
4991
4992 gboolean
4993 gst_play_sink_plugin_init (GstPlugin * plugin)
4994 {
4995   GST_DEBUG_CATEGORY_INIT (gst_play_sink_debug, "playsink", 0, "play bin");
4996   return gst_element_register (plugin, "playsink", GST_RANK_NONE,
4997       GST_TYPE_PLAY_SINK);
4998 }