c544f7f1899fd80df54953f20cf255c1d5264708
[platform/upstream/gstreamer.git] / gst / autoplug / gststaticautoplugrender.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 "gststaticautoplugrender.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 GList*  (*GstAutoplugListFunction) (gpointer data);
31
32
33 static void             gst_static_autoplug_render_class_init   (GstStaticAutoplugRenderClass *klass);
34 static void             gst_static_autoplug_render_init (GstStaticAutoplugRender *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_render   (GstAutoplug *autoplug, 
44                                                          GstCaps *srccaps, GstElement *target, va_list args);
45
46 static GstAutoplugClass *parent_class = NULL;
47
48 GtkType gst_static_autoplug_render_get_type(void)
49 {
50   static GtkType static_autoplug_type = 0;
51
52   if (!static_autoplug_type) {
53     static const GtkTypeInfo static_autoplug_info = {
54       "GstStaticAutoplugRender",
55       sizeof(GstElement),
56       sizeof(GstElementClass),
57       (GtkClassInitFunc)gst_static_autoplug_render_class_init,
58       (GtkObjectInitFunc)gst_static_autoplug_render_init,
59       (GtkArgSetFunc)NULL,
60       (GtkArgGetFunc)NULL,
61       (GtkClassInitFunc)NULL,
62     };
63     static_autoplug_type = gtk_type_unique (GST_TYPE_AUTOPLUG, &static_autoplug_info);
64   }
65   return static_autoplug_type;
66 }
67
68 static void
69 gst_static_autoplug_render_class_init(GstStaticAutoplugRenderClass *klass)
70 {
71   GstAutoplugClass *gstautoplug_class;
72
73   gstautoplug_class = (GstAutoplugClass*) klass;
74
75   parent_class = gtk_type_class(GST_TYPE_AUTOPLUG);
76
77   gstautoplug_class->autoplug_to_renderers = gst_static_autoplug_to_render;
78 }
79
80 static void gst_static_autoplug_render_init(GstStaticAutoplugRender *autoplug) {
81 }
82
83 static gboolean
84 plugin_init (GModule *module, GstPlugin *plugin)
85 {
86   GstAutoplugFactory *factory;
87
88   gst_plugin_set_longname (plugin, "A static autoplugger");
89
90   factory = gst_autoplugfactory_new ("staticrender",
91                   "A static autoplugger, it constructs the complete element before running it",
92                   gst_static_autoplug_render_get_type ());
93
94   if (factory != NULL) {
95      gst_plugin_add_autoplugger (plugin, factory);
96   }
97   return TRUE;
98 }
99
100 GstPluginDesc plugin_desc = {
101   GST_VERSION_MAJOR,
102   GST_VERSION_MINOR,
103   "gststaticautoplugrender",
104   plugin_init
105 };
106
107 static gboolean
108 gst_autoplug_can_match (GstElementFactory *src, GstElementFactory *dest)
109 {
110   GList *srctemps, *desttemps;
111
112   srctemps = src->padtemplates;
113
114   while (srctemps) {
115     GstPadTemplate *srctemp = (GstPadTemplate *)srctemps->data;
116
117     desttemps = dest->padtemplates;
118
119     while (desttemps) {
120       GstPadTemplate *desttemp = (GstPadTemplate *)desttemps->data;
121
122       if (srctemp->direction == GST_PAD_SRC &&
123           desttemp->direction == GST_PAD_SINK) {
124         if (gst_caps_check_compatibility (GST_PADTEMPLATE_CAPS (srctemp), GST_PADTEMPLATE_CAPS (desttemp))) {
125           GST_DEBUG (GST_CAT_AUTOPLUG_ATTEMPT,
126                           "factory \"%s\" can connect with factory \"%s\"", src->name, dest->name);
127           return TRUE;
128         }
129       }
130
131       desttemps = g_list_next (desttemps);
132     }
133     srctemps = g_list_next (srctemps);
134   }
135   GST_DEBUG (GST_CAT_AUTOPLUG_ATTEMPT,
136                   "factory \"%s\" cannot connect with factory \"%s\"", src->name, dest->name);
137   return FALSE;
138 }
139
140 static gboolean
141 gst_autoplug_pads_autoplug_func (GstElement *src, GstPad *pad, GstElement *sink)
142 {
143   GList *sinkpads;
144   gboolean connected = FALSE;
145
146   GST_DEBUG (0,"gstpipeline: autoplug pad connect function for %s %s:%s to \"%s\"\n",
147                   GST_ELEMENT_NAME (src), GST_DEBUG_PAD_NAME(pad), GST_ELEMENT_NAME(sink));
148
149   sinkpads = gst_element_get_pad_list(sink);
150   while (sinkpads) {
151     GstPad *sinkpad = (GstPad *)sinkpads->data;
152
153     // if we have a match, connect the pads
154     if (gst_pad_get_direction(sinkpad) == GST_PAD_SINK &&
155         !GST_PAD_CONNECTED (pad) && !GST_PAD_CONNECTED(sinkpad))
156     {
157       if ((connected = gst_pad_connect (pad, sinkpad))) {
158         break;
159       }
160       else {
161         GST_DEBUG (0,"pads incompatible %s, %s\n", GST_PAD_NAME (pad), GST_PAD_NAME (sinkpad));
162       }
163     }
164     sinkpads = g_list_next(sinkpads);
165   }
166
167   if (!connected) {
168     GST_DEBUG (0,"gstpipeline: no path to sinks for type\n");
169   }
170   return connected;
171 }
172
173 static void
174 gst_autoplug_pads_autoplug (GstElement *src, GstElement *sink)
175 {
176   GList *srcpads;
177   gboolean connected = FALSE;
178
179   srcpads = gst_element_get_pad_list(src);
180
181   while (srcpads && !connected) {
182     GstPad *srcpad = (GstPad *)srcpads->data;
183
184     if (gst_pad_get_direction(srcpad) == GST_PAD_SRC) {
185       connected = gst_autoplug_pads_autoplug_func (src, srcpad, sink);
186       if (connected)
187         break;
188     }
189
190     srcpads = g_list_next(srcpads);
191   }
192
193   if (!connected) {
194     GST_DEBUG (0,"gstpipeline: delaying pad connections for \"%s\" to \"%s\"\n",
195                     GST_ELEMENT_NAME(src), GST_ELEMENT_NAME(sink));
196     gtk_signal_connect(GTK_OBJECT(src),"new_pad",
197                  GTK_SIGNAL_FUNC(gst_autoplug_pads_autoplug_func), sink);
198     gtk_signal_connect(GTK_OBJECT(src),"new_ghost_pad",
199                  GTK_SIGNAL_FUNC(gst_autoplug_pads_autoplug_func), sink);
200   }
201 }
202
203 static GList*
204 gst_autoplug_elementfactory_get_list (gpointer data)
205 {
206   return gst_elementfactory_get_list ();
207 }
208
209 typedef struct {
210   GstCaps *src;
211   GstCaps *sink;
212 } caps_struct;
213
214 #define IS_CAPS(cap) (((cap) == caps->src) || (cap) == caps->sink)
215
216 static guint
217 gst_autoplug_caps_find_cost (gpointer src, gpointer dest, gpointer data)
218 {
219   caps_struct *caps = (caps_struct *)data;
220   gboolean res;
221
222   if (IS_CAPS (src) && IS_CAPS (dest)) {
223     res = gst_caps_check_compatibility ((GstCaps *)src, (GstCaps *)dest);
224     //GST_INFO (GST_CAT_AUTOPLUG_ATTEMPT,"caps %d to caps %d %d", ((GstCaps *)src)->id, ((GstCaps *)dest)->id, res);
225   }
226   else if (IS_CAPS (src)) {
227     res = gst_elementfactory_can_sink_caps ((GstElementFactory *)dest, (GstCaps *)src);
228     //GST_INFO (GST_CAT_AUTOPLUG_ATTEMPT,"factory %s to src caps %d %d", ((GstElementFactory *)dest)->name, ((GstCaps *)src)->id, res);
229   }
230   else if (IS_CAPS (dest)) {
231     res = gst_elementfactory_can_src_caps ((GstElementFactory *)src, (GstCaps *)dest);
232     //GST_INFO (GST_CAT_AUTOPLUG_ATTEMPT,"factory %s to sink caps %d %d", ((GstElementFactory *)src)->name, ((GstCaps *)dest)->id, res);
233   }
234   else {
235     res = gst_autoplug_can_match ((GstElementFactory *)src, (GstElementFactory *)dest);
236   }
237
238   if (res)
239     return 1;
240   else
241     return GST_AUTOPLUG_MAX_COST;
242 }
243
244 static GstElement*
245 gst_static_autoplug_to_render (GstAutoplug *autoplug, GstCaps *srccaps, GstElement *target, va_list args)
246 {
247   caps_struct caps;
248   GstElement *targetelement;
249   GstElement *result = NULL, *srcelement = NULL;
250   GList **factories;
251   GList *chains = NULL;
252   GList *endelements = NULL;
253   guint numsinks = 0, i;
254   gboolean have_common = FALSE;
255
256   targetelement = target;
257
258   /*
259    * We first create a list of elements that are needed
260    * to convert the srcpad caps to the different sinkpad caps.
261    * and add the list of elementfactories to a list (chains).
262    */
263   caps.src  = srccaps;
264
265   while (targetelement) {
266     GList *elements;
267     GstRealPad *pad;
268     GstPadTemplate *templ;
269
270     pad = GST_PAD_REALIZE (gst_element_get_pad_list (targetelement)->data);
271     templ = GST_PAD_PADTEMPLATE (pad);
272
273     if (templ)
274       caps.sink = GST_PADTEMPLATE_CAPS (templ);
275     else 
276       goto next;
277
278     GST_INFO (GST_CAT_AUTOPLUG_ATTEMPT,"autoplugging two caps structures");
279
280     elements =  gst_autoplug_func (caps.src, caps.sink,
281                                    gst_autoplug_elementfactory_get_list,
282                                    gst_autoplug_caps_find_cost,
283                                    &caps);
284
285     if (elements) {
286       chains = g_list_append (chains, elements);
287       endelements = g_list_append (endelements, targetelement);
288       numsinks++;
289     }
290     else {
291     }
292 next:
293     targetelement = va_arg (args, GstElement *);
294   }
295
296   /*
297    * If no list could be found the pipeline cannot be autoplugged and
298    * we return a NULL element
299    */
300   if (numsinks == 0)
301     return NULL;
302
303   /*
304    * We now have a list of lists. We will turn this into an array
305    * of lists, this will make it much more easy to manipulate it
306    * in the next steps.
307    */
308   factories = g_new0 (GList *, numsinks);
309
310   for (i = 0; chains; i++) {
311     GList *elements = (GList *) chains->data;
312
313     factories[i] = elements;
314
315     chains = g_list_next (chains);
316   }
317   //FIXME, free the list
318
319   result = gst_bin_new ("autoplug_bin");
320
321   /*
322    * We now hav a list of lists that is probably like:
323    *
324    *  !
325    *  A -> B -> C
326    *  !
327    *  A -> D -> E
328    *
329    * we now try to find the common elements (A) and add them to
330    * the bin. We remove them from both lists too.
331    */
332   while (factories[0]) {
333     GstElementFactory *factory;
334     GstElement *element;
335
336     // fase 3: add common elements
337     factory = (GstElementFactory *) (factories[0]->data);
338
339     // check to other paths for matching elements (factories)
340     for (i=1; i<numsinks; i++) {
341       if (factory != (GstElementFactory *) (factories[i]->data)) {
342         goto differ;
343       }
344     }
345
346     GST_DEBUG (0,"common factory \"%s\"\n", factory->name);
347
348     element = gst_elementfactory_create (factory, factory->name);
349     gst_bin_add (GST_BIN(result), element);
350
351     if (srcelement != NULL) {
352       gst_autoplug_pads_autoplug (srcelement, element);
353     }
354     // this is the first element, find a good ghostpad
355     else {
356       GList *pads;
357
358       pads = gst_element_get_pad_list (element);
359
360       while (pads) {
361         GstPad *pad = GST_PAD (pads->data);
362         GstPadTemplate *templ = GST_PAD_PADTEMPLATE (pad);
363
364         if (gst_caps_check_compatibility (srccaps, GST_PADTEMPLATE_CAPS (templ))) {
365           gst_element_add_ghost_pad (result, pad, "sink");
366           break;
367         }
368
369         pads = g_list_next (pads);
370       }
371     }
372     gst_autoplug_signal_new_object (GST_AUTOPLUG (autoplug), GST_OBJECT (element));
373
374     srcelement = element;
375
376     // advance the pointer in all lists
377     for (i=0; i<numsinks; i++) {
378       factories[i] = g_list_next (factories[i]);
379     }
380
381     have_common = TRUE;
382   }
383
384 differ:
385
386   // loop over all the sink elements
387   for (i = 0; i < numsinks; i++) {
388     GstElement *thesrcelement = srcelement;
389     GstElement *thebin = GST_ELEMENT(result);
390     GstElement *sinkelement;
391     gboolean use_thread;
392
393     sinkelement = GST_ELEMENT (endelements->data);
394     endelements = g_list_next (endelements);
395
396     use_thread = have_common;
397
398     while (factories[i] || sinkelement) {
399       // fase 4: add other elements...
400       GstElementFactory *factory;
401       GstElement *element;
402
403       if (factories[i]) {
404         factory = (GstElementFactory *)(factories[i]->data);
405
406         GST_DEBUG (0,"factory \"%s\"\n", factory->name);
407         element = gst_elementfactory_create(factory, factory->name);
408       }
409       else {
410         element = sinkelement;
411         sinkelement = NULL;
412       }
413
414       // this element suggests the use of a thread, so we set one up...
415       if (GST_ELEMENT_IS_THREAD_SUGGESTED(element) || use_thread) {
416         GstElement *queue;
417         GstPad *srcpad;
418
419         use_thread = FALSE;
420
421         GST_DEBUG (0,"sugest new thread for \"%s\" %08x\n", GST_ELEMENT_NAME (element), GST_FLAGS(element));
422
423         // create a new queue and add to the previous bin
424         queue = gst_elementfactory_make("queue", g_strconcat("queue_", GST_ELEMENT_NAME(element), NULL));
425         GST_DEBUG (0,"adding element \"%s\"\n", GST_ELEMENT_NAME (element));
426         gst_bin_add(GST_BIN(thebin), queue);
427         gst_autoplug_signal_new_object (GST_AUTOPLUG (autoplug), GST_OBJECT (queue));
428
429         // this will be the new bin for all following elements
430         thebin = gst_elementfactory_make("thread", g_strconcat("thread_", GST_ELEMENT_NAME(element), NULL));
431
432         srcpad = gst_element_get_pad(queue, "src");
433
434         gst_autoplug_pads_autoplug(thesrcelement, queue);
435
436         GST_DEBUG (0,"adding element %s\n", GST_ELEMENT_NAME (element));
437         gst_bin_add(GST_BIN(thebin), element);
438         gst_autoplug_signal_new_object (GST_AUTOPLUG (autoplug), GST_OBJECT (element));
439         GST_DEBUG (0,"adding element %s\n", GST_ELEMENT_NAME (thebin));
440         gst_bin_add(GST_BIN(result), thebin);
441         gst_autoplug_signal_new_object (GST_AUTOPLUG (autoplug), GST_OBJECT (thebin));
442         thesrcelement = queue;
443       }
444       // no thread needed, easy case
445       else {
446         GST_DEBUG (0,"adding element %s\n", GST_ELEMENT_NAME (element));
447         gst_bin_add(GST_BIN(thebin), element);
448         gst_autoplug_signal_new_object (GST_AUTOPLUG (autoplug), GST_OBJECT (element));
449       }
450       gst_autoplug_pads_autoplug(thesrcelement, element);
451
452       // this element is now the new source element
453       thesrcelement = element;
454
455       factories[i] = g_list_next(factories[i]);
456     }
457   }
458
459   return result;
460 }
461
462 /*
463  * shortest path algorithm
464  *
465  */
466 struct _gst_autoplug_node
467 {
468   gpointer iNode;
469   gpointer iPrev;
470   gint iDist;
471 };
472
473 typedef struct _gst_autoplug_node gst_autoplug_node;
474
475 static gint
476 find_factory (gst_autoplug_node *rgnNodes, gpointer factory)
477 {
478   gint i=0;
479
480   while (rgnNodes[i].iNode) {
481     if (rgnNodes[i].iNode == factory) return i;
482     i++;
483   }
484   return 0;
485 }
486
487 static GList*
488 construct_path (gst_autoplug_node *rgnNodes, gpointer factory)
489 {
490   GstElementFactory *current;
491   GList *factories = NULL;
492
493   current = rgnNodes[find_factory(rgnNodes, factory)].iPrev;
494
495   GST_INFO (GST_CAT_AUTOPLUG_ATTEMPT,"factories found in autoplugging (reversed order)");
496
497   while (current != NULL)
498   {
499     gpointer next = NULL;
500
501     next = rgnNodes[find_factory(rgnNodes, current)].iPrev;
502     if (next) {
503       factories = g_list_prepend (factories, current);
504       GST_INFO (GST_CAT_AUTOPLUG_ATTEMPT,"factory: \"%s\"", current->name);
505     }
506     current = next;
507   }
508   return factories;
509 }
510
511 static GList*
512 gst_autoplug_enqueue (GList *queue, gpointer iNode, gint iDist, gpointer iPrev)
513 {
514   gst_autoplug_node *node = g_malloc (sizeof (gst_autoplug_node));
515
516   node->iNode = iNode;
517   node->iDist = iDist;
518   node->iPrev = iPrev;
519
520   queue = g_list_append (queue, node);
521
522   return queue;
523 }
524
525 static GList*
526 gst_autoplug_dequeue (GList *queue, gpointer *iNode, gint *iDist, gpointer *iPrev)
527 {
528   GList *head;
529   gst_autoplug_node *node;
530
531   head = g_list_first (queue);
532
533   if (head) {
534     node = (gst_autoplug_node *)head->data;
535     *iNode = node->iNode;
536     *iPrev = node->iPrev;
537     *iDist = node->iDist;
538     head = g_list_remove (queue, node);
539   }
540
541   return head;
542 }
543
544 static GList*
545 gst_autoplug_func (gpointer src, gpointer sink,
546                    GstAutoplugListFunction list_function,
547                    GstAutoplugCostFunction cost_function,
548                    gpointer data)
549 {
550   gst_autoplug_node *rgnNodes;
551   GList *queue = NULL;
552   gpointer iNode, iPrev;
553   gint iDist, i, iCost;
554
555   GList *elements = g_list_copy (list_function(data));
556   GList *factories;
557   guint num_factories;
558
559   elements = g_list_append (elements, sink);
560   elements = g_list_append (elements, src);
561
562   factories = elements;
563
564   num_factories = g_list_length (factories);
565
566   rgnNodes = g_new0 (gst_autoplug_node, num_factories+1);
567
568   for (i=0; i< num_factories; i++) {
569     gpointer fact = factories->data;
570
571     rgnNodes[i].iNode = fact;
572     rgnNodes[i].iPrev = NULL;
573
574     if (fact == src) {
575       rgnNodes[i].iDist = 0;
576     }
577     else {
578       rgnNodes[i].iDist = GST_AUTOPLUG_MAX_COST;
579     }
580
581     factories = g_list_next (factories);
582   }
583   rgnNodes[num_factories].iNode = NULL;
584
585   queue = gst_autoplug_enqueue (queue, src, 0, NULL);
586
587   while (g_list_length (queue) > 0) {
588     GList *factories2 = elements;
589
590     queue = gst_autoplug_dequeue (queue, &iNode, &iDist, &iPrev);
591
592     for (i=0; i< num_factories; i++) {
593       gpointer current = factories2->data;
594
595       iCost = cost_function (iNode, current, data);
596       if (iCost != GST_AUTOPLUG_MAX_COST) {
597         if ((GST_AUTOPLUG_MAX_COST == rgnNodes[i].iDist) ||
598             (rgnNodes[i].iDist > (iCost + iDist))) {
599           rgnNodes[i].iDist = iDist + iCost;
600           rgnNodes[i].iPrev = iNode;
601
602           queue = gst_autoplug_enqueue (queue, current, iDist + iCost, iNode);
603         }
604       }
605
606       factories2 = g_list_next (factories2);
607     }
608   }
609
610   return construct_path (rgnNodes, sink);
611 }
612