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