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