examples/gstplay/player.c: using g_print instead of g_message.
[platform/upstream/gstreamer.git] / gst-libs / gst / play / gstplay.c
1 /* GStreamer
2  * Copyright (C) 2003 Julien Moutte <julien@moutte.net>
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 "gstplay.h"
25
26 enum
27 {
28   TIME_TICK,
29   STREAM_LENGTH,
30   HAVE_VIDEO_SIZE,
31   LAST_SIGNAL
32 };
33
34 struct _GstPlayPrivate {  
35   char *location;
36   
37   GHashTable *elements;
38   
39   gint64 time_nanos;
40   gint64 length_nanos;
41   
42   gint get_length_attempt;
43   
44   guint tick_id;
45   guint length_id;
46 };
47
48 static guint gst_play_signals[LAST_SIGNAL] = { 0 };
49
50 static GstPipelineClass *parent_class = NULL;
51
52 /* ======================================================= */
53 /*                                                         */
54 /*                    Private Methods                      */
55 /*                                                         */
56 /* ======================================================= */
57
58 static gboolean
59 gst_play_pipeline_setup (GstPlay *play)
60 {
61   GstElement *work_thread, *video_thread;
62   GstElement *source, *autoplugger, *video_switch;
63   GstElement *video_queue, *video_colorspace, *video_scaler, *video_sink;
64   GstElement *audio_thread, *audio_queue, *audio_volume, *audio_sink;
65   GstElement *audio_tee, *vis_thread, *vis_queue, *vis_element;
66   GstPad *audio_tee_pad1, *audio_tee_pad2, *vis_thread_pad, *audio_sink_pad;
67   
68   g_return_val_if_fail (play != NULL, FALSE);
69   g_return_val_if_fail (GST_IS_PLAY (play), FALSE);
70   
71   work_thread = gst_element_factory_make ("thread", "work_thread");
72   if (!GST_IS_ELEMENT (work_thread))
73     return FALSE;
74   
75   g_hash_table_insert (play->priv->elements, "work_thread", work_thread);
76   gst_bin_add (GST_BIN (play), work_thread);
77   
78   /* Placeholder for the source and autoplugger { fakesrc ! spider } */ 
79   source = gst_element_factory_make ("fakesrc", "source");
80   if (!GST_IS_ELEMENT (source))
81     return FALSE;
82   
83   g_hash_table_insert (play->priv->elements, "source", source);
84   
85   autoplugger = gst_element_factory_make ("spider", "autoplugger");
86   if (!GST_IS_ELEMENT (autoplugger))
87     return FALSE;
88   
89   g_hash_table_insert (play->priv->elements, "autoplugger", autoplugger);
90   
91   gst_bin_add_many (GST_BIN (work_thread), source, autoplugger, NULL);
92   gst_element_link (source, autoplugger);
93   
94   /* Creating our video output bin
95      { queue ! colorspace ! videoscale ! fakesink } */
96   video_thread = gst_element_factory_make ("thread", "video_thread");
97   if (!GST_IS_ELEMENT (video_thread))
98     return FALSE;
99   
100   g_hash_table_insert (play->priv->elements, "video_thread", video_thread);
101   gst_bin_add (GST_BIN (work_thread), video_thread);
102   
103   /* Buffer queue for our video thread */
104   video_queue = gst_element_factory_make ("queue", "video_queue");
105   if (!GST_IS_ELEMENT (video_queue))
106     return FALSE;
107   
108   g_hash_table_insert (play->priv->elements, "video_queue", video_queue);
109   
110   /* Colorspace conversion */
111   video_colorspace = gst_element_factory_make ("ffcolorspace",
112                                                "video_colorspace");
113   if (!GST_IS_ELEMENT (video_colorspace)) {
114     video_colorspace = gst_element_factory_make ("colorspace",
115                                                  "video_colorspace");
116     if (!GST_IS_ELEMENT (video_colorspace))
117       return FALSE;
118   }
119   
120   g_hash_table_insert (play->priv->elements, "video_colorspace",
121                        video_colorspace);
122   
123   /* Software scaling of video stream */
124   video_scaler = gst_element_factory_make ("videoscale", "video_scaler");
125   if (!GST_IS_ELEMENT (video_scaler))
126     return FALSE;
127   
128   g_hash_table_insert (play->priv->elements, "video_scaler", video_scaler);
129   
130   /* Placeholder for future video sink bin */
131   video_sink = gst_element_factory_make ("fakesink", "video_sink");
132   if (!GST_IS_ELEMENT (video_sink))
133     return FALSE;
134   
135   g_hash_table_insert (play->priv->elements, "video_sink", video_sink);
136   
137   /* Linking, Adding, Ghosting */
138   gst_element_link_many (video_queue, video_colorspace,
139                          video_scaler, video_sink, NULL);
140   gst_bin_add_many (GST_BIN (video_thread), video_queue, video_colorspace,
141                     video_scaler, video_sink, NULL);
142   gst_element_add_ghost_pad (video_thread,
143                              gst_element_get_pad (video_queue, "sink"),
144                              "sink");
145   
146   video_switch = gst_element_factory_make ("switch",
147                                            "video_switch");
148   if (!GST_IS_ELEMENT (video_switch))
149     return FALSE;
150   
151   g_hash_table_insert (play->priv->elements, "video_switch", video_switch);
152   
153   /*gst_bin_add (GST_BIN (work_thread), video_switch);*/
154   
155   /* Connecting autoplugger to video switch and video switch to video output 
156   gst_element_link (autoplugger, video_switch);
157   gst_element_link (video_switch, video_thread);*/
158   gst_element_link (autoplugger, video_thread);
159   
160   /* Creating our audio output bin 
161      { queue ! volume ! tee ! { queue ! goom } ! fakesink } */
162   audio_thread = gst_element_factory_make ("thread", "audio_thread");
163   if (!GST_IS_ELEMENT (audio_thread))
164     return FALSE;
165   
166   g_hash_table_insert (play->priv->elements, "audio_thread", audio_thread);
167   gst_bin_add (GST_BIN (work_thread), audio_thread);
168   
169   /* Buffer queue for our audio thread */
170   audio_queue = gst_element_factory_make ("queue", "audio_queue");
171   if (!GST_IS_ELEMENT (audio_queue))
172     return FALSE;
173   
174   g_hash_table_insert (play->priv->elements, "audio_queue", audio_queue);
175   
176   /* Volume control */
177   audio_volume = gst_element_factory_make ("volume", "audio_volume");
178   if (!GST_IS_ELEMENT (audio_volume))
179     return FALSE;
180   
181   g_hash_table_insert (play->priv->elements, "audio_volume", audio_volume);
182   
183   /* Duplicate audio signal to sink and visualization thread */
184   audio_tee = gst_element_factory_make ("tee", "audio_tee");
185   if (!GST_IS_ELEMENT (audio_tee))
186     return FALSE;
187   
188   audio_tee_pad1 = gst_element_get_request_pad (audio_tee, "src%d");
189   audio_tee_pad2 = gst_element_get_request_pad (audio_tee, "src%d");
190   g_hash_table_insert (play->priv->elements, "audio_tee_pad1",
191                        audio_tee_pad1);
192   g_hash_table_insert (play->priv->elements, "audio_tee_pad2",
193                        audio_tee_pad2);
194   g_hash_table_insert (play->priv->elements, "audio_tee", audio_tee);
195   
196   /* Placeholder for future audio sink bin */
197   audio_sink = gst_element_factory_make ("fakesink", "audio_sink");
198   if (!GST_IS_ELEMENT (audio_sink))
199     return FALSE;
200   
201   audio_sink_pad = gst_element_get_pad (audio_sink, "sink");
202   g_hash_table_insert (play->priv->elements, "audio_sink_pad",
203                        audio_sink_pad);
204   g_hash_table_insert (play->priv->elements, "audio_sink", audio_sink);
205   
206   /* Visualization thread */
207   vis_thread = gst_element_factory_make ("thread", "vis_thread");
208   if (!GST_IS_ELEMENT (vis_thread))
209     return FALSE;
210   
211   g_hash_table_insert (play->priv->elements, "vis_thread", vis_thread);
212   
213   /* Buffer queue for our visualization thread */
214   vis_queue = gst_element_factory_make ("queue", "vis_queue");
215   if (!GST_IS_ELEMENT (vis_queue))
216     return FALSE;
217   
218   g_hash_table_insert (play->priv->elements, "vis_queue", vis_queue);
219   
220   vis_element = gst_element_factory_make ("identity", "vis_element");
221   if (!GST_IS_ELEMENT (vis_element))
222     return FALSE;
223   
224   g_hash_table_insert (play->priv->elements, "vis_element", vis_element);
225   
226   /* Adding, Linking, Ghosting in visualization */
227   gst_bin_add_many (GST_BIN (vis_thread), vis_queue, vis_element, NULL);
228   gst_element_link (vis_queue, vis_element);
229   vis_thread_pad = gst_element_add_ghost_pad (vis_thread,
230                                      gst_element_get_pad (vis_queue, "sink"),
231                                      "sink");
232   g_hash_table_insert (play->priv->elements, "vis_thread_pad",
233                        vis_thread_pad);
234   
235   
236   /* Linking, Adding, Ghosting in audio */
237   gst_element_link_many (audio_queue, audio_volume, audio_tee, NULL);
238   gst_pad_link (audio_tee_pad1, audio_sink_pad);
239   gst_bin_add_many (GST_BIN (audio_thread), audio_queue, audio_volume,
240                     audio_tee, vis_thread, audio_sink, NULL);
241   gst_element_add_ghost_pad (audio_thread,
242                              gst_element_get_pad (audio_queue, "sink"),
243                              "sink");
244   
245   /* Connecting audio output to autoplugger */
246   gst_element_link (autoplugger, audio_thread);
247   
248   return TRUE;
249 }
250
251 static void
252 gst_play_have_video_size (GstElement *element, gint width,
253                           gint height, GstPlay *play)
254 {
255   g_return_if_fail (play != NULL);
256   g_return_if_fail (GST_IS_PLAY (play));
257   g_signal_emit (G_OBJECT (play), gst_play_signals[HAVE_VIDEO_SIZE],
258                  0, width, height);
259 }
260
261 static gboolean
262 gst_play_tick_callback (GstPlay *play)
263 {
264   GstClock *clock = NULL;
265   
266   g_return_val_if_fail (play != NULL, FALSE);
267   
268   if (!GST_IS_PLAY (play)) {
269     play->priv->tick_id = 0;
270     return FALSE;
271   }
272   
273   clock = gst_bin_get_clock (GST_BIN (play));
274   play->priv->time_nanos = gst_clock_get_time (clock);
275   
276   g_signal_emit (G_OBJECT (play), gst_play_signals[TIME_TICK],
277                  0,play->priv->time_nanos);
278   
279   if (GST_STATE (GST_ELEMENT (play)) == GST_STATE_PLAYING)
280     return TRUE;
281   else {
282     play->priv->tick_id = 0;
283     return FALSE;
284   }
285 }
286
287 static gboolean
288 gst_play_get_length_callback (GstPlay *play)
289 {
290   GstElement *audio_sink_element, *video_sink_element;
291   GstFormat format = GST_FORMAT_TIME;
292   gint64 value;
293   gboolean q = FALSE;
294   
295   g_return_val_if_fail (play != NULL, FALSE);
296   g_return_val_if_fail (GST_IS_PLAY (play), FALSE);
297   
298   /* We try to get length from all real sink elements */
299   audio_sink_element = g_hash_table_lookup (play->priv->elements,
300                                             "audio_sink_element");
301   video_sink_element = g_hash_table_lookup (play->priv->elements,
302                                             "video_sink_element");
303   if (!GST_IS_ELEMENT (audio_sink_element) &&
304       !GST_IS_ELEMENT (video_sink_element)) {
305     play->priv->length_id = 0;
306     return FALSE;
307   }
308   
309   /* Audio first and then Video */
310   if (GST_IS_ELEMENT (audio_sink_element))
311     q = gst_element_query (audio_sink_element, GST_QUERY_TOTAL, &format,
312                            &value);
313   if ( (!q) && (GST_IS_ELEMENT (video_sink_element)) )
314     q = gst_element_query (video_sink_element, GST_QUERY_TOTAL, &format,
315                            &value);
316    
317   if (q) {
318     play->priv->length_nanos = value;
319     g_signal_emit (G_OBJECT (play), gst_play_signals[STREAM_LENGTH],
320                    0,play->priv->length_nanos);
321     play->priv->length_id = 0;
322     return FALSE;
323   }
324   
325   play->priv->get_length_attempt++;
326   
327   /* We try 16 times */
328   if (play->priv->get_length_attempt > 15) {
329     play->priv->length_id = 0;
330     return FALSE;
331   }
332   else
333     return TRUE;
334 }
335
336 static void
337 gst_play_state_change (GstElement *element, GstElementState old,
338                        GstElementState state)
339 {
340   GstPlay *play;
341   
342   g_return_if_fail (element != NULL);
343   g_return_if_fail (GST_IS_PLAY (element));
344   
345   play = GST_PLAY (element);
346   
347   if (state == GST_STATE_PLAYING) {
348     if (play->priv->tick_id) {
349       g_source_remove (play->priv->tick_id);
350       play->priv->tick_id = 0;
351     }
352         
353     play->priv->tick_id = g_timeout_add (200,
354                                          (GSourceFunc) gst_play_tick_callback,
355                                          play);
356       
357     play->priv->get_length_attempt = 0;
358     
359     if (play->priv->length_id) {
360       g_source_remove (play->priv->length_id);
361       play->priv->length_id = 0;
362     }
363         
364     play->priv->length_id = g_timeout_add (200,
365                                    (GSourceFunc) gst_play_get_length_callback,
366                                    play);
367   }
368     
369   if (GST_ELEMENT_CLASS (parent_class)->state_change)
370     GST_ELEMENT_CLASS (parent_class)->state_change (element, old, state);
371 }
372
373 /* =========================================== */
374 /*                                             */
375 /*         Init & Dispose & Class init         */
376 /*                                             */
377 /* =========================================== */
378
379 static void
380 gst_play_dispose (GObject *object)
381 {
382   GstPlay *play;
383   
384   g_return_if_fail (object != NULL);
385   g_return_if_fail (GST_IS_PLAY (object));
386   
387   play = GST_PLAY (object);
388   
389   if (play->priv->length_id) {
390     g_source_remove (play->priv->length_id);
391     play->priv->length_id = 0;
392   }
393     
394   if (play->priv->tick_id) {
395     g_source_remove (play->priv->tick_id);
396     play->priv->tick_id = 0;
397   }
398     
399   if (play->priv->location) {
400     g_free (play->priv->location);
401     play->priv->location = NULL;
402   }
403   
404   if (play->priv->elements) {
405     g_hash_table_destroy (play->priv->elements);
406     play->priv->elements = NULL;
407   }
408     
409   G_OBJECT_CLASS (parent_class)->dispose (object);
410 }
411
412 static void
413 gst_play_init (GstPlay *play)
414 {
415   play->priv = g_new0 (GstPlayPrivate, 1);
416   play->priv->location = NULL;
417   play->priv->length_nanos = 0;
418   play->priv->time_nanos = 0;
419   play->priv->elements = g_hash_table_new (g_str_hash, g_str_equal);
420   
421   if (!gst_play_pipeline_setup (play))
422     g_warning ("libgstplay: failed initializing pipeline");
423 }
424
425 static void
426 gst_play_class_init (GstPlayClass *klass)
427 {
428   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
429   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
430   
431   parent_class = g_type_class_peek_parent (klass);
432
433   gobject_class->dispose = gst_play_dispose;
434
435   element_class->state_change = gst_play_state_change;
436   
437   gst_play_signals[TIME_TICK] =
438     g_signal_new ("time_tick", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST,
439                   G_STRUCT_OFFSET (GstPlayClass, time_tick), NULL, NULL,
440                   gst_marshal_VOID__INT64, G_TYPE_NONE, 1, G_TYPE_INT64);
441   gst_play_signals[STREAM_LENGTH] =
442     g_signal_new ("stream_length", G_TYPE_FROM_CLASS (klass),
443                   G_SIGNAL_RUN_FIRST,
444                   G_STRUCT_OFFSET (GstPlayClass, stream_length), NULL, NULL,
445                   gst_marshal_VOID__INT64, G_TYPE_NONE, 1, G_TYPE_INT64);
446   gst_play_signals[HAVE_VIDEO_SIZE] =
447     g_signal_new ("have_video_size", G_TYPE_FROM_CLASS (klass),
448                   G_SIGNAL_RUN_FIRST,
449                   G_STRUCT_OFFSET (GstPlayClass, have_video_size), NULL, NULL,
450                   gst_marshal_VOID__INT_INT, G_TYPE_NONE, 2,
451                   G_TYPE_INT, G_TYPE_INT);
452 }
453
454 /* ======================================================= */
455 /*                                                         */
456 /*                     Public Methods                      */
457 /*                                                         */
458 /* ======================================================= */
459
460 /**
461  * gst_play_set_location:
462  * @play: a #GstPlay.
463  * @location: a const #char* indicating location to play
464  *
465  * Set location of @play to @location.
466  *
467  * Returns: TRUE if location was set successfully.
468  */
469 gboolean
470 gst_play_set_location (GstPlay *play, const char *location)
471 {
472   GstElement *work_thread, *source, *autoplugger, *video_thread, *audio_thread;
473   
474   g_return_val_if_fail (play != NULL, FALSE);
475   g_return_val_if_fail (GST_IS_PLAY (play), FALSE);
476   
477   if (play->priv->location)
478     g_free (play->priv->location);
479   
480   play->priv->location = g_strdup (location);
481   
482   if (GST_STATE (GST_ELEMENT (play)) != GST_STATE_READY)
483     gst_element_set_state (GST_ELEMENT (play), GST_STATE_READY);
484   
485   work_thread = g_hash_table_lookup (play->priv->elements, "work_thread");
486   if (!GST_IS_ELEMENT (work_thread))
487     return FALSE;
488   video_thread = g_hash_table_lookup (play->priv->elements, "video_thread");
489   if (!GST_IS_ELEMENT (video_thread))
490     return FALSE;
491   audio_thread = g_hash_table_lookup (play->priv->elements, "audio_thread");
492   if (!GST_IS_ELEMENT (audio_thread))
493     return FALSE;
494   source = g_hash_table_lookup (play->priv->elements, "source");
495   if (!GST_IS_ELEMENT (source))
496     return FALSE;
497   autoplugger = g_hash_table_lookup (play->priv->elements, "autoplugger");
498   if (!GST_IS_ELEMENT (autoplugger))
499     return FALSE;
500   
501   /* Spider can autoplugg only once. We remove the actual one and put a new
502      autoplugger */
503   gst_element_unlink (source, autoplugger);
504   gst_element_unlink (autoplugger, video_thread);
505   gst_element_unlink (autoplugger, audio_thread);
506   gst_bin_remove (GST_BIN (work_thread), autoplugger);
507   
508   autoplugger = gst_element_factory_make ("spider", "autoplugger");
509   if (!GST_IS_ELEMENT (autoplugger))
510     return FALSE;
511   
512   gst_bin_add (GST_BIN (work_thread), autoplugger);
513   gst_element_link (source, autoplugger);
514   gst_element_link (autoplugger, video_thread);
515   gst_element_link (autoplugger, audio_thread);
516   
517   g_hash_table_replace (play->priv->elements, "autoplugger", autoplugger);
518   
519   /* FIXME: Why don't we have an interface to do that kind of stuff ? */
520   g_object_set (G_OBJECT (source), "location", play->priv->location, NULL);
521   
522   play->priv->length_nanos = 0LL;
523   play->priv->time_nanos = 0LL;
524   
525   g_signal_emit (G_OBJECT (play), gst_play_signals[STREAM_LENGTH], 0, 0LL);
526   g_signal_emit (G_OBJECT (play), gst_play_signals[TIME_TICK], 0, 0LL);
527   
528   return TRUE;
529 }
530
531 /**
532  * gst_play_get_location:
533  * @play: a #GstPlay.
534  *
535  * Get current location of @play.
536  *
537  * Returns: a const #char* pointer to current location.
538  */
539 char *
540 gst_play_get_location (GstPlay *play)
541 {
542   g_return_val_if_fail (play != NULL, NULL);
543   g_return_val_if_fail (GST_IS_PLAY (play), NULL);
544   return g_strdup (play->priv->location);
545 }
546
547 /**
548  * gst_play_seek_to_time:
549  * @play: a #GstPlay.
550  * @time_nanos: a #gint64 indicating a time position.
551  *
552  * Performs a seek on @play until @time_nanos.
553  */
554 gboolean
555 gst_play_seek_to_time (GstPlay * play, gint64 time_nanos)
556 {
557   GstElement *audio_sink_element, *video_sink_element;
558   
559   g_return_val_if_fail (play != NULL, FALSE);
560   g_return_val_if_fail (GST_IS_PLAY (play), FALSE);
561   
562   if (time_nanos < 0LL)
563     time_nanos = 0LL;
564   
565   audio_sink_element = g_hash_table_lookup (play->priv->elements,
566                                             "audio_sink_element");
567   video_sink_element = g_hash_table_lookup (play->priv->elements,
568                                             "video_sink_element");
569   
570   if (GST_IS_ELEMENT (audio_sink_element) &&
571       GST_IS_ELEMENT (video_sink_element)) {
572     gboolean s = FALSE;
573    
574     s = gst_element_seek (audio_sink_element, GST_FORMAT_TIME |
575                           GST_SEEK_METHOD_SET | GST_SEEK_FLAG_FLUSH,
576                           time_nanos);
577     if (!s) {
578       s = gst_element_seek (video_sink_element, GST_FORMAT_TIME |
579                             GST_SEEK_METHOD_SET | GST_SEEK_FLAG_FLUSH,
580                             time_nanos);
581     }
582     
583     if (s) {
584       GstClock *clock = gst_bin_get_clock (GST_BIN (play));
585       play->priv->time_nanos = gst_clock_get_time (clock);
586       g_signal_emit (G_OBJECT (play), gst_play_signals[TIME_TICK],
587                      0,play->priv->time_nanos);
588     }
589   }
590   
591   return TRUE;
592 }
593
594 /**
595  * gst_play_set_data_src:
596  * @play: a #GstPlay.
597  * @data_src: a #GstElement.
598  *
599  * Set @data_src as the source element of @play.
600  *
601  * Returns: TRUE if call succeeded.
602  */
603 gboolean
604 gst_play_set_data_src (GstPlay *play, GstElement *data_src)
605 {
606   GstElement *work_thread, *old_data_src, *autoplugger;
607   
608   g_return_val_if_fail (play != NULL, FALSE);
609   g_return_val_if_fail (GST_IS_PLAY (play), FALSE);
610   
611   /* We bring back the pipeline to READY */
612   if (GST_STATE (GST_ELEMENT (play)) != GST_STATE_READY)
613     gst_element_set_state (GST_ELEMENT (play), GST_STATE_READY);
614   
615   /* Getting needed objects */
616   work_thread = g_hash_table_lookup (play->priv->elements, "work_thread");
617   if (!GST_IS_ELEMENT (work_thread))
618     return FALSE;
619   old_data_src = g_hash_table_lookup (play->priv->elements, "source");
620   if (!GST_IS_ELEMENT (old_data_src))
621     return FALSE;
622   autoplugger = g_hash_table_lookup (play->priv->elements, "autoplugger");
623   if (!GST_IS_ELEMENT (autoplugger))
624     return FALSE;
625   
626   /* Unlinking old source from autoplugger, removing it from pipeline, adding
627      the new one and connecting it to autoplugger FIXME: we should put a new
628      autoplugger here as spider can autoplugg only once */
629   gst_element_unlink (old_data_src, autoplugger);
630   gst_bin_remove (GST_BIN (work_thread), old_data_src);
631   gst_bin_add (GST_BIN (work_thread), data_src);
632   gst_element_link (data_src, autoplugger);
633   
634   g_hash_table_replace (play->priv->elements, "source", data_src);
635   
636   return TRUE;
637 }
638
639 /**
640  * gst_play_set_video_sink:
641  * @play: a #GstPlay.
642  * @video_sink: a #GstElement.
643  *
644  * Set @video_sink as the video sink element of @play.
645  *
646  * Returns: TRUE if call succeeded.
647  */
648 gboolean
649 gst_play_set_video_sink (GstPlay *play, GstElement *video_sink)
650 {
651   GstElement *video_thread, *old_video_sink, *video_scaler, *video_sink_element;
652   
653   g_return_val_if_fail (play != NULL, FALSE);
654   g_return_val_if_fail (GST_IS_PLAY (play), FALSE);
655   g_return_val_if_fail (video_sink != NULL, FALSE);
656   g_return_val_if_fail (GST_IS_ELEMENT (video_sink), FALSE);
657   
658   /* We bring back the pipeline to READY */
659   if (GST_STATE (GST_ELEMENT (play)) != GST_STATE_READY)
660     gst_element_set_state (GST_ELEMENT (play), GST_STATE_READY);
661   
662   /* Getting needed objects */
663   video_thread = g_hash_table_lookup (play->priv->elements, "video_thread");
664   if (!GST_IS_ELEMENT (video_thread))
665     return FALSE;
666   old_video_sink = g_hash_table_lookup (play->priv->elements, "video_sink");
667   if (!GST_IS_ELEMENT (old_video_sink))
668     return FALSE;
669   video_scaler = g_hash_table_lookup (play->priv->elements, "video_scaler");
670   if (!GST_IS_ELEMENT (video_scaler))
671     return FALSE;
672   
673   /* Unlinking old video sink from video scaler, removing it from pipeline,
674      adding the new one and linking it */
675   gst_element_unlink (video_scaler, old_video_sink);
676   gst_bin_remove (GST_BIN (video_thread), old_video_sink);
677   gst_bin_add (GST_BIN (video_thread), video_sink);
678   gst_element_link (video_scaler, video_sink);
679   
680   g_hash_table_replace (play->priv->elements, "video_sink", video_sink);
681   
682   video_sink_element = gst_play_get_sink_element (play, video_sink,
683                                                   GST_PLAY_SINK_TYPE_VIDEO);
684   if (GST_IS_ELEMENT (video_sink_element)) {
685     g_hash_table_replace (play->priv->elements, "video_sink_element",
686                           video_sink_element);
687     g_signal_connect (G_OBJECT (video_sink_element), "have_video_size",
688                       G_CALLBACK (gst_play_have_video_size), play);
689   } 
690   
691   gst_element_set_state (video_sink, GST_STATE (GST_ELEMENT(play)));
692   
693   return TRUE;
694 }
695
696 /**
697  * gst_play_set_audio_sink:
698  * @play: a #GstPlay.
699  * @audio_sink: a #GstElement.
700  *
701  * Set @audio_sink as the audio sink element of @play.
702  *
703  * Returns: TRUE if call succeeded.
704  */
705 gboolean
706 gst_play_set_audio_sink (GstPlay *play, GstElement *audio_sink)
707 {
708   GstElement *old_audio_sink, *audio_thread, *audio_sink_element;
709   GstPad *audio_tee_pad1, *audio_sink_pad, *old_audio_sink_pad;
710   
711   g_return_val_if_fail (play != NULL, FALSE);
712   g_return_val_if_fail (GST_IS_PLAY (play), FALSE);
713   g_return_val_if_fail (audio_sink != NULL, FALSE);
714   g_return_val_if_fail (GST_IS_ELEMENT (audio_sink), FALSE);
715   
716   /* We bring back the pipeline to READY */
717   if (GST_STATE (GST_ELEMENT (play)) != GST_STATE_READY)
718     gst_element_set_state (GST_ELEMENT (play), GST_STATE_READY);
719   
720   /* Getting needed objects */
721   old_audio_sink = g_hash_table_lookup (play->priv->elements, "audio_sink");
722   if (!GST_IS_ELEMENT (old_audio_sink))
723     return FALSE;
724   old_audio_sink_pad = g_hash_table_lookup (play->priv->elements,
725                                             "audio_sink_pad");
726   if (!GST_IS_PAD (old_audio_sink_pad))
727     return FALSE;
728   audio_thread = g_hash_table_lookup (play->priv->elements, "audio_thread");
729   if (!GST_IS_ELEMENT (audio_thread))
730     return FALSE;
731   audio_tee_pad1 = g_hash_table_lookup (play->priv->elements,
732                                         "audio_tee_pad1");
733   if (!GST_IS_PAD (audio_tee_pad1))
734     return FALSE;
735   audio_sink_pad = gst_element_get_pad (audio_sink, "sink");
736   if (!GST_IS_PAD (audio_sink_pad))
737     return FALSE;
738   
739   /* Unlinking old audiosink, removing it from pipeline, putting the new one
740      and linking it */
741   gst_pad_unlink (audio_tee_pad1, old_audio_sink_pad);
742   gst_bin_remove (GST_BIN (audio_thread), old_audio_sink);
743   gst_bin_add (GST_BIN (audio_thread), audio_sink);
744   gst_pad_link (audio_tee_pad1, audio_sink_pad);
745   
746   g_hash_table_replace (play->priv->elements, "audio_sink", audio_sink);
747   g_hash_table_replace (play->priv->elements, "audio_sink_pad",
748                         audio_sink_pad);
749   
750   audio_sink_element = gst_play_get_sink_element (play, audio_sink,
751                                                   GST_PLAY_SINK_TYPE_AUDIO);
752   if (GST_IS_ELEMENT (audio_sink_element)) {
753     g_hash_table_replace (play->priv->elements, "audio_sink_element",
754                           audio_sink_element);
755   }
756   
757   gst_element_set_state (audio_sink, GST_STATE (GST_ELEMENT(play)));
758   
759   return TRUE;
760 }
761
762 /**
763  * gst_play_set_visualization:
764  * @play: a #GstPlay.
765  * @element: a #GstElement.
766  *
767  * Set @video_sink as the video sink element of @play.
768  *
769  * Returns: TRUE if call succeeded.
770  */
771 gboolean
772 gst_play_set_visualization (GstPlay *play, GstElement *vis_element)
773 {
774   GstElement *old_vis_element, *vis_thread, *vis_queue/*, *video_switch*/;
775   gboolean was_playing = FALSE;
776   
777   g_return_val_if_fail (play != NULL, FALSE);
778   g_return_val_if_fail (GST_IS_PLAY (play), FALSE);
779   g_return_val_if_fail (vis_element != NULL, FALSE);
780   g_return_val_if_fail (GST_IS_ELEMENT (vis_element), FALSE);
781   
782   /* We bring back the pipeline to READY */
783   if (GST_STATE (GST_ELEMENT (play)) == GST_STATE_PLAYING) {
784     gst_element_set_state (GST_ELEMENT (play), GST_STATE_PAUSED);
785     was_playing = TRUE;
786   }
787   
788   /* Getting needed objects */
789   vis_thread = g_hash_table_lookup (play->priv->elements, "vis_thread");
790   if (!GST_IS_ELEMENT (vis_thread))
791     return FALSE;
792   old_vis_element = g_hash_table_lookup (play->priv->elements,
793                                          "vis_element");
794   if (!GST_IS_ELEMENT (old_vis_element))
795     return FALSE;
796   vis_queue = g_hash_table_lookup (play->priv->elements, "vis_queue");
797   if (!GST_IS_ELEMENT (vis_queue))
798     return FALSE;
799   /*video_switch = g_hash_table_lookup (play->priv->elements, "video_switch");
800   if (!GST_IS_ELEMENT (video_switch))
801     return FALSE;*/
802     
803   /* Unlinking, removing the old element then adding and linking the new one */
804   gst_element_unlink (vis_queue, old_vis_element);
805   /*gst_element_unlink (old_vis_element, video_switch);*/
806   gst_bin_remove (GST_BIN (vis_thread), old_vis_element);
807   gst_bin_add (GST_BIN (vis_thread), vis_element);
808   gst_element_link (vis_queue, vis_element);
809   /*gst_element_link (vis_element, video_switch);*/
810   
811   if (was_playing)
812     gst_element_set_state (GST_ELEMENT (play), GST_STATE_PLAYING);
813   
814   return TRUE;
815 }
816
817 /**
818  * gst_play_connect_visualization:
819  * @play: a #GstPlay.
820  * @connect: a #gboolean indicating wether or not
821  * visualization should be connected.
822  *
823  * Connect or disconnect visualization bin in @play.
824  *
825  * Returns: TRUE if call succeeded.
826  */
827 gboolean
828 gst_play_connect_visualization (GstPlay * play, gboolean connect)
829 {
830   GstPad *audio_tee_pad2, *vis_thread_pad;
831   gboolean connected = FALSE, was_playing = FALSE;
832   
833   g_return_val_if_fail (play != NULL, FALSE);
834   g_return_val_if_fail (GST_IS_PLAY (play), FALSE);
835   
836   vis_thread_pad = g_hash_table_lookup (play->priv->elements,
837                                         "vis_thread_pad");
838   if (!GST_IS_PAD (vis_thread_pad))
839     return FALSE;
840   audio_tee_pad2 = g_hash_table_lookup (play->priv->elements,
841                                         "audio_tee_pad2");
842   if (!GST_IS_PAD (audio_tee_pad2))
843     return FALSE;
844   
845   if (GST_STATE (GST_ELEMENT (play)) == GST_STATE_PLAYING) {
846     gst_element_set_state (GST_ELEMENT (play), GST_STATE_PAUSED);
847     was_playing = TRUE;
848   }
849     
850   if (gst_pad_get_peer (vis_thread_pad) != NULL)
851     connected = TRUE;
852   else
853     connected = FALSE;
854
855   if ((connect) && (!connected))
856     gst_pad_link (audio_tee_pad2, vis_thread_pad);
857   else if ((!connect) && (connected))
858     gst_pad_unlink (audio_tee_pad2, vis_thread_pad);
859   
860   if (was_playing)
861     gst_element_set_state (GST_ELEMENT (play), GST_STATE_PLAYING);
862     
863   return TRUE;
864 }
865
866 /**
867  * gst_play_get_sink_element:
868  * @play: a #GstPlay.
869  * @element: a #GstElement.
870  * @sink_type: a #GstPlaySinkType.
871  *
872  * Searches recursively for a sink #GstElement with
873  * type @sink_type in @element which is supposed to be a #GstBin.
874  *
875  * Returns: the sink #GstElement of @element.
876  */
877 GstElement *
878 gst_play_get_sink_element (GstPlay *play,
879                            GstElement *element, GstPlaySinkType sink_type)
880 {
881   GList *elements = NULL;
882   const GList *pads = NULL;
883   gboolean has_src, has_correct_type;
884
885   g_return_val_if_fail (play != NULL, NULL);
886   g_return_val_if_fail (element != NULL, NULL);
887   g_return_val_if_fail (GST_IS_PLAY (play), NULL);
888   g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
889
890   if (!GST_IS_BIN (element)) {
891     /* since its not a bin, we'll presume this 
892      * element is a sink element */
893     return element;
894   }
895
896   elements = (GList *) gst_bin_get_list (GST_BIN (element));
897
898   /* traverse all elements looking for a src pad */
899
900   while (elements) {
901     element = GST_ELEMENT (elements->data);
902
903     /* Recursivity :) */
904
905     if (GST_IS_BIN (element)) {
906       element = gst_play_get_sink_element (play, element, sink_type);
907       if (GST_IS_ELEMENT (element))
908         return element;
909     }
910     else {
911       pads = gst_element_get_pad_list (element);
912       has_src = FALSE;
913       has_correct_type = FALSE;
914       while (pads) {
915         /* check for src pad */
916         if (GST_PAD_DIRECTION (GST_PAD (pads->data)) == GST_PAD_SRC) {
917           has_src = TRUE;
918           break;
919         }
920         else {
921           /* If not a src pad checking caps */
922           GstCaps *caps;
923           caps = gst_pad_get_caps (GST_PAD (pads->data));
924           while (caps) {
925             gboolean has_video_cap = FALSE, has_audio_cap = FALSE;
926             if (g_ascii_strcasecmp (gst_caps_get_mime (caps),
927                                     "audio/x-raw-int") == 0) {
928               has_audio_cap = TRUE;
929             }
930             
931             if ((g_ascii_strcasecmp (gst_caps_get_mime (caps),
932                                      "video/x-raw-yuv") == 0) ||
933                 (g_ascii_strcasecmp (gst_caps_get_mime (caps),
934                                      "video/x-raw-rgb") == 0)) {
935               has_video_cap = TRUE;
936             }
937
938             switch (sink_type) {
939               case GST_PLAY_SINK_TYPE_AUDIO:
940                 if (has_audio_cap)
941                   has_correct_type = TRUE;
942                 break;;
943               case GST_PLAY_SINK_TYPE_VIDEO:
944                 if (has_video_cap)
945                   has_correct_type = TRUE;
946                 break;;
947               case GST_PLAY_SINK_TYPE_ANY:
948                 if ((has_video_cap) || (has_audio_cap))
949                   has_correct_type = TRUE;
950                 break;;
951               default:
952                 has_correct_type = FALSE;
953             }
954             
955             caps = caps->next;
956           }
957         }
958         
959         pads = g_list_next (pads);
960         
961       }
962       
963       if ((!has_src) && (has_correct_type))
964         return element;
965     }
966     
967     elements = g_list_next (elements);
968   }
969   
970   /* we didn't find a sink element */
971   
972   return NULL;
973 }
974
975 GstPlay *
976 gst_play_new (void)
977 {
978   GstPlay *play = g_object_new (GST_TYPE_PLAY, NULL);
979   
980   return play;
981 }
982
983 /* =========================================== */
984 /*                                             */
985 /*          Object typing & Creation           */
986 /*                                             */
987 /* =========================================== */
988
989 GType
990 gst_play_get_type (void)
991 {
992   static GType play_type = 0;
993
994   if (!play_type) {
995     static const GTypeInfo play_info = {
996       sizeof (GstPlayClass),
997       NULL,
998       NULL,
999       (GClassInitFunc) gst_play_class_init,
1000       NULL,
1001       NULL,
1002       sizeof (GstPlay),
1003       0,
1004       (GInstanceInitFunc) gst_play_init,
1005       NULL
1006     };
1007       
1008     play_type = g_type_register_static (GST_TYPE_PIPELINE, "GstPlay",
1009                                         &play_info, 0);
1010   }
1011
1012   return play_type;
1013 }