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