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