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\"\n", 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\"\n", 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 GstElementState state = GST_STATE (gst_element_get_parent (src));
159 if (state == GST_STATE_PLAYING)
160 gst_element_set_state (GST_ELEMENT (gst_element_get_parent (src)), GST_STATE_PAUSED);
162 if ((connected = gst_pad_connect (pad, sinkpad))) {
163 if (state == GST_STATE_PLAYING)
164 gst_element_set_state (GST_ELEMENT (gst_element_get_parent (src)), GST_STATE_PLAYING);
168 GST_DEBUG (0,"pads incompatible %s, %s\n", GST_PAD_NAME (pad), GST_PAD_NAME (sinkpad));
170 if (state == GST_STATE_PLAYING)
171 gst_element_set_state (GST_ELEMENT (gst_element_get_parent (src)), GST_STATE_PLAYING);
173 sinkpads = g_list_next(sinkpads);
177 GST_DEBUG (0,"gstpipeline: no path to sinks for type\n");
183 gst_autoplug_pads_autoplug (GstElement *src, GstElement *sink)
186 gboolean connected = FALSE;
188 srcpads = gst_element_get_pad_list(src);
190 while (srcpads && !connected) {
191 GstPad *srcpad = (GstPad *)srcpads->data;
193 if (gst_pad_get_direction(srcpad) == GST_PAD_SRC) {
194 connected = gst_autoplug_pads_autoplug_func (src, srcpad, sink);
199 srcpads = g_list_next(srcpads);
203 GST_DEBUG (0,"gstpipeline: delaying pad connections for \"%s\" to \"%s\"\n",
204 GST_ELEMENT_NAME(src), GST_ELEMENT_NAME(sink));
205 gtk_signal_connect(GTK_OBJECT(src),"new_pad",
206 GTK_SIGNAL_FUNC(gst_autoplug_pads_autoplug_func), sink);
207 gtk_signal_connect(GTK_OBJECT(src),"new_ghost_pad",
208 GTK_SIGNAL_FUNC(gst_autoplug_pads_autoplug_func), sink);
213 gst_autoplug_elementfactory_get_list (gpointer data)
215 return gst_elementfactory_get_list ();
223 #define IS_CAPS(cap) (((cap) == caps->src) || (cap) == caps->sink)
226 gst_autoplug_caps_find_cost (gpointer src, gpointer dest, gpointer data)
228 caps_struct *caps = (caps_struct *)data;
231 if (IS_CAPS (src) && IS_CAPS (dest)) {
232 res = gst_caps_check_compatibility ((GstCaps *)src, (GstCaps *)dest);
233 //GST_INFO (GST_CAT_AUTOPLUG_ATTEMPT,"caps %d to caps %d %d", ((GstCaps *)src)->id, ((GstCaps *)dest)->id, res);
235 else if (IS_CAPS (src)) {
236 res = gst_elementfactory_can_sink_caps ((GstElementFactory *)dest, (GstCaps *)src);
237 //GST_INFO (GST_CAT_AUTOPLUG_ATTEMPT,"factory %s to src caps %d %d", ((GstElementFactory *)dest)->name, ((GstCaps *)src)->id, res);
239 else if (IS_CAPS (dest)) {
240 res = gst_elementfactory_can_src_caps ((GstElementFactory *)src, (GstCaps *)dest);
241 //GST_INFO (GST_CAT_AUTOPLUG_ATTEMPT,"factory %s to sink caps %d %d", ((GstElementFactory *)src)->name, ((GstCaps *)dest)->id, res);
244 res = gst_autoplug_can_match ((GstElementFactory *)src, (GstElementFactory *)dest);
250 return GST_AUTOPLUG_MAX_COST;
254 gst_static_autoplug_to_render (GstAutoplug *autoplug, GstCaps *srccaps, GstElement *target, va_list args)
257 GstElement *targetelement;
258 GstElement *result = NULL, *srcelement = NULL;
260 GList *chains = NULL;
261 GList *endelements = NULL;
262 guint numsinks = 0, i;
263 gboolean have_common = FALSE;
265 targetelement = target;
268 * We first create a list of elements that are needed
269 * to convert the srcpad caps to the different sinkpad caps.
270 * and add the list of elementfactories to a list (chains).
274 while (targetelement) {
277 GstPadTemplate *templ;
279 pad = GST_PAD_REALIZE (gst_element_get_pad_list (targetelement)->data);
280 templ = GST_PAD_PADTEMPLATE (pad);
283 caps.sink = GST_PADTEMPLATE_CAPS (templ);
287 GST_INFO (GST_CAT_AUTOPLUG_ATTEMPT,"autoplugging two caps structures");
289 elements = gst_autoplug_func (caps.src, caps.sink,
290 gst_autoplug_elementfactory_get_list,
291 gst_autoplug_caps_find_cost,
295 chains = g_list_append (chains, elements);
296 endelements = g_list_append (endelements, targetelement);
302 targetelement = va_arg (args, GstElement *);
306 * If no list could be found the pipeline cannot be autoplugged and
307 * we return a NULL element
313 * We now have a list of lists. We will turn this into an array
314 * of lists, this will make it much more easy to manipulate it
317 factories = g_new0 (GList *, numsinks);
319 for (i = 0; chains; i++) {
320 GList *elements = (GList *) chains->data;
322 factories[i] = elements;
324 chains = g_list_next (chains);
326 //FIXME, free the list
328 result = gst_bin_new ("autoplug_bin");
331 * We now hav a list of lists that is probably like:
338 * we now try to find the common elements (A) and add them to
339 * the bin. We remove them from both lists too.
341 while (factories[0]) {
342 GstElementFactory *factory;
345 // fase 3: add common elements
346 factory = (GstElementFactory *) (factories[0]->data);
348 // check to other paths for matching elements (factories)
349 for (i=1; i<numsinks; i++) {
350 if (factory != (GstElementFactory *) (factories[i]->data)) {
355 GST_DEBUG (0,"common factory \"%s\"\n", factory->name);
357 element = gst_elementfactory_create (factory, factory->name);
358 gst_bin_add (GST_BIN(result), element);
360 if (srcelement != NULL) {
361 gst_autoplug_pads_autoplug (srcelement, element);
363 // this is the first element, find a good ghostpad
367 pads = gst_element_get_pad_list (element);
370 GstPad *pad = GST_PAD (pads->data);
371 GstPadTemplate *templ = GST_PAD_PADTEMPLATE (pad);
373 if (gst_caps_check_compatibility (srccaps, GST_PADTEMPLATE_CAPS (templ))) {
374 gst_element_add_ghost_pad (result, pad, "sink");
378 pads = g_list_next (pads);
381 gst_autoplug_signal_new_object (GST_AUTOPLUG (autoplug), GST_OBJECT (element));
383 srcelement = element;
385 // advance the pointer in all lists
386 for (i=0; i<numsinks; i++) {
387 factories[i] = g_list_next (factories[i]);
395 // loop over all the sink elements
396 for (i = 0; i < numsinks; i++) {
397 GstElement *thesrcelement = srcelement;
398 GstElement *thebin = GST_ELEMENT(result);
399 GstElement *sinkelement;
402 sinkelement = GST_ELEMENT (endelements->data);
403 endelements = g_list_next (endelements);
405 use_thread = have_common;
407 while (factories[i] || sinkelement) {
408 // fase 4: add other elements...
409 GstElementFactory *factory;
413 factory = (GstElementFactory *)(factories[i]->data);
415 GST_DEBUG (0,"factory \"%s\"\n", factory->name);
416 element = gst_elementfactory_create(factory, factory->name);
419 element = sinkelement;
423 // this element suggests the use of a thread, so we set one up...
424 if (GST_ELEMENT_IS_THREAD_SUGGESTED(element) || use_thread) {
430 GST_DEBUG (0,"sugest new thread for \"%s\" %08x\n", GST_ELEMENT_NAME (element), GST_FLAGS(element));
432 // create a new queue and add to the previous bin
433 queue = gst_elementfactory_make("queue", g_strconcat("queue_", GST_ELEMENT_NAME(element), NULL));
434 GST_DEBUG (0,"adding element \"%s\"\n", GST_ELEMENT_NAME (element));
436 // this will be the new bin for all following elements
437 thebin = gst_elementfactory_make("thread", g_strconcat("thread_", GST_ELEMENT_NAME(element), NULL));
439 gst_bin_add(GST_BIN(thebin), queue);
440 gst_autoplug_signal_new_object (GST_AUTOPLUG (autoplug), GST_OBJECT (queue));
442 srcpad = gst_element_get_pad(queue, "src");
444 gst_autoplug_pads_autoplug(thesrcelement, queue);
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 GST_DEBUG (0,"adding element %s\n", GST_ELEMENT_NAME (thebin));
450 gst_bin_add(GST_BIN(result), thebin);
451 gst_autoplug_signal_new_object (GST_AUTOPLUG (autoplug), GST_OBJECT (thebin));
452 thesrcelement = queue;
454 // no thread needed, easy case
456 GST_DEBUG (0,"adding element %s\n", GST_ELEMENT_NAME (element));
457 gst_bin_add(GST_BIN(thebin), element);
458 gst_autoplug_signal_new_object (GST_AUTOPLUG (autoplug), GST_OBJECT (element));
460 gst_autoplug_pads_autoplug(thesrcelement, element);
462 // this element is now the new source element
463 thesrcelement = element;
465 factories[i] = g_list_next(factories[i]);
473 * shortest path algorithm
476 struct _gst_autoplug_node
483 typedef struct _gst_autoplug_node gst_autoplug_node;
486 find_factory (gst_autoplug_node *rgnNodes, gpointer factory)
490 while (rgnNodes[i].iNode) {
491 if (rgnNodes[i].iNode == factory) return i;
498 construct_path (gst_autoplug_node *rgnNodes, gpointer factory)
500 GstElementFactory *current;
501 GList *factories = NULL;
503 current = rgnNodes[find_factory(rgnNodes, factory)].iPrev;
505 GST_INFO (GST_CAT_AUTOPLUG_ATTEMPT,"factories found in autoplugging (reversed order)");
507 while (current != NULL)
509 gpointer next = NULL;
511 next = rgnNodes[find_factory(rgnNodes, current)].iPrev;
513 factories = g_list_prepend (factories, current);
514 GST_INFO (GST_CAT_AUTOPLUG_ATTEMPT,"factory: \"%s\"", current->name);
522 gst_autoplug_enqueue (GList *queue, gpointer iNode, gint iDist, gpointer iPrev)
524 gst_autoplug_node *node = g_malloc (sizeof (gst_autoplug_node));
530 queue = g_list_append (queue, node);
536 gst_autoplug_dequeue (GList *queue, gpointer *iNode, gint *iDist, gpointer *iPrev)
539 gst_autoplug_node *node;
541 head = g_list_first (queue);
544 node = (gst_autoplug_node *)head->data;
545 *iNode = node->iNode;
546 *iPrev = node->iPrev;
547 *iDist = node->iDist;
548 head = g_list_remove (queue, node);
555 gst_autoplug_func (gpointer src, gpointer sink,
556 GstAutoplugListFunction list_function,
557 GstAutoplugCostFunction cost_function,
560 gst_autoplug_node *rgnNodes;
562 gpointer iNode, iPrev;
563 gint iDist, i, iCost;
565 GList *elements = g_list_copy (list_function(data));
569 elements = g_list_append (elements, sink);
570 elements = g_list_append (elements, src);
572 factories = elements;
574 num_factories = g_list_length (factories);
576 rgnNodes = g_new0 (gst_autoplug_node, num_factories+1);
578 for (i=0; i< num_factories; i++) {
579 gpointer fact = factories->data;
581 rgnNodes[i].iNode = fact;
582 rgnNodes[i].iPrev = NULL;
585 rgnNodes[i].iDist = 0;
588 rgnNodes[i].iDist = GST_AUTOPLUG_MAX_COST;
591 factories = g_list_next (factories);
593 rgnNodes[num_factories].iNode = NULL;
595 queue = gst_autoplug_enqueue (queue, src, 0, NULL);
597 while (g_list_length (queue) > 0) {
598 GList *factories2 = elements;
600 queue = gst_autoplug_dequeue (queue, &iNode, &iDist, &iPrev);
602 for (i=0; i< num_factories; i++) {
603 gpointer current = factories2->data;
605 iCost = cost_function (iNode, current, data);
606 if (iCost != GST_AUTOPLUG_MAX_COST) {
607 if ((GST_AUTOPLUG_MAX_COST == rgnNodes[i].iDist) ||
608 (rgnNodes[i].iDist > (iCost + iDist))) {
609 rgnNodes[i].iDist = iDist + iCost;
610 rgnNodes[i].iPrev = iNode;
612 queue = gst_autoplug_enqueue (queue, current, iDist + iCost, iNode);
616 factories2 = g_list_next (factories2);
620 return construct_path (rgnNodes, sink);