MPEG2 types added for better autoplugging. MPEG2 works more or less. fix in autoplugg...
[platform/upstream/gstreamer.git] / gst / gstpipeline.c
1 /* Gnome-Streamer
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 #include <gst/gstpipeline.h>
21 #include <gst/gstthread.h>
22 #include <gst/gstsink.h>
23 #include <gst/gstutils.h>
24 #include <gst/gsttype.h>
25
26 #include "config.h"
27
28 GstElementDetails gst_pipeline_details = {
29   "Pipeline object",
30   "Bin",
31   "Complete pipeline object",
32   VERSION,
33   "Erik Walthinsen <omega@cse.ogi.edu>",
34   "(C) 1999",
35 };
36
37
38 /* Pipeline signals and args */
39 enum {
40   /* FILL ME */
41   LAST_SIGNAL
42 };
43
44 enum {
45   ARG_0,
46   /* FILL ME */
47 };
48
49
50 static void gst_pipeline_class_init(GstPipelineClass *klass);
51 static void gst_pipeline_init(GstPipeline *pipeline);
52
53 static GstElementStateReturn gst_pipeline_change_state(GstElement *element);
54
55 static void gst_pipeline_prepare(GstPipeline *pipeline);
56
57 static void gst_pipeline_have_type(GstSink *sink, GstSink *sink2, gpointer data);
58 static void gst_pipeline_pads_autoplug(GstElement *src, GstElement *sink);
59
60 static GstBin *parent_class = NULL;
61 //static guint gst_pipeline_signals[LAST_SIGNAL] = { 0 };
62
63 GtkType
64 gst_pipeline_get_type(void) {
65   static GtkType pipeline_type = 0;
66
67   if (!pipeline_type) {
68     static const GtkTypeInfo pipeline_info = {
69       "GstPipeline",
70       sizeof(GstPipeline),
71       sizeof(GstPipelineClass),
72       (GtkClassInitFunc)gst_pipeline_class_init,
73       (GtkObjectInitFunc)gst_pipeline_init,
74       (GtkArgSetFunc)NULL,
75       (GtkArgGetFunc)NULL,
76       (GtkClassInitFunc)NULL,
77     };
78     pipeline_type = gtk_type_unique(gst_bin_get_type(),&pipeline_info);
79   }
80   return pipeline_type;
81 }
82
83 static void
84 gst_pipeline_class_init(GstPipelineClass *klass) {
85   GstElementClass *gstelement_class;
86
87   gstelement_class = (GstElementClass*)klass;
88
89   parent_class = gtk_type_class(gst_bin_get_type());
90
91   gstelement_class->change_state = gst_pipeline_change_state;
92   gstelement_class->elementfactory = gst_elementfactory_find("pipeline");
93 }
94
95 static void gst_pipeline_init(GstPipeline *pipeline) {
96   pipeline->src = NULL;
97   pipeline->sinks = NULL;
98 }
99
100
101 /**
102  * gst_pipeline_new:
103  * @name: name of new pipeline
104  *
105  * Create a new pipeline with the given name.
106  *
107  * Returns: newly created GstPipeline
108  */
109 GstElement *gst_pipeline_new(guchar *name) {
110   GstPipeline *pipeline;
111
112   pipeline = gtk_type_new(gst_pipeline_get_type());
113   gst_element_set_name(GST_ELEMENT(pipeline),name);
114   return GST_ELEMENT(pipeline);
115 }
116
117 static void gst_pipeline_prepare(GstPipeline *pipeline) {
118   g_print("GstPipeline: preparing pipeline \"%s\" for playing\n", 
119                   gst_element_get_name(GST_ELEMENT(pipeline)));
120 }
121
122 static void gst_pipeline_have_type(GstSink *sink, GstSink *sink2, gpointer data) {
123   g_print("GstPipeline: pipeline have type %p\n", (gboolean *)data);
124
125   *(gboolean *)data = TRUE;
126 }
127
128 static guint16 gst_pipeline_typefind(GstPipeline *pipeline, GstElement *element) {
129   gboolean found = FALSE;
130   GstElement *typefind;
131   guint16 type_id = 0;
132
133   g_print("GstPipeline: typefind for element \"%s\" %p\n", 
134                   gst_element_get_name(element), &found);
135
136   typefind = gst_elementfactory_make("typefind","typefind");
137   g_return_val_if_fail(typefind != NULL, FALSE);
138
139   gtk_signal_connect(GTK_OBJECT(typefind),"have_type",
140                     GTK_SIGNAL_FUNC(gst_pipeline_have_type), &found);
141
142   gst_pad_connect(gst_element_get_pad(element,"src"),
143                   gst_element_get_pad(typefind,"sink"));
144
145   gst_bin_add(GST_BIN(pipeline), typefind);
146
147   gst_bin_create_plan(GST_BIN(pipeline));
148   gst_element_set_state(GST_ELEMENT(element),GST_STATE_READY);
149   gst_element_set_state(GST_ELEMENT(element),GST_STATE_PLAYING);
150
151   // keep pushing buffers... the have_type signal handler will set the found flag
152   while (!found) {
153     gst_src_push(GST_SRC(element));
154   }
155
156   gst_element_set_state(GST_ELEMENT(element),GST_STATE_NULL);
157
158   if (found) {
159     type_id = gst_util_get_int_arg(GTK_OBJECT(typefind),"type");
160     gst_pad_set_type_id(gst_element_get_pad(element, "src"), type_id);
161   }
162
163   gst_pad_disconnect(gst_element_get_pad(element,"src"),
164                     gst_element_get_pad(typefind,"sink"));
165   gst_bin_remove(GST_BIN(pipeline), typefind);
166   gst_object_unref(GST_OBJECT(typefind));
167
168   return type_id;
169 }
170
171 static void gst_pipeline_pads_autoplug_func(GstElement *src, GstPad *pad, GstElement *sink) {
172   GList *sinkpads;
173   GstPad *sinkpad;
174   gboolean connected = FALSE;
175
176   g_print("gstpipeline: autoplug pad connect function type %d for \"%s\" to \"%s\"\n", pad->type, 
177                   gst_element_get_name(src), gst_element_get_name(sink));
178
179   sinkpads = gst_element_get_pad_list(sink);
180   while (sinkpads) {
181     sinkpad = (GstPad *)sinkpads->data;
182
183     // if we have a match, connect the pads
184     if (sinkpad->type == pad->type && 
185         sinkpad->direction == GST_PAD_SINK && 
186         !GST_PAD_CONNECTED(sinkpad)) 
187     {
188       gst_pad_connect(pad, sinkpad);
189       g_print("gstpipeline: autoconnect pad \"%s\" (%d) in element %s <-> ", pad->name, 
190                       pad->type, gst_element_get_name(src));
191       g_print("pad \"%s\" (%d) in element %s\n", sinkpad->name, sinkpad->type, 
192                       gst_element_get_name(sink));
193       connected = TRUE;
194       break;
195     }
196     sinkpads = g_list_next(sinkpads);
197   }
198
199   if (!connected) {
200     g_print("gstpipeline: no path to sinks for type %d\n", pad->type);
201   }
202 }
203
204 static void gst_pipeline_pads_autoplug(GstElement *src, GstElement *sink) {
205   GList *srcpads, *sinkpads;
206   gboolean connected = FALSE;
207
208   srcpads = gst_element_get_pad_list(src);
209
210   while (srcpads) {
211     GstPad *srcpad = (GstPad *)srcpads->data;
212     GstPad *sinkpad;
213
214     if (srcpad->direction == GST_PAD_SRC && !GST_PAD_CONNECTED(srcpad)) {
215
216       sinkpads = gst_element_get_pad_list(sink);
217       // FIXME could O(n) if the types were sorted...
218       while (sinkpads) {
219         sinkpad = (GstPad *)sinkpads->data;
220
221         // if we have a match, connect the pads
222         if (sinkpad->type == srcpad->type && 
223             sinkpad->direction == GST_PAD_SINK && 
224             !GST_PAD_CONNECTED(sinkpad)) {
225           gst_pad_connect(srcpad, sinkpad);
226           g_print("gstpipeline: autoconnect pad \"%s\" (%d) in element %s <-> ", 
227                           srcpad->name, srcpad->type, gst_element_get_name(src));
228           g_print("pad \"%s\" (%d) in element %s\n", sinkpad->name, 
229                           sinkpad->type, gst_element_get_name(sink));
230           connected = TRUE;
231           goto end;
232         }
233         sinkpads = g_list_next(sinkpads);
234       }
235     }
236     srcpads = g_list_next(srcpads);
237   }
238   
239 end:
240   if (!connected) {
241     g_print("gstpipeline: delaying pad connections for \"%s\" to \"%s\"\n",
242                     gst_element_get_name(src), gst_element_get_name(sink));
243     gtk_signal_connect(GTK_OBJECT(src),"new_pad",
244                  GTK_SIGNAL_FUNC(gst_pipeline_pads_autoplug_func), sink);
245   }
246 }
247
248 /**
249  * gst_pipeline_add_src:
250  * @pipeline: the pipeline to add the src to
251  * @src: the src to add to the pipeline
252  *
253  * Adds a src element to the pipeline. This element
254  * will be used as a src for autoplugging. If you add more
255  * than one src element, the previously added element will
256  * be removed.
257  */
258 void gst_pipeline_add_src(GstPipeline *pipeline, GstElement *src) 
259 {
260   g_return_if_fail(pipeline != NULL);
261   g_return_if_fail(GST_IS_PIPELINE(pipeline));
262   g_return_if_fail(src != NULL);
263   g_return_if_fail(GST_IS_ELEMENT(src));
264
265   if (pipeline->src) {
266     printf("gstpipeline: *WARNING* removing previously added element \"%s\"\n",
267                           gst_element_get_name(pipeline->src));
268     gst_bin_remove(GST_BIN(pipeline), pipeline->src);
269   }
270   pipeline->src = src;
271   gst_bin_add(GST_BIN(pipeline), src);
272 }
273
274 /**
275  * gst_pipeline_add_sink:
276  * @pipeline: the pipeline to add the sink to
277  * @sink: the sink to add to the pipeline
278  *
279  * Adds a sink element to the pipeline. This element
280  * will be used as a sink for autoplugging
281  */
282 void gst_pipeline_add_sink(GstPipeline *pipeline, GstElement *sink) 
283 {
284   g_return_if_fail(pipeline != NULL);
285   g_return_if_fail(GST_IS_PIPELINE(pipeline));
286   g_return_if_fail(sink != NULL);
287   g_return_if_fail(GST_IS_ELEMENT(sink));
288
289   pipeline->sinks = g_list_prepend(pipeline->sinks, sink);
290   //gst_bin_add(GST_BIN(pipeline), sink);
291 }
292
293 /**
294  * gst_pipeline_autoplug:
295  * @pipeline: the pipeline to autoplug
296  *
297  * Constructs a complete pipeline by automatically
298  * detecting the plugins needed.
299  *
300  * Returns: a gboolean indicating success or failure.
301  */
302 gboolean gst_pipeline_autoplug(GstPipeline *pipeline) {
303   GList *elements;
304   GstElement *element, *srcelement = NULL, *sinkelement= NULL;
305   GList **factories;
306   GList **base_factories;
307   GstElementFactory *factory;
308   GList *src_types;
309   guint16 src_type = 0, sink_type = 0;
310   guint i, numsinks;
311   gboolean use_thread = FALSE, have_common = FALSE;
312
313   g_return_val_if_fail(pipeline != NULL, FALSE);
314   g_return_val_if_fail(GST_IS_PIPELINE(pipeline), FALSE);
315
316   g_print("GstPipeline: autopluging pipeline \"%s\"\n", 
317                   gst_element_get_name(GST_ELEMENT(pipeline)));
318
319
320   // fase 1, run typedetect on the source if needed... 
321   if (!pipeline->src) {
322     g_print("GstPipeline: no source detected, can't autoplug pipeline \"%s\"\n", 
323                 gst_element_get_name(GST_ELEMENT(pipeline)));
324     return FALSE;
325   }
326
327   factory = gst_element_get_factory(pipeline->src);
328
329   src_types = factory->src_types;
330   if (src_types == NULL) {
331     g_print("GstPipeline: source \"%s\" has no MIME type, running typefind...\n", 
332                 gst_element_get_name(pipeline->src));
333
334     src_type = gst_pipeline_typefind(pipeline, pipeline->src);
335
336     if (src_type) {
337       g_print("GstPipeline: source \"%s\" type found %d\n", gst_element_get_name(pipeline->src), 
338                   src_type);
339     }
340     else {
341       g_print("GstPipeline: source \"%s\" has no type\n", gst_element_get_name(pipeline->src));
342       return FALSE;
343     }
344   }
345   else {
346     while (src_types) {
347       // FIXME loop over types and find paths...
348       src_types = g_list_next(src_types);
349     }
350   }
351
352   srcelement = pipeline->src;
353
354   elements = pipeline->sinks;
355
356   numsinks = g_list_length(elements);
357   factories = g_new0(GList *, numsinks);
358   base_factories = g_new0(GList *, numsinks);
359
360   i = 0;
361   // fase 2, loop over all the sinks.. 
362   while (elements) {
363     GList *pads;
364     GstPad *pad;
365
366     element = GST_ELEMENT(elements->data);
367
368     pads = gst_element_get_pad_list(element);
369
370     while (pads) {
371       pad = (GstPad *)pads->data;
372
373       if (pad->direction == GST_PAD_SINK) {
374         sink_type = gst_pad_get_type_id(pad);
375         break;
376       }
377
378       pads = g_list_next(pads);
379     }
380
381     base_factories[i] = factories[i] = gst_type_get_sink_to_src(src_type, sink_type);
382     i++;
383
384     elements = g_list_next(elements);
385   }
386
387   while (factories[0]) {
388     // fase 3: add common elements 
389     factory = (GstElementFactory *)(factories[0]->data);
390
391     // check to other paths for mathing elements (factories)
392     for (i=1; i<numsinks; i++) {
393       if (factory != (GstElementFactory *)(factories[i]->data)) {
394         goto differ;
395       }
396       factories[i] = g_list_next(factories[i]);
397     }
398     factory = (GstElementFactory *)(factories[0]->data);
399
400     g_print("GstPipeline: common factory \"%s\"\n", factory->name);
401
402     element = gst_elementfactory_create(factory, factory->name);
403     gst_bin_add(GST_BIN(pipeline), element);
404
405     gst_pipeline_pads_autoplug(srcelement, element);
406
407     srcelement = element;
408
409     factories[0] = g_list_next(factories[0]);
410
411     have_common = TRUE;
412   }
413
414 differ:
415   // loop over all the sink elements
416   elements = pipeline->sinks;
417
418   i = 0;
419   while (elements) {
420     GstElement *thesrcelement = srcelement;
421     GstElement *thebin = GST_ELEMENT(pipeline);
422
423     if (g_list_length(base_factories[i]) == 0) goto next;
424
425     sinkelement = (GstElement *)elements->data;
426
427     use_thread = have_common;
428
429     while (factories[i] || sinkelement) {
430       // fase 4: add other elements...
431        
432       if (factories[i]) {
433         factory = (GstElementFactory *)(factories[i]->data);
434         g_print("GstPipeline: factory \"%s\"\n", factory->name);
435         element = gst_elementfactory_create(factory, factory->name);
436         factories[i] = g_list_next(factories[i]);
437       }
438       // we have arived to the final sink element
439       else {
440         element = sinkelement;
441         sinkelement = NULL;
442       }
443
444       // this element suggests the use of a thread, so we set one up...
445       if (GST_ELEMENT_IS_THREAD_SUGGESTED(element) || use_thread) {
446         GstElement *queue;
447         GList *sinkpads;
448         GstPad *srcpad, *sinkpad;
449
450         use_thread = FALSE;
451
452         g_print("GstPipeline: sugest new thread for \"%s\" %08x\n", element->name, GST_FLAGS(element));
453
454         // create a new queue and add to the previous bin
455         queue = gst_elementfactory_make("queue", g_strconcat("queue_", gst_element_get_name(element), NULL));
456         gst_bin_add(GST_BIN(thebin), queue);
457
458         // this will be the new bin for all following elements
459         thebin = gst_elementfactory_make("thread", g_strconcat("thread_", gst_element_get_name(element), NULL));
460
461         srcpad = gst_element_get_pad(queue, "src");
462
463         sinkpads = gst_element_get_pad_list(element);
464         while (sinkpads) {
465           sinkpad = (GstPad *)sinkpads->data;
466
467           // FIXME connect matching pads, not just the first one...
468           if (sinkpad->direction == GST_PAD_SINK && 
469               !GST_PAD_CONNECTED(sinkpad)) {
470             // the queue has the types of the element it connects
471             srcpad->type = sinkpad->type;
472             gst_element_get_pad(queue, "sink")->type = sinkpad->type;
473             break;
474           }
475           sinkpads = g_list_next(sinkpads);
476         }
477         gst_pipeline_pads_autoplug(thesrcelement, queue);
478
479         gst_bin_add(GST_BIN(thebin), element);
480         gst_bin_add(GST_BIN(pipeline), thebin);
481         thesrcelement = queue;
482       }
483       // no thread needed, easy case
484       else {
485         gst_bin_add(GST_BIN(thebin), element);
486       }
487       gst_pipeline_pads_autoplug(thesrcelement, element);
488
489       // this element is now the new source element
490       thesrcelement = element;
491     }
492 next:
493     elements = g_list_next(elements);
494     i++;
495   }
496   return TRUE;
497   
498   g_print("GstPipeline: unable to autoplug pipeline \"%s\"\n", 
499                   gst_element_get_name(GST_ELEMENT(pipeline)));
500   return FALSE;
501 }
502
503 static GstElementStateReturn gst_pipeline_change_state(GstElement *element) {
504   GstPipeline *pipeline;
505
506   g_return_val_if_fail(GST_IS_PIPELINE(element), FALSE);
507   pipeline = GST_PIPELINE(element);
508
509
510   switch (GST_STATE_PENDING(pipeline)) {
511     case GST_STATE_READY:
512       // we need to set up internal state
513       gst_pipeline_prepare(pipeline);
514       break;
515     default:
516       break;
517   }
518     
519   if (GST_ELEMENT_CLASS(parent_class)->change_state)
520     return GST_ELEMENT_CLASS(parent_class)->change_state(element);
521   return GST_STATE_SUCCESS;
522 }
523
524
525 /**
526  * gst_pipeline_iterate:
527  * @pipeline: GstPipeline to iterate
528  *
529  * Cause the pipeline's contents to be run through one full 'iteration'.
530  */
531 void gst_pipeline_iterate(GstPipeline *pipeline) {
532   g_return_if_fail(pipeline != NULL);
533   g_return_if_fail(GST_IS_PIPELINE(pipeline));
534 }