gst/playback/: Adding stream selection support plus required properties for applicati...
[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 an element for playback of video with subtitles embedded.
433  *
434  *  +--------------------------------------------------+
435  *  | tbin                  +-------------+            |
436  *  |          +-----+      | textoverlay |   +------+ |
437  *  |          | csp | +--video_sink      |   | vbin | |
438  * video_sink-sink  src+ +-text_sink     src-sink    | |
439  *  |          +-----+   |  +-------------+   +------+ |
440  * text_sink-------------+                             |
441  *  +--------------------------------------------------+
442  */
443
444 static GstElement *
445 gen_text_element (GstPlayBin * play_bin)
446 {
447   GstElement *element, *csp, *overlay, *vbin;
448
449   overlay = gst_element_factory_make ("textoverlay", "overlay");
450   g_object_set (G_OBJECT (overlay),
451       "halign", "center", "valign", "bottom", NULL);
452   vbin = gen_video_element (play_bin);
453   if (!overlay) {
454     g_warning ("No overlay (pango) element, subtitles disabled");
455     return vbin;
456   }
457   csp = gst_element_factory_make ("ffmpegcolorspace", "subtitlecsp");
458   element = gst_bin_new ("textbin");
459   gst_element_link_many (csp, overlay, vbin, NULL);
460   gst_bin_add_many (GST_BIN (element), csp, overlay, vbin, NULL);
461
462   gst_element_add_ghost_pad (element,
463       gst_element_get_pad (overlay, "text_sink"), "text_sink");
464   gst_element_add_ghost_pad (element,
465       gst_element_get_pad (csp, "sink"), "sink");
466
467   return element;
468 }
469
470 /* make the element (bin) that contains the elements needed to perform
471  * audio playback. 
472  *
473  *  +-------------------------------------------------------------+
474  *  | abin                                                        |
475  *  |      +---------+   +----------+   +---------+   +---------+ |
476  *  |      |audioconv|   |audioscale|   | volume  |   |audiosink| |
477  *  |   +-sink      src-sink       src-sink      src-sink       | |
478  *  |   |  +---------+   +----------+   +---------+   +---------+ |
479  * sink-+                                                         |
480  *  +-------------------------------------------------------------+
481  *                  
482  */
483 static GstElement *
484 gen_audio_element (GstPlayBin * play_bin)
485 {
486   GstElement *element;
487   GstElement *conv;
488   GstElement *sink;
489   GstElement *volume;
490   GstElement *scale;
491
492   element = g_hash_table_lookup (play_bin->cache, "abin");
493   if (element != NULL) {
494     sink = g_hash_table_lookup (play_bin->cache, "audio_sink");
495     goto done;
496   }
497   element = gst_bin_new ("abin");
498   conv = gst_element_factory_make ("audioconvert", "aconv");
499   scale = gst_element_factory_make ("audioscale", "ascale");
500
501   volume = gst_element_factory_make ("volume", "volume");
502   g_object_set (G_OBJECT (volume), "volume", play_bin->volume, NULL);
503   play_bin->volume_element = volume;
504
505   if (play_bin->audio_sink) {
506     sink = play_bin->audio_sink;
507   } else {
508     sink = gst_element_factory_make ("osssink", "sink");
509     play_bin->audio_sink = GST_ELEMENT (gst_object_ref (GST_OBJECT (sink)));
510   }
511
512   gst_object_ref (GST_OBJECT (sink));
513   g_hash_table_insert (play_bin->cache, "audio_sink", sink);
514
515   gst_bin_add (GST_BIN (element), conv);
516   gst_bin_add (GST_BIN (element), scale);
517   gst_bin_add (GST_BIN (element), volume);
518   gst_bin_add (GST_BIN (element), sink);
519
520   gst_element_link_pads (conv, "src", scale, "sink");
521   gst_element_link_pads (scale, "src", volume, "sink");
522   gst_element_link_pads (volume, "src", sink, "sink");
523
524   gst_element_add_ghost_pad (element,
525       gst_element_get_pad (conv, "sink"), "sink");
526
527   gst_element_set_state (element, GST_STATE_READY);
528
529   /* since we're gonna add it to a bin but don't want to lose it,
530    * we keep a reference. */
531   gst_object_ref (GST_OBJECT (element));
532   g_hash_table_insert (play_bin->cache, "abin", element);
533
534 done:
535   play_bin->seekables = g_list_prepend (play_bin->seekables, sink);
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;
573   GstElement *vthread;
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   vthread = gst_element_factory_make ("thread", "vthread");
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 (vthread), vsink);
587   gst_bin_add (GST_BIN (element), vthread);
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 (GST_OBJECT (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   gst_pad_link (gst_element_get_request_pad (tee, "src%d"),
607       gst_element_get_pad (asink, "sink"));
608
609   gst_pad_link (gst_element_get_request_pad (tee, "src%d"),
610       gst_element_get_pad (conv, "sink"));
611
612   gst_element_add_ghost_pad (element,
613       gst_element_get_pad (tee, "sink"), "sink");
614
615   return element;
616 }
617
618 /* get rid of all installed sinks */
619 static void
620 remove_sinks (GstPlayBin * play_bin)
621 {
622   GList *sinks;
623   GstObject *parent;
624   GstElement *element;
625
626   GST_DEBUG ("removesinks");
627   element = g_hash_table_lookup (play_bin->cache, "abin");
628   if (element != NULL) {
629     parent = gst_element_get_parent (element);
630     if (parent != NULL) {
631       /* we remove the element from the parent so that
632        * there is no unwanted state change when the parent
633        * is disposed */
634       gst_bin_remove (GST_BIN (parent), element);
635     }
636   }
637   element = g_hash_table_lookup (play_bin->cache, "vbin");
638   if (element != NULL) {
639     parent = gst_element_get_parent (element);
640     if (parent != NULL) {
641       gst_bin_remove (GST_BIN (parent), element);
642     }
643   }
644
645   for (sinks = play_bin->sinks; sinks; sinks = g_list_next (sinks)) {
646     GstElement *element = GST_ELEMENT (sinks->data);
647     GstPad *pad = gst_element_get_pad (element, "sink");
648
649     GST_LOG ("removing sink %p", element);
650     if (GST_PAD_PEER (pad))
651       gst_pad_unlink (GST_PAD_PEER (pad), pad);
652     gst_bin_remove (GST_BIN (play_bin), element);
653   }
654   g_list_free (play_bin->sinks);
655   g_list_free (play_bin->seekables);
656   play_bin->sinks = NULL;
657   play_bin->seekables = NULL;
658
659   if (play_bin->frame) {
660     gst_buffer_unref (play_bin->frame);
661     play_bin->frame = NULL;
662   }
663 }
664
665 /* loop over the streams and set up the pipeline to play this
666  * media file. First we count the number of audio and video streams.
667  * If there is no video stream but there exists an audio stream,
668  * we install a visualisation pipeline.
669  * 
670  * Also make sure to only connect the first audio and video pad. FIXME
671  * this should eventually be handled with a tuner interface so that
672  * one can switch the streams.
673  */
674 static gboolean
675 add_sink (GstPlayBin * play_bin, GstElement * sink, GstPad * srcpad)
676 {
677   GstPad *sinkpad;
678   gboolean res;
679
680   /* we found a sink for this stream, now try to install it */
681   gst_bin_add (GST_BIN (play_bin), sink);
682   GST_DEBUG ("Adding sink with state %d (parent: %d, peer: %d)\n",
683       GST_STATE (sink), GST_STATE (play_bin),
684       GST_STATE (gst_pad_get_parent (srcpad)));
685   sinkpad = gst_element_get_pad (sink, "sink");
686
687   /* try to link the pad of the sink to the stream */
688   res = gst_pad_link (srcpad, sinkpad);
689   if (!res) {
690     gchar *capsstr;
691
692     /* could not link this stream */
693     capsstr = gst_caps_to_string (gst_pad_get_caps (srcpad));
694     g_warning ("could not link %s", capsstr);
695     g_free (capsstr);
696     GST_LOG ("removing sink %p", sink);
697     gst_bin_remove (GST_BIN (play_bin), sink);
698   } else {
699     /* we got the sink succesfully linked, now keep the sink
700      * in out internal list */
701     play_bin->sinks = g_list_prepend (play_bin->sinks, sink);
702   }
703
704   return res;
705 }
706
707 static void
708 setup_sinks (GstPlayBaseBin * play_base_bin)
709 {
710   GstPlayBin *play_bin = GST_PLAY_BIN (play_base_bin);
711   GstPlayBaseGroup *group;
712   GList *streaminfo = NULL, *s;
713   gboolean need_vis = FALSE;
714   gboolean need_text = FALSE;
715   GstPad *textsrcpad = NULL, *textsinkpad = NULL;
716   GstElement *sink;
717
718   /* get rid of existing sinks */
719   if (play_bin->sinks) {
720     remove_sinks (play_bin);
721   }
722   GST_DEBUG ("setupsinks");
723
724   /* find out what to do */
725   group = play_base_bin->queued_groups->data;
726   if (group->type[GST_STREAM_TYPE_VIDEO - 1].npads > 0 &&
727       group->type[GST_STREAM_TYPE_TEXT - 1].npads > 0) {
728     need_text = TRUE;
729   } else if (group->type[GST_STREAM_TYPE_VIDEO - 1].npads == 0 &&
730       group->type[GST_STREAM_TYPE_AUDIO - 1].npads > 0 &&
731       play_bin->visualisation != NULL) {
732     need_vis = TRUE;
733   }
734
735   /* now actually connect everything */
736   g_object_get (G_OBJECT (play_base_bin), "stream-info", &streaminfo, NULL);
737   for (s = streaminfo; s; s = g_list_next (s)) {
738     GObject *obj = G_OBJECT (s->data);
739     gint type;
740     GstObject *object;
741
742     g_object_get (obj, "type", &type, NULL);
743     g_object_get (obj, "object", &object, NULL);
744
745     /* use the sink elements as seek entry point */
746     if (type == 4) {
747       play_bin->seekables = g_list_prepend (play_bin->seekables, object);
748     }
749   }
750
751   /* link audio */
752   if (group->type[GST_STREAM_TYPE_AUDIO - 1].npads > 0) {
753     if (need_vis) {
754       sink = gen_vis_element (play_bin);
755     } else {
756       sink = gen_audio_element (play_bin);
757     }
758     //gst_element_link (group->type[GST_STREAM_TYPE_AUDIO - 1].preroll, sink);
759     add_sink (play_bin, sink,
760         gst_element_get_pad (group->type[GST_STREAM_TYPE_AUDIO - 1].preroll,
761             "src"));
762   }
763
764   /* link video */
765   if (group->type[GST_STREAM_TYPE_VIDEO - 1].npads > 0) {
766     if (need_text) {
767       sink = gen_text_element (play_bin);
768
769       textsinkpad = gst_element_get_pad (sink, "text_sink");
770       textsrcpad =
771           gst_element_get_pad (group->type[GST_STREAM_TYPE_TEXT - 1].preroll,
772           "src");
773       if (textsinkpad && textsrcpad) {
774         gst_pad_link (textsrcpad, textsinkpad);
775       }
776     } else {
777       sink = gen_video_element (play_bin);
778     }
779     //gst_element_link (group->type[GST_STREAM_TYPE_VIDEO - 1].preroll, sink);
780     add_sink (play_bin, sink,
781         gst_element_get_pad (group->type[GST_STREAM_TYPE_VIDEO - 1].preroll,
782             "src"));
783   }
784 }
785
786 static GstElementStateReturn
787 gst_play_bin_change_state (GstElement * element)
788 {
789   GstElementStateReturn ret;
790   GstPlayBin *play_bin;
791   int transition;
792
793   play_bin = GST_PLAY_BIN (element);
794
795   transition = GST_STATE_TRANSITION (element);
796
797   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element);
798   if (ret == GST_STATE_FAILURE)
799     return ret;
800
801   switch (transition) {
802     case GST_STATE_PLAYING_TO_PAUSED:
803       /* Set audio sink state to NULL to release the sound device,
804        * but only if we own it (else we might be in chain-transition). */
805       if (play_bin->audio_sink != NULL &&
806           GST_STATE (play_bin->audio_sink) == GST_STATE_PAUSED) {
807         gst_element_set_state (play_bin->audio_sink, GST_STATE_NULL);
808       }
809       break;
810     case GST_STATE_PAUSED_TO_READY:
811       /* Check for NULL because the state transition may be done by
812        * gst_bin_dispose which is called by gst_play_bin_dispose, and in that
813        * case, we don't want to run remove_sinks.
814        * FIXME: should the NULL test be done in remove_sinks? Should we just
815        * set the state to NULL in gst_play_bin_dispose?
816        */
817       if (play_bin->cache != NULL) {
818         remove_sinks (play_bin);
819       }
820       break;
821     default:
822       break;
823   }
824
825   return ret;
826 }
827
828
829 static const GstEventMask *
830 gst_play_bin_get_event_masks (GstElement * element)
831 {
832   /* FIXME, get the list from the number of installed sinks */
833   return NULL;
834 }
835
836 /* send an event to all the sinks */
837 static gboolean
838 gst_play_bin_send_event (GstElement * element, GstEvent * event)
839 {
840   gboolean res = FALSE;
841   GList *s;
842   GstPlayBin *play_bin;
843   GstElementState state;
844   gboolean need_pause = FALSE;
845
846   play_bin = GST_PLAY_BIN (element);
847
848   state = gst_element_get_state (element);
849   /* we pause the pipeline first before sending the event. We only
850    * do this if the pipeline was playing. */
851   if (state == GST_STATE_PLAYING) {
852     need_pause = TRUE;
853     gst_element_set_state (element, GST_STATE_PAUSED);
854   }
855
856   /* loop over all seekables and send the event to them */
857   for (s = play_bin->seekables; s; s = g_list_next (s)) {
858     GstElement *element = GST_ELEMENT (s->data);
859     gboolean ok;
860
861     /* ref each event before sending it */
862     gst_event_ref (event);
863     ok = gst_element_send_event (element, event);
864     res |= ok;
865   }
866   gst_event_unref (event);
867
868   /* and restart the pipeline if we paused it */
869   if (need_pause)
870     gst_element_set_state (element, GST_STATE_PLAYING);
871
872   return res;
873 }
874
875 static const GstFormat *
876 gst_play_bin_get_formats (GstElement * element)
877 {
878   /* FIXME, compile this list from the installed sinks */
879   static GstFormat formats[] = {
880     GST_FORMAT_TIME,
881     0,
882   };
883
884   return formats;
885 }
886
887 static gboolean
888 gst_play_bin_convert (GstElement * element,
889     GstFormat src_format, gint64 src_value,
890     GstFormat * dest_format, gint64 * dest_value)
891 {
892   gboolean res = FALSE;
893   GList *s;
894   GstPlayBin *play_bin;
895
896   play_bin = GST_PLAY_BIN (element);
897
898   /* do a conversion, loop over all sinks, stop as soon as one of the
899    * sinks returns a successful result */
900   for (s = play_bin->seekables; s; s = g_list_next (s)) {
901     GstElement *element = GST_ELEMENT (s->data);
902
903     res = gst_element_convert (element, src_format, src_value,
904         dest_format, dest_value);
905     if (res)
906       break;
907   }
908   return res;
909 }
910
911 static const GstQueryType *
912 gst_play_bin_get_query_types (GstElement * element)
913 {
914   /* FIXME, compile from the installed sinks */
915   static const GstQueryType query_types[] = {
916     GST_QUERY_TOTAL,
917     GST_QUERY_POSITION,
918     0
919   };
920
921   return query_types;
922 }
923
924 static gboolean
925 gst_play_bin_query (GstElement * element, GstQueryType type,
926     GstFormat * format, gint64 * value)
927 {
928   gboolean res = FALSE;
929   GList *s;
930   GstPlayBin *play_bin;
931
932   play_bin = GST_PLAY_BIN (element);
933
934   for (s = play_bin->seekables; s; s = g_list_next (s)) {
935     GstElement *element = GST_ELEMENT (s->data);
936
937     res = gst_element_query (element, type, format, value);
938     if (res)
939       break;
940   }
941   return res;
942 }
943
944 static gboolean
945 plugin_init (GstPlugin * plugin)
946 {
947   GST_DEBUG_CATEGORY_INIT (gst_play_bin_debug, "playbin", 0, "play bin");
948
949   return gst_element_register (plugin, "playbin", GST_RANK_NONE,
950       GST_TYPE_PLAY_BIN);
951 }
952
953 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
954     GST_VERSION_MINOR,
955     "playbin",
956     "player bin", plugin_init, VERSION, GST_LICENSE, GST_PACKAGE, GST_ORIGIN)