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