3932b41a5928b1ad3be2130bbc961dc5a3f501de
[platform/upstream/gstreamer.git] / gst / autoplug / gststaticautoplug.c
1 /* GStreamer
2  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3  *                    2000 Wim Taymans <wtay@chello.be>
4  *
5  * gststaticautoplug.c: A static Autoplugger of pipelines
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 #include "gststaticautoplug.h"
24
25 #include <gst/gst.h>
26
27 #define GST_AUTOPLUG_MAX_COST 999999
28
29 typedef guint           (*GstAutoplugCostFunction) (gpointer src, gpointer dest, gpointer data);
30 typedef const GList*    (*GstAutoplugListFunction) (gpointer data);
31
32
33 static void             gst_static_autoplug_class_init  (GstStaticAutoplugClass *klass);
34 static void             gst_static_autoplug_init        (GstStaticAutoplug *autoplug);
35
36 static GList*           gst_autoplug_func               (gpointer src, gpointer sink,
37                                                          GstAutoplugListFunction list_function,
38                                                          GstAutoplugCostFunction cost_function,
39                                                          gpointer data);
40
41
42
43 static GstElement*      gst_static_autoplug_to_caps     (GstAutoplug *autoplug, 
44                                                          GstCaps *srccaps, GstCaps *sinkcaps, va_list args);
45
46 static GstAutoplugClass *parent_class = NULL;
47
48 GType gst_static_autoplug_get_type(void)
49 {
50   static GType static_autoplug_type = 0;
51
52   if (!static_autoplug_type) {
53     static const GTypeInfo static_autoplug_info = {
54       sizeof(GstElementClass),
55       NULL,
56       NULL,
57       (GClassInitFunc)gst_static_autoplug_class_init,
58       NULL,
59       NULL,
60       sizeof(GstElement),
61       0,
62       (GInstanceInitFunc)gst_static_autoplug_init,
63     };
64     static_autoplug_type = g_type_register_static (GST_TYPE_AUTOPLUG, "GstStaticAutoplug", &static_autoplug_info, 0);
65   }
66   return static_autoplug_type;
67 }
68
69 static void
70 gst_static_autoplug_class_init(GstStaticAutoplugClass *klass)
71 {
72   GstAutoplugClass *gstautoplug_class;
73
74   gstautoplug_class = (GstAutoplugClass*) klass;
75
76   parent_class = g_type_class_ref(GST_TYPE_AUTOPLUG);
77
78   gstautoplug_class->autoplug_to_caps = gst_static_autoplug_to_caps;
79 }
80
81 static void gst_static_autoplug_init(GstStaticAutoplug *autoplug) {
82 }
83
84 static gboolean
85 plugin_init (GModule *module, GstPlugin *plugin)
86 {
87   GstAutoplugFactory *factory;
88
89   gst_plugin_set_longname (plugin, "A static autoplugger");
90
91   factory = gst_autoplug_factory_new ("static",
92                   "A static autoplugger, it constructs the complete element before running it",
93                   gst_static_autoplug_get_type ());
94
95   if (factory != NULL) {
96      gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
97   }
98   return TRUE;
99 }
100
101 GstPluginDesc plugin_desc = {
102   GST_VERSION_MAJOR,
103   GST_VERSION_MINOR,
104   "gststaticautoplug",
105   plugin_init
106 };
107
108 static gboolean
109 gst_autoplug_can_match (GstElementFactory *src, GstElementFactory *dest)
110 {
111   GList *srctemps, *desttemps;
112
113   srctemps = src->padtemplates;
114
115   while (srctemps) {
116     GstPadTemplate *srctemp = (GstPadTemplate *)srctemps->data;
117
118     desttemps = dest->padtemplates;
119
120     while (desttemps) {
121       GstPadTemplate *desttemp = (GstPadTemplate *)desttemps->data;
122
123       if (srctemp->direction == GST_PAD_SRC &&
124           desttemp->direction == GST_PAD_SINK) {
125         if (gst_caps_check_compatibility (gst_pad_template_get_caps (srctemp), 
126                                 gst_pad_template_get_caps (desttemp))) {
127           GST_DEBUG (GST_CAT_AUTOPLUG_ATTEMPT,
128                           "factory \"%s\" can connect with factory \"%s\"\n", GST_OBJECT_NAME (src), 
129                           GST_OBJECT_NAME (dest));
130           return TRUE;
131         }
132       }
133
134       desttemps = g_list_next (desttemps);
135     }
136     srctemps = g_list_next (srctemps);
137   }
138   GST_DEBUG (GST_CAT_AUTOPLUG_ATTEMPT,
139                   "factory \"%s\" cannot connect with factory \"%s\"\n", GST_OBJECT_NAME (src),
140                           GST_OBJECT_NAME (dest));
141   return FALSE;
142 }
143
144 static gboolean
145 gst_autoplug_pads_autoplug_func (GstElement *src, GstPad *pad, GstElement *sink)
146 {
147   const GList *sinkpads;
148   gboolean connected = FALSE;
149
150   GST_DEBUG (0,"gstpipeline: autoplug pad connect function for \"%s\" to \"%s\"",
151                   GST_ELEMENT_NAME(src), GST_ELEMENT_NAME(sink));
152
153   sinkpads = gst_element_get_pad_list(sink);
154   while (sinkpads) {
155     GstPad *sinkpad = (GstPad *)sinkpads->data;
156
157     /* if we have a match, connect the pads */
158     if (gst_pad_get_direction(sinkpad)   == GST_PAD_SINK &&
159         !GST_PAD_IS_CONNECTED(sinkpad))
160     {
161       if (gst_caps_check_compatibility (gst_pad_get_caps(pad), gst_pad_get_caps(sinkpad))) {
162         gst_pad_connect(pad, sinkpad);
163         GST_DEBUG (0,"gstpipeline: autoconnect pad \"%s\" in element %s <-> ", GST_PAD_NAME (pad),
164                        GST_ELEMENT_NAME(src));
165         GST_DEBUG (0,"pad \"%s\" in element %s", GST_PAD_NAME (sinkpad),
166                       GST_ELEMENT_NAME(sink));
167         connected = TRUE;
168         break;
169       }
170       else {
171         GST_DEBUG (0,"pads incompatible %s, %s", GST_PAD_NAME (pad), GST_PAD_NAME (sinkpad));
172       }
173     }
174     sinkpads = g_list_next(sinkpads);
175   }
176
177   if (!connected) {
178     GST_DEBUG (0,"gstpipeline: no path to sinks for type");
179   }
180   return connected;
181 }
182
183 typedef struct {
184   GstElement *result;
185   GstCaps *endcap;
186   gint i;
187 } dynamic_pad_struct;
188
189 static void
190 autoplug_dynamic_pad (GstElement *element, GstPad *pad, gpointer data)
191 {
192   dynamic_pad_struct *info = (dynamic_pad_struct *)data;
193   const GList *pads = gst_element_get_pad_list (element);
194
195   GST_DEBUG (0,"attempting to dynamically create a ghostpad for %s=%s", GST_ELEMENT_NAME (element),
196                   GST_PAD_NAME (pad));
197
198   while (pads) {
199     GstPad *pad = GST_PAD (pads->data);
200     GstPadTemplate *templ = GST_PAD_PAD_TEMPLATE (pad);
201     pads = g_list_next (pads);
202
203     if (gst_caps_check_compatibility (GST_PAD_TEMPLATE_CAPS (templ), info->endcap)) {
204       gchar *name;
205
206       name = g_strdup_printf ("src_%02d", info->i);
207       gst_element_add_ghost_pad (info->result, pad, name);
208       g_free (name);
209       
210       GST_DEBUG (0,"gstpipeline: new dynamic pad %s", GST_PAD_NAME (pad));
211       break;
212     }
213   }
214 }
215
216 static void
217 gst_autoplug_pads_autoplug (GstElement *src, GstElement *sink)
218 {
219   const GList *srcpads;
220   gboolean connected = FALSE;
221
222   srcpads = gst_element_get_pad_list(src);
223
224   while (srcpads && !connected) {
225     GstPad *srcpad = (GstPad *)srcpads->data;
226
227     if (gst_pad_get_direction(srcpad) == GST_PAD_SRC)
228       connected = gst_autoplug_pads_autoplug_func (src, srcpad, sink);
229
230     srcpads = g_list_next(srcpads);
231   }
232
233   if (!connected) {
234     GST_DEBUG (0,"gstpipeline: delaying pad connections for \"%s\" to \"%s\"",
235                     GST_ELEMENT_NAME(src), GST_ELEMENT_NAME(sink));
236     g_signal_connect (G_OBJECT(src), "new_pad", 
237                     G_CALLBACK (gst_autoplug_pads_autoplug_func), sink);
238   }
239 }
240
241 static const GList*
242 gst_autoplug_element_factory_get_list (gpointer data)
243 {
244   return gst_registry_pool_feature_list (GST_TYPE_ELEMENT_FACTORY);
245 }
246
247 typedef struct {
248   GstCaps *src;
249   GstCaps *sink;
250 } caps_struct;
251
252 #define IS_CAPS(cap) (((cap) == caps->src) || (cap) == caps->sink)
253
254 static guint
255 gst_autoplug_caps_find_cost (gpointer src, gpointer dest, gpointer data)
256 {
257   caps_struct *caps = (caps_struct *)data;
258   gboolean res;
259
260   if (IS_CAPS (src) && IS_CAPS (dest)) {
261     res = gst_caps_check_compatibility ((GstCaps *)src, (GstCaps *)dest);
262   }
263   else if (IS_CAPS (src)) {
264     res = gst_element_factory_can_sink_caps ((GstElementFactory *)dest, (GstCaps *)src);
265   }
266   else if (IS_CAPS (dest)) {
267     res = gst_element_factory_can_src_caps ((GstElementFactory *)src, (GstCaps *)dest);
268   }
269   else {
270     res = gst_autoplug_can_match ((GstElementFactory *)src, (GstElementFactory *)dest);
271   }
272
273   if (res)
274     return 1;
275   else
276     return GST_AUTOPLUG_MAX_COST;
277 }
278
279 static GstElement*
280 gst_static_autoplug_to_caps (GstAutoplug *autoplug, GstCaps *srccaps, GstCaps *sinkcaps, va_list args)
281 {
282   caps_struct caps;
283   GstCaps *capslist;
284   GstElement *result = NULL, *srcelement = NULL;
285   GList **factories;
286   GList *chains = NULL;
287   GList *endcaps = NULL;
288   guint numsinks = 0, i;
289   gboolean have_common = FALSE;
290
291   capslist = sinkcaps;
292
293   /*
294    * We first create a list of elements that are needed
295    * to convert the srcpad caps to the different sinkpad caps.
296    * and add the list of elementfactories to a list (chains).
297    */
298   caps.src  = srccaps;
299
300   while (capslist) {
301     GList *elements;
302
303     caps.sink = capslist;
304
305     GST_INFO (GST_CAT_AUTOPLUG_ATTEMPT,"autoplugging two caps structures");
306
307     elements =  gst_autoplug_func (caps.src, caps.sink,
308                                    gst_autoplug_element_factory_get_list,
309                                    gst_autoplug_caps_find_cost,
310                                    &caps);
311
312     if (elements) {
313       chains = g_list_append (chains, elements);
314       endcaps = g_list_append (endcaps, capslist);
315       numsinks++;
316     }
317     else {
318     }
319
320     capslist = va_arg (args, GstCaps *);
321   }
322
323   /*
324    * If no list could be found the pipeline cannot be autoplugged and
325    * we return a NULL element
326    */
327   if (numsinks == 0)
328     return NULL;
329
330   /*
331    * We now have a list of lists. We will turn this into an array
332    * of lists, this will make it much more easy to manipulate it
333    * in the next steps.
334    */
335   factories = g_new0 (GList *, numsinks);
336
337   for (i = 0; chains; i++) {
338     GList *elements = (GList *) chains->data;
339
340     factories[i] = elements;
341
342     chains = g_list_next (chains);
343   }
344   /*FIXME, free the list */
345
346   result = gst_bin_new ("autoplug_bin");
347
348   /*
349    * We now hav a list of lists that is probably like:
350    *
351    *  !
352    *  A -> B -> C
353    *  !
354    *  A -> D -> E
355    *
356    * we now try to find the common elements (A) and add them to
357    * the bin. We remove them from both lists too.
358    */
359   while (factories[0]) {
360     GstElementFactory *factory;
361     GstElement *element;
362     gchar *name;
363
364     /* fase 3: add common elements */
365     factory = (GstElementFactory *) (factories[0]->data);
366
367     /* check to other paths for matching elements (factories) */
368     for (i=1; i<numsinks; i++) {
369       if (factory != (GstElementFactory *) (factories[i]->data)) {
370         goto differ;
371       }
372     }
373
374     GST_DEBUG (0,"common factory \"%s\"", GST_OBJECT_NAME (factory));
375     
376     /* it is likely that the plugin is not loaded yet. thus when it loads it
377      * will replace the elementfactory that gst built from the cache, and the
378      * GST_OBJECT_NAME will no longer be valid. thus we must g_strdup its name.
379      *
380      * this might be an implementation problem, i don't know, if a program keeps
381      * a reference to a cached factory after a factory has been added on plugin
382      * initialization. i raelly don't know though.
383      */
384     name = g_strdup (GST_OBJECT_NAME (factory));
385     element = gst_element_factory_create (factory, name);
386     g_free(name);
387     gst_bin_add (GST_BIN(result), element);
388
389     if (srcelement != NULL) {
390       gst_autoplug_pads_autoplug (srcelement, element);
391     }
392     /* this is the first element, find a good ghostpad */
393     else {
394       const GList *pads;
395
396       pads = gst_element_get_pad_list (element);
397
398       while (pads) {
399         GstPad *pad = GST_PAD (pads->data);
400         GstPadTemplate *templ = GST_PAD_PAD_TEMPLATE (pad);
401
402         if (gst_caps_check_compatibility (srccaps, GST_PAD_TEMPLATE_CAPS (templ))) {
403           gst_element_add_ghost_pad (result, pad, "sink");
404           break;
405         }
406
407         pads = g_list_next (pads);
408       }
409     }
410     gst_autoplug_signal_new_object (GST_AUTOPLUG (autoplug), GST_OBJECT (element));
411
412     srcelement = element;
413
414     /* advance the pointer in all lists */
415     for (i=0; i<numsinks; i++) {
416       factories[i] = g_list_next (factories[i]);
417     }
418
419     have_common = TRUE;
420   }
421
422 differ:
423
424   /* loop over all the sink elements */
425   for (i = 0; i < numsinks; i++) {
426     GstElement *thesrcelement = srcelement;
427     GstElement *thebin = GST_ELEMENT(result);
428
429     while (factories[i]) {
430       /* fase 4: add other elements... */
431       GstElementFactory *factory;
432       GstElement *element;
433
434       factory = (GstElementFactory *)(factories[i]->data);
435
436       GST_DEBUG (0,"factory \"%s\"", GST_OBJECT_NAME (factory));
437       element = gst_element_factory_create(factory, GST_OBJECT_NAME (factory));
438
439       GST_DEBUG (0,"adding element %s", GST_ELEMENT_NAME (element));
440       gst_bin_add(GST_BIN(thebin), element);
441       gst_autoplug_signal_new_object (GST_AUTOPLUG (autoplug), GST_OBJECT (element));
442       
443       gst_autoplug_pads_autoplug(thesrcelement, element);
444
445       /* this element is now the new source element */
446       thesrcelement = element;
447
448       factories[i] = g_list_next(factories[i]);
449     }
450     /*
451      * we're at the last element in the chain,
452      * find a suitable pad to turn into a ghostpad
453      */
454     {
455       GstCaps *endcap = (GstCaps *)(endcaps->data);
456       const GList *pads = gst_element_get_pad_list (thesrcelement);
457       gboolean have_pad = FALSE;
458       endcaps = g_list_next (endcaps);
459
460       GST_DEBUG (0,"attempting to create a ghostpad for %s", GST_ELEMENT_NAME (thesrcelement));
461
462       while (pads) {
463         GstPad *pad = GST_PAD (pads->data);
464         GstPadTemplate *templ = GST_PAD_PAD_TEMPLATE (pad);
465         pads = g_list_next (pads);
466
467         if (gst_caps_check_compatibility (GST_PAD_TEMPLATE_CAPS (templ), endcap)) {
468           gchar *name;
469
470           name = g_strdup_printf ("src_%02d", i);
471           gst_element_add_ghost_pad (result, pad, name);
472           g_free (name);
473           
474           have_pad = TRUE;
475           break;
476         }
477       }
478       if (!have_pad) {
479         dynamic_pad_struct *data = g_new0(dynamic_pad_struct, 1);
480
481         data->result = result;
482         data->endcap = endcap;
483         data->i = i;
484
485         GST_DEBUG (0,"delaying the creation of a ghostpad for %s", GST_ELEMENT_NAME (thesrcelement));
486         g_signal_connect (G_OBJECT (thesrcelement), "new_pad", 
487                         G_CALLBACK (autoplug_dynamic_pad), data);
488       }
489     }
490   }
491
492   return result;
493 }
494
495 /*
496  * shortest path algorithm
497  *
498  */
499 struct _gst_autoplug_node
500 {
501   gpointer iNode;
502   gpointer iPrev;
503   gint iDist;
504 };
505
506 typedef struct _gst_autoplug_node gst_autoplug_node;
507
508 static gint
509 find_factory (gst_autoplug_node *rgnNodes, gpointer factory)
510 {
511   gint i=0;
512
513   while (rgnNodes[i].iNode) {
514     if (rgnNodes[i].iNode == factory) return i;
515     i++;
516   }
517   return 0;
518 }
519
520 static GList*
521 construct_path (gst_autoplug_node *rgnNodes, gpointer factory)
522 {
523   GstElementFactory *current;
524   GList *factories = NULL;
525
526   current = rgnNodes[find_factory(rgnNodes, factory)].iPrev;
527
528   GST_INFO (GST_CAT_AUTOPLUG_ATTEMPT,"factories found in autoplugging (reversed order)");
529
530   while (current != NULL)
531   {
532     gpointer next = NULL;
533
534     next = rgnNodes[find_factory(rgnNodes, current)].iPrev;
535     if (next) {
536       factories = g_list_prepend (factories, current);
537       GST_INFO (GST_CAT_AUTOPLUG_ATTEMPT,"factory: \"%s\"", GST_OBJECT_NAME (current));
538     }
539     current = next;
540   }
541   return factories;
542 }
543
544 static GList*
545 gst_autoplug_enqueue (GList *queue, gpointer iNode, gint iDist, gpointer iPrev)
546 {
547   gst_autoplug_node *node = g_malloc (sizeof (gst_autoplug_node));
548
549   node->iNode = iNode;
550   node->iDist = iDist;
551   node->iPrev = iPrev;
552
553   queue = g_list_append (queue, node);
554
555   return queue;
556 }
557
558 static GList*
559 gst_autoplug_dequeue (GList *queue, gpointer *iNode, gint *iDist, gpointer *iPrev)
560 {
561   GList *head;
562   gst_autoplug_node *node;
563
564   head = g_list_first (queue);
565
566   if (head) {
567     node = (gst_autoplug_node *)head->data;
568     *iNode = node->iNode;
569     *iPrev = node->iPrev;
570     *iDist = node->iDist;
571     head = g_list_remove (queue, node);
572   }
573
574   return head;
575 }
576
577 static GList*
578 gst_autoplug_func (gpointer src, gpointer sink,
579                    GstAutoplugListFunction list_function,
580                    GstAutoplugCostFunction cost_function,
581                    gpointer data)
582 {
583   gst_autoplug_node *rgnNodes;
584   GList *queue = NULL;
585   gpointer iNode, iPrev;
586   gint iDist, i, iCost;
587
588   GList *elements = g_list_copy ((GList *)list_function(data));
589   GList *factories;
590   guint num_factories;
591
592   elements = g_list_append (elements, sink);
593   elements = g_list_append (elements, src);
594
595   factories = elements;
596
597   num_factories = g_list_length (factories);
598
599   rgnNodes = g_new0 (gst_autoplug_node, num_factories+1);
600
601   for (i=0; i< num_factories; i++) {
602     gpointer fact = factories->data;
603
604     rgnNodes[i].iNode = fact;
605     rgnNodes[i].iPrev = NULL;
606
607     if (fact == src) {
608       rgnNodes[i].iDist = 0;
609     }
610     else {
611       rgnNodes[i].iDist = GST_AUTOPLUG_MAX_COST;
612     }
613
614     factories = g_list_next (factories);
615   }
616   rgnNodes[num_factories].iNode = NULL;
617
618   queue = gst_autoplug_enqueue (queue, src, 0, NULL);
619
620   while (g_list_length (queue) > 0) {
621     GList *factories2 = elements;
622
623     queue = gst_autoplug_dequeue (queue, &iNode, &iDist, &iPrev);
624
625     for (i=0; i< num_factories; i++) {
626       gpointer current = factories2->data;
627
628       iCost = cost_function (iNode, current, data);
629       if (iCost != GST_AUTOPLUG_MAX_COST) {
630         if ((GST_AUTOPLUG_MAX_COST == rgnNodes[i].iDist) ||
631             (rgnNodes[i].iDist > (iCost + iDist))) {
632           rgnNodes[i].iDist = iDist + iCost;
633           rgnNodes[i].iPrev = iNode;
634
635           queue = gst_autoplug_enqueue (queue, current, iDist + iCost, iNode);
636         }
637       }
638
639       factories2 = g_list_next (factories2);
640     }
641   }
642
643   return construct_path (rgnNodes, sink);
644 }
645