gst: Remove dead assignments and resulting unused variables
[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   gboolean sink_volume;         /* if the volume was provided by the sink */
59   GstElement *mute;             /* element with the mute property */
60   GstElement *sink;
61 } GstPlayAudioChain;
62
63 typedef struct
64 {
65   GstPlayChain chain;
66   GstPad *sinkpad;
67   GstElement *queue;
68   GstElement *conv;
69   GstElement *scale;
70   GstElement *sink;
71   gboolean async;
72 } GstPlayVideoChain;
73
74 typedef struct
75 {
76   GstPlayChain chain;
77   GstPad *sinkpad;
78   GstElement *queue;
79   GstElement *conv;
80   GstElement *resample;
81   GstPad *blockpad;             /* srcpad of resample, used for switching the vis */
82   GstPad *vissinkpad;           /* visualisation sinkpad, */
83   GstElement *vis;
84   GstPad *vissrcpad;            /* visualisation srcpad, */
85   GstPad *srcpad;               /* outgoing srcpad, used to connect to the next
86                                  * chain */
87 } GstPlayVisChain;
88
89 typedef struct
90 {
91   GstPlayChain chain;
92   GstPad *sinkpad;
93   GstElement *conv;
94   GstElement *overlay;
95   GstPad *videosinkpad;
96   GstPad *textsinkpad;
97   GstPad *srcpad;               /* outgoing srcpad, used to connect to the next
98                                  * chain */
99   GstElement *sink;             /* custom sink to receive subtitle buffers */
100 } GstPlayTextChain;
101
102 typedef struct
103 {
104   GstPlayChain chain;
105   GstPad *sinkpad;
106   GstElement *queue;
107   GstElement *conv;
108   GstElement *overlay;
109   GstPad *videosinkpad;
110   GstPad *subpsinkpad;
111   GstPad *srcpad;               /* outgoing srcpad, used to connect to the next
112                                  * chain */
113   GstElement *sink;             /* custom sink to receive subpicture buffers */
114 } GstPlaySubpChain;
115
116 #define GST_PLAY_SINK_GET_LOCK(playsink) (((GstPlaySink *)playsink)->lock)
117 #define GST_PLAY_SINK_LOCK(playsink)     g_mutex_lock (GST_PLAY_SINK_GET_LOCK (playsink))
118 #define GST_PLAY_SINK_UNLOCK(playsink)   g_mutex_unlock (GST_PLAY_SINK_GET_LOCK (playsink))
119
120 struct _GstPlaySink
121 {
122   GstBin bin;
123
124   GMutex *lock;
125
126   gboolean async_pending;
127   gboolean need_async_start;
128
129   GstPlayFlags flags;
130
131   /* chains */
132   GstPlayAudioChain *audiochain;
133   GstPlayVideoChain *videochain;
134   GstPlayVisChain *vischain;
135   GstPlayTextChain *textchain;
136   GstPlaySubpChain *subpchain;
137
138   /* audio */
139   GstPad *audio_pad;
140   gboolean audio_pad_raw;
141   /* audio tee */
142   GstElement *audio_tee;
143   GstPad *audio_tee_sink;
144   GstPad *audio_tee_asrc;
145   GstPad *audio_tee_vissrc;
146   /* video */
147   GstPad *video_pad;
148   gboolean video_pad_raw;
149   /* text */
150   GstPad *text_pad;
151   /* subpictures */
152   GstPad *subp_pad;
153
154   /* properties */
155   GstElement *audio_sink;
156   GstElement *video_sink;
157   GstElement *visualisation;
158   GstElement *text_sink;
159   GstElement *subp_sink;
160   gfloat volume;
161   gboolean mute;
162   gchar *font_desc;             /* font description */
163   guint connection_speed;       /* connection speed in bits/sec (0 = unknown) */
164   gint count;
165   gboolean volume_changed;      /* volume/mute changed while no audiochain */
166   gboolean mute_changed;        /* ... has been reated yet */
167 };
168
169 struct _GstPlaySinkClass
170 {
171   GstBinClass parent_class;
172 };
173
174
175 /* props */
176 enum
177 {
178   PROP_0,
179   PROP_LAST
180 };
181
182 /* signals */
183 enum
184 {
185   LAST_SIGNAL
186 };
187
188 static void gst_play_sink_class_init (GstPlaySinkClass * klass);
189 static void gst_play_sink_init (GstPlaySink * playsink);
190 static void gst_play_sink_dispose (GObject * object);
191 static void gst_play_sink_finalize (GObject * object);
192
193 static gboolean gst_play_sink_send_event (GstElement * element,
194     GstEvent * event);
195 static GstStateChangeReturn gst_play_sink_change_state (GstElement * element,
196     GstStateChange transition);
197
198 static void gst_play_sink_handle_message (GstBin * bin, GstMessage * message);
199
200 /* static guint gst_play_sink_signals[LAST_SIGNAL] = { 0 }; */
201
202 static const GstElementDetails gst_play_sink_details =
203 GST_ELEMENT_DETAILS ("Player Sink",
204     "Generic/Bin/Player",
205     "Autoplug and play media from an uri",
206     "Wim Taymans <wim.taymans@gmail.com>");
207
208 G_DEFINE_TYPE (GstPlaySink, gst_play_sink, GST_TYPE_BIN);
209
210 static void
211 gst_play_sink_class_init (GstPlaySinkClass * klass)
212 {
213   GObjectClass *gobject_klass;
214   GstElementClass *gstelement_klass;
215   GstBinClass *gstbin_klass;
216
217   gobject_klass = (GObjectClass *) klass;
218   gstelement_klass = (GstElementClass *) klass;
219   gstbin_klass = (GstBinClass *) klass;
220
221   gobject_klass->dispose = GST_DEBUG_FUNCPTR (gst_play_sink_dispose);
222   gobject_klass->finalize = GST_DEBUG_FUNCPTR (gst_play_sink_finalize);
223
224   gst_element_class_set_details (gstelement_klass, &gst_play_sink_details);
225
226   gstelement_klass->change_state =
227       GST_DEBUG_FUNCPTR (gst_play_sink_change_state);
228   gstelement_klass->send_event = GST_DEBUG_FUNCPTR (gst_play_sink_send_event);
229
230   gstbin_klass->handle_message =
231       GST_DEBUG_FUNCPTR (gst_play_sink_handle_message);
232
233   GST_DEBUG_CATEGORY_INIT (gst_play_sink_debug, "playsink", 0, "play bin");
234 }
235
236 static void
237 gst_play_sink_init (GstPlaySink * playsink)
238 {
239   /* init groups */
240   playsink->video_sink = NULL;
241   playsink->audio_sink = NULL;
242   playsink->visualisation = NULL;
243   playsink->text_sink = NULL;
244   playsink->volume = 1.0;
245   playsink->font_desc = NULL;
246   playsink->flags = GST_PLAY_FLAG_SOFT_VOLUME;
247
248   playsink->lock = g_mutex_new ();
249   playsink->need_async_start = TRUE;
250   GST_OBJECT_FLAG_SET (playsink, GST_ELEMENT_IS_SINK);
251 }
252
253 static void
254 free_chain (GstPlayChain * chain)
255 {
256   if (chain) {
257     if (chain->bin)
258       gst_object_unref (chain->bin);
259     g_free (chain);
260   }
261 }
262
263 static void
264 gst_play_sink_dispose (GObject * object)
265 {
266   GstPlaySink *playsink;
267
268   playsink = GST_PLAY_SINK (object);
269
270   if (playsink->audio_sink != NULL) {
271     gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
272     gst_object_unref (playsink->audio_sink);
273     playsink->audio_sink = NULL;
274   }
275   if (playsink->video_sink != NULL) {
276     gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
277     gst_object_unref (playsink->video_sink);
278     playsink->video_sink = NULL;
279   }
280   if (playsink->visualisation != NULL) {
281     gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
282     gst_object_unref (playsink->visualisation);
283     playsink->visualisation = NULL;
284   }
285   if (playsink->text_sink != NULL) {
286     gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
287     gst_object_unref (playsink->text_sink);
288     playsink->text_sink = NULL;
289   }
290
291   free_chain ((GstPlayChain *) playsink->videochain);
292   playsink->videochain = NULL;
293   free_chain ((GstPlayChain *) playsink->audiochain);
294   playsink->audiochain = NULL;
295   free_chain ((GstPlayChain *) playsink->vischain);
296   playsink->vischain = NULL;
297   free_chain ((GstPlayChain *) playsink->textchain);
298   playsink->textchain = NULL;
299
300   if (playsink->audio_tee_sink) {
301     gst_object_unref (playsink->audio_tee_sink);
302     playsink->audio_tee_sink = NULL;
303   }
304
305   if (playsink->audio_tee_vissrc) {
306     gst_element_release_request_pad (playsink->audio_tee,
307         playsink->audio_tee_vissrc);
308     gst_object_unref (playsink->audio_tee_vissrc);
309     playsink->audio_tee_vissrc = NULL;
310   }
311
312   if (playsink->audio_tee_asrc) {
313     gst_element_release_request_pad (playsink->audio_tee,
314         playsink->audio_tee_asrc);
315     gst_object_unref (playsink->audio_tee_asrc);
316     playsink->audio_tee_asrc = NULL;
317   }
318
319   g_free (playsink->font_desc);
320   playsink->font_desc = NULL;
321
322   G_OBJECT_CLASS (gst_play_sink_parent_class)->dispose (object);
323 }
324
325 static void
326 gst_play_sink_finalize (GObject * object)
327 {
328   GstPlaySink *playsink;
329
330   playsink = GST_PLAY_SINK (object);
331
332   g_mutex_free (playsink->lock);
333
334   G_OBJECT_CLASS (gst_play_sink_parent_class)->finalize (object);
335 }
336
337 void
338 gst_play_sink_set_sink (GstPlaySink * playsink, GstPlaySinkType type,
339     GstElement * sink)
340 {
341   GstElement **elem = NULL, *old = NULL;
342
343   GST_LOG ("Setting sink %" GST_PTR_FORMAT " as sink type %d", sink, type);
344
345   GST_PLAY_SINK_LOCK (playsink);
346   switch (type) {
347     case GST_PLAY_SINK_TYPE_AUDIO:
348     case GST_PLAY_SINK_TYPE_AUDIO_RAW:
349       elem = &playsink->audio_sink;
350       break;
351     case GST_PLAY_SINK_TYPE_VIDEO:
352     case GST_PLAY_SINK_TYPE_VIDEO_RAW:
353       elem = &playsink->video_sink;
354       break;
355     case GST_PLAY_SINK_TYPE_TEXT:
356       elem = &playsink->text_sink;
357       break;
358     case GST_PLAY_SINK_TYPE_SUBPIC:
359       elem = &playsink->subp_sink;
360       break;
361     default:
362       break;
363   }
364   if (elem) {
365     old = *elem;
366     if (sink)
367       gst_object_ref (sink);
368     *elem = sink;
369   }
370   GST_PLAY_SINK_UNLOCK (playsink);
371
372   if (old)
373     gst_object_unref (old);
374 }
375
376 GstElement *
377 gst_play_sink_get_sink (GstPlaySink * playsink, GstPlaySinkType type)
378 {
379   GstElement *result = NULL;
380   GstElement *elem = NULL, *chainp = NULL;
381
382   GST_PLAY_SINK_LOCK (playsink);
383   switch (type) {
384     case GST_PLAY_SINK_TYPE_AUDIO:
385     {
386       GstPlayAudioChain *chain;
387       if ((chain = (GstPlayAudioChain *) playsink->audiochain))
388         chainp = chain->sink;
389       elem = playsink->audio_sink;
390       break;
391     }
392     case GST_PLAY_SINK_TYPE_VIDEO:
393     {
394       GstPlayVideoChain *chain;
395       if ((chain = (GstPlayVideoChain *) playsink->videochain))
396         chainp = chain->sink;
397       elem = playsink->video_sink;
398       break;
399     }
400     case GST_PLAY_SINK_TYPE_TEXT:
401     {
402       GstPlayTextChain *chain;
403       if ((chain = (GstPlayTextChain *) playsink->textchain))
404         chainp = chain->sink;
405       elem = playsink->text_sink;
406       break;
407     }
408     case GST_PLAY_SINK_TYPE_SUBPIC:
409     {
410       GstPlaySubpChain *chain;
411       if ((chain = (GstPlaySubpChain *) playsink->subpchain))
412         chainp = chain->sink;
413       elem = playsink->subp_sink;
414       break;
415     }
416     default:
417       break;
418   }
419   if (chainp) {
420     /* we have an active chain with a sink, get the sink */
421     result = gst_object_ref (chainp);
422   }
423   /* nothing found, return last configured sink */
424   if (result == NULL && elem)
425     result = gst_object_ref (elem);
426   GST_PLAY_SINK_UNLOCK (playsink);
427
428   return result;
429 }
430
431 static void
432 gst_play_sink_vis_unblocked (GstPad * tee_pad, gboolean blocked,
433     gpointer user_data)
434 {
435   GstPlaySink *playsink;
436
437   playsink = GST_PLAY_SINK (user_data);
438   /* nothing to do here, we need a dummy callback here to make the async call
439    * non-blocking. */
440   GST_DEBUG_OBJECT (playsink, "vis pad unblocked");
441 }
442
443 static void
444 gst_play_sink_vis_blocked (GstPad * tee_pad, gboolean blocked,
445     gpointer user_data)
446 {
447   GstPlaySink *playsink;
448   GstPlayVisChain *chain;
449
450   playsink = GST_PLAY_SINK (user_data);
451
452   GST_PLAY_SINK_LOCK (playsink);
453   GST_DEBUG_OBJECT (playsink, "vis pad blocked");
454   /* now try to change the plugin in the running vis chain */
455   if (!(chain = (GstPlayVisChain *) playsink->vischain))
456     goto done;
457
458   /* unlink the old plugin and unghost the pad */
459   gst_pad_unlink (chain->blockpad, chain->vissinkpad);
460   gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad), NULL);
461
462   /* set the old plugin to NULL and remove */
463   gst_element_set_state (chain->vis, GST_STATE_NULL);
464   gst_bin_remove (GST_BIN_CAST (chain->chain.bin), chain->vis);
465
466   /* add new plugin and set state to playing */
467   chain->vis = playsink->visualisation;
468   gst_bin_add (GST_BIN_CAST (chain->chain.bin), chain->vis);
469   gst_element_set_state (chain->vis, GST_STATE_PLAYING);
470
471   /* get pads */
472   chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
473   chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
474
475   /* link pads */
476   gst_pad_link (chain->blockpad, chain->vissinkpad);
477   gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad),
478       chain->vissrcpad);
479
480 done:
481   /* Unblock the pad */
482   gst_pad_set_blocked_async (tee_pad, FALSE, gst_play_sink_vis_unblocked,
483       playsink);
484   GST_PLAY_SINK_UNLOCK (playsink);
485 }
486
487 void
488 gst_play_sink_set_vis_plugin (GstPlaySink * playsink, GstElement * vis)
489 {
490   GstPlayVisChain *chain;
491
492   /* setting NULL means creating the default vis plugin */
493   if (vis == NULL)
494     vis = gst_element_factory_make ("goom", "vis");
495
496   /* simply return if we don't have a vis plugin here */
497   if (vis == NULL)
498     return;
499
500   GST_PLAY_SINK_LOCK (playsink);
501   /* first store the new vis */
502   if (playsink->visualisation)
503     gst_object_unref (playsink->visualisation);
504   /* take ownership */
505   gst_object_ref (vis);
506   gst_object_sink (vis);
507   playsink->visualisation = vis;
508
509   /* now try to change the plugin in the running vis chain, if we have no chain,
510    * we don't bother, any future vis chain will be created with the new vis
511    * plugin. */
512   if (!(chain = (GstPlayVisChain *) playsink->vischain))
513     goto done;
514
515   /* block the pad, the next time the callback is called we can change the
516    * visualisation. It's possible that this never happens or that the pad was
517    * already blocked. If the callback never happens, we don't have new data so
518    * we don't need the new vis plugin. If the pad was already blocked, the
519    * function returns FALSE but the previous pad block will do the right thing
520    * anyway. */
521   GST_DEBUG_OBJECT (playsink, "blocking vis pad");
522   gst_pad_set_blocked_async (chain->blockpad, TRUE, gst_play_sink_vis_blocked,
523       playsink);
524 done:
525   GST_PLAY_SINK_UNLOCK (playsink);
526
527   return;
528 }
529
530 GstElement *
531 gst_play_sink_get_vis_plugin (GstPlaySink * playsink)
532 {
533   GstElement *result = NULL;
534   GstPlayVisChain *chain;
535
536   GST_PLAY_SINK_LOCK (playsink);
537   if ((chain = (GstPlayVisChain *) playsink->vischain)) {
538     /* we have an active chain, get the sink */
539     if (chain->vis)
540       result = gst_object_ref (chain->vis);
541   }
542   /* nothing found, return last configured sink */
543   if (result == NULL && playsink->visualisation)
544     result = gst_object_ref (playsink->visualisation);
545   GST_PLAY_SINK_UNLOCK (playsink);
546
547   return result;
548 }
549
550 void
551 gst_play_sink_set_volume (GstPlaySink * playsink, gdouble volume)
552 {
553   GstPlayAudioChain *chain;
554
555   GST_PLAY_SINK_LOCK (playsink);
556   playsink->volume = volume;
557   chain = (GstPlayAudioChain *) playsink->audiochain;
558   if (chain && chain->volume) {
559     GST_LOG_OBJECT (playsink, "elements: volume=%" GST_PTR_FORMAT ", mute=%"
560         GST_PTR_FORMAT "; new volume=%.03f, mute=%d", chain->volume,
561         chain->mute, volume, playsink->mute);
562     /* if there is a mute element or we are not muted, set the volume */
563     if (chain->mute || !playsink->mute)
564       g_object_set (chain->volume, "volume", volume, NULL);
565   } else {
566     GST_LOG_OBJECT (playsink, "no volume element");
567     playsink->volume_changed = TRUE;
568   }
569   GST_PLAY_SINK_UNLOCK (playsink);
570 }
571
572 gdouble
573 gst_play_sink_get_volume (GstPlaySink * playsink)
574 {
575   gdouble result;
576   GstPlayAudioChain *chain;
577
578   GST_PLAY_SINK_LOCK (playsink);
579   chain = (GstPlayAudioChain *) playsink->audiochain;
580   result = playsink->volume;
581   if (chain && chain->volume) {
582     if (chain->mute || !playsink->mute) {
583       g_object_get (chain->volume, "volume", &result, NULL);
584       playsink->volume = result;
585     }
586   }
587   GST_PLAY_SINK_UNLOCK (playsink);
588
589   return result;
590 }
591
592 void
593 gst_play_sink_set_mute (GstPlaySink * playsink, gboolean mute)
594 {
595   GstPlayAudioChain *chain;
596
597   GST_PLAY_SINK_LOCK (playsink);
598   playsink->mute = mute;
599   chain = (GstPlayAudioChain *) playsink->audiochain;
600   if (chain) {
601     if (chain->mute) {
602       g_object_set (chain->mute, "mute", mute, NULL);
603     } else if (chain->volume) {
604       if (mute)
605         g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
606       else
607         g_object_set (chain->volume, "volume", (gdouble) playsink->volume,
608             NULL);
609     }
610   } else {
611     playsink->mute_changed = TRUE;
612   }
613   GST_PLAY_SINK_UNLOCK (playsink);
614 }
615
616 gboolean
617 gst_play_sink_get_mute (GstPlaySink * playsink)
618 {
619   gboolean result;
620   GstPlayAudioChain *chain;
621
622   GST_PLAY_SINK_LOCK (playsink);
623   chain = (GstPlayAudioChain *) playsink->audiochain;
624   if (chain && chain->mute) {
625     g_object_get (chain->mute, "mute", &result, NULL);
626     playsink->mute = result;
627   } else {
628     result = playsink->mute;
629   }
630   GST_PLAY_SINK_UNLOCK (playsink);
631
632   return result;
633 }
634
635 static void
636 post_missing_element_message (GstPlaySink * playsink, const gchar * name)
637 {
638   GstMessage *msg;
639
640   msg = gst_missing_element_message_new (GST_ELEMENT_CAST (playsink), name);
641   gst_element_post_message (GST_ELEMENT_CAST (playsink), msg);
642 }
643
644 static gboolean
645 add_chain (GstPlayChain * chain, gboolean add)
646 {
647   if (chain->added == add)
648     return TRUE;
649
650   if (add)
651     gst_bin_add (GST_BIN_CAST (chain->playsink), chain->bin);
652   else
653     gst_bin_remove (GST_BIN_CAST (chain->playsink), chain->bin);
654
655   chain->added = add;
656
657   return TRUE;
658 }
659
660 static gboolean
661 activate_chain (GstPlayChain * chain, gboolean activate)
662 {
663   GstState state;
664
665   if (chain->activated == activate)
666     return TRUE;
667
668   GST_OBJECT_LOCK (chain->playsink);
669   state = GST_STATE_TARGET (chain->playsink);
670   GST_OBJECT_UNLOCK (chain->playsink);
671
672   if (activate)
673     gst_element_set_state (chain->bin, state);
674   else
675     gst_element_set_state (chain->bin, GST_STATE_NULL);
676
677   chain->activated = activate;
678
679   return TRUE;
680 }
681
682 static gint
683 find_property (GstElement * element, const gchar * name)
684 {
685   gint res;
686
687   if (g_object_class_find_property (G_OBJECT_GET_CLASS (element), name)) {
688     res = 0;
689     GST_DEBUG_OBJECT (element, "found %s property", name);
690   } else {
691     GST_DEBUG_OBJECT (element, "did not find %s property", name);
692     res = 1;
693     gst_object_unref (element);
694   }
695   return res;
696 }
697
698 /* find an object in the hierarchy with a property named @name */
699 static GstElement *
700 gst_play_sink_find_property (GstPlaySink * playsink, GstElement * obj,
701     const gchar * name)
702 {
703   GstElement *result = NULL;
704   GstIterator *it;
705
706   if (GST_IS_BIN (obj)) {
707     it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
708     result = gst_iterator_find_custom (it,
709         (GCompareFunc) find_property, (gpointer) name);
710     gst_iterator_free (it);
711   } else {
712     if (g_object_class_find_property (G_OBJECT_GET_CLASS (obj), name)) {
713       result = obj;
714       gst_object_ref (obj);
715     }
716   }
717   return result;
718 }
719
720 static gint
721 find_property_sink (GstElement * element, const gchar * name)
722 {
723   gint res;
724   gboolean is_sink;
725
726   GST_OBJECT_LOCK (element);
727   is_sink = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_IS_SINK);
728   GST_OBJECT_UNLOCK (element);
729
730   if (is_sink &&
731       g_object_class_find_property (G_OBJECT_GET_CLASS (element), name)) {
732     res = 0;
733     GST_DEBUG_OBJECT (element, "found %s property on sink", name);
734   } else {
735     GST_DEBUG_OBJECT (element, "did not find %s property", name);
736     res = 1;
737     gst_object_unref (element);
738   }
739   return res;
740 }
741
742 /* find a sink in the hierarchy with a property named @name. This function does
743  * not increase the refcount of the returned object and thus remains valid as
744  * long as the bin is valid. */
745 static GstElement *
746 gst_play_sink_find_property_sinks (GstPlaySink * playsink, GstElement * obj,
747     const gchar * name)
748 {
749   GstElement *result = NULL;
750   GstIterator *it;
751
752   if (g_object_class_find_property (G_OBJECT_GET_CLASS (obj), name)) {
753     result = obj;
754   } else if (GST_IS_BIN (obj)) {
755     it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
756     result = gst_iterator_find_custom (it,
757         (GCompareFunc) find_property_sink, (gpointer) name);
758     gst_iterator_free (it);
759     /* we don't need the extra ref */
760     if (result)
761       gst_object_unref (result);
762   }
763   return result;
764 }
765
766 static void
767 do_async_start (GstPlaySink * playsink)
768 {
769   GstMessage *message;
770
771   if (!playsink->need_async_start)
772     return;
773
774   playsink->async_pending = TRUE;
775
776   GST_INFO_OBJECT (playsink, "Sending async_start message");
777   message = gst_message_new_async_start (GST_OBJECT_CAST (playsink), FALSE);
778   GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
779       (playsink), message);
780 }
781
782 static void
783 do_async_done (GstPlaySink * playsink)
784 {
785   GstMessage *message;
786
787   if (playsink->async_pending) {
788     GST_INFO_OBJECT (playsink, "Sending async_done message");
789     message = gst_message_new_async_done (GST_OBJECT_CAST (playsink));
790     GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
791         (playsink), message);
792
793     playsink->async_pending = FALSE;
794   }
795
796   playsink->need_async_start = FALSE;
797 }
798
799 /* try to change the state of an element. This function returns the element when
800  * the state change could be performed. When this function returns NULL an error
801  * occured and the element is unreffed if @unref is TRUE. */
802 static GstElement *
803 try_element (GstPlaySink * playsink, GstElement * element, gboolean unref)
804 {
805   GstStateChangeReturn ret;
806
807   if (element) {
808     ret = gst_element_set_state (element, GST_STATE_READY);
809     if (ret == GST_STATE_CHANGE_FAILURE) {
810       GST_DEBUG_OBJECT (playsink, "failed state change..");
811       gst_element_set_state (element, GST_STATE_NULL);
812       if (unref)
813         gst_object_unref (element);
814       element = NULL;
815     }
816   }
817   return element;
818 }
819
820 /* make the element (bin) that contains the elements needed to perform
821  * video display. 
822  *
823  *  +------------------------------------------------------------+
824  *  | vbin                                                       |
825  *  |      +-------+   +----------+   +----------+   +---------+ |
826  *  |      | queue |   |colorspace|   |videoscale|   |videosink| |
827  *  |   +-sink    src-sink       src-sink       src-sink       | |
828  *  |   |  +-------+   +----------+   +----------+   +---------+ |
829  * sink-+                                                        |
830  *  +------------------------------------------------------------+
831  *           
832  */
833 static GstPlayVideoChain *
834 gen_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async,
835     gboolean queue)
836 {
837   GstPlayVideoChain *chain;
838   GstBin *bin;
839   GstPad *pad;
840   GstElement *head, *prev, *elem = NULL;
841
842   chain = g_new0 (GstPlayVideoChain, 1);
843   chain->chain.playsink = playsink;
844   chain->chain.raw = raw;
845
846   GST_DEBUG_OBJECT (playsink, "making video chain %p", chain);
847
848   if (playsink->video_sink) {
849     GST_DEBUG_OBJECT (playsink, "trying configured videosink");
850     chain->sink = try_element (playsink, playsink->video_sink, FALSE);
851   } else {
852     /* only try fallback if no specific sink was chosen */
853     if (chain->sink == NULL) {
854       GST_DEBUG_OBJECT (playsink, "trying autovideosink");
855       elem = gst_element_factory_make ("autovideosink", "videosink");
856       chain->sink = try_element (playsink, elem, TRUE);
857     }
858     if (chain->sink == NULL) {
859       /* if default sink from config.h is different then try it too */
860       if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
861         GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_VIDEOSINK);
862         elem = gst_element_factory_make (DEFAULT_VIDEOSINK, "videosink");
863         chain->sink = try_element (playsink, elem, TRUE);
864       }
865     }
866   }
867   if (chain->sink == NULL)
868     goto no_sinks;
869
870   /* if we can disable async behaviour of the sink, we can avoid adding a
871    * queue for the audio chain. */
872   elem = gst_play_sink_find_property_sinks (playsink, chain->sink, "async");
873   if (elem) {
874     GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
875         async, GST_ELEMENT_NAME (elem));
876     g_object_set (elem, "async", async, NULL);
877     chain->async = async;
878   } else {
879     GST_DEBUG_OBJECT (playsink, "no async property on the sink");
880     chain->async = TRUE;
881   }
882
883   /* create a bin to hold objects, as we create them we add them to this bin so
884    * that when something goes wrong we only need to unref the bin */
885   chain->chain.bin = gst_bin_new ("vbin");
886   bin = GST_BIN_CAST (chain->chain.bin);
887   gst_object_ref (bin);
888   gst_object_sink (bin);
889   gst_bin_add (bin, chain->sink);
890
891   if (queue) {
892     /* decouple decoder from sink, this improves playback quite a lot since the
893      * decoder can continue while the sink blocks for synchronisation. We don't
894      * need a lot of buffers as this consumes a lot of memory and we don't want
895      * too little because else we would be context switching too quickly. */
896     chain->queue = gst_element_factory_make ("queue", "vqueue");
897     g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
898         "max-size-bytes", 0, "max-size-time", (gint64) 0, NULL);
899     gst_bin_add (bin, chain->queue);
900     head = prev = chain->queue;
901   } else {
902     head = chain->sink;
903     prev = NULL;
904   }
905
906   if (raw && !(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
907     GST_DEBUG_OBJECT (playsink, "creating ffmpegcolorspace");
908     chain->conv = gst_element_factory_make ("ffmpegcolorspace", "vconv");
909     if (chain->conv == NULL) {
910       post_missing_element_message (playsink, "ffmpegcolorspace");
911       GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
912           (_("Missing element '%s' - check your GStreamer installation."),
913               "ffmpegcolorspace"), ("video rendering might fail"));
914     } else {
915       gst_bin_add (bin, chain->conv);
916       if (prev) {
917         if (!gst_element_link_pads (prev, "src", chain->conv, "sink"))
918           goto link_failed;
919       } else {
920         head = chain->conv;
921       }
922       prev = chain->conv;
923     }
924
925     GST_DEBUG_OBJECT (playsink, "creating videoscale");
926     chain->scale = gst_element_factory_make ("videoscale", "vscale");
927     if (chain->scale == NULL) {
928       post_missing_element_message (playsink, "videoscale");
929       GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
930           (_("Missing element '%s' - check your GStreamer installation."),
931               "videoscale"), ("possibly a liboil version mismatch?"));
932     } else {
933       gst_bin_add (bin, chain->scale);
934       if (prev) {
935         if (!gst_element_link_pads (prev, "src", chain->scale, "sink"))
936           goto link_failed;
937       } else {
938         head = chain->scale;
939       }
940       prev = chain->scale;
941     }
942   }
943
944   if (prev) {
945     GST_DEBUG_OBJECT (playsink, "linking to sink");
946     if (!gst_element_link_pads (prev, "src", chain->sink, NULL))
947       goto link_failed;
948   }
949
950   pad = gst_element_get_static_pad (head, "sink");
951   chain->sinkpad = gst_ghost_pad_new ("sink", pad);
952   gst_object_unref (pad);
953
954   gst_element_add_pad (chain->chain.bin, chain->sinkpad);
955
956   return chain;
957
958   /* ERRORS */
959 no_sinks:
960   {
961     if (!elem) {
962       post_missing_element_message (playsink, "autovideosink");
963       if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
964         post_missing_element_message (playsink, DEFAULT_VIDEOSINK);
965         GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
966             (_("Both autovideosink and %s elements are missing."),
967                 DEFAULT_VIDEOSINK), (NULL));
968       } else {
969         GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
970             (_("The autovideosink element is missing.")), (NULL));
971       }
972     } else {
973       if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
974         GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
975             (_("Both autovideosink and %s elements are not working."),
976                 DEFAULT_VIDEOSINK), (NULL));
977       } else {
978         GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
979             (_("The autovideosink element is not working.")), (NULL));
980       }
981     }
982     free_chain ((GstPlayChain *) chain);
983     return NULL;
984   }
985 link_failed:
986   {
987     GST_ELEMENT_ERROR (playsink, CORE, PAD,
988         (NULL), ("Failed to configure the video sink."));
989     free_chain ((GstPlayChain *) chain);
990     return NULL;
991   }
992 }
993
994 static gboolean
995 setup_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async,
996     gboolean queue)
997 {
998   GstElement *elem;
999   GstPlayVideoChain *chain;
1000   GstStateChangeReturn ret;
1001
1002   chain = playsink->videochain;
1003
1004   /* if the chain was active we don't do anything */
1005   if (GST_PLAY_CHAIN (chain)->activated == TRUE)
1006     return TRUE;
1007
1008   if (chain->chain.raw != raw)
1009     return FALSE;
1010
1011   /* try to set the sink element to READY again */
1012   ret = gst_element_set_state (chain->sink, GST_STATE_READY);
1013   if (ret == GST_STATE_CHANGE_FAILURE)
1014     return FALSE;
1015
1016   /* if we can disable async behaviour of the sink, we can avoid adding a
1017    * queue for the audio chain. */
1018   elem = gst_play_sink_find_property_sinks (playsink, chain->sink, "async");
1019   if (elem) {
1020     GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1021         async, GST_ELEMENT_NAME (elem));
1022     g_object_set (elem, "async", async, NULL);
1023     chain->async = async;
1024   } else {
1025     GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1026     chain->async = TRUE;
1027   }
1028   return TRUE;
1029 }
1030
1031 /* make an element for playback of video with subtitles embedded.
1032  *
1033  *  +----------------------------------------------+
1034  *  | tbin                  +-------------+        |
1035  *  |          +-----+      | textoverlay |        |
1036  *  |          | csp | +--video_sink      |        |
1037  * sink-------sink  src+ +-text_sink     src--+    |
1038  *  |          +-----+   |  +-------------+   +-- src   
1039  * text_sink-------------+                         |
1040  *  +----------------------------------------------+
1041  */
1042 static GstPlayTextChain *
1043 gen_text_chain (GstPlaySink * playsink)
1044 {
1045   GstPlayTextChain *chain;
1046   GstBin *bin;
1047   GstElement *elem;
1048   GstPad *videosinkpad, *textsinkpad, *srcpad;
1049
1050   chain = g_new0 (GstPlayTextChain, 1);
1051   chain->chain.playsink = playsink;
1052
1053   GST_DEBUG_OBJECT (playsink, "making text chain %p", chain);
1054
1055   chain->chain.bin = gst_bin_new ("tbin");
1056   bin = GST_BIN_CAST (chain->chain.bin);
1057   gst_object_ref (bin);
1058   gst_object_sink (bin);
1059
1060   videosinkpad = textsinkpad = srcpad = NULL;
1061
1062   /* first try to hook the text pad to the custom sink */
1063   if (playsink->text_sink) {
1064     GST_DEBUG_OBJECT (playsink, "trying configured textsink");
1065     chain->sink = try_element (playsink, playsink->text_sink, FALSE);
1066     if (chain->sink) {
1067       elem = gst_play_sink_find_property_sinks (playsink, chain->sink, "async");
1068       if (elem) {
1069         /* make sure the sparse subtitles don't participate in the preroll */
1070         g_object_set (elem, "async", FALSE, NULL);
1071         /* we have a custom sink, this will be our textsinkpad */
1072         textsinkpad = gst_element_get_static_pad (chain->sink, "sink");
1073         if (textsinkpad) {
1074           /* we're all fine now and we can add the sink to the chain */
1075           GST_DEBUG_OBJECT (playsink, "adding custom text sink");
1076           gst_bin_add (bin, chain->sink);
1077         } else {
1078           GST_WARNING_OBJECT (playsink,
1079               "can't find a sink pad on custom text sink");
1080           gst_object_unref (chain->sink);
1081           chain->sink = NULL;
1082         }
1083         /* try to set sync to true but it's no biggie when we can't */
1084         if ((elem =
1085                 gst_play_sink_find_property_sinks (playsink, chain->sink,
1086                     "sync")))
1087           g_object_set (elem, "sync", TRUE, NULL);
1088       } else {
1089         GST_WARNING_OBJECT (playsink,
1090             "can't find async property in custom text sink");
1091       }
1092     }
1093     if (textsinkpad == NULL) {
1094       GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1095           (_("Custom text sink element is not usable.")),
1096           ("fallback to default textoverlay"));
1097     }
1098   }
1099
1100   if (textsinkpad == NULL) {
1101     if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
1102       /* no custom sink, try to setup the colorspace and textoverlay elements */
1103       chain->conv = gst_element_factory_make ("ffmpegcolorspace", "tconv");
1104       if (chain->conv == NULL) {
1105         /* not really needed, it might work without colorspace */
1106         post_missing_element_message (playsink, "ffmpegcolorspace");
1107         GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1108             (_("Missing element '%s' - check your GStreamer installation."),
1109                 "ffmpegcolorspace"), ("subtitle rendering might fail"));
1110       } else {
1111         gst_bin_add (bin, chain->conv);
1112         videosinkpad = gst_element_get_static_pad (chain->conv, "sink");
1113       }
1114     }
1115
1116     chain->overlay = gst_element_factory_make ("textoverlay", "overlay");
1117     if (chain->overlay == NULL) {
1118       post_missing_element_message (playsink, "textoverlay");
1119       GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1120           (_("Missing element '%s' - check your GStreamer installation."),
1121               "textoverlay"), ("subtitle rendering disabled"));
1122     } else {
1123       gst_bin_add (bin, chain->overlay);
1124
1125       /* Set some parameters */
1126       g_object_set (G_OBJECT (chain->overlay),
1127           "halign", "center", "valign", "bottom", NULL);
1128       if (playsink->font_desc) {
1129         g_object_set (G_OBJECT (chain->overlay), "font-desc",
1130             playsink->font_desc, NULL);
1131       }
1132       g_object_set (G_OBJECT (chain->overlay), "wait-text", FALSE, NULL);
1133
1134       textsinkpad = gst_element_get_static_pad (chain->overlay, "text_sink");
1135
1136       srcpad = gst_element_get_static_pad (chain->overlay, "src");
1137
1138       if (videosinkpad) {
1139         /* if we had a videosinkpad, we had a converter and we can link it, we
1140          * know that this will work */
1141         gst_element_link_pads (chain->conv, "src", chain->overlay,
1142             "video_sink");
1143       } else {
1144         /* no videopad, expose our own video pad then */
1145         videosinkpad =
1146             gst_element_get_static_pad (chain->overlay, "video_sink");
1147       }
1148     }
1149   }
1150
1151   if (videosinkpad == NULL) {
1152     /* if we still don't have a videosink, we don't have a converter nor an
1153      * overlay. the only thing we can do is insert an identity and ghost the src
1154      * and sink pads. */
1155     chain->conv = gst_element_factory_make ("identity", "tidentity");
1156     g_object_set (chain->conv, "signal-handoffs", FALSE, NULL);
1157     g_object_set (chain->conv, "silent", TRUE, NULL);
1158     gst_bin_add (bin, chain->conv);
1159     srcpad = gst_element_get_static_pad (chain->conv, "src");
1160     videosinkpad = gst_element_get_static_pad (chain->conv, "sink");
1161   } else {
1162     /* we have a videosink but maybe not a srcpad because there was no
1163      * overlay */
1164     if (srcpad == NULL) {
1165       /* ghost the source pad of the converter then */
1166       srcpad = gst_element_get_static_pad (chain->conv, "src");
1167     }
1168   }
1169
1170   /* expose the ghostpads */
1171   if (videosinkpad) {
1172     chain->videosinkpad = gst_ghost_pad_new ("sink", videosinkpad);
1173     gst_object_unref (videosinkpad);
1174     gst_element_add_pad (chain->chain.bin, chain->videosinkpad);
1175   }
1176   if (textsinkpad) {
1177     chain->textsinkpad = gst_ghost_pad_new ("text_sink", textsinkpad);
1178     gst_object_unref (textsinkpad);
1179     gst_element_add_pad (chain->chain.bin, chain->textsinkpad);
1180   }
1181   if (srcpad) {
1182     chain->srcpad = gst_ghost_pad_new ("src", srcpad);
1183     gst_object_unref (srcpad);
1184     gst_element_add_pad (chain->chain.bin, chain->srcpad);
1185   }
1186
1187   return chain;
1188 }
1189
1190 /* make an element for playback of video with subpictures embedded.
1191  *
1192  *  +--------------------------------------------------------+
1193  *  | pbin                            +-------------+        |
1194  *  |       +-------+    +-----+      |   dvdspu    |        |
1195  *  |       | queue |    | csp | +---video          |        |
1196  * sink----sink    src--sink  src+ +-subpicture    src--+    |
1197  *  |       +-------+    +-----+   |  +-------------+   +-- src   
1198  * subpicture----------------------+                         |
1199  *  +--------------------------------------------------------+
1200  */
1201 static GstPlaySubpChain *
1202 gen_subp_chain (GstPlaySink * playsink)
1203 {
1204   GstPlaySubpChain *chain;
1205   GstBin *bin;
1206   GstElement *elem, *head;
1207   GstPad *videosinkpad, *subpsinkpad, *srcpad;
1208
1209   chain = g_new0 (GstPlaySubpChain, 1);
1210   chain->chain.playsink = playsink;
1211
1212   GST_DEBUG_OBJECT (playsink, "making subpicture chain %p", chain);
1213
1214   chain->chain.bin = gst_bin_new ("pbin");
1215   bin = GST_BIN_CAST (chain->chain.bin);
1216   gst_object_ref (bin);
1217   gst_object_sink (bin);
1218
1219   subpsinkpad = srcpad = NULL;
1220
1221   /* first try to hook the text pad to the custom sink */
1222   if (playsink->subp_sink) {
1223     GST_DEBUG_OBJECT (playsink, "trying configured subpsink");
1224     chain->sink = try_element (playsink, playsink->text_sink, FALSE);
1225     if (chain->sink) {
1226       elem = gst_play_sink_find_property_sinks (playsink, chain->sink, "async");
1227       if (elem) {
1228         /* make sure the sparse subtitles don't participate in the preroll */
1229         g_object_set (elem, "async", FALSE, NULL);
1230         /* we have a custom sink, this will be our subpsinkpad */
1231         subpsinkpad = gst_element_get_static_pad (chain->sink, "sink");
1232         if (subpsinkpad) {
1233           /* we're all fine now and we can add the sink to the chain */
1234           GST_DEBUG_OBJECT (playsink, "adding custom text sink");
1235           gst_bin_add (bin, chain->sink);
1236         } else {
1237           GST_WARNING_OBJECT (playsink,
1238               "can't find a sink pad on custom text sink");
1239           gst_object_unref (chain->sink);
1240           chain->sink = NULL;
1241         }
1242         /* try to set sync to true but it's no biggie when we can't */
1243         if ((elem =
1244                 gst_play_sink_find_property_sinks (playsink, chain->sink,
1245                     "sync")))
1246           g_object_set (elem, "sync", TRUE, NULL);
1247       } else {
1248         GST_WARNING_OBJECT (playsink,
1249             "can't find async property in custom text sink");
1250       }
1251     }
1252     if (subpsinkpad == NULL) {
1253       GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1254           (_("Custom text sink element is not usable.")),
1255           ("fallback to default dvdspu overlay"));
1256     }
1257   }
1258
1259   /* make a little queue */
1260   chain->queue = gst_element_factory_make ("queue", "vqueue");
1261   g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1262       "max-size-bytes", 0, "max-size-time", (gint64) 0, NULL);
1263   gst_bin_add (bin, chain->queue);
1264   head = chain->queue;
1265
1266   /* video goes into the queue */
1267   videosinkpad = gst_element_get_static_pad (chain->queue, "sink");
1268
1269   if (subpsinkpad == NULL) {
1270     if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
1271       /* no custom sink, try to setup the colorspace and textoverlay elements */
1272       chain->conv = gst_element_factory_make ("ffmpegcolorspace", "tconv");
1273       if (chain->conv == NULL) {
1274         /* not really needed, it might work without colorspace */
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"), ("subpicture rendering might fail"));
1279       } else {
1280         gst_bin_add (bin, chain->conv);
1281         gst_element_link_pads (head, "src", chain->conv, "sink");
1282         head = chain->conv;
1283       }
1284     }
1285
1286     chain->overlay = gst_element_factory_make ("dvdspu", "spuoverlay");
1287     if (chain->overlay == NULL) {
1288       post_missing_element_message (playsink, "dvdspu");
1289       GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1290           (_("Missing element '%s' - check your GStreamer installation."),
1291               "dvdspu"), ("subpicture rendering disabled"));
1292     } else {
1293       gst_bin_add (bin, chain->overlay);
1294       /* Set some parameters */
1295       subpsinkpad = gst_element_get_static_pad (chain->overlay, "subpicture");
1296       /* link to the next element */
1297       gst_element_link_pads (head, "src", chain->overlay, "video");
1298       head = chain->overlay;
1299     }
1300   }
1301   srcpad = gst_element_get_static_pad (head, "src");
1302   chain->srcpad = gst_ghost_pad_new ("src", srcpad);
1303   gst_object_unref (srcpad);
1304   gst_element_add_pad (chain->chain.bin, chain->srcpad);
1305
1306   /* expose the ghostpads */
1307   chain->videosinkpad = gst_ghost_pad_new ("sink", videosinkpad);
1308   gst_object_unref (videosinkpad);
1309   gst_element_add_pad (chain->chain.bin, chain->videosinkpad);
1310
1311   if (subpsinkpad) {
1312     chain->subpsinkpad = gst_ghost_pad_new ("subpicture", subpsinkpad);
1313     gst_object_unref (subpsinkpad);
1314     gst_element_add_pad (chain->chain.bin, chain->subpsinkpad);
1315   }
1316   return chain;
1317 }
1318
1319 /* make the chain that contains the elements needed to perform
1320  * audio playback. 
1321  *
1322  * We add a tee as the first element so that we can link the visualisation chain
1323  * to it when requested.
1324  *
1325  *  +-------------------------------------------------------------+
1326  *  | abin                                                        |
1327  *  |      +---------+   +----------+   +---------+   +---------+ |
1328  *  |      |audioconv|   |audioscale|   | volume  |   |audiosink| |
1329  *  |   +-srck      src-sink       src-sink      src-sink       | |
1330  *  |   |  +---------+   +----------+   +---------+   +---------+ |
1331  * sink-+                                                         |
1332  *  +-------------------------------------------------------------+
1333  */
1334 static GstPlayAudioChain *
1335 gen_audio_chain (GstPlaySink * playsink, gboolean raw, gboolean queue)
1336 {
1337   GstPlayAudioChain *chain;
1338   GstBin *bin;
1339   gboolean have_volume;
1340   GstPad *pad;
1341   GstElement *head, *prev, *elem = NULL;
1342
1343   chain = g_new0 (GstPlayAudioChain, 1);
1344   chain->chain.playsink = playsink;
1345   chain->chain.raw = raw;
1346
1347   GST_DEBUG_OBJECT (playsink, "making audio chain %p", chain);
1348
1349   if (playsink->audio_sink) {
1350     GST_DEBUG_OBJECT (playsink, "trying configured audiosink %" GST_PTR_FORMAT,
1351         playsink->audio_sink);
1352     chain->sink = try_element (playsink, playsink->audio_sink, FALSE);
1353   } else {
1354     /* only try fallback if no specific sink was chosen */
1355     if (chain->sink == NULL) {
1356       GST_DEBUG_OBJECT (playsink, "trying autoaudiosink");
1357       elem = gst_element_factory_make ("autoaudiosink", "audiosink");
1358       chain->sink = try_element (playsink, elem, TRUE);
1359     }
1360     if (chain->sink == NULL) {
1361       /* if default sink from config.h is different then try it too */
1362       if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1363         GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_AUDIOSINK);
1364         elem = gst_element_factory_make (DEFAULT_AUDIOSINK, "audiosink");
1365         chain->sink = try_element (playsink, elem, TRUE);
1366       }
1367     }
1368   }
1369   if (chain->sink == NULL)
1370     goto no_sinks;
1371
1372   chain->chain.bin = gst_bin_new ("abin");
1373   bin = GST_BIN_CAST (chain->chain.bin);
1374   gst_object_ref (bin);
1375   gst_object_sink (bin);
1376   gst_bin_add (bin, chain->sink);
1377
1378   if (queue) {
1379     /* we have to add a queue when we need to decouple for the video sink in
1380      * visualisations */
1381     GST_DEBUG_OBJECT (playsink, "adding audio queue");
1382     chain->queue = gst_element_factory_make ("queue", "aqueue");
1383     gst_bin_add (bin, chain->queue);
1384     prev = head = chain->queue;
1385   } else {
1386     head = chain->sink;
1387     prev = NULL;
1388   }
1389
1390   /* check if the sink, or something within the sink, has the volume property.
1391    * If it does we don't need to add a volume element.  */
1392   elem = gst_play_sink_find_property_sinks (playsink, chain->sink, "volume");
1393   if (elem) {
1394     chain->volume = elem;
1395
1396     GST_DEBUG_OBJECT (playsink, "the sink has a volume property");
1397     have_volume = TRUE;
1398     chain->sink_volume = TRUE;
1399     /* if the sink also has a mute property we can use this as well. We'll only
1400      * use the mute property if there is a volume property. We can simulate the
1401      * mute with the volume otherwise. */
1402     chain->mute =
1403         gst_play_sink_find_property_sinks (playsink, chain->sink, "mute");
1404     if (chain->mute) {
1405       GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
1406     }
1407     /* use the sink to control the volume and mute */
1408     if (playsink->volume_changed) {
1409       g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
1410     }
1411     if (playsink->mute_changed) {
1412       if (chain->mute) {
1413         g_object_set (chain->mute, "mute", playsink->mute, NULL);
1414       } else {
1415         if (playsink->mute)
1416           g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
1417       }
1418     }
1419   } else {
1420     /* no volume, we need to add a volume element when we can */
1421     GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
1422     have_volume = FALSE;
1423     chain->sink_volume = FALSE;
1424   }
1425
1426   if (raw && !(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO)) {
1427     GST_DEBUG_OBJECT (playsink, "creating audioconvert");
1428     chain->conv = gst_element_factory_make ("audioconvert", "aconv");
1429     if (chain->conv == NULL) {
1430       post_missing_element_message (playsink, "audioconvert");
1431       GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1432           (_("Missing element '%s' - check your GStreamer installation."),
1433               "audioconvert"), ("possibly a liboil version mismatch?"));
1434     } else {
1435       gst_bin_add (bin, chain->conv);
1436       if (prev) {
1437         if (!gst_element_link_pads (prev, "src", chain->conv, "sink"))
1438           goto link_failed;
1439       } else {
1440         head = chain->conv;
1441       }
1442       prev = chain->conv;
1443     }
1444
1445     GST_DEBUG_OBJECT (playsink, "creating audioresample");
1446     chain->resample = gst_element_factory_make ("audioresample", "aresample");
1447     if (chain->resample == NULL) {
1448       post_missing_element_message (playsink, "audioresample");
1449       GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1450           (_("Missing element '%s' - check your GStreamer installation."),
1451               "audioresample"), ("possibly a liboil version mismatch?"));
1452     } else {
1453       gst_bin_add (bin, chain->resample);
1454       if (prev) {
1455         if (!gst_element_link_pads (prev, "src", chain->resample, "sink"))
1456           goto link_failed;
1457       } else {
1458         head = chain->resample;
1459       }
1460       prev = chain->resample;
1461     }
1462
1463     if (!have_volume && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME) {
1464       GST_DEBUG_OBJECT (playsink, "creating volume");
1465       chain->volume = gst_element_factory_make ("volume", "volume");
1466       if (chain->volume == NULL) {
1467         post_missing_element_message (playsink, "volume");
1468         GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1469             (_("Missing element '%s' - check your GStreamer installation."),
1470                 "volume"), ("possibly a liboil version mismatch?"));
1471       } else {
1472         have_volume = TRUE;
1473
1474         /* volume also has the mute property */
1475         chain->mute = chain->volume;
1476
1477         /* configure with the latest volume and mute */
1478         g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume,
1479             NULL);
1480         g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
1481         gst_bin_add (bin, chain->volume);
1482
1483         if (prev) {
1484           if (!gst_element_link_pads (prev, "src", chain->volume, "sink"))
1485             goto link_failed;
1486         } else {
1487           head = chain->volume;
1488         }
1489         prev = chain->volume;
1490       }
1491     }
1492   }
1493
1494   if (prev) {
1495     /* we only have to link to the previous element if we have something in
1496      * front of the sink */
1497     GST_DEBUG_OBJECT (playsink, "linking to sink");
1498     if (!gst_element_link_pads (prev, "src", chain->sink, NULL))
1499       goto link_failed;
1500   }
1501
1502   /* post a warning if we have no way to configure the volume */
1503   if (!have_volume) {
1504     GST_ELEMENT_WARNING (playsink, STREAM, NOT_IMPLEMENTED,
1505         (_("No volume control found")), ("Volume/mute is not available"));
1506   }
1507
1508   /* and ghost the sinkpad of the headmost element */
1509   GST_DEBUG_OBJECT (playsink, "ghosting sink pad");
1510   pad = gst_element_get_static_pad (head, "sink");
1511   chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1512   gst_object_unref (pad);
1513   gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1514
1515   return chain;
1516
1517   /* ERRORS */
1518 no_sinks:
1519   {
1520     if (!elem) {
1521       post_missing_element_message (playsink, "autoaudiosink");
1522       if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1523         post_missing_element_message (playsink, DEFAULT_AUDIOSINK);
1524         GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1525             (_("Both autoaudiosink and %s elements are missing."),
1526                 DEFAULT_AUDIOSINK), (NULL));
1527       } else {
1528         GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1529             (_("The autoaudiosink element is missing.")), (NULL));
1530       }
1531     } else {
1532       if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
1533         GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1534             (_("Both autoaudiosink and %s elements are not working."),
1535                 DEFAULT_AUDIOSINK), (NULL));
1536       } else {
1537         GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1538             (_("The autoaudiosink element is not working.")), (NULL));
1539       }
1540     }
1541     free_chain ((GstPlayChain *) chain);
1542     return NULL;
1543   }
1544 link_failed:
1545   {
1546     GST_ELEMENT_ERROR (playsink, CORE, PAD,
1547         (NULL), ("Failed to configure the audio sink."));
1548     free_chain ((GstPlayChain *) chain);
1549     return NULL;
1550   }
1551 }
1552
1553 static gboolean
1554 setup_audio_chain (GstPlaySink * playsink, gboolean raw, gboolean queue)
1555 {
1556   GstElement *elem;
1557   GstPlayAudioChain *chain;
1558   GstStateChangeReturn ret;
1559
1560   chain = playsink->audiochain;
1561
1562   /* if the chain was active we don't do anything */
1563   if (GST_PLAY_CHAIN (chain)->activated == TRUE)
1564     return TRUE;
1565
1566   if (chain->chain.raw != raw)
1567     return FALSE;
1568
1569   /* try to set the sink element to READY again */
1570   ret = gst_element_set_state (chain->sink, GST_STATE_READY);
1571   if (ret == GST_STATE_CHANGE_FAILURE)
1572     return FALSE;
1573
1574   /* check if the sink, or something within the sink, has the volume property.
1575    * If it does we don't need to add a volume element.  */
1576   elem = gst_play_sink_find_property_sinks (playsink, chain->sink, "volume");
1577   if (elem) {
1578     chain->volume = elem;
1579
1580     if (playsink->volume_changed) {
1581       GST_DEBUG_OBJECT (playsink, "the sink has a volume property, setting %f",
1582           playsink->volume);
1583       /* use the sink to control the volume */
1584       g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
1585     }
1586     /* if the sink also has a mute property we can use this as well. We'll only
1587      * use the mute property if there is a volume property. We can simulate the
1588      * mute with the volume otherwise. */
1589     chain->mute =
1590         gst_play_sink_find_property_sinks (playsink, chain->sink, "mute");
1591     if (chain->mute) {
1592       GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
1593     }
1594   } else {
1595     /* no volume, we need to add a volume element when we can */
1596     GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
1597     if (!raw) {
1598       GST_LOG_OBJECT (playsink, "non-raw format, can't do soft volume control");
1599       chain->volume = NULL;
1600       chain->mute = NULL;
1601     } else {
1602       /* both last and current chain are raw audio, there should be a volume
1603        * element already, unless the sink changed from one with a volume
1604        * property to one that hasn't got a volume property, in which case we
1605        * re-generate the chain */
1606       if (chain->volume == NULL) {
1607         GST_DEBUG_OBJECT (playsink, "no existing volume element to re-use");
1608         return FALSE;
1609       }
1610
1611       GST_DEBUG_OBJECT (playsink, "reusing existing volume element");
1612     }
1613   }
1614   return TRUE;
1615 }
1616
1617 /*
1618  *  +-------------------------------------------------------------------+
1619  *  | visbin                                                            |
1620  *  |      +----------+   +------------+   +----------+   +-------+     |
1621  *  |      | visqueue |   | audioconv  |   | audiores |   |  vis  |     |
1622  *  |   +-sink       src-sink + samp  src-sink       src-sink    src-+  |
1623  *  |   |  +----------+   +------------+   +----------+   +-------+  |  |
1624  * sink-+                                                            +-src
1625  *  +-------------------------------------------------------------------+
1626  *           
1627  */
1628 static GstPlayVisChain *
1629 gen_vis_chain (GstPlaySink * playsink)
1630 {
1631   GstPlayVisChain *chain;
1632   GstBin *bin;
1633   gboolean res;
1634   GstPad *pad;
1635   GstElement *elem;
1636
1637   chain = g_new0 (GstPlayVisChain, 1);
1638   chain->chain.playsink = playsink;
1639
1640   GST_DEBUG_OBJECT (playsink, "making vis chain %p", chain);
1641
1642   chain->chain.bin = gst_bin_new ("visbin");
1643   bin = GST_BIN_CAST (chain->chain.bin);
1644   gst_object_ref (bin);
1645   gst_object_sink (bin);
1646
1647   /* we're queuing raw audio here, we can remove this queue when we can disable
1648    * async behaviour in the video sink. */
1649   chain->queue = gst_element_factory_make ("queue", "visqueue");
1650   gst_bin_add (bin, chain->queue);
1651
1652   chain->conv = gst_element_factory_make ("audioconvert", "aconv");
1653   if (chain->conv == NULL)
1654     goto no_audioconvert;
1655   gst_bin_add (bin, chain->conv);
1656
1657   chain->resample = gst_element_factory_make ("audioresample", "aresample");
1658   if (chain->resample == NULL)
1659     goto no_audioresample;
1660   gst_bin_add (bin, chain->resample);
1661
1662   /* this pad will be used for blocking the dataflow and switching the vis
1663    * plugin */
1664   chain->blockpad = gst_element_get_static_pad (chain->resample, "src");
1665
1666   if (playsink->visualisation) {
1667     GST_DEBUG_OBJECT (playsink, "trying configure vis");
1668     chain->vis = try_element (playsink, playsink->visualisation, FALSE);
1669   }
1670   if (chain->vis == NULL) {
1671     GST_DEBUG_OBJECT (playsink, "trying goom");
1672     elem = gst_element_factory_make ("goom", "vis");
1673     chain->vis = try_element (playsink, elem, TRUE);
1674   }
1675   if (chain->vis == NULL)
1676     goto no_goom;
1677
1678   gst_bin_add (bin, chain->vis);
1679
1680   res = gst_element_link_pads (chain->queue, "src", chain->conv, "sink");
1681   res &= gst_element_link_pads (chain->conv, "src", chain->resample, "sink");
1682   res &= gst_element_link_pads (chain->resample, "src", chain->vis, "sink");
1683   if (!res)
1684     goto link_failed;
1685
1686   chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
1687   chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
1688
1689   pad = gst_element_get_static_pad (chain->queue, "sink");
1690   chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1691   gst_object_unref (pad);
1692   gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1693
1694   chain->srcpad = gst_ghost_pad_new ("src", chain->vissrcpad);
1695   gst_element_add_pad (chain->chain.bin, chain->srcpad);
1696
1697   return chain;
1698
1699   /* ERRORS */
1700 no_audioconvert:
1701   {
1702     post_missing_element_message (playsink, "audioconvert");
1703     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1704         (_("Missing element '%s' - check your GStreamer installation."),
1705             "audioconvert"), ("possibly a liboil version mismatch?"));
1706     free_chain ((GstPlayChain *) chain);
1707     return NULL;
1708   }
1709 no_audioresample:
1710   {
1711     post_missing_element_message (playsink, "audioresample");
1712     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1713         (_("Missing element '%s' - check your GStreamer installation."),
1714             "audioresample"), (NULL));
1715     free_chain ((GstPlayChain *) chain);
1716     return NULL;
1717   }
1718 no_goom:
1719   {
1720     post_missing_element_message (playsink, "goom");
1721     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1722         (_("Missing element '%s' - check your GStreamer installation."),
1723             "goom"), (NULL));
1724     free_chain ((GstPlayChain *) chain);
1725     return NULL;
1726   }
1727 link_failed:
1728   {
1729     GST_ELEMENT_ERROR (playsink, CORE, PAD,
1730         (NULL), ("Failed to configure the visualisation element."));
1731     free_chain ((GstPlayChain *) chain);
1732     return NULL;
1733   }
1734 }
1735
1736 /* this function is called when all the request pads are requested and when we
1737  * have to construct the final pipeline. Based on the flags we construct the
1738  * final output pipelines.
1739  */
1740 gboolean
1741 gst_play_sink_reconfigure (GstPlaySink * playsink)
1742 {
1743   GstPlayFlags flags;
1744   gboolean need_audio, need_video, need_vis, need_text, need_subp;
1745
1746   GST_DEBUG_OBJECT (playsink, "reconfiguring");
1747
1748   /* assume we need nothing */
1749   need_audio = need_video = need_vis = need_text = need_subp = FALSE;
1750
1751   GST_PLAY_SINK_LOCK (playsink);
1752   GST_OBJECT_LOCK (playsink);
1753   /* get flags, there are protected with the object lock */
1754   flags = playsink->flags;
1755   GST_OBJECT_UNLOCK (playsink);
1756
1757   /* figure out which components we need */
1758   if (flags & GST_PLAY_FLAG_TEXT && (playsink->text_pad || playsink->subp_pad)) {
1759     /* we have a text_pad and we need text rendering, in this case we need a
1760      * video_pad to combine the video with the text */
1761     if (!playsink->video_pad)
1762       goto subs_but_no_video;
1763
1764     /* we have subtitles and we are requested to show it, we also need to show
1765      * video in this case. */
1766     need_video = TRUE;
1767     need_text = (playsink->text_pad != NULL);
1768     need_subp = (playsink->subp_pad != NULL);
1769
1770     /* we can't handle both of them yet */
1771     if (need_text && need_subp)
1772       goto subs_and_text;
1773   } else if (flags & GST_PLAY_FLAG_VIDEO && playsink->video_pad) {
1774     /* we have video and we are requested to show it */
1775     need_video = TRUE;
1776   }
1777   if (playsink->audio_pad) {
1778     if (flags & GST_PLAY_FLAG_AUDIO) {
1779       need_audio = TRUE;
1780     }
1781     if (playsink->audio_pad_raw) {
1782       /* only can do vis with raw uncompressed audio */
1783       if (flags & GST_PLAY_FLAG_VIS && !need_video) {
1784         /* also add video when we add visualisation */
1785         need_video = TRUE;
1786         need_vis = TRUE;
1787       }
1788     }
1789   }
1790
1791   /* set up video pipeline */
1792   if (need_video) {
1793     gboolean raw, async, queue;
1794
1795     /* we need a raw sink when we do vis or when we have a raw pad */
1796     raw = need_vis ? TRUE : playsink->video_pad_raw;
1797     /* we try to set the sink async=FALSE when we need vis, this way we can
1798      * avoid a queue in the audio chain. */
1799     async = !need_vis;
1800     /* put a little queue in front of the video but only if we are not doing
1801      * subpictures because then we will add the queue in front of the subpicture
1802      * mixer to minimize latency. */
1803     queue = (need_subp == FALSE);
1804
1805     GST_DEBUG_OBJECT (playsink, "adding video, raw %d",
1806         playsink->video_pad_raw);
1807
1808     if (playsink->videochain) {
1809       /* try to reactivate the chain */
1810       if (!setup_video_chain (playsink, raw, async, queue)) {
1811         add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
1812         activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
1813         free_chain ((GstPlayChain *) playsink->videochain);
1814         playsink->videochain = NULL;
1815       }
1816     }
1817
1818     if (!playsink->videochain) {
1819       playsink->videochain = gen_video_chain (playsink, raw, async, queue);
1820     }
1821     if (playsink->videochain) {
1822       GST_DEBUG_OBJECT (playsink, "adding video chain");
1823       add_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
1824       activate_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
1825       /* if we are not part of vis or subtitles, set the ghostpad target */
1826       if (!need_vis && !need_text && playsink->text_pad == NULL) {
1827         GST_DEBUG_OBJECT (playsink, "ghosting video sinkpad");
1828         gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad),
1829             playsink->videochain->sinkpad);
1830       }
1831     }
1832   } else {
1833     GST_DEBUG_OBJECT (playsink, "no video needed");
1834     if (playsink->videochain) {
1835       GST_DEBUG_OBJECT (playsink, "removing video chain");
1836       if (playsink->vischain) {
1837         GstPad *srcpad;
1838
1839         GST_DEBUG_OBJECT (playsink, "unlinking vis chain");
1840
1841         /* also had visualisation, release the tee srcpad before we then 
1842          * unlink the video from it */
1843         if (playsink->audio_tee_vissrc) {
1844           gst_element_release_request_pad (playsink->audio_tee,
1845               playsink->audio_tee_vissrc);
1846           gst_object_unref (playsink->audio_tee_vissrc);
1847           playsink->audio_tee_vissrc = NULL;
1848         }
1849         srcpad =
1850             gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
1851         gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
1852       }
1853       add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
1854       activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
1855     }
1856     if (playsink->video_pad)
1857       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
1858   }
1859
1860   if (need_text) {
1861     GST_DEBUG_OBJECT (playsink, "adding text");
1862     if (!playsink->textchain) {
1863       GST_DEBUG_OBJECT (playsink, "creating text chain");
1864       playsink->textchain = gen_text_chain (playsink);
1865     }
1866     if (playsink->textchain) {
1867       GST_DEBUG_OBJECT (playsink, "adding text chain");
1868       add_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
1869       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad),
1870           playsink->textchain->textsinkpad);
1871       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad),
1872           playsink->textchain->videosinkpad);
1873       gst_pad_link (playsink->textchain->srcpad, playsink->videochain->sinkpad);
1874       activate_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
1875       if (playsink->textchain->overlay)
1876         g_object_set (playsink->textchain->overlay, "silent", FALSE, NULL);
1877     }
1878   } else {
1879     GST_DEBUG_OBJECT (playsink, "no text needed");
1880     /* we have no subtitles/text or we are requested to not show them */
1881     if (playsink->textchain) {
1882       if (playsink->text_pad == NULL) {
1883         /* no text pad, remove the chain entirely */
1884         GST_DEBUG_OBJECT (playsink, "removing text chain");
1885         add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
1886         activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
1887       } else {
1888         /* we have a chain and a textpad, turn the subtitles off */
1889         GST_DEBUG_OBJECT (playsink, "turning off the text");
1890         if (playsink->textchain->overlay)
1891           g_object_set (playsink->textchain->overlay, "silent", TRUE, NULL);
1892       }
1893     }
1894     if (!need_video && playsink->video_pad)
1895       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
1896     if (playsink->text_pad)
1897       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad), NULL);
1898   }
1899
1900   if (need_subp && playsink->videochain) {
1901     GST_DEBUG_OBJECT (playsink, "adding subpicture");
1902     if (!playsink->subpchain) {
1903       GST_DEBUG_OBJECT (playsink, "creating subpicture chain");
1904       playsink->subpchain = gen_subp_chain (playsink);
1905     }
1906     if (playsink->subpchain) {
1907       GST_DEBUG_OBJECT (playsink, "adding subp chain");
1908       add_chain (GST_PLAY_CHAIN (playsink->subpchain), TRUE);
1909       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->subp_pad),
1910           playsink->subpchain->subpsinkpad);
1911       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad),
1912           playsink->subpchain->videosinkpad);
1913       gst_pad_link (playsink->subpchain->srcpad, playsink->videochain->sinkpad);
1914       activate_chain (GST_PLAY_CHAIN (playsink->subpchain), TRUE);
1915     }
1916   } else {
1917     GST_DEBUG_OBJECT (playsink, "no subpicture needed");
1918     /* we have no subpicture or we are requested to not show them */
1919     if (playsink->subpchain) {
1920       if (playsink->subp_pad == NULL) {
1921         /* no subpicture pad, remove the chain entirely */
1922         GST_DEBUG_OBJECT (playsink, "removing subp chain");
1923         add_chain (GST_PLAY_CHAIN (playsink->subpchain), FALSE);
1924         activate_chain (GST_PLAY_CHAIN (playsink->subpchain), FALSE);
1925       } else {
1926         /* we have a chain and a subpicture pad, turn the subtitles off */
1927         GST_DEBUG_OBJECT (playsink, "turning off the subp");
1928       }
1929     }
1930     if (!need_video && playsink->video_pad)
1931       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
1932     if (playsink->subp_pad)
1933       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->subp_pad), NULL);
1934   }
1935
1936   if (need_audio) {
1937     gboolean raw, queue;
1938
1939     GST_DEBUG_OBJECT (playsink, "adding audio");
1940
1941     /* get a raw sink if we are asked for a raw pad */
1942     raw = playsink->audio_pad_raw;
1943     if (need_vis && playsink->videochain) {
1944       /* If we are dealing with visualisations, we need to add a queue to
1945        * decouple the audio from the video part. We only have to do this when
1946        * the video part is async=true */
1947       queue = ((GstPlayVideoChain *) playsink->videochain)->async;
1948       GST_DEBUG_OBJECT (playsink, "need audio queue for vis: %d", queue);
1949     } else {
1950       /* no vis, we can avoid a queue */
1951       GST_DEBUG_OBJECT (playsink, "don't need audio queue");
1952       queue = FALSE;
1953     }
1954
1955     if (playsink->audiochain) {
1956       /* try to reactivate the chain */
1957       if (!setup_audio_chain (playsink, raw, queue)) {
1958         GST_DEBUG_OBJECT (playsink, "removing current audio chain");
1959         if (playsink->audio_tee_asrc) {
1960           gst_element_release_request_pad (playsink->audio_tee,
1961               playsink->audio_tee_asrc);
1962           gst_object_unref (playsink->audio_tee_asrc);
1963           playsink->audio_tee_asrc = NULL;
1964         }
1965         add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
1966         activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
1967         playsink->audiochain->volume = NULL;
1968         playsink->audiochain->mute = NULL;
1969         free_chain ((GstPlayChain *) playsink->audiochain);
1970         playsink->audiochain = NULL;
1971         playsink->volume_changed = playsink->mute_changed = FALSE;
1972       }
1973     }
1974
1975     if (!playsink->audiochain) {
1976       GST_DEBUG_OBJECT (playsink, "creating new audio chain");
1977       playsink->audiochain = gen_audio_chain (playsink, raw, queue);
1978     }
1979
1980     if (playsink->audiochain) {
1981       GST_DEBUG_OBJECT (playsink, "adding audio chain");
1982       if (playsink->audio_tee_asrc == NULL) {
1983         playsink->audio_tee_asrc =
1984             gst_element_get_request_pad (playsink->audio_tee, "src%d");
1985       }
1986       add_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
1987       activate_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
1988       gst_pad_link (playsink->audio_tee_asrc, playsink->audiochain->sinkpad);
1989     }
1990   } else {
1991     GST_DEBUG_OBJECT (playsink, "no audio needed");
1992     /* we have no audio or we are requested to not play audio */
1993     if (playsink->audiochain) {
1994       GST_DEBUG_OBJECT (playsink, "removing audio chain");
1995       /* release the audio pad */
1996       if (playsink->audio_tee_asrc) {
1997         gst_element_release_request_pad (playsink->audio_tee,
1998             playsink->audio_tee_asrc);
1999         gst_object_unref (playsink->audio_tee_asrc);
2000         playsink->audio_tee_asrc = NULL;
2001       }
2002       if (playsink->audiochain->sink_volume) {
2003         playsink->audiochain->volume = NULL;
2004         playsink->audiochain->mute = NULL;
2005       }
2006       add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2007       activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2008     }
2009   }
2010
2011   if (need_vis) {
2012     GstPad *srcpad;
2013
2014     if (!playsink->vischain)
2015       playsink->vischain = gen_vis_chain (playsink);
2016
2017     GST_DEBUG_OBJECT (playsink, "adding visualisation");
2018
2019     if (playsink->vischain) {
2020       GST_DEBUG_OBJECT (playsink, "setting up vis chain");
2021       srcpad =
2022           gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2023       add_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
2024       activate_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
2025       if (playsink->audio_tee_vissrc == NULL) {
2026         playsink->audio_tee_vissrc =
2027             gst_element_get_request_pad (playsink->audio_tee, "src%d");
2028       }
2029       gst_pad_link (playsink->audio_tee_vissrc, playsink->vischain->sinkpad);
2030       gst_pad_link (srcpad, playsink->videochain->sinkpad);
2031       gst_object_unref (srcpad);
2032     }
2033   } else {
2034     GST_DEBUG_OBJECT (playsink, "no vis needed");
2035     if (playsink->vischain) {
2036       if (playsink->audio_tee_vissrc) {
2037         gst_element_release_request_pad (playsink->audio_tee,
2038             playsink->audio_tee_vissrc);
2039         gst_object_unref (playsink->audio_tee_vissrc);
2040         playsink->audio_tee_vissrc = NULL;
2041       }
2042       GST_DEBUG_OBJECT (playsink, "removing vis chain");
2043       add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
2044       activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
2045     }
2046   }
2047   do_async_done (playsink);
2048   GST_PLAY_SINK_UNLOCK (playsink);
2049
2050   return TRUE;
2051
2052   /* ERRORS */
2053 subs_but_no_video:
2054   {
2055     GST_ELEMENT_ERROR (playsink, STREAM, FORMAT,
2056         (_("Can't play a text file without video.")),
2057         ("Have text pad but no video pad"));
2058     GST_PLAY_SINK_UNLOCK (playsink);
2059     return FALSE;
2060   }
2061 subs_and_text:
2062   {
2063     GST_ELEMENT_ERROR (playsink, STREAM, FORMAT,
2064         (_("Can't play a text subtitles and subpictures.")),
2065         ("Have text pad and subpicture pad"));
2066     GST_PLAY_SINK_UNLOCK (playsink);
2067     return FALSE;
2068   }
2069 }
2070
2071 /**
2072  * gst_play_sink_set_flags:
2073  * @playsink: a #GstPlaySink
2074  * @flags: #GstPlayFlags
2075  *
2076  * Configure @flags on @playsink. The flags control the behaviour of @playsink
2077  * when constructing the sink pipelins.
2078  *
2079  * Returns: TRUE if the flags could be configured.
2080  */
2081 gboolean
2082 gst_play_sink_set_flags (GstPlaySink * playsink, GstPlayFlags flags)
2083 {
2084   g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), FALSE);
2085
2086   GST_OBJECT_LOCK (playsink);
2087   playsink->flags = flags;
2088   GST_OBJECT_UNLOCK (playsink);
2089
2090   return TRUE;
2091 }
2092
2093 /**
2094  * gst_play_sink_get_flags:
2095  * @playsink: a #GstPlaySink
2096  *
2097  * Get the flags of @playsink. That flags control the behaviour of the sink when
2098  * it constructs the sink pipelines.
2099  *
2100  * Returns: the currently configured #GstPlayFlags.
2101  */
2102 GstPlayFlags
2103 gst_play_sink_get_flags (GstPlaySink * playsink)
2104 {
2105   GstPlayFlags res;
2106
2107   g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), 0);
2108
2109   GST_OBJECT_LOCK (playsink);
2110   res = playsink->flags;
2111   GST_OBJECT_UNLOCK (playsink);
2112
2113   return res;
2114 }
2115
2116 void
2117 gst_play_sink_set_font_desc (GstPlaySink * playsink, const gchar * desc)
2118 {
2119   GstPlayTextChain *chain;
2120
2121   GST_PLAY_SINK_LOCK (playsink);
2122   chain = (GstPlayTextChain *) playsink->textchain;
2123   g_free (playsink->font_desc);
2124   playsink->font_desc = g_strdup (desc);
2125   if (chain && chain->overlay) {
2126     g_object_set (chain->overlay, "font-desc", desc, NULL);
2127   }
2128   GST_PLAY_SINK_UNLOCK (playsink);
2129 }
2130
2131 gchar *
2132 gst_play_sink_get_font_desc (GstPlaySink * playsink)
2133 {
2134   gchar *result = NULL;
2135   GstPlayTextChain *chain;
2136
2137   GST_PLAY_SINK_LOCK (playsink);
2138   chain = (GstPlayTextChain *) playsink->textchain;
2139   if (chain && chain->overlay) {
2140     g_object_get (chain->overlay, "font-desc", &result, NULL);
2141     playsink->font_desc = g_strdup (result);
2142   } else {
2143     result = g_strdup (playsink->font_desc);
2144   }
2145   GST_PLAY_SINK_UNLOCK (playsink);
2146
2147   return result;
2148 }
2149
2150 /**
2151  * gst_play_sink_get_last_frame:
2152  * @playsink: a #GstPlaySink
2153  *
2154  * Get the last displayed frame from @playsink. This frame is in the native
2155  * format of the sink element, the caps on the result buffer contain the format
2156  * of the frame data.
2157  *
2158  * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
2159  * available.
2160  */
2161 GstBuffer *
2162 gst_play_sink_get_last_frame (GstPlaySink * playsink)
2163 {
2164   GstBuffer *result = NULL;
2165   GstPlayVideoChain *chain;
2166
2167   GST_PLAY_SINK_LOCK (playsink);
2168   GST_DEBUG_OBJECT (playsink, "taking last frame");
2169   /* get the video chain if we can */
2170   if ((chain = (GstPlayVideoChain *) playsink->videochain)) {
2171     GST_DEBUG_OBJECT (playsink, "found video chain");
2172     /* see if the chain is active */
2173     if (chain->chain.activated && chain->sink) {
2174       GstElement *elem;
2175
2176       GST_DEBUG_OBJECT (playsink, "video chain active and has a sink");
2177
2178       /* find and get the last-buffer property now */
2179       if ((elem =
2180               gst_play_sink_find_property (playsink, chain->sink,
2181                   "last-buffer"))) {
2182         GST_DEBUG_OBJECT (playsink, "getting last-buffer property");
2183         g_object_get (elem, "last-buffer", &result, NULL);
2184         gst_object_unref (elem);
2185       }
2186     }
2187   }
2188   GST_PLAY_SINK_UNLOCK (playsink);
2189
2190   return result;
2191 }
2192
2193 /**
2194  * gst_play_sink_request_pad
2195  * @playsink: a #GstPlaySink
2196  * @type: a #GstPlaySinkType
2197  *
2198  * Create or return a pad of @type.
2199  *
2200  * Returns: a #GstPad of @type or %NULL when the pad could not be created.
2201  */
2202 GstPad *
2203 gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type)
2204 {
2205   GstPad *res = NULL;
2206   gboolean created = FALSE;
2207   gboolean raw = FALSE;
2208   gboolean activate = TRUE;
2209
2210   GST_DEBUG_OBJECT (playsink, "request pad type %d", type);
2211
2212   GST_PLAY_SINK_LOCK (playsink);
2213   switch (type) {
2214     case GST_PLAY_SINK_TYPE_AUDIO_RAW:
2215       raw = TRUE;
2216     case GST_PLAY_SINK_TYPE_AUDIO:
2217       if (!playsink->audio_tee) {
2218         GST_LOG_OBJECT (playsink, "creating tee");
2219         /* create tee when needed. This element will feed the audio sink chain
2220          * and the vis chain. */
2221         playsink->audio_tee = gst_element_factory_make ("tee", "audiotee");
2222         playsink->audio_tee_sink =
2223             gst_element_get_static_pad (playsink->audio_tee, "sink");
2224         gst_bin_add (GST_BIN_CAST (playsink), playsink->audio_tee);
2225         gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
2226       } else {
2227         gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
2228       }
2229       if (!playsink->audio_pad) {
2230         GST_LOG_OBJECT (playsink, "ghosting tee sinkpad");
2231         playsink->audio_pad =
2232             gst_ghost_pad_new ("audio_sink", playsink->audio_tee_sink);
2233         created = TRUE;
2234       }
2235       playsink->audio_pad_raw = raw;
2236       res = playsink->audio_pad;
2237       break;
2238     case GST_PLAY_SINK_TYPE_VIDEO_RAW:
2239       raw = TRUE;
2240     case GST_PLAY_SINK_TYPE_VIDEO:
2241       if (!playsink->video_pad) {
2242         GST_LOG_OBJECT (playsink, "ghosting videosink");
2243         playsink->video_pad =
2244             gst_ghost_pad_new_no_target ("video_sink", GST_PAD_SINK);
2245         created = TRUE;
2246       }
2247       playsink->video_pad_raw = raw;
2248       res = playsink->video_pad;
2249       break;
2250     case GST_PLAY_SINK_TYPE_TEXT:
2251       GST_LOG_OBJECT (playsink, "ghosting text");
2252       if (!playsink->text_pad) {
2253         playsink->text_pad =
2254             gst_ghost_pad_new_no_target ("text_sink", GST_PAD_SINK);
2255         created = TRUE;
2256       }
2257       res = playsink->text_pad;
2258       break;
2259     case GST_PLAY_SINK_TYPE_FLUSHING:
2260     {
2261       gchar *padname;
2262
2263       /* we need a unique padname for the flushing pad. */
2264       padname = g_strdup_printf ("flushing_%d", playsink->count);
2265       res = gst_ghost_pad_new_no_target (padname, GST_PAD_SINK);
2266       g_free (padname);
2267       playsink->count++;
2268       activate = FALSE;
2269       created = TRUE;
2270       break;
2271     }
2272     case GST_PLAY_SINK_TYPE_SUBPIC:
2273       GST_LOG_OBJECT (playsink, "ghosting subpicture pad");
2274       if (!playsink->subp_pad) {
2275         playsink->subp_pad =
2276             gst_ghost_pad_new_no_target ("subp_sink", GST_PAD_SINK);
2277         created = TRUE;
2278       }
2279       res = playsink->subp_pad;
2280       break;
2281     default:
2282       res = NULL;
2283       break;
2284   }
2285   GST_PLAY_SINK_UNLOCK (playsink);
2286
2287   if (created && res) {
2288     /* we have to add the pad when it's active or we get an error when the
2289      * element is 'running' */
2290     gst_pad_set_active (res, TRUE);
2291     gst_element_add_pad (GST_ELEMENT_CAST (playsink), res);
2292     if (!activate)
2293       gst_pad_set_active (res, activate);
2294   }
2295
2296   return res;
2297 }
2298
2299 void
2300 gst_play_sink_release_pad (GstPlaySink * playsink, GstPad * pad)
2301 {
2302   GstPad **res = NULL;
2303   gboolean untarget = TRUE;
2304
2305   GST_DEBUG_OBJECT (playsink, "release pad %" GST_PTR_FORMAT, pad);
2306
2307   GST_PLAY_SINK_LOCK (playsink);
2308   if (pad == playsink->video_pad) {
2309     res = &playsink->video_pad;
2310   } else if (pad == playsink->audio_pad) {
2311     res = &playsink->audio_pad;
2312   } else if (pad == playsink->text_pad) {
2313     res = &playsink->text_pad;
2314   } else if (pad == playsink->subp_pad) {
2315     res = &playsink->subp_pad;
2316   } else {
2317     /* try to release the given pad anyway, these could be the FLUSHING pads. */
2318     res = &pad;
2319     untarget = FALSE;
2320   }
2321   GST_PLAY_SINK_UNLOCK (playsink);
2322
2323   if (*res) {
2324     GST_DEBUG_OBJECT (playsink, "deactivate pad %" GST_PTR_FORMAT, *res);
2325     gst_pad_set_active (*res, FALSE);
2326     if (untarget) {
2327       GST_DEBUG_OBJECT (playsink, "untargeting pad %" GST_PTR_FORMAT, *res);
2328       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (*res), NULL);
2329     }
2330     GST_DEBUG_OBJECT (playsink, "remove pad %" GST_PTR_FORMAT, *res);
2331     gst_element_remove_pad (GST_ELEMENT_CAST (playsink), *res);
2332     *res = NULL;
2333   }
2334 }
2335
2336 static void
2337 gst_play_sink_handle_message (GstBin * bin, GstMessage * message)
2338 {
2339   GstPlaySink *playsink;
2340
2341   playsink = GST_PLAY_SINK_CAST (bin);
2342
2343   switch (GST_MESSAGE_TYPE (message)) {
2344     case GST_MESSAGE_STEP_DONE:
2345     {
2346       GstFormat format;
2347       guint64 amount;
2348       gdouble rate;
2349       gboolean flush, intermediate, eos;
2350       guint64 duration;
2351
2352       GST_INFO_OBJECT (playsink, "Handling step-done message");
2353       gst_message_parse_step_done (message, &format, &amount, &rate, &flush,
2354           &intermediate, &duration, &eos);
2355
2356       if (format == GST_FORMAT_BUFFERS) {
2357         /* for the buffer format, we align the other streams */
2358         if (playsink->audiochain) {
2359           GstEvent *event;
2360
2361           event =
2362               gst_event_new_step (GST_FORMAT_TIME, duration, rate, flush,
2363               intermediate);
2364
2365           if (!gst_element_send_event (playsink->audiochain->chain.bin, event)) {
2366             GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
2367           }
2368         }
2369       }
2370       GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
2371       break;
2372     }
2373     default:
2374       GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
2375       break;
2376   }
2377 }
2378
2379 /* Send an event to our sinks until one of them works; don't then send to the
2380  * remaining sinks (unlike GstBin)
2381  */
2382 static gboolean
2383 gst_play_sink_send_event_to_sink (GstPlaySink * playsink, GstEvent * event)
2384 {
2385   gboolean res = TRUE;
2386
2387   if (playsink->videochain) {
2388     gst_event_ref (event);
2389     if ((res = gst_element_send_event (playsink->videochain->chain.bin, event))) {
2390       GST_DEBUG_OBJECT (playsink, "Sent event succesfully to video sink");
2391       goto done;
2392     }
2393     GST_DEBUG_OBJECT (playsink, "Event failed when sent to video sink");
2394   }
2395   if (playsink->audiochain) {
2396     gst_event_ref (event);
2397     if ((res = gst_element_send_event (playsink->audiochain->chain.bin, event))) {
2398       GST_DEBUG_OBJECT (playsink, "Sent event succesfully to audio sink");
2399       goto done;
2400     }
2401     GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
2402   }
2403 done:
2404   gst_event_unref (event);
2405   return res;
2406 }
2407
2408 /* We only want to send the event to a single sink (overriding GstBin's
2409  * behaviour), but we want to keep GstPipeline's behaviour - wrapping seek
2410  * events appropriately. So, this is a messy duplication of code. */
2411 static gboolean
2412 gst_play_sink_send_event (GstElement * element, GstEvent * event)
2413 {
2414   gboolean res = FALSE;
2415   GstEventType event_type = GST_EVENT_TYPE (event);
2416   GstPlaySink *playsink;
2417
2418   playsink = GST_PLAY_SINK_CAST (element);
2419
2420   switch (event_type) {
2421     case GST_EVENT_SEEK:
2422       GST_DEBUG_OBJECT (element, "Sending event to a sink");
2423       res = gst_play_sink_send_event_to_sink (playsink, event);
2424       break;
2425     case GST_EVENT_STEP:
2426     {
2427       GstFormat format;
2428       guint64 amount;
2429       gdouble rate;
2430       gboolean flush, intermediate;
2431
2432       gst_event_parse_step (event, &format, &amount, &rate, &flush,
2433           &intermediate);
2434
2435       if (format == GST_FORMAT_BUFFERS) {
2436         /* for buffers, we will try to step video frames, for other formats we
2437          * send the step to all sinks */
2438         res = gst_play_sink_send_event_to_sink (playsink, event);
2439       } else {
2440         res =
2441             GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
2442             event);
2443       }
2444       break;
2445     }
2446     default:
2447       res =
2448           GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
2449           event);
2450       break;
2451   }
2452   return res;
2453 }
2454
2455 static GstStateChangeReturn
2456 gst_play_sink_change_state (GstElement * element, GstStateChange transition)
2457 {
2458   GstStateChangeReturn ret;
2459   GstStateChangeReturn bret;
2460
2461   GstPlaySink *playsink;
2462
2463   playsink = GST_PLAY_SINK (element);
2464
2465   switch (transition) {
2466     case GST_STATE_CHANGE_READY_TO_PAUSED:
2467       /* we want to go async to PAUSED until we managed to configure and add the
2468        * sinks */
2469       do_async_start (playsink);
2470       ret = GST_STATE_CHANGE_ASYNC;
2471       break;
2472     case GST_STATE_CHANGE_PAUSED_TO_READY:
2473     case GST_STATE_CHANGE_READY_TO_NULL:
2474       if (playsink->audiochain && playsink->audiochain->sink_volume) {
2475         /* remove our links to the mute and volume elements when they were
2476          * provided by a sink */
2477         playsink->audiochain->volume = NULL;
2478         playsink->audiochain->mute = NULL;
2479       }
2480       ret = GST_STATE_CHANGE_SUCCESS;
2481       break;
2482     default:
2483       /* all other state changes return SUCCESS by default, this value can be
2484        * overridden by the result of the children */
2485       ret = GST_STATE_CHANGE_SUCCESS;
2486       break;
2487   }
2488
2489   /* do the state change of the children */
2490   bret =
2491       GST_ELEMENT_CLASS (gst_play_sink_parent_class)->change_state (element,
2492       transition);
2493   /* now look at the result of our children and adjust the return value */
2494   switch (bret) {
2495     case GST_STATE_CHANGE_FAILURE:
2496       /* failure, we stop */
2497       goto activate_failed;
2498     case GST_STATE_CHANGE_NO_PREROLL:
2499       /* some child returned NO_PREROLL. This is strange but we never know. We
2500        * commit our async state change (if any) and return the NO_PREROLL */
2501       do_async_done (playsink);
2502       ret = bret;
2503       break;
2504     case GST_STATE_CHANGE_ASYNC:
2505       /* some child was async, return this */
2506       ret = bret;
2507       break;
2508     default:
2509       /* return our previously configured return value */
2510       break;
2511   }
2512
2513   switch (transition) {
2514     case GST_STATE_CHANGE_READY_TO_PAUSED:
2515       break;
2516     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
2517       /* FIXME Release audio device when we implement that */
2518       playsink->need_async_start = TRUE;
2519       break;
2520     case GST_STATE_CHANGE_PAUSED_TO_READY:
2521     case GST_STATE_CHANGE_READY_TO_NULL:
2522       /* remove sinks we added */
2523       if (playsink->videochain) {
2524         activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2525         add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2526       }
2527       if (playsink->audiochain) {
2528         activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2529         add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2530       }
2531       if (playsink->vischain) {
2532         activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
2533         add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
2534       }
2535       if (playsink->textchain) {
2536         activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
2537         add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
2538       }
2539       if (playsink->subpchain) {
2540         activate_chain (GST_PLAY_CHAIN (playsink->subpchain), FALSE);
2541         add_chain (GST_PLAY_CHAIN (playsink->subpchain), FALSE);
2542       }
2543       do_async_done (playsink);
2544       break;
2545     default:
2546       break;
2547   }
2548   return ret;
2549
2550   /* ERRORS */
2551 activate_failed:
2552   {
2553     GST_DEBUG_OBJECT (element,
2554         "element failed to change states -- activation problem?");
2555     return GST_STATE_CHANGE_FAILURE;
2556   }
2557 }