ext/ogg/gstoggdemux.c: Send EOS when deactivating.
[platform/upstream/gstreamer.git] / gst / playback / gstplaybin.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
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 "gstplaybasebin.h"
28
29 GST_DEBUG_CATEGORY_STATIC (gst_play_bin_debug);
30 #define GST_CAT_DEFAULT gst_play_bin_debug
31
32 #define GST_TYPE_PLAY_BIN               (gst_play_bin_get_type())
33 #define GST_PLAY_BIN(obj)               (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_PLAY_BIN,GstPlayBin))
34 #define GST_PLAY_BIN_CLASS(klass)       (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_PLAY_BIN,GstPlayBinClass))
35 #define GST_IS_PLAY_BIN(obj)            (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_PLAY_BIN))
36 #define GST_IS_PLAY_BIN_CLASS(klass)    (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_PLAY_BIN))
37
38 #define VOLUME_MAX_DOUBLE 4.0
39
40 typedef struct _GstPlayBin GstPlayBin;
41 typedef struct _GstPlayBinClass GstPlayBinClass;
42
43 struct _GstPlayBin
44 {
45   GstPlayBaseBin parent;
46
47   /* the configurable elements */
48   GstElement *fakesink;
49   GstElement *audio_sink;
50   GstElement *video_sink;
51   GstElement *visualisation;
52   GstElement *volume_element;
53   GstElement *textoverlay_element;
54   gfloat volume;
55
56   /* these are the currently active sinks */
57   GList *sinks;
58
59   /* the last captured frame for snapshots */
60   GstBuffer *frame;
61
62   /* our cache for the sinks */
63   GHashTable *cache;
64
65   /* font description */
66   gchar *font_desc;
67 };
68
69 struct _GstPlayBinClass
70 {
71   GstPlayBaseBinClass parent_class;
72 };
73
74 /* props */
75 enum
76 {
77   ARG_0,
78   ARG_AUDIO_SINK,
79   ARG_VIDEO_SINK,
80   ARG_VIS_PLUGIN,
81   ARG_VOLUME,
82   ARG_FRAME,
83   ARG_FONT_DESC
84 };
85
86 /* signals */
87 enum
88 {
89   LAST_SIGNAL
90 };
91
92 static void gst_play_bin_class_init (GstPlayBinClass * klass);
93 static void gst_play_bin_init (GstPlayBin * play_bin);
94 static void gst_play_bin_dispose (GObject * object);
95
96 static void setup_sinks (GstPlayBaseBin * play_base_bin,
97     GstPlayBaseGroup * group);
98 static void remove_sinks (GstPlayBin * play_bin);
99
100 static void gst_play_bin_set_property (GObject * object, guint prop_id,
101     const GValue * value, GParamSpec * spec);
102 static void gst_play_bin_get_property (GObject * object, guint prop_id,
103     GValue * value, GParamSpec * spec);
104 static GstElementStateReturn gst_play_bin_change_state (GstElement * element);
105
106 static GstElementClass *parent_class;
107
108 //static guint gst_play_bin_signals[LAST_SIGNAL] = { 0 };
109
110 static GstElementDetails gst_play_bin_details = {
111   "Player Bin",
112   "Generic/Bin/Player",
113   "Autoplug and play media from an uri",
114   "Wim Taymans <wim@fluendo.com>"
115 };
116
117 static GType
118 gst_play_bin_get_type (void)
119 {
120   static GType gst_play_bin_type = 0;
121
122   if (!gst_play_bin_type) {
123     static const GTypeInfo gst_play_bin_info = {
124       sizeof (GstPlayBinClass),
125       NULL,
126       NULL,
127       (GClassInitFunc) gst_play_bin_class_init,
128       NULL,
129       NULL,
130       sizeof (GstPlayBin),
131       0,
132       (GInstanceInitFunc) gst_play_bin_init,
133       NULL
134     };
135
136     gst_play_bin_type = g_type_register_static (GST_TYPE_PLAY_BASE_BIN,
137         "GstPlayBin", &gst_play_bin_info, 0);
138   }
139
140   return gst_play_bin_type;
141 }
142
143 static void
144 gst_play_bin_class_init (GstPlayBinClass * klass)
145 {
146   GObjectClass *gobject_klass;
147   GstElementClass *gstelement_klass;
148   GstBinClass *gstbin_klass;
149   GstPlayBaseBinClass *playbasebin_klass;
150
151   gobject_klass = (GObjectClass *) klass;
152   gstelement_klass = (GstElementClass *) klass;
153   gstbin_klass = (GstBinClass *) klass;
154   playbasebin_klass = (GstPlayBaseBinClass *) klass;
155
156   parent_class = g_type_class_ref (gst_play_base_bin_get_type ());
157
158   gobject_klass->set_property = gst_play_bin_set_property;
159   gobject_klass->get_property = gst_play_bin_get_property;
160
161   g_object_class_install_property (gobject_klass, ARG_VIDEO_SINK,
162       g_param_spec_object ("video-sink", "Video Sink",
163           "the video output element to use (NULL = default sink)",
164           GST_TYPE_ELEMENT, G_PARAM_READWRITE));
165   g_object_class_install_property (gobject_klass, ARG_AUDIO_SINK,
166       g_param_spec_object ("audio-sink", "Audio Sink",
167           "the audio output element to use (NULL = default sink)",
168           GST_TYPE_ELEMENT, G_PARAM_READWRITE));
169   g_object_class_install_property (gobject_klass, ARG_VIS_PLUGIN,
170       g_param_spec_object ("vis-plugin", "Vis plugin",
171           "the visualization element to use (NULL = none)",
172           GST_TYPE_ELEMENT, G_PARAM_READWRITE));
173   g_object_class_install_property (gobject_klass, ARG_VOLUME,
174       g_param_spec_double ("volume", "volume", "volume",
175           0.0, VOLUME_MAX_DOUBLE, 1.0, G_PARAM_READWRITE));
176   g_object_class_install_property (gobject_klass, ARG_FRAME,
177       gst_param_spec_mini_object ("frame", "Frame",
178           "The last frame (NULL = no video available)",
179           GST_TYPE_BUFFER, G_PARAM_READABLE));
180   g_object_class_install_property (gobject_klass, ARG_FONT_DESC,
181       g_param_spec_string ("subtitle-font-desc",
182           "Subtitle font description",
183           "Pango font description of font "
184           "to be used for subtitle rendering", NULL, G_PARAM_WRITABLE));
185
186   gobject_klass->dispose = GST_DEBUG_FUNCPTR (gst_play_bin_dispose);
187
188   gst_element_class_set_details (gstelement_klass, &gst_play_bin_details);
189
190   gstelement_klass->change_state =
191       GST_DEBUG_FUNCPTR (gst_play_bin_change_state);
192
193   playbasebin_klass->setup_output_pads = setup_sinks;
194 }
195
196 static void
197 gst_play_bin_init (GstPlayBin * play_bin)
198 {
199   play_bin->video_sink = NULL;
200   play_bin->audio_sink = NULL;
201   play_bin->visualisation = NULL;
202   play_bin->volume_element = NULL;
203   play_bin->textoverlay_element = NULL;
204   play_bin->volume = 1.0;
205   play_bin->sinks = NULL;
206   play_bin->frame = NULL;
207   play_bin->font_desc = NULL;
208   play_bin->cache = g_hash_table_new_full (g_str_hash, g_str_equal,
209       NULL, (GDestroyNotify) gst_object_unref);
210 }
211
212 static void
213 gst_play_bin_dispose (GObject * object)
214 {
215   GstPlayBin *play_bin;
216
217   play_bin = GST_PLAY_BIN (object);
218
219   if (play_bin->cache != NULL) {
220     remove_sinks (play_bin);
221     g_hash_table_destroy (play_bin->cache);
222     play_bin->cache = NULL;
223   }
224
225   if (play_bin->audio_sink != NULL) {
226     gst_object_unref (play_bin->audio_sink);
227     play_bin->audio_sink = NULL;
228   }
229   if (play_bin->video_sink != NULL) {
230     gst_object_unref (play_bin->video_sink);
231     play_bin->video_sink = NULL;
232   }
233   if (play_bin->visualisation != NULL) {
234     gst_object_unref (play_bin->visualisation);
235     play_bin->visualisation = NULL;
236   }
237   g_free (play_bin->font_desc);
238   play_bin->font_desc = NULL;
239
240   G_OBJECT_CLASS (parent_class)->dispose (object);
241 }
242
243
244 static void
245 gst_play_bin_set_property (GObject * object, guint prop_id,
246     const GValue * value, GParamSpec * pspec)
247 {
248   GstPlayBin *play_bin;
249
250   g_return_if_fail (GST_IS_PLAY_BIN (object));
251
252   play_bin = GST_PLAY_BIN (object);
253
254   switch (prop_id) {
255     case ARG_VIDEO_SINK:
256       if (play_bin->video_sink != NULL) {
257         gst_object_unref (play_bin->video_sink);
258       }
259       play_bin->video_sink = g_value_get_object (value);
260       if (play_bin->video_sink != NULL) {
261         gst_object_ref (play_bin->video_sink);
262         gst_object_sink (GST_OBJECT (play_bin->video_sink));
263       }
264       /* when changing the videosink, we just remove the
265        * video pipeline from the cache so that it will be 
266        * regenerated with the new sink element */
267       g_hash_table_remove (play_bin->cache, "vbin");
268       break;
269     case ARG_AUDIO_SINK:
270       if (play_bin->audio_sink != NULL) {
271         gst_object_unref (play_bin->audio_sink);
272       }
273       play_bin->audio_sink = g_value_get_object (value);
274       if (play_bin->audio_sink != NULL) {
275         gst_object_ref (play_bin->audio_sink);
276         gst_object_sink (GST_OBJECT (play_bin->audio_sink));
277       }
278       g_hash_table_remove (play_bin->cache, "abin");
279       break;
280     case ARG_VIS_PLUGIN:
281       if (play_bin->visualisation != NULL) {
282         gst_object_unref (play_bin->visualisation);
283       }
284       play_bin->visualisation = g_value_get_object (value);
285       if (play_bin->visualisation != NULL) {
286         gst_object_ref (play_bin->visualisation);
287         gst_object_sink (GST_OBJECT (play_bin->visualisation));
288       }
289       break;
290     case ARG_VOLUME:
291       play_bin->volume = g_value_get_double (value);
292       if (play_bin->volume_element) {
293         g_object_set (G_OBJECT (play_bin->volume_element), "volume",
294             play_bin->volume, NULL);
295       }
296       break;
297     case ARG_FONT_DESC:
298       g_free (play_bin->font_desc);
299       play_bin->font_desc = g_strdup (g_value_get_string (value));
300       if (play_bin->textoverlay_element) {
301         g_object_set (G_OBJECT (play_bin->textoverlay_element),
302             "font-desc", g_value_get_string (value), NULL);
303       }
304       break;
305     default:
306       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
307       break;
308   }
309 }
310
311 static void
312 gst_play_bin_get_property (GObject * object, guint prop_id, GValue * value,
313     GParamSpec * pspec)
314 {
315   GstPlayBin *play_bin;
316
317   g_return_if_fail (GST_IS_PLAY_BIN (object));
318
319   play_bin = GST_PLAY_BIN (object);
320
321   switch (prop_id) {
322     case ARG_VIDEO_SINK:
323       g_value_set_object (value, play_bin->video_sink);
324       break;
325     case ARG_AUDIO_SINK:
326       g_value_set_object (value, play_bin->audio_sink);
327       break;
328     case ARG_VIS_PLUGIN:
329       g_value_set_object (value, play_bin->visualisation);
330       break;
331     case ARG_VOLUME:
332       g_value_set_double (value, play_bin->volume);
333       break;
334     case ARG_FRAME:
335       gst_value_set_mini_object (value, GST_MINI_OBJECT (play_bin->frame));
336       break;
337     default:
338       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
339       break;
340   }
341 }
342
343 /* signal fired when the identity has received a new buffer. This is used for
344  * making screenshots.
345  */
346 static void
347 handoff (GstElement * identity, GstBuffer * frame, gpointer data)
348 {
349   GstPlayBin *play_bin = GST_PLAY_BIN (data);
350
351   if (play_bin->frame) {
352     gst_buffer_unref (play_bin->frame);
353   }
354   play_bin->frame = gst_buffer_ref (frame);
355 }
356
357 /* make the element (bin) that contains the elements needed to perform
358  * video display. We connect a handoff signal to identity so that we
359  * can grab snapshots. Identity's sinkpad is ghosted to vbin.
360  *
361  *  +-------------------------------------------------------------+
362  *  | vbin                                                        |
363  *  |      +--------+   +----------+   +----------+   +---------+ |
364  *  |      |identity|   |colorspace|   |videoscale|   |videosink| |
365  *  |   +-sink     src-sink       src-sink       src-sink       | |
366  *  |   |  +---+----+   +----------+   +----------+   +---------+ |
367  * sink-+      |                                                  |
368  *  +----------|--------------------------------------------------+
369  *           handoff
370  */
371 static GstElement *
372 gen_video_element (GstPlayBin * play_bin)
373 {
374   GstElement *element;
375   GstElement *conv;
376
377   //GstElement *scale;
378   GstElement *sink;
379   GstElement *identity;
380   GstPad *pad;
381
382   /* first see if we have it in the cache */
383   element = g_hash_table_lookup (play_bin->cache, "vbin");
384   if (element != NULL) {
385     return element;
386   }
387
388   element = gst_bin_new ("vbin");
389   identity = gst_element_factory_make ("identity", "id");
390   g_signal_connect (identity, "handoff", G_CALLBACK (handoff), play_bin);
391   conv = gst_element_factory_make ("ffmpegcolorspace", "vconv");
392   //scale = gst_element_factory_make ("videoscale", "vscale");
393   if (play_bin->video_sink) {
394     sink = play_bin->video_sink;
395   } else {
396     sink = gst_element_factory_make ("xvimagesink", "videosink");
397   }
398   gst_object_ref (sink);
399   g_hash_table_insert (play_bin->cache, "video_sink", sink);
400
401   gst_bin_add (GST_BIN (element), identity);
402   gst_bin_add (GST_BIN (element), conv);
403   //gst_bin_add (GST_BIN (element), scale);
404   gst_bin_add (GST_BIN (element), sink);
405   gst_element_link_pads (identity, "src", conv, "sink");
406   gst_element_link_pads (conv, "src",   /*scale, "sink");
407                                            gst_element_link_pads (scale, "src", */ sink, "sink");
408
409   pad = gst_element_get_pad (identity, "sink");
410   gst_element_add_pad (element, gst_ghost_pad_new ("sink", pad));
411   gst_object_unref (pad);
412
413   gst_element_set_state (element, GST_STATE_READY);
414
415   /* since we're gonna add it to a bin but don't want to lose it,
416    * we keep a reference. */
417   gst_object_ref (element);
418   g_hash_table_insert (play_bin->cache, "vbin", element);
419
420   return element;
421 }
422
423 /* make an element for playback of video with subtitles embedded.
424  *
425  *  +--------------------------------------------------+
426  *  | tbin                  +-------------+            |
427  *  |          +-----+      | textoverlay |   +------+ |
428  *  |          | csp | +--video_sink      |   | vbin | |
429  * video_sink-sink  src+ +-text_sink     src-sink    | |
430  *  |          +-----+   |  +-------------+   +------+ |
431  * text_sink-------------+                             |
432  *  +--------------------------------------------------+
433  */
434
435 static GstElement *
436 gen_text_element (GstPlayBin * play_bin)
437 {
438   GstElement *element, *csp, *overlay, *vbin;
439   GstPad *pad;
440
441   overlay = gst_element_factory_make ("textoverlay", "overlay");
442   g_object_set (G_OBJECT (overlay),
443       "halign", "center", "valign", "bottom", NULL);
444   play_bin->textoverlay_element = overlay;
445   if (play_bin->font_desc) {
446     g_object_set (G_OBJECT (play_bin->textoverlay_element),
447         "font-desc", play_bin->font_desc, NULL);
448   }
449   vbin = gen_video_element (play_bin);
450   if (!overlay) {
451     g_warning ("No overlay (pango) element, subtitles disabled");
452     return vbin;
453   }
454   csp = gst_element_factory_make ("ffmpegcolorspace", "subtitlecsp");
455   element = gst_bin_new ("textbin");
456   gst_element_link_many (csp, overlay, vbin, NULL);
457   gst_bin_add_many (GST_BIN (element), csp, overlay, vbin, NULL);
458
459   pad = gst_element_get_pad (overlay, "text_sink");
460 #define gst_element_add_ghost_pad(element, pad, name) \
461     gst_element_add_pad (element, gst_ghost_pad_new (name, pad))
462   gst_element_add_ghost_pad (element, pad, "text_sink");
463   gst_object_unref (pad);
464
465   pad = gst_element_get_pad (csp, "sink");
466   gst_element_add_ghost_pad (element, pad, "sink");
467   gst_object_unref (pad);
468
469   return element;
470 }
471
472 /* make the element (bin) that contains the elements needed to perform
473  * audio playback. 
474  *
475  *  +-------------------------------------------------------------+
476  *  | abin                                                        |
477  *  |      +---------+   +----------+   +---------+   +---------+ |
478  *  |      |audioconv|   |audioscale|   | volume  |   |audiosink| |
479  *  |   +-sink      src-sink       src-sink      src-sink       | |
480  *  |   |  +---------+   +----------+   +---------+   +---------+ |
481  * sink-+                                                         |
482  *  +-------------------------------------------------------------+
483  *                  
484  */
485 static GstElement *
486 gen_audio_element (GstPlayBin * play_bin)
487 {
488   GstElement *element;
489   GstElement *conv;
490   GstElement *sink;
491   GstElement *volume;
492   GstElement *scale;
493   GstPad *pad;
494
495   element = g_hash_table_lookup (play_bin->cache, "abin");
496   if (element != NULL) {
497     return element;
498   }
499   element = gst_bin_new ("abin");
500   conv = gst_element_factory_make ("audioconvert", "aconv");
501   scale = gst_element_factory_make ("audioscale", "ascale");
502
503   volume = gst_element_factory_make ("volume", "volume");
504   g_object_set (G_OBJECT (volume), "volume", play_bin->volume, NULL);
505   play_bin->volume_element = volume;
506
507   if (play_bin->audio_sink) {
508     sink = play_bin->audio_sink;
509   } else {
510     sink = gst_element_factory_make ("alsasink", "audiosink");
511     play_bin->audio_sink = GST_ELEMENT (gst_object_ref (sink));
512   }
513
514   gst_object_ref (sink);
515   g_hash_table_insert (play_bin->cache, "audio_sink", sink);
516
517   gst_bin_add (GST_BIN (element), conv);
518   //gst_bin_add (GST_BIN (element), scale);
519   //gst_bin_add (GST_BIN (element), volume);
520   gst_bin_add (GST_BIN (element), sink);
521
522   gst_element_link_pads (conv, "src",   /*scale, "sink");
523                                            gst_element_link_pads (scale, "src", volume, "sink");
524                                            gst_element_link_pads (volume, "src", */ sink, "sink");
525
526   pad = gst_element_get_pad (conv, "sink");
527   gst_element_add_ghost_pad (element, pad, "sink");
528   gst_object_unref (pad);
529
530   gst_element_set_state (element, GST_STATE_READY);
531
532   /* since we're gonna add it to a bin but don't want to lose it,
533    * we keep a reference. */
534   gst_object_ref (element);
535   g_hash_table_insert (play_bin->cache, "abin", element);
536
537   return element;
538 }
539
540 /* make the element (bin) that contains the elements needed to perform
541  * visualisation ouput.  The idea is to split the audio using tee, then 
542  * sending the output to the regular audio bin and the other output to
543  * the vis plugin that transforms it into a video that is rendered with the
544  * normal video bin. The video bin is run in a thread to make sure it does
545  * not block the audio playback pipeline.
546  *
547  *  +--------------------------------------------------------------------------+
548  *  | visbin                                                                   |
549  *  |      +------+   +----------------+                                       |
550  *  |      | tee  |   |   abin ...     |                                       |
551  *  |   +-sink   src-sink              |                                       |
552  *  |   |  |      |   +----------------+                 +-------------------+ |
553  *  |   |  |      |                                      | vthread           | |
554  *  |   |  |      |   +---------+   +------+   +------+  | +--------------+  | |
555  *  |   |  |      |   |audioconv|   | vis  |   |vqueue|  | | vbin ...     |  | |
556  *  |   |  |     src-sink      src-sink   src-sink   src-sink             |  | |
557  *  |   |  |      |   +---------+   +------+   +------+  | +--------------+  | |
558  *  |   |  |      |                                      +-------------------+ |
559  *  |   |  +------+                                                            |
560  * sink-+                                                                      |
561    +--------------------------------------------------------------------------+
562  */
563 static GstElement *
564 gen_vis_element (GstPlayBin * play_bin)
565 {
566   GstElement *element;
567   GstElement *tee;
568   GstElement *asink;
569   GstElement *vsink;
570   GstElement *conv;
571   GstElement *vis;
572   GstElement *vqueue, *aqueue;
573   GstPad *pad, *rpad;
574
575   element = gst_bin_new ("visbin");
576   tee = gst_element_factory_make ("tee", "tee");
577
578   vqueue = gst_element_factory_make ("queue", "vqueue");
579   aqueue = gst_element_factory_make ("queue", "aqueue");
580
581   asink = gen_audio_element (play_bin);
582   vsink = gen_video_element (play_bin);
583
584   gst_bin_add (GST_BIN (element), asink);
585   gst_bin_add (GST_BIN (element), vqueue);
586   gst_bin_add (GST_BIN (element), aqueue);
587   gst_bin_add (GST_BIN (element), vsink);
588   gst_bin_add (GST_BIN (element), tee);
589
590   conv = gst_element_factory_make ("audioconvert", "aconv");
591   if (play_bin->visualisation) {
592     gst_object_ref (play_bin->visualisation);
593     vis = play_bin->visualisation;
594   } else {
595     vis = gst_element_factory_make ("goom", "vis");
596   }
597
598   gst_bin_add (GST_BIN (element), conv);
599   gst_bin_add (GST_BIN (element), vis);
600
601   gst_element_link_pads (conv, "src", vis, "sink");
602   gst_element_link_pads (vis, "src", vqueue, "sink");
603
604   gst_element_link_pads (vqueue, "src", vsink, "sink");
605
606   pad = gst_element_get_pad (aqueue, "sink");
607   rpad = gst_element_get_request_pad (tee, "src%d");
608   gst_pad_link (rpad, pad);
609   gst_object_unref (rpad);
610   gst_object_unref (pad);
611   gst_element_link_pads (aqueue, "src", asink, "sink");
612
613   pad = gst_element_get_pad (conv, "sink");
614   rpad = gst_element_get_request_pad (tee, "src%d");
615   gst_pad_link (rpad, pad);
616   gst_object_unref (rpad);
617   gst_object_unref (pad);
618
619   pad = gst_element_get_pad (tee, "sink");
620   gst_element_add_ghost_pad (element, pad, "sink");
621   gst_object_unref (pad);
622
623   return element;
624 }
625
626 /* get rid of all installed sinks */
627 static void
628 remove_sinks (GstPlayBin * play_bin)
629 {
630   GList *sinks;
631   GstObject *parent;
632   GstElement *element;
633   GstPad *pad, *peer;
634
635   GST_DEBUG ("removesinks");
636   element = g_hash_table_lookup (play_bin->cache, "abin");
637   if (element != NULL) {
638     parent = gst_element_get_parent (element);
639     if (parent != NULL) {
640       /* we remove the element from the parent so that
641        * there is no unwanted state change when the parent
642        * is disposed */
643       play_bin->sinks = g_list_remove (play_bin->sinks, element);
644       gst_bin_remove (GST_BIN (parent), element);
645       gst_object_unref (parent);
646     }
647     pad = gst_element_get_pad (element, "sink");
648     if (pad != NULL) {
649       peer = gst_pad_get_peer (pad);
650       if (peer != NULL) {
651         gst_pad_unlink (peer, pad);
652         gst_object_unref (peer);
653       }
654       gst_object_unref (pad);
655     }
656   }
657   element = g_hash_table_lookup (play_bin->cache, "vbin");
658   if (element != NULL) {
659     parent = gst_element_get_parent (element);
660     if (parent != NULL) {
661       play_bin->sinks = g_list_remove (play_bin->sinks, element);
662       gst_bin_remove (GST_BIN (parent), element);
663       gst_object_unref (parent);
664     }
665     pad = gst_element_get_pad (element, "sink");
666     if (pad != NULL) {
667       peer = gst_pad_get_peer (pad);
668       if (peer != NULL) {
669         gst_pad_unlink (peer, pad);
670         gst_object_unref (peer);
671       }
672       gst_object_unref (pad);
673     }
674   }
675
676   for (sinks = play_bin->sinks; sinks; sinks = g_list_next (sinks)) {
677     GstElement *element = GST_ELEMENT (sinks->data);
678     GstPad *pad;
679     GstPad *peer;
680
681     pad = gst_element_get_pad (element, "sink");
682
683     GST_LOG ("removing sink %p", element);
684
685     peer = gst_pad_get_peer (pad);
686     if (peer) {
687       gst_pad_unlink (peer, pad);
688       gst_object_unref (peer);
689     }
690     gst_object_unref (pad);
691
692     gst_bin_remove (GST_BIN (play_bin), element);
693   }
694   g_list_free (play_bin->sinks);
695   play_bin->sinks = NULL;
696
697   if (play_bin->frame) {
698     gst_buffer_unref (play_bin->frame);
699     play_bin->frame = NULL;
700   }
701
702   play_bin->textoverlay_element = NULL;
703 }
704
705 /* loop over the streams and set up the pipeline to play this
706  * media file. First we count the number of audio and video streams.
707  * If there is no video stream but there exists an audio stream,
708  * we install a visualisation pipeline.
709  * 
710  * Also make sure to only connect the first audio and video pad. FIXME
711  * this should eventually be handled with a tuner interface so that
712  * one can switch the streams.
713  */
714 static gboolean
715 add_sink (GstPlayBin * play_bin, GstElement * sink, GstPad * srcpad)
716 {
717   GstPad *sinkpad;
718   GstPadLinkReturn res;
719   GstElement *parent;
720
721   gst_bin_add (GST_BIN (play_bin), sink);
722
723   /* we found a sink for this stream, now try to install it */
724   sinkpad = gst_element_get_pad (sink, "sink");
725   res = gst_pad_link (srcpad, sinkpad);
726   gst_object_unref (sinkpad);
727
728   parent = gst_pad_get_parent (srcpad);
729   GST_DEBUG ("Adding sink with state %d (parent: %d, peer: %d)\n",
730       GST_STATE (sink), GST_STATE (play_bin), GST_STATE (parent));
731   gst_object_unref (parent);
732
733   /* try to link the pad of the sink to the stream */
734   if (res < 0) {
735     gchar *capsstr;
736
737     /* could not link this stream */
738     capsstr = gst_caps_to_string (gst_pad_get_caps (srcpad));
739     g_warning ("could not link %s: %d", capsstr, res);
740     g_free (capsstr);
741
742     gst_bin_remove (GST_BIN (play_bin), sink);
743   } else {
744     /* we got the sink succesfully linked, now keep the sink
745      * in out internal list */
746     play_bin->sinks = g_list_prepend (play_bin->sinks, sink);
747     gst_element_set_state (sink,
748         (GST_STATE (play_bin) == GST_STATE_PLAYING) ?
749         GST_STATE_PLAYING : GST_STATE_PAUSED);
750   }
751
752   return res;
753 }
754
755 static void
756 setup_sinks (GstPlayBaseBin * play_base_bin, GstPlayBaseGroup * group)
757 {
758   GstPlayBin *play_bin = GST_PLAY_BIN (play_base_bin);
759   GList *streaminfo = NULL, *s;
760   gboolean need_vis = FALSE;
761   gboolean need_text = FALSE;
762   GstPad *textsrcpad = NULL, *textsinkpad = NULL, *pad;
763   GstElement *sink;
764
765   /* get rid of existing sinks */
766   if (play_bin->sinks) {
767     remove_sinks (play_bin);
768   }
769   GST_DEBUG ("setupsinks");
770
771   /* find out what to do */
772   if (group->type[GST_STREAM_TYPE_VIDEO - 1].npads > 0 &&
773       group->type[GST_STREAM_TYPE_TEXT - 1].npads > 0) {
774     need_text = TRUE;
775   } else if (group->type[GST_STREAM_TYPE_VIDEO - 1].npads == 0 &&
776       group->type[GST_STREAM_TYPE_AUDIO - 1].npads > 0 &&
777       play_bin->visualisation != NULL) {
778     need_vis = TRUE;
779   }
780
781   /* now actually connect everything */
782   g_object_get (G_OBJECT (play_base_bin), "stream-info", &streaminfo, NULL);
783   for (s = streaminfo; s; s = g_list_next (s)) {
784     GObject *obj = G_OBJECT (s->data);
785     gint type;
786     GstObject *object;
787
788     g_object_get (obj, "type", &type, NULL);
789     g_object_get (obj, "object", &object, NULL);
790   }
791
792   /* link audio */
793   if (group->type[GST_STREAM_TYPE_AUDIO - 1].npads > 0) {
794     if (need_vis) {
795       sink = gen_vis_element (play_bin);
796     } else {
797       sink = gen_audio_element (play_bin);
798     }
799     pad = gst_element_get_pad (group->type[GST_STREAM_TYPE_AUDIO - 1].preroll,
800         "src");
801     add_sink (play_bin, sink, pad);
802     gst_object_unref (pad);
803   }
804
805   /* link video */
806   if (group->type[GST_STREAM_TYPE_VIDEO - 1].npads > 0) {
807     if (need_text) {
808       sink = gen_text_element (play_bin);
809
810       textsinkpad = gst_element_get_pad (sink, "text_sink");
811       textsrcpad =
812           gst_element_get_pad (group->type[GST_STREAM_TYPE_TEXT - 1].preroll,
813           "src");
814       gst_pad_link (textsrcpad, textsinkpad);
815       gst_object_unref (textsinkpad);
816       gst_object_unref (textsrcpad);
817     } else {
818       sink = gen_video_element (play_bin);
819     }
820     pad = gst_element_get_pad (group->type[GST_STREAM_TYPE_VIDEO - 1].preroll,
821         "src");
822     add_sink (play_bin, sink, pad);
823     gst_object_unref (pad);
824   }
825
826   if (play_bin->fakesink) {
827     gst_bin_remove (GST_BIN (play_bin), play_bin->fakesink);
828     play_bin->fakesink = NULL;
829   }
830 }
831
832 static GstElementStateReturn
833 gst_play_bin_change_state (GstElement * element)
834 {
835   GstElementStateReturn ret;
836   GstPlayBin *play_bin;
837   int transition;
838
839   play_bin = GST_PLAY_BIN (element);
840
841   transition = GST_STATE_TRANSITION (element);
842
843   switch (transition) {
844     case GST_STATE_READY_TO_PAUSED:
845       if (!play_bin->fakesink) {
846         play_bin->fakesink = gst_element_factory_make ("fakesink", "test");
847         gst_bin_add (GST_BIN (play_bin), play_bin->fakesink);
848       }
849       break;
850     default:
851       break;
852   }
853
854   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element);
855   if (ret == GST_STATE_FAILURE)
856     return ret;
857
858   switch (transition) {
859     case GST_STATE_PLAYING_TO_PAUSED:
860       /* Set audio sink state to NULL to release the sound device,
861        * but only if we own it (else we might be in chain-transition). */
862       //if (play_bin->audio_sink != NULL &&
863       //    GST_STATE (play_bin->audio_sink) == GST_STATE_PAUSED) {
864       //  gst_element_set_state (play_bin->audio_sink, GST_STATE_NULL);
865       //}
866       break;
867     case GST_STATE_PAUSED_TO_READY:
868       /* Check for NULL because the state transition may be done by
869        * gst_bin_dispose which is called by gst_play_bin_dispose, and in that
870        * case, we don't want to run remove_sinks.
871        * FIXME: should the NULL test be done in remove_sinks? Should we just
872        * set the state to NULL in gst_play_bin_dispose?
873        */
874       if (play_bin->cache != NULL) {
875         remove_sinks (play_bin);
876       }
877       if (play_bin->fakesink) {
878         gst_bin_remove (GST_BIN (play_bin), play_bin->fakesink);
879         play_bin->fakesink = NULL;
880       }
881       break;
882     default:
883       break;
884   }
885
886   return ret;
887 }
888
889 static gboolean
890 plugin_init (GstPlugin * plugin)
891 {
892   GST_DEBUG_CATEGORY_INIT (gst_play_bin_debug, "playbin", 0, "play bin");
893
894   return gst_element_register (plugin, "playbin", GST_RANK_NONE,
895       GST_TYPE_PLAY_BIN);
896 }
897
898 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
899     GST_VERSION_MINOR,
900     "playbin",
901     "player bin", plugin_init, VERSION, GST_LICENSE, GST_PACKAGE, GST_ORIGIN)