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