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