gst/playback/gstplay-enum.*: Add enums for configuration flags.
[platform/upstream/gst-plugins-base.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 #define CONNECTION_SPEED_DEFAULT 0
37
38 /* holds the common data fields for the audio and video pipelines. We keep them
39  * in a structure to more easily have all the info available. */
40 typedef struct
41 {
42   GstPlaySink *playsink;
43   GstPad *sinkpad;
44   GstElement *bin;
45   gboolean added;
46   gboolean activated;
47 } GstPlayChain;
48
49 typedef struct
50 {
51   GstPlayChain chain;
52   GstElement *tee;
53   GstPad *teesrc;
54   GstElement *conv;
55   GstElement *resample;
56   GstElement *volume;
57   GstElement *sink;
58 } GstPlayAudioChain;
59
60 typedef struct
61 {
62   GstPlayChain chain;
63   GstElement *conv;
64   GstElement *queue;
65   GstElement *scale;
66   GstElement *sink;
67 } GstPlayVideoChain;
68
69 typedef struct
70 {
71   GstPlayChain chain;
72   GstPad *teesrc;
73   GstElement *queue;
74   GstElement *conv;
75   GstElement *resample;
76   GstElement *vis;
77   GstPad *srcpad;
78 } GstPlayVisChain;
79
80 #define GST_PLAY_SINK_GET_LOCK(playsink) (((GstPlaySink *)playsink)->lock)
81 #define GST_PLAY_SINK_LOCK(playsink)     g_mutex_lock (GST_PLAY_SINK_GET_LOCK (playsink))
82 #define GST_PLAY_SINK_UNLOCK(playsink)   g_mutex_unlock (GST_PLAY_SINK_GET_LOCK (playsink))
83
84 struct _GstPlaySink
85 {
86   GstBin bin;
87
88   GMutex *lock;
89
90   GstPlayFlags flags;
91
92   GstPlayChain *audiochain;
93   GstPlayChain *videochain;
94   GstPlayChain *vischain;
95
96   GstPad *audio_pad;
97   gboolean audio_pad_raw;
98   GstPad *video_pad;
99   gboolean video_pad_raw;
100   GstPad *text_pad;
101
102   /* properties */
103   GstElement *audio_sink;
104   GstElement *video_sink;
105   GstElement *visualisation;
106   gfloat volume;
107   gchar *font_desc;             /* font description */
108   guint connection_speed;       /* connection speed in bits/sec (0 = unknown) */
109
110   /* internal elements */
111   GstElement *textoverlay_element;
112
113   GstElement *pending_visualisation;
114   GstElement *fakesink;
115 };
116
117 struct _GstPlaySinkClass
118 {
119   GstBinClass parent_class;
120 };
121
122
123 /* props */
124 enum
125 {
126   PROP_0,
127   PROP_AUDIO_SINK,
128   PROP_VIDEO_SINK,
129   PROP_VIS_PLUGIN,
130   PROP_VOLUME,
131   PROP_FRAME,
132   PROP_FONT_DESC,
133   PROP_LAST
134 };
135
136 /* signals */
137 enum
138 {
139   LAST_SIGNAL
140 };
141
142 static void gst_play_sink_class_init (GstPlaySinkClass * klass);
143 static void gst_play_sink_init (GstPlaySink * playsink);
144 static void gst_play_sink_dispose (GObject * object);
145 static void gst_play_sink_finalize (GObject * object);
146
147 static void gst_play_sink_set_property (GObject * object, guint prop_id,
148     const GValue * value, GParamSpec * spec);
149 static void gst_play_sink_get_property (GObject * object, guint prop_id,
150     GValue * value, GParamSpec * spec);
151
152 static gboolean gst_play_sink_send_event (GstElement * element,
153     GstEvent * event);
154 static GstStateChangeReturn gst_play_sink_change_state (GstElement * element,
155     GstStateChange transition);
156
157 static GstElementClass *parent_class;
158
159 /* static guint gst_play_sink_signals[LAST_SIGNAL] = { 0 }; */
160
161 static const GstElementDetails gst_play_sink_details =
162 GST_ELEMENT_DETAILS ("Player Sink",
163     "Generic/Bin/Player",
164     "Autoplug and play media from an uri",
165     "Wim Taymans <wim.taymans@gmail.com>");
166
167 GType
168 gst_play_sink_get_type (void)
169 {
170   static GType gst_play_sink_type = 0;
171
172   if (!gst_play_sink_type) {
173     static const GTypeInfo gst_play_sink_info = {
174       sizeof (GstPlaySinkClass),
175       NULL,
176       NULL,
177       (GClassInitFunc) gst_play_sink_class_init,
178       NULL,
179       NULL,
180       sizeof (GstPlaySink),
181       0,
182       (GInstanceInitFunc) gst_play_sink_init,
183       NULL
184     };
185
186     gst_play_sink_type = g_type_register_static (GST_TYPE_BIN,
187         "GstPlaySink", &gst_play_sink_info, 0);
188   }
189
190   return gst_play_sink_type;
191 }
192
193 static void
194 gst_play_sink_class_init (GstPlaySinkClass * klass)
195 {
196   GObjectClass *gobject_klass;
197   GstElementClass *gstelement_klass;
198   GstBinClass *gstbin_klass;
199
200   gobject_klass = (GObjectClass *) klass;
201   gstelement_klass = (GstElementClass *) klass;
202   gstbin_klass = (GstBinClass *) klass;
203
204   parent_class = g_type_class_peek_parent (klass);
205
206   gobject_klass->set_property = gst_play_sink_set_property;
207   gobject_klass->get_property = gst_play_sink_get_property;
208
209   gobject_klass->dispose = GST_DEBUG_FUNCPTR (gst_play_sink_dispose);
210   gobject_klass->finalize = GST_DEBUG_FUNCPTR (gst_play_sink_finalize);
211
212   g_object_class_install_property (gobject_klass, PROP_VIDEO_SINK,
213       g_param_spec_object ("video-sink", "Video Sink",
214           "the video output element to use (NULL = default sink)",
215           GST_TYPE_ELEMENT, G_PARAM_READWRITE));
216   g_object_class_install_property (gobject_klass, PROP_AUDIO_SINK,
217       g_param_spec_object ("audio-sink", "Audio Sink",
218           "the audio output element to use (NULL = default sink)",
219           GST_TYPE_ELEMENT, G_PARAM_READWRITE));
220   g_object_class_install_property (gobject_klass, PROP_VIS_PLUGIN,
221       g_param_spec_object ("vis-plugin", "Vis plugin",
222           "the visualization element to use (NULL = none)",
223           GST_TYPE_ELEMENT, G_PARAM_READWRITE));
224   g_object_class_install_property (gobject_klass, PROP_VOLUME,
225       g_param_spec_double ("volume", "volume", "volume",
226           0.0, VOLUME_MAX_DOUBLE, 1.0, G_PARAM_READWRITE));
227   g_object_class_install_property (gobject_klass, PROP_FRAME,
228       gst_param_spec_mini_object ("frame", "Frame",
229           "The last frame (NULL = no video available)",
230           GST_TYPE_BUFFER, G_PARAM_READABLE));
231   g_object_class_install_property (gobject_klass, PROP_FONT_DESC,
232       g_param_spec_string ("subtitle-font-desc",
233           "Subtitle font description",
234           "Pango font description of font "
235           "to be used for subtitle rendering", NULL, G_PARAM_WRITABLE));
236
237   gst_element_class_set_details (gstelement_klass, &gst_play_sink_details);
238
239   gstelement_klass->change_state =
240       GST_DEBUG_FUNCPTR (gst_play_sink_change_state);
241   gstelement_klass->send_event = GST_DEBUG_FUNCPTR (gst_play_sink_send_event);
242
243   GST_DEBUG_CATEGORY_INIT (gst_play_sink_debug, "playsink", 0, "play bin");
244 }
245
246 static void
247 gst_play_sink_init (GstPlaySink * playsink)
248 {
249   /* init groups */
250   playsink->video_sink = NULL;
251   playsink->audio_sink = NULL;
252   playsink->visualisation = NULL;
253   playsink->pending_visualisation = NULL;
254   playsink->textoverlay_element = NULL;
255   playsink->volume = 1.0;
256   playsink->font_desc = NULL;
257   playsink->flags = GST_PLAY_FLAG_SOFT_VOLUME;
258
259   playsink->lock = g_mutex_new ();
260 }
261
262 static void
263 gst_play_sink_dispose (GObject * object)
264 {
265   GstPlaySink *playsink;
266
267   playsink = GST_PLAY_SINK (object);
268
269   if (playsink->audio_sink != NULL) {
270     gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
271     gst_object_unref (playsink->audio_sink);
272     playsink->audio_sink = NULL;
273   }
274   if (playsink->video_sink != NULL) {
275     gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
276     gst_object_unref (playsink->video_sink);
277     playsink->video_sink = NULL;
278   }
279   if (playsink->visualisation != NULL) {
280     gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
281     gst_object_unref (playsink->visualisation);
282     playsink->visualisation = NULL;
283   }
284   if (playsink->pending_visualisation != NULL) {
285     gst_element_set_state (playsink->pending_visualisation, GST_STATE_NULL);
286     gst_object_unref (playsink->pending_visualisation);
287     playsink->pending_visualisation = NULL;
288   }
289   if (playsink->textoverlay_element != NULL) {
290     gst_object_unref (playsink->textoverlay_element);
291     playsink->textoverlay_element = NULL;
292   }
293   g_free (playsink->font_desc);
294   playsink->font_desc = NULL;
295
296   G_OBJECT_CLASS (parent_class)->dispose (object);
297 }
298
299 static void
300 gst_play_sink_finalize (GObject * object)
301 {
302   GstPlaySink *playsink;
303
304   playsink = GST_PLAY_SINK (object);
305
306   g_mutex_free (playsink->lock);
307
308   G_OBJECT_CLASS (parent_class)->finalize (object);
309 }
310
311 static void
312 gst_play_sink_vis_unblocked (GstPad * tee_pad, gboolean blocked,
313     gpointer user_data)
314 {
315   GstPlaySink *playsink = GST_PLAY_SINK (user_data);
316
317   if (playsink->pending_visualisation)
318     gst_pad_set_blocked_async (tee_pad, FALSE, gst_play_sink_vis_unblocked,
319         playsink);
320 }
321
322 static void
323 gst_play_sink_vis_blocked (GstPad * tee_pad, gboolean blocked,
324     gpointer user_data)
325 {
326   GstPlaySink *playsink = GST_PLAY_SINK (user_data);
327   GstBin *vis_bin = NULL;
328   GstPad *vis_sink_pad = NULL, *vis_src_pad = NULL, *vqueue_pad = NULL;
329   GstState bin_state;
330   GstElement *pending_visualisation;
331
332   GST_OBJECT_LOCK (playsink);
333   pending_visualisation = playsink->pending_visualisation;
334   playsink->pending_visualisation = NULL;
335   GST_OBJECT_UNLOCK (playsink);
336
337   /* We want to disable visualisation */
338   if (!GST_IS_ELEMENT (pending_visualisation)) {
339     /* Set visualisation element to READY */
340     gst_element_set_state (playsink->visualisation, GST_STATE_READY);
341     goto beach;
342   }
343
344   vis_bin =
345       GST_BIN_CAST (gst_object_get_parent (GST_OBJECT_CAST (playsink->
346               visualisation)));
347
348   if (!GST_IS_BIN (vis_bin) || !GST_IS_PAD (tee_pad)) {
349     goto beach;
350   }
351
352   vis_src_pad = gst_element_get_pad (playsink->visualisation, "src");
353   vis_sink_pad = gst_pad_get_peer (tee_pad);
354
355   /* Can be fakesink */
356   if (GST_IS_PAD (vis_src_pad)) {
357     vqueue_pad = gst_pad_get_peer (vis_src_pad);
358   }
359
360   if (!GST_IS_PAD (vis_sink_pad)) {
361     goto beach;
362   }
363
364   /* Check the bin's state */
365   GST_OBJECT_LOCK (vis_bin);
366   bin_state = GST_STATE (vis_bin);
367   GST_OBJECT_UNLOCK (vis_bin);
368
369   /* Unlink */
370   gst_pad_unlink (tee_pad, vis_sink_pad);
371   gst_object_unref (vis_sink_pad);
372   vis_sink_pad = NULL;
373
374   if (GST_IS_PAD (vqueue_pad)) {
375     gst_pad_unlink (vis_src_pad, vqueue_pad);
376     gst_object_unref (vis_src_pad);
377     vis_src_pad = NULL;
378   }
379
380   /* Remove from vis_bin */
381   gst_bin_remove (vis_bin, playsink->visualisation);
382   /* Set state to NULL */
383   gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
384   /* And loose our ref */
385   gst_object_unref (playsink->visualisation);
386
387   if (pending_visualisation) {
388     /* Ref this new visualisation element before adding to the bin */
389     gst_object_ref (pending_visualisation);
390     /* Add the new one */
391     gst_bin_add (vis_bin, pending_visualisation);
392     /* Synchronizing state */
393     gst_element_set_state (pending_visualisation, bin_state);
394
395     vis_sink_pad = gst_element_get_pad (pending_visualisation, "sink");
396     vis_src_pad = gst_element_get_pad (pending_visualisation, "src");
397
398     if (!GST_IS_PAD (vis_sink_pad) || !GST_IS_PAD (vis_src_pad)) {
399       goto beach;
400     }
401
402     /* Link */
403     gst_pad_link (tee_pad, vis_sink_pad);
404     gst_pad_link (vis_src_pad, vqueue_pad);
405   }
406
407   /* We are done */
408   gst_object_unref (playsink->visualisation);
409   playsink->visualisation = pending_visualisation;
410
411 beach:
412   if (vis_sink_pad) {
413     gst_object_unref (vis_sink_pad);
414   }
415   if (vis_src_pad) {
416     gst_object_unref (vis_src_pad);
417   }
418   if (vqueue_pad) {
419     gst_object_unref (vqueue_pad);
420   }
421   if (vis_bin) {
422     gst_object_unref (vis_bin);
423   }
424
425   /* Unblock the pad */
426   gst_pad_set_blocked_async (tee_pad, FALSE, gst_play_sink_vis_unblocked,
427       playsink);
428 }
429
430 void
431 gst_play_sink_set_video_sink (GstPlaySink * playsink, GstElement * sink)
432 {
433   GST_OBJECT_LOCK (playsink);
434   if (playsink->video_sink)
435     gst_object_unref (playsink->video_sink);
436
437   if (sink) {
438     gst_object_ref (sink);
439     gst_object_sink (sink);
440   }
441   playsink->video_sink = sink;
442   GST_OBJECT_UNLOCK (playsink);
443 }
444
445 void
446 gst_play_sink_set_audio_sink (GstPlaySink * playsink, GstElement * sink)
447 {
448   GST_OBJECT_LOCK (playsink);
449   if (playsink->audio_sink)
450     gst_object_unref (playsink->audio_sink);
451
452   if (sink) {
453     gst_object_ref (sink);
454     gst_object_sink (sink);
455   }
456   playsink->audio_sink = sink;
457   GST_OBJECT_UNLOCK (playsink);
458 }
459
460 void
461 gst_play_sink_set_vis_plugin (GstPlaySink * playsink,
462     GstElement * pending_visualisation)
463 {
464   /* Take ownership */
465   if (pending_visualisation) {
466     gst_object_ref (pending_visualisation);
467     gst_object_sink (pending_visualisation);
468   }
469
470   /* Do we already have a visualisation change pending? If yes, change the
471    * pending vis with the new one. */
472   GST_OBJECT_LOCK (playsink);
473   if (playsink->pending_visualisation) {
474     gst_object_unref (playsink->pending_visualisation);
475     playsink->pending_visualisation = pending_visualisation;
476     GST_OBJECT_UNLOCK (playsink);
477   } else {
478     GST_OBJECT_UNLOCK (playsink);
479     /* Was there a visualisation already set ? */
480     if (playsink->visualisation != NULL) {
481       GstBin *vis_bin = NULL;
482
483       vis_bin =
484           GST_BIN_CAST (gst_object_get_parent (GST_OBJECT_CAST (playsink->
485                   visualisation)));
486
487       /* Check if the visualisation is already in a bin */
488       if (GST_IS_BIN (vis_bin)) {
489         GstPad *vis_sink_pad = NULL, *tee_pad = NULL;
490
491         /* Now get tee pad and block it async */
492         vis_sink_pad = gst_element_get_pad (playsink->visualisation, "sink");
493         if (!GST_IS_PAD (vis_sink_pad)) {
494           goto beach;
495         }
496         tee_pad = gst_pad_get_peer (vis_sink_pad);
497         if (!GST_IS_PAD (tee_pad)) {
498           goto beach;
499         }
500
501         playsink->pending_visualisation = pending_visualisation;
502         /* Block with callback */
503         gst_pad_set_blocked_async (tee_pad, TRUE, gst_play_sink_vis_blocked,
504             playsink);
505       beach:
506         if (vis_sink_pad) {
507           gst_object_unref (vis_sink_pad);
508         }
509         if (tee_pad) {
510           gst_object_unref (tee_pad);
511         }
512         gst_object_unref (vis_bin);
513       } else {
514         playsink->visualisation = pending_visualisation;
515       }
516     } else {
517       playsink->visualisation = pending_visualisation;
518     }
519   }
520 }
521
522 static void
523 gst_play_sink_set_property (GObject * object, guint prop_id,
524     const GValue * value, GParamSpec * pspec)
525 {
526   GstPlaySink *playsink;
527
528   playsink = GST_PLAY_SINK (object);
529
530   switch (prop_id) {
531     case PROP_VIDEO_SINK:
532       gst_play_sink_set_video_sink (playsink, g_value_get_object (value));
533       break;
534     case PROP_AUDIO_SINK:
535       gst_play_sink_set_audio_sink (playsink, g_value_get_object (value));
536       break;
537     case PROP_VIS_PLUGIN:
538       gst_play_sink_set_vis_plugin (playsink, g_value_get_object (value));
539       break;
540     case PROP_VOLUME:
541       GST_OBJECT_LOCK (playsink);
542       playsink->volume = g_value_get_double (value);
543       GST_OBJECT_UNLOCK (playsink);
544       break;
545     case PROP_FONT_DESC:
546       GST_OBJECT_LOCK (playsink);
547       g_free (playsink->font_desc);
548       playsink->font_desc = g_strdup (g_value_get_string (value));
549       if (playsink->textoverlay_element) {
550         g_object_set (G_OBJECT (playsink->textoverlay_element),
551             "font-desc", g_value_get_string (value), NULL);
552       }
553       GST_OBJECT_UNLOCK (playsink);
554       break;
555     default:
556       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
557       break;
558   }
559 }
560
561 static void
562 gst_play_sink_get_property (GObject * object, guint prop_id, GValue * value,
563     GParamSpec * pspec)
564 {
565   GstPlaySink *playsink;
566
567   playsink = GST_PLAY_SINK (object);
568
569   switch (prop_id) {
570     case PROP_VIDEO_SINK:
571       GST_OBJECT_LOCK (playsink);
572       g_value_set_object (value, playsink->video_sink);
573       GST_OBJECT_UNLOCK (playsink);
574       break;
575     case PROP_AUDIO_SINK:
576       GST_OBJECT_LOCK (playsink);
577       g_value_set_object (value, playsink->audio_sink);
578       GST_OBJECT_UNLOCK (playsink);
579       break;
580     case PROP_VIS_PLUGIN:
581       GST_OBJECT_LOCK (playsink);
582       g_value_set_object (value, playsink->visualisation);
583       GST_OBJECT_UNLOCK (playsink);
584       break;
585     case PROP_VOLUME:
586       GST_OBJECT_LOCK (playsink);
587       g_value_set_double (value, playsink->volume);
588       GST_OBJECT_UNLOCK (playsink);
589       break;
590     case PROP_FRAME:
591     {
592       break;
593     }
594     default:
595       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
596       break;
597   }
598 }
599
600 static void
601 post_missing_element_message (GstPlaySink * playsink, const gchar * name)
602 {
603   GstMessage *msg;
604
605   msg = gst_missing_element_message_new (GST_ELEMENT_CAST (playsink), name);
606   gst_element_post_message (GST_ELEMENT_CAST (playsink), msg);
607 }
608
609 static void
610 free_chain (GstPlayChain * chain)
611 {
612   if (chain->bin)
613     gst_object_unref (chain->bin);
614   gst_object_unref (chain->playsink);
615   g_free (chain);
616 }
617
618 static gboolean
619 add_chain (GstPlayChain * chain, gboolean add)
620 {
621   if (chain->added == add)
622     return TRUE;
623
624   if (add)
625     gst_bin_add (GST_BIN_CAST (chain->playsink), chain->bin);
626   else
627     gst_bin_remove (GST_BIN_CAST (chain->playsink), chain->bin);
628
629   chain->added = add;
630
631   return TRUE;
632 }
633
634 static gboolean
635 activate_chain (GstPlayChain * chain, gboolean activate)
636 {
637   if (chain->activated == activate)
638     return TRUE;
639
640   if (activate)
641     gst_element_set_state (chain->bin, GST_STATE_PAUSED);
642   else
643     gst_element_set_state (chain->bin, GST_STATE_NULL);
644
645   chain->activated = activate;
646
647   return TRUE;
648 }
649
650 /* make the element (bin) that contains the elements needed to perform
651  * video display. 
652  *
653  *  +------------------------------------------------+
654  *  | vbin                                           |
655  *  |      +----------+   +----------+   +---------+ |
656  *  |      |colorspace|   |videoscale|   |videosink| |
657  *  |   +-sink       src-sink       src-sink       | |
658  *  |   |  +----------+   +----------+   +---------+ |
659  * sink-+                                            |
660  *  +------------------------------------------------+
661  *           
662  */
663 static GstPlayChain *
664 gen_video_chain (GstPlaySink * playsink, gboolean raw)
665 {
666   GstPlayVideoChain *chain;
667   GstBin *bin;
668   GstPad *pad;
669
670   chain = g_new0 (GstPlayVideoChain, 1);
671   chain->chain.playsink = gst_object_ref (playsink);
672
673   if (playsink->video_sink) {
674     chain->sink = playsink->video_sink;
675   } else {
676     chain->sink = gst_element_factory_make ("autovideosink", "videosink");
677     if (chain->sink == NULL) {
678       chain->sink = gst_element_factory_make ("xvimagesink", "videosink");
679     }
680     if (chain->sink == NULL)
681       goto no_sinks;
682   }
683
684   /* create a bin to hold objects, as we create them we add them to this bin so
685    * that when something goes wrong we only need to unref the bin */
686   chain->chain.bin = gst_bin_new ("vbin");
687   bin = GST_BIN_CAST (chain->chain.bin);
688   gst_object_ref (bin);
689   gst_object_sink (bin);
690   gst_bin_add (bin, chain->sink);
691
692   if (raw) {
693     chain->conv = gst_element_factory_make ("ffmpegcolorspace", "vconv");
694     if (chain->conv == NULL)
695       goto no_colorspace;
696     gst_bin_add (bin, chain->conv);
697
698     chain->scale = gst_element_factory_make ("videoscale", "vscale");
699     if (chain->scale == NULL)
700       goto no_videoscale;
701     gst_bin_add (bin, chain->scale);
702   }
703
704   /* decouple decoder from sink, this improves playback quite a lot since the
705    * decoder can continue while the sink blocks for synchronisation. We don't
706    * need a lot of buffers as this consumes a lot of memory and we don't want
707    * too little because else we would be context switching too quickly. */
708   chain->queue = gst_element_factory_make ("queue", "vqueue");
709   g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
710       "max-size-bytes", 0, "max-size-time", (gint64) 0, NULL);
711   gst_bin_add (bin, chain->queue);
712
713   if (raw) {
714     gst_element_link_pads (chain->conv, "src", chain->queue, "sink");
715     gst_element_link_pads (chain->queue, "src", chain->scale, "sink");
716     /* be more careful with the pad from the custom sink element, it might not
717      * be named 'sink' */
718     if (!gst_element_link_pads (chain->scale, "src", chain->sink, NULL))
719       goto link_failed;
720
721     pad = gst_element_get_pad (chain->conv, "sink");
722   } else {
723     if (!gst_element_link_pads (chain->queue, "src", chain->sink, NULL))
724       goto link_failed;
725     pad = gst_element_get_pad (chain->queue, "sink");
726   }
727
728   chain->chain.sinkpad = gst_ghost_pad_new ("sink", pad);
729   gst_object_unref (pad);
730   gst_element_add_pad (chain->chain.bin, chain->chain.sinkpad);
731
732   return (GstPlayChain *) chain;
733
734   /* ERRORS */
735 no_sinks:
736   {
737     post_missing_element_message (playsink, "autovideosink");
738     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
739         (_("Both autovideosink and xvimagesink elements are missing.")),
740         (NULL));
741     free_chain ((GstPlayChain *) chain);
742     return NULL;
743   }
744 no_colorspace:
745   {
746     post_missing_element_message (playsink, "ffmpegcolorspace");
747     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
748         (_("Missing element '%s' - check your GStreamer installation."),
749             "ffmpegcolorspace"), (NULL));
750     free_chain ((GstPlayChain *) chain);
751     return NULL;
752   }
753
754 no_videoscale:
755   {
756     post_missing_element_message (playsink, "videoscale");
757     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
758         (_("Missing element '%s' - check your GStreamer installation."),
759             "videoscale"), ("possibly a liboil version mismatch?"));
760     free_chain ((GstPlayChain *) chain);
761     return NULL;
762   }
763 link_failed:
764   {
765     GST_ELEMENT_ERROR (playsink, CORE, PAD,
766         (NULL), ("Failed to configure the video sink."));
767     free_chain ((GstPlayChain *) chain);
768     return NULL;
769   }
770 }
771
772 #if 0
773 /* make an element for playback of video with subtitles embedded.
774  *
775  *  +--------------------------------------------------+
776  *  | tbin                  +-------------+            |
777  *  |          +-----+      | textoverlay |   +------+ |
778  *  |          | csp | +--video_sink      |   | vbin | |
779  * video_sink-sink  src+ +-text_sink     src-sink    | |
780  *  |          +-----+   |  +-------------+   +------+ |
781  * text_sink-------------+                             |
782  *  +--------------------------------------------------+
783  *
784  *  If there is no subtitle renderer this function will simply return the
785  *  videosink without the text_sink pad.
786  */
787 static GstElement *
788 gen_text_element (GstPlaySink * playsink)
789 {
790   GstElement *element, *csp, *overlay, *vbin;
791   GstPad *pad;
792
793   /* Create the video rendering bin, error is posted when this fails. */
794   vbin = gen_video_element (playsink);
795   if (!vbin)
796     return NULL;
797
798   /* Text overlay */
799   overlay = gst_element_factory_make ("textoverlay", "overlay");
800
801   /* If no overlay return the video bin without subtitle support. */
802   if (!overlay)
803     goto no_overlay;
804
805   /* Create our bin */
806   element = gst_bin_new ("textbin");
807
808   /* Set some parameters */
809   g_object_set (G_OBJECT (overlay),
810       "halign", "center", "valign", "bottom", NULL);
811   if (playsink->font_desc) {
812     g_object_set (G_OBJECT (overlay), "font-desc", playsink->font_desc, NULL);
813   }
814
815   /* Take a ref */
816   playsink->textoverlay_element = GST_ELEMENT_CAST (gst_object_ref (overlay));
817
818   /* we know this will succeed, as the video bin already created one before */
819   csp = gst_element_factory_make ("ffmpegcolorspace", "subtitlecsp");
820
821   /* Add our elements */
822   gst_bin_add_many (GST_BIN_CAST (element), csp, overlay, vbin, NULL);
823
824   /* Link */
825   gst_element_link_pads (csp, "src", overlay, "video_sink");
826   gst_element_link_pads (overlay, "src", vbin, "sink");
827
828   /* Add ghost pads on the subtitle bin */
829   pad = gst_element_get_pad (overlay, "text_sink");
830   gst_element_add_pad (element, gst_ghost_pad_new ("text_sink", pad));
831   gst_object_unref (pad);
832
833   pad = gst_element_get_pad (csp, "sink");
834   gst_element_add_pad (element, gst_ghost_pad_new ("sink", pad));
835   gst_object_unref (pad);
836
837   /* Set state to READY */
838   gst_element_set_state (element, GST_STATE_READY);
839
840   return element;
841
842   /* ERRORS */
843 no_overlay:
844   {
845     post_missing_element_message (playsink, "textoverlay");
846     GST_WARNING_OBJECT (playsink,
847         "No overlay (pango) element, subtitles disabled");
848     return vbin;
849   }
850 }
851 #endif
852
853
854 /* make the chain that contains the elements needed to perform
855  * audio playback. 
856  *
857  * We add a tee as the first element so that we can link the visualisation chain
858  * to it when requested.
859  *
860  *  +-----------------------------------------------------------------------+
861  *  | abin                                                                  |
862  *  |      +-----+   +---------+   +----------+   +---------+   +---------+ |
863  *  |      | tee |   |audioconv|   |audioscale|   | volume  |   |audiosink| |
864  *  |   +-sink  src-sink      src-sink       src-sink      src-sink       | |
865  *  |   |  +-----+   +---------+   +----------+   +---------+   +---------+ |
866  * sink-+                                                                   |
867  *  +-----------------------------------------------------------------------+
868  */
869 static GstPlayChain *
870 gen_audio_chain (GstPlaySink * playsink, gboolean raw)
871 {
872   GstPlayAudioChain *chain;
873   GstBin *bin;
874   gboolean res;
875   GstPad *pad;
876
877   chain = g_new0 (GstPlayAudioChain, 1);
878   chain->chain.playsink = gst_object_ref (playsink);
879
880   if (playsink->audio_sink) {
881     chain->sink = playsink->audio_sink;
882   } else {
883     chain->sink = gst_element_factory_make ("autoaudiosink", "audiosink");
884     if (chain->sink == NULL) {
885       chain->sink = gst_element_factory_make ("alsasink", "audiosink");
886     }
887     if (chain->sink == NULL)
888       goto no_sinks;
889   }
890   chain->chain.bin = gst_bin_new ("abin");
891   bin = GST_BIN_CAST (chain->chain.bin);
892   gst_object_ref (bin);
893   gst_object_sink (bin);
894   gst_bin_add (bin, chain->sink);
895
896   if (raw) {
897     chain->tee = gst_element_factory_make ("tee", "atee");
898     gst_bin_add (bin, chain->tee);
899
900     chain->conv = gst_element_factory_make ("audioconvert", "aconv");
901     if (chain->conv == NULL)
902       goto no_audioconvert;
903     gst_bin_add (bin, chain->conv);
904
905     chain->resample = gst_element_factory_make ("audioresample", "aresample");
906     if (chain->resample == NULL)
907       goto no_audioresample;
908     gst_bin_add (bin, chain->resample);
909
910     chain->teesrc = gst_element_get_request_pad (chain->tee, "src%d");
911     pad = gst_element_get_pad (chain->conv, "sink");
912     gst_pad_link (chain->teesrc, pad);
913     gst_object_unref (pad);
914
915     res = gst_element_link_pads (chain->conv, "src", chain->resample, "sink");
916
917     if (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME) {
918       chain->volume = gst_element_factory_make ("volume", "volume");
919       g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
920       gst_bin_add (bin, chain->volume);
921
922       res &=
923           gst_element_link_pads (chain->resample, "src", chain->volume, "sink");
924       res &= gst_element_link_pads (chain->volume, "src", chain->sink, NULL);
925     } else {
926       res &= gst_element_link_pads (chain->resample, "src", chain->sink, NULL);
927     }
928     if (!res)
929       goto link_failed;
930
931     pad = gst_element_get_pad (chain->tee, "sink");
932   } else {
933     pad = gst_element_get_pad (chain->sink, "sink");
934   }
935   chain->chain.sinkpad = gst_ghost_pad_new ("sink", pad);
936   gst_object_unref (pad);
937   gst_element_add_pad (chain->chain.bin, chain->chain.sinkpad);
938
939   return (GstPlayChain *) chain;
940
941   /* ERRORS */
942 no_sinks:
943   {
944     post_missing_element_message (playsink, "autoaudiosink");
945     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
946         (_("Both autoaudiosink and alsasink elements are missing.")), (NULL));
947     free_chain ((GstPlayChain *) chain);
948     return NULL;
949   }
950 no_audioconvert:
951   {
952     post_missing_element_message (playsink, "audioconvert");
953     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
954         (_("Missing element '%s' - check your GStreamer installation."),
955             "audioconvert"), ("possibly a liboil version mismatch?"));
956     free_chain ((GstPlayChain *) chain);
957     return NULL;
958   }
959
960 no_audioresample:
961   {
962     post_missing_element_message (playsink, "audioresample");
963     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
964         (_("Missing element '%s' - check your GStreamer installation."),
965             "audioresample"), ("possibly a liboil version mismatch?"));
966     free_chain ((GstPlayChain *) chain);
967     return NULL;
968   }
969 link_failed:
970   {
971     GST_ELEMENT_ERROR (playsink, CORE, PAD,
972         (NULL), ("Failed to configure the audio sink."));
973     free_chain ((GstPlayChain *) chain);
974     return NULL;
975   }
976 }
977
978 /*
979  *  +--------------------------------------------------+
980  *  | visbin                                           |
981  *  |      +--------+   +------------+   +-------+     |
982  *  |      | vqueue |   | audioconv  |   |  vis  |     |
983  *  |   +-sink     src-sink + samp  src-sink    src-+  |
984  *  |   |  +--------+   +------------+   +-------+  |  |
985  * sink-+                                           +-src
986  *  +--------------------------------------------------+
987  *           
988  */
989 #if 0
990 static GstPlayChain *
991 gen_vis_chain (GstPlaySink * playsink)
992 {
993   GstPlayVisChain *chain;
994   GstBin *bin;
995   gboolean res;
996   GstPad *pad;
997
998   chain = g_new0 (GstPlayVisChain, 1);
999   chain->chain.playsink = gst_object_ref (playsink);
1000
1001   chain->chain.bin = gst_bin_new ("abin");
1002   bin = GST_BIN_CAST (chain->chain.bin);
1003   gst_object_ref (bin);
1004   gst_object_sink (bin);
1005
1006   chain->queue = gst_element_factory_make ("queue", "visqueue");
1007   gst_bin_add (bin, chain->queue);
1008
1009   chain->conv = gst_element_factory_make ("audioconvert", "aconv");
1010   if (chain->conv == NULL)
1011     goto no_audioconvert;
1012   gst_bin_add (bin, chain->conv);
1013
1014   chain->resample = gst_element_factory_make ("audioresample", "aresample");
1015   if (chain->resample == NULL)
1016     goto no_audioresample;
1017   gst_bin_add (bin, chain->resample);
1018
1019   if (playsink->visualisation) {
1020     chain->vis = playsink->visualisation;
1021   } else {
1022     chain->vis = gst_element_factory_make ("goom", "vis");
1023     if (!chain->vis)
1024       goto no_goom;
1025   }
1026   gst_bin_add (bin, chain->vis);
1027
1028   res = gst_element_link_pads (chain->queue, "src", chain->conv, "sink");
1029   res &= gst_element_link_pads (chain->conv, "src", chain->resample, "sink");
1030   res &= gst_element_link_pads (chain->resample, "src", chain->vis, "sink");
1031   if (!res)
1032     goto link_failed;
1033
1034   pad = gst_element_get_pad (chain->queue, "sink");
1035   chain->chain.sinkpad = gst_ghost_pad_new ("sink", pad);
1036   gst_object_unref (pad);
1037   gst_element_add_pad (chain->chain.bin, chain->chain.sinkpad);
1038
1039   pad = gst_element_get_pad (chain->vis, "src");
1040   chain->srcpad = gst_ghost_pad_new ("src", pad);
1041   gst_object_unref (pad);
1042   gst_element_add_pad (chain->chain.bin, chain->srcpad);
1043
1044   return (GstPlayChain *) chain;
1045
1046   /* ERRORS */
1047 no_audioconvert:
1048   {
1049     post_missing_element_message (playsink, "audioconvert");
1050     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1051         (_("Missing element '%s' - check your GStreamer installation."),
1052             "audioconvert"), ("possibly a liboil version mismatch?"));
1053     free_chain ((GstPlayChain *) chain);
1054     return NULL;
1055   }
1056 no_audioresample:
1057   {
1058     post_missing_element_message (playsink, "audioresample");
1059     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1060         (_("Missing element '%s' - check your GStreamer installation."),
1061             "audioresample"), (NULL));
1062     free_chain ((GstPlayChain *) chain);
1063     return NULL;
1064   }
1065 no_goom:
1066   {
1067     post_missing_element_message (playsink, "goom");
1068     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1069         (_("Missing element '%s' - check your GStreamer installation."),
1070             "goom"), (NULL));
1071     free_chain ((GstPlayChain *) chain);
1072     return NULL;
1073   }
1074 link_failed:
1075   {
1076     GST_ELEMENT_ERROR (playsink, CORE, PAD,
1077         (NULL), ("Failed to configure the visualisation element."));
1078     free_chain ((GstPlayChain *) chain);
1079     return NULL;
1080   }
1081 }
1082 #endif
1083
1084 #if 0
1085 static gboolean
1086 activate_vis (GstPlaySink * playsink, gboolean activate)
1087 {
1088   /* need to have an audio chain */
1089   if (!playsink->audiochain || !playsink->vischain)
1090     return FALSE;
1091
1092   if (playsink->vischain->activated == activate)
1093     return TRUE;
1094
1095   if (activate) {
1096     /* activation: Add the vis chain to the sink bin . Take a new srcpad from
1097      * the tee of the audio chain and link it to the sinkpad of the vis chain.
1098      */
1099
1100   } else {
1101     /* deactivation: release the srcpad from the tee of the audio chain. Set the
1102      * vis chain to NULL and remove it from the sink bin */
1103
1104   }
1105   return TRUE;
1106 }
1107 #endif
1108
1109 /* this function is called when all the request pads are requested and when we
1110  * have to construct the final pipeline.
1111  */
1112 gboolean
1113 gst_play_sink_reconfigure (GstPlaySink * playsink)
1114 {
1115   GstPlayFlags flags;
1116
1117   GST_DEBUG_OBJECT (playsink, "reconfiguring");
1118
1119   GST_PLAY_SINK_LOCK (playsink);
1120   GST_OBJECT_LOCK (playsink);
1121   flags = playsink->flags;
1122   GST_OBJECT_UNLOCK (playsink);
1123
1124   if (flags & GST_PLAY_FLAG_AUDIO && playsink->audio_pad) {
1125     if (!playsink->audiochain)
1126       playsink->audiochain =
1127           gen_audio_chain (playsink, playsink->audio_pad_raw);
1128     add_chain (playsink->audiochain, TRUE);
1129     activate_chain (playsink->audiochain, TRUE);
1130     gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->audio_pad),
1131         playsink->audiochain->sinkpad);
1132   } else {
1133     if (playsink->audiochain) {
1134       add_chain (playsink->audiochain, FALSE);
1135       activate_chain (playsink->audiochain, FALSE);
1136     }
1137     if (playsink->audio_pad)
1138       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->audio_pad), NULL);
1139   }
1140
1141   if (flags & GST_PLAY_FLAG_VIDEO && playsink->video_pad) {
1142     if (!playsink->videochain)
1143       playsink->videochain =
1144           gen_video_chain (playsink, playsink->video_pad_raw);
1145     add_chain (playsink->videochain, TRUE);
1146     activate_chain (playsink->videochain, TRUE);
1147     gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad),
1148         playsink->videochain->sinkpad);
1149   } else {
1150     if (playsink->videochain) {
1151       add_chain (playsink->videochain, FALSE);
1152       activate_chain (playsink->videochain, FALSE);
1153     }
1154     if (playsink->video_pad)
1155       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
1156   }
1157   GST_PLAY_SINK_UNLOCK (playsink);
1158
1159   return TRUE;
1160 }
1161
1162 gboolean
1163 gst_play_sink_set_flags (GstPlaySink * playsink, GstPlayFlags flags)
1164 {
1165   g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), FALSE);
1166
1167   GST_OBJECT_LOCK (playsink);
1168   playsink->flags = flags;
1169   GST_OBJECT_UNLOCK (playsink);
1170
1171   return TRUE;
1172 }
1173
1174 GstPlayFlags
1175 gst_play_sink_get_flags (GstPlaySink * playsink)
1176 {
1177   GstPlayFlags res;
1178
1179   g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), 0);
1180
1181   GST_OBJECT_LOCK (playsink);
1182   res = playsink->flags;
1183   GST_OBJECT_UNLOCK (playsink);
1184
1185   return res;
1186 }
1187
1188
1189 GstPad *
1190 gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type)
1191 {
1192   GstPad *res = NULL;
1193   gboolean created = FALSE;
1194   gboolean raw = FALSE;
1195
1196   GST_PLAY_SINK_LOCK (playsink);
1197   switch (type) {
1198     case GST_PLAY_SINK_TYPE_AUDIO_RAW:
1199       raw = TRUE;
1200     case GST_PLAY_SINK_TYPE_AUDIO:
1201       if (!playsink->audio_pad) {
1202         playsink->audio_pad =
1203             gst_ghost_pad_new_no_target ("audio_sink", GST_PAD_SINK);
1204         created = TRUE;
1205       }
1206       playsink->audio_pad_raw = raw;
1207       res = playsink->audio_pad;
1208       break;
1209     case GST_PLAY_SINK_TYPE_VIDEO_RAW:
1210       raw = TRUE;
1211     case GST_PLAY_SINK_TYPE_VIDEO:
1212       if (!playsink->video_pad) {
1213         playsink->video_pad =
1214             gst_ghost_pad_new_no_target ("video_sink", GST_PAD_SINK);
1215         created = TRUE;
1216       }
1217       playsink->video_pad_raw = raw;
1218       res = playsink->video_pad;
1219       break;
1220     case GST_PLAY_SINK_TYPE_TEXT:
1221       if (!playsink->text_pad) {
1222         playsink->text_pad =
1223             gst_ghost_pad_new_no_target ("text_sink", GST_PAD_SINK);
1224         created = TRUE;
1225       }
1226       res = playsink->text_pad;
1227       break;
1228     default:
1229       res = NULL;
1230       break;
1231   }
1232   GST_PLAY_SINK_UNLOCK (playsink);
1233
1234   if (created && res) {
1235     gst_pad_set_active (res, TRUE);
1236     gst_element_add_pad (GST_ELEMENT_CAST (playsink), res);
1237   }
1238
1239   return res;
1240 }
1241
1242 void
1243 gst_play_sink_release_pad (GstPlaySink * playsink, GstPad * pad)
1244 {
1245   GstPad **res = NULL;
1246
1247   GST_PLAY_SINK_LOCK (playsink);
1248   if (pad == playsink->video_pad) {
1249     res = &playsink->video_pad;
1250   } else if (pad == playsink->audio_pad) {
1251     res = &playsink->audio_pad;
1252   } else if (pad == playsink->text_pad) {
1253     res = &playsink->text_pad;
1254   }
1255   GST_PLAY_SINK_UNLOCK (playsink);
1256
1257   if (*res) {
1258     gst_pad_set_active (*res, FALSE);
1259     gst_element_remove_pad (GST_ELEMENT_CAST (playsink), *res);
1260     *res = NULL;
1261   }
1262 }
1263
1264 /* Send an event to our sinks until one of them works; don't then send to the
1265  * remaining sinks (unlike GstBin)
1266  */
1267 static gboolean
1268 gst_play_sink_send_event_to_sink (GstPlaySink * playsink, GstEvent * event)
1269 {
1270   gboolean res = TRUE;
1271
1272   if (playsink->audiochain) {
1273     gst_event_ref (event);
1274     if ((res = gst_element_send_event (playsink->audiochain->bin, event))) {
1275       GST_DEBUG_OBJECT (playsink, "Sent event succesfully to audio sink");
1276       goto done;
1277     }
1278     GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
1279   }
1280   if (playsink->videochain) {
1281     gst_event_ref (event);
1282     if ((res = gst_element_send_event (playsink->videochain->bin, event))) {
1283       GST_DEBUG_OBJECT (playsink, "Sent event succesfully to video sink");
1284       goto done;
1285     }
1286     GST_DEBUG_OBJECT (playsink, "Event failed when sent to video sink");
1287   }
1288 done:
1289   gst_event_unref (event);
1290   return res;
1291 }
1292
1293 /* We only want to send the event to a single sink (overriding GstBin's
1294  * behaviour), but we want to keep GstPipeline's behaviour - wrapping seek
1295  * events appropriately. So, this is a messy duplication of code. */
1296 static gboolean
1297 gst_play_sink_send_event (GstElement * element, GstEvent * event)
1298 {
1299   gboolean res = FALSE;
1300   GstEventType event_type = GST_EVENT_TYPE (event);
1301
1302   switch (event_type) {
1303     case GST_EVENT_SEEK:
1304       GST_DEBUG_OBJECT (element, "Sending seek event to a sink");
1305       res = gst_play_sink_send_event_to_sink (GST_PLAY_SINK (element), event);
1306       break;
1307     default:
1308       res = parent_class->send_event (element, event);
1309       break;
1310   }
1311   return res;
1312 }
1313
1314 static GstStateChangeReturn
1315 gst_play_sink_change_state (GstElement * element, GstStateChange transition)
1316 {
1317   GstStateChangeReturn ret;
1318   GstPlaySink *playsink;
1319
1320   playsink = GST_PLAY_SINK (element);
1321
1322   switch (transition) {
1323     case GST_STATE_CHANGE_READY_TO_PAUSED:
1324       break;
1325     default:
1326       break;
1327   }
1328
1329   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1330   if (ret == GST_STATE_CHANGE_FAILURE)
1331     return ret;
1332
1333   switch (transition) {
1334     case GST_STATE_CHANGE_READY_TO_PAUSED:
1335       break;
1336     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1337       /* FIXME Release audio device when we implement that */
1338       break;
1339     case GST_STATE_CHANGE_PAUSED_TO_READY:
1340       /* remove sinks we added */
1341       if (playsink->videochain) {
1342         activate_chain (playsink->videochain, FALSE);
1343         add_chain (playsink->videochain, FALSE);
1344       }
1345       if (playsink->audiochain) {
1346         activate_chain (playsink->audiochain, FALSE);
1347         add_chain (playsink->audiochain, FALSE);
1348       }
1349       break;
1350     default:
1351       break;
1352   }
1353
1354   return ret;
1355 }