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 "gststaticautoplugrender.h"
27 #define GST_AUTOPLUG_MAX_COST 999999
29 typedef guint (*GstAutoplugCostFunction) (gpointer src, gpointer dest, gpointer data);
30 typedef GList* (*GstAutoplugListFunction) (gpointer data);
33 static void gst_static_autoplug_render_class_init (GstStaticAutoplugRenderClass *klass);
34 static void gst_static_autoplug_render_init (GstStaticAutoplugRender *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_render (GstAutoplug *autoplug,
44 GstCaps *srccaps, GstElement *target, va_list args);
46 static GstAutoplugClass *parent_class = NULL;
48 GtkType gst_static_autoplug_render_get_type(void)
50 static GtkType static_autoplug_type = 0;
52 if (!static_autoplug_type) {
53 static const GtkTypeInfo static_autoplug_info = {
54 "GstStaticAutoplugRender",
56 sizeof(GstElementClass),
57 (GtkClassInitFunc)gst_static_autoplug_render_class_init,
58 (GtkObjectInitFunc)gst_static_autoplug_render_init,
61 (GtkClassInitFunc)NULL,
63 static_autoplug_type = gtk_type_unique (GST_TYPE_AUTOPLUG, &static_autoplug_info);
65 return static_autoplug_type;
69 gst_static_autoplug_render_class_init(GstStaticAutoplugRenderClass *klass)
71 GstAutoplugClass *gstautoplug_class;
73 gstautoplug_class = (GstAutoplugClass*) klass;
75 parent_class = gtk_type_class(GST_TYPE_AUTOPLUG);
77 gstautoplug_class->autoplug_to_renderers = gst_static_autoplug_to_render;
80 static void gst_static_autoplug_render_init(GstStaticAutoplugRender *autoplug) {
84 plugin_init (GModule *module, GstPlugin *plugin)
86 GstAutoplugFactory *factory;
88 gst_plugin_set_longname (plugin, "A static autoplugger");
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 ());
94 if (factory != NULL) {
95 gst_plugin_add_autoplugger (plugin, factory);
100 GstPluginDesc plugin_desc = {
103 "gststaticautoplugrender",
108 gst_autoplug_can_match (GstElementFactory *src, GstElementFactory *dest)
110 GList *srctemps, *desttemps;
112 srctemps = src->padtemplates;
115 GstPadTemplate *srctemp = (GstPadTemplate *)srctemps->data;
117 desttemps = dest->padtemplates;
120 GstPadTemplate *desttemp = (GstPadTemplate *)desttemps->data;
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);
131 desttemps = g_list_next (desttemps);
133 srctemps = g_list_next (srctemps);
135 GST_DEBUG (GST_CAT_AUTOPLUG_ATTEMPT,
136 "factory \"%s\" cannot connect with factory \"%s\"", src->name, dest->name);
141 gst_autoplug_pads_autoplug_func (GstElement *src, GstPad *pad, GstElement *sink)
144 gboolean connected = FALSE;
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));
149 sinkpads = gst_element_get_pad_list(sink);
151 GstPad *sinkpad = (GstPad *)sinkpads->data;
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))
157 if ((connected = gst_pad_connect (pad, sinkpad))) {
161 GST_DEBUG (0,"pads incompatible %s, %s\n", GST_PAD_NAME (pad), GST_PAD_NAME (sinkpad));
164 sinkpads = g_list_next(sinkpads);
168 GST_DEBUG (0,"gstpipeline: no path to sinks for type\n");
174 gst_autoplug_pads_autoplug (GstElement *src, GstElement *sink)
177 gboolean connected = FALSE;
179 srcpads = gst_element_get_pad_list(src);
181 while (srcpads && !connected) {
182 GstPad *srcpad = (GstPad *)srcpads->data;
184 if (gst_pad_get_direction(srcpad) == GST_PAD_SRC) {
185 connected = gst_autoplug_pads_autoplug_func (src, srcpad, sink);
190 srcpads = g_list_next(srcpads);
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);
204 gst_autoplug_elementfactory_get_list (gpointer data)
206 return gst_elementfactory_get_list ();
214 #define IS_CAPS(cap) (((cap) == caps->src) || (cap) == caps->sink)
217 gst_autoplug_caps_find_cost (gpointer src, gpointer dest, gpointer data)
219 caps_struct *caps = (caps_struct *)data;
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);
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);
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);
235 res = gst_autoplug_can_match ((GstElementFactory *)src, (GstElementFactory *)dest);
241 return GST_AUTOPLUG_MAX_COST;
245 gst_static_autoplug_to_render (GstAutoplug *autoplug, GstCaps *srccaps, GstElement *target, va_list args)
248 GstElement *targetelement;
249 GstElement *result = NULL, *srcelement = NULL;
251 GList *chains = NULL;
252 GList *endelements = NULL;
253 guint numsinks = 0, i;
254 gboolean have_common = FALSE;
256 targetelement = target;
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).
265 while (targetelement) {
268 GstPadTemplate *templ;
270 pad = GST_PAD_REALIZE (gst_element_get_pad_list (targetelement)->data);
271 templ = GST_PAD_PADTEMPLATE (pad);
274 caps.sink = GST_PADTEMPLATE_CAPS (templ);
278 GST_INFO (GST_CAT_AUTOPLUG_ATTEMPT,"autoplugging two caps structures");
280 elements = gst_autoplug_func (caps.src, caps.sink,
281 gst_autoplug_elementfactory_get_list,
282 gst_autoplug_caps_find_cost,
286 chains = g_list_append (chains, elements);
287 endelements = g_list_append (endelements, targetelement);
293 targetelement = va_arg (args, GstElement *);
297 * If no list could be found the pipeline cannot be autoplugged and
298 * we return a NULL element
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
308 factories = g_new0 (GList *, numsinks);
310 for (i = 0; chains; i++) {
311 GList *elements = (GList *) chains->data;
313 factories[i] = elements;
315 chains = g_list_next (chains);
317 //FIXME, free the list
319 result = gst_bin_new ("autoplug_bin");
322 * We now hav a list of lists that is probably like:
329 * we now try to find the common elements (A) and add them to
330 * the bin. We remove them from both lists too.
332 while (factories[0]) {
333 GstElementFactory *factory;
336 // fase 3: add common elements
337 factory = (GstElementFactory *) (factories[0]->data);
339 // check to other paths for matching elements (factories)
340 for (i=1; i<numsinks; i++) {
341 if (factory != (GstElementFactory *) (factories[i]->data)) {
346 GST_DEBUG (0,"common factory \"%s\"\n", factory->name);
348 element = gst_elementfactory_create (factory, factory->name);
349 gst_bin_add (GST_BIN(result), element);
351 if (srcelement != NULL) {
352 gst_autoplug_pads_autoplug (srcelement, element);
354 // this is the first element, find a good ghostpad
358 pads = gst_element_get_pad_list (element);
361 GstPad *pad = GST_PAD (pads->data);
362 GstPadTemplate *templ = GST_PAD_PADTEMPLATE (pad);
364 if (gst_caps_check_compatibility (srccaps, GST_PADTEMPLATE_CAPS (templ))) {
365 gst_element_add_ghost_pad (result, pad, "sink");
369 pads = g_list_next (pads);
372 gst_autoplug_signal_new_object (GST_AUTOPLUG (autoplug), GST_OBJECT (element));
374 srcelement = element;
376 // advance the pointer in all lists
377 for (i=0; i<numsinks; i++) {
378 factories[i] = g_list_next (factories[i]);
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;
393 sinkelement = GST_ELEMENT (endelements->data);
394 endelements = g_list_next (endelements);
396 use_thread = have_common;
398 while (factories[i] || sinkelement) {
399 // fase 4: add other elements...
400 GstElementFactory *factory;
404 factory = (GstElementFactory *)(factories[i]->data);
406 GST_DEBUG (0,"factory \"%s\"\n", factory->name);
407 element = gst_elementfactory_create(factory, factory->name);
410 element = sinkelement;
414 // this element suggests the use of a thread, so we set one up...
415 if (GST_ELEMENT_IS_THREAD_SUGGESTED(element) || use_thread) {
421 GST_DEBUG (0,"sugest new thread for \"%s\" %08x\n", GST_ELEMENT_NAME (element), GST_FLAGS(element));
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));
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));
432 srcpad = gst_element_get_pad(queue, "src");
434 gst_autoplug_pads_autoplug(thesrcelement, queue);
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;
444 // no thread needed, easy case
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));
450 gst_autoplug_pads_autoplug(thesrcelement, element);
452 // this element is now the new source element
453 thesrcelement = element;
455 factories[i] = g_list_next(factories[i]);
463 * shortest path algorithm
466 struct _gst_autoplug_node
473 typedef struct _gst_autoplug_node gst_autoplug_node;
476 find_factory (gst_autoplug_node *rgnNodes, gpointer factory)
480 while (rgnNodes[i].iNode) {
481 if (rgnNodes[i].iNode == factory) return i;
488 construct_path (gst_autoplug_node *rgnNodes, gpointer factory)
490 GstElementFactory *current;
491 GList *factories = NULL;
493 current = rgnNodes[find_factory(rgnNodes, factory)].iPrev;
495 GST_INFO (GST_CAT_AUTOPLUG_ATTEMPT,"factories found in autoplugging (reversed order)");
497 while (current != NULL)
499 gpointer next = NULL;
501 next = rgnNodes[find_factory(rgnNodes, current)].iPrev;
503 factories = g_list_prepend (factories, current);
504 GST_INFO (GST_CAT_AUTOPLUG_ATTEMPT,"factory: \"%s\"", current->name);
512 gst_autoplug_enqueue (GList *queue, gpointer iNode, gint iDist, gpointer iPrev)
514 gst_autoplug_node *node = g_malloc (sizeof (gst_autoplug_node));
520 queue = g_list_append (queue, node);
526 gst_autoplug_dequeue (GList *queue, gpointer *iNode, gint *iDist, gpointer *iPrev)
529 gst_autoplug_node *node;
531 head = g_list_first (queue);
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);
545 gst_autoplug_func (gpointer src, gpointer sink,
546 GstAutoplugListFunction list_function,
547 GstAutoplugCostFunction cost_function,
550 gst_autoplug_node *rgnNodes;
552 gpointer iNode, iPrev;
553 gint iDist, i, iCost;
555 GList *elements = g_list_copy (list_function(data));
559 elements = g_list_append (elements, sink);
560 elements = g_list_append (elements, src);
562 factories = elements;
564 num_factories = g_list_length (factories);
566 rgnNodes = g_new0 (gst_autoplug_node, num_factories+1);
568 for (i=0; i< num_factories; i++) {
569 gpointer fact = factories->data;
571 rgnNodes[i].iNode = fact;
572 rgnNodes[i].iPrev = NULL;
575 rgnNodes[i].iDist = 0;
578 rgnNodes[i].iDist = GST_AUTOPLUG_MAX_COST;
581 factories = g_list_next (factories);
583 rgnNodes[num_factories].iNode = NULL;
585 queue = gst_autoplug_enqueue (queue, src, 0, NULL);
587 while (g_list_length (queue) > 0) {
588 GList *factories2 = elements;
590 queue = gst_autoplug_dequeue (queue, &iNode, &iDist, &iPrev);
592 for (i=0; i< num_factories; i++) {
593 gpointer current = factories2->data;
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;
602 queue = gst_autoplug_enqueue (queue, current, iDist + iCost, iNode);
606 factories2 = g_list_next (factories2);
610 return construct_path (rgnNodes, sink);