2 * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3 * 2000 Wim Taymans <wtay@chello.be>
5 * gststaticautoplug.c: A static Autoplugger of pipelines
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.
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.
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.
23 #include "gststaticautoplug.h"
27 #define GST_AUTOPLUG_MAX_COST 999999
29 typedef guint (*GstAutoplugCostFunction) (gpointer src, gpointer dest, gpointer data);
30 typedef const GList* (*GstAutoplugListFunction) (gpointer data);
33 static void gst_static_autoplug_class_init (GstStaticAutoplugClass *klass);
34 static void gst_static_autoplug_init (GstStaticAutoplug *autoplug);
36 static GList* gst_autoplug_func (gpointer src, gpointer sink,
37 GstAutoplugListFunction list_function,
38 GstAutoplugCostFunction cost_function,
43 static GstElement* gst_static_autoplug_to_caps (GstAutoplug *autoplug,
44 GstCaps *srccaps, GstCaps *sinkcaps, va_list args);
46 static GstAutoplugClass *parent_class = NULL;
48 GType gst_static_autoplug_get_type(void)
50 static GType static_autoplug_type = 0;
52 if (!static_autoplug_type) {
53 static const GTypeInfo static_autoplug_info = {
54 sizeof(GstElementClass),
57 (GClassInitFunc)gst_static_autoplug_class_init,
62 (GInstanceInitFunc)gst_static_autoplug_init,
64 static_autoplug_type = g_type_register_static (GST_TYPE_AUTOPLUG, "GstStaticAutoplug", &static_autoplug_info, 0);
66 return static_autoplug_type;
70 gst_static_autoplug_class_init(GstStaticAutoplugClass *klass)
72 GstAutoplugClass *gstautoplug_class;
74 gstautoplug_class = (GstAutoplugClass*) klass;
76 parent_class = g_type_class_ref(GST_TYPE_AUTOPLUG);
78 gstautoplug_class->autoplug_to_caps = gst_static_autoplug_to_caps;
81 static void gst_static_autoplug_init(GstStaticAutoplug *autoplug) {
85 plugin_init (GModule *module, GstPlugin *plugin)
87 GstAutoplugFactory *factory;
89 gst_plugin_set_longname (plugin, "A static autoplugger");
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 ());
95 if (factory != NULL) {
96 gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
101 GstPluginDesc plugin_desc = {
109 gst_autoplug_can_match (GstElementFactory *src, GstElementFactory *dest)
111 GList *srctemps, *desttemps;
113 srctemps = src->padtemplates;
116 GstPadTemplate *srctemp = (GstPadTemplate *)srctemps->data;
118 desttemps = dest->padtemplates;
121 GstPadTemplate *desttemp = (GstPadTemplate *)desttemps->data;
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));
134 desttemps = g_list_next (desttemps);
136 srctemps = g_list_next (srctemps);
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));
145 gst_autoplug_pads_autoplug_func (GstElement *src, GstPad *pad, GstElement *sink)
147 const GList *sinkpads;
148 gboolean connected = FALSE;
150 GST_DEBUG (0,"gstpipeline: autoplug pad connect function for \"%s\" to \"%s\"",
151 GST_ELEMENT_NAME(src), GST_ELEMENT_NAME(sink));
153 sinkpads = gst_element_get_pad_list(sink);
155 GstPad *sinkpad = (GstPad *)sinkpads->data;
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))
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));
171 GST_DEBUG (0,"pads incompatible %s, %s", GST_PAD_NAME (pad), GST_PAD_NAME (sinkpad));
174 sinkpads = g_list_next(sinkpads);
178 GST_DEBUG (0,"gstpipeline: no path to sinks for type");
187 } dynamic_pad_struct;
190 autoplug_dynamic_pad (GstElement *element, GstPad *pad, gpointer data)
192 dynamic_pad_struct *info = (dynamic_pad_struct *)data;
193 const GList *pads = gst_element_get_pad_list (element);
195 GST_DEBUG (0,"attempting to dynamically create a ghostpad for %s=%s", GST_ELEMENT_NAME (element),
199 GstPad *pad = GST_PAD (pads->data);
200 GstPadTemplate *templ = GST_PAD_PAD_TEMPLATE (pad);
201 pads = g_list_next (pads);
203 if (gst_caps_check_compatibility (GST_PAD_TEMPLATE_CAPS (templ), info->endcap)) {
206 name = g_strdup_printf ("src_%02d", info->i);
207 gst_element_add_ghost_pad (info->result, pad, name);
210 GST_DEBUG (0,"gstpipeline: new dynamic pad %s", GST_PAD_NAME (pad));
217 gst_autoplug_pads_autoplug (GstElement *src, GstElement *sink)
219 const GList *srcpads;
220 gboolean connected = FALSE;
222 srcpads = gst_element_get_pad_list(src);
224 while (srcpads && !connected) {
225 GstPad *srcpad = (GstPad *)srcpads->data;
227 if (gst_pad_get_direction(srcpad) == GST_PAD_SRC)
228 connected = gst_autoplug_pads_autoplug_func (src, srcpad, sink);
230 srcpads = g_list_next(srcpads);
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);
242 gst_autoplug_element_factory_get_list (gpointer data)
244 return gst_registry_pool_feature_list (GST_TYPE_ELEMENT_FACTORY);
252 #define IS_CAPS(cap) (((cap) == caps->src) || (cap) == caps->sink)
255 gst_autoplug_caps_find_cost (gpointer src, gpointer dest, gpointer data)
257 caps_struct *caps = (caps_struct *)data;
260 if (IS_CAPS (src) && IS_CAPS (dest)) {
261 res = gst_caps_check_compatibility ((GstCaps *)src, (GstCaps *)dest);
263 else if (IS_CAPS (src)) {
264 res = gst_element_factory_can_sink_caps ((GstElementFactory *)dest, (GstCaps *)src);
266 else if (IS_CAPS (dest)) {
267 res = gst_element_factory_can_src_caps ((GstElementFactory *)src, (GstCaps *)dest);
270 res = gst_autoplug_can_match ((GstElementFactory *)src, (GstElementFactory *)dest);
276 return GST_AUTOPLUG_MAX_COST;
280 gst_static_autoplug_to_caps (GstAutoplug *autoplug, GstCaps *srccaps, GstCaps *sinkcaps, va_list args)
284 GstElement *result = NULL, *srcelement = NULL;
286 GList *chains = NULL;
287 GList *endcaps = NULL;
288 guint numsinks = 0, i;
289 gboolean have_common = FALSE;
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).
303 caps.sink = capslist;
305 GST_INFO (GST_CAT_AUTOPLUG_ATTEMPT,"autoplugging two caps structures");
307 elements = gst_autoplug_func (caps.src, caps.sink,
308 gst_autoplug_element_factory_get_list,
309 gst_autoplug_caps_find_cost,
313 chains = g_list_append (chains, elements);
314 endcaps = g_list_append (endcaps, capslist);
320 capslist = va_arg (args, GstCaps *);
324 * If no list could be found the pipeline cannot be autoplugged and
325 * we return a NULL element
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
335 factories = g_new0 (GList *, numsinks);
337 for (i = 0; chains; i++) {
338 GList *elements = (GList *) chains->data;
340 factories[i] = elements;
342 chains = g_list_next (chains);
344 /*FIXME, free the list */
346 result = gst_bin_new ("autoplug_bin");
349 * We now hav a list of lists that is probably like:
356 * we now try to find the common elements (A) and add them to
357 * the bin. We remove them from both lists too.
359 while (factories[0]) {
360 GstElementFactory *factory;
364 /* fase 3: add common elements */
365 factory = (GstElementFactory *) (factories[0]->data);
367 /* check to other paths for matching elements (factories) */
368 for (i=1; i<numsinks; i++) {
369 if (factory != (GstElementFactory *) (factories[i]->data)) {
374 GST_DEBUG (0,"common factory \"%s\"", GST_OBJECT_NAME (factory));
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.
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.
384 name = g_strdup (GST_OBJECT_NAME (factory));
385 element = gst_element_factory_create (factory, name);
387 gst_bin_add (GST_BIN(result), element);
389 if (srcelement != NULL) {
390 gst_autoplug_pads_autoplug (srcelement, element);
392 /* this is the first element, find a good ghostpad */
396 pads = gst_element_get_pad_list (element);
399 GstPad *pad = GST_PAD (pads->data);
400 GstPadTemplate *templ = GST_PAD_PAD_TEMPLATE (pad);
402 if (gst_caps_check_compatibility (srccaps, GST_PAD_TEMPLATE_CAPS (templ))) {
403 gst_element_add_ghost_pad (result, pad, "sink");
407 pads = g_list_next (pads);
410 gst_autoplug_signal_new_object (GST_AUTOPLUG (autoplug), GST_OBJECT (element));
412 srcelement = element;
414 /* advance the pointer in all lists */
415 for (i=0; i<numsinks; i++) {
416 factories[i] = g_list_next (factories[i]);
424 /* loop over all the sink elements */
425 for (i = 0; i < numsinks; i++) {
426 GstElement *thesrcelement = srcelement;
427 GstElement *thebin = GST_ELEMENT(result);
429 while (factories[i]) {
430 /* fase 4: add other elements... */
431 GstElementFactory *factory;
434 factory = (GstElementFactory *)(factories[i]->data);
436 GST_DEBUG (0,"factory \"%s\"", GST_OBJECT_NAME (factory));
437 element = gst_element_factory_create(factory, GST_OBJECT_NAME (factory));
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));
443 gst_autoplug_pads_autoplug(thesrcelement, element);
445 /* this element is now the new source element */
446 thesrcelement = element;
448 factories[i] = g_list_next(factories[i]);
451 * we're at the last element in the chain,
452 * find a suitable pad to turn into a ghostpad
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);
460 GST_DEBUG (0,"attempting to create a ghostpad for %s", GST_ELEMENT_NAME (thesrcelement));
463 GstPad *pad = GST_PAD (pads->data);
464 GstPadTemplate *templ = GST_PAD_PAD_TEMPLATE (pad);
465 pads = g_list_next (pads);
467 if (gst_caps_check_compatibility (GST_PAD_TEMPLATE_CAPS (templ), endcap)) {
470 name = g_strdup_printf ("src_%02d", i);
471 gst_element_add_ghost_pad (result, pad, name);
479 dynamic_pad_struct *data = g_new0(dynamic_pad_struct, 1);
481 data->result = result;
482 data->endcap = endcap;
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);
496 * shortest path algorithm
499 struct _gst_autoplug_node
506 typedef struct _gst_autoplug_node gst_autoplug_node;
509 find_factory (gst_autoplug_node *rgnNodes, gpointer factory)
513 while (rgnNodes[i].iNode) {
514 if (rgnNodes[i].iNode == factory) return i;
521 construct_path (gst_autoplug_node *rgnNodes, gpointer factory)
523 GstElementFactory *current;
524 GList *factories = NULL;
526 current = rgnNodes[find_factory(rgnNodes, factory)].iPrev;
528 GST_INFO (GST_CAT_AUTOPLUG_ATTEMPT,"factories found in autoplugging (reversed order)");
530 while (current != NULL)
532 gpointer next = NULL;
534 next = rgnNodes[find_factory(rgnNodes, current)].iPrev;
536 factories = g_list_prepend (factories, current);
537 GST_INFO (GST_CAT_AUTOPLUG_ATTEMPT,"factory: \"%s\"", GST_OBJECT_NAME (current));
545 gst_autoplug_enqueue (GList *queue, gpointer iNode, gint iDist, gpointer iPrev)
547 gst_autoplug_node *node = g_malloc (sizeof (gst_autoplug_node));
553 queue = g_list_append (queue, node);
559 gst_autoplug_dequeue (GList *queue, gpointer *iNode, gint *iDist, gpointer *iPrev)
562 gst_autoplug_node *node;
564 head = g_list_first (queue);
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);
578 gst_autoplug_func (gpointer src, gpointer sink,
579 GstAutoplugListFunction list_function,
580 GstAutoplugCostFunction cost_function,
583 gst_autoplug_node *rgnNodes;
585 gpointer iNode, iPrev;
586 gint iDist, i, iCost;
588 GList *elements = g_list_copy ((GList *)list_function(data));
592 elements = g_list_append (elements, sink);
593 elements = g_list_append (elements, src);
595 factories = elements;
597 num_factories = g_list_length (factories);
599 rgnNodes = g_new0 (gst_autoplug_node, num_factories+1);
601 for (i=0; i< num_factories; i++) {
602 gpointer fact = factories->data;
604 rgnNodes[i].iNode = fact;
605 rgnNodes[i].iPrev = NULL;
608 rgnNodes[i].iDist = 0;
611 rgnNodes[i].iDist = GST_AUTOPLUG_MAX_COST;
614 factories = g_list_next (factories);
616 rgnNodes[num_factories].iNode = NULL;
618 queue = gst_autoplug_enqueue (queue, src, 0, NULL);
620 while (g_list_length (queue) > 0) {
621 GList *factories2 = elements;
623 queue = gst_autoplug_dequeue (queue, &iNode, &iDist, &iPrev);
625 for (i=0; i< num_factories; i++) {
626 gpointer current = factories2->data;
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;
635 queue = gst_autoplug_enqueue (queue, current, iDist + iCost, iNode);
639 factories2 = g_list_next (factories2);
643 return construct_path (rgnNodes, sink);