*.c: Don't cast to GstObject before reffing/unreffing.
[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 *fakesink;
49   GstElement *audio_sink;
50   GstElement *video_sink;
51   GstElement *visualisation;
52   GstElement *volume_element;
53   GstElement *textoverlay_element;
54   gfloat volume;
55
56   /* these are the currently active sinks */
57   GList *sinks;
58
59   /* the last captured frame for snapshots */
60   GstBuffer *frame;
61
62   /* our cache for the sinks */
63   GHashTable *cache;
64
65   /* font description */
66   gchar *font_desc;
67 };
68
69 struct _GstPlayBinClass
70 {
71   GstPlayBaseBinClass parent_class;
72 };
73
74 /* props */
75 enum
76 {
77   ARG_0,
78   ARG_AUDIO_SINK,
79   ARG_VIDEO_SINK,
80   ARG_VIS_PLUGIN,
81   ARG_VOLUME,
82   ARG_FRAME,
83   ARG_FONT_DESC
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 setup_sinks (GstPlayBaseBin * play_base_bin,
97     GstPlayBaseGroup * group);
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 GstElementClass *parent_class;
107
108 //static guint gst_play_bin_signals[LAST_SIGNAL] = { 0 };
109
110 static GstElementDetails gst_play_bin_details = {
111   "Player Bin",
112   "Generic/Bin/Player",
113   "Autoplug and play media from an uri",
114   "Wim Taymans <wim@fluendo.com>"
115 };
116
117 static GType
118 gst_play_bin_get_type (void)
119 {
120   static GType gst_play_bin_type = 0;
121
122   if (!gst_play_bin_type) {
123     static const GTypeInfo gst_play_bin_info = {
124       sizeof (GstPlayBinClass),
125       NULL,
126       NULL,
127       (GClassInitFunc) gst_play_bin_class_init,
128       NULL,
129       NULL,
130       sizeof (GstPlayBin),
131       0,
132       (GInstanceInitFunc) gst_play_bin_init,
133       NULL
134     };
135
136     gst_play_bin_type = g_type_register_static (GST_TYPE_PLAY_BASE_BIN,
137         "GstPlayBin", &gst_play_bin_info, 0);
138   }
139
140   return gst_play_bin_type;
141 }
142
143 static void
144 gst_play_bin_class_init (GstPlayBinClass * klass)
145 {
146   GObjectClass *gobject_klass;
147   GstElementClass *gstelement_klass;
148   GstBinClass *gstbin_klass;
149   GstPlayBaseBinClass *playbasebin_klass;
150
151   gobject_klass = (GObjectClass *) klass;
152   gstelement_klass = (GstElementClass *) klass;
153   gstbin_klass = (GstBinClass *) klass;
154   playbasebin_klass = (GstPlayBaseBinClass *) klass;
155
156   parent_class = g_type_class_ref (gst_play_base_bin_get_type ());
157
158   gobject_klass->set_property = gst_play_bin_set_property;
159   gobject_klass->get_property = gst_play_bin_get_property;
160
161   g_object_class_install_property (gobject_klass, ARG_VIDEO_SINK,
162       g_param_spec_object ("video-sink", "Video Sink",
163           "the video output element to use (NULL = default sink)",
164           GST_TYPE_ELEMENT, G_PARAM_READWRITE));
165   g_object_class_install_property (gobject_klass, ARG_AUDIO_SINK,
166       g_param_spec_object ("audio-sink", "Audio Sink",
167           "the audio output element to use (NULL = default sink)",
168           GST_TYPE_ELEMENT, G_PARAM_READWRITE));
169   g_object_class_install_property (gobject_klass, ARG_VIS_PLUGIN,
170       g_param_spec_object ("vis-plugin", "Vis plugin",
171           "the visualization element to use (NULL = none)",
172           GST_TYPE_ELEMENT, G_PARAM_READWRITE));
173   g_object_class_install_property (gobject_klass, ARG_VOLUME,
174       g_param_spec_double ("volume", "volume", "volume",
175           0.0, VOLUME_MAX_DOUBLE, 1.0, G_PARAM_READWRITE));
176   g_object_class_install_property (gobject_klass, ARG_FRAME,
177       gst_param_spec_mini_object ("frame", "Frame",
178           "The last frame (NULL = no video available)",
179           GST_TYPE_BUFFER, G_PARAM_READABLE));
180   g_object_class_install_property (gobject_klass, ARG_FONT_DESC,
181       g_param_spec_string ("subtitle-font-desc",
182           "Subtitle font description",
183           "Pango font description of font "
184           "to be used for subtitle rendering", NULL, G_PARAM_WRITABLE));
185
186   gobject_klass->dispose = GST_DEBUG_FUNCPTR (gst_play_bin_dispose);
187
188   gst_element_class_set_details (gstelement_klass, &gst_play_bin_details);
189
190   gstelement_klass->change_state =
191       GST_DEBUG_FUNCPTR (gst_play_bin_change_state);
192
193   playbasebin_klass->setup_output_pads = setup_sinks;
194 }
195
196 static void
197 gst_play_bin_init (GstPlayBin * play_bin)
198 {
199   play_bin->video_sink = NULL;
200   play_bin->audio_sink = NULL;
201   play_bin->visualisation = NULL;
202   play_bin->volume_element = NULL;
203   play_bin->textoverlay_element = NULL;
204   play_bin->volume = 1.0;
205   play_bin->sinks = NULL;
206   play_bin->frame = NULL;
207   play_bin->font_desc = NULL;
208   play_bin->cache = g_hash_table_new_full (g_str_hash, g_str_equal,
209       NULL, (GDestroyNotify) gst_object_unref);
210 }
211
212 static void
213 gst_play_bin_dispose (GObject * object)
214 {
215   GstPlayBin *play_bin;
216
217   play_bin = GST_PLAY_BIN (object);
218
219   if (play_bin->cache != NULL) {
220     remove_sinks (play_bin);
221     g_hash_table_destroy (play_bin->cache);
222     play_bin->cache = NULL;
223   }
224
225   if (play_bin->audio_sink != NULL) {
226     gst_object_unref (play_bin->audio_sink);
227     play_bin->audio_sink = NULL;
228   }
229   if (play_bin->video_sink != NULL) {
230     gst_object_unref (play_bin->video_sink);
231     play_bin->video_sink = NULL;
232   }
233   if (play_bin->visualisation != NULL) {
234     gst_object_unref (play_bin->visualisation);
235     play_bin->visualisation = NULL;
236   }
237   g_free (play_bin->font_desc);
238   play_bin->font_desc = NULL;
239
240   G_OBJECT_CLASS (parent_class)->dispose (object);
241 }
242
243
244 static void
245 gst_play_bin_set_property (GObject * object, guint prop_id,
246     const GValue * value, GParamSpec * pspec)
247 {
248   GstPlayBin *play_bin;
249
250   g_return_if_fail (GST_IS_PLAY_BIN (object));
251
252   play_bin = GST_PLAY_BIN (object);
253
254   switch (prop_id) {
255     case ARG_VIDEO_SINK:
256       if (play_bin->video_sink != NULL) {
257         gst_object_unref (play_bin->video_sink);
258       }
259       play_bin->video_sink = g_value_get_object (value);
260       if (play_bin->video_sink != NULL) {
261         gst_object_ref (play_bin->video_sink);
262         gst_object_sink (GST_OBJECT (play_bin->video_sink));
263       }
264       /* when changing the videosink, we just remove the
265        * video pipeline from the cache so that it will be 
266        * regenerated with the new sink element */
267       g_hash_table_remove (play_bin->cache, "vbin");
268       break;
269     case ARG_AUDIO_SINK:
270       if (play_bin->audio_sink != NULL) {
271         gst_object_unref (play_bin->audio_sink);
272       }
273       play_bin->audio_sink = g_value_get_object (value);
274       if (play_bin->audio_sink != NULL) {
275         gst_object_ref (play_bin->audio_sink);
276         gst_object_sink (GST_OBJECT (play_bin->audio_sink));
277       }
278       g_hash_table_remove (play_bin->cache, "abin");
279       break;
280     case ARG_VIS_PLUGIN:
281       if (play_bin->visualisation != NULL) {
282         gst_object_unref (play_bin->visualisation);
283       }
284       play_bin->visualisation = g_value_get_object (value);
285       if (play_bin->visualisation != NULL) {
286         gst_object_ref (play_bin->visualisation);
287         gst_object_sink (GST_OBJECT (play_bin->visualisation));
288       }
289       break;
290     case ARG_VOLUME:
291       play_bin->volume = g_value_get_double (value);
292       if (play_bin->volume_element) {
293         g_object_set (G_OBJECT (play_bin->volume_element), "volume",
294             play_bin->volume, NULL);
295       }
296       break;
297     case ARG_FONT_DESC:
298       g_free (play_bin->font_desc);
299       play_bin->font_desc = g_strdup (g_value_get_string (value));
300       if (play_bin->textoverlay_element) {
301         g_object_set (G_OBJECT (play_bin->textoverlay_element),
302             "font-desc", g_value_get_string (value), NULL);
303       }
304       break;
305     default:
306       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
307       break;
308   }
309 }
310
311 static void
312 gst_play_bin_get_property (GObject * object, guint prop_id, GValue * value,
313     GParamSpec * pspec)
314 {
315   GstPlayBin *play_bin;
316
317   g_return_if_fail (GST_IS_PLAY_BIN (object));
318
319   play_bin = GST_PLAY_BIN (object);
320
321   switch (prop_id) {
322     case ARG_VIDEO_SINK:
323       g_value_set_object (value, play_bin->video_sink);
324       break;
325     case ARG_AUDIO_SINK:
326       g_value_set_object (value, play_bin->audio_sink);
327       break;
328     case ARG_VIS_PLUGIN:
329       g_value_set_object (value, play_bin->visualisation);
330       break;
331     case ARG_VOLUME:
332       g_value_set_double (value, play_bin->volume);
333       break;
334     case ARG_FRAME:
335       g_value_set_boxed (value, play_bin->frame);
336       break;
337     default:
338       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
339       break;
340   }
341 }
342
343 /* signal fired when the identity has received a new buffer. This is used for
344  * making screenshots.
345  */
346 static void
347 handoff (GstElement * identity, GstBuffer * frame, gpointer data)
348 {
349   GstPlayBin *play_bin = GST_PLAY_BIN (data);
350
351   if (play_bin->frame) {
352     gst_buffer_unref (play_bin->frame);
353   }
354   play_bin->frame = gst_buffer_ref (frame);
355 }
356
357 /* make the element (bin) that contains the elements needed to perform
358  * video display. We connect a handoff signal to identity so that we
359  * can grab snapshots. Identity's sinkpad is ghosted to vbin.
360  *
361  *  +-------------------------------------------------------------+
362  *  | vbin                                                        |
363  *  |      +--------+   +----------+   +----------+   +---------+ |
364  *  |      |identity|   |colorspace|   |videoscale|   |videosink| |
365  *  |   +-sink     src-sink       src-sink       src-sink       | |
366  *  |   |  +---+----+   +----------+   +----------+   +---------+ |
367  * sink-+      |                                                  |
368  *  +----------|--------------------------------------------------+
369  *           handoff
370  */
371 static GstElement *
372 gen_video_element (GstPlayBin * play_bin)
373 {
374   GstElement *element;
375   GstElement *conv;
376
377   //GstElement *scale;
378   GstElement *sink;
379   GstElement *identity;
380   GstPad *pad;
381
382   /* first see if we have it in the cache */
383   element = g_hash_table_lookup (play_bin->cache, "vbin");
384   if (element != NULL) {
385     return element;
386   }
387
388   element = gst_bin_new ("vbin");
389   identity = gst_element_factory_make ("identity", "id");
390   g_signal_connect (identity, "handoff", G_CALLBACK (handoff), play_bin);
391   conv = gst_element_factory_make ("ffmpegcolorspace", "vconv");
392   //scale = gst_element_factory_make ("videoscale", "vscale");
393   if (play_bin->video_sink) {
394     sink = play_bin->video_sink;
395   } else {
396     sink = gst_element_factory_make ("xvimagesink", "videosink");
397   }
398   gst_object_ref (sink);
399   g_hash_table_insert (play_bin->cache, "video_sink", sink);
400
401   gst_bin_add (GST_BIN (element), identity);
402   gst_bin_add (GST_BIN (element), conv);
403   //gst_bin_add (GST_BIN (element), scale);
404   gst_bin_add (GST_BIN (element), sink);
405   gst_element_link_pads (identity, "src", conv, "sink");
406   gst_element_link_pads (conv, "src",   /*scale, "sink");
407                                            gst_element_link_pads (scale, "src", */ sink, "sink");
408
409   pad = gst_element_get_pad (identity, "sink");
410   gst_element_add_pad (element, gst_ghost_pad_new ("sink", pad));
411   gst_object_unref (pad);
412
413   gst_element_set_state (element, GST_STATE_READY);
414
415   /* since we're gonna add it to a bin but don't want to lose it,
416    * we keep a reference. */
417   gst_object_ref (element);
418   g_hash_table_insert (play_bin->cache, "vbin", element);
419
420   return element;
421 }
422
423 /* make an element for playback of video with subtitles embedded.
424  *
425  *  +--------------------------------------------------+
426  *  | tbin                  +-------------+            |
427  *  |          +-----+      | textoverlay |   +------+ |
428  *  |          | csp | +--video_sink      |   | vbin | |
429  * video_sink-sink  src+ +-text_sink     src-sink    | |
430  *  |          +-----+   |  +-------------+   +------+ |
431  * text_sink-------------+                             |
432  *  +--------------------------------------------------+
433  */
434
435 static GstElement *
436 gen_text_element (GstPlayBin * play_bin)
437 {
438   GstElement *element, *csp, *overlay, *vbin;
439   GstPad *pad;
440
441   overlay = gst_element_factory_make ("textoverlay", "overlay");
442   g_object_set (G_OBJECT (overlay),
443       "halign", "center", "valign", "bottom", NULL);
444   play_bin->textoverlay_element = overlay;
445   if (play_bin->font_desc) {
446     g_object_set (G_OBJECT (play_bin->textoverlay_element),
447         "font-desc", play_bin->font_desc, NULL);
448   }
449   vbin = gen_video_element (play_bin);
450   if (!overlay) {
451     g_warning ("No overlay (pango) element, subtitles disabled");
452     return vbin;
453   }
454   csp = gst_element_factory_make ("ffmpegcolorspace", "subtitlecsp");
455   element = gst_bin_new ("textbin");
456   gst_element_link_many (csp, overlay, vbin, NULL);
457   gst_bin_add_many (GST_BIN (element), csp, overlay, vbin, NULL);
458
459   pad = gst_element_get_pad (overlay, "text_sink");
460 #define gst_element_add_ghost_pad(element, pad, name) \
461     gst_element_add_pad (element, gst_ghost_pad_new (name, pad))
462   gst_element_add_ghost_pad (element, pad, "text_sink");
463   gst_object_unref (pad);
464
465   pad = gst_element_get_pad (csp, "sink");
466   gst_element_add_ghost_pad (element, pad, "sink");
467   gst_object_unref (pad);
468
469   return element;
470 }
471
472 /* make the element (bin) that contains the elements needed to perform
473  * audio playback. 
474  *
475  *  +-------------------------------------------------------------+
476  *  | abin                                                        |
477  *  |      +---------+   +----------+   +---------+   +---------+ |
478  *  |      |audioconv|   |audioscale|   | volume  |   |audiosink| |
479  *  |   +-sink      src-sink       src-sink      src-sink       | |
480  *  |   |  +---------+   +----------+   +---------+   +---------+ |
481  * sink-+                                                         |
482  *  +-------------------------------------------------------------+
483  *                  
484  */
485 static GstElement *
486 gen_audio_element (GstPlayBin * play_bin)
487 {
488   GstElement *element;
489   GstElement *conv;
490   GstElement *sink;
491   GstElement *volume;
492   GstElement *scale;
493   GstPad *pad;
494
495   element = g_hash_table_lookup (play_bin->cache, "abin");
496   if (element != NULL) {
497     return element;
498   }
499   element = gst_bin_new ("abin");
500   conv = gst_element_factory_make ("audioconvert", "aconv");
501   scale = gst_element_factory_make ("audioscale", "ascale");
502
503   volume = gst_element_factory_make ("volume", "volume");
504   g_object_set (G_OBJECT (volume), "volume", play_bin->volume, NULL);
505   play_bin->volume_element = volume;
506
507   if (play_bin->audio_sink) {
508     sink = play_bin->audio_sink;
509   } else {
510     sink = gst_element_factory_make ("alsasink", "audiosink");
511     play_bin->audio_sink = GST_ELEMENT (gst_object_ref (sink));
512   }
513
514   gst_object_ref (sink);
515   g_hash_table_insert (play_bin->cache, "audio_sink", sink);
516
517   gst_bin_add (GST_BIN (element), conv);
518   //gst_bin_add (GST_BIN (element), scale);
519   //gst_bin_add (GST_BIN (element), volume);
520   gst_bin_add (GST_BIN (element), sink);
521
522   gst_element_link_pads (conv, "src",   /*scale, "sink");
523                                            gst_element_link_pads (scale, "src", volume, "sink");
524                                            gst_element_link_pads (volume, "src", */ sink, "sink");
525
526   pad = gst_element_get_pad (conv, "sink");
527   gst_element_add_ghost_pad (element, pad, "sink");
528   gst_object_unref (pad);
529
530   gst_element_set_state (element, GST_STATE_READY);
531
532   /* since we're gonna add it to a bin but don't want to lose it,
533    * we keep a reference. */
534   gst_object_ref (element);
535   g_hash_table_insert (play_bin->cache, "abin", element);
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, *aqueue;
573   GstPad *pad, *rpad;
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   aqueue = gst_element_factory_make ("queue", "aqueue");
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 (element), aqueue);
587   gst_bin_add (GST_BIN (element), vsink);
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 (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   pad = gst_element_get_pad (aqueue, "sink");
607   rpad = gst_element_get_request_pad (tee, "src%d");
608   gst_pad_link (rpad, pad);
609   gst_object_unref (rpad);
610   gst_object_unref (pad);
611   gst_element_link_pads (aqueue, "src", asink, "sink");
612
613   pad = gst_element_get_pad (conv, "sink");
614   rpad = gst_element_get_request_pad (tee, "src%d");
615   gst_pad_link (rpad, pad);
616   gst_object_unref (rpad);
617   gst_object_unref (pad);
618
619   pad = gst_element_get_pad (tee, "sink");
620   gst_element_add_ghost_pad (element, pad, "sink");
621   gst_object_unref (pad);
622
623   return element;
624 }
625
626 /* get rid of all installed sinks */
627 static void
628 remove_sinks (GstPlayBin * play_bin)
629 {
630   GList *sinks;
631   GstObject *parent;
632   GstElement *element;
633
634   GST_DEBUG ("removesinks");
635   element = g_hash_table_lookup (play_bin->cache, "abin");
636   if (element != NULL) {
637     parent = gst_element_get_parent (element);
638     if (parent != NULL) {
639       /* we remove the element from the parent so that
640        * there is no unwanted state change when the parent
641        * is disposed */
642       play_bin->sinks = g_list_remove (play_bin->sinks, element);
643       gst_bin_remove (GST_BIN (parent), element);
644       gst_object_unref (parent);
645     }
646   }
647   element = g_hash_table_lookup (play_bin->cache, "vbin");
648   if (element != NULL) {
649     parent = gst_element_get_parent (element);
650     if (parent != NULL) {
651       play_bin->sinks = g_list_remove (play_bin->sinks, element);
652       gst_bin_remove (GST_BIN (parent), element);
653       gst_object_unref (parent);
654     }
655   }
656
657   for (sinks = play_bin->sinks; sinks; sinks = g_list_next (sinks)) {
658     GstElement *element = GST_ELEMENT (sinks->data);
659     GstPad *pad;
660     GstPad *peer;
661
662     pad = gst_element_get_pad (element, "sink");
663
664     GST_LOG ("removing sink %p", element);
665
666     peer = gst_pad_get_peer (pad);
667     if (peer) {
668       gst_pad_unlink (peer, pad);
669       gst_object_unref (peer);
670     }
671     gst_object_unref (pad);
672
673     gst_bin_remove (GST_BIN (play_bin), element);
674   }
675   g_list_free (play_bin->sinks);
676   play_bin->sinks = NULL;
677
678   if (play_bin->frame) {
679     gst_buffer_unref (play_bin->frame);
680     play_bin->frame = NULL;
681   }
682
683   play_bin->textoverlay_element = NULL;
684 }
685
686 /* loop over the streams and set up the pipeline to play this
687  * media file. First we count the number of audio and video streams.
688  * If there is no video stream but there exists an audio stream,
689  * we install a visualisation pipeline.
690  * 
691  * Also make sure to only connect the first audio and video pad. FIXME
692  * this should eventually be handled with a tuner interface so that
693  * one can switch the streams.
694  */
695 static gboolean
696 add_sink (GstPlayBin * play_bin, GstElement * sink, GstPad * srcpad)
697 {
698   GstPad *sinkpad;
699   GstPadLinkReturn res;
700   GstElement *parent;
701
702   gst_bin_add (GST_BIN (play_bin), sink);
703
704   /* we found a sink for this stream, now try to install it */
705   sinkpad = gst_element_get_pad (sink, "sink");
706   res = gst_pad_link (srcpad, sinkpad);
707   gst_object_unref (sinkpad);
708
709   parent = gst_pad_get_parent (srcpad);
710   GST_DEBUG ("Adding sink with state %d (parent: %d, peer: %d)\n",
711       GST_STATE (sink), GST_STATE (play_bin), GST_STATE (parent));
712   gst_object_unref (parent);
713
714   /* try to link the pad of the sink to the stream */
715   if (res < 0) {
716     gchar *capsstr;
717
718     /* could not link this stream */
719     capsstr = gst_caps_to_string (gst_pad_get_caps (srcpad));
720     g_warning ("could not link %s", capsstr);
721     g_free (capsstr);
722
723     gst_bin_remove (GST_BIN (play_bin), sink);
724   } else {
725     /* we got the sink succesfully linked, now keep the sink
726      * in out internal list */
727     play_bin->sinks = g_list_prepend (play_bin->sinks, sink);
728     gst_element_set_state (sink,
729         (GST_STATE (play_bin) == GST_STATE_PLAYING) ?
730         GST_STATE_PLAYING : GST_STATE_PAUSED);
731   }
732
733   return res;
734 }
735
736 static void
737 setup_sinks (GstPlayBaseBin * play_base_bin, GstPlayBaseGroup * group)
738 {
739   GstPlayBin *play_bin = GST_PLAY_BIN (play_base_bin);
740   GList *streaminfo = NULL, *s;
741   gboolean need_vis = FALSE;
742   gboolean need_text = FALSE;
743   GstPad *textsrcpad = NULL, *textsinkpad = NULL, *pad;
744   GstElement *sink;
745
746   /* get rid of existing sinks */
747   if (play_bin->sinks) {
748     remove_sinks (play_bin);
749   }
750   GST_DEBUG ("setupsinks");
751
752   /* find out what to do */
753   if (group->type[GST_STREAM_TYPE_VIDEO - 1].npads > 0 &&
754       group->type[GST_STREAM_TYPE_TEXT - 1].npads > 0) {
755     need_text = TRUE;
756   } else if (group->type[GST_STREAM_TYPE_VIDEO - 1].npads == 0 &&
757       group->type[GST_STREAM_TYPE_AUDIO - 1].npads > 0 &&
758       play_bin->visualisation != NULL) {
759     need_vis = TRUE;
760   }
761
762   /* now actually connect everything */
763   g_object_get (G_OBJECT (play_base_bin), "stream-info", &streaminfo, NULL);
764   for (s = streaminfo; s; s = g_list_next (s)) {
765     GObject *obj = G_OBJECT (s->data);
766     gint type;
767     GstObject *object;
768
769     g_object_get (obj, "type", &type, NULL);
770     g_object_get (obj, "object", &object, NULL);
771   }
772
773   /* link audio */
774   if (group->type[GST_STREAM_TYPE_AUDIO - 1].npads > 0) {
775     if (need_vis) {
776       sink = gen_vis_element (play_bin);
777     } else {
778       sink = gen_audio_element (play_bin);
779     }
780     pad = gst_element_get_pad (group->type[GST_STREAM_TYPE_AUDIO - 1].preroll,
781         "src");
782     add_sink (play_bin, sink, pad);
783     gst_object_unref (pad);
784   }
785
786   /* link video */
787   if (group->type[GST_STREAM_TYPE_VIDEO - 1].npads > 0) {
788     if (need_text) {
789       sink = gen_text_element (play_bin);
790
791       textsinkpad = gst_element_get_pad (sink, "text_sink");
792       textsrcpad =
793           gst_element_get_pad (group->type[GST_STREAM_TYPE_TEXT - 1].preroll,
794           "src");
795       gst_pad_link (textsrcpad, textsinkpad);
796       gst_object_unref (textsinkpad);
797       gst_object_unref (textsrcpad);
798     } else {
799       sink = gen_video_element (play_bin);
800     }
801     pad = gst_element_get_pad (group->type[GST_STREAM_TYPE_VIDEO - 1].preroll,
802         "src");
803     add_sink (play_bin, sink, pad);
804     gst_object_unref (pad);
805   }
806
807   if (play_bin->fakesink) {
808     gst_bin_remove (GST_BIN (play_bin), play_bin->fakesink);
809     play_bin->fakesink = NULL;
810   }
811 }
812
813 static GstElementStateReturn
814 gst_play_bin_change_state (GstElement * element)
815 {
816   GstElementStateReturn ret;
817   GstPlayBin *play_bin;
818   int transition;
819
820   play_bin = GST_PLAY_BIN (element);
821
822   transition = GST_STATE_TRANSITION (element);
823
824   switch (transition) {
825     case GST_STATE_READY_TO_PAUSED:
826       if (!play_bin->fakesink) {
827         play_bin->fakesink = gst_element_factory_make ("fakesink", "test");
828         gst_bin_add (GST_BIN (play_bin), play_bin->fakesink);
829       }
830       break;
831     default:
832       break;
833   }
834
835   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element);
836   if (ret == GST_STATE_FAILURE)
837     return ret;
838
839   switch (transition) {
840     case GST_STATE_PLAYING_TO_PAUSED:
841       /* Set audio sink state to NULL to release the sound device,
842        * but only if we own it (else we might be in chain-transition). */
843       //if (play_bin->audio_sink != NULL &&
844       //    GST_STATE (play_bin->audio_sink) == GST_STATE_PAUSED) {
845       //  gst_element_set_state (play_bin->audio_sink, GST_STATE_NULL);
846       //}
847       break;
848     case GST_STATE_PAUSED_TO_READY:
849       /* Check for NULL because the state transition may be done by
850        * gst_bin_dispose which is called by gst_play_bin_dispose, and in that
851        * case, we don't want to run remove_sinks.
852        * FIXME: should the NULL test be done in remove_sinks? Should we just
853        * set the state to NULL in gst_play_bin_dispose?
854        */
855       if (play_bin->cache != NULL) {
856         remove_sinks (play_bin);
857       }
858       if (play_bin->fakesink) {
859         gst_bin_remove (GST_BIN (play_bin), play_bin->fakesink);
860         play_bin->fakesink = NULL;
861       }
862       break;
863     default:
864       break;
865   }
866
867   return ret;
868 }
869
870 static gboolean
871 plugin_init (GstPlugin * plugin)
872 {
873   GST_DEBUG_CATEGORY_INIT (gst_play_bin_debug, "playbin", 0, "play bin");
874
875   return gst_element_register (plugin, "playbin", GST_RANK_NONE,
876       GST_TYPE_PLAY_BIN);
877 }
878
879 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
880     GST_VERSION_MINOR,
881     "playbin",
882     "player bin", plugin_init, VERSION, GST_LICENSE, GST_PACKAGE, GST_ORIGIN)