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