OGM text support, Matroska UTF-8 text support, deadlock fixes all over the place...
[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 void
675 setup_sinks (GstPlayBaseBin * play_base_bin)
676 {
677   GstPlayBin *play_bin = GST_PLAY_BIN (play_base_bin);
678   GList *streaminfo;
679   GList *s;
680   gint num_audio = 0;
681   gint num_video = 0;
682   gint num_text = 0;
683   gboolean need_vis = FALSE;
684   gboolean need_text = FALSE;
685   GstPad *textsrcpad = NULL, *textsinkpad = NULL;
686
687   /* FIXME: do this nicer, like taking a look at the installed
688    * bins and figuring out if we can simply reconnect them, remove
689    * or add them. */
690   if (GST_STATE (play_base_bin) == GST_STATE_PLAYING) {
691     remove_sinks (play_bin);
692   }
693   GST_DEBUG ("setupsinks");
694   /* get info about the stream */
695   g_object_get (G_OBJECT (play_bin), "stream-info", &streaminfo, NULL);
696
697   /* first examine the streams we have */
698   for (s = streaminfo; s; s = g_list_next (s)) {
699     GObject *obj = G_OBJECT (s->data);
700     gint type;
701     GstPad *srcpad;
702
703     g_object_get (obj, "type", &type, NULL);
704     /* we don't care about streams with their own sink */
705     if (type == 4)
706       continue;
707
708     /* else we need to get the pad */
709     g_object_get (obj, "object", &srcpad, NULL);
710
711     /* hmm.. pad is allready linked */
712     if (gst_pad_is_linked (srcpad))
713       continue;
714
715     if (type == 1) {
716       num_audio++;
717     } else if (type == 2) {
718       num_video++;
719     } else if (type == 3) {
720       num_text++;
721     }
722   }
723   /* no video, use vis */
724   if (num_video == 0 && num_audio > 0 && play_bin->visualisation) {
725     need_vis = TRUE;
726   } else if (num_video > 0 && num_text > 0) {
727     need_text = TRUE;
728   }
729
730   num_audio = 0;
731   num_video = 0;
732   num_text = 0;
733
734   /* now actually connect everything */
735   for (s = streaminfo; s; s = g_list_next (s)) {
736     GObject *obj = G_OBJECT (s->data);
737     gint type;
738     GstPad *srcpad, *sinkpad;
739     GstElement *sink = NULL;
740     gboolean res;
741     gboolean mute = FALSE;
742     GstObject *object;
743
744     g_object_get (obj, "type", &type, NULL);
745     g_object_get (obj, "object", &object, NULL);
746
747     /* use the sink elements as seek entry point */
748     if (type == 4) {
749       play_bin->seekables = g_list_prepend (play_bin->seekables, object);
750       continue;
751     }
752
753     srcpad = GST_PAD (object);
754
755     /* pas is allready linked, go to the next pad */
756     if (gst_pad_is_linked (srcpad))
757       continue;
758
759     if (type == 1) {
760       if (num_audio > 0) {
761         g_warning ("two audio streams found, playing first one");
762         mute = TRUE;
763       } else {
764         if (need_vis) {
765           sink = gen_vis_element (play_bin);
766         } else {
767           sink = gen_audio_element (play_bin);
768         }
769         num_audio++;
770       }
771     } else if (type == 2) {
772       if (num_video > 0) {
773         g_warning ("two video streams found, playing first one");
774         mute = TRUE;
775       } else {
776         if (need_text) {
777           sink = gen_text_element (play_bin);
778           textsinkpad = gst_element_get_pad (sink, "text_sink");
779         } else {
780           sink = gen_video_element (play_bin);
781         }
782         num_video++;
783       }
784     } else if (type == 3) {
785       if (num_text > 0) {
786         g_warning ("two subtitle streams found, playing first one");
787         mute = TRUE;
788       } else {
789         textsrcpad = srcpad;
790         num_text++;
791       }
792     } else if (type == 4) {
793       /* we can ignore these streams here */
794     } else {
795       g_warning ("unknown stream found");
796       mute = TRUE;
797     }
798
799     /* we found a sink for this stream, now try to install it */
800     if (sink != NULL) {
801       gst_bin_add (GST_BIN (play_bin), sink);
802       GST_DEBUG ("Adding sink with state %d (parent: %d, peer: %d)\n",
803           GST_STATE (sink), GST_STATE (play_bin),
804           GST_STATE (gst_pad_get_parent (srcpad)));
805       sinkpad = gst_element_get_pad (sink, "sink");
806
807       /* try to link the pad of the sink to the stream */
808       res = gst_pad_link (srcpad, sinkpad);
809       if (!res) {
810         gchar *capsstr;
811
812         /* could not link this stream */
813         capsstr = gst_caps_to_string (gst_pad_get_caps (srcpad));
814         g_warning ("could not link %s", capsstr);
815         g_free (capsstr);
816         GST_LOG ("removing sink %p", sink);
817         gst_bin_remove (GST_BIN (play_bin), sink);
818         mute = TRUE;
819       } else {
820         /* we got the sink succesfully linked, now keep the sink
821          * in out internal list */
822         play_bin->sinks = g_list_prepend (play_bin->sinks, sink);
823       }
824     }
825     if (mute) {
826       /* no sink found/needed for this stream. We mute the stream
827        * so that it does not take any resources */
828       g_object_set (G_OBJECT (obj), "mute", TRUE, NULL);
829     }
830   }
831
832   /* if subtitles, link */
833   if (textsrcpad && num_video > 0) {
834     gst_pad_link (textsrcpad, textsinkpad);
835   }
836 }
837
838 static GstElementStateReturn
839 gst_play_bin_change_state (GstElement * element)
840 {
841   GstElementStateReturn ret;
842   GstPlayBin *play_bin;
843   int transition;
844
845   play_bin = GST_PLAY_BIN (element);
846
847   transition = GST_STATE_TRANSITION (element);
848
849   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element);
850   if (ret == GST_STATE_FAILURE)
851     return ret;
852
853   switch (transition) {
854     case GST_STATE_PLAYING_TO_PAUSED:
855       /* Set audio sink state to NULL to release the sound device,
856        * but only if we own it (else we might be in chain-transition). */
857       if (play_bin->audio_sink != NULL &&
858           GST_STATE (play_bin->audio_sink) == GST_STATE_PAUSED) {
859         gst_element_set_state (play_bin->audio_sink, GST_STATE_NULL);
860       }
861       break;
862     case GST_STATE_PAUSED_TO_READY:
863       /* Check for NULL because the state transition may be done by
864        * gst_bin_dispose which is called by gst_play_bin_dispose, and in that
865        * case, we don't want to run remove_sinks.
866        * FIXME: should the NULL test be done in remove_sinks? Should we just
867        * set the state to NULL in gst_play_bin_dispose?
868        */
869       if (play_bin->cache != NULL) {
870         remove_sinks (play_bin);
871       }
872       break;
873     default:
874       break;
875   }
876
877   return ret;
878 }
879
880
881 static const GstEventMask *
882 gst_play_bin_get_event_masks (GstElement * element)
883 {
884   /* FIXME, get the list from the number of installed sinks */
885   return NULL;
886 }
887
888 /* send an event to all the sinks */
889 static gboolean
890 gst_play_bin_send_event (GstElement * element, GstEvent * event)
891 {
892   gboolean res = FALSE;
893   GList *s;
894   GstPlayBin *play_bin;
895   GstElementState state;
896   gboolean need_pause = FALSE;
897
898   play_bin = GST_PLAY_BIN (element);
899
900   state = gst_element_get_state (element);
901   /* we pause the pipeline first before sending the event. We only
902    * do this if the pipeline was playing. */
903   if (state == GST_STATE_PLAYING) {
904     need_pause = TRUE;
905     gst_element_set_state (element, GST_STATE_PAUSED);
906   }
907
908   /* loop over all seekables and send the event to them */
909   for (s = play_bin->seekables; s; s = g_list_next (s)) {
910     GstElement *element = GST_ELEMENT (s->data);
911     gboolean ok;
912
913     /* ref each event before sending it */
914     gst_event_ref (event);
915     ok = gst_element_send_event (element, event);
916     res |= ok;
917   }
918   gst_event_unref (event);
919
920   /* and restart the pipeline if we paused it */
921   if (need_pause)
922     gst_element_set_state (element, GST_STATE_PLAYING);
923
924   return res;
925 }
926
927 static const GstFormat *
928 gst_play_bin_get_formats (GstElement * element)
929 {
930   /* FIXME, compile this list from the installed sinks */
931   static GstFormat formats[] = {
932     GST_FORMAT_TIME,
933     0,
934   };
935
936   return formats;
937 }
938
939 static gboolean
940 gst_play_bin_convert (GstElement * element,
941     GstFormat src_format, gint64 src_value,
942     GstFormat * dest_format, gint64 * dest_value)
943 {
944   gboolean res = FALSE;
945   GList *s;
946   GstPlayBin *play_bin;
947
948   play_bin = GST_PLAY_BIN (element);
949
950   /* do a conversion, loop over all sinks, stop as soon as one of the
951    * sinks returns a successful result */
952   for (s = play_bin->seekables; s; s = g_list_next (s)) {
953     GstElement *element = GST_ELEMENT (s->data);
954
955     res = gst_element_convert (element, src_format, src_value,
956         dest_format, dest_value);
957     if (res)
958       break;
959   }
960   return res;
961 }
962
963 static const GstQueryType *
964 gst_play_bin_get_query_types (GstElement * element)
965 {
966   /* FIXME, compile from the installed sinks */
967   static const GstQueryType query_types[] = {
968     GST_QUERY_TOTAL,
969     GST_QUERY_POSITION,
970     0
971   };
972
973   return query_types;
974 }
975
976 static gboolean
977 gst_play_bin_query (GstElement * element, GstQueryType type,
978     GstFormat * format, gint64 * value)
979 {
980   gboolean res = FALSE;
981   GList *s;
982   GstPlayBin *play_bin;
983
984   play_bin = GST_PLAY_BIN (element);
985
986   for (s = play_bin->seekables; s; s = g_list_next (s)) {
987     GstElement *element = GST_ELEMENT (s->data);
988
989     res = gst_element_query (element, type, format, value);
990     if (res)
991       break;
992   }
993   return res;
994 }
995
996 static gboolean
997 plugin_init (GstPlugin * plugin)
998 {
999   GST_DEBUG_CATEGORY_INIT (gst_play_bin_debug, "playbin", 0, "play bin");
1000
1001   return gst_element_register (plugin, "playbin", GST_RANK_NONE,
1002       GST_TYPE_PLAY_BIN);
1003 }
1004
1005 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1006     GST_VERSION_MINOR,
1007     "playbin",
1008     "player bin", plugin_init, VERSION, GST_LICENSE, GST_PACKAGE, GST_ORIGIN)