f8681b6ebd6de8c46ac72d569902aeb85be71346
[platform/upstream/gstreamer.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
30 #include "gstplaysink.h"
31
32 GST_DEBUG_CATEGORY_STATIC (gst_play_sink_debug);
33 #define GST_CAT_DEFAULT gst_play_sink_debug
34
35 #define VOLUME_MAX_DOUBLE 10.0
36
37 #define GST_PLAY_CHAIN(c) (GstPlayChain *)(c)
38
39 /* holds the common data fields for the audio and video pipelines. We keep them
40  * in a structure to more easily have all the info available. */
41 typedef struct
42 {
43   GstPlaySink *playsink;
44   GstElement *bin;
45   gboolean added;
46   gboolean activated;
47   gboolean raw;
48 } GstPlayChain;
49
50 typedef struct
51 {
52   GstPlayChain chain;
53   GstPad *sinkpad;
54   GstElement *queue;
55   GstElement *conv;
56   GstElement *resample;
57   GstElement *volume;           /* element with the volume property */
58   GstElement *mute;             /* element with the mute property */
59   GstElement *sink;
60 } GstPlayAudioChain;
61
62 typedef struct
63 {
64   GstPlayChain chain;
65   GstPad *sinkpad;
66   GstElement *queue;
67   GstElement *conv;
68   GstElement *scale;
69   GstElement *sink;
70   gboolean async;
71 } GstPlayVideoChain;
72
73 typedef struct
74 {
75   GstPlayChain chain;
76   GstPad *sinkpad;
77   GstElement *queue;
78   GstElement *conv;
79   GstElement *resample;
80   GstPad *blockpad;             /* srcpad of resample, used for switching the vis */
81   GstPad *vissinkpad;           /* visualisation sinkpad, */
82   GstElement *vis;
83   GstPad *vissrcpad;            /* visualisation srcpad, */
84   GstPad *srcpad;               /* outgoing srcpad, used to connect to the next
85                                  * chain */
86 } GstPlayVisChain;
87
88 typedef struct
89 {
90   GstPlayChain chain;
91   GstPad *sinkpad;
92   GstElement *conv;
93   GstElement *overlay;
94   GstPad *videosinkpad;
95   GstPad *textsinkpad;
96   GstPad *srcpad;               /* outgoing srcpad, used to connect to the next
97                                  * chain */
98 } GstPlayTextChain;
99
100 #define GST_PLAY_SINK_GET_LOCK(playsink) (((GstPlaySink *)playsink)->lock)
101 #define GST_PLAY_SINK_LOCK(playsink)     g_mutex_lock (GST_PLAY_SINK_GET_LOCK (playsink))
102 #define GST_PLAY_SINK_UNLOCK(playsink)   g_mutex_unlock (GST_PLAY_SINK_GET_LOCK (playsink))
103
104 struct _GstPlaySink
105 {
106   GstBin bin;
107
108   GMutex *lock;
109
110   GstPlayFlags flags;
111
112   GstPlayAudioChain *audiochain;
113   GstPlayVideoChain *videochain;
114   GstPlayVisChain *vischain;
115   GstPlayTextChain *textchain;
116
117   GstPad *audio_pad;
118   gboolean audio_pad_raw;
119   GstElement *audio_tee;
120   GstPad *audio_tee_sink;
121   GstPad *audio_tee_asrc;
122   GstPad *audio_tee_vissrc;
123
124   GstPad *video_pad;
125   gboolean video_pad_raw;
126
127   GstPad *text_pad;
128
129   /* properties */
130   GstElement *audio_sink;
131   GstElement *video_sink;
132   GstElement *visualisation;
133   gfloat volume;
134   gboolean mute;
135   gchar *font_desc;             /* font description */
136   guint connection_speed;       /* connection speed in bits/sec (0 = unknown) */
137 };
138
139 struct _GstPlaySinkClass
140 {
141   GstBinClass parent_class;
142 };
143
144
145 /* props */
146 enum
147 {
148   PROP_0,
149   PROP_AUDIO_SINK,
150   PROP_VIDEO_SINK,
151   PROP_VIS_PLUGIN,
152   PROP_VOLUME,
153   PROP_FRAME,
154   PROP_FONT_DESC,
155   PROP_LAST
156 };
157
158 /* signals */
159 enum
160 {
161   LAST_SIGNAL
162 };
163
164 static void gst_play_sink_class_init (GstPlaySinkClass * klass);
165 static void gst_play_sink_init (GstPlaySink * playsink);
166 static void gst_play_sink_dispose (GObject * object);
167 static void gst_play_sink_finalize (GObject * object);
168
169 static gboolean gst_play_sink_send_event (GstElement * element,
170     GstEvent * event);
171 static GstStateChangeReturn gst_play_sink_change_state (GstElement * element,
172     GstStateChange transition);
173
174 /* static guint gst_play_sink_signals[LAST_SIGNAL] = { 0 }; */
175
176 static const GstElementDetails gst_play_sink_details =
177 GST_ELEMENT_DETAILS ("Player Sink",
178     "Generic/Bin/Player",
179     "Autoplug and play media from an uri",
180     "Wim Taymans <wim.taymans@gmail.com>");
181
182 G_DEFINE_TYPE (GstPlaySink, gst_play_sink, GST_TYPE_BIN);
183
184 static void
185 gst_play_sink_class_init (GstPlaySinkClass * klass)
186 {
187   GObjectClass *gobject_klass;
188   GstElementClass *gstelement_klass;
189   GstBinClass *gstbin_klass;
190
191   gobject_klass = (GObjectClass *) klass;
192   gstelement_klass = (GstElementClass *) klass;
193   gstbin_klass = (GstBinClass *) klass;
194
195   gobject_klass->dispose = GST_DEBUG_FUNCPTR (gst_play_sink_dispose);
196   gobject_klass->finalize = GST_DEBUG_FUNCPTR (gst_play_sink_finalize);
197
198   gst_element_class_set_details (gstelement_klass, &gst_play_sink_details);
199
200   gstelement_klass->change_state =
201       GST_DEBUG_FUNCPTR (gst_play_sink_change_state);
202   gstelement_klass->send_event = GST_DEBUG_FUNCPTR (gst_play_sink_send_event);
203
204   GST_DEBUG_CATEGORY_INIT (gst_play_sink_debug, "playsink", 0, "play bin");
205 }
206
207 static void
208 gst_play_sink_init (GstPlaySink * playsink)
209 {
210   /* init groups */
211   playsink->video_sink = NULL;
212   playsink->audio_sink = NULL;
213   playsink->visualisation = NULL;
214   playsink->volume = 1.0;
215   playsink->font_desc = NULL;
216   playsink->flags = GST_PLAY_FLAG_SOFT_VOLUME;
217
218   playsink->lock = g_mutex_new ();
219 }
220
221 static void
222 free_chain (GstPlayChain * chain)
223 {
224   if (chain) {
225     if (chain->bin)
226       gst_object_unref (chain->bin);
227     g_free (chain);
228   }
229 }
230
231 static void
232 gst_play_sink_dispose (GObject * object)
233 {
234   GstPlaySink *playsink;
235
236   playsink = GST_PLAY_SINK (object);
237
238   if (playsink->audio_sink != NULL) {
239     gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
240     gst_object_unref (playsink->audio_sink);
241     playsink->audio_sink = NULL;
242   }
243   if (playsink->video_sink != NULL) {
244     gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
245     gst_object_unref (playsink->video_sink);
246     playsink->video_sink = NULL;
247   }
248   if (playsink->visualisation != NULL) {
249     gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
250     gst_object_unref (playsink->visualisation);
251     playsink->visualisation = NULL;
252   }
253
254   free_chain ((GstPlayChain *) playsink->videochain);
255   playsink->videochain = NULL;
256   free_chain ((GstPlayChain *) playsink->audiochain);
257   playsink->audiochain = NULL;
258   free_chain ((GstPlayChain *) playsink->vischain);
259   playsink->vischain = NULL;
260   free_chain ((GstPlayChain *) playsink->textchain);
261   playsink->textchain = NULL;
262
263
264   if (playsink->audio_tee_sink) {
265     gst_object_unref (playsink->audio_tee_sink);
266     playsink->audio_tee_sink = NULL;
267   }
268
269   if (playsink->audio_tee_vissrc) {
270     gst_element_release_request_pad (playsink->audio_tee,
271         playsink->audio_tee_vissrc);
272     gst_object_unref (playsink->audio_tee_vissrc);
273     playsink->audio_tee_vissrc = NULL;
274   }
275
276   if (playsink->audio_tee_asrc) {
277     gst_element_release_request_pad (playsink->audio_tee,
278         playsink->audio_tee_asrc);
279     gst_object_unref (playsink->audio_tee_asrc);
280     playsink->audio_tee_asrc = NULL;
281   }
282
283   g_free (playsink->font_desc);
284   playsink->font_desc = NULL;
285
286   G_OBJECT_CLASS (gst_play_sink_parent_class)->dispose (object);
287 }
288
289 static void
290 gst_play_sink_finalize (GObject * object)
291 {
292   GstPlaySink *playsink;
293
294   playsink = GST_PLAY_SINK (object);
295
296   g_mutex_free (playsink->lock);
297
298   G_OBJECT_CLASS (gst_play_sink_parent_class)->finalize (object);
299 }
300
301 void
302 gst_play_sink_set_video_sink (GstPlaySink * playsink, GstElement * sink)
303 {
304   GST_PLAY_SINK_LOCK (playsink);
305   if (playsink->video_sink)
306     gst_object_unref (playsink->video_sink);
307
308   if (sink) {
309     gst_object_ref (sink);
310     gst_object_sink (sink);
311   }
312   playsink->video_sink = sink;
313   GST_PLAY_SINK_UNLOCK (playsink);
314 }
315
316 GstElement *
317 gst_play_sink_get_video_sink (GstPlaySink * playsink)
318 {
319   GstElement *result = NULL;
320   GstPlayVideoChain *chain;
321
322   GST_PLAY_SINK_LOCK (playsink);
323   if ((chain = (GstPlayVideoChain *) playsink->videochain)) {
324     /* we have an active chain, get the sink */
325     if (chain->sink)
326       result = gst_object_ref (chain->sink);
327   }
328   /* nothing found, return last configured sink */
329   if (result == NULL && playsink->video_sink)
330     result = gst_object_ref (playsink->video_sink);
331   GST_PLAY_SINK_UNLOCK (playsink);
332
333   return result;
334 }
335
336 void
337 gst_play_sink_set_audio_sink (GstPlaySink * playsink, GstElement * sink)
338 {
339   GST_PLAY_SINK_LOCK (playsink);
340   if (playsink->audio_sink)
341     gst_object_unref (playsink->audio_sink);
342
343   if (sink) {
344     gst_object_ref (sink);
345     gst_object_sink (sink);
346   }
347   playsink->audio_sink = sink;
348   GST_PLAY_SINK_UNLOCK (playsink);
349 }
350
351 GstElement *
352 gst_play_sink_get_audio_sink (GstPlaySink * playsink)
353 {
354   GstElement *result = NULL;
355   GstPlayAudioChain *chain;
356
357   GST_PLAY_SINK_LOCK (playsink);
358   if ((chain = (GstPlayAudioChain *) playsink->audiochain)) {
359     /* we have an active chain, get the sink */
360     if (chain->sink)
361       result = gst_object_ref (chain->sink);
362   }
363   /* nothing found, return last configured sink */
364   if (result == NULL && playsink->audio_sink)
365     result = gst_object_ref (playsink->audio_sink);
366   GST_PLAY_SINK_UNLOCK (playsink);
367
368   return result;
369 }
370
371 static void
372 gst_play_sink_vis_unblocked (GstPad * tee_pad, gboolean blocked,
373     gpointer user_data)
374 {
375   GstPlaySink *playsink;
376
377   playsink = GST_PLAY_SINK (user_data);
378   /* nothing to do here, we need a dummy callback here to make the async call
379    * non-blocking. */
380   GST_DEBUG_OBJECT (playsink, "vis pad unblocked");
381 }
382
383 static void
384 gst_play_sink_vis_blocked (GstPad * tee_pad, gboolean blocked,
385     gpointer user_data)
386 {
387   GstPlaySink *playsink;
388   GstPlayVisChain *chain;
389
390   playsink = GST_PLAY_SINK (user_data);
391
392   GST_PLAY_SINK_LOCK (playsink);
393   GST_DEBUG_OBJECT (playsink, "vis pad blocked");
394   /* now try to change the plugin in the running vis chain */
395   if (!(chain = (GstPlayVisChain *) playsink->vischain))
396     goto done;
397
398   /* unlink the old plugin and unghost the pad */
399   gst_pad_unlink (chain->blockpad, chain->vissinkpad);
400   gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad), NULL);
401
402   /* set the old plugin to NULL and remove */
403   gst_element_set_state (chain->vis, GST_STATE_NULL);
404   gst_bin_remove (GST_BIN_CAST (chain->chain.bin), chain->vis);
405
406   /* add new plugin and set state to playing */
407   chain->vis = gst_object_ref (playsink->visualisation);
408   gst_bin_add (GST_BIN_CAST (chain->chain.bin), chain->vis);
409   gst_element_set_state (chain->vis, GST_STATE_PLAYING);
410
411   /* get pads */
412   chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
413   chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
414
415   /* link pads */
416   gst_pad_link (chain->blockpad, chain->vissinkpad);
417   gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad),
418       chain->vissrcpad);
419
420 done:
421   /* Unblock the pad */
422   gst_pad_set_blocked_async (tee_pad, FALSE, gst_play_sink_vis_unblocked,
423       playsink);
424   GST_PLAY_SINK_UNLOCK (playsink);
425 }
426
427 void
428 gst_play_sink_set_vis_plugin (GstPlaySink * playsink, GstElement * vis)
429 {
430   GstPlayVisChain *chain;
431
432   /* setting NULL means creating the default vis plugin */
433   if (vis == NULL)
434     vis = gst_element_factory_make ("goom", "vis");
435
436   /* simply return if we don't have a vis plugin here */
437   if (vis == NULL)
438     return;
439
440   GST_PLAY_SINK_LOCK (playsink);
441   /* first store the new vis */
442   if (playsink->visualisation)
443     gst_object_unref (playsink->visualisation);
444   playsink->visualisation = gst_object_ref (vis);
445
446   /* now try to change the plugin in the running vis chain, if we have no chain,
447    * we don't bother, any future vis chain will be created with the new vis
448    * plugin. */
449   if (!(chain = (GstPlayVisChain *) playsink->vischain))
450     goto done;
451
452   /* block the pad, the next time the callback is called we can change the
453    * visualisation. It's possible that this never happens or that the pad was
454    * already blocked. If the callback never happens, we don't have new data so
455    * we don't need the new vis plugin. If the pad was already blocked, the
456    * function returns FALSE but the previous pad block will do the right thing
457    * anyway. */
458   GST_DEBUG_OBJECT (playsink, "blocking vis pad");
459   gst_pad_set_blocked_async (chain->blockpad, TRUE, gst_play_sink_vis_blocked,
460       playsink);
461 done:
462   GST_PLAY_SINK_UNLOCK (playsink);
463
464   return;
465 }
466
467 GstElement *
468 gst_play_sink_get_vis_plugin (GstPlaySink * playsink)
469 {
470   GstElement *result = NULL;
471   GstPlayVisChain *chain;
472
473   GST_PLAY_SINK_LOCK (playsink);
474   if ((chain = (GstPlayVisChain *) playsink->vischain)) {
475     /* we have an active chain, get the sink */
476     if (chain->vis)
477       result = gst_object_ref (chain->vis);
478   }
479   /* nothing found, return last configured sink */
480   if (result == NULL && playsink->visualisation)
481     result = gst_object_ref (playsink->visualisation);
482   GST_PLAY_SINK_UNLOCK (playsink);
483
484   return result;
485 }
486
487 void
488 gst_play_sink_set_volume (GstPlaySink * playsink, gdouble volume)
489 {
490   GstPlayAudioChain *chain;
491
492   GST_PLAY_SINK_LOCK (playsink);
493   playsink->volume = volume;
494   chain = (GstPlayAudioChain *) playsink->audiochain;
495   if (chain && chain->volume) {
496     /* if there is a mute element or we are not muted, set the volume */
497     if (chain->mute || !playsink->mute)
498       g_object_set (chain->volume, "volume", volume, NULL);
499   }
500   GST_PLAY_SINK_UNLOCK (playsink);
501 }
502
503 gdouble
504 gst_play_sink_get_volume (GstPlaySink * playsink)
505 {
506   gdouble result;
507   GstPlayAudioChain *chain;
508
509   GST_PLAY_SINK_LOCK (playsink);
510   chain = (GstPlayAudioChain *) playsink->audiochain;
511   result = playsink->volume;
512   if (chain && chain->volume) {
513     if (chain->mute || !playsink->mute) {
514       g_object_get (chain->volume, "volume", &result, NULL);
515       playsink->volume = result;
516     }
517   }
518   GST_PLAY_SINK_UNLOCK (playsink);
519
520   return result;
521 }
522
523 void
524 gst_play_sink_set_mute (GstPlaySink * playsink, gboolean mute)
525 {
526   GstPlayAudioChain *chain;
527
528   GST_PLAY_SINK_LOCK (playsink);
529   playsink->mute = mute;
530   chain = (GstPlayAudioChain *) playsink->audiochain;
531   if (chain) {
532     if (chain->mute) {
533       g_object_set (chain->mute, "mute", mute, NULL);
534     } else if (chain->volume) {
535       if (mute)
536         g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
537       else
538         g_object_set (chain->volume, "volume", (gdouble) playsink->volume,
539             NULL);
540     }
541   }
542   GST_PLAY_SINK_UNLOCK (playsink);
543 }
544
545 gboolean
546 gst_play_sink_get_mute (GstPlaySink * playsink)
547 {
548   gboolean result;
549   GstPlayAudioChain *chain;
550
551   GST_PLAY_SINK_LOCK (playsink);
552   chain = (GstPlayAudioChain *) playsink->audiochain;
553   if (chain && chain->mute) {
554     g_object_get (chain->mute, "mute", &result, NULL);
555     playsink->mute = result;
556   } else {
557     result = playsink->mute;
558   }
559   GST_PLAY_SINK_UNLOCK (playsink);
560
561   return result;
562 }
563
564 static void
565 post_missing_element_message (GstPlaySink * playsink, const gchar * name)
566 {
567   GstMessage *msg;
568
569   msg = gst_missing_element_message_new (GST_ELEMENT_CAST (playsink), name);
570   gst_element_post_message (GST_ELEMENT_CAST (playsink), msg);
571 }
572
573 static gboolean
574 add_chain (GstPlayChain * chain, gboolean add)
575 {
576   if (chain->added == add)
577     return TRUE;
578
579   if (add)
580     gst_bin_add (GST_BIN_CAST (chain->playsink), chain->bin);
581   else
582     gst_bin_remove (GST_BIN_CAST (chain->playsink), chain->bin);
583
584   chain->added = add;
585
586   return TRUE;
587 }
588
589 static gboolean
590 activate_chain (GstPlayChain * chain, gboolean activate)
591 {
592   if (chain->activated == activate)
593     return TRUE;
594
595   if (activate)
596     gst_element_set_state (chain->bin, GST_STATE_PAUSED);
597   else
598     gst_element_set_state (chain->bin, GST_STATE_NULL);
599
600   chain->activated = activate;
601
602   return TRUE;
603 }
604
605 static gint
606 find_property (GstElement * element, const gchar * name)
607 {
608   gint res;
609
610   if (g_object_class_find_property (G_OBJECT_GET_CLASS (element), name)) {
611     res = 0;
612     GST_DEBUG_OBJECT (element, "found %s property", name);
613   } else {
614     GST_DEBUG_OBJECT (element, "did not find %s property", name);
615     res = 1;
616     gst_object_unref (element);
617   }
618   return res;
619 }
620
621 /* find an object in the hierarchy with a property named @name */
622 static GstElement *
623 gst_play_sink_find_property (GstPlaySink * playsink, GstElement * obj,
624     const gchar * name)
625 {
626   GstElement *result = NULL;
627   GstIterator *it;
628
629   if (GST_IS_BIN (obj)) {
630     it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
631     result = gst_iterator_find_custom (it,
632         (GCompareFunc) find_property, (gpointer) name);
633     gst_iterator_free (it);
634   } else {
635     if (g_object_class_find_property (G_OBJECT_GET_CLASS (obj), name)) {
636       result = obj;
637       gst_object_ref (obj);
638     }
639   }
640   return result;
641 }
642
643 /* try to change the state of an element. This function returns the element when
644  * the state change could be performed. When this function returns NULL an error
645  * occured and the element is unreffed. */
646 static GstElement *
647 try_element (GstPlaySink * playsink, GstElement * element)
648 {
649   GstStateChangeReturn ret;
650
651   if (element) {
652     ret = gst_element_set_state (element, GST_STATE_READY);
653     if (ret == GST_STATE_CHANGE_FAILURE) {
654       GST_DEBUG_OBJECT (playsink, "failed state change..");
655       gst_element_set_state (element, GST_STATE_NULL);
656       gst_object_unref (element);
657       element = NULL;
658     }
659   }
660   return element;
661 }
662
663 /* make the element (bin) that contains the elements needed to perform
664  * video display. 
665  *
666  *  +------------------------------------------------------------+
667  *  | vbin                                                       |
668  *  |      +-------+   +----------+   +----------+   +---------+ |
669  *  |      | queue |   |colorspace|   |videoscale|   |videosink| |
670  *  |   +-sink    src-sink       src-sink       src-sink       | |
671  *  |   |  +-------+   +----------+   +----------+   +---------+ |
672  * sink-+                                                        |
673  *  +------------------------------------------------------------+
674  *           
675  */
676 static GstPlayVideoChain *
677 gen_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
678 {
679   GstPlayVideoChain *chain;
680   GstBin *bin;
681   GstPad *pad;
682   GstElement *prev, *elem;
683
684   chain = g_new0 (GstPlayVideoChain, 1);
685   chain->chain.playsink = playsink;
686   chain->chain.raw = raw;
687
688   GST_DEBUG_OBJECT (playsink, "making video chain %p", chain);
689
690   if (playsink->video_sink) {
691     GST_DEBUG_OBJECT (playsink, "trying configured videosink");
692     elem = gst_object_ref (playsink->video_sink);
693     chain->sink = try_element (playsink, elem);
694   }
695   if (chain->sink == NULL) {
696     GST_DEBUG_OBJECT (playsink, "trying autovideosink");
697     elem = gst_element_factory_make ("autovideosink", "videosink");
698     chain->sink = try_element (playsink, elem);
699   }
700   if (chain->sink == NULL) {
701     GST_DEBUG_OBJECT (playsink, "trying xvimagesink");
702     elem = gst_element_factory_make ("xvimagesink", "videosink");
703     chain->sink = try_element (playsink, elem);
704   }
705   if (chain->sink == NULL)
706     goto no_sinks;
707
708   /* if we can disable async behaviour of the sink, we can avoid adding a
709    * queue for the audio chain. We can't use the deep property here because the
710    * sink might change it's internal sink element later. */
711   if (g_object_class_find_property (G_OBJECT_GET_CLASS (chain->sink), "async")) {
712     GST_DEBUG_OBJECT (playsink, "setting async property to %d on video sink",
713         async);
714     g_object_set (chain->sink, "async", async, NULL);
715     chain->async = async;
716   } else {
717     GST_DEBUG_OBJECT (playsink, "no async property on the sink");
718     chain->async = TRUE;
719   }
720
721   /* create a bin to hold objects, as we create them we add them to this bin so
722    * that when something goes wrong we only need to unref the bin */
723   chain->chain.bin = gst_bin_new ("vbin");
724   bin = GST_BIN_CAST (chain->chain.bin);
725   gst_object_ref (bin);
726   gst_object_sink (bin);
727   gst_bin_add (bin, chain->sink);
728
729   /* decouple decoder from sink, this improves playback quite a lot since the
730    * decoder can continue while the sink blocks for synchronisation. We don't
731    * need a lot of buffers as this consumes a lot of memory and we don't want
732    * too little because else we would be context switching too quickly. */
733   chain->queue = gst_element_factory_make ("queue", "vqueue");
734   g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
735       "max-size-bytes", 0, "max-size-time", (gint64) 0, NULL);
736   gst_bin_add (bin, chain->queue);
737   prev = chain->queue;
738
739   if (raw) {
740     GST_DEBUG_OBJECT (playsink, "creating ffmpegcolorspace");
741     chain->conv = gst_element_factory_make ("ffmpegcolorspace", "vconv");
742     if (chain->conv == NULL) {
743       post_missing_element_message (playsink, "ffmpegcolorspace");
744       GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
745           (_("Missing element '%s' - check your GStreamer installation."),
746               "ffmpegcolorspace"), (NULL));
747     } else {
748       gst_bin_add (bin, chain->conv);
749       if (!gst_element_link_pads (prev, "src", chain->conv, "sink"))
750         goto link_failed;
751
752       prev = chain->conv;
753     }
754
755     GST_DEBUG_OBJECT (playsink, "creating videoscale");
756     chain->scale = gst_element_factory_make ("videoscale", "vscale");
757     if (chain->scale == NULL) {
758       post_missing_element_message (playsink, "videoscale");
759       GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
760           (_("Missing element '%s' - check your GStreamer installation."),
761               "videoscale"), ("possibly a liboil version mismatch?"));
762     } else {
763       gst_bin_add (bin, chain->scale);
764       if (!gst_element_link_pads (prev, "src", chain->scale, "sink"))
765         goto link_failed;
766
767       prev = chain->scale;
768     }
769   }
770
771   /* be more careful with the pad from the custom sink element, it might not
772    * be named 'sink' */
773   if (!gst_element_link_pads (prev, "src", chain->sink, NULL))
774     goto link_failed;
775
776   pad = gst_element_get_static_pad (chain->queue, "sink");
777   chain->sinkpad = gst_ghost_pad_new ("sink", pad);
778   gst_object_unref (pad);
779
780   gst_element_add_pad (chain->chain.bin, chain->sinkpad);
781
782   return chain;
783
784   /* ERRORS */
785 no_sinks:
786   {
787     post_missing_element_message (playsink, "autovideosink");
788     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
789         (_("Both autovideosink and xvimagesink elements are missing.")),
790         (NULL));
791     free_chain ((GstPlayChain *) chain);
792     return NULL;
793   }
794 link_failed:
795   {
796     GST_ELEMENT_ERROR (playsink, CORE, PAD,
797         (NULL), ("Failed to configure the video sink."));
798     free_chain ((GstPlayChain *) chain);
799     return NULL;
800   }
801 }
802
803 /* make an element for playback of video with subtitles embedded.
804  *
805  *  +----------------------------------------------+
806  *  | tbin                  +-------------+        |
807  *  |          +-----+      | textoverlay |        |
808  *  |          | csp | +--video_sink      |        |
809  * video_sink-sink  src+ +-text_sink     src--+    |
810  *  |          +-----+   |  +-------------+   +-- src   
811  * text_sink-------------+                         |
812  *  +----------------------------------------------+
813  */
814 static GstPlayTextChain *
815 gen_text_chain (GstPlaySink * playsink)
816 {
817   GstPlayTextChain *chain;
818   GstBin *bin;
819   GstPad *pad;
820
821   chain = g_new0 (GstPlayTextChain, 1);
822   chain->chain.playsink = playsink;
823
824   GST_DEBUG_OBJECT (playsink, "making text chain %p", chain);
825
826   chain->chain.bin = gst_bin_new ("tbin");
827   bin = GST_BIN_CAST (chain->chain.bin);
828   gst_object_ref (bin);
829   gst_object_sink (bin);
830
831   chain->conv = gst_element_factory_make ("ffmpegcolorspace", "tconv");
832   if (chain->conv == NULL)
833     goto no_colorspace;
834   gst_bin_add (bin, chain->conv);
835
836   chain->overlay = gst_element_factory_make ("textoverlay", "overlay");
837   if (chain->overlay == NULL)
838     goto no_overlay;
839   gst_bin_add (bin, chain->overlay);
840
841   /* Set some parameters */
842   g_object_set (G_OBJECT (chain->overlay),
843       "halign", "center", "valign", "bottom", NULL);
844   if (playsink->font_desc) {
845     g_object_set (G_OBJECT (chain->overlay), "font-desc", playsink->font_desc,
846         NULL);
847   }
848   g_object_set (G_OBJECT (chain->overlay), "wait-text", FALSE, NULL);
849
850   /* Link */
851   gst_element_link_pads (chain->conv, "src", chain->overlay, "video_sink");
852
853   /* Add ghost pads on the subtitle bin */
854   pad = gst_element_get_static_pad (chain->overlay, "text_sink");
855   chain->textsinkpad = gst_ghost_pad_new ("text_sink", pad);
856   gst_object_unref (pad);
857   gst_element_add_pad (chain->chain.bin, chain->textsinkpad);
858
859   pad = gst_element_get_static_pad (chain->conv, "sink");
860   chain->videosinkpad = gst_ghost_pad_new ("sink", pad);
861   gst_object_unref (pad);
862   gst_element_add_pad (chain->chain.bin, chain->videosinkpad);
863
864   pad = gst_element_get_static_pad (chain->overlay, "src");
865   chain->srcpad = gst_ghost_pad_new ("src", pad);
866   gst_object_unref (pad);
867   gst_element_add_pad (chain->chain.bin, chain->srcpad);
868
869   return chain;
870
871   /* ERRORS */
872 no_colorspace:
873   {
874     post_missing_element_message (playsink, "ffmpegcolorspace");
875     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
876         (_("Missing element '%s' - check your GStreamer installation."),
877             "ffmpegcolorspace"), (NULL));
878     free_chain ((GstPlayChain *) chain);
879     return NULL;
880   }
881 no_overlay:
882   {
883     post_missing_element_message (playsink, "textoverlay");
884     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
885         (_("Missing element '%s' - check your GStreamer installation."),
886             "textoverlay"), (NULL));
887     free_chain ((GstPlayChain *) chain);
888     return NULL;
889   }
890 }
891
892 /* make the chain that contains the elements needed to perform
893  * audio playback. 
894  *
895  * We add a tee as the first element so that we can link the visualisation chain
896  * to it when requested.
897  *
898  *  +-------------------------------------------------------------+
899  *  | abin                                                        |
900  *  |      +---------+   +----------+   +---------+   +---------+ |
901  *  |      |audioconv|   |audioscale|   | volume  |   |audiosink| |
902  *  |   +-srck      src-sink       src-sink      src-sink       | |
903  *  |   |  +---------+   +----------+   +---------+   +---------+ |
904  * sink-+                                                         |
905  *  +-------------------------------------------------------------+
906  */
907 static GstPlayAudioChain *
908 gen_audio_chain (GstPlaySink * playsink, gboolean raw, gboolean queue)
909 {
910   GstPlayAudioChain *chain;
911   GstBin *bin;
912   gboolean have_volume;
913   GstPad *pad;
914   GstElement *head, *prev, *elem;
915
916   chain = g_new0 (GstPlayAudioChain, 1);
917   chain->chain.playsink = playsink;
918   chain->chain.raw = raw;
919
920   GST_DEBUG_OBJECT (playsink, "making audio chain %p", chain);
921
922   if (playsink->audio_sink) {
923     GST_DEBUG_OBJECT (playsink, "trying configured audiosink");
924     elem = gst_object_ref (playsink->audio_sink);
925     chain->sink = try_element (playsink, elem);
926   }
927   if (chain->sink == NULL) {
928     GST_DEBUG_OBJECT (playsink, "trying autoaudiosink");
929     elem = gst_element_factory_make ("autoaudiosink", "audiosink");
930     chain->sink = try_element (playsink, elem);
931   }
932   if (chain->sink == NULL) {
933     GST_DEBUG_OBJECT (playsink, "trying alsasink");
934     elem = gst_element_factory_make ("alsasink", "audiosink");
935     chain->sink = try_element (playsink, elem);
936   }
937   if (chain->sink == NULL)
938     goto no_sinks;
939
940
941   chain->chain.bin = gst_bin_new ("abin");
942   bin = GST_BIN_CAST (chain->chain.bin);
943   gst_object_ref (bin);
944   gst_object_sink (bin);
945   gst_bin_add (bin, chain->sink);
946
947   if (queue) {
948     /* we have to add a queue when we need to decouple for the video sink in
949      * visualisations */
950     GST_DEBUG_OBJECT (playsink, "adding audio queue");
951     chain->queue = gst_element_factory_make ("queue", "aqueue");
952     gst_bin_add (bin, chain->queue);
953     prev = head = chain->queue;
954   } else {
955     head = chain->sink;
956     prev = NULL;
957   }
958
959   /* check if the sink has the volume property, if it does we don't need to
960    * add a volume element. */
961   if (g_object_class_find_property (G_OBJECT_GET_CLASS (chain->sink), "volume")) {
962     GST_DEBUG_OBJECT (playsink, "the sink has a volume property");
963     have_volume = TRUE;
964     /* use the sink to control the volume */
965     chain->volume = chain->sink;
966     g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
967     /* if the sink also has a mute property we can use this as well. We'll only
968      * use the mute property if there is a volume property. We can simulate the
969      * mute with the volume otherwise. */
970     if (g_object_class_find_property (G_OBJECT_GET_CLASS (chain->sink), "mute")) {
971       GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
972       chain->mute = chain->sink;
973     }
974   } else {
975     /* no volume, we need to add a volume element when we can */
976     GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
977     have_volume = FALSE;
978   }
979
980   if (raw) {
981     GST_DEBUG_OBJECT (playsink, "creating audioconvert");
982     chain->conv = gst_element_factory_make ("audioconvert", "aconv");
983     if (chain->conv == NULL) {
984       post_missing_element_message (playsink, "audioconvert");
985       GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
986           (_("Missing element '%s' - check your GStreamer installation."),
987               "audioconvert"), ("possibly a liboil version mismatch?"));
988     } else {
989       gst_bin_add (bin, chain->conv);
990       if (prev) {
991         if (!gst_element_link_pads (prev, "src", chain->conv, "sink"))
992           goto link_failed;
993       } else {
994         head = chain->conv;
995       }
996       prev = chain->conv;
997     }
998
999     GST_DEBUG_OBJECT (playsink, "creating audioresample");
1000     chain->resample = gst_element_factory_make ("audioresample", "aresample");
1001     if (chain->resample == NULL) {
1002       post_missing_element_message (playsink, "audioresample");
1003       GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1004           (_("Missing element '%s' - check your GStreamer installation."),
1005               "audioresample"), ("possibly a liboil version mismatch?"));
1006     } else {
1007       gst_bin_add (bin, chain->resample);
1008       if (prev) {
1009         if (!gst_element_link_pads (prev, "src", chain->resample, "sink"))
1010           goto link_failed;
1011       } else {
1012         head = chain->resample;
1013       }
1014       prev = chain->resample;
1015     }
1016
1017     if (!have_volume && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME) {
1018       GST_DEBUG_OBJECT (playsink, "creating volume");
1019       chain->volume = gst_element_factory_make ("volume", "volume");
1020       if (chain->volume == NULL) {
1021         post_missing_element_message (playsink, "volume");
1022         GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1023             (_("Missing element '%s' - check your GStreamer installation."),
1024                 "volume"), ("possibly a liboil version mismatch?"));
1025       } else {
1026         have_volume = TRUE;
1027
1028         /* volume also has the mute property */
1029         chain->mute = chain->volume;
1030
1031         /* configure with the latest volume and mute */
1032         g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume,
1033             NULL);
1034         g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
1035         gst_bin_add (bin, chain->volume);
1036
1037         if (prev) {
1038           if (!gst_element_link_pads (prev, "src", chain->volume, "sink"))
1039             goto link_failed;
1040         } else {
1041           head = chain->volume;
1042         }
1043         prev = chain->volume;
1044       }
1045     }
1046   }
1047
1048   if (prev) {
1049     /* we only have to link to the previous element if we have something in
1050      * front of the sink */
1051     GST_DEBUG_OBJECT (playsink, "linking to sink");
1052     if (!gst_element_link_pads (prev, "src", chain->sink, NULL))
1053       goto link_failed;
1054   }
1055
1056   /* post a warning if we have no way to configure the volume */
1057   if (!have_volume) {
1058     GST_ELEMENT_WARNING (playsink, STREAM, NOT_IMPLEMENTED,
1059         (_("No volume control found")), ("No volume control found"));
1060   }
1061
1062   /* and ghost the sinkpad of the headmost element */
1063   GST_DEBUG_OBJECT (playsink, "ghosting sink pad");
1064   pad = gst_element_get_static_pad (head, "sink");
1065   chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1066   gst_object_unref (pad);
1067   gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1068
1069   return chain;
1070
1071   /* ERRORS */
1072 no_sinks:
1073   {
1074     post_missing_element_message (playsink, "autoaudiosink");
1075     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1076         (_("Both autoaudiosink and alsasink elements are missing.")), (NULL));
1077     free_chain ((GstPlayChain *) chain);
1078     return NULL;
1079   }
1080 link_failed:
1081   {
1082     GST_ELEMENT_ERROR (playsink, CORE, PAD,
1083         (NULL), ("Failed to configure the audio sink."));
1084     free_chain ((GstPlayChain *) chain);
1085     return NULL;
1086   }
1087 }
1088
1089 /*
1090  *  +-------------------------------------------------------------------+
1091  *  | visbin                                                            |
1092  *  |      +----------+   +------------+   +----------+   +-------+     |
1093  *  |      | visqueue |   | audioconv  |   | audiores |   |  vis  |     |
1094  *  |   +-sink       src-sink + samp  src-sink       src-sink    src-+  |
1095  *  |   |  +----------+   +------------+   +----------+   +-------+  |  |
1096  * sink-+                                                            +-src
1097  *  +-------------------------------------------------------------------+
1098  *           
1099  */
1100 static GstPlayVisChain *
1101 gen_vis_chain (GstPlaySink * playsink)
1102 {
1103   GstPlayVisChain *chain;
1104   GstBin *bin;
1105   gboolean res;
1106   GstPad *pad;
1107
1108   chain = g_new0 (GstPlayVisChain, 1);
1109   chain->chain.playsink = playsink;
1110
1111   GST_DEBUG_OBJECT (playsink, "making vis chain %p", chain);
1112
1113   chain->chain.bin = gst_bin_new ("visbin");
1114   bin = GST_BIN_CAST (chain->chain.bin);
1115   gst_object_ref (bin);
1116   gst_object_sink (bin);
1117
1118   /* we're queuing raw audio here, we can remove this queue when we can disable
1119    * async behaviour in the video sink. */
1120   chain->queue = gst_element_factory_make ("queue", "visqueue");
1121   gst_bin_add (bin, chain->queue);
1122
1123   chain->conv = gst_element_factory_make ("audioconvert", "aconv");
1124   if (chain->conv == NULL)
1125     goto no_audioconvert;
1126   gst_bin_add (bin, chain->conv);
1127
1128   chain->resample = gst_element_factory_make ("audioresample", "aresample");
1129   if (chain->resample == NULL)
1130     goto no_audioresample;
1131   gst_bin_add (bin, chain->resample);
1132
1133   /* this pad will be used for blocking the dataflow and switching the vis
1134    * plugin */
1135   chain->blockpad = gst_element_get_static_pad (chain->resample, "src");
1136
1137   if (playsink->visualisation) {
1138     chain->vis = gst_object_ref (playsink->visualisation);
1139   } else {
1140     chain->vis = gst_element_factory_make ("goom", "vis");
1141     if (!chain->vis)
1142       goto no_goom;
1143   }
1144   gst_bin_add (bin, chain->vis);
1145
1146   res = gst_element_link_pads (chain->queue, "src", chain->conv, "sink");
1147   res &= gst_element_link_pads (chain->conv, "src", chain->resample, "sink");
1148   res &= gst_element_link_pads (chain->resample, "src", chain->vis, "sink");
1149   if (!res)
1150     goto link_failed;
1151
1152   chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
1153   chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
1154
1155   pad = gst_element_get_static_pad (chain->queue, "sink");
1156   chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1157   gst_object_unref (pad);
1158   gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1159
1160   chain->srcpad = gst_ghost_pad_new ("src", chain->vissrcpad);
1161   gst_element_add_pad (chain->chain.bin, chain->srcpad);
1162
1163   return chain;
1164
1165   /* ERRORS */
1166 no_audioconvert:
1167   {
1168     post_missing_element_message (playsink, "audioconvert");
1169     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1170         (_("Missing element '%s' - check your GStreamer installation."),
1171             "audioconvert"), ("possibly a liboil version mismatch?"));
1172     free_chain ((GstPlayChain *) chain);
1173     return NULL;
1174   }
1175 no_audioresample:
1176   {
1177     post_missing_element_message (playsink, "audioresample");
1178     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1179         (_("Missing element '%s' - check your GStreamer installation."),
1180             "audioresample"), (NULL));
1181     free_chain ((GstPlayChain *) chain);
1182     return NULL;
1183   }
1184 no_goom:
1185   {
1186     post_missing_element_message (playsink, "goom");
1187     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1188         (_("Missing element '%s' - check your GStreamer installation."),
1189             "goom"), (NULL));
1190     free_chain ((GstPlayChain *) chain);
1191     return NULL;
1192   }
1193 link_failed:
1194   {
1195     GST_ELEMENT_ERROR (playsink, CORE, PAD,
1196         (NULL), ("Failed to configure the visualisation element."));
1197     free_chain ((GstPlayChain *) chain);
1198     return NULL;
1199   }
1200 }
1201
1202 /* this function is called when all the request pads are requested and when we
1203  * have to construct the final pipeline. Based on the flags we construct the
1204  * final output pipelines.
1205  */
1206 gboolean
1207 gst_play_sink_reconfigure (GstPlaySink * playsink)
1208 {
1209   GstPlayFlags flags;
1210   gboolean need_audio, need_video, need_vis, need_text;
1211
1212   GST_DEBUG_OBJECT (playsink, "reconfiguring");
1213
1214   /* assume we need nothing */
1215   need_audio = need_video = need_vis = need_text = FALSE;
1216
1217   GST_PLAY_SINK_LOCK (playsink);
1218   GST_OBJECT_LOCK (playsink);
1219   /* get flags, there are protected with the object lock */
1220   flags = playsink->flags;
1221   GST_OBJECT_UNLOCK (playsink);
1222
1223   /* figure out which components we need */
1224   if (flags & GST_PLAY_FLAG_TEXT && playsink->text_pad) {
1225     /* we have a text_pad and we need text rendering, in this case we need a
1226      * video_pad to combine the video with the text */
1227     if (!playsink->video_pad)
1228       goto subs_but_no_video;
1229
1230     /* we have subtitles and we are requested to show it, we also need to show
1231      * video in this case. */
1232     need_video = TRUE;
1233     need_text = TRUE;
1234   } else if (flags & GST_PLAY_FLAG_VIDEO && playsink->video_pad) {
1235     /* we have video and we are requested to show it */
1236     need_video = TRUE;
1237   }
1238   if (playsink->audio_pad) {
1239     if (flags & GST_PLAY_FLAG_AUDIO) {
1240       need_audio = TRUE;
1241     }
1242     if (playsink->audio_pad_raw) {
1243       /* only can do vis with raw uncompressed audio */
1244       if (flags & GST_PLAY_FLAG_VIS && !need_video) {
1245         /* also add video when we add visualisation */
1246         need_video = TRUE;
1247         need_vis = TRUE;
1248       }
1249     }
1250   }
1251
1252   /* set up video pipeline */
1253   if (need_video) {
1254     GST_DEBUG_OBJECT (playsink, "adding video, raw %d",
1255         playsink->video_pad_raw);
1256     if (!playsink->videochain) {
1257       gboolean raw, async;
1258
1259       /* we need a raw sink when we do vis or when we have a raw pad */
1260       raw = need_vis ? TRUE : playsink->video_pad_raw;
1261       /* we try to set the sink async=FALSE when we need vis, this way we can
1262        * avoid a queue in the audio chain. */
1263       async = !need_vis;
1264
1265       playsink->videochain = gen_video_chain (playsink, raw, async);
1266     }
1267     if (playsink->videochain) {
1268       GST_DEBUG_OBJECT (playsink, "adding video chain");
1269       add_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
1270       activate_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
1271       /* if we are not part of vis or subtitles, set the ghostpad target */
1272       if (!need_vis && !need_text) {
1273         GST_DEBUG_OBJECT (playsink, "ghosting video sinkpad");
1274         gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad),
1275             playsink->videochain->sinkpad);
1276       }
1277     }
1278   } else {
1279     GST_DEBUG_OBJECT (playsink, "no video needed");
1280     if (playsink->videochain) {
1281       GST_DEBUG_OBJECT (playsink, "removing video chain");
1282       add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
1283       activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
1284     }
1285     if (playsink->video_pad)
1286       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
1287   }
1288
1289   if (need_text) {
1290     GST_DEBUG_OBJECT (playsink, "adding text");
1291     if (!playsink->textchain) {
1292       GST_DEBUG_OBJECT (playsink, "creating text chain");
1293       playsink->textchain = gen_text_chain (playsink);
1294     }
1295     if (playsink->textchain) {
1296       GST_DEBUG_OBJECT (playsink, "adding text chain");
1297       add_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
1298       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad),
1299           playsink->textchain->textsinkpad);
1300       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad),
1301           playsink->textchain->videosinkpad);
1302       gst_pad_link (playsink->textchain->srcpad, playsink->videochain->sinkpad);
1303       activate_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
1304     }
1305   } else {
1306     GST_DEBUG_OBJECT (playsink, "no text needed");
1307     /* we have no subtitles/text or we are requested to not show them */
1308     if (playsink->textchain) {
1309       GST_DEBUG_OBJECT (playsink, "removing text chain");
1310       add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
1311       activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
1312     }
1313     if (!need_video && playsink->video_pad)
1314       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
1315     if (playsink->text_pad)
1316       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad), NULL);
1317   }
1318
1319   if (need_audio) {
1320     gboolean create_chain = FALSE;
1321     gboolean raw, queue;
1322
1323     GST_DEBUG_OBJECT (playsink, "adding audio");
1324
1325     /* get a raw sink if we are asked for a raw pad */
1326     raw = playsink->audio_pad_raw;
1327     if (need_vis && playsink->videochain) {
1328       /* If we are dealing with visualisations, we need to add a queue to
1329        * decouple the audio from the video part. We only have to do this when
1330        * the video part is async=true */
1331       queue = ((GstPlayVideoChain *) playsink->videochain)->async;
1332       GST_DEBUG_OBJECT (playsink, "need audio queue for vis: %d", queue);
1333     } else {
1334       /* no vis, we can avoid a queue */
1335       GST_DEBUG_OBJECT (playsink, "don't need audio queue");
1336       queue = FALSE;
1337     }
1338
1339     if (!playsink->audiochain) {
1340       /* create chain if we don't already have one */
1341       create_chain = TRUE;
1342     } else {
1343       /* we have a chain, check if it's also raw */
1344       if (playsink->audiochain->chain.raw != raw) {
1345         GST_DEBUG_OBJECT (playsink, "removing current audio chain");
1346         gst_pad_unlink (playsink->audio_tee_asrc,
1347             playsink->audiochain->sinkpad);
1348         add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
1349         activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
1350         create_chain = TRUE;
1351       }
1352     }
1353
1354     if (create_chain) {
1355       GST_DEBUG_OBJECT (playsink, "creating new audio chain");
1356       playsink->audiochain = gen_audio_chain (playsink, raw, queue);
1357     }
1358
1359     if (playsink->audiochain) {
1360       GST_DEBUG_OBJECT (playsink, "adding audio chain");
1361       add_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
1362       gst_pad_link (playsink->audio_tee_asrc, playsink->audiochain->sinkpad);
1363       activate_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
1364     }
1365   } else {
1366     GST_DEBUG_OBJECT (playsink, "no audio needed");
1367     /* we have no audio or we are requested to not play audio */
1368     if (playsink->audiochain) {
1369       GST_DEBUG_OBJECT (playsink, "removing audio chain");
1370       gst_pad_unlink (playsink->audio_tee_asrc, playsink->audiochain->sinkpad);
1371       add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
1372       activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
1373     }
1374   }
1375
1376   if (need_vis) {
1377     GstPad *srcpad;
1378
1379     if (!playsink->vischain)
1380       playsink->vischain = gen_vis_chain (playsink);
1381
1382     GST_DEBUG_OBJECT (playsink, "adding visualisation");
1383
1384     if (playsink->vischain) {
1385       GST_DEBUG_OBJECT (playsink, "setting up vis chain");
1386       srcpad =
1387           gst_element_get_static_pad (GST_ELEMENT_CAST (playsink->vischain->
1388               chain.bin), "src");
1389       add_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
1390       gst_pad_link (playsink->audio_tee_vissrc, playsink->vischain->sinkpad);
1391       gst_pad_link (srcpad, playsink->videochain->sinkpad);
1392       gst_object_unref (srcpad);
1393       activate_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
1394     }
1395   } else {
1396     GST_DEBUG_OBJECT (playsink, "no vis needed");
1397     if (playsink->vischain) {
1398       GST_DEBUG_OBJECT (playsink, "removing vis chain");
1399       add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
1400       activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
1401     }
1402   }
1403   GST_PLAY_SINK_UNLOCK (playsink);
1404
1405   return TRUE;
1406
1407   /* ERRORS */
1408 subs_but_no_video:
1409   {
1410     GST_ELEMENT_ERROR (playsink, STREAM, FORMAT,
1411         (_("Can't play a text file without video.")),
1412         ("Have text pad but no video pad"));
1413     GST_PLAY_SINK_UNLOCK (playsink);
1414     return FALSE;
1415   }
1416 }
1417
1418 /**
1419  * gst_play_sink_set_flags:
1420  * @playsink: a #GstPlaySink
1421  * @flags: #GstPlayFlags
1422  *
1423  * Configure @flags on @playsink. The flags control the behaviour of @playsink
1424  * when constructing the sink pipelins.
1425  *
1426  * Returns: TRUE if the flags could be configured.
1427  */
1428 gboolean
1429 gst_play_sink_set_flags (GstPlaySink * playsink, GstPlayFlags flags)
1430 {
1431   g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), FALSE);
1432
1433   GST_OBJECT_LOCK (playsink);
1434   playsink->flags = flags;
1435   GST_OBJECT_UNLOCK (playsink);
1436
1437   return TRUE;
1438 }
1439
1440 /**
1441  * gst_play_sink_get_flags:
1442  * @playsink: a #GstPlaySink
1443  *
1444  * Get the flags of @playsink. That flags control the behaviour of the sink when
1445  * it constructs the sink pipelines.
1446  *
1447  * Returns: the currently configured #GstPlayFlags.
1448  */
1449 GstPlayFlags
1450 gst_play_sink_get_flags (GstPlaySink * playsink)
1451 {
1452   GstPlayFlags res;
1453
1454   g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), 0);
1455
1456   GST_OBJECT_LOCK (playsink);
1457   res = playsink->flags;
1458   GST_OBJECT_UNLOCK (playsink);
1459
1460   return res;
1461 }
1462
1463 void
1464 gst_play_sink_set_font_desc (GstPlaySink * playsink, const gchar * desc)
1465 {
1466   GstPlayTextChain *chain;
1467
1468   GST_PLAY_SINK_LOCK (playsink);
1469   chain = (GstPlayTextChain *) playsink->textchain;
1470   g_free (playsink->font_desc);
1471   playsink->font_desc = g_strdup (desc);
1472   if (chain && chain->overlay) {
1473     g_object_set (chain->overlay, "font-desc", desc, NULL);
1474   }
1475   GST_PLAY_SINK_UNLOCK (playsink);
1476 }
1477
1478 gchar *
1479 gst_play_sink_get_font_desc (GstPlaySink * playsink)
1480 {
1481   gchar *result = NULL;
1482   GstPlayTextChain *chain;
1483
1484   GST_PLAY_SINK_LOCK (playsink);
1485   chain = (GstPlayTextChain *) playsink->textchain;
1486   if (chain && chain->overlay) {
1487     g_object_get (chain->overlay, "font-desc", &result, NULL);
1488     playsink->font_desc = g_strdup (result);
1489   } else {
1490     result = g_strdup (playsink->font_desc);
1491   }
1492   GST_PLAY_SINK_UNLOCK (playsink);
1493
1494   return result;
1495 }
1496
1497 /**
1498  * gst_play_sink_get_last_frame:
1499  * @playsink: a #GstPlaySink
1500  *
1501  * Get the last displayed frame from @playsink. This frame is in the native
1502  * format of the sink element, the caps on the result buffer contain the format
1503  * of the frame data.
1504  *
1505  * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
1506  * available.
1507  */
1508 GstBuffer *
1509 gst_play_sink_get_last_frame (GstPlaySink * playsink)
1510 {
1511   GstBuffer *result = NULL;
1512   GstPlayVideoChain *chain;
1513
1514   GST_PLAY_SINK_LOCK (playsink);
1515   GST_DEBUG_OBJECT (playsink, "taking last frame");
1516   /* get the video chain if we can */
1517   if ((chain = (GstPlayVideoChain *) playsink->videochain)) {
1518     GST_DEBUG_OBJECT (playsink, "found video chain");
1519     /* see if the chain is active */
1520     if (chain->chain.activated && chain->sink) {
1521       GstElement *elem;
1522
1523       GST_DEBUG_OBJECT (playsink, "video chain active and has a sink");
1524
1525       /* find and get the last-buffer property now */
1526       if ((elem =
1527               gst_play_sink_find_property (playsink, chain->sink,
1528                   "last-buffer"))) {
1529         GST_DEBUG_OBJECT (playsink, "getting last-buffer property");
1530         g_object_get (elem, "last-buffer", &result, NULL);
1531         gst_object_unref (elem);
1532       }
1533     }
1534   }
1535   GST_PLAY_SINK_UNLOCK (playsink);
1536
1537   return result;
1538 }
1539
1540 /**
1541  * gst_play_sink_request_pad
1542  * @playsink: a #GstPlaySink
1543  * @type: a #GstPlaySinkType
1544  *
1545  * Create or return a pad of @type.
1546  *
1547  * Returns: a #GstPad of @type or %NULL when the pad could not be created.
1548  */
1549 GstPad *
1550 gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type)
1551 {
1552   GstPad *res = NULL;
1553   gboolean created = FALSE;
1554   gboolean raw = FALSE;
1555
1556   GST_DEBUG_OBJECT (playsink, "request pad type %d", type);
1557
1558   GST_PLAY_SINK_LOCK (playsink);
1559   switch (type) {
1560     case GST_PLAY_SINK_TYPE_AUDIO_RAW:
1561       raw = TRUE;
1562     case GST_PLAY_SINK_TYPE_AUDIO:
1563       if (!playsink->audio_tee) {
1564         GST_LOG_OBJECT (playsink, "creating tee");
1565         /* create tee when needed. This element will feed the audio sink chain
1566          * and the vis chain. */
1567         playsink->audio_tee = gst_element_factory_make ("tee", "audiotee");
1568         playsink->audio_tee_sink =
1569             gst_element_get_static_pad (playsink->audio_tee, "sink");
1570         /* get two request pads */
1571         playsink->audio_tee_vissrc =
1572             gst_element_get_request_pad (playsink->audio_tee, "src%d");
1573         playsink->audio_tee_asrc =
1574             gst_element_get_request_pad (playsink->audio_tee, "src%d");
1575         gst_bin_add (GST_BIN_CAST (playsink), playsink->audio_tee);
1576         gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
1577       }
1578       if (!playsink->audio_pad) {
1579         GST_LOG_OBJECT (playsink, "ghosting tee sinkpad");
1580         playsink->audio_pad =
1581             gst_ghost_pad_new ("audio_sink", playsink->audio_tee_sink);
1582         created = TRUE;
1583       }
1584       playsink->audio_pad_raw = raw;
1585       res = playsink->audio_pad;
1586       break;
1587     case GST_PLAY_SINK_TYPE_VIDEO_RAW:
1588       raw = TRUE;
1589     case GST_PLAY_SINK_TYPE_VIDEO:
1590       if (!playsink->video_pad) {
1591         GST_LOG_OBJECT (playsink, "ghosting videosink");
1592         playsink->video_pad =
1593             gst_ghost_pad_new_no_target ("video_sink", GST_PAD_SINK);
1594         created = TRUE;
1595       }
1596       playsink->video_pad_raw = raw;
1597       res = playsink->video_pad;
1598       break;
1599     case GST_PLAY_SINK_TYPE_TEXT:
1600       GST_LOG_OBJECT (playsink, "ghosting text");
1601       if (!playsink->text_pad) {
1602         playsink->text_pad =
1603             gst_ghost_pad_new_no_target ("text_sink", GST_PAD_SINK);
1604         created = TRUE;
1605       }
1606       res = playsink->text_pad;
1607       break;
1608     default:
1609       res = NULL;
1610       break;
1611   }
1612   GST_PLAY_SINK_UNLOCK (playsink);
1613
1614   if (created && res) {
1615     gst_pad_set_active (res, TRUE);
1616     gst_element_add_pad (GST_ELEMENT_CAST (playsink), res);
1617   }
1618
1619   return res;
1620 }
1621
1622 void
1623 gst_play_sink_release_pad (GstPlaySink * playsink, GstPad * pad)
1624 {
1625   GstPad **res = NULL;
1626
1627   GST_DEBUG_OBJECT (playsink, "release pad %" GST_PTR_FORMAT, pad);
1628
1629   GST_PLAY_SINK_LOCK (playsink);
1630   if (pad == playsink->video_pad) {
1631     res = &playsink->video_pad;
1632   } else if (pad == playsink->audio_pad) {
1633     res = &playsink->audio_pad;
1634   } else if (pad == playsink->text_pad) {
1635     res = &playsink->text_pad;
1636   }
1637   GST_PLAY_SINK_UNLOCK (playsink);
1638
1639   if (*res) {
1640     GST_DEBUG_OBJECT (playsink, "deactivate pad %" GST_PTR_FORMAT, *res);
1641     gst_pad_set_active (*res, FALSE);
1642     GST_DEBUG_OBJECT (playsink, "remove pad %" GST_PTR_FORMAT, *res);
1643     gst_element_remove_pad (GST_ELEMENT_CAST (playsink), *res);
1644     *res = NULL;
1645   }
1646 }
1647
1648 /* Send an event to our sinks until one of them works; don't then send to the
1649  * remaining sinks (unlike GstBin)
1650  */
1651 static gboolean
1652 gst_play_sink_send_event_to_sink (GstPlaySink * playsink, GstEvent * event)
1653 {
1654   gboolean res = TRUE;
1655
1656   if (playsink->videochain) {
1657     gst_event_ref (event);
1658     if ((res = gst_element_send_event (playsink->videochain->chain.bin, event))) {
1659       GST_DEBUG_OBJECT (playsink, "Sent event succesfully to video sink");
1660       goto done;
1661     }
1662     GST_DEBUG_OBJECT (playsink, "Event failed when sent to video sink");
1663   }
1664   if (playsink->audiochain) {
1665     gst_event_ref (event);
1666     if ((res = gst_element_send_event (playsink->audiochain->chain.bin, event))) {
1667       GST_DEBUG_OBJECT (playsink, "Sent event succesfully to audio sink");
1668       goto done;
1669     }
1670     GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
1671   }
1672 done:
1673   gst_event_unref (event);
1674   return res;
1675 }
1676
1677 /* We only want to send the event to a single sink (overriding GstBin's
1678  * behaviour), but we want to keep GstPipeline's behaviour - wrapping seek
1679  * events appropriately. So, this is a messy duplication of code. */
1680 static gboolean
1681 gst_play_sink_send_event (GstElement * element, GstEvent * event)
1682 {
1683   gboolean res = FALSE;
1684   GstEventType event_type = GST_EVENT_TYPE (event);
1685
1686   switch (event_type) {
1687     case GST_EVENT_SEEK:
1688       GST_DEBUG_OBJECT (element, "Sending seek event to a sink");
1689       res = gst_play_sink_send_event_to_sink (GST_PLAY_SINK (element), event);
1690       break;
1691     default:
1692       res =
1693           GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
1694           event);
1695       break;
1696   }
1697   return res;
1698 }
1699
1700 static GstStateChangeReturn
1701 gst_play_sink_change_state (GstElement * element, GstStateChange transition)
1702 {
1703   GstStateChangeReturn ret;
1704   GstPlaySink *playsink;
1705
1706   playsink = GST_PLAY_SINK (element);
1707
1708   switch (transition) {
1709     case GST_STATE_CHANGE_READY_TO_PAUSED:
1710       break;
1711     default:
1712       break;
1713   }
1714
1715   ret =
1716       GST_ELEMENT_CLASS (gst_play_sink_parent_class)->change_state (element,
1717       transition);
1718   if (ret == GST_STATE_CHANGE_FAILURE)
1719     return ret;
1720
1721   switch (transition) {
1722     case GST_STATE_CHANGE_READY_TO_PAUSED:
1723       break;
1724     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1725       /* FIXME Release audio device when we implement that */
1726       break;
1727     case GST_STATE_CHANGE_PAUSED_TO_READY:
1728       /* remove sinks we added */
1729       if (playsink->videochain) {
1730         activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
1731         add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
1732       }
1733       if (playsink->audiochain) {
1734         activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
1735         add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
1736       }
1737       break;
1738     default:
1739       break;
1740   }
1741
1742   return ret;
1743 }