326baa691707c7def411d897248112241341a0f6
[framework/multimedia/gst-plugins-base0.10.git] / gst / playback / gstplaysink.c
1 /* GStreamer
2  * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include <string.h>
25 #include <gst/gst.h>
26
27 #include <gst/gst-i18n-plugin.h>
28 #include <gst/pbutils/pbutils.h>
29 #include <gst/video/video.h>
30
31 #include "gstplaysink.h"
32 #include "gststreamsynchronizer.h"
33
34 GST_DEBUG_CATEGORY_STATIC (gst_play_sink_debug);
35 #define GST_CAT_DEFAULT gst_play_sink_debug
36
37 #define VOLUME_MAX_DOUBLE 10.0
38
39 #define DEFAULT_FLAGS             GST_PLAY_FLAG_AUDIO | GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_TEXT | \
40                                   GST_PLAY_FLAG_SOFT_VOLUME
41
42 #define GST_PLAY_CHAIN(c) ((GstPlayChain *)(c))
43
44 /* holds the common data fields for the audio and video pipelines. We keep them
45  * in a structure to more easily have all the info available. */
46 typedef struct
47 {
48   GstPlaySink *playsink;
49   GstElement *bin;
50   gboolean added;
51   gboolean activated;
52   gboolean raw;
53 } GstPlayChain;
54
55 typedef struct
56 {
57   GstPlayChain chain;
58   GstPad *sinkpad;
59   GstElement *queue;
60   GstElement *conv;
61   GstElement *resample;
62   GstElement *volume;           /* element with the volume property */
63   gboolean sink_volume;         /* if the volume was provided by the sink */
64   GstElement *mute;             /* element with the mute property */
65   GstElement *sink;
66   GstElement *ts_offset;
67 } GstPlayAudioChain;
68
69 typedef struct
70 {
71   GstPlayChain chain;
72   GstPad *sinkpad, *srcpad;
73   GstElement *conv;
74   GstElement *deinterlace;
75 } GstPlayVideoDeinterlaceChain;
76
77 typedef struct
78 {
79   GstPlayChain chain;
80   GstPad *sinkpad;
81   GstElement *queue;
82   GstElement *conv;
83   GstElement *scale;
84   GstElement *sink;
85   gboolean async;
86   GstElement *ts_offset;
87 } GstPlayVideoChain;
88
89 typedef struct
90 {
91   GstPlayChain chain;
92   GstPad *sinkpad;
93   GstElement *queue;
94   GstElement *conv;
95   GstElement *resample;
96   GstPad *blockpad;             /* srcpad of resample, used for switching the vis */
97   GstPad *vissinkpad;           /* visualisation sinkpad, */
98   GstElement *vis;
99   GstPad *vissrcpad;            /* visualisation srcpad, */
100   GstPad *srcpad;               /* outgoing srcpad, used to connect to the next
101                                  * chain */
102 } GstPlayVisChain;
103
104 typedef struct
105 {
106   GstPlayChain chain;
107   GstPad *sinkpad;
108   GstElement *queue;
109   GstElement *identity;
110   GstElement *overlay;
111   GstPad *videosinkpad;
112   GstPad *textsinkpad;
113   GstPad *srcpad;               /* outgoing srcpad, used to connect to the next
114                                  * chain */
115   GstElement *sink;             /* custom sink to receive subtitle buffers */
116 } GstPlayTextChain;
117
118 #define GST_PLAY_SINK_GET_LOCK(playsink) (&((GstPlaySink *)playsink)->lock)
119 #define GST_PLAY_SINK_LOCK(playsink)     G_STMT_START { \
120   GST_LOG_OBJECT (playsink, "locking from thread %p", g_thread_self ()); \
121   g_static_rec_mutex_lock (GST_PLAY_SINK_GET_LOCK (playsink)); \
122   GST_LOG_OBJECT (playsink, "locked from thread %p", g_thread_self ()); \
123 } G_STMT_END
124 #define GST_PLAY_SINK_UNLOCK(playsink)   G_STMT_START { \
125   GST_LOG_OBJECT (playsink, "unlocking from thread %p", g_thread_self ()); \
126   g_static_rec_mutex_unlock (GST_PLAY_SINK_GET_LOCK (playsink)); \
127 } G_STMT_END
128
129 struct _GstPlaySink
130 {
131   GstBin bin;
132
133   GStaticRecMutex lock;
134
135   gboolean async_pending;
136   gboolean need_async_start;
137
138   GstPlayFlags flags;
139
140   GstStreamSynchronizer *stream_synchronizer;
141
142   /* chains */
143   GstPlayAudioChain *audiochain;
144   GstPlayVideoDeinterlaceChain *videodeinterlacechain;
145   GstPlayVideoChain *videochain;
146   GstPlayVisChain *vischain;
147   GstPlayTextChain *textchain;
148
149   /* audio */
150   GstPad *audio_pad;
151   gboolean audio_pad_raw;
152   GstPad *audio_srcpad_stream_synchronizer;
153   GstPad *audio_sinkpad_stream_synchronizer;
154   /* audio tee */
155   GstElement *audio_tee;
156   GstPad *audio_tee_sink;
157   GstPad *audio_tee_asrc;
158   GstPad *audio_tee_vissrc;
159   /* video */
160   GstPad *video_pad;
161   gboolean video_pad_raw;
162   GstPad *video_srcpad_stream_synchronizer;
163   GstPad *video_sinkpad_stream_synchronizer;
164   /* text */
165   GstPad *text_pad;
166   GstPad *text_srcpad_stream_synchronizer;
167   GstPad *text_sinkpad_stream_synchronizer;
168
169   /* properties */
170   GstElement *audio_sink;
171   GstElement *video_sink;
172   GstElement *visualisation;
173   GstElement *text_sink;
174   gdouble volume;
175   gboolean mute;
176   gchar *font_desc;             /* font description */
177   gchar *subtitle_encoding;     /* subtitle encoding */
178   guint connection_speed;       /* connection speed in bits/sec (0 = unknown) */
179   gint count;
180   gboolean volume_changed;      /* volume/mute changed while no audiochain */
181   gboolean mute_changed;        /* ... has been created yet */
182   gint64 av_offset;
183 };
184
185 struct _GstPlaySinkClass
186 {
187   GstBinClass parent_class;
188
189     gboolean (*reconfigure) (GstPlaySink * playsink);
190
191   GstBuffer *(*convert_frame) (GstPlaySink * playsink, GstCaps * caps);
192 };
193
194 static GstStaticPadTemplate audiorawtemplate =
195 GST_STATIC_PAD_TEMPLATE ("audio_raw_sink",
196     GST_PAD_SINK,
197     GST_PAD_REQUEST,
198     GST_STATIC_CAPS_ANY);
199 static GstStaticPadTemplate audiotemplate =
200 GST_STATIC_PAD_TEMPLATE ("audio_sink",
201     GST_PAD_SINK,
202     GST_PAD_REQUEST,
203     GST_STATIC_CAPS_ANY);
204 static GstStaticPadTemplate videorawtemplate =
205 GST_STATIC_PAD_TEMPLATE ("video_raw_sink",
206     GST_PAD_SINK,
207     GST_PAD_REQUEST,
208     GST_STATIC_CAPS_ANY);
209 static GstStaticPadTemplate videotemplate =
210 GST_STATIC_PAD_TEMPLATE ("video_sink",
211     GST_PAD_SINK,
212     GST_PAD_REQUEST,
213     GST_STATIC_CAPS_ANY);
214 static GstStaticPadTemplate texttemplate = GST_STATIC_PAD_TEMPLATE ("text_sink",
215     GST_PAD_SINK,
216     GST_PAD_REQUEST,
217     GST_STATIC_CAPS_ANY);
218
219 /* props */
220 enum
221 {
222   PROP_0,
223   PROP_FLAGS,
224   PROP_MUTE,
225   PROP_VOLUME,
226   PROP_FONT_DESC,
227   PROP_SUBTITLE_ENCODING,
228   PROP_VIS_PLUGIN,
229   PROP_FRAME,
230   PROP_AV_OFFSET,
231   PROP_LAST
232 };
233
234 /* signals */
235 enum
236 {
237   LAST_SIGNAL
238 };
239
240 static void gst_play_sink_dispose (GObject * object);
241 static void gst_play_sink_finalize (GObject * object);
242 static void gst_play_sink_set_property (GObject * object, guint prop_id,
243     const GValue * value, GParamSpec * spec);
244 static void gst_play_sink_get_property (GObject * object, guint prop_id,
245     GValue * value, GParamSpec * spec);
246
247 static GstPad *gst_play_sink_request_new_pad (GstElement * element,
248     GstPadTemplate * templ, const gchar * name);
249 static void gst_play_sink_release_request_pad (GstElement * element,
250     GstPad * pad);
251 static gboolean gst_play_sink_send_event (GstElement * element,
252     GstEvent * event);
253 static GstStateChangeReturn gst_play_sink_change_state (GstElement * element,
254     GstStateChange transition);
255
256 static void gst_play_sink_handle_message (GstBin * bin, GstMessage * message);
257
258 static void notify_volume_cb (GObject * object, GParamSpec * pspec,
259     GstPlaySink * playsink);
260 static void notify_mute_cb (GObject * object, GParamSpec * pspec,
261     GstPlaySink * playsink);
262
263 static void update_av_offset (GstPlaySink * playsink);
264
265 void
266 gst_play_marshal_BUFFER__BOXED (GClosure * closure,
267     GValue * return_value G_GNUC_UNUSED,
268     guint n_param_values,
269     const GValue * param_values,
270     gpointer invocation_hint G_GNUC_UNUSED, gpointer marshal_data)
271 {
272   typedef GstBuffer *(*GMarshalFunc_OBJECT__BOXED) (gpointer data1,
273       gpointer arg_1, gpointer data2);
274   register GMarshalFunc_OBJECT__BOXED callback;
275   register GCClosure *cc = (GCClosure *) closure;
276   register gpointer data1, data2;
277   GstBuffer *v_return;
278   g_return_if_fail (return_value != NULL);
279   g_return_if_fail (n_param_values == 2);
280
281   if (G_CCLOSURE_SWAP_DATA (closure)) {
282     data1 = closure->data;
283     data2 = g_value_peek_pointer (param_values + 0);
284   } else {
285     data1 = g_value_peek_pointer (param_values + 0);
286     data2 = closure->data;
287   }
288   callback =
289       (GMarshalFunc_OBJECT__BOXED) (marshal_data ? marshal_data : cc->callback);
290
291   v_return = callback (data1, g_value_get_boxed (param_values + 1), data2);
292
293   gst_value_take_buffer (return_value, v_return);
294 }
295
296 /* static guint gst_play_sink_signals[LAST_SIGNAL] = { 0 }; */
297
298 G_DEFINE_TYPE (GstPlaySink, gst_play_sink, GST_TYPE_BIN);
299
300 static void
301 gst_play_sink_class_init (GstPlaySinkClass * klass)
302 {
303   GObjectClass *gobject_klass;
304   GstElementClass *gstelement_klass;
305   GstBinClass *gstbin_klass;
306
307   gobject_klass = (GObjectClass *) klass;
308   gstelement_klass = (GstElementClass *) klass;
309   gstbin_klass = (GstBinClass *) klass;
310
311   gobject_klass->dispose = gst_play_sink_dispose;
312   gobject_klass->finalize = gst_play_sink_finalize;
313   gobject_klass->set_property = gst_play_sink_set_property;
314   gobject_klass->get_property = gst_play_sink_get_property;
315
316
317   /**
318    * GstPlaySink:flags
319    *
320    * Control the behaviour of playsink.
321    */
322   g_object_class_install_property (gobject_klass, PROP_FLAGS,
323       g_param_spec_flags ("flags", "Flags", "Flags to control behaviour",
324           GST_TYPE_PLAY_FLAGS, DEFAULT_FLAGS,
325           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
326
327   /**
328    * GstPlaySink:volume:
329    *
330    * Get or set the current audio stream volume. 1.0 means 100%,
331    * 0.0 means mute. This uses a linear volume scale.
332    *
333    */
334   g_object_class_install_property (gobject_klass, PROP_VOLUME,
335       g_param_spec_double ("volume", "Volume", "The audio volume, 1.0=100%",
336           0.0, VOLUME_MAX_DOUBLE, 1.0,
337           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
338   g_object_class_install_property (gobject_klass, PROP_MUTE,
339       g_param_spec_boolean ("mute", "Mute",
340           "Mute the audio channel without changing the volume", FALSE,
341           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
342   g_object_class_install_property (gobject_klass, PROP_FONT_DESC,
343       g_param_spec_string ("subtitle-font-desc",
344           "Subtitle font description",
345           "Pango font description of font "
346           "to be used for subtitle rendering", NULL,
347           G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
348   g_object_class_install_property (gobject_klass, PROP_SUBTITLE_ENCODING,
349       g_param_spec_string ("subtitle-encoding", "subtitle encoding",
350           "Encoding to assume if input subtitles are not in UTF-8 encoding. "
351           "If not set, the GST_SUBTITLE_ENCODING environment variable will "
352           "be checked for an encoding to use. If that is not set either, "
353           "ISO-8859-15 will be assumed.", NULL,
354           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
355   g_object_class_install_property (gobject_klass, PROP_VIS_PLUGIN,
356       g_param_spec_object ("vis-plugin", "Vis plugin",
357           "the visualization element to use (NULL = default)",
358           GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
359   /**
360    * GstPlaySink:frame:
361    *
362    * Get the currently rendered or prerolled frame in the video sink.
363    * The #GstCaps on the buffer will describe the format of the buffer.
364    *
365    * Since: 0.10.30
366    */
367   g_object_class_install_property (gobject_klass, PROP_FRAME,
368       gst_param_spec_mini_object ("frame", "Frame",
369           "The last frame (NULL = no video available)",
370           GST_TYPE_BUFFER, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
371   /**
372    * GstPlaySink:av-offset:
373    *
374    * Control the synchronisation offset between the audio and video streams.
375    * Positive values make the audio ahead of the video and negative values make
376    * the audio go behind the video.
377    *
378    * Since: 0.10.30
379    */
380   g_object_class_install_property (gobject_klass, PROP_AV_OFFSET,
381       g_param_spec_int64 ("av-offset", "AV Offset",
382           "The synchronisation offset between audio and video in nanoseconds",
383           G_MININT64, G_MAXINT64, 0,
384           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
385
386   g_signal_new ("reconfigure", G_TYPE_FROM_CLASS (klass),
387       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstPlaySinkClass,
388           reconfigure), NULL, NULL, gst_marshal_BOOLEAN__VOID, G_TYPE_BOOLEAN,
389       0, G_TYPE_NONE);
390   /**
391    * GstPlaySink::convert-frame
392    * @playsink: a #GstPlaySink
393    * @caps: the target format of the frame
394    *
395    * Action signal to retrieve the currently playing video frame in the format
396    * specified by @caps.
397    * If @caps is %NULL, no conversion will be performed and this function is
398    * equivalent to the #GstPlaySink::frame property.
399    *
400    * Returns: a #GstBuffer of the current video frame converted to #caps.
401    * The caps on the buffer will describe the final layout of the buffer data.
402    * %NULL is returned when no current buffer can be retrieved or when the
403    * conversion failed.
404    *
405    * Since: 0.10.30
406    */
407   g_signal_new ("convert-frame", G_TYPE_FROM_CLASS (klass),
408       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
409       G_STRUCT_OFFSET (GstPlaySinkClass, convert_frame), NULL, NULL,
410       gst_play_marshal_BUFFER__BOXED, GST_TYPE_BUFFER, 1, GST_TYPE_CAPS);
411
412   gst_element_class_add_pad_template (gstelement_klass,
413       gst_static_pad_template_get (&audiorawtemplate));
414   gst_element_class_add_pad_template (gstelement_klass,
415       gst_static_pad_template_get (&audiotemplate));
416   gst_element_class_add_pad_template (gstelement_klass,
417       gst_static_pad_template_get (&videorawtemplate));
418   gst_element_class_add_pad_template (gstelement_klass,
419       gst_static_pad_template_get (&videotemplate));
420   gst_element_class_add_pad_template (gstelement_klass,
421       gst_static_pad_template_get (&texttemplate));
422   gst_element_class_set_details_simple (gstelement_klass, "Player Sink",
423       "Generic/Bin/Sink",
424       "Convenience sink for multiple streams",
425       "Wim Taymans <wim.taymans@gmail.com>");
426
427   gstelement_klass->change_state =
428       GST_DEBUG_FUNCPTR (gst_play_sink_change_state);
429   gstelement_klass->send_event = GST_DEBUG_FUNCPTR (gst_play_sink_send_event);
430   gstelement_klass->request_new_pad =
431       GST_DEBUG_FUNCPTR (gst_play_sink_request_new_pad);
432   gstelement_klass->release_pad =
433       GST_DEBUG_FUNCPTR (gst_play_sink_release_request_pad);
434
435   gstbin_klass->handle_message =
436       GST_DEBUG_FUNCPTR (gst_play_sink_handle_message);
437
438   klass->reconfigure = GST_DEBUG_FUNCPTR (gst_play_sink_reconfigure);
439   klass->convert_frame = GST_DEBUG_FUNCPTR (gst_play_sink_convert_frame);
440 }
441
442 static void
443 gst_play_sink_init (GstPlaySink * playsink)
444 {
445   /* init groups */
446   playsink->video_sink = NULL;
447   playsink->audio_sink = NULL;
448   playsink->visualisation = NULL;
449   playsink->text_sink = NULL;
450   playsink->volume = 1.0;
451   playsink->font_desc = NULL;
452   playsink->subtitle_encoding = NULL;
453   playsink->flags = DEFAULT_FLAGS;
454
455   playsink->stream_synchronizer =
456       g_object_new (GST_TYPE_STREAM_SYNCHRONIZER, NULL);
457   gst_bin_add (GST_BIN_CAST (playsink),
458       GST_ELEMENT_CAST (playsink->stream_synchronizer));
459
460   g_static_rec_mutex_init (&playsink->lock);
461   GST_OBJECT_FLAG_SET (playsink, GST_ELEMENT_IS_SINK);
462 }
463
464 static void
465 disconnect_chain (GstPlayAudioChain * chain, GstPlaySink * playsink)
466 {
467   if (chain) {
468     if (chain->volume)
469       g_signal_handlers_disconnect_by_func (chain->volume, notify_volume_cb,
470           playsink);
471     if (chain->mute)
472       g_signal_handlers_disconnect_by_func (chain->mute, notify_mute_cb,
473           playsink);
474   }
475 }
476
477 static void
478 free_chain (GstPlayChain * chain)
479 {
480   if (chain) {
481     if (chain->bin)
482       gst_object_unref (chain->bin);
483     g_free (chain);
484   }
485 }
486
487 static void
488 gst_play_sink_dispose (GObject * object)
489 {
490   GstPlaySink *playsink;
491
492   playsink = GST_PLAY_SINK (object);
493
494   if (playsink->audio_sink != NULL) {
495     gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
496     gst_object_unref (playsink->audio_sink);
497     playsink->audio_sink = NULL;
498   }
499   if (playsink->video_sink != NULL) {
500     gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
501     gst_object_unref (playsink->video_sink);
502     playsink->video_sink = NULL;
503   }
504   if (playsink->visualisation != NULL) {
505     gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
506     gst_object_unref (playsink->visualisation);
507     playsink->visualisation = NULL;
508   }
509   if (playsink->text_sink != NULL) {
510     gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
511     gst_object_unref (playsink->text_sink);
512     playsink->text_sink = NULL;
513   }
514
515   free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
516   playsink->videodeinterlacechain = NULL;
517   free_chain ((GstPlayChain *) playsink->videochain);
518   playsink->videochain = NULL;
519   free_chain ((GstPlayChain *) playsink->audiochain);
520   playsink->audiochain = NULL;
521   free_chain ((GstPlayChain *) playsink->vischain);
522   playsink->vischain = NULL;
523   free_chain ((GstPlayChain *) playsink->textchain);
524   playsink->textchain = NULL;
525
526   if (playsink->audio_tee_sink) {
527     gst_object_unref (playsink->audio_tee_sink);
528     playsink->audio_tee_sink = NULL;
529   }
530
531   if (playsink->audio_tee_vissrc) {
532     gst_element_release_request_pad (playsink->audio_tee,
533         playsink->audio_tee_vissrc);
534     gst_object_unref (playsink->audio_tee_vissrc);
535     playsink->audio_tee_vissrc = NULL;
536   }
537
538   if (playsink->audio_tee_asrc) {
539     gst_element_release_request_pad (playsink->audio_tee,
540         playsink->audio_tee_asrc);
541     gst_object_unref (playsink->audio_tee_asrc);
542     playsink->audio_tee_asrc = NULL;
543   }
544
545   g_free (playsink->font_desc);
546   playsink->font_desc = NULL;
547
548   g_free (playsink->subtitle_encoding);
549   playsink->subtitle_encoding = NULL;
550
551   playsink->stream_synchronizer = NULL;
552
553   G_OBJECT_CLASS (gst_play_sink_parent_class)->dispose (object);
554 }
555
556 static void
557 gst_play_sink_finalize (GObject * object)
558 {
559   GstPlaySink *playsink;
560
561   playsink = GST_PLAY_SINK (object);
562
563   g_static_rec_mutex_free (&playsink->lock);
564
565   G_OBJECT_CLASS (gst_play_sink_parent_class)->finalize (object);
566 }
567
568 void
569 gst_play_sink_set_sink (GstPlaySink * playsink, GstPlaySinkType type,
570     GstElement * sink)
571 {
572   GstElement **elem = NULL, *old = NULL;
573
574   GST_LOG ("Setting sink %" GST_PTR_FORMAT " as sink type %d", sink, type);
575
576   GST_PLAY_SINK_LOCK (playsink);
577   switch (type) {
578     case GST_PLAY_SINK_TYPE_AUDIO:
579     case GST_PLAY_SINK_TYPE_AUDIO_RAW:
580       elem = &playsink->audio_sink;
581       break;
582     case GST_PLAY_SINK_TYPE_VIDEO:
583     case GST_PLAY_SINK_TYPE_VIDEO_RAW:
584       elem = &playsink->video_sink;
585       break;
586     case GST_PLAY_SINK_TYPE_TEXT:
587       elem = &playsink->text_sink;
588       break;
589     default:
590       break;
591   }
592   if (elem) {
593     old = *elem;
594     if (sink)
595       gst_object_ref (sink);
596     *elem = sink;
597   }
598   GST_PLAY_SINK_UNLOCK (playsink);
599
600   if (old) {
601     if (old != sink)
602       gst_element_set_state (old, GST_STATE_NULL);
603     gst_object_unref (old);
604   }
605 }
606
607 GstElement *
608 gst_play_sink_get_sink (GstPlaySink * playsink, GstPlaySinkType type)
609 {
610   GstElement *result = NULL;
611   GstElement *elem = NULL, *chainp = NULL;
612
613   GST_PLAY_SINK_LOCK (playsink);
614   switch (type) {
615     case GST_PLAY_SINK_TYPE_AUDIO:
616     {
617       GstPlayAudioChain *chain;
618       if ((chain = (GstPlayAudioChain *) playsink->audiochain))
619         chainp = chain->sink;
620       elem = playsink->audio_sink;
621       break;
622     }
623     case GST_PLAY_SINK_TYPE_VIDEO:
624     {
625       GstPlayVideoChain *chain;
626       if ((chain = (GstPlayVideoChain *) playsink->videochain))
627         chainp = chain->sink;
628       elem = playsink->video_sink;
629       break;
630     }
631     case GST_PLAY_SINK_TYPE_TEXT:
632     {
633       GstPlayTextChain *chain;
634       if ((chain = (GstPlayTextChain *) playsink->textchain))
635         chainp = chain->sink;
636       elem = playsink->text_sink;
637       break;
638     }
639     default:
640       break;
641   }
642   if (chainp) {
643     /* we have an active chain with a sink, get the sink */
644     result = gst_object_ref (chainp);
645   }
646   /* nothing found, return last configured sink */
647   if (result == NULL && elem)
648     result = gst_object_ref (elem);
649   GST_PLAY_SINK_UNLOCK (playsink);
650
651   return result;
652 }
653
654 static void
655 gst_play_sink_vis_unblocked (GstPad * tee_pad, gboolean blocked,
656     gpointer user_data)
657 {
658   GstPlaySink *playsink;
659
660   playsink = GST_PLAY_SINK (user_data);
661   /* nothing to do here, we need a dummy callback here to make the async call
662    * non-blocking. */
663   GST_DEBUG_OBJECT (playsink, "vis pad unblocked");
664 }
665
666 static void
667 gst_play_sink_vis_blocked (GstPad * tee_pad, gboolean blocked,
668     gpointer user_data)
669 {
670   GstPlaySink *playsink;
671   GstPlayVisChain *chain;
672
673   playsink = GST_PLAY_SINK (user_data);
674
675   GST_PLAY_SINK_LOCK (playsink);
676   GST_DEBUG_OBJECT (playsink, "vis pad blocked");
677   /* now try to change the plugin in the running vis chain */
678   if (!(chain = (GstPlayVisChain *) playsink->vischain))
679     goto done;
680
681   /* unlink the old plugin and unghost the pad */
682   gst_pad_unlink (chain->blockpad, chain->vissinkpad);
683   gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad), NULL);
684
685   /* set the old plugin to NULL and remove */
686   gst_element_set_state (chain->vis, GST_STATE_NULL);
687   gst_bin_remove (GST_BIN_CAST (chain->chain.bin), chain->vis);
688
689   /* add new plugin and set state to playing */
690   chain->vis = playsink->visualisation;
691   gst_bin_add (GST_BIN_CAST (chain->chain.bin), chain->vis);
692   gst_element_set_state (chain->vis, GST_STATE_PLAYING);
693
694   /* get pads */
695   chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
696   chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
697
698   /* link pads */
699   gst_pad_link_full (chain->blockpad, chain->vissinkpad,
700       GST_PAD_LINK_CHECK_NOTHING);
701   gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad),
702       chain->vissrcpad);
703
704 done:
705   /* Unblock the pad */
706   gst_pad_set_blocked_async (tee_pad, FALSE, gst_play_sink_vis_unblocked,
707       playsink);
708   GST_PLAY_SINK_UNLOCK (playsink);
709 }
710
711 void
712 gst_play_sink_set_vis_plugin (GstPlaySink * playsink, GstElement * vis)
713 {
714   GstPlayVisChain *chain;
715
716   /* setting NULL means creating the default vis plugin */
717   if (vis == NULL)
718     vis = gst_element_factory_make ("goom", "vis");
719
720   /* simply return if we don't have a vis plugin here */
721   if (vis == NULL)
722     return;
723
724   GST_PLAY_SINK_LOCK (playsink);
725   /* first store the new vis */
726   if (playsink->visualisation)
727     gst_object_unref (playsink->visualisation);
728   /* take ownership */
729   gst_object_ref_sink (vis);
730   playsink->visualisation = vis;
731
732   /* now try to change the plugin in the running vis chain, if we have no chain,
733    * we don't bother, any future vis chain will be created with the new vis
734    * plugin. */
735   if (!(chain = (GstPlayVisChain *) playsink->vischain))
736     goto done;
737
738   /* block the pad, the next time the callback is called we can change the
739    * visualisation. It's possible that this never happens or that the pad was
740    * already blocked. If the callback never happens, we don't have new data so
741    * we don't need the new vis plugin. If the pad was already blocked, the
742    * function returns FALSE but the previous pad block will do the right thing
743    * anyway. */
744   GST_DEBUG_OBJECT (playsink, "blocking vis pad");
745   gst_pad_set_blocked_async (chain->blockpad, TRUE, gst_play_sink_vis_blocked,
746       playsink);
747 done:
748   GST_PLAY_SINK_UNLOCK (playsink);
749
750   return;
751 }
752
753 GstElement *
754 gst_play_sink_get_vis_plugin (GstPlaySink * playsink)
755 {
756   GstElement *result = NULL;
757   GstPlayVisChain *chain;
758
759   GST_PLAY_SINK_LOCK (playsink);
760   if ((chain = (GstPlayVisChain *) playsink->vischain)) {
761     /* we have an active chain, get the sink */
762     if (chain->vis)
763       result = gst_object_ref (chain->vis);
764   }
765   /* nothing found, return last configured sink */
766   if (result == NULL && playsink->visualisation)
767     result = gst_object_ref (playsink->visualisation);
768   GST_PLAY_SINK_UNLOCK (playsink);
769
770   return result;
771 }
772
773 void
774 gst_play_sink_set_volume (GstPlaySink * playsink, gdouble volume)
775 {
776   GstPlayAudioChain *chain;
777
778   GST_PLAY_SINK_LOCK (playsink);
779   playsink->volume = volume;
780   chain = (GstPlayAudioChain *) playsink->audiochain;
781   if (chain && chain->volume) {
782     GST_LOG_OBJECT (playsink, "elements: volume=%" GST_PTR_FORMAT ", mute=%"
783         GST_PTR_FORMAT "; new volume=%.03f, mute=%d", chain->volume,
784         chain->mute, volume, playsink->mute);
785     /* if there is a mute element or we are not muted, set the volume */
786     if (chain->mute || !playsink->mute)
787       g_object_set (chain->volume, "volume", volume, NULL);
788   } else {
789     GST_LOG_OBJECT (playsink, "no volume element");
790     playsink->volume_changed = TRUE;
791   }
792   GST_PLAY_SINK_UNLOCK (playsink);
793 }
794
795 gdouble
796 gst_play_sink_get_volume (GstPlaySink * playsink)
797 {
798   gdouble result;
799   GstPlayAudioChain *chain;
800
801   GST_PLAY_SINK_LOCK (playsink);
802   chain = (GstPlayAudioChain *) playsink->audiochain;
803   result = playsink->volume;
804   if (chain && chain->volume) {
805     if (chain->mute || !playsink->mute) {
806       g_object_get (chain->volume, "volume", &result, NULL);
807       playsink->volume = result;
808     }
809   }
810   GST_PLAY_SINK_UNLOCK (playsink);
811
812   return result;
813 }
814
815 void
816 gst_play_sink_set_mute (GstPlaySink * playsink, gboolean mute)
817 {
818   GstPlayAudioChain *chain;
819
820   GST_PLAY_SINK_LOCK (playsink);
821   playsink->mute = mute;
822   chain = (GstPlayAudioChain *) playsink->audiochain;
823   if (chain) {
824     if (chain->mute) {
825       g_object_set (chain->mute, "mute", mute, NULL);
826     } else if (chain->volume) {
827       if (mute)
828         g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
829       else
830         g_object_set (chain->volume, "volume", (gdouble) playsink->volume,
831             NULL);
832     }
833   } else {
834     playsink->mute_changed = TRUE;
835   }
836   GST_PLAY_SINK_UNLOCK (playsink);
837 }
838
839 gboolean
840 gst_play_sink_get_mute (GstPlaySink * playsink)
841 {
842   gboolean result;
843   GstPlayAudioChain *chain;
844
845   GST_PLAY_SINK_LOCK (playsink);
846   chain = (GstPlayAudioChain *) playsink->audiochain;
847   if (chain && chain->mute) {
848     g_object_get (chain->mute, "mute", &result, NULL);
849     playsink->mute = result;
850   } else {
851     result = playsink->mute;
852   }
853   GST_PLAY_SINK_UNLOCK (playsink);
854
855   return result;
856 }
857
858 static void
859 post_missing_element_message (GstPlaySink * playsink, const gchar * name)
860 {
861   GstMessage *msg;
862
863   msg = gst_missing_element_message_new (GST_ELEMENT_CAST (playsink), name);
864   gst_element_post_message (GST_ELEMENT_CAST (playsink), msg);
865 }
866
867 static gboolean
868 add_chain (GstPlayChain * chain, gboolean add)
869 {
870   if (chain->added == add)
871     return TRUE;
872
873   if (add)
874     gst_bin_add (GST_BIN_CAST (chain->playsink), chain->bin);
875   else {
876     gst_bin_remove (GST_BIN_CAST (chain->playsink), chain->bin);
877     /* we don't want to lose our sink status */
878     GST_OBJECT_FLAG_SET (chain->playsink, GST_ELEMENT_IS_SINK);
879   }
880
881   chain->added = add;
882
883   return TRUE;
884 }
885
886 static gboolean
887 activate_chain (GstPlayChain * chain, gboolean activate)
888 {
889   GstState state;
890
891   if (chain->activated == activate)
892     return TRUE;
893
894   GST_OBJECT_LOCK (chain->playsink);
895   state = GST_STATE_TARGET (chain->playsink);
896   GST_OBJECT_UNLOCK (chain->playsink);
897
898   if (activate)
899     gst_element_set_state (chain->bin, state);
900   else
901     gst_element_set_state (chain->bin, GST_STATE_NULL);
902
903   chain->activated = activate;
904
905   return TRUE;
906 }
907
908 static gboolean
909 element_is_sink (GstElement * element)
910 {
911   gboolean is_sink;
912
913   GST_OBJECT_LOCK (element);
914   is_sink = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_IS_SINK);
915   GST_OBJECT_UNLOCK (element);
916
917   GST_DEBUG_OBJECT (element, "is a sink: %s", (is_sink) ? "yes" : "no");
918   return is_sink;
919 }
920
921 static gboolean
922 element_has_property (GstElement * element, const gchar * pname, GType type)
923 {
924   GParamSpec *pspec;
925
926   pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (element), pname);
927
928   if (pspec == NULL) {
929     GST_DEBUG_OBJECT (element, "no %s property", pname);
930     return FALSE;
931   }
932
933   if (type == G_TYPE_INVALID || type == pspec->value_type ||
934       g_type_is_a (pspec->value_type, type)) {
935     GST_DEBUG_OBJECT (element, "has %s property of type %s", pname,
936         (type == G_TYPE_INVALID) ? "any type" : g_type_name (type));
937     return TRUE;
938   }
939
940   GST_WARNING_OBJECT (element, "has %s property, but property is of type %s "
941       "and we expected it to be of type %s", pname,
942       g_type_name (pspec->value_type), g_type_name (type));
943
944   return FALSE;
945 }
946
947 typedef struct
948 {
949   const gchar *prop_name;
950   GType prop_type;
951   gboolean need_sink;
952 } FindPropertyHelper;
953
954 static gint
955 find_property (GstElement * element, FindPropertyHelper * helper)
956 {
957   if (helper->need_sink && !element_is_sink (element)) {
958     gst_object_unref (element);
959     return 1;
960   }
961
962   if (!element_has_property (element, helper->prop_name, helper->prop_type)) {
963     gst_object_unref (element);
964     return 1;
965   }
966
967   GST_INFO_OBJECT (element, "found %s with %s property", helper->prop_name,
968       (helper->need_sink) ? "sink" : "element");
969   return 0;                     /* keep it */
970 }
971
972 /* FIXME: why not move these functions into core? */
973 /* find a sink in the hierarchy with a property named @name. This function does
974  * not increase the refcount of the returned object and thus remains valid as
975  * long as the bin is valid. */
976 static GstElement *
977 gst_play_sink_find_property_sinks (GstPlaySink * playsink, GstElement * obj,
978     const gchar * name, GType expected_type)
979 {
980   GstElement *result = NULL;
981   GstIterator *it;
982
983   if (element_has_property (obj, name, expected_type)) {
984     result = obj;
985   } else if (GST_IS_BIN (obj)) {
986     FindPropertyHelper helper = { name, expected_type, TRUE };
987
988     it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
989     result = gst_iterator_find_custom (it,
990         (GCompareFunc) find_property, &helper);
991     gst_iterator_free (it);
992     /* we don't need the extra ref */
993     if (result)
994       gst_object_unref (result);
995   }
996   return result;
997 }
998
999 /* find an object in the hierarchy with a property named @name */
1000 static GstElement *
1001 gst_play_sink_find_property (GstPlaySink * playsink, GstElement * obj,
1002     const gchar * name, GType expected_type)
1003 {
1004   GstElement *result = NULL;
1005   GstIterator *it;
1006
1007   if (GST_IS_BIN (obj)) {
1008     FindPropertyHelper helper = { name, expected_type, FALSE };
1009
1010     it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1011     result = gst_iterator_find_custom (it,
1012         (GCompareFunc) find_property, &helper);
1013     gst_iterator_free (it);
1014   } else {
1015     if (element_has_property (obj, name, expected_type)) {
1016       result = obj;
1017       gst_object_ref (obj);
1018     }
1019   }
1020   return result;
1021 }
1022
1023 static void
1024 do_async_start (GstPlaySink * playsink)
1025 {
1026   GstMessage *message;
1027
1028   if (!playsink->need_async_start) {
1029     GST_INFO_OBJECT (playsink, "no async_start needed");
1030     return;
1031   }
1032
1033   playsink->async_pending = TRUE;
1034
1035   GST_INFO_OBJECT (playsink, "Sending async_start message");
1036   message = gst_message_new_async_start (GST_OBJECT_CAST (playsink), FALSE);
1037   GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1038       (playsink), message);
1039 }
1040
1041 static void
1042 do_async_done (GstPlaySink * playsink)
1043 {
1044   GstMessage *message;
1045
1046   if (playsink->async_pending) {
1047     GST_INFO_OBJECT (playsink, "Sending async_done message");
1048     message = gst_message_new_async_done (GST_OBJECT_CAST (playsink));
1049     GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1050         (playsink), message);
1051
1052     playsink->async_pending = FALSE;
1053   }
1054
1055   playsink->need_async_start = FALSE;
1056 }
1057
1058 /* try to change the state of an element. This function returns the element when
1059  * the state change could be performed. When this function returns NULL an error
1060  * occured and the element is unreffed if @unref is TRUE. */
1061 static GstElement *
1062 try_element (GstPlaySink * playsink, GstElement * element, gboolean unref)
1063 {
1064   GstStateChangeReturn ret;
1065
1066   if (element) {
1067     ret = gst_element_set_state (element, GST_STATE_READY);
1068     if (ret == GST_STATE_CHANGE_FAILURE) {
1069       GST_DEBUG_OBJECT (playsink, "failed state change..");
1070       gst_element_set_state (element, GST_STATE_NULL);
1071       if (unref)
1072         gst_object_unref (element);
1073       element = NULL;
1074     }
1075   }
1076   return element;
1077 }
1078
1079 /* make the element (bin) that contains the elements needed to perform
1080  * video display.
1081  *
1082  *  +------------------------------------------------------------+
1083  *  | vbin                                                       |
1084  *  |      +-------+   +----------+   +----------+   +---------+ |
1085  *  |      | queue |   |colorspace|   |videoscale|   |videosink| |
1086  *  |   +-sink    src-sink       src-sink       src-sink       | |
1087  *  |   |  +-------+   +----------+   +----------+   +---------+ |
1088  * sink-+                                                        |
1089  *  +------------------------------------------------------------+
1090  *
1091  */
1092 static GstPlayVideoDeinterlaceChain *
1093 gen_video_deinterlace_chain (GstPlaySink * playsink)
1094 {
1095   GstPlayVideoDeinterlaceChain *chain;
1096   GstBin *bin;
1097   GstPad *pad;
1098   GstElement *head = NULL, *prev = NULL;
1099
1100   chain = g_new0 (GstPlayVideoDeinterlaceChain, 1);
1101   chain->chain.playsink = playsink;
1102
1103   GST_DEBUG_OBJECT (playsink, "making video deinterlace chain %p", chain);
1104
1105   /* create a bin to hold objects, as we create them we add them to this bin so
1106    * that when something goes wrong we only need to unref the bin */
1107   chain->chain.bin = gst_bin_new ("vdbin");
1108   bin = GST_BIN_CAST (chain->chain.bin);
1109   gst_object_ref_sink (bin);
1110
1111   GST_DEBUG_OBJECT (playsink, "creating ffmpegcolorspace");
1112   chain->conv = gst_element_factory_make ("ffmpegcolorspace", "vdconv");
1113   if (chain->conv == NULL) {
1114     post_missing_element_message (playsink, "ffmpegcolorspace");
1115     GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1116         (_("Missing element '%s' - check your GStreamer installation."),
1117             "ffmpegcolorspace"), ("video rendering might fail"));
1118   } else {
1119     gst_bin_add (bin, chain->conv);
1120     head = chain->conv;
1121     prev = chain->conv;
1122   }
1123
1124   GST_DEBUG_OBJECT (playsink, "creating deinterlace");
1125   chain->deinterlace = gst_element_factory_make ("deinterlace", "deinterlace");
1126   if (chain->deinterlace == NULL) {
1127     post_missing_element_message (playsink, "deinterlace");
1128     GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1129         (_("Missing element '%s' - check your GStreamer installation."),
1130             "deinterlace"), ("deinterlacing won't work"));
1131   } else {
1132     gst_bin_add (bin, chain->deinterlace);
1133     if (prev) {
1134       if (!gst_element_link_pads_full (prev, "src", chain->deinterlace, "sink",
1135               GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1136         goto link_failed;
1137     } else {
1138       head = chain->deinterlace;
1139     }
1140     prev = chain->deinterlace;
1141   }
1142
1143   if (head) {
1144     pad = gst_element_get_static_pad (head, "sink");
1145     chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1146     gst_object_unref (pad);
1147   } else {
1148     chain->sinkpad = gst_ghost_pad_new_no_target ("sink", GST_PAD_SINK);
1149   }
1150
1151   if (prev) {
1152     pad = gst_element_get_static_pad (prev, "src");
1153     chain->srcpad = gst_ghost_pad_new ("src", pad);
1154     gst_object_unref (pad);
1155   } else {
1156     chain->srcpad = gst_ghost_pad_new ("src", chain->sinkpad);
1157   }
1158
1159   gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1160   gst_element_add_pad (chain->chain.bin, chain->srcpad);
1161
1162   return chain;
1163
1164 link_failed:
1165   {
1166     GST_ELEMENT_ERROR (playsink, CORE, PAD,
1167         (NULL), ("Failed to configure the video deinterlace chain."));
1168     free_chain ((GstPlayChain *) chain);
1169     return NULL;
1170   }
1171 }
1172
1173 /* make the element (bin) that contains the elements needed to perform
1174  * video display.
1175  *
1176  *  +------------------------------------------------------------+
1177  *  | vbin                                                       |
1178  *  |      +-------+   +----------+   +----------+   +---------+ |
1179  *  |      | queue |   |colorspace|   |videoscale|   |videosink| |
1180  *  |   +-sink    src-sink       src-sink       src-sink       | |
1181  *  |   |  +-------+   +----------+   +----------+   +---------+ |
1182  * sink-+                                                        |
1183  *  +------------------------------------------------------------+
1184  *
1185  */
1186 static GstPlayVideoChain *
1187 gen_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1188 {
1189   GstPlayVideoChain *chain;
1190   GstBin *bin;
1191   GstPad *pad;
1192   GstElement *head = NULL, *prev = NULL, *elem = NULL;
1193
1194   chain = g_new0 (GstPlayVideoChain, 1);
1195   chain->chain.playsink = playsink;
1196   chain->chain.raw = raw;
1197
1198   GST_DEBUG_OBJECT (playsink, "making video chain %p", chain);
1199
1200   if (playsink->video_sink) {
1201     GST_DEBUG_OBJECT (playsink, "trying configured videosink");
1202     chain->sink = try_element (playsink, playsink->video_sink, FALSE);
1203   } else {
1204     /* only try fallback if no specific sink was chosen */
1205     if (chain->sink == NULL) {
1206       GST_DEBUG_OBJECT (playsink, "trying autovideosink");
1207       elem = gst_element_factory_make ("autovideosink", "videosink");
1208       chain->sink = try_element (playsink, elem, TRUE);
1209     }
1210     if (chain->sink == NULL) {
1211       /* if default sink from config.h is different then try it too */
1212       if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1213         GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_VIDEOSINK);
1214         elem = gst_element_factory_make (DEFAULT_VIDEOSINK, "videosink");
1215         chain->sink = try_element (playsink, elem, TRUE);
1216       }
1217     }
1218     if (chain->sink)
1219       playsink->video_sink = gst_object_ref (chain->sink);
1220   }
1221   if (chain->sink == NULL)
1222     goto no_sinks;
1223   head = chain->sink;
1224
1225   /* if we can disable async behaviour of the sink, we can avoid adding a
1226    * queue for the audio chain. */
1227   elem =
1228       gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1229       G_TYPE_BOOLEAN);
1230   if (elem) {
1231     GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1232         async, GST_ELEMENT_NAME (elem));
1233     g_object_set (elem, "async", async, NULL);
1234     chain->async = async;
1235   } else {
1236     GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1237     chain->async = TRUE;
1238   }
1239
1240   /* find ts-offset element */
1241   chain->ts_offset =
1242       gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1243       G_TYPE_INT64);
1244
1245   /* create a bin to hold objects, as we create them we add them to this bin so
1246    * that when something goes wrong we only need to unref the bin */
1247   chain->chain.bin = gst_bin_new ("vbin");
1248   bin = GST_BIN_CAST (chain->chain.bin);
1249   gst_object_ref_sink (bin);
1250   gst_bin_add (bin, chain->sink);
1251
1252   /* decouple decoder from sink, this improves playback quite a lot since the
1253    * decoder can continue while the sink blocks for synchronisation. We don't
1254    * need a lot of buffers as this consumes a lot of memory and we don't want
1255    * too little because else we would be context switching too quickly. */
1256   chain->queue = gst_element_factory_make ("queue", "vqueue");
1257   if (chain->queue == NULL) {
1258     post_missing_element_message (playsink, "queue");
1259     GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1260         (_("Missing element '%s' - check your GStreamer installation."),
1261             "queue"), ("video rendering might be suboptimal"));
1262     head = chain->sink;
1263     prev = NULL;
1264   } else {
1265     g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1266         "max-size-bytes", 0, "max-size-time", (gint64) 0, "silent", TRUE, NULL);
1267     gst_bin_add (bin, chain->queue);
1268     head = prev = chain->queue;
1269   }
1270
1271   if (raw && !(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
1272     GST_DEBUG_OBJECT (playsink, "creating ffmpegcolorspace");
1273     chain->conv = gst_element_factory_make ("ffmpegcolorspace", "vconv");
1274     if (chain->conv == NULL) {
1275       post_missing_element_message (playsink, "ffmpegcolorspace");
1276       GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1277           (_("Missing element '%s' - check your GStreamer installation."),
1278               "ffmpegcolorspace"), ("video rendering might fail"));
1279     } else {
1280       gst_bin_add (bin, chain->conv);
1281       if (prev) {
1282         if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
1283                 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1284           goto link_failed;
1285       } else {
1286         head = chain->conv;
1287       }
1288       prev = chain->conv;
1289     }
1290
1291     GST_DEBUG_OBJECT (playsink, "creating videoscale");
1292     chain->scale = gst_element_factory_make ("videoscale", "vscale");
1293     if (chain->scale == NULL) {
1294       post_missing_element_message (playsink, "videoscale");
1295       GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1296           (_("Missing element '%s' - check your GStreamer installation."),
1297               "videoscale"), ("possibly a liboil version mismatch?"));
1298     } else {
1299       /* Add black borders if necessary to keep the DAR */
1300       g_object_set (chain->scale, "add-borders", TRUE, NULL);
1301       gst_bin_add (bin, chain->scale);
1302       if (prev) {
1303         if (!gst_element_link_pads_full (prev, "src", chain->scale, "sink",
1304                 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1305           goto link_failed;
1306       } else {
1307         head = chain->scale;
1308       }
1309       prev = chain->scale;
1310     }
1311   }
1312
1313   if (prev) {
1314     GST_DEBUG_OBJECT (playsink, "linking to sink");
1315     if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
1316             GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1317       goto link_failed;
1318   }
1319
1320   pad = gst_element_get_static_pad (head, "sink");
1321   chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1322   gst_object_unref (pad);
1323
1324   gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1325
1326   return chain;
1327
1328   /* ERRORS */
1329 no_sinks:
1330   {
1331     if (!elem && !playsink->video_sink) {
1332       post_missing_element_message (playsink, "autovideosink");
1333       if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1334         post_missing_element_message (playsink, DEFAULT_VIDEOSINK);
1335         GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1336             (_("Both autovideosink and %s elements are missing."),
1337                 DEFAULT_VIDEOSINK), (NULL));
1338       } else {
1339         GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1340             (_("The autovideosink element is missing.")), (NULL));
1341       }
1342     } else {
1343       if (playsink->video_sink) {
1344         GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1345             (_("Configured videosink %s is not working."),
1346                 GST_ELEMENT_NAME (playsink->video_sink)), (NULL));
1347       } else if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1348         GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1349             (_("Both autovideosink and %s elements are not working."),
1350                 DEFAULT_VIDEOSINK), (NULL));
1351       } else {
1352         GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1353             (_("The autovideosink element is not working.")), (NULL));
1354       }
1355     }
1356     free_chain ((GstPlayChain *) chain);
1357     return NULL;
1358   }
1359 link_failed:
1360   {
1361     GST_ELEMENT_ERROR (playsink, CORE, PAD,
1362         (NULL), ("Failed to configure the video sink."));
1363     /* checking sink made it READY */
1364     gst_element_set_state (chain->sink, GST_STATE_NULL);
1365     /* Remove chain from the bin to allow reuse later */
1366     gst_bin_remove (bin, chain->sink);
1367     free_chain ((GstPlayChain *) chain);
1368     return NULL;
1369   }
1370 }
1371
1372 static gboolean
1373 setup_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1374 {
1375   GstElement *elem;
1376   GstPlayVideoChain *chain;
1377   GstStateChangeReturn ret;
1378
1379   chain = playsink->videochain;
1380
1381   /* if the chain was active we don't do anything */
1382   if (GST_PLAY_CHAIN (chain)->activated == TRUE)
1383     return TRUE;
1384
1385   if (chain->chain.raw != raw)
1386     return FALSE;
1387
1388   /* try to set the sink element to READY again */
1389   ret = gst_element_set_state (chain->sink, GST_STATE_READY);
1390   if (ret == GST_STATE_CHANGE_FAILURE)
1391     return FALSE;
1392
1393   /* find ts-offset element */
1394   chain->ts_offset =
1395       gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1396       G_TYPE_INT64);
1397
1398   /* if we can disable async behaviour of the sink, we can avoid adding a
1399    * queue for the audio chain. */
1400   elem =
1401       gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1402       G_TYPE_BOOLEAN);
1403   if (elem) {
1404     GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1405         async, GST_ELEMENT_NAME (elem));
1406     g_object_set (elem, "async", async, NULL);
1407     chain->async = async;
1408   } else {
1409     GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1410     chain->async = TRUE;
1411   }
1412   return TRUE;
1413 }
1414
1415 /* make an element for playback of video with subtitles embedded.
1416  *
1417  *  +--------------------------------------------+
1418  *  | tbin                                       |
1419  *  |     +--------+      +-----------------+    |
1420  *  |     | queue  |      | subtitleoverlay |    |
1421  * video--src     sink---video_sink         |    |
1422  *  |     +--------+     |                src--src
1423  * text------------------text_sink          |    |
1424  *  |                     +-----------------+    |
1425  *  +--------------------------------------------+
1426  *
1427  */
1428 static GstPlayTextChain *
1429 gen_text_chain (GstPlaySink * playsink)
1430 {
1431   GstPlayTextChain *chain;
1432   GstBin *bin;
1433   GstElement *elem;
1434   GstPad *videosinkpad, *textsinkpad, *srcpad;
1435
1436   chain = g_new0 (GstPlayTextChain, 1);
1437   chain->chain.playsink = playsink;
1438
1439   GST_DEBUG_OBJECT (playsink, "making text chain %p", chain);
1440
1441   chain->chain.bin = gst_bin_new ("tbin");
1442   bin = GST_BIN_CAST (chain->chain.bin);
1443   gst_object_ref_sink (bin);
1444
1445   videosinkpad = textsinkpad = srcpad = NULL;
1446
1447   /* first try to hook the text pad to the custom sink */
1448   if (playsink->text_sink) {
1449     GST_DEBUG_OBJECT (playsink, "trying configured textsink");
1450     chain->sink = try_element (playsink, playsink->text_sink, FALSE);
1451     if (chain->sink) {
1452       elem =
1453           gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1454           G_TYPE_BOOLEAN);
1455       if (elem) {
1456         /* make sure the sparse subtitles don't participate in the preroll */
1457         g_object_set (elem, "async", FALSE, NULL);
1458         GST_DEBUG_OBJECT (playsink, "adding custom text sink");
1459         gst_bin_add (bin, chain->sink);
1460         /* NOTE streamsynchronizer needs streams decoupled */
1461         /* make a little queue */
1462         chain->queue = gst_element_factory_make ("queue", "subqueue");
1463         if (chain->queue == NULL) {
1464           post_missing_element_message (playsink, "queue");
1465           GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1466               (_("Missing element '%s' - check your GStreamer installation."),
1467                   "queue"), ("rendering might be suboptimal"));
1468         } else {
1469           g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1470               "max-size-bytes", 0, "max-size-time", (gint64) 0,
1471               "silent", TRUE, NULL);
1472           gst_bin_add (bin, chain->queue);
1473         }
1474         /* we have a custom sink, this will be our textsinkpad */
1475         if (gst_element_link_pads_full (chain->queue, "src", chain->sink,
1476                 "sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
1477           /* we're all fine now and we can add the sink to the chain */
1478           GST_DEBUG_OBJECT (playsink, "using custom text sink");
1479           textsinkpad = gst_element_get_static_pad (chain->queue, "sink");
1480         } else {
1481           GST_WARNING_OBJECT (playsink,
1482               "can't find a sink pad on custom text sink");
1483           gst_bin_remove (bin, chain->sink);
1484           gst_bin_remove (bin, chain->queue);
1485           chain->sink = NULL;
1486           chain->queue = NULL;
1487         }
1488         /* try to set sync to true but it's no biggie when we can't */
1489         if ((elem =
1490                 gst_play_sink_find_property_sinks (playsink, chain->sink,
1491                     "sync", G_TYPE_BOOLEAN)))
1492           g_object_set (elem, "sync", TRUE, NULL);
1493
1494         if (!textsinkpad)
1495           gst_bin_remove (bin, chain->sink);
1496       } else {
1497         GST_WARNING_OBJECT (playsink,
1498             "can't find async property in custom text sink");
1499       }
1500     }
1501     if (textsinkpad == NULL) {
1502       GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1503           (_("Custom text sink element is not usable.")),
1504           ("fallback to default textoverlay"));
1505     }
1506   }
1507
1508   if (textsinkpad == NULL) {
1509     if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
1510       /* make a little queue */
1511       chain->queue = gst_element_factory_make ("queue", "vqueue");
1512       if (chain->queue == NULL) {
1513         post_missing_element_message (playsink, "queue");
1514         GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1515             (_("Missing element '%s' - check your GStreamer installation."),
1516                 "queue"), ("video rendering might be suboptimal"));
1517       } else {
1518         g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1519             "max-size-bytes", 0, "max-size-time", (gint64) 0,
1520             "silent", TRUE, NULL);
1521         gst_bin_add (bin, chain->queue);
1522         videosinkpad = gst_element_get_static_pad (chain->queue, "sink");
1523       }
1524
1525       chain->overlay =
1526           gst_element_factory_make ("subtitleoverlay", "suboverlay");
1527       if (chain->overlay == NULL) {
1528         post_missing_element_message (playsink, "subtitleoverlay");
1529         GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1530             (_("Missing element '%s' - check your GStreamer installation."),
1531                 "subtitleoverlay"), ("subtitle rendering disabled"));
1532       } else {
1533         GstElement *element;
1534
1535         gst_bin_add (bin, chain->overlay);
1536
1537         g_object_set (G_OBJECT (chain->overlay), "silent", FALSE, NULL);
1538         if (playsink->font_desc) {
1539           g_object_set (G_OBJECT (chain->overlay), "font-desc",
1540               playsink->font_desc, NULL);
1541         }
1542         if (playsink->subtitle_encoding) {
1543           g_object_set (G_OBJECT (chain->overlay), "subtitle-encoding",
1544               playsink->subtitle_encoding, NULL);
1545         }
1546
1547         gst_element_link_pads_full (chain->queue, "src", chain->overlay,
1548             "video_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS);
1549
1550         /* make another little queue to decouple streams */
1551         element = gst_element_factory_make ("queue", "subqueue");
1552         if (element == NULL) {
1553           post_missing_element_message (playsink, "queue");
1554           GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1555               (_("Missing element '%s' - check your GStreamer installation."),
1556                   "queue"), ("rendering might be suboptimal"));
1557         } else {
1558           g_object_set (G_OBJECT (element), "max-size-buffers", 3,
1559               "max-size-bytes", 0, "max-size-time", (gint64) 0,
1560               "silent", TRUE, NULL);
1561           gst_bin_add (bin, element);
1562           gst_element_link_pads_full (element, "src", chain->overlay,
1563               "subtitle_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS);
1564           textsinkpad = gst_element_get_static_pad (element, "sink");
1565           srcpad = gst_element_get_static_pad (chain->overlay, "src");
1566         }
1567       }
1568     }
1569   }
1570
1571   if (videosinkpad == NULL) {
1572     /* if we still don't have a videosink, we don't have an overlay. the only
1573      * thing we can do is insert an identity and ghost the src
1574      * and sink pads. */
1575     chain->identity = gst_element_factory_make ("identity", "tidentity");
1576     if (chain->identity == NULL) {
1577       post_missing_element_message (playsink, "identity");
1578       GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1579           (_("Missing element '%s' - check your GStreamer installation."),
1580               "identity"), (NULL));
1581     } else {
1582       g_object_set (chain->identity, "signal-handoffs", FALSE, NULL);
1583       g_object_set (chain->identity, "silent", TRUE, NULL);
1584       gst_bin_add (bin, chain->identity);
1585       srcpad = gst_element_get_static_pad (chain->identity, "src");
1586       videosinkpad = gst_element_get_static_pad (chain->identity, "sink");
1587     }
1588   }
1589
1590   /* expose the ghostpads */
1591   if (videosinkpad) {
1592     chain->videosinkpad = gst_ghost_pad_new ("sink", videosinkpad);
1593     gst_object_unref (videosinkpad);
1594     gst_element_add_pad (chain->chain.bin, chain->videosinkpad);
1595   }
1596   if (textsinkpad) {
1597     chain->textsinkpad = gst_ghost_pad_new ("text_sink", textsinkpad);
1598     gst_object_unref (textsinkpad);
1599     gst_element_add_pad (chain->chain.bin, chain->textsinkpad);
1600   }
1601   if (srcpad) {
1602     chain->srcpad = gst_ghost_pad_new ("src", srcpad);
1603     gst_object_unref (srcpad);
1604     gst_element_add_pad (chain->chain.bin, chain->srcpad);
1605   }
1606
1607   return chain;
1608 }
1609
1610 static void
1611 notify_volume_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
1612 {
1613   gdouble vol;
1614
1615   g_object_get (object, "volume", &vol, NULL);
1616   playsink->volume = vol;
1617
1618   g_object_notify (G_OBJECT (playsink), "volume");
1619 }
1620
1621 static void
1622 notify_mute_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
1623 {
1624   gboolean mute;
1625
1626   g_object_get (object, "mute", &mute, NULL);
1627   playsink->mute = mute;
1628
1629   g_object_notify (G_OBJECT (playsink), "mute");
1630 }
1631
1632 /* make the chain that contains the elements needed to perform
1633  * audio playback.
1634  *
1635  * We add a tee as the first element so that we can link the visualisation chain
1636  * to it when requested.
1637  *
1638  *  +-------------------------------------------------------------+
1639  *  | abin                                                        |
1640  *  |      +---------+   +----------+   +---------+   +---------+ |
1641  *  |      |audioconv|   |audioscale|   | volume  |   |audiosink| |
1642  *  |   +-srck      src-sink       src-sink      src-sink       | |
1643  *  |   |  +---------+   +----------+   +---------+   +---------+ |
1644  * sink-+                                                         |
1645  *  +-------------------------------------------------------------+
1646  */
1647 static GstPlayAudioChain *
1648 gen_audio_chain (GstPlaySink * playsink, gboolean raw)
1649 {
1650   GstPlayAudioChain *chain;
1651   GstBin *bin;
1652   gboolean have_volume;
1653   GstPad *pad;
1654   GstElement *head, *prev, *elem = NULL;
1655
1656   chain = g_new0 (GstPlayAudioChain, 1);
1657   chain->chain.playsink = playsink;
1658   chain->chain.raw = raw;
1659
1660   GST_DEBUG_OBJECT (playsink, "making audio chain %p", chain);
1661
1662   if (playsink->audio_sink) {
1663     GST_DEBUG_OBJECT (playsink, "trying configured audiosink %" GST_PTR_FORMAT,
1664         playsink->audio_sink);
1665     chain->sink = try_element (playsink, playsink->audio_sink, FALSE);
1666   } else {
1667     /* only try fallback if no specific sink was chosen */
1668     if (chain->sink == NULL) {
1669       GST_DEBUG_OBJECT (playsink, "trying autoaudiosink");
1670       elem = gst_element_factory_make ("autoaudiosink", "audiosink");
1671       chain->sink = try_element (playsink, elem, TRUE);
1672     }
1673     if (chain->sink == NULL) {
1674       /* if default sink from config.h is different then try it too */
1675       if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1676         GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_AUDIOSINK);
1677         elem = gst_element_factory_make (DEFAULT_AUDIOSINK, "audiosink");
1678         chain->sink = try_element (playsink, elem, TRUE);
1679       }
1680     }
1681     if (chain->sink)
1682       playsink->audio_sink = gst_object_ref (chain->sink);
1683   }
1684   if (chain->sink == NULL)
1685     goto no_sinks;
1686
1687   chain->chain.bin = gst_bin_new ("abin");
1688   bin = GST_BIN_CAST (chain->chain.bin);
1689   gst_object_ref_sink (bin);
1690   gst_bin_add (bin, chain->sink);
1691
1692   /* we have to add a queue when we need to decouple for the video sink in
1693    * visualisations and for streamsynchronizer */
1694   GST_DEBUG_OBJECT (playsink, "adding audio queue");
1695   chain->queue = gst_element_factory_make ("queue", "aqueue");
1696   if (chain->queue == NULL) {
1697     post_missing_element_message (playsink, "queue");
1698     GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1699         (_("Missing element '%s' - check your GStreamer installation."),
1700             "queue"), ("audio playback and visualizations might not work"));
1701     head = chain->sink;
1702     prev = NULL;
1703   } else {
1704     g_object_set (chain->queue, "silent", TRUE, NULL);
1705     gst_bin_add (bin, chain->queue);
1706     prev = head = chain->queue;
1707   }
1708
1709   /* find ts-offset element */
1710   chain->ts_offset =
1711       gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1712       G_TYPE_INT64);
1713
1714   /* check if the sink, or something within the sink, has the volume property.
1715    * If it does we don't need to add a volume element.  */
1716   elem =
1717       gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
1718       G_TYPE_DOUBLE);
1719   if (elem) {
1720     chain->volume = elem;
1721
1722     g_signal_connect (chain->volume, "notify::volume",
1723         G_CALLBACK (notify_volume_cb), playsink);
1724
1725     GST_DEBUG_OBJECT (playsink, "the sink has a volume property");
1726     have_volume = TRUE;
1727     chain->sink_volume = TRUE;
1728     /* if the sink also has a mute property we can use this as well. We'll only
1729      * use the mute property if there is a volume property. We can simulate the
1730      * mute with the volume otherwise. */
1731     chain->mute =
1732         gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
1733         G_TYPE_BOOLEAN);
1734     if (chain->mute) {
1735       GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
1736       g_signal_connect (chain->mute, "notify::mute",
1737           G_CALLBACK (notify_mute_cb), playsink);
1738     }
1739     /* use the sink to control the volume and mute */
1740     if (playsink->volume_changed) {
1741       g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
1742       playsink->volume_changed = FALSE;
1743     }
1744     if (playsink->mute_changed) {
1745       if (chain->mute) {
1746         g_object_set (chain->mute, "mute", playsink->mute, NULL);
1747       } else {
1748         if (playsink->mute)
1749           g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
1750       }
1751       playsink->mute_changed = FALSE;
1752     }
1753   } else {
1754     /* no volume, we need to add a volume element when we can */
1755     GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
1756     have_volume = FALSE;
1757     chain->sink_volume = FALSE;
1758   }
1759
1760   if (raw && !(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO)) {
1761     GST_DEBUG_OBJECT (playsink, "creating audioconvert");
1762     chain->conv = gst_element_factory_make ("audioconvert", "aconv");
1763     if (chain->conv == NULL) {
1764       post_missing_element_message (playsink, "audioconvert");
1765       GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1766           (_("Missing element '%s' - check your GStreamer installation."),
1767               "audioconvert"), ("possibly a liboil version mismatch?"));
1768     } else {
1769       gst_bin_add (bin, chain->conv);
1770       if (prev) {
1771         if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
1772                 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1773           goto link_failed;
1774       } else {
1775         head = chain->conv;
1776       }
1777       prev = chain->conv;
1778     }
1779
1780     GST_DEBUG_OBJECT (playsink, "creating audioresample");
1781     chain->resample = gst_element_factory_make ("audioresample", "aresample");
1782     if (chain->resample == NULL) {
1783       post_missing_element_message (playsink, "audioresample");
1784       GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1785           (_("Missing element '%s' - check your GStreamer installation."),
1786               "audioresample"), ("possibly a liboil version mismatch?"));
1787     } else {
1788       gst_bin_add (bin, chain->resample);
1789       if (prev) {
1790         if (!gst_element_link_pads_full (prev, "src", chain->resample, "sink",
1791                 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1792           goto link_failed;
1793       } else {
1794         head = chain->resample;
1795       }
1796       prev = chain->resample;
1797     }
1798
1799     if (!have_volume && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME) {
1800       GST_DEBUG_OBJECT (playsink, "creating volume");
1801       chain->volume = gst_element_factory_make ("volume", "volume");
1802       if (chain->volume == NULL) {
1803         post_missing_element_message (playsink, "volume");
1804         GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1805             (_("Missing element '%s' - check your GStreamer installation."),
1806                 "volume"), ("possibly a liboil version mismatch?"));
1807       } else {
1808         have_volume = TRUE;
1809
1810         g_signal_connect (chain->volume, "notify::volume",
1811             G_CALLBACK (notify_volume_cb), playsink);
1812
1813         /* volume also has the mute property */
1814         chain->mute = chain->volume;
1815         g_signal_connect (chain->mute, "notify::mute",
1816             G_CALLBACK (notify_mute_cb), playsink);
1817
1818         /* configure with the latest volume and mute */
1819         g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume,
1820             NULL);
1821         g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
1822         gst_bin_add (bin, chain->volume);
1823
1824         if (prev) {
1825           if (!gst_element_link_pads_full (prev, "src", chain->volume, "sink",
1826                   GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1827             goto link_failed;
1828         } else {
1829           head = chain->volume;
1830         }
1831         prev = chain->volume;
1832       }
1833     }
1834   }
1835
1836   if (prev) {
1837     /* we only have to link to the previous element if we have something in
1838      * front of the sink */
1839     GST_DEBUG_OBJECT (playsink, "linking to sink");
1840     if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
1841             GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1842       goto link_failed;
1843   }
1844
1845   /* post a warning if we have no way to configure the volume */
1846   if (!have_volume) {
1847     GST_ELEMENT_WARNING (playsink, STREAM, NOT_IMPLEMENTED,
1848         (_("No volume control found")), ("Volume/mute is not available"));
1849   }
1850
1851   /* and ghost the sinkpad of the headmost element */
1852   GST_DEBUG_OBJECT (playsink, "ghosting sink pad");
1853   pad = gst_element_get_static_pad (head, "sink");
1854   chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1855   gst_object_unref (pad);
1856   gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1857
1858   return chain;
1859
1860   /* ERRORS */
1861 no_sinks:
1862   {
1863     if (!elem && !playsink->audio_sink) {
1864       post_missing_element_message (playsink, "autoaudiosink");
1865       if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1866         post_missing_element_message (playsink, DEFAULT_AUDIOSINK);
1867         GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1868             (_("Both autoaudiosink and %s elements are missing."),
1869                 DEFAULT_AUDIOSINK), (NULL));
1870       } else {
1871         GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1872             (_("The autoaudiosink element is missing.")), (NULL));
1873       }
1874     } else {
1875       if (playsink->audio_sink) {
1876         GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1877             (_("Configured audiosink %s is not working."),
1878                 GST_ELEMENT_NAME (playsink->audio_sink)), (NULL));
1879       } else if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1880         GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1881             (_("Both autoaudiosink and %s elements are not working."),
1882                 DEFAULT_AUDIOSINK), (NULL));
1883       } else {
1884         GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1885             (_("The autoaudiosink element is not working.")), (NULL));
1886       }
1887     }
1888     free_chain ((GstPlayChain *) chain);
1889     return NULL;
1890   }
1891 link_failed:
1892   {
1893     GST_ELEMENT_ERROR (playsink, CORE, PAD,
1894         (NULL), ("Failed to configure the audio sink."));
1895     /* checking sink made it READY */
1896     gst_element_set_state (chain->sink, GST_STATE_NULL);
1897     /* Remove chain from the bin to allow reuse later */
1898     gst_bin_remove (bin, chain->sink);
1899     free_chain ((GstPlayChain *) chain);
1900     return NULL;
1901   }
1902 }
1903
1904 static gboolean
1905 setup_audio_chain (GstPlaySink * playsink, gboolean raw)
1906 {
1907   GstElement *elem;
1908   GstPlayAudioChain *chain;
1909   GstStateChangeReturn ret;
1910
1911   chain = playsink->audiochain;
1912
1913   /* if the chain was active we don't do anything */
1914   if (GST_PLAY_CHAIN (chain)->activated == TRUE)
1915     return TRUE;
1916
1917   if (chain->chain.raw != raw)
1918     return FALSE;
1919
1920   /* try to set the sink element to READY again */
1921   ret = gst_element_set_state (chain->sink, GST_STATE_READY);
1922   if (ret == GST_STATE_CHANGE_FAILURE)
1923     return FALSE;
1924
1925   /* find ts-offset element */
1926   chain->ts_offset =
1927       gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1928       G_TYPE_INT64);
1929
1930   /* check if the sink, or something within the sink, has the volume property.
1931    * If it does we don't need to add a volume element.  */
1932   elem =
1933       gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
1934       G_TYPE_DOUBLE);
1935   if (elem) {
1936     chain->volume = elem;
1937
1938     if (playsink->volume_changed) {
1939       GST_DEBUG_OBJECT (playsink, "the sink has a volume property, setting %f",
1940           playsink->volume);
1941       /* use the sink to control the volume */
1942       g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
1943       playsink->volume_changed = FALSE;
1944     }
1945
1946     g_signal_connect (chain->volume, "notify::volume",
1947         G_CALLBACK (notify_volume_cb), playsink);
1948     /* if the sink also has a mute property we can use this as well. We'll only
1949      * use the mute property if there is a volume property. We can simulate the
1950      * mute with the volume otherwise. */
1951     chain->mute =
1952         gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
1953         G_TYPE_BOOLEAN);
1954     if (chain->mute) {
1955       GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
1956       g_signal_connect (chain->mute, "notify::mute",
1957           G_CALLBACK (notify_mute_cb), playsink);
1958     }
1959   } else {
1960     /* no volume, we need to add a volume element when we can */
1961     GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
1962     if (!raw) {
1963       GST_LOG_OBJECT (playsink, "non-raw format, can't do soft volume control");
1964
1965       disconnect_chain (chain, playsink);
1966       chain->volume = NULL;
1967       chain->mute = NULL;
1968     } else {
1969       /* both last and current chain are raw audio, there should be a volume
1970        * element already, unless the sink changed from one with a volume
1971        * property to one that hasn't got a volume property, in which case we
1972        * re-generate the chain */
1973       if (chain->volume == NULL) {
1974         GST_DEBUG_OBJECT (playsink, "no existing volume element to re-use");
1975         /* undo background state change done earlier */
1976         gst_element_set_state (chain->sink, GST_STATE_NULL);
1977         return FALSE;
1978       }
1979
1980       GST_DEBUG_OBJECT (playsink, "reusing existing volume element");
1981     }
1982   }
1983   return TRUE;
1984 }
1985
1986 /*
1987  *  +-------------------------------------------------------------------+
1988  *  | visbin                                                            |
1989  *  |      +----------+   +------------+   +----------+   +-------+     |
1990  *  |      | visqueue |   | audioconv  |   | audiores |   |  vis  |     |
1991  *  |   +-sink       src-sink + samp  src-sink       src-sink    src-+  |
1992  *  |   |  +----------+   +------------+   +----------+   +-------+  |  |
1993  * sink-+                                                            +-src
1994  *  +-------------------------------------------------------------------+
1995  *
1996  */
1997 static GstPlayVisChain *
1998 gen_vis_chain (GstPlaySink * playsink)
1999 {
2000   GstPlayVisChain *chain;
2001   GstBin *bin;
2002   gboolean res;
2003   GstPad *pad;
2004   GstElement *elem;
2005
2006   chain = g_new0 (GstPlayVisChain, 1);
2007   chain->chain.playsink = playsink;
2008
2009   GST_DEBUG_OBJECT (playsink, "making vis chain %p", chain);
2010
2011   chain->chain.bin = gst_bin_new ("visbin");
2012   bin = GST_BIN_CAST (chain->chain.bin);
2013   gst_object_ref_sink (bin);
2014
2015   /* we're queuing raw audio here, we can remove this queue when we can disable
2016    * async behaviour in the video sink. */
2017   chain->queue = gst_element_factory_make ("queue", "visqueue");
2018   if (chain->queue == NULL)
2019     goto no_queue;
2020   g_object_set (chain->queue, "silent", TRUE, NULL);
2021   gst_bin_add (bin, chain->queue);
2022
2023   chain->conv = gst_element_factory_make ("audioconvert", "aconv");
2024   if (chain->conv == NULL)
2025     goto no_audioconvert;
2026   gst_bin_add (bin, chain->conv);
2027
2028   chain->resample = gst_element_factory_make ("audioresample", "aresample");
2029   if (chain->resample == NULL)
2030     goto no_audioresample;
2031   gst_bin_add (bin, chain->resample);
2032
2033   /* this pad will be used for blocking the dataflow and switching the vis
2034    * plugin */
2035   chain->blockpad = gst_element_get_static_pad (chain->resample, "src");
2036
2037   if (playsink->visualisation) {
2038     GST_DEBUG_OBJECT (playsink, "trying configure vis");
2039     chain->vis = try_element (playsink, playsink->visualisation, FALSE);
2040   }
2041   if (chain->vis == NULL) {
2042     GST_DEBUG_OBJECT (playsink, "trying goom");
2043     elem = gst_element_factory_make ("goom", "vis");
2044     chain->vis = try_element (playsink, elem, TRUE);
2045   }
2046   if (chain->vis == NULL)
2047     goto no_goom;
2048
2049   gst_bin_add (bin, chain->vis);
2050
2051   res = gst_element_link_pads_full (chain->queue, "src", chain->conv, "sink",
2052       GST_PAD_LINK_CHECK_NOTHING);
2053   res &=
2054       gst_element_link_pads_full (chain->conv, "src", chain->resample, "sink",
2055       GST_PAD_LINK_CHECK_NOTHING);
2056   res &=
2057       gst_element_link_pads_full (chain->resample, "src", chain->vis, "sink",
2058       GST_PAD_LINK_CHECK_NOTHING);
2059   if (!res)
2060     goto link_failed;
2061
2062   chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
2063   chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
2064
2065   pad = gst_element_get_static_pad (chain->queue, "sink");
2066   chain->sinkpad = gst_ghost_pad_new ("sink", pad);
2067   gst_object_unref (pad);
2068   gst_element_add_pad (chain->chain.bin, chain->sinkpad);
2069
2070   chain->srcpad = gst_ghost_pad_new ("src", chain->vissrcpad);
2071   gst_element_add_pad (chain->chain.bin, chain->srcpad);
2072
2073   return chain;
2074
2075   /* ERRORS */
2076 no_queue:
2077   {
2078     post_missing_element_message (playsink, "queue");
2079     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2080         (_("Missing element '%s' - check your GStreamer installation."),
2081             "queue"), (NULL));
2082     free_chain ((GstPlayChain *) chain);
2083     return NULL;
2084   }
2085 no_audioconvert:
2086   {
2087     post_missing_element_message (playsink, "audioconvert");
2088     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2089         (_("Missing element '%s' - check your GStreamer installation."),
2090             "audioconvert"), ("possibly a liboil version mismatch?"));
2091     free_chain ((GstPlayChain *) chain);
2092     return NULL;
2093   }
2094 no_audioresample:
2095   {
2096     post_missing_element_message (playsink, "audioresample");
2097     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2098         (_("Missing element '%s' - check your GStreamer installation."),
2099             "audioresample"), (NULL));
2100     free_chain ((GstPlayChain *) chain);
2101     return NULL;
2102   }
2103 no_goom:
2104   {
2105     post_missing_element_message (playsink, "goom");
2106     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2107         (_("Missing element '%s' - check your GStreamer installation."),
2108             "goom"), (NULL));
2109     free_chain ((GstPlayChain *) chain);
2110     return NULL;
2111   }
2112 link_failed:
2113   {
2114     GST_ELEMENT_ERROR (playsink, CORE, PAD,
2115         (NULL), ("Failed to configure the visualisation element."));
2116     /* element made it to READY */
2117     gst_element_set_state (chain->vis, GST_STATE_NULL);
2118     free_chain ((GstPlayChain *) chain);
2119     return NULL;
2120   }
2121 }
2122
2123 /* this function is called when all the request pads are requested and when we
2124  * have to construct the final pipeline. Based on the flags we construct the
2125  * final output pipelines.
2126  */
2127 gboolean
2128 gst_play_sink_reconfigure (GstPlaySink * playsink)
2129 {
2130   GstPlayFlags flags;
2131   gboolean need_audio, need_video, need_deinterlace, need_vis, need_text;
2132
2133   GST_DEBUG_OBJECT (playsink, "reconfiguring");
2134
2135   /* assume we need nothing */
2136   need_audio = need_video = need_deinterlace = need_vis = need_text = FALSE;
2137
2138   GST_PLAY_SINK_LOCK (playsink);
2139   GST_OBJECT_LOCK (playsink);
2140   /* get flags, there are protected with the object lock */
2141   flags = playsink->flags;
2142   GST_OBJECT_UNLOCK (playsink);
2143
2144   /* figure out which components we need */
2145   if (flags & GST_PLAY_FLAG_TEXT && playsink->text_pad) {
2146     /* we have subtitles and we are requested to show it */
2147     need_text = TRUE;
2148   }
2149
2150   if (((flags & GST_PLAY_FLAG_VIDEO)
2151           || (flags & GST_PLAY_FLAG_NATIVE_VIDEO)) && playsink->video_pad) {
2152     /* we have video and we are requested to show it */
2153     need_video = TRUE;
2154
2155     /* we only deinterlace if native video is not requested and
2156      * we have raw video */
2157     if ((flags & GST_PLAY_FLAG_DEINTERLACE)
2158         && !(flags & GST_PLAY_FLAG_NATIVE_VIDEO) && playsink->video_pad_raw)
2159       need_deinterlace = TRUE;
2160   }
2161
2162   if (playsink->audio_pad) {
2163     if ((flags & GST_PLAY_FLAG_AUDIO) || (flags & GST_PLAY_FLAG_NATIVE_AUDIO)) {
2164       need_audio = TRUE;
2165     }
2166     if (playsink->audio_pad_raw) {
2167       /* only can do vis with raw uncompressed audio */
2168       if (flags & GST_PLAY_FLAG_VIS && !need_video) {
2169         /* also add video when we add visualisation */
2170         need_video = TRUE;
2171         need_vis = TRUE;
2172       }
2173     }
2174   }
2175
2176   /* we have a text_pad and we need text rendering, in this case we need a
2177    * video_pad to combine the video with the text or visualizations */
2178   if (need_text && !need_video) {
2179     if (playsink->video_pad) {
2180       need_video = TRUE;
2181     } else if (need_audio) {
2182       GST_ELEMENT_WARNING (playsink, STREAM, FORMAT,
2183           (_("Can't play a text file without video or visualizations.")),
2184           ("Have text pad but no video pad or visualizations"));
2185       need_text = FALSE;
2186     } else {
2187       GST_ELEMENT_ERROR (playsink, STREAM, FORMAT,
2188           (_("Can't play a text file without video or visualizations.")),
2189           ("Have text pad but no video pad or visualizations"));
2190       GST_PLAY_SINK_UNLOCK (playsink);
2191       return FALSE;
2192     }
2193   }
2194
2195   GST_DEBUG_OBJECT (playsink, "audio:%d, video:%d, vis:%d, text:%d", need_audio,
2196       need_video, need_vis, need_text);
2197
2198   /* set up video pipeline */
2199   if (need_video) {
2200     gboolean raw, async;
2201
2202     /* we need a raw sink when we do vis or when we have a raw pad */
2203     raw = need_vis ? TRUE : playsink->video_pad_raw;
2204     /* we try to set the sink async=FALSE when we need vis, this way we can
2205      * avoid a queue in the audio chain. */
2206     async = !need_vis;
2207
2208     GST_DEBUG_OBJECT (playsink, "adding video, raw %d",
2209         playsink->video_pad_raw);
2210
2211     if (playsink->videochain) {
2212       /* try to reactivate the chain */
2213       if (!setup_video_chain (playsink, raw, async)) {
2214         if (playsink->video_sinkpad_stream_synchronizer) {
2215           gst_element_release_request_pad (GST_ELEMENT_CAST
2216               (playsink->stream_synchronizer),
2217               playsink->video_sinkpad_stream_synchronizer);
2218           gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2219           playsink->video_sinkpad_stream_synchronizer = NULL;
2220           gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2221           playsink->video_srcpad_stream_synchronizer = NULL;
2222         }
2223
2224         add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2225
2226         /* Remove the sink from the bin to keep its state
2227          * and unparent it to allow reuse */
2228         if (playsink->videochain->sink)
2229           gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
2230               playsink->videochain->sink);
2231
2232         activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2233         free_chain ((GstPlayChain *) playsink->videochain);
2234         playsink->videochain = NULL;
2235       }
2236     }
2237
2238     if (!playsink->videochain)
2239       playsink->videochain = gen_video_chain (playsink, raw, async);
2240     if (!playsink->videochain)
2241       goto no_chain;
2242
2243     if (!playsink->video_sinkpad_stream_synchronizer) {
2244       GstIterator *it;
2245
2246       playsink->video_sinkpad_stream_synchronizer =
2247           gst_element_get_request_pad (GST_ELEMENT_CAST
2248           (playsink->stream_synchronizer), "sink_%d");
2249       it = gst_pad_iterate_internal_links
2250           (playsink->video_sinkpad_stream_synchronizer);
2251       g_assert (it);
2252       gst_iterator_next (it,
2253           (gpointer *) & playsink->video_srcpad_stream_synchronizer);
2254       g_assert (playsink->video_srcpad_stream_synchronizer);
2255       gst_iterator_free (it);
2256     }
2257
2258     if (playsink->video_pad)
2259       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad),
2260           playsink->video_sinkpad_stream_synchronizer);
2261
2262     if (need_deinterlace) {
2263       if (!playsink->videodeinterlacechain)
2264         playsink->videodeinterlacechain =
2265             gen_video_deinterlace_chain (playsink);
2266       if (!playsink->videodeinterlacechain)
2267         goto no_chain;
2268
2269       GST_DEBUG_OBJECT (playsink, "adding video deinterlace chain");
2270
2271       GST_DEBUG_OBJECT (playsink, "setting up deinterlacing chain");
2272
2273       add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
2274       activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
2275
2276       gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2277           playsink->videodeinterlacechain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2278     } else {
2279       if (playsink->videodeinterlacechain) {
2280         add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2281         activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
2282             FALSE);
2283       }
2284     }
2285
2286     GST_DEBUG_OBJECT (playsink, "adding video chain");
2287     add_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
2288     activate_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
2289     /* if we are not part of vis or subtitles, set the ghostpad target */
2290     if (!need_vis && !need_text && (!playsink->textchain
2291             || !playsink->text_pad)) {
2292       GST_DEBUG_OBJECT (playsink, "ghosting video sinkpad");
2293       if (need_deinterlace)
2294         gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
2295             playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2296       else
2297         gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2298             playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2299     }
2300   } else {
2301     GST_DEBUG_OBJECT (playsink, "no video needed");
2302     if (playsink->videochain) {
2303       GST_DEBUG_OBJECT (playsink, "removing video chain");
2304       if (playsink->vischain) {
2305         GstPad *srcpad;
2306
2307         GST_DEBUG_OBJECT (playsink, "unlinking vis chain");
2308
2309         /* also had visualisation, release the tee srcpad before we then
2310          * unlink the video from it */
2311         if (playsink->audio_tee_vissrc) {
2312           gst_element_release_request_pad (playsink->audio_tee,
2313               playsink->audio_tee_vissrc);
2314           gst_object_unref (playsink->audio_tee_vissrc);
2315           playsink->audio_tee_vissrc = NULL;
2316         }
2317         srcpad =
2318             gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2319         gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
2320       }
2321
2322       if (playsink->video_sinkpad_stream_synchronizer) {
2323         gst_element_release_request_pad (GST_ELEMENT_CAST
2324             (playsink->stream_synchronizer),
2325             playsink->video_sinkpad_stream_synchronizer);
2326         gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2327         playsink->video_sinkpad_stream_synchronizer = NULL;
2328         gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2329         playsink->video_srcpad_stream_synchronizer = NULL;
2330       }
2331
2332       add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2333       activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2334       playsink->videochain->ts_offset = NULL;
2335     }
2336
2337     if (playsink->videodeinterlacechain) {
2338       add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2339       activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2340     }
2341
2342     if (playsink->video_pad)
2343       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
2344   }
2345
2346   if (need_audio) {
2347     gboolean raw;
2348
2349     GST_DEBUG_OBJECT (playsink, "adding audio");
2350
2351     /* get a raw sink if we are asked for a raw pad */
2352     raw = playsink->audio_pad_raw;
2353
2354     if (playsink->audiochain) {
2355       /* try to reactivate the chain */
2356       if (!setup_audio_chain (playsink, raw)) {
2357         GST_DEBUG_OBJECT (playsink, "removing current audio chain");
2358         if (playsink->audio_tee_asrc) {
2359           gst_element_release_request_pad (playsink->audio_tee,
2360               playsink->audio_tee_asrc);
2361           gst_object_unref (playsink->audio_tee_asrc);
2362           playsink->audio_tee_asrc = NULL;
2363         }
2364
2365         if (playsink->audio_sinkpad_stream_synchronizer) {
2366           gst_element_release_request_pad (GST_ELEMENT_CAST
2367               (playsink->stream_synchronizer),
2368               playsink->audio_sinkpad_stream_synchronizer);
2369           gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
2370           playsink->audio_sinkpad_stream_synchronizer = NULL;
2371           gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
2372           playsink->audio_srcpad_stream_synchronizer = NULL;
2373         }
2374
2375         add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2376
2377         /* Remove the sink from the bin to keep its state
2378          * and unparent it to allow reuse */
2379         if (playsink->audiochain->sink)
2380           gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
2381               playsink->audiochain->sink);
2382
2383         activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2384         disconnect_chain (playsink->audiochain, playsink);
2385         playsink->audiochain->volume = NULL;
2386         playsink->audiochain->mute = NULL;
2387         playsink->audiochain->ts_offset = NULL;
2388         free_chain ((GstPlayChain *) playsink->audiochain);
2389         playsink->audiochain = NULL;
2390         playsink->volume_changed = playsink->mute_changed = FALSE;
2391       }
2392     }
2393
2394     if (!playsink->audiochain) {
2395       GST_DEBUG_OBJECT (playsink, "creating new audio chain");
2396       playsink->audiochain = gen_audio_chain (playsink, raw);
2397     }
2398
2399     if (!playsink->audio_sinkpad_stream_synchronizer) {
2400       GstIterator *it;
2401
2402       playsink->audio_sinkpad_stream_synchronizer =
2403           gst_element_get_request_pad (GST_ELEMENT_CAST
2404           (playsink->stream_synchronizer), "sink_%d");
2405       it = gst_pad_iterate_internal_links
2406           (playsink->audio_sinkpad_stream_synchronizer);
2407       g_assert (it);
2408       gst_iterator_next (it,
2409           (gpointer *) & playsink->audio_srcpad_stream_synchronizer);
2410       g_assert (playsink->audio_srcpad_stream_synchronizer);
2411       gst_iterator_free (it);
2412     }
2413
2414     if (playsink->audiochain) {
2415       GST_DEBUG_OBJECT (playsink, "adding audio chain");
2416       if (playsink->audio_tee_asrc == NULL) {
2417         playsink->audio_tee_asrc =
2418             gst_element_get_request_pad (playsink->audio_tee, "src%d");
2419       }
2420       add_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
2421       activate_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
2422       gst_pad_link_full (playsink->audio_tee_asrc,
2423           playsink->audio_sinkpad_stream_synchronizer,
2424           GST_PAD_LINK_CHECK_NOTHING);
2425       gst_pad_link_full (playsink->audio_srcpad_stream_synchronizer,
2426           playsink->audiochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2427     }
2428   } else {
2429     GST_DEBUG_OBJECT (playsink, "no audio needed");
2430     /* we have no audio or we are requested to not play audio */
2431     if (playsink->audiochain) {
2432       GST_DEBUG_OBJECT (playsink, "removing audio chain");
2433       /* release the audio pad */
2434       if (playsink->audio_tee_asrc) {
2435         gst_element_release_request_pad (playsink->audio_tee,
2436             playsink->audio_tee_asrc);
2437         gst_object_unref (playsink->audio_tee_asrc);
2438         playsink->audio_tee_asrc = NULL;
2439       }
2440
2441       if (playsink->audio_sinkpad_stream_synchronizer) {
2442         gst_element_release_request_pad (GST_ELEMENT_CAST
2443             (playsink->stream_synchronizer),
2444             playsink->audio_sinkpad_stream_synchronizer);
2445         gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
2446         playsink->audio_sinkpad_stream_synchronizer = NULL;
2447         gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
2448         playsink->audio_srcpad_stream_synchronizer = NULL;
2449       }
2450
2451       if (playsink->audiochain->sink_volume) {
2452         disconnect_chain (playsink->audiochain, playsink);
2453         playsink->audiochain->volume = NULL;
2454         playsink->audiochain->mute = NULL;
2455         playsink->audiochain->ts_offset = NULL;
2456       }
2457       add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2458       activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2459     }
2460   }
2461
2462   if (need_vis) {
2463     GstPad *srcpad;
2464
2465     if (!playsink->vischain)
2466       playsink->vischain = gen_vis_chain (playsink);
2467
2468     GST_DEBUG_OBJECT (playsink, "adding visualisation");
2469
2470     if (playsink->vischain) {
2471       GST_DEBUG_OBJECT (playsink, "setting up vis chain");
2472       srcpad =
2473           gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2474       add_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
2475       activate_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
2476       if (playsink->audio_tee_vissrc == NULL) {
2477         playsink->audio_tee_vissrc =
2478             gst_element_get_request_pad (playsink->audio_tee, "src%d");
2479       }
2480       gst_pad_link_full (playsink->audio_tee_vissrc,
2481           playsink->vischain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2482       gst_pad_link_full (srcpad, playsink->video_sinkpad_stream_synchronizer,
2483           GST_PAD_LINK_CHECK_NOTHING);
2484       gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2485           playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2486       gst_object_unref (srcpad);
2487     }
2488   } else {
2489     GST_DEBUG_OBJECT (playsink, "no vis needed");
2490     if (playsink->vischain) {
2491       if (playsink->audio_tee_vissrc) {
2492         gst_element_release_request_pad (playsink->audio_tee,
2493             playsink->audio_tee_vissrc);
2494         gst_object_unref (playsink->audio_tee_vissrc);
2495         playsink->audio_tee_vissrc = NULL;
2496       }
2497       GST_DEBUG_OBJECT (playsink, "removing vis chain");
2498       add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
2499       activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
2500     }
2501   }
2502
2503   if (need_text) {
2504     GST_DEBUG_OBJECT (playsink, "adding text");
2505     if (!playsink->textchain) {
2506       GST_DEBUG_OBJECT (playsink, "creating text chain");
2507       playsink->textchain = gen_text_chain (playsink);
2508     }
2509     if (playsink->textchain) {
2510       GstIterator *it;
2511
2512       GST_DEBUG_OBJECT (playsink, "adding text chain");
2513       if (playsink->textchain->overlay)
2514         g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", FALSE,
2515             NULL);
2516       add_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
2517
2518       if (!playsink->text_sinkpad_stream_synchronizer) {
2519         playsink->text_sinkpad_stream_synchronizer =
2520             gst_element_get_request_pad (GST_ELEMENT_CAST
2521             (playsink->stream_synchronizer), "sink_%d");
2522         it = gst_pad_iterate_internal_links
2523             (playsink->text_sinkpad_stream_synchronizer);
2524         g_assert (it);
2525         gst_iterator_next (it,
2526             (gpointer *) & playsink->text_srcpad_stream_synchronizer);
2527         g_assert (playsink->text_srcpad_stream_synchronizer);
2528         gst_iterator_free (it);
2529
2530         gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad),
2531             playsink->text_sinkpad_stream_synchronizer);
2532         gst_pad_link_full (playsink->text_srcpad_stream_synchronizer,
2533             playsink->textchain->textsinkpad, GST_PAD_LINK_CHECK_NOTHING);
2534       }
2535
2536       if (need_vis) {
2537         GstPad *srcpad;
2538
2539         srcpad =
2540             gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2541         gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
2542         gst_pad_link_full (srcpad, playsink->textchain->videosinkpad,
2543             GST_PAD_LINK_CHECK_NOTHING);
2544         gst_object_unref (srcpad);
2545       } else {
2546         if (need_deinterlace)
2547           gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
2548               playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
2549         else
2550           gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2551               playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
2552       }
2553       gst_pad_link_full (playsink->textchain->srcpad,
2554           playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2555
2556       activate_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
2557     }
2558   } else {
2559     GST_DEBUG_OBJECT (playsink, "no text needed");
2560     /* we have no subtitles/text or we are requested to not show them */
2561
2562     if (playsink->text_sinkpad_stream_synchronizer) {
2563       gst_element_release_request_pad (GST_ELEMENT_CAST
2564           (playsink->stream_synchronizer),
2565           playsink->text_sinkpad_stream_synchronizer);
2566       gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
2567       playsink->text_sinkpad_stream_synchronizer = NULL;
2568       gst_object_unref (playsink->text_srcpad_stream_synchronizer);
2569       playsink->text_srcpad_stream_synchronizer = NULL;
2570     }
2571
2572     if (playsink->textchain) {
2573       if (playsink->text_pad == NULL) {
2574         /* no text pad, remove the chain entirely */
2575         GST_DEBUG_OBJECT (playsink, "removing text chain");
2576         add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
2577         activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
2578       } else {
2579         /* we have a chain and a textpad, turn the subtitles off */
2580         GST_DEBUG_OBJECT (playsink, "turning off the text");
2581         if (playsink->textchain->overlay)
2582           g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", TRUE,
2583               NULL);
2584       }
2585     }
2586     if (!need_video && playsink->video_pad) {
2587       if (playsink->video_sinkpad_stream_synchronizer) {
2588         gst_element_release_request_pad (GST_ELEMENT_CAST
2589             (playsink->stream_synchronizer),
2590             playsink->video_sinkpad_stream_synchronizer);
2591         gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2592         playsink->video_sinkpad_stream_synchronizer = NULL;
2593         gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2594         playsink->video_srcpad_stream_synchronizer = NULL;
2595       }
2596
2597       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
2598     }
2599
2600     if (playsink->text_pad && !playsink->textchain)
2601       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad), NULL);
2602   }
2603   update_av_offset (playsink);
2604   do_async_done (playsink);
2605   GST_PLAY_SINK_UNLOCK (playsink);
2606
2607   return TRUE;
2608
2609   /* ERRORS */
2610 no_chain:
2611   {
2612     /* gen_ chain already posted error */
2613     GST_DEBUG_OBJECT (playsink, "failed to setup chain");
2614     GST_PLAY_SINK_UNLOCK (playsink);
2615     return FALSE;
2616   }
2617 }
2618
2619 /**
2620  * gst_play_sink_set_flags:
2621  * @playsink: a #GstPlaySink
2622  * @flags: #GstPlayFlags
2623  *
2624  * Configure @flags on @playsink. The flags control the behaviour of @playsink
2625  * when constructing the sink pipelins.
2626  *
2627  * Returns: TRUE if the flags could be configured.
2628  */
2629 gboolean
2630 gst_play_sink_set_flags (GstPlaySink * playsink, GstPlayFlags flags)
2631 {
2632   g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), FALSE);
2633
2634   GST_OBJECT_LOCK (playsink);
2635   playsink->flags = flags;
2636   GST_OBJECT_UNLOCK (playsink);
2637
2638   return TRUE;
2639 }
2640
2641 /**
2642  * gst_play_sink_get_flags:
2643  * @playsink: a #GstPlaySink
2644  *
2645  * Get the flags of @playsink. That flags control the behaviour of the sink when
2646  * it constructs the sink pipelines.
2647  *
2648  * Returns: the currently configured #GstPlayFlags.
2649  */
2650 GstPlayFlags
2651 gst_play_sink_get_flags (GstPlaySink * playsink)
2652 {
2653   GstPlayFlags res;
2654
2655   g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), 0);
2656
2657   GST_OBJECT_LOCK (playsink);
2658   res = playsink->flags;
2659   GST_OBJECT_UNLOCK (playsink);
2660
2661   return res;
2662 }
2663
2664 void
2665 gst_play_sink_set_font_desc (GstPlaySink * playsink, const gchar * desc)
2666 {
2667   GstPlayTextChain *chain;
2668
2669   GST_PLAY_SINK_LOCK (playsink);
2670   chain = (GstPlayTextChain *) playsink->textchain;
2671   g_free (playsink->font_desc);
2672   playsink->font_desc = g_strdup (desc);
2673   if (chain && chain->overlay) {
2674     g_object_set (chain->overlay, "font-desc", desc, NULL);
2675   }
2676   GST_PLAY_SINK_UNLOCK (playsink);
2677 }
2678
2679 gchar *
2680 gst_play_sink_get_font_desc (GstPlaySink * playsink)
2681 {
2682   gchar *result = NULL;
2683   GstPlayTextChain *chain;
2684
2685   GST_PLAY_SINK_LOCK (playsink);
2686   chain = (GstPlayTextChain *) playsink->textchain;
2687   if (chain && chain->overlay) {
2688     g_object_get (chain->overlay, "font-desc", &result, NULL);
2689     playsink->font_desc = g_strdup (result);
2690   } else {
2691     result = g_strdup (playsink->font_desc);
2692   }
2693   GST_PLAY_SINK_UNLOCK (playsink);
2694
2695   return result;
2696 }
2697
2698 void
2699 gst_play_sink_set_subtitle_encoding (GstPlaySink * playsink,
2700     const gchar * encoding)
2701 {
2702   GstPlayTextChain *chain;
2703
2704   GST_PLAY_SINK_LOCK (playsink);
2705   chain = (GstPlayTextChain *) playsink->textchain;
2706   g_free (playsink->subtitle_encoding);
2707   playsink->subtitle_encoding = g_strdup (encoding);
2708   if (chain && chain->overlay) {
2709     g_object_set (chain->overlay, "subtitle-encoding", encoding, NULL);
2710   }
2711   GST_PLAY_SINK_UNLOCK (playsink);
2712 }
2713
2714 gchar *
2715 gst_play_sink_get_subtitle_encoding (GstPlaySink * playsink)
2716 {
2717   gchar *result = NULL;
2718   GstPlayTextChain *chain;
2719
2720   GST_PLAY_SINK_LOCK (playsink);
2721   chain = (GstPlayTextChain *) playsink->textchain;
2722   if (chain && chain->overlay) {
2723     g_object_get (chain->overlay, "subtitle-encoding", &result, NULL);
2724     playsink->subtitle_encoding = g_strdup (result);
2725   } else {
2726     result = g_strdup (playsink->subtitle_encoding);
2727   }
2728   GST_PLAY_SINK_UNLOCK (playsink);
2729
2730   return result;
2731 }
2732
2733 static void
2734 update_av_offset (GstPlaySink * playsink)
2735 {
2736   gint64 av_offset;
2737   GstPlayAudioChain *achain;
2738   GstPlayVideoChain *vchain;
2739
2740   av_offset = playsink->av_offset;
2741   achain = (GstPlayAudioChain *) playsink->audiochain;
2742   vchain = (GstPlayVideoChain *) playsink->videochain;
2743
2744   if (achain && vchain && achain->ts_offset && vchain->ts_offset) {
2745     g_object_set (achain->ts_offset, "ts-offset", MAX (0, -av_offset), NULL);
2746     g_object_set (vchain->ts_offset, "ts-offset", MAX (0, av_offset), NULL);
2747   } else {
2748     GST_LOG_OBJECT (playsink, "no ts_offset elements");
2749   }
2750 }
2751
2752 void
2753 gst_play_sink_set_av_offset (GstPlaySink * playsink, gint64 av_offset)
2754 {
2755   GST_PLAY_SINK_LOCK (playsink);
2756   playsink->av_offset = av_offset;
2757   update_av_offset (playsink);
2758   GST_PLAY_SINK_UNLOCK (playsink);
2759 }
2760
2761 gint64
2762 gst_play_sink_get_av_offset (GstPlaySink * playsink)
2763 {
2764   gint64 result;
2765
2766   GST_PLAY_SINK_LOCK (playsink);
2767   result = playsink->av_offset;
2768   GST_PLAY_SINK_UNLOCK (playsink);
2769
2770   return result;
2771 }
2772
2773 /**
2774  * gst_play_sink_get_last_frame:
2775  * @playsink: a #GstPlaySink
2776  *
2777  * Get the last displayed frame from @playsink. This frame is in the native
2778  * format of the sink element, the caps on the result buffer contain the format
2779  * of the frame data.
2780  *
2781  * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
2782  * available.
2783  */
2784 GstBuffer *
2785 gst_play_sink_get_last_frame (GstPlaySink * playsink)
2786 {
2787   GstBuffer *result = NULL;
2788   GstPlayVideoChain *chain;
2789
2790   GST_PLAY_SINK_LOCK (playsink);
2791   GST_DEBUG_OBJECT (playsink, "taking last frame");
2792   /* get the video chain if we can */
2793   if ((chain = (GstPlayVideoChain *) playsink->videochain)) {
2794     GST_DEBUG_OBJECT (playsink, "found video chain");
2795     /* see if the chain is active */
2796     if (chain->chain.activated && chain->sink) {
2797       GstElement *elem;
2798
2799       GST_DEBUG_OBJECT (playsink, "video chain active and has a sink");
2800
2801       /* find and get the last-buffer property now */
2802       if ((elem =
2803               gst_play_sink_find_property (playsink, chain->sink,
2804                   "last-buffer", GST_TYPE_BUFFER))) {
2805         GST_DEBUG_OBJECT (playsink, "getting last-buffer property");
2806         g_object_get (elem, "last-buffer", &result, NULL);
2807         gst_object_unref (elem);
2808       }
2809     }
2810   }
2811   GST_PLAY_SINK_UNLOCK (playsink);
2812
2813   return result;
2814 }
2815
2816 /**
2817  * gst_play_sink_convert_frame:
2818  * @playsink: a #GstPlaySink
2819  * @caps: a #GstCaps
2820  *
2821  * Get the last displayed frame from @playsink. If caps is %NULL, the video will
2822  * be in the native format of the sink element and the caps on the buffer
2823  * describe the format of the frame. If @caps is not %NULL, the video
2824  * frame will be converted to the format of the caps.
2825  *
2826  * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
2827  * available or when the conversion failed.
2828  */
2829 GstBuffer *
2830 gst_play_sink_convert_frame (GstPlaySink * playsink, GstCaps * caps)
2831 {
2832   GstBuffer *result;
2833
2834   result = gst_play_sink_get_last_frame (playsink);
2835   if (result != NULL && caps != NULL) {
2836     GstBuffer *temp;
2837     GError *err = NULL;
2838
2839     temp = gst_video_convert_frame (result, caps, 25 * GST_SECOND, &err);
2840     gst_buffer_unref (result);
2841     if (temp == NULL && err) {
2842       /* I'm really uncertain whether we should make playsink post an error
2843        * on the bus or not. It's not like it's a critical issue regarding
2844        * playsink behaviour. */
2845       GST_ERROR ("Error converting frame: %s", err->message);
2846     }
2847     result = temp;
2848   }
2849   return result;
2850 }
2851
2852 /**
2853  * gst_play_sink_request_pad
2854  * @playsink: a #GstPlaySink
2855  * @type: a #GstPlaySinkType
2856  *
2857  * Create or return a pad of @type.
2858  *
2859  * Returns: a #GstPad of @type or %NULL when the pad could not be created.
2860  */
2861 GstPad *
2862 gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type)
2863 {
2864   GstPad *res = NULL;
2865   gboolean created = FALSE;
2866   gboolean raw = FALSE;
2867   gboolean activate = TRUE;
2868   const gchar *pad_name = NULL;
2869
2870   GST_DEBUG_OBJECT (playsink, "request pad type %d", type);
2871
2872   GST_PLAY_SINK_LOCK (playsink);
2873   switch (type) {
2874     case GST_PLAY_SINK_TYPE_AUDIO_RAW:
2875       pad_name = "audio_raw_sink";
2876       raw = TRUE;
2877     case GST_PLAY_SINK_TYPE_AUDIO:
2878       if (pad_name == NULL)
2879         pad_name = "audio_sink";
2880       if (!playsink->audio_tee) {
2881         GST_LOG_OBJECT (playsink, "creating tee");
2882         /* create tee when needed. This element will feed the audio sink chain
2883          * and the vis chain. */
2884         playsink->audio_tee = gst_element_factory_make ("tee", "audiotee");
2885         if (playsink->audio_tee == NULL) {
2886           post_missing_element_message (playsink, "tee");
2887           GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2888               (_("Missing element '%s' - check your GStreamer installation."),
2889                   "tee"), (NULL));
2890           res = NULL;
2891           break;
2892         } else {
2893           playsink->audio_tee_sink =
2894               gst_element_get_static_pad (playsink->audio_tee, "sink");
2895           gst_bin_add (GST_BIN_CAST (playsink), playsink->audio_tee);
2896           gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
2897         }
2898       } else {
2899         gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
2900       }
2901       if (!playsink->audio_pad) {
2902         GST_LOG_OBJECT (playsink, "ghosting tee sinkpad");
2903         playsink->audio_pad =
2904             gst_ghost_pad_new (pad_name, playsink->audio_tee_sink);
2905         created = TRUE;
2906       }
2907       playsink->audio_pad_raw = raw;
2908       res = playsink->audio_pad;
2909       break;
2910     case GST_PLAY_SINK_TYPE_VIDEO_RAW:
2911       pad_name = "video_raw_sink";
2912       raw = TRUE;
2913     case GST_PLAY_SINK_TYPE_VIDEO:
2914       if (pad_name == NULL)
2915         pad_name = "video_sink";
2916       if (!playsink->video_pad) {
2917         GST_LOG_OBJECT (playsink, "ghosting videosink");
2918         playsink->video_pad =
2919             gst_ghost_pad_new_no_target (pad_name, GST_PAD_SINK);
2920         created = TRUE;
2921       }
2922       playsink->video_pad_raw = raw;
2923       res = playsink->video_pad;
2924       break;
2925     case GST_PLAY_SINK_TYPE_TEXT:
2926       GST_LOG_OBJECT (playsink, "ghosting text");
2927       if (!playsink->text_pad) {
2928         playsink->text_pad =
2929             gst_ghost_pad_new_no_target ("text_sink", GST_PAD_SINK);
2930         created = TRUE;
2931       }
2932       res = playsink->text_pad;
2933       break;
2934     case GST_PLAY_SINK_TYPE_FLUSHING:
2935     {
2936       gchar *padname;
2937
2938       /* we need a unique padname for the flushing pad. */
2939       padname = g_strdup_printf ("flushing_%d", playsink->count);
2940       res = gst_ghost_pad_new_no_target (padname, GST_PAD_SINK);
2941       g_free (padname);
2942       playsink->count++;
2943       activate = FALSE;
2944       created = TRUE;
2945       break;
2946     }
2947     default:
2948       res = NULL;
2949       break;
2950   }
2951   GST_PLAY_SINK_UNLOCK (playsink);
2952
2953   if (created && res) {
2954     /* we have to add the pad when it's active or we get an error when the
2955      * element is 'running' */
2956     gst_pad_set_active (res, TRUE);
2957     gst_element_add_pad (GST_ELEMENT_CAST (playsink), res);
2958     if (!activate)
2959       gst_pad_set_active (res, activate);
2960   }
2961
2962   return res;
2963 }
2964
2965 static GstPad *
2966 gst_play_sink_request_new_pad (GstElement * element, GstPadTemplate * templ,
2967     const gchar * name)
2968 {
2969   GstPlaySink *psink;
2970   GstPad *pad;
2971   GstPlaySinkType type;
2972   const gchar *tplname;
2973
2974   g_return_val_if_fail (templ != NULL, NULL);
2975
2976   GST_DEBUG_OBJECT (element, "name:%s", name);
2977
2978   psink = GST_PLAY_SINK (element);
2979   tplname = GST_PAD_TEMPLATE_NAME_TEMPLATE (templ);
2980
2981   /* Figure out the GstPlaySinkType based on the template */
2982   if (!strcmp (tplname, "audio_sink"))
2983     type = GST_PLAY_SINK_TYPE_AUDIO;
2984   else if (!strcmp (tplname, "audio_raw_sink"))
2985     type = GST_PLAY_SINK_TYPE_AUDIO_RAW;
2986   else if (!strcmp (tplname, "video_sink"))
2987     type = GST_PLAY_SINK_TYPE_VIDEO;
2988   else if (!strcmp (tplname, "video_raw_sink"))
2989     type = GST_PLAY_SINK_TYPE_VIDEO_RAW;
2990   else if (!strcmp (tplname, "text_sink"))
2991     type = GST_PLAY_SINK_TYPE_TEXT;
2992   else
2993     goto unknown_template;
2994
2995   pad = gst_play_sink_request_pad (psink, type);
2996   return pad;
2997
2998 unknown_template:
2999   GST_WARNING_OBJECT (element, "Unknown pad template");
3000   return NULL;
3001 }
3002
3003 void
3004 gst_play_sink_release_pad (GstPlaySink * playsink, GstPad * pad)
3005 {
3006   GstPad **res = NULL;
3007   gboolean untarget = TRUE;
3008
3009   GST_DEBUG_OBJECT (playsink, "release pad %" GST_PTR_FORMAT, pad);
3010
3011   GST_PLAY_SINK_LOCK (playsink);
3012   if (pad == playsink->video_pad) {
3013     res = &playsink->video_pad;
3014   } else if (pad == playsink->audio_pad) {
3015     res = &playsink->audio_pad;
3016   } else if (pad == playsink->text_pad) {
3017     res = &playsink->text_pad;
3018   } else {
3019     /* try to release the given pad anyway, these could be the FLUSHING pads. */
3020     res = &pad;
3021     untarget = FALSE;
3022   }
3023   GST_PLAY_SINK_UNLOCK (playsink);
3024
3025   if (*res) {
3026     GST_DEBUG_OBJECT (playsink, "deactivate pad %" GST_PTR_FORMAT, *res);
3027     gst_pad_set_active (*res, FALSE);
3028     if (untarget) {
3029       GST_DEBUG_OBJECT (playsink, "untargeting pad %" GST_PTR_FORMAT, *res);
3030       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (*res), NULL);
3031     }
3032     GST_DEBUG_OBJECT (playsink, "remove pad %" GST_PTR_FORMAT, *res);
3033     gst_element_remove_pad (GST_ELEMENT_CAST (playsink), *res);
3034     *res = NULL;
3035   }
3036 }
3037
3038 static void
3039 gst_play_sink_release_request_pad (GstElement * element, GstPad * pad)
3040 {
3041   GstPlaySink *psink = GST_PLAY_SINK (element);
3042
3043   gst_play_sink_release_pad (psink, pad);
3044 }
3045
3046 static void
3047 gst_play_sink_handle_message (GstBin * bin, GstMessage * message)
3048 {
3049   GstPlaySink *playsink;
3050
3051   playsink = GST_PLAY_SINK_CAST (bin);
3052
3053   switch (GST_MESSAGE_TYPE (message)) {
3054     case GST_MESSAGE_STEP_DONE:
3055     {
3056       GstFormat format;
3057       guint64 amount;
3058       gdouble rate;
3059       gboolean flush, intermediate, eos;
3060       guint64 duration;
3061
3062       GST_INFO_OBJECT (playsink, "Handling step-done message");
3063       gst_message_parse_step_done (message, &format, &amount, &rate, &flush,
3064           &intermediate, &duration, &eos);
3065
3066       if (format == GST_FORMAT_BUFFERS) {
3067         /* for the buffer format, we align the other streams */
3068         if (playsink->audiochain) {
3069           GstEvent *event;
3070
3071           event =
3072               gst_event_new_step (GST_FORMAT_TIME, duration, rate, flush,
3073               intermediate);
3074
3075           if (!gst_element_send_event (playsink->audiochain->chain.bin, event)) {
3076             GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
3077           }
3078         }
3079       }
3080       GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
3081       break;
3082     }
3083     default:
3084       GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
3085       break;
3086   }
3087 }
3088
3089 /* Send an event to our sinks until one of them works; don't then send to the
3090  * remaining sinks (unlike GstBin)
3091  * Special case: If a text sink is set we need to send the event
3092  * to them in case it's source is different from the a/v stream's source.
3093  */
3094 static gboolean
3095 gst_play_sink_send_event_to_sink (GstPlaySink * playsink, GstEvent * event)
3096 {
3097   gboolean res = TRUE;
3098
3099   if (playsink->textchain && playsink->textchain->sink) {
3100     gst_event_ref (event);
3101     if ((res = gst_element_send_event (playsink->textchain->chain.bin, event))) {
3102       GST_DEBUG_OBJECT (playsink, "Sent event succesfully to text sink");
3103     } else {
3104       GST_DEBUG_OBJECT (playsink, "Event failed when sent to text sink");
3105     }
3106   }
3107
3108   if (playsink->videochain) {
3109     gst_event_ref (event);
3110     if ((res = gst_element_send_event (playsink->videochain->chain.bin, event))) {
3111       GST_DEBUG_OBJECT (playsink, "Sent event succesfully to video sink");
3112       goto done;
3113     }
3114     GST_DEBUG_OBJECT (playsink, "Event failed when sent to video sink");
3115   }
3116   if (playsink->audiochain) {
3117     gst_event_ref (event);
3118     if ((res = gst_element_send_event (playsink->audiochain->chain.bin, event))) {
3119       GST_DEBUG_OBJECT (playsink, "Sent event succesfully to audio sink");
3120       goto done;
3121     }
3122     GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
3123   }
3124
3125 done:
3126   gst_event_unref (event);
3127   return res;
3128 }
3129
3130 /* We only want to send the event to a single sink (overriding GstBin's
3131  * behaviour), but we want to keep GstPipeline's behaviour - wrapping seek
3132  * events appropriately. So, this is a messy duplication of code. */
3133 static gboolean
3134 gst_play_sink_send_event (GstElement * element, GstEvent * event)
3135 {
3136   gboolean res = FALSE;
3137   GstEventType event_type = GST_EVENT_TYPE (event);
3138   GstPlaySink *playsink;
3139
3140   playsink = GST_PLAY_SINK_CAST (element);
3141
3142   switch (event_type) {
3143     case GST_EVENT_SEEK:
3144       GST_DEBUG_OBJECT (element, "Sending event to a sink");
3145       res = gst_play_sink_send_event_to_sink (playsink, event);
3146       break;
3147     case GST_EVENT_STEP:
3148     {
3149       GstFormat format;
3150       guint64 amount;
3151       gdouble rate;
3152       gboolean flush, intermediate;
3153
3154       gst_event_parse_step (event, &format, &amount, &rate, &flush,
3155           &intermediate);
3156
3157       if (format == GST_FORMAT_BUFFERS) {
3158         /* for buffers, we will try to step video frames, for other formats we
3159          * send the step to all sinks */
3160         res = gst_play_sink_send_event_to_sink (playsink, event);
3161       } else {
3162         res =
3163             GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
3164             event);
3165       }
3166       break;
3167     }
3168     default:
3169       res =
3170           GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
3171           event);
3172       break;
3173   }
3174   return res;
3175 }
3176
3177 static GstStateChangeReturn
3178 gst_play_sink_change_state (GstElement * element, GstStateChange transition)
3179 {
3180   GstStateChangeReturn ret;
3181   GstStateChangeReturn bret;
3182
3183   GstPlaySink *playsink;
3184
3185   playsink = GST_PLAY_SINK (element);
3186
3187   switch (transition) {
3188     case GST_STATE_CHANGE_READY_TO_PAUSED:
3189       playsink->need_async_start = TRUE;
3190       /* we want to go async to PAUSED until we managed to configure and add the
3191        * sinks */
3192       do_async_start (playsink);
3193       ret = GST_STATE_CHANGE_ASYNC;
3194       break;
3195     case GST_STATE_CHANGE_PAUSED_TO_READY:
3196     case GST_STATE_CHANGE_READY_TO_NULL:
3197       if (playsink->audiochain && playsink->audiochain->sink_volume) {
3198         /* remove our links to the mute and volume elements when they were
3199          * provided by a sink */
3200         disconnect_chain (playsink->audiochain, playsink);
3201         playsink->audiochain->volume = NULL;
3202         playsink->audiochain->mute = NULL;
3203         playsink->audiochain->ts_offset = NULL;
3204       }
3205       ret = GST_STATE_CHANGE_SUCCESS;
3206       break;
3207     default:
3208       /* all other state changes return SUCCESS by default, this value can be
3209        * overridden by the result of the children */
3210       ret = GST_STATE_CHANGE_SUCCESS;
3211       break;
3212   }
3213
3214   /* do the state change of the children */
3215   bret =
3216       GST_ELEMENT_CLASS (gst_play_sink_parent_class)->change_state (element,
3217       transition);
3218   /* now look at the result of our children and adjust the return value */
3219   switch (bret) {
3220     case GST_STATE_CHANGE_FAILURE:
3221       /* failure, we stop */
3222       goto activate_failed;
3223     case GST_STATE_CHANGE_NO_PREROLL:
3224       /* some child returned NO_PREROLL. This is strange but we never know. We
3225        * commit our async state change (if any) and return the NO_PREROLL */
3226       do_async_done (playsink);
3227       ret = bret;
3228       break;
3229     case GST_STATE_CHANGE_ASYNC:
3230       /* some child was async, return this */
3231       ret = bret;
3232       break;
3233     default:
3234       /* return our previously configured return value */
3235       break;
3236   }
3237
3238   switch (transition) {
3239     case GST_STATE_CHANGE_READY_TO_PAUSED:
3240       break;
3241     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
3242       /* FIXME Release audio device when we implement that */
3243       playsink->need_async_start = TRUE;
3244       break;
3245     case GST_STATE_CHANGE_PAUSED_TO_READY:{
3246       if (playsink->video_sinkpad_stream_synchronizer) {
3247         gst_element_release_request_pad (GST_ELEMENT_CAST
3248             (playsink->stream_synchronizer),
3249             playsink->video_sinkpad_stream_synchronizer);
3250         gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3251         playsink->video_sinkpad_stream_synchronizer = NULL;
3252         gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3253         playsink->video_srcpad_stream_synchronizer = NULL;
3254       }
3255       if (playsink->audio_sinkpad_stream_synchronizer) {
3256         gst_element_release_request_pad (GST_ELEMENT_CAST
3257             (playsink->stream_synchronizer),
3258             playsink->audio_sinkpad_stream_synchronizer);
3259         gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
3260         playsink->audio_sinkpad_stream_synchronizer = NULL;
3261         gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
3262         playsink->audio_srcpad_stream_synchronizer = NULL;
3263       }
3264       if (playsink->text_sinkpad_stream_synchronizer) {
3265         gst_element_release_request_pad (GST_ELEMENT_CAST
3266             (playsink->stream_synchronizer),
3267             playsink->text_sinkpad_stream_synchronizer);
3268         gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
3269         playsink->text_sinkpad_stream_synchronizer = NULL;
3270         gst_object_unref (playsink->text_srcpad_stream_synchronizer);
3271         playsink->text_srcpad_stream_synchronizer = NULL;
3272       }
3273     }
3274       /* fall through */
3275     case GST_STATE_CHANGE_READY_TO_NULL:
3276       /* remove sinks we added */
3277       if (playsink->videodeinterlacechain) {
3278         activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
3279             FALSE);
3280         add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3281       }
3282       if (playsink->videochain) {
3283         activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3284         add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3285       }
3286       if (playsink->audiochain) {
3287         activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3288         add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3289       }
3290       if (playsink->vischain) {
3291         activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3292         add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3293       }
3294       if (playsink->textchain) {
3295         activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3296         add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3297       }
3298       do_async_done (playsink);
3299       /* when going to READY, keep elements around as long as possible,
3300        * so they may be re-used faster next time/url around.
3301        * when really going to NULL, clean up everything completely. */
3302       if (transition == GST_STATE_CHANGE_READY_TO_NULL) {
3303
3304         /* Unparent the sinks to allow reuse */
3305         if (playsink->videochain && playsink->videochain->sink)
3306           gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
3307               playsink->videochain->sink);
3308         if (playsink->audiochain && playsink->audiochain->sink)
3309           gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
3310               playsink->audiochain->sink);
3311         if (playsink->textchain && playsink->textchain->sink)
3312           gst_bin_remove (GST_BIN_CAST (playsink->textchain->chain.bin),
3313               playsink->textchain->sink);
3314
3315         if (playsink->audio_sink != NULL)
3316           gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
3317         if (playsink->video_sink != NULL)
3318           gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
3319         if (playsink->visualisation != NULL)
3320           gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
3321         if (playsink->text_sink != NULL)
3322           gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
3323
3324         free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
3325         playsink->videodeinterlacechain = NULL;
3326         free_chain ((GstPlayChain *) playsink->videochain);
3327         playsink->videochain = NULL;
3328         free_chain ((GstPlayChain *) playsink->audiochain);
3329         playsink->audiochain = NULL;
3330         free_chain ((GstPlayChain *) playsink->vischain);
3331         playsink->vischain = NULL;
3332         free_chain ((GstPlayChain *) playsink->textchain);
3333         playsink->textchain = NULL;
3334       }
3335       break;
3336     default:
3337       break;
3338   }
3339   return ret;
3340
3341   /* ERRORS */
3342 activate_failed:
3343   {
3344     GST_DEBUG_OBJECT (element,
3345         "element failed to change states -- activation problem?");
3346     return GST_STATE_CHANGE_FAILURE;
3347   }
3348 }
3349
3350 static void
3351 gst_play_sink_set_property (GObject * object, guint prop_id,
3352     const GValue * value, GParamSpec * spec)
3353 {
3354   GstPlaySink *playsink = GST_PLAY_SINK (object);
3355
3356   switch (prop_id) {
3357     case PROP_FLAGS:
3358       gst_play_sink_set_flags (playsink, g_value_get_flags (value));
3359       break;
3360     case PROP_VOLUME:
3361       gst_play_sink_set_volume (playsink, g_value_get_double (value));
3362       break;
3363     case PROP_MUTE:
3364       gst_play_sink_set_mute (playsink, g_value_get_boolean (value));
3365       break;
3366     case PROP_FONT_DESC:
3367       gst_play_sink_set_font_desc (playsink, g_value_get_string (value));
3368       break;
3369     case PROP_SUBTITLE_ENCODING:
3370       gst_play_sink_set_subtitle_encoding (playsink,
3371           g_value_get_string (value));
3372       break;
3373     case PROP_VIS_PLUGIN:
3374       gst_play_sink_set_vis_plugin (playsink, g_value_get_object (value));
3375       break;
3376     case PROP_AV_OFFSET:
3377       gst_play_sink_set_av_offset (playsink, g_value_get_int64 (value));
3378       break;
3379     default:
3380       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
3381       break;
3382   }
3383 }
3384
3385 static void
3386 gst_play_sink_get_property (GObject * object, guint prop_id,
3387     GValue * value, GParamSpec * spec)
3388 {
3389   GstPlaySink *playsink = GST_PLAY_SINK (object);
3390
3391   switch (prop_id) {
3392     case PROP_FLAGS:
3393       g_value_set_flags (value, gst_play_sink_get_flags (playsink));
3394       break;
3395     case PROP_VOLUME:
3396       g_value_set_double (value, gst_play_sink_get_volume (playsink));
3397       break;
3398     case PROP_MUTE:
3399       g_value_set_boolean (value, gst_play_sink_get_mute (playsink));
3400       break;
3401     case PROP_FONT_DESC:
3402       g_value_take_string (value, gst_play_sink_get_font_desc (playsink));
3403       break;
3404     case PROP_SUBTITLE_ENCODING:
3405       g_value_take_string (value,
3406           gst_play_sink_get_subtitle_encoding (playsink));
3407       break;
3408     case PROP_VIS_PLUGIN:
3409       g_value_take_object (value, gst_play_sink_get_vis_plugin (playsink));
3410       break;
3411     case PROP_FRAME:
3412       gst_value_take_buffer (value, gst_play_sink_get_last_frame (playsink));
3413       break;
3414     case PROP_AV_OFFSET:
3415       g_value_set_int64 (value, gst_play_sink_get_av_offset (playsink));
3416       break;
3417     default:
3418       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
3419       break;
3420   }
3421 }
3422
3423
3424 gboolean
3425 gst_play_sink_plugin_init (GstPlugin * plugin)
3426 {
3427   GST_DEBUG_CATEGORY_INIT (gst_play_sink_debug, "playsink", 0, "play bin");
3428
3429   return gst_element_register (plugin, "playsink", GST_RANK_NONE,
3430       GST_TYPE_PLAY_SINK);
3431 }